]> git.wh0rd.org - fontconfig.git/blobdiff - src/fcxml.c
Whitespace
[fontconfig.git] / src / fcxml.c
index e7725cc5e966a47f22bba72456148d0b409e1fd2..0952b0b4424f30c967cdc975621945131d214fea 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $XFree86: $
+ * fontconfig/src/fcxml.c
  *
- * 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
  * 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
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <stdarg.h>
 #include "fcint.h"
+#include <fcntl.h>
+#include <stdarg.h>
+#include <dirent.h>
 
-static xmlParserInputPtr
-FcEntityLoader (const char *url, const char *id, xmlParserCtxtPtr ctxt)
-{
-    xmlParserInputPtr  ret;
-    FcChar8            *file;
+#ifdef ENABLE_LIBXML2
 
-    file = FcConfigFilename ((FcChar8 *) url);
-    if (!file)
-       return 0;
-    ret = xmlNewInputFromFile (ctxt, (char *) file);
-    free (file);
-    return ret;
-}
+#include <libxml/parser.h>
 
-xmlDocPtr
-FcConfigLoad (const FcChar8 *file)
-{
-    xmlDocPtr              doc;
-    xmlExternalEntityLoader previous;
+#define XML_Char                       xmlChar
+#define XML_Parser                     xmlParserCtxtPtr
+#define XML_ParserFree                 xmlFreeParserCtxt
+#define XML_GetCurrentLineNumber       xmlSAX2GetLineNumber
+#define XML_GetErrorCode               xmlCtxtGetLastError
+#define XML_ErrorString(Error)         (Error)->message
 
-    previous = xmlGetExternalEntityLoader ();
-    xmlSetExternalEntityLoader (FcEntityLoader);
-    doc = xmlParseFile ((char *) file);
-    xmlSetExternalEntityLoader (previous);
-    return doc;
-}
+#else /* ENABLE_LIBXML2 */
 
-#if 0
-int
-FcConfigSave (char *file, xmlDocPtr doc)
-{
-}
+#ifndef HAVE_XMLPARSE_H
+#define HAVE_XMLPARSE_H 0
 #endif
 
-FcTest *
-FcTestCreate (FcQual qual, const char *field, FcOp compare, FcExpr *expr)
-{
-    FcTest     *test = (FcTest *) malloc (sizeof (FcTest));;
+#if HAVE_XMLPARSE_H
+#include <xmlparse.h>
+#else
+#include <expat.h>
+#endif
 
-    if (test)
-    {
-       test->next = 0;
-       test->qual = qual;
-       test->field = (char *) FcStrCopy ((FcChar8 *) field);
-       test->op = compare;
-       test->expr = expr;
-    }
-    return test;
-}
+#endif /* ENABLE_LIBXML2 */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#define STRICT
+#include <windows.h>
+#undef STRICT
+#include <mbstring.h>
+#endif
+
+static void
+FcExprDestroy (FcExpr *e);
 
 void
 FcTestDestroy (FcTest *test)
@@ -81,15 +69,14 @@ FcTestDestroy (FcTest *test)
     if (test->next)
        FcTestDestroy (test->next);
     FcExprDestroy (test->expr);
-    FcStrFree ((FcChar8 *) test->field);
+    FcMemFree (FC_MEM_TEST, sizeof (FcTest));
     free (test);
 }
 
