/*
 * 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 the author(s) not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  The authors make 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
#include <mbstring.h>
#endif

static void
FcExprDestroy (FcExpr *e);

void
FcTestDestroy (FcTest *test)
{
    FcExprDestroy (test->expr);
    free (test);
}

void
FcRuleDestroy (FcRule *rule)
{
    FcRule *n = rule->next;

    switch (rule->type) {
    case FcRuleTest:
	FcTestDestroy (rule->u.test);
	break;
    case FcRuleEdit:
	FcEditDestroy (rule->u.edit);
	break;
    default:
	break;
    }
    free (rule);
    if (n)
	FcRuleDestroy (n);
}

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 = FcStrdup (s);
    }
    return e;
}

static FcExprMatrix *
FcExprMatrixCopyShallow (const FcExprMatrix *matrix)
{
  FcExprMatrix *m = malloc (sizeof (FcExprMatrix));
  if (m)
  {
    *m = *matrix;
  }
  return m;
}

static void
FcExprMatrixFreeShallow (FcExprMatrix *m)
{
  if (!m)
    return;

  free (m);
}

static void
FcExprMatrixFree (FcExprMatrix *m)
{
  if (!m)
    return;

  FcExprDestroy (m->xx);
  FcExprDestroy (m->xy);
  FcExprDestroy (m->yx);
  FcExprDestroy (m->yy);

  free (m);
}

static FcExpr *
FcExprCreateMatrix (FcConfig *config, const FcExprMatrix *matrix)
{
    FcExpr *e = FcConfigAllocExpr (config);
    if (e)
    {
	e->op = FcOpMatrix;
	e->u.mexpr = FcExprMatrixCopyShallow (matrix);
    }
    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 *
FcExprCreateCharSet (FcConfig *config, FcCharSet *charset)
{
    FcExpr *e = FcConfigAllocExpr (config);
    if (e)
    {
	e->op = FcOpCharSet;
	e->u.cval = FcCharSetCopy (charset);
    }
    return e;
}

static FcExpr *
FcExprCreateLangSet (FcConfig *config, FcLangSet *langset)
{
    FcExpr *e = FcConfigAllocExpr (config);
    if (e)
    {
	e->op = FcOpLangSet;
	e->u.lval = FcLangSetCopy (langset);
    }
    return e;
}

static FcExpr *
FcExprCreateName (FcConfig *config, FcExprName name)
{
    FcExpr *e = FcConfigAllocExpr (config);
    if (e)
    {
	e->op = FcOpField;
	e->u.name = name;
    }
    return e;
}

static FcExpr *
FcExprCreateConst (FcConfig *config, const FcChar8 *constant)
{
    FcExpr *e = FcConfigAllocExpr (config);
    if (e)
    {
	e->op = FcOpConst;
	e->u.constant = FcStrdup (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 (FC_OP_GET_OP (e->op)) {
    case FcOpInteger:
	break;
    case FcOpDouble:
	break;
    case FcOpString:
	FcFree (e->u.sval);
	break;
    case FcOpMatrix:
	FcExprMatrixFree (e->u.mexpr);
	break;
    case FcOpRange:
	break;
    case FcOpCharSet:
	FcCharSetDestroy (e->u.cval);
	break;
    case FcOpLangSet:
	FcLangSetDestroy (e->u.lval);
	break;
    case FcOpBool:
	break;
    case FcOpField:
	break;
    case FcOpConst:
	FcFree (e->u.constant);
	break;
    case FcOpAssign:
    case FcOpAssignReplace:
    case FcOpPrepend:
    case FcOpPrependFirst:
    case FcOpAppend:
    case FcOpAppendLast:
    case FcOpDelete:
    case FcOpDeleteAll:
	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->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,
    FcElementRange,
    FcElementBool,
    FcElementCharSet,
    FcElementLangSet,
    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 },
    { "range",		FcElementRange },
    { "bool",		FcElementBool },
    { "charset",	FcElementCharSet },
    { "langset",	FcElementLangSet },
    { "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,
    FcVStackConstant,
    FcVStackGlob,
    FcVStackName,
    FcVStackPattern,

    FcVStackPrefer,
    FcVStackAccept,
    FcVStackDefault,

    FcVStackInteger,
    FcVStackDouble,
    FcVStackMatrix,
    FcVStackRange,
    FcVStackBool,
    FcVStackCharSet,
    FcVStackLangSet,

    FcVStackTest,
    FcVStackExpr,
    FcVStackEdit
} FcVStackTag;

typedef struct _FcVStack {
    struct _FcVStack	*prev;
    FcPStack		*pstack;	/* related parse element */
    FcVStackTag		tag;
    union {
	FcChar8		*string;

	int		integer;
	double		_double;
	FcExprMatrix	*matrix;
	FcRange		range;
	FcBool		bool_;
	FcCharSet	*charset;
	FcLangSet	*langset;
	FcExprName	name;

	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;
    unsigned int    pstack_static_used;
    FcPStack        pstack_static[8];
    unsigned 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 FcExpr *
FcPopExpr (FcConfigParse *parse);


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;
	/* It's perfectly fine to use user-define elements in expressions,
	 * so don't warn in that case. */
	if (value == (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 (FC_OP_GET_OP (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 FcOpLangSet:
	FcTypecheckValue (parse, FcTypeLangSet, type);
	break;
    case FcOpNil:
	break;
    case FcOpField:
	o = FcNameGetObjectType (FcObjectName (expr->u.name.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,
	      unsigned int  compare,
	      FcExpr	    *expr)
{
    FcTest	*test = (FcTest *) malloc (sizeof (FcTest));

    if (test)
    {
	const FcObjectType	*o;
	
	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->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 FcRule *
FcRuleCreate (FcRuleType type,
	      void       *p)
{
    FcRule *r = (FcRule *) malloc (sizeof (FcRule));

    if (!r)
	return NULL;

    r->next = NULL;
    r->type = type;
    switch (type)
    {
    case FcRuleTest:
	r->u.test = (FcTest *) p;
	break;
    case FcRuleEdit:
	r->u.edit = (FcEdit *) p;
	break;
    default:
	free (r);
	r = NULL;
	break;
    }

    return r;
}

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;
    }
    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, FcExprMatrix *matrix)
{
    FcVStack    *vstack;
    vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
    vstack->u.matrix = FcExprMatrixCopyShallow (matrix);
    vstack->tag = FcVStackMatrix;
    return FcTrue;
}

static FcBool
FcVStackPushRange (FcConfigParse *parse, FcRange *range)
{
    FcVStack	*vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
    vstack->u.range.begin = range->begin;
    vstack->u.range.end = range->end;
    vstack->tag = FcVStackRange;
    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
FcVStackPushCharSet (FcConfigParse *parse, FcCharSet *charset)
{
    FcVStack	*vstack;
    if (!charset)
	return FcFalse;
    vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
    vstack->u.charset = charset;
    vstack->tag = FcVStackCharSet;
    return FcTrue;
}

static FcBool
FcVStackPushLangSet (FcConfigParse *parse, FcLangSet *langset)
{
    FcVStack	*vstack;
    if (!langset)
	return FcFalse;
    vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
    vstack->u.langset = langset;
    vstack->tag = FcVStackLangSet;
    return FcTrue;
}

static FcBool
FcVStackPushName (FcConfigParse *parse, FcMatchKind kind, FcObject object)
{
    FcVStack    *vstack = FcVStackCreateAndPush (parse);
    if (!vstack)
	return FcFalse;
    vstack->u.name.object = object;
    vstack->u.name.kind = kind;
    vstack->tag = FcVStackName;
    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 FcVStackName:
	break;
    case FcVStackFamily:
	break;
    case FcVStackString:
    case FcVStackConstant:
    case FcVStackGlob:
	FcStrFree (vstack->u.string);
	break;
    case FcVStackPattern:
	FcPatternDestroy (vstack->u.pattern);
	break;
    case FcVStackInteger:
    case FcVStackDouble:
	break;
    case FcVStackMatrix:
	FcExprMatrixFreeShallow (vstack->u.matrix);
	break;
    case FcVStackRange:
    case FcVStackBool:
	break;
    case FcVStackCharSet:
	FcCharSetDestroy (vstack->u.charset);
	break;
    case FcVStackLangSet:
	FcLangSetDestroy (vstack->u.langset);
	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
	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;
	}
    }
    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;
    }

    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;
    }

    if (parse->pstack->attr)
    {
	/* Warn about unused attrs. */
	FcChar8 **attrs = parse->pstack->attr;
	while (*attrs)
	{
	    if (attrs[0][0])
	    {
		FcConfigMessage (parse, FcSevereError, "invalid attribute '%s'", attrs[0]);
	    }
	    attrs += 2;
	}
    }

    FcVStackClear (parse);
    old = parse->pstack;
    parse->pstack = old->prev;
    FcStrBufDestroy (&old->str);

    if (old->attr && old->attr != old->attr_buf_static)
	free (old->attr);

    if (old == &parse->pstack_static[parse->pstack_static_used - 1])
	parse->pstack_static_used--;
    else
	free (old);
    return FcTrue;
}

static FcBool
FcConfigParseInit (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))
	{
	    attrs[0][0] = '\0'; /* Mark as used. */
	    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);
    FcChar32	i;
    while (n-- > 0)
    {
	FcVStack    *v = FcVStackFetch (parse, n);
	if (!parse->config->blanks)
	{
	    parse->config->blanks = FcBlanksCreate ();
	    if (!parse->config->blanks)
		goto bail;
	}
	switch ((int) v->tag) {
	case FcVStackInteger:
	    if (!FcBlanksAdd (parse->config->blanks, v->u.integer))
		goto bail;
	    break;
	case FcVStackRange:
	    if (v->u.range.begin <= v->u.range.end)
	    {
	      for (i = v->u.range.begin; i <= v->u.range.end; i++)
	      {
		  if (!FcBlanksAdd (parse->config->blanks, i))
		      goto bail;
	      }
	    }
	    break;
	default:
	    FcConfigMessage (parse, FcSevereError, "invalid element in blank");
	    break;
	}
    }
    return;
  bail:
    FcConfigMessage (parse, FcSevereError, "out of memory");
}

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
FcParseName (FcConfigParse *parse)
{
    const FcChar8   *kind_string;
    FcMatchKind	    kind;
    FcChar8 *s;
    FcObject object;

    kind_string = FcConfigGetAttribute (parse, "target");
    if (!kind_string)
	kind = FcMatchDefault;
    else
    {
	if (!strcmp ((char *) kind_string, "pattern"))
	    kind = FcMatchPattern;
	else if (!strcmp ((char *) kind_string, "font"))
	    kind = FcMatchFont;
	else if (!strcmp ((char *) kind_string, "default"))
	    kind = FcMatchDefault;
	else
	{
	    FcConfigMessage (parse, FcSevereWarning, "invalid name target \"%s\"", kind_string);
	    return;
	}
    }

    if (!parse->pstack)
	return;
    s = FcStrBufDone (&parse->pstack->str);
    if (!s)
    {
	FcConfigMessage (parse, FcSevereError, "out of memory");
	return;
    }
    object = FcObjectFromName ((const char *) s);

    FcVStackPushName (parse, kind, object);

    FcStrFree (s);
}

static void
FcParseMatrix (FcConfigParse *parse)
{
    FcExprMatrix m;

    m.yy = FcPopExpr (parse);
    m.yx = FcPopExpr (parse);
    m.xy = FcPopExpr (parse);
    m.xx = FcPopExpr (parse);

    if (FcPopExpr (parse))
      FcConfigMessage (parse, FcSevereError, "wrong number of matrix elements");
    else
      FcVStackPushMatrix (parse, &m);
}

static void
FcParseRange (FcConfigParse *parse)
{
    FcVStack	*vstack;
    FcRange	r = {0, 0};
    FcChar32	n;
    int		count = 1;

    while ((vstack = FcVStackPeek (parse)))
    {
	if (count < 0)
	{
	    FcConfigMessage (parse, FcSevereError, "too many elements in range");
	    return;
	}
	switch ((int) vstack->tag) {
	case FcVStackInteger:
	    n = vstack->u.integer;
	    break;
	default:
	    FcConfigMessage (parse, FcSevereError, "invalid element in range");
	    n = 0;
	    break;
	}
	if (count == 1)
	    r.end = n;
	else
	    r.begin = n;
	count--;
	FcVStackPopAndDestroy (parse);
    }
    if (count < 0)
    {
	if (r.begin > r.end)
	{
	    FcConfigMessage (parse, FcSevereError, "invalid range");
	    return;
	}
	FcVStackPushRange (parse, &r);
    }
    else
	FcConfigMessage (parse, FcSevereError, "invalid range");
}

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 void
FcParseCharSet (FcConfigParse *parse)
{
    FcVStack	*vstack;
    FcCharSet	*charset = FcCharSetCreate ();
    FcChar32	i;
    int n = 0;

    while ((vstack = FcVStackPeek (parse)))
    {
	switch ((int) vstack->tag) {
	case FcVStackInteger:
	    if (!FcCharSetAddChar (charset, vstack->u.integer))
	    {
		FcConfigMessage (parse, FcSevereWarning, "invalid character: 0x%04x", vstack->u.integer);
	    }
	    else
		n++;
	    break;
	case FcVStackRange:
	    if (vstack->u.range.begin <= vstack->u.range.end)
	    {
	      for (i = vstack->u.range.begin; i <= vstack->u.range.end; i++)
	      {
		  if (!FcCharSetAddChar (charset, i))
		  {
		      FcConfigMessage (parse, FcSevereWarning, "invalid character: 0x%04x", i);
		  }
		  else
		      n++;
	      }
	    }
	    break;
	default:
		FcConfigMessage (parse, FcSevereError, "invalid element in charset");
		break;
	}
	FcVStackPopAndDestroy (parse);
    }
    if (n > 0)
	    FcVStackPushCharSet (parse, charset);
    else
	    FcCharSetDestroy (charset);
}

static void
FcParseLangSet (FcConfigParse *parse)
{
    FcVStack	*vstack;
    FcLangSet	*langset = FcLangSetCreate ();
    int n = 0;

    while ((vstack = FcVStackPeek (parse)))
    {
	switch ((int) vstack->tag) {
	case FcVStackString:
	    if (!FcLangSetAdd (langset, vstack->u.string))
	    {
		FcConfigMessage (parse, FcSevereWarning, "invalid langset: %s", vstack->u.string);
	    }
	    else
		n++;
	    break;
	default:
		FcConfigMessage (parse, FcSevereError, "invalid element in langset");
		break;
	}
	FcVStackPopAndDestroy (parse);
    }
    if (n > 0)
	    FcVStackPushLangSet (parse, langset);
    else
	    FcLangSetDestroy (langset);
}

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;
    FcVStack	*vstack;
    FcRule	*rule = NULL, *r;
    FcValueBinding  binding;

    if (!FcConfigLexBinding (parse, FcConfigGetAttribute (parse, "binding"), &binding))
	return;
    while ((vstack = FcVStackPeek (parse)))
    {
	switch ((int) vstack->tag) {
	case FcVStackFamily:
	    if (family)
	    {
		FcConfigMessage (parse, FcSevereWarning, "Having multiple <family> in <alias> isn't supported and may not work as expected");
		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;
	case FcVStackTest:
	    if (rule)
	    {
		r = FcRuleCreate (FcRuleTest, vstack->u.test);
		r->next = rule;
		rule = r;
	    }
	    else
		rule = FcRuleCreate (FcRuleTest, vstack->u.test);
	    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);
	if (rule)
	    FcRuleDestroy (rule);
	return;
    }
    if (!prefer &&
	!accept &&
	!def)
    {
	FcExprDestroy (family);
	return;
    }
    else
    {
	FcTest *t = FcTestCreate (parse, FcMatchPattern,
				  FcQualAny,
				  (FcChar8 *) FC_FAMILY,
				  FC_OP (FcOpEqual, FcOpFlagIgnoreBlanks),
				  family);
	if (rule)
	{
	    for (r = rule; r->next; r = r->next);
	    r->next = FcRuleCreate (FcRuleTest, t);
	    r = r->next;
	}
	else
	{
	    r = rule = FcRuleCreate (FcRuleTest, t);
	}
    }
    if (prefer)
    {
	edit = FcEditCreate (parse,
			     FC_FAMILY_OBJECT,
			     FcOpPrepend,
			     prefer,
			     binding);
	if (!edit)
	    FcExprDestroy (prefer);
	else
	{
	    r->next = FcRuleCreate (FcRuleEdit, edit);
	    r = r->next;
	}
    }
    if (accept)
    {
	edit = FcEditCreate (parse,
			     FC_FAMILY_OBJECT,
			     FcOpAppend,
			     accept,
			     binding);
	if (!edit)
	    FcExprDestroy (accept);
	else
	{
	    r->next = FcRuleCreate (FcRuleEdit, edit);
	    r = r->next;
	}
    }
    if (def)
    {
	edit = FcEditCreate (parse,
			     FC_FAMILY_OBJECT,
			     FcOpAppendLast,
			     def,
			     binding);
	if (!edit)
	    FcExprDestroy (def);
	else
	{
	    r->next = FcRuleCreate (FcRuleEdit, edit);
	    r = r->next;
	}
    }
    if (!FcConfigAddRule (parse->config, rule, FcMatchPattern))
	FcRuleDestroy (rule);
}

static FcExpr *
FcPopExpr (FcConfigParse *parse)
{
    FcVStack	*vstack = FcVStackPeek (parse);
    FcExpr	*expr = 0;
    if (!vstack)
	return 0;
    switch ((int) vstack->tag) {
    case FcVStackNone:
	break;
    case FcVStackString:
    case FcVStackFamily:
	expr = FcExprCreateString (parse->config, vstack->u.string);
	break;
    case FcVStackName:
	expr = FcExprCreateName (parse->config, vstack->u.name);
	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 FcVStackRange:
	break;
    case FcVStackBool:
	expr = FcExprCreateBool (parse->config, vstack->u.bool_);
	break;
    case FcVStackCharSet:
	expr = FcExprCreateCharSet (parse->config, vstack->u.charset);
	break;
    case FcVStackLangSet:
	expr = FcExprCreateLangSet (parse->config, vstack->u.langset);
	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
FcParseDir (FcConfigParse *parse)
{
    const FcChar8 *attr, *data;
    FcChar8 *prefix = NULL, *p;
#ifdef _WIN32
    FcChar8         buffer[1000];
#endif

    attr = FcConfigGetAttribute (parse, "prefix");
    if (attr && FcStrCmp (attr, (const FcChar8 *)"xdg") == 0)
	prefix = FcConfigXdgDataHome ();
    data = FcStrBufDoneStatic (&parse->pstack->str);
    if (!data)
    {
	FcConfigMessage (parse, FcSevereError, "out of memory");
	data = prefix;
	goto bail;
    }
    if (prefix)
    {
	size_t plen = strlen ((const char *)prefix);
	size_t dlen = strlen ((const char *)data);

	p = realloc (prefix, plen + 1 + dlen + 1);
	if (!p)
	{
	    FcConfigMessage (parse, FcSevereError, "out of memory");
	    goto bail;
	}
	prefix = p;
	prefix[plen] = FC_DIR_SEPARATOR;
	memcpy (&prefix[plen + 1], data, dlen);
	prefix[plen + 1 + dlen] = 0;
	data = prefix;
    }
#ifdef _WIN32
    if (strcmp ((const char *) data, "CUSTOMFONTDIR") == 0)
    {
	FcChar8 *p;
	data = buffer;
	if (!GetModuleFileName (NULL, (LPCH) buffer, sizeof (buffer) - 20))
	{
	    FcConfigMessage (parse, FcSevereError, "GetModuleFileName failed");
	    goto bail;
	}
	/*
	 * 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 ((char *) data, "\\fonts");
    }
    else if (strcmp ((const char *) data, "APPSHAREFONTDIR") == 0)
    {
	FcChar8 *p;
	data = buffer;
	if (!GetModuleFileName (NULL, (LPCH) buffer, sizeof (buffer) - 20))
	{
	    FcConfigMessage (parse, FcSevereError, "GetModuleFileName failed");
	    goto bail;
	}
	p = _mbsrchr (data, '\\');
	if (p) *p = '\0';
	strcat ((char *) data, "\\..\\share\\fonts");
    }
    else if (strcmp ((const char *) data, "WINDOWSFONTDIR") == 0)
    {
	int rc;
	data = buffer;
	rc = pGetSystemWindowsDirectory ((LPSTR) buffer, sizeof (buffer) - 20);
	if (rc == 0 || rc > sizeof (buffer) - 20)
	{
	    FcConfigMessage (parse, FcSevereError, "GetSystemWindowsDirectory failed");
	    goto bail;
	}
	if (data [strlen ((const char *) data) - 1] != '\\')
	    strcat ((char *) data, "\\");
	strcat ((char *) 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);

  bail:
    if (prefix)
	FcStrFree (prefix);
}

static void
FcParseCacheDir (FcConfigParse *parse)
{
    const FcChar8 *attr;
    FcChar8 *prefix = NULL, *p, *data;

    attr = FcConfigGetAttribute (parse, "prefix");
    if (attr && FcStrCmp (attr, (const FcChar8 *)"xdg") == 0)
	prefix = FcConfigXdgCacheHome ();
    data = FcStrBufDone (&parse->pstack->str);
    if (!data)
    {
	FcConfigMessage (parse, FcSevereError, "out of memory");
	goto bail;
    }
    if (prefix)
    {
	size_t plen = strlen ((const char *)prefix);
	size_t dlen = strlen ((const char *)data);

	p = realloc (prefix, plen + 1 + dlen + 1);
	if (!p)
	{
	    FcConfigMessage (parse, FcSevereError, "out of memory");
	    data = prefix;
	    goto bail;
	}
	prefix = p;
	prefix[plen] = FC_DIR_SEPARATOR;
	memcpy (&prefix[plen + 1], data, dlen);
	prefix[plen + 1 + dlen] = 0;
	FcStrFree (data);
	data = prefix;
    }
#ifdef _WIN32
    if (strcmp ((const char *) data, "WINDOWSTEMPDIR_FONTCONFIG_CACHE") == 0)
    {
	int rc;
	FcStrFree (data);
	data = malloc (1000);
	if (!data)
	{
	    FcConfigMessage (parse, FcSevereError, "out of memory");
	    goto bail;
	}
	rc = GetTempPath (800, (LPSTR) data);
	if (rc == 0 || rc > 800)
	{
	    FcConfigMessage (parse, FcSevereError, "GetTempPath failed");
	    goto bail;
	}
	if (data [strlen ((const char *) data) - 1] != '\\')
	    strcat ((char *) data, "\\");
	strcat ((char *) data, "fontconfig\\cache");
    }
    else if (strcmp ((const char *) data, "LOCAL_APPDATA_FONTCONFIG_CACHE") == 0)
    {
	char szFPath[MAX_PATH + 1];
	size_t len;

	if (!(pSHGetFolderPathA && SUCCEEDED(pSHGetFolderPathA(NULL, /* CSIDL_LOCAL_APPDATA */ 28, NULL, 0, szFPath))))
	{
	    FcConfigMessage (parse, FcSevereError, "SHGetFolderPathA failed");
	    goto bail;
	}
	strncat(szFPath, "\\fontconfig\\cache", MAX_PATH - 1 - strlen(szFPath));
	len = strlen(szFPath) + 1;
	FcStrFree (data);
	data = malloc(len);
	if (!data)
	{
	    FcConfigMessage (parse, FcSevereError, "out of memory");
	    goto bail;
	}
	strncpy((char *) data, szFPath, len);
    }
