]> git.wh0rd.org - fontconfig.git/blobdiff - src/fcxml.c
Fix unary operator parsing. Add floor, ceil, trunc and round unary
[fontconfig.git] / src / fcxml.c
index 64a5269bebc70e1d2101e0c92cb50d7661604acb..657959c158c7c4733b3df510c1be4f3e41e51163 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $XFree86: xc/lib/fontconfig/src/fcxml.c,v 1.3 2002/02/18 22:29:28 keithp Exp $
+ * $RCSId: xc/lib/fontconfig/src/fcxml.c,v 1.21 2002/08/22 18:53:22 keithp Exp $
  *
- * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
+ * Copyright © 2002 Keith Packard
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  */
 
 #include <stdarg.h>
-#include <expat.h>
 #include "fcint.h"
 
+#ifndef HAVE_XMLPARSE_H
+#define HAVE_XMLPARSE_H 0
+#endif
+
+#if HAVE_XMLPARSE_H
+#include <xmlparse.h>
+#else
+#include <expat.h>
+#endif
+
+#ifdef _WIN32
+#define STRICT
+#include <windows.h>
+#undef STRICT
+#endif
+
 FcTest *
-FcTestCreate (FcQual qual, const FcChar8 *field, FcOp compare, FcExpr *expr)
+FcTestCreate (FcMatchKind   kind, 
+             FcQual        qual,
+             const FcChar8 *field,
+             FcOp          compare,
+             FcExpr        *expr)
 {
     FcTest     *test = (FcTest *) malloc (sizeof (FcTest));
 
     if (test)
     {
+       FcMemAlloc (FC_MEM_TEST, sizeof (FcTest));
        test->next = 0;
+       test->kind = kind;
        test->qual = qual;
        test->field = (char *) FcStrCopy (field);
        test->op = compare;
@@ -49,6 +70,7 @@ FcTestDestroy (FcTest *test)
        FcTestDestroy (test->next);
     FcExprDestroy (test->expr);
     FcStrFree ((FcChar8 *) test->field);
+    FcMemFree (FC_MEM_TEST, sizeof (FcTest));
     free (test);
 }
 
@@ -59,6 +81,7 @@ FcExprCreateInteger (int i)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = FcOpInteger;
        e->u.ival = i;
     }
@@ -72,6 +95,7 @@ FcExprCreateDouble (double d)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = FcOpDouble;
        e->u.dval = d;
     }
@@ -85,6 +109,7 @@ FcExprCreateString (const FcChar8 *s)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = FcOpString;
        e->u.sval = FcStrCopy (s);
     }
@@ -98,6 +123,7 @@ FcExprCreateMatrix (const FcMatrix *m)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = FcOpMatrix;
        e->u.mval = FcMatrixCopy (m);
     }
@@ -111,6 +137,7 @@ FcExprCreateBool (FcBool b)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = FcOpBool;
        e->u.bval = b;
     }
@@ -124,6 +151,7 @@ FcExprCreateNil (void)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = FcOpNil;
     }
     return e;
@@ -136,6 +164,7 @@ FcExprCreateField (const char *field)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = FcOpField;
        e->u.field = (char *) FcStrCopy ((FcChar8 *) field);
     }
@@ -149,6 +178,7 @@ FcExprCreateConst (const FcChar8 *constant)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = FcOpConst;
        e->u.constant = FcStrCopy (constant);
     }
@@ -162,6 +192,7 @@ FcExprCreateOp (FcExpr *left, FcOp op, FcExpr *right)
 
     if (e)
     {
+       FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
        e->op = op;
        e->u.tree.left = left;
        e->u.tree.right = right;
@@ -172,6 +203,8 @@ FcExprCreateOp (FcExpr *left, FcOp op, FcExpr *right)
 void
 FcExprDestroy (FcExpr *e)
 {
+    if (!e)
+       return;
     switch (e->op) {
     case FcOpInteger:
        break;
@@ -204,12 +237,13 @@ FcExprDestroy (FcExpr *e)
     case FcOpOr:
     case FcOpAnd:
     case FcOpEqual:
-    case FcOpContains:
     case FcOpNotEqual:
     case FcOpLess:
     case FcOpLessEqual:
     case FcOpMore:
     case FcOpMoreEqual:
+    case FcOpContains:
+    case FcOpNotContains:
     case FcOpPlus:
     case FcOpMinus:
     case FcOpTimes:
@@ -219,17 +253,22 @@ FcExprDestroy (FcExpr *e)
        FcExprDestroy (e->u.tree.right);
        /* fall through */
     case FcOpNot:
+    case FcOpFloor:
+    case FcOpCeil:
+    case FcOpRound:
+    case FcOpTrunc:
        FcExprDestroy (e->u.tree.left);
        break;
     case FcOpNil:
     case FcOpInvalid:
        break;
     }
+    FcMemFree (FC_MEM_EXPR, sizeof (FcExpr));
     free (e);
 }
 
 FcEdit *
-FcEditCreate (const char *field, FcOp op, FcExpr *expr)
+FcEditCreate (const char *field, FcOp op, FcExpr *expr, FcValueBinding binding)
 {
     FcEdit *e = (FcEdit *) malloc (sizeof (FcEdit));
 
@@ -239,6 +278,7 @@ FcEditCreate (const char *field, FcOp op, FcExpr *expr)
        e->field = field;   /* already saved in grammar */
        e->op = op;
        e->expr = expr;
+       e->binding = binding;
     }
     return e;
 }
