]> git.wh0rd.org Git - fontconfig.git/blob - src/fcxml.c
fontconfig library: build fixes and compiler warning fixes
[fontconfig.git] / src / fcxml.c
1 /*
2  * $XFree86: $
3  *
4  * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 #include <stdarg.h>
26 #include "fcint.h"
27
28 static xmlParserInputPtr
29 FcEntityLoader (const char *url, const char *id, xmlParserCtxtPtr ctxt)
30 {
31     xmlParserInputPtr   ret;
32     FcChar8             *file;
33
34     file = FcConfigFilename ((FcChar8 *) url);
35     if (!file)
36         return 0;
37     ret = xmlNewInputFromFile (ctxt, (char *) file);
38     free (file);
39     return ret;
40 }
41
42 xmlDocPtr
43 FcConfigLoad (const FcChar8 *file)
44 {
45     xmlDocPtr               doc;
46     xmlExternalEntityLoader previous;
47
48     previous = xmlGetExternalEntityLoader ();
49     xmlSetExternalEntityLoader (FcEntityLoader);
50     doc = xmlParseFile ((char *) file);
51     xmlSetExternalEntityLoader (previous);
52     return doc;
53 }
54
55 #if 0
56 int
57 FcConfigSave (char *file, xmlDocPtr doc)
58 {
59 }
60 #endif
61
62 FcTest *
63 FcTestCreate (FcQual qual, const char *field, FcOp compare, FcExpr *expr)
64 {
65     FcTest      *test = (FcTest *) malloc (sizeof (FcTest));;
66
67     if (test)
68     {
69         test->next = 0;
70         test->qual = qual;
71         test->field = (char *) FcStrCopy ((FcChar8 *) field);
72         test->op = compare;
73         test->expr = expr;
74     }
75     return test;
76 }
77
78 void
79 FcTestDestroy (FcTest *test)
80 {
81     if (test->next)
82         FcTestDestroy (test->next);
83     FcExprDestroy (test->expr);
84     FcStrFree ((FcChar8 *) test->field);
85     free (test);
86 }
87
88 FcExpr *
89 FcExprCreateInteger (int i)
90 {
91     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
92
93     if (e)
94     {
95         e->op = FcOpInteger;
96         e->u.ival = i;
97     }
98     return e;
99 }
100
101 FcExpr *
102 FcExprCreateDouble (double d)
103 {
104     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
105
106     if (e)
107     {
108         e->op = FcOpDouble;
109         e->u.dval = d;
110     }
111     return e;
112 }
113
114 FcExpr *
115 FcExprCreateString (const FcChar8 *s)
116 {
117     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
118
119     if (e)
120     {
121         e->op = FcOpString;
122         e->u.sval = FcStrCopy (s);
123     }
124     return e;
125 }
126
127 FcExpr *
128 FcExprCreateMatrix (const FcMatrix *m)
129 {
130     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
131
132     if (e)
133     {
134         e->op = FcOpMatrix;
135         e->u.mval = FcMatrixCopy (m);
136     }
137     return e;
138 }
139
140 FcExpr *
141 FcExprCreateBool (FcBool b)
142 {
143     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
144
145     if (e)
146     {
147         e->op = FcOpBool;
148         e->u.bval = b;
149     }
150     return e;
151 }
152
153 FcExpr *
154 FcExprCreateNil (void)
155 {
156     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
157
158     if (e)
159     {
160         e->op = FcOpNil;
161     }
162     return e;
163 }
164
165 FcExpr *
166 FcExprCreateField (const char *field)
167 {
168     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
169
170     if (e)
171     {
172         e->op = FcOpField;
173         e->u.field = (char *) FcStrCopy ((FcChar8 *) field);
174     }
175     return e;
176 }
177
178 FcExpr *
179 FcExprCreateConst (const FcChar8 *constant)
180 {
181     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
182
183     if (e)
184     {
185         e->op = FcOpConst;
186         e->u.constant = FcStrCopy (constant);
187     }
188     return e;
189 }
190
191 FcExpr *
192 FcExprCreateOp (FcExpr *left, FcOp op, FcExpr *right)
193 {
194     FcExpr *e = (FcExpr *) malloc (sizeof (FcExpr));
195
196     if (e)
197     {
198         e->op = op;
199         e->u.tree.left = left;
200         e->u.tree.right = right;
201     }
202     return e;
203 }
204
205 void
206 FcExprDestroy (FcExpr *e)
207 {
208     switch (e->op) {
209     case FcOpInteger:
210         break;
211     case FcOpDouble:
212         break;
213     case FcOpString:
214         FcStrFree (e->u.sval);
215         break;
216     case FcOpMatrix:
217         FcMatrixFree (e->u.mval);
218         break;
219     case FcOpCharSet:
220         FcCharSetDestroy (e->u.cval);
221         break;
222     case FcOpBool:
223         break;
224     case FcOpField:
225         FcStrFree ((FcChar8 *) e->u.field);
226         break;
227     case FcOpConst:
228         FcStrFree (e->u.constant);
229         break;
230     case FcOpAssign:
231     case FcOpAssignReplace:
232     case FcOpPrepend:
233     case FcOpPrependFirst:
234     case FcOpAppend:
235     case FcOpAppendLast:
236         break;
237     case FcOpOr:
238     case FcOpAnd:
239     case FcOpEqual:
240     case FcOpContains:
241     case FcOpNotEqual:
242     case FcOpLess:
243     case FcOpLessEqual:
244     case FcOpMore:
245     case FcOpMoreEqual:
246     case FcOpPlus:
247     case FcOpMinus:
248     case FcOpTimes:
249     case FcOpDivide:
250     case FcOpQuest:
251     case FcOpComma:
252         FcExprDestroy (e->u.tree.right);
253         /* fall through */
254     case FcOpNot:
255         FcExprDestroy (e->u.tree.left);
256         break;
257     case FcOpNil:
258     case FcOpInvalid:
259         break;
260     }
261     free (e);
262 }
263
264 FcEdit *
265 FcEditCreate (const char *field, FcOp op, FcExpr *expr)
266 {
267     FcEdit *e = (FcEdit *) malloc (sizeof (FcEdit));
268
269     if (e)
270     {
271         e->next = 0;
272         e->field = field;   /* already saved in grammar */
273         e->op = op;
274         e->expr = expr;
275     }
276     return e;
277 }
278
279 void
280 FcEditDestroy (FcEdit *e)
281 {
282     if (e->next)
283         FcEditDestroy (e->next);
284     FcStrFree ((FcChar8 *) e->field);
285     if (e->expr)
286         FcExprDestroy (e->expr);
287 }
288
289 char *
290 FcConfigSaveField (const char *field)
291 {
292     return (char *) FcStrCopy ((FcChar8 *) field);
293 }
294
295 static void
296 FcConfigParseError (char *fmt, ...)
297 {
298     va_list     args;
299
300     va_start (args, fmt);
301     fprintf (stderr, "font configuration error: ");
302     vfprintf (stderr, fmt, args);
303     fprintf (stderr, "\n");
304     va_end (args);
305 }
306
307 static xmlChar *
308 FcConfigContent (xmlDocPtr    doc,
309                  xmlNodePtr   node)
310 {
311     xmlChar         *content;
312     
313     content = xmlNodeListGetString (doc, node->children, 1);
314     if (!content)
315     {
316         FcConfigParseError ("<%s> must have content",
317                             node->name);
318         return FcFalse;
319     }
320     return content;
321 }
322
323 static xmlChar *
324 FcConfigAttr (xmlDocPtr     doc,
325               xmlAttrPtr    attr)
326 {
327     xmlChar         *content;
328     
329     content = xmlNodeListGetString (doc, attr->children, 1);
330     if (!content)
331     {
332         FcConfigParseError ("attribute %s must have a value",
333                             attr->name);
334         return FcFalse;
335     }
336     return content;
337 }
338
339 static struct {
340     char    *name;
341     FcOp    op;
342 } fcOps[] = {
343     { "int",            FcOpInteger         },
344     { "double",         FcOpDouble          },
345     { "string",         FcOpString          },
346     { "matrix",         FcOpMatrix          },
347     { "bool",           FcOpBool            },
348     { "charset",        FcOpCharSet         },
349     { "name",           FcOpField           },
350     { "const",          FcOpConst           },
351     { "field",          FcOpField           },
352     { "if",             FcOpQuest           },
353     { "or",             FcOpOr              },
354     { "and",            FcOpAnd             },
355     { "eq",             FcOpEqual           },
356     { "not_eq",         FcOpNotEqual        },
357     { "less",           FcOpLess            },
358     { "less_eq",        FcOpLessEqual       },
359     { "more",           FcOpMore            },
360     { "more_eq",        FcOpMoreEqual       },
361     { "plus",           FcOpPlus            },
362     { "minus",          FcOpMinus           },
363     { "times",          FcOpTimes           },
364     { "divide",         FcOpDivide          },
365     { "not",            FcOpNot             },
366     { "assign",         FcOpAssign          },
367     { "assign_replace", FcOpAssignReplace   },
368     { "prepend",        FcOpPrepend         },
369     { "prepend_first",  FcOpPrependFirst    },
370     { "append",         FcOpAppend          },
371     { "append_last",    FcOpAppendLast      },
372 };
373
374 #define NUM_OPS (sizeof fcOps / sizeof fcOps[0])
375
376 static FcOp
377 FcConfigLexOp (const xmlChar *op)
378 {
379     int i;
380
381     for (i = 0; i < NUM_OPS; i++)
382         if (!strcmp (op, fcOps[i].name)) return fcOps[i].op;
383     return FcOpInvalid;
384 }
385
386 static FcBool
387 FcConfigLexBool (const xmlChar *bool)
388 {
389     if (*bool == 't' || *bool == 'T')
390         return FcTrue;
391     if (*bool == 'y' || *bool == 'Y')
392         return FcTrue;
393     if (*bool == '1')
394         return FcTrue;
395     return FcFalse;
396 }
397
398 static FcBool
399 FcConfigParseDir (FcConfig      *config,
400                   xmlDocPtr     doc,
401                   xmlNodePtr    dir)
402 {
403     xmlChar *content = FcConfigContent (doc, dir);
404
405     if (!content)
406         return FcFalse;
407     return FcConfigAddDir (config, (FcChar8 *) content);
408 }
409
410 static FcBool
411 FcConfigParseCache (FcConfig    *config,
412                     xmlDocPtr   doc,
413                     xmlNodePtr  dir)
414 {
415     xmlChar *content = FcConfigContent (doc, dir);
416
417     if (!content)
418         return FcFalse;
419     return FcConfigSetCache (config, (FcChar8 *) content);
420 }
421
422 static FcBool
423 FcConfigParseInclude (FcConfig      *config,
424                       xmlDocPtr     doc,
425                       xmlNodePtr    inc)
426 {
427     xmlChar *content = FcConfigContent (doc, inc);
428     xmlAttr     *attr;
429     FcBool      complain = FcTrue;
430
431     if (!content)
432         return FcFalse;
433     
434     for (attr = inc->properties; attr; attr = attr->next)
435     {
436         if (attr->type != XML_ATTRIBUTE_NODE)
437             continue;
438         if (!strcmp (attr->name, "ignore_missing"))
439             complain = !FcConfigLexBool (FcConfigAttr (doc, attr));
440     }
441     return FcConfigParseAndLoad (config, (FcChar8 *) content, complain);
442 }
443
444 static FcBool
445 FcConfigParseBlank (FcConfig        *config,
446                     xmlDocPtr       doc,
447                     xmlNodePtr      blank)
448 {
449     xmlNode     *node;
450     FcChar32    ucs4;
451
452     for (node = blank->children; node; node = node->next)
453     {
454         if (node->type != XML_ELEMENT_NODE)
455             continue;
456         if (!strcmp (node->name, "int"))
457         {
458             ucs4 = (FcChar32) strtol ((char *) FcConfigContent (doc, node), 0, 0);
459             if (!config->blanks)
460             {
461                 config->blanks = FcBlanksCreate ();
462                 if (!config->blanks)
463                     break;
464             }
465             if (!FcBlanksAdd (config->blanks, ucs4))
466                 break;
467         }
468     }
469     if (node)
470         return FcFalse;
471     return FcTrue;
472 }
473
474 static FcBool
475 FcConfigParseConfig (FcConfig       *config,
476                      xmlDocPtr      doc,
477                      xmlNodePtr     cfg)
478 {
479     xmlNode     *node;
480
481     for (node = cfg->children; node; node = node->next)
482     {
483         if (node->type != XML_ELEMENT_NODE)
484             continue;
485         if (!strcmp (node->name, "blank"))
486         {
487             if (!FcConfigParseBlank (config, doc, node))
488                 break;
489         }
490     }
491     if (node)
492         return FcFalse;
493     return FcTrue;
494 }
495
496 static FcMatrix *
497 FcConfigParseMatrix (xmlDocPtr  doc,
498                      xmlNodePtr node)
499 {
500     static FcMatrix m;
501     enum { m_xx, m_xy, m_yx, m_yy, m_done } matrix_state = m_xx;
502     double  v;
503     xmlChar *text;
504     
505     FcMatrixInit (&m);
506
507     for (; node; node = node->next)
508     {
509         if (node->type != XML_ELEMENT_NODE)
510             continue;
511         if (strcmp (node->name, "double"))
512             continue;
513         text = FcConfigContent (doc, node);
514         if (!text)
515             continue;
516         v = strtod ((char *) text, 0);
517         switch (matrix_state) {
518         case m_xx: m.xx = v; break;
519         case m_xy: m.xy = v; break;
520         case m_yx: m.yx = v; break;
521         case m_yy: m.yy = v; break;
522         default: break;
523         }
524         matrix_state++;
525     }
526          
527     return &m;
528 }
529
530 static FcExpr *
531 FcConfigParseExpr (xmlDocPtr    doc,
532                    xmlNodePtr   expr)
533 {
534     FcOp        op = FcConfigLexOp (expr->name);
535     xmlNodePtr  node;
536     FcExpr      *l = 0, *e = 0, *r = 0, *c = 0;
537
538     switch (op) {
539     case FcOpInteger:
540         l = FcExprCreateInteger (strtol ((char *) FcConfigContent (doc, expr), 0, 0));
541         break;
542     case FcOpDouble:
543         l = FcExprCreateDouble (strtod ((char *) FcConfigContent (doc, expr), 0));
544         break;
545     case FcOpString:
546         l = FcExprCreateString ((FcChar8 *) FcConfigContent (doc, expr));
547         break;
548     case FcOpMatrix:
549         l = FcExprCreateMatrix (FcConfigParseMatrix (doc, expr));
550         break;
551     case FcOpBool:
552         l = FcExprCreateBool (FcConfigLexBool(FcConfigContent (doc, expr)));
553         break;
554     case FcOpCharSet:
555         /* not sure what to do here yet */
556         break;
557     case FcOpField:
558         l = FcExprCreateField ((char *) FcConfigContent (doc, expr));
559         break;
560     case FcOpConst:
561         l = FcExprCreateConst ((FcChar8 *) FcConfigContent (doc, expr));
562         break;
563     case FcOpQuest:
564         for (node = expr->children; node; node = node->next)
565         {
566             if (node->type != XML_ELEMENT_NODE)
567                 continue;
568             e = FcConfigParseExpr (doc, node);
569             if (!e)
570                 break;
571             if (!l)
572                 l = e;
573             else if (!c)
574                 c = e;
575             else if (!r)
576                 r = e;
577             else
578                 FcExprDestroy (e);
579         }
580         e = 0;
581         if (!node && l && c && r)
582         {
583             e = FcExprCreateOp (c, FcOpQuest, r);
584             if (e)
585             {
586                 r = e;
587                 c = 0;
588                 e = FcExprCreateOp (l, FcOpQuest, r);
589             }
590             if (!e)
591                 node = expr->children;
592         }
593         if (node || !l || !c || !r || !e)
594         {
595             if (l)
596                 FcExprDestroy (l);
597             if (c)
598                 FcExprDestroy (c);
599             if (r)
600                 FcExprDestroy (r);
601             return 0;
602         }
603         break;
604     default:
605         for (node = expr->children; node; node = node->next)
606         {
607             if (node->type != XML_ELEMENT_NODE)
608                 continue;
609             e = FcConfigParseExpr (doc, node);
610             if (!e)
611                 break;
612             if (!l)
613                 l = e;
614             else
615             {
616                 r = e;
617                 e = FcExprCreateOp (l, op, r);
618                 if (!e)
619                 {
620                     FcExprDestroy (r);
621                     break;
622                 }
623                 l = e;
624             }
625         }
626         if (node || !l)
627         {
628             if (l)
629                 FcExprDestroy (l);
630             return 0;
631         }
632         /*
633          * Special case for unary ops 
634          */
635         if (!r)
636         {
637             e = FcExprCreateOp (l, op, 0);
638             if (!e)
639             {
640                 FcExprDestroy (l);
641                 return 0;
642             }
643         }
644         break;
645     
646     case FcOpInvalid:
647         return 0;
648     }
649     return l;
650 }
651
652 static FcTest *
653 FcConfigParseTest (xmlDocPtr    doc,
654                    xmlNodePtr   test)
655 {
656     xmlNodePtr  node;
657     xmlAttrPtr  attr;
658     FcQual      qual = FcQualAny;
659     FcOp        op = FcOpEqual;
660     xmlChar     *field = 0;
661     FcExpr      *expr = 0;
662
663     for (attr = test->properties; attr; attr = attr->next)
664     {
665         if (attr->type != XML_ATTRIBUTE_NODE)
666             continue;
667         if (!strcmp (attr->name, "qual"))
668         {
669             xmlChar *qual_name = FcConfigAttr (doc, attr);
670             if (!qual_name)
671                 ;
672             else if (!FcStrCmpIgnoreCase ((FcChar8 *) qual_name, (FcChar8 *) "any"))
673                 qual = FcQualAny;
674             else if (!FcStrCmpIgnoreCase ((FcChar8 *) qual_name, (FcChar8 *) "all"))
675                 qual = FcQualAll;
676         }
677         else if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "name"))
678         {
679             field = FcConfigAttr (doc, attr);
680         }
681         else if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "compare"))
682         {
683             xmlChar *compare = FcConfigAttr (doc, attr);
684             
685             if (!compare || (op = FcConfigLexOp (compare)) == FcOpInvalid)
686             {
687                 FcConfigParseError ("Invalid comparison %s", 
688                                     compare ? (char *) compare : "<missing>");
689                 return 0;
690             }
691         }
692     }
693     if (attr)
694         return 0;
695
696     for (node = test->children; node; node = node->next)
697     {
698         if (node->type != XML_ELEMENT_NODE)
699             continue;
700         expr = FcConfigParseExpr (doc, node);
701         if (!expr)
702             return 0;
703         break;
704     }
705
706     if (!expr)
707     {
708         FcConfigParseError ("Missing test expression");
709         return 0;
710     }
711     
712     return FcTestCreate (qual, (char *) field, op, expr);
713 }
714
715 static FcExpr *
716 FcConfigParseExprList (xmlDocPtr    doc,
717                        xmlNodePtr   expr)
718 {
719     FcExpr  *l, *e, *r;
720     
721     if (!expr)
722         return 0;
723     
724     e = FcConfigParseExprList (doc, expr->next);
725
726     if (expr->type == XML_ELEMENT_NODE)
727     {
728         r = e;
729         l = FcConfigParseExpr (doc, expr);
730         if (!l)
731             goto bail;
732         if (r)
733         {
734             e = FcExprCreateOp (l, FcOpComma, r);
735             if (!e)
736                 goto bail;
737         }
738         else
739             e = l;
740     }
741     
742     return e;
743 bail:
744     if (l)
745         FcExprDestroy (l);
746     if (r)
747         FcExprDestroy (r);
748     return 0;
749 }
750
751 static FcEdit *
752 FcConfigParseEdit (xmlDocPtr    doc,
753                    xmlNodePtr   edit)
754 {
755     xmlAttrPtr  attr;
756     xmlChar     *name = 0;
757     FcOp        mode = FcOpAssign;
758     FcExpr      *e;
759     FcEdit      *ed;
760
761     for (attr = edit->properties; attr; attr = attr->next)
762     {
763         if (attr->type != XML_ATTRIBUTE_NODE)
764             continue;
765         if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "name"))
766             name = FcConfigAttr (doc, attr);
767         else if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "mode"))
768             mode = FcConfigLexOp (FcConfigAttr (doc, attr));
769     }
770
771     e = FcConfigParseExprList (doc, edit->children);
772
773     ed = FcEditCreate ((char *) name, mode, e);
774     if (!ed)
775         FcExprDestroy (e);
776     return ed;
777 }
778
779 static FcBool
780 FcConfigParseMatch (FcConfig    *config,
781                     xmlDocPtr   doc,
782                     xmlNodePtr  match)
783 {
784     xmlNodePtr  node;
785     xmlAttrPtr  attr;
786     FcTest      *tests = 0, **prevTest = &tests, *test;
787     FcEdit      *edits = 0, **prevEdit = &edits, *edit;
788     FcMatchKind kind = FcMatchPattern;
789     FcBool      found_kind = FcFalse;
790
791     for (node = match->children; node; node = node->next)
792     {
793         if (node->type != XML_ELEMENT_NODE)
794             continue;
795         if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "test"))
796         {
797             test = FcConfigParseTest (doc, node);
798             if (!test)
799                 break;
800             *prevTest = test;
801             prevTest = &test->next;
802         }
803         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "edit"))
804         {
805             edit = FcConfigParseEdit (doc, node);
806             if (!edit)
807                 break;
808             *prevEdit = edit;
809             prevEdit = &edit->next;
810         }
811     }
812
813     for (attr = match->properties; attr; attr = attr->next)
814     {
815         if (attr->type != XML_ATTRIBUTE_NODE)
816             continue;
817         if (!FcStrCmpIgnoreCase ((FcChar8 *) attr->name, (FcChar8 *) "target"))
818         {
819             xmlChar *target = FcConfigAttr (doc, attr);
820             if (!target)
821             {
822                 FcConfigParseError ("Missing match target");
823                 break;
824             }
825             else if (!FcStrCmpIgnoreCase ((FcChar8 *) target, (FcChar8 *) "pattern"))
826             {
827                 kind = FcMatchPattern;
828                 found_kind = FcTrue;
829             }
830             else if (!FcStrCmpIgnoreCase ((FcChar8 *) target, (FcChar8 *) "font"))
831             {
832                 kind = FcMatchFont;
833                 found_kind = FcTrue;
834             }
835         }
836     }
837
838     if (node || attr || !found_kind || 
839         !FcConfigAddEdit (config, tests, edits, kind))
840     {
841         if (tests)
842             FcTestDestroy (tests);
843         if (edits)
844             FcEditDestroy (edits);
845         return FcFalse;
846     }
847
848     return FcTrue;
849 }
850
851 static FcExpr *
852 FcConfigParseFamilies (xmlDocPtr    doc,
853                        xmlNodePtr   family)
854 {
855     FcExpr  *next = 0, *this = 0, *expr = 0;
856     
857     if (!family)
858         return 0;
859     next = FcConfigParseFamilies (doc, family->next);
860     
861     if (family->type == XML_ELEMENT_NODE && 
862         !FcStrCmpIgnoreCase ((FcChar8 *) family->name, (FcChar8 *) "family"))
863     {
864         this = FcExprCreateString ((FcChar8 *) FcConfigContent (doc, family));
865         if (!this)
866             goto bail;
867         if (next)
868         {
869             expr = FcExprCreateOp (this, FcOpComma, next);
870             if (!expr)
871                 goto bail;
872         }
873         else
874             expr = this;
875     }
876     else
877         expr = next;
878     return expr;
879
880 bail:
881     if (expr)
882         FcExprDestroy (expr);
883     if (this)
884         FcExprDestroy (this);
885     if (next)
886         FcExprDestroy (next);
887     return 0;
888 }
889
890 static FcBool
891 FcConfigParseAlias (FcConfig    *config,
892                     xmlDocPtr   doc,
893                     xmlNodePtr  alias)
894 {
895     xmlNodePtr  node;
896     FcExpr      *prefer = 0, *accept = 0, *def = 0;
897     FcExpr      *family = 0;
898     FcEdit      *edit = 0, *next;
899     FcTest      *test;
900
901     for (node = alias->children; node; node = node->next)
902     {
903         if (node->type != XML_ELEMENT_NODE)
904             continue;
905         if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "family"))
906             family = FcExprCreateString ((FcChar8 *) FcConfigContent (doc, node));
907         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "prefer"))
908             prefer = FcConfigParseFamilies (doc, node->children);
909         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "accept"))
910             accept = FcConfigParseFamilies (doc, node->children);
911         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "default"))
912             def = FcConfigParseFamilies (doc, node->children);
913     }
914     if (!family)
915         return FcFalse;
916     
917     if (prefer)
918     {
919         edit = FcEditCreate (FcConfigSaveField ("family"),
920                              FcOpPrepend,
921                              prefer);
922         if (edit)
923             edit->next = 0;
924     }
925     if (accept)
926     {
927         next = edit;
928         edit = FcEditCreate (FcConfigSaveField ("family"),
929                              FcOpAppend,
930                              accept);
931         if (edit)
932             edit->next = next;
933     }
934     if (def)
935     {
936         next = edit;
937         edit = FcEditCreate (FcConfigSaveField ("family"),
938                               FcOpAppendLast,
939                               def);
940         if (edit)
941             edit->next = next;
942     }
943     if (edit)
944     {
945         test = FcTestCreate (FcQualAny,
946                              FcConfigSaveField ("family"),
947                              FcOpEqual,
948                              family);
949         if (test)
950             FcConfigAddEdit (config, test, edit, FcMatchPattern);
951     }
952     return FcTrue;
953 }
954
955 FcBool
956 FcConfigParse (FcConfig     *config,
957                xmlDocPtr    doc)
958 {
959     xmlNodePtr  cur;
960     xmlNodePtr  node;
961     
962     cur = xmlDocGetRootElement (doc);
963
964     for (node = cur->children; node; node = node->next)
965     {
966         if (node->type != XML_ELEMENT_NODE)
967             continue;
968         if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "dir"))
969         {
970             if (!FcConfigParseDir (config, doc, node))
971                 break;
972         }
973         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "cache"))
974         {
975             if (!FcConfigParseCache (config, doc, node))
976                 break;
977         }
978         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "include"))
979         {
980             if (!FcConfigParseInclude (config, doc, node))
981                 break;
982         }
983         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "config"))
984         {
985             if (!FcConfigParseConfig (config, doc, node))
986                 break;
987         }
988         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "match"))
989         {
990             if (!FcConfigParseMatch (config, doc, node))
991                 break;
992         }
993         else if (!FcStrCmpIgnoreCase ((FcChar8 *) node->name, (FcChar8 *) "alias"))
994         {
995             if (!FcConfigParseAlias (config, doc, node))
996                 break;
997         }
998         else
999         {
1000             FcConfigParseError ("invalid element %s", node->name);
1001             break;
1002         }   
1003     }
1004     if (node)
1005         return FcFalse;
1006     return FcTrue;
1007 }
1008
1009 FcBool
1010 FcConfigParseAndLoad (FcConfig      *config,
1011                       const FcChar8 *file,
1012                       FcBool        complain)
1013 {
1014     xmlDocPtr   doc;
1015     FcBool      ret;
1016
1017     doc = FcConfigLoad (file);
1018     if (doc)
1019     {
1020         ret = FcConfigAddConfigFile (config, file);
1021         if (ret)
1022             ret = FcConfigParse (config, doc);
1023         xmlFreeDoc (doc);
1024         return ret;
1025     }
1026     if (complain)
1027     {
1028         if (file)
1029             FcConfigParseError ("Cannot load config file \"%s\"", file);
1030         else
1031             FcConfigParseError ("Cannot load default config file");
1032         return FcFalse;
1033     }
1034     return FcTrue;
1035 }