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