@@ -270,6 +310,7 @@ typedef enum _FcElement {
     FcElementAlias,
        
     FcElementBlank,
+    FcElementRescan,
 
     FcElementPrefer,
     FcElementAccept,
@@ -294,12 +335,18 @@ typedef enum _FcElement {
     FcElementLessEq,
     FcElementMore,
     FcElementMoreEq,
+    FcElementContains,
+    FcElementNotContains,
     FcElementPlus,
     FcElementMinus,
     FcElementTimes,
     FcElementDivide,
     FcElementNot,
     FcElementIf,
+    FcElementFloor,
+    FcElementCeil,
+    FcElementRound,
+    FcElementTrunc,
     FcElementUnknown
 } FcElement;
 
@@ -319,6 +366,7 @@ FcElementMap (const XML_Char *name)
        { "alias",      FcElementAlias },
        
        { "blank",      FcElementBlank },
+       { "rescan",     FcElementRescan },
 
        { "prefer",     FcElementPrefer },
        { "accept",     FcElementAccept },
@@ -343,12 +391,18 @@ FcElementMap (const XML_Char *name)
        { "less_eq",    FcElementLessEq },
        { "more",       FcElementMore },
        { "more_eq",    FcElementMoreEq },
+       { "contains",   FcElementContains },
+       { "not_contains",FcElementNotContains },
        { "plus",       FcElementPlus },
        { "minus",      FcElementMinus },
        { "times",      FcElementTimes },
        { "divide",     FcElementDivide },
        { "not",        FcElementNot },
        { "if",         FcElementIf },
+       { "floor",      FcElementFloor },
+       { "ceil",       FcElementCeil },
+       { "round",      FcElementRound },
+       { "trunc",      FcElementTrunc },
        
        { 0,            0 }
     };
@@ -418,24 +472,36 @@ typedef struct _FcConfigParse {
     XML_Parser     parser;
 } FcConfigParse;
 
+typedef enum _FcConfigSeverity {
+    FcSevereInfo, FcSevereWarning, FcSevereError
+} FcConfigSeverity;
+
 static void
-FcConfigError (FcConfigParse *parse, char *fmt, ...)
+FcConfigMessage (FcConfigParse *parse, FcConfigSeverity severe, char *fmt, ...)
 {
+    char       *s = "unknown";
     va_list    args;
 
     va_start (args, fmt);
+
+    switch (severe) {
+    case FcSevereInfo: s = "info"; break;
+    case FcSevereWarning: s = "warning"; break;
+    case FcSevereError: s = "error"; break;
+    }
     if (parse)
     {
        if (parse->name)
-           fprintf (stderr, "Fontconfig error: \"%s\", line %d: ",
+           fprintf (stderr, "Fontconfig %s: \"%s\", line %d: ", s,
                     parse->name, XML_GetCurrentLineNumber (parse->parser));
        else
-           fprintf (stderr, "Fontconfig error: line %d: ",
+           fprintf (stderr, "Fontconfig %s: line %d: ", s,
                     XML_GetCurrentLineNumber (parse->parser));
-       parse->error = FcTrue;
+       if (severe >= FcSevereError)
+           parse->error = FcTrue;
     }
     else
-       fprintf (stderr, "Fontconfig error: ");
+       fprintf (stderr, "Fontconfig %s: ", s);
     vfprintf (stderr, fmt, args);
     fprintf (stderr, "\n");
     va_end (args);
@@ -457,6 +523,7 @@ FcVStackCreate (void)
     new = malloc (sizeof (FcVStack));
     if (!new)
        return 0;
+    FcMemAlloc (FC_MEM_VSTACK, sizeof (FcVStack));
     new->tag = FcVStackNone;
     new->prev = 0;
     return new;
@@ -500,6 +567,7 @@ FcVStackDestroy (FcVStack *vstack)
            FcEditDestroy (vstack->u.edit);
            break;
        }
+       FcMemFree (FC_MEM_VSTACK, sizeof (FcVStack));
        free (vstack);
     }
 }
@@ -670,6 +738,7 @@ FcConfigSaveAttr (const XML_Char **attr)
     new = malloc ((i + 1) * sizeof (FcChar8 *) + slen);
     if (!new)
        return 0;
