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