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