2478 lines
53 KiB
C
2478 lines
53 KiB
C
/*
|
|
* $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 <fcntl.h>
|
|
#include <stdarg.h>
|
|
#include "fcint.h"
|
|
#include <dirent.h>
|
|
|
|
#if 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
|
|
#define STRICT
|
|
#include <windows.h>
|
|
#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 <locale.h>
|
|
|
|
static double
|
|
FcStrtod (char *s, char **end)
|
|
{
|
|
struct lconv *locale_data;
|
|
char *dot;
|
|
double v;
|
|
|
|
/*
|
|
* Have to swap the decimal point to match the current locale
|
|
* if that locale doesn't use 0x2e
|
|
*/
|
|
if ((dot = strchr (s, 0x2e)) &&
|
|
(locale_data = localeconv ()) &&
|
|
(locale_data->decimal_point[0] != 0x2e ||
|
|
locale_data->decimal_point[1] != 0))
|
|
{
|
|
char buf[128];
|
|
int slen = strlen (s);
|
|
int dlen = strlen (locale_data->decimal_point);
|
|
|
|
if (slen + dlen > (int) sizeof (buf))
|
|
{
|
|
if (end)
|
|
*end = s;
|
|
v = 0;
|
|
}
|
|
else
|
|
{
|
|
char *buf_end;
|
|
/* mantissa */
|
|
strncpy (buf, s, dot - s);
|
|
/* decimal point */
|
|
strcpy (buf + (dot - s), locale_data->decimal_point);
|
|
/* rest of number */
|
|
strcpy (buf + (dot - s) + dlen, dot + 1);
|
|
buf_end = 0;
|
|
v = strtod (buf, &buf_end);
|
|
if (buf_end) {
|
|
buf_end = s + (buf_end - buf);
|
|
if (buf_end > dot)
|
|
buf_end -= dlen - 1;
|
|
}
|
|
if (end)
|
|
*end = buf_end;
|
|
}
|
|
}
|
|
else
|
|
v = strtod (s, end);
|
|
return v;
|
|
}
|
|
|
|
static void
|
|
FcParseDouble (FcConfigParse *parse)
|
|
{
|
|
FcChar8 *s, *end;
|
|
double d;
|
|
|
|
if (!parse->pstack)
|
|
return;
|
|
s = 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;
|
|
}
|