1033 lines
20 KiB
C
1033 lines
20 KiB
C
/*
|
|
* $XFree86: $
|
|
*
|
|
* Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
|
|
*
|
|
* 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 <stdarg.h>
|
|
#include "fcint.h"
|
|
|
|
static xmlParserInputPtr
|
|
FcEntityLoader (const char *url, const char *id, xmlParserCtxtPtr ctxt)
|
|
{
|
|
xmlParserInputPtr ret;
|
|
char *file;
|
|
|
|
file = FcConfigFilename (url);
|
|
if (!file)
|
|
return 0;
|
|
ret = xmlNewInputFromFile (ctxt, file);
|
|
free (file);
|
|
return ret;
|
|
}
|
|
|
|
xmlDocPtr
|
|
FcConfigLoad (const char *file)
|
|
{
|
|
xmlDocPtr doc;
|
|
xmlExternalEntityLoader previous;
|
|
|
|
previous = xmlGetExternalEntityLoader ();
|
|
xmlSetExternalEntityLoader (FcEntityLoader);
|
|
doc = xmlParseFile (file);
|
|
xmlSetExternalEntityLoader (previous);
|
|
return doc;
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
FcConfigSave (char *file, xmlDocPtr doc)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
FcTest *
|
|
FcTestCreate (FcQual qual, const char *field, FcOp compare, FcExpr *expr)
|
|
{
|
|
FcTest *test = (FcTest *) malloc (sizeof (FcTest));;
|
|
|
|
if (test)
|
|
{
|
|
test->next = 0;
|
|
test->qual = qual;
|
|
test->field = FcStrCopy (field);
|
|
test->op = compare;
|
|
test->expr = expr;
|
|
}
|
|
return test;
|
|
}
|
|
|
|
void
|
|
FcTestDestroy (FcTest *test)
|
|
{
|
|
if (test->next)
|
|
FcTestDestroy (test->next);
|
|
FcExprDestroy (test->expr);
|
|
FcStrFree ((FcChar8 *) test->field);
|
|
free (test);
|
|
}
|
|
|
|
FcExpr *
|
|
FcExprCreateInteger (int i)
|
|
{
|
|
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
|
|
|
|
if (e)
|
|
{
|
|
e->op = FcOpInteger;
|
|
e->u.ival = i;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
FcExpr *
|
|
FcExprCreateDouble (double d)
|
|
{
|
|
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
|
|
|
|
if (e)
|
|
{
|
|
e->op = FcOpDouble;
|
|
e->u.dval = d;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
FcExpr *
|
|
FcExprCreateString (const char *s)
|
|
{
|
|
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
|
|
|
|
if (e)
|
|
{
|
|
e->op = FcOpString;
|
|
e->u.sval = FcStrCopy (s);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
FcExpr *
|
|
FcExprCreateMatrix (const FcMatrix *m)
|
|
{
|
|
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
|
|
|
|
if (e)
|
|
{
|
|
e->op = FcOpMatrix;
|
|
e->u.mval = FcMatrixCopy (m);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
FcExpr *
|
|
FcExprCreateBool (FcBool b)
|
|
{
|
|
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
|
|
|
|
if (e)
|
|
{
|
|
e->op = FcOpBool;
|
|
e->u.bval = b;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
FcExpr *
|
|
FcExprCreateNil (void)
|
|
{
|
|
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
|
|
|
|
if (e)
|
|
{
|
|
e->op = FcOpNil;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
FcExpr *
|
|
FcExprCreateField (const char *field)
|
|
{
|
|
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
|
|
|
|
if (e)
|
|
{
|
|
e->op = FcOpField;
|
|
e->u.field = FcStrCopy (field);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
FcExpr *
|
|
FcExprCreateConst (const char *constant)
|
|
{
|
|
FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
|
|
|
|
if (e)
|
|
{
|
|
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)
|
|
{
|
|
e->op = op;
|
|
e->u.tree.left = left;
|
|
e->u.tree.right = right;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
void
|
|
FcExprDestroy (FcExpr *e)
|
|
{
|
|
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 (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 FcOpContains:
|
|
case FcOpNotEqual:
|
|
case FcOpLess:
|
|
case FcOpLessEqual:
|
|
case FcOpMore:
|
|
case FcOpMoreEqual:
|
|
case FcOpPlus:
|
|
case FcOpMinus:
|
|
case FcOpTimes:
|
|
case FcOpDivide:
|
|
case FcOpQuest:
|
|
case FcOpComma:
|
|
FcExprDestroy (e->u.tree.right);
|
|
/* fall through */
|
|
case FcOpNot:
|
|
FcExprDestroy (e->u.tree.left);
|
|
break;
|
|
case FcOpNil:
|
|
case FcOpInvalid:
|
|
break;
|
|
}
|
|
free (e);
|
|
}
|
|
|
|
FcEdit *
|
|
FcEditCreate (const char *field, FcOp op, FcExpr *expr)
|
|
{
|
|
FcEdit *e = (FcEdit *) malloc (sizeof (FcEdit));
|
|
|
|
if (e)
|
|
{
|
|
e->next = 0;
|
|
e->field = field; /* already saved in grammar */
|
|
e->op = op;
|
|
e->expr = expr;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
void
|
|
FcEditDestroy (FcEdit *e)
|
|
{
|
|
if (e->next)
|
|
FcEditDestroy (e->next);
|
|
FcStrFree ((FcChar8 *) e->field);
|
|
if (e->expr)
|
|
FcExprDestroy (e->expr);
|
|
}
|
|
|
|
char *
|
|
FcConfigSaveField (const char *field)
|
|
{
|
|
return FcStrCopy (field);
|
|
}
|
|
|
|
static void
|
|
FcConfigParseError (char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start (args, fmt);
|
|
fprintf (stderr, "font configuration error: ");
|
|
vfprintf (stderr, fmt, args);
|
|
fprintf (stderr, "\n");
|
|
va_end (args);
|
|
}
|
|
|
|
static char *
|
|
FcConfigContent (xmlDocPtr doc,
|
|
xmlNodePtr node)
|
|
{
|
|
char *content;
|
|
|
|
content = xmlNodeListGetString (doc, node->children, 1);
|
|
if (!content)
|
|
{
|
|
FcConfigParseError ("<%s> must have content",
|
|
node->name);
|
|
return FcFalse;
|
|
}
|
|
return content;
|
|
}
|
|
|
|
static char *
|
|
FcConfigAttr (xmlDocPtr doc,
|
|
xmlAttrPtr attr)
|
|
{
|
|
char *content;
|
|
|
|
content = xmlNodeListGetString (doc, attr->children, 1);
|
|
if (!content)
|
|
{
|
|
FcConfigParseError ("attribute %s must have a value",
|
|
attr->name);
|
|
return FcFalse;
|
|
}
|
|
return content;
|
|
}
|
|
|
|
static struct {
|
|
char *name;
|
|
FcOp op;
|
|
} fcOps[] = {
|
|
{ "int", FcOpInteger },
|
|
{ "double", FcOpDouble },
|
|
{ "string", FcOpString },
|
|
{ "matrix", FcOpMatrix },
|
|
{ "bool", FcOpBool },
|
|
{ "charset", FcOpCharSet },
|
|
{ "name", FcOpField },
|
|
{ "const", FcOpConst },
|
|
{ "field", FcOpField },
|
|
{ "if", FcOpQuest },
|
|
{ "or", FcOpOr },
|
|
{ "and", FcOpAnd },
|
|
{ "eq", FcOpEqual },
|
|
{ "not_eq", FcOpNotEqual },
|
|
{ "less", FcOpLess },
|
|
{ "less_eq", FcOpLessEqual },
|
|
{ "more", FcOpMore },
|
|
{ "more_eq", FcOpMoreEqual },
|
|
{ "plus", FcOpPlus },
|
|
{ "minus", FcOpMinus },
|
|
{ "times", FcOpTimes },
|
|
{ "divide", FcOpDivide },
|
|
{ "not", FcOpNot },
|
|
{ "assign", FcOpAssign },
|
|
{ "assign_replace", FcOpAssignReplace },
|
|
{ "prepend", FcOpPrepend },
|
|
{ "prepend_first", FcOpPrependFirst },
|
|
{ "append", FcOpAppend },
|
|
{ "append_last", FcOpAppendLast },
|
|
};
|
|
|
|
#define NUM_OPS (sizeof fcOps / sizeof fcOps[0])
|
|
|
|
static FcOp
|
|
FcConfigLexOp (const char *op)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_OPS; i++)
|
|
if (!strcmp (op, fcOps[i].name)) return fcOps[i].op;
|
|
return FcOpInvalid;
|
|
}
|
|
|
|
static FcBool
|
|
FcConfigLexBool (const char *bool)
|
|
{
|
|
if (*bool == 't' || *bool == 'T')
|
|
return FcTrue;
|
|
if (*bool == 'y' || *bool == 'Y')
|
|
return FcTrue;
|
|
if (*bool == '1')
|
|
return FcTrue;
|
|
return FcFalse;
|
|
}
|
|
|
|
static FcBool
|
|
FcConfigParseDir (FcConfig *config,
|
|
xmlDocPtr doc,
|
|
xmlNodePtr dir)
|
|
{
|
|
char *content = FcConfigContent (doc, dir);
|
|
|
|
if (!content)
|
|
return FcFalse;
|
|
return FcConfigAddDir (config, content);
|
|
}
|
|
|
|
static FcBool
|
|
FcConfigParseCache (FcConfig *config,
|
|
xmlDocPtr doc,
|
|
xmlNodePtr dir)
|
|
{
|
|
char *content = FcConfigContent (doc, dir);
|
|
|
|
if (!content)
|
|
return FcFalse;
|
|
return FcConfigSetCache (config, content);
|
|
}
|
|
|
|
static FcBool
|
|
FcConfigParseInclude (FcConfig *config,
|
|
xmlDocPtr doc,
|
|
xmlNodePtr inc)
|
|
{
|
|
char *content = FcConfigContent (doc, inc);
|
|
xmlAttr *attr;
|
|
FcBool complain = FcTrue;
|
|
|
|
if (!content)
|
|
return FcFalse;
|
|
|
|
for (attr = inc->properties; attr; attr = attr->next)
|
|
{
|
|
if (attr->type != XML_ATTRIBUTE_NODE)
|
|
continue;
|
|
if (!strcmp (attr->name, "ignore_missing"))
|
|
complain = !FcConfigLexBool (FcConfigAttr (doc, attr));
|
|
}
|
|
return FcConfigParseAndLoad (config, content, complain);
|
|
}
|
|
|
|
static FcBool
|
|
FcConfigParseBlank (FcConfig *config,
|
|
xmlDocPtr doc,
|
|
xmlNodePtr blank)
|
|
{
|
|
xmlNode *node;
|
|
FcChar32 ucs4;
|
|
|
|
for (node = blank->children; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
if (!strcmp (node->name, "int"))
|
|
{
|
|
ucs4 = (FcChar32) strtol (FcConfigContent (doc, node), 0, 0);
|
|
if (!config->blanks)
|
|
{
|
|
config->blanks = FcBlanksCreate ();
|
|
if (!config->blanks)
|
|
break;
|
|
}
|
|
if (!FcBlanksAdd (config->blanks, ucs4))
|
|
break;
|
|
}
|
|
}
|
|
if (node)
|
|
return FcFalse;
|
|
return FcTrue;
|
|
}
|
|
|
|
static FcBool
|
|
FcConfigParseConfig (FcConfig *config,
|
|
xmlDocPtr doc,
|
|
xmlNodePtr cfg)
|
|
{
|
|
xmlNode *node;
|
|
|
|
for (node = cfg->children; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
if (!strcmp (node->name, "blank"))
|
|
{
|
|
if (!FcConfigParseBlank (config, doc, node))
|
|
break;
|
|
}
|
|
}
|
|
if (node)
|
|
return FcFalse;
|
|
return FcTrue;
|
|
}
|
|
|
|
static FcMatrix *
|
|
FcConfigParseMatrix (xmlDocPtr doc,
|
|
xmlNodePtr node)
|
|
{
|
|
static FcMatrix m;
|
|
enum { m_xx, m_xy, m_yx, m_yy, m_done } matrix_state = m_xx;
|
|
double v;
|
|
char *text;
|
|
|
|
FcMatrixInit (&m);
|
|
|
|
for (; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
if (strcmp (node->name, "double"))
|
|
continue;
|
|
text = FcConfigContent (doc, node);
|
|
if (!text)
|
|
continue;
|
|
v = strtod (text, 0);
|
|
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;
|
|
}
|
|
matrix_state++;
|
|
}
|
|
|
|
return &m;
|
|
}
|
|
|
|
static FcExpr *
|
|
FcConfigParseExpr (xmlDocPtr doc,
|
|
xmlNodePtr expr)
|
|
{
|
|
FcOp op = FcConfigLexOp (expr->name);
|
|
xmlNodePtr node;
|
|
FcExpr *l = 0, *e = 0, *r = 0, *c = 0;
|
|
|
|
switch (op) {
|
|
case FcOpInteger:
|
|
l = FcExprCreateInteger (strtol (FcConfigContent (doc, expr), 0, 0));
|
|
break;
|
|
case FcOpDouble:
|
|
l = FcExprCreateDouble (strtod (FcConfigContent (doc, expr), 0));
|
|
break;
|
|
case FcOpString:
|
|
l = FcExprCreateString (FcConfigContent (doc, expr));
|
|
break;
|
|
case FcOpMatrix:
|
|
l = FcExprCreateMatrix (FcConfigParseMatrix (doc, expr));
|
|
break;
|
|
case FcOpBool:
|
|
l = FcExprCreateBool (FcConfigLexBool(FcConfigContent (doc, expr)));
|
|
break;
|
|
case FcOpCharSet:
|
|
/* not sure what to do here yet */
|
|
break;
|
|
case FcOpField:
|
|
l = FcExprCreateField (FcConfigContent (doc, expr));
|
|
break;
|
|
case FcOpConst:
|
|
l = FcExprCreateConst (FcConfigContent (doc, expr));
|
|
break;
|
|
case FcOpQuest:
|
|
for (node = expr->children; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
e = FcConfigParseExpr (doc, node);
|
|
if (!e)
|
|
break;
|
|
if (!l)
|
|
l = e;
|
|
else if (!c)
|
|
c = e;
|
|
else if (!r)
|
|
r = e;
|
|
else
|
|
FcExprDestroy (e);
|
|
}
|
|
e = 0;
|
|
if (!node && l && c && r)
|
|
{
|
|
e = FcExprCreateOp (c, FcOpQuest, r);
|
|
if (e)
|
|
{
|
|
r = e;
|
|
c = 0;
|
|
e = FcExprCreateOp (l, FcOpQuest, r);
|
|
}
|
|
if (!e)
|
|
node = expr->children;
|
|
}
|
|
if (node || !l || !c || !r || !e)
|
|
{
|
|
if (l)
|
|
FcExprDestroy (l);
|
|
if (c)
|
|
FcExprDestroy (c);
|
|
if (r)
|
|
FcExprDestroy (r);
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
for (node = expr->children; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
e = FcConfigParseExpr (doc, node);
|
|
if (!e)
|
|
break;
|
|
if (!l)
|
|
l = e;
|
|
else
|
|
{
|
|
r = e;
|
|
e = FcExprCreateOp (l, op, r);
|
|
if (!e)
|
|
{
|
|
FcExprDestroy (r);
|
|
break;
|
|
}
|
|
l = e;
|
|
}
|
|
}
|
|
if (node || !l)
|
|
{
|
|
if (l)
|
|
FcExprDestroy (l);
|
|
return 0;
|
|
}
|
|
/*
|
|
* Special case for unary ops
|
|
*/
|
|
if (!r)
|
|
{
|
|
e = FcExprCreateOp (l, op, 0);
|
|
if (!e)
|
|
{
|
|
FcExprDestroy (l);
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FcOpInvalid:
|
|
return 0;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
static FcTest *
|
|
FcConfigParseTest (xmlDocPtr doc,
|
|
xmlNodePtr test)
|
|
{
|
|
xmlNodePtr node;
|
|
xmlAttrPtr attr;
|
|
FcQual qual = FcQualAny;
|
|
FcOp op = FcOpEqual;
|
|
char *field = 0;
|
|
FcExpr *expr = 0;
|
|
|
|
for (attr = test->properties; attr; attr = attr->next)
|
|
{
|
|
if (attr->type != XML_ATTRIBUTE_NODE)
|
|
continue;
|
|
if (!strcmp (attr->name, "qual"))
|
|
{
|
|
char *qual_name = FcConfigAttr (doc, attr);
|
|
if (!qual_name)
|
|
;
|
|
else if (!strcmp (qual_name, "any"))
|
|
qual = FcQualAny;
|
|
else if (!strcmp (qual_name, "all"))
|
|
qual = FcQualAll;
|
|
}
|
|
else if (!strcmp (attr->name, "name"))
|
|
{
|
|
field = FcConfigAttr (doc, attr);
|
|
}
|
|
else if (!strcmp (attr->name, "compare"))
|
|
{
|
|
char *compare = FcConfigAttr (doc, attr);
|
|
|
|
if (!compare || (op = FcConfigLexOp (compare)) == FcOpInvalid)
|
|
{
|
|
FcConfigParseError ("Invalid comparison %s",
|
|
compare ? compare : "<missing>");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
if (attr)
|
|
return 0;
|
|
|
|
for (node = test->children; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
expr = FcConfigParseExpr (doc, node);
|
|
if (!expr)
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
if (!expr)
|
|
{
|
|
FcConfigParseError ("Missing test expression");
|
|
return 0;
|
|
}
|
|
|
|
return FcTestCreate (qual, field, op, expr);
|
|
}
|
|
|
|
static FcExpr *
|
|
FcConfigParseExprList (xmlDocPtr doc,
|
|
xmlNodePtr expr)
|
|
{
|
|
FcExpr *l, *e, *r;
|
|
|
|
if (!expr)
|
|
return 0;
|
|
|
|
e = FcConfigParseExprList (doc, expr->next);
|
|
|
|
if (expr->type == XML_ELEMENT_NODE)
|
|
{
|
|
r = e;
|
|
l = FcConfigParseExpr (doc, expr);
|
|
if (!l)
|
|
goto bail;
|
|
if (r)
|
|
{
|
|
e = FcExprCreateOp (l, FcOpComma, r);
|
|
if (!e)
|
|
goto bail;
|
|
}
|
|
else
|
|
e = l;
|
|
}
|
|
|
|
return e;
|
|
bail:
|
|
if (l)
|
|
FcExprDestroy (l);
|
|
if (r)
|
|
FcExprDestroy (r);
|
|
return 0;
|
|
}
|
|
|
|
static FcEdit *
|
|
FcConfigParseEdit (xmlDocPtr doc,
|
|
xmlNodePtr edit)
|
|
{
|
|
xmlAttrPtr attr;
|
|
char *name = 0;
|
|
FcOp mode = FcOpAssign;
|
|
FcExpr *e;
|
|
FcEdit *ed;
|
|
|
|
for (attr = edit->properties; attr; attr = attr->next)
|
|
{
|
|
if (attr->type != XML_ATTRIBUTE_NODE)
|
|
continue;
|
|
if (!strcmp (attr->name, "name"))
|
|
name = FcConfigAttr (doc, attr);
|
|
else if (!strcmp (attr->name, "mode"))
|
|
mode = FcConfigLexOp (FcConfigAttr (doc, attr));
|
|
}
|
|
|
|
e = FcConfigParseExprList (doc, edit->children);
|
|
|
|
ed = FcEditCreate (name, mode, e);
|
|
if (!ed)
|
|
FcExprDestroy (e);
|
|
return ed;
|
|
}
|
|
|
|
static FcBool
|
|
FcConfigParseMatch (FcConfig *config,
|
|
xmlDocPtr doc,
|
|
xmlNodePtr match)
|
|
{
|
|
xmlNodePtr node;
|
|
xmlAttrPtr attr;
|
|
FcTest *tests = 0, **prevTest = &tests, *test;
|
|
FcEdit *edits = 0, **prevEdit = &edits, *edit;
|
|
FcMatchKind kind;
|
|
FcBool found_kind = FcFalse;
|
|
|
|
for (node = match->children; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
if (!strcmp (node->name, "test"))
|
|
{
|
|
test = FcConfigParseTest (doc, node);
|
|
if (!test)
|
|
break;
|
|
*prevTest = test;
|
|
prevTest = &test->next;
|
|
}
|
|
else if (!strcmp (node->name, "edit"))
|
|
{
|
|
edit = FcConfigParseEdit (doc, node);
|
|
if (!edit)
|
|
break;
|
|
*prevEdit = edit;
|
|
prevEdit = &edit->next;
|
|
}
|
|
}
|
|
|
|
for (attr = match->properties; attr; attr = attr->next)
|
|
{
|
|
if (attr->type != XML_ATTRIBUTE_NODE)
|
|
continue;
|
|
if (!strcmp (attr->name, "target"))
|
|
{
|
|
char *target = FcConfigAttr (doc, attr);
|
|
if (!target)
|
|
{
|
|
FcConfigParseError ("Missing match target");
|
|
break;
|
|
}
|
|
else if (!strcmp (target, "pattern"))
|
|
{
|
|
kind = FcMatchPattern;
|
|
found_kind = FcTrue;
|
|
}
|
|
else if (!strcmp (target, "font"))
|
|
{
|
|
kind = FcMatchFont;
|
|
found_kind = FcTrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (node || attr || !found_kind ||
|
|
!FcConfigAddEdit (config, tests, edits, kind))
|
|
{
|
|
if (tests)
|
|
FcTestDestroy (tests);
|
|
if (edits)
|
|
FcEditDestroy (edits);
|
|
return FcFalse;
|
|
}
|
|
|
|
return FcTrue;
|
|
}
|
|
|
|
static FcExpr *
|
|
FcConfigParseFamilies (xmlDocPtr doc,
|
|
xmlNodePtr family)
|
|
{
|
|
FcExpr *next = 0, *this = 0, *expr = 0;
|
|
|
|
if (!family)
|
|
return 0;
|
|
next = FcConfigParseFamilies (doc, family->next);
|
|
|
|
if (family->type == XML_ELEMENT_NODE && !strcmp (family->name, "family"))
|
|
{
|
|
this = FcExprCreateString (FcConfigContent (doc, family));
|
|
if (!this)
|
|
goto bail;
|
|
if (next)
|
|
{
|
|
expr = FcExprCreateOp (this, FcOpComma, next);
|
|
if (!expr)
|
|
goto bail;
|
|
}
|
|
else
|
|
expr = this;
|
|
}
|
|
else
|
|
expr = next;
|
|
return expr;
|
|
|
|
bail:
|
|
if (expr)
|
|
FcExprDestroy (expr);
|
|
if (this)
|
|
FcExprDestroy (this);
|
|
if (next)
|
|
FcExprDestroy (next);
|
|
return 0;
|
|
}
|
|
|
|
static FcBool
|
|
FcConfigParseAlias (FcConfig *config,
|
|
xmlDocPtr doc,
|
|
xmlNodePtr alias)
|
|
{
|
|
xmlNodePtr node;
|
|
FcExpr *prefer = 0, *accept = 0, *def = 0;
|
|
FcExpr *family;
|
|
FcEdit *edit = 0, *next;
|
|
FcTest *test;
|
|
|
|
for (node = alias->children; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
if (!strcmp (node->name, "family"))
|
|
family = FcExprCreateString (FcConfigContent (doc, node));
|
|
else if (!strcmp (node->name, "prefer"))
|
|
prefer = FcConfigParseFamilies (doc, node->children);
|
|
else if (!strcmp (node->name, "accept"))
|
|
accept = FcConfigParseFamilies (doc, node->children);
|
|
else if (!strcmp (node->name, "default"))
|
|
def = FcConfigParseFamilies (doc, node->children);
|
|
}
|
|
|
|
if (prefer)
|
|
{
|
|
edit = FcEditCreate (FcConfigSaveField ("family"),
|
|
FcOpPrepend,
|
|
prefer);
|
|
if (edit)
|
|
edit->next = 0;
|
|
}
|
|
if (accept)
|
|
{
|
|
next = edit;
|
|
edit = FcEditCreate (FcConfigSaveField ("family"),
|
|
FcOpAppend,
|
|
accept);
|
|
if (edit)
|
|
edit->next = next;
|
|
}
|
|
if (def)
|
|
{
|
|
next = edit;
|
|
edit = FcEditCreate (FcConfigSaveField ("family"),
|
|
FcOpAppendLast,
|
|
def);
|
|
if (edit)
|
|
edit->next = next;
|
|
}
|
|
if (edit)
|
|
{
|
|
test = FcTestCreate (FcQualAny,
|
|
FcConfigSaveField ("family"),
|
|
FcOpEqual,
|
|
family);
|
|
if (test)
|
|
FcConfigAddEdit (config, test, edit, FcMatchPattern);
|
|
}
|
|
return FcTrue;
|
|
}
|
|
|
|
FcBool
|
|
FcConfigParse (FcConfig *config,
|
|
xmlDocPtr doc)
|
|
{
|
|
xmlNodePtr cur;
|
|
xmlNodePtr node;
|
|
|
|
cur = xmlDocGetRootElement (doc);
|
|
|
|
for (node = cur->children; node; node = node->next)
|
|
{
|
|
if (node->type != XML_ELEMENT_NODE)
|
|
continue;
|
|
if (!strcmp (node->name, "dir"))
|
|
{
|
|
if (!FcConfigParseDir (config, doc, node))
|
|
break;
|
|
}
|
|
else if (!strcmp (node->name, "cache"))
|
|
{
|
|
if (!FcConfigParseCache (config, doc, node))
|
|
break;
|
|
}
|
|
else if (!strcmp (node->name, "include"))
|
|
{
|
|
if (!FcConfigParseInclude (config, doc, node))
|
|
break;
|
|
}
|
|
else if (!strcmp (node->name, "config"))
|
|
{
|
|
if (!FcConfigParseConfig (config, doc, node))
|
|
break;
|
|
}
|
|
else if (!strcmp (node->name, "match"))
|
|
{
|
|
if (!FcConfigParseMatch (config, doc, node))
|
|
break;
|
|
}
|
|
else if (!strcmp (node->name, "alias"))
|
|
{
|
|
if (!FcConfigParseAlias (config, doc, node))
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
FcConfigParseError ("invalid element %s", node->name);
|
|
break;
|
|
}
|
|
}
|
|
if (node)
|
|
return FcFalse;
|
|
return FcTrue;
|
|
}
|
|
|
|
FcBool
|
|
FcConfigParseAndLoad (FcConfig *config,
|
|
const char *file,
|
|
FcBool complain)
|
|
{
|
|
xmlDocPtr doc;
|
|
FcBool ret;
|
|
|
|
doc = FcConfigLoad (file);
|
|
if (doc)
|
|
{
|
|
ret = FcConfigAddConfigFile (config, file);
|
|
if (ret)
|
|
ret = FcConfigParse (config, doc);
|
|
xmlFreeDoc (doc);
|
|
return ret;
|
|
}
|
|
if (complain)
|
|
{
|
|
if (file)
|
|
FcConfigParseError ("Cannot load config file \"%s\"", file);
|
|
else
|
|
FcConfigParseError ("Cannot load default config file");
|
|
return FcFalse;
|
|
}
|
|
return FcTrue;
|
|
}
|