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