]> git.wh0rd.org - fontconfig.git/blobdiff - src/fcformat.c
Replace 'KEITH PACKARD' with 'THE AUTHOR(S)' in license text in all files
[fontconfig.git] / src / fcformat.c
index f91b6e0fb695e18643a6d5ab4b015db32b20ad27..99c4f858fcd31cd4c8643739863f899f2363d35d 100644 (file)
@@ -13,9 +13,9 @@
  * 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>
 
 
+/* XXX Document the language.
+ *
+ * These are mostly the features implemented but not documented:
+ *
+ * width   %[[-]width]{tag}
+ * index   %{tag[ids]}
+ * name=   %{tag=|decorator}
+ * :name=  %{:tag=|decorator}
+ * subexpr %{{expr}|decorator1|decorator2}
+ * delete  %{-charset,lang{expr}|decorator}
+ * filter  %{+family,familylang{expr}|decorator}
+ * cond    %{?tag1,tag2,!tag3{}{}}
+ * decorat %{tag|decorator1|decorator2}
+ * default %{parameter:-word}
+ * array   %{[]family,familylang{expr}|decorator}
+ * langset enumeration using the same syntax as arrays
+ *
+ * filters:
+ * basename        FcStrBasename
+ * dirname         FcStrDirname
+ * downcase        FcStrDowncase
+ * shescape
+ * cescape
+ * xmlescape
+ * delete          delete chars
+ * escape          escape chars
+ * translate       translate chars
+ *
+ * builtins:
+ * unparse
+ * fcmatch
+ * fclist
+ * pkgkit
+ */
+
 /*
  * 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}}'
  * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
  */
@@ -40,6 +73,7 @@
 
 #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
@@ -269,6 +303,7 @@ interpret_builtin (FcFormatContext *c,
        ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
     BUILTIN ("fcmatch",  FCMATCH_FORMAT);
     BUILTIN ("fclist",   FCLIST_FORMAT);
+    BUILTIN ("pkgkit",   PKGKIT_FORMAT);
 #undef BUILTIN
     else
        ret = FcFalse;
@@ -476,9 +511,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, ','));
 
@@ -532,6 +566,122 @@ 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;
+    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], idx, &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)))
+           {
+               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))
+               {
+                   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,
@@ -559,7 +709,8 @@ 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, '='))
@@ -969,6 +1120,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;
     }