-FcExpr *
-FcExprCreateInteger (int i)
+static FcExpr *
+FcExprCreateInteger (FcConfig *config, int i)
 {
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
+    FcExpr *e = FcConfigAllocExpr (config);
     if (e)
     {
        e->op = FcOpInteger;
@@ -98,11 +85,10 @@ FcExprCreateInteger (int i)
     return e;
 }
 
-FcExpr *
-FcExprCreateDouble (double d)
+static FcExpr *
+FcExprCreateDouble (FcConfig *config, double d)
 {
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
+    FcExpr *e = FcConfigAllocExpr (config);
     if (e)
     {
        e->op = FcOpDouble;
@@ -111,24 +97,22 @@ FcExprCreateDouble (double d)
     return e;
 }
 
-FcExpr *
-FcExprCreateString (const FcChar8 *s)
+static FcExpr *
+FcExprCreateString (FcConfig *config, const FcChar8 *s)
 {
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
+    FcExpr *e = FcConfigAllocExpr (config);
     if (e)
     {
        e->op = FcOpString;
-       e->u.sval = FcStrCopy (s);
+       e->u.sval = FcStrStaticName (s);
     }
     return e;
 }
 
-FcExpr *
-FcExprCreateMatrix (const FcMatrix *m)
+static FcExpr *
+FcExprCreateMatrix (FcConfig *config, const FcMatrix *m)
 {
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
+    FcExpr *e = FcConfigAllocExpr (config);
     if (e)
     {
        e->op = FcOpMatrix;
@@ -137,11 +121,10 @@ FcExprCreateMatrix (const FcMatrix *m)
     return e;
 }
 
-FcExpr *
-FcExprCreateBool (FcBool b)
+static FcExpr *
+FcExprCreateBool (FcConfig *config, FcBool b)
 {
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
+    FcExpr *e = FcConfigAllocExpr (config);
     if (e)
     {
        e->op = FcOpBool;
@@ -150,49 +133,34 @@ FcExprCreateBool (FcBool b)
     return e;
 }
 
-FcExpr *
-FcExprCreateNil (void)
-{
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
-    if (e)
-    {
-       e->op = FcOpNil;
-    }
-    return e;
-}
-
-FcExpr *
-FcExprCreateField (const char *field)
+static FcExpr *
+FcExprCreateField (FcConfig *config, const char *field)
 {
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
+    FcExpr *e = FcConfigAllocExpr (config);
     if (e)
     {
        e->op = FcOpField;
-       e->u.field = (char *) FcStrCopy ((FcChar8 *) field);
+       e->u.object = FcObjectFromName (field);
     }
     return e;
 }
 
-FcExpr *
-FcExprCreateConst (const FcChar8 *constant)
+static FcExpr *
+FcExprCreateConst (FcConfig *config, const FcChar8 *constant)
 {
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
+    FcExpr *e = FcConfigAllocExpr (config);
     if (e)
     {
        e->op = FcOpConst;
-       e->u.constant = FcStrCopy (constant);
+       e->u.constant = FcStrStaticName (constant);
     }
     return e;
 }
 
-FcExpr *
-FcExprCreateOp (FcExpr *left, FcOp op, FcExpr *right)
+static FcExpr *
+FcExprCreateOp (FcConfig *config, FcExpr *left, FcOp op, FcExpr *right)
 {
-    FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
-
+    FcExpr *e = FcConfigAllocExpr (config);
     if (e)
     {
        e->op = op;
@@ -202,16 +170,17 @@ FcExprCreateOp (FcExpr *left, FcOp op, FcExpr *right)
     return e;
 }
 
-void
+static void
 FcExprDestroy (FcExpr *e)
 {
+    if (!e)
+       return;
     switch (e->op) {
     case FcOpInteger:
        break;
     case FcOpDouble:
        break;
     case FcOpString:
-       FcStrFree (e->u.sval);
        break;
     case FcOpMatrix:
        FcMatrixFree (e->u.mval);
@@ -222,10 +191,8 @@ FcExprDestroy (FcExpr *e)
     case FcOpBool:
        break;
     case FcOpField:
-       FcStrFree ((FcChar8 *) e->u.field);
        break;
     case FcOpConst:
-       FcStrFree (e->u.constant);
        break;
     case FcOpAssign:
     case FcOpAssignReplace:
@@ -237,12 +204,14 @@ FcExprDestroy (FcExpr *e)
     case FcOpOr:
     case FcOpAnd:
     case FcOpEqual:
-    case FcOpContains:
     case FcOpNotEqual:
     case FcOpLess:
     case FcOpLessEqual:
     case FcOpMore:
     case FcOpMoreEqual:
+    case FcOpContains:
+    case FcOpListing:
+    case FcOpNotContains:
     case FcOpPlus:
     case FcOpMinus:
     case FcOpTimes:
@@ -252,28 +221,18 @@ 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;
     }
-    free (e);
-}
 
-FcEdit *
-FcEditCreate (const char *field, FcOp op, FcExpr *expr)
-{
-    FcEdit *e = (FcEdit *) malloc (sizeof (FcEdit));
-
-    if (e)
-    {
-       e->next = 0;
-       e->field = field;   /* already saved in grammar */
-       e->op = op;
-       e->expr = expr;
-    }
-    return e;
+    e->op = FcOpNil;
 }
 
 void
@@ -281,755 +240,2331 @@ FcEditDestroy (FcEdit *e)
 {
     if (e->next)
        FcEditDestroy (e->next);
-    FcStrFree ((FcChar8 *) e->field);
     if (e->expr)
        FcExprDestroy (e->expr);
+    free (e);
 }
 
-char *
-FcConfigSaveField (const char *field)
+typedef enum _FcElement {
+    FcElementNone,
+    FcElementFontconfig,
+    FcElementDir,
+    FcElementCacheDir,
+    FcElementCache,
+    FcElementInclude,
+    FcElementConfig,
+    FcElementMatch,
+    FcElementAlias,
+       
+    FcElementBlank,
+    FcElementRescan,
+
+    FcElementPrefer,
+    FcElementAccept,
+    FcElementDefault,
+    FcElementFamily,
+
+    FcElementSelectfont,
+    FcElementAcceptfont,
+    FcElementRejectfont,
+    FcElementGlob,
+    FcElementPattern,
+    FcElementPatelt,
+
+    FcElementTest,
+    FcElementEdit,
+    FcElementInt,
+    FcElementDouble,
+    FcElementString,
+    FcElementMatrix,
+    FcElementBool,
+    FcElementCharset,
+    FcElementName,
+    FcElementConst,
+    FcElementOr,
+    FcElementAnd,
+    FcElementEq,
+    FcElementNotEq,
+    FcElementLess,
+    FcElementLessEq,
+    FcElementMore,
+    FcElementMoreEq,
+    FcElementContains,
+    FcElementNotContains,
+    FcElementPlus,
+    FcElementMinus,
+    FcElementTimes,
+    FcElementDivide,
+    FcElementNot,
+    FcElementIf,
+    FcElementFloor,
+    FcElementCeil,
+    FcElementRound,
+    FcElementTrunc,
+    FcElementUnknown
+} FcElement;
+
+static const struct {
+    const char  name[16];
+    FcElement   element;
+} fcElementMap[] = {
+    { "fontconfig",    FcElementFontconfig },
+    { "dir",           FcElementDir },
+    { "cachedir",      FcElementCacheDir },
+    { "cache",         FcElementCache },
+    { "include",       FcElementInclude },
+    { "config",                FcElementConfig },
+    { "match",         FcElementMatch },
+    { "alias",         FcElementAlias },
+
+    { "blank",         FcElementBlank },
+    { "rescan",                FcElementRescan },
+
+    { "prefer",                FcElementPrefer },
+    { "accept",                FcElementAccept },
+    { "default",       FcElementDefault },
+    { "family",                FcElementFamily },
+
+    { "selectfont",    FcElementSelectfont },
+    { "acceptfont",    FcElementAcceptfont },
+    { "rejectfont",    FcElementRejectfont },
+    { "glob",          FcElementGlob },
+    { "pattern",       FcElementPattern },
+    { "patelt",                FcElementPatelt },
+
+    { "test",          FcElementTest },
+    { "edit",          FcElementEdit },
+    { "int",           FcElementInt },
+    { "double",                FcElementDouble },
+    { "string",                FcElementString },
+    { "matrix",                FcElementMatrix },
+    { "bool",          FcElementBool },
+    { "charset",       FcElementCharset },
+    { "name",          FcElementName },
+    { "const",         FcElementConst },
+    { "or",            FcElementOr },
+    { "and",           FcElementAnd },
+    { "eq",            FcElementEq },
+    { "not_eq",                FcElementNotEq },
+    { "less",          FcElementLess },
+    { "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 },
+};
+#define NUM_ELEMENT_MAPS (int) (sizeof fcElementMap / sizeof fcElementMap[0])
+
+static FcElement
+FcElementMap (const XML_Char *name)
 {
-    return (char *) FcStrCopy ((FcChar8 *) field);
+
+    int            i;
+    for (i = 0; i < NUM_ELEMENT_MAPS; i++)
+       if (!strcmp ((char *) name, fcElementMap[i].name))
+           return fcElementMap[i].element;
+    return FcElementUnknown;
 }
 
+typedef struct _FcPStack {
+    struct _FcPStack   *prev;
+    FcElement          element;
+    FcChar8            **attr;
+    FcStrBuf           str;
+    FcChar8            *attr_buf_static[16];
+} FcPStack;
+    
+typedef enum _FcVStackTag {
+    FcVStackNone,
+
+    FcVStackString,
+    FcVStackFamily,
+    FcVStackField,
+    FcVStackConstant,
+    FcVStackGlob,
+    FcVStackPattern,
+    
+    FcVStackPrefer,
+    FcVStackAccept,
+    FcVStackDefault,
+    
+    FcVStackInteger,
+    FcVStackDouble,
+    FcVStackMatrix,
+    FcVStackBool,
+    
+    FcVStackTest,
+    FcVStackExpr,
+    FcVStackEdit
+} FcVStackTag;
+
+typedef struct _FcVStack {
+    struct _FcVStack   *prev;
+    FcPStack           *pstack;        /* related parse element */
+    FcVStackTag                tag;
+    union {
+       FcChar8         *string;
+
+       int             integer;
+       double          _double;
+       FcMatrix        *matrix;
+       FcBool          bool_;
+
+       FcTest          *test;
+       FcQual          qual;
+       FcOp            op;
+       FcExpr          *expr;
+       FcEdit          *edit;
+
+       FcPattern       *pattern;
+    } u;
+} FcVStack;
+
+typedef struct _FcConfigParse {
+    FcPStack       *pstack;
+    FcVStack       *vstack;
+    FcBool         error;
+    const FcChar8   *name;
+    FcConfig       *config;
+    XML_Parser     parser;
+    int             pstack_static_used;
+    FcPStack        pstack_static[8];
+    int             vstack_static_used;
+    FcVStack        vstack_static[64];
+} FcConfigParse;
+
+typedef enum _FcConfigSeverity {
+    FcSevereInfo, FcSevereWarning, FcSevereError
+} FcConfigSeverity;
+
 static void
-FcConfigParseError (char *fmt, ...)
+FcConfigMessage (FcConfigParse *parse, FcConfigSeverity severe, const char *fmt, ...)
 {
+    const char *s = "unknown";
     va_list    args;
 
     va_start (args, fmt);
-    fprintf (stderr, "font configuration error: ");
+
+    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 %s: \"%s\", line %d: ", s,
+                    parse->name, (int)XML_GetCurrentLineNumber (parse->parser));
+       else
+           fprintf (stderr, "Fontconfig %s: line %d: ", s,
+                    (int)XML_GetCurrentLineNumber (parse->parser));
+       if (severe >= FcSevereError)
+           parse->error = FcTrue;
+    }
+    else
+       fprintf (stderr, "Fontconfig %s: ", s);
     vfprintf (stderr, fmt, args);
     fprintf (stderr, "\n");
     va_end (args);
 }
 
-static xmlChar *
-FcConfigContent (xmlDocPtr    doc,
-                xmlNodePtr   node)
+
+static const char *
+FcTypeName (FcType type)
 {
-    xmlChar        *content;
-    
-    content = xmlNodeListGetString (doc, node->children, 1);
-    if (!content)
+    switch (type) {
+    case FcTypeVoid:
+       return "void";
+    case FcTypeInteger:
+    case FcTypeDouble:
+       return "number";
+    case FcTypeString:
+       return "string";
+    case FcTypeBool:
+       return "bool";
+    case FcTypeMatrix:
+       return "matrix";
+    case FcTypeCharSet:
+       return "charset";
+    case FcTypeFTFace:
+       return "FT_Face";
+    case FcTypeLangSet:
+       return "langset";
+    default:
+       return "unknown";
+    }
+}
+
+static void
+FcTypecheckValue (FcConfigParse *parse, FcType value, FcType type)
+{
+    if (value == FcTypeInteger)
+       value = FcTypeDouble;
+    if (type == FcTypeInteger)
+       type = FcTypeDouble;
+    if (value != type)
     {
-       FcConfigParseError ("<%s> must have content",
-                           node->name);
-       return FcFalse;
+       if ((value == FcTypeLangSet && type == FcTypeString) ||
+           (value == FcTypeString && type == FcTypeLangSet))
+           return;
+       if (type == (FcType) -1)
+           return;
+       FcConfigMessage (parse, FcSevereWarning, "saw %s, expected %s",
+                        FcTypeName (value), FcTypeName (type));
     }
-    return content;
 }
 
-static xmlChar *
-FcConfigAttr (xmlDocPtr            doc,
-             xmlAttrPtr    attr)
+static void
+FcTypecheckExpr (FcConfigParse *parse, FcExpr *expr, FcType type)
 {
-    xmlChar        *content;
+    const FcObjectType *o;
+    const FcConstant   *c;
     
-    content = xmlNodeListGetString (doc, attr->children, 1);
-    if (!content)
+    /* If parsing the expression failed, some nodes may be NULL */
+    if (!expr)
+       return;
+
+    switch (expr->op) {
+    case FcOpInteger:
+    case FcOpDouble:
+       FcTypecheckValue (parse, FcTypeDouble, type);
+       break;
+    case FcOpString:
+       FcTypecheckValue (parse, FcTypeString, type);
+       break;
+    case FcOpMatrix:
+       FcTypecheckValue (parse, FcTypeMatrix, type);
+       break;
+    case FcOpBool:
+       FcTypecheckValue (parse, FcTypeBool, type);
+       break;
+    case FcOpCharSet:
+       FcTypecheckValue (parse, FcTypeCharSet, type);
+       break;
+    case FcOpNil:
+       break;
+    case FcOpField:
+       o = FcNameGetObjectType (FcObjectName (expr->u.object));
+       if (o)
+           FcTypecheckValue (parse, o->type, type);
+       break;
+    case FcOpConst:
+       c = FcNameGetConstant (expr->u.constant);
+       if (c)
+       {
+           o = FcNameGetObjectType (c->object);
+           if (o)
+               FcTypecheckValue (parse, o->type, type);
+       }
+        else 
+            FcConfigMessage (parse, FcSevereWarning, 
+                             "invalid constant used : %s",
+                             expr->u.constant);
+       break;
+    case FcOpQuest:
+       FcTypecheckExpr (parse, expr->u.tree.left, FcTypeBool);
+       FcTypecheckExpr (parse, expr->u.tree.right->u.tree.left, type);
+       FcTypecheckExpr (parse, expr->u.tree.right->u.tree.right, type);
+       break;
+    case FcOpAssign:
+    case FcOpAssignReplace:
+       break;
+    case FcOpEqual:
+    case FcOpNotEqual:
+    case FcOpLess:
+    case FcOpLessEqual:
+    case FcOpMore:
+    case FcOpMoreEqual:
+    case FcOpContains:
+    case FcOpNotContains:
+    case FcOpListing:
+       FcTypecheckValue (parse, FcTypeBool, type);
+       break;
+    case FcOpComma:
+    case FcOpOr:
+    case FcOpAnd:
+    case FcOpPlus:
+    case FcOpMinus:
+    case FcOpTimes:
+    case FcOpDivide:
+       FcTypecheckExpr (parse, expr->u.tree.left, type);
+       FcTypecheckExpr (parse, expr->u.tree.right, type);
+       break;
+    case FcOpNot:
+       FcTypecheckValue (parse, FcTypeBool, type);
+       FcTypecheckExpr (parse, expr->u.tree.left, FcTypeBool);
+       break;
+    case FcOpFloor:
+    case FcOpCeil:
+    case FcOpRound:
+    case FcOpTrunc:
+       FcTypecheckValue (parse, FcTypeDouble, type);
+       FcTypecheckExpr (parse, expr->u.tree.left, FcTypeDouble);
+       break;
+    default:
+       break;
+    }
+}
+
+static FcTest *
+FcTestCreate (FcConfigParse *parse,
+             FcMatchKind   kind, 
+             FcQual        qual,
+             const FcChar8 *field,
+             FcOp          compare,
+             FcExpr        *expr)
+{
+    FcTest     *test = (FcTest *) malloc (sizeof (FcTest));
+
+    if (test)
     {
-       FcConfigParseError ("attribute %s must have a value",
-                           attr->name);
-       return FcFalse;
+       const FcObjectType      *o;
+       
+       FcMemAlloc (FC_MEM_TEST, sizeof (FcTest));
+       test->next = 0;
+       test->kind = kind;
+       test->qual = qual;
+       test->object = FcObjectFromName ((const char *) field);
+       test->op = compare;
+       test->expr = expr;
+       o = FcNameGetObjectType (FcObjectName (test->object));
+       if (o)
+           FcTypecheckExpr (parse, expr, o->type);
     }
-    return content;
+    return test;
 }
 
-static struct {
-    char    *name;
-    FcOp    op;
-} fcOps[] = {
-    { "int",           FcOpInteger         },
-    { "double",                FcOpDouble          },
-    { "string",                FcOpString          },
-    { "matrix",                FcOpMatrix          },
-    { "bool",          FcOpBool            },
-    { "charset",       FcOpCharSet         },
-    { "name",          FcOpField           },
-    { "const",         FcOpConst           },
-    { "field",         FcOpField           },
-    { "if",            FcOpQuest           },
-    { "or",            FcOpOr              },
-    { "and",           FcOpAnd             },
-    { "eq",            FcOpEqual           },
-    { "not_eq",                FcOpNotEqual        },
-    { "less",          FcOpLess            },
-    { "less_eq",       FcOpLessEqual       },
-    { "more",          FcOpMore            },
-    { "more_eq",       FcOpMoreEqual       },
-    { "plus",          FcOpPlus            },
-    { "minus",         FcOpMinus           },
-    { "times",         FcOpTimes           },
-    { "divide",                FcOpDivide          },
-    { "not",           FcOpNot             },
-    { "assign",                FcOpAssign          },
-    { "assign_replace",        FcOpAssignReplace   },
-    { "prepend",       FcOpPrepend         },
-    { "prepend_first", FcOpPrependFirst    },
-    { "append",                FcOpAppend          },
-    { "append_last",   FcOpAppendLast      },
-};
+static FcEdit *
+FcEditCreate (FcConfigParse    *parse,
+             FcObject          object,
+             FcOp              op,
+             FcExpr            *expr,
+             FcValueBinding    binding)
+{
+    FcEdit *e = (FcEdit *) malloc (sizeof (FcEdit));
 
-#define NUM_OPS (sizeof fcOps / sizeof fcOps[0])
+    if (e)
+    {
+       const FcObjectType      *o;
 
-static FcOp
-FcConfigLexOp (const xmlChar *op)
+       e->next = 0;
+       e->object = object;
+       e->op = op;
+       e->expr = expr;
+       e->binding = binding;
+       o = FcNameGetObjectType (FcObjectName (e->object));
+       if (o)
+           FcTypecheckExpr (parse, expr, o->type);
+    }
+    return e;
+}
+
+static FcVStack *
+FcVStackCreateAndPush (FcConfigParse *parse)
 {
-    int        i;
+    FcVStack    *new;
 
-    for (i = 0; i < NUM_OPS; i++)
-       if (!strcmp (op, fcOps[i].name)) return fcOps[i].op;
-    return FcOpInvalid;
+    if (parse->vstack_static_used < sizeof (parse->vstack_static) / sizeof (parse->vstack_static[0]))
+       new = &parse->vstack_static[parse->vstack_static_used++];
+    else
+    {
+       new = malloc (sizeof (FcVStack));
+       if (!new)
+           return 0;
+       FcMemAlloc (FC_MEM_VSTACK, sizeof (FcVStack));
+    }
+    new->tag = FcVStackNone;
+    new->prev = 0;
+
+    new->prev = parse->vstack;
+    new->pstack = parse->pstack ? parse->pstack->prev : 0;
+    parse->vstack = new;
+
+    return new;
 }
 
 static FcBool
-FcConfigLexBool (const xmlChar *bool)
+FcVStackPushString (FcConfigParse *parse, FcVStackTag tag, FcChar8 *string)
 {
-    if (*bool == 't' || *bool == 'T')
-       return FcTrue;
-    if (*bool == 'y' || *bool == 'Y')
-       return FcTrue;
-    if (*bool == '1')
-       return FcTrue;
-    return FcFalse;
+    FcVStack    *vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
+       return FcFalse;
+    vstack->u.string = string;
+    vstack->tag = tag;
+    return FcTrue;
 }
 
 static FcBool
-FcConfigParseDir (FcConfig     *config,
-                 xmlDocPtr     doc,
-                 xmlNodePtr    dir)
+FcVStackPushInteger (FcConfigParse *parse, int integer)
 {
-    xmlChar *content = FcConfigContent (doc, dir);
-
-    if (!content)
+    FcVStack    *vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
        return FcFalse;
-    return FcConfigAddDir (config, (FcChar8 *) content);
+    vstack->u.integer = integer;
+    vstack->tag = FcVStackInteger;
+    return FcTrue;
 }
 
 static FcBool
-FcConfigParseCache (FcConfig   *config,
-                   xmlDocPtr   doc,
-                   xmlNodePtr  dir)
+FcVStackPushDouble (FcConfigParse *parse, double _double)
 {
-    xmlChar *content = FcConfigContent (doc, dir);
-
-    if (!content)
+    FcVStack    *vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
        return FcFalse;
-    return FcConfigSetCache (config, (FcChar8 *) content);
+    vstack->u._double = _double;
+    vstack->tag = FcVStackDouble;
+    return FcTrue;
 }
 
 static FcBool
-FcConfigParseInclude (FcConfig     *config,
-                     xmlDocPtr     doc,
-                     xmlNodePtr    inc)
+FcVStackPushMatrix (FcConfigParse *parse, FcMatrix *matrix)
 {
-    xmlChar *content = FcConfigContent (doc, inc);
-    xmlAttr    *attr;
-    FcBool     complain = FcTrue;
+    FcVStack    *vstack;
+    matrix = FcMatrixCopy (matrix);
+    if (!matrix)
+       return FcFalse;
+    vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
+       return FcFalse;
+    vstack->u.matrix = matrix;
+    vstack->tag = FcVStackMatrix;
+    return FcTrue;
+}
 
-    if (!content)
+static FcBool
+FcVStackPushBool (FcConfigParse *parse, FcBool bool_)
+{
+    FcVStack    *vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
        return FcFalse;
-    
-    for (attr = inc->properties; attr; attr = attr->next)
-    {
-       if (attr->type != XML_ATTRIBUTE_NODE)
-           continue;
-       if (!strcmp (attr->name, "ignore_missing"))
-           complain = !FcConfigLexBool (FcConfigAttr (doc, attr));
-    }
-    return FcConfigParseAndLoad (config, (FcChar8 *) content, complain);
+    vstack->u.bool_ = bool_;
+    vstack->tag = FcVStackBool;
+    return FcTrue;
 }
 
 static FcBool
-FcConfigParseBlank (FcConfig       *config,
-                   xmlDocPtr       doc,
-                   xmlNodePtr      blank)
+FcVStackPushTest (FcConfigParse *parse, FcTest *test)
 {
-    xmlNode    *node;
-    FcChar32   ucs4;
+    FcVStack    *vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
+       return FcFalse;
+    vstack->u.test = test;
+    vstack->tag = FcVStackTest;
+    return FcTrue;
+}
 
-    for (node = blank->children; node; node = node->next)
-    {
-       if (node->type != XML_ELEMENT_NODE)
-           continue;
-       if (!strcmp (node->name, "int"))
-       {
-           ucs4 = (FcChar32) strtol ((char *) FcConfigContent (doc, node), 0, 0);
-           if (!config->blanks)
-           {
-               config->blanks = FcBlanksCreate ();
-               if (!config->blanks)
-                   break;
-           }
-           if (!FcBlanksAdd (config->blanks, ucs4))
-               break;
-       }
-    }
-    if (node)
+static FcBool
+FcVStackPushExpr (FcConfigParse *parse, FcVStackTag tag, FcExpr *expr)
+{
+    FcVStack    *vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
        return FcFalse;
+    vstack->u.expr = expr;
+    vstack->tag = tag;
     return FcTrue;
 }
 
 static FcBool
-FcConfigParseConfig (FcConfig      *config,
-                    xmlDocPtr      doc,
-                    xmlNodePtr     cfg)
+FcVStackPushEdit (FcConfigParse *parse, FcEdit *edit)
 {
-    xmlNode    *node;
+    FcVStack    *vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
+       return FcFalse;
+    vstack->u.edit = edit;
+    vstack->tag = FcVStackEdit;
+    return FcTrue;
+}
 
-    for (node = cfg->children; node; node = node->next)
-    {
-       if (node->type != XML_ELEMENT_NODE)
-           continue;
-       if (!strcmp (node->name, "blank"))
-       {
-           if (!FcConfigParseBlank (config, doc, node))
-               break;
-       }
-    }
-    if (node)
+static FcBool
+FcVStackPushPattern (FcConfigParse *parse, FcPattern *pattern)
+{
+    FcVStack    *vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
        return FcFalse;
+    vstack->u.pattern = pattern;
+    vstack->tag = FcVStackPattern;
     return FcTrue;
 }
 
-static FcMatrix *
-FcConfigParseMatrix (xmlDocPtr doc,
-                    xmlNodePtr node)
+static FcVStack *
+FcVStackFetch (FcConfigParse *parse, int off)
 {
-    static FcMatrix m;
-    enum { m_xx, m_xy, m_yx, m_yy, m_done } matrix_state = m_xx;
-    double  v;
-    xmlChar *text;
-    
-    FcMatrixInit (&m);
+    FcVStack    *vstack;
 
-    for (; node; node = node->next)
-    {
-       if (node->type != XML_ELEMENT_NODE)
-           continue;
-       if (strcmp (node->name, "double"))
-           continue;
-       text = FcConfigContent (doc, node);
-       if (!text)
-           continue;
-       v = strtod ((char *) text, 0);
-       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++;
-    }
-        
-    return &m;
+    for (vstack = parse->vstack; vstack && off-- > 0; vstack = vstack->prev);
+    return vstack;
 }
 
-static FcExpr *
-FcConfigParseExpr (xmlDocPtr   doc,
-                  xmlNodePtr   expr)
+static FcVStack *
+FcVStackPeek (FcConfigParse *parse)
 {
-    FcOp       op = FcConfigLexOp (expr->name);
-    xmlNodePtr node;
-    FcExpr     *l = 0, *e = 0, *r = 0, *c = 0;
+    FcVStack   *vstack = parse->vstack;
 
-    switch (op) {
-    case FcOpInteger:
-       l = FcExprCreateInteger (strtol ((char *) FcConfigContent (doc, expr), 0, 0));
+    return vstack && vstack->pstack == parse->pstack ? vstack : 0;
+}
+
+static void
+FcVStackPopAndDestroy (FcConfigParse *parse)
+{
+    FcVStack   *vstack = parse->vstack;
+    
+    if (!vstack || vstack->pstack != parse->pstack)
+       return;
+
+    parse->vstack = vstack->prev;
+
+    switch (vstack->tag) {
+    case FcVStackNone:
        break;
-    case FcOpDouble:
-       l = FcExprCreateDouble (strtod ((char *) FcConfigContent (doc, expr), 0));
+    case FcVStackFamily:
        break;
-    case FcOpString:
-       l = FcExprCreateString ((FcChar8 *) FcConfigContent (doc, expr));
+    case FcVStackString:
+    case FcVStackField:
+    case FcVStackConstant:
+    case FcVStackGlob:
+       FcStrFree (vstack->u.string);
        break;
-    case FcOpMatrix:
-       l = FcExprCreateMatrix (FcConfigParseMatrix (doc, expr));
+    case FcVStackPattern:
+       FcPatternDestroy (vstack->u.pattern);
        break;
-    case FcOpBool:
-       l = FcExprCreateBool (FcConfigLexBool(FcConfigContent (doc, expr)));
+    case FcVStackInteger:
+    case FcVStackDouble:
        break;
-    case FcOpCharSet:
-       /* not sure what to do here yet */
+    case FcVStackMatrix:
+       FcMatrixFree (vstack->u.matrix);
        break;
-    case FcOpField:
-       l = FcExprCreateField ((char *) FcConfigContent (doc, expr));
+    case FcVStackBool:
        break;
-    case FcOpConst:
-       l = FcExprCreateConst ((FcChar8 *) FcConfigContent (doc, expr));
+    case FcVStackTest:
+       FcTestDestroy (vstack->u.test);
        break;
-    case FcOpQuest:
-       for (node = expr->children; node; node = node->next)
-       {
-           if (node->type != XML_ELEMENT_NODE)
-               continue;
-           e = FcConfigParseExpr (doc, node);
-           if (!e)
-               break;
-           if (!l)
-               l = e;
-           else if (!c)
-               c = e;
-           else if (!r)
-               r = e;
-           else
-               FcExprDestroy (e);
-       }
-       e = 0;
-       if (!node && l && c && r)
-       {
-           e = FcExprCreateOp (c, FcOpQuest, r);
-           if (e)
-           {
-               r = e;
-               c = 0;
-               e = FcExprCreateOp (l, FcOpQuest, r);
-           }
-           if (!e)
-               node = expr->children;
-       }
-       if (node || !l || !c || !r || !e)
+    case FcVStackExpr:
+    case FcVStackPrefer:
+    case FcVStackAccept:
+    case FcVStackDefault:
+       FcExprDestroy (vstack->u.expr);
+       break;
+    case FcVStackEdit:
+       FcEditDestroy (vstack->u.edit);
+       break;
+    }
+
+    if (vstack == &parse->vstack_static[parse->vstack_static_used - 1])
+       parse->vstack_static_used--;
+    else
+    {
+       FcMemFree (FC_MEM_VSTACK, sizeof (FcVStack));
+       free (vstack);
+    }
+}
+
+static void
+FcVStackClear (FcConfigParse *parse)
+{
+    while (FcVStackPeek (parse))
+       FcVStackPopAndDestroy (parse);
+}
+
+static int
+FcVStackElements (FcConfigParse *parse)
+{
+    int                h = 0;
+    FcVStack   *vstack = parse->vstack;
+    while (vstack && vstack->pstack == parse->pstack)
+    {
+       h++;
+       vstack = vstack->prev;
+    }
+    return h;
+}
+
+static FcChar8 **
+FcConfigSaveAttr (const XML_Char **attr, FcChar8 **buf, int size_bytes)
+{
+    int                slen;
+    int                i;
+    FcChar8    **new;
+    FcChar8    *s;
+
+    if (!attr)
+       return 0;
+    slen = 0;
+    for (i = 0; attr[i]; i++)
+       slen += strlen ((char *) attr[i]) + 1;
+    if (i == 0)
+       return 0;
+    slen += (i + 1) * sizeof (FcChar8 *);
+    if (slen <= size_bytes)
+       new = buf;
+    else
+    {
+       new = malloc (slen);
+       if (!new)
        {
-           if (l)
-               FcExprDestroy (l);
-           if (c)
-               FcExprDestroy (c);
-           if (r)
-               FcExprDestroy (r);
+           FcConfigMessage (0, FcSevereError, "out of memory");
            return 0;
        }
-       break;
-    default:
-       for (node = expr->children; node; node = node->next)
+       FcMemAlloc (FC_MEM_ATTR, 1);    /* size is too expensive */
+    }
+    s = (FcChar8 *) (new + (i + 1));
+    for (i = 0; attr[i]; i++)
+    {
+       new[i] = s;
+       strcpy ((char *) s, (char *) attr[i]);
+       s += strlen ((char *) s) + 1;
+    }
+    new[i] = 0;
+    return new;
+}
+
+static FcBool
+FcPStackPush (FcConfigParse *parse, FcElement element, const XML_Char **attr)
+{
+    FcPStack   *new;
+
+    if (parse->pstack_static_used < sizeof (parse->pstack_static) / sizeof (parse->pstack_static[0]))
+       new = &parse->pstack_static[parse->pstack_static_used++];
+    else
+    {
+       new = malloc (sizeof (FcPStack));
+       if (!new)
+           return FcFalse;
+       FcMemAlloc (FC_MEM_PSTACK, sizeof (FcPStack));
+    }
+
+    new->prev = parse->pstack;
+    new->element = element;
+    new->attr = FcConfigSaveAttr (attr, new->attr_buf_static, sizeof (new->attr_buf_static));
+    FcStrBufInit (&new->str, 0, 0);
+    parse->pstack = new;
+    return FcTrue;
+}
+
+static FcBool
+FcPStackPop (FcConfigParse *parse)
+{
+    FcPStack   *old;
+    
+    if (!parse->pstack) 
+    {
+       FcConfigMessage (parse, FcSevereError, "mismatching element");
+       return FcFalse;
+    }
+    FcVStackClear (parse);
+    old = parse->pstack;
+    parse->pstack = old->prev;
+    FcStrBufDestroy (&old->str);
+    if (old->attr && old->attr != old->attr_buf_static)
+    {
+       FcMemFree (FC_MEM_ATTR, 1); /* size is to expensive */
+       free (old->attr);
+    }
+
+    if (old == &parse->pstack_static[parse->pstack_static_used - 1])
+       parse->pstack_static_used--;
+    else
+    {
+       FcMemFree (FC_MEM_PSTACK, sizeof (FcPStack));
+       free (old);
+    }
+    return FcTrue;
+}
+
+static FcBool
+FcConfigInit (FcConfigParse *parse, const FcChar8 *name, FcConfig *config, XML_Parser parser)
+{
+    parse->pstack = 0;
+    parse->pstack_static_used = 0;
+    parse->vstack = 0;
+    parse->vstack_static_used = 0;
+    parse->error = FcFalse;
+    parse->name = name;
+    parse->config = config;
+    parse->parser = parser;
+    return FcTrue;
+}
+
+static void
+FcConfigCleanup (FcConfigParse *parse)
+{
+    while (parse->pstack)
+       FcPStackPop (parse);
+}
+
+static const FcChar8 *
+FcConfigGetAttribute (FcConfigParse *parse, const char *attr)
+{
+    FcChar8 **attrs;
+    if (!parse->pstack)
+       return 0;
+
+    attrs = parse->pstack->attr;
+    if (!attrs)
+        return 0;
+
+    while (*attrs)
+    {
+       if (!strcmp ((char *) *attrs, attr))
+           return attrs[1];
+       attrs += 2;
+    }
+    return 0;
+}
+
+static void
+FcStartElement(void *userData, const XML_Char *name, const XML_Char **attr)
+{
+    FcConfigParse   *parse = userData;
+    FcElement      element;
+    
+    element = FcElementMap (name);
+    if (element == FcElementUnknown)
+       FcConfigMessage (parse, FcSevereWarning, "unknown element \"%s\"", name);
+    
+    if (!FcPStackPush (parse, element, attr))
+    {
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
+    }
+    return;
+}
+
+static void
+FcParseBlank (FcConfigParse *parse)
+{
+    int            n = FcVStackElements (parse);
+    while (n-- > 0)
+    {
+       FcVStack    *v = FcVStackFetch (parse, n);
+       if (v->tag != FcVStackInteger)
+           FcConfigMessage (parse, FcSevereError, "non-integer blank");
+       else
        {
-           if (node->type != XML_ELEMENT_NODE)
-               continue;
-           e = FcConfigParseExpr (doc, node);
-           if (!e)
-               break;
-           if (!l)
-               l = e;
-           else
+           if (!parse->config->blanks)
            {
-               r = e;
-               e = FcExprCreateOp (l, op, r);
-               if (!e)
+               parse->config->blanks = FcBlanksCreate ();
+               if (!parse->config->blanks)
                {
-                   FcExprDestroy (r);
+                   FcConfigMessage (parse, FcSevereError, "out of memory");
                    break;
                }
-               l = e;
            }
-       }
-       if (node || !l)
-       {
-           if (l)
-               FcExprDestroy (l);
-           return 0;
-       }
-       /*
-        * Special case for unary ops 
-        */
-       if (!r)
-       {
-           e = FcExprCreateOp (l, op, 0);
-           if (!e)
+           if (!FcBlanksAdd (parse->config->blanks, v->u.integer))
            {
-               FcExprDestroy (l);
-               return 0;
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+               break;
            }
        }
-       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)
+{
+    FcChar8 *s, *end;
+    int            l;
     
-    case FcOpInvalid:
-       return 0;
+    if (!parse->pstack)
+       return;
+    s = FcStrBufDoneStatic (&parse->pstack->str);
+    if (!s)
+    {
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
     }
-    return l;
+    end = 0;
+    l = (int) strtol ((char *) s, (char **)&end, 0);
+    if (end != s + strlen ((char *) s))
+       FcConfigMessage (parse, FcSevereError, "\"%s\": not a valid integer", s);
+    else
+       FcVStackPushInteger (parse, l);
+    FcStrBufDestroy (&parse->pstack->str);
 }
 
-static FcTest *
-FcConfigParseTest (xmlDocPtr   doc,
-                  xmlNodePtr   test)
-{
-    xmlNodePtr node;
-    xmlAttrPtr attr;
-    FcQual     qual = FcQualAny;
-    FcOp       op = FcOpEqual;
-    xmlChar    *field = 0;
-    FcExpr     *expr = 0;
+/*
+ * idea copied from glib g_ascii_strtod with 
+ * permission of the author (Alexander Larsson) 
+ */
 
-    for (attr = test->properties; attr; attr = attr->next)
+#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))
     {
-       if (attr->type != XML_ATTRIBUTE_NODE)
-           continue;
-       if (!strcmp (attr->name, "qual"))
-       {
-           xmlChar *qual_name = FcConfigAttr (doc, attr);
-           if (!qual_name)
-               ;
-           else if (!FcStrCmpIgnoreCase ((FcChar8 *) qual_name, (FcChar8 *) "any"))
-               qual = FcQualAny;
-           else if (!FcStrCmpIgnoreCase ((FcChar8 *) qual_name, (FcChar8 *) "all"))
-               qual = FcQualAll;
-       }
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "name"))
+       char    buf[128];
+       int     slen = strlen (s);
+       int     dlen = strlen (locale_data->decimal_point);
+       
+       if (slen + dlen > (int) sizeof (buf))
        {
-           field = FcConfigAttr (doc, attr);
+           if (end)
+               *end = s;
+           v = 0;
        }
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "compare"))
+       else
        {
-           xmlChar *compare = FcConfigAttr (doc, attr);
-           
-           if (!compare || (op = FcConfigLexOp (compare)) == FcOpInvalid)
-           {
-               FcConfigParseError ("Invalid comparison %s", 
-                                   compare ? (char *) compare : "<missing>");
-               return 0;
+           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 (buf_end > dot)
+                   buf_end -= dlen - 1;
            }
+           if (end)
+               *end = buf_end;
        }
     }
-    if (attr)
-       return 0;
+    else
+       v = strtod (s, end);
+    return v;
+}
 
-    for (node = test->children; node; node = node->next)
+static void
+FcParseDouble (FcConfigParse *parse)
+{
+    FcChar8 *s, *end;
+    double  d;
+    
+    if (!parse->pstack)
+       return;
+    s = FcStrBufDoneStatic (&parse->pstack->str);
+    if (!s)
     {
-       if (node->type != XML_ELEMENT_NODE)
-           continue;
-       expr = FcConfigParseExpr (doc, node);
-       if (!expr)
-           return 0;
-       break;
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
     }
+    end = 0;
+    d = FcStrtod ((char *) s, (char **)&end);
+    if (end != s + strlen ((char *) s))
+       FcConfigMessage (parse, FcSevereError, "\"%s\": not a valid double", s);
+    else
+       FcVStackPushDouble (parse, d);
+    FcStrBufDestroy (&parse->pstack->str);
+}
 
-    if (!expr)
+static void
+FcParseString (FcConfigParse *parse, FcVStackTag tag)
+{
+    FcChar8 *s;
+    
+    if (!parse->pstack)
+       return;
+    s = FcStrBufDone (&parse->pstack->str);
+    if (!s)
     {
-       FcConfigParseError ("Missing test expression");
-       return 0;
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
     }
-    
-    return FcTestCreate (qual, (char *) field, op, expr);
+    if (!FcVStackPushString (parse, tag, s))
+       FcStrFree (s);
 }
 
-static FcExpr *
-FcConfigParseExprList (xmlDocPtr    doc,
-                      xmlNodePtr   expr)
+static void
+FcParseMatrix (FcConfigParse *parse)
 {
-    FcExpr  *l, *e, *r;
-    
-    if (!expr)
-       return 0;
+    FcVStack   *vstack;
+    enum { m_done, m_xx, m_xy, m_yx, m_yy } matrix_state = m_yy;
+    FcMatrix   m;
     
-    e = FcConfigParseExprList (doc, expr->next);
-
-    if (expr->type == XML_ELEMENT_NODE)
+    while ((vstack = FcVStackPeek (parse)))
     {
-       r = e;
-       l = FcConfigParseExpr (doc, expr);
-       if (!l)
-           goto bail;
-       if (r)
-       {
-           e = FcExprCreateOp (l, FcOpComma, r);
-           if (!e)
-               goto bail;
+       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;
        }
-       else
-           e = l;
+       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;
+       }
+       FcVStackPopAndDestroy (parse);
+       matrix_state--;
     }
-    
-    return e;
-bail:
-    if (l)
-       FcExprDestroy (l);
-    if (r)
-       FcExprDestroy (r);
-    return 0;
+    if (matrix_state != m_done)
+       FcConfigMessage (parse, FcSevereError, "wrong number of matrix elements");
+    else
+       FcVStackPushMatrix (parse, &m);
 }
 
-static FcEdit *
-FcConfigParseEdit (xmlDocPtr   doc,
-                  xmlNodePtr   edit)
+static FcBool
+FcConfigLexBool (FcConfigParse *parse, const FcChar8 *bool_)
 {
-    xmlAttrPtr attr;
-    xmlChar    *name = 0;
-    FcOp       mode = FcOpAssign;
-    FcExpr     *e;
-    FcEdit     *ed;
+    FcBool  result = FcFalse;
 
-    for (attr = edit->properties; attr; attr = attr->next)
-    {
-       if (attr->type != XML_ATTRIBUTE_NODE)
-           continue;
-       if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "name"))
-           name = FcConfigAttr (doc, attr);
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "mode"))
-           mode = FcConfigLexOp (FcConfigAttr (doc, attr));
-    }
+    if (!FcNameBool (bool_, &result))
+       FcConfigMessage (parse, FcSevereWarning, "\"%s\" is not known boolean",
+                        bool_);
+    return result;
+}
 