+    FcMemAlloc (FC_MEM_ATTR, 1);    /* size is too expensive */
     s = (FcChar8 *) (new + (i + 1));
     for (i = 0; attr[i]; i++)
     {
@@ -688,13 +757,14 @@ FcPStackPush (FcConfigParse *parse, FcElement element, const XML_Char **attr)
 
     if (!new)
        return FcFalse;
+    FcMemAlloc (FC_MEM_PSTACK, sizeof (FcPStack));
     new->prev = parse->pstack;
     new->element = element;
     if (attr)
     {
        new->attr = FcConfigSaveAttr (attr);
        if (!new->attr)
-           FcConfigError (parse, "out of memory");
+           FcConfigMessage (parse, FcSevereError, "out of memory");
     }
     else
        new->attr = 0;
@@ -710,7 +780,7 @@ FcPStackPop (FcConfigParse *parse)
     
     if (!parse->pstack) 
     {
-       FcConfigError (parse, "mismatching element");
+       FcConfigMessage (parse, FcSevereError, "mismatching element");
        return FcFalse;
     }
     FcVStackClear (parse);
@@ -718,7 +788,11 @@ FcPStackPop (FcConfigParse *parse)
     parse->pstack = old->prev;
     FcStrBufDestroy (&old->str);
     if (old->attr)
+    {
+       FcMemFree (FC_MEM_ATTR, 1); /* size is to expensive */
        free (old->attr);
+    }
+    FcMemFree (FC_MEM_PSTACK, sizeof (FcPStack));
     free (old);
     return FcTrue;
 }
@@ -767,14 +841,11 @@ FcStartElement(void *userData, const XML_Char *name, const XML_Char **attr)
     
     element = FcElementMap (name);
     if (element == FcElementUnknown)
-    {
-       FcConfigError (parse, "unknown element \"%s\"", name);
-       return;
-    }
+       FcConfigMessage (parse, FcSevereWarning, "unknown element \"%s\"", name);
     
     if (!FcPStackPush (parse, element, attr))
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        return;
     }
     return;
@@ -788,7 +859,7 @@ FcParseBlank (FcConfigParse *parse)
     {
        FcVStack    *v = FcVStackFetch (parse, n);
        if (v->tag != FcVStackInteger)
-           FcConfigError (parse, "non-integer blank");
+           FcConfigMessage (parse, FcSevereError, "non-integer blank");
        else
        {
            if (!parse->config->blanks)
@@ -796,19 +867,33 @@ FcParseBlank (FcConfigParse *parse)
                parse->config->blanks = FcBlanksCreate ();
                if (!parse->config->blanks)
                {
-                   FcConfigError (parse, "out of memory");
+                   FcConfigMessage (parse, FcSevereError, "out of memory");
                    break;
                }
            }
            if (!FcBlanksAdd (parse->config->blanks, v->u.integer))
            {
-               FcConfigError (parse, "out of memory");
+               FcConfigMessage (parse, FcSevereError, "out of memory");
                break;
            }
        }
     }
 }
 
+static void
+FcParseRescan (FcConfigParse *parse)
+{
+    int            n = FcVStackElements (parse);
+    while (n-- > 0)
+    {
+       FcVStack    *v = FcVStackFetch (parse, n);
+       if (v->tag != FcVStackInteger)
+           FcConfigMessage (parse, FcSevereWarning, "non-integer rescan");
+       else
+           parse->config->rescanInterval = v->u.integer;
+    }
+}
+
 static void
 FcParseInt (FcConfigParse *parse)
 {
@@ -820,18 +905,73 @@ FcParseInt (FcConfigParse *parse)
     s = FcStrBufDone (&parse->pstack->str);
     if (!s)
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        return;
     }
     end = 0;
     l = (int) strtol ((char *) s, (char **)&end, 0);
     if (end != s + strlen ((char *) s))
-       FcConfigError (parse, "\"%s\": not a valid integer", s);
+       FcConfigMessage (parse, FcSevereError, "\"%s\": not a valid integer", s);
     else
        FcVStackPushInteger (parse, l);
     FcStrFree (s);
 }
 
+/*
+ * idea copied from glib g_ascii_strtod with 
+ * permission of the author (Alexander Larsson) 
+ */
+
+#include <locale.h>
+
+static double 
+FcStrtod (char *s, char **end)
+{
+    struct lconv    *locale_data;
+    char           *dot;
+    double         v;
+
+    /*
+     * Have to swap the decimal point to match the current locale
+     * if that locale doesn't use 0x2e
+     */
+    if ((dot = strchr (s, 0x2e)) &&
+       (locale_data = localeconv ()) &&
+       (locale_data->decimal_point[0] != 0x2e ||
+        locale_data->decimal_point[1] != 0))
+    {
+       char    buf[128];
+       int     slen = strlen (s);
+       int     dlen = strlen (locale_data->decimal_point);
+       
+       if (slen + dlen > sizeof (buf))
+       {
+           if (end)
+               *end = s;
+           v = 0;
+       }
+       else
+       {
+           char        *buf_end;
+           /* mantissa */
+           strncpy (buf, s, dot - s);
+           /* decimal point */
+           strcpy (buf + (dot - s), locale_data->decimal_point);
+           /* rest of number */
+           strcpy (buf + (dot - s) + dlen, dot + 1);
+           buf_end = 0;
+           v = strtod (buf, &buf_end);
+           if (buf_end)
+               buf_end = s + (buf_end - buf);
+           if (end)
+               *end = buf_end;
+       }
+    }
+    else
+       v = strtod (s, end);
+    return v;
+}
+
 static void
 FcParseDouble (FcConfigParse *parse)
 {
@@ -843,13 +983,13 @@ FcParseDouble (FcConfigParse *parse)
     s = FcStrBufDone (&parse->pstack->str);
     if (!s)
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        return;
     }
     end = 0;
