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