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