/* * 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 #include "fcint.h" #include #include #ifdef HAVE_DIRENT_H #include #endif #ifdef ENABLE_LIBXML2 #include #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 #else #include #endif #endif /* ENABLE_LIBXML2 */ #ifdef _WIN32 #include extern FcChar8 fontconfig_instprefix[]; #endif static FcChar8 *__fc_userdir = NULL; static FcChar8 *__fc_userconf = NULL; static void FcExprDestroy (FcExpr *e); static FcBool _FcConfigParse (FcConfig *config, const FcChar8 *name, FcBool complain, FcBool load); 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; case FcRuleUnknown: 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 * FcExprCreateRange (FcConfig *config, FcRange *range) { FcExpr *e = FcConfigAllocExpr (config); if (e) { e->op = FcOpRange; e->u.rval = FcRangeCopy (range); } 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: FcRangeDestroy (e->u.rval); 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, FcElementDescription, FcElementRemapDir, FcElementResetDirs, 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 }, { "description", FcElementDescription }, { "remap-dir", FcElementRemapDir }, { "reset-dirs", FcElementResetDirs }, { "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 const char *fcElementIgnoreName[16] = { "its:", NULL }; 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; for (i = 0; fcElementIgnoreName[i] != NULL; i++) if (!strncmp ((char *) name, fcElementIgnoreName[i], strlen (fcElementIgnoreName[i]))) return FcElementNone; return FcElementUnknown; } static const char * FcElementReverseMap (FcElement e) { int i; for (i = 0; i < NUM_ELEMENT_MAPS; i++) if (fcElementMap[i].element == e) return fcElementMap[i].name; return NULL; } 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; FcRuleSet *ruleset; XML_Parser parser; unsigned int pstack_static_used; FcPStack pstack_static[8]; unsigned int vstack_static_used; FcVStack vstack_static[64]; FcBool scanOnly; } 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"; case FcTypeRange: return "range"; case FcTypeUnknown: 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) || (value == FcTypeDouble && type == FcTypeRange)) return; if (type == FcTypeUnknown) return; /* It's perfectly fine to use user-define elements in expressions, * so don't warn in that case. */ if (value == FcTypeUnknown) 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 FcOpRange: FcTypecheckValue (parse, FcTypeRange, 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; case FcRuleUnknown: 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 = range; 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 FcVStackBool: break; case FcVStackRange: FcRangeDestroy (vstack->u.range); 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; } /* Don't check the attributes for FcElementNone */ if (parse->pstack->element != FcElementNone && parse->pstack->attr) { /* Warn about unused attrs. */ FcChar8 **attrs = parse->pstack->attr; while (*attrs) { if (attrs[0][0]) { FcConfigMessage (parse, FcSevereWarning, "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, FcBool enabled) { 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->ruleset = FcRuleSetCreate (name); parse->parser = parser; parse->scanOnly = !enabled; FcRuleSetEnable (parse->ruleset, enabled); return FcTrue; } static void FcConfigCleanup (FcConfigParse *parse) { while (parse->pstack) FcPStackPop (parse); FcRuleSetDestroy (parse->ruleset); parse->ruleset = NULL; } 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 FcStrSet * _get_real_paths_from_prefix(FcConfigParse *parse, const FcChar8 *path, const FcChar8 *prefix) { #ifdef _WIN32 FcChar8 buffer[1000] = { 0 }; #endif FcChar8 *parent = NULL, *retval = NULL; FcStrSet *e = NULL; if (prefix) { if (FcStrCmp (prefix, (const FcChar8 *) "xdg") == 0) { parent = FcConfigXdgDataHome (); if (!parent) { /* Home directory might be disabled */ return NULL; } e = FcConfigXdgDataDirs (); if (!e) { FcStrFree (parent); return NULL; } } else if (FcStrCmp (prefix, (const FcChar8 *) "default") == 0 || FcStrCmp (prefix, (const FcChar8 *) "cwd") == 0) { /* Nothing to do */ } else if (FcStrCmp (prefix, (const FcChar8 *) "relative") == 0) { parent = FcStrDirname (parse->name); if (!parent) return NULL; } } #ifndef _WIN32 /* For Win32, check this later for dealing with special cases */ else { if (!FcStrIsAbsoluteFilename (path) && path[0] != '~') FcConfigMessage (parse, FcSevereWarning, "Use of ambiguous path in <%s> element. please add prefix=\"cwd\" if current behavior is desired.", FcElementReverseMap (parse->pstack->element)); } #else if (strcmp ((const char *) path, "CUSTOMFONTDIR") == 0) { FcChar8 *p; path = buffer; if (!GetModuleFileName (NULL, (LPCH) buffer, sizeof (buffer) - 20)) { FcConfigMessage (parse, FcSevereError, "GetModuleFileName failed"); return NULL; } /* * 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 (path, '\\'); if (p) *p = '\0'; strcat ((char *) path, "\\fonts"); } else if (strcmp ((const char *) path, "APPSHAREFONTDIR") == 0) { FcChar8 *p; path = buffer; if (!GetModuleFileName (NULL, (LPCH) buffer, sizeof (buffer) - 20)) { FcConfigMessage (parse, FcSevereError, "GetModuleFileName failed"); return NULL; } p = _mbsrchr (path, '\\'); if (p) *p = '\0'; strcat ((char *) path, "\\..\\share\\fonts"); } else if (strcmp ((const char *) path, "WINDOWSFONTDIR") == 0) { int rc; path = buffer; rc = pGetSystemWindowsDirectory ((LPSTR) buffer, sizeof (buffer) - 20); if (rc == 0 || rc > sizeof (buffer) - 20) { FcConfigMessage (parse, FcSevereError, "GetSystemWindowsDirectory failed"); return NULL; } if (path [strlen ((const char *) path) - 1] != '\\') strcat ((char *) path, "\\"); strcat ((char *) path, "fonts"); } else { if (!prefix) { if (!FcStrIsAbsoluteFilename (path) && path[0] != '~') FcConfigMessage (parse, FcSevereWarning, "Use of ambiguous path in <%s> element. please add prefix=\"cwd\" if current behavior is desired.", FcElementReverseMap (parse->pstack->element)); } } #endif if (parent) { retval = FcStrBuildFilename (parent, path, NULL); FcStrFree (parent); } else { retval = FcStrdup (path); } if (!e) e = FcStrSetCreate (); else { FcChar8 *s; int i; for (i = 0; i < e->num; i++) { s = FcStrBuildFilename (e->strs[i], path, NULL); FcStrFree (e->strs[i]); e->strs[i] = s; } } if (!FcStrSetInsert (e, retval, 0)) { FcStrSetDestroy (e); e = NULL; } FcStrFree (retval); return e; } 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 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 static double FcStrtod (char *s, char **end) { #ifndef __BIONIC__ struct lconv *locale_data; #endif const char *decimal_point; int dlen; char *dot; double v; /* * Have to swap the decimal point to match the current locale * if that locale doesn't use 0x2e */ #ifndef __BIONIC__ locale_data = localeconv (); decimal_point = locale_data->decimal_point; dlen = strlen (decimal_point); #else decimal_point = "."; dlen = 1; #endif if ((dot = strchr (s, 0x2e)) && (decimal_point[0] != 0x2e || decimal_point[1] != 0)) { char buf[128]; int slen = strlen (s); 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), 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 (!m.yy || !m.yx || !m.xy || !m.xx) { FcConfigMessage (parse, FcSevereWarning, "Missing values in matrix element"); return; } if (FcPopExpr (parse)) FcConfigMessage (parse, FcSevereError, "wrong number of matrix elements"); else FcVStackPushMatrix (parse, &m); } static void FcParseRange (FcConfigParse *parse) { FcVStack *vstack; FcRange *r; FcChar32 n[2] = {0, 0}; int count = 1; double d[2] = {0.0L, 0.0L}; FcBool dflag = FcFalse; while ((vstack = FcVStackPeek (parse))) { if (count < 0) { FcConfigMessage (parse, FcSevereError, "too many elements in range"); return; } switch ((int) vstack->tag) { case FcVStackInteger: if (dflag) d[count] = (double)vstack->u.integer; else n[count] = vstack->u.integer; break; case FcVStackDouble: if (count == 0 && !dflag) d[1] = (double)n[1]; d[count] = vstack->u._double; dflag = FcTrue; break; default: FcConfigMessage (parse, FcSevereError, "invalid element in range"); if (dflag) d[count] = 0.0L; else n[count] = 0; break; } count--; FcVStackPopAndDestroy (parse); } if (count >= 0) { FcConfigMessage (parse, FcSevereError, "invalid range"); return; } if (dflag) { if (d[0] > d[1]) { FcConfigMessage (parse, FcSevereError, "invalid range"); return; } r = FcRangeCreateDouble (d[0], d[1]); } else { if (n[0] > n[1]) { FcConfigMessage (parse, FcSevereError, "invalid range"); return; } r = FcRangeCreateInteger (n[0], n[1]); } FcVStackPushRange (parse, r); } 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, begin, end; 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: begin = (FcChar32) vstack->u.range->begin; end = (FcChar32) vstack->u.range->end; if (begin <= end) { for (i = begin; i <= 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; int n; 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 in 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); if (rule) FcRuleDestroy (rule); 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 ((n = FcRuleSetAdd (parse->ruleset, rule, FcMatchPattern)) == -1) FcRuleDestroy (rule); else if (parse->config->maxObjects < n) parse->config->maxObjects = n; } static void FcParseDescription (FcConfigParse *parse) { const FcChar8 *domain; FcChar8 *desc; domain = FcConfigGetAttribute (parse, "domain"); desc = FcStrBufDone (&parse->pstack->str); if (!desc) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } FcRuleSetAddDescription (parse->ruleset, domain, desc); FcStrFree (desc); } static void FcParseRemapDir (FcConfigParse *parse) { const FcChar8 *path, *attr, *data, *salt; FcStrSet *prefix_dirs = NULL; data = FcStrBufDoneStatic (&parse->pstack->str); if (!data) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } if (data[0] == 0) { FcConfigMessage (parse, FcSevereWarning, "empty font directory name for remap ignored"); return; } path = FcConfigGetAttribute (parse, "as-path"); if (!path) { FcConfigMessage (parse, FcSevereWarning, "Missing as-path in remap-dir"); return; } attr = FcConfigGetAttribute (parse, "prefix"); salt = FcConfigGetAttribute (parse, "salt"); prefix_dirs = _get_real_paths_from_prefix (parse, data, attr); if (prefix_dirs) { FcStrList *l = FcStrListCreate (prefix_dirs); FcChar8 *prefix; FcStrSetDestroy (prefix_dirs); while ((prefix = FcStrListNext (l))) { if (!prefix || prefix[0] == 0) { /* nop */ } else if (!parse->scanOnly && (!FcStrUsesHome (prefix) || FcConfigHome ())) { if (!FcConfigAddFontDir (parse->config, prefix, path, salt)) FcConfigMessage (parse, FcSevereError, "out of memory; cannot create remap data for %s as %s", prefix, path); } FcStrBufDestroy (&parse->pstack->str); } FcStrListDone (l); } } static void FcParseResetDirs (FcConfigParse *parse) { if (!parse->scanOnly) { if (!FcConfigResetFontDirs (parse->config)) FcConfigMessage (parse, FcSevereError, "Unable to reset fonts dirs"); } } 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: expr = FcExprCreateRange (parse->config, vstack->u.range); 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, *salt; FcStrSet *prefix_dirs = NULL; data = FcStrBufDoneStatic (&parse->pstack->str); if (!data) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } if (data[0] == 0) { FcConfigMessage (parse, FcSevereWarning, "empty font directory name ignored"); return; } attr = FcConfigGetAttribute (parse, "prefix"); salt = FcConfigGetAttribute (parse, "salt"); prefix_dirs = _get_real_paths_from_prefix (parse, data, attr); if (prefix_dirs) { FcStrList *l = FcStrListCreate (prefix_dirs); FcChar8 *prefix; FcStrSetDestroy (prefix_dirs); while ((prefix = FcStrListNext (l))) { if (!prefix || prefix[0] == 0) { /* nop */ } else if (!parse->scanOnly && (!FcStrUsesHome (prefix) || FcConfigHome ())) { if (!FcConfigAddFontDir (parse->config, prefix, NULL, salt)) FcConfigMessage (parse, FcSevereError, "out of memory; cannot add directory %s", prefix); } FcStrBufDestroy (&parse->pstack->str); } FcStrListDone (l); } } static void FcParseCacheDir (FcConfigParse *parse) { const FcChar8 *attr; FcChar8 *prefix = NULL, *p, *data = NULL; attr = FcConfigGetAttribute (parse, "prefix"); if (attr && FcStrCmp (attr, (const FcChar8 *)"xdg") == 0) { prefix = FcConfigXdgCacheHome (); /* home directory might be disabled. * simply ignore this element. */ if (!prefix) goto bail; } data = FcStrBufDone (&parse->pstack->str); if (!data) { FcConfigMessage (parse, FcSevereError, "out of memory"); data = prefix; goto bail; } if (data[0] == 0) { FcConfigMessage (parse, FcSevereWarning, "empty cache directory name ignored"); FcStrFree (data); 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"); FcStrFree (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 else if (data[0] == '/' && fontconfig_instprefix[0] != '\0') { size_t plen = strlen ((const char *)fontconfig_instprefix); size_t dlen = strlen ((const char *)data); prefix = malloc (plen + 1 + dlen + 1); if (!prefix) { FcConfigMessage (parse, FcSevereError, "out of memory"); goto bail; } strcpy ((char *) prefix, (char *) fontconfig_instprefix); prefix[plen] = FC_DIR_SEPARATOR; memcpy (&prefix[plen + 1], data, dlen); prefix[plen + 1 + dlen] = 0; FcStrFree (data); data = prefix; } else 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 (!parse->scanOnly && (!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); } void FcConfigPathFini (void) { FcChar8 *s; retry_dir: s = fc_atomic_ptr_get (&__fc_userdir); if (!fc_atomic_ptr_cmpexch (&__fc_userdir, s, NULL)) goto retry_dir; free (s); retry_conf: s = fc_atomic_ptr_get (&__fc_userconf); if (!fc_atomic_ptr_cmpexch (&__fc_userconf, s, NULL)) goto retry_conf; free (s); } static void FcParseInclude (FcConfigParse *parse) { FcChar8 *s; const FcChar8 *attr; FcBool ignore_missing = FcFalse; #ifndef _WIN32 FcBool deprecated = FcFalse; #endif FcChar8 *prefix = NULL, *p; FcChar8 *userdir = NULL, *userconf = NULL; FcRuleSet *ruleset; FcMatchKind k; 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"); #ifndef _WIN32 if (attr && FcConfigLexBool (parse, (FcChar8 *) attr) == FcTrue) deprecated = FcTrue; #endif attr = FcConfigGetAttribute (parse, "prefix"); if (attr && FcStrCmp (attr, (const FcChar8 *)"xdg") == 0) { prefix = FcConfigXdgConfigHome (); /* home directory might be disabled. * simply ignore this element. */ if (!prefix) goto bail; } if (prefix) { size_t plen = strlen ((const char *)prefix); size_t dlen = strlen ((const char *)s); FcChar8 *u; 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: userdir = fc_atomic_ptr_get (&__fc_userdir); if (!userdir) { u = FcStrdup (s); if (!fc_atomic_ptr_cmpexch (&__fc_userdir, userdir, u)) { free (u); goto userdir; } userdir = u; } } else if (FcFileIsFile (s)) { userconf: userconf = fc_atomic_ptr_get (&__fc_userconf); if (!userconf) { u = FcStrdup (s); if (!fc_atomic_ptr_cmpexch (&__fc_userconf, userconf, u)) { free (u); goto userconf; } userconf = u; } } 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; } } /* flush the ruleset into the queue */ ruleset = parse->ruleset; parse->ruleset = FcRuleSetCreate (ruleset->name); FcRuleSetEnable (parse->ruleset, ruleset->enabled); FcRuleSetAddDescription (parse->ruleset, ruleset->domain, ruleset->description); for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++) { FcPtrListIter iter; FcPtrListIterInit (ruleset->subst[k], &iter); if (FcPtrListIterIsValid (ruleset->subst[k], &iter)) { FcPtrListIterInitAtLast (parse->config->subst[k], &iter); FcRuleSetReference (ruleset); FcPtrListIterAdd (parse->config->subst[k], &iter, ruleset); } } FcRuleSetDestroy (ruleset); if (!_FcConfigParse (parse->config, s, !ignore_missing, !parse->scanOnly)) parse->error = FcTrue; #ifndef _WIN32 else { FcChar8 *filename; static FcBool warn_conf = FcFalse, warn_confd = FcFalse; filename = FcConfigGetFilename(parse->config, s); if (deprecated == FcTrue && filename != NULL && userdir != 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 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; int n; 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, " 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 (!rule) { FcConfigMessage (parse, FcSevereWarning, "No nor elements in "); return; } if ((n = FcRuleSetAdd (parse->ruleset, rule, kind)) == -1) { FcConfigMessage (parse, FcSevereError, "out of memory"); FcRuleDestroy (rule); } else if (parse->config->maxObjects < n) parse->config->maxObjects = n; } static void FcParseAcceptRejectFont (FcConfigParse *parse, FcElement element) { FcVStack *vstack; while ((vstack = FcVStackPeek (parse))) { switch ((int) vstack->tag) { case FcVStackGlob: if (!parse->scanOnly && !FcConfigGlobAdd (parse->config, vstack->u.string, element == FcElementAcceptfont)) { FcConfigMessage (parse, FcSevereError, "out of memory"); } else { if (parse->scanOnly && vstack->u.string) { FcStrFree (vstack->u.string); vstack->tag = FcVStackNone; } } break; case FcVStackPattern: if (!parse->scanOnly && !FcConfigPatternsAdd (parse->config, vstack->u.pattern, element == FcElementAcceptfont)) { FcConfigMessage (parse, FcSevereError, "out of memory"); } else { if (parse->scanOnly && vstack->u.pattern) FcPatternDestroy (vstack->u.pattern); 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; case FcVStackRange: value.u.r = FcRangeCopy (vstack->u.range); if (value.u.r) value.type = FcTypeRange; 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 FcElementDescription: FcParseDescription (parse); break; case FcElementRemapDir: FcParseRemapDir (parse); break; case FcElementResetDirs: FcParseResetDirs (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, FcBool load) { 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 = FcStrSetCreateEx (FCSS_GROW_BY_64); if (!files) { ret = FcFalse; goto bail2; } if (FcDebug () & FC_DBG_CONFIG) printf ("\tScanning config dir %s\n", dir); if (load) FcConfigAddConfigDir (config, 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 */ d_len = strlen (e->d_name); if ('0' <= e->d_name[0] && e->d_name[0] <= '9' && 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 = _FcConfigParse (config, files->strs[i], complain, load); } bail3: FcStrSetDestroy (files); bail2: free (file); bail1: closedir (d); bail0: return ret || !complain; } #ifdef _WIN32 pfnGetSystemWindowsDirectory pGetSystemWindowsDirectory = NULL; pfnSHGetFolderPathA pSHGetFolderPathA = NULL; #endif static FcBool FcConfigParseAndLoadFromMemoryInternal (FcConfig *config, const FcChar8 *filename, const FcChar8 *buffer, FcBool complain, FcBool load) { XML_Parser p; size_t len; FcConfigParse parse; FcBool error = FcTrue; FcMatchKind k; FcPtrListIter liter; #ifdef ENABLE_LIBXML2 xmlSAXHandler sax; #else void *buf; const FcChar8 *s; size_t buflen; #endif if (!buffer) return FcFalse; len = strlen ((const char *) buffer); if (FcDebug () & FC_DBG_CONFIG) printf ("\t%s config file from %s\n", load ? "Loading" : "Scanning", filename); #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 if (!p) goto bail1; if (!FcConfigParseInit (&parse, filename, config, p, load)) 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 */ #ifndef ENABLE_LIBXML2 s = buffer; do { buf = XML_GetBuffer (p, BUFSIZ); if (!buf) { FcConfigMessage (&parse, FcSevereError, "cannot get parse buffer"); goto bail3; } if (len > BUFSIZ) { buflen = BUFSIZ; len -= BUFSIZ; } else { buflen = len; len = 0; } memcpy (buf, s, buflen); s = s + buflen; #endif #ifdef ENABLE_LIBXML2 if (xmlParseChunk (p, (const char *)buffer, len, len == 0)) #else if (!XML_ParseBuffer (p, buflen, buflen == 0)) #endif { FcConfigMessage (&parse, FcSevereError, "%s", XML_ErrorString (XML_GetErrorCode (p))); goto bail3; } #ifndef ENABLE_LIBXML2 } while (buflen != 0); #endif error = parse.error; if (load) { for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++) { FcPtrListIter iter; FcPtrListIterInit (parse.ruleset->subst[k], &iter); if (FcPtrListIterIsValid (parse.ruleset->subst[k], &iter)) { FcPtrListIterInitAtLast (parse.config->subst[k], &iter); FcRuleSetReference (parse.ruleset); FcPtrListIterAdd (parse.config->subst[k], &iter, parse.ruleset); } } } FcPtrListIterInitAtLast (parse.config->rulesetList, &liter); FcRuleSetReference (parse.ruleset); FcPtrListIterAdd (parse.config->rulesetList, &liter, parse.ruleset); bail3: FcConfigCleanup (&parse); bail2: XML_ParserFree (p); bail1: if (error && complain) { FcConfigMessage (0, FcSevereError, "Cannot %s config file from %s", load ? "load" : "scan", filename); return FcFalse; } if (FcDebug () & FC_DBG_CONFIG) printf ("\t%s config file from %s done\n", load ? "Loading" : "Scanning", filename); return FcTrue; } static FcBool _FcConfigParse (FcConfig *config, const FcChar8 *name, FcBool complain, FcBool load) { FcChar8 *filename = NULL, *realfilename = NULL; int fd; int len; FcStrBuf sbuf; char buf[BUFSIZ]; FcBool ret = FcFalse, complain_again = complain; FcStrBuf reason; FcStrBufInit (&reason, NULL, 0); #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 = FcConfigGetFilename (config, name); if (!filename) { FcStrBufString (&reason, (FcChar8 *)"No such file: "); FcStrBufString (&reason, name ? name : (FcChar8 *)"(null)"); goto bail0; } realfilename = FcConfigRealFilename (config, name); if (!realfilename) { FcStrBufString (&reason, (FcChar8 *)"No such realfile: "); FcStrBufString (&reason, name ? name : (FcChar8 *)"(null)"); goto bail0; } if (FcStrSetMember (config->availConfigFiles, realfilename)) { FcStrFree (filename); FcStrFree (realfilename); return FcTrue; } if (load) { if (!FcStrSetAdd (config->configFiles, filename)) goto bail0; } if (!FcStrSetAdd (config->availConfigFiles, realfilename)) goto bail0; if (FcFileIsDir (realfilename)) { ret = FcConfigParseAndLoadDir (config, name, realfilename, complain, load); FcStrFree (filename); FcStrFree (realfilename); return ret; } FcStrBufInit (&sbuf, NULL, 0); fd = FcOpen ((char *) realfilename, O_RDONLY); if (fd == -1) { FcStrBufString (&reason, (FcChar8 *)"Unable to open "); FcStrBufString (&reason, realfilename); goto bail1; } do { len = read (fd, buf, BUFSIZ); if (len < 0) { int errno_ = errno; char ebuf[BUFSIZ+1]; #if HAVE_STRERROR_R strerror_r (errno_, ebuf, BUFSIZ); #elif HAVE_STRERROR char *tmp = strerror (errno_); size_t len = strlen (tmp); memcpy (ebuf, tmp, FC_MIN (BUFSIZ, len)); ebuf[FC_MIN (BUFSIZ, len)] = 0; #else ebuf[0] = 0; #endif FcConfigMessage (0, FcSevereError, "failed reading config file: %s: %s (errno %d)", realfilename, ebuf, errno_); close (fd); goto bail1; } FcStrBufData (&sbuf, (const FcChar8 *)buf, len); } while (len != 0); close (fd); ret = FcConfigParseAndLoadFromMemoryInternal (config, filename, FcStrBufDoneStatic (&sbuf), complain, load); complain_again = FcFalse; /* no need to reclaim here */ bail1: FcStrBufDestroy (&sbuf); bail0: if (filename) FcStrFree (filename); if (realfilename) FcStrFree (realfilename); if (!complain) return FcTrue; if (!ret && complain_again) { if (name) FcConfigMessage (0, FcSevereError, "Cannot %s config file \"%s\": %s", load ? "load" : "scan", name, FcStrBufDoneStatic (&reason)); else FcConfigMessage (0, FcSevereError, "Cannot %s default config file: %s", load ? "load" : "scan", FcStrBufDoneStatic (&reason)); FcStrBufDestroy (&reason); return FcFalse; } FcStrBufDestroy (&reason); return ret; } FcBool FcConfigParseOnly (FcConfig *config, const FcChar8 *name, FcBool complain) { return _FcConfigParse (config, name, complain, FcFalse); } FcBool FcConfigParseAndLoad (FcConfig *config, const FcChar8 *name, FcBool complain) { return _FcConfigParse (config, name, complain, FcTrue); } FcBool FcConfigParseAndLoadFromMemory (FcConfig *config, const FcChar8 *buffer, FcBool complain) { return FcConfigParseAndLoadFromMemoryInternal (config, (const FcChar8 *)"memory", buffer, complain, FcTrue); } #define __fcxml__ #include "fcaliastail.h" #undef __fcxml__