]> git.wh0rd.org - fontconfig.git/blobdiff - src/fcformat.c
[fcformat] Support 'default value' for simple tags
[fontconfig.git] / src / fcformat.c
index 971ee083e0272e89f550087f7d9c49531fcd6974..f91b6e0fb695e18643a6d5ab4b015db32b20ad27 100644 (file)
 /*
  * Some ideas for future syntax extensions:
  *
+ * - array enumeration using '%{[]family,familylang{expr}|decorator}'
+ * - langset enumeration using same syntax as array enumeration
  * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
- * - allow indexing simple tags using '%{elt[idx]}'
- * - conditional/filtering/deletion on binding (using '(w)'/'(s)' notation)
+ * - 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}"
+
+
 static void
 message (const char *fmt, ...)
 {
@@ -54,15 +60,28 @@ typedef struct _FcFormatContext
     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;
 }
@@ -70,7 +89,7 @@ FcFormatContextInit (FcFormatContext *c,
 static void
 FcFormatContextDone (FcFormatContext *c)
 {
-    if (c)
+    if (c && c->word_allocated)
     {
        free (c->word);
     }
@@ -163,7 +182,7 @@ read_word (FcFormatContext *c)
 
     if (p == c->word)
     {
-       message ("expected element name at %d",
+       message ("expected identifier at %d",
                 c->format - c->format_orig + 1);
        return FcFalse;
     }
@@ -203,6 +222,64 @@ read_chars (FcFormatContext *c,
     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 ("fcmatch",  FCMATCH_FORMAT);
+    BUILTIN ("fclist",   FCLIST_FORMAT);
+#undef BUILTIN
+    else
+       ret = FcFalse;
+
+    if (!ret)
+       message ("unknown builtin \"%s\"",
+                c->word);
+
+    return ret;
+}
+
 static FcBool
 interpret_expr (FcFormatContext *c,
                FcPattern       *pat,
@@ -463,6 +540,8 @@ interpret_simple (FcFormatContext *c,
     FcPatternElt *e;
     FcBool        add_colon = FcFalse;
     FcBool        add_elt_name = FcFalse;
+    int           idx;
+    FcChar8      *else_string;
 
     if (consume_char (c, ':'))
        add_colon = FcTrue;
@@ -470,9 +549,41 @@ interpret_simple (FcFormatContext *c,
     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;
+       }
+       expect_char (c, ']');
+    }
+
     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)
@@ -488,89 +599,104 @@ interpret_simple (FcFormatContext *c,
        }
 
        l = FcPatternEltValues(e);
-       FcNameUnparseValueList (buf, l, '\0');
+
+       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
+       {
+           FcNameUnparseValueList (buf, l, '\0');
+       }
+    }
+    else
+notfound:
+    {
+       if (else_string)
+           printf ("%s", 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];
-
-    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];
-
-    FcStrBufInit (&buf, buf_static, sizeof (buf_static));
     while(*str)
     {
        switch (*str)
        {
-       case '&': FcStrBufString (&buf, (const FcChar8 *) "&amp;"); break;
-       case '<': FcStrBufString (&buf, (const FcChar8 *) "&lt;");  break;
-       case '>': FcStrBufString (&buf, (const FcChar8 *) "&gt;");  break;
-       default:  FcStrBufChar   (&buf, *str);                      break;
+       case '&': FcStrBufString (buf, (const FcChar8 *) "&amp;"); break;
+       case '<': FcStrBufString (buf, (const FcChar8 *) "&lt;");  break;
+       case '>': FcStrBufString (buf, (const FcChar8 *) "&gt;");  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;
@@ -578,34 +704,32 @@ delete_chars (FcFormatContext *c,
        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;
@@ -613,27 +737,27 @@ escape_chars (FcFormatContext *c,
        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;
 
@@ -642,13 +766,13 @@ translate_chars (FcFormatContext *c,
     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, ')'))
     {
@@ -663,7 +787,6 @@ translate_chars (FcFormatContext *c,
     if (!expect_char (c, ')'))
        return FcFalse;
 
-    FcStrBufInit (&buf, buf_static, sizeof (buf_static));
     while(*str)
     {
        FcChar8 *p;
@@ -672,46 +795,97 @@ translate_chars (FcFormatContext *c,
        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);
 
-    message ("unknown converter \"%s\"",
-            c->word);
-    return NULL;
+    FcStrBufDestroy (&new_buf);
+
+    return ret;
 }
 
 static FcBool
@@ -719,26 +893,10 @@ maybe_interpret_converts (FcFormatContext *c,
                           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;
 }
 
@@ -805,6 +963,7 @@ 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;
@@ -844,21 +1003,37 @@ interpret_expr (FcFormatContext *c,
     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