-    d = strtod ((char *) s, (char **)&end);
+    d = FcStrtod ((char *) s, (char **)&end);
     if (end != s + strlen ((char *) s))
-       FcConfigError (parse, "\"%s\": not a valid double", s);
+       FcConfigMessage (parse, FcSevereError, "\"%s\": not a valid double", s);
     else
        FcVStackPushDouble (parse, d);
     FcStrFree (s);
@@ -865,7 +1005,7 @@ FcParseString (FcConfigParse *parse, FcVStackTag tag)
     s = FcStrBufDone (&parse->pstack->str);
     if (!s)
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        return;
     }
     if (!FcVStackPushString (parse, tag, s))
@@ -881,23 +1021,31 @@ FcParseMatrix (FcConfigParse *parse)
     
     while ((vstack = FcVStackPop (parse)))
     {
-       if (vstack->tag != FcVStackDouble)
-           FcConfigError (parse, "non-double matrix element");
-       else
-       {
-           double v = vstack->u._double;
-           switch (matrix_state) {
-           case m_xx: m.xx = v; break;
-           case m_xy: m.xy = v; break;
-           case m_yx: m.yx = v; break;
-           case m_yy: m.yy = v; break;
-           default: break;
-           }
-           matrix_state--;
+       double  v;
+       switch (vstack->tag) {
+       case FcVStackInteger:
+           v = vstack->u.integer;
+           break;
+       case FcVStackDouble:
+           v = vstack->u._double;
+           break;
+       default:
+           FcConfigMessage (parse, FcSevereError, "non-double matrix element");
+           v = 1.0;
+           break;
+       }
+       switch (matrix_state) {
+       case m_xx: m.xx = v; break;
+       case m_xy: m.xy = v; break;
+       case m_yx: m.yx = v; break;
+       case m_yy: m.yy = v; break;
+       default: break;
        }
+       FcVStackDestroy (vstack);
+       matrix_state--;
     }
     if (matrix_state != m_done)
-       FcConfigError (parse, "wrong number of matrix elements");
+       FcConfigMessage (parse, FcSevereError, "wrong number of matrix elements");
     else
        FcVStackPushMatrix (parse, &m);
 }
@@ -924,7 +1072,7 @@ FcParseBool (FcConfigParse *parse)
     s = FcStrBufDone (&parse->pstack->str);
     if (!s)
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        return;
     }
     FcVStackPushBool (parse, FcConfigLexBool (s));
