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