-    e = FcConfigParseExprList (doc, edit->children);
+static void
+FcParseBool (FcConfigParse *parse)
+{
+    FcChar8 *s;
 
-    ed = FcEditCreate ((char *) name, mode, e);
-    if (!ed)
-       FcExprDestroy (e);
-    return ed;
+    if (!parse->pstack)
+       return;
+    s = FcStrBufDoneStatic (&parse->pstack->str);
+    if (!s)
+    {
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
+    }
+    FcVStackPushBool (parse, FcConfigLexBool (parse, s));
+    FcStrBufDestroy (&parse->pstack->str);
 }
 
 static FcBool
-FcConfigParseMatch (FcConfig   *config,
-                   xmlDocPtr   doc,
-                   xmlNodePtr  match)
+FcConfigLexBinding (FcConfigParse   *parse,
+                   const FcChar8   *binding_string,
+                   FcValueBinding  *binding_ret)
 {
-    xmlNodePtr node;
-    xmlAttrPtr attr;
-    FcTest     *tests = 0, **prevTest = &tests, *test;
-    FcEdit     *edits = 0, **prevEdit = &edits, *edit;
-    FcMatchKind        kind = FcMatchPattern;
-    FcBool     found_kind = FcFalse;
-
-    for (node = match->children; node; node = node->next)
+    FcValueBinding binding;
+    
+    if (!binding_string)
+       binding = FcValueBindingWeak;
+    else
     {
-       if (node->type != XML_ELEMENT_NODE)
-           continue;
-       if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "test"))
-       {
-           test = FcConfigParseTest (doc, node);
-           if (!test)
-               break;
-           *prevTest = test;
-           prevTest = &test->next;
-       }
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "edit"))
+       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
        {
-           edit = FcConfigParseEdit (doc, node);
-           if (!edit)
-               break;
-           *prevEdit = edit;
-           prevEdit = &edit->next;
+           FcConfigMessage (parse, FcSevereWarning, "invalid binding \"%s\"", binding_string);
+           return FcFalse;
        }
     }