@@ -941,8 +1089,9 @@ FcParseFamilies (FcConfigParse *parse, FcVStackTag tag)
     {
        if (vstack->tag != FcVStackFamily)
        {
-           FcConfigError (parse, "non-family");
-           break;
+           FcConfigMessage (parse, FcSevereWarning, "non-family");
+           FcVStackDestroy (vstack);
+           continue;
        }
        left = vstack->u.expr;
        vstack->tag = FcVStackNone;
@@ -952,7 +1101,7 @@ FcParseFamilies (FcConfigParse *parse, FcVStackTag tag)
            new = FcExprCreateOp (left, FcOpComma, expr);
            if (!new)
            {
-               FcConfigError (parse, "out of memory");
+               FcConfigMessage (parse, FcSevereError, "out of memory");
                FcExprDestroy (left);
                FcExprDestroy (expr);
                break;
@@ -966,7 +1115,7 @@ FcParseFamilies (FcConfigParse *parse, FcVStackTag tag)
     {
        if (!FcVStackPushExpr (parse, tag, expr))
        {
-           FcConfigError (parse, "out of memory");
+           FcConfigMessage (parse, FcSevereError, "out of memory");
            if (expr)
                FcExprDestroy (expr);
        }
@@ -984,7 +1133,7 @@ FcParseFamily (FcConfigParse *parse)
     s = FcStrBufDone (&parse->pstack->str);
     if (!s)
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        return;
     }
     expr = FcExprCreateString (s);
@@ -996,7 +1145,7 @@ FcParseFamily (FcConfigParse *parse)
 static void
 FcParseAlias (FcConfigParse *parse)
 {
-    FcExpr     *family = 0, *accept = 0, *prefer = 0, *def = 0;
+    FcExpr     *family = 0, *accept = 0, *prefer = 0, *def = 0, *new = 0;
     FcEdit     *edit = 0, *next;
     FcVStack   *vstack;
     FcTest     *test;
@@ -1006,9 +1155,20 @@ FcParseAlias (FcConfigParse *parse)
        switch (vstack->tag) {
        case FcVStackFamily:
            if (family)
-               FcExprDestroy (family);
-           family = vstack->u.expr;
-           vstack->tag = FcVStackNone;
+           {
+               new = FcExprCreateOp (vstack->u.expr, FcOpComma, family);
+               if (!new)
+                   FcConfigMessage (parse, FcSevereError, "out of memory");
+               else
+                   family = new;
+           }
+           else
+               new = vstack->u.expr;
+           if (new)
+           {
+               family = new;
+               vstack->tag = FcVStackNone;
+           }
            break;
        case FcVStackPrefer:
            if (prefer)
@@ -1029,21 +1189,28 @@ FcParseAlias (FcConfigParse *parse)
            vstack->tag = FcVStackNone;
            break;
        default:
-           FcConfigError (parse, "bad alias");
+           FcConfigMessage (parse, FcSevereWarning, "bad alias");
            break;
        }
        FcVStackDestroy (vstack);
     }
     if (!family)
     {
-       FcConfigError (parse, "missing family in alias");
+       FcConfigMessage (parse, FcSevereError, "missing family in alias");
+       if (prefer)
+           FcExprDestroy (prefer);
+       if (accept)
+           FcExprDestroy (accept);
+       if (def)
+           FcExprDestroy (def);
        return;
     }
     if (prefer)
     {
        edit = FcEditCreate (FcConfigSaveField ("family"),
                             FcOpPrepend,
-                            prefer);
+                            prefer,
+                            FcValueBindingWeak);
        if (edit)
            edit->next = 0;
        else
@@ -1054,7 +1221,8 @@ FcParseAlias (FcConfigParse *parse)
        next = edit;
        edit = FcEditCreate (FcConfigSaveField ("family"),
                             FcOpAppend,
-                            accept);
+                            accept,
+                            FcValueBindingWeak);
        if (edit)
            edit->next = next;
        else
@@ -1064,8 +1232,9 @@ FcParseAlias (FcConfigParse *parse)
     {
        next = edit;
        edit = FcEditCreate (FcConfigSaveField ("family"),
-                             FcOpAppendLast,
-                             def);
+                            FcOpAppendLast,
+                            def,
+                            FcValueBindingWeak);
        if (edit)
            edit->next = next;
        else
@@ -1073,8 +1242,9 @@ FcParseAlias (FcConfigParse *parse)
     }
     if (edit)
     {
-       test = FcTestCreate (FcQualAny,
-                            FcStrCopy ((FcChar8 *) "family"),
+       test = FcTestCreate (FcMatchPattern,
+                            FcQualAny,
+                            (FcChar8 *) FC_FAMILY,
                             FcOpEqual,
                             family);
        if (test)
@@ -1097,9 +1267,13 @@ FcPopExpr (FcConfigParse *parse)
        break;
     case FcVStackString:
     case FcVStackFamily:
+       expr = FcExprCreateString (vstack->u.string);
+       break;
     case FcVStackField:
+       expr = FcExprCreateField ((char *) vstack->u.string);
+       break;
     case FcVStackConstant:
-       expr = FcExprCreateString (vstack->u.string);
+       expr = FcExprCreateConst (vstack->u.string);
        break;
     case FcVStackPrefer:
     case FcVStackAccept:
@@ -1123,6 +1297,7 @@ FcPopExpr (FcConfigParse *parse)
        break;
     case FcVStackExpr:
        expr = vstack->u.expr;
+       vstack->tag = FcVStackNone;
        break;
     case FcVStackEdit:
        break;
@@ -1131,8 +1306,17 @@ FcPopExpr (FcConfigParse *parse)
     return expr;
 }
 
+/*
+ * This builds a tree of binary operations.  Note
+ * that every operator is defined so that if only
+ * a single operand is contained, the value of the
+ * whole expression is the value of the operand.
+ *
+ * This code reduces in that case to returning that
+ * operand.
+ */
 static FcExpr *
-FcPopExprs (FcConfigParse *parse, FcOp op)
+FcPopBinary (FcConfigParse *parse, FcOp op)
 {
     FcExpr  *left, *expr = 0, *new;
 
@@ -1143,7 +1327,7 @@ FcPopExprs (FcConfigParse *parse, FcOp op)
            new = FcExprCreateOp (left, op, expr);
            if (!new)
            {
-               FcConfigError (parse, "out of memory");
+               FcConfigMessage (parse, FcSevereError, "out of memory");
                FcExprDestroy (left);
                FcExprDestroy (expr);
                break;
@@ -1157,9 +1341,39 @@ FcPopExprs (FcConfigParse *parse, FcOp op)
 }
 
 static void
-FcParseExpr (FcConfigParse *parse, FcOp op)
+FcParseBinary (FcConfigParse *parse, FcOp op)
 {
-    FcExpr  *expr = FcPopExprs (parse, op);
+    FcExpr  *expr = FcPopBinary (parse, op);
+    if (expr)
+       FcVStackPushExpr (parse, FcVStackExpr, expr);
+}
+
+/*
+ * This builds a a unary operator, it consumes only
+ * a single operand
+ */
+
+static FcExpr *
+FcPopUnary (FcConfigParse *parse, FcOp op)
+{
+    FcExpr  *operand, *new = 0;
+
+    if ((operand = FcPopExpr (parse)))
+    {
+       new = FcExprCreateOp (operand, op, 0);
+       if (!new)
+       {
+           FcExprDestroy (operand);
+           FcConfigMessage (parse, FcSevereError, "out of memory");
+       }
+    }
+    return new;
+}
+
+static void
+FcParseUnary (FcConfigParse *parse, FcOp op)
+{
+    FcExpr  *expr = FcPopUnary (parse, op);
     if (expr)
        FcVStackPushExpr (parse, FcVStackExpr, expr);
 }
@@ -1174,7 +1388,7 @@ FcParseInclude (FcConfigParse *parse)
     s = FcStrBufDone (&parse->pstack->str);
     if (!s)
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        return;
     }
     i = FcConfigGetAttribute (parse, "ignore_missing");
@@ -1182,7 +1396,7 @@ FcParseInclude (FcConfigParse *parse)
        ignore_missing = FcTrue;
     if (!FcConfigParseAndLoad (parse->config, s, !ignore_missing))
        parse->error = FcTrue;
-    free (s);
+    FcStrFree (s);
 }
 
 typedef struct _FcOpMap {
@@ -1207,7 +1421,9 @@ static const FcOpMap fcCompareOps[] = {
     { "less",          FcOpLess            },
     { "less_eq",       FcOpLessEqual       },
     { "more",          FcOpMore            },
-    { "more_eq",       FcOpMoreEqual       }
+    { "more_eq",       FcOpMoreEqual       },
+    { "contains",      FcOpContains        },
+    { "not_contains",  FcOpNotContains     }
 };
 
 #define NUM_COMPARE_OPS (sizeof fcCompareOps / sizeof fcCompareOps[0])
@@ -1222,6 +1438,8 @@ FcConfigLexCompare (const FcChar8 *compare)
 static void
 FcParseTest (FcConfigParse *parse)
 {
+    const FcChar8   *kind_string;
+    FcMatchKind            kind;
     const FcChar8   *qual_string;
     FcQual         qual;
     const FcChar8   *name;
@@ -1230,6 +1448,23 @@ FcParseTest (FcConfigParse *parse)
     FcExpr         *expr;
     FcTest         *test;
 
+    kind_string = FcConfigGetAttribute (parse, "target");
+    if (!kind_string)
+       kind = FcMatchDefault;
+    else
+    {
+       if (!strcmp ((char *) kind_string, "pattern"))
+           kind = FcMatchPattern;
+       else if (!strcmp ((char *) kind_string, "font"))
+           kind = FcMatchFont;
+       else if (!strcmp ((char *) kind_string, "default"))
+           kind = FcMatchDefault;
+       else
+       {
+           FcConfigMessage (parse, FcSevereWarning, "invalid test target \"%s\"", kind_string);
+           return;
+       }
+    }
     qual_string = FcConfigGetAttribute (parse, "qual");
     if (!qual_string)
        qual = FcQualAny;
@@ -1239,16 +1474,20 @@ FcParseTest (FcConfigParse *parse)
            qual = FcQualAny;
        else if (!strcmp ((char *) qual_string, "all"))
            qual = FcQualAll;
+       else if (!strcmp ((char *) qual_string, "first"))
+           qual = FcQualFirst;
+       else if (!strcmp ((char *) qual_string, "not_first"))
+           qual = FcQualNotFirst;
        else
        {
-           FcConfigError (parse, "invalid test qual \"%s\"", qual_string);
+           FcConfigMessage (parse, FcSevereWarning, "invalid test qual \"%s\"", qual_string);
            return;
        }
     }
     name = FcConfigGetAttribute (parse, "name");
     if (!name)
     {
-       FcConfigError (parse, "missing test name");
+       FcConfigMessage (parse, FcSevereWarning, "missing test name");
        return;
     }
     compare_string = FcConfigGetAttribute (parse, "compare");
@@ -1259,20 +1498,20 @@ FcParseTest (FcConfigParse *parse)
        compare = FcConfigLexCompare (compare_string);
        if (compare == FcOpInvalid)
        {
-           FcConfigError (parse, "invalid test compare \"%s\"", compare_string);
+           FcConfigMessage (parse, FcSevereWarning, "invalid test compare \"%s\"", compare_string);
            return;
        }
     }
