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