+    *binding_ret = binding;
+    return FcTrue;
+}
 
-    for (attr = match->properties; attr; attr = attr->next)
+static void
+FcParseFamilies (FcConfigParse *parse, FcVStackTag tag)
+{
+    FcVStack   *vstack;
+    FcExpr     *left, *expr = 0, *new;
+
+    while ((vstack = FcVStackPeek (parse)))
     {
-       if (attr->type != XML_ATTRIBUTE_NODE)
+       if (vstack->tag != FcVStackFamily)
+       {
+           FcConfigMessage (parse, FcSevereWarning, "non-family");
+           FcVStackPopAndDestroy (parse);
            continue;
-       if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "target"))
+       }
+       left = vstack->u.expr;
+       vstack->tag = FcVStackNone;
+       FcVStackPopAndDestroy (parse);
+       if (expr)
        {
-           xmlChar *target = FcConfigAttr (doc, attr);
-           if (!target)
+           new = FcExprCreateOp (parse->config, left, FcOpComma, expr);
+           if (!new)
            {
-               FcConfigParseError ("Missing match target");
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+               FcExprDestroy (left);
+               FcExprDestroy (expr);
                break;
            }
-           else if (!FcStrCmpIgnoreCase ((FcChar8 *) target, (FcChar8 *) "pattern"))
-           {
-               kind = FcMatchPattern;
-               found_kind = FcTrue;
-           }
-           else if (!FcStrCmpIgnoreCase ((FcChar8 *) target, (FcChar8 *) "font"))
-           {
-               kind = FcMatchFont;
-               found_kind = FcTrue;
-           }
+           expr = new;
        }