-    expr = FcPopExpr (parse);
+    expr = FcPopBinary (parse, FcOpComma);
     if (!expr)
     {
-       FcConfigError (parse, "missing test expression");
+       FcConfigMessage (parse, FcSevereWarning, "missing test expression");
        return;
     }
-    test = FcTestCreate (qual, name, compare, expr);
+    test = FcTestCreate (kind, qual, name, compare, expr);
     if (!test)
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        return;
     }
     FcVStackPushTest (parse, test);
@@ -1300,33 +1539,52 @@ FcParseEdit (FcConfigParse *parse)
 {
     const FcChar8   *name;
     const FcChar8   *mode_string;
+    const FcChar8   *binding_string;
     FcOp           mode;
+    FcValueBinding  binding;
     FcExpr         *expr;
     FcEdit         *edit;
 
     name = FcConfigGetAttribute (parse, "name");
     if (!name)
     {
-       FcConfigError (parse, "missing edit name");
+       FcConfigMessage (parse, FcSevereWarning, "missing edit name");
        return;
     }
     mode_string = FcConfigGetAttribute (parse, "mode");
     if (!mode_string)
-       mode = FcOpEqual;
+       mode = FcOpAssign;
     else
     {
        mode = FcConfigLexMode (mode_string);
        if (mode == FcOpInvalid)
        {
-           FcConfigError (parse, "invalid edit mode \"%s\"", mode_string);
+           FcConfigMessage (parse, FcSevereWarning, "invalid edit mode \"%s\"", mode_string);
+           return;
+       }
+    }
+    binding_string = FcConfigGetAttribute (parse, "binding");
+    if (!binding_string)
+       binding = FcValueBindingWeak;
+    else
+    {
+       if (!strcmp ((char *) binding_string, "weak"))
+           binding = FcValueBindingWeak;
+       else if (!strcmp ((char *) binding_string, "strong"))
+           binding = FcValueBindingStrong;
+       else if (!strcmp ((char *) binding_string, "same"))
+           binding = FcValueBindingSame;
+       else
+       {
+           FcConfigMessage (parse, FcSevereWarning, "invalid edit binding \"%s\"", binding_string);
            return;
        }
     }
-    expr = FcPopExprs (parse, FcOpComma);
-    edit = FcEditCreate ((char *) FcStrCopy (name), mode, expr);
+    expr = FcPopBinary (parse, FcOpComma);
+    edit = FcEditCreate ((char *) FcStrCopy (name), mode, expr, binding);
     if (!edit)
     {
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
        FcExprDestroy (expr);
        return;
     }
@@ -1354,7 +1612,7 @@ FcParseMatch (FcConfigParse *parse)
            kind = FcMatchFont;
        else
        {
-           FcConfigError (parse, "invalid match target \"%s\"", kind_name);
+           FcConfigMessage (parse, FcSevereWarning, "invalid match target \"%s\"", kind_name);
            return;
        }
     }
