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