diff options
Diffstat (limited to 'fontconfig/src/fcformat.c')
-rw-r--r-- | fontconfig/src/fcformat.c | 2414 |
1 files changed, 1211 insertions, 1203 deletions
diff --git a/fontconfig/src/fcformat.c b/fontconfig/src/fcformat.c index bf9401740..28433a705 100644 --- a/fontconfig/src/fcformat.c +++ b/fontconfig/src/fcformat.c @@ -1,1203 +1,1211 @@ -/* - * Copyright © 2008,2009 Red Hat, Inc. - * - * Red Hat Author(s): Behdad Esfahbod - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Keith Packard not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Keith Packard makes no - * representations about the suitability of this software for any purpose. It - * is provided "as is" without express or implied warranty. - * - * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include "fcint.h" -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> - - -/* The language is documented in doc/fcformat.fncs - * These are the features implemented: - * - * simple %{elt} - * width %width{elt} - * index %{elt[idx]} - * name= %{elt=} - * :name= %{:elt} - * default %{elt:-word} - * count %{#elt} - * subexpr %{{expr}} - * filter-out %{-elt1,elt2,elt3{expr}} - * filter-in %{+elt1,elt2,elt3{expr}} - * conditional %{?elt1,elt2,!elt3{}{}} - * enumerate %{[]elt1,elt2{expr}} - * langset langset enumeration using the same syntax - * builtin %{=blt} - * convert %{elt|conv1|conv2|conv3} - * - * converters: - * basename FcStrBasename - * dirname FcStrDirname - * downcase FcStrDowncase - * shescape - * cescape - * xmlescape - * delete delete chars - * escape escape chars - * translate translate chars - * - * builtins: - * unparse FcNameUnparse - * fcmatch fc-match default - * fclist fc-list default - * pkgkit PackageKit package tag format - * - * - * Some ideas for future syntax extensions: - * - * - verbose builtin that is like FcPatternPrint - * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}' - * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation) - */ - - -#define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\"" -#define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}" -#define PKGKIT_FORMAT "%{[]family{font(%{family|downcase|delete( )})\n}}%{[]lang{font(:lang=%{lang|downcase|translate(_,-)})\n}}" - - -static void -message (const char *fmt, ...) -{ - va_list args; - va_start (args, fmt); - fprintf (stderr, "Fontconfig: Pattern format error: "); - vfprintf (stderr, fmt, args); - fprintf (stderr, ".\n"); - va_end (args); -} - - -typedef struct _FcFormatContext -{ - const FcChar8 *format_orig; - const FcChar8 *format; - int format_len; - FcChar8 *word; - FcBool word_allocated; -} FcFormatContext; - -static FcBool -FcFormatContextInit (FcFormatContext *c, - const FcChar8 *format, - FcChar8 *scratch, - int scratch_len) -{ - c->format_orig = c->format = format; - c->format_len = strlen ((const char *) format); - - if (c->format_len < scratch_len) - { - c->word = scratch; - c->word_allocated = FcFalse; - } - else - { - c->word = malloc (c->format_len + 1); - c->word_allocated = FcTrue; - } - - return c->word != NULL; -} - -static void -FcFormatContextDone (FcFormatContext *c) -{ - if (c && c->word_allocated) - { - free (c->word); - } -} - -static FcBool -consume_char (FcFormatContext *c, - FcChar8 term) -{ - if (*c->format != term) - return FcFalse; - - c->format++; - return FcTrue; -} - -static FcBool -expect_char (FcFormatContext *c, - FcChar8 term) -{ - FcBool res = consume_char (c, term); - if (!res) - { - if (c->format == c->format_orig + c->format_len) - message ("format ended while expecting '%c'", - term); - else - message ("expected '%c' at %d", - term, c->format - c->format_orig + 1); - } - return res; -} - -static FcBool -FcCharIsPunct (const FcChar8 c) -{ - if (c < '0') - return FcTrue; - if (c <= '9') - return FcFalse; - if (c < 'A') - return FcTrue; - if (c <= 'Z') - return FcFalse; - if (c < 'a') - return FcTrue; - if (c <= 'z') - return FcFalse; - if (c <= '~') - return FcTrue; - return FcFalse; -} - -static char escaped_char(const char ch) -{ - switch (ch) { - case 'a': return '\a'; - case 'b': return '\b'; - case 'f': return '\f'; - case 'n': return '\n'; - case 'r': return '\r'; - case 't': return '\t'; - case 'v': return '\v'; - default: return ch; - } -} - -static FcBool -read_word (FcFormatContext *c) -{ - FcChar8 *p; - - p = c->word; - - while (*c->format) - { - if (*c->format == '\\') - { - c->format++; - if (*c->format) - *p++ = escaped_char (*c->format++); - continue; - } - else if (FcCharIsPunct (*c->format)) - break; - - *p++ = *c->format++; - } - *p = '\0'; - - if (p == c->word) - { - message ("expected identifier at %d", - c->format - c->format_orig + 1); - return FcFalse; - } - - return FcTrue; -} - -static FcBool -read_chars (FcFormatContext *c, - FcChar8 term) -{ - FcChar8 *p; - - p = c->word; - - while (*c->format && *c->format != '}' && *c->format != term) - { - if (*c->format == '\\') - { - c->format++; - if (*c->format) - *p++ = escaped_char (*c->format++); - continue; - } - - *p++ = *c->format++; - } - *p = '\0'; - - if (p == c->word) - { - message ("expected character data at %d", - c->format - c->format_orig + 1); - return FcFalse; - } - - return FcTrue; -} - -static FcBool -FcPatternFormatToBuf (FcPattern *pat, - const FcChar8 *format, - FcStrBuf *buf); - -static FcBool -interpret_builtin (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - FcChar8 *new_str; - FcBool ret; - - if (!expect_char (c, '=') || - !read_word (c)) - return FcFalse; - - /* try simple builtins first */ - if (0) { } -#define BUILTIN(name, func) \ - else if (0 == strcmp ((const char *) c->word, name))\ - do { new_str = func (pat); ret = FcTrue; } while (0) - BUILTIN ("unparse", FcNameUnparse); - /* BUILTIN ("verbose", FcPatternPrint); XXX */ -#undef BUILTIN - else - ret = FcFalse; - - if (ret) - { - if (new_str) - { - FcStrBufString (buf, new_str); - free (new_str); - return FcTrue; - } - else - return FcFalse; - } - - /* now try our custom formats */ - if (0) { } -#define BUILTIN(name, format) \ - else if (0 == strcmp ((const char *) c->word, name))\ - ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf) - BUILTIN ("fcmatch", FCMATCH_FORMAT); - BUILTIN ("fclist", FCLIST_FORMAT); - BUILTIN ("pkgkit", PKGKIT_FORMAT); -#undef BUILTIN - else - ret = FcFalse; - - if (!ret) - message ("unknown builtin \"%s\"", - c->word); - - return ret; -} - -static FcBool -interpret_expr (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf, - FcChar8 term); - -static FcBool -interpret_subexpr (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - return expect_char (c, '{') && - interpret_expr (c, pat, buf, '}') && - expect_char (c, '}'); -} - -static FcBool -maybe_interpret_subexpr (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - return (*c->format == '{') ? - interpret_subexpr (c, pat, buf) : - FcTrue; -} - -static FcBool -skip_subexpr (FcFormatContext *c); - -static FcBool -skip_percent (FcFormatContext *c) -{ - int width; - - if (!expect_char (c, '%')) - return FcFalse; - - /* skip an optional width specifier */ - width = strtol ((const char *) c->format, (char **) &c->format, 10); - - if (!expect_char (c, '{')) - return FcFalse; - - while(*c->format && *c->format != '}') - { - switch (*c->format) - { - case '\\': - c->format++; /* skip over '\\' */ - if (*c->format) - c->format++; - continue; - case '{': - if (!skip_subexpr (c)) - return FcFalse; - continue; - } - c->format++; - } - - return expect_char (c, '}'); -} - -static FcBool -skip_expr (FcFormatContext *c) -{ - while(*c->format && *c->format != '}') - { - switch (*c->format) - { - case '\\': - c->format++; /* skip over '\\' */ - if (*c->format) - c->format++; - continue; - case '%': - if (!skip_percent (c)) - return FcFalse; - continue; - } - c->format++; - } - - return FcTrue; -} - -static FcBool -skip_subexpr (FcFormatContext *c) -{ - return expect_char (c, '{') && - skip_expr (c) && - expect_char (c, '}'); -} - -static FcBool -maybe_skip_subexpr (FcFormatContext *c) -{ - return (*c->format == '{') ? - skip_subexpr (c) : - FcTrue; -} - -static FcBool -interpret_filter_in (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - FcObjectSet *os; - FcPattern *subpat; - - if (!expect_char (c, '+')) - return FcFalse; - - os = FcObjectSetCreate (); - if (!os) - return FcFalse; - - do - { - if (!read_word (c) || - !FcObjectSetAdd (os, (const char *) c->word)) - { - FcObjectSetDestroy (os); - return FcFalse; - } - } - while (consume_char (c, ',')); - - subpat = FcPatternFilter (pat, os); - FcObjectSetDestroy (os); - - if (!subpat || - !interpret_subexpr (c, subpat, buf)) - return FcFalse; - - FcPatternDestroy (subpat); - return FcTrue; -} - -static FcBool -interpret_filter_out (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - FcPattern *subpat; - - if (!expect_char (c, '-')) - return FcFalse; - - subpat = FcPatternDuplicate (pat); - if (!subpat) - return FcFalse; - - do - { - if (!read_word (c)) - { - FcPatternDestroy (subpat); - return FcFalse; - } - - FcPatternDel (subpat, (const char *) c->word); - } - while (consume_char (c, ',')); - - if (!interpret_subexpr (c, subpat, buf)) - return FcFalse; - - FcPatternDestroy (subpat); - return FcTrue; -} - -static FcBool -interpret_cond (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - FcBool pass; - - if (!expect_char (c, '?')) - return FcFalse; - - pass = FcTrue; - - do - { - FcBool negate; - FcValue v; - - negate = consume_char (c, '!'); - - if (!read_word (c)) - return FcFalse; - - pass = pass && - (negate ^ - (FcResultMatch == - FcPatternGet (pat, (const char *) c->word, 0, &v))); - } - while (consume_char (c, ',')); - - if (pass) - { - if (!interpret_subexpr (c, pat, buf) || - !maybe_skip_subexpr (c)) - return FcFalse; - } - else - { - if (!skip_subexpr (c) || - !maybe_interpret_subexpr (c, pat, buf)) - return FcFalse; - } - - return FcTrue; -} - -static FcBool -interpret_count (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - int count; - FcPatternElt *e; - FcChar8 buf_static[64]; - - if (!expect_char (c, '#')) - return FcFalse; - - if (!read_word (c)) - return FcFalse; - - count = 0; - e = FcPatternObjectFindElt (pat, - FcObjectFromName ((const char *) c->word)); - if (e) - { - FcValueListPtr l; - count++; - for (l = FcPatternEltValues(e); - l->next; - l = l->next) - count++; - } - - snprintf ((char *) buf_static, sizeof (buf_static), "%d", count); - FcStrBufString (buf, buf_static); - - return FcTrue; -} - -static FcBool -interpret_enumerate (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - FcObjectSet *os; - FcPattern *subpat; - const FcChar8 *format_save; - int idx; - FcBool ret, done; - FcStrList *lang_strs; - - if (!expect_char (c, '[') || - !expect_char (c, ']')) - return FcFalse; - - os = FcObjectSetCreate (); - if (!os) - return FcFalse; - - ret = FcTrue; - - do - { - if (!read_word (c) || - !FcObjectSetAdd (os, (const char *) c->word)) - { - FcObjectSetDestroy (os); - return FcFalse; - } - } - while (consume_char (c, ',')); - - /* If we have one element and it's of type FcLangSet, we want - * to enumerate the languages in it. */ - lang_strs = NULL; - if (os->nobject == 1) - { - FcLangSet *langset; - if (FcResultMatch == - FcPatternGetLangSet (pat, os->objects[0], idx, &langset)) - { - FcStrSet *ss; - if (!(ss = FcLangSetGetLangs (langset)) || - !(lang_strs = FcStrListCreate (ss))) - goto bail0; - } - } - - subpat = FcPatternDuplicate (pat); - if (!subpat) - goto bail0; - - format_save = c->format; - idx = 0; - do - { - int i; - - done = FcTrue; - - if (lang_strs) - { - FcChar8 *lang; - - FcPatternDel (subpat, os->objects[0]); - if ((lang = FcStrListNext (lang_strs))) - { - FcPatternAddString (subpat, os->objects[0], lang); - done = FcFalse; - } - } - else - { - for (i = 0; i < os->nobject; i++) - { - FcValue v; - - /* XXX this can be optimized by accessing valuelist linked lists - * directly and remembering where we were. Most (all) value lists - * in normal uses are pretty short though (language tags are - * stored as a LangSet, not separate values.). */ - FcPatternDel (subpat, os->objects[i]); - if (FcResultMatch == - FcPatternGet (pat, os->objects[i], idx, &v)) - { - FcPatternAdd (subpat, os->objects[i], v, FcFalse); - done = FcFalse; - } - } - } - - if (!done) - { - c->format = format_save; - ret = interpret_subexpr (c, subpat, buf); - if (!ret) - goto bail; - } - - idx++; - } while (!done); - - if (c->format == format_save) - skip_subexpr (c); - -bail: - FcPatternDestroy (subpat); -bail0: - if (lang_strs) - FcStrListDone (lang_strs); - FcObjectSetDestroy (os); - - return ret; -} - -static FcBool -interpret_simple (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - FcPatternElt *e; - FcBool add_colon = FcFalse; - FcBool add_elt_name = FcFalse; - int idx; - FcChar8 *else_string; - - if (consume_char (c, ':')) - add_colon = FcTrue; - - if (!read_word (c)) - return FcFalse; - - idx = -1; - if (consume_char (c, '[')) - { - idx = strtol ((const char *) c->format, (char **) &c->format, 10); - if (idx < 0) - { - message ("expected non-negative number at %d", - c->format-1 - c->format_orig + 1); - return FcFalse; - } - if (!expect_char (c, ']')) - return FcFalse; - } - - if (consume_char (c, '=')) - add_elt_name = FcTrue; - - /* modifiers */ - else_string = NULL; - if (consume_char (c, ':')) - { - FcChar8 *orig; - /* divert the c->word for now */ - orig = c->word; - c->word = c->word + strlen ((const char *) c->word) + 1; - /* for now we just support 'default value' */ - if (!expect_char (c, '-') || - !read_chars (c, '\0')) - { - c->word = orig; - return FcFalse; - } - else_string = c->word; - c->word = orig; - } - - e = FcPatternObjectFindElt (pat, - FcObjectFromName ((const char *) c->word)); - if (e || else_string) - { - FcValueListPtr l = NULL; - - if (add_colon) - FcStrBufChar (buf, ':'); - if (add_elt_name) - { - FcStrBufString (buf, c->word); - FcStrBufChar (buf, '='); - } - - if (e) - l = FcPatternEltValues(e); - - if (idx != -1) - { - while (l && idx > 0) - { - l = FcValueListNext(l); - idx--; - } - if (l && idx == 0) - { - if (!FcNameUnparseValue (buf, &l->value, '\0')) - return FcFalse; - } - else goto notfound; - } - else if (l) - { - FcNameUnparseValueList (buf, l, '\0'); - } - else - { - notfound: - if (else_string) - FcStrBufString (buf, else_string); - } - } - - return FcTrue; -} - -static FcBool -cescape (FcFormatContext *c, - const FcChar8 *str, - FcStrBuf *buf) -{ - while(*str) - { - switch (*str) - { - case '\\': - case '"': - FcStrBufChar (buf, '\\'); - break; - } - FcStrBufChar (buf, *str++); - } - return FcTrue; -} - -static FcBool -shescape (FcFormatContext *c, - const FcChar8 *str, - FcStrBuf *buf) -{ - FcStrBufChar (buf, '\''); - while(*str) - { - if (*str == '\'') - FcStrBufString (buf, (const FcChar8 *) "'\\''"); - else - FcStrBufChar (buf, *str); - str++; - } - FcStrBufChar (buf, '\''); - return FcTrue; -} - -static FcBool -xmlescape (FcFormatContext *c, - const FcChar8 *str, - FcStrBuf *buf) -{ - while(*str) - { - switch (*str) - { - case '&': FcStrBufString (buf, (const FcChar8 *) "&"); break; - case '<': FcStrBufString (buf, (const FcChar8 *) "<"); break; - case '>': FcStrBufString (buf, (const FcChar8 *) ">"); break; - default: FcStrBufChar (buf, *str); break; - } - str++; - } - return FcTrue; -} - -static FcBool -delete_chars (FcFormatContext *c, - const FcChar8 *str, - FcStrBuf *buf) -{ - /* XXX not UTF-8 aware */ - - if (!expect_char (c, '(') || - !read_chars (c, ')') || - !expect_char (c, ')')) - return FcFalse; - - while(*str) - { - FcChar8 *p; - - p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word); - if (p) - { - FcStrBufData (buf, str, p - str); - str = p + 1; - } - else - { - FcStrBufString (buf, str); - break; - } - - } - - return FcTrue; -} - -static FcBool -escape_chars (FcFormatContext *c, - const FcChar8 *str, - FcStrBuf *buf) -{ - /* XXX not UTF-8 aware */ - - if (!expect_char (c, '(') || - !read_chars (c, ')') || - !expect_char (c, ')')) - return FcFalse; - - while(*str) - { - FcChar8 *p; - - p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word); - if (p) - { - FcStrBufData (buf, str, p - str); - FcStrBufChar (buf, c->word[0]); - FcStrBufChar (buf, *p); - str = p + 1; - } - else - { - FcStrBufString (buf, str); - break; - } - - } - - return FcTrue; -} - -static FcBool -translate_chars (FcFormatContext *c, - const FcChar8 *str, - FcStrBuf *buf) -{ - char *from, *to, repeat; - int from_len, to_len; - - /* XXX not UTF-8 aware */ - - if (!expect_char (c, '(') || - !read_chars (c, ',') || - !expect_char (c, ',')) - return FcFalse; - - from = (char *) c->word; - from_len = strlen (from); - to = from + from_len + 1; - - /* hack: we temporarily divert c->word */ - c->word = (FcChar8 *) to; - if (!read_chars (c, ')')) - { - c->word = (FcChar8 *) from; - return FcFalse; - } - c->word = (FcChar8 *) from; - - to_len = strlen (to); - repeat = to[to_len - 1]; - - if (!expect_char (c, ')')) - return FcFalse; - - while(*str) - { - FcChar8 *p; - - p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from); - if (p) - { - int i; - FcStrBufData (buf, str, p - str); - i = strchr (from, *p) - from; - FcStrBufChar (buf, i < to_len ? to[i] : repeat); - str = p + 1; - } - else - { - FcStrBufString (buf, str); - break; - } - - } - - return FcTrue; -} - -static FcBool -interpret_convert (FcFormatContext *c, - FcStrBuf *buf, - int start) -{ - const FcChar8 *str; - FcChar8 *new_str; - FcStrBuf new_buf; - FcChar8 buf_static[8192]; - FcBool ret; - - if (!expect_char (c, '|') || - !read_word (c)) - return FcFalse; - - /* prepare the buffer */ - FcStrBufChar (buf, '\0'); - if (buf->failed) - return FcFalse; - str = buf->buf + start; - buf->len = start; - - /* try simple converters first */ - if (0) { } -#define CONVERTER(name, func) \ - else if (0 == strcmp ((const char *) c->word, name))\ - do { new_str = func (str); ret = FcTrue; } while (0) - CONVERTER ("downcase", FcStrDowncase); - CONVERTER ("basename", FcStrBasename); - CONVERTER ("dirname", FcStrDirname); -#undef CONVERTER - else - ret = FcFalse; - - if (ret) - { - if (new_str) - { - FcStrBufString (buf, new_str); - free (new_str); - return FcTrue; - } - else - return FcFalse; - } - - FcStrBufInit (&new_buf, buf_static, sizeof (buf_static)); - - /* now try our custom converters */ - if (0) { } -#define CONVERTER(name, func) \ - else if (0 == strcmp ((const char *) c->word, name))\ - ret = func (c, str, &new_buf) - CONVERTER ("cescape", cescape); - CONVERTER ("shescape", shescape); - CONVERTER ("xmlescape", xmlescape); - CONVERTER ("delete", delete_chars); - CONVERTER ("escape", escape_chars); - CONVERTER ("translate", translate_chars); -#undef CONVERTER - else - ret = FcFalse; - - if (ret) - { - FcStrBufChar (&new_buf, '\0'); - FcStrBufString (buf, new_buf.buf); - } - else - message ("unknown converter \"%s\"", - c->word); - - FcStrBufDestroy (&new_buf); - - return ret; -} - -static FcBool -maybe_interpret_converts (FcFormatContext *c, - FcStrBuf *buf, - int start) -{ - while (*c->format == '|') - if (!interpret_convert (c, buf, start)) - return FcFalse; - - 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 -interpret_percent (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) -{ - int width, start; - FcBool ret; - - if (!expect_char (c, '%')) - return FcFalse; - - if (consume_char (c, '%')) /* "%%" */ - { - FcStrBufChar (buf, '%'); - return FcTrue; - } - - /* parse an optional width specifier */ - width = strtol ((const char *) c->format, (char **) &c->format, 10); - - if (!expect_char (c, '{')) - return FcFalse; - - start = buf->len; - - switch (*c->format) { - case '=': ret = interpret_builtin (c, pat, buf); break; - case '{': ret = interpret_subexpr (c, pat, buf); break; - case '+': ret = interpret_filter_in (c, pat, buf); break; - case '-': ret = interpret_filter_out (c, pat, buf); break; - case '?': ret = interpret_cond (c, pat, buf); break; - case '#': ret = interpret_count (c, pat, buf); break; - case '[': ret = interpret_enumerate (c, pat, buf); break; - default: ret = interpret_simple (c, pat, buf); break; - } - - return ret && - maybe_interpret_converts (c, buf, start) && - align_to_width (buf, start, width) && - expect_char (c, '}'); -} - -static FcBool -interpret_expr (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf, - FcChar8 term) -{ - while (*c->format && *c->format != term) - { - switch (*c->format) - { - case '\\': - c->format++; /* skip over '\\' */ - if (*c->format) - FcStrBufChar (buf, escaped_char (*c->format++)); - continue; - case '%': - if (!interpret_percent (c, pat, buf)) - return FcFalse; - continue; - } - FcStrBufChar (buf, *c->format++); - } - return FcTrue; -} - -static FcBool -FcPatternFormatToBuf (FcPattern *pat, - const FcChar8 *format, - FcStrBuf *buf) -{ - FcFormatContext c; - FcChar8 word_static[1024]; - FcBool ret; - - if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static))) - return FcFalse; - - ret = interpret_expr (&c, pat, buf, '\0'); - - FcFormatContextDone (&c); - - return ret; -} - -FcChar8 * -FcPatternFormat (FcPattern *pat, - const FcChar8 *format) -{ - FcStrBuf buf; - FcChar8 buf_static[8192 - 1024]; - FcBool ret; - - FcStrBufInit (&buf, buf_static, sizeof (buf_static)); - - ret = FcPatternFormatToBuf (pat, format, &buf); - - if (ret) - return FcStrBufDone (&buf); - else - { - FcStrBufDestroy (&buf); - return NULL; - } -} - -#define __fcformat__ -#include "fcaliastail.h" -#undef __fcformat__ +/*
+ * Copyright © 2008,2009 Red Hat, Inc.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the author(s) not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. The authors make no
+ * representations about the suitability of this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ *
+ * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "fcint.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+
+/* The language is documented in doc/fcformat.fncs
+ * These are the features implemented:
+ *
+ * simple %{elt}
+ * width %width{elt}
+ * index %{elt[idx]}
+ * name= %{elt=}
+ * :name= %{:elt}
+ * default %{elt:-word}
+ * count %{#elt}
+ * subexpr %{{expr}}
+ * filter-out %{-elt1,elt2,elt3{expr}}
+ * filter-in %{+elt1,elt2,elt3{expr}}
+ * conditional %{?elt1,elt2,!elt3{}{}}
+ * enumerate %{[]elt1,elt2{expr}}
+ * langset langset enumeration using the same syntax
+ * builtin %{=blt}
+ * convert %{elt|conv1|conv2|conv3}
+ *
+ * converters:
+ * basename FcStrBasename
+ * dirname FcStrDirname
+ * downcase FcStrDowncase
+ * shescape
+ * cescape
+ * xmlescape
+ * delete delete chars
+ * escape escape chars
+ * translate translate chars
+ *
+ * builtins:
+ * unparse FcNameUnparse
+ * fcmatch fc-match default
+ * fclist fc-list default
+ * pkgkit PackageKit package tag format
+ *
+ *
+ * Some ideas for future syntax extensions:
+ *
+ * - verbose builtin that is like FcPatternPrint
+ * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
+ * - allow indexing in +, -, ? filtering?
+ * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
+ */
+
+
+#define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
+#define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
+#define PKGKIT_FORMAT "%{[]family{font(%{family|downcase|delete( )})\n}}%{[]lang{font(:lang=%{lang|downcase|translate(_,-)})\n}}"
+
+
+static void
+message (const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ fprintf (stderr, "Fontconfig: Pattern format error: ");
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, ".\n");
+ va_end (args);
+}
+
+
+typedef struct _FcFormatContext
+{
+ const FcChar8 *format_orig;
+ const FcChar8 *format;
+ int format_len;
+ FcChar8 *word;
+ FcBool word_allocated;
+} FcFormatContext;
+
+static FcBool
+FcFormatContextInit (FcFormatContext *c,
+ const FcChar8 *format,
+ FcChar8 *scratch,
+ int scratch_len)
+{
+ c->format_orig = c->format = format;
+ c->format_len = strlen ((const char *) format);
+
+ if (c->format_len < scratch_len)
+ {
+ c->word = scratch;
+ c->word_allocated = FcFalse;
+ }
+ else
+ {
+ c->word = malloc (c->format_len + 1);
+ c->word_allocated = FcTrue;
+ }
+
+ return c->word != NULL;
+}
+
+static void
+FcFormatContextDone (FcFormatContext *c)
+{
+ if (c && c->word_allocated)
+ {
+ free (c->word);
+ }
+}
+
+static FcBool
+consume_char (FcFormatContext *c,
+ FcChar8 term)
+{
+ if (*c->format != term)
+ return FcFalse;
+
+ c->format++;
+ return FcTrue;
+}
+
+static FcBool
+expect_char (FcFormatContext *c,
+ FcChar8 term)
+{
+ FcBool res = consume_char (c, term);
+ if (!res)
+ {
+ if (c->format == c->format_orig + c->format_len)
+ message ("format ended while expecting '%c'",
+ term);
+ else
+ message ("expected '%c' at %d",
+ term, c->format - c->format_orig + 1);
+ }
+ return res;
+}
+
+static FcBool
+FcCharIsPunct (const FcChar8 c)
+{
+ if (c < '0')
+ return FcTrue;
+ if (c <= '9')
+ return FcFalse;
+ if (c < 'A')
+ return FcTrue;
+ if (c <= 'Z')
+ return FcFalse;
+ if (c < 'a')
+ return FcTrue;
+ if (c <= 'z')
+ return FcFalse;
+ if (c <= '~')
+ return FcTrue;
+ return FcFalse;
+}
+
+static char escaped_char(const char ch)
+{
+ switch (ch) {
+ case 'a': return '\a';
+ case 'b': return '\b';
+ case 'f': return '\f';
+ case 'n': return '\n';
+ case 'r': return '\r';
+ case 't': return '\t';
+ case 'v': return '\v';
+ default: return ch;
+ }
+}
+
+static FcBool
+read_word (FcFormatContext *c)
+{
+ FcChar8 *p;
+
+ p = c->word;
+
+ while (*c->format)
+ {
+ if (*c->format == '\\')
+ {
+ c->format++;
+ if (*c->format)
+ *p++ = escaped_char (*c->format++);
+ continue;
+ }
+ else if (FcCharIsPunct (*c->format))
+ break;
+
+ *p++ = *c->format++;
+ }
+ *p = '\0';
+
+ if (p == c->word)
+ {
+ message ("expected identifier at %d",
+ c->format - c->format_orig + 1);
+ return FcFalse;
+ }
+
+ return FcTrue;
+}
+
+static FcBool
+read_chars (FcFormatContext *c,
+ FcChar8 term)
+{
+ FcChar8 *p;
+
+ p = c->word;
+
+ while (*c->format && *c->format != '}' && *c->format != term)
+ {
+ if (*c->format == '\\')
+ {
+ c->format++;
+ if (*c->format)
+ *p++ = escaped_char (*c->format++);
+ continue;
+ }
+
+ *p++ = *c->format++;
+ }
+ *p = '\0';
+
+ if (p == c->word)
+ {
+ message ("expected character data at %d",
+ c->format - c->format_orig + 1);
+ return FcFalse;
+ }
+
+ return FcTrue;
+}
+
+static FcBool
+FcPatternFormatToBuf (FcPattern *pat,
+ const FcChar8 *format,
+ FcStrBuf *buf);
+
+static FcBool
+interpret_builtin (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ FcChar8 *new_str;
+ FcBool ret;
+
+ if (!expect_char (c, '=') ||
+ !read_word (c))
+ return FcFalse;
+
+ /* try simple builtins first */
+ if (0) { }
+#define BUILTIN(name, func) \
+ else if (0 == strcmp ((const char *) c->word, name))\
+ do { new_str = func (pat); ret = FcTrue; } while (0)
+ BUILTIN ("unparse", FcNameUnparse);
+ /* BUILTIN ("verbose", FcPatternPrint); XXX */
+#undef BUILTIN
+ else
+ ret = FcFalse;
+
+ if (ret)
+ {
+ if (new_str)
+ {
+ FcStrBufString (buf, new_str);
+ free (new_str);
+ return FcTrue;
+ }
+ else
+ return FcFalse;
+ }
+
+ /* now try our custom formats */
+ if (0) { }
+#define BUILTIN(name, format) \
+ else if (0 == strcmp ((const char *) c->word, name))\
+ ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
+ BUILTIN ("fcmatch", FCMATCH_FORMAT);
+ BUILTIN ("fclist", FCLIST_FORMAT);
+ BUILTIN ("pkgkit", PKGKIT_FORMAT);
+#undef BUILTIN
+ else
+ ret = FcFalse;
+
+ if (!ret)
+ message ("unknown builtin \"%s\"",
+ c->word);
+
+ return ret;
+}
+
+static FcBool
+interpret_expr (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf,
+ FcChar8 term);
+
+static FcBool
+interpret_subexpr (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ return expect_char (c, '{') &&
+ interpret_expr (c, pat, buf, '}') &&
+ expect_char (c, '}');
+}
+
+static FcBool
+maybe_interpret_subexpr (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ return (*c->format == '{') ?
+ interpret_subexpr (c, pat, buf) :
+ FcTrue;
+}
+
+static FcBool
+skip_subexpr (FcFormatContext *c);
+
+static FcBool
+skip_percent (FcFormatContext *c)
+{
+ int width;
+
+ if (!expect_char (c, '%'))
+ return FcFalse;
+
+ /* skip an optional width specifier */
+ width = strtol ((const char *) c->format, (char **) &c->format, 10);
+
+ if (!expect_char (c, '{'))
+ return FcFalse;
+
+ while(*c->format && *c->format != '}')
+ {
+ switch (*c->format)
+ {
+ case '\\':
+ c->format++; /* skip over '\\' */
+ if (*c->format)
+ c->format++;
+ continue;
+ case '{':
+ if (!skip_subexpr (c))
+ return FcFalse;
+ continue;
+ }
+ c->format++;
+ }
+
+ return expect_char (c, '}');
+}
+
+static FcBool
+skip_expr (FcFormatContext *c)
+{
+ while(*c->format && *c->format != '}')
+ {
+ switch (*c->format)
+ {
+ case '\\':
+ c->format++; /* skip over '\\' */
+ if (*c->format)
+ c->format++;
+ continue;
+ case '%':
+ if (!skip_percent (c))
+ return FcFalse;
+ continue;
+ }
+ c->format++;
+ }
+
+ return FcTrue;
+}
+
+static FcBool
+skip_subexpr (FcFormatContext *c)
+{
+ return expect_char (c, '{') &&
+ skip_expr (c) &&
+ expect_char (c, '}');
+}
+
+static FcBool
+maybe_skip_subexpr (FcFormatContext *c)
+{
+ return (*c->format == '{') ?
+ skip_subexpr (c) :
+ FcTrue;
+}
+
+static FcBool
+interpret_filter_in (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ FcObjectSet *os;
+ FcPattern *subpat;
+
+ if (!expect_char (c, '+'))
+ return FcFalse;
+
+ os = FcObjectSetCreate ();
+ if (!os)
+ return FcFalse;
+
+ do
+ {
+ /* XXX binding */
+ if (!read_word (c) ||
+ !FcObjectSetAdd (os, (const char *) c->word))
+ {
+ FcObjectSetDestroy (os);
+ return FcFalse;
+ }
+ }
+ while (consume_char (c, ','));
+
+ subpat = FcPatternFilter (pat, os);
+ FcObjectSetDestroy (os);
+
+ if (!subpat ||
+ !interpret_subexpr (c, subpat, buf))
+ return FcFalse;
+
+ FcPatternDestroy (subpat);
+ return FcTrue;
+}
+
+static FcBool
+interpret_filter_out (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ FcPattern *subpat;
+
+ if (!expect_char (c, '-'))
+ return FcFalse;
+
+ subpat = FcPatternDuplicate (pat);
+ if (!subpat)
+ return FcFalse;
+
+ do
+ {
+ if (!read_word (c))
+ {
+ FcPatternDestroy (subpat);
+ return FcFalse;
+ }
+
+ FcPatternDel (subpat, (const char *) c->word);
+ }
+ while (consume_char (c, ','));
+
+ if (!interpret_subexpr (c, subpat, buf))
+ return FcFalse;
+
+ FcPatternDestroy (subpat);
+ return FcTrue;
+}
+
+static FcBool
+interpret_cond (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ FcBool pass;
+
+ if (!expect_char (c, '?'))
+ return FcFalse;
+
+ pass = FcTrue;
+
+ do
+ {
+ FcBool negate;
+ FcValue v;
+
+ negate = consume_char (c, '!');
+
+ if (!read_word (c))
+ return FcFalse;
+
+ pass = pass &&
+ (negate ^
+ (FcResultMatch ==
+ FcPatternGet (pat, (const char *) c->word, 0, &v)));
+ }
+ while (consume_char (c, ','));
+
+ if (pass)
+ {
+ if (!interpret_subexpr (c, pat, buf) ||
+ !maybe_skip_subexpr (c))
+ return FcFalse;
+ }
+ else
+ {
+ if (!skip_subexpr (c) ||
+ !maybe_interpret_subexpr (c, pat, buf))
+ return FcFalse;
+ }
+
+ return FcTrue;
+}
+
+static FcBool
+interpret_count (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ int count;
+ FcPatternElt *e;
+ FcChar8 buf_static[64];
+
+ if (!expect_char (c, '#'))
+ return FcFalse;
+
+ if (!read_word (c))
+ return FcFalse;
+
+ count = 0;
+ e = FcPatternObjectFindElt (pat,
+ FcObjectFromName ((const char *) c->word));
+ if (e)
+ {
+ FcValueListPtr l;
+ count++;
+ for (l = FcPatternEltValues(e);
+ l->next;
+ l = l->next)
+ count++;
+ }
+
+ snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
+ FcStrBufString (buf, buf_static);
+
+ return FcTrue;
+}
+
+static FcBool
+interpret_enumerate (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ FcObjectSet *os;
+ FcPattern *subpat;
+ const FcChar8 *format_save;
+ int idx;
+ FcBool ret, done;
+ FcStrList *lang_strs;
+
+ if (!expect_char (c, '[') ||
+ !expect_char (c, ']'))
+ return FcFalse;
+
+ os = FcObjectSetCreate ();
+ if (!os)
+ return FcFalse;
+
+ ret = FcTrue;
+
+ do
+ {
+ if (!read_word (c) ||
+ !FcObjectSetAdd (os, (const char *) c->word))
+ {
+ FcObjectSetDestroy (os);
+ return FcFalse;
+ }
+ }
+ while (consume_char (c, ','));
+
+ /* If we have one element and it's of type FcLangSet, we want
+ * to enumerate the languages in it. */
+ lang_strs = NULL;
+ if (os->nobject == 1)
+ {
+ FcLangSet *langset;
+ if (FcResultMatch ==
+ FcPatternGetLangSet (pat, os->objects[0], 0, &langset))
+ {
+ FcStrSet *ss;
+ if (!(ss = FcLangSetGetLangs (langset)) ||
+ !(lang_strs = FcStrListCreate (ss)))
+ goto bail0;
+ }
+ }
+
+ subpat = FcPatternDuplicate (pat);
+ if (!subpat)
+ goto bail0;
+
+ format_save = c->format;
+ idx = 0;
+ do
+ {
+ int i;
+
+ done = FcTrue;
+
+ if (lang_strs)
+ {
+ FcChar8 *lang;
+
+ FcPatternDel (subpat, os->objects[0]);
+ if ((lang = FcStrListNext (lang_strs)))
+ {
+ /* XXX binding? */
+ FcPatternAddString (subpat, os->objects[0], lang);
+ done = FcFalse;
+ }
+ }
+ else
+ {
+ for (i = 0; i < os->nobject; i++)
+ {
+ FcValue v;
+
+ /* XXX this can be optimized by accessing valuelist linked lists
+ * directly and remembering where we were. Most (all) value lists
+ * in normal uses are pretty short though (language tags are
+ * stored as a LangSet, not separate values.). */
+ FcPatternDel (subpat, os->objects[i]);
+ if (FcResultMatch ==
+ FcPatternGet (pat, os->objects[i], idx, &v))
+ {
+ /* XXX binding */
+ FcPatternAdd (subpat, os->objects[i], v, FcFalse);
+ done = FcFalse;
+ }
+ }
+ }
+
+ if (!done)
+ {
+ c->format = format_save;
+ ret = interpret_subexpr (c, subpat, buf);
+ if (!ret)
+ goto bail;
+ }
+
+ idx++;
+ } while (!done);
+
+ if (c->format == format_save)
+ skip_subexpr (c);
+
+bail:
+ FcPatternDestroy (subpat);
+bail0:
+ if (lang_strs)
+ FcStrListDone (lang_strs);
+ FcObjectSetDestroy (os);
+
+ return ret;
+}
+
+static FcBool
+interpret_simple (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ FcPatternElt *e;
+ FcBool add_colon = FcFalse;
+ FcBool add_elt_name = FcFalse;
+ int idx;
+ FcChar8 *else_string;
+
+ if (consume_char (c, ':'))
+ add_colon = FcTrue;
+
+ if (!read_word (c))
+ return FcFalse;
+
+ idx = -1;
+ if (consume_char (c, '['))
+ {
+ idx = strtol ((const char *) c->format, (char **) &c->format, 10);
+ if (idx < 0)
+ {
+ message ("expected non-negative number at %d",
+ c->format-1 - c->format_orig + 1);
+ return FcFalse;
+ }
+ if (!expect_char (c, ']'))
+ return FcFalse;
+ }
+
+ if (consume_char (c, '='))
+ add_elt_name = FcTrue;
+
+ /* modifiers */
+ else_string = NULL;
+ if (consume_char (c, ':'))
+ {
+ FcChar8 *orig;
+ /* divert the c->word for now */
+ orig = c->word;
+ c->word = c->word + strlen ((const char *) c->word) + 1;
+ /* for now we just support 'default value' */
+ if (!expect_char (c, '-') ||
+ !read_chars (c, '\0'))
+ {
+ c->word = orig;
+ return FcFalse;
+ }
+ else_string = c->word;
+ c->word = orig;
+ }
+
+ e = FcPatternObjectFindElt (pat,
+ FcObjectFromName ((const char *) c->word));
+ if (e || else_string)
+ {
+ FcValueListPtr l = NULL;
+
+ if (add_colon)
+ FcStrBufChar (buf, ':');
+ if (add_elt_name)
+ {
+ FcStrBufString (buf, c->word);
+ FcStrBufChar (buf, '=');
+ }
+
+ if (e)
+ l = FcPatternEltValues(e);
+
+ if (idx != -1)
+ {
+ while (l && idx > 0)
+ {
+ l = FcValueListNext(l);
+ idx--;
+ }
+ if (l && idx == 0)
+ {
+ if (!FcNameUnparseValue (buf, &l->value, '\0'))
+ return FcFalse;
+ }
+ else goto notfound;
+ }
+ else if (l)
+ {
+ FcNameUnparseValueList (buf, l, '\0');
+ }
+ else
+ {
+ notfound:
+ if (else_string)
+ FcStrBufString (buf, else_string);
+ }
+ }
+
+ return FcTrue;
+}
+
+static FcBool
+cescape (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
+{
+ /* XXX escape \n etc? */
+
+ while(*str)
+ {
+ switch (*str)
+ {
+ case '\\':
+ case '"':
+ FcStrBufChar (buf, '\\');
+ break;
+ }
+ FcStrBufChar (buf, *str++);
+ }
+ return FcTrue;
+}
+
+static FcBool
+shescape (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
+{
+ FcStrBufChar (buf, '\'');
+ while(*str)
+ {
+ if (*str == '\'')
+ FcStrBufString (buf, (const FcChar8 *) "'\\''");
+ else
+ FcStrBufChar (buf, *str);
+ str++;
+ }
+ FcStrBufChar (buf, '\'');
+ return FcTrue;
+}
+
+static FcBool
+xmlescape (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
+{
+ /* XXX escape \n etc? */
+
+ while(*str)
+ {
+ switch (*str)
+ {
+ case '&': FcStrBufString (buf, (const FcChar8 *) "&"); break;
+ case '<': FcStrBufString (buf, (const FcChar8 *) "<"); break;
+ case '>': FcStrBufString (buf, (const FcChar8 *) ">"); break;
+ default: FcStrBufChar (buf, *str); break;
+ }
+ str++;
+ }
+ return FcTrue;
+}
+
+static FcBool
+delete_chars (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
+{
+ /* XXX not UTF-8 aware */
+
+ if (!expect_char (c, '(') ||
+ !read_chars (c, ')') ||
+ !expect_char (c, ')'))
+ return FcFalse;
+
+ while(*str)
+ {
+ FcChar8 *p;
+
+ p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
+ if (p)
+ {
+ FcStrBufData (buf, str, p - str);
+ str = p + 1;
+ }
+ else
+ {
+ FcStrBufString (buf, str);
+ break;
+ }
+
+ }
+
+ return FcTrue;
+}
+
+static FcBool
+escape_chars (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
+{
+ /* XXX not UTF-8 aware */
+
+ if (!expect_char (c, '(') ||
+ !read_chars (c, ')') ||
+ !expect_char (c, ')'))
+ return FcFalse;
+
+ while(*str)
+ {
+ FcChar8 *p;
+
+ p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
+ if (p)
+ {
+ FcStrBufData (buf, str, p - str);
+ FcStrBufChar (buf, c->word[0]);
+ FcStrBufChar (buf, *p);
+ str = p + 1;
+ }
+ else
+ {
+ FcStrBufString (buf, str);
+ break;
+ }
+
+ }
+
+ return FcTrue;
+}
+
+static FcBool
+translate_chars (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
+{
+ char *from, *to, repeat;
+ int from_len, to_len;
+
+ /* XXX not UTF-8 aware */
+
+ if (!expect_char (c, '(') ||
+ !read_chars (c, ',') ||
+ !expect_char (c, ','))
+ return FcFalse;
+
+ from = (char *) c->word;
+ from_len = strlen (from);
+ to = from + from_len + 1;
+
+ /* hack: we temporarily divert c->word */
+ c->word = (FcChar8 *) to;
+ if (!read_chars (c, ')'))
+ {
+ c->word = (FcChar8 *) from;
+ return FcFalse;
+ }
+ c->word = (FcChar8 *) from;
+
+ to_len = strlen (to);
+ repeat = to[to_len - 1];
+
+ if (!expect_char (c, ')'))
+ return FcFalse;
+
+ while(*str)
+ {
+ FcChar8 *p;
+
+ p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
+ if (p)
+ {
+ int i;
+ FcStrBufData (buf, str, p - str);
+ i = strchr (from, *p) - from;
+ FcStrBufChar (buf, i < to_len ? to[i] : repeat);
+ str = p + 1;
+ }
+ else
+ {
+ FcStrBufString (buf, str);
+ break;
+ }
+
+ }
+
+ return FcTrue;
+}
+
+static FcBool
+interpret_convert (FcFormatContext *c,
+ FcStrBuf *buf,
+ int start)
+{
+ const FcChar8 *str;
+ FcChar8 *new_str;
+ FcStrBuf new_buf;
+ FcChar8 buf_static[8192];
+ FcBool ret;
+
+ if (!expect_char (c, '|') ||
+ !read_word (c))
+ return FcFalse;
+
+ /* prepare the buffer */
+ FcStrBufChar (buf, '\0');
+ if (buf->failed)
+ return FcFalse;
+ str = buf->buf + start;
+ buf->len = start;
+
+ /* try simple converters first */
+ if (0) { }
+#define CONVERTER(name, func) \
+ else if (0 == strcmp ((const char *) c->word, name))\
+ do { new_str = func (str); ret = FcTrue; } while (0)
+ CONVERTER ("downcase", FcStrDowncase);
+ CONVERTER ("basename", FcStrBasename);
+ CONVERTER ("dirname", FcStrDirname);
+#undef CONVERTER
+ else
+ ret = FcFalse;
+
+ if (ret)
+ {
+ if (new_str)
+ {
+ FcStrBufString (buf, new_str);
+ free (new_str);
+ return FcTrue;
+ }
+ else
+ return FcFalse;
+ }
+
+ FcStrBufInit (&new_buf, buf_static, sizeof (buf_static));
+
+ /* now try our custom converters */
+ if (0) { }
+#define CONVERTER(name, func) \
+ else if (0 == strcmp ((const char *) c->word, name))\
+ ret = func (c, str, &new_buf)
+ CONVERTER ("cescape", cescape);
+ CONVERTER ("shescape", shescape);
+ CONVERTER ("xmlescape", xmlescape);
+ CONVERTER ("delete", delete_chars);
+ CONVERTER ("escape", escape_chars);
+ CONVERTER ("translate", translate_chars);
+#undef CONVERTER
+ else
+ ret = FcFalse;
+
+ if (ret)
+ {
+ FcStrBufChar (&new_buf, '\0');
+ FcStrBufString (buf, new_buf.buf);
+ }
+ else
+ message ("unknown converter \"%s\"",
+ c->word);
+
+ FcStrBufDestroy (&new_buf);
+
+ return ret;
+}
+
+static FcBool
+maybe_interpret_converts (FcFormatContext *c,
+ FcStrBuf *buf,
+ int start)
+{
+ while (*c->format == '|')
+ if (!interpret_convert (c, buf, start))
+ return FcFalse;
+
+ 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
+interpret_percent (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ int width, start;
+ FcBool ret;
+
+ if (!expect_char (c, '%'))
+ return FcFalse;
+
+ if (consume_char (c, '%')) /* "%%" */
+ {
+ FcStrBufChar (buf, '%');
+ return FcTrue;
+ }
+
+ /* parse an optional width specifier */
+ width = strtol ((const char *) c->format, (char **) &c->format, 10);
+
+ if (!expect_char (c, '{'))
+ return FcFalse;
+
+ start = buf->len;
+
+ switch (*c->format) {
+ case '=': ret = interpret_builtin (c, pat, buf); break;
+ case '{': ret = interpret_subexpr (c, pat, buf); break;
+ case '+': ret = interpret_filter_in (c, pat, buf); break;
+ case '-': ret = interpret_filter_out (c, pat, buf); break;
+ case '?': ret = interpret_cond (c, pat, buf); break;
+ case '#': ret = interpret_count (c, pat, buf); break;
+ case '[': ret = interpret_enumerate (c, pat, buf); break;
+ default: ret = interpret_simple (c, pat, buf); break;
+ }
+
+ return ret &&
+ maybe_interpret_converts (c, buf, start) &&
+ align_to_width (buf, start, width) &&
+ expect_char (c, '}');
+}
+
+static FcBool
+interpret_expr (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf,
+ FcChar8 term)
+{
+ while (*c->format && *c->format != term)
+ {
+ switch (*c->format)
+ {
+ case '\\':
+ c->format++; /* skip over '\\' */
+ if (*c->format)
+ FcStrBufChar (buf, escaped_char (*c->format++));
+ continue;
+ case '%':
+ if (!interpret_percent (c, pat, buf))
+ return FcFalse;
+ continue;
+ }
+ FcStrBufChar (buf, *c->format++);
+ }
+ return FcTrue;
+}
+
+static FcBool
+FcPatternFormatToBuf (FcPattern *pat,
+ const FcChar8 *format,
+ FcStrBuf *buf)
+{
+ FcFormatContext c;
+ FcChar8 word_static[1024];
+ FcBool ret;
+
+ if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static)))
+ return FcFalse;
+
+ ret = interpret_expr (&c, pat, buf, '\0');
+
+ FcFormatContextDone (&c);
+
+ return ret;
+}
+
+FcChar8 *
+FcPatternFormat (FcPattern *pat,
+ const FcChar8 *format)
+{
+ FcStrBuf buf;
+ FcChar8 buf_static[8192 - 1024];
+ FcBool ret;
+
+ FcStrBufInit (&buf, buf_static, sizeof (buf_static));
+
+ ret = FcPatternFormatToBuf (pat, format, &buf);
+
+ if (ret)
+ return FcStrBufDone (&buf);
+ else
+ {
+ FcStrBufDestroy (&buf);
+ return NULL;
+ }
+}
+
+#define __fcformat__
+#include "fcaliastail.h"
+#undef __fcformat__
|