]> git.wh0rd.org - fontconfig.git/blob - src/fccfg.c
Pass FONTCONFIG_PATH in arguments to get expanded
[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 #define _FcDoubleFloor(d) ((int) (d))
652 #define _FcDoubleCeil(d) ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
653 #define FcDoubleFloor(d) ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
654 #define FcDoubleCeil(d) ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
655 #define FcDoubleRound(d) FcDoubleFloor ((d) + 0.5)
656 #define FcDoubleTrunc(d) ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
657
658 static FcValue
659 FcConfigEvaluate (FcPattern *p, FcExpr *e)
660 {
661 FcValue v, vl, vr;
662 FcResult r;
663 FcMatrix *m;
664
665 switch (e->op) {
666 case FcOpInteger:
667 v.type = FcTypeInteger;
668 v.u.i = e->u.ival;
669 break;
670 case FcOpDouble:
671 v.type = FcTypeDouble;
672 v.u.d = e->u.dval;
673 break;
674 case FcOpString:
675 v.type = FcTypeString;
676 v.u.s = e->u.sval;
677 v = FcValueSave (v);
678 break;
679 case FcOpMatrix:
680 v.type = FcTypeMatrix;
681 v.u.m = e->u.mval;
682 v = FcValueSave (v);
683 break;
684 case FcOpCharSet:
685 v.type = FcTypeCharSet;
686 v.u.c = e->u.cval;
687 v = FcValueSave (v);
688 break;
689 case FcOpBool:
690 v.type = FcTypeBool;
691 v.u.b = e->u.bval;
692 break;
693 case FcOpField:
694 r = FcPatternGet (p, e->u.field, 0, &v);
695 if (r != FcResultMatch)
696 v.type = FcTypeVoid;
697 break;
698 case FcOpConst:
699 if (FcNameConstant (e->u.constant, &v.u.i))
700 v.type = FcTypeInteger;
701 else
702 v.type = FcTypeVoid;
703 break;
704 case FcOpQuest:
705 vl = FcConfigEvaluate (p, e->u.tree.left);
706 if (vl.type == FcTypeBool)
707 {
708 if (vl.u.b)
709 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
710 else
711 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
712 }
713 else
714 v.type = FcTypeVoid;
715 FcValueDestroy (vl);
716 break;
717 case FcOpEqual:
718 case FcOpNotEqual:
719 case FcOpLess:
720 case FcOpLessEqual:
721 case FcOpMore:
722 case FcOpMoreEqual:
723 case FcOpContains:
724 case FcOpNotContains:
725 vl = FcConfigEvaluate (p, e->u.tree.left);
726 vr = FcConfigEvaluate (p, e->u.tree.right);
727 v.type = FcTypeBool;
728 v.u.b = FcConfigCompareValue (vl, e->op, vr);
729 FcValueDestroy (vl);
730 FcValueDestroy (vr);
731 break;
732 case FcOpOr:
733 case FcOpAnd:
734 case FcOpPlus:
735 case FcOpMinus:
736 case FcOpTimes:
737 case FcOpDivide:
738 vl = FcConfigEvaluate (p, e->u.tree.left);
739 vr = FcConfigEvaluate (p, e->u.tree.right);
740 vl = FcConfigPromote (vl, vr);
741 vr = FcConfigPromote (vr, vl);
742 if (vl.type == vr.type)
743 {
744 switch (vl.type) {
745 case FcTypeDouble:
746 switch (e->op) {
747 case FcOpPlus:
748 v.type = FcTypeDouble;
749 v.u.d = vl.u.d + vr.u.d;
750 break;
751 case FcOpMinus:
752 v.type = FcTypeDouble;
753 v.u.d = vl.u.d - vr.u.d;
754 break;
755 case FcOpTimes:
756 v.type = FcTypeDouble;
757 v.u.d = vl.u.d * vr.u.d;
758 break;
759 case FcOpDivide:
760 v.type = FcTypeDouble;
761 v.u.d = vl.u.d / vr.u.d;
762 break;
763 default:
764 v.type = FcTypeVoid;
765 break;
766 }
767 if (v.type == FcTypeDouble &&
768 v.u.d == (double) (int) v.u.d)
769 {
770 v.type = FcTypeInteger;
771 v.u.i = (int) v.u.d;
772 }
773 break;
774 case FcTypeBool:
775 switch (e->op) {
776 case FcOpOr:
777 v.type = FcTypeBool;
778 v.u.b = vl.u.b || vr.u.b;
779 break;
780 case FcOpAnd:
781 v.type = FcTypeBool;
782 v.u.b = vl.u.b && vr.u.b;
783 break;
784 default:
785 v.type = FcTypeVoid;
786 break;
787 }
788 break;
789 case FcTypeString:
790 switch (e->op) {
791 case FcOpPlus:
792 v.type = FcTypeString;
793 v.u.s = FcStrPlus (vl.u.s, vr.u.s);
794 if (!v.u.s)
795 v.type = FcTypeVoid;
796 break;
797 default:
798 v.type = FcTypeVoid;
799 break;
800 }
801 break;
802 case FcTypeMatrix:
803 switch (e->op) {
804 case FcOpTimes:
805 v.type = FcTypeMatrix;
806 m = malloc (sizeof (FcMatrix));
807 if (m)
808 {
809 FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
810 FcMatrixMultiply (m, vl.u.m, vr.u.m);
811 v.u.m = m;
812 }
813 else
814 {
815 v.type = FcTypeVoid;
816 }
817 break;
818 default:
819 v.type = FcTypeVoid;
820 break;
821 }
822 break;
823 default:
824 v.type = FcTypeVoid;
825 break;
826 }
827 }
828 else
829 v.type = FcTypeVoid;
830 FcValueDestroy (vl);
831 FcValueDestroy (vr);
832 break;
833 case FcOpNot:
834 vl = FcConfigEvaluate (p, e->u.tree.left);
835 switch (vl.type) {
836 case FcTypeBool:
837 v.type = FcTypeBool;
838 v.u.b = !vl.u.b;
839 break;
840 default:
841 v.type = FcTypeVoid;
842 break;
843 }
844 FcValueDestroy (vl);
845 break;
846 case FcOpFloor:
847 vl = FcConfigEvaluate (p, e->u.tree.left);
848 switch (vl.type) {
849 case FcTypeInteger:
850 v = vl;
851 break;
852 case FcTypeDouble:
853 v.type = FcTypeInteger;
854 v.u.i = FcDoubleFloor (vl.u.d);
855 break;
856 default:
857 v.type = FcTypeVoid;
858 break;
859 }
860 FcValueDestroy (vl);
861 break;
862 case FcOpCeil:
863 vl = FcConfigEvaluate (p, e->u.tree.left);
864 switch (vl.type) {
865 case FcTypeInteger:
866 v = vl;
867 break;
868 case FcTypeDouble:
869 v.type = FcTypeInteger;
870 v.u.i = FcDoubleCeil (vl.u.d);
871 break;
872 default:
873 v.type = FcTypeVoid;
874 break;
875 }
876 FcValueDestroy (vl);
877 break;
878 case FcOpRound:
879 vl = FcConfigEvaluate (p, e->u.tree.left);
880 switch (vl.type) {
881 case FcTypeInteger:
882 v = vl;
883 break;
884 case FcTypeDouble:
885 v.type = FcTypeInteger;
886 v.u.i = FcDoubleRound (vl.u.d);
887 break;
888 default:
889 v.type = FcTypeVoid;
890 break;
891 }
892 FcValueDestroy (vl);
893 break;
894 case FcOpTrunc:
895 vl = FcConfigEvaluate (p, e->u.tree.left);
896 switch (vl.type) {
897 case FcTypeInteger:
898 v = vl;
899 break;
900 case FcTypeDouble:
901 v.type = FcTypeInteger;
902 v.u.i = FcDoubleTrunc (vl.u.d);
903 break;
904 default:
905 v.type = FcTypeVoid;
906 break;
907 }
908 FcValueDestroy (vl);
909 break;
910 default:
911 v.type = FcTypeVoid;
912 break;
913 }
914 return v;
915 }
916
917 static FcValueList *
918 FcConfigMatchValueList (FcPattern *p,
919 FcTest *t,
920 FcValueList *values)
921 {
922 FcValueList *ret = 0;
923 FcExpr *e = t->expr;
924 FcValue value;
925 FcValueList *v;
926
927 while (e)
928 {
929 if (e->op == FcOpComma)
930 {
931 value = FcConfigEvaluate (p, e->u.tree.left);
932 e = e->u.tree.right;
933 }
934 else
935 {
936 value = FcConfigEvaluate (p, e);
937 e = 0;
938 }
939
940 for (v = values; v; v = v->next)
941 {
942 if (FcConfigCompareValue (v->value, t->op, value))
943 {
944 if (!ret)
945 ret = v;
946 }
947 else
948 {
949 if (t->qual == FcQualAll)
950 {
951 ret = 0;
952 break;
953 }
954 }
955 }
956 FcValueDestroy (value);
957 }
958 return ret;
959 }
960
961 static FcValueList *
962 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
963 {
964 FcValueList *l;
965
966 if (!e)
967 return 0;
968 l = (FcValueList *) malloc (sizeof (FcValueList));
969 if (!l)
970 return 0;
971 FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
972 if (e->op == FcOpComma)
973 {
974 l->value = FcConfigEvaluate (p, e->u.tree.left);
975 l->next = FcConfigValues (p, e->u.tree.right, binding);
976 }
977 else
978 {
979 l->value = FcConfigEvaluate (p, e);
980 l->next = 0;
981 }
982 l->binding = binding;
983 while (l && l->value.type == FcTypeVoid)
984 {
985 FcValueList *next = l->next;
986
987 FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
988 free (l);
989 l = next;
990 }
991 return l;
992 }
993
994 static FcBool
995 FcConfigAdd (FcValueList **head,
996 FcValueList *position,
997 FcBool append,
998 FcValueList *new)
999 {
1000 FcValueList **prev, *last, *v;
1001 FcValueBinding sameBinding;
1002
1003 if (position)
1004 sameBinding = position->binding;
1005 else
1006 sameBinding = FcValueBindingWeak;
1007 for (v = new; v; v = v->next)
1008 if (v->binding == FcValueBindingSame)
1009 v->binding = sameBinding;
1010 if (append)
1011 {
1012 if (position)
1013 prev = &position->next;
1014 else
1015 for (prev = head; *prev; prev = &(*prev)->next)
1016 ;
1017 }
1018 else
1019 {
1020 if (position)
1021 {
1022 for (prev = head; *prev; prev = &(*prev)->next)
1023 {
1024 if (*prev == position)
1025 break;
1026 }
1027 }
1028 else
1029 prev = head;
1030
1031 if (FcDebug () & FC_DBG_EDIT)
1032 {
1033 if (!*prev)
1034 printf ("position not on list\n");
1035 }
1036 }
1037
1038 if (FcDebug () & FC_DBG_EDIT)
1039 {
1040 printf ("%s list before ", append ? "Append" : "Prepend");
1041 FcValueListPrint (*head);
1042 printf ("\n");
1043 }
1044
1045 if (new)
1046 {
1047 last = new;
1048 while (last->next)
1049 last = last->next;
1050
1051 last->next = *prev;
1052 *prev = new;
1053 }
1054
1055 if (FcDebug () & FC_DBG_EDIT)
1056 {
1057 printf ("%s list after ", append ? "Append" : "Prepend");
1058 FcValueListPrint (*head);
1059 printf ("\n");
1060 }
1061
1062 return FcTrue;
1063 }
1064
1065 static void
1066 FcConfigDel (FcValueList **head,
1067 FcValueList *position)
1068 {
1069 FcValueList **prev;
1070
1071 for (prev = head; *prev; prev = &(*prev)->next)
1072 {
1073 if (*prev == position)
1074 {
1075 *prev = position->next;
1076 position->next = 0;
1077 FcValueListDestroy (position);
1078 break;
1079 }
1080 }
1081 }
1082
1083 static void
1084 FcConfigPatternAdd (FcPattern *p,
1085 const char *object,
1086 FcValueList *list,
1087 FcBool append)
1088 {
1089 if (list)
1090 {
1091 FcPatternElt *e = FcPatternInsertElt (p, object);
1092
1093 if (!e)
1094 return;
1095 FcConfigAdd (&e->values, 0, append, list);
1096 }
1097 }
1098
1099 /*
1100 * Delete all values associated with a field
1101 */
1102 static void
1103 FcConfigPatternDel (FcPattern *p,
1104 const char *object)
1105 {
1106 FcPatternElt *e = FcPatternFindElt (p, object);
1107 if (!e)
1108 return;
1109 while (e->values)
1110 FcConfigDel (&e->values, e->values);
1111 }
1112
1113 static void
1114 FcConfigPatternCanon (FcPattern *p,
1115 const char *object)
1116 {
1117 FcPatternElt *e = FcPatternFindElt (p, object);
1118 if (!e)
1119 return;
1120 if (!e->values)
1121 FcPatternDel (p, object);
1122 }
1123
1124 FcBool
1125 FcConfigSubstituteWithPat (FcConfig *config,
1126 FcPattern *p,
1127 FcPattern *p_pat,
1128 FcMatchKind kind)
1129 {
1130 FcSubst *s;
1131 FcSubState *st;
1132 int i;
1133 FcTest *t;
1134 FcEdit *e;
1135 FcValueList *l;
1136 FcPattern *m;
1137
1138 if (!config)
1139 {
1140 config = FcConfigGetCurrent ();
1141 if (!config)
1142 return FcFalse;
1143 }
1144
1145 st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1146 if (!st && config->maxObjects)
1147 return FcFalse;
1148 FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1149
1150 if (FcDebug () & FC_DBG_EDIT)
1151 {
1152 printf ("FcConfigSubstitute ");
1153 FcPatternPrint (p);
1154 }
1155 if (kind == FcMatchPattern)
1156 s = config->substPattern;
1157 else
1158 s = config->substFont;
1159 for (; s; s = s->next)
1160 {
1161 /*
1162 * Check the tests to see if
1163 * they all match the pattern
1164 */
1165 for (t = s->test, i = 0; t; t = t->next, i++)
1166 {
1167 if (FcDebug () & FC_DBG_EDIT)
1168 {
1169 printf ("FcConfigSubstitute test ");
1170 FcTestPrint (t);
1171 }
1172 st[i].elt = 0;
1173 if (kind == FcMatchFont && t->kind == FcMatchPattern)
1174 m = p_pat;
1175 else
1176 m = p;
1177 if (m)
1178 st[i].elt = FcPatternFindElt (m, t->field);
1179 else
1180 st[i].elt = 0;
1181 /*
1182 * If there's no such field in the font,
1183 * then FcQualAll matches while FcQualAny does not
1184 */
1185 if (!st[i].elt)
1186 {
1187 if (t->qual == FcQualAll)
1188 {
1189 st[i].value = 0;
1190 continue;
1191 }
1192 else
1193 break;
1194 }
1195 /*
1196 * Check to see if there is a match, mark the location
1197 * to apply match-relative edits
1198 */
1199 st[i].value = FcConfigMatchValueList (m, t, st[i].elt->values);
1200 if (!st[i].value)
1201 break;
1202 if (t->qual == FcQualFirst && st[i].value != st[i].elt->values)
1203 break;
1204 if (t->qual == FcQualNotFirst && st[i].value == st[i].elt->values)
1205 break;
1206 }
1207 if (t)
1208 {
1209 if (FcDebug () & FC_DBG_EDIT)
1210 printf ("No match\n");
1211 continue;
1212 }
1213 if (FcDebug () & FC_DBG_EDIT)
1214 {
1215 printf ("Substitute ");
1216 FcSubstPrint (s);
1217 }
1218 for (e = s->edit; e; e = e->next)
1219 {
1220 /*
1221 * Evaluate the list of expressions
1222 */
1223 l = FcConfigValues (p, e->expr, e->binding);
1224 /*
1225 * Locate any test associated with this field, skipping
1226 * tests associated with the pattern when substituting in
1227 * the font
1228 */
1229 for (t = s->test, i = 0; t; t = t->next, i++)
1230 {
1231 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1232 !FcStrCmpIgnoreCase ((FcChar8 *) t->field,
1233 (FcChar8 *) e->field))
1234 {
1235 if (!st[i].elt)
1236 t = 0;
1237 break;
1238 }
1239 }
1240 switch (e->op) {
1241 case FcOpAssign:
1242 /*
1243 * If there was a test, then replace the matched
1244 * value with the new list of values
1245 */
1246 if (t)
1247 {
1248 FcValueList *thisValue = st[i].value;
1249 FcValueList *nextValue = thisValue ? thisValue->next : 0;
1250
1251 /*
1252 * Append the new list of values after the current value
1253 */
1254 FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1255 /*
1256 * Delete the marked value
1257 */
1258 FcConfigDel (&st[i].elt->values, thisValue);
1259 /*
1260 * Adjust any pointers into the value list to ensure
1261 * future edits occur at the same place
1262 */
1263 for (t = s->test, i = 0; t; t = t->next, i++)
1264 {
1265 if (st[i].value == thisValue)
1266 st[i].value = nextValue;
1267 }
1268 break;
1269 }
1270 /* fall through ... */
1271 case FcOpAssignReplace:
1272 /*
1273 * Delete all of the values and insert
1274 * the new set
1275 */
1276 FcConfigPatternDel (p, e->field);
1277 FcConfigPatternAdd (p, e->field, l, FcTrue);
1278 /*
1279 * Adjust any pointers into the value list as they no
1280 * longer point to anything valid
1281 */
1282 if (t)
1283 {
1284 FcPatternElt *thisElt = st[i].elt;
1285 for (t = s->test, i = 0; t; t = t->next, i++)
1286 {
1287 if (st[i].elt == thisElt)
1288 st[i].value = 0;
1289 }
1290 }
1291 break;
1292 case FcOpPrepend:
1293 if (t)
1294 {
1295 FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1296 break;
1297 }
1298 /* fall through ... */
1299 case FcOpPrependFirst:
1300 FcConfigPatternAdd (p, e->field, l, FcFalse);
1301 break;
1302 case FcOpAppend:
1303 if (t)
1304 {
1305 FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1306 break;
1307 }
1308 /* fall through ... */
1309 case FcOpAppendLast:
1310 FcConfigPatternAdd (p, e->field, l, FcTrue);
1311 break;
1312 default:
1313 break;
1314 }
1315 }
1316 /*
1317 * Now go through the pattern and eliminate
1318 * any properties without data
1319 */
1320 for (e = s->edit; e; e = e->next)
1321 FcConfigPatternCanon (p, e->field);
1322
1323 if (FcDebug () & FC_DBG_EDIT)
1324 {
1325 printf ("FcConfigSubstitute edit");
1326 FcPatternPrint (p);
1327 }
1328 }
1329 FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1330 free (st);
1331 if (FcDebug () & FC_DBG_EDIT)
1332 {
1333 printf ("FcConfigSubstitute done");
1334 FcPatternPrint (p);
1335 }
1336 return FcTrue;
1337 }
1338
1339 FcBool
1340 FcConfigSubstitute (FcConfig *config,
1341 FcPattern *p,
1342 FcMatchKind kind)
1343 {
1344 return FcConfigSubstituteWithPat (config, p, 0, kind);
1345 }
1346
1347 #if defined (_WIN32) && defined (PIC)
1348
1349 static FcChar8 fontconfig_path[1000] = "";
1350
1351 BOOL WINAPI
1352 DllMain (HINSTANCE hinstDLL,
1353 DWORD fdwReason,
1354 LPVOID lpvReserved)
1355 {
1356 FcChar8 *p;
1357
1358 switch (fdwReason) {
1359 case DLL_PROCESS_ATTACH:
1360 if (!GetModuleFileName ((HMODULE) hinstDLL, fontconfig_path,
1361 sizeof (fontconfig_path)))
1362 break;
1363
1364 /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
1365 * assume it's a Unix-style installation tree, and use
1366 * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
1367 * folder where the DLL is as FONTCONFIG_PATH.
1368 */
1369 p = strrchr (fontconfig_path, '\\');
1370 if (p)
1371 {
1372 *p = '\0';
1373 p = strrchr (fontconfig_path, '\\');
1374 if (p && (FcStrCmpIgnoreCase (p + 1, "bin") == 0 ||
1375 FcStrCmpIgnoreCase (p + 1, "lib") == 0))
1376 *p = '\0';
1377 strcat (fontconfig_path, "\\etc\\fonts");
1378 }
1379 else
1380 fontconfig_path[0] = '\0';
1381
1382 break;
1383 }
1384
1385 return TRUE;
1386 }
1387
1388 #undef FONTCONFIG_PATH
1389 #define FONTCONFIG_PATH fontconfig_path
1390
1391 #else /* !(_WIN32 && PIC) */
1392
1393 #endif /* !(_WIN32 && PIC) */
1394
1395 #ifndef FONTCONFIG_FILE
1396 #define FONTCONFIG_FILE "fonts.conf"
1397 #endif
1398
1399 static FcChar8 *
1400 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1401 {
1402 FcChar8 *path;
1403
1404 if (!dir)
1405 dir = (FcChar8 *) "";
1406 path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1407 if (!path)
1408 return 0;
1409
1410 strcpy ((char *) path, (const char *) dir);
1411 /* make sure there's a single separator */
1412 #ifdef _WIN32
1413 if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
1414 path[strlen((char *) path)-1] != '\\')) &&
1415 (file[0] != '/' && file[0] != '\\'))
1416 strcat ((char *) path, "\\");
1417 #else
1418 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1419 strcat ((char *) path, "/");
1420 #endif
1421 strcat ((char *) path, (char *) file);
1422
1423 FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1424 if (access ((char *) path, R_OK) == 0)
1425 return path;
1426
1427 FcStrFree (path);
1428 return 0;
1429 }
1430
1431 static FcChar8 **
1432 FcConfigGetPath (void)
1433 {
1434 FcChar8 **path;
1435 FcChar8 *env, *e, *colon;
1436 FcChar8 *dir;
1437 int npath;
1438 int i;
1439
1440 npath = 2; /* default dir + null */
1441 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1442 if (env)
1443 {
1444 e = env;
1445 npath++;
1446 while (*e)
1447 if (*e++ == FC_SEARCH_PATH_SEPARATOR)
1448 npath++;
1449 }
1450 path = calloc (npath, sizeof (FcChar8 *));
1451 if (!path)
1452 goto bail0;
1453 i = 0;
1454
1455 if (env)
1456 {
1457 e = env;
1458 while (*e)
1459 {
1460 colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
1461 if (!colon)
1462 colon = e + strlen ((char *) e);
1463 path[i] = malloc (colon - e + 1);
1464 if (!path[i])
1465 goto bail1;
1466 strncpy ((char *) path[i], (const char *) e, colon - e);
1467 path[i][colon - e] = '\0';
1468 if (*colon)
1469 e = colon + 1;
1470 else
1471 e = colon;
1472 i++;
1473 }
1474 }
1475
1476 dir = (FcChar8 *) FONTCONFIG_PATH;
1477 path[i] = malloc (strlen ((char *) dir) + 1);
1478 if (!path[i])
1479 goto bail1;
1480 strcpy ((char *) path[i], (const char *) dir);
1481 return path;
1482
1483 bail1:
1484 for (i = 0; path[i]; i++)
1485 free (path[i]);
1486 free (path);
1487 bail0:
1488 return 0;
1489 }
1490
1491 static void
1492 FcConfigFreePath (FcChar8 **path)
1493 {
1494 FcChar8 **p;
1495
1496 for (p = path; *p; p++)
1497 free (*p);
1498 free (path);
1499 }
1500
1501 static FcBool _FcConfigHomeEnabled = FcTrue;
1502
1503 FcChar8 *
1504 FcConfigHome (void)
1505 {
1506 if (_FcConfigHomeEnabled)
1507 {
1508 char *home = getenv ("HOME");
1509
1510 #ifdef _WIN32
1511 if (home == NULL)
1512 home = getenv ("USERPROFILE");
1513 #endif
1514
1515 return home;
1516 }
1517 return 0;
1518 }
1519
1520 FcBool
1521 FcConfigEnableHome (FcBool enable)
1522 {
1523 FcBool prev = _FcConfigHomeEnabled;
1524 _FcConfigHomeEnabled = enable;
1525 return prev;
1526 }
1527
1528 FcChar8 *
1529 FcConfigFilename (const FcChar8 *url)
1530 {
1531 FcChar8 *file, *dir, **path, **p;
1532
1533 if (!url || !*url)
1534 {
1535 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1536 if (!url)
1537 url = (FcChar8 *) FONTCONFIG_FILE;
1538 }
1539 file = 0;
1540
1541 #ifdef _WIN32
1542 if (isalpha (*url) &&
1543 url[1] == ':' &&
1544 (url[2] == '/' || url[2] == '\\'))
1545 goto absolute_path;
1546 #endif
1547
1548 switch (*url) {
1549 case '~':
1550 dir = FcConfigHome ();
1551 if (dir)
1552 file = FcConfigFileExists (dir, url + 1);
1553 else
1554 file = 0;
1555 break;
1556 #ifdef _WIN32
1557 case '\\':
1558 absolute_path:
1559 #endif
1560 case '/':
1561 file = FcConfigFileExists (0, url);
1562 break;
1563 default:
1564 path = FcConfigGetPath ();
1565 if (!path)
1566 return 0;
1567 for (p = path; *p; p++)
1568 {
1569 file = FcConfigFileExists (*p, url);
1570 if (file)
1571 break;
1572 }
1573 FcConfigFreePath (path);
1574 break;
1575 }
1576 return file;
1577 }
1578
1579 /*
1580 * Manage the application-specific fonts
1581 */
1582
1583 FcBool
1584 FcConfigAppFontAddFile (FcConfig *config,
1585 const FcChar8 *file)
1586 {
1587 FcFontSet *set;
1588 FcStrSet *subdirs;
1589 FcStrList *sublist;
1590 FcChar8 *subdir;
1591
1592 if (!config)
1593 {
1594 config = FcConfigGetCurrent ();
1595 if (!config)
1596 return FcFalse;
1597 }
1598
1599 subdirs = FcStrSetCreate ();
1600 if (!subdirs)
1601 return FcFalse;
1602
1603 set = FcConfigGetFonts (config, FcSetApplication);
1604 if (!set)
1605 {
1606 set = FcFontSetCreate ();
1607 if (!set)
1608 {
1609 FcStrSetDestroy (subdirs);
1610 return FcFalse;
1611 }
1612 FcConfigSetFonts (config, set, FcSetApplication);
1613 }
1614
1615 if (!FcFileScan (set, subdirs, 0, config->blanks, file, FcFalse))
1616 {
1617 FcStrSetDestroy (subdirs);
1618 return FcFalse;
1619 }
1620 if ((sublist = FcStrListCreate (subdirs)))
1621 {
1622 while ((subdir = FcStrListNext (sublist)))
1623 {
1624 FcConfigAppFontAddDir (config, subdir);
1625 }
1626 FcStrListDone (sublist);
1627 }
1628 return FcTrue;
1629 }
1630
1631 FcBool
1632 FcConfigAppFontAddDir (FcConfig *config,
1633 const FcChar8 *dir)
1634 {
1635 FcFontSet *set;
1636 FcStrSet *subdirs;
1637 FcStrList *sublist;
1638 FcChar8 *subdir;
1639
1640 if (!config)
1641 {
1642 config = FcConfigGetCurrent ();
1643 if (!config)
1644 return FcFalse;
1645 }
1646 subdirs = FcStrSetCreate ();
1647 if (!subdirs)
1648 return FcFalse;
1649
1650 set = FcConfigGetFonts (config, FcSetApplication);
1651 if (!set)
1652 {
1653 set = FcFontSetCreate ();
1654 if (!set)
1655 {
1656 FcStrSetDestroy (subdirs);
1657 return FcFalse;
1658 }
1659 FcConfigSetFonts (config, set, FcSetApplication);
1660 }
1661
1662 if (!FcDirScan (set, subdirs, 0, config->blanks, dir, FcFalse))
1663 {
1664 FcStrSetDestroy (subdirs);
1665 return FcFalse;
1666 }
1667 if ((sublist = FcStrListCreate (subdirs)))
1668 {
1669 while ((subdir = FcStrListNext (sublist)))
1670 {
1671 FcConfigAppFontAddDir (config, subdir);
1672 }
1673 FcStrListDone (sublist);
1674 }
1675 return FcTrue;
1676 }
1677
1678 void
1679 FcConfigAppFontClear (FcConfig *config)
1680 {
1681 FcConfigSetFonts (config, 0, FcSetApplication);
1682 }