fontconfig/src/fcxml.c

2608 lines
56 KiB
C
Raw Normal View History

2002-02-15 00:34:13 +01:00
/*
2008-08-12 22:34:24 +02:00
* fontconfig/src/fcxml.c
2002-02-15 00:34:13 +01:00
*
2004-12-07 02:14:46 +01:00
* Copyright © 2002 Keith Packard
2002-02-15 00:34:13 +01:00
*
* 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.
*
* THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
2002-02-15 00:34:13 +01:00
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
2002-02-15 00:34:13 +01:00
* 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 "fcint.h"
#include <fcntl.h>
2002-02-15 00:34:13 +01:00
#include <stdarg.h>
#include <dirent.h>
#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
2002-02-15 00:34:13 +01:00
#endif /* ENABLE_LIBXML2 */
#ifdef _WIN32
#define STRICT
#include <windows.h>
#undef STRICT
#endif
2002-02-15 00:34:13 +01:00
void
FcTestDestroy (FcTest *test)
{
if (test->next)
FcTestDestroy (test->next);
FcExprDestroy (test->expr);
FcMemFree (FC_MEM_TEST, sizeof (FcTest));
2002-02-15 00:34:13 +01:00
free (test);
}
FcExpr *
FcExprCreateInteger (int i)
{
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
if (e)
{
FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
2002-02-15 00:34:13 +01:00
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));
2002-02-15 00:34:13 +01:00
e->op = FcOpDouble;
e->u.dval = d;
}
return e;
}
FcExpr *
FcExprCreateString (const FcChar8 *s)
2002-02-15 00:34:13 +01:00
{
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
if (e)
{
FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
2002-02-15 00:34:13 +01:00
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));
2002-02-15 00:34:13 +01:00
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));
2002-02-15 00:34:13 +01:00
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));
2002-02-15 00:34:13 +01:00
e->op = FcOpNil;
}
return e;
}
FcExpr *
FcExprCreateField (const char *field)
{
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
if (e)
{
FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
2002-02-15 00:34:13 +01:00
e->op = FcOpField;
e->u.object = FcObjectFromName (field);
2002-02-15 00:34:13 +01:00
}
return e;
}
FcExpr *
FcExprCreateConst (const FcChar8 *constant)
2002-02-15 00:34:13 +01:00
{
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
if (e)
{
FcMemAlloc (FC_MEM_EXPR, sizeof (FcExpr));
2002-02-15 00:34:13 +01:00
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));
2002-02-15 00:34:13 +01:00
e->op = op;
e->u.tree.left = left;
e->u.tree.right = right;
}
return e;
}
void
FcExprDestroy (FcExpr *e)
{
if (!e)
return;
2002-02-15 00:34:13 +01:00
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:
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:
2002-02-15 00:34:13 +01:00
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:
2002-02-15 00:34:13 +01:00
FcExprDestroy (e->u.tree.left);
break;
case FcOpNil:
case FcOpInvalid:
break;
}
FcMemFree (FC_MEM_EXPR, sizeof (FcExpr));
2002-02-15 00:34:13 +01:00
free (e);
}
void
FcEditDestroy (FcEdit *e)
{
if (e->next)
FcEditDestroy (e->next);
if (e->expr)
FcExprDestroy (e->expr);
free (e);
2002-02-15 00:34:13 +01:00
}
typedef enum _FcElement {
FcElementNone,
FcElementFontconfig,
FcElementDir,
FcElementCacheDir,
FcElementCache,
FcElementInclude,
FcElementConfig,
FcElementMatch,
FcElementAlias,
FcElementBlank,
FcElementRescan,
FcElementPrefer,
FcElementAccept,
FcElementDefault,
FcElementFamily,
FcElementSelectfont,
FcElementAcceptfont,
FcElementRejectfont,
FcElementGlob,
FcElementPattern,
FcElementPatelt,
FcElementTest,
FcElementEdit,
FcElementInt,
FcElementDouble,
FcElementString,
FcElementMatrix,
FcElementBool,
FcElementCharset,
FcElementName,
FcElementConst,
FcElementOr,
FcElementAnd,
FcElementEq,
FcElementNotEq,
FcElementLess,
FcElementLessEq,
FcElementMore,
FcElementMoreEq,
FcElementContains,
FcElementNotContains,
FcElementPlus,
FcElementMinus,
FcElementTimes,
FcElementDivide,
FcElementNot,
FcElementIf,
FcElementFloor,
FcElementCeil,
FcElementRound,
FcElementTrunc,
FcElementUnknown
} FcElement;
static const struct {
const char name[16];
FcElement element;
} fcElementMap[] = {
{ "fontconfig", FcElementFontconfig },
{ "dir", FcElementDir },
{ "cachedir", FcElementCacheDir },
{ "cache", FcElementCache },
{ "include", FcElementInclude },
{ "config", FcElementConfig },
{ "match", FcElementMatch },
{ "alias", FcElementAlias },
{ "blank", FcElementBlank },
{ "rescan", FcElementRescan },
{ "prefer", FcElementPrefer },
{ "accept", FcElementAccept },
{ "default", FcElementDefault },
{ "family", FcElementFamily },
{ "selectfont", FcElementSelectfont },
{ "acceptfont", FcElementAcceptfont },
{ "rejectfont", FcElementRejectfont },
{ "glob", FcElementGlob },
{ "pattern", FcElementPattern },
{ "patelt", FcElementPatelt },
{ "test", FcElementTest },
{ "edit", FcElementEdit },
{ "int", FcElementInt },
{ "double", FcElementDouble },
{ "string", FcElementString },
{ "matrix", FcElementMatrix },
{ "bool", FcElementBool },
{ "charset", FcElementCharset },
{ "name", FcElementName },
{ "const", FcElementConst },
{ "or", FcElementOr },
{ "and", FcElementAnd },
{ "eq", FcElementEq },
{ "not_eq", FcElementNotEq },
{ "less", FcElementLess },
{ "less_eq", FcElementLessEq },
{ "more", FcElementMore },
{ "more_eq", FcElementMoreEq },
{ "contains", FcElementContains },
{ "not_contains", FcElementNotContains },
{ "plus", FcElementPlus },
{ "minus", FcElementMinus },
{ "times", FcElementTimes },
{ "divide", FcElementDivide },
{ "not", FcElementNot },
{ "if", FcElementIf },
{ "floor", FcElementFloor },
{ "ceil", FcElementCeil },
{ "round", FcElementRound },
{ "trunc", FcElementTrunc },
};
#define NUM_ELEMENT_MAPS (int) (sizeof fcElementMap / sizeof fcElementMap[0])
static FcElement
FcElementMap (const XML_Char *name)
{
int i;
for (i = 0; i < NUM_ELEMENT_MAPS; i++)
if (!strcmp ((char *) name, fcElementMap[i].name))
return fcElementMap[i].element;
return FcElementUnknown;
}
typedef struct _FcPStack {
struct _FcPStack *prev;
FcElement element;
FcChar8 **attr;
FcStrBuf str;
FcChar8 *attr_buf_static[16];
} FcPStack;
typedef enum _FcVStackTag {
FcVStackNone,
FcVStackString,
FcVStackFamily,
FcVStackField,
FcVStackConstant,
FcVStackGlob,
FcVStackPattern,
FcVStackPrefer,
FcVStackAccept,
FcVStackDefault,
FcVStackInteger,
FcVStackDouble,
FcVStackMatrix,
FcVStackBool,
FcVStackTest,
FcVStackExpr,
FcVStackEdit
} FcVStackTag;
typedef struct _FcVStack {
struct _FcVStack *prev;
FcPStack *pstack; /* related parse element */
FcVStackTag tag;
union {
FcChar8 *string;
int integer;
double _double;
FcMatrix *matrix;
FcBool bool_;
FcTest *test;
FcQual qual;
FcOp op;
FcExpr *expr;
FcEdit *edit;
FcPattern *pattern;
} u;
} FcVStack;
typedef struct _FcConfigParse {
FcPStack *pstack;
FcVStack *vstack;
FcBool error;
const FcChar8 *name;
FcConfig *config;
XML_Parser parser;
int pstack_static_used;
FcPStack pstack_static[8];
int vstack_static_used;
FcVStack vstack_static[64];
} FcConfigParse;
typedef enum _FcConfigSeverity {
FcSevereInfo, FcSevereWarning, FcSevereError
} FcConfigSeverity;
2002-02-15 00:34:13 +01:00
static void
FcConfigMessage (FcConfigParse *parse, FcConfigSeverity severe, const char *fmt, ...)
2002-02-15 00:34:13 +01:00
{
const char *s = "unknown";
2002-02-15 00:34:13 +01:00
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);
2002-02-15 00:34:13 +01:00
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;
if (type == (FcType) -1)
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 (expr->op) {
case FcOpInteger:
case FcOpDouble:
FcTypecheckValue (parse, FcTypeDouble, type);
break;
case FcOpString:
FcTypecheckValue (parse, FcTypeString, type);
break;
case FcOpMatrix:
FcTypecheckValue (parse, FcTypeMatrix, type);
break;
case FcOpBool:
FcTypecheckValue (parse, FcTypeBool, type);
break;
case FcOpCharSet:
FcTypecheckValue (parse, FcTypeCharSet, type);
break;
case FcOpNil:
break;
case FcOpField:
o = FcNameGetObjectType (FcObjectName (expr->u.object));
if (o)
FcTypecheckValue (parse, o->type, type);
break;
case FcOpConst:
c = FcNameGetConstant (expr->u.constant);
if (c)
{
o = FcNameGetObjectType (c->object);
if (o)
FcTypecheckValue (parse, o->type, type);
}
else
FcConfigMessage (parse, FcSevereWarning,
"invalid constant used : %s",
expr->u.constant);
break;
case FcOpQuest:
FcTypecheckExpr (parse, expr->u.tree.left, FcTypeBool);
FcTypecheckExpr (parse, expr->u.tree.right->u.tree.left, type);
FcTypecheckExpr (parse, expr->u.tree.right->u.tree.right, type);
break;
case FcOpAssign:
case FcOpAssignReplace:
break;
case FcOpEqual:
case FcOpNotEqual:
case FcOpLess:
case FcOpLessEqual:
case FcOpMore:
case FcOpMoreEqual:
case FcOpContains:
case FcOpNotContains:
case FcOpListing:
FcTypecheckValue (parse, FcTypeBool, type);
break;
case FcOpComma:
case FcOpOr:
case FcOpAnd:
case FcOpPlus:
case FcOpMinus:
case FcOpTimes:
case FcOpDivide:
FcTypecheckExpr (parse, expr->u.tree.left, type);
FcTypecheckExpr (parse, expr->u.tree.right, type);
break;
case FcOpNot:
FcTypecheckValue (parse, FcTypeBool, type);
FcTypecheckExpr (parse, expr->u.tree.left, FcTypeBool);
break;
case FcOpFloor:
case FcOpCeil:
case FcOpRound:
case FcOpTrunc:
FcTypecheckValue (parse, FcTypeDouble, type);
FcTypecheckExpr (parse, expr->u.tree.left, FcTypeDouble);
break;
default:
break;
}
}
static FcTest *
FcTestCreate (FcConfigParse *parse,
FcMatchKind kind,
FcQual qual,
const FcChar8 *field,
FcOp compare,
FcExpr *expr)
{
FcTest *test = (FcTest *) malloc (sizeof (FcTest));
if (test)
{
const FcObjectType *o;
FcMemAlloc (FC_MEM_TEST, sizeof (FcTest));
test->next = 0;
test->kind = kind;
test->qual = qual;
test->object = FcObjectFromName ((const char *) field);
test->op = compare;
test->expr = expr;
o = FcNameGetObjectType (FcObjectName (test->object));
if (o)
FcTypecheckExpr (parse, expr, o->type);
}
return 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->next = 0;
e->object = object;
e->op = op;
e->expr = expr;
e->binding = binding;
o = FcNameGetObjectType (FcObjectName (e->object));
if (o)
FcTypecheckExpr (parse, expr, o->type);
}
return e;
}
static FcVStack *
FcVStackCreateAndPush (FcConfigParse *parse)
2002-02-15 00:34:13 +01:00
{
FcVStack *new;
2002-02-15 00:34:13 +01:00
if (parse->vstack_static_used < sizeof (parse->vstack_static) / sizeof (parse->vstack_static[0]))
new = &parse->vstack_static[parse->vstack_static_used++];
else
{
new = malloc (sizeof (FcVStack));
if (!new)
return 0;
FcMemAlloc (FC_MEM_VSTACK, sizeof (FcVStack));
}
new->tag = FcVStackNone;
new->prev = 0;
2002-02-15 00:34:13 +01:00
new->prev = parse->vstack;
new->pstack = parse->pstack ? parse->pstack->prev : 0;
parse->vstack = new;
2002-02-15 00:34:13 +01:00
return new;
2002-02-15 00:34:13 +01:00
}
static FcBool
FcVStackPushString (FcConfigParse *parse, FcVStackTag tag, FcChar8 *string)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack = FcVStackCreateAndPush (parse);
if (!vstack)
return FcFalse;
vstack->u.string = string;
vstack->tag = tag;
return FcTrue;
2002-02-15 00:34:13 +01:00
}
static FcBool
FcVStackPushInteger (FcConfigParse *parse, int integer)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack = FcVStackCreateAndPush (parse);
if (!vstack)
2002-02-15 00:34:13 +01:00
return FcFalse;
vstack->u.integer = integer;
vstack->tag = FcVStackInteger;
return FcTrue;
2002-02-15 00:34:13 +01:00
}
static FcBool
FcVStackPushDouble (FcConfigParse *parse, double _double)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack = FcVStackCreateAndPush (parse);
if (!vstack)
2002-02-15 00:34:13 +01:00
return FcFalse;
vstack->u._double = _double;
vstack->tag = FcVStackDouble;
return FcTrue;
2002-02-15 00:34:13 +01:00
}
static FcBool
FcVStackPushMatrix (FcConfigParse *parse, FcMatrix *matrix)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack;
matrix = FcMatrixCopy (matrix);
if (!matrix)
return FcFalse;
vstack = FcVStackCreateAndPush (parse);
if (!vstack)
return FcFalse;
vstack->u.matrix = matrix;
vstack->tag = FcVStackMatrix;
return FcTrue;
2002-02-15 00:34:13 +01:00
}
static FcBool
FcVStackPushBool (FcConfigParse *parse, FcBool bool_)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack = FcVStackCreateAndPush (parse);
if (!vstack)
return FcFalse;
vstack->u.bool_ = bool_;
vstack->tag = FcVStackBool;
return FcTrue;
}
2002-02-15 00:34:13 +01:00
static FcBool
FcVStackPushTest (FcConfigParse *parse, FcTest *test)
{
FcVStack *vstack = FcVStackCreateAndPush (parse);
if (!vstack)
2002-02-15 00:34:13 +01:00
return FcFalse;
vstack->u.test = test;
vstack->tag = FcVStackTest;
2002-02-15 00:34:13 +01:00
return FcTrue;
}
static FcBool
FcVStackPushExpr (FcConfigParse *parse, FcVStackTag tag, FcExpr *expr)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack = FcVStackCreateAndPush (parse);
if (!vstack)
return FcFalse;
vstack->u.expr = expr;
vstack->tag = tag;
return FcTrue;
}
2002-02-15 00:34:13 +01:00
static FcBool
FcVStackPushEdit (FcConfigParse *parse, FcEdit *edit)
{
FcVStack *vstack = FcVStackCreateAndPush (parse);
if (!vstack)
2002-02-15 00:34:13 +01:00
return FcFalse;
vstack->u.edit = edit;
vstack->tag = FcVStackEdit;
2002-02-15 00:34:13 +01:00
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)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack;
2002-02-15 00:34:13 +01:00
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;
2002-02-15 00:34:13 +01:00
}
static void
FcVStackPopAndDestroy (FcConfigParse *parse)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack = parse->vstack;
2002-02-15 00:34:13 +01:00
if (!vstack || vstack->pstack != parse->pstack)
return;
parse->vstack = 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;
}
if (vstack == &parse->vstack_static[parse->vstack_static_used - 1])
parse->vstack_static_used--;
else
{
FcMemFree (FC_MEM_VSTACK, sizeof (FcVStack));
free (vstack);
}
}
static void
FcVStackClear (FcConfigParse *parse)
{
while (FcVStackPeek (parse))
FcVStackPopAndDestroy (parse);
}
static int
FcVStackElements (FcConfigParse *parse)
{
int h = 0;
FcVStack *vstack = parse->vstack;
while (vstack && vstack->pstack == parse->pstack)
{
h++;
vstack = vstack->prev;
2002-02-15 00:34:13 +01:00
}
return h;
2002-02-15 00:34:13 +01:00
}
static FcChar8 **
FcConfigSaveAttr (const XML_Char **attr, FcChar8 **buf, int size_bytes)
2002-02-15 00:34:13 +01:00
{
int slen;
int i;
FcChar8 **new;
FcChar8 *s;
2002-02-15 00:34:13 +01:00
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;
}
FcMemAlloc (FC_MEM_ATTR, 1); /* size is too expensive */
}
s = (FcChar8 *) (new + (i + 1));
for (i = 0; attr[i]; i++)
2002-02-15 00:34:13 +01:00
{
new[i] = s;
strcpy ((char *) s, (char *) attr[i]);
s += strlen ((char *) s) + 1;
2002-02-15 00:34:13 +01:00
}
new[i] = 0;
return new;
}
2002-02-15 00:34:13 +01:00
static FcBool
FcPStackPush (FcConfigParse *parse, FcElement element, const XML_Char **attr)
{
FcPStack *new;
if (parse->pstack_static_used < sizeof (parse->pstack_static) / sizeof (parse->pstack_static[0]))
new = &parse->pstack_static[parse->pstack_static_used++];
else
{
new = malloc (sizeof (FcPStack));
if (!new)
return FcFalse;
FcMemAlloc (FC_MEM_PSTACK, sizeof (FcPStack));
}
new->prev = parse->pstack;
new->element = element;
new->attr = FcConfigSaveAttr (attr, new->attr_buf_static, sizeof (new->attr_buf_static));
FcStrBufInit (&new->str, 0, 0);
parse->pstack = new;
return FcTrue;
}
2002-02-15 00:34:13 +01:00
static FcBool
FcPStackPop (FcConfigParse *parse)
{
FcPStack *old;
if (!parse->pstack)
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereError, "mismatching element");
return FcFalse;
2002-02-15 00:34:13 +01:00
}
FcVStackClear (parse);
old = parse->pstack;
parse->pstack = old->prev;
FcStrBufDestroy (&old->str);
if (old->attr && old->attr != old->attr_buf_static)
{
FcMemFree (FC_MEM_ATTR, 1); /* size is to expensive */
free (old->attr);
}
if (old == &parse->pstack_static[parse->pstack_static_used - 1])
parse->pstack_static_used--;
else
{
FcMemFree (FC_MEM_PSTACK, sizeof (FcPStack));
free (old);
}
return FcTrue;
2002-02-15 00:34:13 +01:00
}
static FcBool
FcConfigInit (FcConfigParse *parse, const FcChar8 *name, FcConfig *config, XML_Parser parser)
2002-02-15 00:34:13 +01:00
{
parse->pstack = 0;
parse->pstack_static_used = 0;
parse->vstack = 0;
parse->vstack_static_used = 0;
parse->error = FcFalse;
parse->name = name;
parse->config = config;
parse->parser = parser;
return FcTrue;
}
static void
FcConfigCleanup (FcConfigParse *parse)
{
while (parse->pstack)
FcPStackPop (parse);
}
static const FcChar8 *
FcConfigGetAttribute (FcConfigParse *parse, const char *attr)
{
FcChar8 **attrs;
if (!parse->pstack)
2002-02-15 00:34:13 +01:00
return 0;
attrs = parse->pstack->attr;
if (!attrs)
return 0;
while (*attrs)
2002-02-15 00:34:13 +01:00
{
if (!strcmp ((char *) *attrs, attr))
return attrs[1];
attrs += 2;
2002-02-15 00:34:13 +01:00
}
return 0;
}
static void
FcStartElement(void *userData, const XML_Char *name, const XML_Char **attr)
2002-02-15 00:34:13 +01:00
{
FcConfigParse *parse = userData;
FcElement element;
element = FcElementMap (name);
if (element == FcElementUnknown)
FcConfigMessage (parse, FcSevereWarning, "unknown element \"%s\"", name);
if (!FcPStackPush (parse, element, attr))
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereError, "out of memory");
return;
2002-02-15 00:34:13 +01:00
}
return;
}
2002-02-15 00:34:13 +01:00
static void
FcParseBlank (FcConfigParse *parse)
{
int n = FcVStackElements (parse);
while (n-- > 0)
2002-02-15 00:34:13 +01:00
{
FcVStack *v = FcVStackFetch (parse, n);
if (v->tag != FcVStackInteger)
FcConfigMessage (parse, FcSevereError, "non-integer blank");
else
2002-02-15 00:34:13 +01:00
{
if (!parse->config->blanks)
2002-02-15 00:34:13 +01:00
{
parse->config->blanks = FcBlanksCreate ();
if (!parse->config->blanks)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
break;
}
2002-02-15 00:34:13 +01:00
}
if (!FcBlanksAdd (parse->config->blanks, v->u.integer))
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereError, "out of memory");
break;
2002-02-15 00:34:13 +01:00
}
}
}
}
2002-02-15 00:34:13 +01:00
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)
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereError, "out of memory");
return;
2002-02-15 00:34:13 +01:00
}
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);
2002-02-15 00:34:13 +01:00
}
/*
* 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)
2002-02-15 00:34:13 +01:00
{
FcChar8 *s, *end;
double d;
2002-02-15 00:34:13 +01:00
if (!parse->pstack)
return;
s = FcStrBufDoneStatic (&parse->pstack->str);
if (!s)
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereError, "out of memory");
return;
2002-02-15 00:34:13 +01:00
}
end = 0;
d = FcStrtod ((char *) s, (char **)&end);
if (end != s + strlen ((char *) s))
FcConfigMessage (parse, FcSevereError, "\"%s\": not a valid double", s);
2002-02-15 00:34:13 +01:00
else
FcVStackPushDouble (parse, d);
FcStrBufDestroy (&parse->pstack->str);
}
2002-02-15 00:34:13 +01:00
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 = FcVStackPeek (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;
}
FcVStackPopAndDestroy (parse);
matrix_state--;
}
if (matrix_state != m_done)
FcConfigMessage (parse, FcSevereError, "wrong number of matrix elements");
else
FcVStackPushMatrix (parse, &m);
2002-02-15 00:34:13 +01:00
}
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 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)
2002-02-15 00:34:13 +01:00
{
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 (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 (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;
2002-02-15 00:34:13 +01:00
FcEdit *edit = 0, *next;
FcVStack *vstack;
2002-02-15 00:34:13 +01:00
FcTest *test;
FcValueBinding binding;
2002-02-15 00:34:13 +01:00
if (!FcConfigLexBinding (parse, FcConfigGetAttribute (parse, "binding"), &binding))
return;
while ((vstack = FcVStackPeek (parse)))
2002-02-15 00:34:13 +01:00
{
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;
}
FcVStackPopAndDestroy (parse);
2002-02-15 00:34:13 +01:00
}
if (!family)
{
FcConfigMessage (parse, FcSevereError, "missing family in alias");
if (prefer)
FcExprDestroy (prefer);
if (accept)
FcExprDestroy (accept);
if (def)
FcExprDestroy (def);
return;
}
2002-02-15 00:34:13 +01:00
if (prefer)
{
edit = FcEditCreate (parse,
FC_FAMILY_OBJECT,
2002-02-15 00:34:13 +01:00
FcOpPrepend,
2002-07-31 03:36:37 +02:00
prefer,
binding);
2002-02-15 00:34:13 +01:00
if (edit)
edit->next = 0;
else
FcExprDestroy (prefer);
2002-02-15 00:34:13 +01:00
}
if (accept)
{
next = edit;
edit = FcEditCreate (parse,
FC_FAMILY_OBJECT,
2002-02-15 00:34:13 +01:00
FcOpAppend,
2002-07-31 03:36:37 +02:00
accept,
binding);
2002-02-15 00:34:13 +01:00
if (edit)
edit->next = next;
else
FcExprDestroy (accept);
2002-02-15 00:34:13 +01:00
}
if (def)
{
next = edit;
edit = FcEditCreate (parse,
FC_FAMILY_OBJECT,
2002-07-31 03:36:37 +02:00
FcOpAppendLast,
def,
binding);
2002-02-15 00:34:13 +01:00
if (edit)
edit->next = next;
else
FcExprDestroy (def);
2002-02-15 00:34:13 +01:00
}
if (edit)
{
test = FcTestCreate (parse, FcMatchPattern,
FcQualAny,
2002-06-18 18:47:33 +02:00
(FcChar8 *) FC_FAMILY,
2002-02-15 00:34:13 +01:00
FcOpEqual,
family);
if (test)
if (!FcConfigAddEdit (parse->config, test, edit, FcMatchPattern))
FcTestDestroy (test);
2002-02-15 00:34:13 +01:00
}
else
FcExprDestroy (family);
2002-02-15 00:34:13 +01:00
}
static FcExpr *
FcPopExpr (FcConfigParse *parse)
2002-02-15 00:34:13 +01:00
{
FcVStack *vstack = FcVStackPeek (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;
}
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;
2002-02-15 00:34:13 +01:00
while ((left = FcPopExpr (parse)))
2002-02-15 00:34:13 +01:00
{
if (expr)
2002-02-15 00:34:13 +01:00
{
new = FcExprCreateOp (left, op, expr);
if (!new)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
FcExprDestroy (left);
FcExprDestroy (expr);
return 0;
}
expr = new;
2002-02-15 00:34:13 +01:00
}
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 = FcStrBufDoneStatic (&parse->pstack->str);
if (!s)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
return;
}
i = FcConfigGetAttribute (parse, "ignore_missing");
if (i && FcConfigLexBool (parse, (FcChar8 *) i) == FcTrue)
ignore_missing = FcTrue;
if (!FcConfigParseAndLoad (parse->config, s, !ignore_missing))
parse->error = FcTrue;
FcStrBufDestroy (&parse->pstack->str);
}
typedef struct _FcOpMap {
char name[16];
FcOp op;
} FcOpMap;
static FcOp
FcConfigLexOp (const FcChar8 *op, const FcOpMap *map, int nmap)
{
int i;
for (i = 0; i < nmap; i++)
if (!strcmp ((char *) op, map[i].name))
return map[i].op;
return FcOpInvalid;
}
static const FcOpMap fcCompareOps[] = {
{ "eq", FcOpEqual },
{ "not_eq", FcOpNotEqual },
{ "less", FcOpLess },
{ "less_eq", FcOpLessEqual },
{ "more", FcOpMore },
{ "more_eq", FcOpMoreEqual },
{ "contains", FcOpContains },
{ "not_contains", FcOpNotContains }
};
#define NUM_COMPARE_OPS (int) (sizeof fcCompareOps / sizeof fcCompareOps[0])
static FcOp
FcConfigLexCompare (const FcChar8 *compare)
{
return FcConfigLexOp (compare, fcCompareOps, NUM_COMPARE_OPS);
}
static void
FcParseTest (FcConfigParse *parse)
{
const FcChar8 *kind_string;
FcMatchKind kind;
const FcChar8 *qual_string;
FcQual qual;
const FcChar8 *name;
const FcChar8 *compare_string;
FcOp compare;
FcExpr *expr;
FcTest *test;
kind_string = FcConfigGetAttribute (parse, "target");
if (!kind_string)
kind = FcMatchDefault;
else
{
if (!strcmp ((char *) kind_string, "pattern"))
kind = FcMatchPattern;
else if (!strcmp ((char *) kind_string, "font"))
kind = FcMatchFont;
else if (!strcmp ((char *) kind_string, "scan"))
kind = FcMatchScan;
else if (!strcmp ((char *) kind_string, "default"))
kind = FcMatchDefault;
else
{
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
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereWarning, "invalid test qual \"%s\"", qual_string);
return;
2002-02-15 00:34:13 +01:00
}
}
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)
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereWarning, "invalid test compare \"%s\"", compare_string);
return;
2002-02-15 00:34:13 +01:00
}
}
expr = FcPopBinary (parse, FcOpComma);
if (!expr)
{
FcConfigMessage (parse, FcSevereWarning, "missing test expression");
return;
}
test = FcTestCreate (parse, kind, qual, name, compare, expr);
if (!test)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
return;
}
FcVStackPushTest (parse, test);
}
static const FcOpMap fcModeOps[] = {
{ "assign", FcOpAssign },
{ "assign_replace", FcOpAssignReplace },
{ "prepend", FcOpPrepend },
{ "prepend_first", FcOpPrependFirst },
{ "append", FcOpAppend },
{ "append_last", FcOpAppendLast },
};
#define NUM_MODE_OPS (int) (sizeof fcModeOps / sizeof fcModeOps[0])
static FcOp
FcConfigLexMode (const FcChar8 *mode)
{
return FcConfigLexOp (mode, fcModeOps, NUM_MODE_OPS);
}
static void
FcParseEdit (FcConfigParse *parse)
{
const FcChar8 *name;
const FcChar8 *mode_string;
FcOp mode;
2002-07-31 03:36:37 +02:00
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)
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereWarning, "invalid edit mode \"%s\"", mode_string);
return;
2002-02-15 00:34:13 +01:00
}
}
if (!FcConfigLexBinding (parse, FcConfigGetAttribute (parse, "binding"), &binding))
return;
expr = FcPopBinary (parse, FcOpComma);
edit = FcEditCreate (parse, FcObjectFromName ((char *) name),
mode, expr, binding);
if (!edit)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
FcExprDestroy (expr);
return;
}
if (!FcVStackPushEdit (parse, edit))
FcEditDestroy (edit);
}
static void
FcParseMatch (FcConfigParse *parse)
{
const FcChar8 *kind_name;
FcMatchKind kind;
FcTest *test = 0;
FcEdit *edit = 0;
FcVStack *vstack;
kind_name = FcConfigGetAttribute (parse, "target");
if (!kind_name)
kind = FcMatchPattern;
else
{
if (!strcmp ((char *) kind_name, "pattern"))
kind = FcMatchPattern;
else if (!strcmp ((char *) kind_name, "font"))
kind = FcMatchFont;
else if (!strcmp ((char *) kind_name, "scan"))
kind = FcMatchScan;
else
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereWarning, "invalid match target \"%s\"", kind_name);
return;
2002-02-15 00:34:13 +01:00
}
}
while ((vstack = FcVStackPeek (parse)))
{
switch (vstack->tag) {
case FcVStackTest:
vstack->u.test->next = test;
test = vstack->u.test;
vstack->tag = FcVStackNone;
break;
case FcVStackEdit:
vstack->u.edit->next = edit;
edit = vstack->u.edit;
vstack->tag = FcVStackNone;
if (kind == FcMatchScan && edit->object > FC_MAX_BASE_OBJECT)
{
FcConfigMessage (parse, FcSevereError,
"<match target=\"scan\"> cannot edit user-defined object \"%s\"",
FcObjectName(edit->object));
}
break;
default:
FcConfigMessage (parse, FcSevereWarning, "invalid match element");
break;
}
FcVStackPopAndDestroy (parse);
}
if (!FcConfigAddEdit (parse->config, test, edit, kind))
FcConfigMessage (parse, FcSevereError, "out of memory");
}
static void
FcParseAcceptRejectFont (FcConfigParse *parse, FcElement element)
{
FcVStack *vstack;
while ((vstack = FcVStackPeek (parse)))
{
switch (vstack->tag) {
case FcVStackGlob:
if (!FcConfigGlobAdd (parse->config,
vstack->u.string,
element == FcElementAcceptfont))
{
FcConfigMessage (parse, FcSevereError, "out of memory");
}
break;
case FcVStackPattern:
if (!FcConfigPatternsAdd (parse->config,
vstack->u.pattern,
element == FcElementAcceptfont))
{
FcConfigMessage (parse, FcSevereError, "out of memory");
}
else
vstack->tag = FcVStackNone;
break;
default:
FcConfigMessage (parse, FcSevereWarning, "bad font selector");
break;
}
FcVStackPopAndDestroy (parse);
}
}
static FcValue
FcPopValue (FcConfigParse *parse)
{
FcVStack *vstack = FcVStackPeek (parse);
FcValue value;
value.type = FcTypeVoid;
if (!vstack)
return value;
switch (vstack->tag) {
case FcVStackString:
value.u.s = 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;
}
FcVStackPopAndDestroy (parse);
return value;
}
static void
FcParsePatelt (FcConfigParse *parse)
{
FcValue value;
FcPattern *pattern = FcPatternCreate ();
const char *name;
if (!pattern)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
return;
}
name = (char *) FcConfigGetAttribute (parse, "name");
if (!name)
{
FcConfigMessage (parse, FcSevereWarning, "missing pattern element name");
FcPatternDestroy (pattern);
return;
}
for (;;)
{
value = FcPopValue (parse);
if (value.type == FcTypeVoid)
break;
if (!FcPatternAdd (pattern, name, value, FcTrue))
{
FcConfigMessage (parse, FcSevereError, "out of memory");
FcValueDestroy(value);
break;
}
FcValueDestroy(value);
}
2006-04-27 09:54:07 +02:00
FcVStackPushPattern (parse, pattern);
}
static void
FcParsePattern (FcConfigParse *parse)
{
FcVStack *vstack;
FcPattern *pattern = FcPatternCreate ();
if (!pattern)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
return;
}
while ((vstack = FcVStackPeek (parse)))
{
switch (vstack->tag) {
case FcVStackPattern:
if (!FcPatternAppend (pattern, vstack->u.pattern))
{
FcConfigMessage (parse, FcSevereError, "out of memory");
FcPatternDestroy (pattern);
return;
}
break;
default:
FcConfigMessage (parse, FcSevereWarning, "unknown pattern element");
break;
}
FcVStackPopAndDestroy (parse);
}
FcVStackPushPattern (parse, pattern);
}
static void
FcEndElement(void *userData, const XML_Char *name)
{
FcConfigParse *parse = userData;
FcChar8 *data;
if (!parse->pstack)
return;
switch (parse->pstack->element) {
case FcElementNone:
break;
case FcElementFontconfig:
break;
case FcElementDir:
data = FcStrBufDoneStatic (&parse->pstack->str);
if (!data)
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereError, "out of memory");
break;
2002-02-15 00:34:13 +01:00
}
#ifdef _WIN32
if (strcmp (data, "CUSTOMFONTDIR") == 0)
{
2008-08-12 20:10:03 +02:00
char *p;
FcStrFree (data);
data = malloc (1000);
if (!data)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
break;
}
FcMemAlloc (FC_MEM_STRING, 1000);
if(!GetModuleFileName(NULL, data, 1000))
{
FcConfigMessage (parse, FcSevereError, "GetModuleFileName failed");
FcStrFree (data);
break;
}
2008-08-12 20:10:03 +02:00
p = strrchr (data, '\\');
if (p) *p = '\0';
strcat (data, "\\fonts");
}
else if (strcmp (data, "APPSHAREFONTDIR") == 0)
{
char *p;
FcStrFree (data);
data = malloc (1000);
if (!data)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
break;
}
FcMemAlloc (FC_MEM_STRING, 1000);
if(!GetModuleFileName(NULL, data, 1000))
{
FcConfigMessage (parse, FcSevereError, "GetModuleFileName failed");
FcStrFree (data);
break;
}
p = strrchr (data, '\\');
if (p) *p = '\0';
strcat (data, "\\..\\share\\fonts");
}
else 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 (strlen ((char *) data) == 0)
FcConfigMessage (parse, FcSevereWarning, "empty font directory name ignored");
else if (!FcStrUsesHome (data) || FcConfigHome ())
{
if (!FcConfigAddDir (parse->config, data))
FcConfigMessage (parse, FcSevereError, "out of memory; cannot add directory %s", data);
}
FcStrBufDestroy (&parse->pstack->str);
break;
case FcElementCacheDir:
data = FcStrBufDone (&parse->pstack->str);
if (!data)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
break;
}
Workaround for stat() brokenness in Microsoft's C library (bug 8526) Fix a couple of longstanding problems with fontconfig on Windows that manifest themselves especially in GIMP. The root cause to the problems is in Microsoft's incredibly stupid stat() implementation. Basically, stat() returns wrong timestamp fields for files on NTFS filesystems on machines that use automatic DST switching. See for instance http://bugzilla.gnome.org/show_bug.cgi?id=154968 and http://www.codeproject.com/datetime/dstbugs.asp As fccache.c now looks at more fields in the stat struct I fill in them all. I noticed that fstat() is used only on a fd just after opening it, so on Win32 I just call my stat() replacement before opening instead... Implementing a good replacement for fstat() would be harder because the code in fccache.c wants to compare inode numbers. There are no (readily accessible) inode numbers on Win32, so I fake it with the hash of the full file name, in the case as it is on disk. And fstat() doesn't know the full file name, so it would be rather hard to come up with a inode number to identify the file. The patch also adds similar handling for the cache directory as for the fonts directory: If a cachedir element in fonts.conf contains the magic string "WINDOWSTEMPDIR_FONTCONFIG_CACHE" it is replaced at runtime with a path under the machine's (or user's) temp folder as returned by GetTempPath(). I don't want to hardcode any pathnames in a fonts.conf intended to be distributed to end-users, most of which who wouldn't know how to edit it anyway. And requiring an installer to edit it gets complicated.
2007-11-14 01:41:55 +01:00
#ifdef _WIN32
if (strcmp (data, "WINDOWSTEMPDIR_FONTCONFIG_CACHE") == 0)
{
int rc;
FcStrFree (data);
data = malloc (1000);
if (!data)
{
FcConfigMessage (parse, FcSevereError, "out of memory");
break;
}
FcMemAlloc (FC_MEM_STRING, 1000);
rc = GetTempPath (800, data);
if (rc == 0 || rc > 800)
{
FcConfigMessage (parse, FcSevereError, "GetWindowsDirectory failed");
FcStrFree (data);
break;
}
if (data [strlen (data) - 1] != '\\')
strcat (data, "\\");
strcat (data, "fontconfig\\cache");
}
#endif
if (!FcStrUsesHome (data) || FcConfigHome ())
{
if (!FcConfigAddCacheDir (parse->config, data))
FcConfigMessage (parse, FcSevereError, "out of memory; cannot add cache directory %s", data);
}
FcStrFree (data);
break;
case FcElementCache:
data = FcStrBufDoneStatic (&parse->pstack->str);
if (!data)
2002-02-15 00:34:13 +01:00
{
FcConfigMessage (parse, FcSevereError, "out of memory");
2002-02-15 00:34:13 +01:00
break;
}
/* discard this data; no longer used */
FcStrBufDestroy (&parse->pstack->str);
break;
case FcElementInclude:
FcParseInclude (parse);
break;
case FcElementConfig:
break;
case FcElementMatch:
FcParseMatch (parse);
break;
case FcElementAlias:
FcParseAlias (parse);
break;
case FcElementBlank:
FcParseBlank (parse);
break;
case FcElementRescan:
FcParseRescan (parse);
break;
case FcElementPrefer:
FcParseFamilies (parse, FcVStackPrefer);
break;
case FcElementAccept:
FcParseFamilies (parse, FcVStackAccept);
break;
case FcElementDefault:
FcParseFamilies (parse, FcVStackDefault);
break;
case FcElementFamily:
FcParseFamily (parse);
break;
case FcElementTest:
FcParseTest (parse);
break;
case FcElementEdit:
FcParseEdit (parse);
break;
case FcElementInt:
FcParseInt (parse);
break;
case FcElementDouble:
FcParseDouble (parse);
break;
case FcElementString:
FcParseString (parse, FcVStackString);
break;
case FcElementMatrix:
FcParseMatrix (parse);
break;
case FcElementBool:
FcParseBool (parse);
break;
case FcElementCharset:
/* FcParseCharset (parse); */
break;
case FcElementSelectfont:
break;
case FcElementAcceptfont:
case FcElementRejectfont:
FcParseAcceptRejectFont (parse, parse->pstack->element);
break;
case FcElementGlob:
FcParseString (parse, FcVStackGlob);
break;
case FcElementPattern:
FcParsePattern (parse);
break;
case FcElementPatelt:
FcParsePatelt (parse);
break;
case FcElementName:
FcParseString (parse, FcVStackField);
break;
case FcElementConst:
FcParseString (parse, FcVStackConstant);
break;
case FcElementOr:
FcParseBinary (parse, FcOpOr);
break;
case FcElementAnd:
FcParseBinary (parse, FcOpAnd);
break;
case FcElementEq:
FcParseBinary (parse, FcOpEqual);
break;
case FcElementNotEq:
FcParseBinary (parse, FcOpNotEqual);
break;
case FcElementLess:
FcParseBinary (parse, FcOpLess);
break;
case FcElementLessEq:
FcParseBinary (parse, FcOpLessEqual);
break;
case FcElementMore:
FcParseBinary (parse, FcOpMore);
break;
case FcElementMoreEq:
FcParseBinary (parse, FcOpMoreEqual);
break;
case FcElementContains:
FcParseBinary (parse, FcOpContains);
break;
case FcElementNotContains:
FcParseBinary (parse, FcOpNotContains);
break;
case FcElementPlus:
FcParseBinary (parse, FcOpPlus);
break;
case FcElementMinus:
FcParseBinary (parse, FcOpMinus);
break;
case FcElementTimes:
FcParseBinary (parse, FcOpTimes);
break;
case FcElementDivide:
FcParseBinary (parse, FcOpDivide);
break;
case FcElementNot:
FcParseUnary (parse, FcOpNot);
break;
case FcElementIf:
FcParseBinary (parse, FcOpQuest);
break;
case FcElementFloor:
FcParseUnary (parse, FcOpFloor);
break;
case FcElementCeil:
FcParseUnary (parse, FcOpCeil);
break;
case FcElementRound:
FcParseUnary (parse, FcOpRound);
break;
case FcElementTrunc:
FcParseUnary (parse, FcOpTrunc);
break;
case FcElementUnknown:
break;
2002-02-15 00:34:13 +01:00
}
(void) FcPStackPop (parse);
}
static void
FcCharacterData (void *userData, const XML_Char *s, int len)
{
FcConfigParse *parse = userData;
if (!parse->pstack)
return;
if (!FcStrBufData (&parse->pstack->str, (FcChar8 *) s, len))
FcConfigMessage (parse, FcSevereError, "out of memory");
}
static void
FcStartDoctypeDecl (void *userData,
const XML_Char *doctypeName,
const XML_Char *sysid,
const XML_Char *pubid,
int has_internal_subset)
{
FcConfigParse *parse = userData;
if (strcmp ((char *) doctypeName, "fontconfig") != 0)
FcConfigMessage (parse, FcSevereError, "invalid doctype \"%s\"", doctypeName);
}
#ifdef ENABLE_LIBXML2
static void
FcInternalSubsetDecl (void *userData,
const XML_Char *doctypeName,
const XML_Char *sysid,
const XML_Char *pubid)
{
FcStartDoctypeDecl (userData, doctypeName, sysid, pubid, 1);
}
static void
FcExternalSubsetDecl (void *userData,
const XML_Char *doctypeName,
const XML_Char *sysid,
const XML_Char *pubid)
{
FcStartDoctypeDecl (userData, doctypeName, sysid, pubid, 0);
}
#else /* ENABLE_LIBXML2 */
static void
FcEndDoctypeDecl (void *userData)
{
2002-02-15 00:34:13 +01:00
}
#endif /* ENABLE_LIBXML2 */
static int
FcSortCmpStr (const void *a, const void *b)
{
const FcChar8 *as = *((FcChar8 **) a);
const FcChar8 *bs = *((FcChar8 **) b);
return FcStrCmp (as, bs);
}
static FcBool
FcConfigParseAndLoadDir (FcConfig *config,
const FcChar8 *name,
const FcChar8 *dir,
FcBool complain)
{
DIR *d;
struct dirent *e;
FcBool ret = FcTrue;
FcChar8 *file;
FcChar8 *base;
FcStrSet *files;
d = opendir ((char *) dir);
if (!d)
{
if (complain)
FcConfigMessage (0, FcSevereError, "Cannot open config dir \"%s\"",
name);
ret = FcFalse;
goto bail0;
}
/* freed below */
file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1);
if (!file)
{
ret = FcFalse;
goto bail1;
}
strcpy ((char *) file, (char *) dir);
strcat ((char *) file, "/");
base = file + strlen ((char *) file);
files = FcStrSetCreate ();
if (!files)
{
ret = FcFalse;
goto bail2;
}
if (FcDebug () & FC_DBG_CONFIG)
printf ("\tScanning config dir %s\n", dir);
while (ret && (e = readdir (d)))
{
int d_len;
#define TAIL ".conf"
#define TAIL_LEN 5
/*
* Add all files of the form [0-9]*.conf
*/
if ('0' <= e->d_name[0] && e->d_name[0] <= '9' &&
(d_len = strlen (e->d_name)) < FC_MAX_FILE_LEN &&
d_len > TAIL_LEN &&
strcmp (e->d_name + d_len - TAIL_LEN, TAIL) == 0)
{
strcpy ((char *) base, (char *) e->d_name);
if (!FcStrSetAdd (files, file))
{
ret = FcFalse;
goto bail3;
}
}
}
if (ret)
{
int i;
qsort (files->strs, files->num, sizeof (FcChar8 *),
(int (*)(const void *, const void *)) FcSortCmpStr);
for (i = 0; ret && i < files->num; i++)
ret = FcConfigParseAndLoad (config, files->strs[i], complain);
}
bail3:
FcStrSetDestroy (files);
bail2:
free (file);
bail1:
closedir (d);
bail0:
return ret || !complain;
}
2002-02-15 00:34:13 +01:00
FcBool
FcConfigParseAndLoad (FcConfig *config,
const FcChar8 *name,
2002-02-15 00:34:13 +01:00
FcBool complain)
{
XML_Parser p;
FcChar8 *filename;
int fd;
int len;
FcConfigParse parse;
FcBool error = FcTrue;
#ifdef ENABLE_LIBXML2
xmlSAXHandler sax;
char buf[BUFSIZ];
#else
void *buf;
#endif
filename = FcConfigFilename (name);
if (!filename)
goto bail0;
if (FcStrSetMember (config->configFiles, filename))
{
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;
}
#ifdef ENABLE_LIBXML2
memset(&sax, 0, sizeof(sax));
sax.internalSubset = FcInternalSubsetDecl;
sax.externalSubset = FcExternalSubsetDecl;
sax.startElement = FcStartElement;
sax.endElement = FcEndElement;
sax.characters = FcCharacterData;
p = xmlCreatePushParserCtxt (&sax, &parse, NULL, 0, (const char *) filename);
#else
p = XML_ParserCreate ("UTF-8");
#endif
FcStrFree (filename);
if (!p)
goto bail1;
if (!FcConfigInit (&parse, name, config, p))
goto bail2;
#ifndef ENABLE_LIBXML2
XML_SetUserData (p, &parse);
XML_SetDoctypeDeclHandler (p, FcStartDoctypeDecl, FcEndDoctypeDecl);
XML_SetElementHandler (p, FcStartElement, FcEndElement);
XML_SetCharacterDataHandler (p, FcCharacterData);
#endif /* ENABLE_LIBXML2 */
do {
#ifndef ENABLE_LIBXML2
buf = XML_GetBuffer (p, BUFSIZ);
if (!buf)
{
FcConfigMessage (&parse, FcSevereError, "cannot get parse buffer");
goto bail3;
}
#endif
len = read (fd, buf, BUFSIZ);
if (len < 0)
{
FcConfigMessage (&parse, FcSevereError, "failed reading config file");
goto bail3;
}
#ifdef ENABLE_LIBXML2
if (xmlParseChunk (p, buf, len, len == 0))
#else
if (!XML_ParseBuffer (p, len, len == 0))
#endif
{
FcConfigMessage (&parse, FcSevereError, "%s",
XML_ErrorString (XML_GetErrorCode (p)));
goto bail3;
}
} while (len != 0);
error = parse.error;
bail3:
FcConfigCleanup (&parse);
bail2:
XML_ParserFree (p);
bail1:
close (fd);
fd = -1;
bail0:
if (error && complain)
2002-02-15 00:34:13 +01:00
{
if (name)
FcConfigMessage (0, FcSevereError, "Cannot load config file \"%s\"", name);
2002-02-15 00:34:13 +01:00
else
FcConfigMessage (0, FcSevereError, "Cannot load default config file");
2002-02-15 00:34:13 +01:00
return FcFalse;
}
return FcTrue;
}
#define __fcxml__
#include "fcaliastail.h"
#undef __fcxml__