+       else
+           expr = left;
     }
-
-    if (node || attr || !found_kind || 
-       !FcConfigAddEdit (config, tests, edits, kind))
+    if (expr)
     {
-       if (tests)
-           FcTestDestroy (tests);
-       if (edits)
-           FcEditDestroy (edits);
-       return FcFalse;
+       if (!FcVStackPushExpr (parse, tag, expr))
+       {
+           FcConfigMessage (parse, FcSevereError, "out of memory");
+            FcExprDestroy (expr);
+       }
     }
-
-    return FcTrue;
 }
 
-static FcExpr *
-FcConfigParseFamilies (xmlDocPtr    doc,
-                      xmlNodePtr   family)
+static void
+FcParseFamily (FcConfigParse *parse)
 {
-    FcExpr  *next = 0, *this = 0, *expr = 0;
-    
-    if (!family)
-       return 0;
-    next = FcConfigParseFamilies (doc, family->next);
-    
-    if (family->type == XML_ELEMENT_NODE && 
-       !FcStrCmpIgnoreCase ((FcChar8 *) family->name, (FcChar8 *) "family"))
+    FcChar8 *s;
+    FcExpr  *expr;
+
+    if (!parse->pstack)
+       return;
+    s = FcStrBufDoneStatic (&parse->pstack->str);
+    if (!s)
     {
-       this = FcExprCreateString ((FcChar8 *) FcConfigContent (doc, family));
-       if (!this)
-           goto bail;
-       if (next)
-       {
-           expr = FcExprCreateOp (this, FcOpComma, next);
-           if (!expr)
-               goto bail;
-       }
-       else
-           expr = this;
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
     }
-    else
-       expr = next;
-    return expr;
-
-bail:
+    expr = FcExprCreateString (parse->config, s);
+    FcStrBufDestroy (&parse->pstack->str);
     if (expr)
-       FcExprDestroy (expr);
-    if (this)
-       FcExprDestroy (this);
-    if (next)
-       FcExprDestroy (next);
-    return 0;
+       FcVStackPushExpr (parse, FcVStackFamily, expr);
 }
 
-static FcBool
-FcConfigParseAlias (FcConfig   *config,
-                   xmlDocPtr   doc,
-                   xmlNodePtr  alias)
+static void
+FcParseAlias (FcConfigParse *parse)
 {
-    xmlNodePtr node;
-    FcExpr     *prefer = 0, *accept = 0, *def = 0;
-    FcExpr     *family = 0;
+    FcExpr     *family = 0, *accept = 0, *prefer = 0, *def = 0, *new = 0;
     FcEdit     *edit = 0, *next;
+    FcVStack   *vstack;
     FcTest     *test;
+    FcValueBinding  binding;
 
-    for (node = alias->children; node; node = node->next)
+    if (!FcConfigLexBinding (parse, FcConfigGetAttribute (parse, "binding"), &binding))
+       return;
+    while ((vstack = FcVStackPeek (parse)))
     {
-       if (node->type != XML_ELEMENT_NODE)
-           continue;
-       if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "family"))
-           family = FcExprCreateString ((FcChar8 *) FcConfigContent (doc, node));
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "prefer"))
-           prefer = FcConfigParseFamilies (doc, node->children);
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "accept"))
-           accept = FcConfigParseFamilies (doc, node->children);
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "default"))
-           def = FcConfigParseFamilies (doc, node->children);
+       switch (vstack->tag) {
+       case FcVStackFamily:
+           if (family)
+           {
+               new = FcExprCreateOp (parse->config, 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)
+               FcExprDestroy (prefer);
+           prefer = vstack->u.expr;
+           vstack->tag = FcVStackNone;
+           break;
+       case FcVStackAccept:
+           if (accept)
+               FcExprDestroy (accept);
+           accept = vstack->u.expr;
+           vstack->tag = FcVStackNone;
+           break;
+       case FcVStackDefault:
+           if (def)
+               FcExprDestroy (def);
+           def = vstack->u.expr;
+           vstack->tag = FcVStackNone;
+           break;
+       default:
+           FcConfigMessage (parse, FcSevereWarning, "bad alias");
+           break;
+       }
+       FcVStackPopAndDestroy (parse);
     }
     if (!family)
-       return FcFalse;
-    
+    {
+       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"),
+       edit = FcEditCreate (parse, 
+                            FC_FAMILY_OBJECT,
                             FcOpPrepend,
-                            prefer);
+                            prefer,
+                            binding);
        if (edit)
            edit->next = 0;
