]> git.wh0rd.org - fontconfig.git/blob - src/fccfg.c
Fix autoconf build process for fontconfig
[fontconfig.git] / src / fccfg.c
1 /*
2 * $XFree86: xc/lib/fontconfig/src/fccfg.c,v 1.5 2002/03/01 01:00:54 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 = (FcMatrix *) &FcIdentityMatrix;
475 v.type = FcTypeMatrix;
476 }
477 return v;
478 }
479
480 FcBool
481 FcConfigCompareValue (FcValue m,
482 FcOp op,
483 FcValue v)
484 {
485 FcBool ret = FcFalse;
486
487 m = FcConfigPromote (m, v);
488 v = FcConfigPromote (v, m);
489 if (m.type == v.type)
490 {
491 ret = FcFalse;
492 switch (m.type) {
493 case FcTypeInteger:
494 break; /* FcConfigPromote prevents this from happening */
495 case FcTypeDouble:
496 switch (op) {
497 case FcOpEqual:
498 case FcOpContains:
499 ret = m.u.d == v.u.d;
500 break;
501 case FcOpNotEqual:
502 ret = m.u.d != v.u.d;
503 break;
504 case FcOpLess:
505 ret = m.u.d < v.u.d;
506 break;
507 case FcOpLessEqual:
508 ret = m.u.d <= v.u.d;
509 break;
510 case FcOpMore:
511 ret = m.u.d > v.u.d;
512 break;
513 case FcOpMoreEqual:
514 ret = m.u.d >= v.u.d;
515 break;
516 default:
517 break;
518 }
519 break;
520 case FcTypeBool:
521 switch (op) {
522 case FcOpEqual:
523 case FcOpContains:
524 ret = m.u.b == v.u.b;
525 break;
526 case FcOpNotEqual:
527 ret = m.u.b != v.u.b;
528 break;
529 default:
530 break;
531 }
532 break;
533 case FcTypeString:
534 switch (op) {
535 case FcOpEqual:
536 case FcOpContains:
537 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) == 0;
538 break;
539 case FcOpNotEqual:
540 ret = FcStrCmpIgnoreCase (m.u.s, v.u.s) != 0;
541 break;
542 default:
543 break;
544 }
545 break;
546 case FcTypeMatrix:
547 switch (op) {
548 case FcOpEqual:
549 case FcOpContains:
550 ret = FcMatrixEqual (m.u.m, v.u.m);
551 break;
552 case FcOpNotEqual:
553 ret = !FcMatrixEqual (m.u.m, v.u.m);
554 break;
555 default:
556 break;
557 }
558 break;
559 case FcTypeCharSet:
560 switch (op) {
561 case FcOpContains:
562 /* m contains v if v - m is empty */
563 ret = FcCharSetSubtractCount (v.u.c, m.u.c) == 0;
564 break;
565 case FcOpEqual:
566 ret = FcCharSetEqual (m.u.c, v.u.c);
567 break;
568 case FcOpNotEqual:
569 ret = !FcCharSetEqual (m.u.c, v.u.c);
570 break;
571 default:
572 break;
573 }
574 break;
575 case FcTypeVoid:
576 switch (op) {
577 case FcOpEqual:
578 case FcOpContains:
579 ret = FcTrue;
580 break;
581 default:
582 break;
583 }
584 break;
585 }
586 }
587 else
588 {
589 if (op == FcOpNotEqual)
590 ret = FcTrue;
591 }
592 return ret;
593 }
594
595
596 static FcValue
597 FcConfigEvaluate (FcPattern *p, FcExpr *e)
598 {
599 FcValue v, vl, vr;
600 FcResult r;
601 FcMatrix *m;
602
603 switch (e->op) {
604 case FcOpInteger:
605 v.type = FcTypeInteger;
606 v.u.i = e->u.ival;
607 break;
608 case FcOpDouble:
609 v.type = FcTypeDouble;
610 v.u.d = e->u.dval;
611 break;
612 case FcOpString:
613 v.type = FcTypeString;
614 v.u.s = e->u.sval;
615 v = FcValueSave (v);
616 break;
617 case FcOpMatrix:
618 v.type = FcTypeMatrix;
619 v.u.m = e->u.mval;
620 v = FcValueSave (v);
621 break;
622 case FcOpCharSet:
623 v.type = FcTypeCharSet;
624 v.u.c = e->u.cval;
625 v = FcValueSave (v);
626 break;
627 case FcOpBool:
628 v.type = FcTypeBool;
629 v.u.b = e->u.bval;
630 break;
631 case FcOpField:
632 r = FcPatternGet (p, e->u.field, 0, &v);
633 if (r != FcResultMatch)
634 v.type = FcTypeVoid;
635 break;
636 case FcOpConst:
637 if (FcNameConstant (e->u.constant, &v.u.i))
638 v.type = FcTypeInteger;
639 else
640 v.type = FcTypeVoid;
641 break;
642 case FcOpQuest:
643 vl = FcConfigEvaluate (p, e->u.tree.left);
644 if (vl.type == FcTypeBool)
645 {
646 if (vl.u.b)
647 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
648 else
649 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
650 }
651 else
652 v.type = FcTypeVoid;
653 FcValueDestroy (vl);
654 break;
655 case FcOpOr:
656 case FcOpAnd:
657 case FcOpEqual:
658 case FcOpContains:
659 case FcOpNotEqual:
660 case FcOpLess:
661 case FcOpLessEqual:
662 case FcOpMore:
663 case FcOpMoreEqual:
664 case FcOpPlus:
665 case FcOpMinus:
666 case FcOpTimes:
667 case FcOpDivide:
668 vl = FcConfigEvaluate (p, e->u.tree.left);
669 vr = FcConfigEvaluate (p, e->u.tree.right);
670 vl = FcConfigPromote (vl, vr);
671 vr = FcConfigPromote (vr, vl);
672 if (vl.type == vr.type)
673 {
674 switch (vl.type) {
675 case FcTypeDouble:
676 switch (e->op) {
677 case FcOpPlus:
678 v.type = FcTypeDouble;
679 v.u.d = vl.u.d + vr.u.d;
680 break;
681 case FcOpMinus:
682 v.type = FcTypeDouble;
683 v.u.d = vl.u.d - vr.u.d;
684 break;
685 case FcOpTimes:
686 v.type = FcTypeDouble;
687 v.u.d = vl.u.d * vr.u.d;
688 break;
689 case FcOpDivide:
690 v.type = FcTypeDouble;
691 v.u.d = vl.u.d / vr.u.d;
692 break;
693 case FcOpEqual:
694 case FcOpContains:
695 v.type = FcTypeBool;
696 v.u.b = vl.u.d == vr.u.d;
697 break;
698 case FcOpNotEqual:
699 v.type = FcTypeBool;
700 v.u.b = vl.u.d != vr.u.d;
701 break;
702 case FcOpLess:
703 v.type = FcTypeBool;
704 v.u.b = vl.u.d < vr.u.d;
705 break;
706 case FcOpLessEqual:
707 v.type = FcTypeBool;
708 v.u.b = vl.u.d <= vr.u.d;
709 break;
710 case FcOpMore:
711 v.type = FcTypeBool;
712 v.u.b = vl.u.d > vr.u.d;
713 break;
714 case FcOpMoreEqual:
715 v.type = FcTypeBool;
716 v.u.b = vl.u.d >= vr.u.d;
717 break;
718 default:
719 v.type = FcTypeVoid;
720 break;
721 }
722 if (v.type == FcTypeDouble &&
723 v.u.d == (double) (int) v.u.d)
724 {
725 v.type = FcTypeInteger;
726 v.u.i = (int) v.u.d;
727 }
728 break;
729 case FcTypeBool:
730 switch (e->op) {
731 case FcOpOr:
732 v.type = FcTypeBool;
733 v.u.b = vl.u.b || vr.u.b;
734 break;
735 case FcOpAnd:
736 v.type = FcTypeBool;
737 v.u.b = vl.u.b && vr.u.b;
738 break;
739 case FcOpEqual:
740 case FcOpContains:
741 v.type = FcTypeBool;
742 v.u.b = vl.u.b == vr.u.b;
743 break;
744 case FcOpNotEqual:
745 v.type = FcTypeBool;
746 v.u.b = vl.u.b != vr.u.b;
747 break;
748 default:
749 v.type = FcTypeVoid;
750 break;
751 }
752 break;
753 case FcTypeString:
754 switch (e->op) {
755 case FcOpEqual:
756 case FcOpContains:
757 v.type = FcTypeBool;
758 v.u.b = FcStrCmpIgnoreCase (vl.u.s, vr.u.s) == 0;
759 break;
760 case FcOpNotEqual:
761 v.type = FcTypeBool;
762 v.u.b = FcStrCmpIgnoreCase (vl.u.s, vr.u.s) != 0;
763 break;
764 case FcOpPlus:
765 v.type = FcTypeString;
766 v.u.s = FcStrPlus (vl.u.s, vr.u.s);
767 if (!v.u.s)
768 v.type = FcTypeVoid;
769 break;
770 default:
771 v.type = FcTypeVoid;
772 break;
773 }
774 case FcTypeMatrix:
775 switch (e->op) {
776 case FcOpEqual:
777 case FcOpContains:
778 v.type = FcTypeBool;
779 v.u.b = FcMatrixEqual (vl.u.m, vr.u.m);
780 break;
781 case FcOpNotEqual:
782 v.type = FcTypeBool;
783 v.u.b = FcMatrixEqual (vl.u.m, vr.u.m);
784 break;
785 case FcOpTimes:
786 v.type = FcTypeMatrix;
787 m = malloc (sizeof (FcMatrix));
788 if (m)
789 {
790 FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
791 FcMatrixMultiply (m, vl.u.m, vr.u.m);
792 v.u.m = m;
793 }
794 else
795 {
796 v.type = FcTypeVoid;
797 }
798 break;
799 default:
800 v.type = FcTypeVoid;
801 break;
802 }
803 break;
804 case FcTypeCharSet:
805 switch (e->op) {
806 case FcOpContains:
807 /* vl contains vr if vr - vl is empty */
808 v.type = FcTypeBool;
809 v.u.b = FcCharSetSubtractCount (vr.u.c, vl.u.c) == 0;
810 break;
811 case FcOpEqual:
812 v.type = FcTypeBool;
813 v.u.b = FcCharSetEqual (vl.u.c, vr.u.c);
814 break;
815 case FcOpNotEqual:
816 v.type = FcTypeBool;
817 v.u.b = !FcCharSetEqual (vl.u.c, vr.u.c);
818 break;
819 default:
820 v.type = FcTypeVoid;
821 break;
822 }
823 break;
824 default:
825 v.type = FcTypeVoid;
826 break;
827 }
828 }
829 else
830 v.type = FcTypeVoid;
831 FcValueDestroy (vl);
832 FcValueDestroy (vr);
833 break;
834 case FcOpNot:
835 vl = FcConfigEvaluate (p, e->u.tree.left);
836 switch (vl.type) {
837 case FcTypeBool:
838 v.type = FcTypeBool;
839 v.u.b = !vl.u.b;
840 break;
841 default:
842 v.type = FcTypeVoid;
843 break;
844 }
845 FcValueDestroy (vl);
846 break;
847 default:
848 v.type = FcTypeVoid;
849 break;
850 }
851 return v;
852 }
853
854 static FcValueList *
855 FcConfigMatchValueList (FcPattern *p,
856 FcTest *t,
857 FcValueList *v)
858 {
859 FcValueList *ret = 0;
860 FcExpr *e = t->expr;
861 FcValue value;
862
863 while (e)
864 {
865 if (e->op == FcOpComma)
866 {
867 value = FcConfigEvaluate (p, e->u.tree.left);
868 e = e->u.tree.right;
869 }
870 else
871 {
872 value = FcConfigEvaluate (p, e);
873 e = 0;
874 }
875
876 for (; v; v = v->next)
877 {
878 if (FcConfigCompareValue (v->value, t->op, value))
879 {
880 if (!ret)
881 ret = v;
882 }
883 else
884 {
885 if (t->qual == FcQualAll)
886 {
887 ret = 0;
888 break;
889 }
890 }
891 }
892 }
893 FcValueDestroy (value);
894 return ret;
895 }
896
897 static FcValueList *
898 FcConfigValues (FcPattern *p, FcExpr *e)
899 {
900 FcValueList *l;
901
902 if (!e)
903 return 0;
904 l = (FcValueList *) malloc (sizeof (FcValueList));
905 if (!l)
906 return 0;
907 FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
908 if (e->op == FcOpComma)
909 {
910 l->value = FcConfigEvaluate (p, e->u.tree.left);
911 l->next = FcConfigValues (p, e->u.tree.right);
912 }
913 else
914 {
915 l->value = FcConfigEvaluate (p, e);
916 l->next = 0;
917 }
918 while (l && l->value.type == FcTypeVoid)
919 {
920 FcValueList *next = l->next;
921
922 FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
923 free (l);
924 l = next;
925 }
926 return l;
927 }
928
929 static FcBool
930 FcConfigAdd (FcValueList **head,
931 FcValueList *position,
932 FcBool append,
933 FcValueList *new)
934 {
935 FcValueList **prev, *last;
936
937 if (append)
938 {
939 if (position)
940 prev = &position->next;
941 else
942 for (prev = head; *prev; prev = &(*prev)->next)
943 ;
944 }
945 else
946 {
947 if (position)
948 {
949 for (prev = head; *prev; prev = &(*prev)->next)
950 {
951 if (*prev == position)
952 break;
953 }
954 }
955 else
956 prev = head;
957
958 if (FcDebug () & FC_DBG_EDIT)
959 {
960 if (!*prev)
961 printf ("position not on list\n");
962 }
963 }
964
965 if (FcDebug () & FC_DBG_EDIT)
966 {
967 printf ("%s list before ", append ? "Append" : "Prepend");
968 FcValueListPrint (*head);
969 printf ("\n");
970 }
971
972 if (new)
973 {
974 last = new;
975 while (last->next)
976 last = last->next;
977
978 last->next = *prev;
979 *prev = new;
980 }
981
982 if (FcDebug () & FC_DBG_EDIT)
983 {
984 printf ("%s list after ", append ? "Append" : "Prepend");
985 FcValueListPrint (*head);
986 printf ("\n");
987 }
988
989 return FcTrue;
990 }
991
992 static void
993 FcConfigDel (FcValueList **head,
994 FcValueList *position)
995 {
996 FcValueList **prev;
997
998 for (prev = head; *prev; prev = &(*prev)->next)
999 {
1000 if (*prev == position)
1001 {
1002 *prev = position->next;
1003 position->next = 0;
1004 FcValueListDestroy (position);
1005 break;
1006 }
1007 }
1008 }
1009
1010 static void
1011 FcConfigPatternAdd (FcPattern *p,
1012 const char *object,
1013 FcValueList *list,
1014 FcBool append)
1015 {
1016 if (list)
1017 {
1018 FcPatternElt *e = FcPatternFind (p, object, FcTrue);
1019
1020 if (!e)
1021 return;
1022 FcConfigAdd (&e->values, 0, append, list);
1023 }
1024 }
1025
1026 /*
1027 * Delete all values associated with a field
1028 */
1029 static void
1030 FcConfigPatternDel (FcPattern *p,
1031 const char *object)
1032 {
1033 FcPatternElt *e = FcPatternFind (p, object, FcFalse);
1034 if (!e)
1035 return;
1036 while (e->values)
1037 FcConfigDel (&e->values, e->values);
1038 }
1039
1040 static void
1041 FcConfigPatternCanon (FcPattern *p,
1042 const char *object)
1043 {
1044 FcPatternElt *e = FcPatternFind (p, object, FcFalse);
1045 if (!e)
1046 return;
1047 if (!e->values)
1048 FcPatternDel (p, object);
1049 }
1050
1051 FcBool
1052 FcConfigSubstitute (FcConfig *config,
1053 FcPattern *p,
1054 FcMatchKind kind)
1055 {
1056 FcSubst *s;
1057 FcSubState *st;
1058 int i;
1059 FcTest *t;
1060 FcEdit *e;
1061 FcValueList *l;
1062
1063 if (!config)
1064 {
1065 config = FcConfigGetCurrent ();
1066 if (!config)
1067 return FcFalse;
1068 }
1069
1070 st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1071 if (!st && config->maxObjects)
1072 return FcFalse;
1073 FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1074
1075 if (FcDebug () & FC_DBG_EDIT)
1076 {
1077 printf ("FcConfigSubstitute ");
1078 FcPatternPrint (p);
1079 }
1080 if (kind == FcMatchPattern)
1081 s = config->substPattern;
1082 else
1083 s = config->substFont;
1084 for (; s; s = s->next)
1085 {
1086 /*
1087 * Check the tests to see if
1088 * they all match the pattern
1089 */
1090 for (t = s->test, i = 0; t; t = t->next, i++)
1091 {
1092 if (FcDebug () & FC_DBG_EDIT)
1093 {
1094 printf ("FcConfigSubstitute test ");
1095 FcTestPrint (t);
1096 }
1097 st[i].elt = FcPatternFind (p, t->field, FcFalse);
1098 /*
1099 * If there's no such field in the font,
1100 * then FcQualAll matches while FcQualAny does not
1101 */
1102 if (!st[i].elt)
1103 {
1104 if (t->qual == FcQualAll)
1105 {
1106 st[i].value = 0;
1107 continue;
1108 }
1109 else
1110 break;
1111 }
1112 /*
1113 * Check to see if there is a match, mark the location
1114 * to apply match-relative edits
1115 */
1116 st[i].value = FcConfigMatchValueList (p, t, st[i].elt->values);
1117 if (!st[i].value)
1118 break;
1119 }
1120 if (t)
1121 {
1122 if (FcDebug () & FC_DBG_EDIT)
1123 printf ("No match\n");
1124 continue;
1125 }
1126 if (FcDebug () & FC_DBG_EDIT)
1127 {
1128 printf ("Substitute ");
1129 FcSubstPrint (s);
1130 }
1131 for (e = s->edit; e; e = e->next)
1132 {
1133 /*
1134 * Evaluate the list of expressions
1135 */
1136 l = FcConfigValues (p, e->expr);
1137 /*
1138 * Locate any test associated with this field
1139 */
1140 for (t = s->test, i = 0; t; t = t->next, i++)
1141 if (!FcStrCmpIgnoreCase ((FcChar8 *) t->field, (FcChar8 *) e->field))
1142 break;
1143 switch (e->op) {
1144 case FcOpAssign:
1145 /*
1146 * If there was a test, then replace the matched
1147 * value with the new list of values
1148 */
1149 if (t)
1150 {
1151 FcValueList *thisValue = st[i].value;
1152 FcValueList *nextValue = thisValue ? thisValue->next : 0;
1153
1154 /*
1155 * Append the new list of values after the current value
1156 */
1157 FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1158 /*
1159 * Delete the marked value
1160 */
1161 FcConfigDel (&st[i].elt->values, thisValue);
1162 /*
1163 * Adjust any pointers into the value list to ensure
1164 * future edits occur at the same place
1165 */
1166 for (t = s->test, i = 0; t; t = t->next, i++)
1167 {
1168 if (st[i].value == thisValue)
1169 st[i].value = nextValue;
1170 }
1171 break;
1172 }
1173 /* fall through ... */
1174 case FcOpAssignReplace:
1175 /*
1176 * Delete all of the values and insert
1177 * the new set
1178 */
1179 FcConfigPatternDel (p, e->field);
1180 FcConfigPatternAdd (p, e->field, l, FcTrue);
1181 /*
1182 * Adjust any pointers into the value list as they no
1183 * longer point to anything valid
1184 */
1185 if (t)
1186 {
1187 FcPatternElt *thisElt = st[i].elt;
1188 for (t = s->test, i = 0; t; t = t->next, i++)
1189 {
1190 if (st[i].elt == thisElt)
1191 st[i].value = 0;
1192 }
1193 }
1194 break;
1195 case FcOpPrepend:
1196 if (t)
1197 {
1198 FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1199 break;
1200 }
1201 /* fall through ... */
1202 case FcOpPrependFirst:
1203 FcConfigPatternAdd (p, e->field, l, FcFalse);
1204 break;
1205 case FcOpAppend:
1206 if (t)
1207 {
1208 FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1209 break;
1210 }
1211 /* fall through ... */
1212 case FcOpAppendLast:
1213 FcConfigPatternAdd (p, e->field, l, FcTrue);
1214 break;
1215 default:
1216 break;
1217 }
1218 }
1219 /*
1220 * Now go through the pattern and eliminate
1221 * any properties without data
1222 */
1223 for (e = s->edit; e; e = e->next)
1224 FcConfigPatternCanon (p, e->field);
1225
1226 if (FcDebug () & FC_DBG_EDIT)
1227 {
1228 printf ("FcConfigSubstitute edit");
1229 FcPatternPrint (p);
1230 }
1231 }
1232 FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1233 free (st);
1234 if (FcDebug () & FC_DBG_EDIT)
1235 {
1236 printf ("FcConfigSubstitute done");
1237 FcPatternPrint (p);
1238 }
1239 return FcTrue;
1240 }
1241
1242 #ifndef FONTCONFIG_PATH
1243 #define FONTCONFIG_PATH "/etc/fonts"
1244 #endif
1245
1246 #ifndef FONTCONFIG_FILE
1247 #define FONTCONFIG_FILE "fonts.conf"
1248 #endif
1249
1250 static FcChar8 *
1251 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1252 {
1253 FcChar8 *path;
1254
1255 if (!dir)
1256 dir = (FcChar8 *) "";
1257 path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1258 if (!path)
1259 return 0;
1260
1261 strcpy ((char *) path, (const char *) dir);
1262 /* make sure there's a single separating / */
1263 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1264 strcat ((char *) path, "/");
1265 strcat ((char *) path, (char *) file);
1266
1267 if (access ((char *) path, R_OK) == 0)
1268 return path;
1269
1270 free (path);
1271 return 0;
1272 }
1273
1274 static FcChar8 **
1275 FcConfigGetPath (void)
1276 {
1277 FcChar8 **path;
1278 FcChar8 *env, *e, *colon;
1279 FcChar8 *dir;
1280 int npath;
1281 int i;
1282
1283 npath = 2; /* default dir + null */
1284 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1285 if (env)
1286 {
1287 e = env;
1288 npath++;
1289 while (*e)
1290 if (*e++ == ':')
1291 npath++;
1292 }
1293 path = calloc (npath, sizeof (FcChar8 *));
1294 if (!path)
1295 goto bail0;
1296 i = 0;
1297
1298 if (env)
1299 {
1300 e = env;
1301 while (*e)
1302 {
1303 colon = (FcChar8 *) strchr ((char *) e, ':');
1304 if (!colon)
1305 colon = e + strlen ((char *) e);
1306 path[i] = malloc (colon - e + 1);
1307 if (!path[i])
1308 goto bail1;
1309 strncpy ((char *) path[i], (const char *) e, colon - e);
1310 path[i][colon - e] = '\0';
1311 if (*colon)
1312 e = colon + 1;
1313 else
1314 e = colon;
1315 i++;
1316 }
1317 }
1318
1319 dir = (FcChar8 *) FONTCONFIG_PATH;
1320 path[i] = malloc (strlen ((char *) dir) + 1);
1321 if (!path[i])
1322 goto bail1;
1323 strcpy ((char *) path[i], (const char *) dir);
1324 return path;
1325
1326 bail1:
1327 for (i = 0; path[i]; i++)
1328 free (path[i]);
1329 free (path);
1330 bail0:
1331 return 0;
1332 }
1333
1334 static void
1335 FcConfigFreePath (FcChar8 **path)
1336 {
1337 FcChar8 **p;
1338
1339 for (p = path; *p; p++)
1340 free (*p);
1341 free (path);
1342 }
1343
1344 FcChar8 *
1345 FcConfigFilename (const FcChar8 *url)
1346 {
1347 FcChar8 *file, *dir, **path, **p;
1348
1349 if (!url || !*url)
1350 {
1351 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1352 if (!url)
1353 url = (FcChar8 *) FONTCONFIG_FILE;
1354 }
1355 file = 0;
1356 switch (*url) {
1357 case '~':
1358 dir = (FcChar8 *) getenv ("HOME");
1359 if (dir)
1360 file = FcConfigFileExists (dir, url + 1);
1361 else
1362 file = 0;
1363 break;
1364 case '/':
1365 file = FcConfigFileExists (0, url);
1366 break;
1367 default:
1368 path = FcConfigGetPath ();
1369 if (!path)
1370 return 0;
1371 for (p = path; *p; p++)
1372 {
1373 file = FcConfigFileExists (*p, url);
1374 if (file)
1375 break;
1376 }
1377 FcConfigFreePath (path);
1378 break;
1379 }
1380 return file;
1381 }
1382
1383 /*
1384 * Manage the application-specific fonts
1385 */
1386
1387 FcBool
1388 FcConfigAppFontAddFile (FcConfig *config,
1389 const FcChar8 *file)
1390 {
1391 FcFontSet *set;
1392 FcStrSet *subdirs;
1393 FcStrList *sublist;
1394 FcChar8 *subdir;
1395
1396 if (!config)
1397 {
1398 config = FcConfigGetCurrent ();
1399 if (!config)
1400 return FcFalse;
1401 }
1402
1403 subdirs = FcStrSetCreate ();
1404 if (!subdirs)
1405 return FcFalse;
1406
1407 set = FcConfigGetFonts (config, FcSetApplication);
1408 if (!set)
1409 {
1410 set = FcFontSetCreate ();
1411 if (!set)
1412 {
1413 FcStrSetDestroy (subdirs);
1414 return FcFalse;
1415 }
1416 FcConfigSetFonts (config, set, FcSetApplication);
1417 }
1418
1419 if (!FcFileScan (set, subdirs, 0, config->blanks, file, FcFalse))
1420 {
1421 FcStrSetDestroy (subdirs);
1422 return FcFalse;
1423 }
1424 if ((sublist = FcStrListCreate (subdirs)))
1425 {
1426 while ((subdir = FcStrListNext (sublist)))
1427 {
1428 FcConfigAppFontAddDir (config, subdir);
1429 }
1430 FcStrListDone (sublist);
1431 }
1432 return FcTrue;
1433 }
1434
1435 FcBool
1436 FcConfigAppFontAddDir (FcConfig *config,
1437 const FcChar8 *dir)
1438 {
1439 FcFontSet *set;
1440 FcStrSet *subdirs;
1441 FcStrList *sublist;
1442 FcChar8 *subdir;
1443
1444 if (!config)
1445 {
1446 config = FcConfigGetCurrent ();
1447 if (!config)
1448 return FcFalse;
1449 }
1450 subdirs = FcStrSetCreate ();
1451 if (!subdirs)
1452 return FcFalse;
1453
1454 set = FcConfigGetFonts (config, FcSetApplication);
1455 if (!set)
1456 {
1457 set = FcFontSetCreate ();
1458 if (!set)
1459 {
1460 FcStrSetDestroy (subdirs);
1461 return FcFalse;
1462 }
1463 FcConfigSetFonts (config, set, FcSetApplication);
1464 }
1465
1466 if (!FcDirScan (set, subdirs, 0, config->blanks, dir, FcFalse))
1467 {
1468 FcStrSetDestroy (subdirs);
1469 return FcFalse;
1470 }
1471 if ((sublist = FcStrListCreate (subdirs)))
1472 {
1473 while ((subdir = FcStrListNext (sublist)))
1474 {
1475 FcConfigAppFontAddDir (config, subdir);
1476 }
1477 FcStrListDone (sublist);
1478 }
1479 return FcTrue;
1480 }
1481
1482 void
1483 FcConfigAppFontClear (FcConfig *config)
1484 {
1485 FcConfigSetFonts (config, 0, FcSetApplication);
1486 }