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