[fcformat] Add simple converters
The format '%{family|downcase}' for example prints the lowercase of the family element. Three converters are defined right now: 'downcase', 'basename', and 'dirname'.
This commit is contained in:
parent
7717b25ffd
commit
2017a5eb79
216
src/fcformat.c
216
src/fcformat.c
|
@ -28,6 +28,14 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some ideas for future syntax extensions:
|
||||||
|
*
|
||||||
|
* - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
|
||||||
|
* - allow indexing simple tags using '%{elt[idx]}'
|
||||||
|
* - conditional/filtering/deletion on binding (using '(w)'/'(s)' notation)
|
||||||
|
*/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
message (const char *fmt, ...)
|
message (const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -45,7 +53,7 @@ typedef struct _FcFormatContext
|
||||||
const FcChar8 *format_orig;
|
const FcChar8 *format_orig;
|
||||||
const FcChar8 *format;
|
const FcChar8 *format;
|
||||||
int format_len;
|
int format_len;
|
||||||
FcChar8 *scratch;
|
FcChar8 *word;
|
||||||
} FcFormatContext;
|
} FcFormatContext;
|
||||||
|
|
||||||
static FcBool
|
static FcBool
|
||||||
|
@ -54,9 +62,9 @@ FcFormatContextInit (FcFormatContext *c,
|
||||||
{
|
{
|
||||||
c->format_orig = c->format = format;
|
c->format_orig = c->format = format;
|
||||||
c->format_len = strlen ((const char *) format);
|
c->format_len = strlen ((const char *) format);
|
||||||
c->scratch = malloc (c->format_len + 1);
|
c->word = malloc (c->format_len + 1);
|
||||||
|
|
||||||
return c->scratch != NULL;
|
return c->word != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -64,7 +72,7 @@ FcFormatContextDone (FcFormatContext *c)
|
||||||
{
|
{
|
||||||
if (c)
|
if (c)
|
||||||
{
|
{
|
||||||
free (c->scratch);
|
free (c->word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,11 +125,11 @@ FcCharIsPunct (const FcChar8 c)
|
||||||
}
|
}
|
||||||
|
|
||||||
static FcBool
|
static FcBool
|
||||||
read_elt_name_to_scratch (FcFormatContext *c)
|
read_word (FcFormatContext *c)
|
||||||
{
|
{
|
||||||
FcChar8 *p;
|
FcChar8 *p;
|
||||||
|
|
||||||
p = c->scratch;
|
p = c->word;
|
||||||
|
|
||||||
while (*c->format)
|
while (*c->format)
|
||||||
{
|
{
|
||||||
|
@ -139,7 +147,7 @@ read_elt_name_to_scratch (FcFormatContext *c)
|
||||||
}
|
}
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
if (p == c->scratch)
|
if (p == c->word)
|
||||||
{
|
{
|
||||||
message ("expected element name at %d",
|
message ("expected element name at %d",
|
||||||
c->format - c->format_orig + 1);
|
c->format - c->format_orig + 1);
|
||||||
|
@ -181,11 +189,13 @@ skip_subexpr (FcFormatContext *c);
|
||||||
static FcBool
|
static FcBool
|
||||||
skip_percent (FcFormatContext *c)
|
skip_percent (FcFormatContext *c)
|
||||||
{
|
{
|
||||||
|
int width;
|
||||||
|
|
||||||
if (!expect_char (c, '%'))
|
if (!expect_char (c, '%'))
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
|
|
||||||
/* skip an optional width specifier */
|
/* skip an optional width specifier */
|
||||||
strtol ((const char *) c->format, (char **) &c->format, 10);
|
width = strtol ((const char *) c->format, (char **) &c->format, 10);
|
||||||
|
|
||||||
if (!expect_char (c, '{'))
|
if (!expect_char (c, '{'))
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
|
@ -250,7 +260,7 @@ maybe_skip_subexpr (FcFormatContext *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
static FcBool
|
static FcBool
|
||||||
interpret_simple_tag (FcFormatContext *c,
|
interpret_simple (FcFormatContext *c,
|
||||||
FcPattern *pat,
|
FcPattern *pat,
|
||||||
FcStrBuf *buf)
|
FcStrBuf *buf)
|
||||||
{
|
{
|
||||||
|
@ -261,14 +271,14 @@ interpret_simple_tag (FcFormatContext *c,
|
||||||
if (consume_char (c, ':'))
|
if (consume_char (c, ':'))
|
||||||
add_colon = FcTrue;
|
add_colon = FcTrue;
|
||||||
|
|
||||||
if (!read_elt_name_to_scratch (c))
|
if (!read_word (c))
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
|
|
||||||
if (consume_char (c, '='))
|
if (consume_char (c, '='))
|
||||||
add_elt_name = FcTrue;
|
add_elt_name = FcTrue;
|
||||||
|
|
||||||
e = FcPatternObjectFindElt (pat,
|
e = FcPatternObjectFindElt (pat,
|
||||||
FcObjectFromName ((const char *) c->scratch));
|
FcObjectFromName ((const char *) c->word));
|
||||||
if (e)
|
if (e)
|
||||||
{
|
{
|
||||||
FcValueListPtr l;
|
FcValueListPtr l;
|
||||||
|
@ -277,7 +287,7 @@ interpret_simple_tag (FcFormatContext *c,
|
||||||
FcStrBufChar (buf, ':');
|
FcStrBufChar (buf, ':');
|
||||||
if (add_elt_name)
|
if (add_elt_name)
|
||||||
{
|
{
|
||||||
FcStrBufString (buf, c->scratch);
|
FcStrBufString (buf, c->word);
|
||||||
FcStrBufChar (buf, '=');
|
FcStrBufChar (buf, '=');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,8 +315,8 @@ interpret_filter (FcFormatContext *c,
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (!read_elt_name_to_scratch (c) ||
|
if (!read_word (c) ||
|
||||||
!FcObjectSetAdd (os, (const char *) c->scratch))
|
!FcObjectSetAdd (os, (const char *) c->word))
|
||||||
{
|
{
|
||||||
FcObjectSetDestroy (os);
|
FcObjectSetDestroy (os);
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
|
@ -341,13 +351,13 @@ interpret_delete (FcFormatContext *c,
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (!read_elt_name_to_scratch (c))
|
if (!read_word (c))
|
||||||
{
|
{
|
||||||
FcPatternDestroy (subpat);
|
FcPatternDestroy (subpat);
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
FcPatternDel (subpat, (const char *) c->scratch);
|
FcPatternDel (subpat, (const char *) c->word);
|
||||||
}
|
}
|
||||||
while (consume_char (c, ','));
|
while (consume_char (c, ','));
|
||||||
|
|
||||||
|
@ -359,7 +369,7 @@ interpret_delete (FcFormatContext *c,
|
||||||
}
|
}
|
||||||
|
|
||||||
static FcBool
|
static FcBool
|
||||||
interpret_conditional (FcFormatContext *c,
|
interpret_cond (FcFormatContext *c,
|
||||||
FcPattern *pat,
|
FcPattern *pat,
|
||||||
FcStrBuf *buf)
|
FcStrBuf *buf)
|
||||||
{
|
{
|
||||||
|
@ -377,14 +387,14 @@ interpret_conditional (FcFormatContext *c,
|
||||||
|
|
||||||
negate = consume_char (c, '!');
|
negate = consume_char (c, '!');
|
||||||
|
|
||||||
if (!read_elt_name_to_scratch (c))
|
if (!read_word (c))
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
|
|
||||||
pass = pass &&
|
pass = pass &&
|
||||||
(negate ^
|
(negate ^
|
||||||
FcResultMatch == FcPatternGet (pat,
|
(FcResultMatch == FcPatternGet (pat,
|
||||||
(const char *) c->scratch,
|
(const char *) c->word,
|
||||||
0, &v));
|
0, &v)));
|
||||||
}
|
}
|
||||||
while (consume_char (c, ','));
|
while (consume_char (c, ','));
|
||||||
|
|
||||||
|
@ -404,12 +414,96 @@ interpret_conditional (FcFormatContext *c,
|
||||||
return FcTrue;
|
return FcTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FcChar8 *
|
||||||
|
convert (FcFormatContext *c,
|
||||||
|
const FcChar8 *str)
|
||||||
|
{
|
||||||
|
if (!read_word (c))
|
||||||
|
return NULL;
|
||||||
|
else if (0 == strcmp ((const char *) c->word, "downcase"))
|
||||||
|
return FcStrDowncase (str);
|
||||||
|
else if (0 == strcmp ((const char *) c->word, "basename"))
|
||||||
|
return FcStrBasename (str);
|
||||||
|
else if (0 == strcmp ((const char *) c->word, "dirname"))
|
||||||
|
return FcStrDirname (str);
|
||||||
|
|
||||||
|
message ("unknown converter \"%s\"",
|
||||||
|
c->word);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FcBool
|
||||||
|
maybe_interpret_converts (FcFormatContext *c,
|
||||||
|
FcStrBuf *buf,
|
||||||
|
int start)
|
||||||
|
{
|
||||||
|
while (consume_char (c, '|'))
|
||||||
|
{
|
||||||
|
const FcChar8 *str;
|
||||||
|
FcChar8 *new_str;
|
||||||
|
|
||||||
|
/* nul-terminate the buffer */
|
||||||
|
FcStrBufChar (buf, '\0');
|
||||||
|
if (buf->failed)
|
||||||
|
return FcFalse;
|
||||||
|
str = buf->buf + start;
|
||||||
|
|
||||||
|
if (!(new_str = convert (c, str)))
|
||||||
|
return FcFalse;
|
||||||
|
|
||||||
|
/* replace in the buffer */
|
||||||
|
buf->len = start;
|
||||||
|
FcStrBufString (buf, new_str);
|
||||||
|
free (new_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FcTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FcBool
|
||||||
|
align_to_width (FcStrBuf *buf,
|
||||||
|
int start,
|
||||||
|
int width)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (buf->failed)
|
||||||
|
return FcFalse;
|
||||||
|
|
||||||
|
len = buf->len - start;
|
||||||
|
if (len < -width)
|
||||||
|
{
|
||||||
|
/* left align */
|
||||||
|
while (len++ < -width)
|
||||||
|
FcStrBufChar (buf, ' ');
|
||||||
|
}
|
||||||
|
else if (len < width)
|
||||||
|
{
|
||||||
|
int old_len;
|
||||||
|
old_len = len;
|
||||||
|
/* right align */
|
||||||
|
while (len++ < width)
|
||||||
|
FcStrBufChar (buf, ' ');
|
||||||
|
if (buf->failed)
|
||||||
|
return FcFalse;
|
||||||
|
len = old_len;
|
||||||
|
memmove (buf->buf + buf->len - len,
|
||||||
|
buf->buf + buf->len - width,
|
||||||
|
len);
|
||||||
|
memset (buf->buf + buf->len - width,
|
||||||
|
' ',
|
||||||
|
width - len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !buf->failed;
|
||||||
|
}
|
||||||
static FcBool
|
static FcBool
|
||||||
interpret_percent (FcFormatContext *c,
|
interpret_percent (FcFormatContext *c,
|
||||||
FcPattern *pat,
|
FcPattern *pat,
|
||||||
FcStrBuf *buf)
|
FcStrBuf *buf)
|
||||||
{
|
{
|
||||||
int width, before;
|
int width, start;
|
||||||
|
FcBool ret;
|
||||||
|
|
||||||
if (!expect_char (c, '%'))
|
if (!expect_char (c, '%'))
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
|
@ -423,79 +517,23 @@ interpret_percent (FcFormatContext *c,
|
||||||
/* parse an optional width specifier */
|
/* parse an optional width specifier */
|
||||||
width = strtol ((const char *) c->format, (char **) &c->format, 10);
|
width = strtol ((const char *) c->format, (char **) &c->format, 10);
|
||||||
|
|
||||||
before = buf->len;
|
|
||||||
|
|
||||||
if (!expect_char (c, '{'))
|
if (!expect_char (c, '{'))
|
||||||
return FcFalse;
|
return FcFalse;
|
||||||
|
|
||||||
|
start = buf->len;
|
||||||
|
|
||||||
switch (*c->format) {
|
switch (*c->format) {
|
||||||
|
case '{': ret = interpret_subexpr (c, pat, buf); break;
|
||||||
case '{':
|
case '+': ret = interpret_filter (c, pat, buf); break;
|
||||||
/* subexpression */
|
case '-': ret = interpret_delete (c, pat, buf); break;
|
||||||
if (!interpret_subexpr (c, pat, buf))
|
case '?': ret = interpret_cond (c, pat, buf); break;
|
||||||
return FcFalse;
|
default: ret = interpret_simple (c, pat, buf); break;
|
||||||
break;
|
|
||||||
|
|
||||||
case '+':
|
|
||||||
/* filtering pattern elements */
|
|
||||||
if (!interpret_filter (c, pat, buf))
|
|
||||||
return FcFalse;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '-':
|
|
||||||
/* deleting pattern elements */
|
|
||||||
if (!interpret_delete (c, pat, buf))
|
|
||||||
return FcFalse;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '?':
|
|
||||||
/* conditional on pattern elements */
|
|
||||||
if (!interpret_conditional (c, pat, buf))
|
|
||||||
return FcFalse;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* simple tag */
|
|
||||||
if (!interpret_simple_tag (c, pat, buf))
|
|
||||||
return FcFalse;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* handle filters, if any */
|
return ret &&
|
||||||
/* XXX */
|
maybe_interpret_converts (c, buf, start) &&
|
||||||
|
align_to_width (buf, start, width) &&
|
||||||
/* align to width */
|
expect_char (c, '}');
|
||||||
if (!buf->failed)
|
|
||||||
{
|
|
||||||
int after, len;
|
|
||||||
|
|
||||||
after = buf->len;
|
|
||||||
|
|
||||||
len = after - before;
|
|
||||||
|
|
||||||
if (len < -width)
|
|
||||||
{
|
|
||||||
/* left align */
|
|
||||||
while (len++ < -width)
|
|
||||||
FcStrBufChar (buf, ' ');
|
|
||||||
}
|
|
||||||
else if (len < width)
|
|
||||||
{
|
|
||||||
/* right align */
|
|
||||||
while (len++ < width)
|
|
||||||
FcStrBufChar (buf, ' ');
|
|
||||||
len = after - before;
|
|
||||||
memmove (buf->buf + buf->len - len,
|
|
||||||
buf->buf + buf->len - width,
|
|
||||||
len);
|
|
||||||
memset (buf->buf + buf->len - width,
|
|
||||||
' ',
|
|
||||||
width - len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expect_char (c, '}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char escaped_char(const char ch)
|
static char escaped_char(const char ch)
|
||||||
|
|
Loading…
Reference in New Issue