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