+       else
+           FcExprDestroy (prefer);
     }
     if (accept)
     {
        next = edit;
-       edit = FcEditCreate (FcConfigSaveField ("family"),
+       edit = FcEditCreate (parse,
+                            FC_FAMILY_OBJECT,
                             FcOpAppend,
-                            accept);
+                            accept,
+                            binding);
        if (edit)
            edit->next = next;
+       else
+           FcExprDestroy (accept);
     }
     if (def)
     {
        next = edit;
-       edit = FcEditCreate (FcConfigSaveField ("family"),
-                             FcOpAppendLast,
-                             def);
+       edit = FcEditCreate (parse,
+                            FC_FAMILY_OBJECT,
+                            FcOpAppendLast,
+                            def,
+                            binding);
        if (edit)
            edit->next = next;
+       else
+           FcExprDestroy (def);
     }
     if (edit)
     {
-       test = FcTestCreate (FcQualAny,
-                            FcConfigSaveField ("family"),
+       test = FcTestCreate (parse, FcMatchPattern,
+                            FcQualAny,
+                            (FcChar8 *) FC_FAMILY,
                             FcOpEqual,
                             family);
        if (test)
-           FcConfigAddEdit (config, test, edit, FcMatchPattern);
+           if (!FcConfigAddEdit (parse->config, test, edit, FcMatchPattern))
+               FcTestDestroy (test);
     }
-    return FcTrue;
+    else
+       FcExprDestroy (family);
 }
 
-FcBool
-FcConfigParse (FcConfig            *config,
-              xmlDocPtr    doc)
+static FcExpr *
+FcPopExpr (FcConfigParse *parse)
 {
-    xmlNodePtr cur;
-    xmlNodePtr node;
-    
-    cur = xmlDocGetRootElement (doc);
-
-    for (node = cur->children; node; node = node->next)
-    {
-       if (node->type != XML_ELEMENT_NODE)
-           continue;
-       if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "dir"))
+    FcVStack   *vstack = FcVStackPeek (parse);
+    FcExpr     *expr = 0;
+    if (!vstack)
+       return 0;
+    switch (vstack->tag) {
+    case FcVStackNone:
+       break;
+    case FcVStackString:
+    case FcVStackFamily:
+       expr = FcExprCreateString (parse->config, vstack->u.string);
+       break;
+    case FcVStackField:
+       expr = FcExprCreateField (parse->config, (char *) vstack->u.string);
+       break;
+    case FcVStackConstant:
+       expr = FcExprCreateConst (parse->config, vstack->u.string);
+       break;
+    case FcVStackGlob:
+       /* XXX: What's the correct action here? (CDW) */
+       break;
+    case FcVStackPrefer:
+    case FcVStackAccept:
+    case FcVStackDefault:
+       expr = vstack->u.expr;
+       vstack->tag = FcVStackNone;
+       break;
+    case FcVStackInteger:
+       expr = FcExprCreateInteger (parse->config, vstack->u.integer);
+       break;
+    case FcVStackDouble:
+       expr = FcExprCreateDouble (parse->config, vstack->u._double);
+       break;
+    case FcVStackMatrix:
+       expr = FcExprCreateMatrix (parse->config, vstack->u.matrix);
+       break;
+    case FcVStackBool:
+       expr = FcExprCreateBool (parse->config, vstack->u.bool_);
+       break;
+    case FcVStackTest:
+       break;
+    case FcVStackExpr:
+       expr = vstack->u.expr;
+       vstack->tag = FcVStackNone;
+       break;
+    case FcVStackEdit:
+       break;
+    default:
+       break;
+    }
+    FcVStackPopAndDestroy (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 *
+FcPopBinary (FcConfigParse *parse, FcOp op)
+{
+    FcExpr  *left, *expr = 0, *new;
+
+    while ((left = FcPopExpr (parse)))
+    {
+       if (expr)
        {
-           if (!FcConfigParseDir (config, doc, node))
-               break;
+           new = FcExprCreateOp (parse->config, left, op, expr);
+           if (!new)
+           {
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+               FcExprDestroy (left);
+               FcExprDestroy (expr);
+               return 0;
+           }
+           expr = new;
        }
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "cache"))
+       else
+           expr = left;
+    }
+    return expr;
+}
+
+static void
+FcParseBinary (FcConfigParse *parse, FcOp 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 (parse->config, operand, op, 0);
+       if (!new)
        {
-           if (!FcConfigParseCache (config, doc, node))
-               break;
+           FcExprDestroy (operand);
+           FcConfigMessage (parse, FcSevereError, "out of memory");
        }
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "include"))
+    }
+    return new;
+}
+
+static void
+FcParseUnary (FcConfigParse *parse, FcOp op)
+{
+    FcExpr  *expr = FcPopUnary (parse, op);
+    if (expr)
+       FcVStackPushExpr (parse, FcVStackExpr, expr);
+}
+
+static void
+FcParseInclude (FcConfigParse *parse)
+{
+    FcChar8        *s;
+    const FcChar8   *i;
+    FcBool         ignore_missing = FcFalse;
+    
+    s = FcStrBufDoneStatic (&parse->pstack->str);
+    if (!s)
+    {
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
+    }
+    i = FcConfigGetAttribute (parse, "ignore_missing");
+    if (i && FcConfigLexBool (parse, (FcChar8 *) i) == FcTrue)
+       ignore_missing = FcTrue;
+    if (!FcConfigParseAndLoad (parse->config, s, !ignore_missing))
+       parse->error = FcTrue;
+    FcStrBufDestroy (&parse->pstack->str);
+}
+
+typedef struct _FcOpMap {
+    char    name[16];
+    FcOp    op;
+} FcOpMap;
+
+static FcOp
+FcConfigLexOp (const FcChar8 *op, const FcOpMap        *map, int nmap)
+{
+    int        i;
+
+    for (i = 0; i < nmap; i++)
+       if (!strcmp ((char *) op, map[i].name)) 
+           return map[i].op;
+    return FcOpInvalid;
+}
+
+static const FcOpMap fcCompareOps[] = {
+    { "eq",            FcOpEqual           },
+    { "not_eq",                FcOpNotEqual        },
+    { "less",          FcOpLess            },
+    { "less_eq",       FcOpLessEqual       },
+    { "more",          FcOpMore            },
+    { "more_eq",       FcOpMoreEqual       },
+    { "contains",      FcOpContains        },
+    { "not_contains",  FcOpNotContains     }
+};
+
+#define NUM_COMPARE_OPS        (int) (sizeof fcCompareOps / sizeof fcCompareOps[0])
+
+static FcOp
+FcConfigLexCompare (const FcChar8 *compare)
+{
+    return FcConfigLexOp (compare, fcCompareOps, NUM_COMPARE_OPS);
+}
+
+static void
+FcParseTest (FcConfigParse *parse)
+{
+    const FcChar8   *kind_string;
+    FcMatchKind            kind;
+    const FcChar8   *qual_string;
+    FcQual         qual;
+    const FcChar8   *name;
+    const FcChar8   *compare_string;
+    FcOp           compare;
+    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, "scan"))
+           kind = FcMatchScan;
+       else if (!strcmp ((char *) kind_string, "default"))
+           kind = FcMatchDefault;
+       else
        {
-           if (!FcConfigParseInclude (config, doc, node))
-               break;
+           FcConfigMessage (parse, FcSevereWarning, "invalid test target \"%s\"", kind_string);
+           return;
        }
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "config"))
+    }
+    qual_string = FcConfigGetAttribute (parse, "qual");
+    if (!qual_string)
+       qual = FcQualAny;
+    else
+    {
+       if (!strcmp ((char *) qual_string, "any"))
+           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
        {
-           if (!FcConfigParseConfig (config, doc, node))
-               break;
+           FcConfigMessage (parse, FcSevereWarning, "invalid test qual \"%s\"", qual_string);
+           return;
+       }
+    }
+    name = FcConfigGetAttribute (parse, "name");
+    if (!name)
+    {
+       FcConfigMessage (parse, FcSevereWarning, "missing test name");
+       return;
+    }
+    compare_string = FcConfigGetAttribute (parse, "compare");
+    if (!compare_string)
+       compare = FcOpEqual;
+    else
+    {
+       compare = FcConfigLexCompare (compare_string);
+       if (compare == FcOpInvalid)
+       {
+           FcConfigMessage (parse, FcSevereWarning, "invalid test compare \"%s\"", compare_string);
+           return;
+       }
+    }
+    expr = FcPopBinary (parse, FcOpComma);
+    if (!expr)
+    {
+       FcConfigMessage (parse, FcSevereWarning, "missing test expression");
+       return;
+    }
+    test = FcTestCreate (parse, kind, qual, name, compare, expr);
+    if (!test)
+    {
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
+    }
+    FcVStackPushTest (parse, test);
+}
+
+static const FcOpMap fcModeOps[] = {
+    { "assign",                FcOpAssign          },
+    { "assign_replace",        FcOpAssignReplace   },
+    { "prepend",       FcOpPrepend         },
+    { "prepend_first", FcOpPrependFirst    },
+    { "append",                FcOpAppend          },
+    { "append_last",   FcOpAppendLast      },
+};
+
+#define NUM_MODE_OPS (int) (sizeof fcModeOps / sizeof fcModeOps[0])
+
+static FcOp
+FcConfigLexMode (const FcChar8 *mode)
+{
+    return FcConfigLexOp (mode, fcModeOps, NUM_MODE_OPS);
+}
+
+static void
+FcParseEdit (FcConfigParse *parse)
+{
+    const FcChar8   *name;
+    const FcChar8   *mode_string;
+    FcOp           mode;
+    FcValueBinding  binding;
+    FcExpr         *expr;
+    FcEdit         *edit;
+
+    name = FcConfigGetAttribute (parse, "name");
+    if (!name)
+    {
+       FcConfigMessage (parse, FcSevereWarning, "missing edit name");
+       return;
+    }
+    mode_string = FcConfigGetAttribute (parse, "mode");
+    if (!mode_string)
+       mode = FcOpAssign;
+    else
+    {
+       mode = FcConfigLexMode (mode_string);
+       if (mode == FcOpInvalid)
+       {
+           FcConfigMessage (parse, FcSevereWarning, "invalid edit mode \"%s\"", mode_string);
+           return;
+       }
+    }
+    if (!FcConfigLexBinding (parse, FcConfigGetAttribute (parse, "binding"), &binding))
+       return;
+
+    expr = FcPopBinary (parse, FcOpComma);
+    edit = FcEditCreate (parse, FcObjectFromName ((char *) name),
+                        mode, expr, binding);
+    if (!edit)
+    {
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       FcExprDestroy (expr);
+       return;
+    }
+    if (!FcVStackPushEdit (parse, edit))
+       FcEditDestroy (edit);
+}
+
+static void
+FcParseMatch (FcConfigParse *parse)
+{
+    const FcChar8   *kind_name;
+    FcMatchKind            kind;
+    FcTest         *test = 0;
+    FcEdit         *edit = 0;
+    FcVStack       *vstack;
+
+    kind_name = FcConfigGetAttribute (parse, "target");
+    if (!kind_name)
+       kind = FcMatchPattern;
+    else
+    {
+       if (!strcmp ((char *) kind_name, "pattern"))
+           kind = FcMatchPattern;
+       else if (!strcmp ((char *) kind_name, "font"))
+           kind = FcMatchFont;
+       else if (!strcmp ((char *) kind_name, "scan"))
+           kind = FcMatchScan;
+       else
+       {
+           FcConfigMessage (parse, FcSevereWarning, "invalid match target \"%s\"", kind_name);
+           return;
+       }
+    }
+    while ((vstack = FcVStackPeek (parse)))
+    {
+       switch (vstack->tag) {
+       case FcVStackTest:
+           vstack->u.test->next = test;
+           test = vstack->u.test;
+           vstack->tag = FcVStackNone;
+           break;
+       case FcVStackEdit:
+           vstack->u.edit->next = edit;
+           edit = vstack->u.edit;
+           vstack->tag = FcVStackNone;
+           if (kind == FcMatchScan && edit->object > FC_MAX_BASE_OBJECT)
+           {
+               FcConfigMessage (parse, FcSevereError, 
+                                "<match target=\"scan\"> cannot edit user-defined object \"%s\"",
+                                FcObjectName(edit->object));
+           }
+           break;
+       default:
+           FcConfigMessage (parse, FcSevereWarning, "invalid match element");
+           break;
+       }
+       FcVStackPopAndDestroy (parse);
+    }
+    if (!FcConfigAddEdit (parse->config, test, edit, kind))
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+}
+
+static void
+FcParseAcceptRejectFont (FcConfigParse *parse, FcElement element)
+{
+    FcVStack   *vstack;
+
+    while ((vstack = FcVStackPeek (parse)))
+    {
+       switch (vstack->tag) {
+       case FcVStackGlob:
+           if (!FcConfigGlobAdd (parse->config, 
+                                 vstack->u.string,
+                                 element == FcElementAcceptfont))
+           {
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+           }
+           break;
+       case FcVStackPattern:
+           if (!FcConfigPatternsAdd (parse->config,
+                                     vstack->u.pattern,
+                                     element == FcElementAcceptfont))
+           {
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+           }
+           else
+               vstack->tag = FcVStackNone;
+           break;
+       default:
+           FcConfigMessage (parse, FcSevereWarning, "bad font selector");
+           break;
+       }
+       FcVStackPopAndDestroy (parse);
+    }
+}
+
+
+static FcValue
+FcPopValue (FcConfigParse *parse)
+{
+    FcVStack   *vstack = FcVStackPeek (parse);
+    FcValue    value;
+    
+    value.type = FcTypeVoid;
+    
+    if (!vstack)
+       return value;
+    
+    switch (vstack->tag) {
+    case FcVStackString:
+       value.u.s = FcStrStaticName (vstack->u.string);
+       if (value.u.s)
+           value.type = FcTypeString;
+       break;
+    case FcVStackConstant:
+       if (FcNameConstant (vstack->u.string, &value.u.i))
+           value.type = FcTypeInteger;
+       break;
+    case FcVStackInteger:
+       value.u.i = vstack->u.integer;
+       value.type = FcTypeInteger;
+       break;
+    case FcVStackDouble:
+       value.u.d = vstack->u._double;
+       value.type = FcTypeInteger;
+       break;
+    case FcVStackMatrix:
+       value.u.m = FcMatrixCopy (vstack->u.matrix);
+       if (value.u.m)
+           value.type = FcTypeMatrix;
+       break;
+    case FcVStackBool:
+       value.u.b = vstack->u.bool_;
+       value.type = FcTypeBool;
+       break;
+    default:
+       FcConfigMessage (parse, FcSevereWarning, "unknown pattern element %d", 
+                        vstack->tag);
+       break;
+    }
+    FcVStackPopAndDestroy (parse);
+    
+    return value;
+}
+
+static void
+FcParsePatelt (FcConfigParse *parse)
+{
+    FcValue    value;
+    FcPattern  *pattern = FcPatternCreate ();
+    const char *name;
+
+    if (!pattern)
+    {
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
+    }
+
+    name = (char *) FcConfigGetAttribute (parse, "name");
+    if (!name)
+    {
+       FcConfigMessage (parse, FcSevereWarning, "missing pattern element name");
+       FcPatternDestroy (pattern);
+       return;
+    }
+    
+    for (;;)
+    {
+       value = FcPopValue (parse);
+       if (value.type == FcTypeVoid)
+           break;
+       if (!FcPatternAdd (pattern, name, value, FcTrue))
+       {
+           FcConfigMessage (parse, FcSevereError, "out of memory");
+            FcValueDestroy(value);
+           break;
+       }
+        FcValueDestroy(value);
+    }
+
+    FcVStackPushPattern (parse, pattern);
+}
+
+static void
+FcParsePattern (FcConfigParse *parse)
+{
+    FcVStack   *vstack;
+    FcPattern  *pattern = FcPatternCreate ();
+
+    if (!pattern)
+    {
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+       return;
+    }
+       
+    while ((vstack = FcVStackPeek (parse)))
+    {
+       switch (vstack->tag) {
+       case FcVStackPattern:
+           if (!FcPatternAppend (pattern, vstack->u.pattern))
+           {
+               FcConfigMessage (parse, FcSevereError, "out of memory");
+               FcPatternDestroy (pattern);
+               return;
+           }
+           break;
+       default:
+           FcConfigMessage (parse, FcSevereWarning, "unknown pattern element");
+           break;
+       }
+       FcVStackPopAndDestroy (parse);
+    }
+
+    FcVStackPushPattern (parse, pattern);
+}
+
+static void
+FcEndElement(void *userData, const XML_Char *name)
+{
+    FcConfigParse   *parse = userData;
+    FcChar8        *data;
+#ifdef _WIN32
+    FcChar8         buffer[1000];
+#endif
+
+    if (!parse->pstack)
+       return;
+    switch (parse->pstack->element) {
+    case FcElementNone:
+       break;
+    case FcElementFontconfig:
+       break;
+    case FcElementDir:
+       data = FcStrBufDoneStatic (&parse->pstack->str);
+       if (!data)
+       {
+           FcConfigMessage (parse, FcSevereError, "out of memory");
+           break;
        }
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "match"))
+#ifdef _WIN32
+       if (strcmp (data, "CUSTOMFONTDIR") == 0)
        {
-           if (!FcConfigParseMatch (config, doc, node))
+               char *p;
+               data = buffer;
+               if (!GetModuleFileName (NULL, buffer, sizeof (buffer) - 20))
+               {
+                       FcConfigMessage (parse, FcSevereError, "GetModuleFileName failed");
+                       break;
+               }
+               /*
+                * Must use the multi-byte aware function to search
+                * for backslash because East Asian double-byte code
+                * pages have characters with backslash as the second
+                * byte.
+                */
+               p = _mbsrchr (data, '\\');
+               if (p) *p = '\0';
+               strcat (data, "\\fonts");
+       }
+       else if (strcmp (data, "APPSHAREFONTDIR") == 0)
+       {
+               char *p;
+               data = buffer;
+               if (!GetModuleFileName (NULL, buffer, sizeof (buffer) - 20))
+               {
+                       FcConfigMessage (parse, FcSevereError, "GetModuleFileName failed");
+                       break;
+               }
+               p = _mbsrchr (data, '\\');
+               if (p) *p = '\0';
+               strcat (data, "\\..\\share\\fonts");
+       }
+       else if (strcmp (data, "WINDOWSFONTDIR") == 0)
+       {
+           int rc;
+           data = buffer;
+#if _WIN32_WINNT >= 0x0500
+           rc = GetSystemWindowsDirectory (buffer, sizeof (buffer) - 20);
+#else
+           rc = GetWindowsDirectory (buffer, sizeof (buffer) - 20);
+#endif
+           if (rc == 0 || rc > sizeof (buffer) - 20)
+           {
+               FcConfigMessage (parse, FcSevereError, "GetSystemWindowsDirectory failed");
                break;
+           }
+           if (data [strlen (data) - 1] != '\\')
+               strcat (data, "\\");
+           strcat (data, "fonts");
+       }
+#endif
+       if (strlen ((char *) data) == 0)
+           FcConfigMessage (parse, FcSevereWarning, "empty font directory name ignored");
+       else if (!FcStrUsesHome (data) || FcConfigHome ())
+       {
+           if (!FcConfigAddDir (parse->config, data))
+               FcConfigMessage (parse, FcSevereError, "out of memory; cannot add directory %s", data);
        }
