]> git.wh0rd.org - fontconfig.git/blob - src/fccfg.c
Allow config->cache to be null (as it is when $HOME is not set)
[fontconfig.git] / src / fccfg.c
1 /*
2 * $RCSId: xc/lib/fontconfig/src/fccfg.c,v 1.23 2002/08/31 22:17:32 keithp Exp $
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 Keith Packard not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. Keith Packard makes 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 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL KEITH PACKARD 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 #include "fcint.h"
26
27 #if defined (_WIN32) && (defined (PIC) || defined (DLL_EXPORT))
28 #define STRICT
29 #include <windows.h>
30 #undef STRICT
31 #endif
32
33 #if defined (_WIN32) && !defined (R_OK)
34 #define R_OK 4
35 #endif
36
37 FcConfig *_fcConfig;
38
39 FcConfig *
40 FcConfigCreate (void)
41 {
42 FcSetName set;
43 FcConfig *config;
44
45 config = malloc (sizeof (FcConfig));
46 if (!config)
47 goto bail0;
48 FcMemAlloc (FC_MEM_CONFIG, sizeof (FcConfig));
49
50 config->configDirs = FcStrSetCreate ();
51 if (!config->configDirs)
52 goto bail1;
53
54 config->configFiles = FcStrSetCreate ();
55 if (!config->configFiles)
56 goto bail2;
57
58 config->fontDirs = FcStrSetCreate ();
59 if (!config->fontDirs)
60 goto bail3;
61
62 config->acceptGlobs = FcStrSetCreate ();
63 if (!config->acceptGlobs)
64 goto bail4;
65
66 config->rejectGlobs = FcStrSetCreate ();
67 if (!config->rejectGlobs)
68 goto bail5;
69
70 config->cache = 0;
71 if (FcConfigHome())
72 if (!FcConfigSetCache (config, (FcChar8 *) ("~/" FC_USER_CACHE_FILE)))
73 goto bail6;
74
75 config->blanks = 0;
76
77 config->substPattern = 0;
78 config->substFont = 0;
79 config->maxObjects = 0;
80 for (set = FcSetSystem; set <= FcSetApplication; set++)
81 config->fonts[set] = 0;
82
83 config->rescanTime = time(0);
84 config->rescanInterval = 30;
85
86 return config;
87
88 bail6:
89 FcStrSetDestroy (config->rejectGlobs);
90 bail5:
91 FcStrSetDestroy (config->acceptGlobs);
92 bail4:
93 FcStrSetDestroy (config->fontDirs);
94 bail3:
95 FcStrSetDestroy (config->configFiles);
96 bail2:
97 FcStrSetDestroy (config->configDirs);
98 bail1:
99 free (config);
100 FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
101 bail0:
102 return 0;
103 }
104
105 typedef struct _FcFileTime {
106 time_t time;
107 FcBool set;
108 } FcFileTime;
109
110 static FcFileTime
111 FcConfigNewestFile (FcStrSet *files)
112 {
113 FcStrList *list = FcStrListCreate (files);
114 FcFileTime newest = { 0, FcFalse };
115 FcChar8 *file;
116 struct stat statb;
117
118 if (list)
119 {
120 while ((file = FcStrListNext (list)))
121 if (stat ((char *) file, &statb) == 0)
122 if (!newest.set || statb.st_mtime - newest.time > 0)
123 newest.time = statb.st_mtime;
124 FcStrListDone (list);
125 }
126 return newest;
127 }
128
129 FcBool
130 FcConfigUptoDate (FcConfig *config)
131 {
132 FcFileTime config_time, font_time;
133 time_t now = time(0);
134 if (!config)
135 {
136 config = FcConfigGetCurrent ();
137 if (!config)
138 return FcFalse;
139 }
140 config_time = FcConfigNewestFile (config->configFiles);
141 font_time = FcConfigNewestFile (config->configDirs);
142 if ((config_time.set && config_time.time - config->rescanTime > 0) ||
143 (font_time.set && font_time.time - config->rescanTime) > 0)
144 {
145 return FcFalse;
146 }
147 config->rescanTime = now;
148 return FcTrue;
149 }
150
151 static void
152 FcSubstDestroy (FcSubst *s)
153 {
154 FcSubst *n;
155
156 while (s)
157 {
158 n = s->next;
159 if (s->test)
160 FcTestDestroy (s->test);
161 if (s->edit)
162 FcEditDestroy (s->edit);
163 s = n;
164 }
165 }
166
167 void
168 FcConfigDestroy (FcConfig *config)
169 {
170 FcSetName set;
171
172 if (config == _fcConfig)
173 _fcConfig = 0;
174
175 FcStrSetDestroy (config->configDirs);
176 FcStrSetDestroy (config->fontDirs);
177 FcStrSetDestroy (config->configFiles);
178 FcStrSetDestroy (config->acceptGlobs);
179 FcStrSetDestroy (config->rejectGlobs);
180
181 if (config->cache)
182 FcStrFree (config->cache);
183
184 FcSubstDestroy (config->substPattern);
185 FcSubstDestroy (config->substFont);
186 for (set = FcSetSystem; set <= FcSetApplication; set++)
187 if (config->fonts[set])
188 FcFontSetDestroy (config->fonts[set]);
189 free (config);
190 FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
191 }
192
193 /*
194 * Scan the current list of directories in the configuration
195 * and build the set of available fonts. Update the
196 * per-user cache file to reflect the new configuration
197 */
198
199 FcBool
200 FcConfigBuildFonts (FcConfig *config)
201 {
202 FcFontSet *fonts;
203 FcGlobalCache *cache;
204 FcStrList *list;
205 FcChar8 *dir;
206
207 fonts = FcFontSetCreate ();
208 if (!fonts)
209 goto bail0;
210
211 cache = FcGlobalCacheCreate ();
212 if (!cache)
213 goto bail1;
214
215 if (config->cache)
216 FcGlobalCacheLoad (cache, config->cache);
217
218 list = FcConfigGetFontDirs (config);
219 if (!list)
220 goto bail1;
221
222 while ((dir = FcStrListNext (list)))
223 {
224 if (FcDebug () & FC_DBG_FONTSET)
225 printf ("scan dir %s\n", dir);
226 FcDirScanConfig (fonts, config->fontDirs, cache,
227 config->blanks, dir, FcFalse, config);
228 }
229
230 FcStrListDone (list);
231
232 if (FcDebug () & FC_DBG_FONTSET)
233 FcFontSetPrint (fonts);
234
235 if (config->cache)
236 FcGlobalCacheSave (cache, config->cache);
237 FcGlobalCacheDestroy (cache);
238
239 FcConfigSetFonts (config, fonts, FcSetSystem);
240
241 return FcTrue;
242 bail1:
243 FcFontSetDestroy (fonts);
244 bail0:
245 return FcFalse;
246 }
247
248 FcBool
249 FcConfigSetCurrent (FcConfig *config)
250 {
251 if (!config->fonts)
252 if (!FcConfigBuildFonts (config))
253 return FcFalse;
254
255 if (_fcConfig)
256 FcConfigDestroy (_fcConfig);
257 _fcConfig = config;
258 return FcTrue;
259 }
260
261 FcConfig *
262 FcConfigGetCurrent (void)
263 {
264 if (!_fcConfig)
265 if (!FcInit ())
266 return 0;
267 return _fcConfig;
268 }
269
270 FcBool
271 FcConfigAddConfigDir (FcConfig *config,
272 const FcChar8 *d)
273 {
274 return FcStrSetAddFilename (config->configDirs, d);
275 }
276
277 FcStrList *
278 FcConfigGetConfigDirs (FcConfig *config)
279 {
280 if (!config)
281 {
282 config = FcConfigGetCurrent ();
283 if (!config)
284 return 0;
285 }
286 return FcStrListCreate (config->configDirs);
287 }
288
289 FcBool
290 FcConfigAddFontDir (FcConfig *config,
291 const FcChar8 *d)
292 {
293 return FcStrSetAddFilename (config->fontDirs, d);
294 }
295
296 FcBool
297 FcConfigAddDir (FcConfig *config,
298 const FcChar8 *d)
299 {
300 return (FcConfigAddConfigDir (config, d) &&
301 FcConfigAddFontDir (config, d));
302 }
303
304 FcStrList *
305 FcConfigGetFontDirs (FcConfig *config)
306 {
307 if (!config)
308 {
309 config = FcConfigGetCurrent ();
310 if (!config)
311 return 0;
312 }
313 return FcStrListCreate (config->fontDirs);
314 }
315
316 FcBool
317 FcConfigAddConfigFile (FcConfig *config,
318 const FcChar8 *f)
319 {
320 FcBool ret;
321 FcChar8 *file = FcConfigFilename (f);
322
323 if (!file)
324 return FcFalse;
325
326 ret = FcStrSetAdd (config->configFiles, file);
327 FcStrFree (file);
328 return ret;
329 }
330
331 FcStrList *
332 FcConfigGetConfigFiles (FcConfig *config)
333 {
334 if (!config)
335 {
336 config = FcConfigGetCurrent ();
337 if (!config)
338 return 0;
339 }
340 return FcStrListCreate (config->configFiles);
341 }
342
343 FcBool
344 FcConfigSetCache (FcConfig *config,
345 const FcChar8 *c)
346 {
347 FcChar8 *new = FcStrCopyFilename (c);
348
349 if (!new)
350 return FcFalse;
351 if (config->cache)
352 FcStrFree (config->cache);
353 config->cache = new;
354 return FcTrue;
355 }
356
357 FcChar8 *
358 FcConfigGetCache (FcConfig *config)
359 {
360 if (!config)
361 {
362 config = FcConfigGetCurrent ();
363 if (!config)
364 return 0;
365 }
366 return config->cache;
367 }
368
369 FcFontSet *
370 FcConfigGetFonts (FcConfig *config,
371 FcSetName set)
372 {
373 if (!config)
374 {
375 config = FcConfigGetCurrent ();
376 if (!config)
377 return 0;
378 }
379 return config->fonts[set];
380 }
381
382 void
383 FcConfigSetFonts (FcConfig *config,
384 FcFontSet *fonts,
385 FcSetName set)
386 {
387 if (config->fonts[set])
388 FcFontSetDestroy (config->fonts[set]);
389 config->fonts[set] = fonts;
390 }
391
392
393
394 FcBlanks *
395 FcConfigGetBlanks (FcConfig *config)
396 {
397 if (!config)
398 {
399 config = FcConfigGetCurrent ();
400 if (!config)
401 return 0;
402 }
403 return config->blanks;
404 }
405
406 FcBool
407 FcConfigAddBlank (FcConfig *config,
408 FcChar32 blank)
409 {
410 FcBlanks *b;
411
412 b = config->blanks;
413 if (!b)
414 {
415 b = FcBlanksCreate ();
416 if (!b)
417 return FcFalse;
418 }
419 if (!FcBlanksAdd (b, blank))
420 return FcFalse;
421 config->blanks = b;
422 return FcTrue;
423 }
424
425 int
426 FcConfigGetRescanInverval (FcConfig *config)
427 {
428 if (!config)
429 {
430 config = FcConfigGetCurrent ();
431 if (!config)
432 return 0;
433 }
434 return config->rescanInterval;
435 }
436
437 FcBool
438 FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
439 {
440 if (!config)
441 {
442 config = FcConfigGetCurrent ();
443 if (!config)
444 return FcFalse;
445 }
446 config->rescanInterval = rescanInterval;
447 return FcTrue;
448 }
449
450 FcBool
451 FcConfigAddEdit (FcConfig *config,
452 FcTest *test,
453 FcEdit *edit,
454 FcMatchKind kind)
455 {
456 FcSubst *subst, **prev;
457 FcTest *t;
458 int num;
459
460 subst = (FcSubst *) malloc (sizeof (FcSubst));
461 if (!subst)
462 return FcFalse;
463 FcMemAlloc (FC_MEM_SUBST, sizeof (FcSubst));
464 if (kind == FcMatchPattern)
465 prev = &config->substPattern;
466 else
467 prev = &config->substFont;
468 for (; *prev; prev = &(*prev)->next);
469 *prev = subst;
470 subst->next = 0;
471 subst->test = test;
472 subst->edit = edit;
473 num = 0;
474 for (t = test; t; t = t->next)
475 {
476 if (t->kind == FcMatchDefault)
477 t->kind = kind;
478 num++;
479 }
480 if (config->maxObjects < num)
481 config->maxObjects = num;
482 if (FcDebug () & FC_DBG_EDIT)
483 {
484 printf ("Add Subst ");
485 FcSubstPrint (subst);
486 }
487 return FcTrue;
488 }
489
490 typedef struct _FcSubState {
491 FcPatternElt *elt;
492 FcValueList *value;
493 } FcSubState;
494
495 static FcValue
496 FcConfigPromote (FcValue v, FcValue u)
497 {
498 if (v.type == FcTypeInteger)
499 {
500 v.type = FcTypeDouble;
501 v.u.d = (double) v.u.i;
502 }
503 else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
504 {
505 v.u.m = &FcIdentityMatrix;
506 v.type = FcTypeMatrix;
507 }
508 else if (v.type == FcTypeString && u.type == FcTypeLangSet)
509 {
510 v.u.l = FcLangSetPromote (v.u.s);
511 v.type = FcTypeLangSet;
512 }
513 return v;
514 }
515
516 FcBool
517 FcConfigCompareValue (const FcValue m_o,
518 FcOp op,
519 const FcValue v_o)
520 {
521 FcValue m = m_o;
522 FcValue v = v_o;
523 FcBool ret = FcFalse;
524
525 m = FcConfigPromote (m, v);
526 v = FcConfigPromote (v, m);
527 if (m.type == v.type)
528 {
529 switch (m.type) {
530 case FcTypeInteger:
531 break; /* FcConfigPromote prevents this from happening */
532 case FcTypeDouble:
533 switch (op) {
534 case FcOpEqual:
535 case FcOpContains:
536 ret = m.u.d == v.u.d;
537 break;
538 case FcOpNotEqual:
539 case FcOpNotContains:
540 ret = m.u.d != v.u.d;
541 break;
542 case FcOpLess:
543 ret = m.u.d < v.u.d;
544 break;
545 case FcOpLessEqual:
546 ret = m.u.d <= v.u.d;
547 break;
548 case FcOpMore:
549 ret = m.u.d > v.u.d;
550 break;
551 case FcOpMoreEqual:
552 ret = m.u.d >= v.u.d;
553 break;
554 default:
555 break;
556 }
557 break;
558 case FcTypeBool:
559 switch (op) {
560 case FcOpEqual:
561 case FcOpContains:
562 ret = m.u.b == v.u.b;
563 break;
564 case FcOpNotEqual:
565 case FcOpNotContains:
566 ret = m.u.b != v.u.b;
567 break;
568 default:
569 break;
570 }
571 break;
572 case FcTypeString:
573 switch (op) {
574 case FcOpEqual:
575 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) == 0;
576 break;
577 case FcOpContains:
578 ret = FcStrStrIgnoreCase (m.u.s, v.u.s) != 0;
579 break;
580 case FcOpNotEqual:
581 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) != 0;
582 break;
583 case FcOpNotContains:
584 ret = FcStrStrIgnoreCase (m.u.s, v.u.s) == 0;
585 break;
586 default:
587 break;
588 }
589 break;
590 case FcTypeMatrix:
591 switch (op) {
592 case FcOpEqual:
593 case FcOpContains:
594 ret = FcMatrixEqual (m.u.m, v.u.m);
595 break;
596 case FcOpNotEqual:
597 case FcOpNotContains:
598 ret = !FcMatrixEqual (m.u.m, v.u.m);
599 break;
600 default:
601 break;
602 }
603 break;
604 case FcTypeCharSet:
605 switch (op) {
606 case FcOpContains:
607 /* v contains m if m is a subset of v */
608 ret = FcCharSetIsSubset (m.u.c, v.u.c);
609 break;
610 case FcOpNotContains:
611 /* v contains m if m is a subset of v */
612 ret = !FcCharSetIsSubset (m.u.c, v.u.c);
613 break;
614 case FcOpEqual:
615 ret = FcCharSetEqual (m.u.c, v.u.c);
616 break;
617 case FcOpNotEqual:
618 ret = !FcCharSetEqual (m.u.c, v.u.c);
619 break;
620 default:
621 break;
622 }
623 break;
624 case FcTypeLangSet:
625 switch (op) {
626 case FcOpContains:
627 ret = FcLangSetContains (m.u.l, v.u.l);
628 break;
629 case FcOpNotContains:
630 ret = FcLangSetContains (m.u.l, v.u.l);
631 break;
632 case FcOpEqual:
633 ret = FcLangSetEqual (m.u.l, v.u.l);
634 break;
635 case FcOpNotEqual:
636 ret = !FcLangSetEqual (m.u.l, v.u.l);
637 break;
638 default:
639 break;
640 }
641 break;
642 case FcTypeVoid:
643 switch (op) {
644 case FcOpEqual:
645 case FcOpContains:
646 ret = FcTrue;
647 break;
648 default:
649 break;
650 }
651 break;
652 case FcTypeFTFace:
653 switch (op) {
654 case FcOpEqual:
655 case FcOpContains:
656 ret = m.u.f == v.u.f;
657 break;
658 case FcOpNotEqual:
659 case FcOpNotContains:
660 ret = m.u.f != v.u.f;
661 break;
662 default:
663 break;
664 }
665 break;
666 }
667 }
668 else
669 {
670 if (op == FcOpNotEqual || op == FcOpNotContains)
671 ret = FcTrue;
672 }
673 return ret;
674 }
675
676
677 #define _FcDoubleFloor(d) ((int) (d))
678 #define _FcDoubleCeil(d) ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
679 #define FcDoubleFloor(d) ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
680 #define FcDoubleCeil(d) ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
681 #define FcDoubleRound(d) FcDoubleFloor ((d) + 0.5)
682 #define FcDoubleTrunc(d) ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
683
684 static FcValue
685 FcConfigEvaluate (FcPattern *p, FcExpr *e)
686 {
687 FcValue v, vl, vr;
688 FcResult r;
689 FcMatrix *m;
690
691 switch (e->op) {
692 case FcOpInteger:
693 v.type = FcTypeInteger;
694 v.u.i = e->u.ival;
695 break;
696 case FcOpDouble:
697 v.type = FcTypeDouble;
698 v.u.d = e->u.dval;
699 break;
700 case FcOpString:
701 v.type = FcTypeString;
702 v.u.s = e->u.sval;
703 v = FcValueSave (v);
704 break;
705 case FcOpMatrix:
706 v.type = FcTypeMatrix;
707 v.u.m = e->u.mval;
708 v = FcValueSave (v);
709 break;
710 case FcOpCharSet:
711 v.type = FcTypeCharSet;
712 v.u.c = e->u.cval;
713 v = FcValueSave (v);
714 break;
715 case FcOpBool:
716 v.type = FcTypeBool;
717 v.u.b = e->u.bval;
718 break;
719 case FcOpField:
720 r = FcPatternGet (p, e->u.field, 0, &v);
721 if (r != FcResultMatch)
722 v.type = FcTypeVoid;
723 break;
724 case FcOpConst:
725 if (FcNameConstant (e->u.constant, &v.u.i))
726 v.type = FcTypeInteger;
727 else
728 v.type = FcTypeVoid;
729 break;
730 case FcOpQuest:
731 vl = FcConfigEvaluate (p, e->u.tree.left);
732 if (vl.type == FcTypeBool)
733 {
734 if (vl.u.b)
735 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
736 else
737 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
738 }
739 else
740 v.type = FcTypeVoid;
741 FcValueDestroy (vl);
742 break;
743 case FcOpEqual:
744 case FcOpNotEqual:
745 case FcOpLess:
746 case FcOpLessEqual:
747 case FcOpMore:
748 case FcOpMoreEqual:
749 case FcOpContains:
750 case FcOpNotContains:
751 vl = FcConfigEvaluate (p, e->u.tree.left);
752 vr = FcConfigEvaluate (p, e->u.tree.right);
753 v.type = FcTypeBool;
754 v.u.b = FcConfigCompareValue (vl, e->op, vr);
755 FcValueDestroy (vl);
756 FcValueDestroy (vr);
757 break;
758 case FcOpOr:
759 case FcOpAnd:
760 case FcOpPlus:
761 case FcOpMinus:
762 case FcOpTimes:
763 case FcOpDivide:
764 vl = FcConfigEvaluate (p, e->u.tree.left);
765 vr = FcConfigEvaluate (p, e->u.tree.right);
766 vl = FcConfigPromote (vl, vr);
767 vr = FcConfigPromote (vr, vl);
768 if (vl.type == vr.type)
769 {
770 switch (vl.type) {
771 case FcTypeDouble:
772 switch (e->op) {
773 case FcOpPlus:
774 v.type = FcTypeDouble;
775 v.u.d = vl.u.d + vr.u.d;
776 break;
777 case FcOpMinus:
778 v.type = FcTypeDouble;
779 v.u.d = vl.u.d - vr.u.d;
780 break;
781 case FcOpTimes:
782 v.type = FcTypeDouble;
783 v.u.d = vl.u.d * vr.u.d;
784 break;
785 case FcOpDivide:
786 v.type = FcTypeDouble;
787 v.u.d = vl.u.d / vr.u.d;
788 break;
789 default:
790 v.type = FcTypeVoid;
791 break;
792 }
793 if (v.type == FcTypeDouble &&
794 v.u.d == (double) (int) v.u.d)
795 {
796 v.type = FcTypeInteger;
797 v.u.i = (int) v.u.d;
798 }
799 break;
800 case FcTypeBool:
801 switch (e->op) {
802 case FcOpOr:
803 v.type = FcTypeBool;
804 v.u.b = vl.u.b || vr.u.b;
805 break;
806 case FcOpAnd:
807 v.type = FcTypeBool;
808 v.u.b = vl.u.b && vr.u.b;
809 break;
810 default:
811 v.type = FcTypeVoid;
812 break;
813 }
814 break;
815 case FcTypeString:
816 switch (e->op) {
817 case FcOpPlus:
818 v.type = FcTypeString;
819 v.u.s = FcStrPlus (vl.u.s, vr.u.s);
820 if (!v.u.s)
821 v.type = FcTypeVoid;
822 break;
823 default:
824 v.type = FcTypeVoid;
825 break;
826 }
827 break;
828 case FcTypeMatrix:
829 switch (e->op) {
830 case FcOpTimes:
831 v.type = FcTypeMatrix;
832 m = malloc (sizeof (FcMatrix));
833 if (m)
834 {
835 FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
836 FcMatrixMultiply (m, vl.u.m, vr.u.m);
837 v.u.m = m;
838 }
839 else
840 {
841 v.type = FcTypeVoid;
842 }
843 break;
844 default:
845 v.type = FcTypeVoid;
846 break;
847 }
848 break;
849 default:
850 v.type = FcTypeVoid;
851 break;
852 }
853 }
854 else
855 v.type = FcTypeVoid;
856 FcValueDestroy (vl);
857 FcValueDestroy (vr);
858 break;
859 case FcOpNot:
860 vl = FcConfigEvaluate (p, e->u.tree.left);
861 switch (vl.type) {
862 case FcTypeBool:
863 v.type = FcTypeBool;
864 v.u.b = !vl.u.b;
865 break;
866 default:
867 v.type = FcTypeVoid;
868 break;
869 }
870 FcValueDestroy (vl);
871 break;
872 case FcOpFloor:
873 vl = FcConfigEvaluate (p, e->u.tree.left);
874 switch (vl.type) {
875 case FcTypeInteger:
876 v = vl;
877 break;
878 case FcTypeDouble:
879 v.type = FcTypeInteger;
880 v.u.i = FcDoubleFloor (vl.u.d);
881 break;
882 default:
883 v.type = FcTypeVoid;
884 break;
885 }
886 FcValueDestroy (vl);
887 break;
888 case FcOpCeil:
889 vl = FcConfigEvaluate (p, e->u.tree.left);
890 switch (vl.type) {
891 case FcTypeInteger:
892 v = vl;
893 break;
894 case FcTypeDouble:
895 v.type = FcTypeInteger;
896 v.u.i = FcDoubleCeil (vl.u.d);
897 break;
898 default:
899 v.type = FcTypeVoid;
900 break;
901 }
902 FcValueDestroy (vl);
903 break;
904 case FcOpRound:
905 vl = FcConfigEvaluate (p, e->u.tree.left);
906 switch (vl.type) {
907 case FcTypeInteger:
908 v = vl;
909 break;
910 case FcTypeDouble:
911 v.type = FcTypeInteger;
912 v.u.i = FcDoubleRound (vl.u.d);
913 break;
914 default:
915 v.type = FcTypeVoid;
916 break;
917 }
918 FcValueDestroy (vl);
919 break;
920 case FcOpTrunc:
921 vl = FcConfigEvaluate (p, e->u.tree.left);
922 switch (vl.type) {
923 case FcTypeInteger:
924 v = vl;
925 break;
926 case FcTypeDouble:
927 v.type = FcTypeInteger;
928 v.u.i = FcDoubleTrunc (vl.u.d);
929 break;
930 default:
931 v.type = FcTypeVoid;
932 break;
933 }
934 FcValueDestroy (vl);
935 break;
936 default:
937 v.type = FcTypeVoid;
938 break;
939 }
940 return v;
941 }
942
943 static FcValueList *
944 FcConfigMatchValueList (FcPattern *p,
945 FcTest *t,
946 FcValueList *values)
947 {
948 FcValueList *ret = 0;
949 FcExpr *e = t->expr;
950 FcValue value;
951 FcValueList *v;
952
953 while (e)
954 {
955 if (e->op == FcOpComma)
956 {
957 value = FcConfigEvaluate (p, e->u.tree.left);
958 e = e->u.tree.right;
959 }
960 else
961 {
962 value = FcConfigEvaluate (p, e);
963 e = 0;
964 }
965
966 for (v = values; v; v = v->next)
967 {
968 if (FcConfigCompareValue (v->value, t->op, value))
969 {
970 if (!ret)
971 ret = v;
972 }
973 else
974 {
975 if (t->qual == FcQualAll)
976 {
977 ret = 0;
978 break;
979 }
980 }
981 }
982 FcValueDestroy (value);
983 }
984 return ret;
985 }
986
987 static FcValueList *
988 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
989 {
990 FcValueList *l;
991
992 if (!e)
993 return 0;
994 l = (FcValueList *) malloc (sizeof (FcValueList));
995 if (!l)
996 return 0;
997 FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
998 if (e->op == FcOpComma)
999 {
1000 l->value = FcConfigEvaluate (p, e->u.tree.left);
1001 l->next = FcConfigValues (p, e->u.tree.right, binding);
1002 }
1003 else
1004 {
1005 l->value = FcConfigEvaluate (p, e);
1006 l->next = 0;
1007 }
1008 l->binding = binding;
1009 while (l && l->value.type == FcTypeVoid)
1010 {
1011 FcValueList *next = l->next;
1012
1013 FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
1014 free (l);
1015 l = next;
1016 }
1017 return l;
1018 }
1019
1020 static FcBool
1021 FcConfigAdd (FcValueList **head,
1022 FcValueList *position,
1023 FcBool append,
1024 FcValueList *new)
1025 {
1026 FcValueList **prev, *last, *v;
1027 FcValueBinding sameBinding;
1028
1029 if (position)
1030 sameBinding = position->binding;
1031 else
1032 sameBinding = FcValueBindingWeak;
1033 for (v = new; v; v = v->next)
1034 if (v->binding == FcValueBindingSame)
1035 v->binding = sameBinding;
1036 if (append)
1037 {
1038 if (position)
1039 prev = &position->next;
1040 else
1041 for (prev = head; *prev; prev = &(*prev)->next)
1042 ;
1043 }
1044 else
1045 {
1046 if (position)
1047 {
1048 for (prev = head; *prev; prev = &(*prev)->next)
1049 {
1050 if (*prev == position)
1051 break;
1052 }
1053 }
1054 else
1055 prev = head;
1056
1057 if (FcDebug () & FC_DBG_EDIT)
1058 {
1059 if (!*prev)
1060 printf ("position not on list\n");
1061 }
1062 }
1063
1064 if (FcDebug () & FC_DBG_EDIT)
1065 {
1066 printf ("%s list before ", append ? "Append" : "Prepend");
1067 FcValueListPrint (*head);
1068 printf ("\n");
1069 }
1070
1071 if (new)
1072 {
1073 last = new;
1074 while (last->next)
1075 last = last->next;
1076
1077 last->next = *prev;
1078 *prev = new;
1079 }
1080
1081 if (FcDebug () & FC_DBG_EDIT)
1082 {
1083 printf ("%s list after ", append ? "Append" : "Prepend");
1084 FcValueListPrint (*head);
1085 printf ("\n");
1086 }
1087
1088 return FcTrue;
1089 }
1090
1091 static void
1092 FcConfigDel (FcValueList **head,
1093 FcValueList *position)
1094 {
1095 FcValueList **prev;
1096
1097 for (prev = head; *prev; prev = &(*prev)->next)
1098 {
1099 if (*prev == position)
1100 {
1101 *prev = position->next;
1102 position->next = 0;
1103 FcValueListDestroy (position);
1104 break;
1105 }
1106 }
1107 }
1108
1109 static void
1110 FcConfigPatternAdd (FcPattern *p,
1111 const char *object,
1112 FcValueList *list,
1113 FcBool append)
1114 {
1115 if (list)
1116 {
1117 FcPatternElt *e = FcPatternInsertElt (p, object);
1118
1119 if (!e)
1120 return;
1121 FcConfigAdd (&e->values, 0, append, list);
1122 }
1123 }
1124
1125 /*
1126 * Delete all values associated with a field
1127 */
1128 static void
1129 FcConfigPatternDel (FcPattern *p,
1130 const char *object)
1131 {
1132 FcPatternElt *e = FcPatternFindElt (p, object);
1133 if (!e)
1134 return;
1135 while (e->values)
1136 FcConfigDel (&e->values, e->values);
1137 }
1138
1139 static void
1140 FcConfigPatternCanon (FcPattern *p,
1141 const char *object)
1142 {
1143 FcPatternElt *e = FcPatternFindElt (p, object);
1144 if (!e)
1145 return;
1146 if (!e->values)
1147 FcPatternDel (p, object);
1148 }
1149
1150 FcBool
1151 FcConfigSubstituteWithPat (FcConfig *config,
1152 FcPattern *p,
1153 FcPattern *p_pat,
1154 FcMatchKind kind)
1155 {
1156 FcSubst *s;
1157 FcSubState *st;
1158 int i;
1159 FcTest *t;
1160 FcEdit *e;
1161 FcValueList *l;
1162 FcPattern *m;
1163
1164 if (!config)
1165 {
1166 config = FcConfigGetCurrent ();
1167 if (!config)
1168 return FcFalse;
1169 }
1170
1171 st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1172 if (!st && config->maxObjects)
1173 return FcFalse;
1174 FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1175
1176 if (FcDebug () & FC_DBG_EDIT)
1177 {
1178 printf ("FcConfigSubstitute ");
1179 FcPatternPrint (p);
1180 }
1181 if (kind == FcMatchPattern)
1182 s = config->substPattern;
1183 else
1184 s = config->substFont;
1185 for (; s; s = s->next)
1186 {
1187 /*
1188 * Check the tests to see if
1189 * they all match the pattern
1190 */
1191 for (t = s->test, i = 0; t; t = t->next, i++)
1192 {
1193 if (FcDebug () & FC_DBG_EDIT)
1194 {
1195 printf ("FcConfigSubstitute test ");
1196 FcTestPrint (t);
1197 }
1198 st[i].elt = 0;
1199 if (kind == FcMatchFont && t->kind == FcMatchPattern)
1200 m = p_pat;
1201 else
1202 m = p;
1203 if (m)
1204 st[i].elt = FcPatternFindElt (m, t->field);
1205 else
1206 st[i].elt = 0;
1207 /*
1208 * If there's no such field in the font,
1209 * then FcQualAll matches while FcQualAny does not
1210 */
1211 if (!st[i].elt)
1212 {
1213 if (t->qual == FcQualAll)
1214 {
1215 st[i].value = 0;
1216 continue;
1217 }
1218 else
1219 break;
1220 }
1221 /*
1222 * Check to see if there is a match, mark the location
1223 * to apply match-relative edits
1224 */
1225 st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1226 if (!st[i].value)
1227 break;
1228 if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1229 break;
1230 if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1231 break;
1232 }
1233 if (t)
1234 {
1235 if (FcDebug () & FC_DBG_EDIT)
1236 printf ("No match\n");
1237 continue;
1238 }
1239 if (FcDebug () & FC_DBG_EDIT)
1240 {
1241 printf ("Substitute ");
1242 FcSubstPrint (s);
1243 }
1244 for (e = s->edit; e; e = e->next)
1245 {
1246 /*
1247 * Evaluate the list of expressions
1248 */
1249 l = FcConfigValues (p, e->expr, e->binding);
1250 /*
1251 * Locate any test associated with this field, skipping
1252 * tests associated with the pattern when substituting in
1253 * the font
1254 */
1255 for (t = s->test, i = 0; t; t = t->next, i++)
1256 {
1257 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1258 !FcStrCmpIgnoreCase ((FcChar8 *) t->field,
1259 (FcChar8 *) e->field))
1260 {
1261 /*
1262 * KLUDGE - the pattern may have been reallocated or
1263 * things may have been inserted or deleted above
1264 * this element by other edits. Go back and find
1265 * the element again
1266 */
1267 if (e != s->edit && st[i].elt)
1268 st[i].elt = FcPatternFindElt (p, t->field);
1269 if (!st[i].elt)
1270 t = 0;
1271 break;
1272 }
1273 }
1274 switch (e->op) {
1275 case FcOpAssign:
1276 /*
1277 * If there was a test, then replace the matched
1278 * value with the new list of values
1279 */
1280 if (t)
1281 {
1282 FcValueList *thisValue = st[i].value;
1283 FcValueList *nextValue = thisValue ? thisValue->next : 0;
1284
1285 /*
1286 * Append the new list of values after the current value
1287 */
1288 FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1289 /*
1290 * Delete the marked value
1291 */
1292 FcConfigDel (&st[i].elt->values, thisValue);
1293 /*
1294 * Adjust any pointers into the value list to ensure
1295 * future edits occur at the same place
1296 */
1297 for (t = s->test, i = 0; t; t = t->next, i++)
1298 {
1299 if (st[i].value == thisValue)
1300 st[i].value = nextValue;
1301 }
1302 break;
1303 }
1304 /* fall through ... */
1305 case FcOpAssignReplace:
1306 /*
1307 * Delete all of the values and insert
1308 * the new set
1309 */
1310 FcConfigPatternDel (p, e->field);
1311 FcConfigPatternAdd (p, e->field, l, FcTrue);
1312 /*
1313 * Adjust any pointers into the value list as they no
1314 * longer point to anything valid
1315 */
1316 if (t)
1317 {
1318 FcPatternElt *thisElt = st[i].elt;
1319 for (t = s->test, i = 0; t; t = t->next, i++)
1320 {
1321 if (st[i].elt == thisElt)
1322 st[i].value = 0;
1323 }
1324 }
1325 break;
1326 case FcOpPrepend:
1327 if (t)
1328 {
1329 FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1330 break;
1331 }
1332 /* fall through ... */
1333 case FcOpPrependFirst:
1334 FcConfigPatternAdd (p, e->field, l, FcFalse);
1335 break;
1336 case FcOpAppend:
1337 if (t)
1338 {
1339 FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1340 break;
1341 }
1342 /* fall through ... */
1343 case FcOpAppendLast:
1344 FcConfigPatternAdd (p, e->field, l, FcTrue);
1345 break;
1346 default:
1347 break;
1348 }
1349 }
1350 /*
1351 * Now go through the pattern and eliminate
1352 * any properties without data
1353 */
1354 for (e = s->edit; e; e = e->next)
1355 FcConfigPatternCanon (p, e->field);
1356
1357 if (FcDebug () & FC_DBG_EDIT)
1358 {
1359 printf ("FcConfigSubstitute edit");
1360 FcPatternPrint (p);
1361 }
1362 }
1363 FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1364 free (st);
1365 if (FcDebug () & FC_DBG_EDIT)
1366 {
1367 printf ("FcConfigSubstitute done");
1368 FcPatternPrint (p);
1369 }
1370 return FcTrue;
1371 }
1372
1373 FcBool
1374 FcConfigSubstitute (FcConfig *config,
1375 FcPattern *p,
1376 FcMatchKind kind)
1377 {
1378 return FcConfigSubstituteWithPat (config, p, 0, kind);
1379 }
1380
1381 #if defined (_WIN32) && (defined (PIC) || defined (DLL_EXPORT))
1382
1383 static FcChar8 fontconfig_path[1000] = "";
1384
1385 BOOL WINAPI
1386 DllMain (HINSTANCE hinstDLL,
1387 DWORD fdwReason,
1388 LPVOID lpvReserved)
1389 {
1390 FcChar8 *p;
1391
1392 switch (fdwReason) {
1393 case DLL_PROCESS_ATTACH:
1394 if (!GetModuleFileName ((HMODULE) hinstDLL, fontconfig_path,
1395 sizeof (fontconfig_path)))
1396 break;
1397
1398 /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
1399 * assume it's a Unix-style installation tree, and use
1400 * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
1401 * folder where the DLL is as FONTCONFIG_PATH.
1402 */
1403 p = strrchr (fontconfig_path, '\\');
1404 if (p)
1405 {
1406 *p = '\0';
1407 p = strrchr (fontconfig_path, '\\');
1408 if (p && (FcStrCmpIgnoreCase (p + 1, "bin") == 0 ||
1409 FcStrCmpIgnoreCase (p + 1, "lib") == 0))
1410 *p = '\0';
1411 strcat (fontconfig_path, "\\etc\\fonts");
1412 }
1413 else
1414 fontconfig_path[0] = '\0';
1415
1416 break;
1417 }
1418
1419 return TRUE;
1420 }
1421
1422 #undef FONTCONFIG_PATH
1423 #define FONTCONFIG_PATH fontconfig_path
1424
1425 #else /* !(_WIN32 && PIC) */
1426
1427 #endif /* !(_WIN32 && PIC) */
1428
1429 #ifndef FONTCONFIG_FILE
1430 #define FONTCONFIG_FILE "fonts.conf"
1431 #endif
1432
1433 static FcChar8 *
1434 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1435 {
1436 FcChar8 *path;
1437
1438 if (!dir)
1439 dir = (FcChar8 *) "";
1440 path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1441 if (!path)
1442 return 0;
1443
1444 strcpy ((char *) path, (const char *) dir);
1445 /* make sure there's a single separator */
1446 #ifdef _WIN32
1447 if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
1448 path[strlen((char *) path)-1] != '\\')) &&
1449 (file[0] != '/' && file[0] != '\\'))
1450 strcat ((char *) path, "\\");
1451 #else
1452 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1453 strcat ((char *) path, "/");
1454 #endif
1455 strcat ((char *) path, (char *) file);
1456
1457 FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1458 if (access ((char *) path, R_OK) == 0)
1459 return path;
1460
1461 FcStrFree (path);
1462 return 0;
1463 }
1464
1465 static FcChar8 **
1466 FcConfigGetPath (void)
1467 {
1468 FcChar8 **path;
1469 FcChar8 *env, *e, *colon;
1470 FcChar8 *dir;
1471 int npath;
1472 int i;
1473
1474 npath = 2; /* default dir + null */
1475 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1476 if (env)
1477 {
1478 e = env;
1479 npath++;
1480 while (*e)
1481 if (*e++ == FC_SEARCH_PATH_SEPARATOR)
1482 npath++;
1483 }
1484 path = calloc (npath, sizeof (FcChar8 *));
1485 if (!path)
1486 goto bail0;
1487 i = 0;
1488
1489 if (env)
1490 {
1491 e = env;
1492 while (*e)
1493 {
1494 colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
1495 if (!colon)
1496 colon = e + strlen ((char *) e);
1497 path[i] = malloc (colon - e + 1);
1498 if (!path[i])
1499 goto bail1;
1500 strncpy ((char *) path[i], (const char *) e, colon - e);
1501 path[i][colon - e] = '\0';
1502 if (*colon)
1503 e = colon + 1;
1504 else
1505 e = colon;
1506 i++;
1507 }
1508 }
1509
1510 dir = (FcChar8 *) FONTCONFIG_PATH;
1511 path[i] = malloc (strlen ((char *) dir) + 1);
1512 if (!path[i])
1513 goto bail1;
1514 strcpy ((char *) path[i], (const char *) dir);
1515 return path;
1516
1517 bail1:
1518 for (i = 0; path[i]; i++)
1519 free (path[i]);
1520 free (path);
1521 bail0:
1522 return 0;
1523 }
1524
1525 static void
1526 FcConfigFreePath (FcChar8 **path)
1527 {
1528 FcChar8 **p;
1529
1530 for (p = path; *p; p++)
1531 free (*p);
1532 free (path);
1533 }
1534
1535 static FcBool _FcConfigHomeEnabled = FcTrue;
1536
1537 FcChar8 *
1538 FcConfigHome (void)
1539 {
1540 if (_FcConfigHomeEnabled)
1541 {
1542 char *home = getenv ("HOME");
1543
1544 #ifdef _WIN32
1545 if (home == NULL)
1546 home = getenv ("USERPROFILE");
1547 #endif
1548
1549 return home;
1550 }
1551 return 0;
1552 }
1553
1554 FcBool
1555 FcConfigEnableHome (FcBool enable)
1556 {
1557 FcBool prev = _FcConfigHomeEnabled;
1558 _FcConfigHomeEnabled = enable;
1559 return prev;
1560 }
1561
1562 FcChar8 *
1563 FcConfigFilename (const FcChar8 *url)
1564 {
1565 FcChar8 *file, *dir, **path, **p;
1566
1567 if (!url || !*url)
1568 {
1569 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1570 if (!url)
1571 url = (FcChar8 *) FONTCONFIG_FILE;
1572 }
1573 file = 0;
1574
1575 #ifdef _WIN32
1576 if (isalpha (*url) &&
1577 url[1] == ':' &&
1578 (url[2] == '/' || url[2] == '\\'))
1579 goto absolute_path;
1580 #endif
1581
1582 switch (*url) {
1583 case '~':
1584 dir = FcConfigHome ();
1585 if (dir)
1586 file = FcConfigFileExists (dir, url + 1);
1587 else
1588 file = 0;
1589 break;
1590 #ifdef _WIN32
1591 case '\\':
1592 absolute_path:
1593 #endif
1594 case '/':
1595 file = FcConfigFileExists (0, url);
1596 break;
1597 default:
1598 path = FcConfigGetPath ();
1599 if (!path)
1600 return 0;
1601 for (p = path; *p; p++)
1602 {
1603 file = FcConfigFileExists (*p, url);
1604 if (file)
1605 break;
1606 }
1607 FcConfigFreePath (path);
1608 break;
1609 }
1610 return file;
1611 }
1612
1613 /*
1614 * Manage the application-specific fonts
1615 */
1616
1617 FcBool
1618 FcConfigAppFontAddFile (FcConfig *config,
1619 const FcChar8 *file)
1620 {
1621 FcFontSet *set;
1622 FcStrSet *subdirs;
1623 FcStrList *sublist;
1624 FcChar8 *subdir;
1625
1626 if (!config)
1627 {
1628 config = FcConfigGetCurrent ();
1629 if (!config)
1630 return FcFalse;
1631 }
1632
1633 subdirs = FcStrSetCreate ();
1634 if (!subdirs)
1635 return FcFalse;
1636
1637 set = FcConfigGetFonts (config, FcSetApplication);
1638 if (!set)
1639 {
1640 set = FcFontSetCreate ();
1641 if (!set)
1642 {
1643 FcStrSetDestroy (subdirs);
1644 return FcFalse;
1645 }
1646 FcConfigSetFonts (config, set, FcSetApplication);
1647 }
1648
1649 if (!FcFileScanConfig (set, subdirs, 0, config->blanks, file, FcFalse, config))
1650 {
1651 FcStrSetDestroy (subdirs);
1652 return FcFalse;
1653 }
1654 if ((sublist = FcStrListCreate (subdirs)))
1655 {
1656 while ((subdir = FcStrListNext (sublist)))
1657 {
1658 FcConfigAppFontAddDir (config, subdir);
1659 }
1660 FcStrListDone (sublist);
1661 }
1662 return FcTrue;
1663 }
1664
1665 FcBool
1666 FcConfigAppFontAddDir (FcConfig *config,
1667 const FcChar8 *dir)
1668 {
1669 FcFontSet *set;
1670 FcStrSet *subdirs;
1671 FcStrList *sublist;
1672 FcChar8 *subdir;
1673
1674 if (!config)
1675 {
1676 config = FcConfigGetCurrent ();
1677 if (!config)
1678 return FcFalse;
1679 }
1680 subdirs = FcStrSetCreate ();
1681 if (!subdirs)
1682 return FcFalse;
1683
1684 set = FcConfigGetFonts (config, FcSetApplication);
1685 if (!set)
1686 {
1687 set = FcFontSetCreate ();
1688 if (!set)
1689 {
1690 FcStrSetDestroy (subdirs);
1691 return FcFalse;
1692 }
1693 FcConfigSetFonts (config, set, FcSetApplication);
1694 }
1695
1696 if (!FcDirScanConfig (set, subdirs, 0, config->blanks, dir, FcFalse, config))
1697 {
1698 FcStrSetDestroy (subdirs);
1699 return FcFalse;
1700 }
1701 if ((sublist = FcStrListCreate (subdirs)))
1702 {
1703 while ((subdir = FcStrListNext (sublist)))
1704 {
1705 FcConfigAppFontAddDir (config, subdir);
1706 }
1707 FcStrListDone (sublist);
1708 }
1709 return FcTrue;
1710 }
1711
1712 void
1713 FcConfigAppFontClear (FcConfig *config)
1714 {
1715 FcConfigSetFonts (config, 0, FcSetApplication);
1716 }
1717
1718 /*
1719 * Manage filename-based font source selectors
1720 */
1721
1722 FcBool
1723 FcConfigGlobAdd (FcConfig *config,
1724 const FcChar8 *glob,
1725 FcBool accept)
1726 {
1727 FcStrSet *set = accept ? config->acceptGlobs : config->rejectGlobs;
1728
1729 return FcStrSetAdd (set, glob);
1730 }
1731
1732 static FcBool
1733 FcConfigGlobMatch (const FcChar8 *glob,
1734 const FcChar8 *string)
1735 {
1736 FcChar8 c;
1737
1738 while ((c = *glob++))
1739 {
1740 switch (c) {
1741 case '*':
1742 /* short circuit common case */
1743 if (!*glob)
1744 return FcTrue;
1745 /* short circuit another common case */
1746 if (strchr ((char *) glob, '*') == 0)
1747 string += strlen ((char *) string) - strlen ((char *) glob);
1748 while (*string)
1749 {
1750 if (FcConfigGlobMatch (glob, string))
1751 return FcTrue;
1752 string++;
1753 }
1754 return FcFalse;
1755 case '?':
1756 if (*string++ == '\0')
1757 return FcFalse;
1758 break;
1759 default:
1760 if (*string++ != c)
1761 return FcFalse;
1762 break;
1763 }
1764 }
1765 return *string == '\0';
1766 }
1767
1768 static FcBool
1769 FcConfigGlobsMatch (const FcStrSet *globs,
1770 const FcChar8 *string)
1771 {
1772 int i;
1773
1774 for (i = 0; i < globs->num; i++)
1775 if (FcConfigGlobMatch (globs->strs[i], string))
1776 return FcTrue;
1777 return FcFalse;
1778 }
1779
1780 FcBool
1781 FcConfigAcceptFilename (FcConfig *config,
1782 const FcChar8 *filename)
1783 {
1784 if (FcConfigGlobsMatch (config->acceptGlobs, filename))
1785 return FcTrue;
1786 if (FcConfigGlobsMatch (config->rejectGlobs, filename))
1787 return FcFalse;
1788 return FcTrue;
1789 }