#endif
    if (strlen ((char *) data) == 0)
	FcConfigMessage (parse, FcSevereWarning, "empty cache directory name ignored");
    else if (!FcStrUsesHome (data) || FcConfigHome ())
    {
	if (!FcConfigAddCacheDir (parse->config, data))
	    FcConfigMessage (parse, FcSevereError, "out of memory; cannot add cache directory %s", data);
    }
    FcStrBufDestroy (&parse->pstack->str);

  bail:
    if (data)
	FcStrFree (data);
}

static void
FcParseInclude (FcConfigParse *parse)
{
    FcChar8	    *s;
    const FcChar8   *attr;
    FcBool	    ignore_missing = FcFalse;
    FcBool	    deprecated = FcFalse;
    FcChar8	    *prefix = NULL, *p;
    static FcChar8  *userdir = NULL;
    static FcChar8  *userconf = NULL;

    s = FcStrBufDoneStatic (&parse->pstack->str);
    if (!s)
    {
	FcConfigMessage (parse, FcSevereError, "out of memory");
	goto bail;
    }
    attr = FcConfigGetAttribute (parse, "ignore_missing");
    if (attr && FcConfigLexBool (parse, (FcChar8 *) attr) == FcTrue)
	ignore_missing = FcTrue;
    attr = FcConfigGetAttribute (parse, "deprecated");
    if (attr && FcConfigLexBool (parse, (FcChar8 *) attr) == FcTrue)
        deprecated = FcTrue;
    attr = FcConfigGetAttribute (parse, "prefix");
    if (attr && FcStrCmp (attr, (const FcChar8 *)"xdg") == 0)
	prefix = FcConfigXdgConfigHome ();
    if (prefix)
    {
	size_t plen = strlen ((const char *)prefix);
	size_t dlen = strlen ((const char *)s);

	p = realloc (prefix, plen + 1 + dlen + 1);
	if (!p)
	{
	    FcConfigMessage (parse, FcSevereError, "out of memory");
	    goto bail;
	}
	prefix = p;
	prefix[plen] = FC_DIR_SEPARATOR;
	memcpy (&prefix[plen + 1], s, dlen);
	prefix[plen + 1 + dlen] = 0;
	s = prefix;
	if (FcFileIsDir (s))
	{
	userdir:
	    if (!userdir)
		userdir = FcStrdup (s);
	}
	else if (FcFileIsFile (s))
	{
	userconf:
	    if (!userconf)
		userconf = FcStrdup (s);
	}
	else
	{
	    /* No config dir nor file on the XDG directory spec compliant place
	     * so need to guess what it is supposed to be.
	     */
	    if (FcStrStr (s, (const FcChar8 *)"conf.d") != NULL)
		goto userdir;
	    else
		goto userconf;
	}
    }
    if (!FcConfigParseAndLoad (parse->config, s, !ignore_missing))
	parse->error = FcTrue;
#ifndef _WIN32
    else
    {
        FcChar8 *filename;
	static FcBool warn_conf = FcFalse, warn_confd = FcFalse;

        filename = FcConfigFilename(s);
	if (deprecated == FcTrue &&
	    filename != NULL &&
	    !FcFileIsLink (filename))
	{
	    if (FcFileIsDir (filename))
	    {
		FcChar8 *parent = FcStrDirname (userdir);

		if (!FcFileIsDir (parent))
		    FcMakeDirectory (parent);
		FcStrFree (parent);
		if (FcFileIsDir (userdir) ||
		    rename ((const char *)filename, (const char *)userdir) != 0 ||
		    symlink ((const char *)userdir, (const char *)filename) != 0)
		{
		    if (!warn_confd)
		    {
			FcConfigMessage (parse, FcSevereWarning, "reading configurations from %s is deprecated. please move it to %s manually", s, userdir);
			warn_confd = FcTrue;
		    }
		}
	    }
	    else
	    {
		FcChar8 *parent = FcStrDirname (userconf);

		if (!FcFileIsDir (parent))
		    FcMakeDirectory (parent);
		FcStrFree (parent);
		if (FcFileIsFile (userconf) ||
		    rename ((const char *)filename, (const char *)userconf) != 0 ||
		    symlink ((const char *)userconf, (const char *)filename) != 0)
		{
		    if (!warn_conf)
		    {
			FcConfigMessage (parse, FcSevereWarning, "reading configurations from %s is deprecated. please move it to %s manually", s, userconf);
			warn_conf = FcTrue;
		    }
		}
	    }
        }
        if(filename)
            FcStrFree(filename);
    }
#endif
    FcStrBufDestroy (&parse->pstack->str);

  bail:
    if (prefix)
	FcStrFree (prefix);
}

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;
    const FcChar8   *iblanks_string;
    int              flags = 0;

    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;
	}
    }
    iblanks_string = FcConfigGetAttribute (parse, "ignore-blanks");
    if (iblanks_string)
    {
	FcBool f = FcFalse;

	if (!FcNameBool (iblanks_string, &f))
	{
	    FcConfigMessage (parse,
			     FcSevereWarning,
			     "invalid test ignore-blanks \"%s\"", iblanks_string);
	}
	if (f)
	    flags |= FcOpFlagIgnoreBlanks;
    }
    expr = FcPopBinary (parse, FcOpComma);
    if (!expr)
    {
	FcConfigMessage (parse, FcSevereWarning, "missing test expression");
	return;
    }
    if (expr->op == FcOpComma)
    {
	FcConfigMessage (parse, FcSevereWarning, "Having multiple values in <test> isn't supported and may not work as expected");
    }
    test = FcTestCreate (parse, kind, qual, name, FC_OP (compare, flags), 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	    },
    { "delete",		FcOpDelete	    },
    { "delete_all",	FcOpDeleteAll	    },
};

