]> git.wh0rd.org - fontconfig.git/blob - src/fccfg.c
fontconfig library: build fixes and compiler warning fixes
[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, (FcChar8 *) ("~/" 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 (FcChar8 **strings)
92 {
93 FcChar8 **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 (FcChar8 ***strings, FcChar8 *string)
103 {
104 int n;
105 FcChar8 **s;
106
107 n = 0;
108 for (s = *strings; s && *s; s++)
109 n++;
110 s = malloc ((n + 2) * sizeof (FcChar8 *));
111 if (!s)
112 return FcFalse;
113 s[n] = string;
114 s[n+1] = 0;
115 memcpy (s, *strings, n * sizeof (FcChar8 *));
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 FcChar8 **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 FcChar8 *d)
204 {
205 FcChar8 *dir;
206 FcChar8 *h;
207
208 if (*d == '~')
209 {
210 h = (FcChar8 *) getenv ("HOME");
211 if (!h)
212 return FcFalse;
213 dir = (FcChar8 *) malloc (strlen ((char *) h) + strlen ((char *) d));
214 if (!dir)
215 return FcFalse;
216 strcpy ((char *) dir, (char *) h);
217 strcat ((char *) dir, (char *) d+1);
218 }
219 else
220 {
221 dir = (FcChar8 *) malloc (strlen ((char *) 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 FcChar8 **
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 FcChar8 *f)
249 {
250 FcChar8 *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 FcChar8 **
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 FcChar8 *c)
277 {
278 FcChar8 *new;
279 FcChar8 *h;
280
281 if (*c == '~')
282 {
283 h = (FcChar8 *) getenv ("HOME");
284 if (!h)
285 return FcFalse;
286 new = (FcChar8 *) malloc (strlen ((char *) h) + strlen ((char *) c));
287 if (!new)
288 return FcFalse;
289 strcpy ((char *) new, (char *) h);
290 strcat ((char *) new, (char *) 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 FcChar8 *
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
549 switch (e->op) {
550 case FcOpInteger:
551 v.type = FcTypeInteger;
552 v.u.i = e->u.ival;
553 break;
554 case FcOpDouble:
555 v.type = FcTypeDouble;
556 v.u.d = e->u.dval;
557 break;
558 case FcOpString:
559 v.type = FcTypeString;
560 v.u.s = e->u.sval;
561 v = FcValueSave (v);
562 break;
563 case FcOpMatrix:
564 v.type = FcTypeMatrix;
565 v.u.m = e->u.mval;
566 v = FcValueSave (v);
567 break;
568 case FcOpCharSet:
569 v.type = FcTypeCharSet;
570 v.u.c = e->u.cval;
571 v = FcValueSave (v);
572 break;
573 case FcOpBool:
574 v.type = FcTypeBool;
575 v.u.b = e->u.bval;
576 break;
577 case FcOpField:
578 r = FcPatternGet (p, e->u.field, 0, &v);
579 if (r != FcResultMatch)
580 v.type = FcTypeVoid;
581 break;
582 case FcOpConst:
583 if (FcNameConstant (e->u.constant, &v.u.i))
584 v.type = FcTypeInteger;
585 else
586 v.type = FcTypeVoid;
587 break;
588 case FcOpQuest:
589 vl = FcConfigEvaluate (p, e->u.tree.left);
590 if (vl.type == FcTypeBool)
591 {
592 if (vl.u.b)
593 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
594 else
595 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
596 }
597 else
598 v.type = FcTypeVoid;
599 FcValueDestroy (vl);
600 break;
601 case FcOpOr:
602 case FcOpAnd:
603 case FcOpEqual:
604 case FcOpContains:
605 case FcOpNotEqual:
606 case FcOpLess:
607 case FcOpLessEqual:
608 case FcOpMore:
609 case FcOpMoreEqual:
610 case FcOpPlus:
611 case FcOpMinus:
612 case FcOpTimes:
613 case FcOpDivide:
614 vl = FcConfigEvaluate (p, e->u.tree.left);
615 vr = FcConfigEvaluate (p, e->u.tree.right);
616 vl = FcConfigPromote (vl, vr);
617 vr = FcConfigPromote (vr, vl);
618 if (vl.type == vr.type)
619 {
620 switch (vl.type) {
621 case FcTypeDouble:
622 switch (e->op) {
623 case FcOpPlus:
624 v.type = FcTypeDouble;
625 v.u.d = vl.u.d + vr.u.d;
626 break;
627 case FcOpMinus:
628 v.type = FcTypeDouble;
629 v.u.d = vl.u.d - vr.u.d;
630 break;
631 case FcOpTimes:
632 v.type = FcTypeDouble;
633 v.u.d = vl.u.d * vr.u.d;
634 break;
635 case FcOpDivide:
636 v.type = FcTypeDouble;
637 v.u.d = vl.u.d / vr.u.d;
638 break;
639 case FcOpEqual:
640 case FcOpContains:
641 v.type = FcTypeBool;
642 v.u.b = vl.u.d == vr.u.d;
643 break;
644 case FcOpNotEqual:
645 v.type = FcTypeBool;
646 v.u.b = vl.u.d != vr.u.d;
647 break;
648 case FcOpLess:
649 v.type = FcTypeBool;
650 v.u.b = vl.u.d < vr.u.d;
651 break;
652 case FcOpLessEqual:
653 v.type = FcTypeBool;
654 v.u.b = vl.u.d <= vr.u.d;
655 break;
656 case FcOpMore:
657 v.type = FcTypeBool;
658 v.u.b = vl.u.d > vr.u.d;
659 break;
660 case FcOpMoreEqual:
661 v.type = FcTypeBool;
662 v.u.b = vl.u.d >= vr.u.d;
663 break;
664 default:
665 v.type = FcTypeVoid;
666 break;
667 }
668 if (v.type == FcTypeDouble &&
669 v.u.d == (double) (int) v.u.d)
670 {
671 v.type = FcTypeInteger;
672 v.u.i = (int) v.u.d;
673 }
674 break;
675 case FcTypeBool:
676 switch (e->op) {
677 case FcOpOr:
678 v.type = FcTypeBool;
679 v.u.b = vl.u.b || vr.u.b;
680 break;
681 case FcOpAnd:
682 v.type = FcTypeBool;
683 v.u.b = vl.u.b && vr.u.b;
684 break;
685 case FcOpEqual:
686 case FcOpContains:
687 v.type = FcTypeBool;
688 v.u.b = vl.u.b == vr.u.b;
689 break;
690 case FcOpNotEqual:
691 v.type = FcTypeBool;
692 v.u.b = vl.u.b != vr.u.b;
693 break;
694 default:
695 v.type = FcTypeVoid;
696 break;
697 }
698 break;
699 case FcTypeString:
700 switch (e->op) {
701 case FcOpEqual:
702 case FcOpContains:
703 v.type = FcTypeBool;
704 v.u.b = FcStrCmpIgnoreCase (vl.u.s, vr.u.s) == 0;
705 break;
706 case FcOpNotEqual:
707 v.type = FcTypeBool;
708 v.u.b = FcStrCmpIgnoreCase (vl.u.s, vr.u.s) != 0;
709 break;
710 case FcOpPlus:
711 v.type = FcTypeString;
712 v.u.s = FcStrPlus (vl.u.s, vr.u.s);
713 if (!v.u.s)
714 v.type = FcTypeVoid;
715 break;
716 default:
717 v.type = FcTypeVoid;
718 break;
719 }
720 case FcTypeMatrix:
721 switch (e->op) {
722 case FcOpEqual:
723 case FcOpContains:
724 v.type = FcTypeBool;
725 v.u.b = FcMatrixEqual (vl.u.m, vr.u.m);
726 break;
727 case FcOpNotEqual:
728 v.type = FcTypeBool;
729 v.u.b = FcMatrixEqual (vl.u.m, vr.u.m);
730 break;
731 case FcOpTimes:
732 v.type = FcTypeMatrix;
733 m = malloc (sizeof (FcMatrix));
734 if (m)
735 {
736 FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
737 FcMatrixMultiply (m, vl.u.m, vr.u.m);
738 v.u.m = m;
739 }
740 else
741 {
742 v.type = FcTypeVoid;
743 }
744 break;
745 default:
746 v.type = FcTypeVoid;
747 break;
748 }
749 break;
750 case FcTypeCharSet:
751 switch (e->op) {
752 case FcOpContains:
753 /* vl contains vr if vr - vl is empty */
754 v.type = FcTypeBool;
755 v.u.b = FcCharSetSubtractCount (vr.u.c, vl.u.c) == 0;
756 break;
757 case FcOpEqual:
758 v.type = FcTypeBool;
759 v.u.b = FcCharSetEqual (vl.u.c, vr.u.c);
760 break;
761 case FcOpNotEqual:
762 v.type = FcTypeBool;
763 v.u.b = !FcCharSetEqual (vl.u.c, vr.u.c);
764 break;
765 default:
766 v.type = FcTypeVoid;
767 break;
768 }
769 break;
770 default:
771 v.type = FcTypeVoid;
772 break;
773 }
774 }
775 else
776 v.type = FcTypeVoid;
777 FcValueDestroy (vl);
778 FcValueDestroy (vr);
779 break;
780 case FcOpNot:
781 vl = FcConfigEvaluate (p, e->u.tree.left);
782 switch (vl.type) {
783 case FcTypeBool:
784 v.type = FcTypeBool;
785 v.u.b = !vl.u.b;
786 break;
787 default:
788 v.type = FcTypeVoid;
789 break;
790 }
791 FcValueDestroy (vl);
792 break;
793 default:
794 v.type = FcTypeVoid;
795 break;
796 }
797 return v;
798 }
799
800 static FcValueList *
801 FcConfigMatchValueList (FcPattern *p,
802 FcTest *t,
803 FcValueList *v)
804 {
805 FcValueList *ret = 0;
806 FcValue value = FcConfigEvaluate (p, t->expr);
807
808 for (; v; v = v->next)
809 {
810 if (FcConfigCompareValue (v->value, t->op, value))
811 {
812 if (!ret)
813 ret = v;
814 }
815 else
816 {
817 if (t->qual == FcQualAll)
818 {
819 ret = 0;
820 break;
821 }
822 }
823 }
824 FcValueDestroy (value);
825 return ret;
826 }
827
828 static FcValueList *
829 FcConfigValues (FcPattern *p, FcExpr *e)
830 {
831 FcValueList *l;
832
833 if (!e)
834 return 0;
835 l = (FcValueList *) malloc (sizeof (FcValueList));
836 if (!l)
837 return 0;
838 FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
839 if (e->op == FcOpComma)
840 {
841 l->value = FcConfigEvaluate (p, e->u.tree.left);
842 l->next = FcConfigValues (p, e->u.tree.right);
843 }
844 else
845 {
846 l->value = FcConfigEvaluate (p, e);
847 l->next = 0;
848 }
849 while (l->value.type == FcTypeVoid)
850 {
851 FcValueList *next = l->next;
852
853 FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
854 free (l);
855 l = next;
856 }
857 return l;
858 }
859
860 static FcBool
861 FcConfigAdd (FcValueList **head,
862 FcValueList *position,
863 FcBool append,
864 FcValueList *new)
865 {
866 FcValueList **prev, *last;
867
868 if (append)
869 {
870 if (position)
871 prev = &position->next;
872 else
873 for (prev = head; *prev; prev = &(*prev)->next)
874 ;
875 }
876 else
877 {
878 if (position)
879 {
880 for (prev = head; *prev; prev = &(*prev)->next)
881 {
882 if (*prev == position)
883 break;
884 }
885 }
886 else
887 prev = head;
888
889 if (FcDebug () & FC_DBG_EDIT)
890 {
891 if (!*prev)
892 printf ("position not on list\n");
893 }
894 }
895
896 if (FcDebug () & FC_DBG_EDIT)
897 {
898 printf ("%s list before ", append ? "Append" : "Prepend");
899 FcValueListPrint (*head);
900 printf ("\n");
901 }
902
903 if (new)
904 {
905 last = new;
906 while (last->next)
907 last = last->next;
908
909 last->next = *prev;
910 *prev = new;
911 }
912
913 if (FcDebug () & FC_DBG_EDIT)
914 {
915 printf ("%s list after ", append ? "Append" : "Prepend");
916 FcValueListPrint (*head);
917 printf ("\n");
918 }
919
920 return FcTrue;
921 }
922
923 static void
924 FcConfigDel (FcValueList **head,
925 FcValueList *position)
926 {
927 FcValueList **prev;
928
929 for (prev = head; *prev; prev = &(*prev)->next)
930 {
931 if (*prev == position)
932 {
933 *prev = position->next;
934 position->next = 0;
935 FcValueListDestroy (position);
936 break;
937 }
938 }
939 }
940
941 static void
942 FcConfigPatternAdd (FcPattern *p,
943 const char *object,
944 FcValueList *list,
945 FcBool append)
946 {
947 if (list)
948 {
949 FcPatternElt *e = FcPatternFind (p, object, FcTrue);
950
951 if (!e)
952 return;
953 FcConfigAdd (&e->values, 0, append, list);
954 }
955 }
956
957 /*
958 * Delete all values associated with a field
959 */
960 static void
961 FcConfigPatternDel (FcPattern *p,
962 const char *object)
963 {
964 FcPatternElt *e = FcPatternFind (p, object, FcFalse);
965 if (!e)
966 return;
967 while (e->values)
968 FcConfigDel (&e->values, e->values);
969 }
970
971 static void
972 FcConfigPatternCanon (FcPattern *p,
973 const char *object)
974 {
975 FcPatternElt *e = FcPatternFind (p, object, FcFalse);
976 if (!e)
977 return;
978 if (!e->values)
979 FcPatternDel (p, object);
980 }
981
982 FcBool
983 FcConfigSubstitute (FcConfig *config,
984 FcPattern *p,
985 FcMatchKind kind)
986 {
987 FcSubst *s;
988 FcSubState *st;
989 int i;
990 FcTest *t;
991 FcEdit *e;
992 FcValueList *l;
993
994 if (!config)
995 {
996 config = FcConfigGetCurrent ();
997 if (!config)
998 return FcFalse;
999 }
1000
1001 st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1002 if (!st && config->maxObjects)
1003 return FcFalse;
1004 FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1005
1006 if (FcDebug () & FC_DBG_EDIT)
1007 {
1008 printf ("FcConfigSubstitute ");
1009 FcPatternPrint (p);
1010 }
1011 if (kind == FcMatchPattern)
1012 s = config->substPattern;
1013 else
1014 s = config->substFont;
1015 for (; s; s = s->next)
1016 {
1017 /*
1018 * Check the tests to see if
1019 * they all match the pattern
1020 */
1021 for (t = s->test, i = 0; t; t = t->next, i++)
1022 {
1023 if (FcDebug () & FC_DBG_EDIT)
1024 {
1025 printf ("FcConfigSubstitute test ");
1026 FcTestPrint (t);
1027 }
1028 st[i].elt = FcPatternFind (p, t->field, FcFalse);
1029 /*
1030 * If there's no such field in the font,
1031 * then FcQualAll matches while FcQualAny does not
1032 */
1033 if (!st[i].elt)
1034 {
1035 if (t->qual == FcQualAll)
1036 {
1037 st[i].value = 0;
1038 continue;
1039 }
1040 else
1041 break;
1042 }
1043 /*
1044 * Check to see if there is a match, mark the location
1045 * to apply match-relative edits
1046 */
1047 st[i].value = FcConfigMatchValueList (p, t, st[i].elt->values);
1048 if (!st[i].value)
1049 break;
1050 }
1051 if (t)
1052 {
1053 if (FcDebug () & FC_DBG_EDIT)
1054 printf ("No match\n");
1055 continue;
1056 }
1057 if (FcDebug () & FC_DBG_EDIT)
1058 {
1059 printf ("Substitute ");
1060 FcSubstPrint (s);
1061 }
1062 for (e = s->edit; e; e = e->next)
1063 {
1064 /*
1065 * Evaluate the list of expressions
1066 */
1067 l = FcConfigValues (p, e->expr);
1068 /*
1069 * Locate any test associated with this field
1070 */
1071 for (t = s->test, i = 0; t; t = t->next, i++)
1072 if (!FcStrCmpIgnoreCase ((FcChar8 *) t->field, (FcChar8 *) e->field))
1073 break;
1074 switch (e->op) {
1075 case FcOpAssign:
1076 /*
1077 * If there was a test, then replace the matched
1078 * value with the new list of values
1079 */
1080 if (t)
1081 {
1082 FcValueList *thisValue = st[i].value;
1083 FcValueList *nextValue = thisValue ? thisValue->next : 0;
1084
1085 /*
1086 * Append the new list of values after the current value
1087 */
1088 FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1089 /*
1090 * Delete the marked value
1091 */
1092 FcConfigDel (&st[i].elt->values, thisValue);
1093 /*
1094 * Adjust any pointers into the value list to ensure
1095 * future edits occur at the same place
1096 */
1097 for (t = s->test, i = 0; t; t = t->next, i++)
1098 {
1099 if (st[i].value == thisValue)
1100 st[i].value = nextValue;
1101 }
1102 break;
1103 }
1104 /* fall through ... */
1105 case FcOpAssignReplace:
1106 /*
1107 * Delete all of the values and insert
1108 * the new set
1109 */
1110 FcConfigPatternDel (p, e->field);
1111 FcConfigPatternAdd (p, e->field, l, FcTrue);
1112 /*
1113 * Adjust any pointers into the value list as they no
1114 * longer point to anything valid
1115 */
1116 if (t)
1117 {
1118 FcPatternElt *thisElt = st[i].elt;
1119 for (t = s->test, i = 0; t; t = t->next, i++)
1120 {
1121 if (st[i].elt == thisElt)
1122 st[i].value = 0;
1123 }
1124 }
1125 break;
1126 case FcOpPrepend:
1127 if (t)
1128 {
1129 FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1130 break;
1131 }
1132 /* fall through ... */
1133 case FcOpPrependFirst:
1134 FcConfigPatternAdd (p, e->field, l, FcFalse);
1135 break;
1136 case FcOpAppend:
1137 if (t)
1138 {
1139 FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1140 break;
1141 }
1142 /* fall through ... */
1143 case FcOpAppendLast:
1144 FcConfigPatternAdd (p, e->field, l, FcTrue);
1145 break;
1146 default:
1147 break;
1148 }
1149 }
1150 /*
1151 * Now go through the pattern and eliminate
1152 * any properties without data
1153 */
1154 for (e = s->edit; e; e = e->next)
1155 FcConfigPatternCanon (p, e->field);
1156
1157 if (FcDebug () & FC_DBG_EDIT)
1158 {
1159 printf ("FcConfigSubstitute edit");
1160 FcPatternPrint (p);
1161 }
1162 }
1163 FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1164 free (st);
1165 if (FcDebug () & FC_DBG_EDIT)
1166 {
1167 printf ("FcConfigSubstitute done");
1168 FcPatternPrint (p);
1169 }
1170 return FcTrue;
1171 }
1172
1173 #ifndef FONTCONFIG_PATH
1174 #define FONTCONFIG_PATH "/etc/fonts"
1175 #endif
1176
1177 #ifndef FONTCONFIG_FILE
1178 #define FONTCONFIG_FILE "fonts.conf"
1179 #endif
1180
1181 static FcChar8 *
1182 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1183 {
1184 FcChar8 *path;
1185
1186 if (!dir)
1187 dir = (FcChar8 *) "";
1188 path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1189 if (!path)
1190 return 0;
1191
1192 strcpy (path, dir);
1193 /* make sure there's a single separating / */
1194 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1195 strcat ((char *) path, "/");
1196 strcat ((char *) path, (char *) file);
1197
1198 if (access ((char *) path, R_OK) == 0)
1199 return path;
1200
1201 free (path);
1202 return 0;
1203 }
1204
1205 static FcChar8 **
1206 FcConfigGetPath (void)
1207 {
1208 FcChar8 **path;
1209 FcChar8 *env, *e, *colon;
1210 FcChar8 *dir;
1211 int npath;
1212 int i;
1213
1214 npath = 2; /* default dir + null */
1215 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1216 if (env)
1217 {
1218 e = env;
1219 npath++;
1220 while (*e)
1221 if (*e++ == ':')
1222 npath++;
1223 }
1224 path = calloc (npath, sizeof (FcChar8 *));
1225 if (!path)
1226 goto bail0;
1227 i = 0;
1228
1229 if (env)
1230 {
1231 e = env;
1232 while (*e)
1233 {
1234 colon = (FcChar8 *) strchr ((char *) e, ':');
1235 if (!colon)
1236 colon = e + strlen ((char *) e);
1237 path[i] = malloc (colon - e + 1);
1238 if (!path[i])
1239 goto bail1;
1240 strncpy (path[i], e, colon - e);
1241 path[i][colon - e] = '\0';
1242 if (*colon)
1243 e = colon + 1;
1244 else
1245 e = colon;
1246 i++;
1247 }
1248 }
1249
1250 dir = (FcChar8 *) FONTCONFIG_PATH;
1251 path[i] = malloc (strlen ((char *) dir) + 1);
1252 if (!path[i])
1253 goto bail1;
1254 strcpy (path[i], dir);
1255 return path;
1256
1257 bail1:
1258 for (i = 0; path[i]; i++)
1259 free (path[i]);
1260 free (path);
1261 bail0:
1262 return 0;
1263 }
1264
1265 static void
1266 FcConfigFreePath (FcChar8 **path)
1267 {
1268 FcChar8 **p;
1269
1270 for (p = path; *p; p++)
1271 free (*p);
1272 free (path);
1273 }
1274
1275 FcChar8 *
1276 FcConfigFilename (const FcChar8 *url)
1277 {
1278 FcChar8 *file, *dir, **path, **p;
1279
1280 if (!url || !*url)
1281 {
1282 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1283 if (!url)
1284 url = (FcChar8 *) FONTCONFIG_FILE;
1285 }
1286 file = 0;
1287 switch (*url) {
1288 case '~':
1289 dir = (FcChar8 *) 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 FcChar8 *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 FcChar8 *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 }