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