]> git.wh0rd.org - fontconfig.git/blobdiff - src/fcformat.c
[fcformat] Implement array enumeration
[fontconfig.git] / src / fcformat.c
index 898ee324151371476cfd8fba1e9d8efe42d7c99d..5aa1977b8a22ec9cf0a0a3c4cc5f4c26b5ee6e86 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 simple tags using '%{elt[idx]}'
  * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
  * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
  */
 
 
-/* fc-match needs '<unknown filename>', etc handling. */
-#define FCMATCH_FORMAT "%{file|basename}: \"%{family[0]}\" \"%{style[0]}\""
+#define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
 #define FCLIST_FORMAT  "%{?file{%{file}: }}%{=unparse}"
 
 
@@ -247,7 +244,7 @@ interpret_builtin (FcFormatContext *c,
     else if (0 == strcmp ((const char *) c->word, name))\
        do { new_str = func (pat); ret = FcTrue; } while (0)
     BUILTIN ("unparse",  FcNameUnparse);
- /* BUILTIN ("verbose",  FcPatternPrint); */
+ /* BUILTIN ("verbose",  FcPatternPrint); XXX */
 #undef BUILTIN
     else
        ret = FcFalse;
@@ -478,9 +475,8 @@ interpret_cond (FcFormatContext *c,
 
        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, ','));
 
@@ -534,6 +530,86 @@ interpret_count (FcFormatContext *c,
     return FcTrue;
 }
 
+static FcBool
+interpret_array (FcFormatContext *c,
+                FcPattern       *pat,
+                FcStrBuf        *buf)
+{
+    FcObjectSet   *os;
+    FcPattern     *subpat;
+    const FcChar8 *format_save;
+    int            idx;
+    FcBool         ret, done;
+
+    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, ','));
+
+    subpat = FcPatternDuplicate (pat);
+    if (!subpat)
+       goto bail0;
+
+    format_save = c->format;
+    idx = 0;
+    do
+    {
+       int i;
+
+       done = FcTrue;
+
+       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))
+           {
+               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);
+
+bail:
+    FcPatternDestroy (subpat);
+bail0:
+    FcObjectSetDestroy (os);
+
+    return ret;
+}
+
 static FcBool
 interpret_simple (FcFormatContext *c,
                  FcPattern       *pat,
@@ -543,6 +619,7 @@ interpret_simple (FcFormatContext *c,
     FcBool        add_colon = FcFalse;
     FcBool        add_elt_name = FcFalse;
     int           idx;
+    FcChar8      *else_string;
 
     if (consume_char (c, ':'))
        add_colon = FcTrue;
@@ -560,12 +637,32 @@ interpret_simple (FcFormatContext *c,
                     c->format-1 - c->format_orig + 1);
            return FcFalse;
        }
-       expect_char (c, ']');
+       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)
@@ -604,6 +701,8 @@ interpret_simple (FcFormatContext *c,
     else
 notfound:
     {
+       if (else_string)
+           printf ("%s", else_string);
     }
 
     return FcTrue;
@@ -752,7 +851,7 @@ translate_chars (FcFormatContext *c,
     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, ')'))
     {
@@ -949,6 +1048,7 @@ interpret_percent (FcFormatContext *c,
     case '-': ret = interpret_delete  (c, pat, buf); break;
     case '?': ret = interpret_cond    (c, pat, buf); break;
     case '#': ret = interpret_count   (c, pat, buf); break;
+    case '[': ret = interpret_array   (c, pat, buf); break;
     default:  ret = interpret_simple  (c, pat, buf); break;
     }