/* * $RCSId: xc/lib/fontconfig/src/fcxml.c,v 1.21 2002/08/22 18:53:22 keithp Exp $ * * Copyright © 2002 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD 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 * 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 #include "fcint.h" #include #if 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 #define STRICT #include #undef STRICT #endif void 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) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = FcOpInteger; e->u.ival = i; } return e; } FcExpr * FcExprCreateDouble (double d) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = FcOpDouble; e->u.dval = d; } return e; } FcExpr * FcExprCreateString (const FcChar8 *s) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = FcOpString; e->u.sval = FcStrCopy (s); } return e; } FcExpr * FcExprCreateMatrix (const FcMatrix *m) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = FcOpMatrix; e->u.mval = FcMatrixCopy (m); } return e; } FcExpr * FcExprCreateBool (FcBool b) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = FcOpBool; e->u.bval = b; } return e; } FcExpr * FcExprCreateNil (void) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = FcOpNil; } return e; } FcExpr * FcExprCreateField (const char *field) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = FcOpField; e->u.field = (char *) FcStrCopy ((FcChar8 *) field); } return e; } FcExpr * FcExprCreateConst (const FcChar8 *constant) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = FcOpConst; e->u.constant = FcStrCopy (constant); } return e; } FcExpr * FcExprCreateOp (FcExpr *left, FcOp op, FcExpr *right) { FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr)); if (e) { FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr)); e->op = op; e->u.tree.left = left; e->u.tree.right = right; } return e; } 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); break; case FcOpCharSet: FcCharSetDestroy (e->u.cval); break; case FcOpBool: break; case FcOpField: FcStrFree ((FcChar8 *) e->u.field); break; case FcOpConst: FcStrFree (e->u.constant); break; case FcOpAssign: case FcOpAssignReplace: case FcOpPrepend: case FcOpPrependFirst: case FcOpAppend: case FcOpAppendLast: break; case FcOpOr: case FcOpAnd: case FcOpEqual: case FcOpNotEqual: case FcOpLess: case FcOpLessEqual: case FcOpMore: case FcOpMoreEqual: case FcOpContains: case FcOpListing: case FcOpNotContains: case FcOpPlus: case FcOpMinus: case FcOpTimes: case FcOpDivide: case FcOpQuest: case FcOpComma: FcExprDestroy (e->u.tree.right); /* fall through */ case FcOpNot: case FcOpFloor: case FcOpCeil: case FcOpRound: case FcOpTrunc: FcExprDestroy (e->u.tree.left); break; case FcOpNil: case FcOpInvalid: break; } FcMemFree (FC_MEM_EXPR, sizeof (FcExpr)); free (e); } void 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) { return (char *) FcStrCopy ((FcChar8 *) field); } typedef enum _FcElement { FcElementNone, FcElementFontconfig, FcElementDir, 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 struct { const char name[16]; FcElement element; } fcElementMap[] = { { "fontconfig", FcElementFontconfig }, { "dir", FcElementDir }, { "cache", FcElementCache }, { "include", FcElementInclude }, { "config", FcElementConfig }, { "match", FcElementMatch }, { "alias", FcElementAlias }, { "blank", FcElementBlank }, { "rescan", FcElementRescan }, { "prefer", FcElementPrefer }, { "accept", FcElementAccept }, { "default", FcElementDefault }, { "family", FcElementFamily }, { "selectfont", FcElementSelectfont }, { "acceptfont", FcElementAcceptfont }, { "rejectfont", FcElementRejectfont }, { "glob", FcElementGlob }, { "pattern", FcElementPattern }, { "patelt", FcElementPatelt }, { "test", FcElementTest }, { "edit", FcElementEdit }, { "int", FcElementInt }, { "double", FcElementDouble }, { "string", FcElementString }, { "matrix", FcElementMatrix }, { "bool", FcElementBool }, { "charset", FcElementCharset }, { "name", FcElementName }, { "const", FcElementConst }, { "or", FcElementOr }, { "and", FcElementAnd }, { "eq", FcElementEq }, { "not_eq", FcElementNotEq }, { "less", FcElementLess }, { "less_eq", FcElementLessEq }, { "more", FcElementMore }, { "more_eq", FcElementMoreEq }, { "contains", FcElementContains }, { "not_contains", FcElementNotContains }, { "plus", FcElementPlus }, { "minus", FcElementMinus }, { "times", FcElementTimes }, { "divide", FcElementDivide }, { "not", FcElementNot }, { "if", FcElementIf }, { "floor", FcElementFloor }, { "ceil", FcElementCeil }, { "round", FcElementRound }, { "trunc", FcElementTrunc }, }; #define NUM_ELEMENT_MAPS (int) (sizeof fcElementMap / sizeof fcElementMap[0]) static FcElement FcElementMap (const XML_Char *name) { int i; for (i = 0; i < NUM_ELEMENT_MAPS; i++) if (!strcmp ((char *) name, fcElementMap[i].name)) return fcElementMap[i].element; return FcElementUnknown; } typedef struct _FcPStack { struct _FcPStack *prev; FcElement element; FcChar8 **attr; FcStrBuf str; } 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; } FcConfigParse; typedef enum _FcConfigSeverity { FcSevereInfo, FcSevereWarning, FcSevereError } FcConfigSeverity; static void FcConfigMessage (FcConfigParse *parse, FcConfigSeverity severe, const char *fmt, ...) { const char *s = "unknown"; va_list args; va_start (args, fmt); switch (severe) { case FcSevereInfo: s = "info"; break; case FcSevereWarning: s = "warning"; break; case FcSevereError: s = "error"; break; } if (parse) { if (parse->name) fprintf (stderr, "Fontconfig %s: \"%s\", line %d: ", s, parse->name, (int)XML_GetCurrentLineNumber (parse->parser)); else fprintf (stderr, "Fontconfig %s: line %d: ", s, (int)XML_GetCurrentLineNumber (parse->parser)); if (severe >= FcSevereError) parse->error = FcTrue; } else fprintf (stderr, "Fontconfig %s: ", s); vfprintf (stderr, fmt, args); fprintf (stderr, "\n"); va_end (args); } static const char * FcTypeName (FcType type) { switch (type) { case FcTypeVoid: return "void"; case FcTypeInteger: case FcTypeDouble: return "number"; case FcTypeString: return "string"; case FcTypeBool: return "bool"; case FcTypeMatrix: return "matrix"; case FcTypeCharSet: return "charset"; case FcTypeFTFace: return "FT_Face"; case FcTypeLangSet: return "langset"; default: return "unknown"; } } static void FcTypecheckValue (FcConfigParse *parse, FcType value, FcType type) { if (value == FcTypeInteger) value = FcTypeDouble; if (type == FcTypeInteger) type = FcTypeDouble; if (value != type) { if ((value == FcTypeLangSet && type == FcTypeString) || (value == FcTypeString && type == FcTypeLangSet)) return; 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; 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 (expr->u.field); if (o) FcTypecheckValue (parse, o->type, type); break; case FcOpConst: c = FcNameGetConstant (expr->u.constant); if (c) { o = FcNameGetObjectType (c->object); if (o) FcTypecheckValue (parse, o->type, type); } else FcConfigMessage (parse, FcSevereWarning, "invalid constant used : %s", expr->u.constant); break; case FcOpQuest: FcTypecheckExpr (parse, expr->u.tree.left, FcTypeBool); FcTypecheckExpr (parse, expr->u.tree.right->u.tree.left, type); FcTypecheckExpr (parse, expr->u.tree.right->u.tree.right, type); break; case FcOpAssign: case FcOpAssignReplace: break; case FcOpEqual: case FcOpNotEqual: case FcOpLess: case FcOpLessEqual: case FcOpMore: case FcOpMoreEqual: case FcOpContains: case FcOpNotContains: case FcOpListing: FcTypecheckValue (parse, FcTypeBool, type); break; case FcOpComma: case FcOpOr: case FcOpAnd: case FcOpPlus: case FcOpMinus: case FcOpTimes: case FcOpDivide: FcTypecheckExpr (parse, expr->u.tree.left, type); FcTypecheckExpr (parse, expr->u.tree.right, type); break; case FcOpNot: FcTypecheckValue (parse, FcTypeBool, type); FcTypecheckExpr (parse, expr->u.tree.left, FcTypeBool); break; case FcOpFloor: case FcOpCeil: case FcOpRound: case FcOpTrunc: FcTypecheckValue (parse, FcTypeDouble, type); FcTypecheckExpr (parse, expr->u.tree.left, FcTypeDouble); break; default: break; } } static FcTest * FcTestCreate (FcConfigParse *parse, FcMatchKind kind, FcQual qual, const FcChar8 *field, FcOp compare, FcExpr *expr) { FcTest *test = (FcTest *) malloc (sizeof (FcTest)); if (test) { const FcObjectType *o; FcMemAlloc (FC_MEM_TEST, sizeof (FcTest)); test->next = 0; test->kind = kind; test->qual = qual; test->field = (char *) FcStrCopy (field); test->op = compare; test->expr = expr; o = FcNameGetObjectType (test->field); if (o) FcTypecheckExpr (parse, expr, o->type); } return test; } static FcEdit * FcEditCreate (FcConfigParse *parse, const char *field, FcOp op, FcExpr *expr, FcValueBinding binding) { FcEdit *e = (FcEdit *) malloc (sizeof (FcEdit)); if (e) { const FcObjectType *o; e->next = 0; e->field = field; /* already saved in grammar */ e->op = op; e->expr = expr; e->binding = binding; o = FcNameGetObjectType (e->field); if (o) FcTypecheckExpr (parse, expr, o->type); } return e; } static void FcVStackPush (FcConfigParse *parse, FcVStack *vstack) { vstack->prev = parse->vstack; vstack->pstack = parse->pstack ? parse->pstack->prev : 0; parse->vstack = vstack; } static FcVStack * FcVStackCreate (void) { FcVStack *new; new = malloc (sizeof (FcVStack)); if (!new) return 0; FcMemAlloc (FC_MEM_VSTACK, sizeof (FcVStack)); new->tag = FcVStackNone; new->prev = 0; return new; } static void FcVStackDestroy (FcVStack *vstack) { FcVStack *prev; for (; vstack; vstack = prev) { prev = vstack->prev; switch (vstack->tag) { case FcVStackNone: break; case FcVStackString: case FcVStackFamily: case FcVStackField: case FcVStackConstant: case FcVStackGlob: FcStrFree (vstack->u.string); break; case FcVStackPattern: FcPatternDestroy (vstack->u.pattern); break; case FcVStackInteger: case FcVStackDouble: break; case FcVStackMatrix: FcMatrixFree (vstack->u.matrix); break; case FcVStackBool: break; case FcVStackTest: FcTestDestroy (vstack->u.test); break; case FcVStackExpr: case FcVStackPrefer: case FcVStackAccept: case FcVStackDefault: FcExprDestroy (vstack->u.expr); break; case FcVStackEdit: FcEditDestroy (vstack->u.edit); break; } FcMemFree (FC_MEM_VSTACK, sizeof (FcVStack)); free (vstack); } } static FcBool FcVStackPushString (FcConfigParse *parse, FcVStackTag tag, FcChar8 *string) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; vstack->u.string = string; vstack->tag = tag; FcVStackPush (parse, vstack); return FcTrue; } static FcBool FcVStackPushInteger (FcConfigParse *parse, int integer) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; vstack->u.integer = integer; vstack->tag = FcVStackInteger; FcVStackPush (parse, vstack); return FcTrue; } static FcBool FcVStackPushDouble (FcConfigParse *parse, double _double) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; vstack->u._double = _double; vstack->tag = FcVStackDouble; FcVStackPush (parse, vstack); return FcTrue; } static FcBool FcVStackPushMatrix (FcConfigParse *parse, FcMatrix *matrix) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; matrix = FcMatrixCopy (matrix); if (!matrix) { FcVStackDestroy (vstack); return FcFalse; } vstack->u.matrix = matrix; vstack->tag = FcVStackMatrix; FcVStackPush (parse, vstack); return FcTrue; } static FcBool FcVStackPushBool (FcConfigParse *parse, FcBool bool) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; vstack->u.bool = bool; vstack->tag = FcVStackBool; FcVStackPush (parse, vstack); return FcTrue; } static FcBool FcVStackPushTest (FcConfigParse *parse, FcTest *test) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; vstack->u.test = test; vstack->tag = FcVStackTest; FcVStackPush (parse, vstack); return FcTrue; } static FcBool FcVStackPushExpr (FcConfigParse *parse, FcVStackTag tag, FcExpr *expr) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; vstack->u.expr = expr; vstack->tag = tag; FcVStackPush (parse, vstack); return FcTrue; } static FcBool FcVStackPushEdit (FcConfigParse *parse, FcEdit *edit) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; vstack->u.edit = edit; vstack->tag = FcVStackEdit; FcVStackPush (parse, vstack); return FcTrue; } static FcBool FcVStackPushPattern (FcConfigParse *parse, FcPattern *pattern) { FcVStack *vstack = FcVStackCreate (); if (!vstack) return FcFalse; vstack->u.pattern = pattern; vstack->tag = FcVStackPattern; FcVStackPush (parse, vstack); return FcTrue; } static FcVStack * FcVStackFetch (FcConfigParse *parse, int off) { FcVStack *vstack; for (vstack = parse->vstack; vstack && off-- > 0; vstack = vstack->prev); return vstack; } static void FcVStackClear (FcConfigParse *parse) { while (parse->vstack && parse->vstack->pstack == parse->pstack) { FcVStack *vstack = parse->vstack; parse->vstack = vstack->prev; vstack->prev = 0; FcVStackDestroy (vstack); } } static FcVStack * FcVStackPop (FcConfigParse *parse) { FcVStack *vstack = parse->vstack; if (!vstack || vstack->pstack != parse->pstack) return 0; parse->vstack = vstack->prev; vstack->prev = 0; return vstack; } 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) { 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; new = malloc ((i + 1) * sizeof (FcChar8 *) + slen); if (!new) return 0; FcMemAlloc (FC_MEM_ATTR, 1); /* size is too expensive */ s = (FcChar8 *) (new + (i + 1)); for (i = 0; attr[i]; i++) { new[i] = s; strcpy ((char *) s, (char *) attr[i]); s += strlen ((char *) s) + 1; } new[i] = 0; return new; } static FcBool FcPStackPush (FcConfigParse *parse, FcElement element, const XML_Char **attr) { FcPStack *new = malloc (sizeof (FcPStack)); if (!new) return FcFalse; FcMemAlloc (FC_MEM_PSTACK, sizeof (FcPStack)); new->prev = parse->pstack; new->element = element; if (attr) { new->attr = FcConfigSaveAttr (attr); if (!new->attr) FcConfigMessage (parse, FcSevereError, "out of memory"); } else new->attr = 0; 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) { FcMemFree (FC_MEM_ATTR, 1); /* size is to expensive */ free (old->attr); } 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->vstack = 0; parse->error = FcFalse; parse->name = name; parse->config = config; parse->parser = parser; return FcTrue; } static void FcConfigCleanup (FcConfigParse *parse) { while (parse->pstack) FcPStackPop (parse); } static const FcChar8 * FcConfigGetAttribute (FcConfigParse *parse, const char *attr) { FcChar8 **attrs; if (!parse->pstack) return 0; attrs = parse->pstack->attr; if (!attrs) return 0; while (*attrs) { if (!strcmp ((char *) *attrs, attr)) return attrs[1]; attrs += 2; } return 0; } static void FcStartElement(void *userData, const XML_Char *name, const XML_Char **attr) { FcConfigParse *parse = userData; FcElement element; element = FcElementMap (name); if (element == FcElementUnknown) FcConfigMessage (parse, FcSevereWarning, "unknown element \"%s\"", name); if (!FcPStackPush (parse, element, attr)) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } return; } static void FcParseBlank (FcConfigParse *parse) { int n = FcVStackElements (parse); while (n-- > 0) { FcVStack *v = FcVStackFetch (parse, n); if (v->tag != FcVStackInteger) FcConfigMessage (parse, FcSevereError, "non-integer blank"); else { if (!parse->config->blanks) { parse->config->blanks = FcBlanksCreate (); if (!parse->config->blanks) { FcConfigMessage (parse, FcSevereError, "out of memory"); break; } } if (!FcBlanksAdd (parse->config->blanks, v->u.integer)) { FcConfigMessage (parse, FcSevereError, "out of memory"); break; } } } } static void FcParseRescan (FcConfigParse *parse) { int n = FcVStackElements (parse); while (n-- > 0) { FcVStack *v = FcVStackFetch (parse, n); if (v->tag != FcVStackInteger) FcConfigMessage (parse, FcSevereWarning, "non-integer rescan"); else parse->config->rescanInterval = v->u.integer; } } static void FcParseInt (FcConfigParse *parse) { FcChar8 *s, *end; int l; if (!parse->pstack) return; s = FcStrBufDone (&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); FcStrFree (s); } /* * idea copied from glib g_ascii_strtod with * permission of the author (Alexander Larsson) */ #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)) { char buf[128]; int slen = strlen (s); int dlen = strlen (locale_data->decimal_point); if (slen + dlen > (int) sizeof (buf)) { if (end) *end = s; v = 0; } else { char *buf_end; /* mantissa */ strncpy (buf, s, dot - s); /* decimal point */ strcpy (buf + (dot - s), locale_data->decimal_point); /* rest of number */ strcpy (buf + (dot - s) + dlen, dot + 1); buf_end = 0; v = strtod (buf, &buf_end); if (buf_end) { buf_end = s + (buf_end - buf); if (buf_end > dot) buf_end -= dlen - 1; } if (end) *end = buf_end; } } else v = strtod (s, end); return v; } static void FcParseDouble (FcConfigParse *parse) { FcChar8 *s, *end; double d; if (!parse->pstack) return; s = FcStrBufDone (&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); FcStrFree (s); } static void FcParseString (FcConfigParse *parse, FcVStackTag tag) { FcChar8 *s; if (!parse->pstack) return; s = FcStrBufDone (&parse->pstack->str); if (!s) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } if (!FcVStackPushString (parse, tag, s)) FcStrFree (s); } static void FcParseMatrix (FcConfigParse *parse) { FcVStack *vstack; enum { m_done, m_xx, m_xy, m_yx, m_yy } matrix_state = m_yy; FcMatrix m; while ((vstack = FcVStackPop (parse))) { double v; switch (vstack->tag) { case FcVStackInteger: v = vstack->u.integer; break; case FcVStackDouble: v = vstack->u._double; break; default: FcConfigMessage (parse, FcSevereError, "non-double matrix element"); v = 1.0; break; } switch (matrix_state) { case m_xx: m.xx = v; break; case m_xy: m.xy = v; break; case m_yx: m.yx = v; break; case m_yy: m.yy = v; break; default: break; } FcVStackDestroy (vstack); matrix_state--; } if (matrix_state != m_done) FcConfigMessage (parse, FcSevereError, "wrong number of matrix elements"); else FcVStackPushMatrix (parse, &m); } static FcBool FcConfigLexBool (FcConfigParse *parse, const FcChar8 *bool) { FcBool result = FcFalse; if (!FcNameBool (bool, &result)) FcConfigMessage (parse, FcSevereWarning, "\"%s\" is not known boolean", bool); return result; } static void FcParseBool (FcConfigParse *parse) { FcChar8 *s; if (!parse->pstack) return; s = FcStrBufDone (&parse->pstack->str); if (!s) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } FcVStackPushBool (parse, FcConfigLexBool (parse, s)); FcStrFree (s); } static void FcParseFamilies (FcConfigParse *parse, FcVStackTag tag) { FcVStack *vstack; FcExpr *left, *expr = 0, *new; while ((vstack = FcVStackPop (parse))) { if (vstack->tag != FcVStackFamily) { FcConfigMessage (parse, FcSevereWarning, "non-family"); FcVStackDestroy (vstack); continue; } left = vstack->u.expr; vstack->tag = FcVStackNone; FcVStackDestroy (vstack); if (expr) { new = FcExprCreateOp (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 = FcStrBufDone (&parse->pstack->str); if (!s) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } expr = FcExprCreateString (s); FcStrFree (s); if (expr) FcVStackPushExpr (parse, FcVStackFamily, expr); } static void FcParseAlias (FcConfigParse *parse) { FcExpr *family = 0, *accept = 0, *prefer = 0, *def = 0, *new = 0; FcEdit *edit = 0, *next; FcVStack *vstack; FcTest *test; while ((vstack = FcVStackPop (parse))) { switch (vstack->tag) { case FcVStackFamily: if (family) { new = FcExprCreateOp (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; } FcVStackDestroy (vstack); } if (!family) { FcConfigMessage (parse, FcSevereError, "missing family in alias"); if (prefer) FcExprDestroy (prefer); if (accept) FcExprDestroy (accept); if (def) FcExprDestroy (def); return; } if (prefer) { edit = FcEditCreate (parse, FcConfigSaveField ("family"), FcOpPrepend, prefer, FcValueBindingWeak); if (edit) edit->next = 0; else FcExprDestroy (prefer); } if (accept) { next = edit; edit = FcEditCreate (parse, FcConfigSaveField ("family"), FcOpAppend, accept, FcValueBindingWeak); if (edit) edit->next = next; else FcExprDestroy (accept); } if (def) { next = edit; edit = FcEditCreate (parse, FcConfigSaveField ("family"), FcOpAppendLast, def, FcValueBindingWeak); if (edit) edit->next = next; else FcExprDestroy (def); } if (edit) { test = FcTestCreate (parse, FcMatchPattern, FcQualAny, (FcChar8 *) FC_FAMILY, FcOpEqual, family); if (test) if (!FcConfigAddEdit (parse->config, test, edit, FcMatchPattern)) FcTestDestroy (test); } else FcExprDestroy (family); } static FcExpr * FcPopExpr (FcConfigParse *parse) { FcVStack *vstack = FcVStackPop (parse); FcExpr *expr = 0; if (!vstack) return 0; switch (vstack->tag) { case FcVStackNone: break; case FcVStackString: case FcVStackFamily: expr = FcExprCreateString (vstack->u.string); break; case FcVStackField: expr = FcExprCreateField ((char *) vstack->u.string); break; case FcVStackConstant: expr = FcExprCreateConst (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 (vstack->u.integer); break; case FcVStackDouble: expr = FcExprCreateDouble (vstack->u._double); break; case FcVStackMatrix: expr = FcExprCreateMatrix (vstack->u.matrix); break; case FcVStackBool: expr = FcExprCreateBool (vstack->u.bool); break; case FcVStackTest: break; case FcVStackExpr: expr = vstack->u.expr; vstack->tag = FcVStackNone; break; case FcVStackEdit: break; default: break; } FcVStackDestroy (vstack); 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 (left, op, expr); if (!new) { FcConfigMessage (parse, FcSevereError, "out of memory"); FcExprDestroy (left); FcExprDestroy (expr); break; } 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 (operand, op, 0); if (!new) { FcExprDestroy (operand); FcConfigMessage (parse, FcSevereError, "out of memory"); } } return new; } static void FcParseUnary (FcConfigParse *parse, FcOp op) { FcExpr *expr = FcPopUnary (parse, op); if (expr) FcVStackPushExpr (parse, FcVStackExpr, expr); } static void FcParseInclude (FcConfigParse *parse) { FcChar8 *s; const FcChar8 *i; FcBool ignore_missing = FcFalse; s = FcStrBufDone (&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; FcStrFree (s); } 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, "default")) kind = FcMatchDefault; else { FcConfigMessage (parse, FcSevereWarning, "invalid test target \"%s\"", kind_string); return; } } qual_string = FcConfigGetAttribute (parse, "qual"); if (!qual_string) qual = FcQualAny; else { if (!strcmp ((char *) qual_string, "any")) qual = FcQualAny; else if (!strcmp ((char *) qual_string, "all")) qual = FcQualAll; else if (!strcmp ((char *) qual_string, "first")) qual = FcQualFirst; else if (!strcmp ((char *) qual_string, "not_first")) qual = FcQualNotFirst; else { FcConfigMessage (parse, FcSevereWarning, "invalid test qual \"%s\"", qual_string); return; } } name = FcConfigGetAttribute (parse, "name"); if (!name) { FcConfigMessage (parse, FcSevereWarning, "missing test name"); return; } compare_string = FcConfigGetAttribute (parse, "compare"); if (!compare_string) compare = FcOpEqual; else { compare = FcConfigLexCompare (compare_string); if (compare == FcOpInvalid) { FcConfigMessage (parse, FcSevereWarning, "invalid test compare \"%s\"", compare_string); return; } } expr = FcPopBinary (parse, FcOpComma); if (!expr) { FcConfigMessage (parse, FcSevereWarning, "missing test expression"); return; } test = FcTestCreate (parse, kind, qual, name, compare, expr); if (!test) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } FcVStackPushTest (parse, test); } static const FcOpMap fcModeOps[] = { { "assign", FcOpAssign }, { "assign_replace", FcOpAssignReplace }, { "prepend", FcOpPrepend }, { "prepend_first", FcOpPrependFirst }, { "append", FcOpAppend }, { "append_last", FcOpAppendLast }, }; #define NUM_MODE_OPS (int) (sizeof fcModeOps / sizeof fcModeOps[0]) static FcOp FcConfigLexMode (const FcChar8 *mode) { return FcConfigLexOp (mode, fcModeOps, NUM_MODE_OPS); } static void FcParseEdit (FcConfigParse *parse) { const FcChar8 *name; const FcChar8 *mode_string; const FcChar8 *binding_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; } } binding_string = FcConfigGetAttribute (parse, "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 edit binding \"%s\"", binding_string); return; } } expr = FcPopBinary (parse, FcOpComma); edit = FcEditCreate (parse, (char *) FcStrCopy (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 { FcConfigMessage (parse, FcSevereWarning, "invalid match target \"%s\"", kind_name); return; } } while ((vstack = FcVStackPop (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; break; default: FcConfigMessage (parse, FcSevereWarning, "invalid match element"); break; } FcVStackDestroy (vstack); } if (!FcConfigAddEdit (parse->config, test, edit, kind)) FcConfigMessage (parse, FcSevereError, "out of memory"); } static void FcParseAcceptRejectFont (FcConfigParse *parse, FcElement element) { FcVStack *vstack; while ((vstack = FcVStackPop (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; } FcVStackDestroy (vstack); } } static FcValue FcPopValue (FcConfigParse *parse) { FcVStack *vstack = FcVStackPop (parse); FcValue value; value.type = FcTypeVoid; if (!vstack) return value; switch (vstack->tag) { case FcVStackString: value.u.s = FcStrCopy (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; } FcVStackDestroy (vstack); 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"); return; } for (;;) { value = FcPopValue (parse); if (value.type == FcTypeVoid) break; if (!FcPatternAdd (pattern, name, value, FcTrue)) { FcConfigMessage (parse, FcSevereError, "out of memory"); break; } } FcVStackPushPattern (parse, FcPatternFreeze(pattern)); FcPatternDestroy (pattern); } static void FcParsePattern (FcConfigParse *parse) { FcVStack *vstack; FcPattern *pattern = FcPatternCreate (); if (!pattern) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } while ((vstack = FcVStackPop (parse))) { switch (vstack->tag) { case FcVStackPattern: if (!FcPatternAppend (pattern, vstack->u.pattern)) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } break; default: FcConfigMessage (parse, FcSevereWarning, "unknown pattern element"); break; } FcVStackDestroy (vstack); } FcVStackPushPattern (parse, pattern); } static void FcEndElement(void *userData, const XML_Char *name) { FcConfigParse *parse = userData; FcChar8 *data; if (!parse->pstack) return; switch (parse->pstack->element) { case FcElementNone: break; case FcElementFontconfig: break; case FcElementDir: data = FcStrBufDone (&parse->pstack->str); if (!data) { FcConfigMessage (parse, FcSevereError, "out of memory"); break; } #ifdef _WIN32 if (strcmp (data, "WINDOWSFONTDIR") == 0) { int rc; FcStrFree (data); data = malloc (1000); if (!data) { FcConfigMessage (parse, FcSevereError, "out of memory"); break; } FcMemAlloc (FC_MEM_STRING, 1000); rc = GetWindowsDirectory (data, 800); if (rc == 0 || rc > 800) { FcConfigMessage (parse, FcSevereError, "GetWindowsDirectory failed"); FcStrFree (data); break; } if (data [strlen (data) - 1] != '\\') strcat (data, "\\"); strcat (data, "fonts"); } #endif if (!FcStrUsesHome (data) || FcConfigHome ()) { if (!FcConfigAddDir (parse->config, data)) FcConfigMessage (parse, FcSevereError, "out of memory; cannot add directory %s", data); } FcStrFree (data); break; case FcElementCache: data = FcStrBufDone (&parse->pstack->str); if (!data) { FcConfigMessage (parse, FcSevereError, "out of memory"); break; } if (!FcStrUsesHome (data) || FcConfigHome ()) { if (!FcConfigSetCache (parse->config, data)) FcConfigMessage (parse, FcSevereError, "out of memory"); } FcStrFree (data); break; case FcElementInclude: FcParseInclude (parse); break; case FcElementConfig: break; case FcElementMatch: FcParseMatch (parse); break; case FcElementAlias: FcParseAlias (parse); break; case FcElementBlank: FcParseBlank (parse); break; case FcElementRescan: FcParseRescan (parse); break; case FcElementPrefer: FcParseFamilies (parse, FcVStackPrefer); break; case FcElementAccept: FcParseFamilies (parse, FcVStackAccept); break; case FcElementDefault: FcParseFamilies (parse, FcVStackDefault); break; case FcElementFamily: FcParseFamily (parse); break; case FcElementTest: FcParseTest (parse); break; case FcElementEdit: FcParseEdit (parse); break; case FcElementInt: FcParseInt (parse); break; case FcElementDouble: FcParseDouble (parse); break; case FcElementString: FcParseString (parse, FcVStackString); break; case FcElementMatrix: FcParseMatrix (parse); break; case FcElementBool: FcParseBool (parse); break; case FcElementCharset: /* FcParseCharset (parse); */ break; case FcElementSelectfont: break; case FcElementAcceptfont: case FcElementRejectfont: FcParseAcceptRejectFont (parse, parse->pstack->element); break; case FcElementGlob: FcParseString (parse, FcVStackGlob); break; case FcElementPattern: FcParsePattern (parse); break; case FcElementPatelt: FcParsePatelt (parse); break; case FcElementName: FcParseString (parse, FcVStackField); break; case FcElementConst: FcParseString (parse, FcVStackConstant); break; case FcElementOr: FcParseBinary (parse, FcOpOr); break; case FcElementAnd: FcParseBinary (parse, FcOpAnd); break; case FcElementEq: FcParseBinary (parse, FcOpEqual); break; case FcElementNotEq: FcParseBinary (parse, FcOpNotEqual); break; case FcElementLess: FcParseBinary (parse, FcOpLess); break; case FcElementLessEq: FcParseBinary (parse, FcOpLessEqual); break; case FcElementMore: FcParseBinary (parse, FcOpMore); break; case FcElementMoreEq: FcParseBinary (parse, FcOpMoreEqual); break; case FcElementContains: FcParseBinary (parse, FcOpContains); break; case FcElementNotContains: FcParseBinary (parse, FcOpNotContains); break; case FcElementPlus: FcParseBinary (parse, FcOpPlus); break; case FcElementMinus: FcParseBinary (parse, FcOpMinus); break; case FcElementTimes: FcParseBinary (parse, FcOpTimes); break; case FcElementDivide: FcParseBinary (parse, FcOpDivide); break; case FcElementNot: FcParseUnary (parse, FcOpNot); break; case FcElementIf: FcParseBinary (parse, FcOpQuest); break; case FcElementFloor: FcParseUnary (parse, FcOpFloor); break; case FcElementCeil: FcParseUnary (parse, FcOpCeil); break; case FcElementRound: FcParseUnary (parse, FcOpRound); break; case FcElementTrunc: FcParseUnary (parse, FcOpTrunc); break; case FcElementUnknown: break; } (void) FcPStackPop (parse); } static void FcCharacterData (void *userData, const XML_Char *s, int len) { FcConfigParse *parse = userData; if (!parse->pstack) return; if (!FcStrBufData (&parse->pstack->str, (FcChar8 *) s, len)) FcConfigMessage (parse, FcSevereError, "out of memory"); } static void FcStartDoctypeDecl (void *userData, const XML_Char *doctypeName, const XML_Char *sysid, const XML_Char *pubid, int has_internal_subset) { FcConfigParse *parse = userData; if (strcmp ((char *) doctypeName, "fontconfig") != 0) FcConfigMessage (parse, FcSevereError, "invalid doctype \"%s\"", doctypeName); } #if 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 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 *)) FcStrCmp); for (i = 0; ret && i < files->num; i++) ret = FcConfigParseAndLoad (config, files->strs[i], complain); } bail3: FcStrSetDestroy (files); bail2: free (file); bail1: closedir (d); bail0: return ret || !complain; } FcBool FcConfigParseAndLoad (FcConfig *config, const FcChar8 *name, FcBool complain) { XML_Parser p; FcChar8 *filename; int fd; int len; FcConfigParse parse; FcBool error = FcTrue; #if ENABLE_LIBXML2 xmlSAXHandler sax; char buf[BUFSIZ]; #else void *buf; #endif filename = FcConfigFilename (name); if (!filename) goto bail0; if (FcStrSetMember (config->configFiles, filename)) { FcStrFree (filename); return FcTrue; } if (!FcStrSetAdd (config->configFiles, filename)) { FcStrFree (filename); goto bail0; } if (FcFileIsDir (filename)) { FcBool ret = FcConfigParseAndLoadDir (config, name, filename, complain); FcStrFree (filename); return ret; } if (FcDebug () & FC_DBG_CONFIG) printf ("\tLoading config file %s\n", filename); fd = open ((char *) filename, O_RDONLY); if (fd == -1) { FcStrFree (filename); goto bail0; } #if 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; #if !ENABLE_LIBXML2 XML_SetUserData (p, &parse); XML_SetDoctypeDeclHandler (p, FcStartDoctypeDecl, FcEndDoctypeDecl); XML_SetElementHandler (p, FcStartElement, FcEndElement); XML_SetCharacterDataHandler (p, FcCharacterData); #endif /* ENABLE_LIBXML2 */ do { #if !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; } #if ENABLE_LIBXML2 if (xmlParseChunk (p, buf, len, len == 0)) #else if (!XML_ParseBuffer (p, len, len == 0)) #endif { FcConfigMessage (&parse, FcSevereError, "%s", XML_ErrorString (XML_GetErrorCode (p))); goto bail3; } } while (len != 0); error = parse.error; bail3: FcConfigCleanup (&parse); bail2: XML_ParserFree (p); bail1: close (fd); fd = -1; bail0: if (error && complain) { if (name) FcConfigMessage (0, FcSevereError, "Cannot load config file \"%s\"", name); else FcConfigMessage (0, FcSevereError, "Cannot load default config file"); return FcFalse; } return FcTrue; }