| /* |
| * 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 <string.h> |
| #include "fcint.h" |
| #include <fcntl.h> |
| #include <stdarg.h> |
| |
| #ifdef HAVE_DIRENT_H |
| #include <dirent.h> |
| #endif |
| |
| #ifdef ENABLE_LIBXML2 |
| |
| #include <libxml/parser.h> |
| |
| #define XML_Char xmlChar |
| #define XML_Parser xmlParserCtxtPtr |
| #define XML_ParserFree xmlFreeParserCtxt |
| #define XML_GetCurrentLineNumber xmlSAX2GetLineNumber |
| #define XML_GetErrorCode xmlCtxtGetLastError |
| #define XML_ErrorString(Error) (Error)->message |
| |
| #else /* ENABLE_LIBXML2 */ |
| |
| #ifndef HAVE_XMLPARSE_H |
| #define HAVE_XMLPARSE_H 0 |
| #endif |
| |
| #if HAVE_XMLPARSE_H |
| #include <xmlparse.h> |
| #else |
| #include <expat.h> |
| #endif |
| |
| #endif /* ENABLE_LIBXML2 */ |
| |
| #ifdef _WIN32 |
| #include <mbstring.h> |
| 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 FcChar8 * |
| _get_real_path_from_prefix(FcConfigParse *parse, const FcChar8 *path, const FcChar8 *prefix) |
| { |
| #ifdef _WIN32 |
| FcChar8 buffer[1000] = { 0 }; |
| #endif |
| FcChar8 *parent = NULL, *retval = NULL; |
| |
| if (prefix) |
| { |
| if (FcStrCmp (prefix, (const FcChar8 *) "xdg") == 0) |
| { |
| parent = FcConfigXdgDataHome (); |
| if (!parent) |
| { |
| /* Home directory might be disabled */ |
| 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); |
| } |
| |
| return retval; |
| } |
| |
| 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 <locale.h> |
| |
| 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 <family> in <alias> isn't supported and may not work as expected"); |
| new = FcExprCreateOp (parse->config, vstack->u.expr, FcOpComma, family); |
| if (!new) |
| FcConfigMessage (parse, FcSevereError, "out of memory"); |
| else |
| family = new; |
| } |
| else |
| new = vstack->u.expr; |
| if (new) |
| { |
| family = new; |
| vstack->tag = FcVStackNone; |
| } |
| break; |
| case FcVStackPrefer: |
| if (prefer) |
| FcExprDestroy (prefer); |
| prefer = vstack->u.expr; |
| vstack->tag = FcVStackNone; |
| break; |
| case FcVStackAccept: |
| if (accept) |
| FcExprDestroy (accept); |
| accept = vstack->u.expr; |
| vstack->tag = FcVStackNone; |
| break; |
| case FcVStackDefault: |
| if (def) |
| FcExprDestroy (def); |
| def = vstack->u.expr; |
| vstack->tag = FcVStackNone; |
| break; |
| case FcVStackTest: |
| if (rule) |
| { |
| r = FcRuleCreate (FcRuleTest, vstack->u.test); |
| r->next = rule; |
| rule = r; |
| } |
| else |
| rule = FcRuleCreate (FcRuleTest, vstack->u.test); |
| vstack->tag = FcVStackNone; |
| break; |
| default: |
| FcConfigMessage (parse, FcSevereWarning, "bad alias"); |
| break; |
| } |
| FcVStackPopAndDestroy (parse); |
| } |
| if (!family) |
| { |
| FcConfigMessage (parse, FcSevereError, "missing family in alias"); |
| if (prefer) |
| FcExprDestroy (prefer); |
| if (accept) |
| FcExprDestroy (accept); |
| if (def) |
| FcExprDestroy (def); |
| if (rule) |
| FcRuleDestroy (rule); |
| return; |
| } |
| if (!prefer && |
| !accept && |
| !def) |
| { |
| FcExprDestroy (family); |
| 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; |
| FcChar8 *prefix = 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 = _get_real_path_from_prefix (parse, data, attr); |
| 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); |
| |
| if (prefix) |
| FcStrFree (prefix); |
| } |
| |
| 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; |
| FcChar8 *prefix = 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 = _get_real_path_from_prefix (parse, data, attr); |
| 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); |
| |
| if (prefix) |
| FcStrFree (prefix); |
| } |
| |
| 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 <test> isn't supported and may not work as expected"); |
| } |
| test = FcTestCreate (parse, kind, qual, name, FC_OP (compare, flags), expr); |
| if (!test) |
| { |
| FcConfigMessage (parse, FcSevereError, "out of memory"); |
| return; |
| } |
| FcVStackPushTest (parse, test); |
| } |
| |
| static const FcOpMap fcModeOps[] = { |
| { "assign", FcOpAssign }, |
| { "assign_replace", FcOpAssignReplace }, |
| { "prepend", FcOpPrepend }, |
| { "prepend_first", FcOpPrependFirst }, |
| { "append", FcOpAppend }, |
| { "append_last", FcOpAppendLast }, |
| { "delete", FcOpDelete }, |
| { "delete_all", FcOpDeleteAll }, |
| }; |
| |
| #define NUM_MODE_OPS (int) (sizeof fcModeOps / sizeof fcModeOps[0]) |
| |
| static FcOp |
| FcConfigLexMode (const FcChar8 *mode) |
| { |
| return FcConfigLexOp (mode, fcModeOps, NUM_MODE_OPS); |
| } |
| |
| static void |
| FcParseEdit (FcConfigParse *parse) |
| { |
| const FcChar8 *name; |
| const FcChar8 *mode_string; |
| FcOp mode; |
| FcValueBinding binding; |
| FcExpr *expr; |
| FcEdit *edit; |
| |
| name = FcConfigGetAttribute (parse, "name"); |
| if (!name) |
| { |
| FcConfigMessage (parse, FcSevereWarning, "missing edit name"); |
| return; |
| } |
| mode_string = FcConfigGetAttribute (parse, "mode"); |
| if (!mode_string) |
| mode = FcOpAssign; |
| else |
| { |
| mode = FcConfigLexMode (mode_string); |
| if (mode == FcOpInvalid) |
| { |
| FcConfigMessage (parse, FcSevereWarning, "invalid edit mode \"%s\"", mode_string); |
| return; |
| } |
| } |
| if (!FcConfigLexBinding (parse, FcConfigGetAttribute (parse, "binding"), &binding)) |
| return; |
| |
| expr = FcPopBinary (parse, FcOpComma); |
| if ((mode == FcOpDelete || mode == FcOpDeleteAll) && |
| expr != NULL) |
| { |
| FcConfigMessage (parse, FcSevereWarning, "Expression doesn't take any effects for delete and delete_all"); |
| FcExprDestroy (expr); |
| expr = NULL; |
| } |
| edit = FcEditCreate (parse, FcObjectFromName ((char *) name), |
| mode, expr, binding); |
| if (!edit) |
| { |
| FcConfigMessage (parse, FcSevereError, "out of memory"); |
| FcExprDestroy (expr); |
| return; |
| } |
| if (!FcVStackPushEdit (parse, edit)) |
| FcEditDestroy (edit); |
| } |
| |
| static void |
| FcParseMatch (FcConfigParse *parse) |
| { |
| const FcChar8 *kind_name; |
| FcMatchKind kind; |
| FcVStack *vstack; |
| FcRule *rule = NULL, *r; |
| 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, |
| "<match target=\"scan\"> cannot edit user-defined object \"%s\"", |
| FcObjectName(vstack->u.edit->object)); |
| if (rule) |
| FcRuleDestroy (rule); |
| return; |
| } |
| r = FcRuleCreate (FcRuleEdit, vstack->u.edit); |
| if (rule) |
| r->next = rule; |
| rule = r; |
| vstack->tag = FcVStackNone; |
| break; |
| default: |
| FcConfigMessage (parse, FcSevereWarning, "invalid match element"); |
| break; |
| } |
| FcVStackPopAndDestroy (parse); |
| } |
| if (!rule) |
| { |
| FcConfigMessage (parse, FcSevereWarning, "No <test> nor <edit> elements in <match>"); |
| 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; |
<
|