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