@@ -1372,13 +1630,13 @@ FcParseMatch (FcConfigParse *parse)
            vstack->tag = FcVStackNone;
            break;
        default:
-           FcConfigError (parse, "invalid match element");
+           FcConfigMessage (parse, FcSevereWarning, "invalid match element");
            break;
        }
        FcVStackDestroy (vstack);
     }
     if (!FcConfigAddEdit (parse->config, test, edit, kind))
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
 }
 
 static void
@@ -1398,23 +1656,53 @@ FcEndElement(void *userData, const XML_Char *name)
        data = FcStrBufDone (&parse->pstack->str);
        if (!data)
        {
-           FcConfigError (parse, "out of memory");
+           FcConfigMessage (parse, FcSevereError, "out of memory");
            break;
        }
-       if (!FcConfigAddDir (parse->config, data))
-           FcConfigError (parse, "out of memory");
-       free (data);
+#ifdef _WIN32
+       if (strcmp (data, "WINDOWSFONTDIR") == 0)
+       {
+           int rc;
+           FcStrFree (data);
+           data = malloc (1000);
+           if (!data)
+           {
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+               break;
+           }
+           FcMemAlloc (FC_MEM_STRING, 1000);
+           rc = GetWindowsDirectory (data, 800);
+           if (rc == 0 || rc > 800)
+           {
+               FcConfigMessage (parse, FcSevereError, "GetWindowsDirectory failed");
+               FcStrFree (data);
+               break;
+           }
+           if (data [strlen (data) - 1] != '\\')
+               strcat (data, "\\");
+           strcat (data, "fonts");
+       }
+#endif
+       if (!FcStrUsesHome (data) || FcConfigHome ())
+       {
+           if (!FcConfigAddDir (parse->config, data))
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+       }
+       FcStrFree (data);
        break;
     case FcElementCache:
        data = FcStrBufDone (&parse->pstack->str);
        if (!data)
        {
-           FcConfigError (parse, "out of memory");
+           FcConfigMessage (parse, FcSevereError, "out of memory");
            break;
        }
-       if (!FcConfigSetCache (parse->config, data))
-           FcConfigError (parse, "out of memory");
-       free (data);
+       if (!FcStrUsesHome (data) || FcConfigHome ())
+       {
+           if (!FcConfigSetCache (parse->config, data))
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+       }
+       FcStrFree (data);
        break;
     case FcElementInclude:
        FcParseInclude (parse);
@@ -1431,6 +1719,9 @@ FcEndElement(void *userData, const XML_Char *name)
     case FcElementBlank:
        FcParseBlank (parse);
        break;
+    case FcElementRescan:
+       FcParseRescan (parse);
+       break;
        
     case FcElementPrefer:
        FcParseFamilies (parse, FcVStackPrefer);
@@ -1478,46 +1769,64 @@ FcEndElement(void *userData, const XML_Char *name)
        FcParseString (parse, FcVStackConstant);
        break;
     case FcElementOr:
-       FcParseExpr (parse, FcOpOr);
+       FcParseBinary (parse, FcOpOr);
        break;
     case FcElementAnd:
-       FcParseExpr (parse, FcOpAnd);
+       FcParseBinary (parse, FcOpAnd);
        break;
     case FcElementEq:
-       FcParseExpr (parse, FcOpEqual);
+       FcParseBinary (parse, FcOpEqual);
        break;
     case FcElementNotEq:
-       FcParseExpr (parse, FcOpNotEqual);
+       FcParseBinary (parse, FcOpNotEqual);
        break;
     case FcElementLess:
-       FcParseExpr (parse, FcOpLess);
+       FcParseBinary (parse, FcOpLess);
        break;
     case FcElementLessEq:
-       FcParseExpr (parse, FcOpLessEqual);
+       FcParseBinary (parse, FcOpLessEqual);
        break;
     case FcElementMore:
-       FcParseExpr (parse, FcOpMore);
+       FcParseBinary (parse, FcOpMore);
        break;
     case FcElementMoreEq:
-       FcParseExpr (parse, FcOpMoreEqual);
+       FcParseBinary (parse, FcOpMoreEqual);
+       break;
+    case FcElementContains:
+       FcParseBinary (parse, FcOpContains);
+       break;
+    case FcElementNotContains:
+       FcParseBinary (parse, FcOpNotContains);
        break;
     case FcElementPlus:
-       FcParseExpr (parse, FcOpPlus);
+       FcParseBinary (parse, FcOpPlus);
        break;
     case FcElementMinus:
-       FcParseExpr (parse, FcOpMinus);
+       FcParseBinary (parse, FcOpMinus);
        break;
     case FcElementTimes:
-       FcParseExpr (parse, FcOpTimes);
+       FcParseBinary (parse, FcOpTimes);
        break;
     case FcElementDivide:
-       FcParseExpr (parse, FcOpDivide);
+       FcParseBinary (parse, FcOpDivide);
        break;
     case FcElementNot:
-       FcParseExpr (parse, FcOpNot);
+       FcParseUnary (parse, FcOpNot);
        break;
     case FcElementIf:
-       FcParseExpr (parse, FcOpQuest);
+       FcParseBinary (parse, FcOpQuest);
+       break;
+    case FcElementFloor:
+       FcParseUnary (parse, FcOpFloor);
+       break;
+    case FcElementCeil:
+       FcParseUnary (parse, FcOpCeil);
+       break;
+    case FcElementRound:
+       FcParseUnary (parse, FcOpRound);
+       break;
+    case FcElementTrunc:
+       FcParseUnary (parse, FcOpTrunc);
        break;
     case FcElementUnknown:
        break;
@@ -1533,7 +1842,7 @@ FcCharacterData (void *userData, const XML_Char *s, int len)
     if (!parse->pstack)
        return;
     if (!FcStrBufData (&parse->pstack->str, (FcChar8 *) s, len))
-       FcConfigError (parse, "out of memory");
+       FcConfigMessage (parse, FcSevereError, "out of memory");
 }
 
 static void
@@ -1546,7 +1855,7 @@ FcStartDoctypeDecl (void      *userData,
     FcConfigParse   *parse = userData;
 
     if (strcmp ((char *) doctypeName, "fontconfig") != 0)
-       FcConfigError (parse, "invalid doctype \"%s\"", doctypeName);
+       FcConfigMessage (parse, FcSevereError, "invalid doctype \"%s\"", doctypeName);
 }
 
 static void
@@ -1571,8 +1880,15 @@ FcConfigParseAndLoad (FcConfig       *config,
     filename = FcConfigFilename (name);
     if (!filename)
        goto bail0;
+    
+    if (!FcStrSetAdd (config->configFiles, filename))
+    {
+       FcStrFree (filename);
+       goto bail0;
+    }
+
     f = fopen ((char *) filename, "r");
-    free (filename);
+    FcStrFree (filename);
     if (!f)
        goto bail0;
     
@@ -1592,12 +1908,22 @@ FcConfigParseAndLoad (FcConfig      *config,
     do {
        buf = XML_GetBuffer (p, BUFSIZ);
        if (!buf)
+       {
+           FcConfigMessage (&parse, FcSevereError, "cannot get parse buffer");
            goto bail3;
+       }
        len = fread (buf, 1, BUFSIZ, f);
        if (len < 0)
+       {
+           FcConfigMessage (&parse, FcSevereError, "failed reading config file");
            goto bail3;
+       }
        if (!XML_ParseBuffer (p, len, len == 0))
+       {
+           FcConfigMessage (&parse, FcSevereError, "%s", 
+                          XML_ErrorString (XML_GetErrorCode (p)));
            goto bail3;
+       }
     } while (len != 0);
     error = parse.error;
 bail3:
@@ -1610,9 +1936,9 @@ bail0:
     if (error && complain)
     {
        if (name)
-           FcConfigError (0, "Cannot load config file \"%s\"", name);
+           FcConfigMessage (0, FcSevereError, "Cannot load config file \"%s\"", name);
        else
-           FcConfigError (0, "Cannot load default config file");
+           FcConfigMessage (0, FcSevereError, "Cannot load default config file");
        return FcFalse;
     }
     return FcTrue;