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