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