Edit

IABSD.fr/xenocara/lib/fontconfig/src/fcxml.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2010-03-25 21:45:57
    Hash : e248f656
    Message : Update to fontconfig 2.8.0. Tested on a full ports build by naddy@.

  • lib/fontconfig/src/fcxml.c
  • /*
     * fontconfig/src/fcxml.c
     *
     * 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
     * the above copyright notice appear in all copies and that both that
     * copyright notice and this permission notice appear in supporting
     * documentation, and that the name of Keith Packard not be used in
     * advertising or publicity pertaining to distribution of the software without
     * specific, written prior permission.  Keith Packard makes no
     * representations about the suitability of this software for any purpose.  It
     * is provided "as is" without express or implied warranty.
     *
     * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     * 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 "fcint.h"
    #include <fcntl.h>
    #include <stdarg.h>
    #include <dirent.h>
    
    #ifdef ENABLE_LIBXML2
    
    #include <libxml/parser.h>
    
    #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
    
    #else /* ENABLE_LIBXML2 */
    
    #ifndef HAVE_XMLPARSE_H
    #define HAVE_XMLPARSE_H 0
    #endif
    
    #if HAVE_XMLPARSE_H
    #include <xmlparse.h>
    #else
    #include <expat.h>
    #endif
    
    #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)
    {
        if (test->next)
    	FcTestDestroy (test->next);
        FcExprDestroy (test->expr);
        FcMemFree (FC_MEM_TEST, sizeof (FcTest));
        free (test);
    }
    
    static FcExpr *
    FcExprCreateInteger (FcConfig *config, int i)
    {
        FcExpr *e = FcConfigAllocExpr (config);
        if (e)
        {
    	e->op = FcOpInteger;
    	e->u.ival = i;
        }
        return e;
    }
    
    static FcExpr *
    FcExprCreateDouble (FcConfig *config, double d)
    {
        FcExpr *e = FcConfigAllocExpr (config);
        if (e)
        {
    	e->op = FcOpDouble;
    	e->u.dval = d;
        }
        return e;
    }
    
    static FcExpr *
    FcExprCreateString (FcConfig *config, const FcChar8 *s)
    {
        FcExpr *e = FcConfigAllocExpr (config);
        if (e)
        {
    	e->op = FcOpString;
    	e->u.sval = FcStrStaticName (s);
        }
        return e;
    }
    
    static FcExpr *
    FcExprCreateMatrix (FcConfig *config, const FcMatrix *m)
    {
        FcExpr *e = FcConfigAllocExpr (config);
        if (e)
        {
    	e->op = FcOpMatrix;
    	e->u.mval = FcMatrixCopy (m);
        }
        return e;
    }
    
    static FcExpr *
    FcExprCreateBool (FcConfig *config, FcBool b)
    {
        FcExpr *e = FcConfigAllocExpr (config);
        if (e)
        {
    	e->op = FcOpBool;
    	e->u.bval = b;
        }
        return e;
    }
    
    static FcExpr *
    FcExprCreateField (FcConfig *config, const char *field)
    {
        FcExpr *e = FcConfigAllocExpr (config);
        if (e)
        {
    	e->op = FcOpField;
    	e->u.object = FcObjectFromName (field);
        }
        return e;
    }
    
    static FcExpr *
    FcExprCreateConst (FcConfig *config, const FcChar8 *constant)
    {
        FcExpr *e = FcConfigAllocExpr (config);
        if (e)
        {
    	e->op = FcOpConst;
    	e->u.constant = FcStrStaticName (constant);
        }
        return e;
    }
    
    static FcExpr *
    FcExprCreateOp (FcConfig *config, FcExpr *left, FcOp op, FcExpr *right)
    {
        FcExpr *e = FcConfigAllocExpr (config);
        if (e)
        {
    	e->op = op;
    	e->u.tree.left = left;
    	e->u.tree.right = right;
        }
        return e;
    }
    
    static void
    FcExprDestroy (FcExpr *e)
    {
        if (!e)
    	return;
        switch (e->op) {
        case FcOpInteger:
    	break;
        case FcOpDouble:
    	break;
        case FcOpString:
    	break;
        case FcOpMatrix:
    	FcMatrixFree (e->u.mval);
    	break;
        case FcOpCharSet:
    	FcCharSetDestroy (e->u.cval);
    	break;
        case FcOpBool:
    	break;
        case FcOpField:
    	break;
        case FcOpConst:
    	break;
        case FcOpAssign:
        case FcOpAssignReplace:
        case FcOpPrepend:
        case FcOpPrependFirst:
        case FcOpAppend:
        case FcOpAppendLast:
    	break;
        case FcOpOr:
        case FcOpAnd:
        case FcOpEqual:
        case FcOpNotEqual:
        case FcOpLess:
        case FcOpLessEqual:
        case FcOpMore:
        case FcOpMoreEqual:
        case FcOpContains:
        case FcOpListing:
        case FcOpNotContains:
        case FcOpPlus:
        case FcOpMinus:
        case FcOpTimes:
        case FcOpDivide:
        case FcOpQuest:
        case FcOpComma:
    	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;
        }
    
        e->op = FcOpNil;
    }
    
    void
    FcEditDestroy (FcEdit *e)
    {
        if (e->next)
    	FcEditDestroy (e->next);
        if (e->expr)
    	FcExprDestroy (e->expr);
        free (e);
    }
    
    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)
    {
    
        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
    FcConfigMessage (FcConfigParse *parse, FcConfigSeverity severe, const char *fmt, ...)
    {
        const 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 %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 const char *
    FcTypeName (FcType type)
    {
        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)
        {
    	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));
        }
    }
    
    static void
    FcTypecheckExpr (FcConfigParse *parse, FcExpr *expr, FcType type)
    {
        const FcObjectType	*o;
        const FcConstant	*c;
        
        /* 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)
        {
    	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 test;
    }
    
    static FcEdit *
    FcEditCreate (FcConfigParse	*parse,
    	      FcObject		object,
    	      FcOp		op,
    	      FcExpr		*expr,
    	      FcValueBinding	binding)
    {
        FcEdit *e = (FcEdit *) malloc (sizeof (FcEdit));
    
        if (e)
        {
    	const FcObjectType	*o;
    
    	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)
    {
        FcVStack    *new;
    
        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
    FcVStackPushString (FcConfigParse *parse, FcVStackTag tag, FcChar8 *string)
    {
        FcVStack    *vstack = FcVStackCreateAndPush (parse);
        if (!vstack)
    	return FcFalse;
        vstack->u.string = string;
        vstack->tag = tag;
        return FcTrue;
    }
    
    static FcBool
    FcVStackPushInteger (FcConfigParse *parse, int integer)
    {
        FcVStack    *vstack = FcVStackCreateAndPush (parse);
        if (!vstack)
    	return FcFalse;
        vstack->u.integer = integer;
        vstack->tag = FcVStackInteger;
        return FcTrue;
    }
    
    static FcBool
    FcVStackPushDouble (FcConfigParse *parse, double _double)
    {
        FcVStack    *vstack = FcVStackCreateAndPush (parse);
        if (!vstack)
    	return FcFalse;
        vstack->u._double = _double;
        vstack->tag = FcVStackDouble;
        return FcTrue;
    }
    
    static FcBool
    FcVStackPushMatrix (FcConfigParse *parse, FcMatrix *matrix)
    {
        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;
    }
    
    static FcBool
    FcVStackPushBool (FcConfigParse *parse, FcBool bool_)
    {
        FcVStack    *vstack = FcVStackCreateAndPush (parse);
        if (!vstack)
    	return FcFalse;
        vstack->u.bool_ = bool_;
        vstack->tag = FcVStackBool;
        return FcTrue;
    }
    
    static FcBool
    FcVStackPushTest (FcConfigParse *parse, FcTest *test)
    {
        FcVStack    *vstack = FcVStackCreateAndPush (parse);
        if (!vstack)
    	return FcFalse;
        vstack->u.test = test;
        vstack->tag = FcVStackTest;
        return FcTrue;
    }
    
    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
    FcVStackPushEdit (FcConfigParse *parse, FcEdit *edit)
    {
        FcVStack    *vstack = FcVStackCreateAndPush (parse);
        if (!vstack)
    	return FcFalse;
        vstack->u.edit = edit;
        vstack->tag = FcVStackEdit;
        return FcTrue;
    }
    
    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 FcVStack *
    FcVStackFetch (FcConfigParse *parse, int off)
    {
        FcVStack    *vstack;
    
        for (vstack = parse->vstack; vstack && off-- > 0; vstack = vstack->prev);
        return vstack;
    }
    
    static FcVStack *
    FcVStackPeek (FcConfigParse *parse)
    {
        FcVStack	*vstack = parse->vstack;
    
        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 FcVStackFamily:
    	break;
        case FcVStackString:
        case FcVStackField:
        case FcVStackConstant:
        case FcVStackGlob:
    	FcStrFree (vstack->u.string);
    	break;
        case FcVStackPattern:
    	FcPatternDestroy (vstack->u.pattern);
    	break;
        case FcVStackInteger:
        case FcVStackDouble:
    	break;
        case FcVStackMatrix:
    	FcMatrixFree (vstack->u.matrix);
    	break;
        case FcVStackBool:
    	break;
        case FcVStackTest:
    	FcTestDestroy (vstack->u.test);
    	break;
        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)
    	{
    	    FcConfigMessage (0, FcSevereError, "out of memory");
    	    return 0;
    	}
    	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 (!parse->config->blanks)
    	    {
    		parse->config->blanks = FcBlanksCreate ();
    		if (!parse->config->blanks)
    		{
    		    FcConfigMessage (parse, FcSevereError, "out of memory");
    		    break;
    		}
    	    }
    	    if (!FcBlanksAdd (parse->config->blanks, v->u.integer))
    	    {
    		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)
    {
        FcChar8 *s, *end;
        int	    l;
        
        if (!parse->pstack)
    	return;
        s = FcStrBufDoneStatic (&parse->pstack->str);
        if (!s)
        {
    	FcConfigMessage (parse, FcSevereError, "out of memory");
    	return;
        }
        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);
    }
    
    /*
     * 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 > (int) 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 (buf_end > dot)
    		    buf_end -= dlen - 1;
    	    }
    	    if (end)
    		*end = buf_end;
    	}
        }
        else
    	v = strtod (s, end);
        return v;
    }
    
    static void
    FcParseDouble (FcConfigParse *parse)
    {
        FcChar8 *s, *end;
        double  d;
        
        if (!parse->pstack)
    	return;
        s = FcStrBufDoneStatic (&parse->pstack->str);
        if (!s)
        {
    	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);
    }
    
    static void
    FcParseString (FcConfigParse *parse, FcVStackTag tag)
    {
        FcChar8 *s;
        
        if (!parse->pstack)
    	return;
        s = FcStrBufDone (&parse->pstack->str);
        if (!s)
        {
    	FcConfigMessage (parse, FcSevereError, "out of memory");
    	return;
        }
        if (!FcVStackPushString (parse, tag, s))
    	FcStrFree (s);
    }
    
    static void
    FcParseMatrix (FcConfigParse *parse)
    {
        FcVStack	*vstack;
        enum { m_done, m_xx, m_xy, m_yx, m_yy } matrix_state = m_yy;
        FcMatrix	m;
        
        while ((vstack = FcVStackPeek (parse)))
        {
    	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;
    	}
    	FcVStackPopAndDestroy (parse);
    	matrix_state--;
        }
        if (matrix_state != m_done)
    	FcConfigMessage (parse, FcSevereError, "wrong number of matrix elements");
        else
    	FcVStackPushMatrix (parse, &m);
    }
    
    static FcBool
    FcConfigLexBool (FcConfigParse *parse, const FcChar8 *bool_)
    {
        FcBool  result = FcFalse;
    
        if (!FcNameBool (bool_, &result))
    	FcConfigMessage (parse, FcSevereWarning, "\"%s\" is not known boolean",
    			 bool_);
        return result;
    }
    
    static void
    FcParseBool (FcConfigParse *parse)
    {
        FcChar8 *s;
    
        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
    FcConfigLexBinding (FcConfigParse   *parse,
    		    const FcChar8   *binding_string,
    		    FcValueBinding  *binding_ret)
    {
        FcValueBinding 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 binding \"%s\"", binding_string);
    	    return FcFalse;
    	}
        }
        *binding_ret = binding;
        return FcTrue;
    }
    
    static void
    FcParseFamilies (FcConfigParse *parse, FcVStackTag tag)
    {
        FcVStack	*vstack;
        FcExpr	*left, *expr = 0, *new;
    
        while ((vstack = FcVStackPeek (parse)))
        {
    	if (vstack->tag != FcVStackFamily)
    	{
    	    FcConfigMessage (parse, FcSevereWarning, "non-family");
    	    FcVStackPopAndDestroy (parse);
    	    continue;
    	}
    	left = vstack->u.expr;
    	vstack->tag = FcVStackNone;
    	FcVStackPopAndDestroy (parse);
    	if (expr)
    	{
    	    new = FcExprCreateOp (parse->config, left, FcOpComma, expr);
    	    if (!new)
    	    {
    		FcConfigMessage (parse, FcSevereError, "out of memory");
    		FcExprDestroy (left);
    		FcExprDestroy (expr);
    		break;
    	    }
    	    expr = new;
    	}
    	else
    	    expr = left;
        }
        if (expr)
        {
    	if (!FcVStackPushExpr (parse, tag, expr))
    	{
    	    FcConfigMessage (parse, FcSevereError, "out of memory");
                FcExprDestroy (expr);
    	}
        }
    }
    
    static void
    FcParseFamily (FcConfigParse *parse)
    {
        FcChar8 *s;
        FcExpr  *expr;
    
        if (!parse->pstack)
    	return;
        s = FcStrBufDoneStatic (&parse->pstack->str);
        if (!s)
        {
    	FcConfigMessage (parse, FcSevereError, "out of memory");
    	return;
        }
        expr = FcExprCreateString (parse->config, s);
        FcStrBufDestroy (&parse->pstack->str);
        if (expr)
    	FcVStackPushExpr (parse, FcVStackFamily, expr);
    }
    
    static void
    FcParseAlias (FcConfigParse *parse)
    {
        FcExpr	*family = 0, *accept = 0, *prefer = 0, *def = 0, *new = 0;
        FcEdit	*edit = 0, *next;
        FcVStack	*vstack;
        FcTest	*test;
        FcValueBinding  binding;
    
        if (!FcConfigLexBinding (parse, FcConfigGetAttribute (parse, "binding"), &binding))
    	return;
        while ((vstack = FcVStackPeek (parse)))
        {
    	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)
        {
    	FcConfigMessage (parse, FcSevereError, "missing family in alias");
    	if (prefer)
    	    FcExprDestroy (prefer);
    	if (accept)
    	    FcExprDestroy (accept);
    	if (def)
    	    FcExprDestroy (def);
    	return;
        }
        if (prefer)
        {
    	edit = FcEditCreate (parse, 
    			     FC_FAMILY_OBJECT,
    			     FcOpPrepend,
    			     prefer,
    			     binding);
    	if (edit)
    	    edit->next = 0;
    	else
    	    FcExprDestroy (prefer);
        }
        if (accept)
        {
    	next = edit;
    	edit = FcEditCreate (parse,
    			     FC_FAMILY_OBJECT,
    			     FcOpAppend,
    			     accept,
    			     binding);
    	if (edit)
    	    edit->next = next;
    	else
    	    FcExprDestroy (accept);
        }
        if (def)
        {
    	next = edit;
    	edit = FcEditCreate (parse,
    			     FC_FAMILY_OBJECT,
    			     FcOpAppendLast,
    			     def,
    			     binding);
    	if (edit)
    	    edit->next = next;
    	else
    	    FcExprDestroy (def);
        }
        if (edit)
        {
    	test = FcTestCreate (parse, FcMatchPattern,
    			     FcQualAny,
    			     (FcChar8 *) FC_FAMILY,
    			     FcOpEqual,
    			     family);
    	if (test)
    	    if (!FcConfigAddEdit (parse->config, test, edit, FcMatchPattern))
    		FcTestDestroy (test);
        }
        else
    	FcExprDestroy (family);
    }
    
    static FcExpr *
    FcPopExpr (FcConfigParse *parse)
    {
        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)
    	{
    	    new = FcExprCreateOp (parse->config, left, op, expr);
    	    if (!new)
    	    {
    		FcConfigMessage (parse, FcSevereError, "out of memory");
    		FcExprDestroy (left);
    		FcExprDestroy (expr);
    		return 0;
    	    }
    	    expr = new;
    	}
    	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)
    	{
    	    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);
    }
    
    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
    	{
    	    FcConfigMessage (parse, FcSevereWarning, "invalid test target \"%s\"", kind_string);
    	    return;
    	}
        }
        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
    	{
    	    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;
    	}
    #ifdef _WIN32
    	if (strcmp (data, "CUSTOMFONTDIR") == 0)
    	{
    		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);
    	}
    	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)
    	{
    	    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");
    	}
    #endif
    	if (!FcStrUsesHome (data) || FcConfigHome ())
    	{
    	    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;
        }
        (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 *name,
    		      FcBool	    complain)
    {
    
        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))
        {
            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 (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 (name)
    	    FcConfigMessage (0, FcSevereError, "Cannot load config file \"%s\"", name);
    	else
    	    FcConfigMessage (0, FcSevereError, "Cannot load default config file");
    	return FcFalse;
        }
        return FcTrue;
    }
    #define __fcxml__
    #include "fcaliastail.h"
    #undef __fcxml__