diff options
Diffstat (limited to 'fontconfig/src/fcformat.c')
-rw-r--r-- | fontconfig/src/fcformat.c | 2428 |
1 files changed, 1214 insertions, 1214 deletions
diff --git a/fontconfig/src/fcformat.c b/fontconfig/src/fcformat.c index df184f704..d8518f458 100644 --- a/fontconfig/src/fcformat.c +++ b/fontconfig/src/fcformat.c @@ -1,1214 +1,1214 @@ -/*
- * 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
- * fccat fc-cat 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 FCCAT_FORMAT "\"%{file|basename|cescape}\" %{index} \"%{-file{%{=unparse|cescape}}}\""
-#define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
-#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
-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 ("fccat", FCCAT_FORMAT);
- 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, '|'))
- {
- 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__
+/* + * 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 + * fccat fc-cat 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 FCCAT_FORMAT "\"%{file|basename|cescape}\" %{index} \"%{-file{%{=unparse|cescape}}}\"" +#define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\"" +#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 +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 ("fccat", FCCAT_FORMAT); + 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, '|')) + { + 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__ |