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