#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);
    if ((mode == FcOpDelete || mode == FcOpDeleteAll) &&
	expr != NULL)
    {
	FcConfigMessage (parse, FcSevereWarning, "Expression doesn't take any effects for delete and delete_all");
	FcExprDestroy (expr);
	expr = NULL;
    }
    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;
    FcVStack	    *vstack;
    FcRule	    *rule = NULL, *r;

    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 ((int) vstack->tag) {
	case FcVStackTest:
	    r = FcRuleCreate (FcRuleTest, vstack->u.test);
	    if (rule)
		r->next = rule;
	    rule = r;
	    vstack->tag = FcVStackNone;
	    break;
	case FcVStackEdit:
	    if (kind == FcMatchScan && vstack->u.edit->object > FC_MAX_BASE_OBJECT)
	    {
		FcConfigMessage (parse, FcSevereError,
				 "<match target=\"scan\"> cannot edit user-defined object \"%s\"",
				 FcObjectName(vstack->u.edit->object));
		if (rule)
		    FcRuleDestroy (rule);
		return;
	    }
	    r = FcRuleCreate (FcRuleEdit, vstack->u.edit);
	    if (rule)
		r->next = rule;
	    rule = r;
	    vstack->tag = FcVStackNone;
	    break;
	default:
	    FcConfigMessage (parse, FcSevereWarning, "invalid match element");
	    break;
	}
	FcVStackPopAndDestroy (parse);
    }
    if (!FcConfigAddRule (parse->config, rule, kind))
	FcConfigMessage (parse, FcSevereError, "out of memory");
}

