]> git.wh0rd.org - fontconfig.git/blob - src/fccfg.c
Implement new semantics for Contains and LISTING:
[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 left_o,
518 FcOp op,
519 const FcValue right_o)
520 {
521 FcValue left = left_o;
522 FcValue right = right_o;
523 FcBool ret = FcFalse;
524
525 left = FcConfigPromote (left, right);
526 right = FcConfigPromote (right, left);
527 if (left.type == right.type)
528 {
529 switch (left.type) {
530 case FcTypeInteger:
531 break; /* FcConfigPromote prevents this from happening */
532 case FcTypeDouble:
533 switch (op) {
534 case FcOpEqual:
535 case FcOpContains:
536 case FcOpListing:
537 ret = left.u.d == right.u.d;
538 break;
539 case FcOpNotEqual:
540 case FcOpNotContains:
541 ret = left.u.d != right.u.d;
542 break;
543 case FcOpLess:
544 ret = left.u.d < right.u.d;
545 break;
546 case FcOpLessEqual:
547 ret = left.u.d <= right.u.d;
548 break;
549 case FcOpMore:
550 ret = left.u.d > right.u.d;
551 break;
552 case FcOpMoreEqual:
553 ret = left.u.d >= right.u.d;
554 break;
555 default:
556 break;
557 }
558 break;
559 case FcTypeBool:
560 switch (op) {
561 case FcOpEqual:
562 case FcOpContains:
563 case FcOpListing:
564 ret = left.u.b == right.u.b;
565 break;
566 case FcOpNotEqual:
567 case FcOpNotContains:
568 ret = left.u.b != right.u.b;
569 break;
570 default:
571 break;
572 }
573 break;
574 case FcTypeString:
575 switch (op) {
576 case FcOpEqual:
577 case FcOpListing:
578 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
579 break;
580 case FcOpContains:
581 ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
582 break;
583 case FcOpNotEqual:
584 case FcOpNotContains:
585 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
586 break;
587 default:
588 break;
589 }
590 break;
591 case FcTypeMatrix:
592 switch (op) {
593 case FcOpEqual:
594 case FcOpContains:
595 case FcOpListing:
596 ret = FcMatrixEqual (left.u.m, right.u.m);
597 break;
598 case FcOpNotEqual:
599 case FcOpNotContains:
600 ret = !FcMatrixEqual (left.u.m, right.u.m);
601 break;
602 default:
603 break;
604 }
605 break;
606 case FcTypeCharSet:
607 switch (op) {
608 case FcOpContains:
609 case FcOpListing:
610 /* left contains right if right is a subset of left */
611 ret = FcCharSetIsSubset (right.u.c, left.u.c);
612 break;
613 case FcOpNotContains:
614 /* left contains right if right is a subset of left */
615 ret = !FcCharSetIsSubset (right.u.c, left.u.c);
616 break;
617 case FcOpEqual:
618 ret = FcCharSetEqual (left.u.c, right.u.c);
619 break;
620 case FcOpNotEqual:
621 ret = !FcCharSetEqual (left.u.c, right.u.c);
622 break;
623 default:
624 break;
625 }
626 break;
627 case FcTypeLangSet:
628 switch (op) {
629 case FcOpContains:
630 case FcOpListing:
631 ret = FcLangSetContains (left.u.l, right.u.l);
632 break;
633 case FcOpNotContains:
634 ret = FcLangSetContains (left.u.l, right.u.l);
635 break;
636 case FcOpEqual:
637 ret = FcLangSetEqual (left.u.l, right.u.l);
638 break;
639 case FcOpNotEqual:
640 ret = !FcLangSetEqual (left.u.l, right.u.l);
641 break;
642 default:
643 break;
644 }
645 break;
646 case FcTypeVoid:
647 switch (op) {
648 case FcOpEqual:
649 case FcOpContains:
650 case FcOpListing:
651 ret = FcTrue;
652 break;
653 default:
654 break;
655 }
656 break;
657 case FcTypeFTFace:
658 switch (op) {
659 case FcOpEqual:
660 case FcOpContains:
661 case FcOpListing:
662 ret = left.u.f == right.u.f;
663 break;
664 case FcOpNotEqual:
665 case FcOpNotContains:
666 ret = left.u.f != right.u.f;
667 break;
668 default:
669 break;
670 }
671 break;
672 }
673 }
674 else
675 {
676 if (op == FcOpNotEqual || op == FcOpNotContains)
677 ret = FcTrue;
678 }
679 return ret;
680 }
681
682
683 #define _FcDoubleFloor(d) ((int) (d))
684 #define _FcDoubleCeil(d) ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
685 #define FcDoubleFloor(d) ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
686 #define FcDoubleCeil(d) ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
687 #define FcDoubleRound(d) FcDoubleFloor ((d) + 0.5)
688 #define FcDoubleTrunc(d) ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
689
690 static FcValue
691 FcConfigEvaluate (FcPattern *p, FcExpr *e)
692 {
693 FcValue v, vl, vr;
694 FcResult r;
695 FcMatrix *m;
696
697 switch (e->op) {
698 case FcOpInteger:
699 v.type = FcTypeInteger;
700 v.u.i = e->u.ival;
701 break;
702 case FcOpDouble:
703 v.type = FcTypeDouble;
704 v.u.d = e->u.dval;
705 break;
706 case FcOpString:
707 v.type = FcTypeString;
708 v.u.s = e->u.sval;
709 v = FcValueSave (v);
710 break;
711 case FcOpMatrix:
712 v.type = FcTypeMatrix;
713 v.u.m = e->u.mval;
714 v = FcValueSave (v);
715 break;
716 case FcOpCharSet:
717 v.type = FcTypeCharSet;
718 v.u.c = e->u.cval;
719 v = FcValueSave (v);
720 break;
721 case FcOpBool:
722 v.type = FcTypeBool;
723 v.u.b = e->u.bval;
724 break;
725 case FcOpField:
726 r = FcPatternGet (p, e->u.field, 0, &v);
727 if (r != FcResultMatch)
728 v.type = FcTypeVoid;
729 break;
730 case FcOpConst:
731 if (FcNameConstant (e->u.constant, &v.u.i))
732 v.type = FcTypeInteger;
733 else
734 v.type = FcTypeVoid;
735 break;
736 case FcOpQuest:
737 vl = FcConfigEvaluate (p, e->u.tree.left);
738 if (vl.type == FcTypeBool)
739 {
740 if (vl.u.b)
741 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
742 else
743 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
744 }
745 else
746 v.type = FcTypeVoid;
747 FcValueDestroy (vl);
748 break;
749 case FcOpEqual:
750 case FcOpNotEqual:
751 case FcOpLess:
752 case FcOpLessEqual:
753 case FcOpMore:
754 case FcOpMoreEqual:
755 case FcOpContains:
756 case FcOpNotContains:
757 case FcOpListing:
758 vl = FcConfigEvaluate (p, e->u.tree.left);
759 vr = FcConfigEvaluate (p, e->u.tree.right);
760 v.type = FcTypeBool;
761 v.u.b = FcConfigCompareValue (vl, e->op, vr);
762 FcValueDestroy (vl);
763 FcValueDestroy (vr);
764 break;
765 case FcOpOr:
766 case FcOpAnd:
767 case FcOpPlus:
768 case FcOpMinus:
769 case FcOpTimes:
770 case FcOpDivide:
771 vl = FcConfigEvaluate (p, e->u.tree.left);
772 vr = FcConfigEvaluate (p, e->u.tree.right);
773 vl = FcConfigPromote (vl, vr);
774 vr = FcConfigPromote (vr, vl);
775 if (vl.type == vr.type)
776 {
777 switch (vl.type) {
778 case FcTypeDouble:
779 switch (e->op) {
780 case FcOpPlus:
781 v.type = FcTypeDouble;
782 v.u.d = vl.u.d + vr.u.d;
783 break;
784 case FcOpMinus:
785 v.type = FcTypeDouble;
786 v.u.d = vl.u.d - vr.u.d;
787 break;
788 case FcOpTimes:
789 v.type = FcTypeDouble;
790 v.u.d = vl.u.d * vr.u.d;
791 break;
792 case FcOpDivide:
793 v.type = FcTypeDouble;
794 v.u.d = vl.u.d / vr.u.d;
795 break;
796 default:
797 v.type = FcTypeVoid;
798 break;
799 }
800 if (v.type == FcTypeDouble &&
801 v.u.d == (double) (int) v.u.d)
802 {
803 v.type = FcTypeInteger;
804 v.u.i = (int) v.u.d;
805 }
806 break;
807 case FcTypeBool:
808 switch (e->op) {
809 case FcOpOr:
810 v.type = FcTypeBool;
811 v.u.b = vl.u.b || vr.u.b;
812 break;
813 case FcOpAnd:
814 v.type = FcTypeBool;
815 v.u.b = vl.u.b && vr.u.b;
816 break;
817 default:
818 v.type = FcTypeVoid;
819 break;
820 }
821 break;
822 case FcTypeString:
823 switch (e->op) {
824 case FcOpPlus:
825 v.type = FcTypeString;
826 v.u.s = FcStrPlus (vl.u.s, vr.u.s);
827 if (!v.u.s)
828 v.type = FcTypeVoid;
829 break;
830 default:
831 v.type = FcTypeVoid;
832 break;
833 }
834 break;
835 case FcTypeMatrix:
836 switch (e->op) {
837 case FcOpTimes:
838 v.type = FcTypeMatrix;
839 m = malloc (sizeof (FcMatrix));
840 if (m)
841 {
842 FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
843 FcMatrixMultiply (m, vl.u.m, vr.u.m);
844 v.u.m = m;
845 }
846 else
847 {
848 v.type = FcTypeVoid;
849 }
850 break;
851 default:
852 v.type = FcTypeVoid;
853 break;
854 }
855 break;
856 default:
857 v.type = FcTypeVoid;
858 break;
859 }
860 }
861 else
862 v.type = FcTypeVoid;
863 FcValueDestroy (vl);
864 FcValueDestroy (vr);
865 break;
866 case FcOpNot:
867 vl = FcConfigEvaluate (p, e->u.tree.left);
868 switch (vl.type) {
869 case FcTypeBool:
870 v.type = FcTypeBool;
871 v.u.b = !vl.u.b;
872 break;
873 default:
874 v.type = FcTypeVoid;
875 break;
876 }
877 FcValueDestroy (vl);
878 break;
879 case FcOpFloor:
880 vl = FcConfigEvaluate (p, e->u.tree.left);
881 switch (vl.type) {
882 case FcTypeInteger:
883 v = vl;
884 break;
885 case FcTypeDouble:
886 v.type = FcTypeInteger;
887 v.u.i = FcDoubleFloor (vl.u.d);
888 break;
889 default:
890 v.type = FcTypeVoid;
891 break;
892 }
893 FcValueDestroy (vl);
894 break;
895 case FcOpCeil:
896 vl = FcConfigEvaluate (p, e->u.tree.left);
897 switch (vl.type) {
898 case FcTypeInteger:
899 v = vl;
900 break;
901 case FcTypeDouble:
902 v.type = FcTypeInteger;
903 v.u.i = FcDoubleCeil (vl.u.d);
904 break;
905 default:
906 v.type = FcTypeVoid;
907 break;
908 }
909 FcValueDestroy (vl);
910 break;
911 case FcOpRound:
912 vl = FcConfigEvaluate (p, e->u.tree.left);
913 switch (vl.type) {
914 case FcTypeInteger:
915 v = vl;
916 break;
917 case FcTypeDouble:
918 v.type = FcTypeInteger;
919 v.u.i = FcDoubleRound (vl.u.d);
920 break;
921 default:
922 v.type = FcTypeVoid;
923 break;
924 }
925 FcValueDestroy (vl);
926 break;
927 case FcOpTrunc:
928 vl = FcConfigEvaluate (p, e->u.tree.left);
929 switch (vl.type) {
930 case FcTypeInteger:
931 v = vl;
932 break;
933 case FcTypeDouble:
934 v.type = FcTypeInteger;
935 v.u.i = FcDoubleTrunc (vl.u.d);
936 break;
937 default:
938 v.type = FcTypeVoid;
939 break;
940 }
941 FcValueDestroy (vl);
942 break;
943 default:
944 v.type = FcTypeVoid;
945 break;
946 }
947 return v;
948 }
949
950 static FcValueList *
951 FcConfigMatchValueList (FcPattern *p,
952 FcTest *t,
953 FcValueList *values)
954 {
955 FcValueList *ret = 0;
956 FcExpr *e = t->expr;
957 FcValue value;
958 FcValueList *v;
959
960 while (e)
961 {
962 /* Compute the value of the match expression */
963 if (e->op == FcOpComma)
964 {
965 value = FcConfigEvaluate (p, e->u.tree.left);
966 e = e->u.tree.right;
967 }
968 else
969 {
970 value = FcConfigEvaluate (p, e);
971 e = 0;
972 }
973
974 for (v = values; v; v = v->next)
975 {
976 /* Compare the pattern value to the match expression value */
977 if (FcConfigCompareValue (v->value, t->op, value))
978 {
979 if (!ret)
980 ret = v;
981 }
982 else
983 {
984 if (t->qual == FcQualAll)
985 {
986 ret = 0;
987 break;
988 }
989 }
990 }
991 FcValueDestroy (value);
992 }
993 return ret;
994 }
995
996 static FcValueList *
997 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
998 {
999 FcValueList *l;
1000
1001 if (!e)
1002 return 0;
1003 l = (FcValueList *) malloc (sizeof (FcValueList));
1004 if (!l)
1005 return 0;
1006 FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
1007 if (e->op == FcOpComma)
1008 {
1009 l->value = FcConfigEvaluate (p, e->u.tree.left);
1010 l->next = FcConfigValues (p, e->u.tree.right, binding);
1011 }
1012 else
1013 {
1014 l->value = FcConfigEvaluate (p, e);
1015 l->next = 0;
1016 }
1017 l->binding = binding;
1018 while (l && l->value.type == FcTypeVoid)
1019 {
1020 FcValueList *next = l->next;
1021
1022 FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
1023 free (l);
1024 l = next;
1025 }
1026 return l;
1027 }
1028
1029 static FcBool
1030 FcConfigAdd (FcValueList **head,
1031 FcValueList *position,
1032 FcBool append,
1033 FcValueList *new)
1034 {
1035 FcValueList **prev, *last, *v;
1036 FcValueBinding sameBinding;
1037
1038 if (position)
1039 sameBinding = position->binding;
1040 else
1041 sameBinding = FcValueBindingWeak;
1042 for (v = new; v; v = v->next)
1043 if (v->binding == FcValueBindingSame)
1044 v->binding = sameBinding;
1045 if (append)
1046 {
1047 if (position)
1048 prev = &position->next;
1049 else
1050 for (prev = head; *prev; prev = &(*prev)->next)
1051 ;
1052 }
1053 else
1054 {
1055 if (position)
1056 {
1057 for (prev = head; *prev; prev = &(*prev)->next)
1058 {
1059 if (*prev == position)
1060 break;
1061 }
1062 }
1063 else
1064 prev = head;
1065
1066 if (FcDebug () & FC_DBG_EDIT)
1067 {
1068 if (!*prev)
1069 printf ("position not on list\n");
1070 }
1071 }
1072
1073 if (FcDebug () & FC_DBG_EDIT)
1074 {
1075 printf ("%s list before ", append ? "Append" : "Prepend");
1076 FcValueListPrint (*head);
1077 printf ("\n");
1078 }
1079
1080 if (new)
1081 {
1082 last = new;
1083 while (last->next)
1084 last = last->next;
1085
1086 last->next = *prev;
1087 *prev = new;
1088 }
1089
1090 if (FcDebug () & FC_DBG_EDIT)
1091 {
1092 printf ("%s list after ", append ? "Append" : "Prepend");
1093 FcValueListPrint (*head);
1094 printf ("\n");
1095 }
1096
1097 return FcTrue;
1098 }
1099
1100 static void
1101 FcConfigDel (FcValueList **head,
1102 FcValueList *position)
1103 {
1104 FcValueList **prev;
1105
1106 for (prev = head; *prev; prev = &(*prev)->next)
1107 {
1108 if (*prev == position)
1109 {
1110 *prev = position->next;
1111 position->next = 0;
1112 FcValueListDestroy (position);
1113 break;
1114 }
1115 }
1116 }
1117
1118 static void
1119 FcConfigPatternAdd (FcPattern *p,
1120 const char *object,
1121 FcValueList *list,
1122 FcBool append)
1123 {
1124 if (list)
1125 {
1126 FcPatternElt *e = FcPatternInsertElt (p, object);
1127
1128 if (!e)
1129 return;
1130 FcConfigAdd (&e->values, 0, append, list);
1131 }
1132 }
1133
1134 /*
1135 * Delete all values associated with a field
1136 */
1137 static void
1138 FcConfigPatternDel (FcPattern *p,
1139 const char *object)
1140 {
1141 FcPatternElt *e = FcPatternFindElt (p, object);
1142 if (!e)
1143 return;
1144 while (e->values)
1145 FcConfigDel (&e->values, e->values);
1146 }
1147
1148 static void
1149 FcConfigPatternCanon (FcPattern *p,
1150 const char *object)
1151 {
1152 FcPatternElt *e = FcPatternFindElt (p, object);
1153 if (!e)
1154 return;
1155 if (!e->values)
1156 FcPatternDel (p, object);
1157 }
1158
1159 FcBool
1160 FcConfigSubstituteWithPat (FcConfig *config,
1161 FcPattern *p,
1162 FcPattern *p_pat,
1163 FcMatchKind kind)
1164 {
1165 FcSubst *s;
1166 FcSubState *st;
1167 int i;
1168 FcTest *t;
1169 FcEdit *e;
1170 FcValueList *l;
1171 FcPattern *m;
1172
1173 if (!config)
1174 {
1175 config = FcConfigGetCurrent ();
1176 if (!config)
1177 return FcFalse;
1178 }
1179
1180 st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1181 if (!st && config->maxObjects)
1182 return FcFalse;
1183 FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1184
1185 if (FcDebug () & FC_DBG_EDIT)
1186 {
1187 printf ("FcConfigSubstitute ");
1188 FcPatternPrint (p);
1189 }
1190 if (kind == FcMatchPattern)
1191 s = config->substPattern;
1192 else
1193 s = config->substFont;
1194 for (; s; s = s->next)
1195 {
1196 /*
1197 * Check the tests to see if
1198 * they all match the pattern
1199 */
1200 for (t = s->test, i = 0; t; t = t->next, i++)
1201 {
1202 if (FcDebug () & FC_DBG_EDIT)
1203 {
1204 printf ("FcConfigSubstitute test ");
1205 FcTestPrint (t);
1206 }
1207 st[i].elt = 0;
1208 if (kind == FcMatchFont && t->kind == FcMatchPattern)
1209 m = p_pat;
1210 else
1211 m = p;
1212 if (m)
1213 st[i].elt = FcPatternFindElt (m, t->field);
1214 else
1215 st[i].elt = 0;
1216 /*
1217 * If there's no such field in the font,
1218 * then FcQualAll matches while FcQualAny does not
1219 */
1220 if (!st[i].elt)
1221 {
1222 if (t->qual == FcQualAll)
1223 {
1224 st[i].value = 0;
1225 continue;
1226 }
1227 else
1228 break;
1229 }
1230 /*
1231 * Check to see if there is a match, mark the location
1232 * to apply match-relative edits
1233 */
1234 st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1235 if (!st[i].value)
1236 break;
1237 if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1238 break;
1239 if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1240 break;
1241 }
1242 if (t)
1243 {
1244 if (FcDebug () & FC_DBG_EDIT)
1245 printf ("No match\n");
1246 continue;
1247 }
1248 if (FcDebug () & FC_DBG_EDIT)
1249 {
1250 printf ("Substitute ");
1251 FcSubstPrint (s);
1252 }
1253 for (e = s->edit; e; e = e->next)
1254 {
1255 /*
1256 * Evaluate the list of expressions
1257 */
1258 l = FcConfigValues (p, e->expr, e->binding);
1259 /*
1260 * Locate any test associated with this field, skipping
1261 * tests associated with the pattern when substituting in
1262 * the font
1263 */
1264 for (t = s->test, i = 0; t; t = t->next, i++)
1265 {
1266 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1267 !FcStrCmpIgnoreCase ((FcChar8 *) t->field,
1268 (FcChar8 *) e->field))
1269 {
1270 /*
1271 * KLUDGE - the pattern may have been reallocated or
1272 * things may have been inserted or deleted above
1273 * this element by other edits. Go back and find
1274 * the element again
1275 */
1276 if (e != s->edit && st[i].elt)
1277 st[i].elt = FcPatternFindElt (p, t->field);
1278 if (!st[i].elt)
1279 t = 0;
1280 break;
1281 }
1282 }
1283 switch (e->op) {
1284 case FcOpAssign:
1285 /*
1286 * If there was a test, then replace the matched
1287 * value with the new list of values
1288 */
1289 if (t)
1290 {
1291 FcValueList *thisValue = st[i].value;
1292 FcValueList *nextValue = thisValue ? thisValue->next : 0;
1293
1294 /*
1295 * Append the new list of values after the current value
1296 */
1297 FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1298 /*
1299 * Delete the marked value
1300 */
1301 FcConfigDel (&st[i].elt->values, thisValue);
1302 /*
1303 * Adjust any pointers into the value list to ensure
1304 * future edits occur at the same place
1305 */
1306 for (t = s->test, i = 0; t; t = t->next, i++)
1307 {
1308 if (st[i].value == thisValue)
1309 st[i].value = nextValue;
1310 }
1311 break;
1312 }
1313 /* fall through ... */
1314 case FcOpAssignReplace:
1315 /*
1316 * Delete all of the values and insert
1317 * the new set
1318 */
1319 FcConfigPatternDel (p, e->field);
1320 FcConfigPatternAdd (p, e->field, l, FcTrue);
1321 /*
1322 * Adjust any pointers into the value list as they no
1323 * longer point to anything valid
1324 */
1325 if (t)
1326 {
1327 FcPatternElt *thisElt = st[i].elt;
1328 for (t = s->test, i = 0; t; t = t->next, i++)
1329 {
1330 if (st[i].elt == thisElt)
1331 st[i].value = 0;
1332 }
1333 }
1334 break;
1335 case FcOpPrepend:
1336 if (t)
1337 {
1338 FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1339 break;
1340 }
1341 /* fall through ... */
1342 case FcOpPrependFirst:
1343 FcConfigPatternAdd (p, e->field, l, FcFalse);
1344 break;
1345 case FcOpAppend:
1346 if (t)
1347 {
1348 FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1349 break;
1350 }
1351 /* fall through ... */
1352 case FcOpAppendLast:
1353 FcConfigPatternAdd (p, e->field, l, FcTrue);
1354 break;
1355 default:
1356 break;
1357 }
1358 }
1359 /*
1360 * Now go through the pattern and eliminate
1361 * any properties without data
1362 */
1363 for (e = s->edit; e; e = e->next)
1364 FcConfigPatternCanon (p, e->field);
1365
1366 if (FcDebug () & FC_DBG_EDIT)
1367 {
1368 printf ("FcConfigSubstitute edit");
1369 FcPatternPrint (p);
1370 }
1371 }
1372 FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1373 free (st);
1374 if (FcDebug () & FC_DBG_EDIT)
1375 {
1376 printf ("FcConfigSubstitute done");
1377 FcPatternPrint (p);
1378 }
1379 return FcTrue;
1380 }
1381
1382 FcBool
1383 FcConfigSubstitute (FcConfig *config,
1384 FcPattern *p,
1385 FcMatchKind kind)
1386 {
1387 return FcConfigSubstituteWithPat (config, p, 0, kind);
1388 }
1389
1390 #if defined (_WIN32) && (defined (PIC) || defined (DLL_EXPORT))
1391
1392 static FcChar8 fontconfig_path[1000] = "";
1393
1394 BOOL WINAPI
1395 DllMain (HINSTANCE hinstDLL,
1396 DWORD fdwReason,
1397 LPVOID lpvReserved)
1398 {
1399 FcChar8 *p;
1400
1401 switch (fdwReason) {
1402 case DLL_PROCESS_ATTACH:
1403 if (!GetModuleFileName ((HMODULE) hinstDLL, fontconfig_path,
1404 sizeof (fontconfig_path)))
1405 break;
1406
1407 /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
1408 * assume it's a Unix-style installation tree, and use
1409 * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
1410 * folder where the DLL is as FONTCONFIG_PATH.
1411 */
1412 p = strrchr (fontconfig_path, '\\');
1413 if (p)
1414 {
1415 *p = '\0';
1416 p = strrchr (fontconfig_path, '\\');
1417 if (p && (FcStrCmpIgnoreCase (p + 1, "bin") == 0 ||
1418 FcStrCmpIgnoreCase (p + 1, "lib") == 0))
1419 *p = '\0';
1420 strcat (fontconfig_path, "\\etc\\fonts");
1421 }
1422 else
1423 fontconfig_path[0] = '\0';
1424
1425 break;
1426 }
1427
1428 return TRUE;
1429 }
1430
1431 #undef FONTCONFIG_PATH
1432 #define FONTCONFIG_PATH fontconfig_path
1433
1434 #else /* !(_WIN32 && PIC) */
1435
1436 #endif /* !(_WIN32 && PIC) */
1437
1438 #ifndef FONTCONFIG_FILE
1439 #define FONTCONFIG_FILE "fonts.conf"
1440 #endif
1441
1442 static FcChar8 *
1443 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1444 {
1445 FcChar8 *path;
1446
1447 if (!dir)
1448 dir = (FcChar8 *) "";
1449 path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1450 if (!path)
1451 return 0;
1452
1453 strcpy ((char *) path, (const char *) dir);
1454 /* make sure there's a single separator */
1455 #ifdef _WIN32
1456 if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
1457 path[strlen((char *) path)-1] != '\\')) &&
1458 (file[0] != '/' && file[0] != '\\'))
1459 strcat ((char *) path, "\\");
1460 #else
1461 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1462 strcat ((char *) path, "/");
1463 #endif
1464 strcat ((char *) path, (char *) file);
1465
1466 FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1467 if (access ((char *) path, R_OK) == 0)
1468 return path;
1469
1470 FcStrFree (path);
1471 return 0;
1472 }
1473
1474 static FcChar8 **
1475 FcConfigGetPath (void)
1476 {
1477 FcChar8 **path;
1478 FcChar8 *env, *e, *colon;
1479 FcChar8 *dir;
1480 int npath;
1481 int i;
1482
1483 npath = 2; /* default dir + null */
1484 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1485 if (env)
1486 {
1487 e = env;
1488 npath++;
1489 while (*e)
1490 if (*e++ == FC_SEARCH_PATH_SEPARATOR)
1491 npath++;
1492 }
1493 path = calloc (npath, sizeof (FcChar8 *));
1494 if (!path)
1495 goto bail0;
1496 i = 0;
1497
1498 if (env)
1499 {
1500 e = env;
1501 while (*e)
1502 {
1503 colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
1504 if (!colon)
1505 colon = e + strlen ((char *) e);
1506 path[i] = malloc (colon - e + 1);
1507 if (!path[i])
1508 goto bail1;
1509 strncpy ((char *) path[i], (const char *) e, colon - e);
1510 path[i][colon - e] = '\0';
1511 if (*colon)
1512 e = colon + 1;
1513 else
1514 e = colon;
1515 i++;
1516 }
1517 }
1518
1519 dir = (FcChar8 *) FONTCONFIG_PATH;
1520 path[i] = malloc (strlen ((char *) dir) + 1);
1521 if (!path[i])
1522 goto bail1;
1523 strcpy ((char *) path[i], (const char *) dir);
1524 return path;
1525
1526 bail1:
1527 for (i = 0; path[i]; i++)
1528 free (path[i]);
1529 free (path);
1530 bail0:
1531 return 0;
1532 }
1533
1534 static void
1535 FcConfigFreePath (FcChar8 **path)
1536 {
1537 FcChar8 **p;
1538
1539 for (p = path; *p; p++)
1540 free (*p);
1541 free (path);
1542 }
1543
1544 static FcBool _FcConfigHomeEnabled = FcTrue;
1545
1546 FcChar8 *
1547 FcConfigHome (void)
1548 {
1549 if (_FcConfigHomeEnabled)
1550 {
1551 char *home = getenv ("HOME");
1552
1553 #ifdef _WIN32
1554 if (home == NULL)
1555 home = getenv ("USERPROFILE");
1556 #endif
1557
1558 return home;
1559 }
1560 return 0;
1561 }
1562
1563 FcBool
1564 FcConfigEnableHome (FcBool enable)
1565 {
1566 FcBool prev = _FcConfigHomeEnabled;
1567 _FcConfigHomeEnabled = enable;
1568 return prev;
1569 }
1570
1571 FcChar8 *
1572 FcConfigFilename (const FcChar8 *url)
1573 {
1574 FcChar8 *file, *dir, **path, **p;
1575
1576 if (!url || !*url)
1577 {
1578 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1579 if (!url)
1580 url = (FcChar8 *) FONTCONFIG_FILE;
1581 }
1582 file = 0;
1583
1584 #ifdef _WIN32
1585 if (isalpha (*url) &&
1586 url[1] == ':' &&
1587 (url[2] == '/' || url[2] == '\\'))
1588 goto absolute_path;
1589 #endif
1590
1591 switch (*url) {
1592 case '~':
1593 dir = FcConfigHome ();
1594 if (dir)
1595 file = FcConfigFileExists (dir, url + 1);
1596 else
1597 file = 0;
1598 break;
1599 #ifdef _WIN32
1600 case '\\':
1601 absolute_path:
1602 #endif
1603 case '/':
1604 file = FcConfigFileExists (0, url);
1605 break;
1606 default:
1607 path = FcConfigGetPath ();
1608 if (!path)
1609 return 0;
1610 for (p = path; *p; p++)
1611 {
1612 file = FcConfigFileExists (*p, url);
1613 if (file)
1614 break;
1615 }
1616 FcConfigFreePath (path);
1617 break;
1618 }
1619 return file;
1620 }
1621
1622 /*
1623 * Manage the application-specific fonts
1624 */
1625
1626 FcBool
1627 FcConfigAppFontAddFile (FcConfig *config,
1628 const FcChar8 *file)
1629 {
1630 FcFontSet *set;
1631 FcStrSet *subdirs;
1632 FcStrList *sublist;
1633 FcChar8 *subdir;
1634
1635 if (!config)
1636 {
1637 config = FcConfigGetCurrent ();
1638 if (!config)
1639 return FcFalse;
1640 }
1641
1642 subdirs = FcStrSetCreate ();
1643 if (!subdirs)
1644 return FcFalse;
1645
1646 set = FcConfigGetFonts (config, FcSetApplication);
1647 if (!set)
1648 {
1649 set = FcFontSetCreate ();
1650 if (!set)
1651 {
1652 FcStrSetDestroy (subdirs);
1653 return FcFalse;
1654 }
1655 FcConfigSetFonts (config, set, FcSetApplication);
1656 }
1657
1658 if (!FcFileScanConfig (set, subdirs, 0, config->blanks, file, FcFalse, config))
1659 {
1660 FcStrSetDestroy (subdirs);
1661 return FcFalse;
1662 }
1663 if ((sublist = FcStrListCreate (subdirs)))
1664 {
1665 while ((subdir = FcStrListNext (sublist)))
1666 {
1667 FcConfigAppFontAddDir (config, subdir);
1668 }
1669 FcStrListDone (sublist);
1670 }
1671 return FcTrue;
1672 }
1673
1674 FcBool
1675 FcConfigAppFontAddDir (FcConfig *config,
1676 const FcChar8 *dir)
1677 {
1678 FcFontSet *set;
1679 FcStrSet *subdirs;
1680 FcStrList *sublist;
1681 FcChar8 *subdir;
1682
1683 if (!config)
1684 {
1685 config = FcConfigGetCurrent ();
1686 if (!config)
1687 return FcFalse;
1688 }
1689 subdirs = FcStrSetCreate ();
1690 if (!subdirs)
1691 return FcFalse;
1692
1693 set = FcConfigGetFonts (config, FcSetApplication);
1694 if (!set)
1695 {
1696 set = FcFontSetCreate ();
1697 if (!set)
1698 {
1699 FcStrSetDestroy (subdirs);
1700 return FcFalse;
1701 }
1702 FcConfigSetFonts (config, set, FcSetApplication);
1703 }
1704
1705 if (!FcDirScanConfig (set, subdirs, 0, config->blanks, dir, FcFalse, config))
1706 {
1707 FcStrSetDestroy (subdirs);
1708 return FcFalse;
1709 }
1710 if ((sublist = FcStrListCreate (subdirs)))
1711 {
1712 while ((subdir = FcStrListNext (sublist)))
1713 {
1714 FcConfigAppFontAddDir (config, subdir);
1715 }
1716 FcStrListDone (sublist);
1717 }
1718 return FcTrue;
1719 }
1720
1721 void
1722 FcConfigAppFontClear (FcConfig *config)
1723 {
1724 FcConfigSetFonts (config, 0, FcSetApplication);
1725 }
1726
1727 /*
1728 * Manage filename-based font source selectors
1729 */
1730
1731 FcBool
1732 FcConfigGlobAdd (FcConfig *config,
1733 const FcChar8 *glob,
1734 FcBool accept)
1735 {
1736 FcStrSet *set = accept ? config->acceptGlobs : config->rejectGlobs;
1737
1738 return FcStrSetAdd (set, glob);
1739 }
1740
1741 static FcBool
1742 FcConfigGlobMatch (const FcChar8 *glob,
1743 const FcChar8 *string)
1744 {
1745 FcChar8 c;
1746
1747 while ((c = *glob++))
1748 {
1749 switch (c) {
1750 case '*':
1751 /* short circuit common case */
1752 if (!*glob)
1753 return FcTrue;
1754 /* short circuit another common case */
1755 if (strchr ((char *) glob, '*') == 0)
1756 string += strlen ((char *) string) - strlen ((char *) glob);
1757 while (*string)
1758 {
1759 if (FcConfigGlobMatch (glob, string))
1760 return FcTrue;
1761 string++;
1762 }
1763 return FcFalse;
1764 case '?':
1765 if (*string++ == '\0')
1766 return FcFalse;
1767 break;
1768 default:
1769 if (*string++ != c)
1770 return FcFalse;
1771 break;
1772 }
1773 }
1774 return *string == '\0';
1775 }
1776
1777 static FcBool
1778 FcConfigGlobsMatch (const FcStrSet *globs,
1779 const FcChar8 *string)
1780 {
1781 int i;
1782
1783 for (i = 0; i < globs->num; i++)
1784 if (FcConfigGlobMatch (globs->strs[i], string))
1785 return FcTrue;
1786 return FcFalse;
1787 }
1788
1789 FcBool
1790 FcConfigAcceptFilename (FcConfig *config,
1791 const FcChar8 *filename)
1792 {
1793 if (FcConfigGlobsMatch (config->acceptGlobs, filename))
1794 return FcTrue;
1795 if (FcConfigGlobsMatch (config->rejectGlobs, filename))
1796 return FcFalse;
1797 return FcTrue;
1798 }