+ 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, ' ');