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