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