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