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