]> git.wh0rd.org Git - fontconfig.git/blob - src/fccfg.c
Fix inconsistent const usage in FcConfigCompareValue
[fontconfig.git] / src / fccfg.c
1 /*
2  * $XFree86: xc/lib/fontconfig/src/fccfg.c,v 1.23 2002/08/31 22:17:32 keithp Exp $
3  *
4  * Copyright © 2000 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 "fcint.h"
26
27 FcConfig    *_fcConfig;
28
29 FcConfig *
30 FcConfigCreate (void)
31 {
32     FcSetName   set;
33     FcConfig    *config;
34
35     config = malloc (sizeof (FcConfig));
36     if (!config)
37         goto bail0;
38     FcMemAlloc (FC_MEM_CONFIG, sizeof (FcConfig));
39     
40     config->configDirs = FcStrSetCreate ();
41     if (!config->configDirs)
42         goto bail1;
43     
44     config->configFiles = FcStrSetCreate ();
45     if (!config->configFiles)
46         goto bail2;
47     
48     config->fontDirs = FcStrSetCreate ();
49     if (!config->fontDirs)
50         goto bail3;
51     
52     config->cache = 0;
53     if (!FcConfigSetCache (config, (FcChar8 *) ("~/" FC_USER_CACHE_FILE)))
54         goto bail4;
55
56     config->blanks = 0;
57
58     config->substPattern = 0;
59     config->substFont = 0;
60     config->maxObjects = 0;
61     for (set = FcSetSystem; set <= FcSetApplication; set++)
62         config->fonts[set] = 0;
63
64     config->rescanTime = time(0);
65     config->rescanInterval = 30;    
66     
67     return config;
68
69 bail4:
70     FcStrSetDestroy (config->fontDirs);
71 bail3:
72     FcStrSetDestroy (config->configFiles);
73 bail2:
74     FcStrSetDestroy (config->configDirs);
75 bail1:
76     free (config);
77     FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
78 bail0:
79     return 0;
80 }
81
82 typedef struct _FcFileTime {
83     time_t  time;
84     FcBool  set;
85 } FcFileTime;
86
87 static FcFileTime
88 FcConfigNewestFile (FcStrSet *files)
89 {
90     FcStrList       *list = FcStrListCreate (files);
91     FcFileTime      newest = { 0, FcFalse };
92     FcChar8         *file;
93     struct  stat    statb;
94
95     if (list)
96     {
97         while ((file = FcStrListNext (list)))
98             if (stat ((char *) file, &statb) == 0)
99                 if (!newest.set || statb.st_mtime - newest.time > 0)
100                     newest.time = statb.st_mtime;
101         FcStrListDone (list);
102     }
103     return newest;
104 }
105
106 FcBool
107 FcConfigUptoDate (FcConfig *config)
108 {
109     FcFileTime  config_time, font_time;
110     time_t      now = time(0);
111     if (!config)
112     {
113         config = FcConfigGetCurrent ();
114         if (!config)
115             return FcFalse;
116     }
117     config_time = FcConfigNewestFile (config->configFiles);
118     font_time = FcConfigNewestFile (config->configDirs);
119     if ((config_time.set && config_time.time - config->rescanTime > 0) ||
120         (font_time.set && font_time.time - config->rescanTime) > 0)
121     {
122         return FcFalse;
123     }
124     config->rescanTime = now;
125     return FcTrue;
126 }
127
128 static void
129 FcSubstDestroy (FcSubst *s)
130 {
131     FcSubst *n;
132     
133     while (s)
134     {
135         n = s->next;
136         FcTestDestroy (s->test);
137         FcEditDestroy (s->edit);
138         s = n;
139     }
140 }
141
142 void
143 FcConfigDestroy (FcConfig *config)
144 {
145     FcSetName   set;
146
147     if (config == _fcConfig)
148         _fcConfig = 0;
149
150     FcStrSetDestroy (config->configDirs);
151     FcStrSetDestroy (config->fontDirs);
152     FcStrSetDestroy (config->configFiles);
153
154     FcStrFree (config->cache);
155
156     FcSubstDestroy (config->substPattern);
157     FcSubstDestroy (config->substFont);
158     for (set = FcSetSystem; set <= FcSetApplication; set++)
159         if (config->fonts[set])
160             FcFontSetDestroy (config->fonts[set]);
161     free (config);
162     FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
163 }
164
165 /*
166  * Scan the current list of directories in the configuration
167  * and build the set of available fonts. Update the
168  * per-user cache file to reflect the new configuration
169  */
170
171 FcBool
172 FcConfigBuildFonts (FcConfig *config)
173 {
174     FcFontSet       *fonts;
175     FcGlobalCache   *cache;
176     FcStrList       *list;
177     FcChar8         *dir;
178
179     fonts = FcFontSetCreate ();
180     if (!fonts)
181         goto bail0;
182     
183     cache = FcGlobalCacheCreate ();
184     if (!cache)
185         goto bail1;
186
187     FcGlobalCacheLoad (cache, config->cache);
188
189     list = FcConfigGetFontDirs (config);
190     if (!list)
191         goto bail1;
192
193     while ((dir = FcStrListNext (list)))
194     {
195         if (FcDebug () & FC_DBG_FONTSET)
196             printf ("scan dir %s\n", dir);
197         FcDirScan (fonts, config->fontDirs, cache, config->blanks, dir, FcFalse);
198     }
199     
200     FcStrListDone (list);
201     
202     if (FcDebug () & FC_DBG_FONTSET)
203         FcFontSetPrint (fonts);
204
205     FcGlobalCacheSave (cache, config->cache);
206     FcGlobalCacheDestroy (cache);
207
208     FcConfigSetFonts (config, fonts, FcSetSystem);
209     
210     return FcTrue;
211 bail1:
212     FcFontSetDestroy (fonts);
213 bail0:
214     return FcFalse;
215 }
216
217 FcBool
218 FcConfigSetCurrent (FcConfig *config)
219 {
220     if (!config->fonts)
221         if (!FcConfigBuildFonts (config))
222             return FcFalse;
223
224     if (_fcConfig)
225         FcConfigDestroy (_fcConfig);
226     _fcConfig = config;
227     return FcTrue;
228 }
229
230 FcConfig *
231 FcConfigGetCurrent (void)
232 {
233     if (!_fcConfig)
234         if (!FcInit ())
235             return 0;
236     return _fcConfig;
237 }
238
239 FcBool
240 FcConfigAddConfigDir (FcConfig      *config,
241                       const FcChar8 *d)
242 {
243     return FcStrSetAddFilename (config->configDirs, d);
244 }
245
246 FcStrList *
247 FcConfigGetConfigDirs (FcConfig   *config)
248 {
249     if (!config)
250     {
251         config = FcConfigGetCurrent ();
252         if (!config)
253             return 0;
254     }
255     return FcStrListCreate (config->configDirs);
256 }
257
258 FcBool
259 FcConfigAddFontDir (FcConfig        *config,
260                     const FcChar8   *d)
261 {
262     return FcStrSetAddFilename (config->fontDirs, d);
263 }
264
265 FcBool
266 FcConfigAddDir (FcConfig            *config,
267                 const FcChar8       *d)
268 {
269     return (FcConfigAddConfigDir (config, d) && 
270             FcConfigAddFontDir (config, d));
271 }
272
273 FcStrList *
274 FcConfigGetFontDirs (FcConfig   *config)
275 {
276     if (!config)
277     {
278         config = FcConfigGetCurrent ();
279         if (!config)
280             return 0;
281     }
282     return FcStrListCreate (config->fontDirs);
283 }
284
285 FcBool
286 FcConfigAddConfigFile (FcConfig     *config,
287                        const FcChar8   *f)
288 {
289     FcBool      ret;
290     FcChar8     *file = FcConfigFilename (f);
291     
292     if (!file)
293         return FcFalse;
294     
295     ret = FcStrSetAdd (config->configFiles, file);
296     FcStrFree (file);
297     return ret;
298 }
299
300 FcStrList *
301 FcConfigGetConfigFiles (FcConfig    *config)
302 {
303     if (!config)
304     {
305         config = FcConfigGetCurrent ();
306         if (!config)
307             return 0;
308     }
309     return FcStrListCreate (config->configFiles);
310 }
311
312 FcBool
313 FcConfigSetCache (FcConfig      *config,
314                   const FcChar8 *c)
315 {
316     FcChar8    *new = FcStrCopyFilename (c);
317     
318     if (!new)
319         return FcFalse;
320     if (config->cache)
321         FcStrFree (config->cache);
322     config->cache = new;
323     return FcTrue;
324 }
325
326 FcChar8 *
327 FcConfigGetCache (FcConfig  *config)
328 {
329     if (!config)
330     {
331         config = FcConfigGetCurrent ();
332         if (!config)
333             return 0;
334     }
335     return config->cache;
336 }
337
338 FcFontSet *
339 FcConfigGetFonts (FcConfig      *config,
340                   FcSetName     set)
341 {
342     if (!config)
343     {
344         config = FcConfigGetCurrent ();
345         if (!config)
346             return 0;
347     }
348     return config->fonts[set];
349 }
350
351 void
352 FcConfigSetFonts (FcConfig      *config,
353                   FcFontSet     *fonts,
354                   FcSetName     set)
355 {
356     if (config->fonts[set])
357         FcFontSetDestroy (config->fonts[set]);
358     config->fonts[set] = fonts;
359 }
360
361
362
363 FcBlanks *
364 FcConfigGetBlanks (FcConfig     *config)
365 {
366     if (!config)
367     {
368         config = FcConfigGetCurrent ();
369         if (!config)
370             return 0;
371     }
372     return config->blanks;
373 }
374
375 FcBool
376 FcConfigAddBlank (FcConfig      *config,
377                   FcChar32      blank)
378 {
379     FcBlanks    *b;
380     
381     b = config->blanks;
382     if (!b)
383     {
384         b = FcBlanksCreate ();
385         if (!b)
386             return FcFalse;
387     }
388     if (!FcBlanksAdd (b, blank))
389         return FcFalse;
390     config->blanks = b;
391     return FcTrue;
392 }
393
394 int
395 FcConfigGetRescanInverval (FcConfig *config)
396 {
397     if (!config)
398     {
399         config = FcConfigGetCurrent ();
400         if (!config)
401             return 0;
402     }
403     return config->rescanInterval;
404 }
405
406 FcBool
407 FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
408 {
409     if (!config)
410     {
411         config = FcConfigGetCurrent ();
412         if (!config)
413             return FcFalse;
414     }
415     config->rescanInterval = rescanInterval;
416     return FcTrue;
417 }
418
419 FcBool
420 FcConfigAddEdit (FcConfig       *config,
421                  FcTest         *test,
422                  FcEdit         *edit,
423                  FcMatchKind    kind)
424 {
425     FcSubst     *subst, **prev;
426     FcTest      *t;
427     int         num;
428
429     subst = (FcSubst *) malloc (sizeof (FcSubst));
430     if (!subst)
431         return FcFalse;
432     FcMemAlloc (FC_MEM_SUBST, sizeof (FcSubst));
433     if (kind == FcMatchPattern)
434         prev = &config->substPattern;
435     else
436         prev = &config->substFont;
437     for (; *prev; prev = &(*prev)->next);
438     *prev = subst;
439     subst->next = 0;
440     subst->test = test;
441     subst->edit = edit;
442     num = 0;
443     for (t = test; t; t = t->next)
444     {
445         if (t->kind == FcMatchDefault)
446             t->kind = kind;
447         num++;
448     }
449     if (config->maxObjects < num)
450         config->maxObjects = num;
451     if (FcDebug () & FC_DBG_EDIT)
452     {
453         printf ("Add Subst ");
454         FcSubstPrint (subst);
455     }
456     return FcTrue;
457 }
458
459 typedef struct _FcSubState {
460     FcPatternElt   *elt;
461     FcValueList    *value;
462 } FcSubState;
463
464 static FcValue
465 FcConfigPromote (FcValue v, FcValue u)
466 {
467     if (v.type == FcTypeInteger)
468     {
469         v.type = FcTypeDouble;
470         v.u.d = (double) v.u.i;
471     }
472     else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
473     {
474         v.u.m = &FcIdentityMatrix;
475         v.type = FcTypeMatrix;
476     }
477     else if (v.type == FcTypeString && u.type == FcTypeLangSet)
478     {
479         v.u.l = FcLangSetPromote (v.u.s);
480         v.type = FcTypeLangSet;
481     }
482     return v;
483 }
484
485 FcBool
486 FcConfigCompareValue (const FcValue     m_o,
487                       FcOp              op,
488                       const FcValue     v_o)
489 {
490     FcValue     m = m_o;
491     FcValue     v = v_o;
492     FcBool      ret = FcFalse;
493     
494     m = FcConfigPromote (m, v);
495     v = FcConfigPromote (v, m);
496     if (m.type == v.type) 
497     {
498         switch (m.type) {
499         case FcTypeInteger:
500             break;      /* FcConfigPromote prevents this from happening */
501         case FcTypeDouble:
502             switch (op) {
503             case FcOpEqual:
504             case FcOpContains:
505                 ret = m.u.d == v.u.d;
506                 break;
507             case FcOpNotEqual:
508             case FcOpNotContains:
509                 ret = m.u.d != v.u.d;
510                 break;
511             case FcOpLess:    
512                 ret = m.u.d < v.u.d;
513                 break;
514             case FcOpLessEqual:    
515                 ret = m.u.d <= v.u.d;
516                 break;
517             case FcOpMore:    
518                 ret = m.u.d > v.u.d;
519                 break;
520             case FcOpMoreEqual:    
521                 ret = m.u.d >= v.u.d;
522                 break;
523             default:
524                 break;
525             }
526             break;
527         case FcTypeBool:
528             switch (op) {
529             case FcOpEqual:    
530             case FcOpContains:
531                 ret = m.u.b == v.u.b;
532                 break;
533             case FcOpNotEqual:
534             case FcOpNotContains:
535                 ret = m.u.b != v.u.b;
536                 break;
537             default:
538                 break;
539             }
540             break;
541         case FcTypeString:
542             switch (op) {
543             case FcOpEqual:    
544             case FcOpContains:
545                 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) == 0;
546                 break;
547             case FcOpNotEqual:
548             case FcOpNotContains:
549                 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) != 0;
550                 break;
551             default:
552                 break;
553             }
554             break;
555         case FcTypeMatrix:
556             switch (op) {
557             case FcOpEqual:
558             case FcOpContains:
559                 ret = FcMatrixEqual (m.u.m, v.u.m);
560                 break;
561             case FcOpNotEqual:
562             case FcOpNotContains:
563                 ret = !FcMatrixEqual (m.u.m, v.u.m);
564                 break;
565             default:
566                 break;
567             }
568             break;
569         case FcTypeCharSet:
570             switch (op) {
571             case FcOpContains:
572                 /* m contains v if v is a subset of m */
573                 ret = FcCharSetIsSubset (v.u.c, m.u.c);
574                 break;
575             case FcOpNotContains:
576                 /* m contains v if v is a subset of m */
577                 ret = !FcCharSetIsSubset (v.u.c, m.u.c);
578                 break;
579             case FcOpEqual:
580                 ret = FcCharSetEqual (m.u.c, v.u.c);
581                 break;
582             case FcOpNotEqual:
583                 ret = !FcCharSetEqual (m.u.c, v.u.c);
584                 break;
585             default:
586                 break;
587             }
588             break;
589         case FcTypeLangSet:
590             switch (op) {
591             case FcOpContains:
592                 ret = FcLangSetCompare (v.u.l, m.u.l) != FcLangDifferentLang;
593                 break;
594             case FcOpNotContains:
595                 ret = FcLangSetCompare (v.u.l, m.u.l) == FcLangDifferentLang;
596                 break;
597             case FcOpEqual:
598                 ret = FcLangSetEqual (v.u.l, m.u.l);
599                 break;
600             case FcOpNotEqual:
601                 ret = !FcLangSetEqual (v.u.l, m.u.l);
602                 break;
603             default:
604                 break;
605             }
606             break;
607         case FcTypeVoid:
608             switch (op) {
609             case FcOpEqual:
610             case FcOpContains:
611                 ret = FcTrue;
612                 break;
613             default:
614                 break;
615             }
616             break;
617         case FcTypeFTFace:
618             switch (op) {
619             case FcOpEqual:
620             case FcOpContains:
621                 ret = m.u.f == v.u.f;
622                 break;
623             case FcOpNotEqual:
624             case FcOpNotContains:
625                 ret = m.u.f != v.u.f;
626                 break;
627             default:
628                 break;
629             }
630             break;
631         }
632     }
633     else
634     {
635         if (op == FcOpNotEqual || op == FcOpNotContains)
636             ret = FcTrue;
637     }
638     return ret;
639 }
640
641
642 static FcValue
643 FcConfigEvaluate (FcPattern *p, FcExpr *e)
644 {
645     FcValue     v, vl, vr;
646     FcResult    r;
647     FcMatrix    *m;
648     
649     switch (e->op) {
650     case FcOpInteger:
651         v.type = FcTypeInteger;
652         v.u.i = e->u.ival;
653         break;
654     case FcOpDouble:
655         v.type = FcTypeDouble;
656         v.u.d = e->u.dval;
657         break;
658     case FcOpString:
659         v.type = FcTypeString;
660         v.u.s = e->u.sval;
661         v = FcValueSave (v);
662         break;
663     case FcOpMatrix:
664         v.type = FcTypeMatrix;
665         v.u.m = e->u.mval;
666         v = FcValueSave (v);
667         break;
668     case FcOpCharSet:
669         v.type = FcTypeCharSet;
670         v.u.c = e->u.cval;
671         v = FcValueSave (v);
672         break;
673     case FcOpBool:
674         v.type = FcTypeBool;
675         v.u.b = e->u.bval;
676         break;
677     case FcOpField:
678         r = FcPatternGet (p, e->u.field, 0, &v);
679         if (r != FcResultMatch)
680             v.type = FcTypeVoid;
681         break;
682     case FcOpConst:
683         if (FcNameConstant (e->u.constant, &v.u.i))
684             v.type = FcTypeInteger;
685         else
686             v.type = FcTypeVoid;
687         break;
688     case FcOpQuest:
689         vl = FcConfigEvaluate (p, e->u.tree.left);
690         if (vl.type == FcTypeBool)
691         {
692             if (vl.u.b)
693                 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
694             else
695                 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
696         }
697         else
698             v.type = FcTypeVoid;
699         FcValueDestroy (vl);
700         break;
701     case FcOpEqual:
702     case FcOpNotEqual:
703     case FcOpLess:
704     case FcOpLessEqual:
705     case FcOpMore:
706     case FcOpMoreEqual:
707     case FcOpContains:
708     case FcOpNotContains:
709         vl = FcConfigEvaluate (p, e->u.tree.left);
710         vr = FcConfigEvaluate (p, e->u.tree.right);
711         v.type = FcTypeBool;
712         v.u.b = FcConfigCompareValue (vl, e->op, vr);
713         FcValueDestroy (vl);
714         FcValueDestroy (vr);
715         break;  
716     case FcOpOr:
717     case FcOpAnd:
718     case FcOpPlus:
719     case FcOpMinus:
720     case FcOpTimes:
721     case FcOpDivide:
722         vl = FcConfigEvaluate (p, e->u.tree.left);
723         vr = FcConfigEvaluate (p, e->u.tree.right);
724         vl = FcConfigPromote (vl, vr);
725         vr = FcConfigPromote (vr, vl);
726         if (vl.type == vr.type)
727         {
728             switch (vl.type) {
729             case FcTypeDouble:
730                 switch (e->op) {
731                 case FcOpPlus:     
732                     v.type = FcTypeDouble;
733                     v.u.d = vl.u.d + vr.u.d; 
734                     break;
735                 case FcOpMinus:
736                     v.type = FcTypeDouble;
737                     v.u.d = vl.u.d - vr.u.d; 
738                     break;
739                 case FcOpTimes:
740                     v.type = FcTypeDouble;
741                     v.u.d = vl.u.d * vr.u.d; 
742                     break;
743                 case FcOpDivide:
744                     v.type = FcTypeDouble;
745                     v.u.d = vl.u.d / vr.u.d; 
746                     break;
747                 default:
748                     v.type = FcTypeVoid; 
749                     break;
750                 }
751                 if (v.type == FcTypeDouble &&
752                     v.u.d == (double) (int) v.u.d)
753                 {
754                     v.type = FcTypeInteger;
755                     v.u.i = (int) v.u.d;
756                 }
757                 break;
758             case FcTypeBool:
759                 switch (e->op) {
760                 case FcOpOr:
761                     v.type = FcTypeBool;
762                     v.u.b = vl.u.b || vr.u.b;
763                     break;
764                 case FcOpAnd:
765                     v.type = FcTypeBool;
766                     v.u.b = vl.u.b && vr.u.b;
767                     break;
768                 default:
769                     v.type = FcTypeVoid; 
770                     break;
771                 }
772                 break;
773             case FcTypeString:
774                 switch (e->op) {
775                 case FcOpPlus:
776                     v.type = FcTypeString;
777                     v.u.s = FcStrPlus (vl.u.s, vr.u.s);
778                     if (!v.u.s)
779                         v.type = FcTypeVoid;
780                     break;
781                 default:
782                     v.type = FcTypeVoid;
783                     break;
784                 }
785                 break;
786             case FcTypeMatrix:
787                 switch (e->op) {
788                 case FcOpTimes:
789                     v.type = FcTypeMatrix;
790                     m = malloc (sizeof (FcMatrix));
791                     if (m)
792                     {
793                         FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
794                         FcMatrixMultiply (m, vl.u.m, vr.u.m);
795                         v.u.m = m;
796                     }
797                     else
798                     {
799                         v.type = FcTypeVoid;
800                     }
801                     break;
802                 default:
803                     v.type = FcTypeVoid;
804                     break;
805                 }
806                 break;
807             default:
808                 v.type = FcTypeVoid;
809                 break;
810             }
811         }
812         else
813             v.type = FcTypeVoid;
814         FcValueDestroy (vl);
815         FcValueDestroy (vr);
816         break;
817     case FcOpNot:
818         vl = FcConfigEvaluate (p, e->u.tree.left);
819         switch (vl.type) {
820         case FcTypeBool:
821             v.type = FcTypeBool;
822             v.u.b = !vl.u.b;
823             break;
824         default:
825             v.type = FcTypeVoid;
826             break;
827         }
828         FcValueDestroy (vl);
829         break;
830     default:
831         v.type = FcTypeVoid;
832         break;
833     }
834     return v;
835 }
836
837 static FcValueList *
838 FcConfigMatchValueList (FcPattern       *p,
839                         FcTest          *t,
840                         FcValueList     *values)
841 {
842     FcValueList     *ret = 0;
843     FcExpr          *e = t->expr;
844     FcValue         value;
845     FcValueList     *v;
846     
847     while (e)
848     {
849         if (e->op == FcOpComma)
850         {
851             value = FcConfigEvaluate (p, e->u.tree.left);
852             e = e->u.tree.right;
853         }
854         else
855         {
856             value = FcConfigEvaluate (p, e);
857             e = 0;
858         }
859
860         for (v = values; v; v = v->next)
861         {
862             if (FcConfigCompareValue (v->value, t->op, value))
863             {
864                 if (!ret)
865                     ret = v;
866             }
867             else
868             {
869                 if (t->qual == FcQualAll)
870                 {
871                     ret = 0;
872                     break;
873                 }
874             }
875         }
876         FcValueDestroy (value);
877     }
878     return ret;
879 }
880
881 static FcValueList *
882 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
883 {
884     FcValueList *l;
885     
886     if (!e)
887         return 0;
888     l = (FcValueList *) malloc (sizeof (FcValueList));
889     if (!l)
890         return 0;
891     FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
892     if (e->op == FcOpComma)
893     {
894         l->value = FcConfigEvaluate (p, e->u.tree.left);
895         l->next  = FcConfigValues (p, e->u.tree.right, binding);
896     }
897     else
898     {
899         l->value = FcConfigEvaluate (p, e);
900         l->next  = 0;
901     }
902     l->binding = binding;
903     while (l && l->value.type == FcTypeVoid)
904     {
905         FcValueList     *next = l->next;
906         
907         FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
908         free (l);
909         l = next;
910     }
911     return l;
912 }
913
914 static FcBool
915 FcConfigAdd (FcValueList    **head,
916              FcValueList    *position,
917              FcBool         append,
918              FcValueList    *new)
919 {
920     FcValueList    **prev, *last;
921     
922     if (append)
923     {
924         if (position)
925             prev = &position->next;
926         else
927             for (prev = head; *prev; prev = &(*prev)->next)
928                 ;
929     }
930     else
931     {
932         if (position)
933         {
934             for (prev = head; *prev; prev = &(*prev)->next)
935             {
936                 if (*prev == position)
937                     break;
938             }
939         }
940         else
941             prev = head;
942
943         if (FcDebug () & FC_DBG_EDIT)
944         {
945             if (!*prev)
946                 printf ("position not on list\n");
947         }
948     }
949
950     if (FcDebug () & FC_DBG_EDIT)
951     {
952         printf ("%s list before ", append ? "Append" : "Prepend");
953         FcValueListPrint (*head);
954         printf ("\n");
955     }
956     
957     if (new)
958     {
959         last = new;
960         while (last->next)
961             last = last->next;
962     
963         last->next = *prev;
964         *prev = new;
965     }
966     
967     if (FcDebug () & FC_DBG_EDIT)
968     {
969         printf ("%s list after ", append ? "Append" : "Prepend");
970         FcValueListPrint (*head);
971         printf ("\n");
972     }
973     
974     return FcTrue;
975 }
976
977 static void
978 FcConfigDel (FcValueList    **head,
979              FcValueList    *position)
980 {
981     FcValueList    **prev;
982
983     for (prev = head; *prev; prev = &(*prev)->next)
984     {
985         if (*prev == position)
986         {
987             *prev = position->next;
988             position->next = 0;
989             FcValueListDestroy (position);
990             break;
991         }
992     }
993 }
994
995 static void
996 FcConfigPatternAdd (FcPattern   *p,
997                     const char  *object,
998                     FcValueList *list,
999                     FcBool      append)
1000 {
1001     if (list)
1002     {
1003         FcPatternElt    *e = FcPatternInsertElt (p, object);
1004     
1005         if (!e)
1006             return;
1007         FcConfigAdd (&e->values, 0, append, list);
1008     }
1009 }
1010
1011 /*
1012  * Delete all values associated with a field
1013  */
1014 static void
1015 FcConfigPatternDel (FcPattern   *p,
1016                     const char  *object)
1017 {
1018     FcPatternElt    *e = FcPatternFindElt (p, object);
1019     if (!e)
1020         return;
1021     while (e->values)
1022         FcConfigDel (&e->values, e->values);
1023 }
1024
1025 static void
1026 FcConfigPatternCanon (FcPattern     *p,
1027                       const char    *object)
1028 {
1029     FcPatternElt    *e = FcPatternFindElt (p, object);
1030     if (!e)
1031         return;
1032     if (!e->values)
1033         FcPatternDel (p, object);
1034 }
1035
1036 FcBool
1037 FcConfigSubstituteWithPat (FcConfig    *config,
1038                            FcPattern   *p,
1039                            FcPattern   *p_pat,
1040                            FcMatchKind kind)
1041 {
1042     FcSubst         *s;
1043     FcSubState      *st;
1044     int             i;
1045     FcTest          *t;
1046     FcEdit          *e;
1047     FcValueList     *l;
1048     FcPattern       *m;
1049
1050     if (!config)
1051     {
1052         config = FcConfigGetCurrent ();
1053         if (!config)
1054             return FcFalse;
1055     }
1056
1057     st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1058     if (!st && config->maxObjects)
1059         return FcFalse;
1060     FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1061
1062     if (FcDebug () & FC_DBG_EDIT)
1063     {
1064         printf ("FcConfigSubstitute ");
1065         FcPatternPrint (p);
1066     }
1067     if (kind == FcMatchPattern)
1068         s = config->substPattern;
1069     else
1070         s = config->substFont;
1071     for (; s; s = s->next)
1072     {
1073         /*
1074          * Check the tests to see if
1075          * they all match the pattern
1076          */
1077         for (t = s->test, i = 0; t; t = t->next, i++)
1078         {
1079             if (FcDebug () & FC_DBG_EDIT)
1080             {
1081                 printf ("FcConfigSubstitute test ");
1082                 FcTestPrint (t);
1083             }
1084             st[i].elt = 0;
1085             if (kind == FcMatchFont && t->kind == FcMatchPattern)
1086                 m = p_pat;
1087             else
1088                 m = p;
1089             if (m)
1090                 st[i].elt = FcPatternFindElt (m, t->field);
1091             else
1092                 st[i].elt = 0;
1093             /*
1094              * If there's no such field in the font,
1095              * then FcQualAll matches while FcQualAny does not
1096              */
1097             if (!st[i].elt)
1098             {
1099                 if (t->qual == FcQualAll)
1100                 {
1101                     st[i].value = 0;
1102                     continue;
1103                 }
1104                 else
1105                     break;
1106             }
1107             /*
1108              * Check to see if there is a match, mark the location
1109              * to apply match-relative edits
1110              */
1111             st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1112             if (!st[i].value)
1113                 break;
1114             if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1115                 break;
1116             if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1117                 break;
1118         }
1119         if (t)
1120         {
1121             if (FcDebug () & FC_DBG_EDIT)
1122                 printf ("No match\n");
1123             continue;
1124         }
1125         if (FcDebug () & FC_DBG_EDIT)
1126         {
1127             printf ("Substitute ");
1128             FcSubstPrint (s);
1129         }
1130         for (e = s->edit; e; e = e->next)
1131         {
1132             /*
1133              * Evaluate the list of expressions
1134              */
1135             l = FcConfigValues (p, e->expr, e->binding);
1136             /*
1137              * Locate any test associated with this field, skipping
1138              * tests associated with the pattern when substituting in
1139              * the font
1140              */
1141             for (t = s->test, i = 0; t; t = t->next, i++)
1142             {
1143                 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1144                     !FcStrCmpIgnoreCase ((FcChar8 *) t->field, 
1145                                          (FcChar8 *) e->field))
1146                 {
1147                     if (!st[i].elt)
1148                         t = 0;
1149                     break;
1150                 }
1151             }
1152             switch (e->op) {
1153             case FcOpAssign:
1154                 /*
1155                  * If there was a test, then replace the matched
1156                  * value with the new list of values
1157                  */
1158                 if (t)
1159                 {
1160                     FcValueList *thisValue = st[i].value;
1161                     FcValueList *nextValue = thisValue ? thisValue->next : 0;
1162                     
1163                     /*
1164                      * Append the new list of values after the current value
1165                      */
1166                     FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1167                     /*
1168                      * Delete the marked value
1169                      */
1170                     FcConfigDel (&st[i].elt->values, thisValue);
1171                     /*
1172                      * Adjust any pointers into the value list to ensure
1173                      * future edits occur at the same place
1174                      */
1175                     for (t = s->test, i = 0; t; t = t->next, i++)
1176                     {
1177                         if (st[i].value == thisValue)
1178                             st[i].value = nextValue;
1179                     }
1180                     break;
1181                 }
1182                 /* fall through ... */
1183             case FcOpAssignReplace:
1184                 /*
1185                  * Delete all of the values and insert
1186                  * the new set
1187                  */
1188                 FcConfigPatternDel (p, e->field);
1189                 FcConfigPatternAdd (p, e->field, l, FcTrue);
1190                 /*
1191                  * Adjust any pointers into the value list as they no
1192                  * longer point to anything valid
1193                  */
1194                 if (t)
1195                 {
1196                     FcPatternElt    *thisElt = st[i].elt;
1197                     for (t = s->test, i = 0; t; t = t->next, i++)
1198                     {
1199                         if (st[i].elt == thisElt)
1200                             st[i].value = 0;
1201                     }
1202                 }
1203                 break;
1204             case FcOpPrepend:
1205                 if (t)
1206                 {
1207                     FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1208                     break;
1209                 }
1210                 /* fall through ... */
1211             case FcOpPrependFirst:
1212                 FcConfigPatternAdd (p, e->field, l, FcFalse);
1213                 break;
1214             case FcOpAppend:
1215                 if (t)
1216                 {
1217                     FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1218                     break;
1219                 }
1220                 /* fall through ... */
1221             case FcOpAppendLast:
1222                 FcConfigPatternAdd (p, e->field, l, FcTrue);
1223                 break;
1224             default:
1225                 break;
1226             }
1227         }
1228         /*
1229          * Now go through the pattern and eliminate
1230          * any properties without data
1231          */
1232         for (e = s->edit; e; e = e->next)
1233             FcConfigPatternCanon (p, e->field);
1234
1235         if (FcDebug () & FC_DBG_EDIT)
1236         {
1237             printf ("FcConfigSubstitute edit");
1238             FcPatternPrint (p);
1239         }
1240     }
1241     FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1242     free (st);
1243     if (FcDebug () & FC_DBG_EDIT)
1244     {
1245         printf ("FcConfigSubstitute done");
1246         FcPatternPrint (p);
1247     }
1248     return FcTrue;
1249 }
1250
1251 FcBool
1252 FcConfigSubstitute (FcConfig    *config,
1253                     FcPattern   *p,
1254                     FcMatchKind kind)
1255 {
1256     return FcConfigSubstituteWithPat (config, p, 0, kind);
1257 }
1258
1259 #ifndef FONTCONFIG_PATH
1260 #define FONTCONFIG_PATH "/etc/fonts"
1261 #endif
1262
1263 #ifndef FONTCONFIG_FILE
1264 #define FONTCONFIG_FILE "fonts.conf"
1265 #endif
1266
1267 static FcChar8 *
1268 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1269 {
1270     FcChar8    *path;
1271
1272     if (!dir)
1273         dir = (FcChar8 *) "";
1274     path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1275     if (!path)
1276         return 0;
1277
1278     strcpy ((char *) path, (const char *) dir);
1279     /* make sure there's a single separating / */
1280     if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1281         strcat ((char *) path, "/");
1282     strcat ((char *) path, (char *) file);
1283
1284     FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1285     if (access ((char *) path, R_OK) == 0)
1286         return path;
1287     
1288     FcStrFree (path);
1289     return 0;
1290 }
1291
1292 static FcChar8 **
1293 FcConfigGetPath (void)
1294 {
1295     FcChar8    **path;
1296     FcChar8    *env, *e, *colon;
1297     FcChar8    *dir;
1298     int     npath;
1299     int     i;
1300
1301     npath = 2;  /* default dir + null */
1302     env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1303     if (env)
1304     {
1305         e = env;
1306         npath++;
1307         while (*e)
1308             if (*e++ == ':')
1309                 npath++;
1310     }
1311     path = calloc (npath, sizeof (FcChar8 *));
1312     if (!path)
1313         goto bail0;
1314     i = 0;
1315
1316     if (env)
1317     {
1318         e = env;
1319         while (*e) 
1320         {
1321             colon = (FcChar8 *) strchr ((char *) e, ':');
1322             if (!colon)
1323                 colon = e + strlen ((char *) e);
1324             path[i] = malloc (colon - e + 1);
1325             if (!path[i])
1326                 goto bail1;
1327             strncpy ((char *) path[i], (const char *) e, colon - e);
1328             path[i][colon - e] = '\0';
1329             if (*colon)
1330                 e = colon + 1;
1331             else
1332                 e = colon;
1333             i++;
1334         }
1335     }
1336     
1337     dir = (FcChar8 *) FONTCONFIG_PATH;
1338     path[i] = malloc (strlen ((char *) dir) + 1);
1339     if (!path[i])
1340         goto bail1;
1341     strcpy ((char *) path[i], (const char *) dir);
1342     return path;
1343
1344 bail1:
1345     for (i = 0; path[i]; i++)
1346         free (path[i]);
1347     free (path);
1348 bail0:
1349     return 0;
1350 }
1351
1352 static void
1353 FcConfigFreePath (FcChar8 **path)
1354 {
1355     FcChar8    **p;
1356
1357     for (p = path; *p; p++)
1358         free (*p);
1359     free (path);
1360 }
1361
1362 FcChar8 *
1363 FcConfigFilename (const FcChar8 *url)
1364 {
1365     FcChar8    *file, *dir, **path, **p;
1366     
1367     if (!url || !*url)
1368     {
1369         url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1370         if (!url)
1371             url = (FcChar8 *) FONTCONFIG_FILE;
1372     }
1373     file = 0;
1374     switch (*url) {
1375     case '~':
1376         dir = (FcChar8 *) getenv ("HOME");
1377         if (dir)
1378             file = FcConfigFileExists (dir, url + 1);
1379         else
1380             file = 0;
1381         break;
1382     case '/':
1383         file = FcConfigFileExists (0, url);
1384         break;
1385     default:
1386         path = FcConfigGetPath ();
1387         if (!path)
1388             return 0;
1389         for (p = path; *p; p++)
1390         {
1391             file = FcConfigFileExists (*p, url);
1392             if (file)
1393                 break;
1394         }
1395         FcConfigFreePath (path);
1396         break;
1397     }
1398     return file;
1399 }
1400
1401 /*
1402  * Manage the application-specific fonts
1403  */
1404
1405 FcBool
1406 FcConfigAppFontAddFile (FcConfig    *config,
1407                         const FcChar8  *file)
1408 {
1409     FcFontSet   *set;
1410     FcStrSet    *subdirs;
1411     FcStrList   *sublist;
1412     FcChar8     *subdir;
1413
1414     if (!config)
1415     {
1416         config = FcConfigGetCurrent ();
1417         if (!config)
1418             return FcFalse;
1419     }
1420
1421     subdirs = FcStrSetCreate ();
1422     if (!subdirs)
1423         return FcFalse;
1424     
1425     set = FcConfigGetFonts (config, FcSetApplication);
1426     if (!set)
1427     {
1428         set = FcFontSetCreate ();
1429         if (!set)
1430         {
1431             FcStrSetDestroy (subdirs);
1432             return FcFalse;
1433         }
1434         FcConfigSetFonts (config, set, FcSetApplication);
1435     }
1436         
1437     if (!FcFileScan (set, subdirs, 0, config->blanks, file, FcFalse))
1438     {
1439         FcStrSetDestroy (subdirs);
1440         return FcFalse;
1441     }
1442     if ((sublist = FcStrListCreate (subdirs)))
1443     {
1444         while ((subdir = FcStrListNext (sublist)))
1445         {
1446             FcConfigAppFontAddDir (config, subdir);
1447         }
1448         FcStrListDone (sublist);
1449     }
1450     return FcTrue;
1451 }
1452
1453 FcBool
1454 FcConfigAppFontAddDir (FcConfig     *config,
1455                        const FcChar8   *dir)
1456 {
1457     FcFontSet   *set;
1458     FcStrSet    *subdirs;
1459     FcStrList   *sublist;
1460     FcChar8     *subdir;
1461     
1462     if (!config)
1463     {
1464         config = FcConfigGetCurrent ();
1465         if (!config)
1466             return FcFalse;
1467     }
1468     subdirs = FcStrSetCreate ();
1469     if (!subdirs)
1470         return FcFalse;
1471     
1472     set = FcConfigGetFonts (config, FcSetApplication);
1473     if (!set)
1474     {
1475         set = FcFontSetCreate ();
1476         if (!set)
1477         {
1478             FcStrSetDestroy (subdirs);
1479             return FcFalse;
1480         }
1481         FcConfigSetFonts (config, set, FcSetApplication);
1482     }
1483     
1484     if (!FcDirScan (set, subdirs, 0, config->blanks, dir, FcFalse))
1485     {
1486         FcStrSetDestroy (subdirs);
1487         return FcFalse;
1488     }
1489     if ((sublist = FcStrListCreate (subdirs)))
1490     {
1491         while ((subdir = FcStrListNext (sublist)))
1492         {
1493             FcConfigAppFontAddDir (config, subdir);
1494         }
1495         FcStrListDone (sublist);
1496     }
1497     return FcTrue;
1498 }
1499
1500 void
1501 FcConfigAppFontClear (FcConfig      *config)
1502 {
1503     FcConfigSetFonts (config, 0, FcSetApplication);
1504 }