static void
FcParseAcceptRejectFont (FcConfigParse *parse, FcElement element)
{
    FcVStack	*vstack;

    while ((vstack = FcVStackPeek (parse)))
    {
	switch ((int) 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 ((int) vstack->tag) {
    case FcVStackString:
	value.u.s = FcStrdup (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 = FcTypeDouble;
	break;
    case FcVStackBool:
	value.u.b = vstack->u.bool_;
	value.type = FcTypeBool;
	break;
    case FcVStackCharSet:
	value.u.c = FcCharSetCopy (vstack->u.charset);
	if (value.u.c)
	    value.type = FcTypeCharSet;
	break;
    case FcVStackLangSet:
	value.u.l = FcLangSetCopy (vstack->u.langset);
	if (value.u.l)
	    value.type = FcTypeLangSet;
	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 ((int) 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 FC_UNUSED)
{
    FcConfigParse   *parse = userData;
    FcChar8	    *data;

    if (!parse->pstack)
	return;
    switch (parse->pstack->element) {
    case FcElementNone:
	break;
    case FcElementFontconfig:
	break;
    case FcElementDir:
	FcParseDir (parse);
	break;
    case FcElementCacheDir:
	FcParseCacheDir (parse);
	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 FcElementRange:
	FcParseRange (parse);
	break;
    case FcElementBool:
	FcParseBool (parse);
	break;
    case FcElementCharSet:
	FcParseCharSet (parse);
	break;
    case FcElementLangSet:
	FcParseLangSet (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:
	FcParseName (parse);
	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 FC_UNUSED,
		    const XML_Char  *pubid FC_UNUSED,
		    int		    has_internal_subset FC_UNUSED)
{
    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 FC_UNUSED)
{
}

#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;
}

#ifdef _WIN32
pfnGetSystemWindowsDirectory pGetSystemWindowsDirectory = NULL;
pfnSHGetFolderPathA pSHGetFolderPathA = NULL;
#endif

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

#ifdef _WIN32
    if (!pGetSystemWindowsDirectory)
    {
        HMODULE hk32 = GetModuleHandleA("kernel32.dll");
        if (!(pGetSystemWindowsDirectory = (pfnGetSystemWindowsDirectory) GetProcAddress(hk32, "GetSystemWindowsDirectoryA")))
            pGetSystemWindowsDirectory = (pfnGetSystemWindowsDirectory) GetWindowsDirectory;
    }
    if (!pSHGetFolderPathA)
    {
        HMODULE hSh = LoadLibraryA("shfolder.dll");
        /* the check is done later, because there is no provided fallback */
        if (hSh)
            pSHGetFolderPathA = (pfnSHGetFolderPathA) GetProcAddress(hSh, "SHGetFolderPathA");
    }
#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 = FcOpen ((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 (!FcConfigParseInit (&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__
