* 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
#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 simple tags using '%{elt[idx]}'
+ * - 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, ...)
{
const FcChar8 *format;
int format_len;
FcChar8 *word;
+ FcBool word_allocated;
} FcFormatContext;
static FcBool
FcFormatContextInit (FcFormatContext *c,
- const FcChar8 *format)
+ const FcChar8 *format,
+ FcChar8 *scratch,
+ int scratch_len)
{
c->format_orig = c->format = format;
c->format_len = strlen ((const char *) format);
- c->word = malloc (c->format_len + 1);
+
+ 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)
+ if (c && c->word_allocated)
{
free (c->word);
}
if (p == c->word)
{
- message ("expected element name at %d",
+ message ("expected identifier 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)
{
- if (!expect_char (c, '='))
- return FcFalse;
+ FcChar8 *new_str;
+ FcBool ret;
- if (!read_word (c))
+ 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))\
- return func (c, pat, buf)
- BUILTIN ("unparse", FcNameUnparse);
- BUILTIN ("verbose", FcPatternPrint);
- BUILTIN2 ("fcmatch", FcStrDirname);
- BUILTIN2 ("fclist", FcStrDirname);
- BUILTIN2 ("pkgkit", FcStrDirname);
-
- message ("unknown builtin \"%s\"",
- c->word);
- return FcFalse;
+ 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
}
static FcBool
-interpret_filter (FcFormatContext *c,
- FcPattern *pat,
- FcStrBuf *buf)
+interpret_filter_in (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
{
FcObjectSet *os;
FcPattern *subpat;
do
{
+ /* XXX binding */
if (!read_word (c) ||
!FcObjectSetAdd (os, (const char *) c->word))
{
}
static FcBool
-interpret_delete (FcFormatContext *c,
- FcPattern *pat,
- FcStrBuf *buf)
+interpret_filter_out (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
{
FcPattern *subpat;
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, ','));
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,
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)
+ if (e || else_string)
{
- FcValueListPtr l;
+ FcValueListPtr l = NULL;
if (add_colon)
FcStrBufChar (buf, ':');
FcStrBufChar (buf, '=');
}
- l = FcPatternEltValues(e);
- FcNameUnparseValueList (buf, l, '\0');
+ 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 FcChar8 *
-cescape (const FcChar8 *str)
+static FcBool
+cescape (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
{
- FcStrBuf buf;
- FcChar8 buf_static[8192];
+ /* XXX escape \n etc? */
- FcStrBufInit (&buf, buf_static, sizeof (buf_static));
while(*str)
{
switch (*str)
{
case '\\':
case '"':
- FcStrBufChar (&buf, '\\');
+ FcStrBufChar (buf, '\\');
break;
}
- FcStrBufChar (&buf, *str++);
+ FcStrBufChar (buf, *str++);
}
- return FcStrBufDone (&buf);
+ return FcTrue;
}
-static FcChar8 *
-shescape (const FcChar8 *str)
+static FcBool
+shescape (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
{
- FcStrBuf buf;
- FcChar8 buf_static[8192];
-
- FcStrBufInit (&buf, buf_static, sizeof (buf_static));
- FcStrBufChar (&buf, '\'');
+ FcStrBufChar (buf, '\'');
while(*str)
{
if (*str == '\'')
- FcStrBufString (&buf, (const FcChar8 *) "'\\''");
+ FcStrBufString (buf, (const FcChar8 *) "'\\''");
else
- FcStrBufChar (&buf, *str);
+ FcStrBufChar (buf, *str);
str++;
}
- FcStrBufChar (&buf, '\'');
- return FcStrBufDone (&buf);
+ FcStrBufChar (buf, '\'');
+ return FcTrue;
}
-static FcChar8 *
-xmlescape (const FcChar8 *str)
+static FcBool
+xmlescape (FcFormatContext *c,
+ const FcChar8 *str,
+ FcStrBuf *buf)
{
- FcStrBuf buf;
- FcChar8 buf_static[8192];
+ /* XXX escape \n etc? */
- FcStrBufInit (&buf, buf_static, sizeof (buf_static));
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;
+ 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 FcStrBufDone (&buf);
+ return FcTrue;
}
-static FcChar8 *
+static FcBool
delete_chars (FcFormatContext *c,
- const FcChar8 *str)
+ const FcChar8 *str,
+ FcStrBuf *buf)
{
- FcStrBuf buf;
- FcChar8 buf_static[8192];
-
/* XXX not UTF-8 aware */
if (!expect_char (c, '(') ||
!read_chars (c, ')') ||
!expect_char (c, ')'))
- return NULL;
+ return FcFalse;
- FcStrBufInit (&buf, buf_static, sizeof (buf_static));
while(*str)
{
FcChar8 *p;
p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
if (p)
{
- FcStrBufData (&buf, str, p - str);
+ FcStrBufData (buf, str, p - str);
str = p + 1;
}
else
{
- FcStrBufString (&buf, str);
+ FcStrBufString (buf, str);
break;
}
}
- return FcStrBufDone (&buf);
+
+ return FcTrue;
}
-static FcChar8 *
+static FcBool
escape_chars (FcFormatContext *c,
- const FcChar8 *str)
+ const FcChar8 *str,
+ FcStrBuf *buf)
{
- FcStrBuf buf;
- FcChar8 buf_static[8192];
-
/* XXX not UTF-8 aware */
if (!expect_char (c, '(') ||
!read_chars (c, ')') ||
!expect_char (c, ')'))
- return NULL;
+ return FcFalse;
- FcStrBufInit (&buf, buf_static, sizeof (buf_static));
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);
+ FcStrBufData (buf, str, p - str);
+ FcStrBufChar (buf, c->word[0]);
+ FcStrBufChar (buf, *p);
str = p + 1;
}
else
{
- FcStrBufString (&buf, str);
+ FcStrBufString (buf, str);
break;
}
}
- return FcStrBufDone (&buf);
+
+ return FcTrue;
}
-static FcChar8 *
+static FcBool
translate_chars (FcFormatContext *c,
- const FcChar8 *str)
+ const FcChar8 *str,
+ FcStrBuf *buf)
{
- FcStrBuf buf;
- FcChar8 buf_static[8192];
char *from, *to, repeat;
int from_len, to_len;
if (!expect_char (c, '(') ||
!read_chars (c, ',') ||
!expect_char (c, ','))
- return NULL;
+ return FcFalse;
from = (char *) c->word;
from_len = strlen (from);
to = from + from_len + 1;
- /* hack: we temporarily diverge c->word */
+ /* hack: we temporarily divert c->word */
c->word = (FcChar8 *) to;
if (!read_chars (c, ')'))
{
if (!expect_char (c, ')'))
return FcFalse;
- FcStrBufInit (&buf, buf_static, sizeof (buf_static));
while(*str)
{
FcChar8 *p;
if (p)
{
int i;
- FcStrBufData (&buf, str, p - str);
+ FcStrBufData (buf, str, p - str);
i = strchr (from, *p) - from;
- FcStrBufChar (&buf, i < to_len ? to[i] : repeat);
+ FcStrBufChar (buf, i < to_len ? to[i] : repeat);
str = p + 1;
}
else
{
- FcStrBufString (&buf, str);
+ FcStrBufString (buf, str);
break;
}
}
- return FcStrBufDone (&buf);
+
+ return FcTrue;
}
-static FcChar8 *
-convert (FcFormatContext *c,
- const FcChar8 *str)
+static FcBool
+interpret_convert (FcFormatContext *c,
+ FcStrBuf *buf,
+ int start)
{
- if (!read_word (c))
- return NULL;
+ 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))\
- return func (str)
-#define CONVERTER2(name, func) \
- else if (0 == strcmp ((const char *) c->word, name))\
- return func (c, str)
+ do { new_str = func (str); ret = FcTrue; } while (0)
CONVERTER ("downcase", FcStrDowncase);
CONVERTER ("basename", FcStrBasename);
CONVERTER ("dirname", FcStrDirname);
- CONVERTER ("cescape", cescape);
- CONVERTER ("shescape", shescape);
- CONVERTER ("xmlescape", xmlescape);
- CONVERTER2 ("delete", delete_chars);
- CONVERTER2 ("escape", escape_chars);
- CONVERTER2 ("translate", translate_chars);
+#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);
- message ("unknown converter \"%s\"",
- c->word);
- return NULL;
+ return ret;
}
static FcBool
FcStrBuf *buf,
int start)
{
- while (consume_char (c, '|'))
- {
- const FcChar8 *str;
- FcChar8 *new_str;
-
- /* nul-terminate the buffer */
- FcStrBufChar (buf, '\0');
- if (buf->failed)
- return FcFalse;
- str = buf->buf + start;
-
- if (!(new_str = convert (c, str)))
+ while (*c->format == '|')
+ if (!interpret_convert (c, buf, start))
return FcFalse;
- /* replace in the buffer */
- buf->len = start;
- FcStrBufString (buf, new_str);
- free (new_str);
- }
-
return FcTrue;
}
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 &&
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)
+FcPatternFormat (FcPattern *pat,
+ const FcChar8 *format)
{
FcStrBuf buf;
- FcChar8 buf_static[8192];
- FcFormatContext c;
+ FcChar8 buf_static[8192 - 1024];
FcBool ret;
FcStrBufInit (&buf, buf_static, sizeof (buf_static));
- if (!FcFormatContextInit (&c, format))
- return NULL;
- ret = interpret_expr (&c, pat, &buf, '\0');
+ ret = FcPatternFormatToBuf (pat, format, &buf);
- FcFormatContextDone (&c);
if (ret)
return FcStrBufDone (&buf);
else