]> git.wh0rd.org - fontconfig.git/blob - src/fccfg.c
Don't rescan when trying to normalize a non-declared font dir. Don't add
[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 <dirent.h>
26 #include <sys/types.h>
27 #include "fcint.h"
28
29 #if defined (_WIN32) && (defined (PIC) || defined (DLL_EXPORT))
30 #define STRICT
31 #include <windows.h>
32 #undef STRICT
33 #endif
34
35 #if defined (_WIN32) && !defined (R_OK)
36 #define R_OK 4
37 #endif
38
39 FcConfig *_fcConfig;
40
41 FcConfig *
42 FcConfigCreate (void)
43 {
44 FcSetName set;
45 FcConfig *config;
46
47 config = malloc (sizeof (FcConfig));
48 if (!config)
49 goto bail0;
50 FcMemAlloc (FC_MEM_CONFIG, sizeof (FcConfig));
51
52 config->configDirs = FcStrSetCreate ();
53 if (!config->configDirs)
54 goto bail1;
55
56 config->configFiles = FcStrSetCreate ();
57 if (!config->configFiles)
58 goto bail2;
59
60 config->fontDirs = FcStrSetCreate ();
61 if (!config->fontDirs)
62 goto bail3;
63
64 config->acceptGlobs = FcStrSetCreate ();
65 if (!config->acceptGlobs)
66 goto bail4;
67
68 config->rejectGlobs = FcStrSetCreate ();
69 if (!config->rejectGlobs)
70 goto bail5;
71
72 config->acceptPatterns = FcFontSetCreate ();
73 if (!config->acceptPatterns)
74 goto bail6;
75
76 config->rejectPatterns = FcFontSetCreate ();
77 if (!config->rejectPatterns)
78 goto bail7;
79
80 config->cache = 0;
81 if (FcConfigHome())
82 if (!FcConfigSetCache (config, (FcChar8 *) ("~/" FC_USER_CACHE_FILE)))
83 goto bail8;
84
85 #ifdef _WIN32
86 if (config->cache == 0)
87 {
88 /* If no home, use the temp folder. */
89 FcChar8 dummy[1];
90 int templen = GetTempPath (1, dummy);
91 FcChar8 *temp = malloc (templen + 1);
92
93 if (temp)
94 {
95 FcChar8 *cache_dir;
96
97 GetTempPath (templen + 1, temp);
98 cache_dir = FcStrPlus (temp, FC_USER_CACHE_FILE);
99 free (temp);
100 if (!FcConfigSetCache (config, cache_dir))
101 {
102 FcStrFree (cache_dir);
103 goto bail6;
104 }
105 FcStrFree (cache_dir);
106 }
107 }
108 #endif
109
110 config->blanks = 0;
111
112 config->substPattern = 0;
113 config->substFont = 0;
114 config->maxObjects = 0;
115 for (set = FcSetSystem; set <= FcSetApplication; set++)
116 config->fonts[set] = 0;
117
118 config->rescanTime = time(0);
119 config->rescanInterval = 30;
120
121 return config;
122
123 bail8:
124 FcFontSetDestroy (config->rejectPatterns);
125 bail7:
126 FcFontSetDestroy (config->acceptPatterns);
127 bail6:
128 FcStrSetDestroy (config->rejectGlobs);
129 bail5:
130 FcStrSetDestroy (config->acceptGlobs);
131 bail4:
132 FcStrSetDestroy (config->fontDirs);
133 bail3:
134 FcStrSetDestroy (config->configFiles);
135 bail2:
136 FcStrSetDestroy (config->configDirs);
137 bail1:
138 free (config);
139 FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
140 bail0:
141 return 0;
142 }
143
144 static FcFileTime
145 FcConfigNewestFile (FcStrSet *files)
146 {
147 FcStrList *list = FcStrListCreate (files);
148 FcFileTime newest = { 0, FcFalse };
149 FcChar8 *file;
150 struct stat statb;
151
152 if (list)
153 {
154 while ((file = FcStrListNext (list)))
155 if (stat ((char *) file, &statb) == 0)
156 if (!newest.set || statb.st_mtime - newest.time > 0)
157 {
158 newest.set = FcTrue;
159 newest.time = statb.st_mtime;
160 }
161 FcStrListDone (list);
162 }
163 return newest;
164 }
165
166 FcFileTime
167 FcConfigModifiedTime (FcConfig *config)
168 {
169 if (!config)
170 {
171 FcFileTime v = { 0, FcFalse };
172 config = FcConfigGetCurrent ();
173 if (!config)
174 return v;
175 }
176 return FcConfigNewestFile (config->configFiles);
177 }
178
179 FcBool
180 FcConfigUptoDate (FcConfig *config)
181 {
182 FcFileTime config_time, font_time;
183 time_t now = time(0);
184 if (!config)
185 {
186 config = FcConfigGetCurrent ();
187 if (!config)
188 return FcFalse;
189 }
190 config_time = FcConfigNewestFile (config->configFiles);
191 font_time = FcConfigNewestFile (config->fontDirs);
192 if ((config_time.set && config_time.time - config->rescanTime > 0) ||
193 (font_time.set && (font_time.time - config->rescanTime) > 0))
194 {
195 return FcFalse;
196 }
197 config->rescanTime = now;
198 return FcTrue;
199 }
200
201 static void
202 FcSubstDestroy (FcSubst *s)
203 {
204 FcSubst *n;
205
206 while (s)
207 {
208 n = s->next;
209 if (s->test)
210 FcTestDestroy (s->test);
211 if (s->edit)
212 FcEditDestroy (s->edit);
213 free (s);
214 FcMemFree (FC_MEM_SUBST, sizeof (FcSubst));
215 s = n;
216 }
217 }
218
219 void
220 FcConfigDestroy (FcConfig *config)
221 {
222 FcSetName set;
223
224 if (config == _fcConfig)
225 _fcConfig = 0;
226
227 FcStrSetDestroy (config->configDirs);
228 FcStrSetDestroy (config->fontDirs);
229 FcStrSetDestroy (config->configFiles);
230 FcStrSetDestroy (config->acceptGlobs);
231 FcStrSetDestroy (config->rejectGlobs);
232 FcFontSetDestroy (config->acceptPatterns);
233 FcFontSetDestroy (config->rejectPatterns);
234
235 if (config->blanks)
236 FcBlanksDestroy (config->blanks);
237
238 if (config->cache)
239 FcStrFree (config->cache);
240
241 FcSubstDestroy (config->substPattern);
242 FcSubstDestroy (config->substFont);
243 for (set = FcSetSystem; set <= FcSetApplication; set++)
244 if (config->fonts[set])
245 FcFontSetDestroy (config->fonts[set]);
246
247 free (config);
248 FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
249 }
250
251 /*
252 * Scan the current list of directories in the configuration
253 * and build the set of available fonts. Update the
254 * per-user cache file to reflect the new configuration
255 */
256
257 FcBool
258 FcConfigBuildFonts (FcConfig *config)
259 {
260 FcFontSet *fonts, *cached_fonts;
261 FcGlobalCache *cache;
262 FcStrList *list;
263 FcStrSet *oldDirs;
264 FcChar8 *dir;
265
266 fonts = FcFontSetCreate ();
267 if (!fonts)
268 goto bail0;
269
270 cache = FcGlobalCacheCreate ();
271 if (!cache)
272 goto bail1;
273
274 oldDirs = FcStrSetCreate ();
275 if (!oldDirs)
276 goto bail2;
277
278 if (config->cache)
279 FcGlobalCacheLoad (cache, oldDirs, config->cache, config);
280
281 cached_fonts = FcCacheRead(config, cache);
282 if (!cached_fonts)
283 {
284 list = FcConfigGetFontDirs (config);
285 if (!list)
286 goto bail2;
287
288 while ((dir = FcStrListNext (list)))
289 {
290 if (FcDebug () & FC_DBG_FONTSET)
291 printf ("build scan dir %s\n", dir);
292 FcDirScanConfig (fonts, config->fontDirs, cache,
293 config->blanks, dir, FcFalse, config);
294 }
295
296 FcStrListDone (list);
297 }
298 else
299 {
300 int i;
301
302 for (i = 0; i < oldDirs->num; i++)
303 {
304 if (FcDebug () & FC_DBG_FONTSET)
305 printf ("scan dir %s\n", oldDirs->strs[i]);
306 FcDirScanConfig (fonts, config->fontDirs, cache,
307 config->blanks, oldDirs->strs[i],
308 FcFalse, config);
309 }
310
311 for (i = 0; i < cached_fonts->nfont; i++)
312 {
313 FcChar8 *cfn;
314 FcPatternGetString (cached_fonts->fonts[i], FC_FILE, 0, &cfn);
315
316 if (FcConfigAcceptFont (config, cached_fonts->fonts[i]) &&
317 (cfn && FcConfigAcceptFilename (config, cfn)))
318 FcFontSetAdd (fonts, cached_fonts->fonts[i]);
319
320 cached_fonts->fonts[i] = 0; /* prevent free in FcFontSetDestroy */
321 }
322 cached_fonts->nfont = 0;
323 FcFontSetDestroy (cached_fonts);
324 }
325
326 if (FcDebug () & FC_DBG_FONTSET)
327 FcFontSetPrint (fonts);
328
329 if (config->cache)
330 FcGlobalCacheSave (cache, config->cache, config);
331 FcGlobalCacheDestroy (cache);
332 FcStrSetDestroy (oldDirs);
333
334 FcConfigSetFonts (config, fonts, FcSetSystem);
335
336 return FcTrue;
337 bail2:
338 FcStrSetDestroy (oldDirs);
339 bail1:
340 FcFontSetDestroy (fonts);
341 bail0:
342 return FcFalse;
343 }
344
345 FcBool
346 FcConfigSetCurrent (FcConfig *config)
347 {
348 if (!config->fonts)
349 if (!FcConfigBuildFonts (config))
350 return FcFalse;
351
352 if (_fcConfig)
353 FcConfigDestroy (_fcConfig);
354 _fcConfig = config;
355 return FcTrue;
356 }
357
358 FcConfig *
359 FcConfigGetCurrent (void)
360 {
361 if (!_fcConfig)
362 if (!FcInit ())
363 return 0;
364 return _fcConfig;
365 }
366
367 FcBool
368 FcConfigAddConfigDir (FcConfig *config,
369 const FcChar8 *d)
370 {
371 return FcStrSetAddFilename (config->configDirs, d);
372 }
373
374 FcStrList *
375 FcConfigGetConfigDirs (FcConfig *config)
376 {
377 if (!config)
378 {
379 config = FcConfigGetCurrent ();
380 if (!config)
381 return 0;
382 }
383 return FcStrListCreate (config->configDirs);
384 }
385
386 static FcChar8 *
387 FcConfigInodeMatchFontDir (FcConfig *config, const FcChar8 *d)
388 {
389 int n;
390 ino_t di;
391 dev_t dd;
392 struct stat s;
393
394 /* first we do string matches rather than file accesses */
395 /* FcStrSetMember doesn't tell us the index so that we can return
396 * the config-owned copy. */
397 for (n = 0; n < config->fontDirs->num; n++)
398 {
399 if (!FcStrCmp (config->fontDirs->strs[n], d))
400 return config->fontDirs->strs[n];
401 }
402
403 /* If this is a bottleneck, we can cache the fontDir inodes. */
404 if (stat ((char *)d, &s) == -1)
405 return 0;
406 di = s.st_ino; dd = s.st_dev;
407
408 for (n = 0; n < config->fontDirs->num; n++)
409 {
410 if (stat ((char *)config->fontDirs->strs[n], &s) == -1)
411 continue;
412 if (di == s.st_ino && dd == s.st_dev)
413 return config->fontDirs->strs[n];
414 }
415 return 0;
416 }
417
418 FcBool
419 FcConfigAddFontDir (FcConfig *config,
420 const FcChar8 *d)
421 {
422 /* Avoid adding d if it's an alias of something else, too. */
423 if (FcConfigInodeMatchFontDir(config, d))
424 return FcTrue;
425 return FcStrSetAddFilename (config->fontDirs, d);
426 }
427
428 static FcBool
429 FcConfigAddFontDirSubdirs (FcConfig *config,
430 const FcChar8 *d)
431 {
432 DIR *dir;
433 struct dirent *e;
434 FcChar8 *subdir;
435 FcBool added = FcFalse;
436
437 if (!(dir = opendir ((char *) d)))
438 return FcFalse;
439 if (!(subdir = (FcChar8 *) malloc (strlen ((char *) d) + FC_MAX_FILE_LEN + 2)))
440 {
441 fprintf (stderr, "out of memory");
442 return FcFalse;
443 }
444 while ((e = readdir (dir)))
445 {
446 if (strcmp (e->d_name, ".") && strcmp (e->d_name, "..") &&
447 strlen (e->d_name) < FC_MAX_FILE_LEN)
448 {
449 strcpy ((char *)subdir, (char *)d);
450 strcat ((char *)subdir, "/");
451 strcat ((char *)subdir, e->d_name);
452 if (FcFileIsDir (subdir))
453 {
454 if (FcConfigInodeMatchFontDir(config, subdir))
455 continue; /* already added */
456 FcStrSetAddFilename (config->fontDirs, subdir);
457 FcConfigAddFontDirSubdirs (config, subdir);
458 added = FcTrue;
459 }
460 }
461 }
462 free (subdir);
463 closedir (dir);
464 return added;
465 }
466
467 const FcChar8 *
468 FcConfigNormalizeFontDir (FcConfig *config,
469 const FcChar8 *d)
470 {
471 FcChar8 *d0;
472 int n, n0;
473 FcBool added = FcFalse;
474
475 d0 = FcConfigInodeMatchFontDir(config, d);
476 if (d0)
477 return d0;
478
479 /* Ok, we didn't find it in fontDirs; let's add subdirs.... */
480 for (n = 0, n0 = config->fontDirs->num; n < n0; n++)
481 {
482 if (FcConfigAddFontDirSubdirs (config, config->fontDirs->strs[n]))
483 added = FcTrue;
484 }
485
486 /* ... and try again. */
487 if (added)
488 return FcConfigInodeMatchFontDir(config, d);
489
490 return 0;
491 }
492
493 FcBool
494 FcConfigAddDir (FcConfig *config,
495 const FcChar8 *d)
496 {
497 return (FcConfigAddConfigDir (config, d) &&
498 FcConfigAddFontDir (config, d));
499 }
500
501 FcStrList *
502 FcConfigGetFontDirs (FcConfig *config)
503 {
504 if (!config)
505 {
506 config = FcConfigGetCurrent ();
507 if (!config)
508 return 0;
509 }
510 return FcStrListCreate (config->fontDirs);
511 }
512
513 FcBool
514 FcConfigAddConfigFile (FcConfig *config,
515 const FcChar8 *f)
516 {
517 FcBool ret;
518 FcChar8 *file = FcConfigFilename (f);
519
520 if (!file)
521 return FcFalse;
522
523 ret = FcStrSetAdd (config->configFiles, file);
524 FcStrFree (file);
525 return ret;
526 }
527
528 FcStrList *
529 FcConfigGetConfigFiles (FcConfig *config)
530 {
531 if (!config)
532 {
533 config = FcConfigGetCurrent ();
534 if (!config)
535 return 0;
536 }
537 return FcStrListCreate (config->configFiles);
538 }
539
540 FcBool
541 FcConfigSetCache (FcConfig *config,
542 const FcChar8 *c)
543 {
544 FcChar8 *new = FcStrCopyFilename (c);
545
546 if (!new)
547 return FcFalse;
548 if (config->cache)
549 FcStrFree (config->cache);
550 config->cache = new;
551 return FcTrue;
552 }
553
554 FcChar8 *
555 FcConfigGetCache (FcConfig *config)
556 {
557 if (!config)
558 {
559 config = FcConfigGetCurrent ();
560 if (!config)
561 return 0;
562 }
563 return config->cache;
564 }
565
566 FcFontSet *
567 FcConfigGetFonts (FcConfig *config,
568 FcSetName set)
569 {
570 if (!config)
571 {
572 config = FcConfigGetCurrent ();
573 if (!config)
574 return 0;
575 }
576 return config->fonts[set];
577 }
578
579 void
580 FcConfigSetFonts (FcConfig *config,
581 FcFontSet *fonts,
582 FcSetName set)
583 {
584 if (config->fonts[set])
585 FcFontSetDestroy (config->fonts[set]);
586 config->fonts[set] = fonts;
587 }
588
589
590
591 FcBlanks *
592 FcConfigGetBlanks (FcConfig *config)
593 {
594 if (!config)
595 {
596 config = FcConfigGetCurrent ();
597 if (!config)
598 return 0;
599 }
600 return config->blanks;
601 }
602
603 FcBool
604 FcConfigAddBlank (FcConfig *config,
605 FcChar32 blank)
606 {
607 FcBlanks *b;
608
609 b = config->blanks;
610 if (!b)
611 {
612 b = FcBlanksCreate ();
613 if (!b)
614 return FcFalse;
615 }
616 if (!FcBlanksAdd (b, blank))
617 return FcFalse;
618 config->blanks = b;
619 return FcTrue;
620 }
621
622 int
623 FcConfigGetRescanInverval (FcConfig *config)
624 {
625 if (!config)
626 {
627 config = FcConfigGetCurrent ();
628 if (!config)
629 return 0;
630 }
631 return config->rescanInterval;
632 }
633
634 FcBool
635 FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
636 {
637 if (!config)
638 {
639 config = FcConfigGetCurrent ();
640 if (!config)
641 return FcFalse;
642 }
643 config->rescanInterval = rescanInterval;
644 return FcTrue;
645 }
646
647 FcBool
648 FcConfigAddEdit (FcConfig *config,
649 FcTest *test,
650 FcEdit *edit,
651 FcMatchKind kind)
652 {
653 FcSubst *subst, **prev;
654 FcTest *t;
655 int num;
656
657 subst = (FcSubst *) malloc (sizeof (FcSubst));
658 if (!subst)
659 return FcFalse;
660 FcMemAlloc (FC_MEM_SUBST, sizeof (FcSubst));
661 if (kind == FcMatchPattern)
662 prev = &config->substPattern;
663 else
664 prev = &config->substFont;
665 for (; *prev; prev = &(*prev)->next);
666 *prev = subst;
667 subst->next = 0;
668 subst->test = test;
669 subst->edit = edit;
670 num = 0;
671 for (t = test; t; t = t->next)
672 {
673 if (t->kind == FcMatchDefault)
674 t->kind = kind;
675 num++;
676 }
677 if (config->maxObjects < num)
678 config->maxObjects = num;
679 if (FcDebug () & FC_DBG_EDIT)
680 {
681 printf ("Add Subst ");
682 FcSubstPrint (subst);
683 }
684 return FcTrue;
685 }
686
687 typedef struct _FcSubState {
688 FcPatternElt *elt;
689 FcValueList *value;
690 } FcSubState;
691
692 static FcValue
693 FcConfigPromote (FcValue v, FcValue u)
694 {
695 if (v.type == FcTypeInteger)
696 {
697 v.type = FcTypeDouble;
698 v.u.d = (double) v.u.i;
699 }
700 else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
701 {
702 v.u.m = &FcIdentityMatrix;
703 v.type = FcTypeMatrix;
704 }
705 else if (v.type == FcTypeString && u.type == FcTypeLangSet)
706 {
707 v.u.l = FcLangSetPromote (v.u.s);
708 v.type = FcTypeLangSet;
709 }
710 return v;
711 }
712
713 FcBool
714 FcConfigCompareValue (const FcValue *left_o,
715 FcOp op,
716 const FcValue *right_o)
717 {
718 FcValue left = FcValueCanonicalize(left_o);
719 FcValue right = FcValueCanonicalize(right_o);
720 FcBool ret = FcFalse;
721
722 left = FcConfigPromote (left, right);
723 right = FcConfigPromote (right, left);
724 if (left.type == right.type)
725 {
726 switch (left.type) {
727 case FcTypeInteger:
728 break; /* FcConfigPromote prevents this from happening */
729 case FcTypeDouble:
730 switch (op) {
731 case FcOpEqual:
732 case FcOpContains:
733 case FcOpListing:
734 ret = left.u.d == right.u.d;
735 break;
736 case FcOpNotEqual:
737 case FcOpNotContains:
738 ret = left.u.d != right.u.d;
739 break;
740 case FcOpLess:
741 ret = left.u.d < right.u.d;
742 break;
743 case FcOpLessEqual:
744 ret = left.u.d <= right.u.d;
745 break;
746 case FcOpMore:
747 ret = left.u.d > right.u.d;
748 break;
749 case FcOpMoreEqual:
750 ret = left.u.d >= right.u.d;
751 break;
752 default:
753 break;
754 }
755 break;
756 case FcTypeBool:
757 switch (op) {
758 case FcOpEqual:
759 case FcOpContains:
760 case FcOpListing:
761 ret = left.u.b == right.u.b;
762 break;
763 case FcOpNotEqual:
764 case FcOpNotContains:
765 ret = left.u.b != right.u.b;
766 break;
767 default:
768 break;
769 }
770 break;
771 case FcTypeString:
772 switch (op) {
773 case FcOpEqual:
774 case FcOpListing:
775 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
776 break;
777 case FcOpContains:
778 ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
779 break;
780 case FcOpNotEqual:
781 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
782 break;
783 case FcOpNotContains:
784 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
785 break;
786 default:
787 break;
788 }
789 break;
790 case FcTypeMatrix:
791 switch (op) {
792 case FcOpEqual:
793 case FcOpContains:
794 case FcOpListing:
795 ret = FcMatrixEqual (left.u.m, right.u.m);
796 break;
797 case FcOpNotEqual:
798 case FcOpNotContains:
799 ret = !FcMatrixEqual (left.u.m, right.u.m);
800 break;
801 default:
802 break;
803 }
804 break;
805 case FcTypeCharSet:
806 switch (op) {
807 case FcOpContains:
808 case FcOpListing:
809 /* left contains right if right is a subset of left */
810 ret = FcCharSetIsSubset (right.u.c, left.u.c);
811 break;
812 case FcOpNotContains:
813 /* left contains right if right is a subset of left */
814 ret = !FcCharSetIsSubset (right.u.c, left.u.c);
815 break;
816 case FcOpEqual:
817 ret = FcCharSetEqual (left.u.c, right.u.c);
818 break;
819 case FcOpNotEqual:
820 ret = !FcCharSetEqual (left.u.c, right.u.c);
821 break;
822 default:
823 break;
824 }
825 break;
826 case FcTypeLangSet:
827 switch (op) {
828 case FcOpContains:
829 case FcOpListing:
830 ret = FcLangSetContains (left.u.l, right.u.l);
831 break;
832 case FcOpNotContains:
833 ret = !FcLangSetContains (left.u.l, right.u.l);
834 break;
835 case FcOpEqual:
836 ret = FcLangSetEqual (left.u.l, right.u.l);
837 break;
838 case FcOpNotEqual:
839 ret = !FcLangSetEqual (left.u.l, right.u.l);
840 break;
841 default:
842 break;
843 }
844 break;
845 case FcTypeVoid:
846 switch (op) {
847 case FcOpEqual:
848 case FcOpContains:
849 case FcOpListing:
850 ret = FcTrue;
851 break;
852 default:
853 break;
854 }
855 break;
856 case FcTypeFTFace:
857 switch (op) {
858 case FcOpEqual:
859 case FcOpContains:
860 case FcOpListing:
861 ret = left.u.f == right.u.f;
862 break;
863 case FcOpNotEqual:
864 case FcOpNotContains:
865 ret = left.u.f != right.u.f;
866 break;
867 default:
868 break;
869 }
870 break;
871 }
872 }
873 else
874 {
875 if (op == FcOpNotEqual || op == FcOpNotContains)
876 ret = FcTrue;
877 }
878 return ret;
879 }
880
881
882 #define _FcDoubleFloor(d) ((int) (d))
883 #define _FcDoubleCeil(d) ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
884 #define FcDoubleFloor(d) ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
885 #define FcDoubleCeil(d) ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
886 #define FcDoubleRound(d) FcDoubleFloor ((d) + 0.5)
887 #define FcDoubleTrunc(d) ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
888
889 static FcValue
890 FcConfigEvaluate (FcPattern *p, FcExpr *e)
891 {
892 FcValue v, vl, vr;
893 FcResult r;
894 FcMatrix *m;
895
896 switch (e->op) {
897 case FcOpInteger:
898 v.type = FcTypeInteger;
899 v.u.i = e->u.ival;
900 break;
901 case FcOpDouble:
902 v.type = FcTypeDouble;
903 v.u.d = e->u.dval;
904 break;
905 case FcOpString:
906 v.type = FcTypeString;
907 v.u.s = FcStrStaticName(e->u.sval);
908 break;
909 case FcOpMatrix:
910 v.type = FcTypeMatrix;
911 v.u.m = e->u.mval;
912 v = FcValueSave (v);
913 break;
914 case FcOpCharSet:
915 v.type = FcTypeCharSet;
916 v.u.c = e->u.cval;
917 v = FcValueSave (v);
918 break;
919 case FcOpBool:
920 v.type = FcTypeBool;
921 v.u.b = e->u.bval;
922 break;
923 case FcOpField:
924 r = FcPatternGet (p, e->u.field, 0, &v);
925 if (r != FcResultMatch)
926 v.type = FcTypeVoid;
927 v = FcValueSave (v);
928 break;
929 case FcOpConst:
930 if (FcNameConstant (e->u.constant, &v.u.i))
931 v.type = FcTypeInteger;
932 else
933 v.type = FcTypeVoid;
934 break;
935 case FcOpQuest:
936 vl = FcConfigEvaluate (p, e->u.tree.left);
937 if (vl.type == FcTypeBool)
938 {
939 if (vl.u.b)
940 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.left);
941 else
942 v = FcConfigEvaluate (p, e->u.tree.right->u.tree.right);
943 }
944 else
945 v.type = FcTypeVoid;
946 FcValueDestroy (vl);
947 break;
948 case FcOpEqual:
949 case FcOpNotEqual:
950 case FcOpLess:
951 case FcOpLessEqual:
952 case FcOpMore:
953 case FcOpMoreEqual:
954 case FcOpContains:
955 case FcOpNotContains:
956 case FcOpListing:
957 vl = FcConfigEvaluate (p, e->u.tree.left);
958 vr = FcConfigEvaluate (p, e->u.tree.right);
959 v.type = FcTypeBool;
960 v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
961 FcValueDestroy (vl);
962 FcValueDestroy (vr);
963 break;
964 case FcOpOr:
965 case FcOpAnd:
966 case FcOpPlus:
967 case FcOpMinus:
968 case FcOpTimes:
969 case FcOpDivide:
970 vl = FcConfigEvaluate (p, e->u.tree.left);
971 vr = FcConfigEvaluate (p, e->u.tree.right);
972 vl = FcConfigPromote (vl, vr);
973 vr = FcConfigPromote (vr, vl);
974 if (vl.type == vr.type)
975 {
976 switch (vl.type) {
977 case FcTypeDouble:
978 switch (e->op) {
979 case FcOpPlus:
980 v.type = FcTypeDouble;
981 v.u.d = vl.u.d + vr.u.d;
982 break;
983 case FcOpMinus:
984 v.type = FcTypeDouble;
985 v.u.d = vl.u.d - vr.u.d;
986 break;
987 case FcOpTimes:
988 v.type = FcTypeDouble;
989 v.u.d = vl.u.d * vr.u.d;
990 break;
991 case FcOpDivide:
992 v.type = FcTypeDouble;
993 v.u.d = vl.u.d / vr.u.d;
994 break;
995 default:
996 v.type = FcTypeVoid;
997 break;
998 }
999 if (v.type == FcTypeDouble &&
1000 v.u.d == (double) (int) v.u.d)
1001 {
1002 v.type = FcTypeInteger;
1003 v.u.i = (int) v.u.d;
1004 }
1005 break;
1006 case FcTypeBool:
1007 switch (e->op) {
1008 case FcOpOr:
1009 v.type = FcTypeBool;
1010 v.u.b = vl.u.b || vr.u.b;
1011 break;
1012 case FcOpAnd:
1013 v.type = FcTypeBool;
1014 v.u.b = vl.u.b && vr.u.b;
1015 break;
1016 default:
1017 v.type = FcTypeVoid;
1018 break;
1019 }
1020 break;
1021 case FcTypeString:
1022 switch (e->op) {
1023 case FcOpPlus:
1024 v.type = FcTypeString;
1025 v.u.s = FcStrStaticName (FcStrPlus (vl.u.s, vr.u.s));
1026
1027 if (!v.u.s)
1028 v.type = FcTypeVoid;
1029 break;
1030 default:
1031 v.type = FcTypeVoid;
1032 break;
1033 }
1034 break;
1035 case FcTypeMatrix:
1036 switch (e->op) {
1037 case FcOpTimes:
1038 v.type = FcTypeMatrix;
1039 m = malloc (sizeof (FcMatrix));
1040 if (m)
1041 {
1042 FcMemAlloc (FC_MEM_MATRIX, sizeof (FcMatrix));
1043 FcMatrixMultiply (m, vl.u.m, vr.u.m);
1044 v.u.m = m;
1045 }
1046 else
1047 {
1048 v.type = FcTypeVoid;
1049 }
1050 break;
1051 default:
1052 v.type = FcTypeVoid;
1053 break;
1054 }
1055 break;
1056 default:
1057 v.type = FcTypeVoid;
1058 break;
1059 }
1060 }
1061 else
1062 v.type = FcTypeVoid;
1063 FcValueDestroy (vl);
1064 FcValueDestroy (vr);
1065 break;
1066 case FcOpNot:
1067 vl = FcConfigEvaluate (p, e->u.tree.left);
1068 switch (vl.type) {
1069 case FcTypeBool:
1070 v.type = FcTypeBool;
1071 v.u.b = !vl.u.b;
1072 break;
1073 default:
1074 v.type = FcTypeVoid;
1075 break;
1076 }
1077 FcValueDestroy (vl);
1078 break;
1079 case FcOpFloor:
1080 vl = FcConfigEvaluate (p, e->u.tree.left);
1081 switch (vl.type) {
1082 case FcTypeInteger:
1083 v = vl;
1084 break;
1085 case FcTypeDouble:
1086 v.type = FcTypeInteger;
1087 v.u.i = FcDoubleFloor (vl.u.d);
1088 break;
1089 default:
1090 v.type = FcTypeVoid;
1091 break;
1092 }
1093 FcValueDestroy (vl);
1094 break;
1095 case FcOpCeil:
1096 vl = FcConfigEvaluate (p, e->u.tree.left);
1097 switch (vl.type) {
1098 case FcTypeInteger:
1099 v = vl;
1100 break;
1101 case FcTypeDouble:
1102 v.type = FcTypeInteger;
1103 v.u.i = FcDoubleCeil (vl.u.d);
1104 break;
1105 default:
1106 v.type = FcTypeVoid;
1107 break;
1108 }
1109 FcValueDestroy (vl);
1110 break;
1111 case FcOpRound:
1112 vl = FcConfigEvaluate (p, e->u.tree.left);
1113 switch (vl.type) {
1114 case FcTypeInteger:
1115 v = vl;
1116 break;
1117 case FcTypeDouble:
1118 v.type = FcTypeInteger;
1119 v.u.i = FcDoubleRound (vl.u.d);
1120 break;
1121 default:
1122 v.type = FcTypeVoid;
1123 break;
1124 }
1125 FcValueDestroy (vl);
1126 break;
1127 case FcOpTrunc:
1128 vl = FcConfigEvaluate (p, e->u.tree.left);
1129 switch (vl.type) {
1130 case FcTypeInteger:
1131 v = vl;
1132 break;
1133 case FcTypeDouble:
1134 v.type = FcTypeInteger;
1135 v.u.i = FcDoubleTrunc (vl.u.d);
1136 break;
1137 default:
1138 v.type = FcTypeVoid;
1139 break;
1140 }
1141 FcValueDestroy (vl);
1142 break;
1143 default:
1144 v.type = FcTypeVoid;
1145 break;
1146 }
1147 return v;
1148 }
1149
1150 static FcValueList *
1151 FcConfigMatchValueList (FcPattern *p,
1152 FcTest *t,
1153 FcValueList *values)
1154 {
1155 FcValueList *ret = 0;
1156 FcExpr *e = t->expr;
1157 FcValue value;
1158 FcValueList *v;
1159
1160 while (e)
1161 {
1162 /* Compute the value of the match expression */
1163 if (e->op == FcOpComma)
1164 {
1165 value = FcConfigEvaluate (p, e->u.tree.left);
1166 e = e->u.tree.right;
1167 }
1168 else
1169 {
1170 value = FcConfigEvaluate (p, e);
1171 e = 0;
1172 }
1173
1174 for (v = values; v; v = FcValueListPtrU(v->next))
1175 {
1176 /* Compare the pattern value to the match expression value */
1177 if (FcConfigCompareValue (&v->value, t->op, &value))
1178 {
1179 if (!ret)
1180 ret = v;
1181 }
1182 else
1183 {
1184 if (t->qual == FcQualAll)
1185 {
1186 ret = 0;
1187 break;
1188 }
1189 }
1190 }
1191 FcValueDestroy (value);
1192 }
1193 return ret;
1194 }
1195
1196 static FcValueList *
1197 FcConfigValues (FcPattern *p, FcExpr *e, FcValueBinding binding)
1198 {
1199 FcValueList *l;
1200
1201 if (!e)
1202 return 0;
1203 l = (FcValueList *) malloc (sizeof (FcValueList));
1204 if (!l)
1205 return 0;
1206 FcMemAlloc (FC_MEM_VALLIST, sizeof (FcValueList));
1207 if (e->op == FcOpComma)
1208 {
1209 l->value = FcConfigEvaluate (p, e->u.tree.left);
1210 l->next = FcValueListPtrCreateDynamic(FcConfigValues (p, e->u.tree.right, binding));
1211 }
1212 else
1213 {
1214 l->value = FcConfigEvaluate (p, e);
1215 l->next = FcValueListPtrCreateDynamic(0);
1216 }
1217 l->binding = binding;
1218 if (l->value.type == FcTypeVoid)
1219 {
1220 FcValueList *next = FcValueListPtrU(l->next);
1221
1222 FcMemFree (FC_MEM_VALLIST, sizeof (FcValueList));
1223 free (l);
1224 l = next;
1225 }
1226
1227 return l;
1228 }
1229
1230 static FcBool
1231 FcConfigAdd (FcValueListPtr *head,
1232 FcValueList *position,
1233 FcBool append,
1234 FcValueList *new)
1235 {
1236 FcValueListPtr *prev, last, v;
1237 FcValueBinding sameBinding;
1238
1239 if (position)
1240 sameBinding = position->binding;
1241 else
1242 sameBinding = FcValueBindingWeak;
1243 for (v = FcValueListPtrCreateDynamic(new); FcValueListPtrU(v);
1244 v = FcValueListPtrU(v)->next)
1245 if (FcValueListPtrU(v)->binding == FcValueBindingSame)
1246 FcValueListPtrU(v)->binding = sameBinding;
1247 if (append)
1248 {
1249 if (position)
1250 prev = &position->next;
1251 else
1252 for (prev = head; FcValueListPtrU(*prev);
1253 prev = &(FcValueListPtrU(*prev)->next))
1254 ;
1255 }
1256 else
1257 {
1258 if (position)
1259 {
1260 for (prev = head; FcValueListPtrU(*prev);
1261 prev = &(FcValueListPtrU(*prev)->next))
1262 {
1263 if (FcValueListPtrU(*prev) == position)
1264 break;
1265 }
1266 }
1267 else
1268 prev = head;
1269
1270 if (FcDebug () & FC_DBG_EDIT)
1271 {
1272 if (!FcValueListPtrU(*prev))
1273 printf ("position not on list\n");
1274 }
1275 }
1276
1277 if (FcDebug () & FC_DBG_EDIT)
1278 {
1279 printf ("%s list before ", append ? "Append" : "Prepend");
1280 FcValueListPrint (*head);
1281 printf ("\n");
1282 }
1283
1284 if (new)
1285 {
1286 last = FcValueListPtrCreateDynamic(new);
1287 while (FcValueListPtrU(FcValueListPtrU(last)->next))
1288 last = FcValueListPtrU(last)->next;
1289
1290 FcValueListPtrU(last)->next = *prev;
1291 *prev = FcValueListPtrCreateDynamic(new);
1292 }
1293
1294 if (FcDebug () & FC_DBG_EDIT)
1295 {
1296 printf ("%s list after ", append ? "Append" : "Prepend");
1297 FcValueListPrint (*head);
1298 printf ("\n");
1299 }
1300
1301 return FcTrue;
1302 }
1303
1304 static void
1305 FcConfigDel (FcValueListPtr *head,
1306 FcValueList *position)
1307 {
1308 FcValueListPtr *prev;
1309
1310 for (prev = head; FcValueListPtrU(*prev);
1311 prev = &(FcValueListPtrU(*prev)->next))
1312 {
1313 if (FcValueListPtrU(*prev) == position)
1314 {
1315 *prev = position->next;
1316 position->next = FcValueListPtrCreateDynamic(0);
1317 FcValueListDestroy (FcValueListPtrCreateDynamic(position));
1318 break;
1319 }
1320 }
1321 }
1322
1323 static void
1324 FcConfigPatternAdd (FcPattern *p,
1325 const char *object,
1326 FcValueList *list,
1327 FcBool append)
1328 {
1329 if (list)
1330 {
1331 FcPatternElt *e = FcPatternInsertElt (p, object);
1332
1333 if (!e)
1334 return;
1335 FcConfigAdd (&e->values, 0, append, list);
1336 }
1337 }
1338
1339 /*
1340 * Delete all values associated with a field
1341 */
1342 static void
1343 FcConfigPatternDel (FcPattern *p,
1344 const char *object)
1345 {
1346 FcPatternElt *e = FcPatternFindElt (p, object);
1347 if (!e)
1348 return;
1349 while (FcValueListPtrU(e->values))
1350 FcConfigDel (&e->values, FcValueListPtrU(e->values));
1351 }
1352
1353 static void
1354 FcConfigPatternCanon (FcPattern *p,
1355 const char *object)
1356 {
1357 FcPatternElt *e = FcPatternFindElt (p, object);
1358 if (!e)
1359 return;
1360 if (!FcValueListPtrU(e->values))
1361 FcPatternDel (p, object);
1362 }
1363
1364 FcBool
1365 FcConfigSubstituteWithPat (FcConfig *config,
1366 FcPattern *p,
1367 FcPattern *p_pat,
1368 FcMatchKind kind)
1369 {
1370 FcSubst *s;
1371 FcSubState *st;
1372 int i;
1373 FcTest *t;
1374 FcEdit *e;
1375 FcValueList *l;
1376 FcPattern *m;
1377
1378 if (!config)
1379 {
1380 config = FcConfigGetCurrent ();
1381 if (!config)
1382 return FcFalse;
1383 }
1384
1385 st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
1386 if (!st && config->maxObjects)
1387 return FcFalse;
1388 FcMemAlloc (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1389
1390 if (FcDebug () & FC_DBG_EDIT)
1391 {
1392 printf ("FcConfigSubstitute ");
1393 FcPatternPrint (p);
1394 }
1395 if (kind == FcMatchPattern)
1396 s = config->substPattern;
1397 else
1398 s = config->substFont;
1399 for (; s; s = s->next)
1400 {
1401 /*
1402 * Check the tests to see if
1403 * they all match the pattern
1404 */
1405 for (t = s->test, i = 0; t; t = t->next, i++)
1406 {
1407 if (FcDebug () & FC_DBG_EDIT)
1408 {
1409 printf ("FcConfigSubstitute test ");
1410 FcTestPrint (t);
1411 }
1412 st[i].elt = 0;
1413 if (kind == FcMatchFont && t->kind == FcMatchPattern)
1414 m = p_pat;
1415 else
1416 m = p;
1417 if (m)
1418 st[i].elt = FcPatternFindElt (m, t->field);
1419 else
1420 st[i].elt = 0;
1421 /*
1422 * If there's no such field in the font,
1423 * then FcQualAll matches while FcQualAny does not
1424 */
1425 if (!st[i].elt)
1426 {
1427 if (t->qual == FcQualAll)
1428 {
1429 st[i].value = 0;
1430 continue;
1431 }
1432 else
1433 break;
1434 }
1435 /*
1436 * Check to see if there is a match, mark the location
1437 * to apply match-relative edits
1438 */
1439 st[i].value = FcConfigMatchValueList (m, t, FcValueListPtrU(st[i].elt->values));
1440 if (!st[i].value)
1441 break;
1442 if (t->qual == FcQualFirst && st[i].value != FcValueListPtrU(st[i].elt->values))
1443 break;
1444 if (t->qual == FcQualNotFirst && st[i].value == FcValueListPtrU(st[i].elt->values))
1445 break;
1446 }
1447 if (t)
1448 {
1449 if (FcDebug () & FC_DBG_EDIT)
1450 printf ("No match\n");
1451 continue;
1452 }
1453 if (FcDebug () & FC_DBG_EDIT)
1454 {
1455 printf ("Substitute ");
1456 FcSubstPrint (s);
1457 }
1458 for (e = s->edit; e; e = e->next)
1459 {
1460 /*
1461 * Evaluate the list of expressions
1462 */
1463 l = FcConfigValues (p, e->expr, e->binding);
1464 /*
1465 * Locate any test associated with this field, skipping
1466 * tests associated with the pattern when substituting in
1467 * the font
1468 */
1469 for (t = s->test, i = 0; t; t = t->next, i++)
1470 {
1471 if ((t->kind == FcMatchFont || kind == FcMatchPattern) &&
1472 !FcStrCmpIgnoreCase ((FcChar8 *) t->field,
1473 (FcChar8 *) e->field))
1474 {
1475 /*
1476 * KLUDGE - the pattern may have been reallocated or
1477 * things may have been inserted or deleted above
1478 * this element by other edits. Go back and find
1479 * the element again
1480 */
1481 if (e != s->edit && st[i].elt)
1482 st[i].elt = FcPatternFindElt (p, t->field);
1483 if (!st[i].elt)
1484 t = 0;
1485 break;
1486 }
1487 }
1488 switch (e->op) {
1489 case FcOpAssign:
1490 /*
1491 * If there was a test, then replace the matched
1492 * value with the new list of values
1493 */
1494 if (t)
1495 {
1496 FcValueList *thisValue = st[i].value;
1497 FcValueList *nextValue = thisValue ? FcValueListPtrU(thisValue->next) : 0;
1498
1499 /*
1500 * Append the new list of values after the current value
1501 */
1502 FcConfigAdd (&st[i].elt->values, thisValue, FcTrue, l);
1503 /*
1504 * Delete the marked value
1505 */
1506 FcConfigDel (&st[i].elt->values, thisValue);
1507 /*
1508 * Adjust any pointers into the value list to ensure
1509 * future edits occur at the same place
1510 */
1511 for (t = s->test, i = 0; t; t = t->next, i++)
1512 {
1513 if (st[i].value == thisValue)
1514 st[i].value = nextValue;
1515 }
1516 break;
1517 }
1518 /* fall through ... */
1519 case FcOpAssignReplace:
1520 /*
1521 * Delete all of the values and insert
1522 * the new set
1523 */
1524 FcConfigPatternDel (p, e->field);
1525 FcConfigPatternAdd (p, e->field, l, FcTrue);
1526 /*
1527 * Adjust any pointers into the value list as they no
1528 * longer point to anything valid
1529 */
1530 if (t)
1531 {
1532 FcPatternElt *thisElt = st[i].elt;
1533 for (t = s->test, i = 0; t; t = t->next, i++)
1534 {
1535 if (st[i].elt == thisElt)
1536 st[i].value = 0;
1537 }
1538 }
1539 break;
1540 case FcOpPrepend:
1541 if (t)
1542 {
1543 FcConfigAdd (&st[i].elt->values, st[i].value, FcFalse, l);
1544 break;
1545 }
1546 /* fall through ... */
1547 case FcOpPrependFirst:
1548 FcConfigPatternAdd (p, e->field, l, FcFalse);
1549 break;
1550 case FcOpAppend:
1551 if (t)
1552 {
1553 FcConfigAdd (&st[i].elt->values, st[i].value, FcTrue, l);
1554 break;
1555 }
1556 /* fall through ... */
1557 case FcOpAppendLast:
1558 FcConfigPatternAdd (p, e->field, l, FcTrue);
1559 break;
1560 default:
1561 break;
1562 }
1563 }
1564 /*
1565 * Now go through the pattern and eliminate
1566 * any properties without data
1567 */
1568 for (e = s->edit; e; e = e->next)
1569 FcConfigPatternCanon (p, e->field);
1570
1571 if (FcDebug () & FC_DBG_EDIT)
1572 {
1573 printf ("FcConfigSubstitute edit");
1574 FcPatternPrint (p);
1575 }
1576 }
1577 FcMemFree (FC_MEM_SUBSTATE, config->maxObjects * sizeof (FcSubState));
1578 free (st);
1579 if (FcDebug () & FC_DBG_EDIT)
1580 {
1581 printf ("FcConfigSubstitute done");
1582 FcPatternPrint (p);
1583 }
1584 return FcTrue;
1585 }
1586
1587 FcBool
1588 FcConfigSubstitute (FcConfig *config,
1589 FcPattern *p,
1590 FcMatchKind kind)
1591 {
1592 return FcConfigSubstituteWithPat (config, p, 0, kind);
1593 }
1594
1595 #if defined (_WIN32) && (defined (PIC) || defined (DLL_EXPORT))
1596
1597 static FcChar8 fontconfig_path[1000] = "";
1598
1599 BOOL WINAPI
1600 DllMain (HINSTANCE hinstDLL,
1601 DWORD fdwReason,
1602 LPVOID lpvReserved)
1603 {
1604 FcChar8 *p;
1605
1606 switch (fdwReason) {
1607 case DLL_PROCESS_ATTACH:
1608 if (!GetModuleFileName ((HMODULE) hinstDLL, fontconfig_path,
1609 sizeof (fontconfig_path)))
1610 break;
1611
1612 /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
1613 * assume it's a Unix-style installation tree, and use
1614 * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
1615 * folder where the DLL is as FONTCONFIG_PATH.
1616 */
1617 p = strrchr (fontconfig_path, '\\');
1618 if (p)
1619 {
1620 *p = '\0';
1621 p = strrchr (fontconfig_path, '\\');
1622 if (p && (FcStrCmpIgnoreCase (p + 1, "bin") == 0 ||
1623 FcStrCmpIgnoreCase (p + 1, "lib") == 0))
1624 *p = '\0';
1625 strcat (fontconfig_path, "\\etc\\fonts");
1626 }
1627 else
1628 fontconfig_path[0] = '\0';
1629
1630 break;
1631 }
1632
1633 return TRUE;
1634 }
1635
1636 #undef FONTCONFIG_PATH
1637 #define FONTCONFIG_PATH fontconfig_path
1638
1639 #else /* !(_WIN32 && PIC) */
1640
1641 #endif /* !(_WIN32 && PIC) */
1642
1643 #ifndef FONTCONFIG_FILE
1644 #define FONTCONFIG_FILE "fonts.conf"
1645 #endif
1646
1647 static FcChar8 *
1648 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
1649 {
1650 FcChar8 *path;
1651
1652 if (!dir)
1653 dir = (FcChar8 *) "";
1654 path = malloc (strlen ((char *) dir) + 1 + strlen ((char *) file) + 1);
1655 if (!path)
1656 return 0;
1657
1658 strcpy ((char *) path, (const char *) dir);
1659 /* make sure there's a single separator */
1660 #ifdef _WIN32
1661 if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
1662 path[strlen((char *) path)-1] != '\\')) &&
1663 !(file[0] == '/' ||
1664 file[0] == '\\' ||
1665 (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
1666 strcat ((char *) path, "\\");
1667 #else
1668 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
1669 strcat ((char *) path, "/");
1670 #endif
1671 strcat ((char *) path, (char *) file);
1672
1673 FcMemAlloc (FC_MEM_STRING, strlen ((char *) path) + 1);
1674 if (access ((char *) path, R_OK) == 0)
1675 return path;
1676
1677 FcStrFree (path);
1678 return 0;
1679 }
1680
1681 static FcChar8 **
1682 FcConfigGetPath (void)
1683 {
1684 FcChar8 **path;
1685 FcChar8 *env, *e, *colon;
1686 FcChar8 *dir;
1687 int npath;
1688 int i;
1689
1690 npath = 2; /* default dir + null */
1691 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
1692 if (env)
1693 {
1694 e = env;
1695 npath++;
1696 while (*e)
1697 if (*e++ == FC_SEARCH_PATH_SEPARATOR)
1698 npath++;
1699 }
1700 path = calloc (npath, sizeof (FcChar8 *));
1701 if (!path)
1702 goto bail0;
1703 i = 0;
1704
1705 if (env)
1706 {
1707 e = env;
1708 while (*e)
1709 {
1710 colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
1711 if (!colon)
1712 colon = e + strlen ((char *) e);
1713 path[i] = malloc (colon - e + 1);
1714 if (!path[i])
1715 goto bail1;
1716 strncpy ((char *) path[i], (const char *) e, colon - e);
1717 path[i][colon - e] = '\0';
1718 if (*colon)
1719 e = colon + 1;
1720 else
1721 e = colon;
1722 i++;
1723 }
1724 }
1725
1726 dir = (FcChar8 *) FONTCONFIG_PATH;
1727 path[i] = malloc (strlen ((char *) dir) + 1);
1728 if (!path[i])
1729 goto bail1;
1730 strcpy ((char *) path[i], (const char *) dir);
1731 return path;
1732
1733 bail1:
1734 for (i = 0; path[i]; i++)
1735 free (path[i]);
1736 free (path);
1737 bail0:
1738 return 0;
1739 }
1740
1741 static void
1742 FcConfigFreePath (FcChar8 **path)
1743 {
1744 FcChar8 **p;
1745
1746 for (p = path; *p; p++)
1747 free (*p);
1748 free (path);
1749 }
1750
1751 static FcBool _FcConfigHomeEnabled = FcTrue;
1752
1753 FcChar8 *
1754 FcConfigHome (void)
1755 {
1756 if (_FcConfigHomeEnabled)
1757 {
1758 char *home = getenv ("HOME");
1759
1760 #ifdef _WIN32
1761 if (home == NULL)
1762 home = getenv ("USERPROFILE");
1763 #endif
1764
1765 return (FcChar8 *) home;
1766 }
1767 return 0;
1768 }
1769
1770 FcBool
1771 FcConfigEnableHome (FcBool enable)
1772 {
1773 FcBool prev = _FcConfigHomeEnabled;
1774 _FcConfigHomeEnabled = enable;
1775 return prev;
1776 }
1777
1778 FcChar8 *
1779 FcConfigFilename (const FcChar8 *url)
1780 {
1781 FcChar8 *file, *dir, **path, **p;
1782
1783 if (!url || !*url)
1784 {
1785 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
1786 if (!url)
1787 url = (FcChar8 *) FONTCONFIG_FILE;
1788 }
1789 file = 0;
1790
1791 #ifdef _WIN32
1792 if (isalpha (*url) &&
1793 url[1] == ':' &&
1794 (url[2] == '/' || url[2] == '\\'))
1795 goto absolute_path;
1796 #endif
1797
1798 switch (*url) {
1799 case '~':
1800 dir = FcConfigHome ();
1801 if (dir)
1802 file = FcConfigFileExists (dir, url + 1);
1803 else
1804 file = 0;
1805 break;
1806 #ifdef _WIN32
1807 case '\\':
1808 absolute_path:
1809 #endif
1810 case '/':
1811 file = FcConfigFileExists (0, url);
1812 break;
1813 default:
1814 path = FcConfigGetPath ();
1815 if (!path)
1816 return 0;
1817 for (p = path; *p; p++)
1818 {
1819 file = FcConfigFileExists (*p, url);
1820 if (file)
1821 break;
1822 }
1823 FcConfigFreePath (path);
1824 break;
1825 }
1826 return file;
1827 }
1828
1829 /*
1830 * Manage the application-specific fonts
1831 */
1832
1833 FcBool
1834 FcConfigAppFontAddFile (FcConfig *config,
1835 const FcChar8 *file)
1836 {
1837 FcFontSet *set;
1838 FcStrSet *subdirs;
1839 FcStrList *sublist;
1840 FcChar8 *subdir;
1841
1842 if (!config)
1843 {
1844 config = FcConfigGetCurrent ();
1845 if (!config)
1846 return FcFalse;
1847 }
1848
1849 subdirs = FcStrSetCreate ();
1850 if (!subdirs)
1851 return FcFalse;
1852
1853 set = FcConfigGetFonts (config, FcSetApplication);
1854 if (!set)
1855 {
1856 set = FcFontSetCreate ();
1857 if (!set)
1858 {
1859 FcStrSetDestroy (subdirs);
1860 return FcFalse;
1861 }
1862 FcConfigSetFonts (config, set, FcSetApplication);
1863 }
1864
1865 if (!FcFileScanConfig (set, subdirs, 0, config->blanks, file, FcFalse, config))
1866 {
1867 FcStrSetDestroy (subdirs);
1868 return FcFalse;
1869 }
1870 if ((sublist = FcStrListCreate (subdirs)))
1871 {
1872 while ((subdir = FcStrListNext (sublist)))
1873 {
1874 FcConfigAppFontAddDir (config, subdir);
1875 }
1876 FcStrListDone (sublist);
1877 }
1878 FcStrSetDestroy (subdirs);
1879 return FcTrue;
1880 }
1881
1882 FcBool
1883 FcConfigAppFontAddDir (FcConfig *config,
1884 const FcChar8 *dir)
1885 {
1886 FcFontSet *set;
1887 FcStrSet *subdirs;
1888 FcStrList *sublist;
1889 FcChar8 *subdir;
1890
1891 if (!config)
1892 {
1893 config = FcConfigGetCurrent ();
1894 if (!config)
1895 return FcFalse;
1896 }
1897 subdirs = FcStrSetCreate ();
1898 if (!subdirs)
1899 return FcFalse;
1900
1901 set = FcConfigGetFonts (config, FcSetApplication);
1902 if (!set)
1903 {
1904 set = FcFontSetCreate ();
1905 if (!set)
1906 {
1907 FcStrSetDestroy (subdirs);
1908 return FcFalse;
1909 }
1910 FcConfigSetFonts (config, set, FcSetApplication);
1911 }
1912
1913 if (!FcDirScanConfig (set, subdirs, 0, config->blanks, dir, FcFalse, config))
1914 {
1915 FcStrSetDestroy (subdirs);
1916 return FcFalse;
1917 }
1918 if ((sublist = FcStrListCreate (subdirs)))
1919 {
1920 while ((subdir = FcStrListNext (sublist)))
1921 {
1922 FcConfigAppFontAddDir (config, subdir);
1923 }
1924 FcStrListDone (sublist);
1925 }
1926 FcStrSetDestroy (subdirs);
1927 return FcTrue;
1928 }
1929
1930 void
1931 FcConfigAppFontClear (FcConfig *config)
1932 {
1933 if (!config)
1934 {
1935 config = FcConfigGetCurrent ();
1936 if (!config)
1937 return;
1938 }
1939
1940 FcConfigSetFonts (config, 0, FcSetApplication);
1941 }
1942
1943 /*
1944 * Manage filename-based font source selectors
1945 */
1946
1947 FcBool
1948 FcConfigGlobAdd (FcConfig *config,
1949 const FcChar8 *glob,
1950 FcBool accept)
1951 {
1952 FcStrSet *set = accept ? config->acceptGlobs : config->rejectGlobs;
1953
1954 return FcStrSetAdd (set, glob);
1955 }
1956
1957 static FcBool
1958 FcConfigGlobMatch (const FcChar8 *glob,
1959 const FcChar8 *string)
1960 {
1961 FcChar8 c;
1962
1963 while ((c = *glob++))
1964 {
1965 switch (c) {
1966 case '*':
1967 /* short circuit common case */
1968 if (!*glob)
1969 return FcTrue;
1970 /* short circuit another common case */
1971 if (strchr ((char *) glob, '*') == 0)
1972 string += strlen ((char *) string) - strlen ((char *) glob);
1973 while (*string)
1974 {
1975 if (FcConfigGlobMatch (glob, string))
1976 return FcTrue;
1977 string++;
1978 }
1979 return FcFalse;
1980 case '?':
1981 if (*string++ == '\0')
1982 return FcFalse;
1983 break;
1984 default:
1985 if (*string++ != c)
1986 return FcFalse;
1987 break;
1988 }
1989 }
1990 return *string == '\0';
1991 }
1992
1993 static FcBool
1994 FcConfigGlobsMatch (const FcStrSet *globs,
1995 const FcChar8 *string)
1996 {
1997 int i;
1998
1999 for (i = 0; i < globs->num; i++)
2000 if (FcConfigGlobMatch (globs->strs[i], string))
2001 return FcTrue;
2002 return FcFalse;
2003 }
2004
2005 FcBool
2006 FcConfigAcceptFilename (FcConfig *config,
2007 const FcChar8 *filename)
2008 {
2009 if (FcConfigGlobsMatch (config->acceptGlobs, filename))
2010 return FcTrue;
2011 if (FcConfigGlobsMatch (config->rejectGlobs, filename))
2012 return FcFalse;
2013 return FcTrue;
2014 }
2015
2016 /*
2017 * Manage font-pattern based font source selectors
2018 */
2019
2020 FcBool
2021 FcConfigPatternsAdd (FcConfig *config,
2022 FcPattern *pattern,
2023 FcBool accept)
2024 {
2025 FcFontSet *set = accept ? config->acceptPatterns : config->rejectPatterns;
2026
2027 return FcFontSetAdd (set, pattern);
2028 }
2029
2030 static FcBool
2031 FcConfigPatternsMatch (const FcFontSet *patterns,
2032 const FcPattern *font)
2033 {
2034 int i;
2035
2036 for (i = 0; i < patterns->nfont; i++)
2037 if (FcListPatternMatchAny (patterns->fonts[i], font))
2038 return FcTrue;
2039 return FcFalse;
2040 }
2041
2042 FcBool
2043 FcConfigAcceptFont (FcConfig *config,
2044 const FcPattern *font)
2045 {
2046 if (FcConfigPatternsMatch (config->acceptPatterns, font))
2047 return FcTrue;
2048 if (FcConfigPatternsMatch (config->rejectPatterns, font))
2049 return FcFalse;
2050 return FcTrue;
2051 }