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