+ 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)