Add uuid in configure file
[platform/upstream/fontconfig.git] / src / fccfg.c
1 /*
2  * fontconfig/src/fccfg.c
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 the author(s) not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  The authors make 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  * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL THE AUTHOR(S) 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 /* Objects MT-safe for readonly access. */
26
27 #include "fcint.h"
28 #ifdef HAVE_DIRENT_H
29 #include <dirent.h>
30 #endif
31 #include <sys/types.h>
32
33 #if defined (_WIN32) && !defined (R_OK)
34 #define R_OK 4
35 #endif
36
37 #if defined(_WIN32) && !defined(S_ISFIFO)
38 #define S_ISFIFO(m) 0
39 #endif
40
41 static FcConfig    *_fcConfig; /* MT-safe */
42 static FcMutex     *_lock;
43
44 static void
45 lock_config (void)
46 {
47     FcMutex *lock;
48 retry:
49     lock = fc_atomic_ptr_get (&_lock);
50     if (!lock)
51     {
52         lock = (FcMutex *) malloc (sizeof (FcMutex));
53         FcMutexInit (lock);
54         if (!fc_atomic_ptr_cmpexch (&_lock, NULL, lock))
55         {
56             FcMutexFinish (lock);
57             free (lock);
58             goto retry;
59         }
60         FcMutexLock (lock);
61         /* Initialize random state */
62         FcRandom ();
63         return;
64     }
65     FcMutexLock (lock);
66 }
67
68 static void
69 unlock_config (void)
70 {
71     FcMutex *lock;
72     lock = fc_atomic_ptr_get (&_lock);
73     FcMutexUnlock (lock);
74 }
75
76 static void
77 free_lock (void)
78 {
79     FcMutex *lock;
80     lock = fc_atomic_ptr_get (&_lock);
81     if (lock && fc_atomic_ptr_cmpexch (&_lock, lock, NULL))
82     {
83         FcMutexFinish (lock);
84         free (lock);
85     }
86 }
87
88 static FcConfig *
89 FcConfigEnsure (void)
90 {
91     FcConfig    *config;
92 retry:
93     config = fc_atomic_ptr_get (&_fcConfig);
94     if (!config)
95     {
96         config = FcInitLoadConfigAndFonts ();
97
98         if (!config || !fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
99             if (config)
100                 FcConfigDestroy (config);
101             goto retry;
102         }
103     }
104     return config;
105 }
106
107 static void
108 FcDestroyAsRule (void *data)
109 {
110     FcRuleDestroy (data);
111 }
112
113 static void
114 FcDestroyAsRuleSet (void *data)
115 {
116     FcRuleSetDestroy (data);
117 }
118
119 FcBool
120 FcConfigInit (void)
121 {
122   return FcConfigEnsure () ? FcTrue : FcFalse;
123 }
124
125 void
126 FcConfigFini (void)
127 {
128     FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
129     if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
130         FcConfigDestroy (cfg);
131     free_lock ();
132 }
133
134 FcConfig *
135 FcConfigCreate (void)
136 {
137     FcSetName   set;
138     FcConfig    *config;
139     FcMatchKind k;
140     FcBool      err = FcFalse;
141
142     config = malloc (sizeof (FcConfig));
143     if (!config)
144         goto bail0;
145
146     config->configDirs = FcStrSetCreate ();
147     if (!config->configDirs)
148         goto bail1;
149
150     config->configMapDirs = FcStrSetCreate();
151     if (!config->configMapDirs)
152         goto bail1_5;
153
154     config->configFiles = FcStrSetCreate ();
155     if (!config->configFiles)
156         goto bail2;
157
158     config->fontDirs = FcStrSetCreate ();
159     if (!config->fontDirs)
160         goto bail3;
161
162     config->acceptGlobs = FcStrSetCreate ();
163     if (!config->acceptGlobs)
164         goto bail4;
165
166     config->rejectGlobs = FcStrSetCreate ();
167     if (!config->rejectGlobs)
168         goto bail5;
169
170     config->acceptPatterns = FcFontSetCreate ();
171     if (!config->acceptPatterns)
172         goto bail6;
173
174     config->rejectPatterns = FcFontSetCreate ();
175     if (!config->rejectPatterns)
176         goto bail7;
177
178     config->cacheDirs = FcStrSetCreate ();
179     if (!config->cacheDirs)
180         goto bail8;
181
182     for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
183     {
184         config->subst[k] = FcPtrListCreate (FcDestroyAsRuleSet);
185         if (!config->subst[k])
186             err = FcTrue;
187     }
188     if (err)
189         goto bail9;
190
191     config->maxObjects = 0;
192     for (set = FcSetSystem; set <= FcSetApplication; set++)
193         config->fonts[set] = 0;
194
195     config->rescanTime = time(0);
196     config->rescanInterval = 30;
197
198     config->expr_pool = NULL;
199
200     config->sysRoot = FcStrRealPath ((const FcChar8 *) getenv("FONTCONFIG_SYSROOT"));
201
202     config->rulesetList = FcPtrListCreate (FcDestroyAsRuleSet);
203     if (!config->rulesetList)
204         goto bail9;
205     config->availConfigFiles = FcStrSetCreate ();
206     if (!config->availConfigFiles)
207         goto bail10;
208
209     FcRefInit (&config->ref, 1);
210
211     return config;
212
213 bail10:
214     FcPtrListDestroy (config->rulesetList);
215 bail9:
216     for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
217         if (config->subst[k])
218             FcPtrListDestroy (config->subst[k]);
219     FcStrSetDestroy (config->cacheDirs);
220 bail8:
221     FcFontSetDestroy (config->rejectPatterns);
222 bail7:
223     FcFontSetDestroy (config->acceptPatterns);
224 bail6:
225     FcStrSetDestroy (config->rejectGlobs);
226 bail5:
227     FcStrSetDestroy (config->acceptGlobs);
228 bail4:
229     FcStrSetDestroy (config->fontDirs);
230 bail3:
231     FcStrSetDestroy (config->configFiles);
232 bail2:
233     FcStrSetDestroy (config->configMapDirs);
234 bail1_5:
235     FcStrSetDestroy (config->configDirs);
236 bail1:
237     free (config);
238 bail0:
239     return 0;
240 }
241
242 static FcFileTime
243 FcConfigNewestFile (FcStrSet *files)
244 {
245     FcStrList       *list = FcStrListCreate (files);
246     FcFileTime      newest = { 0, FcFalse };
247     FcChar8         *file;
248     struct  stat    statb;
249
250     if (list)
251     {
252         while ((file = FcStrListNext (list)))
253             if (FcStat (file, &statb) == 0)
254                 if (!newest.set || statb.st_mtime - newest.time > 0)
255                 {
256                     newest.set = FcTrue;
257                     newest.time = statb.st_mtime;
258                 }
259         FcStrListDone (list);
260     }
261     return newest;
262 }
263
264 FcBool
265 FcConfigUptoDate (FcConfig *config)
266 {
267     FcFileTime  config_time, config_dir_time, font_time;
268     time_t      now = time(0);
269     FcBool      ret = FcTrue;
270
271     config = FcConfigReference (config);
272     if (!config)
273         return FcFalse;
274
275     config_time = FcConfigNewestFile (config->configFiles);
276     config_dir_time = FcConfigNewestFile (config->configDirs);
277     font_time = FcConfigNewestFile (config->fontDirs);
278     if ((config_time.set && config_time.time - config->rescanTime > 0) ||
279         (config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) ||
280         (font_time.set && (font_time.time - config->rescanTime) > 0))
281     {
282         /* We need to check for potential clock problems here (OLPC ticket #6046) */
283         if ((config_time.set && (config_time.time - now) > 0) ||
284         (config_dir_time.set && (config_dir_time.time - now) > 0) ||
285         (font_time.set && (font_time.time - now) > 0))
286         {
287             fprintf (stderr,
288                     "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n");
289             config->rescanTime = now;
290             goto bail;
291         }
292         else
293         {
294             ret = FcFalse;
295             goto bail;
296         }
297     }
298     config->rescanTime = now;
299 bail:
300     FcConfigDestroy (config);
301
302     return ret;
303 }
304
305 FcExpr *
306 FcConfigAllocExpr (FcConfig *config)
307 {
308     if (!config->expr_pool || config->expr_pool->next == config->expr_pool->end)
309     {
310         FcExprPage *new_page;
311
312         new_page = malloc (sizeof (FcExprPage));
313         if (!new_page)
314             return 0;
315
316         new_page->next_page = config->expr_pool;
317         new_page->next = new_page->exprs;
318         config->expr_pool = new_page;
319     }
320
321     return config->expr_pool->next++;
322 }
323
324 FcConfig *
325 FcConfigReference (FcConfig *config)
326 {
327     if (!config)
328     {
329         /* lock during obtaining the value from _fcConfig and count up refcount there,
330          * there are the race between them.
331          */
332         lock_config ();
333     retry:
334         config = fc_atomic_ptr_get (&_fcConfig);
335         if (!config)
336         {
337             unlock_config ();
338
339             config = FcInitLoadConfigAndFonts ();
340             if (!config)
341                 goto retry;
342             lock_config ();
343             if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config))
344             {
345                 FcConfigDestroy (config);
346                 goto retry;
347             }
348         }
349         FcRefInc (&config->ref);
350         unlock_config ();
351     }
352     else
353         FcRefInc (&config->ref);
354
355     return config;
356 }
357
358 void
359 FcConfigDestroy (FcConfig *config)
360 {
361     FcSetName   set;
362     FcExprPage  *page;
363     FcMatchKind k;
364
365     if (config)
366     {
367         if (FcRefDec (&config->ref) != 1)
368             return;
369
370         (void) fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
371
372         FcStrSetDestroy (config->configDirs);
373         FcStrSetDestroy (config->configMapDirs);
374         FcStrSetDestroy (config->fontDirs);
375         FcStrSetDestroy (config->cacheDirs);
376         FcStrSetDestroy (config->configFiles);
377         FcStrSetDestroy (config->acceptGlobs);
378         FcStrSetDestroy (config->rejectGlobs);
379         FcFontSetDestroy (config->acceptPatterns);
380         FcFontSetDestroy (config->rejectPatterns);
381
382         for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
383             FcPtrListDestroy (config->subst[k]);
384         FcPtrListDestroy (config->rulesetList);
385         FcStrSetDestroy (config->availConfigFiles);
386         for (set = FcSetSystem; set <= FcSetApplication; set++)
387             if (config->fonts[set])
388                 FcFontSetDestroy (config->fonts[set]);
389
390         page = config->expr_pool;
391         while (page)
392         {
393             FcExprPage *next = page->next_page;
394             free (page);
395             page = next;
396         }
397         if (config->sysRoot)
398         FcStrFree (config->sysRoot);
399
400         free (config);
401     }
402 }
403
404 /*
405  * Add cache to configuration, adding fonts and directories
406  */
407
408 FcBool
409 FcConfigAddCache (FcConfig *config, FcCache *cache,
410                   FcSetName set, FcStrSet *dirSet, FcChar8 *forDir)
411 {
412     FcFontSet   *fs;
413     intptr_t    *dirs;
414     int         i;
415     FcBool      relocated = FcFalse;
416
417     if (strcmp ((char *)FcCacheDir(cache), (char *)forDir) != 0)
418       relocated = FcTrue;
419
420     /*
421      * Add fonts
422      */
423     fs = FcCacheSet (cache);
424     if (fs)
425     {
426         int     nref = 0;
427
428         for (i = 0; i < fs->nfont; i++)
429         {
430             FcPattern   *font = FcFontSetFont (fs, i);
431             FcChar8     *font_file;
432             FcChar8     *relocated_font_file = NULL;
433
434             if (FcPatternObjectGetString (font, FC_FILE_OBJECT,
435                                           0, &font_file) == FcResultMatch)
436             {
437                 if (relocated)
438                   {
439                     FcChar8 *slash = FcStrLastSlash (font_file);
440                     relocated_font_file = FcStrBuildFilename (forDir, slash + 1, NULL);
441                     font_file = relocated_font_file;
442                   }
443
444                 /*
445                  * Check to see if font is banned by filename
446                  */
447                 if (!FcConfigAcceptFilename (config, font_file))
448                 {
449                     free (relocated_font_file);
450                     continue;
451                 }
452             }
453
454             /*
455              * Check to see if font is banned by pattern
456              */
457             if (!FcConfigAcceptFont (config, font))
458             {
459                 free (relocated_font_file);
460                 continue;
461             }
462
463             if (relocated_font_file)
464             {
465               font = FcPatternCacheRewriteFile (font, cache, relocated_font_file);
466               free (relocated_font_file);
467             }
468
469             if (FcFontSetAdd (config->fonts[set], font))
470                 nref++;
471         }
472         FcDirCacheReference (cache, nref);
473     }
474
475     /*
476      * Add directories
477      */
478     dirs = FcCacheDirs (cache);
479     if (dirs)
480     {
481         for (i = 0; i < cache->dirs_count; i++)
482         {
483             const FcChar8 *dir = FcCacheSubdir (cache, i);
484             FcChar8 *s = NULL;
485
486             if (relocated)
487             {
488                 FcChar8 *base = FcStrBasename (dir);
489                 dir = s = FcStrBuildFilename (forDir, base, NULL);
490                 FcStrFree (base);
491             }
492             if (FcConfigAcceptFilename (config, dir))
493                 FcStrSetAddFilename (dirSet, dir);
494             if (s)
495                 FcStrFree (s);
496         }
497     }
498     return FcTrue;
499 }
500
501 static FcBool
502 FcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet)
503 {
504     FcStrList       *dirlist;
505     FcChar8         *dir;
506     FcCache         *cache;
507
508     dirlist = FcStrListCreate (dirSet);
509     if (!dirlist)
510         return FcFalse;
511
512     while ((dir = FcStrListNext (dirlist)))
513     {
514         if (FcDebug () & FC_DBG_FONTSET)
515             printf ("adding fonts from %s\n", dir);
516         cache = FcDirCacheRead (dir, FcFalse, config);
517         if (!cache)
518             continue;
519         FcConfigAddCache (config, cache, set, dirSet, dir);
520         FcDirCacheUnload (cache);
521     }
522     FcStrListDone (dirlist);
523     return FcTrue;
524 }
525
526 /*
527  * Scan the current list of directories in the configuration
528  * and build the set of available fonts.
529  */
530
531 FcBool
532 FcConfigBuildFonts (FcConfig *config)
533 {
534     FcFontSet       *fonts;
535     FcBool          ret = FcTrue;
536
537     config = FcConfigReference (config);
538     if (!config)
539         return FcFalse;
540
541     fonts = FcFontSetCreate ();
542     if (!fonts)
543     {
544         ret = FcFalse;
545         goto bail;
546     }
547
548     FcConfigSetFonts (config, fonts, FcSetSystem);
549
550     if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs))
551     {
552         ret = FcFalse;
553         goto bail;
554     }
555     if (FcDebug () & FC_DBG_FONTSET)
556         FcFontSetPrint (fonts);
557 bail:
558     FcConfigDestroy (config);
559
560     return ret;
561 }
562
563 FcBool
564 FcConfigSetCurrent (FcConfig *config)
565 {
566     FcConfig *cfg;
567
568     if (config)
569     {
570         if (!config->fonts[FcSetSystem])
571             if (!FcConfigBuildFonts (config))
572                 return FcFalse;
573         FcRefInc (&config->ref);
574     }
575
576     lock_config ();
577 retry:
578     cfg = fc_atomic_ptr_get (&_fcConfig);
579
580     if (config == cfg)
581     {
582         unlock_config ();
583         if (config)
584             FcConfigDestroy (config);
585         return FcTrue;
586     }
587
588     if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
589         goto retry;
590     unlock_config ();
591     if (cfg)
592         FcConfigDestroy (cfg);
593
594     return FcTrue;
595 }
596
597 FcConfig *
598 FcConfigGetCurrent (void)
599 {
600     return FcConfigEnsure ();
601 }
602
603 FcBool
604 FcConfigAddConfigDir (FcConfig      *config,
605                       const FcChar8 *d)
606 {
607     return FcStrSetAddFilename (config->configDirs, d);
608 }
609
610 FcStrList *
611 FcConfigGetConfigDirs (FcConfig   *config)
612 {
613     FcStrList *ret;
614
615     config = FcConfigReference (config);
616     if (!config)
617         return NULL;
618     ret = FcStrListCreate (config->configDirs);
619     FcConfigDestroy (config);
620
621     return ret;
622 }
623
624 FcBool
625 FcConfigAddFontDir (FcConfig        *config,
626                     const FcChar8   *d,
627                     const FcChar8   *m,
628                     const FcChar8   *salt)
629 {
630     if (FcDebug() & FC_DBG_CACHE)
631     {
632         if (m)
633         {
634             printf ("%s -> %s%s%s%s\n", d, m, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
635         }
636         else if (salt)
637         {
638             printf ("%s%s%s%s\n", d, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
639         }
640     }
641     return FcStrSetAddFilenamePairWithSalt (config->fontDirs, d, m, salt);
642 }
643
644 FcBool
645 FcConfigResetFontDirs (FcConfig *config)
646 {
647     if (FcDebug() & FC_DBG_CACHE)
648     {
649         printf ("Reset font directories!\n");
650     }
651     return FcStrSetDeleteAll (config->fontDirs);
652 }
653
654 FcStrList *
655 FcConfigGetFontDirs (FcConfig   *config)
656 {
657     FcStrList *ret;
658
659     config = FcConfigReference (config);
660     if (!config)
661         return NULL;
662     ret = FcStrListCreate (config->fontDirs);
663     FcConfigDestroy (config);
664
665     return ret;
666 }
667
668 static FcBool
669 FcConfigPathStartsWith(const FcChar8    *path,
670                        const FcChar8    *start)
671 {
672     int len = strlen((char *) start);
673
674     if (strncmp((char *) path, (char *) start, len) != 0)
675         return FcFalse;
676
677     switch (path[len]) {
678     case '\0':
679     case FC_DIR_SEPARATOR:
680         return FcTrue;
681     default:
682         return FcFalse;
683     }
684 }
685
686 FcChar8 *
687 FcConfigMapFontPath(FcConfig            *config,
688                     const FcChar8       *path)
689 {
690     FcStrList   *list;
691     FcChar8     *dir;
692     const FcChar8 *map, *rpath;
693     FcChar8     *retval;
694
695     list = FcConfigGetFontDirs(config);
696     if (!list)
697         return 0;
698     while ((dir = FcStrListNext(list)))
699         if (FcConfigPathStartsWith(path, dir))
700             break;
701     FcStrListDone(list);
702     if (!dir)
703         return 0;
704     map = FcStrTripleSecond(dir);
705     if (!map)
706         return 0;
707     rpath = path + strlen ((char *) dir);
708     while (*rpath == '/')
709         rpath++;
710     retval = FcStrBuildFilename(map, rpath, NULL);
711     if (retval)
712     {
713         size_t len = strlen ((const char *) retval);
714         while (len > 0 && retval[len-1] == '/')
715             len--;
716         /* trim the last slash */
717         retval[len] = 0;
718     }
719     return retval;
720 }
721
722 const FcChar8 *
723 FcConfigMapSalt (FcConfig      *config,
724                  const FcChar8 *path)
725 {
726     FcStrList *list;
727     FcChar8 *dir;
728
729     list = FcConfigGetFontDirs (config);
730     if (!list)
731         return NULL;
732     while ((dir = FcStrListNext (list)))
733         if (FcConfigPathStartsWith (path, dir))
734             break;
735     FcStrListDone (list);
736     if (!dir)
737         return NULL;
738
739     return FcStrTripleThird (dir);
740 }
741
742 FcBool
743 FcConfigAddCacheDir (FcConfig       *config,
744                      const FcChar8  *d)
745 {
746     return FcStrSetAddFilename (config->cacheDirs, d);
747 }
748
749 FcStrList *
750 FcConfigGetCacheDirs (FcConfig *config)
751 {
752     FcStrList *ret;
753
754     config = FcConfigReference (config);
755     if (!config)
756         return NULL;
757     ret = FcStrListCreate (config->cacheDirs);
758     FcConfigDestroy (config);
759
760     return ret;
761 }
762
763 FcBool
764 FcConfigAddConfigFile (FcConfig     *config,
765                        const FcChar8   *f)
766 {
767     FcBool      ret;
768     FcChar8     *file = FcConfigGetFilename (config, f);
769
770     if (!file)
771         return FcFalse;
772
773     ret = FcStrSetAdd (config->configFiles, file);
774     FcStrFree (file);
775     return ret;
776 }
777
778 FcStrList *
779 FcConfigGetConfigFiles (FcConfig    *config)
780 {
781     FcStrList *ret;
782
783     config = FcConfigReference (config);
784     if (!config)
785         return NULL;
786     ret = FcStrListCreate (config->configFiles);
787     FcConfigDestroy (config);
788
789     return ret;
790 }
791
792 FcChar8 *
793 FcConfigGetCache (FcConfig  *config FC_UNUSED)
794 {
795     return NULL;
796 }
797
798 FcFontSet *
799 FcConfigGetFonts (FcConfig      *config,
800                   FcSetName     set)
801 {
802     if (!config)
803     {
804         config = FcConfigGetCurrent ();
805         if (!config)
806             return 0;
807     }
808     return config->fonts[set];
809 }
810
811 void
812 FcConfigSetFonts (FcConfig      *config,
813                   FcFontSet     *fonts,
814                   FcSetName     set)
815 {
816     if (config->fonts[set])
817         FcFontSetDestroy (config->fonts[set]);
818     config->fonts[set] = fonts;
819 }
820
821
822 FcBlanks *
823 FcBlanksCreate (void)
824 {
825     /* Deprecated. */
826     return NULL;
827 }
828
829 void
830 FcBlanksDestroy (FcBlanks *b FC_UNUSED)
831 {
832     /* Deprecated. */
833 }
834
835 FcBool
836 FcBlanksAdd (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
837 {
838     /* Deprecated. */
839     return FcFalse;
840 }
841
842 FcBool
843 FcBlanksIsMember (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
844 {
845     /* Deprecated. */
846     return FcFalse;
847 }
848
849 FcBlanks *
850 FcConfigGetBlanks (FcConfig     *config FC_UNUSED)
851 {
852     /* Deprecated. */
853     return NULL;
854 }
855
856 FcBool
857 FcConfigAddBlank (FcConfig      *config FC_UNUSED,
858                   FcChar32      blank FC_UNUSED)
859 {
860     /* Deprecated. */
861     return FcFalse;
862 }
863
864
865 int
866 FcConfigGetRescanInterval (FcConfig *config)
867 {
868     int ret;
869
870     config = FcConfigReference (config);
871     if (!config)
872         return 0;
873     ret = config->rescanInterval;
874     FcConfigDestroy (config);
875
876     return ret;
877 }
878
879 FcBool
880 FcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
881 {
882     config = FcConfigReference (config);
883     if (!config)
884         return FcFalse;
885     config->rescanInterval = rescanInterval;
886     FcConfigDestroy (config);
887
888     return FcTrue;
889 }
890
891 /*
892  * A couple of typos escaped into the library
893  */
894 int
895 FcConfigGetRescanInverval (FcConfig *config)
896 {
897     return FcConfigGetRescanInterval (config);
898 }
899
900 FcBool
901 FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
902 {
903     return FcConfigSetRescanInterval (config, rescanInterval);
904 }
905
906 FcBool
907 FcConfigAddRule (FcConfig       *config,
908                  FcRule         *rule,
909                  FcMatchKind    kind)
910 {
911     /* deprecated */
912     return FcFalse;
913 }
914
915 static FcValue
916 FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
917 {
918     switch (v.type)
919     {
920     case FcTypeInteger:
921         v.type = FcTypeDouble;
922         v.u.d = (double) v.u.i;
923         /* Fallthrough */
924     case FcTypeDouble:
925         if (u.type == FcTypeRange && buf)
926         {
927             v.u.r = FcRangePromote (v.u.d, buf);
928             v.type = FcTypeRange;
929         }
930         break;
931     case FcTypeVoid:
932         if (u.type == FcTypeMatrix)
933         {
934             v.u.m = &FcIdentityMatrix;
935             v.type = FcTypeMatrix;
936         }
937         else if (u.type == FcTypeLangSet && buf)
938         {
939             v.u.l = FcLangSetPromote (NULL, buf);
940             v.type = FcTypeLangSet;
941         }
942         else if (u.type == FcTypeCharSet && buf)
943         {
944             v.u.c = FcCharSetPromote (buf);
945             v.type = FcTypeCharSet;
946         }
947         break;
948     case FcTypeString:
949         if (u.type == FcTypeLangSet && buf)
950         {
951             v.u.l = FcLangSetPromote (v.u.s, buf);
952             v.type = FcTypeLangSet;
953         }
954         break;
955     default:
956         break;
957     }
958     return v;
959 }
960
961 FcBool
962 FcConfigCompareValue (const FcValue     *left_o,
963                       unsigned int      op_,
964                       const FcValue     *right_o)
965 {
966     FcValue     left;
967     FcValue     right;
968     FcBool      ret = FcFalse;
969     FcOp        op = FC_OP_GET_OP (op_);
970     int         flags = FC_OP_GET_FLAGS (op_);
971     FcValuePromotionBuffer buf1, buf2;
972
973     if (left_o->type != right_o->type)
974     {
975         left = FcValueCanonicalize(left_o);
976         right = FcValueCanonicalize(right_o);
977         left = FcConfigPromote (left, right, &buf1);
978         right = FcConfigPromote (right, left, &buf2);
979         left_o = &left;
980         right_o = &right;
981         if (left_o->type != right_o->type)
982         {
983             if (op == FcOpNotEqual || op == FcOpNotContains)
984                 ret = FcTrue;
985             return ret;
986         }
987     }
988     switch (left_o->type) {
989     case FcTypeUnknown:
990         break;  /* No way to guess how to compare for this object */
991     case FcTypeInteger: {
992         int l = left_o->u.i;
993         int r = right_o->u.i;
994         switch ((int) op) {
995         case FcOpEqual:
996         case FcOpContains:
997         case FcOpListing:
998             ret = l == r;
999             break;
1000         case FcOpNotEqual:
1001         case FcOpNotContains:
1002             ret = l != r;
1003             break;
1004         case FcOpLess:
1005             ret = l < r;
1006             break;
1007         case FcOpLessEqual:
1008             ret = l <= r;
1009             break;
1010         case FcOpMore:
1011             ret = l > r;
1012             break;
1013         case FcOpMoreEqual:
1014             ret = l >= r;
1015             break;
1016         default:
1017             break;
1018         }
1019         break;
1020     }
1021     case FcTypeDouble: {
1022         double l = left_o->u.d;
1023         double r = right_o->u.d;
1024         switch ((int) op) {
1025         case FcOpEqual:
1026         case FcOpContains:
1027         case FcOpListing:
1028             ret = l == r;
1029             break;
1030         case FcOpNotEqual:
1031         case FcOpNotContains:
1032             ret = l != r;
1033             break;
1034         case FcOpLess:
1035             ret = l < r;
1036             break;
1037         case FcOpLessEqual:
1038             ret = l <= r;
1039             break;
1040         case FcOpMore:
1041             ret = l > r;
1042             break;
1043         case FcOpMoreEqual:
1044             ret = l >= r;
1045             break;
1046         default:
1047             break;
1048         }
1049         break;
1050     }
1051     case FcTypeBool: {
1052         FcBool l = left_o->u.b;
1053         FcBool r = right_o->u.b;
1054         switch ((int) op) {
1055         case FcOpEqual:
1056             ret = l == r;
1057             break;
1058         case FcOpContains:
1059         case FcOpListing:
1060             ret = l == r || l >= FcDontCare;
1061             break;
1062         case FcOpNotEqual:
1063             ret = l != r;
1064             break;
1065         case FcOpNotContains:
1066             ret = !(l == r || l >= FcDontCare);
1067             break;
1068         case FcOpLess:
1069             ret = l != r && r >= FcDontCare;
1070             break;
1071         case FcOpLessEqual:
1072             ret = l == r || r >= FcDontCare;
1073             break;
1074         case FcOpMore:
1075             ret = l != r && l >= FcDontCare;
1076             break;
1077         case FcOpMoreEqual:
1078             ret = l == r || l >= FcDontCare;
1079             break;
1080         default:
1081             break;
1082         }
1083         break;
1084     }
1085     case FcTypeString: {
1086         const FcChar8 *l = FcValueString (left_o);
1087         const FcChar8 *r = FcValueString (right_o);
1088         switch ((int) op) {
1089         case FcOpEqual:
1090         case FcOpListing:
1091             if (flags & FcOpFlagIgnoreBlanks)
1092                 ret = FcStrCmpIgnoreBlanksAndCase (l, r) == 0;
1093             else
1094                 ret = FcStrCmpIgnoreCase (l, r) == 0;
1095             break;
1096         case FcOpContains:
1097             ret = FcStrStrIgnoreCase (l, r) != 0;
1098             break;
1099         case FcOpNotEqual:
1100             if (flags & FcOpFlagIgnoreBlanks)
1101                 ret = FcStrCmpIgnoreBlanksAndCase (l, r) != 0;
1102             else
1103                 ret = FcStrCmpIgnoreCase (l, r) != 0;
1104             break;
1105         case FcOpNotContains:
1106             ret = FcStrStrIgnoreCase (l, r) == 0;
1107             break;
1108         default:
1109             break;
1110         }
1111         break;
1112     }
1113     case FcTypeMatrix: {
1114         switch ((int) op) {
1115         case FcOpEqual:
1116         case FcOpContains:
1117         case FcOpListing:
1118             ret = FcMatrixEqual (left_o->u.m, right_o->u.m);
1119             break;
1120         case FcOpNotEqual:
1121         case FcOpNotContains:
1122             ret = !FcMatrixEqual (left_o->u.m, right_o->u.m);
1123             break;
1124         default:
1125             break;
1126         }
1127         break;
1128     }
1129     case FcTypeCharSet: {
1130         const FcCharSet *l = FcValueCharSet (left_o);
1131         const FcCharSet *r = FcValueCharSet (right_o);
1132         switch ((int) op) {
1133         case FcOpContains:
1134         case FcOpListing:
1135             /* left contains right if right is a subset of left */
1136             ret = FcCharSetIsSubset (r, l);
1137             break;
1138         case FcOpNotContains:
1139             /* left contains right if right is a subset of left */
1140             ret = !FcCharSetIsSubset (r, l);
1141             break;
1142         case FcOpEqual:
1143             ret = FcCharSetEqual (l, r);
1144             break;
1145         case FcOpNotEqual:
1146             ret = !FcCharSetEqual (l, r);
1147             break;
1148         default:
1149             break;
1150         }
1151         break;
1152     }
1153     case FcTypeLangSet: {
1154         const FcLangSet *l = FcValueLangSet (left_o);
1155         const FcLangSet *r = FcValueLangSet (right_o);
1156         switch ((int) op) {
1157         case FcOpContains:
1158         case FcOpListing:
1159             ret = FcLangSetContains (l, r);
1160             break;
1161         case FcOpNotContains:
1162             ret = !FcLangSetContains (l, r);
1163             break;
1164         case FcOpEqual:
1165             ret = FcLangSetEqual (l, r);
1166             break;
1167         case FcOpNotEqual:
1168             ret = !FcLangSetEqual (l, r);
1169             break;
1170         default:
1171             break;
1172         }
1173         break;
1174     }
1175     case FcTypeVoid:
1176         switch ((int) op) {
1177         case FcOpEqual:
1178         case FcOpContains:
1179         case FcOpListing:
1180             ret = FcTrue;
1181             break;
1182         default:
1183             break;
1184         }
1185         break;
1186     case FcTypeFTFace:
1187         switch ((int) op) {
1188         case FcOpEqual:
1189         case FcOpContains:
1190         case FcOpListing:
1191             ret = left_o->u.f == right_o->u.f;
1192             break;
1193         case FcOpNotEqual:
1194         case FcOpNotContains:
1195             ret = left_o->u.f != right_o->u.f;
1196             break;
1197         default:
1198             break;
1199         }
1200         break;
1201     case FcTypeRange: {
1202         const FcRange *l = FcValueRange (left_o);
1203         const FcRange *r = FcValueRange (right_o);
1204         ret = FcRangeCompare (op, l, r);
1205         break;
1206     }
1207     }
1208     return ret;
1209 }
1210
1211
1212 #define _FcDoubleFloor(d)       ((int) (d))
1213 #define _FcDoubleCeil(d)        ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
1214 #define FcDoubleFloor(d)        ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
1215 #define FcDoubleCeil(d)         ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
1216 #define FcDoubleRound(d)        FcDoubleFloor ((d) + 0.5)
1217 #define FcDoubleTrunc(d)        ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
1218
1219 static FcValue
1220 FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
1221 {
1222     FcValue     v, vl, vr, vle, vre;
1223     FcMatrix    *m;
1224     FcChar8     *str;
1225     FcOp        op = FC_OP_GET_OP (e->op);
1226     FcValuePromotionBuffer buf1, buf2;
1227
1228     switch ((int) op) {
1229     case FcOpInteger:
1230         v.type = FcTypeInteger;
1231         v.u.i = e->u.ival;
1232         break;
1233     case FcOpDouble:
1234         v.type = FcTypeDouble;
1235         v.u.d = e->u.dval;
1236         break;
1237     case FcOpString:
1238         v.type = FcTypeString;
1239         v.u.s = e->u.sval;
1240         v = FcValueSave (v);
1241         break;
1242     case FcOpMatrix:
1243         {
1244           FcMatrix m;
1245           FcValue xx, xy, yx, yy;
1246           v.type = FcTypeMatrix;
1247           xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xx), v, NULL);
1248           xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xy), v, NULL);
1249           yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yx), v, NULL);
1250           yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yy), v, NULL);
1251           if (xx.type == FcTypeDouble && xy.type == FcTypeDouble &&
1252               yx.type == FcTypeDouble && yy.type == FcTypeDouble)
1253           {
1254             m.xx = xx.u.d;
1255             m.xy = xy.u.d;
1256             m.yx = yx.u.d;
1257             m.yy = yy.u.d;
1258             v.u.m = &m;
1259           }
1260           else
1261             v.type = FcTypeVoid;
1262           v = FcValueSave (v);
1263         }
1264         break;
1265     case FcOpCharSet:
1266         v.type = FcTypeCharSet;
1267         v.u.c = e->u.cval;
1268         v = FcValueSave (v);
1269         break;
1270     case FcOpLangSet:
1271         v.type = FcTypeLangSet;
1272         v.u.l = e->u.lval;
1273         v = FcValueSave (v);
1274         break;
1275     case FcOpRange:
1276         v.type = FcTypeRange;
1277         v.u.r = e->u.rval;
1278         v = FcValueSave (v);
1279         break;
1280     case FcOpBool:
1281         v.type = FcTypeBool;
1282         v.u.b = e->u.bval;
1283         break;
1284     case FcOpField:
1285         if (kind == FcMatchFont && e->u.name.kind == FcMatchPattern)
1286         {
1287             if (FcResultMatch != FcPatternObjectGet (p_pat, e->u.name.object, 0, &v))
1288                 v.type = FcTypeVoid;
1289         }
1290         else if (kind == FcMatchPattern && e->u.name.kind == FcMatchFont)
1291         {
1292             fprintf (stderr,
1293                     "Fontconfig warning: <name> tag has target=\"font\" in a <match target=\"pattern\">.\n");
1294             v.type = FcTypeVoid;
1295         }
1296         else
1297         {
1298             if (FcResultMatch != FcPatternObjectGet (p, e->u.name.object, 0, &v))
1299                 v.type = FcTypeVoid;
1300         }
1301         v = FcValueSave (v);
1302         break;
1303     case FcOpConst:
1304         if (FcNameConstant (e->u.constant, &v.u.i))
1305             v.type = FcTypeInteger;
1306         else
1307             v.type = FcTypeVoid;
1308         break;
1309     case FcOpQuest:
1310         vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1311         if (vl.type == FcTypeBool)
1312         {
1313             if (vl.u.b)
1314                 v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.left);
1315             else
1316                 v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.right);
1317         }
1318         else
1319             v.type = FcTypeVoid;
1320         FcValueDestroy (vl);
1321         break;
1322     case FcOpEqual:
1323     case FcOpNotEqual:
1324     case FcOpLess:
1325     case FcOpLessEqual:
1326     case FcOpMore:
1327     case FcOpMoreEqual:
1328     case FcOpContains:
1329     case FcOpNotContains:
1330     case FcOpListing:
1331         vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1332         vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
1333         v.type = FcTypeBool;
1334         v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
1335         FcValueDestroy (vl);
1336         FcValueDestroy (vr);
1337         break;
1338     case FcOpOr:
1339     case FcOpAnd:
1340     case FcOpPlus:
1341     case FcOpMinus:
1342     case FcOpTimes:
1343     case FcOpDivide:
1344         vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1345         vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
1346         vle = FcConfigPromote (vl, vr, &buf1);
1347         vre = FcConfigPromote (vr, vle, &buf2);
1348         if (vle.type == vre.type)
1349         {
1350             switch ((int) vle.type) {
1351             case FcTypeDouble:
1352                 switch ((int) op) {
1353                 case FcOpPlus:
1354                     v.type = FcTypeDouble;
1355                     v.u.d = vle.u.d + vre.u.d;
1356                     break;
1357                 case FcOpMinus:
1358                     v.type = FcTypeDouble;
1359                     v.u.d = vle.u.d - vre.u.d;
1360                     break;
1361                 case FcOpTimes:
1362                     v.type = FcTypeDouble;
1363                     v.u.d = vle.u.d * vre.u.d;
1364                     break;
1365                 case FcOpDivide:
1366                     v.type = FcTypeDouble;
1367                     v.u.d = vle.u.d / vre.u.d;
1368                     break;
1369                 default:
1370                     v.type = FcTypeVoid;
1371                     break;
1372                 }
1373                 if (v.type == FcTypeDouble &&
1374                     v.u.d == (double) (int) v.u.d)
1375                 {
1376                     v.type = FcTypeInteger;
1377                     v.u.i = (int) v.u.d;
1378                 }
1379                 break;
1380             case FcTypeBool:
1381                 switch ((int) op) {
1382                 case FcOpOr:
1383                     v.type = FcTypeBool;
1384                     v.u.b = vle.u.b || vre.u.b;
1385                     break;
1386                 case FcOpAnd:
1387                     v.type = FcTypeBool;
1388                     v.u.b = vle.u.b && vre.u.b;
1389                     break;
1390                 default:
1391                     v.type = FcTypeVoid;
1392                     break;
1393                 }
1394                 break;
1395             case FcTypeString:
1396                 switch ((int) op) {
1397                 case FcOpPlus:
1398                     v.type = FcTypeString;
1399                     str = FcStrPlus (vle.u.s, vre.u.s);
1400                     v.u.s = FcStrdup (str);
1401                     FcStrFree (str);
1402
1403                     if (!v.u.s)
1404                         v.type = FcTypeVoid;
1405                     break;
1406                 default:
1407                     v.type = FcTypeVoid;
1408                     break;
1409                 }
1410                 break;
1411             case FcTypeMatrix:
1412                 switch ((int) op) {
1413                 case FcOpTimes:
1414                     v.type = FcTypeMatrix;
1415                     m = malloc (sizeof (FcMatrix));
1416                     if (m)
1417                     {
1418                         FcMatrixMultiply (m, vle.u.m, vre.u.m);
1419                         v.u.m = m;
1420                     }
1421                     else
1422                     {
1423                         v.type = FcTypeVoid;
1424                     }
1425                     break;
1426                 default:
1427                     v.type = FcTypeVoid;
1428                     break;
1429                 }
1430                 break;
1431             case FcTypeCharSet:
1432                 switch ((int) op) {
1433                 case FcOpPlus:
1434                     v.type = FcTypeCharSet;
1435                     v.u.c = FcCharSetUnion (vle.u.c, vre.u.c);
1436                     if (!v.u.c)
1437                         v.type = FcTypeVoid;
1438                     break;
1439                 case FcOpMinus:
1440                     v.type = FcTypeCharSet;
1441                     v.u.c = FcCharSetSubtract (vle.u.c, vre.u.c);
1442                     if (!v.u.c)
1443                         v.type = FcTypeVoid;
1444                     break;
1445                 default:
1446                     v.type = FcTypeVoid;
1447                     break;
1448                 }
1449                 break;
1450             case FcTypeLangSet:
1451                 switch ((int) op) {
1452                 case FcOpPlus:
1453                     v.type = FcTypeLangSet;
1454                     v.u.l = FcLangSetUnion (vle.u.l, vre.u.l);
1455                     if (!v.u.l)
1456                         v.type = FcTypeVoid;
1457                     break;
1458                 case FcOpMinus:
1459                     v.type = FcTypeLangSet;
1460                     v.u.l = FcLangSetSubtract (vle.u.l, vre.u.l);
1461                     if (!v.u.l)
1462                         v.type = FcTypeVoid;
1463                     break;
1464                 default:
1465                     v.type = FcTypeVoid;
1466                     break;
1467                 }
1468                 break;
1469             default:
1470                 v.type = FcTypeVoid;
1471                 break;
1472             }
1473         }
1474         else
1475             v.type = FcTypeVoid;
1476         FcValueDestroy (vl);
1477         FcValueDestroy (vr);
1478         break;
1479     case FcOpNot:
1480         vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1481         switch ((int) vl.type) {
1482         case FcTypeBool:
1483             v.type = FcTypeBool;
1484             v.u.b = !vl.u.b;
1485             break;
1486         default:
1487             v.type = FcTypeVoid;
1488             break;
1489         }
1490         FcValueDestroy (vl);
1491         break;
1492     case FcOpFloor:
1493         vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1494         switch ((int) vl.type) {
1495         case FcTypeInteger:
1496             v = vl;
1497             break;
1498         case FcTypeDouble:
1499             v.type = FcTypeInteger;
1500             v.u.i = FcDoubleFloor (vl.u.d);
1501             break;
1502         default:
1503             v.type = FcTypeVoid;
1504             break;
1505         }
1506         FcValueDestroy (vl);
1507         break;
1508     case FcOpCeil:
1509         vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1510         switch ((int) vl.type) {
1511         case FcTypeInteger:
1512             v = vl;
1513             break;
1514         case FcTypeDouble:
1515             v.type = FcTypeInteger;
1516             v.u.i = FcDoubleCeil (vl.u.d);
1517             break;
1518         default:
1519             v.type = FcTypeVoid;
1520             break;
1521         }
1522         FcValueDestroy (vl);
1523         break;
1524     case FcOpRound:
1525         vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1526         switch ((int) vl.type) {
1527         case FcTypeInteger:
1528             v = vl;
1529             break;
1530         case FcTypeDouble:
1531             v.type = FcTypeInteger;
1532             v.u.i = FcDoubleRound (vl.u.d);
1533             break;
1534         default:
1535             v.type = FcTypeVoid;
1536             break;
1537         }
1538         FcValueDestroy (vl);
1539         break;
1540     case FcOpTrunc:
1541         vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1542         switch ((int) vl.type) {
1543         case FcTypeInteger:
1544             v = vl;
1545             break;
1546         case FcTypeDouble:
1547             v.type = FcTypeInteger;
1548             v.u.i = FcDoubleTrunc (vl.u.d);
1549             break;
1550         default:
1551             v.type = FcTypeVoid;
1552             break;
1553         }
1554         FcValueDestroy (vl);
1555         break;
1556     default:
1557         v.type = FcTypeVoid;
1558         break;
1559     }
1560     return v;
1561 }
1562
1563 /* The bulk of the time in FcConfigSubstitute is spent walking
1564  * lists of family names. We speed this up with a hash table.
1565  * Since we need to take the ignore-blanks option into account,
1566  * we use two separate hash tables.
1567  */
1568 typedef struct
1569 {
1570   int count;
1571 } FamilyTableEntry;
1572
1573
1574 typedef struct
1575 {
1576   FcHashTable *family_blank_hash;
1577   FcHashTable *family_hash;
1578 } FamilyTable;
1579
1580 static FcBool
1581 FamilyTableLookup (FamilyTable   *table,
1582                    FcOp           _op,
1583                    const FcChar8 *s)
1584 {
1585     FamilyTableEntry *fe;
1586     int flags = FC_OP_GET_FLAGS (_op);
1587     FcHashTable *hash;
1588
1589     if (flags & FcOpFlagIgnoreBlanks)
1590         hash = table->family_blank_hash;
1591     else
1592         hash = table->family_hash;
1593
1594     return FcHashTableFind (hash, (const void *)s, (void **)&fe);
1595 }
1596
1597 static void
1598 FamilyTableAdd (FamilyTable    *table,
1599                 FcValueListPtr  values)
1600 {
1601     FcValueListPtr ll;
1602     for (ll = values; ll; ll = FcValueListNext (ll))
1603         {
1604             const FcChar8 *s = FcValueString (&ll->value);
1605             FamilyTableEntry *fe;
1606
1607             if (!FcHashTableFind (table->family_hash, (const void *)s, (void **)&fe))
1608             {
1609                 fe = malloc (sizeof (FamilyTableEntry));
1610                 fe->count = 0;
1611                 FcHashTableAdd (table->family_hash, (void *)s, fe);
1612             }
1613             fe->count++;
1614
1615             if (!FcHashTableFind (table->family_blank_hash, (const void *)s, (void **)&fe))
1616             {
1617                 fe = malloc (sizeof (FamilyTableEntry));
1618                 fe->count = 0;
1619                 FcHashTableAdd (table->family_blank_hash, (void *)s, fe);
1620             }
1621             fe->count++;
1622        }
1623 }
1624
1625 static void
1626 FamilyTableDel (FamilyTable   *table,
1627                 const FcChar8 *s)
1628 {
1629     FamilyTableEntry *fe;
1630
1631     if (FcHashTableFind (table->family_hash, (void *)s, (void **)&fe))
1632     {
1633         fe->count--;
1634         if (fe->count == 0)
1635             FcHashTableRemove (table->family_hash, (void *)s);
1636     }
1637
1638     if (FcHashTableFind (table->family_blank_hash, (void *)s, (void **)&fe))
1639     {
1640         fe->count--;
1641         if (fe->count == 0)
1642             FcHashTableRemove (table->family_blank_hash, (void *)s);
1643     }
1644 }
1645
1646 static FcBool
1647 copy_string (const void *src, void **dest)
1648 {
1649   *dest = strdup ((char *)src);
1650   return FcTrue;
1651 }
1652
1653 static void
1654 FamilyTableInit (FamilyTable *table,
1655                  FcPattern *p)
1656 {
1657     FcPatternElt *e;
1658
1659     table->family_blank_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
1660                                           (FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
1661                                           (FcCopyFunc)copy_string,
1662                                           NULL,
1663                                           free,
1664                                           free);
1665     table->family_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreCase,
1666                                           (FcCompareFunc)FcStrCmpIgnoreCase,
1667                                           (FcCopyFunc)copy_string,
1668                                           NULL,
1669                                           free,
1670                                           free);
1671     e = FcPatternObjectFindElt (p, FC_FAMILY_OBJECT);
1672     if (e)
1673         FamilyTableAdd (table, FcPatternEltValues (e));
1674 }
1675
1676 static void
1677 FamilyTableClear (FamilyTable *table)
1678 {
1679     if (table->family_blank_hash)
1680         FcHashTableDestroy (table->family_blank_hash);
1681     if (table->family_hash)
1682         FcHashTableDestroy (table->family_hash);
1683 }
1684
1685 static FcValueList *
1686 FcConfigMatchValueList (FcPattern       *p,
1687                         FcPattern       *p_pat,
1688                         FcMatchKind      kind,
1689                         FcTest          *t,
1690                         FcValueList     *values,
1691                         FamilyTable     *table)
1692 {
1693     FcValueList     *ret = 0;
1694     FcExpr          *e = t->expr;
1695     FcValue         value;
1696     FcValueList     *v;
1697     FcOp            op;
1698
1699     while (e)
1700     {
1701         /* Compute the value of the match expression */
1702         if (FC_OP_GET_OP (e->op) == FcOpComma)
1703         {
1704             value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1705             e = e->u.tree.right;
1706         }
1707         else
1708         {
1709             value = FcConfigEvaluate (p, p_pat, kind, e);
1710             e = 0;
1711         }
1712
1713         if (t->object == FC_FAMILY_OBJECT && table)
1714         {
1715             op = FC_OP_GET_OP (t->op);
1716             if (op == FcOpEqual || op == FcOpListing)
1717             {
1718                 if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
1719                 {
1720                     ret = 0;
1721                     goto done;
1722                 }
1723             }
1724             if (op == FcOpNotEqual && t->qual == FcQualAll)
1725             {
1726                 ret = 0;
1727                 if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
1728                 {
1729                     ret = values;
1730                 }
1731                 goto done;
1732             }
1733         }
1734         for (v = values; v; v = FcValueListNext(v))
1735         {
1736             /* Compare the pattern value to the match expression value */
1737             if (FcConfigCompareValue (&v->value, t->op, &value))
1738             {
1739                 if (!ret)
1740                     ret = v;
1741                 if (t->qual != FcQualAll)
1742                     break;
1743             }
1744             else
1745             {
1746                 if (t->qual == FcQualAll)
1747                 {
1748                     ret = 0;
1749                     break;
1750                 }
1751             }
1752         }
1753 done:
1754         FcValueDestroy (value);
1755     }
1756     return ret;
1757 }
1758
1759 static FcValueList *
1760 FcConfigValues (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e, FcValueBinding binding)
1761 {
1762     FcValueList *l;
1763
1764     if (!e)
1765         return 0;
1766     l = (FcValueList *) malloc (sizeof (FcValueList));
1767     if (!l)
1768         return 0;
1769     if (FC_OP_GET_OP (e->op) == FcOpComma)
1770     {
1771         l->value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1772         l->next = FcConfigValues (p, p_pat, kind, e->u.tree.right, binding);
1773     }
1774     else
1775     {
1776         l->value = FcConfigEvaluate (p, p_pat, kind, e);
1777         l->next = NULL;
1778     }
1779     l->binding = binding;
1780     if (l->value.type == FcTypeVoid)
1781     {
1782         FcValueList  *next = FcValueListNext(l);
1783
1784         free (l);
1785         l = next;
1786     }
1787
1788     return l;
1789 }
1790
1791 static FcBool
1792 FcConfigAdd (FcValueListPtr *head,
1793              FcValueList    *position,
1794              FcBool         append,
1795              FcValueList    *new,
1796              FcObject        object,
1797              FamilyTable    *table)
1798 {
1799     FcValueListPtr  *prev, l, last, v;
1800     FcValueBinding  sameBinding;
1801
1802     /*
1803      * Make sure the stored type is valid for built-in objects
1804      */
1805     for (l = new; l != NULL; l = FcValueListNext (l))
1806     {
1807         if (!FcObjectValidType (object, l->value.type))
1808         {
1809             fprintf (stderr,
1810                      "Fontconfig warning: FcPattern object %s does not accept value", FcObjectName (object));
1811             FcValuePrintFile (stderr, l->value);
1812             fprintf (stderr, "\n");
1813
1814             if (FcDebug () & FC_DBG_EDIT)
1815             {
1816                 printf ("Not adding\n");
1817             }
1818
1819             return FcFalse;
1820         }
1821     }
1822
1823     if (object == FC_FAMILY_OBJECT && table)
1824     {
1825         FamilyTableAdd (table, new);
1826     }
1827
1828     if (position)
1829         sameBinding = position->binding;
1830     else
1831         sameBinding = FcValueBindingWeak;
1832     for (v = new; v != NULL; v = FcValueListNext(v))
1833         if (v->binding == FcValueBindingSame)
1834             v->binding = sameBinding;
1835     if (append)
1836     {
1837         if (position)
1838             prev = &position->next;
1839         else
1840             for (prev = head; *prev != NULL;
1841                  prev = &(*prev)->next)
1842                 ;
1843     }
1844     else
1845     {
1846         if (position)
1847         {
1848             for (prev = head; *prev != NULL;
1849                  prev = &(*prev)->next)
1850             {
1851                 if (*prev == position)
1852                     break;
1853             }
1854         }
1855         else
1856             prev = head;
1857
1858         if (FcDebug () & FC_DBG_EDIT)
1859         {
1860             if (*prev == NULL)
1861                 printf ("position not on list\n");
1862         }
1863     }
1864
1865     if (FcDebug () & FC_DBG_EDIT)
1866     {
1867         printf ("%s list before ", append ? "Append" : "Prepend");
1868         FcValueListPrintWithPosition (*head, *prev);
1869         printf ("\n");
1870     }
1871
1872     if (new)
1873     {
1874         last = new;
1875         while (last->next != NULL)
1876             last = last->next;
1877
1878         last->next = *prev;
1879         *prev = new;
1880     }
1881
1882     if (FcDebug () & FC_DBG_EDIT)
1883     {
1884         printf ("%s list after ", append ? "Append" : "Prepend");
1885         FcValueListPrint (*head);
1886         printf ("\n");
1887     }
1888
1889     return FcTrue;
1890 }
1891
1892 static void
1893 FcConfigDel (FcValueListPtr *head,
1894              FcValueList    *position,
1895              FcObject        object,
1896              FamilyTable    *table)
1897 {
1898     FcValueListPtr *prev;
1899
1900     if (object == FC_FAMILY_OBJECT && table)
1901     {
1902         FamilyTableDel (table, FcValueString (&position->value));
1903     }
1904
1905     for (prev = head; *prev != NULL; prev = &(*prev)->next)
1906     {
1907         if (*prev == position)
1908         {
1909             *prev = position->next;
1910             position->next = NULL;
1911             FcValueListDestroy (position);
1912             break;
1913         }
1914     }
1915 }
1916
1917 static void
1918 FcConfigPatternAdd (FcPattern   *p,
1919                     FcObject     object,
1920                     FcValueList *list,
1921                     FcBool       append,
1922                     FamilyTable *table)
1923 {
1924     if (list)
1925     {
1926         FcPatternElt    *e = FcPatternObjectInsertElt (p, object);
1927
1928         if (!e)
1929             return;
1930         FcConfigAdd (&e->values, 0, append, list, object, table);
1931     }
1932 }
1933
1934 /*
1935  * Delete all values associated with a field
1936  */
1937 static void
1938 FcConfigPatternDel (FcPattern   *p,
1939                     FcObject     object,
1940                     FamilyTable *table)
1941 {
1942     FcPatternElt    *e = FcPatternObjectFindElt (p, object);
1943     if (!e)
1944         return;
1945     while (e->values != NULL)
1946         FcConfigDel (&e->values, e->values, object, table);
1947 }
1948
1949 static void
1950 FcConfigPatternCanon (FcPattern     *p,
1951                       FcObject      object)
1952 {
1953     FcPatternElt    *e = FcPatternObjectFindElt (p, object);
1954     if (!e)
1955         return;
1956     if (e->values == NULL)
1957         FcPatternObjectDel (p, object);
1958 }
1959
1960 FcBool
1961 FcConfigSubstituteWithPat (FcConfig    *config,
1962                            FcPattern   *p,
1963                            FcPattern   *p_pat,
1964                            FcMatchKind kind)
1965 {
1966     FcValue v;
1967     FcPtrList       *s;
1968     FcPtrListIter    iter, iter2;
1969     FcRule          *r;
1970     FcRuleSet       *rs;
1971     FcValueList     *l, **value = NULL, *vl;
1972     FcPattern       *m;
1973     FcStrSet        *strs;
1974     FcObject        object = FC_INVALID_OBJECT;
1975     FcPatternElt    **elt = NULL, *e;
1976     int             i, nobjs;
1977     FcBool          retval = FcTrue;
1978     FcTest          **tst = NULL;
1979     FamilyTable     data;
1980     FamilyTable     *table = &data;
1981
1982     if (kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
1983         return FcFalse;
1984
1985     config = FcConfigReference (config);
1986     if (!config)
1987         return FcFalse;
1988
1989     s = config->subst[kind];
1990     if (kind == FcMatchPattern)
1991     {
1992         strs = FcGetDefaultLangs ();
1993         if (strs)
1994         {
1995             FcStrList *l = FcStrListCreate (strs);
1996             FcChar8 *lang;
1997             FcValue v;
1998             FcLangSet *lsund = FcLangSetCreate ();
1999
2000             FcLangSetAdd (lsund, (const FcChar8 *)"und");
2001             FcStrSetDestroy (strs);
2002             while (l && (lang = FcStrListNext (l)))
2003             {
2004                 FcPatternElt *e = FcPatternObjectFindElt (p, FC_LANG_OBJECT);
2005
2006                 if (e)
2007                 {
2008                     FcValueListPtr ll;
2009
2010                     for (ll = FcPatternEltValues (e); ll; ll = FcValueListNext (ll))
2011                     {
2012                         FcValue vv = FcValueCanonicalize (&ll->value);
2013
2014                         if (vv.type == FcTypeLangSet)
2015                         {
2016                             FcLangSet *ls = FcLangSetCreate ();
2017                             FcBool b;
2018
2019                             FcLangSetAdd (ls, lang);
2020                             b = FcLangSetContains (vv.u.l, ls);
2021                             FcLangSetDestroy (ls);
2022                             if (b)
2023                                 goto bail_lang;
2024                             if (FcLangSetContains (vv.u.l, lsund))
2025                                 goto bail_lang;
2026                         }
2027                         else
2028                         {
2029                             if (FcStrCmpIgnoreCase (vv.u.s, lang) == 0)
2030                                 goto bail_lang;
2031                             if (FcStrCmpIgnoreCase (vv.u.s, (const FcChar8 *)"und") == 0)
2032                                 goto bail_lang;
2033                         }
2034                     }
2035                 }
2036                 v.type = FcTypeString;
2037                 v.u.s = lang;
2038
2039                 FcPatternObjectAddWithBinding (p, FC_LANG_OBJECT, v, FcValueBindingWeak, FcTrue);
2040             }
2041         bail_lang:
2042             FcStrListDone (l);
2043             FcLangSetDestroy (lsund);
2044         }
2045         if (FcPatternObjectGet (p, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch)
2046         {
2047             FcChar8 *prgname = FcGetPrgname ();
2048             if (prgname)
2049                 FcPatternObjectAddString (p, FC_PRGNAME_OBJECT, prgname);
2050         }
2051     }
2052
2053     nobjs = FC_MAX_BASE_OBJECT + config->maxObjects + 2;
2054     value = (FcValueList **) malloc (SIZEOF_VOID_P * nobjs);
2055     if (!value)
2056     {
2057         retval = FcFalse;
2058         goto bail1;
2059     }
2060     elt = (FcPatternElt **) malloc (SIZEOF_VOID_P * nobjs);
2061     if (!elt)
2062     {
2063         retval = FcFalse;
2064         goto bail1;
2065     }
2066     tst = (FcTest **) malloc (SIZEOF_VOID_P * nobjs);
2067     if (!tst)
2068     {
2069         retval = FcFalse;
2070         goto bail1;
2071     }
2072
2073     if (FcDebug () & FC_DBG_EDIT)
2074     {
2075         printf ("FcConfigSubstitute ");
2076         FcPatternPrint (p);
2077     }
2078
2079     FamilyTableInit (&data, p);
2080
2081     FcPtrListIterInit (s, &iter);
2082     for (; FcPtrListIterIsValid (s, &iter); FcPtrListIterNext (s, &iter))
2083     {
2084         rs = (FcRuleSet *) FcPtrListIterGetValue (s, &iter);
2085         if (FcDebug () & FC_DBG_EDIT)
2086         {
2087             printf ("\nRule Set: %s\n", rs->name);
2088         }
2089         FcPtrListIterInit (rs->subst[kind], &iter2);
2090         for (; FcPtrListIterIsValid (rs->subst[kind], &iter2); FcPtrListIterNext (rs->subst[kind], &iter2))
2091         {
2092             r = (FcRule *) FcPtrListIterGetValue (rs->subst[kind], &iter2);
2093             for (i = 0; i < nobjs; i++)
2094             {
2095                 elt[i] = NULL;
2096                 value[i] = NULL;
2097                 tst[i] = NULL;
2098             }
2099             for (; r; r = r->next)
2100             {
2101                 switch (r->type) {
2102                 case FcRuleUnknown:
2103                     /* shouldn't be reached */
2104                     break;
2105                 case FcRuleTest:
2106                     object = FC_OBJ_ID (r->u.test->object);
2107                     /*
2108                      * Check the tests to see if
2109                      * they all match the pattern
2110                      */
2111                     if (FcDebug () & FC_DBG_EDIT)
2112                     {
2113                         printf ("FcConfigSubstitute test ");
2114                         FcTestPrint (r->u.test);
2115                     }
2116                     if (kind == FcMatchFont && r->u.test->kind == FcMatchPattern)
2117                     {
2118                         m = p_pat;
2119                         table = NULL;
2120                     }
2121                     else
2122                     {
2123                         m = p;
2124                         table = &data;
2125                     }
2126                     if (m)
2127                         e = FcPatternObjectFindElt (m, r->u.test->object);
2128                     else
2129                         e = NULL;
2130                     /* different 'kind' won't be the target of edit */
2131                     if (!elt[object] && kind == r->u.test->kind)
2132                     {
2133                         elt[object] = e;
2134                         tst[object] = r->u.test;
2135                     }
2136                     /*
2137                      * If there's no such field in the font,
2138                      * then FcQualAll matches while FcQualAny does not
2139                      */
2140                     if (!e)
2141                     {
2142                         if (r->u.test->qual == FcQualAll)
2143                         {
2144                             value[object] = NULL;
2145                             continue;
2146                         }
2147                         else
2148                         {
2149                             if (FcDebug () & FC_DBG_EDIT)
2150                                 printf ("No match\n");
2151                             goto bail;
2152                         }
2153                     }
2154                     /*
2155                      * Check to see if there is a match, mark the location
2156                      * to apply match-relative edits
2157                      */
2158                     vl = FcConfigMatchValueList (m, p_pat, kind, r->u.test, e->values, table);
2159                     /* different 'kind' won't be the target of edit */
2160                     if (!value[object] && kind == r->u.test->kind)
2161                         value[object] = vl;
2162                     if (!vl ||
2163                         (r->u.test->qual == FcQualFirst && vl != e->values) ||
2164                         (r->u.test->qual == FcQualNotFirst && vl == e->values))
2165                     {
2166                         if (FcDebug () & FC_DBG_EDIT)
2167                             printf ("No match\n");
2168                         goto bail;
2169                     }
2170                     break;
2171                 case FcRuleEdit:
2172                     object = FC_OBJ_ID (r->u.edit->object);
2173                     if (FcDebug () & FC_DBG_EDIT)
2174                     {
2175                         printf ("Substitute ");
2176                         FcEditPrint (r->u.edit);
2177                         printf ("\n\n");
2178                     }
2179                     /*
2180                      * Evaluate the list of expressions
2181                      */
2182                     l = FcConfigValues (p, p_pat, kind, r->u.edit->expr, r->u.edit->binding);
2183                     if (tst[object] && (tst[object]->kind == FcMatchFont || kind == FcMatchPattern))
2184                         elt[object] = FcPatternObjectFindElt (p, tst[object]->object);
2185
2186                     switch (FC_OP_GET_OP (r->u.edit->op)) {
2187                     case FcOpAssign:
2188                         /*
2189                          * If there was a test, then replace the matched
2190                          * value with the new list of values
2191                          */
2192                         if (value[object])
2193                         {
2194                             FcValueList *thisValue = value[object];
2195                             FcValueList *nextValue = l;
2196
2197                             /*
2198                              * Append the new list of values after the current value
2199                              */
2200                             FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object, table);
2201                             /*
2202                              * Delete the marked value
2203                              */
2204                             if (thisValue)
2205                                 FcConfigDel (&elt[object]->values, thisValue, object, table);
2206                             /*
2207                              * Adjust a pointer into the value list to ensure
2208                              * future edits occur at the same place
2209                              */
2210                             value[object] = nextValue;
2211                             break;
2212                         }
2213                         /* fall through ... */
2214                     case FcOpAssignReplace:
2215                         /*
2216                          * Delete all of the values and insert
2217                          * the new set
2218                          */
2219                         FcConfigPatternDel (p, r->u.edit->object, table);
2220                         FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
2221                         /*
2222                          * Adjust a pointer into the value list as they no
2223                          * longer point to anything valid
2224                          */
2225                         value[object] = NULL;
2226                         break;
2227                     case FcOpPrepend:
2228                         if (value[object])
2229                         {
2230                             FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object, table);
2231                             break;
2232                         }
2233                         /* fall through ... */
2234                     case FcOpPrependFirst:
2235                         FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse, table);
2236                         break;
2237                     case FcOpAppend:
2238                         if (value[object])
2239                         {
2240                             FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object, table);
2241                             break;
2242                         }
2243                         /* fall through ... */
2244                     case FcOpAppendLast:
2245                         FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
2246                         break;
2247                     case FcOpDelete:
2248                         if (value[object])
2249                         {
2250                             FcConfigDel (&elt[object]->values, value[object], object, table);
2251                             FcValueListDestroy (l);
2252                             break;
2253                         }
2254                         /* fall through ... */
2255                     case FcOpDeleteAll:
2256                         FcConfigPatternDel (p, r->u.edit->object, table);
2257                         FcValueListDestroy (l);
2258                         break;
2259                     default:
2260                         FcValueListDestroy (l);
2261                         break;
2262                     }
2263                     /*
2264                      * Now go through the pattern and eliminate
2265                      * any properties without data
2266                      */
2267                     FcConfigPatternCanon (p, r->u.edit->object);
2268
2269                     if (FcDebug () & FC_DBG_EDIT)
2270                     {
2271                         printf ("FcConfigSubstitute edit");
2272                         FcPatternPrint (p);
2273                     }
2274                     break;
2275                 }
2276             }
2277         bail:;
2278         }
2279     }
2280     if (FcDebug () & FC_DBG_EDIT)
2281     {
2282         printf ("FcConfigSubstitute done");
2283         FcPatternPrint (p);
2284     }
2285 bail1:
2286     FamilyTableClear (&data);
2287     if (elt)
2288         free (elt);
2289     if (value)
2290         free (value);
2291     if (tst)
2292         free (tst);
2293     FcConfigDestroy (config);
2294
2295     return retval;
2296 }
2297
2298 FcBool
2299 FcConfigSubstitute (FcConfig    *config,
2300                     FcPattern   *p,
2301                     FcMatchKind kind)
2302 {
2303     return FcConfigSubstituteWithPat (config, p, 0, kind);
2304 }
2305
2306 #if defined (_WIN32)
2307
2308 static FcChar8 fontconfig_path[1000] = ""; /* MT-dontcare */
2309 FcChar8 fontconfig_instprefix[1000] = ""; /* MT-dontcare */
2310
2311 #  if (defined (PIC) || defined (DLL_EXPORT))
2312
2313 BOOL WINAPI
2314 DllMain (HINSTANCE hinstDLL,
2315          DWORD     fdwReason,
2316          LPVOID    lpvReserved);
2317
2318 BOOL WINAPI
2319 DllMain (HINSTANCE hinstDLL,
2320          DWORD     fdwReason,
2321          LPVOID    lpvReserved)
2322 {
2323   FcChar8 *p;
2324
2325   switch (fdwReason) {
2326   case DLL_PROCESS_ATTACH:
2327       if (!GetModuleFileName ((HMODULE) hinstDLL, (LPCH) fontconfig_path,
2328                               sizeof (fontconfig_path)))
2329           break;
2330
2331       /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
2332        * assume it's a Unix-style installation tree, and use
2333        * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
2334        * folder where the DLL is as FONTCONFIG_PATH.
2335        */
2336       p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
2337       if (p)
2338       {
2339           *p = '\0';
2340           p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
2341           if (p && (FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "bin") == 0 ||
2342                     FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "lib") == 0))
2343               *p = '\0';
2344           strcat ((char *) fontconfig_instprefix, (char *) fontconfig_path);
2345           strcat ((char *) fontconfig_path, "\\etc\\fonts");
2346       }
2347       else
2348           fontconfig_path[0] = '\0';
2349
2350       break;
2351   }
2352
2353   return TRUE;
2354 }
2355
2356 #  endif /* !PIC */
2357
2358 #undef FONTCONFIG_PATH
2359 #define FONTCONFIG_PATH fontconfig_path
2360
2361 #endif /* !_WIN32 */
2362
2363 #ifndef FONTCONFIG_FILE
2364 #define FONTCONFIG_FILE "fonts.conf"
2365 #endif
2366
2367 static FcChar8 *
2368 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
2369 {
2370     FcChar8    *path;
2371     int         size, osize;
2372
2373     if (!dir)
2374         dir = (FcChar8 *) "";
2375
2376     osize = strlen ((char *) dir) + 1 + strlen ((char *) file) + 1;
2377     /*
2378      * workaround valgrind warning because glibc takes advantage of how it knows memory is
2379      * allocated to implement strlen by reading in groups of 4
2380      */
2381     size = (osize + 3) & ~3;
2382
2383     path = malloc (size);
2384     if (!path)
2385         return 0;
2386
2387     strcpy ((char *) path, (const char *) dir);
2388     /* make sure there's a single separator */
2389 #ifdef _WIN32
2390     if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
2391                       path[strlen((char *) path)-1] != '\\')) &&
2392         !(file[0] == '/' ||
2393           file[0] == '\\' ||
2394           (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
2395         strcat ((char *) path, "\\");
2396 #else
2397     if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
2398         strcat ((char *) path, "/");
2399     else
2400         osize--;
2401 #endif
2402     strcat ((char *) path, (char *) file);
2403
2404     if (access ((char *) path, R_OK) == 0)
2405         return path;
2406
2407     FcStrFree (path);
2408
2409     return 0;
2410 }
2411
2412 static FcChar8 **
2413 FcConfigGetPath (void)
2414 {
2415     FcChar8    **path;
2416     FcChar8    *env, *e, *colon;
2417     FcChar8    *dir;
2418     int     npath;
2419     int     i;
2420
2421     npath = 2;  /* default dir + null */
2422     env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
2423     if (env)
2424     {
2425         e = env;
2426         npath++;
2427         while (*e)
2428             if (*e++ == FC_SEARCH_PATH_SEPARATOR)
2429                 npath++;
2430     }
2431     path = calloc (npath, sizeof (FcChar8 *));
2432     if (!path)
2433         goto bail0;
2434     i = 0;
2435
2436     if (env)
2437     {
2438         e = env;
2439         while (*e)
2440         {
2441             colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
2442             if (!colon)
2443                 colon = e + strlen ((char *) e);
2444             path[i] = malloc (colon - e + 1);
2445             if (!path[i])
2446                 goto bail1;
2447             strncpy ((char *) path[i], (const char *) e, colon - e);
2448             path[i][colon - e] = '\0';
2449             if (*colon)
2450                 e = colon + 1;
2451             else
2452                 e = colon;
2453             i++;
2454         }
2455     }
2456
2457 #ifdef _WIN32
2458         if (fontconfig_path[0] == '\0')
2459         {
2460                 char *p;
2461                 if(!GetModuleFileName(NULL, (LPCH) fontconfig_path, sizeof(fontconfig_path)))
2462                         goto bail1;
2463                 p = strrchr ((const char *) fontconfig_path, '\\');
2464                 if (p) *p = '\0';
2465                 strcat ((char *) fontconfig_path, "\\fonts");
2466         }
2467 #endif
2468     dir = (FcChar8 *) FONTCONFIG_PATH;
2469     path[i] = malloc (strlen ((char *) dir) + 1);
2470     if (!path[i])
2471         goto bail1;
2472     strcpy ((char *) path[i], (const char *) dir);
2473     return path;
2474
2475 bail1:
2476     for (i = 0; path[i]; i++)
2477         free (path[i]);
2478     free (path);
2479 bail0:
2480     return 0;
2481 }
2482
2483 static void
2484 FcConfigFreePath (FcChar8 **path)
2485 {
2486     FcChar8    **p;
2487
2488     for (p = path; *p; p++)
2489         free (*p);
2490     free (path);
2491 }
2492
2493 static FcBool   _FcConfigHomeEnabled = FcTrue; /* MT-goodenough */
2494
2495 FcChar8 *
2496 FcConfigHome (void)
2497 {
2498     if (_FcConfigHomeEnabled)
2499     {
2500         char *home = getenv ("HOME");
2501
2502 #ifdef _WIN32
2503         if (home == NULL)
2504             home = getenv ("USERPROFILE");
2505 #endif
2506
2507         return (FcChar8 *) home;
2508     }
2509     return 0;
2510 }
2511
2512 FcChar8 *
2513 FcConfigXdgCacheHome (void)
2514 {
2515     const char *env = getenv ("XDG_CACHE_HOME");
2516     FcChar8 *ret = NULL;
2517
2518     if (!_FcConfigHomeEnabled)
2519         return NULL;
2520     if (env && env[0])
2521         ret = FcStrCopy ((const FcChar8 *)env);
2522     else
2523     {
2524         const FcChar8 *home = FcConfigHome ();
2525         size_t len = home ? strlen ((const char *)home) : 0;
2526
2527         ret = malloc (len + 7 + 1);
2528         if (ret)
2529         {
2530             if (home)
2531                 memcpy (ret, home, len);
2532             memcpy (&ret[len], FC_DIR_SEPARATOR_S ".cache", 7);
2533             ret[len + 7] = 0;
2534         }
2535     }
2536
2537     return ret;
2538 }
2539
2540 FcChar8 *
2541 FcConfigXdgConfigHome (void)
2542 {
2543     const char *env = getenv ("XDG_CONFIG_HOME");
2544     FcChar8 *ret = NULL;
2545
2546     if (!_FcConfigHomeEnabled)
2547         return NULL;
2548     if (env)
2549         ret = FcStrCopy ((const FcChar8 *)env);
2550     else
2551     {
2552         const FcChar8 *home = FcConfigHome ();
2553         size_t len = home ? strlen ((const char *)home) : 0;
2554
2555         ret = malloc (len + 8 + 1);
2556         if (ret)
2557         {
2558             if (home)
2559                 memcpy (ret, home, len);
2560             memcpy (&ret[len], FC_DIR_SEPARATOR_S ".config", 8);
2561             ret[len + 8] = 0;
2562         }
2563     }
2564
2565     return ret;
2566 }
2567
2568 FcChar8 *
2569 FcConfigXdgDataHome (void)
2570 {
2571     const char *env = getenv ("XDG_DATA_HOME");
2572     FcChar8 *ret = NULL;
2573
2574     if (!_FcConfigHomeEnabled)
2575         return NULL;
2576     if (env)
2577         ret = FcStrCopy ((const FcChar8 *)env);
2578     else
2579     {
2580         const FcChar8 *home = FcConfigHome ();
2581         size_t len = home ? strlen ((const char *)home) : 0;
2582
2583         ret = malloc (len + 13 + 1);
2584         if (ret)
2585         {
2586             if (home)
2587                 memcpy (ret, home, len);
2588             memcpy (&ret[len], FC_DIR_SEPARATOR_S ".local" FC_DIR_SEPARATOR_S "share", 13);
2589             ret[len + 13] = 0;
2590         }
2591     }
2592
2593     return ret;
2594 }
2595
2596 FcStrSet *
2597 FcConfigXdgDataDirs (void)
2598 {
2599     const char *env = getenv ("XDG_DATA_DIRS");
2600     FcStrSet *ret = FcStrSetCreate ();
2601
2602     if (env)
2603     {
2604         FcChar8 *ee, *e = ee = FcStrCopy ((const FcChar8 *) env);
2605
2606         /* We don't intentionally use FC_SEARCH_PATH_SEPARATOR here because of:
2607          *   The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
2608          * in doc.
2609          */
2610         while (e)
2611         {
2612             FcChar8 *p = (FcChar8 *) strchr ((const char *) e, ':');
2613             FcChar8 *s;
2614             size_t len;
2615
2616             if (!p)
2617             {
2618                 s = FcStrCopy (e);
2619                 e = NULL;
2620             }
2621             else
2622             {
2623                 *p = 0;
2624                 s = FcStrCopy (e);
2625                 e = p + 1;
2626             }
2627             len = strlen ((const char *) s);
2628             if (s[len - 1] == FC_DIR_SEPARATOR)
2629             {
2630                 do
2631                 {
2632                     len--;
2633                 }
2634                 while (len > 1 && s[len - 1] == FC_DIR_SEPARATOR);
2635                 s[len] = 0;
2636             }
2637             FcStrSetAdd (ret, s);
2638             FcStrFree (s);
2639         }
2640         FcStrFree (ee);
2641     }
2642     else
2643     {
2644         /* From spec doc at https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
2645          *
2646          * If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
2647          */
2648         FcStrSetAdd (ret, (const FcChar8 *) "/usr/local/share");
2649         FcStrSetAdd (ret, (const FcChar8 *) "/usr/share");
2650     }
2651
2652     return ret;
2653 }
2654
2655 FcBool
2656 FcConfigEnableHome (FcBool enable)
2657 {
2658     FcBool  prev = _FcConfigHomeEnabled;
2659     _FcConfigHomeEnabled = enable;
2660     return prev;
2661 }
2662
2663 FcChar8 *
2664 FcConfigGetFilename (FcConfig      *config,
2665                      const FcChar8 *url)
2666 {
2667     FcChar8    *file, *dir, **path, **p;
2668     const FcChar8 *sysroot;
2669
2670     config = FcConfigReference (config);
2671     if (!config)
2672         return NULL;
2673     sysroot = FcConfigGetSysRoot (config);
2674     if (!url || !*url)
2675     {
2676         url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
2677         if (!url)
2678             url = (FcChar8 *) FONTCONFIG_FILE;
2679     }
2680     file = 0;
2681
2682     if (FcStrIsAbsoluteFilename(url))
2683     {
2684         if (sysroot)
2685         {
2686             size_t len = strlen ((const char *) sysroot);
2687
2688             /* Workaround to avoid adding sysroot repeatedly */
2689             if (strncmp ((const char *) url, (const char *) sysroot, len) == 0)
2690                 sysroot = NULL;
2691         }
2692         file = FcConfigFileExists (sysroot, url);
2693         goto bail;
2694     }
2695
2696     if (*url == '~')
2697     {
2698         dir = FcConfigHome ();
2699         if (dir)
2700         {
2701             FcChar8 *s;
2702
2703             if (sysroot)
2704                 s = FcStrBuildFilename (sysroot, dir, NULL);
2705             else
2706                 s = dir;
2707             file = FcConfigFileExists (s, url + 1);
2708             if (sysroot)
2709                 FcStrFree (s);
2710         }
2711         else
2712             file = 0;
2713     }
2714     else
2715     {
2716         path = FcConfigGetPath ();
2717         if (!path)
2718         {
2719             file = NULL;
2720             goto bail;
2721         }
2722         for (p = path; *p; p++)
2723         {
2724             FcChar8 *s;
2725
2726             if (sysroot)
2727                 s = FcStrBuildFilename (sysroot, *p, NULL);
2728             else
2729                 s = *p;
2730             file = FcConfigFileExists (s, url);
2731             if (sysroot)
2732                 FcStrFree (s);
2733             if (file)
2734                 break;
2735         }
2736         FcConfigFreePath (path);
2737     }
2738 bail:
2739     FcConfigDestroy (config);
2740
2741     return file;
2742 }
2743
2744 FcChar8 *
2745 FcConfigFilename (const FcChar8 *url)
2746 {
2747     return FcConfigGetFilename (NULL, url);
2748 }
2749
2750 FcChar8 *
2751 FcConfigRealFilename (FcConfig          *config,
2752                       const FcChar8     *url)
2753 {
2754     FcChar8 *n = FcConfigGetFilename (config, url);
2755
2756     if (n)
2757     {
2758         FcChar8 buf[FC_PATH_MAX];
2759         ssize_t len;
2760         struct stat sb;
2761
2762         if ((len = FcReadLink (n, buf, sizeof (buf) - 1)) != -1)
2763         {
2764             buf[len] = 0;
2765
2766             /* We try to pick up a config from FONTCONFIG_FILE
2767              * when url is null. don't try to address the real filename
2768              * if it is a named pipe.
2769              */
2770             if (!url && FcStat (n, &sb) == 0 && S_ISFIFO (sb.st_mode))
2771                 return n;
2772             else if (!FcStrIsAbsoluteFilename (buf))
2773             {
2774                 FcChar8 *dirname = FcStrDirname (n);
2775                 FcStrFree (n);
2776                 if (!dirname)
2777                     return NULL;
2778
2779                 FcChar8 *path = FcStrBuildFilename (dirname, buf, NULL);
2780                 FcStrFree (dirname);
2781                 if (!path)
2782                     return NULL;
2783
2784                 n = FcStrCanonFilename (path);
2785                 FcStrFree (path);
2786             }
2787             else
2788             {
2789                 FcStrFree (n);
2790                 n = FcStrdup (buf);
2791             }
2792         }
2793     }
2794
2795     return n;
2796 }
2797
2798 /*
2799  * Manage the application-specific fonts
2800  */
2801
2802 FcBool
2803 FcConfigAppFontAddFile (FcConfig    *config,
2804                         const FcChar8  *file)
2805 {
2806     FcFontSet   *set;
2807     FcStrSet    *subdirs;
2808     FcStrList   *sublist;
2809     FcChar8     *subdir;
2810     FcBool      ret = FcTrue;
2811
2812     config = FcConfigReference (config);
2813     if (!config)
2814         return FcFalse;
2815
2816     subdirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
2817     if (!subdirs)
2818     {
2819         ret = FcFalse;
2820         goto bail;
2821     }
2822
2823     set = FcConfigGetFonts (config, FcSetApplication);
2824     if (!set)
2825     {
2826         set = FcFontSetCreate ();
2827         if (!set)
2828         {
2829             FcStrSetDestroy (subdirs);
2830             ret = FcFalse;
2831             goto bail;
2832         }
2833         FcConfigSetFonts (config, set, FcSetApplication);
2834     }
2835
2836     if (!FcFileScanConfig (set, subdirs, file, config))
2837     {
2838         FcStrSetDestroy (subdirs);
2839         ret = FcFalse;
2840         goto bail;
2841     }
2842     if ((sublist = FcStrListCreate (subdirs)))
2843     {
2844         while ((subdir = FcStrListNext (sublist)))
2845         {
2846             FcConfigAppFontAddDir (config, subdir);
2847         }
2848         FcStrListDone (sublist);
2849     }
2850     FcStrSetDestroy (subdirs);
2851 bail:
2852     FcConfigDestroy (config);
2853
2854     return ret;
2855 }
2856
2857 FcBool
2858 FcConfigAppFontAddDir (FcConfig     *config,
2859                        const FcChar8   *dir)
2860 {
2861     FcFontSet   *set;
2862     FcStrSet    *dirs;
2863     FcBool      ret = FcTrue;
2864
2865     config = FcConfigReference (config);
2866     if (!config)
2867         return FcFalse;
2868
2869     dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
2870     if (!dirs)
2871     {
2872         ret = FcFalse;
2873         goto bail;
2874     }
2875
2876     set = FcConfigGetFonts (config, FcSetApplication);
2877     if (!set)
2878     {
2879         set = FcFontSetCreate ();
2880         if (!set)
2881         {
2882             FcStrSetDestroy (dirs);
2883             ret = FcFalse;
2884             goto bail;
2885         }
2886         FcConfigSetFonts (config, set, FcSetApplication);
2887     }
2888
2889     FcStrSetAddFilename (dirs, dir);
2890
2891     if (!FcConfigAddDirList (config, FcSetApplication, dirs))
2892     {
2893         FcStrSetDestroy (dirs);
2894         ret = FcFalse;
2895         goto bail;
2896     }
2897     FcStrSetDestroy (dirs);
2898 bail:
2899     FcConfigDestroy (config);
2900
2901     return ret;
2902 }
2903
2904 void
2905 FcConfigAppFontClear (FcConfig      *config)
2906 {
2907     config = FcConfigReference (config);
2908     if (!config)
2909         return;
2910
2911     FcConfigSetFonts (config, 0, FcSetApplication);
2912
2913     FcConfigDestroy (config);
2914 }
2915
2916 /*
2917  * Manage filename-based font source selectors
2918  */
2919
2920 FcBool
2921 FcConfigGlobAdd (FcConfig       *config,
2922                  const FcChar8  *glob,
2923                  FcBool         accept)
2924 {
2925     FcStrSet    *set = accept ? config->acceptGlobs : config->rejectGlobs;
2926         FcChar8 *realglob = FcStrCopyFilename(glob);
2927         if (!realglob)
2928                 return FcFalse;
2929
2930     FcBool       ret = FcStrSetAdd (set, realglob);
2931     FcStrFree(realglob);
2932     return ret;
2933 }
2934
2935 static FcBool
2936 FcConfigGlobsMatch (const FcStrSet      *globs,
2937                     const FcChar8       *string)
2938 {
2939     int i;
2940
2941     for (i = 0; i < globs->num; i++)
2942         if (FcStrGlobMatch (globs->strs[i], string))
2943             return FcTrue;
2944     return FcFalse;
2945 }
2946
2947 FcBool
2948 FcConfigAcceptFilename (FcConfig        *config,
2949                         const FcChar8   *filename)
2950 {
2951     if (FcConfigGlobsMatch (config->acceptGlobs, filename))
2952         return FcTrue;
2953     if (FcConfigGlobsMatch (config->rejectGlobs, filename))
2954         return FcFalse;
2955     return FcTrue;
2956 }
2957
2958 /*
2959  * Manage font-pattern based font source selectors
2960  */
2961
2962 FcBool
2963 FcConfigPatternsAdd (FcConfig   *config,
2964                      FcPattern  *pattern,
2965                      FcBool     accept)
2966 {
2967     FcFontSet   *set = accept ? config->acceptPatterns : config->rejectPatterns;
2968
2969     return FcFontSetAdd (set, pattern);
2970 }
2971
2972 static FcBool
2973 FcConfigPatternsMatch (const FcFontSet  *patterns,
2974                        const FcPattern  *font)
2975 {
2976     int i;
2977
2978     for (i = 0; i < patterns->nfont; i++)
2979         if (FcListPatternMatchAny (patterns->fonts[i], font))
2980             return FcTrue;
2981     return FcFalse;
2982 }
2983
2984 FcBool
2985 FcConfigAcceptFont (FcConfig        *config,
2986                     const FcPattern *font)
2987 {
2988     if (FcConfigPatternsMatch (config->acceptPatterns, font))
2989         return FcTrue;
2990     if (FcConfigPatternsMatch (config->rejectPatterns, font))
2991         return FcFalse;
2992     return FcTrue;
2993 }
2994
2995 const FcChar8 *
2996 FcConfigGetSysRoot (const FcConfig *config)
2997 {
2998     if (!config)
2999     {
3000         config = FcConfigGetCurrent ();
3001         if (!config)
3002             return NULL;
3003     }
3004     return config->sysRoot;
3005 }
3006
3007 void
3008 FcConfigSetSysRoot (FcConfig      *config,
3009                     const FcChar8 *sysroot)
3010 {
3011     FcChar8 *s = NULL;
3012     FcBool init = FcFalse;
3013     int nretry = 3;
3014
3015 retry:
3016     if (!config)
3017     {
3018         /* We can't use FcConfigGetCurrent() here to ensure
3019          * the sysroot is set prior to initialize FcConfig,
3020          * to avoid loading caches from non-sysroot dirs.
3021          * So postpone the initialization later.
3022          */
3023         config = fc_atomic_ptr_get (&_fcConfig);
3024         if (!config)
3025         {
3026             config = FcConfigCreate ();
3027             if (!config)
3028                 return;
3029             init = FcTrue;
3030         }
3031     }
3032
3033     if (sysroot)
3034     {
3035         s = FcStrRealPath (sysroot);
3036         if (!s)
3037             return;
3038     }
3039
3040     if (config->sysRoot)
3041         FcStrFree (config->sysRoot);
3042
3043     config->sysRoot = s;
3044     if (init)
3045     {
3046         config = FcInitLoadOwnConfigAndFonts (config);
3047         if (!config)
3048         {
3049             /* Something failed. this is usually unlikely. so retrying */
3050             init = FcFalse;
3051             if (--nretry == 0)
3052             {
3053                 fprintf (stderr, "Fontconfig warning: Unable to initialize config and retry limit exceeded. sysroot functionality may not work as expected.\n");
3054                 return;
3055             }
3056             goto retry;
3057         }
3058         FcConfigSetCurrent (config);
3059         /* FcConfigSetCurrent() increases the refcount.
3060          * decrease it here to avoid the memory leak.
3061          */
3062         FcConfigDestroy (config);
3063     }
3064 }
3065
3066 FcRuleSet *
3067 FcRuleSetCreate (const FcChar8 *name)
3068 {
3069     FcRuleSet *ret = (FcRuleSet *) malloc (sizeof (FcRuleSet));
3070     FcMatchKind k;
3071     const FcChar8 *p;
3072
3073     if (!name)
3074         p = (const FcChar8 *)"";
3075     else
3076         p = name;
3077
3078     if (ret)
3079     {
3080         ret->name = FcStrdup (p);
3081         ret->description = NULL;
3082         ret->domain = NULL;
3083         for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
3084             ret->subst[k] = FcPtrListCreate (FcDestroyAsRule);
3085         FcRefInit (&ret->ref, 1);
3086     }
3087
3088     return ret;
3089 }
3090
3091 void
3092 FcRuleSetDestroy (FcRuleSet *rs)
3093 {
3094     FcMatchKind k;
3095
3096     if (!rs)
3097         return;
3098     if (FcRefDec (&rs->ref) != 1)
3099         return;
3100
3101     if (rs->name)
3102         FcStrFree (rs->name);
3103     if (rs->description)
3104         FcStrFree (rs->description);
3105     if (rs->domain)
3106         FcStrFree (rs->domain);
3107     for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
3108         FcPtrListDestroy (rs->subst[k]);
3109
3110     free (rs);
3111 }
3112
3113 void
3114 FcRuleSetReference (FcRuleSet *rs)
3115 {
3116     if (!FcRefIsConst (&rs->ref))
3117         FcRefInc (&rs->ref);
3118 }
3119
3120 void
3121 FcRuleSetEnable (FcRuleSet      *rs,
3122                  FcBool         flag)
3123 {
3124     if (rs)
3125     {
3126         rs->enabled = flag;
3127         /* XXX: we may want to provide a feature
3128          * to enable/disable rulesets through API
3129          * in the future?
3130          */
3131     }
3132 }
3133
3134 void
3135 FcRuleSetAddDescription (FcRuleSet      *rs,
3136                          const FcChar8  *domain,
3137                          const FcChar8  *description)
3138 {
3139     if (rs->domain)
3140         FcStrFree (rs->domain);
3141     if (rs->description)
3142         FcStrFree (rs->description);
3143
3144     rs->domain = domain ? FcStrdup (domain) : NULL;
3145     rs->description = description ? FcStrdup (description) : NULL;
3146 }
3147
3148 int
3149 FcRuleSetAdd (FcRuleSet         *rs,
3150               FcRule            *rule,
3151               FcMatchKind       kind)
3152 {
3153     FcPtrListIter iter;
3154     FcRule *r;
3155     int n = 0, ret;
3156
3157     if (!rs ||
3158        kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
3159         return -1;
3160     FcPtrListIterInitAtLast (rs->subst[kind], &iter);
3161     if (!FcPtrListIterAdd (rs->subst[kind], &iter, rule))
3162         return -1;
3163
3164     for (r = rule; r; r = r->next)
3165     {
3166         switch (r->type)
3167         {
3168         case FcRuleTest:
3169             if (r->u.test)
3170             {
3171                 if (r->u.test->kind == FcMatchDefault)
3172                     r->u.test->kind = kind;
3173                 if (n < r->u.test->object)
3174                     n = r->u.test->object;
3175             }
3176             break;
3177         case FcRuleEdit:
3178             if (n < r->u.edit->object)
3179                 n = r->u.edit->object;
3180             break;
3181         default:
3182             break;
3183         }
3184     }
3185     if (FcDebug () & FC_DBG_EDIT)
3186     {
3187         printf ("Add Rule(kind:%d, name: %s) ", kind, rs->name);
3188         FcRulePrint (rule);
3189     }
3190     ret = FC_OBJ_ID (n) - FC_MAX_BASE_OBJECT;
3191
3192     return ret < 0 ? 0 : ret;
3193 }
3194
3195 void
3196 FcConfigFileInfoIterInit (FcConfig              *config,
3197                           FcConfigFileInfoIter  *iter)
3198 {
3199     FcConfig *c;
3200     FcPtrListIter *i = (FcPtrListIter *)iter;
3201
3202     if (!config)
3203         c = FcConfigGetCurrent ();
3204     else
3205         c = config;
3206     FcPtrListIterInit (c->rulesetList, i);
3207 }
3208
3209 FcBool
3210 FcConfigFileInfoIterNext (FcConfig              *config,
3211                           FcConfigFileInfoIter  *iter)
3212 {
3213     FcConfig *c;
3214     FcPtrListIter *i = (FcPtrListIter *)iter;
3215
3216     if (!config)
3217         c = FcConfigGetCurrent ();
3218     else
3219         c = config;
3220     if (FcPtrListIterIsValid (c->rulesetList, i))
3221     {
3222         FcPtrListIterNext (c->rulesetList, i);
3223     }
3224     else
3225         return FcFalse;
3226
3227     return FcTrue;
3228 }
3229
3230 FcBool
3231 FcConfigFileInfoIterGet (FcConfig               *config,
3232                          FcConfigFileInfoIter   *iter,
3233                          FcChar8                **name,
3234                          FcChar8                **description,
3235                          FcBool                 *enabled)
3236 {
3237     FcConfig *c;
3238     FcRuleSet *r;
3239     FcPtrListIter *i = (FcPtrListIter *)iter;
3240
3241     if (!config)
3242         c = FcConfigGetCurrent ();
3243     else
3244         c = config;
3245     if (!FcPtrListIterIsValid (c->rulesetList, i))
3246         return FcFalse;
3247     r = FcPtrListIterGetValue (c->rulesetList, i);
3248     if (name)
3249         *name = FcStrdup (r->name && r->name[0] ? r->name : (const FcChar8 *) "fonts.conf");
3250     if (description)
3251         *description = FcStrdup (!r->description ? _("No description") :
3252                                  dgettext (r->domain ? (const char *) r->domain : GETTEXT_PACKAGE "-conf",
3253                                            (const char *) r->description));
3254     if (enabled)
3255         *enabled = r->enabled;
3256
3257     return FcTrue;
3258 }
3259
3260 #define __fccfg__
3261 #include "fcaliastail.h"
3262 #undef __fccfg__