-       else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "alias"))
+       FcStrBufDestroy (&parse->pstack->str);
+       break;
+    case FcElementCacheDir:
+       data = FcStrBufDone (&parse->pstack->str);
+       if (!data)
+       {
+           FcConfigMessage (parse, FcSevereError, "out of memory");
+           break;
+       }
+#ifdef _WIN32
+       if (strcmp (data, "WINDOWSTEMPDIR_FONTCONFIG_CACHE") == 0)
        {
-           if (!FcConfigParseAlias (config, doc, node))
+           int rc;
+           FcStrFree (data);
+           data = malloc (1000);
+           if (!data)
+           {
+               FcConfigMessage (parse, FcSevereError, "out of memory");
                break;
+           }
+           FcMemAlloc (FC_MEM_STRING, 1000);
+           rc = GetTempPath (800, data);
+           if (rc == 0 || rc > 800)
+           {
+               FcConfigMessage (parse, FcSevereError, "GetTempPath failed");
+               FcStrFree (data);
+               break;
+           }
+           if (data [strlen (data) - 1] != '\\')
+               strcat (data, "\\");
+           strcat (data, "fontconfig\\cache");
        }
-       else
+#endif
+       if (!FcStrUsesHome (data) || FcConfigHome ())
        {
-           FcConfigParseError ("invalid element %s", node->name);
+           if (!FcConfigAddCacheDir (parse->config, data))
+               FcConfigMessage (parse, FcSevereError, "out of memory; cannot add cache directory %s", data);
+       }
+       FcStrFree (data);
+       break;
+       
+    case FcElementCache:
+       data = FcStrBufDoneStatic (&parse->pstack->str);
+       if (!data)
+       {
+           FcConfigMessage (parse, FcSevereError, "out of memory");
            break;
-       }   
+       }
+       /* discard this data; no longer used */
+       FcStrBufDestroy (&parse->pstack->str);
+       break;
+    case FcElementInclude:
+       FcParseInclude (parse);
+       break;
+    case FcElementConfig:
+       break;
+    case FcElementMatch:
+       FcParseMatch (parse);
+       break;
+    case FcElementAlias:
+       FcParseAlias (parse);
+       break;
+
+    case FcElementBlank:
+       FcParseBlank (parse);
+       break;
+    case FcElementRescan:
+       FcParseRescan (parse);
+       break;
+       
+    case FcElementPrefer:
+       FcParseFamilies (parse, FcVStackPrefer);
+       break;
+    case FcElementAccept:
+       FcParseFamilies (parse, FcVStackAccept);
+       break;
+    case FcElementDefault:
+       FcParseFamilies (parse, FcVStackDefault);
+       break;
+    case FcElementFamily:
+       FcParseFamily (parse);
+       break;
+
+    case FcElementTest:
+       FcParseTest (parse);
+       break;
+    case FcElementEdit:
+       FcParseEdit (parse);
+       break;
+
+    case FcElementInt:
+       FcParseInt (parse);
+       break;
+    case FcElementDouble:
+       FcParseDouble (parse);
+       break;
+    case FcElementString:
+       FcParseString (parse, FcVStackString);
+       break;
+    case FcElementMatrix:
+       FcParseMatrix (parse);
+       break;
+    case FcElementBool:
+       FcParseBool (parse);
+       break;
+    case FcElementCharset:
+/*     FcParseCharset (parse); */
+       break;
+    case FcElementSelectfont:
+       break;
+    case FcElementAcceptfont:
+    case FcElementRejectfont:
+       FcParseAcceptRejectFont (parse, parse->pstack->element);
+       break;
+    case FcElementGlob:
+       FcParseString (parse, FcVStackGlob);
+       break;
+    case FcElementPattern:
+       FcParsePattern (parse);
+       break;
+    case FcElementPatelt:
+       FcParsePatelt (parse);
+       break;
+    case FcElementName:
+       FcParseString (parse, FcVStackField);
+       break;
+    case FcElementConst:
+       FcParseString (parse, FcVStackConstant);
+       break;
+    case FcElementOr:
+       FcParseBinary (parse, FcOpOr);
+       break;
+    case FcElementAnd:
+       FcParseBinary (parse, FcOpAnd);
+       break;
+    case FcElementEq:
+       FcParseBinary (parse, FcOpEqual);
+       break;
+    case FcElementNotEq:
+       FcParseBinary (parse, FcOpNotEqual);
+       break;
+    case FcElementLess:
+       FcParseBinary (parse, FcOpLess);
+       break;
+    case FcElementLessEq:
+       FcParseBinary (parse, FcOpLessEqual);
+       break;
+    case FcElementMore:
+       FcParseBinary (parse, FcOpMore);
+       break;
+    case FcElementMoreEq:
+       FcParseBinary (parse, FcOpMoreEqual);
+       break;
+    case FcElementContains:
+       FcParseBinary (parse, FcOpContains);
+       break;
+    case FcElementNotContains:
+       FcParseBinary (parse, FcOpNotContains);
+       break;
+    case FcElementPlus:
+       FcParseBinary (parse, FcOpPlus);
+       break;
+    case FcElementMinus:
+       FcParseBinary (parse, FcOpMinus);
+       break;
+    case FcElementTimes:
+       FcParseBinary (parse, FcOpTimes);
+       break;
+    case FcElementDivide:
+       FcParseBinary (parse, FcOpDivide);
+       break;
+    case FcElementNot:
+       FcParseUnary (parse, FcOpNot);
+       break;
+    case FcElementIf:
+       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;
     }
