]> git.wh0rd.org Git - fontconfig.git/blob - src/fccfg.c
2ec9f779eea385fb10afd165dacc40ee8e172a91
[fontconfig.git] / src / fccfg.c
1 /*
2  * $XFree86: xc/lib/fontconfig/src/fccfg.c,v 1.20 2002/08/19 19:32:05 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     if (kind == FcMatchPattern)
433         prev = &config->substPattern;
434     else
435         prev = &config->substFont;
436     for (; *prev; prev = &(*prev)->next);
437     *prev = subst;
438     subst->next = 0;
439     subst->test = test;
440     subst->edit = edit;
441     num = 0;
442     for (t = test; t; t = t->next)
443     {
444         if (t->kind == FcMatchDefault)
445             t->kind = kind;
446         num++;
447     }
448     if (config->maxObjects < num)
449         config->maxObjects = num;
450     if (FcDebug () & FC_DBG_EDIT)
451     {
452         printf ("Add Subst ");
453         FcSubstPrint (subst);
454     }
455     return FcTrue;
456 }
457
458 typedef struct _FcSubState {
459     FcPatternElt   *elt;
460     FcValueList    *value;
461 } FcSubState;
462
463 static FcValue
464 FcConfigPromote (FcValue v, FcValue u)
465 {
466     if (v.type == FcTypeInteger)
467     {
468         v.type = FcTypeDouble;
469         v.u.d = (double) v.u.i;
470     }
471     else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
472     {
473         v.u.m = &FcIdentityMatrix;
474         v.type = FcTypeMatrix;
475     }
476     else if (v.type == FcTypeString && u.type == FcTypeLangSet)
477     {
478         v.u.l = FcLangSetPromote (v.u.s);
479         v.type = FcTypeLangSet;
480     }
481     return v;
482 }
483
484 FcBool
485 FcConfigCompareValue (FcValue   m,
486                       FcOp      op,
487                       FcValue   v)
488 {
489     FcBool    ret = FcFalse;
490     
491     m = FcConfigPromote (m, v);
492     v = FcConfigPromote (v, m);
493     if (m.type == v.type) 
494     {
495         switch (m.type) {
496         case FcTypeInteger:
497             break;      /* FcConfigPromote prevents this from happening */
498         case FcTypeDouble:
499             switch (op) {
500             case FcOpEqual:
501             case FcOpContains:
502                 ret = m.u.d == v.u.d;
503                 break;
504             case FcOpNotEqual:    
505                 ret = m.u.d != v.u.d;
506                 break;
507             case FcOpLess:    
508                 ret = m.u.d < v.u.d;
509                 break;
510             case FcOpLessEqual:    
511                 ret = m.u.d <= v.u.d;
512                 break;
513             case FcOpMore:    
514                 ret = m.u.d > v.u.d;
515                 break;
516             case FcOpMoreEqual:    
517                 ret = m.u.d >= v.u.d;
518                 break;
519             default:
520                 break;
521             }
522             break;
523         case FcTypeBool:
524             switch (op) {
525             case FcOpEqual:    
526             case FcOpContains:
527                 ret = m.u.b == v.u.b;
528                 break;
529             case FcOpNotEqual:    
530                 ret = m.u.b != v.u.b;
531                 break;
532             default:
533                 break;
534             }
535             break;
536         case FcTypeString:
537             switch (op) {
538             case FcOpEqual:    
539             case FcOpContains:
540                 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) == 0;
541                 break;
542             case FcOpNotEqual:    
543                 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) != 0;
544                 break;
545             default:
546                 break;
547             }
548             break;
549         case FcTypeMatrix:
550             switch (op) {
551             case FcOpEqual:
552             case FcOpContains:
553                 ret = FcMatrixEqual (m.u.m, v.u.m);
554                 break;
555             case FcOpNotEqual:
556                 ret = !FcMatrixEqual (m.u.m, v.u.m);
557                 break;
558             default:
559                 break;
560             }
561             break;
562         case FcTypeCharSet:
563             switch (op) {
564             case FcOpContains:
565                 /* m contains v if v is a subset of m */
566                 ret = FcCharSetIsSubset (v.u.c, m.u.c);
567                 break;
568             case FcOpEqual:
569                 ret = FcCharSetEqual (m.u.c, v.u.c);
570                 break;
571             case FcOpNotEqual:
572                 ret = !FcCharSetEqual (m.u.c, v.u.c);
573                 break;
574             default:
575                 break;
576             }
577             break;
578         case FcTypeLangSet:
579             switch (op) {
580             case FcOpContains:
581                 ret = FcLangSetCompare (v.u.l, m.u.l) != FcLangDifferentLang;
582                 break;
583             case FcOpEqual:
584                 ret = FcLangSetEqual (v.u.l, m.u.l);
585                 break;
586             case FcOpNotEqual:
587                 ret = !FcLangSetEqual (v.u.l, m.u.l);
588                 break;
589             default:
590                 break;
591             }
592             break;
593         case FcTypeVoid:
594             switch (op) {
595             case FcOpEqual:
596             case FcOpContains:
597                 ret = FcTrue;
598                 break;
599             default:
600                 break;
601             }
602             break;
603         case FcTypeFTFace:
604             switch (op) {
605             case FcOpEqual:
606                 ret = m.u.f == v.u.f;
607                 break;
608             case FcOpNotEqual:
609                 ret = m.u.f != v.u.f;
610                 break;
611             default:
612                 break;
613             }
614             break;
615         }
616     }
617     else
618     {
619         if (op == FcOpNotEqual)
620             ret = FcTrue;
621     }
622     return ret;
623 }
624
625
626 static FcValue
627 FcConfigEvaluate (FcPattern *p, FcExpr *e)
628 {
629     FcValue     v, vl, vr;
630     FcResult    r;
631     FcMatrix    *m;
632     
633     switch (e->op) {
634     case FcOpInteger:
635         v.type = FcTypeInteger;
636         v.u.i = e->u.ival;
637         break;
638     case FcOpDouble:
639         v.type = FcTypeDouble;
640         v.u.d = e->u.dval;
641         break;
642     case FcOpString:
643         v.type = FcTypeString;
644         v.u.s = e->u.sval;
645         v = FcValueSave (v);
646         break;
647     case FcOpMatrix:
648         v.type = FcTypeMatrix;
649         v.u.m = e->u.mval;
650         v = FcValueSave (v);
651         break;
652     case FcOpCharSet:
653         v.type = FcTypeCharSet;
654         v.u.c = e->u.cval;
655         v = FcValueSave (v);
656         break;
657     case FcOpBool:
658         v.type = FcTypeBool;
659         v.u.b = e->u.bval;
660         break;
661     case FcOpField:
662         r = FcPatternGet (p, e->u.field, 0, &v);
663         if (r != FcResultMatch)
664             v.type = FcTypeVoid;
665         break;
666     case FcOpConst:
667         if (FcNameConstant (e->u.constant, &v.u.i))
668             v.type = FcTypeInteger;
669         else
670             v.type = FcTypeVoid;
671         break;
672     case FcOpQuest:
673         vl = FcConfigEvaluate (p, e->u.tree.left);
674         if (vl.type == FcTypeBool)
675         {
676             if (vl.u.b)
677                 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
678             else
679                 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
680         }
681         else
682             v.type = FcTypeVoid;
683         FcValueDestroy (vl);
684         break;
685     case FcOpContains:
686     case FcOpNotEqual:
687     case FcOpLess:
688     case FcOpLessEqual:
689     case FcOpMore:
690     case FcOpMoreEqual:
691         vl = FcConfigEvaluate (p, e->u.tree.left);
692         vr = FcConfigEvaluate (p, e->u.tree.right);
693         v.type = FcTypeBool;
694         v.u.b = FcConfigCompareValue (vl, e->op, vr);
695         FcValueDestroy (vl);
696         FcValueDestroy (vr);
697         break;  
698     case FcOpOr:
699     case FcOpAnd:
700     case FcOpEqual:
701     case FcOpPlus:
702     case FcOpMinus:
703     case FcOpTimes:
704     case FcOpDivide:
705         vl = FcConfigEvaluate (p, e->u.tree.left);
706         vr = FcConfigEvaluate (p, e->u.tree.right);
707         vl = FcConfigPromote (vl, vr);
708         vr = FcConfigPromote (vr, vl);
709         if (vl.type == vr.type)
710         {
711             switch (vl.type) {
712             case FcTypeDouble:
713                 switch (e->op) {
714                 case FcOpPlus:     
715                     v.type = FcTypeDouble;
716                     v.u.d = vl.u.d + vr.u.d; 
717                     break;
718                 case FcOpMinus:
719                     v.type = FcTypeDouble;
720                     v.u.d = vl.u.d - vr.u.d; 
721                     break;
722                 case FcOpTimes:
723                     v.type = FcTypeDouble;
724                     v.u.d = vl.u.d * vr.u.d; 
725                     break;
726                 case FcOpDivide:
727                     v.type = FcTypeDouble;
728                     v.u.d = vl.u.d / vr.u.d; 
729                     break;
730                 default:
731                     v.type = FcTypeVoid; 
732                     break;
733                 }
734                 if (v.type == FcTypeDouble &&
735                     v.u.d == (double) (int) v.u.d)
736                 {
737                     v.type = FcTypeInteger;
738                     v.u.i = (int) v.u.d;
739                 }
740                 break;
741             case FcTypeBool:
742                 switch (e->op) {
743                 case FcOpOr:
744                     v.type = FcTypeBool;
745                     v.u.b = vl.u.b || vr.u.b;
746                     break;
747                 case FcOpAnd:
748                     v.type = FcTypeBool;
749                     v.u.b = vl.u.b && vr.u.b;
750                     break;
751                 default:
752                     v.type = FcTypeVoid; 
753                     break;
754                 }
755                 break;
756             case FcTypeString:
757                 switch (e->op) {
758                 case FcOpPlus:
759                     v.type = FcTypeString;
760                     v.u.s = FcStrPlus (vl.u.s, vr.u.s);
761                     if (!v.u.s)
762                         v.type = FcTypeVoid;
763                     break;
764                 default:
765                     v.type = FcTypeVoid;
766                     break;
767                 }
768                 break;
769             case FcTypeMatrix:
770                 switch (e->op) {
771                 case FcOpTimes:
772                     v.type = FcTypeMatrix;
773                     m = malloc (sizeof (FcMatrix));
774                     if (m)
775                     {
776                         FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
777                         FcMatrixMultiply (m, vl.u.m, vr.u.m);
778                         v.u.m = m;
779                     }
780                     else
781                     {
782                         v.type = FcTypeVoid;
783                     }
784                     break;
785                 default:
786                     v.type = FcTypeVoid;
787                     break;
788                 }
789                 break;
790             default:
791                 v.type = FcTypeVoid;
792                 break;
793             }
794         }
795         else
796             v.type = FcTypeVoid;
797         FcValueDestroy (vl);
798         FcValueDestroy (vr);
799         break;
800     case FcOpNot:
801         vl = FcConfigEvaluate (p, e->u.tree.left);
802         switch (vl.type) {
803         case FcTypeBool:
804             v.type = FcTypeBool;
805             v.u.b = !vl.u.b;
806             break;
807         default:
808             v.type = FcTypeVoid;
809             break;
810         }
811         FcValueDestroy (vl);
812         break;
813     default:
814         v.type = FcTypeVoid;
815         break;
816     }
817     return v;
818 }
819
820 static FcValueList *
821 FcConfigMatchValueList (FcPattern       *p,
822                         FcTest          *t,
823                         FcValueList     *values)
824 {
825     FcValueList     *ret = 0;
826     FcExpr          *e = t->expr;
827     FcValue         value;
828     FcValueList     *v;
829     
830     while (e)
831     {
832         if (e->op == FcOpComma)
833         {
834             value = FcConfigEvaluate (p, e->u.tree.left);
835             e = e->u.tree.right;
836         }
837         else
838         {
839             value = FcConfigEvaluate (p, e);
840             e = 0;
841         }
842
843         for (v = values; v; v = v->next)
844         {
845             if (FcConfigCompareValue (v->value, t->op, value))
846             {
847                 if (!ret)
848                     ret = v;
849             }
850             else
851             {
852                 if (t->qual == FcQualAll)
853                 {
854                     ret = 0;
855                     break;
856                 }
857             }
858         }
859         FcValueDestroy (value);
860     }
861     return ret;
862 }
863
864 static FcValueList *
865 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
866 {
867     FcValueList *l;
868     
869     if (!e)
870         return 0;
871     l = (FcValueList *) malloc (sizeof (FcValueList));
872     if (!l)
873         return 0;
874     FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
875     if (e->op == FcOpComma)
876     {
877         l->value = FcConfigEvaluate (p, e->u.tree.left);
878         l->next  = FcConfigValues (p, e->u.tree.right, binding);
879     }
880     else
881     {
882         l->value = FcConfigEvaluate (p, e);
883         l->next  = 0;
884     }
885     l->binding = binding;
886     while (l && l->value.type == FcTypeVoid)
887     {
888         FcValueList     *next = l->next;
889         
890         FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
891         free (l);
892         l = next;
893     }
894     return l;
895 }
896
897 static FcBool
898 FcConfigAdd (FcValueList    **head,
899              FcValueList    *position,
900              FcBool         append,
901              FcValueList    *new)
902 {
903     FcValueList    **prev, *last;
904     
905     if (append)
906     {
907         if (position)
908             prev = &position->next;
909         else
910             for (prev = head; *prev; prev = &(*prev)->next)
911                 ;
912     }
913     else
914     {
915         if (position)
916         {
917             for (prev = head; *prev; prev = &(*prev)->next)
918             {
919                 if (*prev == position)
920                     break;
921             }
922         }
923         else
924             prev = head;
925
926         if (FcDebug () & FC_DBG_EDIT)
927         {
928             if (!*prev)
929                 printf ("position not on list\n");
930         }
931     }
932
933     if (FcDebug () & FC_DBG_EDIT)
934     {
935         printf ("%s list before ", append ? "Append" : "Prepend");
936         FcValueListPrint (*head);
937         printf ("\n");
938     }
939     
940     if (new)
941     {
942         last = new;
943         while (last->next)
944             last = last->next;
945     
946         last->next = *prev;
947         *prev = new;
948     }
949     
950     if (FcDebug () & FC_DBG_EDIT)
951     {
952         printf ("%s list after ", append ? "Append" : "Prepend");
953         FcValueListPrint (*head);
954         printf ("\n");
955     }
956     
957     return FcTrue;
958 }
959
960 static void
961 FcConfigDel (FcValueList    **head,
962              FcValueList    *position)
963 {
964     FcValueList    **prev;
965
966     for (prev = head; *prev; prev = &(*prev)->next)
967     {
968         if (*prev == position)
969         {
970             *prev = position->next;
971             position->next = 0;
972             FcValueListDestroy (position);
973             break;
974         }
975     }
976 }
977
978 static void
979 FcConfigPatternAdd (FcPattern   *p,
980                     const char  *object,
981                     FcValueList *list,
982                     FcBool      append)
983 {
984     if (list)
985     {
986         FcPatternElt    *e = FcPatternInsertElt (p, object);
987     
988         if (!e)
989             return;
990         FcConfigAdd (&e->values, 0, append, list);
991     }
992 }
993
994 /*
995  * Delete all values associated with a field
996  */
997 static void
998 FcConfigPatternDel (FcPattern   *p,
999                     const char  *object)
1000 {
1001     FcPatternElt    *e = FcPatternFindElt (p, object);
1002     if (!e)
1003         return;
1004     while (e->values)
1005         FcConfigDel (&e->values, e->values);
1006 }
1007
1008 static void
1009 FcConfigPatternCanon (FcPattern     *p,
1010                       const char    *object)
1011 {
1012     FcPatternElt    *e = FcPatternFindElt (p, object);
1013     if (!e)
1014         return;
1015     if (!e->values)
1016         FcPatternDel (p, object);
1017 }
1018
1019 FcBool
1020 FcConfigSubstituteWithPat (FcConfig    *config,
1021                            FcPattern   *p,
1022                            FcPattern   *p_pat,
1023                            FcMatchKind kind)
1024 {
1025     FcSubst         *s;
1026     FcSubState      *st;
1027     int             i;
1028     FcTest          *t;
1029     FcEdit          *e;
1030     FcValueList     *l;
1031     FcPattern       *m;
1032
1033     if (!config)
1034     {
1035         config = FcConfigGetCurrent ();
1036         if (!config)
1037             return FcFalse;
1038     }
1039
1040     st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1041     if (!st && config->maxObjects)
1042         return FcFalse;
1043     FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1044
1045     if (FcDebug () & FC_DBG_EDIT)
1046     {
1047         printf ("FcConfigSubstitute ");
1048         FcPatternPrint (p);
1049     }
1050     if (kind == FcMatchPattern)
1051         s = config->substPattern;
1052     else
1053         s = config->substFont;
1054     for (; s; s = s->next)
1055     {
1056         /*
1057          * Check the tests to see if
1058          * they all match the pattern
1059          */
1060         for (t = s->test, i = 0; t; t = t->next, i++)
1061         {
1062             if (FcDebug () & FC_DBG_EDIT)
1063             {
1064                 printf ("FcConfigSubstitute test ");
1065                 FcTestPrint (t);
1066             }
1067             st[i].elt = 0;
1068             if (kind == FcMatchFont && t->kind == FcMatchPattern)
1069                 m = p_pat;
1070             else
1071                 m = p;
1072             if (m)
1073                 st[i].elt = FcPatternFindElt (m, t->field);
1074             else
1075                 st[i].elt = 0;
1076             /*
1077              * If there's no such field in the font,
1078              * then FcQualAll matches while FcQualAny does not
1079              */
1080             if (!st[i].elt)
1081             {
1082                 if (t->qual == FcQualAll)
1083                 {
1084                     st[i].value = 0;
1085                     continue;
1086                 }
1087                 else
1088                     break;
1089             }
1090             /*
1091              * Check to see if there is a match, mark the location
1092              * to apply match-relative edits
1093              */
1094             st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1095             if (!st[i].value)
1096                 break;
1097             if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1098                 break;
1099             if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1100                 break;
1101         }
1102         if (t)
1103         {
1104             if (FcDebug () & FC_DBG_EDIT)
1105                 printf ("No match\n");
1106             continue;
1107         }
1108         if (FcDebug () & FC_DBG_EDIT)
1109         {
1110             printf ("Substitute ");
1111             FcSubstPrint (s);
1112         }
1113         for (e = s->edit; e; e = e->next)
1114         {
1115             /*
1116              * Evaluate the list of expressions
1117              */
1118             l = FcConfigValues (p, e->expr, e->binding);
1119             /*
1120              * Locate any test associated with this field, skipping
1121              * tests associated with the pattern when substituting in
1122              * the font
1123              */
1124             for (t = s->test, i = 0; t; t = t->next, i++)
1125             {
1126                 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1127                     !FcStrCmpIgnoreCase ((FcChar8 *) t->field, 
1128                                          (FcChar8 *) e->field))
1129                     break;
1130             }
1131             switch (e->op) {
1132             case FcOpAssign:
1133                 /*
1134                  * If there was a test, then replace the matched
1135                  * value with the new list of values
1136                  */
1137                 if (t)
1138                 {
1139                     FcValueList *thisValue = st[i].value;
1140                     FcValueList *nextValue = thisValue ? thisValue->next : 0;
1141                     
1142                     /*
1143                      * Append the new list of values after the current value
1144                      */
1145                     FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1146                     /*
1147                      * Delete the marked value
1148                      */
1149                     FcConfigDel (&st[i].elt->values, thisValue);
1150                     /*
1151                      * Adjust any pointers into the value list to ensure
1152                      * future edits occur at the same place
1153                      */
1154                     for (t = s->test, i = 0; t; t = t->next, i++)
1155                     {
1156                         if (st[i].value == thisValue)
1157                             st[i].value = nextValue;
1158                     }
1159                     break;
1160                 }
1161                 /* fall through ... */
1162             case FcOpAssignReplace:
1163                 /*
1164                  * Delete all of the values and insert
1165                  * the new set
1166                  */
1167                 FcConfigPatternDel (p, e->field);
1168                 FcConfigPatternAdd (p, e->field, l, FcTrue);
1169                 /*
1170                  * Adjust any pointers into the value list as they no
1171                  * longer point to anything valid
1172                  */
1173                 if (t)
1174                 {
1175                     FcPatternElt    *thisElt = st[i].elt;
1176                     for (t = s->test, i = 0; t; t = t->next, i++)
1177                     {
1178                         if (st[i].elt == thisElt)
1179                             st[i].value = 0;
1180                     }
1181                 }
1182                 break;
1183             case FcOpPrepend:
1184                 if (t)
1185                 {
1186                     FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1187                     break;
1188                 }
1189                 /* fall through ... */
1190             case FcOpPrependFirst:
1191                 FcConfigPatternAdd (p, e->field, l, FcFalse);
1192                 break;
1193             case FcOpAppend:
1194                 if (t)
1195                 {
1196                     FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1197                     break;
1198                 }
1199                 /* fall through ... */
1200             case FcOpAppendLast:
1201                 FcConfigPatternAdd (p, e->field, l, FcTrue);
1202                 break;
1203             default:
1204                 break;
1205             }
1206         }
1207         /*
1208          * Now go through the pattern and eliminate
1209          * any properties without data
1210          */
1211         for (e = s->edit; e; e = e->next)
1212             FcConfigPatternCanon (p, e->field);
1213
1214         if (FcDebug () & FC_DBG_EDIT)
1215         {
1216             printf ("FcConfigSubstitute edit");
1217             FcPatternPrint (p);
1218         }
1219     }
1220     FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1221     free (st);
1222     if (FcDebug () & FC_DBG_EDIT)
1223     {
1224         printf ("FcConfigSubstitute done");
1225         FcPatternPrint (p);
1226     }
1227     return FcTrue;
1228 }
1229
1230 FcBool
1231 FcConfigSubstitute (FcConfig    *config,
1232                     FcPattern   *p,
1233                     FcMatchKind kind)
1234 {
1235     return FcConfigSubstituteWithPat (config, p, 0, kind);
1236 }
1237
1238 #ifndef FONTCONFIG_PATH
1239 #define FONTCONFIG_PATH "/etc/fonts"
1240 #endif
1241
1242 #ifndef FONTCONFIG_FILE
1243 #define FONTCONFIG_FILE "fonts.conf"
1244 #endif
1245
1246 static FcChar8 *
1247 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1248 {
1249     FcChar8    *path;
1250
1251     if (!dir)
1252         dir = (FcChar8 *) "";
1253     path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1254     if (!path)
1255         return 0;
1256
1257     strcpy ((char *) path, (const char *) dir);
1258     /* make sure there's a single separating / */
1259     if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1260         strcat ((char *) path, "/");
1261     strcat ((char *) path, (char *) file);
1262
1263     if (access ((char *) path, R_OK) == 0)
1264         return path;
1265     
1266     free (path);
1267     return 0;
1268 }
1269
1270 static FcChar8 **
1271 FcConfigGetPath (void)
1272 {
1273     FcChar8    **path;
1274     FcChar8    *env, *e, *colon;
1275     FcChar8    *dir;
1276     int     npath;
1277     int     i;
1278
1279     npath = 2;  /* default dir + null */
1280     env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1281     if (env)
1282     {
1283         e = env;
1284         npath++;
1285         while (*e)
1286             if (*e++ == ':')
1287                 npath++;
1288     }
1289     path = calloc (npath, sizeof (FcChar8 *));
1290     if (!path)
1291         goto bail0;
1292     i = 0;
1293
1294     if (env)
1295     {
1296         e = env;
1297         while (*e) 
1298         {
1299             colon = (FcChar8 *) strchr ((char *) e, ':');
1300             if (!colon)
1301                 colon = e + strlen ((char *) e);
1302             path[i] = malloc (colon - e + 1);
1303             if (!path[i])
1304                 goto bail1;
1305             strncpy ((char *) path[i], (const char *) e, colon - e);
1306             path[i][colon - e] = '\0';
1307             if (*colon)
1308                 e = colon + 1;
1309             else
1310                 e = colon;
1311             i++;
1312         }
1313     }
1314     
1315     dir = (FcChar8 *) FONTCONFIG_PATH;
1316     path[i] = malloc (strlen ((char *) dir) + 1);
1317     if (!path[i])
1318         goto bail1;
1319     strcpy ((char *) path[i], (const char *) dir);
1320     return path;
1321
1322 bail1:
1323     for (i = 0; path[i]; i++)
1324         free (path[i]);
1325     free (path);
1326 bail0:
1327     return 0;
1328 }
1329
1330 static void
1331 FcConfigFreePath (FcChar8 **path)
1332 {
1333     FcChar8    **p;
1334
1335     for (p = path; *p; p++)
1336         free (*p);
1337     free (path);
1338 }
1339
1340 FcChar8 *
1341 FcConfigFilename (const FcChar8 *url)
1342 {
1343     FcChar8    *file, *dir, **path, **p;
1344     
1345     if (!url || !*url)
1346     {
1347         url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1348         if (!url)
1349             url = (FcChar8 *) FONTCONFIG_FILE;
1350     }
1351     file = 0;
1352     switch (*url) {
1353     case '~':
1354         dir = (FcChar8 *) getenv ("HOME");
1355         if (dir)
1356             file = FcConfigFileExists (dir, url + 1);
1357         else
1358             file = 0;
1359         break;
1360     case '/':
1361         file = FcConfigFileExists (0, url);
1362         break;
1363     default:
1364         path = FcConfigGetPath ();
1365         if (!path)
1366             return 0;
1367         for (p = path; *p; p++)
1368         {
1369             file = FcConfigFileExists (*p, url);
1370             if (file)
1371                 break;
1372         }
1373         FcConfigFreePath (path);
1374         break;
1375     }
1376     return file;
1377 }
1378
1379 /*
1380  * Manage the application-specific fonts
1381  */
1382
1383 FcBool
1384 FcConfigAppFontAddFile (FcConfig    *config,
1385                         const FcChar8  *file)
1386 {
1387     FcFontSet   *set;
1388     FcStrSet    *subdirs;
1389     FcStrList   *sublist;
1390     FcChar8     *subdir;
1391
1392     if (!config)
1393     {
1394         config = FcConfigGetCurrent ();
1395         if (!config)
1396             return FcFalse;
1397     }
1398
1399     subdirs = FcStrSetCreate ();
1400     if (!subdirs)
1401         return FcFalse;
1402     
1403     set = FcConfigGetFonts (config, FcSetApplication);
1404     if (!set)
1405     {
1406         set = FcFontSetCreate ();
1407         if (!set)
1408         {
1409             FcStrSetDestroy (subdirs);
1410             return FcFalse;
1411         }
1412         FcConfigSetFonts (config, set, FcSetApplication);
1413     }
1414         
1415     if (!FcFileScan (set, subdirs, 0, config->blanks, file, FcFalse))
1416     {
1417         FcStrSetDestroy (subdirs);
1418         return FcFalse;
1419     }
1420     if ((sublist = FcStrListCreate (subdirs)))
1421     {
1422         while ((subdir = FcStrListNext (sublist)))
1423         {
1424             FcConfigAppFontAddDir (config, subdir);
1425         }
1426         FcStrListDone (sublist);
1427     }
1428     return FcTrue;
1429 }
1430
1431 FcBool
1432 FcConfigAppFontAddDir (FcConfig     *config,
1433                        const FcChar8   *dir)
1434 {
1435     FcFontSet   *set;
1436     FcStrSet    *subdirs;
1437     FcStrList   *sublist;
1438     FcChar8     *subdir;
1439     
1440     if (!config)
1441     {
1442         config = FcConfigGetCurrent ();
1443         if (!config)
1444             return FcFalse;
1445     }
1446     subdirs = FcStrSetCreate ();
1447     if (!subdirs)
1448         return FcFalse;
1449     
1450     set = FcConfigGetFonts (config, FcSetApplication);
1451     if (!set)
1452     {
1453         set = FcFontSetCreate ();
1454         if (!set)
1455         {
1456             FcStrSetDestroy (subdirs);
1457             return FcFalse;
1458         }
1459         FcConfigSetFonts (config, set, FcSetApplication);
1460     }
1461     
1462     if (!FcDirScan (set, subdirs, 0, config->blanks, dir, FcFalse))
1463     {
1464         FcStrSetDestroy (subdirs);
1465         return FcFalse;
1466     }
1467     if ((sublist = FcStrListCreate (subdirs)))
1468     {
1469         while ((subdir = FcStrListNext (sublist)))
1470         {
1471             FcConfigAppFontAddDir (config, subdir);
1472         }
1473         FcStrListDone (sublist);
1474     }
1475     return FcTrue;
1476 }
1477
1478 void
1479 FcConfigAppFontClear (FcConfig      *config)
1480 {
1481     FcConfigSetFonts (config, 0, FcSetApplication);
1482 }