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