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