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