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