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 
35 static FcConfig    *_fcConfig; /* MT-safe */
36 
37 static FcConfig *
FcConfigEnsure(void)38 FcConfigEnsure (void)
39 {
40     FcConfig	*config;
41 retry:
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 
55 FcBool
FcConfigInit(void)56 FcConfigInit (void)
57 {
58   return FcConfigEnsure () ? FcTrue : FcFalse;
59 }
60 
61 void
FcConfigFini(void)62 FcConfigFini (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 
70 FcConfig *
FcConfigCreate(void)71 FcConfigCreate (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 
132 bail8:
133     FcFontSetDestroy (config->rejectPatterns);
134 bail7:
135     FcFontSetDestroy (config->acceptPatterns);
136 bail6:
137     FcStrSetDestroy (config->rejectGlobs);
138 bail5:
139     FcStrSetDestroy (config->acceptGlobs);
140 bail4:
141     FcStrSetDestroy (config->fontDirs);
142 bail3:
143     FcStrSetDestroy (config->configFiles);
144 bail2:
145     FcStrSetDestroy (config->configDirs);
146 bail1:
147     free (config);
148 bail0:
149     return 0;
150 }
151 
152 static FcFileTime
FcConfigNewestFile(FcStrSet * files)153 FcConfigNewestFile (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 
174 FcBool
FcConfigUptoDate(FcConfig * config)175 FcConfigUptoDate (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 
209 static void
FcSubstDestroy(FcSubst * s)210 FcSubstDestroy (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 
224 FcExpr *
FcConfigAllocExpr(FcConfig * config)225 FcConfigAllocExpr (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 
243 FcConfig *
FcConfigReference(FcConfig * config)244 FcConfigReference (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 
258 void
FcConfigDestroy(FcConfig * config)259 FcConfigDestroy (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 
305 FcBool
FcConfigAddCache(FcConfig * config,FcCache * cache,FcSetName set,FcStrSet * dirSet)306 FcConfigAddCache (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 
364 static FcBool
FcConfigAddDirList(FcConfig * config,FcSetName set,FcStrSet * dirSet)365 FcConfigAddDirList (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 
394 FcBool
FcConfigBuildFonts(FcConfig * config)395 FcConfigBuildFonts (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 
419 FcBool
FcConfigSetCurrent(FcConfig * config)420 FcConfigSetCurrent (FcConfig *config)
421 {
422     FcConfig *cfg;
423 
424 retry:
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 
443 FcConfig *
FcConfigGetCurrent(void)444 FcConfigGetCurrent (void)
445 {
446     return FcConfigEnsure ();
447 }
448 
449 FcBool
FcConfigAddConfigDir(FcConfig * config,const FcChar8 * d)450 FcConfigAddConfigDir (FcConfig	    *config,
451 		      const FcChar8 *d)
452 {
453     return FcStrSetAddFilename (config->configDirs, d);
454 }
455 
456 FcStrList *
FcConfigGetConfigDirs(FcConfig * config)457 FcConfigGetConfigDirs (FcConfig   *config)
458 {
459     if (!config)
460     {
461 	config = FcConfigGetCurrent ();
462 	if (!config)
463 	    return 0;
464     }
465     return FcStrListCreate (config->configDirs);
466 }
467 
468 FcBool
FcConfigAddFontDir(FcConfig * config,const FcChar8 * d)469 FcConfigAddFontDir (FcConfig	    *config,
470 		    const FcChar8   *d)
471 {
472     return FcStrSetAddFilename (config->fontDirs, d);
473 }
474 
475 FcBool
FcConfigAddDir(FcConfig * config,const FcChar8 * d)476 FcConfigAddDir (FcConfig	    *config,
477 		const FcChar8	    *d)
478 {
479     return (FcConfigAddConfigDir (config, d) &&
480 	    FcConfigAddFontDir (config, d));
481 }
482 
483 FcStrList *
FcConfigGetFontDirs(FcConfig * config)484 FcConfigGetFontDirs (FcConfig	*config)
485 {
486     if (!config)
487     {
488 	config = FcConfigGetCurrent ();
489 	if (!config)
490 	    return 0;
491     }
492     return FcStrListCreate (config->fontDirs);
493 }
494 
495 FcBool
FcConfigAddCacheDir(FcConfig * config,const FcChar8 * d)496 FcConfigAddCacheDir (FcConfig	    *config,
497 		     const FcChar8  *d)
498 {
499     return FcStrSetAddFilename (config->cacheDirs, d);
500 }
501 
502 FcStrList *
FcConfigGetCacheDirs(const FcConfig * config)503 FcConfigGetCacheDirs (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 
514 FcBool
FcConfigAddConfigFile(FcConfig * config,const FcChar8 * f)515 FcConfigAddConfigFile (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 
529 FcStrList *
FcConfigGetConfigFiles(FcConfig * config)530 FcConfigGetConfigFiles (FcConfig    *config)
531 {
532     if (!config)
533     {
534 	config = FcConfigGetCurrent ();
535 	if (!config)
536 	    return 0;
537     }
538     return FcStrListCreate (config->configFiles);
539 }
540 
541 FcChar8 *
FcConfigGetCache(FcConfig * config FC_UNUSED)542 FcConfigGetCache (FcConfig  *config FC_UNUSED)
543 {
544     return NULL;
545 }
546 
547 FcFontSet *
FcConfigGetFonts(FcConfig * config,FcSetName set)548 FcConfigGetFonts (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 
560 void
FcConfigSetFonts(FcConfig * config,FcFontSet * fonts,FcSetName set)561 FcConfigSetFonts (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 
570 FcBlanks *
FcConfigGetBlanks(FcConfig * config)571 FcConfigGetBlanks (FcConfig	*config)
572 {
573     if (!config)
574     {
575 	config = FcConfigGetCurrent ();
576 	if (!config)
577 	    return 0;
578     }
579     return config->blanks;
580 }
581 
582 FcBool
FcConfigAddBlank(FcConfig * config,FcChar32 blank)583 FcConfigAddBlank (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 
605 int
FcConfigGetRescanInterval(FcConfig * config)606 FcConfigGetRescanInterval (FcConfig *config)
607 {
608     if (!config)
609     {
610 	config = FcConfigGetCurrent ();
611 	if (!config)
612 	    return 0;
613     }
614     return config->rescanInterval;
615 }
616 
617 FcBool
FcConfigSetRescanInterval(FcConfig * config,int rescanInterval)618 FcConfigSetRescanInterval (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  */
633 int
FcConfigGetRescanInverval(FcConfig * config)634 FcConfigGetRescanInverval (FcConfig *config)
635 {
636     return FcConfigGetRescanInterval (config);
637 }
638 
639 FcBool
FcConfigSetRescanInverval(FcConfig * config,int rescanInterval)640 FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
641 {
642     return FcConfigSetRescanInterval (config, rescanInterval);
643 }
644 
645 FcBool
FcConfigAddRule(FcConfig * config,FcRule * rule,FcMatchKind kind)646 FcConfigAddRule (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 
707 static FcValue
FcConfigPromote(FcValue v,FcValue u,FcValuePromotionBuffer * buf)708 FcConfigPromote (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 
728 FcBool
FcConfigCompareValue(const FcValue * left_o,unsigned int op_,const FcValue * right_o)729 FcConfigCompareValue (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 
915 static FcValue
FcConfigEvaluate(FcPattern * p,FcPattern * p_pat,FcMatchKind kind,FcExpr * e)916 FcConfigEvaluate (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 
1253 static FcValueList *
FcConfigMatchValueList(FcPattern * p,FcPattern * p_pat,FcMatchKind kind,FcTest * t,FcValueList * values)1254 FcConfigMatchValueList (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 
1301 static FcValueList *
FcConfigValues(FcPattern * p,FcPattern * p_pat,FcMatchKind kind,FcExpr * e,FcValueBinding binding)1302 FcConfigValues (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 
1333 static FcBool
FcConfigAdd(FcValueListPtr * head,FcValueList * position,FcBool append,FcValueList * new,FcObject object)1334 FcConfigAdd (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 
1428 static void
FcConfigDel(FcValueListPtr * head,FcValueList * position)1429 FcConfigDel (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 
1446 static void
FcConfigPatternAdd(FcPattern * p,FcObject object,FcValueList * list,FcBool append)1447 FcConfigPatternAdd (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  */
1465 static void
FcConfigPatternDel(FcPattern * p,FcObject object)1466 FcConfigPatternDel (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 
1476 static void
FcConfigPatternCanon(FcPattern * p,FcObject object)1477 FcConfigPatternCanon (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 
1487 FcBool
FcConfigSubstituteWithPat(FcConfig * config,FcPattern * p,FcPattern * p_pat,FcMatchKind kind)1488 FcConfigSubstituteWithPat (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     }
1759 bail1:
1760     if (elt)
1761 	free (elt);
1762     if (value)
1763 	free (value);
1764     if (tst)
1765 	free (tst);
1766 
1767     return retval;
1768 }
1769 
1770 FcBool
FcConfigSubstitute(FcConfig * config,FcPattern * p,FcMatchKind kind)1771 FcConfigSubstitute (FcConfig	*config,
1772 		    FcPattern	*p,
1773 		    FcMatchKind	kind)
1774 {
1775     return FcConfigSubstituteWithPat (config, p, 0, kind);
1776 }
1777 
1778 #if defined (_WIN32)
1779 
1780 static FcChar8 fontconfig_path[1000] = ""; /* MT-dontcare */
1781 
1782 #  if (defined (PIC) || defined (DLL_EXPORT))
1783 
1784 BOOL WINAPI
1785 DllMain (HINSTANCE hinstDLL,
1786 	 DWORD     fdwReason,
1787 	 LPVOID    lpvReserved);
1788 
1789 BOOL WINAPI
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)1790 DllMain (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 
1837 static FcChar8 *
FcConfigFileExists(const FcChar8 * dir,const FcChar8 * file)1838 FcConfigFileExists (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 
1882 static FcChar8 **
FcConfigGetPath(void)1883 FcConfigGetPath (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 
1945 bail1:
1946     for (i = 0; path[i]; i++)
1947 	free (path[i]);
1948     free (path);
1949 bail0:
1950     return 0;
1951 }
1952 
1953 static void
FcConfigFreePath(FcChar8 ** path)1954 FcConfigFreePath (FcChar8 **path)
1955 {
1956     FcChar8    **p;
1957 
1958     for (p = path; *p; p++)
1959 	free (*p);
1960     free (path);
1961 }
1962 
1963 static FcBool	_FcConfigHomeEnabled = FcTrue; /* MT-goodenough */
1964 
1965 FcChar8 *
FcConfigHome(void)1966 FcConfigHome (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 
1982 FcChar8 *
FcConfigXdgCacheHome(void)1983 FcConfigXdgCacheHome (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 
2007 FcChar8 *
FcConfigXdgConfigHome(void)2008 FcConfigXdgConfigHome (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 
2032 FcChar8 *
FcConfigXdgDataHome(void)2033 FcConfigXdgDataHome (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 
2057 FcBool
FcConfigEnableHome(FcBool enable)2058 FcConfigEnableHome (FcBool enable)
2059 {
2060     FcBool  prev = _FcConfigHomeEnabled;
2061     _FcConfigHomeEnabled = enable;
2062     return prev;
2063 }
2064 
2065 FcChar8 *
FcConfigFilename(const FcChar8 * url)2066 FcConfigFilename (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 
2121 FcBool
FcConfigAppFontAddFile(FcConfig * config,const FcChar8 * file)2122 FcConfigAppFontAddFile (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 
2170 FcBool
FcConfigAppFontAddDir(FcConfig * config,const FcChar8 * dir)2171 FcConfigAppFontAddDir (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 
2211 void
FcConfigAppFontClear(FcConfig * config)2212 FcConfigAppFontClear (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 
2228 FcBool
FcConfigGlobAdd(FcConfig * config,const FcChar8 * glob,FcBool accept)2229 FcConfigGlobAdd (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 
2238 static FcBool
FcConfigGlobsMatch(const FcStrSet * globs,const FcChar8 * string)2239 FcConfigGlobsMatch (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 
2250 FcBool
FcConfigAcceptFilename(FcConfig * config,const FcChar8 * filename)2251 FcConfigAcceptFilename (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 
2265 FcBool
FcConfigPatternsAdd(FcConfig * config,FcPattern * pattern,FcBool accept)2266 FcConfigPatternsAdd (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 
2275 static FcBool
FcConfigPatternsMatch(const FcFontSet * patterns,const FcPattern * font)2276 FcConfigPatternsMatch (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 
2287 FcBool
FcConfigAcceptFont(FcConfig * config,const FcPattern * font)2288 FcConfigAcceptFont (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 
2298 const FcChar8 *
FcConfigGetSysRoot(const FcConfig * config)2299 FcConfigGetSysRoot (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 
2311 void
FcConfigSetSysRoot(FcConfig * config,const FcChar8 * sysroot)2312 FcConfigSetSysRoot (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