X-Git-Url: https://git.wh0rd.org/?a=blobdiff_plain;f=src%2Ffcformat.c;h=d8518f458252fba1abc6ee4b54111807c750a7c7;hb=HEAD;hp=f91b6e0fb695e18643a6d5ab4b015db32b20ad27;hpb=0673ef3881d24820e627b9a8cd3a4b3e3889c545;p=fontconfig.git diff --git a/src/fcformat.c b/src/fcformat.c index f91b6e0..d8518f4 100644 --- a/src/fcformat.c +++ b/src/fcformat.c @@ -7,15 +7,15 @@ * 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 + * 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. Keith Packard makes no + * 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. * - * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * 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 @@ -28,18 +28,57 @@ #include -/* +/* 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 + * fccat fc-cat default + * pkgkit PackageKit package tag format + * + * * Some ideas for future syntax extensions: * - * - array enumeration using '%{[]family,familylang{expr}|decorator}' - * - langset enumeration using same syntax as array enumeration + * - 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 FCCAT_FORMAT "\"%{file|basename|cescape}\" %{index} \"%{-file{%{=unparse|cescape}}}\"" #define FCMATCH_FORMAT "%{file:-|basename}: \"%{family[0]:-}\" \"%{style[0]:-}\"" -#define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}" +#define FCLIST_FORMAT "%{?file{%{file}: }}%{-file{%{=unparse}}}" +#define PKGKIT_FORMAT "%{[]family{font(%{family|downcase|delete( )})\n}}%{[]lang{font(:lang=%{lang|downcase|translate(_,-)})\n}}" static void @@ -267,8 +306,10 @@ interpret_builtin (FcFormatContext *c, #define BUILTIN(name, format) \ else if (0 == strcmp ((const char *) c->word, name))\ ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf) + BUILTIN ("fccat", FCCAT_FORMAT); BUILTIN ("fcmatch", FCMATCH_FORMAT); BUILTIN ("fclist", FCLIST_FORMAT); + BUILTIN ("pkgkit", PKGKIT_FORMAT); #undef BUILTIN else ret = FcFalse; @@ -383,9 +424,9 @@ maybe_skip_subexpr (FcFormatContext *c) } static FcBool -interpret_filter (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) +interpret_filter_in (FcFormatContext *c, + FcPattern *pat, + FcStrBuf *buf) { FcObjectSet *os; FcPattern *subpat; @@ -399,6 +440,7 @@ interpret_filter (FcFormatContext *c, do { + /* XXX binding */ if (!read_word (c) || !FcObjectSetAdd (os, (const char *) c->word)) { @@ -420,9 +462,9 @@ interpret_filter (FcFormatContext *c, } static FcBool -interpret_delete (FcFormatContext *c, - FcPattern *pat, - FcStrBuf *buf) +interpret_filter_out (FcFormatContext *c, + FcPattern *pat, + FcStrBuf *buf) { FcPattern *subpat; @@ -476,9 +518,8 @@ interpret_cond (FcFormatContext *c, pass = pass && (negate ^ - (FcResultMatch == FcPatternGet (pat, - (const char *) c->word, - 0, &v))); + (FcResultMatch == + FcPatternGet (pat, (const char *) c->word, 0, &v))); } while (consume_char (c, ',')); @@ -532,6 +573,124 @@ interpret_count (FcFormatContext *c, 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, @@ -559,7 +718,8 @@ interpret_simple (FcFormatContext *c, c->format-1 - c->format_orig + 1); return FcFalse; } - expect_char (c, ']'); + if (!expect_char (c, ']')) + return FcFalse; } if (consume_char (c, '=')) @@ -575,7 +735,7 @@ interpret_simple (FcFormatContext *c, 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')) + !read_chars (c, '|')) { c->word = orig; return FcFalse; @@ -586,9 +746,9 @@ interpret_simple (FcFormatContext *c, e = FcPatternObjectFindElt (pat, FcObjectFromName ((const char *) c->word)); - if (e) + if (e || else_string) { - FcValueListPtr l; + FcValueListPtr l = NULL; if (add_colon) FcStrBufChar (buf, ':'); @@ -598,7 +758,8 @@ interpret_simple (FcFormatContext *c, FcStrBufChar (buf, '='); } - l = FcPatternEltValues(e); + if (e) + l = FcPatternEltValues(e); if (idx != -1) { @@ -614,16 +775,16 @@ interpret_simple (FcFormatContext *c, } else goto notfound; } - else + else if (l) { FcNameUnparseValueList (buf, l, '\0'); } - } - else -notfound: - { - if (else_string) - printf ("%s", else_string); + else + { + notfound: + if (else_string) + FcStrBufString (buf, else_string); + } } return FcTrue; @@ -634,6 +795,8 @@ cescape (FcFormatContext *c, const FcChar8 *str, FcStrBuf *buf) { + /* XXX escape \n etc? */ + while(*str) { switch (*str) @@ -671,6 +834,8 @@ xmlescape (FcFormatContext *c, const FcChar8 *str, FcStrBuf *buf) { + /* XXX escape \n etc? */ + while(*str) { switch (*str) @@ -963,13 +1128,14 @@ interpret_percent (FcFormatContext *c, 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 (c, pat, buf); break; - case '-': ret = interpret_delete (c, pat, buf); break; - case '?': ret = interpret_cond (c, pat, buf); break; - case '#': ret = interpret_count (c, pat, buf); break; - default: ret = interpret_simple (c, pat, buf); break; + 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 &&