-    if (node)
-       return FcFalse;
-    return FcTrue;
+    (void) FcPStackPop (parse);
+}
+
+static void
+FcCharacterData (void *userData, const XML_Char *s, int len)
+{
+    FcConfigParse   *parse = userData;
+    
+    if (!parse->pstack)
+       return;
+    if (!FcStrBufData (&parse->pstack->str, (FcChar8 *) s, len))
+       FcConfigMessage (parse, FcSevereError, "out of memory");
+}
+
+static void
+FcStartDoctypeDecl (void           *userData,
+                   const XML_Char  *doctypeName,
+                   const XML_Char  *sysid,
+                   const XML_Char  *pubid,
+                   int             has_internal_subset)
+{
+    FcConfigParse   *parse = userData;
+
+    if (strcmp ((char *) doctypeName, "fontconfig") != 0)
+       FcConfigMessage (parse, FcSevereError, "invalid doctype \"%s\"", doctypeName);
+}
+
+#ifdef ENABLE_LIBXML2
+
+static void
+FcInternalSubsetDecl (void            *userData,
+                     const XML_Char  *doctypeName,
+                     const XML_Char  *sysid,
+                     const XML_Char  *pubid)
+{
+    FcStartDoctypeDecl (userData, doctypeName, sysid, pubid, 1);
+}
+
+static void
+FcExternalSubsetDecl (void            *userData,
+                     const XML_Char  *doctypeName,
+                     const XML_Char  *sysid,
+                     const XML_Char  *pubid)
+{
+    FcStartDoctypeDecl (userData, doctypeName, sysid, pubid, 0);
+}
+
+#else /* ENABLE_LIBXML2 */
+
+static void
+FcEndDoctypeDecl (void *userData)
+{
+}
+
+#endif /* ENABLE_LIBXML2 */
+
+static int
+FcSortCmpStr (const void *a, const void *b)
+{
+    const FcChar8    *as = *((FcChar8 **) a);
+    const FcChar8    *bs = *((FcChar8 **) b);
+    return FcStrCmp (as, bs);
+}
+
+static FcBool
+FcConfigParseAndLoadDir (FcConfig      *config,
+                        const FcChar8  *name,
+                        const FcChar8  *dir,
+                        FcBool         complain)
+{
+    DIR                    *d;
+    struct dirent   *e;
+    FcBool         ret = FcTrue;
+    FcChar8        *file;
+    FcChar8        *base;
+    FcStrSet       *files;
+
+    d = opendir ((char *) dir);
+    if (!d)
+    {
+       if (complain)
+           FcConfigMessage (0, FcSevereError, "Cannot open config dir \"%s\"",
+                            name);
+       ret = FcFalse;
+       goto bail0;
+    }
+    /* freed below */
+    file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1);
+    if (!file)
+    {
+       ret = FcFalse;
+       goto bail1;
+    }
+    
+    strcpy ((char *) file, (char *) dir);
+    strcat ((char *) file, "/");
+    base = file + strlen ((char *) file);
+    
+    files = FcStrSetCreate ();
+    if (!files)
+    {
+       ret = FcFalse;
+       goto bail2;
+    }
+    
+    if (FcDebug () & FC_DBG_CONFIG)
+       printf ("\tScanning config dir %s\n", dir);
+       
+    while (ret && (e = readdir (d)))
+    {
+       int d_len;
+#define TAIL       ".conf"
+#define TAIL_LEN    5
+       /*
+        * Add all files of the form [0-9]*.conf
+        */
+       if ('0' <= e->d_name[0] && e->d_name[0] <= '9' &&
+           (d_len = strlen (e->d_name)) < FC_MAX_FILE_LEN &&
+           d_len > TAIL_LEN &&
+           strcmp (e->d_name + d_len - TAIL_LEN, TAIL) == 0)
+       {
+           strcpy ((char *) base, (char *) e->d_name);
+           if (!FcStrSetAdd (files, file))
+           {
+               ret = FcFalse;
+               goto bail3;
+           }
+       }
+    }
+    if (ret)
+    {
+       int i;
+       qsort (files->strs, files->num, sizeof (FcChar8 *), 
+              (int (*)(const void *, const void *)) FcSortCmpStr);
+       for (i = 0; ret && i < files->num; i++)
+           ret = FcConfigParseAndLoad (config, files->strs[i], complain);
+    }
+bail3:
+    FcStrSetDestroy (files);
+bail2:
+    free (file);
+bail1:
+    closedir (d);
+bail0:
+    return ret || !complain;
 }
 
 FcBool
 FcConfigParseAndLoad (FcConfig     *config,
-                     const FcChar8 *file,
+                     const FcChar8 *name,
                      FcBool        complain)
 {
-    xmlDocPtr  doc;
-    FcBool     ret;
 
-    doc = FcConfigLoad (file);
-    if (doc)
+    XML_Parser     p;
+    FcChar8        *filename;
+    int                    fd;
+    int                    len;
+    FcConfigParse   parse;
+    FcBool         error = FcTrue;
+    
+#ifdef ENABLE_LIBXML2
+    xmlSAXHandler   sax;
+    char            buf[BUFSIZ];
+#else
+    void           *buf;
+#endif
+    
+    filename = FcConfigFilename (name);
+    if (!filename)
+       goto bail0;
+    
+    if (FcStrSetMember (config->configFiles, filename))
     {
-       ret = FcConfigAddConfigFile (config, file);
-       if (ret)
-           ret = FcConfigParse (config, doc);
-       xmlFreeDoc (doc);
+        FcStrFree (filename);
+        return FcTrue;
+    }
+
+    if (!FcStrSetAdd (config->configFiles, filename))
+    {
+       FcStrFree (filename);
+       goto bail0;
+    }
+
+    if (FcFileIsDir (filename))
+    {
+       FcBool ret = FcConfigParseAndLoadDir (config, name, filename, complain);
+       FcStrFree (filename);
        return ret;
     }
-    if (complain)
+
+    if (FcDebug () & FC_DBG_CONFIG)
+       printf ("\tLoading config file %s\n", filename);
+
+    fd = open ((char *) filename, O_RDONLY);
+    if (fd == -1) { 
+       FcStrFree (filename);
+       goto bail0;
+    }
+    
+#ifdef ENABLE_LIBXML2
+    memset(&sax, 0, sizeof(sax));
+
+    sax.internalSubset = FcInternalSubsetDecl;
+    sax.externalSubset = FcExternalSubsetDecl;
+    sax.startElement = FcStartElement;
+    sax.endElement = FcEndElement;
+    sax.characters = FcCharacterData;
+
+    p = xmlCreatePushParserCtxt (&sax, &parse, NULL, 0, (const char *) filename);
+#else
+    p = XML_ParserCreate ("UTF-8");
+#endif
+    FcStrFree (filename);
+
+    if (!p)
+       goto bail1;
+
+    if (!FcConfigInit (&parse, name, config, p))
+       goto bail2;
+
+#ifndef ENABLE_LIBXML2
+
+    XML_SetUserData (p, &parse);
+    
+    XML_SetDoctypeDeclHandler (p, FcStartDoctypeDecl, FcEndDoctypeDecl);
+    XML_SetElementHandler (p, FcStartElement, FcEndElement);
+    XML_SetCharacterDataHandler (p, FcCharacterData);
+       
+#endif /* ENABLE_LIBXML2 */
+
+    do {
+#ifndef ENABLE_LIBXML2
+       buf = XML_GetBuffer (p, BUFSIZ);
+       if (!buf)
+       {
+           FcConfigMessage (&parse, FcSevereError, "cannot get parse buffer");
+           goto bail3;
+       }
+#endif
+       len = read (fd, buf, BUFSIZ);
+       if (len < 0)
+       {
+           FcConfigMessage (&parse, FcSevereError, "failed reading config file");
+           goto bail3;
+       }
+
+#ifdef ENABLE_LIBXML2
+       if (xmlParseChunk (p, buf, len, len == 0))
+#else
+       if (!XML_ParseBuffer (p, len, len == 0))
+#endif
+       {
+           FcConfigMessage (&parse, FcSevereError, "%s", 
+                          XML_ErrorString (XML_GetErrorCode (p)));
+           goto bail3;
+       }
+    } while (len != 0);
+    error = parse.error;
+bail3:
+    FcConfigCleanup (&parse);
+bail2:
+    XML_ParserFree (p);
+bail1:
+    close (fd);
+    fd = -1;
+bail0:
+    if (error && complain)
     {
-       if (file)
-           FcConfigParseError ("Cannot load config file \"%s\"", file);
+       if (name)
+           FcConfigMessage (0, FcSevereError, "Cannot load config file \"%s\"", name);
        else
-           FcConfigParseError ("Cannot load default config file");
+           FcConfigMessage (0, FcSevereError, "Cannot load default config file");
        return FcFalse;
     }
     return FcTrue;
 }
+#define __fcxml__
+#include "fcaliastail.h"
+#undef __fcxml__