]> git.wh0rd.org Git - fontconfig.git/blob - src/fccfg.c
2006-08-04 Keith Packard (keithp@keithp.com) reviewed by: plam
[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 #include <dirent.h>
27 #include <sys/types.h>
28
29 #if defined (_WIN32) && (defined (PIC) || defined (DLL_EXPORT))
30 #define STRICT
31 #include <windows.h>
32 #undef STRICT
33 #endif
34
35 #if defined (_WIN32) && !defined (R_OK)
36 #define R_OK 4
37 #endif
38
39 FcConfig    *_fcConfig;
40
41 FcConfig *
42 FcConfigCreate (void)
43 {
44     FcSetName   set;
45     FcConfig    *config;
46
47     config = malloc (sizeof (FcConfig));
48     if (!config)
49         goto bail0;
50     FcMemAlloc (FC_MEM_CONFIG, sizeof (FcConfig));
51     
52     config->configDirs = FcStrSetCreate ();
53     if (!config->configDirs)
54         goto bail1;
55     
56     config->configFiles = FcStrSetCreate ();
57     if (!config->configFiles)
58         goto bail2;
59     
60     config->fontDirs = FcStrSetCreate ();
61     if (!config->fontDirs)
62         goto bail3;
63     
64     config->acceptGlobs = FcStrSetCreate ();
65     if (!config->acceptGlobs)
66         goto bail4;
67
68     config->rejectGlobs = FcStrSetCreate ();
69     if (!config->rejectGlobs)
70         goto bail5;
71
72     config->acceptPatterns = FcFontSetCreate ();
73     if (!config->acceptPatterns)
74         goto bail6;
75     
76     config->rejectPatterns = FcFontSetCreate ();
77     if (!config->rejectPatterns)
78         goto bail7;
79
80     config->cache = 0;
81     if (FcConfigHome())
82         if (!FcConfigSetCache (config, (FcChar8 *) ("~/" FC_USER_CACHE_FILE)))
83             goto bail8;
84
85 #ifdef _WIN32
86     if (config->cache == 0)
87     {
88         /* If no home, use the temp folder. */
89         FcChar8     dummy[1];
90         int         templen = GetTempPath (1, dummy);
91         FcChar8     *temp = malloc (templen + 1);
92
93         if (temp)
94         {
95             FcChar8 *cache_dir;
96
97             GetTempPath (templen + 1, temp);
98             cache_dir = FcStrPlus (temp, FC_USER_CACHE_FILE);
99             free (temp);
100             if (!FcConfigSetCache (config, cache_dir))
101             {
102                 FcStrFree (cache_dir);
103                 goto bail8;
104             }
105             FcStrFree (cache_dir);
106         }
107     }
108 #endif
109
110     config->cacheDirs = FcStrSetCreate ();
111     if (!config->cacheDirs)
112         goto bail9;
113     
114     config->blanks = 0;
115
116     config->substPattern = 0;
117     config->substFont = 0;
118     config->maxObjects = 0;
119     for (set = FcSetSystem; set <= FcSetApplication; set++)
120         config->fonts[set] = 0;
121
122     config->rescanTime = time(0);
123     config->rescanInterval = 30;    
124     
125     return config;
126
127 bail9:
128     FcStrFree (config->cache);
129 bail8:
130     FcFontSetDestroy (config->rejectPatterns);
131 bail7:
132     FcFontSetDestroy (config->acceptPatterns);
133 bail6:
134     FcStrSetDestroy (config->rejectGlobs);
135 bail5:
136     FcStrSetDestroy (config->acceptGlobs);
137 bail4:
138     FcStrSetDestroy (config->fontDirs);
139 bail3:
140     FcStrSetDestroy (config->configFiles);
141 bail2:
142     FcStrSetDestroy (config->configDirs);
143 bail1:
144     free (config);
145     FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
146 bail0:
147     return 0;
148 }
149
150 static FcFileTime
151 FcConfigNewestFile (FcStrSet *files)
152 {
153     FcStrList       *list = FcStrListCreate (files);
154     FcFileTime      newest = { 0, FcFalse };
155     FcChar8         *file;
156     struct  stat    statb;
157
158     if (list)
159     {
160         while ((file = FcStrListNext (list)))
161             if (stat ((char *) file, &statb) == 0)
162                 if (!newest.set || statb.st_mtime - newest.time > 0)
163                 {
164                     newest.set = FcTrue;
165                     newest.time = statb.st_mtime;
166                 }
167         FcStrListDone (list);
168     }
169     return newest;
170 }
171
172 FcFileTime
173 FcConfigModifiedTime (FcConfig *config)
174 {
175     if (!config)
176     {
177         FcFileTime v = { 0, FcFalse };
178         config = FcConfigGetCurrent ();
179         if (!config)
180             return v;
181     }
182     return FcConfigNewestFile (config->configFiles);
183 }
184
185 FcBool
186 FcConfigUptoDate (FcConfig *config)
187 {
188     FcFileTime  config_time, font_time;
189     time_t      now = time(0);
190     if (!config)
191     {
192         config = FcConfigGetCurrent ();
193         if (!config)
194             return FcFalse;
195     }
196     config_time = FcConfigNewestFile (config->configFiles);
197     font_time = FcConfigNewestFile (config->fontDirs);
198     if ((config_time.set && config_time.time - config->rescanTime > 0) ||
199         (font_time.set && (font_time.time - config->rescanTime) > 0))
200     {
201         return FcFalse;
202     }
203     config->rescanTime = now;
204     return FcTrue;
205 }
206
207 static void
208 FcSubstDestroy (FcSubst *s)
209 {
210     FcSubst *n;
211     
212     while (s)
213     {
214         n = s->next;
215         if (s->test)
216             FcTestDestroy (s->test);
217         if (s->edit)
218             FcEditDestroy (s->edit);
219         free (s);
220         FcMemFree (FC_MEM_SUBST, sizeof (FcSubst));
221         s = n;
222     }
223 }
224
225 void
226 FcConfigDestroy (FcConfig *config)
227 {
228     FcSetName   set;
229
230     if (config == _fcConfig)
231         _fcConfig = 0;
232
233     FcStrSetDestroy (config->configDirs);
234     FcStrSetDestroy (config->fontDirs);
235     FcStrSetDestroy (config->cacheDirs);
236     FcStrSetDestroy (config->configFiles);
237     FcStrSetDestroy (config->acceptGlobs);
238     FcStrSetDestroy (config->rejectGlobs);
239     FcFontSetDestroy (config->acceptPatterns);
240     FcFontSetDestroy (config->rejectPatterns);
241
242     if (config->blanks)
243         FcBlanksDestroy (config->blanks);
244
245     if (config->cache)
246         FcStrFree (config->cache);
247
248     FcSubstDestroy (config->substPattern);
249     FcSubstDestroy (config->substFont);
250     for (set = FcSetSystem; set <= FcSetApplication; set++)
251         if (config->fonts[set])
252             FcFontSetDestroy (config->fonts[set]);
253
254     free (config);
255     FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
256 }
257
258 /*
259  * Scan the current list of directories in the configuration
260  * and build the set of available fonts. Update the
261  * per-user cache file to reflect the new configuration
262  */
263
264 FcBool
265 FcConfigBuildFonts (FcConfig *config)
266 {
267     FcFontSet       *fonts, *cached_fonts;
268     FcGlobalCache   *cache;
269     FcStrList       *list;
270     FcStrSet        *oldDirs;
271     FcChar8         *dir;
272
273     fonts = FcFontSetCreate ();
274     if (!fonts)
275         goto bail0;
276     
277     cache = FcGlobalCacheCreate ();
278     if (!cache)
279         goto bail1;
280
281     oldDirs = FcStrSetCreate ();
282     if (!oldDirs)
283         goto bail2;
284
285     if (config->cache)
286         FcGlobalCacheLoad (cache, oldDirs, config->cache, config);
287
288     cached_fonts = FcCacheRead(config, cache);
289     if (!cached_fonts)
290     {
291         list = FcConfigGetFontDirs (config);
292         if (!list)
293             goto bail3;
294         
295         while ((dir = FcStrListNext (list)))
296         {
297             if (FcDebug () & FC_DBG_FONTSET)
298                 printf ("build scan dir %s\n", dir);
299             FcDirScanConfig (fonts, config->fontDirs, cache, 
300                              config->blanks, dir, FcFalse, config);
301         }
302         
303         FcStrListDone (list);
304     }
305     else
306     {
307         int i;
308
309         for (i = 0; i < oldDirs->num; i++)
310         {
311             if (FcDebug () & FC_DBG_FONTSET)
312                 printf ("scan dir %s\n", oldDirs->strs[i]);
313             FcDirScanConfig (fonts, config->fontDirs, cache, 
314                              config->blanks, oldDirs->strs[i], 
315                              FcFalse, config);
316         }
317
318         for (i = 0; i < cached_fonts->nfont; i++)
319         {
320             FcChar8     *cfn; 
321             FcPatternGetString (cached_fonts->fonts[i], FC_FILE, 0, &cfn);
322
323             if (FcConfigAcceptFont (config, cached_fonts->fonts[i]) &&
324                 (cfn && FcConfigAcceptFilename (config, cfn)))
325                 FcFontSetAdd (fonts, cached_fonts->fonts[i]);
326
327             cached_fonts->fonts[i] = 0; /* prevent free in FcFontSetDestroy */
328         }
329         cached_fonts->nfont = 0;
330         FcFontSetDestroy (cached_fonts);
331     }
332     
333     if (FcDebug () & FC_DBG_FONTSET)
334         FcFontSetPrint (fonts);
335
336     if (config->cache)
337         FcGlobalCacheSave (cache, config->cache, config);
338     FcGlobalCacheDestroy (cache);
339     FcStrSetDestroy (oldDirs);
340
341     FcConfigSetFonts (config, fonts, FcSetSystem);
342     
343     return FcTrue;
344 bail3:
345     FcStrSetDestroy (oldDirs);
346 bail2:
347     FcGlobalCacheDestroy (cache);
348 bail1:
349     FcFontSetDestroy (fonts);
350 bail0:
351     return FcFalse;
352 }
353
354 FcBool
355 FcConfigSetCurrent (FcConfig *config)
356 {
357     if (!config->fonts)
358         if (!FcConfigBuildFonts (config))
359             return FcFalse;
360
361     if (_fcConfig)
362         FcConfigDestroy (_fcConfig);
363     _fcConfig = config;
364     return FcTrue;
365 }
366
367 FcConfig *
368 FcConfigGetCurrent (void)
369 {
370     if (!_fcConfig)
371         if (!FcInit ())
372             return 0;
373     return _fcConfig;
374 }
375
376 FcBool
377 FcConfigAddConfigDir (FcConfig      *config,
378                       const FcChar8 *d)
379 {
380     return FcStrSetAddFilename (config->configDirs, d);
381 }
382
383 FcStrList *
384 FcConfigGetConfigDirs (FcConfig   *config)
385 {
386     if (!config)
387     {
388         config = FcConfigGetCurrent ();
389         if (!config)
390             return 0;
391     }
392     return FcStrListCreate (config->configDirs);
393 }
394
395 static FcChar8 *
396 FcConfigInodeMatchFontDir (FcConfig *config, const FcChar8 *d)
397 {
398     int         n;
399     ino_t       di;
400     dev_t       dd;
401     struct stat s;
402
403     /* first we do string matches rather than file accesses */
404     /* FcStrSetMember doesn't tell us the index so that we can return
405      * the config-owned copy. */
406     for (n = 0; n < config->fontDirs->num; n++)
407     {
408         if (!FcStrCmp (config->fontDirs->strs[n], d))
409             return config->fontDirs->strs[n];
410     }
411
412     /* If this is a bottleneck, we can cache the fontDir inodes. */
413     if (stat ((char *)d, &s) == -1)
414         return 0;
415     di = s.st_ino; dd = s.st_dev;
416
417     for (n = 0; n < config->fontDirs->num; n++)
418     {
419         if (stat ((char *)config->fontDirs->strs[n], &s) == -1)
420             continue;
421         if (di == s.st_ino && dd == s.st_dev)
422             return config->fontDirs->strs[n];
423     }
424     return 0;
425 }
426
427 FcBool
428 FcConfigAddFontDir (FcConfig        *config,
429                     const FcChar8   *d)
430 {
431     /* Avoid adding d if it's an alias of something else, too. */
432     if (FcConfigInodeMatchFontDir(config, d))
433         return FcTrue;
434     return FcStrSetAddFilename (config->fontDirs, d);
435 }
436
437 static FcBool
438 FcConfigAddFontDirSubdirs (FcConfig        *config,
439                            const FcChar8   *d)
440 {
441     DIR *dir;
442     struct dirent *e;
443     FcChar8 *subdir;
444     FcBool added = FcFalse;
445     
446     if (!(dir = opendir ((char *) d)))
447         return FcFalse;
448     if (!(subdir = (FcChar8 *) malloc (strlen ((char *) d) + FC_MAX_FILE_LEN + 2)))
449     {
450         fprintf (stderr, "out of memory");
451         return FcFalse;
452     }
453     while ((e = readdir (dir)))
454     {
455         if (strcmp (e->d_name, ".") && strcmp (e->d_name, "..") &&
456             strlen (e->d_name) < FC_MAX_FILE_LEN)
457         {
458             strcpy ((char *)subdir, (char *)d);
459             strcat ((char *)subdir, "/");
460             strcat ((char *)subdir, e->d_name);
461             if (FcFileIsDir (subdir))
462             {
463                 if (FcConfigInodeMatchFontDir(config, subdir))
464                     continue; /* already added */
465                 FcStrSetAddFilename (config->fontDirs, subdir);
466                 FcConfigAddFontDirSubdirs (config, subdir);
467                 added = FcTrue;
468             }
469         }
470     }
471     free (subdir);
472     closedir (dir);
473     return added;
474 }
475
476 const FcChar8 *
477 FcConfigNormalizeFontDir (FcConfig      *config, 
478                           const FcChar8 *d)
479 {
480     FcChar8     *d0;
481     int         n, n0;
482     FcBool      added = FcFalse;
483
484     d0 = FcConfigInodeMatchFontDir(config, d);
485     if (d0)
486         return d0;
487
488     /* Ok, we didn't find it in fontDirs; let's add subdirs.... */
489     for (n = 0, n0 = config->fontDirs->num; n < n0; n++) 
490     {
491         if (FcConfigAddFontDirSubdirs (config, config->fontDirs->strs[n]))
492             added = FcTrue;
493     }
494
495     /* ... and try again. */
496     if (added)
497         return FcConfigInodeMatchFontDir(config, d);
498
499     return 0;
500 }
501
502 FcBool
503 FcConfigAddDir (FcConfig            *config,
504                 const FcChar8       *d)
505 {
506     return (FcConfigAddConfigDir (config, d) && 
507             FcConfigAddFontDir (config, d));
508 }
509
510 FcStrList *
511 FcConfigGetFontDirs (FcConfig   *config)
512 {
513     if (!config)
514     {
515         config = FcConfigGetCurrent ();
516         if (!config)
517             return 0;
518     }
519     return FcStrListCreate (config->fontDirs);
520 }
521
522 FcBool
523 FcConfigAddCacheDir (FcConfig       *config,
524                      const FcChar8  *d)
525 {
526     return FcStrSetAddFilename (config->cacheDirs, d);
527 }
528
529 FcStrList *
530 FcConfigGetCacheDirs (FcConfig  *config)
531 {
532     if (!config)
533     {
534         config = FcConfigGetCurrent ();
535         if (!config)
536             return 0;
537     }
538     return FcStrListCreate (config->cacheDirs);
539 }
540     
541 FcBool
542 FcConfigAddConfigFile (FcConfig     *config,
543                        const FcChar8   *f)
544 {
545     FcBool      ret;
546     FcChar8     *file = FcConfigFilename (f);
547     
548     if (!file)
549         return FcFalse;
550     
551     ret = FcStrSetAdd (config->configFiles, file);
552     FcStrFree (file);
553     return ret;
554 }
555
556 FcStrList *
557 FcConfigGetConfigFiles (FcConfig    *config)
558 {
559     if (!config)
560     {
561         config = FcConfigGetCurrent ();
562         if (!config)
563             return 0;
564     }
565     return FcStrListCreate (config->configFiles);
566 }
567
568 FcBool
569 FcConfigSetCache (FcConfig      *config,
570                   const FcChar8 *c)
571 {
572     FcChar8    *new = FcStrCopyFilename (c);
573     
574     if (!new)
575         return FcFalse;
576     if (config->cache)
577         FcStrFree (config->cache);
578     config->cache = new;
579     return FcTrue;
580 }
581
582 FcChar8 *
583 FcConfigGetCache (FcConfig  *config)
584 {
585     if (!config)
586     {
587         config = FcConfigGetCurrent ();
588         if (!config)
589             return 0;
590     }
591     return config->cache;
592 }
593
594 FcFontSet *
595 FcConfigGetFonts (FcConfig      *config,
596                   FcSetName     set)
597 {
598     if (!config)
599     {
600         config = FcConfigGetCurrent ();
601         if (!config)
602             return 0;
603     }
604     return config->fonts[set];
605 }
606
607 void
608 FcConfigSetFonts (FcConfig      *config,
609                   FcFontSet     *fonts,
610                   FcSetName     set)
611 {
612     if (config->fonts[set])
613         FcFontSetDestroy (config->fonts[set]);
614     config->fonts[set] = fonts;
615 }
616
617
618
619 FcBlanks *
620 FcConfigGetBlanks (FcConfig     *config)
621 {
622     if (!config)
623     {
624         config = FcConfigGetCurrent ();
625         if (!config)
626             return 0;
627     }
628     return config->blanks;
629 }
630
631 FcBool
632 FcConfigAddBlank (FcConfig      *config,
633                   FcChar32      blank)
634 {
635     FcBlanks    *b, *freeme = 0;
636     
637     b = config->blanks;
638     if (!b)
639     {
640         freeme = b = FcBlanksCreate ();
641         if (!b)
642             return FcFalse;
643     }
644     if (!FcBlanksAdd (b, blank))
645     {
646         if (freeme)
647             FcBlanksDestroy (freeme);
648         return FcFalse;
649     }
650     config->blanks = b;
651     return FcTrue;
652 }
653
654 int
655 FcConfigGetRescanInverval (FcConfig *config)
656 {
657     if (!config)
658     {
659         config = FcConfigGetCurrent ();
660         if (!config)
661             return 0;
662     }
663     return config->rescanInterval;
664 }
665
666 FcBool
667 FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
668 {
669     if (!config)
670     {
671         config = FcConfigGetCurrent ();
672         if (!config)
673             return FcFalse;
674     }
675     config->rescanInterval = rescanInterval;
676     return FcTrue;
677 }
678
679 FcBool
680 FcConfigAddEdit (FcConfig       *config,
681                  FcTest         *test,
682                  FcEdit         *edit,
683                  FcMatchKind    kind)
684 {
685     FcSubst     *subst, **prev;
686     FcTest      *t;
687     int         num;
688
689     subst = (FcSubst *) malloc (sizeof (FcSubst));
690     if (!subst)
691         return FcFalse;
692     FcMemAlloc (FC_MEM_SUBST, sizeof (FcSubst));
693     if (kind == FcMatchPattern)
694         prev = &config->substPattern;
695     else
696         prev = &config->substFont;
697     for (; *prev; prev = &(*prev)->next);
698     *prev = subst;
699     subst->next = 0;
700     subst->test = test;
701     subst->edit = edit;
702     num = 0;
703     for (t = test; t; t = t->next)
704     {
705         if (t->kind == FcMatchDefault)
706             t->kind = kind;
707         num++;
708     }
709     if (config->maxObjects < num)
710         config->maxObjects = num;
711     if (FcDebug () & FC_DBG_EDIT)
712     {
713         printf ("Add Subst ");
714         FcSubstPrint (subst);
715     }
716     return FcTrue;
717 }
718
719 typedef struct _FcSubState {
720     FcPatternElt   *elt;
721     FcValueList    *value;
722 } FcSubState;
723
724 static FcValue
725 FcConfigPromote (FcValue v, FcValue u)
726 {
727     if (v.type == FcTypeInteger)
728     {
729         v.type = FcTypeDouble;
730         v.u.d = (double) v.u.i;
731     }
732     else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
733     {
734         v.u.m = &FcIdentityMatrix;
735         v.type = FcTypeMatrix;
736     }
737     else if (v.type == FcTypeString && u.type == FcTypeLangSet)
738     {
739         v.u.l = FcLangSetPromote (v.u.s);
740         v.type = FcTypeLangSet;
741     }
742     return v;
743 }
744
745 FcBool
746 FcConfigCompareValue (const FcValue     *left_o,
747                       FcOp              op,
748                       const FcValue     *right_o)
749 {
750     FcValue     left = FcValueCanonicalize(left_o);
751     FcValue     right = FcValueCanonicalize(right_o);
752     FcBool      ret = FcFalse;
753     
754     left = FcConfigPromote (left, right);
755     right = FcConfigPromote (right, left);
756     if (left.type == right.type) 
757     {
758         switch (left.type) {
759         case FcTypeInteger:
760             break;      /* FcConfigPromote prevents this from happening */
761         case FcTypeDouble:
762             switch (op) {
763             case FcOpEqual:
764             case FcOpContains:
765             case FcOpListing:
766                 ret = left.u.d == right.u.d;
767                 break;
768             case FcOpNotEqual:
769             case FcOpNotContains:
770                 ret = left.u.d != right.u.d;
771                 break;
772             case FcOpLess:    
773                 ret = left.u.d < right.u.d;
774                 break;
775             case FcOpLessEqual:    
776                 ret = left.u.d <= right.u.d;
777                 break;
778             case FcOpMore:    
779                 ret = left.u.d > right.u.d;
780                 break;
781             case FcOpMoreEqual:    
782                 ret = left.u.d >= right.u.d;
783                 break;
784             default:
785                 break;
786             }
787             break;
788         case FcTypeBool:
789             switch (op) {
790             case FcOpEqual:    
791             case FcOpContains:
792             case FcOpListing:
793                 ret = left.u.b == right.u.b;
794                 break;
795             case FcOpNotEqual:
796             case FcOpNotContains:
797                 ret = left.u.b != right.u.b;
798                 break;
799             default:
800                 break;
801             }
802             break;
803         case FcTypeString:
804             switch (op) {
805             case FcOpEqual:    
806             case FcOpListing:
807                 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
808                 break;
809             case FcOpContains:
810                 ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
811                 break;
812             case FcOpNotEqual:
813                 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
814                 break;
815             case FcOpNotContains:
816                 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
817                 break;
818             default:
819                 break;
820             }
821             break;
822         case FcTypeMatrix:
823             switch (op) {
824             case FcOpEqual:
825             case FcOpContains:
826             case FcOpListing:
827                 ret = FcMatrixEqual (left.u.m, right.u.m);
828                 break;
829             case FcOpNotEqual:
830             case FcOpNotContains:
831                 ret = !FcMatrixEqual (left.u.m, right.u.m);
832                 break;
833             default:
834                 break;
835             }
836             break;
837         case FcTypeCharSet:
838             switch (op) {
839             case FcOpContains:
840             case FcOpListing:
841                 /* left contains right if right is a subset of left */
842                 ret = FcCharSetIsSubset (right.u.c, left.u.c);
843                 break;
844             case FcOpNotContains:
845                 /* left contains right if right is a subset of left */
846                 ret = !FcCharSetIsSubset (right.u.c, left.u.c);
847                 break;
848             case FcOpEqual:
849                 ret = FcCharSetEqual (left.u.c, right.u.c);
850                 break;
851             case FcOpNotEqual:
852                 ret = !FcCharSetEqual (left.u.c, right.u.c);
853                 break;
854             default:
855                 break;
856             }
857             break;
858         case FcTypeLangSet:
859             switch (op) {
860             case FcOpContains:
861             case FcOpListing:
862                 ret = FcLangSetContains (left.u.l, right.u.l);
863                 break;
864             case FcOpNotContains:
865                 ret = !FcLangSetContains (left.u.l, right.u.l);
866                 break;
867             case FcOpEqual:
868                 ret = FcLangSetEqual (left.u.l, right.u.l);
869                 break;
870             case FcOpNotEqual:
871                 ret = !FcLangSetEqual (left.u.l, right.u.l);
872                 break;
873             default:
874                 break;
875             }
876             break;
877         case FcTypeVoid:
878             switch (op) {
879             case FcOpEqual:
880             case FcOpContains:
881             case FcOpListing:
882                 ret = FcTrue;
883                 break;
884             default:
885                 break;
886             }
887             break;
888         case FcTypeFTFace:
889             switch (op) {
890             case FcOpEqual:
891             case FcOpContains:
892             case FcOpListing:
893                 ret = left.u.f == right.u.f;
894                 break;
895             case FcOpNotEqual:
896             case FcOpNotContains:
897                 ret = left.u.f != right.u.f;
898                 break;
899             default:
900                 break;
901             }
902             break;
903         }
904     }
905     else
906     {
907         if (op == FcOpNotEqual || op == FcOpNotContains)
908             ret = FcTrue;
909     }
910     return ret;
911 }
912
913
914 #define _FcDoubleFloor(d)       ((int) (d))
915 #define _FcDoubleCeil(d)        ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
916 #define FcDoubleFloor(d)        ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
917 #define FcDoubleCeil(d)         ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
918 #define FcDoubleRound(d)        FcDoubleFloor ((d) + 0.5)
919 #define FcDoubleTrunc(d)        ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
920
921 static FcValue
922 FcConfigEvaluate (FcPattern *p, FcExpr *e)
923 {
924     FcValue     v, vl, vr;
925     FcResult    r;
926     FcMatrix    *m;
927     FcChar8     *str;
928     
929     switch (e->op) {
930     case FcOpInteger:
931         v.type = FcTypeInteger;
932         v.u.i = e->u.ival;
933         break;
934     case FcOpDouble:
935         v.type = FcTypeDouble;
936         v.u.d = e->u.dval;
937         break;
938     case FcOpString:
939         v.type = FcTypeString;
940         v.u.s = FcStrStaticName(e->u.sval);
941         break;
942     case FcOpMatrix:
943         v.type = FcTypeMatrix;
944         v.u.m = e->u.mval;
945         v = FcValueSave (v);
946         break;
947     case FcOpCharSet:
948         v.type = FcTypeCharSet;
949         v.u.c = e->u.cval;
950         v = FcValueSave (v);
951         break;
952     case FcOpBool:
953         v.type = FcTypeBool;
954         v.u.b = e->u.bval;
955         break;
956     case FcOpField:
957         r = FcPatternGet (p, e->u.field, 0, &v);
958         if (r != FcResultMatch)
959             v.type = FcTypeVoid;
960         v = FcValueSave (v);
961         break;
962     case FcOpConst:
963         if (FcNameConstant (e->u.constant, &v.u.i))
964             v.type = FcTypeInteger;
965         else
966             v.type = FcTypeVoid;
967         break;
968     case FcOpQuest:
969         vl = FcConfigEvaluate (p, e->u.tree.left);
970         if (vl.type == FcTypeBool)
971         {
972             if (vl.u.b)
973                 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
974             else
975                 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
976         }
977         else
978             v.type = FcTypeVoid;
979         FcValueDestroy (vl);
980         break;
981     case FcOpEqual:
982     case FcOpNotEqual:
983     case FcOpLess:
984     case FcOpLessEqual:
985     case FcOpMore:
986     case FcOpMoreEqual:
987     case FcOpContains:
988     case FcOpNotContains:
989     case FcOpListing:
990         vl = FcConfigEvaluate (p, e->u.tree.left);
991         vr = FcConfigEvaluate (p, e->u.tree.right);
992         v.type = FcTypeBool;
993         v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
994         FcValueDestroy (vl);
995         FcValueDestroy (vr);
996         break;  
997     case FcOpOr:
998     case FcOpAnd:
999     case FcOpPlus:
1000     case FcOpMinus:
1001     case FcOpTimes:
1002     case FcOpDivide:
1003         vl = FcConfigEvaluate (p, e->u.tree.left);
1004         vr = FcConfigEvaluate (p, e->u.tree.right);
1005         vl = FcConfigPromote (vl, vr);
1006         vr = FcConfigPromote (vr, vl);
1007         if (vl.type == vr.type)
1008         {
1009             switch (vl.type) {
1010             case FcTypeDouble:
1011                 switch (e->op) {
1012                 case FcOpPlus:     
1013                     v.type = FcTypeDouble;
1014                     v.u.d = vl.u.d + vr.u.d; 
1015                     break;
1016                 case FcOpMinus:
1017                     v.type = FcTypeDouble;
1018                     v.u.d = vl.u.d - vr.u.d; 
1019                     break;
1020                 case FcOpTimes:
1021                     v.type = FcTypeDouble;
1022                     v.u.d = vl.u.d * vr.u.d; 
1023                     break;
1024                 case FcOpDivide:
1025                     v.type = FcTypeDouble;
1026                     v.u.d = vl.u.d / vr.u.d; 
1027                     break;
1028                 default:
1029                     v.type = FcTypeVoid; 
1030                     break;
1031                 }
1032                 if (v.type == FcTypeDouble &&
1033                     v.u.d == (double) (int) v.u.d)
1034                 {
1035                     v.type = FcTypeInteger;
1036                     v.u.i = (int) v.u.d;
1037                 }
1038                 break;
1039             case FcTypeBool:
1040                 switch (e->op) {
1041                 case FcOpOr:
1042                     v.type = FcTypeBool;
1043                     v.u.b = vl.u.b || vr.u.b;
1044                     break;
1045                 case FcOpAnd:
1046                     v.type = FcTypeBool;
1047                     v.u.b = vl.u.b && vr.u.b;
1048                     break;
1049                 default:
1050                     v.type = FcTypeVoid; 
1051                     break;
1052                 }
1053                 break;
1054             case FcTypeString:
1055                 switch (e->op) {
1056                 case FcOpPlus:
1057                     v.type = FcTypeString;
1058                     str = FcStrPlus (vl.u.s, vr.u.s);
1059                     v.u.s = FcStrStaticName (str);
1060                     FcStrFree (str);
1061                          
1062                     if (!v.u.s)
1063                         v.type = FcTypeVoid;
1064                     break;
1065                 default:
1066                     v.type = FcTypeVoid;
1067                     break;
1068                 }
1069                 break;
1070             case FcTypeMatrix:
1071                 switch (e->op) {
1072                 case FcOpTimes:
1073                     v.type = FcTypeMatrix;
1074                     m = malloc (sizeof (FcMatrix));
1075                     if (m)
1076                     {
1077                         FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
1078                         FcMatrixMultiply (m, vl.u.m, vr.u.m);
1079                         v.u.m = m;
1080                     }
1081                     else
1082                     {
1083                         v.type = FcTypeVoid;
1084                     }
1085                     break;
1086                 default:
1087                     v.type = FcTypeVoid;
1088                     break;
1089                 }
1090                 break;
1091             default:
1092                 v.type = FcTypeVoid;
1093                 break;
1094             }
1095         }
1096         else
1097             v.type = FcTypeVoid;
1098         FcValueDestroy (vl);
1099         FcValueDestroy (vr);
1100         break;
1101     case FcOpNot:
1102         vl = FcConfigEvaluate (p, e->u.tree.left);
1103         switch (vl.type) {
1104         case FcTypeBool:
1105             v.type = FcTypeBool;
1106             v.u.b = !vl.u.b;
1107             break;
1108         default:
1109             v.type = FcTypeVoid;
1110             break;
1111         }
1112         FcValueDestroy (vl);
1113         break;
1114     case FcOpFloor:
1115         vl = FcConfigEvaluate (p, e->u.tree.left);
1116         switch (vl.type) {
1117         case FcTypeInteger:
1118             v = vl;
1119             break;
1120         case FcTypeDouble:
1121             v.type = FcTypeInteger;
1122             v.u.i = FcDoubleFloor (vl.u.d);
1123             break;
1124         default:
1125             v.type = FcTypeVoid;
1126             break;
1127         }
1128         FcValueDestroy (vl);
1129         break;
1130     case FcOpCeil:
1131         vl = FcConfigEvaluate (p, e->u.tree.left);
1132         switch (vl.type) {
1133         case FcTypeInteger:
1134             v = vl;
1135             break;
1136         case FcTypeDouble:
1137             v.type = FcTypeInteger;
1138             v.u.i = FcDoubleCeil (vl.u.d);
1139             break;
1140         default:
1141             v.type = FcTypeVoid;
1142             break;
1143         }
1144         FcValueDestroy (vl);
1145         break;
1146     case FcOpRound:
1147         vl = FcConfigEvaluate (p, e->u.tree.left);
1148         switch (vl.type) {
1149         case FcTypeInteger:
1150             v = vl;
1151             break;
1152         case FcTypeDouble:
1153             v.type = FcTypeInteger;
1154             v.u.i = FcDoubleRound (vl.u.d);
1155             break;
1156         default:
1157             v.type = FcTypeVoid;
1158             break;
1159         }
1160         FcValueDestroy (vl);
1161         break;
1162     case FcOpTrunc:
1163         vl = FcConfigEvaluate (p, e->u.tree.left);
1164         switch (vl.type) {
1165         case FcTypeInteger:
1166             v = vl;
1167             break;
1168         case FcTypeDouble:
1169             v.type = FcTypeInteger;
1170             v.u.i = FcDoubleTrunc (vl.u.d);
1171             break;
1172         default:
1173             v.type = FcTypeVoid;
1174             break;
1175         }
1176         FcValueDestroy (vl);
1177         break;
1178     default:
1179         v.type = FcTypeVoid;
1180         break;
1181     }
1182     return v;
1183 }
1184
1185 static FcValueList *
1186 FcConfigMatchValueList (FcPattern       *p,
1187                         FcTest          *t,
1188                         FcValueList     *values)
1189 {
1190     FcValueList     *ret = 0;
1191     FcExpr          *e = t->expr;
1192     FcValue         value;
1193     FcValueList     *v;
1194     
1195     while (e)
1196     {
1197         /* Compute the value of the match expression */
1198         if (e->op == FcOpComma)
1199         {
1200             value = FcConfigEvaluate (p, e->u.tree.left);
1201             e = e->u.tree.right;
1202         }
1203         else
1204         {
1205             value = FcConfigEvaluate (p, e);
1206             e = 0;
1207         }
1208
1209         for (v = values; v; v = FcValueListPtrU(v->next))
1210         {
1211             /* Compare the pattern value to the match expression value */
1212             if (FcConfigCompareValue (&v->value, t->op, &value))
1213             {
1214                 if (!ret)
1215                     ret = v;
1216             }
1217             else
1218             {
1219                 if (t->qual == FcQualAll)
1220                 {
1221                     ret = 0;
1222                     break;
1223                 }
1224             }
1225         }
1226         FcValueDestroy (value);
1227     }
1228     return ret;
1229 }
1230
1231 static FcValueList *
1232 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
1233 {
1234     FcValueList *l;
1235     
1236     if (!e)
1237         return 0;
1238     l = (FcValueList *) malloc (sizeof (FcValueList));
1239     if (!l)
1240         return 0;
1241     FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
1242     if (e->op == FcOpComma)
1243     {
1244         l->value = FcConfigEvaluate (p, e->u.tree.left);
1245         l->next  = FcValueListPtrCreateDynamic(FcConfigValues (p, e->u.tree.right, binding));
1246     }
1247     else
1248     {
1249         l->value = FcConfigEvaluate (p, e);
1250         l->next  = FcValueListPtrCreateDynamic(0);
1251     }
1252     l->binding = binding;
1253     if (l->value.type == FcTypeVoid)
1254     {
1255         FcValueList  *next = FcValueListPtrU(l->next);
1256
1257         FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
1258         free (l);
1259         l = next;
1260     }
1261
1262     return l;
1263 }
1264
1265 static FcBool
1266 FcConfigAdd (FcValueListPtr *head,
1267              FcValueList    *position,
1268              FcBool         append,
1269              FcValueList    *new)
1270 {
1271     FcValueListPtr  *prev, last, v;
1272     FcValueBinding  sameBinding;
1273     
1274     if (position)
1275         sameBinding = position->binding;
1276     else
1277         sameBinding = FcValueBindingWeak;
1278     for (v = FcValueListPtrCreateDynamic(new); FcValueListPtrU(v); 
1279          v = FcValueListPtrU(v)->next)
1280         if (FcValueListPtrU(v)->binding == FcValueBindingSame)
1281             FcValueListPtrU(v)->binding = sameBinding;
1282     if (append)
1283     {
1284         if (position)
1285             prev = &position->next;
1286         else
1287             for (prev = head; FcValueListPtrU(*prev); 
1288                  prev = &(FcValueListPtrU(*prev)->next))
1289                 ;
1290     }
1291     else
1292     {
1293         if (position)
1294         {
1295             for (prev = head; FcValueListPtrU(*prev); 
1296                  prev = &(FcValueListPtrU(*prev)->next))
1297             {
1298                 if (FcValueListPtrU(*prev) == position)
1299                     break;
1300             }
1301         }
1302         else
1303             prev = head;
1304
1305         if (FcDebug () & FC_DBG_EDIT)
1306         {
1307             if (!FcValueListPtrU(*prev))
1308                 printf ("position not on list\n");
1309         }
1310     }
1311
1312     if (FcDebug () & FC_DBG_EDIT)
1313     {
1314         printf ("%s list before ", append ? "Append" : "Prepend");
1315         FcValueListPrint (*head);
1316         printf ("\n");
1317     }
1318     
1319     if (new)
1320     {
1321         last = FcValueListPtrCreateDynamic(new);
1322         while (FcValueListPtrU(FcValueListPtrU(last)->next))
1323             last = FcValueListPtrU(last)->next;
1324     
1325         FcValueListPtrU(last)->next = *prev;
1326         *prev = FcValueListPtrCreateDynamic(new);
1327     }
1328     
1329     if (FcDebug () & FC_DBG_EDIT)
1330     {
1331         printf ("%s list after ", append ? "Append" : "Prepend");
1332         FcValueListPrint (*head);
1333         printf ("\n");
1334     }
1335     
1336     return FcTrue;
1337 }
1338
1339 static void
1340 FcConfigDel (FcValueListPtr *head,
1341              FcValueList    *position)
1342 {
1343     FcValueListPtr *prev;
1344
1345     for (prev = head; FcValueListPtrU(*prev); 
1346          prev = &(FcValueListPtrU(*prev)->next))
1347     {
1348         if (FcValueListPtrU(*prev) == position)
1349         {
1350             *prev = position->next;
1351             position->next = FcValueListPtrCreateDynamic(0);
1352             FcValueListDestroy (FcValueListPtrCreateDynamic(position));
1353             break;
1354         }
1355     }
1356 }
1357
1358 static void
1359 FcConfigPatternAdd (FcPattern   *p,
1360                     const char  *object,
1361                     FcValueList *list,
1362                     FcBool      append)
1363 {
1364     if (list)
1365     {
1366         FcPatternElt    *e = FcPatternInsertElt (p, object);
1367     
1368         if (!e)
1369             return;
1370         FcConfigAdd (&e->values, 0, append, list);
1371     }
1372 }
1373
1374 /*
1375  * Delete all values associated with a field
1376  */
1377 static void
1378 FcConfigPatternDel (FcPattern   *p,
1379                     const char  *object)
1380 {
1381     FcPatternElt    *e = FcPatternFindElt (p, object);
1382     if (!e)
1383         return;
1384     while (FcValueListPtrU(e->values))
1385         FcConfigDel (&e->values, FcValueListPtrU(e->values));
1386 }
1387
1388 static void
1389 FcConfigPatternCanon (FcPattern     *p,
1390                       const char    *object)
1391 {
1392     FcPatternElt    *e = FcPatternFindElt (p, object);
1393     if (!e)
1394         return;
1395     if (!FcValueListPtrU(e->values))
1396         FcPatternDel (p, object);
1397 }
1398
1399 FcBool
1400 FcConfigSubstituteWithPat (FcConfig    *config,
1401                            FcPattern   *p,
1402                            FcPattern   *p_pat,
1403                            FcMatchKind kind)
1404 {
1405     FcSubst         *s;
1406     FcSubState      *st;
1407     int             i;
1408     FcTest          *t;
1409     FcEdit          *e;
1410     FcValueList     *l;
1411     FcPattern       *m;
1412
1413     if (!config)
1414     {
1415         config = FcConfigGetCurrent ();
1416         if (!config)
1417             return FcFalse;
1418     }
1419
1420     st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1421     if (!st && config->maxObjects)
1422         return FcFalse;
1423     FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1424
1425     if (FcDebug () & FC_DBG_EDIT)
1426     {
1427         printf ("FcConfigSubstitute ");
1428         FcPatternPrint (p);
1429     }
1430     if (kind == FcMatchPattern)
1431         s = config->substPattern;
1432     else
1433         s = config->substFont;
1434     for (; s; s = s->next)
1435     {
1436         /*
1437          * Check the tests to see if
1438          * they all match the pattern
1439          */
1440         for (t = s->test, i = 0; t; t = t->next, i++)
1441         {
1442             if (FcDebug () & FC_DBG_EDIT)
1443             {
1444                 printf ("FcConfigSubstitute test ");
1445                 FcTestPrint (t);
1446             }
1447             st[i].elt = 0;
1448             if (kind == FcMatchFont && t->kind == FcMatchPattern)
1449                 m = p_pat;
1450             else
1451                 m = p;
1452             if (m)
1453                 st[i].elt = FcPatternFindElt (m, t->field);
1454             else
1455                 st[i].elt = 0;
1456             /*
1457              * If there's no such field in the font,
1458              * then FcQualAll matches while FcQualAny does not
1459              */
1460             if (!st[i].elt)
1461             {
1462                 if (t->qual == FcQualAll)
1463                 {
1464                     st[i].value = 0;
1465                     continue;
1466                 }
1467                 else
1468                     break;
1469             }
1470             /*
1471              * Check to see if there is a match, mark the location
1472              * to apply match-relative edits
1473              */
1474             st[i].value = FcConfigMatchValueList (m, t, FcValueListPtrU(st[i].elt->values));
1475             if (!st[i].value)
1476                 break;
1477             if (t->qual == FcQualFirst && st[i].value != FcValueListPtrU(st[i].elt->values))
1478                 break;
1479             if (t->qual == FcQualNotFirst && st[i].value == FcValueListPtrU(st[i].elt->values))
1480                 break;
1481         }
1482         if (t)
1483         {
1484             if (FcDebug () & FC_DBG_EDIT)
1485                 printf ("No match\n");
1486             continue;
1487         }
1488         if (FcDebug () & FC_DBG_EDIT)
1489         {
1490             printf ("Substitute ");
1491             FcSubstPrint (s);
1492         }
1493         for (e = s->edit; e; e = e->next)
1494         {
1495             /*
1496              * Evaluate the list of expressions
1497              */
1498             l = FcConfigValues (p, e->expr, e->binding);
1499             /*
1500              * Locate any test associated with this field, skipping
1501              * tests associated with the pattern when substituting in
1502              * the font
1503              */
1504             for (t = s->test, i = 0; t; t = t->next, i++)
1505             {
1506                 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1507                     !FcStrCmpIgnoreCase ((FcChar8 *) t->field, 
1508                                          (FcChar8 *) e->field))
1509                 {
1510                     /* 
1511                      * KLUDGE - the pattern may have been reallocated or
1512                      * things may have been inserted or deleted above
1513                      * this element by other edits.  Go back and find
1514                      * the element again
1515                      */
1516                     if (e != s->edit && st[i].elt)
1517                         st[i].elt = FcPatternFindElt (p, t->field);
1518                     if (!st[i].elt)
1519                         t = 0;
1520                     break;
1521                 }
1522             }
1523             switch (e->op) {
1524             case FcOpAssign:
1525                 /*
1526                  * If there was a test, then replace the matched
1527                  * value with the new list of values
1528                  */
1529                 if (t)
1530                 {
1531                     FcValueList *thisValue = st[i].value;
1532                     FcValueList *nextValue = thisValue ? FcValueListPtrU(thisValue->next) : 0;
1533                     
1534                     /*
1535                      * Append the new list of values after the current value
1536                      */
1537                     FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1538                     /*
1539                      * Delete the marked value
1540                      */
1541                     if (thisValue)
1542                         FcConfigDel (&st[i].elt->values, thisValue);
1543                     /*
1544                      * Adjust any pointers into the value list to ensure
1545                      * future edits occur at the same place
1546                      */
1547                     for (t = s->test, i = 0; t; t = t->next, i++)
1548                     {
1549                         if (st[i].value == thisValue)
1550                             st[i].value = nextValue;
1551                     }
1552                     break;
1553                 }
1554                 /* fall through ... */
1555             case FcOpAssignReplace:
1556                 /*
1557                  * Delete all of the values and insert
1558                  * the new set
1559                  */
1560                 FcConfigPatternDel (p, e->field);
1561                 FcConfigPatternAdd (p, e->field, l, FcTrue);
1562                 /*
1563                  * Adjust any pointers into the value list as they no
1564                  * longer point to anything valid
1565                  */
1566                 if (t)
1567                 {
1568                     FcPatternElt    *thisElt = st[i].elt;
1569                     for (t = s->test, i = 0; t; t = t->next, i++)
1570                     {
1571                         if (st[i].elt == thisElt)
1572                             st[i].value = 0;
1573                     }
1574                 }
1575                 break;
1576             case FcOpPrepend:
1577                 if (t)
1578                 {
1579                     FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1580                     break;
1581                 }
1582                 /* fall through ... */
1583             case FcOpPrependFirst:
1584                 FcConfigPatternAdd (p, e->field, l, FcFalse);
1585                 break;
1586             case FcOpAppend:
1587                 if (t)
1588                 {
1589                     FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1590                     break;
1591                 }
1592                 /* fall through ... */
1593             case FcOpAppendLast:
1594                 FcConfigPatternAdd (p, e->field, l, FcTrue);
1595                 break;
1596             default:
1597                 FcValueListDestroy (FcValueListPtrCreateDynamic(l));
1598                 break;
1599             }
1600         }
1601         /*
1602          * Now go through the pattern and eliminate
1603          * any properties without data
1604          */
1605         for (e = s->edit; e; e = e->next)
1606             FcConfigPatternCanon (p, e->field);
1607
1608         if (FcDebug () & FC_DBG_EDIT)
1609         {
1610             printf ("FcConfigSubstitute edit");
1611             FcPatternPrint (p);
1612         }
1613     }
1614     FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1615     free (st);
1616     if (FcDebug () & FC_DBG_EDIT)
1617     {
1618         printf ("FcConfigSubstitute done");
1619         FcPatternPrint (p);
1620     }
1621     return FcTrue;
1622 }
1623
1624 FcBool
1625 FcConfigSubstitute (FcConfig    *config,
1626                     FcPattern   *p,
1627                     FcMatchKind kind)
1628 {
1629     return FcConfigSubstituteWithPat (config, p, 0, kind);
1630 }
1631
1632 #if defined (_WIN32) && (defined (PIC) || defined (DLL_EXPORT))
1633
1634 static FcChar8 fontconfig_path[1000] = "";
1635
1636 BOOL WINAPI
1637 DllMain (HINSTANCE hinstDLL,
1638          DWORD     fdwReason,
1639          LPVOID    lpvReserved)
1640 {
1641   FcChar8 *p;
1642
1643   switch (fdwReason) {
1644   case DLL_PROCESS_ATTACH:
1645       if (!GetModuleFileName ((HMODULE) hinstDLL, fontconfig_path,
1646                               sizeof (fontconfig_path)))
1647           break;
1648
1649       /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
1650        * assume it's a Unix-style installation tree, and use
1651        * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
1652        * folder where the DLL is as FONTCONFIG_PATH.
1653        */
1654       p = strrchr (fontconfig_path, '\\');
1655       if (p)
1656       {
1657           *p = '\0';
1658           p = strrchr (fontconfig_path, '\\');
1659           if (p && (FcStrCmpIgnoreCase (p + 1, "bin") == 0 ||
1660                     FcStrCmpIgnoreCase (p + 1, "lib") == 0))
1661               *p = '\0';
1662           strcat (fontconfig_path, "\\etc\\fonts");
1663       }
1664       else
1665           fontconfig_path[0] = '\0';
1666       
1667       break;
1668   }
1669
1670   return TRUE;
1671 }
1672
1673 #undef FONTCONFIG_PATH
1674 #define FONTCONFIG_PATH fontconfig_path
1675
1676 #else /* !(_WIN32 && PIC) */
1677
1678 #endif /* !(_WIN32 && PIC) */
1679
1680 #ifndef FONTCONFIG_FILE
1681 #define FONTCONFIG_FILE "fonts.conf"
1682 #endif
1683
1684 static FcChar8 *
1685 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1686 {
1687     FcChar8    *path;
1688
1689     if (!dir)
1690         dir = (FcChar8 *) "";
1691     path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1692     if (!path)
1693         return 0;
1694
1695     strcpy ((char *) path, (const char *) dir);
1696     /* make sure there's a single separator */
1697 #ifdef _WIN32
1698     if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
1699                       path[strlen((char *) path)-1] != '\\')) &&
1700         !(file[0] == '/' ||
1701           file[0] == '\\' ||
1702           (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
1703         strcat ((char *) path, "\\");
1704 #else
1705     if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1706         strcat ((char *) path, "/");
1707 #endif
1708     strcat ((char *) path, (char *) file);
1709
1710     FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1711     if (access ((char *) path, R_OK) == 0)
1712         return path;
1713     
1714     FcStrFree (path);
1715     return 0;
1716 }
1717
1718 static FcChar8 **
1719 FcConfigGetPath (void)
1720 {
1721     FcChar8    **path;
1722     FcChar8    *env, *e, *colon;
1723     FcChar8    *dir;
1724     int     npath;
1725     int     i;
1726
1727     npath = 2;  /* default dir + null */
1728     env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1729     if (env)
1730     {
1731         e = env;
1732         npath++;
1733         while (*e)
1734             if (*e++ == FC_SEARCH_PATH_SEPARATOR)
1735                 npath++;
1736     }
1737     path = calloc (npath, sizeof (FcChar8 *));
1738     if (!path)
1739         goto bail0;
1740     i = 0;
1741
1742     if (env)
1743     {
1744         e = env;
1745         while (*e) 
1746         {
1747             colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
1748             if (!colon)
1749                 colon = e + strlen ((char *) e);
1750             path[i] = malloc (colon - e + 1);
1751             if (!path[i])
1752                 goto bail1;
1753             strncpy ((char *) path[i], (const char *) e, colon - e);
1754             path[i][colon - e] = '\0';
1755             if (*colon)
1756                 e = colon + 1;
1757             else
1758                 e = colon;
1759             i++;
1760         }
1761     }
1762     
1763     dir = (FcChar8 *) FONTCONFIG_PATH;
1764     path[i] = malloc (strlen ((char *) dir) + 1);
1765     if (!path[i])
1766         goto bail1;
1767     strcpy ((char *) path[i], (const char *) dir);
1768     return path;
1769
1770 bail1:
1771     for (i = 0; path[i]; i++)
1772         free (path[i]);
1773     free (path);
1774 bail0:
1775     return 0;
1776 }
1777
1778 static void
1779 FcConfigFreePath (FcChar8 **path)
1780 {
1781     FcChar8    **p;
1782
1783     for (p = path; *p; p++)
1784         free (*p);
1785     free (path);
1786 }
1787
1788 static FcBool   _FcConfigHomeEnabled = FcTrue;
1789
1790 FcChar8 *
1791 FcConfigHome (void)
1792 {
1793     if (_FcConfigHomeEnabled)
1794     {
1795         char *home = getenv ("HOME");
1796
1797 #ifdef _WIN32
1798         if (home == NULL)
1799             home = getenv ("USERPROFILE");
1800 #endif
1801
1802         return (FcChar8 *) home;
1803     }
1804     return 0;
1805 }
1806
1807 FcBool
1808 FcConfigEnableHome (FcBool enable)
1809 {
1810     FcBool  prev = _FcConfigHomeEnabled;
1811     _FcConfigHomeEnabled = enable;
1812     return prev;
1813 }
1814
1815 FcChar8 *
1816 FcConfigFilename (const FcChar8 *url)
1817 {
1818     FcChar8    *file, *dir, **path, **p;
1819     
1820     if (!url || !*url)
1821     {
1822         url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1823         if (!url)
1824             url = (FcChar8 *) FONTCONFIG_FILE;
1825     }
1826     file = 0;
1827
1828 #ifdef _WIN32
1829     if (isalpha (*url) &&
1830         url[1] == ':' &&
1831         (url[2] == '/' || url[2] == '\\'))
1832         goto absolute_path;
1833 #endif
1834
1835     switch (*url) {
1836     case '~':
1837         dir = FcConfigHome ();
1838         if (dir)
1839             file = FcConfigFileExists (dir, url + 1);
1840         else
1841             file = 0;
1842         break;
1843 #ifdef _WIN32
1844     case '\\':
1845     absolute_path:
1846 #endif
1847     case '/':
1848         file = FcConfigFileExists (0, url);
1849         break;
1850     default:
1851         path = FcConfigGetPath ();
1852         if (!path)
1853             return 0;
1854         for (p = path; *p; p++)
1855         {
1856             file = FcConfigFileExists (*p, url);
1857             if (file)
1858                 break;
1859         }
1860         FcConfigFreePath (path);
1861         break;
1862     }
1863     return file;
1864 }
1865
1866 /*
1867  * Manage the application-specific fonts
1868  */
1869
1870 FcBool
1871 FcConfigAppFontAddFile (FcConfig    *config,
1872                         const FcChar8  *file)
1873 {
1874     FcFontSet   *set;
1875     FcStrSet    *subdirs;
1876     FcStrList   *sublist;
1877     FcChar8     *subdir;
1878
1879     if (!config)
1880     {
1881         config = FcConfigGetCurrent ();
1882         if (!config)
1883             return FcFalse;
1884     }
1885
1886     subdirs = FcStrSetCreate ();
1887     if (!subdirs)
1888         return FcFalse;
1889     
1890     set = FcConfigGetFonts (config, FcSetApplication);
1891     if (!set)
1892     {
1893         set = FcFontSetCreate ();
1894         if (!set)
1895         {
1896             FcStrSetDestroy (subdirs);
1897             return FcFalse;
1898         }
1899         FcConfigSetFonts (config, set, FcSetApplication);
1900     }
1901         
1902     if (!FcFileScanConfig (set, subdirs, 0, config->blanks, file, FcFalse, config))
1903     {
1904         FcStrSetDestroy (subdirs);
1905         return FcFalse;
1906     }
1907     if ((sublist = FcStrListCreate (subdirs)))
1908     {
1909         while ((subdir = FcStrListNext (sublist)))
1910         {
1911             FcConfigAppFontAddDir (config, subdir);
1912         }
1913         FcStrListDone (sublist);
1914     }
1915     FcStrSetDestroy (subdirs);
1916     return FcTrue;
1917 }
1918
1919 FcBool
1920 FcConfigAppFontAddDir (FcConfig     *config,
1921                        const FcChar8   *dir)
1922 {
1923     FcFontSet   *set;
1924     FcStrSet    *subdirs;
1925     FcStrList   *sublist;
1926     FcChar8     *subdir;
1927     
1928     if (!config)
1929     {
1930         config = FcConfigGetCurrent ();
1931         if (!config)
1932             return FcFalse;
1933     }
1934     subdirs = FcStrSetCreate ();
1935     if (!subdirs)
1936         return FcFalse;
1937     
1938     set = FcConfigGetFonts (config, FcSetApplication);
1939     if (!set)
1940     {
1941         set = FcFontSetCreate ();
1942         if (!set)
1943         {
1944             FcStrSetDestroy (subdirs);
1945             return FcFalse;
1946         }
1947         FcConfigSetFonts (config, set, FcSetApplication);
1948     }
1949     
1950     if (!FcDirScanConfig (set, subdirs, 0, config->blanks, dir, FcFalse, config))
1951     {
1952         FcStrSetDestroy (subdirs);
1953         return FcFalse;
1954     }
1955     if ((sublist = FcStrListCreate (subdirs)))
1956     {
1957         while ((subdir = FcStrListNext (sublist)))
1958         {
1959             FcConfigAppFontAddDir (config, subdir);
1960         }
1961         FcStrListDone (sublist);
1962     }
1963     FcStrSetDestroy (subdirs);
1964     return FcTrue;
1965 }
1966
1967 void
1968 FcConfigAppFontClear (FcConfig      *config)
1969 {
1970     if (!config)
1971     {
1972         config = FcConfigGetCurrent ();
1973         if (!config)
1974             return;
1975     }
1976
1977     FcConfigSetFonts (config, 0, FcSetApplication);
1978 }
1979
1980 /*
1981  * Manage filename-based font source selectors
1982  */
1983
1984 FcBool
1985 FcConfigGlobAdd (FcConfig       *config,
1986                  const FcChar8  *glob,
1987                  FcBool         accept)
1988 {
1989     FcStrSet    *set = accept ? config->acceptGlobs : config->rejectGlobs;
1990
1991     return FcStrSetAdd (set, glob);
1992 }
1993
1994 static FcBool
1995 FcConfigGlobMatch (const FcChar8    *glob,
1996                    const FcChar8    *string)
1997 {
1998     FcChar8     c;
1999
2000     while ((c = *glob++)) 
2001     {
2002         switch (c) {
2003         case '*':
2004             /* short circuit common case */
2005             if (!*glob)
2006                 return FcTrue;
2007             /* short circuit another common case */
2008             if (strchr ((char *) glob, '*') == 0)
2009                 string += strlen ((char *) string) - strlen ((char *) glob);
2010             while (*string)
2011             {
2012                 if (FcConfigGlobMatch (glob, string))
2013                     return FcTrue;
2014                 string++;
2015             }
2016             return FcFalse;
2017         case '?':
2018             if (*string++ == '\0')
2019                 return FcFalse;
2020             break;
2021         default:
2022             if (*string++ != c)
2023                 return FcFalse;
2024             break;
2025         }
2026     }
2027     return *string == '\0';
2028 }
2029
2030 static FcBool
2031 FcConfigGlobsMatch (const FcStrSet      *globs,
2032                     const FcChar8       *string)
2033 {
2034     int i;
2035
2036     for (i = 0; i < globs->num; i++)
2037         if (FcConfigGlobMatch (globs->strs[i], string))
2038             return FcTrue;
2039     return FcFalse;
2040 }
2041
2042 FcBool
2043 FcConfigAcceptFilename (FcConfig        *config,
2044                         const FcChar8   *filename)
2045 {
2046     if (FcConfigGlobsMatch (config->acceptGlobs, filename))
2047         return FcTrue;
2048     if (FcConfigGlobsMatch (config->rejectGlobs, filename))
2049         return FcFalse;
2050     return FcTrue;
2051 }
2052
2053 /*
2054  * Manage font-pattern based font source selectors
2055  */
2056
2057 FcBool
2058 FcConfigPatternsAdd (FcConfig   *config,
2059                      FcPattern  *pattern,
2060                      FcBool     accept)
2061 {
2062     FcFontSet   *set = accept ? config->acceptPatterns : config->rejectPatterns;
2063
2064     return FcFontSetAdd (set, pattern);
2065 }
2066
2067 static FcBool
2068 FcConfigPatternsMatch (const FcFontSet  *patterns,
2069                        const FcPattern  *font)
2070 {
2071     int i;
2072     
2073     for (i = 0; i < patterns->nfont; i++)
2074         if (FcListPatternMatchAny (patterns->fonts[i], font))
2075             return FcTrue;
2076     return FcFalse;
2077 }
2078
2079 FcBool
2080 FcConfigAcceptFont (FcConfig        *config,
2081                     const FcPattern *font)
2082 {
2083     if (FcConfigPatternsMatch (config->acceptPatterns, font))
2084         return FcTrue;
2085     if (FcConfigPatternsMatch (config->rejectPatterns, font))
2086         return FcFalse;
2087     return FcTrue;
2088 }