12c393a42Smrg/*
2a6844aabSmrg * fontconfig/src/fccfg.c
32c393a42Smrg *
42c393a42Smrg * Copyright © 2000 Keith Packard
52c393a42Smrg *
62c393a42Smrg * Permission to use, copy, modify, distribute, and sell this software and its
72c393a42Smrg * documentation for any purpose is hereby granted without fee, provided that
82c393a42Smrg * the above copyright notice appear in all copies and that both that
92c393a42Smrg * copyright notice and this permission notice appear in supporting
10ca08ab68Smrg * documentation, and that the name of the author(s) not be used in
112c393a42Smrg * advertising or publicity pertaining to distribution of the software without
12ca08ab68Smrg * specific, written prior permission.  The authors make no
132c393a42Smrg * representations about the suitability of this software for any purpose.  It
142c393a42Smrg * is provided "as is" without express or implied warranty.
152c393a42Smrg *
16a6844aabSmrg * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
172c393a42Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18a6844aabSmrg * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
192c393a42Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
202c393a42Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
212c393a42Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
222c393a42Smrg * PERFORMANCE OF THIS SOFTWARE.
232c393a42Smrg */
242c393a42Smrg
25c9710b42Smrg/* Objects MT-safe for readonly access. */
26c9710b42Smrg
272c393a42Smrg#include "fcint.h"
287872e0a1Smrg#ifdef HAVE_DIRENT_H
292c393a42Smrg#include <dirent.h>
307872e0a1Smrg#endif
312c393a42Smrg#include <sys/types.h>
322c393a42Smrg
332c393a42Smrg#if defined (_WIN32) && !defined (R_OK)
342c393a42Smrg#define R_OK 4
352c393a42Smrg#endif
362c393a42Smrg
377872e0a1Smrg#if defined(_WIN32) && !defined(S_ISFIFO)
387872e0a1Smrg#define S_ISFIFO(m) 0
397872e0a1Smrg#endif
407872e0a1Smrg
41c9710b42Smrgstatic FcConfig    *_fcConfig; /* MT-safe */
427872e0a1Smrgstatic FcMutex	   *_lock;
437872e0a1Smrg
447872e0a1Smrgstatic void
457872e0a1Smrglock_config (void)
467872e0a1Smrg{
477872e0a1Smrg    FcMutex *lock;
487872e0a1Smrgretry:
497872e0a1Smrg    lock = fc_atomic_ptr_get (&_lock);
507872e0a1Smrg    if (!lock)
517872e0a1Smrg    {
527872e0a1Smrg	lock = (FcMutex *) malloc (sizeof (FcMutex));
537872e0a1Smrg	FcMutexInit (lock);
547872e0a1Smrg	if (!fc_atomic_ptr_cmpexch (&_lock, NULL, lock))
557872e0a1Smrg	{
567872e0a1Smrg	    FcMutexFinish (lock);
577872e0a1Smrg	    free (lock);
587872e0a1Smrg	    goto retry;
597872e0a1Smrg	}
607872e0a1Smrg	FcMutexLock (lock);
617872e0a1Smrg	/* Initialize random state */
627872e0a1Smrg	FcRandom ();
637872e0a1Smrg	return;
647872e0a1Smrg    }
657872e0a1Smrg    FcMutexLock (lock);
667872e0a1Smrg}
677872e0a1Smrg
687872e0a1Smrgstatic void
697872e0a1Smrgunlock_config (void)
707872e0a1Smrg{
717872e0a1Smrg    FcMutex *lock;
727872e0a1Smrg    lock = fc_atomic_ptr_get (&_lock);
737872e0a1Smrg    FcMutexUnlock (lock);
747872e0a1Smrg}
757872e0a1Smrg
767872e0a1Smrgstatic void
777872e0a1Smrgfree_lock (void)
787872e0a1Smrg{
797872e0a1Smrg    FcMutex *lock;
807872e0a1Smrg    lock = fc_atomic_ptr_get (&_lock);
817872e0a1Smrg    if (lock && fc_atomic_ptr_cmpexch (&_lock, lock, NULL))
827872e0a1Smrg    {
837872e0a1Smrg	FcMutexFinish (lock);
847872e0a1Smrg	free (lock);
857872e0a1Smrg    }
867872e0a1Smrg}
87c9710b42Smrg
88c9710b42Smrgstatic FcConfig *
89c9710b42SmrgFcConfigEnsure (void)
90c9710b42Smrg{
91c9710b42Smrg    FcConfig	*config;
92c9710b42Smrgretry:
93c9710b42Smrg    config = fc_atomic_ptr_get (&_fcConfig);
94c9710b42Smrg    if (!config)
95c9710b42Smrg    {
96c9710b42Smrg	config = FcInitLoadConfigAndFonts ();
97c9710b42Smrg
987872e0a1Smrg	if (!config || !fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
997872e0a1Smrg	    if (config)
1007872e0a1Smrg		FcConfigDestroy (config);
101c9710b42Smrg	    goto retry;
102c9710b42Smrg	}
103c9710b42Smrg    }
104c9710b42Smrg    return config;
105c9710b42Smrg}
106c9710b42Smrg
1071887081fSmrgstatic void
1081887081fSmrgFcDestroyAsRule (void *data)
1091887081fSmrg{
1101887081fSmrg    FcRuleDestroy (data);
1111887081fSmrg}
1121887081fSmrg
1131887081fSmrgstatic void
1141887081fSmrgFcDestroyAsRuleSet (void *data)
1151887081fSmrg{
1161887081fSmrg    FcRuleSetDestroy (data);
1171887081fSmrg}
1181887081fSmrg
119c9710b42SmrgFcBool
120c9710b42SmrgFcConfigInit (void)
121c9710b42Smrg{
122c9710b42Smrg  return FcConfigEnsure () ? FcTrue : FcFalse;
123c9710b42Smrg}
124c9710b42Smrg
125c9710b42Smrgvoid
126c9710b42SmrgFcConfigFini (void)
127c9710b42Smrg{
128c9710b42Smrg    FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
129c9710b42Smrg    if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
130c9710b42Smrg	FcConfigDestroy (cfg);
1317872e0a1Smrg    free_lock ();
132c9710b42Smrg}
133c9710b42Smrg
1342c393a42SmrgFcConfig *
1352c393a42SmrgFcConfigCreate (void)
1362c393a42Smrg{
1372c393a42Smrg    FcSetName	set;
1382c393a42Smrg    FcConfig	*config;
1391887081fSmrg    FcMatchKind	k;
1401887081fSmrg    FcBool	err = FcFalse;
1412c393a42Smrg
1422c393a42Smrg    config = malloc (sizeof (FcConfig));
1432c393a42Smrg    if (!config)
1442c393a42Smrg	goto bail0;
145ca08ab68Smrg
1462c393a42Smrg    config->configDirs = FcStrSetCreate ();
1472c393a42Smrg    if (!config->configDirs)
1482c393a42Smrg	goto bail1;
149ca08ab68Smrg
1502c393a42Smrg    config->configFiles = FcStrSetCreate ();
1512c393a42Smrg    if (!config->configFiles)
1522c393a42Smrg	goto bail2;
153ca08ab68Smrg
1542c393a42Smrg    config->fontDirs = FcStrSetCreate ();
1552c393a42Smrg    if (!config->fontDirs)
1562c393a42Smrg	goto bail3;
157ca08ab68Smrg
1582c393a42Smrg    config->acceptGlobs = FcStrSetCreate ();
1592c393a42Smrg    if (!config->acceptGlobs)
1602c393a42Smrg	goto bail4;
1612c393a42Smrg
1622c393a42Smrg    config->rejectGlobs = FcStrSetCreate ();
1632c393a42Smrg    if (!config->rejectGlobs)
1642c393a42Smrg	goto bail5;
1652c393a42Smrg
1662c393a42Smrg    config->acceptPatterns = FcFontSetCreate ();
1672c393a42Smrg    if (!config->acceptPatterns)
1682c393a42Smrg	goto bail6;
169ca08ab68Smrg
1702c393a42Smrg    config->rejectPatterns = FcFontSetCreate ();
1712c393a42Smrg    if (!config->rejectPatterns)
1722c393a42Smrg	goto bail7;
1732c393a42Smrg
1742c393a42Smrg    config->cacheDirs = FcStrSetCreate ();
1752c393a42Smrg    if (!config->cacheDirs)
1762c393a42Smrg	goto bail8;
177ca08ab68Smrg
1781887081fSmrg    for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
1791887081fSmrg    {
1801887081fSmrg	config->subst[k] = FcPtrListCreate (FcDestroyAsRuleSet);
1811887081fSmrg	if (!config->subst[k])
1821887081fSmrg	    err = FcTrue;
1831887081fSmrg    }
1841887081fSmrg    if (err)
1851887081fSmrg	goto bail9;
1862c393a42Smrg
1872c393a42Smrg    config->maxObjects = 0;
1882c393a42Smrg    for (set = FcSetSystem; set <= FcSetApplication; set++)
1892c393a42Smrg	config->fonts[set] = 0;
1902c393a42Smrg
1912c393a42Smrg    config->rescanTime = time(0);
192ca08ab68Smrg    config->rescanInterval = 30;
193a6844aabSmrg
194a6844aabSmrg    config->expr_pool = NULL;
195a6844aabSmrg
1967872e0a1Smrg    config->sysRoot = FcStrRealPath ((const FcChar8 *) getenv("FONTCONFIG_SYSROOT"));
197c9710b42Smrg
1981887081fSmrg    config->rulesetList = FcPtrListCreate (FcDestroyAsRuleSet);
1991887081fSmrg    if (!config->rulesetList)
2001887081fSmrg	goto bail9;
2011887081fSmrg    config->availConfigFiles = FcStrSetCreate ();
2021887081fSmrg    if (!config->availConfigFiles)
2031887081fSmrg	goto bail10;
2041887081fSmrg
205c9710b42Smrg    FcRefInit (&config->ref, 1);
206ca08ab68Smrg
2072c393a42Smrg    return config;
2082c393a42Smrg
2091887081fSmrgbail10:
2101887081fSmrg    FcPtrListDestroy (config->rulesetList);
2111887081fSmrgbail9:
2121887081fSmrg    for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
2131887081fSmrg	if (config->subst[k])
2141887081fSmrg	    FcPtrListDestroy (config->subst[k]);
2151887081fSmrg    FcStrSetDestroy (config->cacheDirs);
2162c393a42Smrgbail8:
2172c393a42Smrg    FcFontSetDestroy (config->rejectPatterns);
2182c393a42Smrgbail7:
2192c393a42Smrg    FcFontSetDestroy (config->acceptPatterns);
2202c393a42Smrgbail6:
2212c393a42Smrg    FcStrSetDestroy (config->rejectGlobs);
2222c393a42Smrgbail5:
2232c393a42Smrg    FcStrSetDestroy (config->acceptGlobs);
2242c393a42Smrgbail4:
2252c393a42Smrg    FcStrSetDestroy (config->fontDirs);
2262c393a42Smrgbail3:
2272c393a42Smrg    FcStrSetDestroy (config->configFiles);
2282c393a42Smrgbail2:
2292c393a42Smrg    FcStrSetDestroy (config->configDirs);
2302c393a42Smrgbail1:
2312c393a42Smrg    free (config);
2322c393a42Smrgbail0:
2332c393a42Smrg    return 0;
2342c393a42Smrg}
2352c393a42Smrg
2362c393a42Smrgstatic FcFileTime
2372c393a42SmrgFcConfigNewestFile (FcStrSet *files)
2382c393a42Smrg{
2392c393a42Smrg    FcStrList	    *list = FcStrListCreate (files);
2402c393a42Smrg    FcFileTime	    newest = { 0, FcFalse };
2412c393a42Smrg    FcChar8	    *file;
2422c393a42Smrg    struct  stat    statb;
2432c393a42Smrg
2442c393a42Smrg    if (list)
2452c393a42Smrg    {
2462c393a42Smrg	while ((file = FcStrListNext (list)))
247ca08ab68Smrg	    if (FcStat (file, &statb) == 0)
2482c393a42Smrg		if (!newest.set || statb.st_mtime - newest.time > 0)
2492c393a42Smrg		{
2502c393a42Smrg		    newest.set = FcTrue;
2512c393a42Smrg		    newest.time = statb.st_mtime;
2522c393a42Smrg		}
2532c393a42Smrg	FcStrListDone (list);
2542c393a42Smrg    }
2552c393a42Smrg    return newest;
2562c393a42Smrg}
2572c393a42Smrg
2582c393a42SmrgFcBool
2592c393a42SmrgFcConfigUptoDate (FcConfig *config)
2602c393a42Smrg{
2612c393a42Smrg    FcFileTime	config_time, config_dir_time, font_time;
2622c393a42Smrg    time_t	now = time(0);
2637872e0a1Smrg    FcBool	ret = FcTrue;
2647872e0a1Smrg
2657872e0a1Smrg    config = FcConfigReference (config);
2662c393a42Smrg    if (!config)
2677872e0a1Smrg	return FcFalse;
2687872e0a1Smrg
2692c393a42Smrg    config_time = FcConfigNewestFile (config->configFiles);
2702c393a42Smrg    config_dir_time = FcConfigNewestFile (config->configDirs);
2712c393a42Smrg    font_time = FcConfigNewestFile (config->fontDirs);
2722c393a42Smrg    if ((config_time.set && config_time.time - config->rescanTime > 0) ||
2732c393a42Smrg	(config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) ||
2742c393a42Smrg	(font_time.set && (font_time.time - config->rescanTime) > 0))
2752c393a42Smrg    {
2762c393a42Smrg	/* We need to check for potential clock problems here (OLPC ticket #6046) */
2772c393a42Smrg	if ((config_time.set && (config_time.time - now) > 0) ||
2782c393a42Smrg    	(config_dir_time.set && (config_dir_time.time - now) > 0) ||
2792c393a42Smrg        (font_time.set && (font_time.time - now) > 0))
2802c393a42Smrg	{
2812c393a42Smrg	    fprintf (stderr,
282c9710b42Smrg                    "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n");
2832c393a42Smrg	    config->rescanTime = now;
2847872e0a1Smrg	    goto bail;
2852c393a42Smrg	}
2862c393a42Smrg	else
2877872e0a1Smrg	{
2887872e0a1Smrg	    ret = FcFalse;
2897872e0a1Smrg	    goto bail;
2907872e0a1Smrg	}
2912c393a42Smrg    }
2922c393a42Smrg    config->rescanTime = now;
2937872e0a1Smrgbail:
2947872e0a1Smrg    FcConfigDestroy (config);
2957872e0a1Smrg
2967872e0a1Smrg    return ret;
2972c393a42Smrg}
2982c393a42Smrg
299a6844aabSmrgFcExpr *
300a6844aabSmrgFcConfigAllocExpr (FcConfig *config)
301a6844aabSmrg{
3026fc018e4Smrg    if (!config->expr_pool || config->expr_pool->next == config->expr_pool->end)
3036fc018e4Smrg    {
3046fc018e4Smrg	FcExprPage *new_page;
305a6844aabSmrg
3066fc018e4Smrg	new_page = malloc (sizeof (FcExprPage));
3076fc018e4Smrg	if (!new_page)
3086fc018e4Smrg	    return 0;
309a6844aabSmrg
3106fc018e4Smrg	new_page->next_page = config->expr_pool;
3116fc018e4Smrg	new_page->next = new_page->exprs;
3126fc018e4Smrg	config->expr_pool = new_page;
3136fc018e4Smrg    }
314a6844aabSmrg
3156fc018e4Smrg    return config->expr_pool->next++;
316a6844aabSmrg}
317a6844aabSmrg
318a6844aabSmrgFcConfig *
319a6844aabSmrgFcConfigReference (FcConfig *config)
320a6844aabSmrg{
321a6844aabSmrg    if (!config)
322a6844aabSmrg    {
3237872e0a1Smrg	/* lock during obtaining the value from _fcConfig and count up refcount there,
3247872e0a1Smrg	 * there are the race between them.
3257872e0a1Smrg	 */
3267872e0a1Smrg	lock_config ();
3277872e0a1Smrg    retry:
3287872e0a1Smrg	config = fc_atomic_ptr_get (&_fcConfig);
329a6844aabSmrg	if (!config)
3307872e0a1Smrg	{
3317872e0a1Smrg	    unlock_config ();
332a6844aabSmrg
3337872e0a1Smrg	    config = FcInitLoadConfigAndFonts ();
3347872e0a1Smrg	    if (!config)
3357872e0a1Smrg		goto retry;
3367872e0a1Smrg	    lock_config ();
3377872e0a1Smrg	    if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config))
3387872e0a1Smrg	    {
3397872e0a1Smrg		FcConfigDestroy (config);
3407872e0a1Smrg		goto retry;
3417872e0a1Smrg	    }
3427872e0a1Smrg	}
3437872e0a1Smrg	FcRefInc (&config->ref);
3447872e0a1Smrg	unlock_config ();
3457872e0a1Smrg    }
3467872e0a1Smrg    else
3477872e0a1Smrg	FcRefInc (&config->ref);
348a6844aabSmrg
349a6844aabSmrg    return config;
350a6844aabSmrg}
351a6844aabSmrg
3522c393a42Smrgvoid
3532c393a42SmrgFcConfigDestroy (FcConfig *config)
3542c393a42Smrg{
3552c393a42Smrg    FcSetName	set;
356a6844aabSmrg    FcExprPage	*page;
3571887081fSmrg    FcMatchKind	k;
358a6844aabSmrg
35946bb3e47Smrg    if (config)
36046bb3e47Smrg    {
36146bb3e47Smrg	if (FcRefDec (&config->ref) != 1)
36246bb3e47Smrg	    return;
3632c393a42Smrg
36446bb3e47Smrg	(void) fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
3652c393a42Smrg
36646bb3e47Smrg	FcStrSetDestroy (config->configDirs);
36746bb3e47Smrg	FcStrSetDestroy (config->fontDirs);
36846bb3e47Smrg	FcStrSetDestroy (config->cacheDirs);
36946bb3e47Smrg	FcStrSetDestroy (config->configFiles);
37046bb3e47Smrg	FcStrSetDestroy (config->acceptGlobs);
37146bb3e47Smrg	FcStrSetDestroy (config->rejectGlobs);
37246bb3e47Smrg	FcFontSetDestroy (config->acceptPatterns);
37346bb3e47Smrg	FcFontSetDestroy (config->rejectPatterns);
3742c393a42Smrg
37546bb3e47Smrg	for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
37646bb3e47Smrg	    FcPtrListDestroy (config->subst[k]);
37746bb3e47Smrg	FcPtrListDestroy (config->rulesetList);
37846bb3e47Smrg	FcStrSetDestroy (config->availConfigFiles);
37946bb3e47Smrg	for (set = FcSetSystem; set <= FcSetApplication; set++)
38046bb3e47Smrg	    if (config->fonts[set])
38146bb3e47Smrg		FcFontSetDestroy (config->fonts[set]);
38246bb3e47Smrg
38346bb3e47Smrg	page = config->expr_pool;
38446bb3e47Smrg	while (page)
38546bb3e47Smrg	{
38646bb3e47Smrg	    FcExprPage *next = page->next_page;
38746bb3e47Smrg	    free (page);
38846bb3e47Smrg	    page = next;
38946bb3e47Smrg	}
39046bb3e47Smrg	if (config->sysRoot)
391c9710b42Smrg	FcStrFree (config->sysRoot);
392a6844aabSmrg
39346bb3e47Smrg	free (config);
39446bb3e47Smrg    }
3952c393a42Smrg}
3962c393a42Smrg
3972c393a42Smrg/*
3982c393a42Smrg * Add cache to configuration, adding fonts and directories
3992c393a42Smrg */
4002c393a42Smrg
4012c393a42SmrgFcBool
402ca08ab68SmrgFcConfigAddCache (FcConfig *config, FcCache *cache,
4031887081fSmrg		  FcSetName set, FcStrSet *dirSet, FcChar8 *forDir)
4042c393a42Smrg{
4052c393a42Smrg    FcFontSet	*fs;
4062c393a42Smrg    intptr_t	*dirs;
4072c393a42Smrg    int		i;
4081887081fSmrg    FcBool      relocated = FcFalse;
4091887081fSmrg
4101887081fSmrg    if (strcmp ((char *)FcCacheDir(cache), (char *)forDir) != 0)
4111887081fSmrg      relocated = FcTrue;
4122c393a42Smrg
4132c393a42Smrg    /*
4142c393a42Smrg     * Add fonts
4152c393a42Smrg     */
4162c393a42Smrg    fs = FcCacheSet (cache);
4172c393a42Smrg    if (fs)
4182c393a42Smrg    {
4192c393a42Smrg	int	nref = 0;
42046bb3e47Smrg
4212c393a42Smrg	for (i = 0; i < fs->nfont; i++)
4222c393a42Smrg	{
4232c393a42Smrg	    FcPattern	*font = FcFontSetFont (fs, i);
4242c393a42Smrg	    FcChar8	*font_file;
4251887081fSmrg	    FcChar8	*relocated_font_file = NULL;
4262c393a42Smrg
4272c393a42Smrg	    if (FcPatternObjectGetString (font, FC_FILE_OBJECT,
4281887081fSmrg					  0, &font_file) == FcResultMatch)
4292c393a42Smrg	    {
4301887081fSmrg		if (relocated)
4311887081fSmrg		  {
4321887081fSmrg		    FcChar8 *slash = FcStrLastSlash (font_file);
4331887081fSmrg		    relocated_font_file = FcStrBuildFilename (forDir, slash + 1, NULL);
4341887081fSmrg		    font_file = relocated_font_file;
4351887081fSmrg		  }
4361887081fSmrg
4371887081fSmrg		/*
4381887081fSmrg		 * Check to see if font is banned by filename
4391887081fSmrg		 */
4401887081fSmrg		if (!FcConfigAcceptFilename (config, font_file))
4411887081fSmrg		{
4421887081fSmrg		    free (relocated_font_file);
4431887081fSmrg		    continue;
4441887081fSmrg		}
4452c393a42Smrg	    }
4461887081fSmrg
4472c393a42Smrg	    /*
4482c393a42Smrg	     * Check to see if font is banned by pattern
4492c393a42Smrg	     */
4502c393a42Smrg	    if (!FcConfigAcceptFont (config, font))
4511887081fSmrg	    {
4521887081fSmrg		free (relocated_font_file);
4532c393a42Smrg		continue;
4541887081fSmrg	    }
4551887081fSmrg
4561887081fSmrg	    if (relocated_font_file)
4571887081fSmrg	    {
4581887081fSmrg	      font = FcPatternCacheRewriteFile (font, cache, relocated_font_file);
4591887081fSmrg	      free (relocated_font_file);
4601887081fSmrg	    }
4611887081fSmrg
462c9710b42Smrg	    if (FcFontSetAdd (config->fonts[set], font))
463c9710b42Smrg		nref++;
4642c393a42Smrg	}
4652c393a42Smrg	FcDirCacheReference (cache, nref);
4662c393a42Smrg    }
4672c393a42Smrg
4682c393a42Smrg    /*
4692c393a42Smrg     * Add directories
4702c393a42Smrg     */
4712c393a42Smrg    dirs = FcCacheDirs (cache);
4722c393a42Smrg    if (dirs)
4732c393a42Smrg    {
4742c393a42Smrg	for (i = 0; i < cache->dirs_count; i++)
4752c393a42Smrg	{
4761887081fSmrg	    const FcChar8 *dir = FcCacheSubdir (cache, i);
4771887081fSmrg	    FcChar8 *s = NULL;
4781887081fSmrg
4791887081fSmrg	    if (relocated)
4801887081fSmrg	    {
4811887081fSmrg		FcChar8 *base = FcStrBasename (dir);
4821887081fSmrg		dir = s = FcStrBuildFilename (forDir, base, NULL);
4831887081fSmrg		FcStrFree (base);
4841887081fSmrg	    }
4852c393a42Smrg	    if (FcConfigAcceptFilename (config, dir))
4862c393a42Smrg		FcStrSetAddFilename (dirSet, dir);
4871887081fSmrg	    if (s)
4881887081fSmrg		FcStrFree (s);
4892c393a42Smrg	}
4902c393a42Smrg    }
4912c393a42Smrg    return FcTrue;
4922c393a42Smrg}
4932c393a42Smrg
4942c393a42Smrgstatic FcBool
4952c393a42SmrgFcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet)
4962c393a42Smrg{
4972c393a42Smrg    FcStrList	    *dirlist;
4982c393a42Smrg    FcChar8	    *dir;
4992c393a42Smrg    FcCache	    *cache;
500ca08ab68Smrg
5012c393a42Smrg    dirlist = FcStrListCreate (dirSet);
5022c393a42Smrg    if (!dirlist)
5032c393a42Smrg        return FcFalse;
50446bb3e47Smrg
5052c393a42Smrg    while ((dir = FcStrListNext (dirlist)))
5062c393a42Smrg    {
5072c393a42Smrg	if (FcDebug () & FC_DBG_FONTSET)
50818bd4a06Smrg	    printf ("adding fonts from %s\n", dir);
5092c393a42Smrg	cache = FcDirCacheRead (dir, FcFalse, config);
5102c393a42Smrg	if (!cache)
5112c393a42Smrg	    continue;
5121887081fSmrg	FcConfigAddCache (config, cache, set, dirSet, dir);
5132c393a42Smrg	FcDirCacheUnload (cache);
5142c393a42Smrg    }
5152c393a42Smrg    FcStrListDone (dirlist);
5162c393a42Smrg    return FcTrue;
5172c393a42Smrg}
5182c393a42Smrg
5192c393a42Smrg/*
5202c393a42Smrg * Scan the current list of directories in the configuration
5212c393a42Smrg * and build the set of available fonts.
5222c393a42Smrg */
5232c393a42Smrg
5242c393a42SmrgFcBool
5252c393a42SmrgFcConfigBuildFonts (FcConfig *config)
5262c393a42Smrg{
5272c393a42Smrg    FcFontSet	    *fonts;
5287872e0a1Smrg    FcBool	    ret = FcTrue;
5292c393a42Smrg
5307872e0a1Smrg    config = FcConfigReference (config);
5312c393a42Smrg    if (!config)
5327872e0a1Smrg	return FcFalse;
53346bb3e47Smrg
5342c393a42Smrg    fonts = FcFontSetCreate ();
5352c393a42Smrg    if (!fonts)
5367872e0a1Smrg    {
5377872e0a1Smrg	ret = FcFalse;
5387872e0a1Smrg	goto bail;
5397872e0a1Smrg    }
540ca08ab68Smrg
5412c393a42Smrg    FcConfigSetFonts (config, fonts, FcSetSystem);
542ca08ab68Smrg
5432c393a42Smrg    if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs))
5447872e0a1Smrg    {
5457872e0a1Smrg	ret = FcFalse;
5467872e0a1Smrg	goto bail;
5477872e0a1Smrg    }
5482c393a42Smrg    if (FcDebug () & FC_DBG_FONTSET)
5492c393a42Smrg	FcFontSetPrint (fonts);
5507872e0a1Smrgbail:
5517872e0a1Smrg    FcConfigDestroy (config);
5527872e0a1Smrg
5537872e0a1Smrg    return ret;
5542c393a42Smrg}
5552c393a42Smrg
5562c393a42SmrgFcBool
5572c393a42SmrgFcConfigSetCurrent (FcConfig *config)
5582c393a42Smrg{
559c9710b42Smrg    FcConfig *cfg;
560c9710b42Smrg
5617872e0a1Smrg    if (config)
5627872e0a1Smrg    {
5637872e0a1Smrg	if (!config->fonts[FcSetSystem])
5647872e0a1Smrg	    if (!FcConfigBuildFonts (config))
5657872e0a1Smrg		return FcFalse;
5667872e0a1Smrg	FcRefInc (&config->ref);
5677872e0a1Smrg    }
5687872e0a1Smrg
5697872e0a1Smrg    lock_config ();
570c9710b42Smrgretry:
571c9710b42Smrg    cfg = fc_atomic_ptr_get (&_fcConfig);
572c9710b42Smrg
573c9710b42Smrg    if (config == cfg)
5747872e0a1Smrg    {
5757872e0a1Smrg	unlock_config ();
5767872e0a1Smrg	if (config)
5777872e0a1Smrg	    FcConfigDestroy (config);
5782c393a42Smrg	return FcTrue;
5797872e0a1Smrg    }
5802c393a42Smrg
581c9710b42Smrg    if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
582c9710b42Smrg	goto retry;
5837872e0a1Smrg    unlock_config ();
584c9710b42Smrg    if (cfg)
585c9710b42Smrg	FcConfigDestroy (cfg);
586c9710b42Smrg
5872c393a42Smrg    return FcTrue;
5882c393a42Smrg}
5892c393a42Smrg
5902c393a42SmrgFcConfig *
5912c393a42SmrgFcConfigGetCurrent (void)
5922c393a42Smrg{
593c9710b42Smrg    return FcConfigEnsure ();
5942c393a42Smrg}
5952c393a42Smrg
5962c393a42SmrgFcBool
5972c393a42SmrgFcConfigAddConfigDir (FcConfig	    *config,
5982c393a42Smrg		      const FcChar8 *d)
5992c393a42Smrg{
6002c393a42Smrg    return FcStrSetAddFilename (config->configDirs, d);
6012c393a42Smrg}
6022c393a42Smrg
6032c393a42SmrgFcStrList *
6042c393a42SmrgFcConfigGetConfigDirs (FcConfig   *config)
6052c393a42Smrg{
6067872e0a1Smrg    FcStrList *ret;
6077872e0a1Smrg
6087872e0a1Smrg    config = FcConfigReference (config);
6092c393a42Smrg    if (!config)
6107872e0a1Smrg	return NULL;
6117872e0a1Smrg    ret = FcStrListCreate (config->configDirs);
6127872e0a1Smrg    FcConfigDestroy (config);
6137872e0a1Smrg
6147872e0a1Smrg    return ret;
6157872e0a1Smrg}
6167872e0a1Smrg
6177872e0a1SmrgFcBool
6187872e0a1SmrgFcConfigAddFontDir (FcConfig	    *config,
6197872e0a1Smrg		    const FcChar8   *d,
6207872e0a1Smrg		    const FcChar8   *m,
6217872e0a1Smrg		    const FcChar8   *salt)
6227872e0a1Smrg{
6237872e0a1Smrg    if (FcDebug() & FC_DBG_CACHE)
6242c393a42Smrg    {
6257872e0a1Smrg	if (m)
6267872e0a1Smrg	{
6277872e0a1Smrg	    printf ("%s -> %s%s%s%s\n", d, m, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
6287872e0a1Smrg	}
6297872e0a1Smrg	else if (salt)
6307872e0a1Smrg	{
6317872e0a1Smrg	    printf ("%s%s%s%s\n", d, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
6327872e0a1Smrg	}
6332c393a42Smrg    }
6347872e0a1Smrg    return FcStrSetAddFilenamePairWithSalt (config->fontDirs, d, m, salt);
6352c393a42Smrg}
6362c393a42Smrg
6372c393a42SmrgFcBool
6387872e0a1SmrgFcConfigResetFontDirs (FcConfig *config)
6392c393a42Smrg{
6407872e0a1Smrg    if (FcDebug() & FC_DBG_CACHE)
6417872e0a1Smrg    {
6427872e0a1Smrg	printf ("Reset font directories!\n");
6437872e0a1Smrg    }
6447872e0a1Smrg    return FcStrSetDeleteAll (config->fontDirs);
6452c393a42Smrg}
6462c393a42Smrg
6472c393a42SmrgFcStrList *
6482c393a42SmrgFcConfigGetFontDirs (FcConfig	*config)
6492c393a42Smrg{
6507872e0a1Smrg    FcStrList *ret;
6517872e0a1Smrg
6527872e0a1Smrg    config = FcConfigReference (config);
6532c393a42Smrg    if (!config)
6547872e0a1Smrg	return NULL;
6557872e0a1Smrg    ret = FcStrListCreate (config->fontDirs);
6567872e0a1Smrg    FcConfigDestroy (config);
6577872e0a1Smrg
6587872e0a1Smrg    return ret;
6597872e0a1Smrg}
6607872e0a1Smrg
6617872e0a1Smrgstatic FcBool
6627872e0a1SmrgFcConfigPathStartsWith(const FcChar8	*path,
6637872e0a1Smrg		       const FcChar8	*start)
6647872e0a1Smrg{
6657872e0a1Smrg    int len = strlen((char *) start);
6667872e0a1Smrg
6677872e0a1Smrg    if (strncmp((char *) path, (char *) start, len) != 0)
6687872e0a1Smrg	return FcFalse;
6697872e0a1Smrg
6707872e0a1Smrg    switch (path[len]) {
6717872e0a1Smrg    case '\0':
6727872e0a1Smrg    case FC_DIR_SEPARATOR:
6737872e0a1Smrg	return FcTrue;
6747872e0a1Smrg    default:
6757872e0a1Smrg	return FcFalse;
6762c393a42Smrg    }
6777872e0a1Smrg}
6787872e0a1Smrg
6797872e0a1SmrgFcChar8 *
6807872e0a1SmrgFcConfigMapFontPath(FcConfig		*config,
6817872e0a1Smrg		    const FcChar8	*path)
6827872e0a1Smrg{
6837872e0a1Smrg    FcStrList	*list;
6847872e0a1Smrg    FcChar8	*dir;
6857872e0a1Smrg    const FcChar8 *map, *rpath;
6867872e0a1Smrg    FcChar8     *retval;
6877872e0a1Smrg
6887872e0a1Smrg    list = FcConfigGetFontDirs(config);
6897872e0a1Smrg    if (!list)
6907872e0a1Smrg	return 0;
6917872e0a1Smrg    while ((dir = FcStrListNext(list)))
6927872e0a1Smrg	if (FcConfigPathStartsWith(path, dir))
6937872e0a1Smrg	    break;
6947872e0a1Smrg    FcStrListDone(list);
6957872e0a1Smrg    if (!dir)
6967872e0a1Smrg	return 0;
6977872e0a1Smrg    map = FcStrTripleSecond(dir);
6987872e0a1Smrg    if (!map)
6997872e0a1Smrg	return 0;
7007872e0a1Smrg    rpath = path + strlen ((char *) dir);
7017872e0a1Smrg    while (*rpath == '/')
7027872e0a1Smrg	rpath++;
7037872e0a1Smrg    retval = FcStrBuildFilename(map, rpath, NULL);
7047872e0a1Smrg    if (retval)
7057872e0a1Smrg    {
7067872e0a1Smrg	size_t len = strlen ((const char *) retval);
7077872e0a1Smrg	while (len > 0 && retval[len-1] == '/')
7087872e0a1Smrg	    len--;
7097872e0a1Smrg	/* trim the last slash */
7107872e0a1Smrg	retval[len] = 0;
7117872e0a1Smrg    }
7127872e0a1Smrg    return retval;
7137872e0a1Smrg}
7147872e0a1Smrg
7157872e0a1Smrgconst FcChar8 *
7167872e0a1SmrgFcConfigMapSalt (FcConfig      *config,
7177872e0a1Smrg		 const FcChar8 *path)
7187872e0a1Smrg{
7197872e0a1Smrg    FcStrList *list;
7207872e0a1Smrg    FcChar8 *dir;
7217872e0a1Smrg
7227872e0a1Smrg    list = FcConfigGetFontDirs (config);
7237872e0a1Smrg    if (!list)
7247872e0a1Smrg	return NULL;
7257872e0a1Smrg    while ((dir = FcStrListNext (list)))
7267872e0a1Smrg	if (FcConfigPathStartsWith (path, dir))
7277872e0a1Smrg	    break;
7287872e0a1Smrg    FcStrListDone (list);
7297872e0a1Smrg    if (!dir)
7307872e0a1Smrg	return NULL;
7317872e0a1Smrg
7327872e0a1Smrg    return FcStrTripleThird (dir);
7332c393a42Smrg}
7342c393a42Smrg
7352c393a42SmrgFcBool
7362c393a42SmrgFcConfigAddCacheDir (FcConfig	    *config,
7372c393a42Smrg		     const FcChar8  *d)
7382c393a42Smrg{
7392c393a42Smrg    return FcStrSetAddFilename (config->cacheDirs, d);
7402c393a42Smrg}
7412c393a42Smrg
7422c393a42SmrgFcStrList *
7437872e0a1SmrgFcConfigGetCacheDirs (FcConfig *config)
7442c393a42Smrg{
7457872e0a1Smrg    FcStrList *ret;
7467872e0a1Smrg
7477872e0a1Smrg    config = FcConfigReference (config);
7482c393a42Smrg    if (!config)
7497872e0a1Smrg	return NULL;
7507872e0a1Smrg    ret = FcStrListCreate (config->cacheDirs);
7517872e0a1Smrg    FcConfigDestroy (config);
7527872e0a1Smrg
7537872e0a1Smrg    return ret;
7542c393a42Smrg}
755ca08ab68Smrg
7562c393a42SmrgFcBool
7572c393a42SmrgFcConfigAddConfigFile (FcConfig	    *config,
7582c393a42Smrg		       const FcChar8   *f)
7592c393a42Smrg{
7602c393a42Smrg    FcBool	ret;
7617872e0a1Smrg    FcChar8	*file = FcConfigGetFilename (config, f);
762ca08ab68Smrg
7632c393a42Smrg    if (!file)
7642c393a42Smrg	return FcFalse;
765ca08ab68Smrg
7662c393a42Smrg    ret = FcStrSetAdd (config->configFiles, file);
7672c393a42Smrg    FcStrFree (file);
7682c393a42Smrg    return ret;
7692c393a42Smrg}
7702c393a42Smrg
7712c393a42SmrgFcStrList *
7722c393a42SmrgFcConfigGetConfigFiles (FcConfig    *config)
7732c393a42Smrg{
7747872e0a1Smrg    FcStrList *ret;
7757872e0a1Smrg
7767872e0a1Smrg    config = FcConfigReference (config);
7772c393a42Smrg    if (!config)
7787872e0a1Smrg	return NULL;
7797872e0a1Smrg    ret = FcStrListCreate (config->configFiles);
7807872e0a1Smrg    FcConfigDestroy (config);
7817872e0a1Smrg
7827872e0a1Smrg    return ret;
7832c393a42Smrg}
7842c393a42Smrg
7852c393a42SmrgFcChar8 *
786c9710b42SmrgFcConfigGetCache (FcConfig  *config FC_UNUSED)
7872c393a42Smrg{
7882c393a42Smrg    return NULL;
7892c393a42Smrg}
7902c393a42Smrg
7912c393a42SmrgFcFontSet *
7922c393a42SmrgFcConfigGetFonts (FcConfig	*config,
7932c393a42Smrg		  FcSetName	set)
7942c393a42Smrg{
7952c393a42Smrg    if (!config)
7962c393a42Smrg    {
7972c393a42Smrg	config = FcConfigGetCurrent ();
7982c393a42Smrg	if (!config)
7992c393a42Smrg	    return 0;
8002c393a42Smrg    }
8012c393a42Smrg    return config->fonts[set];
8022c393a42Smrg}
8032c393a42Smrg
8042c393a42Smrgvoid
8052c393a42SmrgFcConfigSetFonts (FcConfig	*config,
8062c393a42Smrg		  FcFontSet	*fonts,
8072c393a42Smrg		  FcSetName	set)
8082c393a42Smrg{
8092c393a42Smrg    if (config->fonts[set])
8102c393a42Smrg	FcFontSetDestroy (config->fonts[set]);
8112c393a42Smrg    config->fonts[set] = fonts;
8122c393a42Smrg}
8132c393a42Smrg
8141887081fSmrg
8152c393a42SmrgFcBlanks *
8161887081fSmrgFcBlanksCreate (void)
8172c393a42Smrg{
8181887081fSmrg    /* Deprecated. */
8191887081fSmrg    return NULL;
8201887081fSmrg}
8211887081fSmrg
8221887081fSmrgvoid
8231887081fSmrgFcBlanksDestroy (FcBlanks *b FC_UNUSED)
8241887081fSmrg{
8251887081fSmrg    /* Deprecated. */
8262c393a42Smrg}
8272c393a42Smrg
8282c393a42SmrgFcBool
8291887081fSmrgFcBlanksAdd (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
8302c393a42Smrg{
8311887081fSmrg    /* Deprecated. */
8321887081fSmrg    return FcFalse;
8331887081fSmrg}
834ca08ab68Smrg
8351887081fSmrgFcBool
8361887081fSmrgFcBlanksIsMember (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
8371887081fSmrg{
8381887081fSmrg    /* Deprecated. */
8391887081fSmrg    return FcFalse;
8401887081fSmrg}
8411887081fSmrg
8421887081fSmrgFcBlanks *
8431887081fSmrgFcConfigGetBlanks (FcConfig	*config FC_UNUSED)
8441887081fSmrg{
8451887081fSmrg    /* Deprecated. */
8461887081fSmrg    return NULL;
8471887081fSmrg}
8481887081fSmrg
8491887081fSmrgFcBool
8501887081fSmrgFcConfigAddBlank (FcConfig	*config FC_UNUSED,
8511887081fSmrg		  FcChar32    	blank FC_UNUSED)
8521887081fSmrg{
8531887081fSmrg    /* Deprecated. */
8541887081fSmrg    return FcFalse;
8552c393a42Smrg}
8562c393a42Smrg
8571887081fSmrg
8582c393a42Smrgint
8592c393a42SmrgFcConfigGetRescanInterval (FcConfig *config)
8602c393a42Smrg{
8617872e0a1Smrg    int ret;
8627872e0a1Smrg
8637872e0a1Smrg    config = FcConfigReference (config);
8642c393a42Smrg    if (!config)
8657872e0a1Smrg	return 0;
8667872e0a1Smrg    ret = config->rescanInterval;
8677872e0a1Smrg    FcConfigDestroy (config);
8687872e0a1Smrg
8697872e0a1Smrg    return ret;
8702c393a42Smrg}
8712c393a42Smrg
8722c393a42SmrgFcBool
8732c393a42SmrgFcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
8742c393a42Smrg{
8757872e0a1Smrg    config = FcConfigReference (config);
8762c393a42Smrg    if (!config)
8777872e0a1Smrg	return FcFalse;
8782c393a42Smrg    config->rescanInterval = rescanInterval;
8797872e0a1Smrg    FcConfigDestroy (config);
8807872e0a1Smrg
8812c393a42Smrg    return FcTrue;
8822c393a42Smrg}
8832c393a42Smrg
8842c393a42Smrg/*
8852c393a42Smrg * A couple of typos escaped into the library
8862c393a42Smrg */
8872c393a42Smrgint
8882c393a42SmrgFcConfigGetRescanInverval (FcConfig *config)
8892c393a42Smrg{
8902c393a42Smrg    return FcConfigGetRescanInterval (config);
8912c393a42Smrg}
8922c393a42Smrg
8932c393a42SmrgFcBool
8942c393a42SmrgFcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
8952c393a42Smrg{
8962c393a42Smrg    return FcConfigSetRescanInterval (config, rescanInterval);
8972c393a42Smrg}
8982c393a42Smrg
8992c393a42SmrgFcBool
9006fc018e4SmrgFcConfigAddRule (FcConfig	*config,
9016fc018e4Smrg		 FcRule		*rule,
9022c393a42Smrg		 FcMatchKind	kind)
9032c393a42Smrg{
9041887081fSmrg    /* deprecated */
9051887081fSmrg    return FcFalse;
9062c393a42Smrg}
9072c393a42Smrg
9082c393a42Smrgstatic FcValue
909c9710b42SmrgFcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
9102c393a42Smrg{
9117872e0a1Smrg    switch (v.type)
9127872e0a1Smrg    {
9137872e0a1Smrg    case FcTypeInteger:
9147872e0a1Smrg        v.type = FcTypeDouble;
9157872e0a1Smrg        v.u.d = (double) v.u.i;
9167872e0a1Smrg        /* Fallthrough */
9177872e0a1Smrg    case FcTypeDouble:
9187872e0a1Smrg        if (u.type == FcTypeRange && buf)
9197872e0a1Smrg        {
9207872e0a1Smrg            v.u.r = FcRangePromote (v.u.d, buf);
9217872e0a1Smrg            v.type = FcTypeRange;
9227872e0a1Smrg        }
9237872e0a1Smrg        break;
9247872e0a1Smrg    case FcTypeVoid:
9257872e0a1Smrg        if (u.type == FcTypeMatrix)
9267872e0a1Smrg        {
9277872e0a1Smrg            v.u.m = &FcIdentityMatrix;
9287872e0a1Smrg            v.type = FcTypeMatrix;
9297872e0a1Smrg        }
9307872e0a1Smrg        else if (u.type == FcTypeLangSet && buf)
9317872e0a1Smrg        {
9327872e0a1Smrg            v.u.l = FcLangSetPromote (NULL, buf);
9337872e0a1Smrg            v.type = FcTypeLangSet;
9347872e0a1Smrg        }
9357872e0a1Smrg        else if (u.type == FcTypeCharSet && buf)
9367872e0a1Smrg        {
9377872e0a1Smrg            v.u.c = FcCharSetPromote (buf);
9387872e0a1Smrg            v.type = FcTypeCharSet;
9397872e0a1Smrg        }
9407872e0a1Smrg        break;
9417872e0a1Smrg    case FcTypeString:
9427872e0a1Smrg        if (u.type == FcTypeLangSet && buf)
9437872e0a1Smrg        {
9447872e0a1Smrg            v.u.l = FcLangSetPromote (v.u.s, buf);
9457872e0a1Smrg            v.type = FcTypeLangSet;
9467872e0a1Smrg        }
9477872e0a1Smrg        break;
9487872e0a1Smrg    default:
9497872e0a1Smrg        break;
95018bd4a06Smrg    }
9512c393a42Smrg    return v;
9522c393a42Smrg}
9532c393a42Smrg
9542c393a42SmrgFcBool
9552c393a42SmrgFcConfigCompareValue (const FcValue	*left_o,
9566fc018e4Smrg		      unsigned int      op_,
9572c393a42Smrg		      const FcValue	*right_o)
9582c393a42Smrg{
9597872e0a1Smrg    FcValue     left;
9607872e0a1Smrg    FcValue     right;
9612c393a42Smrg    FcBool	ret = FcFalse;
962ca08ab68Smrg    FcOp	op = FC_OP_GET_OP (op_);
963ca08ab68Smrg    int		flags = FC_OP_GET_FLAGS (op_);
964c9710b42Smrg    FcValuePromotionBuffer buf1, buf2;
965ca08ab68Smrg
9667872e0a1Smrg    if (left_o->type != right_o->type)
9677872e0a1Smrg    {
9687872e0a1Smrg        left = FcValueCanonicalize(left_o);
9697872e0a1Smrg        right = FcValueCanonicalize(right_o);
9707872e0a1Smrg        left = FcConfigPromote (left, right, &buf1);
9717872e0a1Smrg        right = FcConfigPromote (right, left, &buf2);
9727872e0a1Smrg        left_o = &left;
9737872e0a1Smrg        right_o = &right;
9747872e0a1Smrg        if (left_o->type != right_o->type)
9757872e0a1Smrg        {
9767872e0a1Smrg	    if (op == FcOpNotEqual || op == FcOpNotContains)
9777872e0a1Smrg	        ret = FcTrue;
9787872e0a1Smrg            return ret;
9797872e0a1Smrg        }
9807872e0a1Smrg    }
9817872e0a1Smrg    switch (left_o->type) {
9827872e0a1Smrg    case FcTypeUnknown:
9837872e0a1Smrg        break;	/* No way to guess how to compare for this object */
9847872e0a1Smrg    case FcTypeInteger: {
9857872e0a1Smrg        int l = left_o->u.i;
9867872e0a1Smrg        int r = right_o->u.i;
9877872e0a1Smrg        switch ((int) op) {
9887872e0a1Smrg        case FcOpEqual:
9897872e0a1Smrg        case FcOpContains:
9907872e0a1Smrg        case FcOpListing:
9917872e0a1Smrg            ret = l == r;
9927872e0a1Smrg            break;
9937872e0a1Smrg        case FcOpNotEqual:
9947872e0a1Smrg        case FcOpNotContains:
9957872e0a1Smrg            ret = l != r;
9967872e0a1Smrg            break;
9977872e0a1Smrg        case FcOpLess:
9987872e0a1Smrg            ret = l < r;
9997872e0a1Smrg            break;
10007872e0a1Smrg        case FcOpLessEqual:
10017872e0a1Smrg            ret = l <= r;
10027872e0a1Smrg            break;
10037872e0a1Smrg        case FcOpMore:
10047872e0a1Smrg            ret = l > r;
10057872e0a1Smrg            break;
10067872e0a1Smrg        case FcOpMoreEqual:
10077872e0a1Smrg            ret = l >= r;
10087872e0a1Smrg            break;
10097872e0a1Smrg        default:
10107872e0a1Smrg            break;
10117872e0a1Smrg        }
10127872e0a1Smrg        break;
10137872e0a1Smrg    }
10147872e0a1Smrg    case FcTypeDouble: {
10157872e0a1Smrg        double l = left_o->u.d;
10167872e0a1Smrg        double r = right_o->u.d;
10177872e0a1Smrg        switch ((int) op) {
10187872e0a1Smrg        case FcOpEqual:
10197872e0a1Smrg        case FcOpContains:
10207872e0a1Smrg        case FcOpListing:
10217872e0a1Smrg            ret = l == r;
10227872e0a1Smrg            break;
10237872e0a1Smrg        case FcOpNotEqual:
10247872e0a1Smrg        case FcOpNotContains:
10257872e0a1Smrg            ret = l != r;
10267872e0a1Smrg            break;
10277872e0a1Smrg        case FcOpLess:
10287872e0a1Smrg            ret = l < r;
10297872e0a1Smrg            break;
10307872e0a1Smrg        case FcOpLessEqual:
10317872e0a1Smrg            ret = l <= r;
10327872e0a1Smrg            break;
10337872e0a1Smrg        case FcOpMore:
10347872e0a1Smrg            ret = l > r;
10357872e0a1Smrg            break;
10367872e0a1Smrg        case FcOpMoreEqual:
10377872e0a1Smrg            ret = l >= r;
10387872e0a1Smrg            break;
10397872e0a1Smrg        default:
10407872e0a1Smrg            break;
10417872e0a1Smrg        }
10427872e0a1Smrg        break;
10437872e0a1Smrg    }
10447872e0a1Smrg    case FcTypeBool: {
10457872e0a1Smrg        FcBool l = left_o->u.b;
10467872e0a1Smrg        FcBool r = right_o->u.b;
10477872e0a1Smrg        switch ((int) op) {
10487872e0a1Smrg        case FcOpEqual:
10497872e0a1Smrg            ret = l == r;
10507872e0a1Smrg            break;
10517872e0a1Smrg        case FcOpContains:
10527872e0a1Smrg        case FcOpListing:
10537872e0a1Smrg            ret = l == r || l >= FcDontCare;
10547872e0a1Smrg            break;
10557872e0a1Smrg        case FcOpNotEqual:
10567872e0a1Smrg            ret = l != r;
10577872e0a1Smrg            break;
10587872e0a1Smrg        case FcOpNotContains:
10597872e0a1Smrg            ret = !(l == r || l >= FcDontCare);
10607872e0a1Smrg            break;
10617872e0a1Smrg        case FcOpLess:
10627872e0a1Smrg            ret = l != r && r >= FcDontCare;
10637872e0a1Smrg            break;
10647872e0a1Smrg        case FcOpLessEqual:
10657872e0a1Smrg            ret = l == r || r >= FcDontCare;
10667872e0a1Smrg            break;
10677872e0a1Smrg        case FcOpMore:
10687872e0a1Smrg            ret = l != r && l >= FcDontCare;
10697872e0a1Smrg            break;
10707872e0a1Smrg        case FcOpMoreEqual:
10717872e0a1Smrg            ret = l == r || l >= FcDontCare;
10727872e0a1Smrg            break;
10737872e0a1Smrg        default:
10747872e0a1Smrg            break;
10757872e0a1Smrg        }
10767872e0a1Smrg        break;
10777872e0a1Smrg    }
10787872e0a1Smrg    case FcTypeString: {
10797872e0a1Smrg        const FcChar8 *l = FcValueString (left_o);
10807872e0a1Smrg        const FcChar8 *r = FcValueString (right_o);
10817872e0a1Smrg        switch ((int) op) {
10827872e0a1Smrg        case FcOpEqual:
10837872e0a1Smrg        case FcOpListing:
10847872e0a1Smrg            if (flags & FcOpFlagIgnoreBlanks)
10857872e0a1Smrg                ret = FcStrCmpIgnoreBlanksAndCase (l, r) == 0;
10867872e0a1Smrg            else
10877872e0a1Smrg                ret = FcStrCmpIgnoreCase (l, r) == 0;
10887872e0a1Smrg            break;
10897872e0a1Smrg        case FcOpContains:
10907872e0a1Smrg            ret = FcStrStrIgnoreCase (l, r) != 0;
10917872e0a1Smrg            break;
10927872e0a1Smrg        case FcOpNotEqual:
10937872e0a1Smrg            if (flags & FcOpFlagIgnoreBlanks)
10947872e0a1Smrg                ret = FcStrCmpIgnoreBlanksAndCase (l, r) != 0;
10957872e0a1Smrg            else
10967872e0a1Smrg                ret = FcStrCmpIgnoreCase (l, r) != 0;
10977872e0a1Smrg            break;
10987872e0a1Smrg        case FcOpNotContains:
10997872e0a1Smrg            ret = FcStrStrIgnoreCase (l, r) == 0;
11007872e0a1Smrg            break;
11017872e0a1Smrg        default:
11027872e0a1Smrg            break;
11037872e0a1Smrg        }
11047872e0a1Smrg        break;
11057872e0a1Smrg    }
11067872e0a1Smrg    case FcTypeMatrix: {
11077872e0a1Smrg        switch ((int) op) {
11087872e0a1Smrg        case FcOpEqual:
11097872e0a1Smrg        case FcOpContains:
11107872e0a1Smrg        case FcOpListing:
11117872e0a1Smrg            ret = FcMatrixEqual (left_o->u.m, right_o->u.m);
11127872e0a1Smrg            break;
11137872e0a1Smrg        case FcOpNotEqual:
11147872e0a1Smrg        case FcOpNotContains:
11157872e0a1Smrg            ret = !FcMatrixEqual (left_o->u.m, right_o->u.m);
11167872e0a1Smrg            break;
11177872e0a1Smrg        default:
11187872e0a1Smrg            break;
11197872e0a1Smrg        }
11207872e0a1Smrg        break;
11217872e0a1Smrg    }
11227872e0a1Smrg    case FcTypeCharSet: {
11237872e0a1Smrg        const FcCharSet *l = FcValueCharSet (left_o);
11247872e0a1Smrg        const FcCharSet *r = FcValueCharSet (right_o);
11257872e0a1Smrg        switch ((int) op) {
11267872e0a1Smrg        case FcOpContains:
11277872e0a1Smrg        case FcOpListing:
11287872e0a1Smrg            /* left contains right if right is a subset of left */
11297872e0a1Smrg            ret = FcCharSetIsSubset (r, l);
11307872e0a1Smrg            break;
11317872e0a1Smrg        case FcOpNotContains:
11327872e0a1Smrg            /* left contains right if right is a subset of left */
11337872e0a1Smrg            ret = !FcCharSetIsSubset (r, l);
11347872e0a1Smrg            break;
11357872e0a1Smrg        case FcOpEqual:
11367872e0a1Smrg            ret = FcCharSetEqual (l, r);
11377872e0a1Smrg            break;
11387872e0a1Smrg        case FcOpNotEqual:
11397872e0a1Smrg            ret = !FcCharSetEqual (l, r);
11407872e0a1Smrg            break;
11417872e0a1Smrg        default:
11427872e0a1Smrg            break;
11437872e0a1Smrg        }
11447872e0a1Smrg        break;
11457872e0a1Smrg    }
11467872e0a1Smrg    case FcTypeLangSet: {
11477872e0a1Smrg        const FcLangSet *l = FcValueLangSet (left_o);
11487872e0a1Smrg        const FcLangSet *r = FcValueLangSet (right_o);
11497872e0a1Smrg        switch ((int) op) {
11507872e0a1Smrg        case FcOpContains:
11517872e0a1Smrg        case FcOpListing:
11527872e0a1Smrg            ret = FcLangSetContains (l, r);
11537872e0a1Smrg            break;
11547872e0a1Smrg        case FcOpNotContains:
11557872e0a1Smrg            ret = !FcLangSetContains (l, r);
11567872e0a1Smrg            break;
11577872e0a1Smrg        case FcOpEqual:
11587872e0a1Smrg            ret = FcLangSetEqual (l, r);
11597872e0a1Smrg            break;
11607872e0a1Smrg        case FcOpNotEqual:
11617872e0a1Smrg            ret = !FcLangSetEqual (l, r);
11627872e0a1Smrg            break;
11637872e0a1Smrg        default:
11647872e0a1Smrg            break;
11657872e0a1Smrg        }
11667872e0a1Smrg        break;
11677872e0a1Smrg    }
11687872e0a1Smrg    case FcTypeVoid:
11697872e0a1Smrg        switch ((int) op) {
11707872e0a1Smrg        case FcOpEqual:
11717872e0a1Smrg        case FcOpContains:
11727872e0a1Smrg        case FcOpListing:
11737872e0a1Smrg            ret = FcTrue;
11747872e0a1Smrg            break;
11757872e0a1Smrg        default:
11767872e0a1Smrg            break;
11777872e0a1Smrg        }
11787872e0a1Smrg        break;
11797872e0a1Smrg    case FcTypeFTFace:
11807872e0a1Smrg        switch ((int) op) {
11817872e0a1Smrg        case FcOpEqual:
11827872e0a1Smrg        case FcOpContains:
11837872e0a1Smrg        case FcOpListing:
11847872e0a1Smrg            ret = left_o->u.f == right_o->u.f;
11857872e0a1Smrg            break;
11867872e0a1Smrg        case FcOpNotEqual:
11877872e0a1Smrg        case FcOpNotContains:
11887872e0a1Smrg            ret = left_o->u.f != right_o->u.f;
11897872e0a1Smrg            break;
11907872e0a1Smrg        default:
11917872e0a1Smrg            break;
11927872e0a1Smrg        }
11937872e0a1Smrg        break;
11947872e0a1Smrg    case FcTypeRange: {
11957872e0a1Smrg        const FcRange *l = FcValueRange (left_o);
11967872e0a1Smrg        const FcRange *r = FcValueRange (right_o);
11977872e0a1Smrg        ret = FcRangeCompare (op, l, r);
11987872e0a1Smrg        break;
11992c393a42Smrg    }
12002c393a42Smrg    }
12012c393a42Smrg    return ret;
12022c393a42Smrg}
12032c393a42Smrg
12042c393a42Smrg
12052c393a42Smrg#define _FcDoubleFloor(d)	((int) (d))
12062c393a42Smrg#define _FcDoubleCeil(d)	((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
12072c393a42Smrg#define FcDoubleFloor(d)	((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
12082c393a42Smrg#define FcDoubleCeil(d)		((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
12092c393a42Smrg#define FcDoubleRound(d)	FcDoubleFloor ((d) + 0.5)
12102c393a42Smrg#define FcDoubleTrunc(d)	((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
12112c393a42Smrg
12122c393a42Smrgstatic FcValue
1213c9710b42SmrgFcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
12142c393a42Smrg{
121518bd4a06Smrg    FcValue	v, vl, vr, vle, vre;
12162c393a42Smrg    FcMatrix	*m;
12172c393a42Smrg    FcChar8     *str;
1218ca08ab68Smrg    FcOp	op = FC_OP_GET_OP (e->op);
121918bd4a06Smrg    FcValuePromotionBuffer buf1, buf2;
1220ca08ab68Smrg
1221c9710b42Smrg    switch ((int) op) {
12222c393a42Smrg    case FcOpInteger:
12232c393a42Smrg	v.type = FcTypeInteger;
12242c393a42Smrg	v.u.i = e->u.ival;
12252c393a42Smrg	break;
12262c393a42Smrg    case FcOpDouble:
12272c393a42Smrg	v.type = FcTypeDouble;
12282c393a42Smrg	v.u.d = e->u.dval;
12292c393a42Smrg	break;
12302c393a42Smrg    case FcOpString:
12312c393a42Smrg	v.type = FcTypeString;
1232a6844aabSmrg	v.u.s = e->u.sval;
1233a6844aabSmrg	v = FcValueSave (v);
12342c393a42Smrg	break;
12352c393a42Smrg    case FcOpMatrix:
1236c9710b42Smrg	{
1237c9710b42Smrg	  FcMatrix m;
1238c9710b42Smrg	  FcValue xx, xy, yx, yy;
1239c9710b42Smrg	  v.type = FcTypeMatrix;
1240c9710b42Smrg	  xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xx), v, NULL);
1241c9710b42Smrg	  xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xy), v, NULL);
1242c9710b42Smrg	  yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yx), v, NULL);
1243c9710b42Smrg	  yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yy), v, NULL);
1244c9710b42Smrg	  if (xx.type == FcTypeDouble && xy.type == FcTypeDouble &&
1245c9710b42Smrg	      yx.type == FcTypeDouble && yy.type == FcTypeDouble)
1246c9710b42Smrg	  {
1247c9710b42Smrg	    m.xx = xx.u.d;
1248c9710b42Smrg	    m.xy = xy.u.d;
1249c9710b42Smrg	    m.yx = yx.u.d;
1250c9710b42Smrg	    m.yy = yy.u.d;
1251c9710b42Smrg	    v.u.m = &m;
1252c9710b42Smrg	  }
1253c9710b42Smrg	  else
1254c9710b42Smrg	    v.type = FcTypeVoid;
1255c9710b42Smrg	  v = FcValueSave (v);
1256c9710b42Smrg	}
12572c393a42Smrg	break;
12582c393a42Smrg    case FcOpCharSet:
12592c393a42Smrg	v.type = FcTypeCharSet;
12602c393a42Smrg	v.u.c = e->u.cval;
12612c393a42Smrg	v = FcValueSave (v);
12622c393a42Smrg	break;
1263ca08ab68Smrg    case FcOpLangSet:
1264ca08ab68Smrg	v.type = FcTypeLangSet;
1265ca08ab68Smrg	v.u.l = e->u.lval;
1266ca08ab68Smrg	v = FcValueSave (v);
1267ca08ab68Smrg	break;
126818bd4a06Smrg    case FcOpRange:
126918bd4a06Smrg	v.type = FcTypeRange;
127018bd4a06Smrg	v.u.r = e->u.rval;
127118bd4a06Smrg	v = FcValueSave (v);
127218bd4a06Smrg	break;
12732c393a42Smrg    case FcOpBool:
12742c393a42Smrg	v.type = FcTypeBool;
12752c393a42Smrg	v.u.b = e->u.bval;
12762c393a42Smrg	break;
12772c393a42Smrg    case FcOpField:
1278c9710b42Smrg	if (kind == FcMatchFont && e->u.name.kind == FcMatchPattern)
1279c9710b42Smrg	{
1280c9710b42Smrg	    if (FcResultMatch != FcPatternObjectGet (p_pat, e->u.name.object, 0, &v))
1281c9710b42Smrg		v.type = FcTypeVoid;
1282c9710b42Smrg	}
1283c9710b42Smrg	else if (kind == FcMatchPattern && e->u.name.kind == FcMatchFont)
1284c9710b42Smrg	{
1285c9710b42Smrg	    fprintf (stderr,
1286c9710b42Smrg                    "Fontconfig warning: <name> tag has target=\"font\" in a <match target=\"pattern\">.\n");
12872c393a42Smrg	    v.type = FcTypeVoid;
1288c9710b42Smrg	}
1289c9710b42Smrg	else
1290c9710b42Smrg	{
1291c9710b42Smrg	    if (FcResultMatch != FcPatternObjectGet (p, e->u.name.object, 0, &v))
1292c9710b42Smrg		v.type = FcTypeVoid;
1293c9710b42Smrg	}
12942c393a42Smrg	v = FcValueSave (v);
12952c393a42Smrg	break;
12962c393a42Smrg    case FcOpConst:
12972c393a42Smrg	if (FcNameConstant (e->u.constant, &v.u.i))
12982c393a42Smrg	    v.type = FcTypeInteger;
12992c393a42Smrg	else
13002c393a42Smrg	    v.type = FcTypeVoid;
13012c393a42Smrg	break;
13022c393a42Smrg    case FcOpQuest:
1303c9710b42Smrg	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
13042c393a42Smrg	if (vl.type == FcTypeBool)
13052c393a42Smrg	{
13062c393a42Smrg	    if (vl.u.b)
1307c9710b42Smrg		v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.left);
13082c393a42Smrg	    else
1309c9710b42Smrg		v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.right);
13102c393a42Smrg	}
13112c393a42Smrg	else
13122c393a42Smrg	    v.type = FcTypeVoid;
13132c393a42Smrg	FcValueDestroy (vl);
13142c393a42Smrg	break;
13152c393a42Smrg    case FcOpEqual:
13162c393a42Smrg    case FcOpNotEqual:
13172c393a42Smrg    case FcOpLess:
13182c393a42Smrg    case FcOpLessEqual:
13192c393a42Smrg    case FcOpMore:
13202c393a42Smrg    case FcOpMoreEqual:
13212c393a42Smrg    case FcOpContains:
13222c393a42Smrg    case FcOpNotContains:
13232c393a42Smrg    case FcOpListing:
1324c9710b42Smrg	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1325c9710b42Smrg	vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
13262c393a42Smrg	v.type = FcTypeBool;
13272c393a42Smrg	v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
13282c393a42Smrg	FcValueDestroy (vl);
13292c393a42Smrg	FcValueDestroy (vr);
133046bb3e47Smrg	break;
13312c393a42Smrg    case FcOpOr:
13322c393a42Smrg    case FcOpAnd:
13332c393a42Smrg    case FcOpPlus:
13342c393a42Smrg    case FcOpMinus:
13352c393a42Smrg    case FcOpTimes:
13362c393a42Smrg    case FcOpDivide:
1337c9710b42Smrg	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1338c9710b42Smrg	vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
133918bd4a06Smrg	vle = FcConfigPromote (vl, vr, &buf1);
134018bd4a06Smrg	vre = FcConfigPromote (vr, vle, &buf2);
134118bd4a06Smrg	if (vle.type == vre.type)
13422c393a42Smrg	{
134318bd4a06Smrg	    switch ((int) vle.type) {
13442c393a42Smrg	    case FcTypeDouble:
1345c9710b42Smrg		switch ((int) op) {
134646bb3e47Smrg		case FcOpPlus:
13472c393a42Smrg		    v.type = FcTypeDouble;
134818bd4a06Smrg		    v.u.d = vle.u.d + vre.u.d;
13492c393a42Smrg		    break;
13502c393a42Smrg		case FcOpMinus:
13512c393a42Smrg		    v.type = FcTypeDouble;
135218bd4a06Smrg		    v.u.d = vle.u.d - vre.u.d;
13532c393a42Smrg		    break;
13542c393a42Smrg		case FcOpTimes:
13552c393a42Smrg		    v.type = FcTypeDouble;
135618bd4a06Smrg		    v.u.d = vle.u.d * vre.u.d;
13572c393a42Smrg		    break;
13582c393a42Smrg		case FcOpDivide:
13592c393a42Smrg		    v.type = FcTypeDouble;
136018bd4a06Smrg		    v.u.d = vle.u.d / vre.u.d;
13612c393a42Smrg		    break;
13622c393a42Smrg		default:
1363ca08ab68Smrg		    v.type = FcTypeVoid;
13642c393a42Smrg		    break;
13652c393a42Smrg		}
13662c393a42Smrg		if (v.type == FcTypeDouble &&
13672c393a42Smrg		    v.u.d == (double) (int) v.u.d)
13682c393a42Smrg		{
13692c393a42Smrg		    v.type = FcTypeInteger;
13702c393a42Smrg		    v.u.i = (int) v.u.d;
13712c393a42Smrg		}
13722c393a42Smrg		break;
13732c393a42Smrg	    case FcTypeBool:
1374c9710b42Smrg		switch ((int) op) {
13752c393a42Smrg		case FcOpOr:
13762c393a42Smrg		    v.type = FcTypeBool;
137718bd4a06Smrg		    v.u.b = vle.u.b || vre.u.b;
13782c393a42Smrg		    break;
13792c393a42Smrg		case FcOpAnd:
13802c393a42Smrg		    v.type = FcTypeBool;
138118bd4a06Smrg		    v.u.b = vle.u.b && vre.u.b;
13822c393a42Smrg		    break;
13832c393a42Smrg		default:
1384ca08ab68Smrg		    v.type = FcTypeVoid;
13852c393a42Smrg		    break;
13862c393a42Smrg		}
13872c393a42Smrg		break;
13882c393a42Smrg	    case FcTypeString:
1389c9710b42Smrg		switch ((int) op) {
13902c393a42Smrg		case FcOpPlus:
13912c393a42Smrg		    v.type = FcTypeString;
139218bd4a06Smrg		    str = FcStrPlus (vle.u.s, vre.u.s);
1393c9710b42Smrg		    v.u.s = FcStrdup (str);
13942c393a42Smrg		    FcStrFree (str);
139546bb3e47Smrg
13962c393a42Smrg		    if (!v.u.s)
13972c393a42Smrg			v.type = FcTypeVoid;
13982c393a42Smrg		    break;
13992c393a42Smrg		default:
14002c393a42Smrg		    v.type = FcTypeVoid;
14012c393a42Smrg		    break;
14022c393a42Smrg		}
14032c393a42Smrg		break;
14042c393a42Smrg	    case FcTypeMatrix:
1405c9710b42Smrg		switch ((int) op) {
14062c393a42Smrg		case FcOpTimes:
14072c393a42Smrg		    v.type = FcTypeMatrix;
14082c393a42Smrg		    m = malloc (sizeof (FcMatrix));
14092c393a42Smrg		    if (m)
14102c393a42Smrg		    {
141118bd4a06Smrg			FcMatrixMultiply (m, vle.u.m, vre.u.m);
14122c393a42Smrg			v.u.m = m;
14132c393a42Smrg		    }
14142c393a42Smrg		    else
14152c393a42Smrg		    {
14162c393a42Smrg			v.type = FcTypeVoid;
14172c393a42Smrg		    }
14182c393a42Smrg		    break;
14192c393a42Smrg		default:
14202c393a42Smrg		    v.type = FcTypeVoid;
14212c393a42Smrg		    break;
14222c393a42Smrg		}
14232c393a42Smrg		break;
1424ca08ab68Smrg	    case FcTypeCharSet:
1425c9710b42Smrg		switch ((int) op) {
1426ca08ab68Smrg		case FcOpPlus:
1427ca08ab68Smrg		    v.type = FcTypeCharSet;
142818bd4a06Smrg		    v.u.c = FcCharSetUnion (vle.u.c, vre.u.c);
1429ca08ab68Smrg		    if (!v.u.c)
1430ca08ab68Smrg			v.type = FcTypeVoid;
1431ca08ab68Smrg		    break;
1432ca08ab68Smrg		case FcOpMinus:
1433ca08ab68Smrg		    v.type = FcTypeCharSet;
143418bd4a06Smrg		    v.u.c = FcCharSetSubtract (vle.u.c, vre.u.c);
1435ca08ab68Smrg		    if (!v.u.c)
1436ca08ab68Smrg			v.type = FcTypeVoid;
1437ca08ab68Smrg		    break;
1438ca08ab68Smrg		default:
1439ca08ab68Smrg		    v.type = FcTypeVoid;
1440ca08ab68Smrg		    break;
1441ca08ab68Smrg		}
1442ca08ab68Smrg		break;
1443ca08ab68Smrg	    case FcTypeLangSet:
1444c9710b42Smrg		switch ((int) op) {
1445ca08ab68Smrg		case FcOpPlus:
1446ca08ab68Smrg		    v.type = FcTypeLangSet;
144718bd4a06Smrg		    v.u.l = FcLangSetUnion (vle.u.l, vre.u.l);
1448ca08ab68Smrg		    if (!v.u.l)
1449ca08ab68Smrg			v.type = FcTypeVoid;
1450ca08ab68Smrg		    break;
1451ca08ab68Smrg		case FcOpMinus:
1452ca08ab68Smrg		    v.type = FcTypeLangSet;
145318bd4a06Smrg		    v.u.l = FcLangSetSubtract (vle.u.l, vre.u.l);
1454ca08ab68Smrg		    if (!v.u.l)
1455ca08ab68Smrg			v.type = FcTypeVoid;
1456ca08ab68Smrg		    break;
1457ca08ab68Smrg		default:
1458ca08ab68Smrg		    v.type = FcTypeVoid;
1459ca08ab68Smrg		    break;
1460ca08ab68Smrg		}
1461ca08ab68Smrg		break;
14622c393a42Smrg	    default:
14632c393a42Smrg		v.type = FcTypeVoid;
14642c393a42Smrg		break;
14652c393a42Smrg	    }
14662c393a42Smrg	}
14672c393a42Smrg	else
14682c393a42Smrg	    v.type = FcTypeVoid;
14692c393a42Smrg	FcValueDestroy (vl);
14702c393a42Smrg	FcValueDestroy (vr);
14712c393a42Smrg	break;
14722c393a42Smrg    case FcOpNot:
1473c9710b42Smrg	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1474c9710b42Smrg	switch ((int) vl.type) {
14752c393a42Smrg	case FcTypeBool:
14762c393a42Smrg	    v.type = FcTypeBool;
14772c393a42Smrg	    v.u.b = !vl.u.b;
14782c393a42Smrg	    break;
14792c393a42Smrg	default:
14802c393a42Smrg	    v.type = FcTypeVoid;
14812c393a42Smrg	    break;
14822c393a42Smrg	}
14832c393a42Smrg	FcValueDestroy (vl);
14842c393a42Smrg	break;
14852c393a42Smrg    case FcOpFloor:
1486c9710b42Smrg	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1487c9710b42Smrg	switch ((int) vl.type) {
14882c393a42Smrg	case FcTypeInteger:
14892c393a42Smrg	    v = vl;
14902c393a42Smrg	    break;
14912c393a42Smrg	case FcTypeDouble:
14922c393a42Smrg	    v.type = FcTypeInteger;
14932c393a42Smrg	    v.u.i = FcDoubleFloor (vl.u.d);
14942c393a42Smrg	    break;
14952c393a42Smrg	default:
14962c393a42Smrg	    v.type = FcTypeVoid;
14972c393a42Smrg	    break;
14982c393a42Smrg	}
14992c393a42Smrg	FcValueDestroy (vl);
15002c393a42Smrg	break;
15012c393a42Smrg    case FcOpCeil:
1502c9710b42Smrg	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1503c9710b42Smrg	switch ((int) vl.type) {
15042c393a42Smrg	case FcTypeInteger:
15052c393a42Smrg	    v = vl;
15062c393a42Smrg	    break;
15072c393a42Smrg	case FcTypeDouble:
15082c393a42Smrg	    v.type = FcTypeInteger;
15092c393a42Smrg	    v.u.i = FcDoubleCeil (vl.u.d);
15102c393a42Smrg	    break;
15112c393a42Smrg	default:
15122c393a42Smrg	    v.type = FcTypeVoid;
15132c393a42Smrg	    break;
15142c393a42Smrg	}
15152c393a42Smrg	FcValueDestroy (vl);
15162c393a42Smrg	break;
15172c393a42Smrg    case FcOpRound:
1518c9710b42Smrg	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1519c9710b42Smrg	switch ((int) vl.type) {
15202c393a42Smrg	case FcTypeInteger:
15212c393a42Smrg	    v = vl;
15222c393a42Smrg	    break;
15232c393a42Smrg	case FcTypeDouble:
15242c393a42Smrg	    v.type = FcTypeInteger;
15252c393a42Smrg	    v.u.i = FcDoubleRound (vl.u.d);
15262c393a42Smrg	    break;
15272c393a42Smrg	default:
15282c393a42Smrg	    v.type = FcTypeVoid;
15292c393a42Smrg	    break;
15302c393a42Smrg	}
15312c393a42Smrg	FcValueDestroy (vl);
15322c393a42Smrg	break;
15332c393a42Smrg    case FcOpTrunc:
1534c9710b42Smrg	vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1535c9710b42Smrg	switch ((int) vl.type) {
15362c393a42Smrg	case FcTypeInteger:
15372c393a42Smrg	    v = vl;
15382c393a42Smrg	    break;
15392c393a42Smrg	case FcTypeDouble:
15402c393a42Smrg	    v.type = FcTypeInteger;
15412c393a42Smrg	    v.u.i = FcDoubleTrunc (vl.u.d);
15422c393a42Smrg	    break;
15432c393a42Smrg	default:
15442c393a42Smrg	    v.type = FcTypeVoid;
15452c393a42Smrg	    break;
15462c393a42Smrg	}
15472c393a42Smrg	FcValueDestroy (vl);
15482c393a42Smrg	break;
15492c393a42Smrg    default:
15502c393a42Smrg	v.type = FcTypeVoid;
15512c393a42Smrg	break;
15522c393a42Smrg    }
15532c393a42Smrg    return v;
15542c393a42Smrg}
15552c393a42Smrg
15567872e0a1Smrg/* The bulk of the time in FcConfigSubstitute is spent walking
15577872e0a1Smrg * lists of family names. We speed this up with a hash table.
15587872e0a1Smrg * Since we need to take the ignore-blanks option into account,
15597872e0a1Smrg * we use two separate hash tables.
15607872e0a1Smrg */
15617872e0a1Smrgtypedef struct
15627872e0a1Smrg{
15637872e0a1Smrg  int count;
15647872e0a1Smrg} FamilyTableEntry;
15657872e0a1Smrg
15667872e0a1Smrg
15677872e0a1Smrgtypedef struct
15687872e0a1Smrg{
15697872e0a1Smrg  FcHashTable *family_blank_hash;
15707872e0a1Smrg  FcHashTable *family_hash;
15717872e0a1Smrg} FamilyTable;
15727872e0a1Smrg
15737872e0a1Smrgstatic FcBool
15747872e0a1SmrgFamilyTableLookup (FamilyTable   *table,
15757872e0a1Smrg                   FcOp           _op,
15767872e0a1Smrg                   const FcChar8 *s)
15777872e0a1Smrg{
15787872e0a1Smrg    FamilyTableEntry *fe;
15797872e0a1Smrg    int flags = FC_OP_GET_FLAGS (_op);
15807872e0a1Smrg    FcHashTable *hash;
15817872e0a1Smrg
15827872e0a1Smrg    if (flags & FcOpFlagIgnoreBlanks)
15837872e0a1Smrg        hash = table->family_blank_hash;
15847872e0a1Smrg    else
15857872e0a1Smrg        hash = table->family_hash;
15867872e0a1Smrg
15877872e0a1Smrg    return FcHashTableFind (hash, (const void *)s, (void **)&fe);
15887872e0a1Smrg}
15897872e0a1Smrg
15907872e0a1Smrgstatic void
15917872e0a1SmrgFamilyTableAdd (FamilyTable    *table,
15927872e0a1Smrg                FcValueListPtr  values)
15937872e0a1Smrg{
15947872e0a1Smrg    FcValueListPtr ll;
15957872e0a1Smrg    for (ll = values; ll; ll = FcValueListNext (ll))
15967872e0a1Smrg        {
15977872e0a1Smrg            const FcChar8 *s = FcValueString (&ll->value);
15987872e0a1Smrg            FamilyTableEntry *fe;
15997872e0a1Smrg
16007872e0a1Smrg            if (!FcHashTableFind (table->family_hash, (const void *)s, (void **)&fe))
16017872e0a1Smrg            {
16027872e0a1Smrg                fe = malloc (sizeof (FamilyTableEntry));
16037872e0a1Smrg                fe->count = 0;
16047872e0a1Smrg                FcHashTableAdd (table->family_hash, (void *)s, fe);
16057872e0a1Smrg            }
16067872e0a1Smrg            fe->count++;
16077872e0a1Smrg
16087872e0a1Smrg            if (!FcHashTableFind (table->family_blank_hash, (const void *)s, (void **)&fe))
16097872e0a1Smrg            {
16107872e0a1Smrg                fe = malloc (sizeof (FamilyTableEntry));
16117872e0a1Smrg                fe->count = 0;
16127872e0a1Smrg                FcHashTableAdd (table->family_blank_hash, (void *)s, fe);
16137872e0a1Smrg            }
16147872e0a1Smrg            fe->count++;
16157872e0a1Smrg       }
16167872e0a1Smrg}
16177872e0a1Smrg
16187872e0a1Smrgstatic void
16197872e0a1SmrgFamilyTableDel (FamilyTable   *table,
16207872e0a1Smrg                const FcChar8 *s)
16217872e0a1Smrg{
16227872e0a1Smrg    FamilyTableEntry *fe;
16237872e0a1Smrg
16247872e0a1Smrg    if (FcHashTableFind (table->family_hash, (void *)s, (void **)&fe))
16257872e0a1Smrg    {
16267872e0a1Smrg        fe->count--;
16277872e0a1Smrg        if (fe->count == 0)
16287872e0a1Smrg            FcHashTableRemove (table->family_hash, (void *)s);
16297872e0a1Smrg    }
16307872e0a1Smrg
16317872e0a1Smrg    if (FcHashTableFind (table->family_blank_hash, (void *)s, (void **)&fe))
16327872e0a1Smrg    {
16337872e0a1Smrg        fe->count--;
16347872e0a1Smrg        if (fe->count == 0)
16357872e0a1Smrg            FcHashTableRemove (table->family_blank_hash, (void *)s);
16367872e0a1Smrg    }
16377872e0a1Smrg}
16387872e0a1Smrg
16397872e0a1Smrgstatic FcBool
16407872e0a1Smrgcopy_string (const void *src, void **dest)
16417872e0a1Smrg{
16427872e0a1Smrg  *dest = strdup ((char *)src);
16437872e0a1Smrg  return FcTrue;
16447872e0a1Smrg}
16457872e0a1Smrg
16467872e0a1Smrgstatic void
16477872e0a1SmrgFamilyTableInit (FamilyTable *table,
16487872e0a1Smrg                 FcPattern *p)
16497872e0a1Smrg{
16507872e0a1Smrg    FcPatternElt *e;
16517872e0a1Smrg
16527872e0a1Smrg    table->family_blank_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
16537872e0a1Smrg                                          (FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
16547872e0a1Smrg                                          (FcCopyFunc)copy_string,
16557872e0a1Smrg                                          NULL,
16567872e0a1Smrg                                          free,
16577872e0a1Smrg                                          free);
16587872e0a1Smrg    table->family_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreCase,
16597872e0a1Smrg                                          (FcCompareFunc)FcStrCmpIgnoreCase,
16607872e0a1Smrg                                          (FcCopyFunc)copy_string,
16617872e0a1Smrg                                          NULL,
16627872e0a1Smrg                                          free,
16637872e0a1Smrg                                          free);
16647872e0a1Smrg    e = FcPatternObjectFindElt (p, FC_FAMILY_OBJECT);
16657872e0a1Smrg    if (e)
16667872e0a1Smrg        FamilyTableAdd (table, FcPatternEltValues (e));
16677872e0a1Smrg}
16687872e0a1Smrg
16697872e0a1Smrgstatic void
16707872e0a1SmrgFamilyTableClear (FamilyTable *table)
16717872e0a1Smrg{
16727872e0a1Smrg    if (table->family_blank_hash)
16737872e0a1Smrg        FcHashTableDestroy (table->family_blank_hash);
16747872e0a1Smrg    if (table->family_hash)
16757872e0a1Smrg        FcHashTableDestroy (table->family_hash);
16767872e0a1Smrg}
16777872e0a1Smrg
16782c393a42Smrgstatic FcValueList *
16792c393a42SmrgFcConfigMatchValueList (FcPattern	*p,
1680c9710b42Smrg			FcPattern	*p_pat,
1681c9710b42Smrg			FcMatchKind      kind,
16822c393a42Smrg			FcTest		*t,
16837872e0a1Smrg			FcValueList	*values,
16847872e0a1Smrg                        FamilyTable     *table)
16852c393a42Smrg{
16862c393a42Smrg    FcValueList	    *ret = 0;
16872c393a42Smrg    FcExpr	    *e = t->expr;
16882c393a42Smrg    FcValue	    value;
16892c393a42Smrg    FcValueList	    *v;
16907872e0a1Smrg    FcOp            op;
1691ca08ab68Smrg
16922c393a42Smrg    while (e)
16932c393a42Smrg    {
16942c393a42Smrg	/* Compute the value of the match expression */
1695ca08ab68Smrg	if (FC_OP_GET_OP (e->op) == FcOpComma)
16962c393a42Smrg	{
1697c9710b42Smrg	    value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
16982c393a42Smrg	    e = e->u.tree.right;
16992c393a42Smrg	}
17002c393a42Smrg	else
17012c393a42Smrg	{
1702c9710b42Smrg	    value = FcConfigEvaluate (p, p_pat, kind, e);
17032c393a42Smrg	    e = 0;
17042c393a42Smrg	}
17052c393a42Smrg
17067872e0a1Smrg        if (t->object == FC_FAMILY_OBJECT && table)
17077872e0a1Smrg        {
17087872e0a1Smrg            op = FC_OP_GET_OP (t->op);
17097872e0a1Smrg            if (op == FcOpEqual || op == FcOpListing)
17107872e0a1Smrg            {
17117872e0a1Smrg                if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
17127872e0a1Smrg                {
17137872e0a1Smrg                    ret = 0;
17147872e0a1Smrg                    goto done;
17157872e0a1Smrg                }
17167872e0a1Smrg            }
17177872e0a1Smrg            if (op == FcOpNotEqual && t->qual == FcQualAll)
17187872e0a1Smrg            {
17197872e0a1Smrg                ret = 0;
17207872e0a1Smrg                if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
17217872e0a1Smrg                {
17227872e0a1Smrg                    ret = values;
17237872e0a1Smrg                }
17247872e0a1Smrg                goto done;
17257872e0a1Smrg            }
17267872e0a1Smrg        }
17272c393a42Smrg	for (v = values; v; v = FcValueListNext(v))
17282c393a42Smrg	{
17292c393a42Smrg	    /* Compare the pattern value to the match expression value */
17302c393a42Smrg	    if (FcConfigCompareValue (&v->value, t->op, &value))
17312c393a42Smrg	    {
17322c393a42Smrg		if (!ret)
17332c393a42Smrg		    ret = v;
17347872e0a1Smrg                if (t->qual != FcQualAll)
17357872e0a1Smrg                    break;
17362c393a42Smrg	    }
17372c393a42Smrg	    else
17382c393a42Smrg	    {
17392c393a42Smrg		if (t->qual == FcQualAll)
17402c393a42Smrg		{
17412c393a42Smrg		    ret = 0;
17422c393a42Smrg		    break;
17432c393a42Smrg		}
17442c393a42Smrg	    }
17452c393a42Smrg	}
17467872e0a1Smrgdone:
17472c393a42Smrg	FcValueDestroy (value);
17482c393a42Smrg    }
17492c393a42Smrg    return ret;
17502c393a42Smrg}
17512c393a42Smrg
17522c393a42Smrgstatic FcValueList *
1753c9710b42SmrgFcConfigValues (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e, FcValueBinding binding)
17542c393a42Smrg{
17552c393a42Smrg    FcValueList	*l;
1756ca08ab68Smrg
17572c393a42Smrg    if (!e)
17582c393a42Smrg	return 0;
17592c393a42Smrg    l = (FcValueList *) malloc (sizeof (FcValueList));
17602c393a42Smrg    if (!l)
17612c393a42Smrg	return 0;
1762ca08ab68Smrg    if (FC_OP_GET_OP (e->op) == FcOpComma)
17632c393a42Smrg    {
1764c9710b42Smrg	l->value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1765c9710b42Smrg	l->next = FcConfigValues (p, p_pat, kind, e->u.tree.right, binding);
17662c393a42Smrg    }
17672c393a42Smrg    else
17682c393a42Smrg    {
1769c9710b42Smrg	l->value = FcConfigEvaluate (p, p_pat, kind, e);
17702c393a42Smrg	l->next = NULL;
17712c393a42Smrg    }
17722c393a42Smrg    l->binding = binding;
17732c393a42Smrg    if (l->value.type == FcTypeVoid)
17742c393a42Smrg    {
17752c393a42Smrg	FcValueList  *next = FcValueListNext(l);
17762c393a42Smrg
17772c393a42Smrg	free (l);
17782c393a42Smrg	l = next;
17792c393a42Smrg    }
17802c393a42Smrg
17812c393a42Smrg    return l;
17822c393a42Smrg}
17832c393a42Smrg
17842c393a42Smrgstatic FcBool
17852c393a42SmrgFcConfigAdd (FcValueListPtr *head,
17862c393a42Smrg	     FcValueList    *position,
17872c393a42Smrg	     FcBool	    append,
1788c9710b42Smrg	     FcValueList    *new,
17897872e0a1Smrg	     FcObject        object,
17907872e0a1Smrg             FamilyTable    *table)
17912c393a42Smrg{
1792c9710b42Smrg    FcValueListPtr  *prev, l, last, v;
17932c393a42Smrg    FcValueBinding  sameBinding;
1794ca08ab68Smrg
1795c9710b42Smrg    /*
1796c9710b42Smrg     * Make sure the stored type is valid for built-in objects
1797c9710b42Smrg     */
1798c9710b42Smrg    for (l = new; l != NULL; l = FcValueListNext (l))
1799c9710b42Smrg    {
1800c9710b42Smrg	if (!FcObjectValidType (object, l->value.type))
1801c9710b42Smrg	{
1802c9710b42Smrg	    fprintf (stderr,
1803c9710b42Smrg		     "Fontconfig warning: FcPattern object %s does not accept value", FcObjectName (object));
1804c9710b42Smrg	    FcValuePrintFile (stderr, l->value);
1805c9710b42Smrg	    fprintf (stderr, "\n");
1806c9710b42Smrg
1807c9710b42Smrg	    if (FcDebug () & FC_DBG_EDIT)
1808c9710b42Smrg	    {
1809c9710b42Smrg		printf ("Not adding\n");
1810c9710b42Smrg	    }
1811c9710b42Smrg
1812c9710b42Smrg	    return FcFalse;
1813c9710b42Smrg	}
1814c9710b42Smrg    }
1815c9710b42Smrg
18167872e0a1Smrg    if (object == FC_FAMILY_OBJECT && table)
18177872e0a1Smrg    {
18187872e0a1Smrg        FamilyTableAdd (table, new);
18197872e0a1Smrg    }
18207872e0a1Smrg
18212c393a42Smrg    if (position)
18222c393a42Smrg	sameBinding = position->binding;
18232c393a42Smrg    else
18242c393a42Smrg	sameBinding = FcValueBindingWeak;
18252c393a42Smrg    for (v = new; v != NULL; v = FcValueListNext(v))
18262c393a42Smrg	if (v->binding == FcValueBindingSame)
18272c393a42Smrg	    v->binding = sameBinding;
18282c393a42Smrg    if (append)
18292c393a42Smrg    {
18302c393a42Smrg	if (position)
18312c393a42Smrg	    prev = &position->next;
18322c393a42Smrg	else
1833ca08ab68Smrg	    for (prev = head; *prev != NULL;
18342c393a42Smrg		 prev = &(*prev)->next)
18352c393a42Smrg		;
18362c393a42Smrg    }
18372c393a42Smrg    else
18382c393a42Smrg    {
18392c393a42Smrg	if (position)
18402c393a42Smrg	{
1841ca08ab68Smrg	    for (prev = head; *prev != NULL;
18422c393a42Smrg		 prev = &(*prev)->next)
18432c393a42Smrg	    {
18442c393a42Smrg		if (*prev == position)
18452c393a42Smrg		    break;
18462c393a42Smrg	    }
18472c393a42Smrg	}
18482c393a42Smrg	else
18492c393a42Smrg	    prev = head;
18502c393a42Smrg
18512c393a42Smrg	if (FcDebug () & FC_DBG_EDIT)
18522c393a42Smrg	{
18532c393a42Smrg	    if (*prev == NULL)
18542c393a42Smrg		printf ("position not on list\n");
18552c393a42Smrg	}
18562c393a42Smrg    }
18572c393a42Smrg
18582c393a42Smrg    if (FcDebug () & FC_DBG_EDIT)
18592c393a42Smrg    {
18602c393a42Smrg	printf ("%s list before ", append ? "Append" : "Prepend");
1861ca08ab68Smrg	FcValueListPrintWithPosition (*head, *prev);
18622c393a42Smrg	printf ("\n");
18632c393a42Smrg    }
1864ca08ab68Smrg
18652c393a42Smrg    if (new)
18662c393a42Smrg    {
18672c393a42Smrg	last = new;
18682c393a42Smrg	while (last->next != NULL)
18692c393a42Smrg	    last = last->next;
1870ca08ab68Smrg
18712c393a42Smrg	last->next = *prev;
18722c393a42Smrg	*prev = new;
18732c393a42Smrg    }
1874ca08ab68Smrg
18752c393a42Smrg    if (FcDebug () & FC_DBG_EDIT)
18762c393a42Smrg    {
18772c393a42Smrg	printf ("%s list after ", append ? "Append" : "Prepend");
18782c393a42Smrg	FcValueListPrint (*head);
18792c393a42Smrg	printf ("\n");
18802c393a42Smrg    }
1881ca08ab68Smrg
18822c393a42Smrg    return FcTrue;
18832c393a42Smrg}
18842c393a42Smrg
18852c393a42Smrgstatic void
18862c393a42SmrgFcConfigDel (FcValueListPtr *head,
18877872e0a1Smrg	     FcValueList    *position,
18887872e0a1Smrg             FcObject        object,
18897872e0a1Smrg             FamilyTable    *table)
18902c393a42Smrg{
18912c393a42Smrg    FcValueListPtr *prev;
18922c393a42Smrg
18937872e0a1Smrg    if (object == FC_FAMILY_OBJECT && table)
18947872e0a1Smrg    {
18957872e0a1Smrg        FamilyTableDel (table, FcValueString (&position->value));
18967872e0a1Smrg    }
18977872e0a1Smrg
18982c393a42Smrg    for (prev = head; *prev != NULL; prev = &(*prev)->next)
18992c393a42Smrg    {
19002c393a42Smrg	if (*prev == position)
19012c393a42Smrg	{
19022c393a42Smrg	    *prev = position->next;
19032c393a42Smrg	    position->next = NULL;
19042c393a42Smrg	    FcValueListDestroy (position);
19052c393a42Smrg	    break;
19062c393a42Smrg	}
19072c393a42Smrg    }
19082c393a42Smrg}
19092c393a42Smrg
19102c393a42Smrgstatic void
19112c393a42SmrgFcConfigPatternAdd (FcPattern	*p,
19127872e0a1Smrg		    FcObject	 object,
19132c393a42Smrg		    FcValueList	*list,
19147872e0a1Smrg		    FcBool	 append,
19157872e0a1Smrg                    FamilyTable *table)
19162c393a42Smrg{
19172c393a42Smrg    if (list)
19182c393a42Smrg    {
19192c393a42Smrg	FcPatternElt    *e = FcPatternObjectInsertElt (p, object);
1920ca08ab68Smrg
19212c393a42Smrg	if (!e)
19222c393a42Smrg	    return;
19237872e0a1Smrg	FcConfigAdd (&e->values, 0, append, list, object, table);
19242c393a42Smrg    }
19252c393a42Smrg}
19262c393a42Smrg
19272c393a42Smrg/*
19282c393a42Smrg * Delete all values associated with a field
19292c393a42Smrg */
19302c393a42Smrgstatic void
19312c393a42SmrgFcConfigPatternDel (FcPattern	*p,
19327872e0a1Smrg		    FcObject	 object,
19337872e0a1Smrg                    FamilyTable *table)
19342c393a42Smrg{
19352c393a42Smrg    FcPatternElt    *e = FcPatternObjectFindElt (p, object);
19362c393a42Smrg    if (!e)
19372c393a42Smrg	return;
19382c393a42Smrg    while (e->values != NULL)
19397872e0a1Smrg	FcConfigDel (&e->values, e->values, object, table);
19402c393a42Smrg}
19412c393a42Smrg
19422c393a42Smrgstatic void
19432c393a42SmrgFcConfigPatternCanon (FcPattern	    *p,
19442c393a42Smrg		      FcObject	    object)
19452c393a42Smrg{
19462c393a42Smrg    FcPatternElt    *e = FcPatternObjectFindElt (p, object);
19472c393a42Smrg    if (!e)
19482c393a42Smrg	return;
19492c393a42Smrg    if (e->values == NULL)
19502c393a42Smrg	FcPatternObjectDel (p, object);
19512c393a42Smrg}
19522c393a42Smrg
19532c393a42SmrgFcBool
19542c393a42SmrgFcConfigSubstituteWithPat (FcConfig    *config,
19552c393a42Smrg			   FcPattern   *p,
19562c393a42Smrg			   FcPattern   *p_pat,
19572c393a42Smrg			   FcMatchKind kind)
19582c393a42Smrg{
1959c9710b42Smrg    FcValue v;
19601887081fSmrg    FcPtrList	    *s;
19611887081fSmrg    FcPtrListIter    iter, iter2;
19626fc018e4Smrg    FcRule          *r;
19631887081fSmrg    FcRuleSet	    *rs;
19646fc018e4Smrg    FcValueList	    *l, **value = NULL, *vl;
19652c393a42Smrg    FcPattern	    *m;
1966ca08ab68Smrg    FcStrSet	    *strs;
19676fc018e4Smrg    FcObject	    object = FC_INVALID_OBJECT;
19686fc018e4Smrg    FcPatternElt    **elt = NULL, *e;
19696fc018e4Smrg    int		    i, nobjs;
19706fc018e4Smrg    FcBool	    retval = FcTrue;
19716fc018e4Smrg    FcTest	    **tst = NULL;
19727872e0a1Smrg    FamilyTable     data;
19737872e0a1Smrg    FamilyTable     *table = &data;
19742c393a42Smrg
19751887081fSmrg    if (kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
19761887081fSmrg	return FcFalse;
19777872e0a1Smrg
19787872e0a1Smrg    config = FcConfigReference (config);
19797872e0a1Smrg    if (!config)
19807872e0a1Smrg	return FcFalse;
19817872e0a1Smrg
19821887081fSmrg    s = config->subst[kind];
19831887081fSmrg    if (kind == FcMatchPattern)
19841887081fSmrg    {
1985ca08ab68Smrg	strs = FcGetDefaultLangs ();
1986ca08ab68Smrg	if (strs)
1987ca08ab68Smrg	{
1988ca08ab68Smrg	    FcStrList *l = FcStrListCreate (strs);
1989ca08ab68Smrg	    FcChar8 *lang;
1990ca08ab68Smrg	    FcValue v;
199118bd4a06Smrg	    FcLangSet *lsund = FcLangSetCreate ();
1992ca08ab68Smrg
199318bd4a06Smrg	    FcLangSetAdd (lsund, (const FcChar8 *)"und");
1994ca08ab68Smrg	    FcStrSetDestroy (strs);
1995ca08ab68Smrg	    while (l && (lang = FcStrListNext (l)))
1996ca08ab68Smrg	    {
199718bd4a06Smrg		FcPatternElt *e = FcPatternObjectFindElt (p, FC_LANG_OBJECT);
199818bd4a06Smrg
199918bd4a06Smrg		if (e)
200018bd4a06Smrg		{
200118bd4a06Smrg		    FcValueListPtr ll;
200218bd4a06Smrg
200318bd4a06Smrg		    for (ll = FcPatternEltValues (e); ll; ll = FcValueListNext (ll))
200418bd4a06Smrg		    {
200518bd4a06Smrg			FcValue vv = FcValueCanonicalize (&ll->value);
200618bd4a06Smrg
200718bd4a06Smrg			if (vv.type == FcTypeLangSet)
200818bd4a06Smrg			{
200918bd4a06Smrg			    FcLangSet *ls = FcLangSetCreate ();
201018bd4a06Smrg			    FcBool b;
201118bd4a06Smrg
201218bd4a06Smrg			    FcLangSetAdd (ls, lang);
201318bd4a06Smrg			    b = FcLangSetContains (vv.u.l, ls);
201418bd4a06Smrg			    FcLangSetDestroy (ls);
201518bd4a06Smrg			    if (b)
201618bd4a06Smrg				goto bail_lang;
201718bd4a06Smrg			    if (FcLangSetContains (vv.u.l, lsund))
201818bd4a06Smrg				goto bail_lang;
201918bd4a06Smrg			}
202018bd4a06Smrg			else
202118bd4a06Smrg			{
202218bd4a06Smrg			    if (FcStrCmpIgnoreCase (vv.u.s, lang) == 0)
202318bd4a06Smrg				goto bail_lang;
202418bd4a06Smrg			    if (FcStrCmpIgnoreCase (vv.u.s, (const FcChar8 *)"und") == 0)
202518bd4a06Smrg				goto bail_lang;
202618bd4a06Smrg			}
202718bd4a06Smrg		    }
202818bd4a06Smrg		}
2029ca08ab68Smrg		v.type = FcTypeString;
2030ca08ab68Smrg		v.u.s = lang;
203118bd4a06Smrg
2032ca08ab68Smrg		FcPatternObjectAddWithBinding (p, FC_LANG_OBJECT, v, FcValueBindingWeak, FcTrue);
2033ca08ab68Smrg	    }
203418bd4a06Smrg	bail_lang:
2035ca08ab68Smrg	    FcStrListDone (l);
203618bd4a06Smrg	    FcLangSetDestroy (lsund);
2037ca08ab68Smrg	}
2038c9710b42Smrg	if (FcPatternObjectGet (p, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch)
2039c9710b42Smrg	{
2040c9710b42Smrg	    FcChar8 *prgname = FcGetPrgname ();
2041c9710b42Smrg	    if (prgname)
2042c9710b42Smrg		FcPatternObjectAddString (p, FC_PRGNAME_OBJECT, prgname);
2043c9710b42Smrg	}
20442c393a42Smrg    }
20452c393a42Smrg
20466fc018e4Smrg    nobjs = FC_MAX_BASE_OBJECT + config->maxObjects + 2;
2047cc1bebd6Smrg    value = (FcValueList **) malloc (sizeof(void *) * nobjs);
20486fc018e4Smrg    if (!value)
20496fc018e4Smrg    {
20506fc018e4Smrg	retval = FcFalse;
20516fc018e4Smrg	goto bail1;
20526fc018e4Smrg    }
2053cc1bebd6Smrg    elt = (FcPatternElt **) malloc (sizeof(void *) * nobjs);
20546fc018e4Smrg    if (!elt)
20556fc018e4Smrg    {
20566fc018e4Smrg	retval = FcFalse;
20576fc018e4Smrg	goto bail1;
20586fc018e4Smrg    }
2059cc1bebd6Smrg    tst = (FcTest **) malloc (sizeof(void *) * nobjs);
20606fc018e4Smrg    if (!tst)
20616fc018e4Smrg    {
20626fc018e4Smrg	retval = FcFalse;
20636fc018e4Smrg	goto bail1;
20646fc018e4Smrg    }
20652c393a42Smrg
20662c393a42Smrg    if (FcDebug () & FC_DBG_EDIT)
20672c393a42Smrg    {
20682c393a42Smrg	printf ("FcConfigSubstitute ");
20692c393a42Smrg	FcPatternPrint (p);
20702c393a42Smrg    }
20717872e0a1Smrg
20727872e0a1Smrg    FamilyTableInit (&data, p);
20737872e0a1Smrg
20741887081fSmrg    FcPtrListIterInit (s, &iter);
20751887081fSmrg    for (; FcPtrListIterIsValid (s, &iter); FcPtrListIterNext (s, &iter))
20762c393a42Smrg    {
20771887081fSmrg	rs = (FcRuleSet *) FcPtrListIterGetValue (s, &iter);
20781887081fSmrg	if (FcDebug () & FC_DBG_EDIT)
20792c393a42Smrg	{
20801887081fSmrg	    printf ("\nRule Set: %s\n", rs->name);
20816fc018e4Smrg	}
20821887081fSmrg	FcPtrListIterInit (rs->subst[kind], &iter2);
20831887081fSmrg	for (; FcPtrListIterIsValid (rs->subst[kind], &iter2); FcPtrListIterNext (rs->subst[kind], &iter2))
20846fc018e4Smrg	{
20851887081fSmrg	    r = (FcRule *) FcPtrListIterGetValue (rs->subst[kind], &iter2);
20861887081fSmrg	    for (i = 0; i < nobjs; i++)
20871887081fSmrg	    {
20881887081fSmrg		elt[i] = NULL;
20891887081fSmrg		value[i] = NULL;
20901887081fSmrg		tst[i] = NULL;
20911887081fSmrg	    }
20921887081fSmrg	    for (; r; r = r->next)
20931887081fSmrg	    {
20941887081fSmrg		switch (r->type) {
20951887081fSmrg		case FcRuleUnknown:
20961887081fSmrg		    /* shouldn't be reached */
20971887081fSmrg		    break;
20981887081fSmrg		case FcRuleTest:
20991887081fSmrg		    object = FC_OBJ_ID (r->u.test->object);
21001887081fSmrg		    /*
21011887081fSmrg		     * Check the tests to see if
21021887081fSmrg		     * they all match the pattern
21031887081fSmrg		     */
21041887081fSmrg		    if (FcDebug () & FC_DBG_EDIT)
21056fc018e4Smrg		    {
21061887081fSmrg			printf ("FcConfigSubstitute test ");
21071887081fSmrg			FcTestPrint (r->u.test);
21086fc018e4Smrg		    }
21091887081fSmrg		    if (kind == FcMatchFont && r->u.test->kind == FcMatchPattern)
21107872e0a1Smrg                    {
21111887081fSmrg			m = p_pat;
21127872e0a1Smrg                        table = NULL;
21137872e0a1Smrg                    }
21146fc018e4Smrg		    else
21157872e0a1Smrg                    {
21161887081fSmrg			m = p;
21177872e0a1Smrg                        table = &data;
21187872e0a1Smrg                    }
21191887081fSmrg		    if (m)
21201887081fSmrg			e = FcPatternObjectFindElt (m, r->u.test->object);
21211887081fSmrg		    else
21221887081fSmrg			e = NULL;
21231887081fSmrg		    /* different 'kind' won't be the target of edit */
21241887081fSmrg		    if (!elt[object] && kind == r->u.test->kind)
21251887081fSmrg		    {
21261887081fSmrg			elt[object] = e;
21271887081fSmrg			tst[object] = r->u.test;
21281887081fSmrg		    }
21291887081fSmrg		    /*
21301887081fSmrg		     * If there's no such field in the font,
21311887081fSmrg		     * then FcQualAll matches while FcQualAny does not
21321887081fSmrg		     */
21331887081fSmrg		    if (!e)
21341887081fSmrg		    {
21351887081fSmrg			if (r->u.test->qual == FcQualAll)
21361887081fSmrg			{
21371887081fSmrg			    value[object] = NULL;
21381887081fSmrg			    continue;
21391887081fSmrg			}
21401887081fSmrg			else
21411887081fSmrg			{
21421887081fSmrg			    if (FcDebug () & FC_DBG_EDIT)
21431887081fSmrg				printf ("No match\n");
21441887081fSmrg			    goto bail;
21451887081fSmrg			}
21461887081fSmrg		    }
21471887081fSmrg		    /*
21481887081fSmrg		     * Check to see if there is a match, mark the location
21491887081fSmrg		     * to apply match-relative edits
21501887081fSmrg		     */
21517872e0a1Smrg		    vl = FcConfigMatchValueList (m, p_pat, kind, r->u.test, e->values, table);
21521887081fSmrg		    /* different 'kind' won't be the target of edit */
21531887081fSmrg		    if (!value[object] && kind == r->u.test->kind)
21541887081fSmrg			value[object] = vl;
21551887081fSmrg		    if (!vl ||
21561887081fSmrg			(r->u.test->qual == FcQualFirst && vl != e->values) ||
21571887081fSmrg			(r->u.test->qual == FcQualNotFirst && vl == e->values))
21586fc018e4Smrg		    {
21596fc018e4Smrg			if (FcDebug () & FC_DBG_EDIT)
21606fc018e4Smrg			    printf ("No match\n");
21616fc018e4Smrg			goto bail;
21626fc018e4Smrg		    }
21631887081fSmrg		    break;
21641887081fSmrg		case FcRuleEdit:
21651887081fSmrg		    object = FC_OBJ_ID (r->u.edit->object);
21666fc018e4Smrg		    if (FcDebug () & FC_DBG_EDIT)
21671887081fSmrg		    {
21681887081fSmrg			printf ("Substitute ");
21691887081fSmrg			FcEditPrint (r->u.edit);
21701887081fSmrg			printf ("\n\n");
21711887081fSmrg		    }
21722c393a42Smrg		    /*
21731887081fSmrg		     * Evaluate the list of expressions
21742c393a42Smrg		     */
21757872e0a1Smrg		    l = FcConfigValues (p, p_pat, kind, r->u.edit->expr, r->u.edit->binding);
21761887081fSmrg		    if (tst[object] && (tst[object]->kind == FcMatchFont || kind == FcMatchPattern))
21771887081fSmrg			elt[object] = FcPatternObjectFindElt (p, tst[object]->object);
21786fc018e4Smrg
21791887081fSmrg		    switch (FC_OP_GET_OP (r->u.edit->op)) {
21801887081fSmrg		    case FcOpAssign:
21816fc018e4Smrg			/*
21821887081fSmrg			 * If there was a test, then replace the matched
21831887081fSmrg			 * value with the new list of values
21846fc018e4Smrg			 */
21851887081fSmrg			if (value[object])
21861887081fSmrg			{
21871887081fSmrg			    FcValueList	*thisValue = value[object];
21881887081fSmrg			    FcValueList	*nextValue = l;
21891887081fSmrg
21901887081fSmrg			    /*
21911887081fSmrg			     * Append the new list of values after the current value
21921887081fSmrg			     */
21937872e0a1Smrg			    FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object, table);
21941887081fSmrg			    /*
21951887081fSmrg			     * Delete the marked value
21961887081fSmrg			     */
21971887081fSmrg			    if (thisValue)
21987872e0a1Smrg				FcConfigDel (&elt[object]->values, thisValue, object, table);
21991887081fSmrg			    /*
22001887081fSmrg			     * Adjust a pointer into the value list to ensure
22011887081fSmrg			     * future edits occur at the same place
22021887081fSmrg			     */
22031887081fSmrg			    value[object] = nextValue;
22041887081fSmrg			    break;
22051887081fSmrg			}
22061887081fSmrg			/* fall through ... */
22071887081fSmrg		    case FcOpAssignReplace:
22086fc018e4Smrg			/*
22091887081fSmrg			 * Delete all of the values and insert
22101887081fSmrg			 * the new set
22116fc018e4Smrg			 */
22127872e0a1Smrg			FcConfigPatternDel (p, r->u.edit->object, table);
22137872e0a1Smrg			FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
22146fc018e4Smrg			/*
22151887081fSmrg			 * Adjust a pointer into the value list as they no
22161887081fSmrg			 * longer point to anything valid
22176fc018e4Smrg			 */
22181887081fSmrg			value[object] = NULL;
22196fc018e4Smrg			break;
22201887081fSmrg		    case FcOpPrepend:
22211887081fSmrg			if (value[object])
22221887081fSmrg			{
22237872e0a1Smrg			    FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object, table);
22241887081fSmrg			    break;
22251887081fSmrg			}
22261887081fSmrg			/* fall through ... */
22271887081fSmrg		    case FcOpPrependFirst:
22287872e0a1Smrg			FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse, table);
22296fc018e4Smrg			break;
22301887081fSmrg		    case FcOpAppend:
22311887081fSmrg			if (value[object])
22321887081fSmrg			{
22337872e0a1Smrg			    FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object, table);
22341887081fSmrg			    break;
22351887081fSmrg			}
22361887081fSmrg			/* fall through ... */
22371887081fSmrg		    case FcOpAppendLast:
22387872e0a1Smrg			FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
22391887081fSmrg			break;
22401887081fSmrg		    case FcOpDelete:
22411887081fSmrg			if (value[object])
22421887081fSmrg			{
22437872e0a1Smrg			    FcConfigDel (&elt[object]->values, value[object], object, table);
22441887081fSmrg			    FcValueListDestroy (l);
22451887081fSmrg			    break;
22461887081fSmrg			}
22471887081fSmrg			/* fall through ... */
22481887081fSmrg		    case FcOpDeleteAll:
22497872e0a1Smrg			FcConfigPatternDel (p, r->u.edit->object, table);
22501887081fSmrg			FcValueListDestroy (l);
22511887081fSmrg			break;
22521887081fSmrg		    default:
22531887081fSmrg			FcValueListDestroy (l);
22546fc018e4Smrg			break;
22552c393a42Smrg		    }
22561887081fSmrg		    /*
22571887081fSmrg		     * Now go through the pattern and eliminate
22581887081fSmrg		     * any properties without data
22591887081fSmrg		     */
22601887081fSmrg		    FcConfigPatternCanon (p, r->u.edit->object);
22611887081fSmrg
22621887081fSmrg		    if (FcDebug () & FC_DBG_EDIT)
22636fc018e4Smrg		    {
22641887081fSmrg			printf ("FcConfigSubstitute edit");
22651887081fSmrg			FcPatternPrint (p);
22666fc018e4Smrg		    }
22672c393a42Smrg		    break;
22682c393a42Smrg		}
22692c393a42Smrg	    }
22701887081fSmrg	bail:;
22712c393a42Smrg	}
22722c393a42Smrg    }
22732c393a42Smrg    if (FcDebug () & FC_DBG_EDIT)
22742c393a42Smrg    {
22752c393a42Smrg	printf ("FcConfigSubstitute done");
22762c393a42Smrg	FcPatternPrint (p);
22772c393a42Smrg    }
22787872e0a1Smrg    FamilyTableClear (&data);
227932c2f8aeSmrgbail1:
22806fc018e4Smrg    if (elt)
22816fc018e4Smrg	free (elt);
22826fc018e4Smrg    if (value)
22836fc018e4Smrg	free (value);
22846fc018e4Smrg    if (tst)
22856fc018e4Smrg	free (tst);
22867872e0a1Smrg    FcConfigDestroy (config);
22876fc018e4Smrg
22886fc018e4Smrg    return retval;
22892c393a42Smrg}
22902c393a42Smrg
22912c393a42SmrgFcBool
22922c393a42SmrgFcConfigSubstitute (FcConfig	*config,
22932c393a42Smrg		    FcPattern	*p,
22942c393a42Smrg		    FcMatchKind	kind)
22952c393a42Smrg{
22962c393a42Smrg    return FcConfigSubstituteWithPat (config, p, 0, kind);
22972c393a42Smrg}
22982c393a42Smrg
22992c393a42Smrg#if defined (_WIN32)
23002c393a42Smrg
2301c9710b42Smrgstatic FcChar8 fontconfig_path[1000] = ""; /* MT-dontcare */
230218bd4a06SmrgFcChar8 fontconfig_instprefix[1000] = ""; /* MT-dontcare */
23032c393a42Smrg
23042c393a42Smrg#  if (defined (PIC) || defined (DLL_EXPORT))
23052c393a42Smrg
2306c9710b42SmrgBOOL WINAPI
2307c9710b42SmrgDllMain (HINSTANCE hinstDLL,
2308c9710b42Smrg	 DWORD     fdwReason,
2309c9710b42Smrg	 LPVOID    lpvReserved);
2310c9710b42Smrg
23112c393a42SmrgBOOL WINAPI
23122c393a42SmrgDllMain (HINSTANCE hinstDLL,
23132c393a42Smrg	 DWORD     fdwReason,
23142c393a42Smrg	 LPVOID    lpvReserved)
23152c393a42Smrg{
23162c393a42Smrg  FcChar8 *p;
23172c393a42Smrg
23182c393a42Smrg  switch (fdwReason) {
23192c393a42Smrg  case DLL_PROCESS_ATTACH:
2320ca08ab68Smrg      if (!GetModuleFileName ((HMODULE) hinstDLL, (LPCH) fontconfig_path,
23212c393a42Smrg			      sizeof (fontconfig_path)))
23222c393a42Smrg	  break;
23232c393a42Smrg
23242c393a42Smrg      /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
23252c393a42Smrg       * assume it's a Unix-style installation tree, and use
23262c393a42Smrg       * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
23272c393a42Smrg       * folder where the DLL is as FONTCONFIG_PATH.
23282c393a42Smrg       */
2329ca08ab68Smrg      p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
23302c393a42Smrg      if (p)
23312c393a42Smrg      {
23322c393a42Smrg	  *p = '\0';
2333ca08ab68Smrg	  p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
2334ca08ab68Smrg	  if (p && (FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "bin") == 0 ||
2335ca08ab68Smrg		    FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "lib") == 0))
23362c393a42Smrg	      *p = '\0';
233718bd4a06Smrg	  strcat ((char *) fontconfig_instprefix, (char *) fontconfig_path);
2338ca08ab68Smrg	  strcat ((char *) fontconfig_path, "\\etc\\fonts");
23392c393a42Smrg      }
23402c393a42Smrg      else
23412c393a42Smrg          fontconfig_path[0] = '\0';
2342ca08ab68Smrg
23432c393a42Smrg      break;
23442c393a42Smrg  }
23452c393a42Smrg
23462c393a42Smrg  return TRUE;
23472c393a42Smrg}
23482c393a42Smrg
23492c393a42Smrg#  endif /* !PIC */
23502c393a42Smrg
23512c393a42Smrg#undef FONTCONFIG_PATH
23522c393a42Smrg#define FONTCONFIG_PATH fontconfig_path
23532c393a42Smrg
23542c393a42Smrg#endif /* !_WIN32 */
23552c393a42Smrg
23562c393a42Smrg#ifndef FONTCONFIG_FILE
23572c393a42Smrg#define FONTCONFIG_FILE	"fonts.conf"
23582c393a42Smrg#endif
23592c393a42Smrg
23602c393a42Smrgstatic FcChar8 *
23612c393a42SmrgFcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
23622c393a42Smrg{
23632c393a42Smrg    FcChar8    *path;
2364ca08ab68Smrg    int         size, osize;
23652c393a42Smrg
23662c393a42Smrg    if (!dir)
23672c393a42Smrg	dir = (FcChar8 *) "";
2368ca08ab68Smrg
2369ca08ab68Smrg    osize = strlen ((char *) dir) + 1 + strlen ((char *) file) + 1;
2370ca08ab68Smrg    /*
2371ca08ab68Smrg     * workaround valgrind warning because glibc takes advantage of how it knows memory is
2372ca08ab68Smrg     * allocated to implement strlen by reading in groups of 4
2373ca08ab68Smrg     */
2374ca08ab68Smrg    size = (osize + 3) & ~3;
2375ca08ab68Smrg
2376ca08ab68Smrg    path = malloc (size);
23772c393a42Smrg    if (!path)
23782c393a42Smrg	return 0;
23792c393a42Smrg
23802c393a42Smrg    strcpy ((char *) path, (const char *) dir);
23812c393a42Smrg    /* make sure there's a single separator */
23822c393a42Smrg#ifdef _WIN32
23832c393a42Smrg    if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
23842c393a42Smrg		      path[strlen((char *) path)-1] != '\\')) &&
23852c393a42Smrg	!(file[0] == '/' ||
23862c393a42Smrg	  file[0] == '\\' ||
23872c393a42Smrg	  (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
23882c393a42Smrg	strcat ((char *) path, "\\");
23892c393a42Smrg#else
23902c393a42Smrg    if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
23912c393a42Smrg	strcat ((char *) path, "/");
2392c9710b42Smrg    else
2393c9710b42Smrg	osize--;
23942c393a42Smrg#endif
23952c393a42Smrg    strcat ((char *) path, (char *) file);
23962c393a42Smrg
23972c393a42Smrg    if (access ((char *) path, R_OK) == 0)
23982c393a42Smrg	return path;
2399ca08ab68Smrg
24002c393a42Smrg    FcStrFree (path);
2401ca08ab68Smrg
24022c393a42Smrg    return 0;
24032c393a42Smrg}
24042c393a42Smrg
24052c393a42Smrgstatic FcChar8 **
24062c393a42SmrgFcConfigGetPath (void)
24072c393a42Smrg{
24082c393a42Smrg    FcChar8    **path;
24092c393a42Smrg    FcChar8    *env, *e, *colon;
24102c393a42Smrg    FcChar8    *dir;
24112c393a42Smrg    int	    npath;
24122c393a42Smrg    int	    i;
24132c393a42Smrg
24142c393a42Smrg    npath = 2;	/* default dir + null */
24152c393a42Smrg    env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
24162c393a42Smrg    if (env)
24172c393a42Smrg    {
24182c393a42Smrg	e = env;
24192c393a42Smrg	npath++;
24202c393a42Smrg	while (*e)
24212c393a42Smrg	    if (*e++ == FC_SEARCH_PATH_SEPARATOR)
24222c393a42Smrg		npath++;
24232c393a42Smrg    }
24242c393a42Smrg    path = calloc (npath, sizeof (FcChar8 *));
24252c393a42Smrg    if (!path)
24262c393a42Smrg	goto bail0;
24272c393a42Smrg    i = 0;
24282c393a42Smrg
24292c393a42Smrg    if (env)
24302c393a42Smrg    {
24312c393a42Smrg	e = env;
2432ca08ab68Smrg	while (*e)
24332c393a42Smrg	{
24342c393a42Smrg	    colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
24352c393a42Smrg	    if (!colon)
24362c393a42Smrg		colon = e + strlen ((char *) e);
24372c393a42Smrg	    path[i] = malloc (colon - e + 1);
24382c393a42Smrg	    if (!path[i])
24392c393a42Smrg		goto bail1;
24402c393a42Smrg	    strncpy ((char *) path[i], (const char *) e, colon - e);
24412c393a42Smrg	    path[i][colon - e] = '\0';
24422c393a42Smrg	    if (*colon)
24432c393a42Smrg		e = colon + 1;
24442c393a42Smrg	    else
24452c393a42Smrg		e = colon;
24462c393a42Smrg	    i++;
24472c393a42Smrg	}
24482c393a42Smrg    }
2449ca08ab68Smrg
24502c393a42Smrg#ifdef _WIN32
24512c393a42Smrg	if (fontconfig_path[0] == '\0')
24522c393a42Smrg	{
2453a6844aabSmrg		char *p;
2454ca08ab68Smrg		if(!GetModuleFileName(NULL, (LPCH) fontconfig_path, sizeof(fontconfig_path)))
24552c393a42Smrg			goto bail1;
2456ca08ab68Smrg		p = strrchr ((const char *) fontconfig_path, '\\');
24572c393a42Smrg		if (p) *p = '\0';
2458ca08ab68Smrg		strcat ((char *) fontconfig_path, "\\fonts");
24592c393a42Smrg	}
24602c393a42Smrg#endif
24612c393a42Smrg    dir = (FcChar8 *) FONTCONFIG_PATH;
24622c393a42Smrg    path[i] = malloc (strlen ((char *) dir) + 1);
24632c393a42Smrg    if (!path[i])
24642c393a42Smrg	goto bail1;
24652c393a42Smrg    strcpy ((char *) path[i], (const char *) dir);
24662c393a42Smrg    return path;
24672c393a42Smrg
24682c393a42Smrgbail1:
24692c393a42Smrg    for (i = 0; path[i]; i++)
24702c393a42Smrg	free (path[i]);
24712c393a42Smrg    free (path);
24722c393a42Smrgbail0:
24732c393a42Smrg    return 0;
24742c393a42Smrg}
24752c393a42Smrg
24762c393a42Smrgstatic void
24772c393a42SmrgFcConfigFreePath (FcChar8 **path)
24782c393a42Smrg{
24792c393a42Smrg    FcChar8    **p;
24802c393a42Smrg
24812c393a42Smrg    for (p = path; *p; p++)
24822c393a42Smrg	free (*p);
24832c393a42Smrg    free (path);
24842c393a42Smrg}
24852c393a42Smrg
2486c9710b42Smrgstatic FcBool	_FcConfigHomeEnabled = FcTrue; /* MT-goodenough */
24872c393a42Smrg
24882c393a42SmrgFcChar8 *
24892c393a42SmrgFcConfigHome (void)
24902c393a42Smrg{
24912c393a42Smrg    if (_FcConfigHomeEnabled)
24922c393a42Smrg    {
24932c393a42Smrg        char *home = getenv ("HOME");
24942c393a42Smrg
24952c393a42Smrg#ifdef _WIN32
24962c393a42Smrg	if (home == NULL)
24972c393a42Smrg	    home = getenv ("USERPROFILE");
24982c393a42Smrg#endif
24992c393a42Smrg
25002c393a42Smrg	return (FcChar8 *) home;
25012c393a42Smrg    }
25022c393a42Smrg    return 0;
25032c393a42Smrg}
25042c393a42Smrg
2505ca08ab68SmrgFcChar8 *
2506ca08ab68SmrgFcConfigXdgCacheHome (void)
2507ca08ab68Smrg{
2508ca08ab68Smrg    const char *env = getenv ("XDG_CACHE_HOME");
2509ca08ab68Smrg    FcChar8 *ret = NULL;
2510ca08ab68Smrg
251118bd4a06Smrg    if (!_FcConfigHomeEnabled)
251218bd4a06Smrg	return NULL;
25137872e0a1Smrg    if (env && env[0])
2514ca08ab68Smrg	ret = FcStrCopy ((const FcChar8 *)env);
2515ca08ab68Smrg    else
2516ca08ab68Smrg    {
2517ca08ab68Smrg	const FcChar8 *home = FcConfigHome ();
2518ca08ab68Smrg	size_t len = home ? strlen ((const char *)home) : 0;
2519ca08ab68Smrg
2520ca08ab68Smrg	ret = malloc (len + 7 + 1);
2521ca08ab68Smrg	if (ret)
2522ca08ab68Smrg	{
25231887081fSmrg	    if (home)
25241887081fSmrg		memcpy (ret, home, len);
2525ca08ab68Smrg	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".cache", 7);
2526ca08ab68Smrg	    ret[len + 7] = 0;
2527ca08ab68Smrg	}
2528ca08ab68Smrg    }
2529ca08ab68Smrg
2530ca08ab68Smrg    return ret;
2531ca08ab68Smrg}
2532ca08ab68Smrg
2533ca08ab68SmrgFcChar8 *
2534ca08ab68SmrgFcConfigXdgConfigHome (void)
2535ca08ab68Smrg{
2536ca08ab68Smrg    const char *env = getenv ("XDG_CONFIG_HOME");
2537ca08ab68Smrg    FcChar8 *ret = NULL;
2538ca08ab68Smrg
253918bd4a06Smrg    if (!_FcConfigHomeEnabled)
254018bd4a06Smrg	return NULL;
2541ca08ab68Smrg    if (env)
2542ca08ab68Smrg	ret = FcStrCopy ((const FcChar8 *)env);
2543ca08ab68Smrg    else
2544ca08ab68Smrg    {
2545ca08ab68Smrg	const FcChar8 *home = FcConfigHome ();
2546ca08ab68Smrg	size_t len = home ? strlen ((const char *)home) : 0;
2547ca08ab68Smrg
2548ca08ab68Smrg	ret = malloc (len + 8 + 1);
2549ca08ab68Smrg	if (ret)
2550ca08ab68Smrg	{
25511887081fSmrg	    if (home)
25521887081fSmrg		memcpy (ret, home, len);
2553ca08ab68Smrg	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".config", 8);
2554ca08ab68Smrg	    ret[len + 8] = 0;
2555ca08ab68Smrg	}
2556ca08ab68Smrg    }
2557ca08ab68Smrg
2558ca08ab68Smrg    return ret;
2559ca08ab68Smrg}
2560ca08ab68Smrg
2561ca08ab68SmrgFcChar8 *
2562ca08ab68SmrgFcConfigXdgDataHome (void)
2563ca08ab68Smrg{
2564ca08ab68Smrg    const char *env = getenv ("XDG_DATA_HOME");
2565ca08ab68Smrg    FcChar8 *ret = NULL;
2566ca08ab68Smrg
256718bd4a06Smrg    if (!_FcConfigHomeEnabled)
256818bd4a06Smrg	return NULL;
2569ca08ab68Smrg    if (env)
2570ca08ab68Smrg	ret = FcStrCopy ((const FcChar8 *)env);
2571ca08ab68Smrg    else
2572ca08ab68Smrg    {
2573ca08ab68Smrg	const FcChar8 *home = FcConfigHome ();
2574ca08ab68Smrg	size_t len = home ? strlen ((const char *)home) : 0;
2575ca08ab68Smrg
2576ca08ab68Smrg	ret = malloc (len + 13 + 1);
2577ca08ab68Smrg	if (ret)
2578ca08ab68Smrg	{
25791887081fSmrg	    if (home)
25801887081fSmrg		memcpy (ret, home, len);
2581ca08ab68Smrg	    memcpy (&ret[len], FC_DIR_SEPARATOR_S ".local" FC_DIR_SEPARATOR_S "share", 13);
2582ca08ab68Smrg	    ret[len + 13] = 0;
2583ca08ab68Smrg	}
2584ca08ab68Smrg    }
2585ca08ab68Smrg
2586ca08ab68Smrg    return ret;
2587ca08ab68Smrg}
2588ca08ab68Smrg
25897872e0a1SmrgFcStrSet *
25907872e0a1SmrgFcConfigXdgDataDirs (void)
25917872e0a1Smrg{
25927872e0a1Smrg    const char *env = getenv ("XDG_DATA_DIRS");
25937872e0a1Smrg    FcStrSet *ret = FcStrSetCreate ();
25947872e0a1Smrg
25957872e0a1Smrg    if (env)
25967872e0a1Smrg    {
25977872e0a1Smrg	FcChar8 *ee, *e = ee = FcStrCopy ((const FcChar8 *) env);
25987872e0a1Smrg
25997872e0a1Smrg	/* We don't intentionally use FC_SEARCH_PATH_SEPARATOR here because of:
26007872e0a1Smrg	 *   The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
26017872e0a1Smrg	 * in doc.
26027872e0a1Smrg	 */
26037872e0a1Smrg	while (e)
26047872e0a1Smrg	{
26057872e0a1Smrg	    FcChar8 *p = (FcChar8 *) strchr ((const char *) e, ':');
26067872e0a1Smrg	    FcChar8 *s;
26077872e0a1Smrg	    size_t len;
26087872e0a1Smrg
26097872e0a1Smrg	    if (!p)
26107872e0a1Smrg	    {
26117872e0a1Smrg		s = FcStrCopy (e);
26127872e0a1Smrg		e = NULL;
26137872e0a1Smrg	    }
26147872e0a1Smrg	    else
26157872e0a1Smrg	    {
26167872e0a1Smrg		*p = 0;
26177872e0a1Smrg		s = FcStrCopy (e);
26187872e0a1Smrg		e = p + 1;
26197872e0a1Smrg	    }
26207872e0a1Smrg	    len = strlen ((const char *) s);
26217872e0a1Smrg	    if (s[len - 1] == FC_DIR_SEPARATOR)
26227872e0a1Smrg	    {
26237872e0a1Smrg		do
26247872e0a1Smrg		{
26257872e0a1Smrg		    len--;
26267872e0a1Smrg		}
26277872e0a1Smrg		while (len > 1 && s[len - 1] == FC_DIR_SEPARATOR);
26287872e0a1Smrg		s[len] = 0;
26297872e0a1Smrg	    }
26307872e0a1Smrg	    FcStrSetAdd (ret, s);
26317872e0a1Smrg	    FcStrFree (s);
26327872e0a1Smrg	}
26337872e0a1Smrg	FcStrFree (ee);
26347872e0a1Smrg    }
26357872e0a1Smrg    else
26367872e0a1Smrg    {
26377872e0a1Smrg	/* From spec doc at https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
26387872e0a1Smrg	 *
26397872e0a1Smrg	 * If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
26407872e0a1Smrg	 */
26417872e0a1Smrg	FcStrSetAdd (ret, (const FcChar8 *) "/usr/local/share");
26427872e0a1Smrg	FcStrSetAdd (ret, (const FcChar8 *) "/usr/share");
26437872e0a1Smrg    }
26447872e0a1Smrg
26457872e0a1Smrg    return ret;
26467872e0a1Smrg}
26477872e0a1Smrg
26482c393a42SmrgFcBool
26492c393a42SmrgFcConfigEnableHome (FcBool enable)
26502c393a42Smrg{
26512c393a42Smrg    FcBool  prev = _FcConfigHomeEnabled;
26522c393a42Smrg    _FcConfigHomeEnabled = enable;
26532c393a42Smrg    return prev;
26542c393a42Smrg}
26552c393a42Smrg
26562c393a42SmrgFcChar8 *
26577872e0a1SmrgFcConfigGetFilename (FcConfig      *config,
26587872e0a1Smrg		     const FcChar8 *url)
26592c393a42Smrg{
26602c393a42Smrg    FcChar8    *file, *dir, **path, **p;
26617872e0a1Smrg    const FcChar8 *sysroot;
26622c393a42Smrg
26637872e0a1Smrg    config = FcConfigReference (config);
26647872e0a1Smrg    if (!config)
26657872e0a1Smrg	return NULL;
26667872e0a1Smrg    sysroot = FcConfigGetSysRoot (config);
26672c393a42Smrg    if (!url || !*url)
26682c393a42Smrg    {
26692c393a42Smrg	url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
26702c393a42Smrg	if (!url)
26712c393a42Smrg	    url = (FcChar8 *) FONTCONFIG_FILE;
26722c393a42Smrg    }
26732c393a42Smrg    file = 0;
26742c393a42Smrg
26751887081fSmrg    if (FcStrIsAbsoluteFilename(url))
26767872e0a1Smrg    {
26777872e0a1Smrg	if (sysroot)
26787872e0a1Smrg	{
26797872e0a1Smrg	    size_t len = strlen ((const char *) sysroot);
26807872e0a1Smrg
26817872e0a1Smrg	    /* Workaround to avoid adding sysroot repeatedly */
26827872e0a1Smrg	    if (strncmp ((const char *) url, (const char *) sysroot, len) == 0)
26837872e0a1Smrg		sysroot = NULL;
26847872e0a1Smrg	}
26857872e0a1Smrg	file = FcConfigFileExists (sysroot, url);
26867872e0a1Smrg	goto bail;
26877872e0a1Smrg    }
26882c393a42Smrg
26891887081fSmrg    if (*url == '~')
26901887081fSmrg    {
26912c393a42Smrg	dir = FcConfigHome ();
26922c393a42Smrg	if (dir)
26937872e0a1Smrg	{
26947872e0a1Smrg	    FcChar8 *s;
26957872e0a1Smrg
26967872e0a1Smrg	    if (sysroot)
26977872e0a1Smrg		s = FcStrBuildFilename (sysroot, dir, NULL);
26987872e0a1Smrg	    else
26997872e0a1Smrg		s = dir;
27007872e0a1Smrg	    file = FcConfigFileExists (s, url + 1);
27017872e0a1Smrg	    if (sysroot)
27027872e0a1Smrg		FcStrFree (s);
27037872e0a1Smrg	}
27042c393a42Smrg	else
27052c393a42Smrg	    file = 0;
27061887081fSmrg    }
27077872e0a1Smrg    else
27081887081fSmrg    {
27097872e0a1Smrg	path = FcConfigGetPath ();
27107872e0a1Smrg	if (!path)
27117872e0a1Smrg	{
27127872e0a1Smrg	    file = NULL;
27137872e0a1Smrg	    goto bail;
27147872e0a1Smrg	}
27157872e0a1Smrg	for (p = path; *p; p++)
27167872e0a1Smrg	{
27177872e0a1Smrg	    FcChar8 *s;
27187872e0a1Smrg
27197872e0a1Smrg	    if (sysroot)
27207872e0a1Smrg		s = FcStrBuildFilename (sysroot, *p, NULL);
27217872e0a1Smrg	    else
27227872e0a1Smrg		s = *p;
27237872e0a1Smrg	    file = FcConfigFileExists (s, url);
27247872e0a1Smrg	    if (sysroot)
27257872e0a1Smrg		FcStrFree (s);
27267872e0a1Smrg	    if (file)
27277872e0a1Smrg		break;
27287872e0a1Smrg	}
27297872e0a1Smrg	FcConfigFreePath (path);
27301887081fSmrg    }
27317872e0a1Smrgbail:
27327872e0a1Smrg    FcConfigDestroy (config);
27337872e0a1Smrg
27341887081fSmrg    return file;
27351887081fSmrg}
27361887081fSmrg
27377872e0a1SmrgFcChar8 *
27387872e0a1SmrgFcConfigFilename (const FcChar8 *url)
27397872e0a1Smrg{
27407872e0a1Smrg    return FcConfigGetFilename (NULL, url);
27417872e0a1Smrg}
27427872e0a1Smrg
27431887081fSmrgFcChar8 *
27441887081fSmrgFcConfigRealFilename (FcConfig		*config,
27451887081fSmrg		      const FcChar8	*url)
27461887081fSmrg{
27477872e0a1Smrg    FcChar8 *n = FcConfigGetFilename (config, url);
27481887081fSmrg
27491887081fSmrg    if (n)
27501887081fSmrg    {
27517872e0a1Smrg	FcChar8 buf[FC_PATH_MAX];
27521887081fSmrg	ssize_t len;
27537872e0a1Smrg	struct stat sb;
27541887081fSmrg
27557872e0a1Smrg	if ((len = FcReadLink (n, buf, sizeof (buf) - 1)) != -1)
27562c393a42Smrg	{
27571887081fSmrg	    buf[len] = 0;
27581887081fSmrg
27597872e0a1Smrg	    /* We try to pick up a config from FONTCONFIG_FILE
27607872e0a1Smrg	     * when url is null. don't try to address the real filename
27617872e0a1Smrg	     * if it is a named pipe.
27627872e0a1Smrg	     */
27637872e0a1Smrg	    if (!url && FcStat (n, &sb) == 0 && S_ISFIFO (sb.st_mode))
27647872e0a1Smrg		return n;
27657872e0a1Smrg	    else if (!FcStrIsAbsoluteFilename (buf))
27661887081fSmrg	    {
27677872e0a1Smrg		FcChar8 *dirname = FcStrDirname (n);
27687872e0a1Smrg		FcStrFree (n);
27691887081fSmrg		if (!dirname)
27701887081fSmrg		    return NULL;
27711887081fSmrg
27721887081fSmrg		FcChar8 *path = FcStrBuildFilename (dirname, buf, NULL);
27731887081fSmrg		FcStrFree (dirname);
27741887081fSmrg		if (!path)
27751887081fSmrg		    return NULL;
27761887081fSmrg
27777872e0a1Smrg		n = FcStrCanonFilename (path);
27781887081fSmrg		FcStrFree (path);
27791887081fSmrg	    }
27801887081fSmrg	    else
27811887081fSmrg	    {
27827872e0a1Smrg		FcStrFree (n);
27837872e0a1Smrg		n = FcStrdup (buf);
27841887081fSmrg	    }
27852c393a42Smrg	}
27862c393a42Smrg    }
2787ca08ab68Smrg
27887872e0a1Smrg    return n;
27892c393a42Smrg}
27902c393a42Smrg
27912c393a42Smrg/*
27922c393a42Smrg * Manage the application-specific fonts
27932c393a42Smrg */
27942c393a42Smrg
27952c393a42SmrgFcBool
27962c393a42SmrgFcConfigAppFontAddFile (FcConfig    *config,
27972c393a42Smrg			const FcChar8  *file)
27982c393a42Smrg{
27992c393a42Smrg    FcFontSet	*set;
28002c393a42Smrg    FcStrSet	*subdirs;
28012c393a42Smrg    FcStrList	*sublist;
28022c393a42Smrg    FcChar8	*subdir;
28037872e0a1Smrg    FcBool	ret = FcTrue;
28042c393a42Smrg
28057872e0a1Smrg    config = FcConfigReference (config);
28062c393a42Smrg    if (!config)
28077872e0a1Smrg	return FcFalse;
28082c393a42Smrg
280918bd4a06Smrg    subdirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
28102c393a42Smrg    if (!subdirs)
28117872e0a1Smrg    {
28127872e0a1Smrg	ret = FcFalse;
28137872e0a1Smrg	goto bail;
28147872e0a1Smrg    }
2815ca08ab68Smrg
28162c393a42Smrg    set = FcConfigGetFonts (config, FcSetApplication);
28172c393a42Smrg    if (!set)
28182c393a42Smrg    {
28192c393a42Smrg	set = FcFontSetCreate ();
28202c393a42Smrg	if (!set)
28212c393a42Smrg	{
28222c393a42Smrg	    FcStrSetDestroy (subdirs);
28237872e0a1Smrg	    ret = FcFalse;
28247872e0a1Smrg	    goto bail;
28252c393a42Smrg	}
28262c393a42Smrg	FcConfigSetFonts (config, set, FcSetApplication);
28272c393a42Smrg    }
282846bb3e47Smrg
28291887081fSmrg    if (!FcFileScanConfig (set, subdirs, file, config))
28302c393a42Smrg    {
28312c393a42Smrg	FcStrSetDestroy (subdirs);
28327872e0a1Smrg	ret = FcFalse;
28337872e0a1Smrg	goto bail;
28342c393a42Smrg    }
28352c393a42Smrg    if ((sublist = FcStrListCreate (subdirs)))
28362c393a42Smrg    {
28372c393a42Smrg	while ((subdir = FcStrListNext (sublist)))
28382c393a42Smrg	{
28392c393a42Smrg	    FcConfigAppFontAddDir (config, subdir);
28402c393a42Smrg	}
28412c393a42Smrg	FcStrListDone (sublist);
28422c393a42Smrg    }
28432c393a42Smrg    FcStrSetDestroy (subdirs);
28447872e0a1Smrgbail:
28457872e0a1Smrg    FcConfigDestroy (config);
28467872e0a1Smrg
28477872e0a1Smrg    return ret;
28482c393a42Smrg}
28492c393a42Smrg
28502c393a42SmrgFcBool
28512c393a42SmrgFcConfigAppFontAddDir (FcConfig	    *config,
28522c393a42Smrg		       const FcChar8   *dir)
28532c393a42Smrg{
28542c393a42Smrg    FcFontSet	*set;
28552c393a42Smrg    FcStrSet	*dirs;
28567872e0a1Smrg    FcBool	ret = FcTrue;
2857ca08ab68Smrg
28587872e0a1Smrg    config = FcConfigReference (config);
28592c393a42Smrg    if (!config)
28607872e0a1Smrg	return FcFalse;
28612c393a42Smrg
286218bd4a06Smrg    dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
28632c393a42Smrg    if (!dirs)
28647872e0a1Smrg    {
28657872e0a1Smrg	ret = FcFalse;
28667872e0a1Smrg	goto bail;
28677872e0a1Smrg    }
2868ca08ab68Smrg
28692c393a42Smrg    set = FcConfigGetFonts (config, FcSetApplication);
28702c393a42Smrg    if (!set)
28712c393a42Smrg    {
28722c393a42Smrg	set = FcFontSetCreate ();
28732c393a42Smrg	if (!set)
28742c393a42Smrg	{
28752c393a42Smrg	    FcStrSetDestroy (dirs);
28767872e0a1Smrg	    ret = FcFalse;
28777872e0a1Smrg	    goto bail;
28782c393a42Smrg	}
28792c393a42Smrg	FcConfigSetFonts (config, set, FcSetApplication);
28802c393a42Smrg    }
2881ca08ab68Smrg
28822c393a42Smrg    FcStrSetAddFilename (dirs, dir);
2883ca08ab68Smrg
28842c393a42Smrg    if (!FcConfigAddDirList (config, FcSetApplication, dirs))
28852c393a42Smrg    {
28862c393a42Smrg	FcStrSetDestroy (dirs);
28877872e0a1Smrg	ret = FcFalse;
28887872e0a1Smrg	goto bail;
28892c393a42Smrg    }
28902c393a42Smrg    FcStrSetDestroy (dirs);
28917872e0a1Smrgbail:
28927872e0a1Smrg    FcConfigDestroy (config);
28937872e0a1Smrg
28947872e0a1Smrg    return ret;
28952c393a42Smrg}
28962c393a42Smrg
28972c393a42Smrgvoid
28982c393a42SmrgFcConfigAppFontClear (FcConfig	    *config)
28992c393a42Smrg{
29007872e0a1Smrg    config = FcConfigReference (config);
29012c393a42Smrg    if (!config)
29027872e0a1Smrg	return;
29032c393a42Smrg
29042c393a42Smrg    FcConfigSetFonts (config, 0, FcSetApplication);
29057872e0a1Smrg
29067872e0a1Smrg    FcConfigDestroy (config);
29072c393a42Smrg}
29082c393a42Smrg
29092c393a42Smrg/*
29102c393a42Smrg * Manage filename-based font source selectors
29112c393a42Smrg */
29122c393a42Smrg
29132c393a42SmrgFcBool
29142c393a42SmrgFcConfigGlobAdd (FcConfig	*config,
29152c393a42Smrg		 const FcChar8  *glob,
29162c393a42Smrg		 FcBool		accept)
29172c393a42Smrg{
29182c393a42Smrg    FcStrSet	*set = accept ? config->acceptGlobs : config->rejectGlobs;
291946bb3e47Smrg	FcChar8	*realglob = FcStrCopyFilename(glob);
292046bb3e47Smrg	if (!realglob)
292146bb3e47Smrg		return FcFalse;
29222c393a42Smrg
292346bb3e47Smrg    FcBool	 ret = FcStrSetAdd (set, realglob);
292446bb3e47Smrg    FcStrFree(realglob);
292546bb3e47Smrg    return ret;
29262c393a42Smrg}
29272c393a42Smrg
29282c393a42Smrgstatic FcBool
29292c393a42SmrgFcConfigGlobsMatch (const FcStrSet	*globs,
29302c393a42Smrg		    const FcChar8	*string)
29312c393a42Smrg{
29322c393a42Smrg    int	i;
29332c393a42Smrg
29342c393a42Smrg    for (i = 0; i < globs->num; i++)
2935c9710b42Smrg	if (FcStrGlobMatch (globs->strs[i], string))
29362c393a42Smrg	    return FcTrue;
29372c393a42Smrg    return FcFalse;
29382c393a42Smrg}
29392c393a42Smrg
29402c393a42SmrgFcBool
29412c393a42SmrgFcConfigAcceptFilename (FcConfig	*config,
29422c393a42Smrg			const FcChar8	*filename)
29432c393a42Smrg{
29442c393a42Smrg    if (FcConfigGlobsMatch (config->acceptGlobs, filename))
29452c393a42Smrg	return FcTrue;
29462c393a42Smrg    if (FcConfigGlobsMatch (config->rejectGlobs, filename))
29472c393a42Smrg	return FcFalse;
29482c393a42Smrg    return FcTrue;
29492c393a42Smrg}
29502c393a42Smrg
29512c393a42Smrg/*
29522c393a42Smrg * Manage font-pattern based font source selectors
29532c393a42Smrg */
29542c393a42Smrg
29552c393a42SmrgFcBool
29562c393a42SmrgFcConfigPatternsAdd (FcConfig	*config,
29572c393a42Smrg		     FcPattern	*pattern,
29582c393a42Smrg		     FcBool	accept)
29592c393a42Smrg{
29602c393a42Smrg    FcFontSet	*set = accept ? config->acceptPatterns : config->rejectPatterns;
29612c393a42Smrg
29622c393a42Smrg    return FcFontSetAdd (set, pattern);
29632c393a42Smrg}
29642c393a42Smrg
29652c393a42Smrgstatic FcBool
29662c393a42SmrgFcConfigPatternsMatch (const FcFontSet	*patterns,
29672c393a42Smrg		       const FcPattern	*font)
29682c393a42Smrg{
29692c393a42Smrg    int i;
2970ca08ab68Smrg
29712c393a42Smrg    for (i = 0; i < patterns->nfont; i++)
29722c393a42Smrg	if (FcListPatternMatchAny (patterns->fonts[i], font))
29732c393a42Smrg	    return FcTrue;
29742c393a42Smrg    return FcFalse;
29752c393a42Smrg}
29762c393a42Smrg
29772c393a42SmrgFcBool
29782c393a42SmrgFcConfigAcceptFont (FcConfig	    *config,
29792c393a42Smrg		    const FcPattern *font)
29802c393a42Smrg{
29812c393a42Smrg    if (FcConfigPatternsMatch (config->acceptPatterns, font))
29822c393a42Smrg	return FcTrue;
29832c393a42Smrg    if (FcConfigPatternsMatch (config->rejectPatterns, font))
29842c393a42Smrg	return FcFalse;
29852c393a42Smrg    return FcTrue;
29862c393a42Smrg}
2987c9710b42Smrg
2988c9710b42Smrgconst FcChar8 *
2989c9710b42SmrgFcConfigGetSysRoot (const FcConfig *config)
2990c9710b42Smrg{
2991c9710b42Smrg    if (!config)
2992c9710b42Smrg    {
2993c9710b42Smrg	config = FcConfigGetCurrent ();
2994c9710b42Smrg	if (!config)
2995c9710b42Smrg	    return NULL;
2996c9710b42Smrg    }
29977872e0a1Smrg    return config->sysRoot;
2998c9710b42Smrg}
2999c9710b42Smrg
3000c9710b42Smrgvoid
3001c9710b42SmrgFcConfigSetSysRoot (FcConfig      *config,
3002c9710b42Smrg		    const FcChar8 *sysroot)
3003c9710b42Smrg{
300418bd4a06Smrg    FcChar8 *s = NULL;
3005c9710b42Smrg    FcBool init = FcFalse;
30067872e0a1Smrg    int nretry = 3;
3007c9710b42Smrg
30087872e0a1Smrgretry:
3009c9710b42Smrg    if (!config)
3010c9710b42Smrg    {
3011c9710b42Smrg	/* We can't use FcConfigGetCurrent() here to ensure
3012c9710b42Smrg	 * the sysroot is set prior to initialize FcConfig,
3013c9710b42Smrg	 * to avoid loading caches from non-sysroot dirs.
3014c9710b42Smrg	 * So postpone the initialization later.
3015c9710b42Smrg	 */
3016c9710b42Smrg	config = fc_atomic_ptr_get (&_fcConfig);
3017c9710b42Smrg	if (!config)
3018c9710b42Smrg	{
3019c9710b42Smrg	    config = FcConfigCreate ();
3020c9710b42Smrg	    if (!config)
3021c9710b42Smrg		return;
3022c9710b42Smrg	    init = FcTrue;
3023c9710b42Smrg	}
3024c9710b42Smrg    }
3025c9710b42Smrg
302618bd4a06Smrg    if (sysroot)
302718bd4a06Smrg    {
30287872e0a1Smrg	s = FcStrRealPath (sysroot);
302918bd4a06Smrg	if (!s)
303018bd4a06Smrg	    return;
303118bd4a06Smrg    }
3032c9710b42Smrg
3033c9710b42Smrg    if (config->sysRoot)
3034c9710b42Smrg	FcStrFree (config->sysRoot);
3035c9710b42Smrg
3036c9710b42Smrg    config->sysRoot = s;
3037c9710b42Smrg    if (init)
3038c9710b42Smrg    {
3039c9710b42Smrg	config = FcInitLoadOwnConfigAndFonts (config);
30407872e0a1Smrg	if (!config)
30417872e0a1Smrg	{
30427872e0a1Smrg	    /* Something failed. this is usually unlikely. so retrying */
30437872e0a1Smrg	    init = FcFalse;
30447872e0a1Smrg	    if (--nretry == 0)
30457872e0a1Smrg	    {
30467872e0a1Smrg		fprintf (stderr, "Fontconfig warning: Unable to initialize config and retry limit exceeded. sysroot functionality may not work as expected.\n");
30477872e0a1Smrg		return;
30487872e0a1Smrg	    }
30497872e0a1Smrg	    goto retry;
30507872e0a1Smrg	}
3051c9710b42Smrg	FcConfigSetCurrent (config);
305218bd4a06Smrg	/* FcConfigSetCurrent() increases the refcount.
305318bd4a06Smrg	 * decrease it here to avoid the memory leak.
305418bd4a06Smrg	 */
305518bd4a06Smrg	FcConfigDestroy (config);
3056c9710b42Smrg    }
3057c9710b42Smrg}
3058c9710b42Smrg
30591887081fSmrgFcRuleSet *
30601887081fSmrgFcRuleSetCreate (const FcChar8 *name)
30611887081fSmrg{
30621887081fSmrg    FcRuleSet *ret = (FcRuleSet *) malloc (sizeof (FcRuleSet));
30631887081fSmrg    FcMatchKind k;
30641887081fSmrg    const FcChar8 *p;
30651887081fSmrg
30661887081fSmrg    if (!name)
30671887081fSmrg	p = (const FcChar8 *)"";
30681887081fSmrg    else
30691887081fSmrg	p = name;
30701887081fSmrg
30711887081fSmrg    if (ret)
30721887081fSmrg    {
30731887081fSmrg	ret->name = FcStrdup (p);
30741887081fSmrg	ret->description = NULL;
30751887081fSmrg	ret->domain = NULL;
30761887081fSmrg	for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
30771887081fSmrg	    ret->subst[k] = FcPtrListCreate (FcDestroyAsRule);
30781887081fSmrg	FcRefInit (&ret->ref, 1);
30791887081fSmrg    }
30801887081fSmrg
30811887081fSmrg    return ret;
30821887081fSmrg}
30831887081fSmrg
30841887081fSmrgvoid
30851887081fSmrgFcRuleSetDestroy (FcRuleSet *rs)
30861887081fSmrg{
30871887081fSmrg    FcMatchKind k;
30881887081fSmrg
30891887081fSmrg    if (!rs)
30901887081fSmrg	return;
30911887081fSmrg    if (FcRefDec (&rs->ref) != 1)
30921887081fSmrg	return;
30931887081fSmrg
30941887081fSmrg    if (rs->name)
30951887081fSmrg	FcStrFree (rs->name);
30961887081fSmrg    if (rs->description)
30971887081fSmrg	FcStrFree (rs->description);
30981887081fSmrg    if (rs->domain)
30991887081fSmrg	FcStrFree (rs->domain);
31001887081fSmrg    for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
31011887081fSmrg	FcPtrListDestroy (rs->subst[k]);
31021887081fSmrg
31031887081fSmrg    free (rs);
31041887081fSmrg}
31051887081fSmrg
31061887081fSmrgvoid
31071887081fSmrgFcRuleSetReference (FcRuleSet *rs)
31081887081fSmrg{
31091887081fSmrg    if (!FcRefIsConst (&rs->ref))
31101887081fSmrg	FcRefInc (&rs->ref);
31111887081fSmrg}
31121887081fSmrg
31131887081fSmrgvoid
31141887081fSmrgFcRuleSetEnable (FcRuleSet	*rs,
31151887081fSmrg		 FcBool		flag)
31161887081fSmrg{
31171887081fSmrg    if (rs)
31181887081fSmrg    {
31191887081fSmrg	rs->enabled = flag;
31201887081fSmrg	/* XXX: we may want to provide a feature
31211887081fSmrg	 * to enable/disable rulesets through API
31221887081fSmrg	 * in the future?
31231887081fSmrg	 */
31241887081fSmrg    }
31251887081fSmrg}
31261887081fSmrg
31271887081fSmrgvoid
31281887081fSmrgFcRuleSetAddDescription (FcRuleSet	*rs,
31291887081fSmrg			 const FcChar8	*domain,
31301887081fSmrg			 const FcChar8	*description)
31311887081fSmrg{
31321887081fSmrg    if (rs->domain)
31331887081fSmrg	FcStrFree (rs->domain);
31341887081fSmrg    if (rs->description)
31351887081fSmrg	FcStrFree (rs->description);
31361887081fSmrg
31371887081fSmrg    rs->domain = domain ? FcStrdup (domain) : NULL;
31381887081fSmrg    rs->description = description ? FcStrdup (description) : NULL;
31391887081fSmrg}
31401887081fSmrg
31411887081fSmrgint
31421887081fSmrgFcRuleSetAdd (FcRuleSet		*rs,
31431887081fSmrg	      FcRule		*rule,
31441887081fSmrg	      FcMatchKind	kind)
31451887081fSmrg{
31461887081fSmrg    FcPtrListIter iter;
31471887081fSmrg    FcRule *r;
31481887081fSmrg    int n = 0, ret;
31491887081fSmrg
31501887081fSmrg    if (!rs ||
31511887081fSmrg       kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
31521887081fSmrg	return -1;
31531887081fSmrg    FcPtrListIterInitAtLast (rs->subst[kind], &iter);
31541887081fSmrg    if (!FcPtrListIterAdd (rs->subst[kind], &iter, rule))
31551887081fSmrg	return -1;
31561887081fSmrg
31571887081fSmrg    for (r = rule; r; r = r->next)
31581887081fSmrg    {
31591887081fSmrg	switch (r->type)
31601887081fSmrg	{
31611887081fSmrg	case FcRuleTest:
31621887081fSmrg	    if (r->u.test)
31631887081fSmrg	    {
31641887081fSmrg		if (r->u.test->kind == FcMatchDefault)
31651887081fSmrg		    r->u.test->kind = kind;
31661887081fSmrg		if (n < r->u.test->object)
31671887081fSmrg		    n = r->u.test->object;
31681887081fSmrg	    }
31691887081fSmrg	    break;
31701887081fSmrg	case FcRuleEdit:
31711887081fSmrg	    if (n < r->u.edit->object)
31721887081fSmrg		n = r->u.edit->object;
31731887081fSmrg	    break;
31741887081fSmrg	default:
31751887081fSmrg	    break;
31761887081fSmrg	}
31771887081fSmrg    }
31781887081fSmrg    if (FcDebug () & FC_DBG_EDIT)
31791887081fSmrg    {
31801887081fSmrg	printf ("Add Rule(kind:%d, name: %s) ", kind, rs->name);
31811887081fSmrg	FcRulePrint (rule);
31821887081fSmrg    }
31831887081fSmrg    ret = FC_OBJ_ID (n) - FC_MAX_BASE_OBJECT;
31841887081fSmrg
31851887081fSmrg    return ret < 0 ? 0 : ret;
31861887081fSmrg}
31871887081fSmrg
31881887081fSmrgvoid
31891887081fSmrgFcConfigFileInfoIterInit (FcConfig		*config,
31901887081fSmrg			  FcConfigFileInfoIter	*iter)
31911887081fSmrg{
31921887081fSmrg    FcConfig *c;
31931887081fSmrg    FcPtrListIter *i = (FcPtrListIter *)iter;
31941887081fSmrg
31951887081fSmrg    if (!config)
31961887081fSmrg	c = FcConfigGetCurrent ();
31971887081fSmrg    else
31981887081fSmrg	c = config;
31991887081fSmrg    FcPtrListIterInit (c->rulesetList, i);
32001887081fSmrg}
32011887081fSmrg
32021887081fSmrgFcBool
32031887081fSmrgFcConfigFileInfoIterNext (FcConfig		*config,
32041887081fSmrg			  FcConfigFileInfoIter	*iter)
32051887081fSmrg{
32061887081fSmrg    FcConfig *c;
32071887081fSmrg    FcPtrListIter *i = (FcPtrListIter *)iter;
32081887081fSmrg
32091887081fSmrg    if (!config)
32101887081fSmrg	c = FcConfigGetCurrent ();
32111887081fSmrg    else
32121887081fSmrg	c = config;
32131887081fSmrg    if (FcPtrListIterIsValid (c->rulesetList, i))
32141887081fSmrg    {
32151887081fSmrg	FcPtrListIterNext (c->rulesetList, i);
32161887081fSmrg    }
32171887081fSmrg    else
32181887081fSmrg	return FcFalse;
32191887081fSmrg
32201887081fSmrg    return FcTrue;
32211887081fSmrg}
32221887081fSmrg
32231887081fSmrgFcBool
32241887081fSmrgFcConfigFileInfoIterGet (FcConfig		*config,
32251887081fSmrg			 FcConfigFileInfoIter	*iter,
32261887081fSmrg			 FcChar8		**name,
32271887081fSmrg			 FcChar8		**description,
32281887081fSmrg			 FcBool			*enabled)
32291887081fSmrg{
32301887081fSmrg    FcConfig *c;
32311887081fSmrg    FcRuleSet *r;
32321887081fSmrg    FcPtrListIter *i = (FcPtrListIter *)iter;
32331887081fSmrg
32341887081fSmrg    if (!config)
32351887081fSmrg	c = FcConfigGetCurrent ();
32361887081fSmrg    else
32371887081fSmrg	c = config;
32381887081fSmrg    if (!FcPtrListIterIsValid (c->rulesetList, i))
32391887081fSmrg	return FcFalse;
32401887081fSmrg    r = FcPtrListIterGetValue (c->rulesetList, i);
32411887081fSmrg    if (name)
32421887081fSmrg	*name = FcStrdup (r->name && r->name[0] ? r->name : (const FcChar8 *) "fonts.conf");
32431887081fSmrg    if (description)
32441887081fSmrg	*description = FcStrdup (!r->description ? _("No description") :
32451887081fSmrg				 dgettext (r->domain ? (const char *) r->domain : GETTEXT_PACKAGE "-conf",
32461887081fSmrg					   (const char *) r->description));
32471887081fSmrg    if (enabled)
32481887081fSmrg	*enabled = r->enabled;
32491887081fSmrg
32501887081fSmrg    return FcTrue;
32511887081fSmrg}
32521887081fSmrg
32532c393a42Smrg#define __fccfg__
32542c393a42Smrg#include "fcaliastail.h"
32552c393a42Smrg#undef __fccfg__
3256