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