1 /*
2 * fontconfig/src/fccfg.c
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 the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. The authors make 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 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) 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 /* Objects MT-safe for readonly access. */
26
27 #include "fcint.h"
28 #ifdef HAVE_DIRENT_H
29 #include <dirent.h>
30 #endif
31 #include <sys/types.h>
32
33 #if defined (_WIN32) && !defined (R_OK)
34 #define R_OK 4
35 #endif
36
37 #if defined(_WIN32) && !defined(S_ISFIFO)
38 #define S_ISFIFO(m) 0
39 #endif
40
41 static FcConfig *_fcConfig; /* MT-safe */
42 static FcMutex *_lock;
43
44 static void
45 lock_config (void)
46 {
47 FcMutex *lock;
48 retry:
49 lock = fc_atomic_ptr_get (&_lock);
50 if (!lock)
51 {
52 lock = (FcMutex *) malloc (sizeof (FcMutex));
53 FcMutexInit (lock);
54 if (!fc_atomic_ptr_cmpexch (&_lock, NULL, lock))
55 {
56 FcMutexFinish (lock);
57 free (lock);
58 goto retry;
59 }
60 FcMutexLock (lock);
61 /* Initialize random state */
62 FcRandom ();
63 return;
64 }
65 FcMutexLock (lock);
66 }
67
68 static void
69 unlock_config (void)
70 {
71 FcMutex *lock;
72 lock = fc_atomic_ptr_get (&_lock);
73 FcMutexUnlock (lock);
74 }
75
76 static void
77 free_lock (void)
78 {
79 FcMutex *lock;
80 lock = fc_atomic_ptr_get (&_lock);
81 if (lock && fc_atomic_ptr_cmpexch (&_lock, lock, NULL))
82 {
83 FcMutexFinish (lock);
84 free (lock);
85 }
86 }
87
88 static FcConfig *
89 FcConfigEnsure (void)
90 {
91 FcConfig *config;
92 retry:
93 config = fc_atomic_ptr_get (&_fcConfig);
94 if (!config)
95 {
96 config = FcInitLoadConfigAndFonts ();
97
98 if (!config || !fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
99 if (config)
100 FcConfigDestroy (config);
101 goto retry;
102 }
103 }
104 return config;
105 }
106
107 static void
108 FcDestroyAsRule (void *data)
109 {
110 FcRuleDestroy (data);
111 }
112
113 static void
114 FcDestroyAsRuleSet (void *data)
115 {
116 FcRuleSetDestroy (data);
117 }
118
119 FcBool
120 FcConfigInit (void)
121 {
122 return FcConfigEnsure () ? FcTrue : FcFalse;
123 }
124
125 void
126 FcConfigFini (void)
127 {
128 FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
129 if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
130 FcConfigDestroy (cfg);
131 free_lock ();
132 }
133
134 FcConfig *
135 FcConfigCreate (void)
136 {
137 FcSetName set;
138 FcConfig *config;
139 FcMatchKind k;
140 FcBool err = FcFalse;
141
142 config = malloc (sizeof (FcConfig));
143 if (!config)
144 goto bail0;
145
146 config->configDirs = FcStrSetCreate ();
147 if (!config->configDirs)
148 goto bail1;
149
150 config->configMapDirs = FcStrSetCreate();
151 if (!config->configMapDirs)
152 goto bail1_5;
153
154 config->configFiles = FcStrSetCreate ();
155 if (!config->configFiles)
156 goto bail2;
157
158 config->fontDirs = FcStrSetCreate ();
159 if (!config->fontDirs)
160 goto bail3;
161
162 config->acceptGlobs = FcStrSetCreate ();
163 if (!config->acceptGlobs)
164 goto bail4;
165
166 config->rejectGlobs = FcStrSetCreate ();
167 if (!config->rejectGlobs)
168 goto bail5;
169
170 config->acceptPatterns = FcFontSetCreate ();
171 if (!config->acceptPatterns)
172 goto bail6;
173
174 config->rejectPatterns = FcFontSetCreate ();
175 if (!config->rejectPatterns)
176 goto bail7;
177
178 config->cacheDirs = FcStrSetCreate ();
179 if (!config->cacheDirs)
180 goto bail8;
181
182 for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
183 {
184 config->subst[k] = FcPtrListCreate (FcDestroyAsRuleSet);
185 if (!config->subst[k])
186 err = FcTrue;
187 }
188 if (err)
189 goto bail9;
190
191 config->maxObjects = 0;
192 for (set = FcSetSystem; set <= FcSetApplication; set++)
193 config->fonts[set] = 0;
194
195 config->rescanTime = time(0);
196 config->rescanInterval = 30;
197
198 config->expr_pool = NULL;
199
200 config->sysRoot = FcStrRealPath ((const FcChar8 *) getenv("FONTCONFIG_SYSROOT"));
201
202 config->rulesetList = FcPtrListCreate (FcDestroyAsRuleSet);
203 if (!config->rulesetList)
204 goto bail9;
205 config->availConfigFiles = FcStrSetCreate ();
206 if (!config->availConfigFiles)
207 goto bail10;
208
209 FcRefInit (&config->ref, 1);
210
211 return config;
212
213 bail10:
214 FcPtrListDestroy (config->rulesetList);
215 bail9:
216 for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
217 if (config->subst[k])
218 FcPtrListDestroy (config->subst[k]);
219 FcStrSetDestroy (config->cacheDirs);
220 bail8:
221 FcFontSetDestroy (config->rejectPatterns);
222 bail7:
223 FcFontSetDestroy (config->acceptPatterns);
224 bail6:
225 FcStrSetDestroy (config->rejectGlobs);
226 bail5:
227 FcStrSetDestroy (config->acceptGlobs);
228 bail4:
229 FcStrSetDestroy (config->fontDirs);
230 bail3:
231 FcStrSetDestroy (config->configFiles);
232 bail2:
233 FcStrSetDestroy (config->configMapDirs);
234 bail1_5:
235 FcStrSetDestroy (config->configDirs);
236 bail1:
237 free (config);
238 bail0:
239 return 0;
240 }
241
242 static FcFileTime
243 FcConfigNewestFile (FcStrSet *files)
244 {
245 FcStrList *list = FcStrListCreate (files);
246 FcFileTime newest = { 0, FcFalse };
247 FcChar8 *file;
248 struct stat statb;
249
250 if (list)
251 {
252 while ((file = FcStrListNext (list)))
253 if (FcStat (file, &statb) == 0)
254 if (!newest.set || statb.st_mtime - newest.time > 0)
255 {
256 newest.set = FcTrue;
257 newest.time = statb.st_mtime;
258 }
259 FcStrListDone (list);
260 }
261 return newest;
262 }
263
264 FcBool
265 FcConfigUptoDate (FcConfig *config)
266 {
267 FcFileTime config_time, config_dir_time, font_time;
268 time_t now = time(0);
269 FcBool ret = FcTrue;
270
271 config = FcConfigReference (config);
272 if (!config)
273 return FcFalse;
274
275 config_time = FcConfigNewestFile (config->configFiles);
276 config_dir_time = FcConfigNewestFile (config->configDirs);
277 font_time = FcConfigNewestFile (config->fontDirs);
278 if ((config_time.set && config_time.time - config->rescanTime > 0) ||
279 (config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) ||
280 (font_time.set && (font_time.time - config->rescanTime) > 0))
281 {
282 /* We need to check for potential clock problems here (OLPC ticket #6046) */
283 if ((config_time.set && (config_time.time - now) > 0) ||
284 (config_dir_time.set && (config_dir_time.time - now) > 0) ||
285 (font_time.set && (font_time.time - now) > 0))
286 {
287 fprintf (stderr,
288 "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n");
289 config->rescanTime = now;
290 goto bail;
291 }
292 else
293 {
294 ret = FcFalse;
295 goto bail;
296 }
297 }
298 config->rescanTime = now;
299 bail:
300 FcConfigDestroy (config);
301
302 return ret;
303 }
304
305 FcExpr *
306 FcConfigAllocExpr (FcConfig *config)
307 {
308 if (!config->expr_pool || config->expr_pool->next == config->expr_pool->end)
309 {
310 FcExprPage *new_page;
311
312 new_page = malloc (sizeof (FcExprPage));
313 if (!new_page)
314 return 0;
315
316 new_page->next_page = config->expr_pool;
317 new_page->next = new_page->exprs;
318 config->expr_pool = new_page;
319 }
320
321 return config->expr_pool->next++;
322 }
323
324 FcConfig *
325 FcConfigReference (FcConfig *config)
326 {
327 if (!config)
328 {
329 /* lock during obtaining the value from _fcConfig and count up refcount there,
330 * there are the race between them.
331 */
332 lock_config ();
333 retry:
334 config = fc_atomic_ptr_get (&_fcConfig);
335 if (!config)
336 {
337 unlock_config ();
338
339 config = FcInitLoadConfigAndFonts ();
340 if (!config)
341 goto retry;
342 lock_config ();
343 if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config))
344 {
345 FcConfigDestroy (config);
346 goto retry;
347 }
348 }
349 FcRefInc (&config->ref);
350 unlock_config ();
351 }
352 else
353 FcRefInc (&config->ref);
354
355 return config;
356 }
357
358 void
359 FcConfigDestroy (FcConfig *config)
360 {
361 FcSetName set;
362 FcExprPage *page;
363 FcMatchKind k;
364
365 if (config)
366 {
367 if (FcRefDec (&config->ref) != 1)
368 return;
369
370 (void) fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
371
372 FcStrSetDestroy (config->configDirs);
373 FcStrSetDestroy (config->configMapDirs);
374 FcStrSetDestroy (config->fontDirs);
375 FcStrSetDestroy (config->cacheDirs);
376 FcStrSetDestroy (config->configFiles);
377 FcStrSetDestroy (config->acceptGlobs);
378 FcStrSetDestroy (config->rejectGlobs);
379 FcFontSetDestroy (config->acceptPatterns);
380 FcFontSetDestroy (config->rejectPatterns);
381
382 for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
383 FcPtrListDestroy (config->subst[k]);
384 FcPtrListDestroy (config->rulesetList);
385 FcStrSetDestroy (config->availConfigFiles);
386 for (set = FcSetSystem; set <= FcSetApplication; set++)
387 if (config->fonts[set])
388 FcFontSetDestroy (config->fonts[set]);
389
390 page = config->expr_pool;
391 while (page)
392 {
393 FcExprPage *next = page->next_page;
394 free (page);
395 page = next;
396 }
397 if (config->sysRoot)
398 FcStrFree (config->sysRoot);
399
400 free (config);
401 }
402 }
403
404 /*
405 * Add cache to configuration, adding fonts and directories
406 */
407
408 FcBool
409 FcConfigAddCache (FcConfig *config, FcCache *cache,
410 FcSetName set, FcStrSet *dirSet, FcChar8 *forDir)
411 {
412 FcFontSet *fs;
413 intptr_t *dirs;
414 int i;
415 FcBool relocated = FcFalse;
416
417 if (strcmp ((char *)FcCacheDir(cache), (char *)forDir) != 0)
418 relocated = FcTrue;
419
420 /*
421 * Add fonts
422 */
423 fs = FcCacheSet (cache);
424 if (fs)
425 {
426 int nref = 0;
427
428 for (i = 0; i < fs->nfont; i++)
429 {
430 FcPattern *font = FcFontSetFont (fs, i);
431 FcChar8 *font_file;
432 FcChar8 *relocated_font_file = NULL;
433
434 if (FcPatternObjectGetString (font, FC_FILE_OBJECT,
435 0, &font_file) == FcResultMatch)
436 {
437 if (relocated)
438 {
439 FcChar8 *slash = FcStrLastSlash (font_file);
440 relocated_font_file = FcStrBuildFilename (forDir, slash + 1, NULL);
441 font_file = relocated_font_file;
442 }
443
444 /*
445 * Check to see if font is banned by filename
446 */
447 if (!FcConfigAcceptFilename (config, font_file))
448 {
449 free (relocated_font_file);
450 continue;
451 }
452 }
453
454 /*
455 * Check to see if font is banned by pattern
456 */
457 if (!FcConfigAcceptFont (config, font))
458 {
459 free (relocated_font_file);
460 continue;
461 }
462
463 if (relocated_font_file)
464 {
465 font = FcPatternCacheRewriteFile (font, cache, relocated_font_file);
466 free (relocated_font_file);
467 }
468
469 if (FcFontSetAdd (config->fonts[set], font))
470 nref++;
471 }
472 FcDirCacheReference (cache, nref);
473 }
474
475 /*
476 * Add directories
477 */
478 dirs = FcCacheDirs (cache);
479 if (dirs)
480 {
481 for (i = 0; i < cache->dirs_count; i++)
482 {
483 const FcChar8 *dir = FcCacheSubdir (cache, i);
484 FcChar8 *s = NULL;
485
486 if (relocated)
487 {
488 FcChar8 *base = FcStrBasename (dir);
489 dir = s = FcStrBuildFilename (forDir, base, NULL);
490 FcStrFree (base);
491 }
492 if (FcConfigAcceptFilename (config, dir))
493 FcStrSetAddFilename (dirSet, dir);
494 if (s)
495 FcStrFree (s);
496 }
497 }
498 return FcTrue;
499 }
500
501 static FcBool
502 FcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet)
503 {
504 FcStrList *dirlist;
505 FcChar8 *dir;
506 FcCache *cache;
507
508 dirlist = FcStrListCreate (dirSet);
509 if (!dirlist)
510 return FcFalse;
511
512 while ((dir = FcStrListNext (dirlist)))
513 {
514 if (FcDebug () & FC_DBG_FONTSET)
515 printf ("adding fonts from %s\n", dir);
516 cache = FcDirCacheRead (dir, FcFalse, config);
517 if (!cache)
518 continue;
519 FcConfigAddCache (config, cache, set, dirSet, dir);
520 FcDirCacheUnload (cache);
521 }
522 FcStrListDone (dirlist);
523 return FcTrue;
524 }
525
526 /*
527 * Scan the current list of directories in the configuration
528 * and build the set of available fonts.
529 */
530
531 FcBool
532 FcConfigBuildFonts (FcConfig *config)
533 {
534 FcFontSet *fonts;
535 FcBool ret = FcTrue;
536
537 config = FcConfigReference (config);
538 if (!config)
539 return FcFalse;
540
541 fonts = FcFontSetCreate ();
542 if (!fonts)
543 {
544 ret = FcFalse;
545 goto bail;
546 }
547
548 FcConfigSetFonts (config, fonts, FcSetSystem);
549
550 if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs))
551 {
552 ret = FcFalse;
553 goto bail;
554 }
555 if (FcDebug () & FC_DBG_FONTSET)
556 FcFontSetPrint (fonts);
557 bail:
558 FcConfigDestroy (config);
559
560 return ret;
561 }
562
563 FcBool
564 FcConfigSetCurrent (FcConfig *config)
565 {
566 FcConfig *cfg;
567
568 if (config)
569 {
570 if (!config->fonts[FcSetSystem])
571 if (!FcConfigBuildFonts (config))
572 return FcFalse;
573 FcRefInc (&config->ref);
574 }
575
576 lock_config ();
577 retry:
578 cfg = fc_atomic_ptr_get (&_fcConfig);
579
580 if (config == cfg)
581 {
582 unlock_config ();
583 if (config)
584 FcConfigDestroy (config);
585 return FcTrue;
586 }
587
588 if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
589 goto retry;
590 unlock_config ();
591 if (cfg)
592 FcConfigDestroy (cfg);
593
594 return FcTrue;
595 }
596
597 FcConfig *
598 FcConfigGetCurrent (void)
599 {
600 return FcConfigEnsure ();
601 }
602
603 FcBool
604 FcConfigAddConfigDir (FcConfig *config,
605 const FcChar8 *d)
606 {
607 return FcStrSetAddFilename (config->configDirs, d);
608 }
609
610 FcStrList *
611 FcConfigGetConfigDirs (FcConfig *config)
612 {
613 FcStrList *ret;
614
615 config = FcConfigReference (config);
616 if (!config)
617 return NULL;
618 ret = FcStrListCreate (config->configDirs);
619 FcConfigDestroy (config);
620
621 return ret;
622 }
623
624 FcBool
625 FcConfigAddFontDir (FcConfig *config,
626 const FcChar8 *d,
627 const FcChar8 *m,
628 const FcChar8 *salt)
629 {
630 if (FcDebug() & FC_DBG_CACHE)
631 {
632 if (m)
633 {
634 printf ("%s -> %s%s%s%s\n", d, m, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
635 }
636 else if (salt)
637 {
638 printf ("%s%s%s%s\n", d, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
639 }
640 }
641 return FcStrSetAddFilenamePairWithSalt (config->fontDirs, d, m, salt);
642 }
643
644 FcBool
645 FcConfigResetFontDirs (FcConfig *config)
646 {
647 if (FcDebug() & FC_DBG_CACHE)
648 {
649 printf ("Reset font directories!\n");
650 }
651 return FcStrSetDeleteAll (config->fontDirs);
652 }
653
654 FcStrList *
655 FcConfigGetFontDirs (FcConfig *config)
656 {
657 FcStrList *ret;
658
659 config = FcConfigReference (config);
660 if (!config)
661 return NULL;
662 ret = FcStrListCreate (config->fontDirs);
663 FcConfigDestroy (config);
664
665 return ret;
666 }
667
668 static FcBool
669 FcConfigPathStartsWith(const FcChar8 *path,
670 const FcChar8 *start)
671 {
672 int len = strlen((char *) start);
673
674 if (strncmp((char *) path, (char *) start, len) != 0)
675 return FcFalse;
676
677 switch (path[len]) {
678 case '\0':
679 case FC_DIR_SEPARATOR:
680 return FcTrue;
681 default:
682 return FcFalse;
683 }
684 }
685
686 FcChar8 *
687 FcConfigMapFontPath(FcConfig *config,
688 const FcChar8 *path)
689 {
690 FcStrList *list;
691 FcChar8 *dir;
692 const FcChar8 *map, *rpath;
693 FcChar8 *retval;
694
695 list = FcConfigGetFontDirs(config);
696 if (!list)
697 return 0;
698 while ((dir = FcStrListNext(list)))
699 if (FcConfigPathStartsWith(path, dir))
700 break;
701 FcStrListDone(list);
702 if (!dir)
703 return 0;
704 map = FcStrTripleSecond(dir);
705 if (!map)
706 return 0;
707 rpath = path + strlen ((char *) dir);
708 while (*rpath == '/')
709 rpath++;
710 retval = FcStrBuildFilename(map, rpath, NULL);
711 if (retval)
712 {
713 size_t len = strlen ((const char *) retval);
714 while (len > 0 && retval[len-1] == '/')
715 len--;
716 /* trim the last slash */
717 retval[len] = 0;
718 }
719 return retval;
720 }
721
722 const FcChar8 *
723 FcConfigMapSalt (FcConfig *config,
724 const FcChar8 *path)
725 {
726 FcStrList *list;
727 FcChar8 *dir;
728
729 list = FcConfigGetFontDirs (config);
730 if (!list)
731 return NULL;
732 while ((dir = FcStrListNext (list)))
733 if (FcConfigPathStartsWith (path, dir))
734 break;
735 FcStrListDone (list);
736 if (!dir)
737 return NULL;
738
739 return FcStrTripleThird (dir);
740 }
741
742 FcBool
743 FcConfigAddCacheDir (FcConfig *config,
744 const FcChar8 *d)
745 {
746 return FcStrSetAddFilename (config->cacheDirs, d);
747 }
748
749 FcStrList *
750 FcConfigGetCacheDirs (FcConfig *config)
751 {
752 FcStrList *ret;
753
754 config = FcConfigReference (config);
755 if (!config)
756 return NULL;
757 ret = FcStrListCreate (config->cacheDirs);
758 FcConfigDestroy (config);
759
760 return ret;
761 }
762
763 FcBool
764 FcConfigAddConfigFile (FcConfig *config,
765 const FcChar8 *f)
766 {
767 FcBool ret;
768 FcChar8 *file = FcConfigGetFilename (config, f);
769
770 if (!file)
771 return FcFalse;
772
773 ret = FcStrSetAdd (config->configFiles, file);
774 FcStrFree (file);
775 return ret;
776 }
777
778 FcStrList *
779 FcConfigGetConfigFiles (FcConfig *config)
780 {
781 FcStrList *ret;
782
783 config = FcConfigReference (config);
784 if (!config)
785 return NULL;
786 ret = FcStrListCreate (config->configFiles);
787 FcConfigDestroy (config);
788
789 return ret;
790 }
791
792 FcChar8 *
793 FcConfigGetCache (FcConfig *config FC_UNUSED)
794 {
795 return NULL;
796 }
797
798 FcFontSet *
799 FcConfigGetFonts (FcConfig *config,
800 FcSetName set)
801 {
802 if (!config)
803 {
804 config = FcConfigGetCurrent ();
805 if (!config)
806 return 0;
807 }
808 return config->fonts[set];
809 }
810
811 void
812 FcConfigSetFonts (FcConfig *config,
813 FcFontSet *fonts,
814 FcSetName set)
815 {
816 if (config->fonts[set])
817 FcFontSetDestroy (config->fonts[set]);
818 config->fonts[set] = fonts;
819 }
820
821
822 FcBlanks *
823 FcBlanksCreate (void)
824 {
825 /* Deprecated. */
826 return NULL;
827 }
828
829 void
830 FcBlanksDestroy (FcBlanks *b FC_UNUSED)
831 {
832 /* Deprecated. */
833 }
834
835 FcBool
836 FcBlanksAdd (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
837 {
838 /* Deprecated. */
839 return FcFalse;
840 }
841
842 FcBool
843 FcBlanksIsMember (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
844 {
845 /* Deprecated. */
846 return FcFalse;
847 }
848
849 FcBlanks *
850 FcConfigGetBlanks (FcConfig *config FC_UNUSED)
851 {
852 /* Deprecated. */
853 return NULL;
854 }
855
856 FcBool
857 FcConfigAddBlank (FcConfig *config FC_UNUSED,
858 FcChar32 blank FC_UNUSED)
859 {
860 /* Deprecated. */
861 return FcFalse;
862 }
863
864
865 int
866 FcConfigGetRescanInterval (FcConfig *config)
867 {
868 int ret;
869
870 config = FcConfigReference (config);
871 if (!config)
872 return 0;
873 ret = config->rescanInterval;
874 FcConfigDestroy (config);
875
876 return ret;
877 }
878
879 FcBool
880 FcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
881 {
882 config = FcConfigReference (config);
883 if (!config)
884 return FcFalse;
885 config->rescanInterval = rescanInterval;
886 FcConfigDestroy (config);
887
888 return FcTrue;
889 }
890
891 /*
892 * A couple of typos escaped into the library
893 */
894 int
895 FcConfigGetRescanInverval (FcConfig *config)
896 {
897 return FcConfigGetRescanInterval (config);
898 }
899
900 FcBool
901 FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
902 {
903 return FcConfigSetRescanInterval (config, rescanInterval);
904 }
905
906 FcBool
907 FcConfigAddRule (FcConfig *config,
908 FcRule *rule,
909 FcMatchKind kind)
910 {
911 /* deprecated */
912 return FcFalse;
913 }
914
915 static FcValue
916 FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
917 {
918 switch (v.type)
919 {
920 case FcTypeInteger:
921 v.type = FcTypeDouble;
922 v.u.d = (double) v.u.i;
923 /* Fallthrough */
924 case FcTypeDouble:
925 if (u.type == FcTypeRange && buf)
926 {
927 v.u.r = FcRangePromote (v.u.d, buf);
928 v.type = FcTypeRange;
929 }
930 break;
931 case FcTypeVoid:
932 if (u.type == FcTypeMatrix)
933 {
934 v.u.m = &FcIdentityMatrix;
935 v.type = FcTypeMatrix;
936 }
937 else if (u.type == FcTypeLangSet && buf)
938 {
939 v.u.l = FcLangSetPromote (NULL, buf);
940 v.type = FcTypeLangSet;
941 }
942 else if (u.type == FcTypeCharSet && buf)
943 {
944 v.u.c = FcCharSetPromote (buf);
945 v.type = FcTypeCharSet;
946 }
947 break;
948 case FcTypeString:
949 if (u.type == FcTypeLangSet && buf)
950 {
951 v.u.l = FcLangSetPromote (v.u.s, buf);
952 v.type = FcTypeLangSet;
953 }
954 break;
955 default:
956 break;
957 }
958 return v;
959 }
960
961 FcBool
962 FcConfigCompareValue (const FcValue *left_o,
963 unsigned int op_,
964 const FcValue *right_o)
965 {
966 FcValue left;
967 FcValue right;
968 FcBool ret = FcFalse;
969 FcOp op = FC_OP_GET_OP (op_);
970 int flags = FC_OP_GET_FLAGS (op_);
971 FcValuePromotionBuffer buf1, buf2;
972
973 if (left_o->type != right_o->type)
974 {
975 left = FcValueCanonicalize(left_o);
976 right = FcValueCanonicalize(right_o);
977 left = FcConfigPromote (left, right, &buf1);
978 right = FcConfigPromote (right, left, &buf2);
979 left_o = &left;
980 right_o = &right;
981 if (left_o->type != right_o->type)
982 {
983 if (op == FcOpNotEqual || op == FcOpNotContains)
984 ret = FcTrue;
985 return ret;
986 }
987 }
988 switch (left_o->type) {
989 case FcTypeUnknown:
990 break; /* No way to guess how to compare for this object */
991 case FcTypeInteger: {
992 int l = left_o->u.i;
993 int r = right_o->u.i;
994 switch ((int) op) {
995 case FcOpEqual:
996 case FcOpContains:
997 case FcOpListing:
998 ret = l == r;
999 break;
1000 case FcOpNotEqual:
1001 case FcOpNotContains:
1002 ret = l != r;
1003 break;
1004 case FcOpLess:
1005 ret = l < r;
1006 break;
1007 case FcOpLessEqual:
1008 ret = l <= r;
1009 break;
1010 case FcOpMore:
1011 ret = l > r;
1012 break;
1013 case FcOpMoreEqual:
1014 ret = l >= r;
1015 break;
1016 default:
1017 break;
1018 }
1019 break;
1020 }
1021 case FcTypeDouble: {
1022 double l = left_o->u.d;
1023 double r = right_o->u.d;
1024 switch ((int) op) {
1025 case FcOpEqual:
1026 case FcOpContains:
1027 case FcOpListing:
1028 ret = l == r;
1029 break;
1030 case FcOpNotEqual:
1031 case FcOpNotContains:
1032 ret = l != r;
1033 break;
1034 case FcOpLess:
1035 ret = l < r;
1036 break;
1037 case FcOpLessEqual:
1038 ret = l <= r;
1039 break;
1040 case FcOpMore:
1041 ret = l > r;
1042 break;
1043 case FcOpMoreEqual:
1044 ret = l >= r;
1045 break;
1046 default:
1047 break;
1048 }
1049 break;
1050 }
1051 case FcTypeBool: {
1052 FcBool l = left_o->u.b;
1053 FcBool r = right_o->u.b;
1054 switch ((int) op) {
1055 case FcOpEqual:
1056 ret = l == r;
1057 break;
1058 case FcOpContains:
1059 case FcOpListing:
1060 ret = l == r || l >= FcDontCare;
1061 break;
1062 case FcOpNotEqual:
1063 ret = l != r;
1064 break;
1065 case FcOpNotContains:
1066 ret = !(l == r || l >= FcDontCare);
1067 break;
1068 case FcOpLess:
1069 ret = l != r && r >= FcDontCare;
1070 break;
1071 case FcOpLessEqual:
1072 ret = l == r || r >= FcDontCare;
1073 break;
1074 case FcOpMore:
1075 ret = l != r && l >= FcDontCare;
1076 break;
1077 case FcOpMoreEqual:
1078 ret = l == r || l >= FcDontCare;
1079 break;
1080 default:
1081 break;
1082 }
1083 break;
1084 }
1085 case FcTypeString: {
1086 const FcChar8 *l = FcValueString (left_o);
1087 const FcChar8 *r = FcValueString (right_o);
1088 switch ((int) op) {
1089 case FcOpEqual:
1090 case FcOpListing:
1091 if (flags & FcOpFlagIgnoreBlanks)
1092 ret = FcStrCmpIgnoreBlanksAndCase (l, r) == 0;
1093 else
1094 ret = FcStrCmpIgnoreCase (l, r) == 0;
1095 break;
1096 case FcOpContains:
1097 ret = FcStrStrIgnoreCase (l, r) != 0;
1098 break;
1099 case FcOpNotEqual:
1100 if (flags & FcOpFlagIgnoreBlanks)
1101 ret = FcStrCmpIgnoreBlanksAndCase (l, r) != 0;
1102 else
1103 ret = FcStrCmpIgnoreCase (l, r) != 0;
1104 break;
1105 case FcOpNotContains:
1106 ret = FcStrStrIgnoreCase (l, r) == 0;
1107 break;
1108 default:
1109 break;
1110 }
1111 break;
1112 }
1113 case FcTypeMatrix: {
1114 switch ((int) op) {
1115 case FcOpEqual:
1116 case FcOpContains:
1117 case FcOpListing:
1118 ret = FcMatrixEqual (left_o->u.m, right_o->u.m);
1119 break;
1120 case FcOpNotEqual:
1121 case FcOpNotContains:
1122 ret = !FcMatrixEqual (left_o->u.m, right_o->u.m);
1123 break;
1124 default:
1125 break;
1126 }
1127 break;
1128 }
1129 case FcTypeCharSet: {
1130 const FcCharSet *l = FcValueCharSet (left_o);
1131 const FcCharSet *r = FcValueCharSet (right_o);
1132 switch ((int) op) {
1133 case FcOpContains:
1134 case FcOpListing:
1135 /* left contains right if right is a subset of left */
1136 ret = FcCharSetIsSubset (r, l);
1137 break;
1138 case FcOpNotContains:
1139 /* left contains right if right is a subset of left */
1140 ret = !FcCharSetIsSubset (r, l);
1141 break;
1142 case FcOpEqual:
1143 ret = FcCharSetEqual (l, r);
1144 break;
1145 case FcOpNotEqual:
1146 ret = !FcCharSetEqual (l, r);
1147 break;
1148 default:
1149 break;
1150 }
1151 break;
1152 }
1153 case FcTypeLangSet: {
1154 const FcLangSet *l = FcValueLangSet (left_o);
1155 const FcLangSet *r = FcValueLangSet (right_o);
1156 switch ((int) op) {
1157 case FcOpContains:
1158 case FcOpListing:
1159 ret = FcLangSetContains (l, r);
1160 break;
1161 case FcOpNotContains:
1162 ret = !FcLangSetContains (l, r);
1163 break;
1164 case FcOpEqual:
1165 ret = FcLangSetEqual (l, r);
1166 break;
1167 case FcOpNotEqual:
1168 ret = !FcLangSetEqual (l, r);
1169 break;
1170 default:
1171 break;
1172 }
1173 break;
1174 }
1175 case FcTypeVoid:
1176 switch ((int) op) {
1177 case FcOpEqual:
1178 case FcOpContains:
1179 case FcOpListing:
1180 ret = FcTrue;
1181 break;
1182 default:
1183 break;
1184 }
1185 break;
1186 case FcTypeFTFace:
1187 switch ((int) op) {
1188 case FcOpEqual:
1189 case FcOpContains:
1190 case FcOpListing:
1191 ret = left_o->u.f == right_o->u.f;
1192 break;
1193 case FcOpNotEqual:
1194 case FcOpNotContains:
1195 ret = left_o->u.f != right_o->u.f;
1196 break;
1197 default:
1198 break;
1199 }
1200 break;
1201 case FcTypeRange: {
1202 const FcRange *l = FcValueRange (left_o);
1203 const FcRange *r = FcValueRange (right_o);
1204 ret = FcRangeCompare (op, l, r);
1205 break;
1206 }
1207 }
1208 return ret;
1209 }
1210
1211
1212 #define _FcDoubleFloor(d) ((int) (d))
1213 #define _FcDoubleCeil(d) ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
1214 #define FcDoubleFloor(d) ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
1215 #define FcDoubleCeil(d) ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
1216 #define FcDoubleRound(d) FcDoubleFloor ((d) + 0.5)
1217 #define FcDoubleTrunc(d) ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
1218
1219 static FcValue
1220 FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
1221 {
1222 FcValue v, vl, vr, vle, vre;
1223 FcMatrix *m;
1224 FcChar8 *str;
1225 FcOp op = FC_OP_GET_OP (e->op);
1226 FcValuePromotionBuffer buf1, buf2;
1227
1228 switch ((int) op) {
1229 case FcOpInteger:
1230 v.type = FcTypeInteger;
1231 v.u.i = e->u.ival;
1232 break;
1233 case FcOpDouble:
1234 v.type = FcTypeDouble;
1235 v.u.d = e->u.dval;
1236 break;
1237 case FcOpString:
1238 v.type = FcTypeString;
1239 v.u.s = e->u.sval;
1240 v = FcValueSave (v);
1241 break;
1242 case FcOpMatrix:
1243 {
1244 FcMatrix m;
1245 FcValue xx, xy, yx, yy;
1246 v.type = FcTypeMatrix;
1247 xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xx), v, NULL);
1248 xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xy), v, NULL);
1249 yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yx), v, NULL);
1250 yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yy), v, NULL);
1251 if (xx.type == FcTypeDouble && xy.type == FcTypeDouble &&
1252 yx.type == FcTypeDouble && yy.type == FcTypeDouble)
1253 {
1254 m.xx = xx.u.d;
1255 m.xy = xy.u.d;
1256 m.yx = yx.u.d;
1257 m.yy = yy.u.d;
1258 v.u.m = &m;
1259 }
1260 else
1261 v.type = FcTypeVoid;
1262 v = FcValueSave (v);
1263 }
1264 break;
1265 case FcOpCharSet:
1266 v.type = FcTypeCharSet;
1267 v.u.c = e->u.cval;
1268 v = FcValueSave (v);
1269 break;
1270 case FcOpLangSet:
1271 v.type = FcTypeLangSet;
1272 v.u.l = e->u.lval;
1273 v = FcValueSave (v);
1274 break;
1275 case FcOpRange:
1276 v.type = FcTypeRange;
1277 v.u.r = e->u.rval;
1278 v = FcValueSave (v);
1279 break;
1280 case FcOpBool:
1281 v.type = FcTypeBool;
1282 v.u.b = e->u.bval;
1283 break;
1284 case FcOpField:
1285 if (kind == FcMatchFont && e->u.name.kind == FcMatchPattern)
1286 {
1287 if (FcResultMatch != FcPatternObjectGet (p_pat, e->u.name.object, 0, &v))
1288 v.type = FcTypeVoid;
1289 }
1290 else if (kind == FcMatchPattern && e->u.name.kind == FcMatchFont)
1291 {
1292 fprintf (stderr,
1293 "Fontconfig warning: <name> tag has target=\"font\" in a <match target=\"pattern\">.\n");
1294 v.type = FcTypeVoid;
1295 }
1296 else
1297 {
1298 if (FcResultMatch != FcPatternObjectGet (p, e->u.name.object, 0, &v))
1299 v.type = FcTypeVoid;
1300 }
1301 v = FcValueSave (v);
1302 break;
1303 case FcOpConst:
1304 if (FcNameConstant (e->u.constant, &v.u.i))
1305 v.type = FcTypeInteger;
1306 else
1307 v.type = FcTypeVoid;
1308 break;
1309 case FcOpQuest:
1310 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1311 if (vl.type == FcTypeBool)
1312 {
1313 if (vl.u.b)
1314 v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.left);
1315 else
1316 v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.right);
1317 }
1318 else
1319 v.type = FcTypeVoid;
1320 FcValueDestroy (vl);
1321 break;
1322 case FcOpEqual:
1323 case FcOpNotEqual:
1324 case FcOpLess:
1325 case FcOpLessEqual:
1326 case FcOpMore:
1327 case FcOpMoreEqual:
1328 case FcOpContains:
1329 case FcOpNotContains:
1330 case FcOpListing:
1331 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1332 vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
1333 v.type = FcTypeBool;
1334 v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
1335 FcValueDestroy (vl);
1336 FcValueDestroy (vr);
1337 break;
1338 case FcOpOr:
1339 case FcOpAnd:
1340 case FcOpPlus:
1341 case FcOpMinus:
1342 case FcOpTimes:
1343 case FcOpDivide:
1344 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1345 vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
1346 vle = FcConfigPromote (vl, vr, &buf1);
1347 vre = FcConfigPromote (vr, vle, &buf2);
1348 if (vle.type == vre.type)
1349 {
1350 switch ((int) vle.type) {
1351 case FcTypeDouble:
1352 switch ((int) op) {
1353 case FcOpPlus:
1354 v.type = FcTypeDouble;
1355 v.u.d = vle.u.d + vre.u.d;
1356 break;
1357 case FcOpMinus:
1358 v.type = FcTypeDouble;
1359 v.u.d = vle.u.d - vre.u.d;
1360 break;
1361 case FcOpTimes:
1362 v.type = FcTypeDouble;
1363 v.u.d = vle.u.d * vre.u.d;
1364 break;
1365 case FcOpDivide:
1366 v.type = FcTypeDouble;
1367 v.u.d = vle.u.d / vre.u.d;
1368 break;
1369 default:
1370 v.type = FcTypeVoid;
1371 break;
1372 }
1373 if (v.type == FcTypeDouble &&
1374 v.u.d == (double) (int) v.u.d)
1375 {
1376 v.type = FcTypeInteger;
1377 v.u.i = (int) v.u.d;
1378 }
1379 break;
1380 case FcTypeBool:
1381 switch ((int) op) {
1382 case FcOpOr:
1383 v.type = FcTypeBool;
1384 v.u.b = vle.u.b || vre.u.b;
1385 break;
1386 case FcOpAnd:
1387 v.type = FcTypeBool;
1388 v.u.b = vle.u.b && vre.u.b;
1389 break;
1390 default:
1391 v.type = FcTypeVoid;
1392 break;
1393 }
1394 break;
1395 case FcTypeString:
1396 switch ((int) op) {
1397 case FcOpPlus:
1398 v.type = FcTypeString;
1399 str = FcStrPlus (vle.u.s, vre.u.s);
1400 v.u.s = FcStrdup (str);
1401 FcStrFree (str);
1402
1403 if (!v.u.s)
1404 v.type = FcTypeVoid;
1405 break;
1406 default:
1407 v.type = FcTypeVoid;
1408 break;
1409 }
1410 break;
1411 case FcTypeMatrix:
1412 switch ((int) op) {
1413 case FcOpTimes:
1414 v.type = FcTypeMatrix;
1415 m = malloc (sizeof (FcMatrix));
1416 if (m)
1417 {
1418 FcMatrixMultiply (m, vle.u.m, vre.u.m);
1419 v.u.m = m;
1420 }
1421 else
1422 {
1423 v.type = FcTypeVoid;
1424 }
1425 break;
1426 default:
1427 v.type = FcTypeVoid;
1428 break;
1429 }
1430 break;
1431 case FcTypeCharSet:
1432 switch ((int) op) {
1433 case FcOpPlus:
1434 v.type = FcTypeCharSet;
1435 v.u.c = FcCharSetUnion (vle.u.c, vre.u.c);
1436 if (!v.u.c)
1437 v.type = FcTypeVoid;
1438 break;
1439 case FcOpMinus:
1440 v.type = FcTypeCharSet;
1441 v.u.c = FcCharSetSubtract (vle.u.c, vre.u.c);
1442 if (!v.u.c)
1443 v.type = FcTypeVoid;
1444 break;
1445 default:
1446 v.type = FcTypeVoid;
1447 break;
1448 }
1449 break;
1450 case FcTypeLangSet:
1451 switch ((int) op) {
1452 case FcOpPlus:
1453 v.type = FcTypeLangSet;
1454 v.u.l = FcLangSetUnion (vle.u.l, vre.u.l);
1455 if (!v.u.l)
1456 v.type = FcTypeVoid;
1457 break;
1458 case FcOpMinus:
1459 v.type = FcTypeLangSet;
1460 v.u.l = FcLangSetSubtract (vle.u.l, vre.u.l);
1461 if (!v.u.l)
1462 v.type = FcTypeVoid;
1463 break;
1464 default:
1465 v.type = FcTypeVoid;
1466 break;
1467 }
1468 break;
1469 default:
1470 v.type = FcTypeVoid;
1471 break;
1472 }
1473 }
1474 else
1475 v.type = FcTypeVoid;
1476 FcValueDestroy (vl);
1477 FcValueDestroy (vr);
1478 break;
1479 case FcOpNot:
1480 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1481 switch ((int) vl.type) {
1482 case FcTypeBool:
1483 v.type = FcTypeBool;
1484 v.u.b = !vl.u.b;
1485 break;
1486 default:
1487 v.type = FcTypeVoid;
1488 break;
1489 }
1490 FcValueDestroy (vl);
1491 break;
1492 case FcOpFloor:
1493 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1494 switch ((int) vl.type) {
1495 case FcTypeInteger:
1496 v = vl;
1497 break;
1498 case FcTypeDouble:
1499 v.type = FcTypeInteger;
1500 v.u.i = FcDoubleFloor (vl.u.d);
1501 break;
1502 default:
1503 v.type = FcTypeVoid;
1504 break;
1505 }
1506 FcValueDestroy (vl);
1507 break;
1508 case FcOpCeil:
1509 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1510 switch ((int) vl.type) {
1511 case FcTypeInteger:
1512 v = vl;
1513 break;
1514 case FcTypeDouble:
1515 v.type = FcTypeInteger;
1516 v.u.i = FcDoubleCeil (vl.u.d);
1517 break;
1518 default:
1519 v.type = FcTypeVoid;
1520 break;
1521 }
1522 FcValueDestroy (vl);
1523 break;
1524 case FcOpRound:
1525 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1526 switch ((int) vl.type) {
1527 case FcTypeInteger:
1528 v = vl;
1529 break;
1530 case FcTypeDouble:
1531 v.type = FcTypeInteger;
1532 v.u.i = FcDoubleRound (vl.u.d);
1533 break;
1534 default:
1535 v.type = FcTypeVoid;
1536 break;
1537 }
1538 FcValueDestroy (vl);
1539 break;
1540 case FcOpTrunc:
1541 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1542 switch ((int) vl.type) {
1543 case FcTypeInteger:
1544 v = vl;
1545 break;
1546 case FcTypeDouble:
1547 v.type = FcTypeInteger;
1548 v.u.i = FcDoubleTrunc (vl.u.d);
1549 break;
1550 default:
1551 v.type = FcTypeVoid;
1552 break;
1553 }
1554 FcValueDestroy (vl);
1555 break;
1556 default:
1557 v.type = FcTypeVoid;
1558 break;
1559 }
1560 return v;
1561 }
1562
1563 /* The bulk of the time in FcConfigSubstitute is spent walking
1564 * lists of family names. We speed this up with a hash table.
1565 * Since we need to take the ignore-blanks option into account,
1566 * we use two separate hash tables.
1567 */
1568 typedef struct
1569 {
1570 int count;
1571 } FamilyTableEntry;
1572
1573
1574 typedef struct
1575 {
1576 FcHashTable *family_blank_hash;
1577 FcHashTable *family_hash;
1578 } FamilyTable;
1579
1580 static FcBool
1581 FamilyTableLookup (FamilyTable *table,
1582 FcOp _op,
1583 const FcChar8 *s)
1584 {
1585 FamilyTableEntry *fe;
1586 int flags = FC_OP_GET_FLAGS (_op);
1587 FcHashTable *hash;
1588
1589 if (flags & FcOpFlagIgnoreBlanks)
1590 hash = table->family_blank_hash;
1591 else
1592 hash = table->family_hash;
1593
1594 return FcHashTableFind (hash, (const void *)s, (void **)&fe);
1595 }
1596
1597 static void
1598 FamilyTableAdd (FamilyTable *table,
1599 FcValueListPtr values)
1600 {
1601 FcValueListPtr ll;
1602 for (ll = values; ll; ll = FcValueListNext (ll))
1603 {
1604 const FcChar8 *s = FcValueString (&ll->value);
1605 FamilyTableEntry *fe;
1606
1607 if (!FcHashTableFind (table->family_hash, (const void *)s, (void **)&fe))
1608 {
1609 fe = malloc (sizeof (FamilyTableEntry));
1610 fe->count = 0;
1611 FcHashTableAdd (table->family_hash, (void *)s, fe);
1612 }
1613 fe->count++;
1614
1615 if (!FcHashTableFind (table->family_blank_hash, (const void *)s, (void **)&fe))
1616 {
1617 fe = malloc (sizeof (FamilyTableEntry));
1618 fe->count = 0;
1619 FcHashTableAdd (table->family_blank_hash, (void *)s, fe);
1620 }
1621 fe->count++;
1622 }
1623 }
1624
1625 static void
1626 FamilyTableDel (FamilyTable *table,
1627 const FcChar8 *s)
1628 {
1629 FamilyTableEntry *fe;
1630
1631 if (FcHashTableFind (table->family_hash, (void *)s, (void **)&fe))
1632 {
1633 fe->count--;
1634 if (fe->count == 0)
1635 FcHashTableRemove (table->family_hash, (void *)s);
1636 }
1637
1638 if (FcHashTableFind (table->family_blank_hash, (void *)s, (void **)&fe))
1639 {
1640 fe->count--;
1641 if (fe->count == 0)
1642 FcHashTableRemove (table->family_blank_hash, (void *)s);
1643 }
1644 }
1645
1646 static FcBool
1647 copy_string (const void *src, void **dest)
1648 {
1649 *dest = strdup ((char *)src);
1650 return FcTrue;
1651 }
1652
1653 static void
1654 FamilyTableInit (FamilyTable *table,
1655 FcPattern *p)
1656 {
1657 FcPatternElt *e;
1658
1659 table->family_blank_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
1660 (FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
1661 (FcCopyFunc)copy_string,
1662 NULL,
1663 free,
1664 free);
1665 table->family_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreCase,
1666 (FcCompareFunc)FcStrCmpIgnoreCase,
1667 (FcCopyFunc)copy_string,
1668 NULL,
1669 free,
1670 free);
1671 e = FcPatternObjectFindElt (p, FC_FAMILY_OBJECT);
1672 if (e)
1673 FamilyTableAdd (table, FcPatternEltValues (e));
1674 }
1675
1676 static void
1677 FamilyTableClear (FamilyTable *table)
1678 {
1679 if (table->family_blank_hash)
1680 FcHashTableDestroy (table->family_blank_hash);
1681 if (table->family_hash)
1682 FcHashTableDestroy (table->family_hash);
1683 }
1684
1685 static FcValueList *
1686 FcConfigMatchValueList (FcPattern *p,
1687 FcPattern *p_pat,
1688 FcMatchKind kind,
1689 FcTest *t,
1690 FcValueList *values,
1691 FamilyTable *table)
1692 {
1693 FcValueList *ret = 0;
1694 FcExpr *e = t->expr;
1695 FcValue value;
1696 FcValueList *v;
1697 FcOp op;
1698
1699 while (e)
1700 {
1701 /* Compute the value of the match expression */
1702 if (FC_OP_GET_OP (e->op) == FcOpComma)
1703 {
1704 value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1705 e = e->u.tree.right;
1706 }
1707 else
1708 {
1709 value = FcConfigEvaluate (p, p_pat, kind, e);
1710 e = 0;
1711 }
1712
1713 if (t->object == FC_FAMILY_OBJECT && table)
1714 {
1715 op = FC_OP_GET_OP (t->op);
1716 if (op == FcOpEqual || op == FcOpListing)
1717 {
1718 if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
1719 {
1720 ret = 0;
1721 goto done;
1722 }
1723 }
1724 if (op == FcOpNotEqual && t->qual == FcQualAll)
1725 {
1726 ret = 0;
1727 if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
1728 {
1729 ret = values;
1730 }
1731 goto done;
1732 }
1733 }
1734 for (v = values; v; v = FcValueListNext(v))
1735 {
1736 /* Compare the pattern value to the match expression value */
1737 if (FcConfigCompareValue (&v->value, t->op, &value))
1738 {
1739 if (!ret)
1740 ret = v;
1741 if (t->qual != FcQualAll)
1742 break;
1743 }
1744 else
1745 {
1746 if (t->qual == FcQualAll)
1747 {
1748 ret = 0;
1749 break;
1750 }
1751 }
1752 }
1753 done:
1754 FcValueDestroy (value);
1755 }
1756 return ret;
1757 }
1758
1759 static FcValueList *
1760 FcConfigValues (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e, FcValueBinding binding)
1761 {
1762 FcValueList *l;
1763
1764 if (!e)
1765 return 0;
1766 l = (FcValueList *) malloc (sizeof (FcValueList));
1767 if (!l)
1768 return 0;
1769 if (FC_OP_GET_OP (e->op) == FcOpComma)
1770 {
1771 l->value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
1772 l->next = FcConfigValues (p, p_pat, kind, e->u.tree.right, binding);
1773 }
1774 else
1775 {
1776 l->value = FcConfigEvaluate (p, p_pat, kind, e);
1777 l->next = NULL;
1778 }
1779 l->binding = binding;
1780 if (l->value.type == FcTypeVoid)
1781 {
1782 FcValueList *next = FcValueListNext(l);
1783
1784 free (l);
1785 l = next;
1786 }
1787
1788 return l;
1789 }
1790
1791 static FcBool
1792 FcConfigAdd (FcValueListPtr *head,
1793 FcValueList *position,
1794 FcBool append,
1795 FcValueList *new,
1796 FcObject object,
1797 FamilyTable *table)
1798 {
1799 FcValueListPtr *prev, l, last, v;
1800 FcValueBinding sameBinding;
1801
1802 /*
1803 * Make sure the stored type is valid for built-in objects
1804 */
1805 for (l = new; l != NULL; l = FcValueListNext (l))
1806 {
1807 if (!FcObjectValidType (object, l->value.type))
1808 {
1809 fprintf (stderr,
1810 "Fontconfig warning: FcPattern object %s does not accept value", FcObjectName (object));
1811 FcValuePrintFile (stderr, l->value);
1812 fprintf (stderr, "\n");
1813
1814 if (FcDebug () & FC_DBG_EDIT)
1815 {
1816 printf ("Not adding\n");
1817 }
1818
1819 return FcFalse;
1820 }
1821 }
1822
1823 if (object == FC_FAMILY_OBJECT && table)
1824 {
1825 FamilyTableAdd (table, new);
1826 }
1827
1828 if (position)
1829 sameBinding = position->binding;
1830 else
1831 sameBinding = FcValueBindingWeak;
1832 for (v = new; v != NULL; v = FcValueListNext(v))
1833 if (v->binding == FcValueBindingSame)
1834 v->binding = sameBinding;
1835 if (append)
1836 {
1837 if (position)
1838 prev = &position->next;
1839 else
1840 for (prev = head; *prev != NULL;
1841 prev = &(*prev)->next)
1842 ;
1843 }
1844 else
1845 {
1846 if (position)
1847 {
1848 for (prev = head; *prev != NULL;
1849 prev = &(*prev)->next)
1850 {
1851 if (*prev == position)
1852 break;
1853 }
1854 }
1855 else
1856 prev = head;
1857
1858 if (FcDebug () & FC_DBG_EDIT)
1859 {
1860 if (*prev == NULL)
1861 printf ("position not on list\n");
1862 }
1863 }
1864
1865 if (FcDebug () & FC_DBG_EDIT)
1866 {
1867 printf ("%s list before ", append ? "Append" : "Prepend");
1868 FcValueListPrintWithPosition (*head, *prev);
1869 printf ("\n");
1870 }
1871
1872 if (new)
1873 {
1874 last = new;
1875 while (last->next != NULL)
1876 last = last->next;
1877
1878 last->next = *prev;
1879 *prev = new;
1880 }
1881
1882 if (FcDebug () & FC_DBG_EDIT)
1883 {
1884 printf ("%s list after ", append ? "Append" : "Prepend");
1885 FcValueListPrint (*head);
1886 printf ("\n");
1887 }
1888
1889 return FcTrue;
1890 }
1891
1892 static void
1893 FcConfigDel (FcValueListPtr *head,
1894 FcValueList *position,
1895 FcObject object,
1896 FamilyTable *table)
1897 {
1898 FcValueListPtr *prev;
1899
1900 if (object == FC_FAMILY_OBJECT && table)
1901 {
1902 FamilyTableDel (table, FcValueString (&position->value));
1903 }
1904
1905 for (prev = head; *prev != NULL; prev = &(*prev)->next)
1906 {
1907 if (*prev == position)
1908 {
1909 *prev = position->next;
1910 position->next = NULL;
1911 FcValueListDestroy (position);
1912 break;
1913 }
1914 }
1915 }
1916
1917 static void
1918 FcConfigPatternAdd (FcPattern *p,
1919 FcObject object,
1920 FcValueList *list,
1921 FcBool append,
1922 FamilyTable *table)
1923 {
1924 if (list)
1925 {
1926 FcPatternElt *e = FcPatternObjectInsertElt (p, object);
1927
1928 if (!e)
1929 return;
1930 FcConfigAdd (&e->values, 0, append, list, object, table);
1931 }
1932 }
1933
1934 /*
1935 * Delete all values associated with a field
1936 */
1937 static void
1938 FcConfigPatternDel (FcPattern *p,
1939 FcObject object,
1940 FamilyTable *table)
1941 {
1942 FcPatternElt *e = FcPatternObjectFindElt (p, object);
1943 if (!e)
1944 return;
1945 while (e->values != NULL)
1946 FcConfigDel (&e->values, e->values, object, table);
1947 }
1948
1949 static void
1950 FcConfigPatternCanon (FcPattern *p,
1951 FcObject object)
1952 {
1953 FcPatternElt *e = FcPatternObjectFindElt (p, object);
1954 if (!e)
1955 return;
1956 if (e->values == NULL)
1957 FcPatternObjectDel (p, object);
1958 }
1959
1960 FcBool
1961 FcConfigSubstituteWithPat (FcConfig *config,
1962 FcPattern *p,
1963 FcPattern *p_pat,
1964 FcMatchKind kind)
1965 {
1966 FcValue v;
1967 FcPtrList *s;
1968 FcPtrListIter iter, iter2;
1969 FcRule *r;
1970 FcRuleSet *rs;
1971 FcValueList *l, **value = NULL, *vl;
1972 FcPattern *m;
1973 FcStrSet *strs;
1974 FcObject object = FC_INVALID_OBJECT;
1975 FcPatternElt **elt = NULL, *e;
1976 int i, nobjs;
1977 FcBool retval = FcTrue;
1978 FcTest **tst = NULL;
1979 FamilyTable data;
1980 FamilyTable *table = &data;
1981
1982 if (kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
1983 return FcFalse;
1984
1985 config = FcConfigReference (config);
1986 if (!config)
1987 return FcFalse;
1988
1989 s = config->subst[kind];
1990 if (kind == FcMatchPattern)
1991 {
1992 strs = FcGetDefaultLangs ();
1993 if (strs)
1994 {
1995 FcStrList *l = FcStrListCreate (strs);
1996 FcChar8 *lang;
1997 FcValue v;
1998 FcLangSet *lsund = FcLangSetCreate ();
1999
2000 FcLangSetAdd (lsund, (const FcChar8 *)"und");
2001 FcStrSetDestroy (strs);
2002 while (l && (lang = FcStrListNext (l)))
2003 {
2004 FcPatternElt *e = FcPatternObjectFindElt (p, FC_LANG_OBJECT);
2005
2006 if (e)
2007 {
2008 FcValueListPtr ll;
2009
2010 for (ll = FcPatternEltValues (e); ll; ll = FcValueListNext (ll))
2011 {
2012 FcValue vv = FcValueCanonicalize (&ll->value);
2013
2014 if (vv.type == FcTypeLangSet)
2015 {
2016 FcLangSet *ls = FcLangSetCreate ();
2017 FcBool b;
2018
2019 FcLangSetAdd (ls, lang);
2020 b = FcLangSetContains (vv.u.l, ls);
2021 FcLangSetDestroy (ls);
2022 if (b)
2023 goto bail_lang;
2024 if (FcLangSetContains (vv.u.l, lsund))
2025 goto bail_lang;
2026 }
2027 else
2028 {
2029 if (FcStrCmpIgnoreCase (vv.u.s, lang) == 0)
2030 goto bail_lang;
2031 if (FcStrCmpIgnoreCase (vv.u.s, (const FcChar8 *)"und") == 0)
2032 goto bail_lang;
2033 }
2034 }
2035 }
2036 v.type = FcTypeString;
2037 v.u.s = lang;
2038
2039 FcPatternObjectAddWithBinding (p, FC_LANG_OBJECT, v, FcValueBindingWeak, FcTrue);
2040 }
2041 bail_lang:
2042 FcStrListDone (l);
2043 FcLangSetDestroy (lsund);
2044 }
2045 if (FcPatternObjectGet (p, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch)
2046 {
2047 FcChar8 *prgname = FcGetPrgname ();
2048 if (prgname)
2049 FcPatternObjectAddString (p, FC_PRGNAME_OBJECT, prgname);
2050 }
2051 }
2052
2053 nobjs = FC_MAX_BASE_OBJECT + config->maxObjects + 2;
2054 value = (FcValueList **) malloc (SIZEOF_VOID_P * nobjs);
2055 if (!value)
2056 {
2057 retval = FcFalse;
2058 goto bail1;
2059 }
2060 elt = (FcPatternElt **) malloc (SIZEOF_VOID_P * nobjs);
2061 if (!elt)
2062 {
2063 retval = FcFalse;
2064 goto bail1;
2065 }
2066 tst = (FcTest **) malloc (SIZEOF_VOID_P * nobjs);
2067 if (!tst)
2068 {
2069 retval = FcFalse;
2070 goto bail1;
2071 }
2072
2073 if (FcDebug () & FC_DBG_EDIT)
2074 {
2075 printf ("FcConfigSubstitute ");
2076 FcPatternPrint (p);
2077 }
2078
2079 FamilyTableInit (&data, p);
2080
2081 FcPtrListIterInit (s, &iter);
2082 for (; FcPtrListIterIsValid (s, &iter); FcPtrListIterNext (s, &iter))
2083 {
2084 rs = (FcRuleSet *) FcPtrListIterGetValue (s, &iter);
2085 if (FcDebug () & FC_DBG_EDIT)
2086 {
2087 printf ("\nRule Set: %s\n", rs->name);
2088 }
2089 FcPtrListIterInit (rs->subst[kind], &iter2);
2090 for (; FcPtrListIterIsValid (rs->subst[kind], &iter2); FcPtrListIterNext (rs->subst[kind], &iter2))
2091 {
2092 r = (FcRule *) FcPtrListIterGetValue (rs->subst[kind], &iter2);
2093 for (i = 0; i < nobjs; i++)
2094 {
2095 elt[i] = NULL;
2096 value[i] = NULL;
2097 tst[i] = NULL;
2098 }
2099 for (; r; r = r->next)
2100 {
2101 switch (r->type) {
2102 case FcRuleUnknown:
2103 /* shouldn't be reached */
2104 break;
2105 case FcRuleTest:
2106 object = FC_OBJ_ID (r->u.test->object);
2107 /*
2108 * Check the tests to see if
2109 * they all match the pattern
2110 */
2111 if (FcDebug () & FC_DBG_EDIT)
2112 {
2113 printf ("FcConfigSubstitute test ");
2114 FcTestPrint (r->u.test);
2115 }
2116 if (kind == FcMatchFont && r->u.test->kind == FcMatchPattern)
2117 {
2118 m = p_pat;
2119 table = NULL;
2120 }
2121 else
2122 {
2123 m = p;
2124 table = &data;
2125 }
2126 if (m)
2127 e = FcPatternObjectFindElt (m, r->u.test->object);
2128 else
2129 e = NULL;
2130 /* different 'kind' won't be the target of edit */
2131 if (!elt[object] && kind == r->u.test->kind)
2132 {
2133 elt[object] = e;
2134 tst[object] = r->u.test;
2135 }
2136 /*
2137 * If there's no such field in the font,
2138 * then FcQualAll matches while FcQualAny does not
2139 */
2140 if (!e)
2141 {
2142 if (r->u.test->qual == FcQualAll)
2143 {
2144 value[object] = NULL;
2145 continue;
2146 }
2147 else
2148 {
2149 if (FcDebug () & FC_DBG_EDIT)
2150 printf ("No match\n");
2151 goto bail;
2152 }
2153 }
2154 /*
2155 * Check to see if there is a match, mark the location
2156 * to apply match-relative edits
2157 */
2158 vl = FcConfigMatchValueList (m, p_pat, kind, r->u.test, e->values, table);
2159 /* different 'kind' won't be the target of edit */
2160 if (!value[object] && kind == r->u.test->kind)
2161 value[object] = vl;
2162 if (!vl ||
2163 (r->u.test->qual == FcQualFirst && vl != e->values) ||
2164 (r->u.test->qual == FcQualNotFirst && vl == e->values))
2165 {
2166 if (FcDebug () & FC_DBG_EDIT)
2167 printf ("No match\n");
2168 goto bail;
2169 }
2170 break;
2171 case FcRuleEdit:
2172 object = FC_OBJ_ID (r->u.edit->object);
2173 if (FcDebug () & FC_DBG_EDIT)
2174 {
2175 printf ("Substitute ");
2176 FcEditPrint (r->u.edit);
2177 printf ("\n\n");
2178 }
2179 /*
2180 * Evaluate the list of expressions
2181 */
2182 l = FcConfigValues (p, p_pat, kind, r->u.edit->expr, r->u.edit->binding);
2183 if (tst[object] && (tst[object]->kind == FcMatchFont || kind == FcMatchPattern))
2184 elt[object] = FcPatternObjectFindElt (p, tst[object]->object);
2185
2186 switch (FC_OP_GET_OP (r->u.edit->op)) {
2187 case FcOpAssign:
2188 /*
2189 * If there was a test, then replace the matched
2190 * value with the new list of values
2191 */
2192 if (value[object])
2193 {
2194 FcValueList *thisValue = value[object];
2195 FcValueList *nextValue = l;
2196
2197 /*
2198 * Append the new list of values after the current value
2199 */
2200 FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object, table);
2201 /*
2202 * Delete the marked value
2203 */
2204 if (thisValue)
2205 FcConfigDel (&elt[object]->values, thisValue, object, table);
2206 /*
2207 * Adjust a pointer into the value list to ensure
2208 * future edits occur at the same place
2209 */
2210 value[object] = nextValue;
2211 break;
2212 }
2213 /* fall through ... */
2214 case FcOpAssignReplace:
2215 /*
2216 * Delete all of the values and insert
2217 * the new set
2218 */
2219 FcConfigPatternDel (p, r->u.edit->object, table);
2220 FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
2221 /*
2222 * Adjust a pointer into the value list as they no
2223 * longer point to anything valid
2224 */
2225 value[object] = NULL;
2226 break;
2227 case FcOpPrepend:
2228 if (value[object])
2229 {
2230 FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object, table);
2231 break;
2232 }
2233 /* fall through ... */
2234 case FcOpPrependFirst:
2235 FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse, table);
2236 break;
2237 case FcOpAppend:
2238 if (value[object])
2239 {
2240 FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object, table);
2241 break;
2242 }
2243 /* fall through ... */
2244 case FcOpAppendLast:
2245 FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
2246 break;
2247 case FcOpDelete:
2248 if (value[object])
2249 {
2250 FcConfigDel (&elt[object]->values, value[object], object, table);
2251 FcValueListDestroy (l);
2252 break;
2253 }
2254 /* fall through ... */
2255 case FcOpDeleteAll:
2256 FcConfigPatternDel (p, r->u.edit->object, table);
2257 FcValueListDestroy (l);
2258 break;
2259 default:
2260 FcValueListDestroy (l);
2261 break;
2262 }
2263 /*
2264 * Now go through the pattern and eliminate
2265 * any properties without data
2266 */
2267 FcConfigPatternCanon (p, r->u.edit->object);
2268
2269 if (FcDebug () & FC_DBG_EDIT)
2270 {
2271 printf ("FcConfigSubstitute edit");
2272 FcPatternPrint (p);
2273 }
2274 break;
2275 }
2276 }
2277 bail:;
2278 }
2279 }
2280 if (FcDebug () & FC_DBG_EDIT)
2281 {
2282 printf ("FcConfigSubstitute done");
2283 FcPatternPrint (p);
2284 }
2285 bail1:
2286 FamilyTableClear (&data);
2287 if (elt)
2288 free (elt);
2289 if (value)
2290 free (value);
2291 if (tst)
2292 free (tst);
2293 FcConfigDestroy (config);
2294
2295 return retval;
2296 }
2297
2298 FcBool
2299 FcConfigSubstitute (FcConfig *config,
2300 FcPattern *p,
2301 FcMatchKind kind)
2302 {
2303 return FcConfigSubstituteWithPat (config, p, 0, kind);
2304 }
2305
2306 #if defined (_WIN32)
2307
2308 static FcChar8 fontconfig_path[1000] = ""; /* MT-dontcare */
2309 FcChar8 fontconfig_instprefix[1000] = ""; /* MT-dontcare */
2310
2311 # if (defined (PIC) || defined (DLL_EXPORT))
2312
2313 BOOL WINAPI
2314 DllMain (HINSTANCE hinstDLL,
2315 DWORD fdwReason,
2316 LPVOID lpvReserved);
2317
2318 BOOL WINAPI
2319 DllMain (HINSTANCE hinstDLL,
2320 DWORD fdwReason,
2321 LPVOID lpvReserved)
2322 {
2323 FcChar8 *p;
2324
2325 switch (fdwReason) {
2326 case DLL_PROCESS_ATTACH:
2327 if (!GetModuleFileName ((HMODULE) hinstDLL, (LPCH) fontconfig_path,
2328 sizeof (fontconfig_path)))
2329 break;
2330
2331 /* If the fontconfig DLL is in a "bin" or "lib" subfolder,
2332 * assume it's a Unix-style installation tree, and use
2333 * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
2334 * folder where the DLL is as FONTCONFIG_PATH.
2335 */
2336 p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
2337 if (p)
2338 {
2339 *p = '\0';
2340 p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
2341 if (p && (FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "bin") == 0 ||
2342 FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "lib") == 0))
2343 *p = '\0';
2344 strcat ((char *) fontconfig_instprefix, (char *) fontconfig_path);
2345 strcat ((char *) fontconfig_path, "\\etc\\fonts");
2346 }
2347 else
2348 fontconfig_path[0] = '\0';
2349
2350 break;
2351 }
2352
2353 return TRUE;
2354 }
2355
2356 # endif /* !PIC */
2357
2358 #undef FONTCONFIG_PATH
2359 #define FONTCONFIG_PATH fontconfig_path
2360
2361 #endif /* !_WIN32 */
2362
2363 #ifndef FONTCONFIG_FILE
2364 #define FONTCONFIG_FILE "fonts.conf"
2365 #endif
2366
2367 static FcChar8 *
2368 FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
2369 {
2370 FcChar8 *path;
2371 int size, osize;
2372
2373 if (!dir)
2374 dir = (FcChar8 *) "";
2375
2376 osize = strlen ((char *) dir) + 1 + strlen ((char *) file) + 1;
2377 /*
2378 * workaround valgrind warning because glibc takes advantage of how it knows memory is
2379 * allocated to implement strlen by reading in groups of 4
2380 */
2381 size = (osize + 3) & ~3;
2382
2383 path = malloc (size);
2384 if (!path)
2385 return 0;
2386
2387 strcpy ((char *) path, (const char *) dir);
2388 /* make sure there's a single separator */
2389 #ifdef _WIN32
2390 if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
2391 path[strlen((char *) path)-1] != '\\')) &&
2392 !(file[0] == '/' ||
2393 file[0] == '\\' ||
2394 (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
2395 strcat ((char *) path, "\\");
2396 #else
2397 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
2398 strcat ((char *) path, "/");
2399 else
2400 osize--;
2401 #endif
2402 strcat ((char *) path, (char *) file);
2403
2404 if (access ((char *) path, R_OK) == 0)
2405 return path;
2406
2407 FcStrFree (path);
2408
2409 return 0;
2410 }
2411
2412 static FcChar8 **
2413 FcConfigGetPath (void)
2414 {
2415 FcChar8 **path;
2416 FcChar8 *env, *e, *colon;
2417 FcChar8 *dir;
2418 int npath;
2419 int i;
2420
2421 npath = 2; /* default dir + null */
2422 env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
2423 if (env)
2424 {
2425 e = env;
2426 npath++;
2427 while (*e)
2428 if (*e++ == FC_SEARCH_PATH_SEPARATOR)
2429 npath++;
2430 }
2431 path = calloc (npath, sizeof (FcChar8 *));
2432 if (!path)
2433 goto bail0;
2434 i = 0;
2435
2436 if (env)
2437 {
2438 e = env;
2439 while (*e)
2440 {
2441 colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
2442 if (!colon)
2443 colon = e + strlen ((char *) e);
2444 path[i] = malloc (colon - e + 1);
2445 if (!path[i])
2446 goto bail1;
2447 strncpy ((char *) path[i], (const char *) e, colon - e);
2448 path[i][colon - e] = '\0';
2449 if (*colon)
2450 e = colon + 1;
2451 else
2452 e = colon;
2453 i++;
2454 }
2455 }
2456
2457 #ifdef _WIN32
2458 if (fontconfig_path[0] == '\0')
2459 {
2460 char *p;
2461 if(!GetModuleFileName(NULL, (LPCH) fontconfig_path, sizeof(fontconfig_path)))
2462 goto bail1;
2463 p = strrchr ((const char *) fontconfig_path, '\\');
2464 if (p) *p = '\0';
2465 strcat ((char *) fontconfig_path, "\\fonts");
2466 }
2467 #endif
2468 dir = (FcChar8 *) FONTCONFIG_PATH;
2469 path[i] = malloc (strlen ((char *) dir) + 1);
2470 if (!path[i])
2471 goto bail1;
2472 strcpy ((char *) path[i], (const char *) dir);
2473 return path;
2474
2475 bail1:
2476 for (i = 0; path[i]; i++)
2477 free (path[i]);
2478 free (path);
2479 bail0:
2480 return 0;
2481 }
2482
2483 static void
2484 FcConfigFreePath (FcChar8 **path)
2485 {
2486 FcChar8 **p;
2487
2488 for (p = path; *p; p++)
2489 free (*p);
2490 free (path);
2491 }
2492
2493 static FcBool _FcConfigHomeEnabled = FcTrue; /* MT-goodenough */
2494
2495 FcChar8 *
2496 FcConfigHome (void)
2497 {
2498 if (_FcConfigHomeEnabled)
2499 {
2500 char *home = getenv ("HOME");
2501
2502 #ifdef _WIN32
2503 if (home == NULL)
2504 home = getenv ("USERPROFILE");
2505 #endif
2506
2507 return (FcChar8 *) home;
2508 }
2509 return 0;
2510 }
2511
2512 FcChar8 *
2513 FcConfigXdgCacheHome (void)
2514 {
2515 const char *env = getenv ("XDG_CACHE_HOME");
2516 FcChar8 *ret = NULL;
2517
2518 if (!_FcConfigHomeEnabled)
2519 return NULL;
2520 if (env && env[0])
2521 ret = FcStrCopy ((const FcChar8 *)env);
2522 else
2523 {
2524 const FcChar8 *home = FcConfigHome ();
2525 size_t len = home ? strlen ((const char *)home) : 0;
2526
2527 ret = malloc (len + 7 + 1);
2528 if (ret)
2529 {
2530 if (home)
2531 memcpy (ret, home, len);
2532 memcpy (&ret[len], FC_DIR_SEPARATOR_S ".cache", 7);
2533 ret[len + 7] = 0;
2534 }
2535 }
2536
2537 return ret;
2538 }
2539
2540 FcChar8 *
2541 FcConfigXdgConfigHome (void)
2542 {
2543 const char *env = getenv ("XDG_CONFIG_HOME");
2544 FcChar8 *ret = NULL;
2545
2546 if (!_FcConfigHomeEnabled)
2547 return NULL;
2548 if (env)
2549 ret = FcStrCopy ((const FcChar8 *)env);
2550 else
2551 {
2552 const FcChar8 *home = FcConfigHome ();
2553 size_t len = home ? strlen ((const char *)home) : 0;
2554
2555 ret = malloc (len + 8 + 1);
2556 if (ret)
2557 {
2558 if (home)
2559 memcpy (ret, home, len);
2560 memcpy (&ret[len], FC_DIR_SEPARATOR_S ".config", 8);
2561 ret[len + 8] = 0;
2562 }
2563 }
2564
2565 return ret;
2566 }
2567
2568 FcChar8 *
2569 FcConfigXdgDataHome (void)
2570 {
2571 const char *env = getenv ("XDG_DATA_HOME");
2572 FcChar8 *ret = NULL;
2573
2574 if (!_FcConfigHomeEnabled)
2575 return NULL;
2576 if (env)
2577 ret = FcStrCopy ((const FcChar8 *)env);
2578 else
2579 {
2580 const FcChar8 *home = FcConfigHome ();
2581 size_t len = home ? strlen ((const char *)home) : 0;
2582
2583 ret = malloc (len + 13 + 1);
2584 if (ret)
2585 {
2586 if (home)
2587 memcpy (ret, home, len);
2588 memcpy (&ret[len], FC_DIR_SEPARATOR_S ".local" FC_DIR_SEPARATOR_S "share", 13);
2589 ret[len + 13] = 0;
2590 }
2591 }
2592
2593 return ret;
2594 }
2595
2596 FcStrSet *
2597 FcConfigXdgDataDirs (void)
2598 {
2599 const char *env = getenv ("XDG_DATA_DIRS");
2600 FcStrSet *ret = FcStrSetCreate ();
2601
2602 if (env)
2603 {
2604 FcChar8 *ee, *e = ee = FcStrCopy ((const FcChar8 *) env);
2605
2606 /* We don't intentionally use FC_SEARCH_PATH_SEPARATOR here because of:
2607 * The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
2608 * in doc.
2609 */
2610 while (e)
2611 {
2612 FcChar8 *p = (FcChar8 *) strchr ((const char *) e, ':');
2613 FcChar8 *s;
2614 size_t len;
2615
2616 if (!p)
2617 {
2618 s = FcStrCopy (e);
2619 e = NULL;
2620 }
2621 else
2622 {
2623 *p = 0;
2624 s = FcStrCopy (e);
2625 e = p + 1;
2626 }
2627 len = strlen ((const char *) s);
2628 if (s[len - 1] == FC_DIR_SEPARATOR)
2629 {
2630 do
2631 {
2632 len--;
2633 }
2634 while (len > 1 && s[len - 1] == FC_DIR_SEPARATOR);
2635 s[len] = 0;
2636 }
2637 FcStrSetAdd (ret, s);
2638 FcStrFree (s);
2639 }
2640 FcStrFree (ee);
2641 }
2642 else
2643 {
2644 /* From spec doc at https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
2645 *
2646 * If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
2647 */
2648 FcStrSetAdd (ret, (const FcChar8 *) "/usr/local/share");
2649 FcStrSetAdd (ret, (const FcChar8 *) "/usr/share");
2650 }
2651
2652 return ret;
2653 }
2654
2655 FcBool
2656 FcConfigEnableHome (FcBool enable)
2657 {
2658 FcBool prev = _FcConfigHomeEnabled;
2659 _FcConfigHomeEnabled = enable;
2660 return prev;
2661 }
2662
2663 FcChar8 *
2664 FcConfigGetFilename (FcConfig *config,
2665 const FcChar8 *url)
2666 {
2667 FcChar8 *file, *dir, **path, **p;
2668 const FcChar8 *sysroot;
2669
2670 config = FcConfigReference (config);
2671 if (!config)
2672 return NULL;
2673 sysroot = FcConfigGetSysRoot (config);
2674 if (!url || !*url)
2675 {
2676 url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
2677 if (!url)
2678 url = (FcChar8 *) FONTCONFIG_FILE;
2679 }
2680 file = 0;
2681
2682 if (FcStrIsAbsoluteFilename(url))
2683 {
2684 if (sysroot)
2685 {
2686 size_t len = strlen ((const char *) sysroot);
2687
2688 /* Workaround to avoid adding sysroot repeatedly */
2689 if (strncmp ((const char *) url, (const char *) sysroot, len) == 0)
2690 sysroot = NULL;
2691 }
2692 file = FcConfigFileExists (sysroot, url);
2693 goto bail;
2694 }
2695
2696 if (*url == '~')
2697 {
2698 dir = FcConfigHome ();
2699 if (dir)
2700 {
2701 FcChar8 *s;
2702
2703 if (sysroot)
2704 s = FcStrBuildFilename (sysroot, dir, NULL);
2705 else
2706 s = dir;
2707 file = FcConfigFileExists (s, url + 1);
2708 if (sysroot)
2709 FcStrFree (s);
2710 }
2711 else
2712 file = 0;
2713 }
2714 else
2715 {
2716 path = FcConfigGetPath ();
2717 if (!path)
2718 {
2719 file = NULL;
2720 goto bail;
2721 }
2722 for (p = path; *p; p++)
2723 {
2724 FcChar8 *s;
2725
2726 if (sysroot)
2727 s = FcStrBuildFilename (sysroot, *p, NULL);
2728 else
2729 s = *p;
2730 file = FcConfigFileExists (s, url);
2731 if (sysroot)
2732 FcStrFree (s);
2733 if (file)
2734 break;
2735 }
2736 FcConfigFreePath (path);
2737 }
2738 bail:
2739 FcConfigDestroy (config);
2740
2741 return file;
2742 }
2743
2744 FcChar8 *
2745 FcConfigFilename (const FcChar8 *url)
2746 {
2747 return FcConfigGetFilename (NULL, url);
2748 }
2749
2750 FcChar8 *
2751 FcConfigRealFilename (FcConfig *config,
2752 const FcChar8 *url)
2753 {
2754 FcChar8 *n = FcConfigGetFilename (config, url);
2755
2756 if (n)
2757 {
2758 FcChar8 buf[FC_PATH_MAX];
2759 ssize_t len;
2760 struct stat sb;
2761
2762 if ((len = FcReadLink (n, buf, sizeof (buf) - 1)) != -1)
2763 {
2764 buf[len] = 0;
2765
2766 /* We try to pick up a config from FONTCONFIG_FILE
2767 * when url is null. don't try to address the real filename
2768 * if it is a named pipe.
2769 */
2770 if (!url && FcStat (n, &sb) == 0 && S_ISFIFO (sb.st_mode))
2771 return n;
2772 else if (!FcStrIsAbsoluteFilename (buf))
2773 {
2774 FcChar8 *dirname = FcStrDirname (n);
2775 FcStrFree (n);
2776 if (!dirname)
2777 return NULL;
2778
2779 FcChar8 *path = FcStrBuildFilename (dirname, buf, NULL);
2780 FcStrFree (dirname);
2781 if (!path)
2782 return NULL;
2783
2784 n = FcStrCanonFilename (path);
2785 FcStrFree (path);
2786 }
2787 else
2788 {
2789 FcStrFree (n);
2790 n = FcStrdup (buf);
2791 }
2792 }
2793 }
2794
2795 return n;
2796 }
2797
2798 /*
2799 * Manage the application-specific fonts
2800 */
2801
2802 FcBool
2803 FcConfigAppFontAddFile (FcConfig *config,
2804 const FcChar8 *file)
2805 {
2806 FcFontSet *set;
2807 FcStrSet *subdirs;
2808 FcStrList *sublist;
2809 FcChar8 *subdir;
2810 FcBool ret = FcTrue;
2811
2812 config = FcConfigReference (config);
2813 if (!config)
2814 return FcFalse;
2815
2816 subdirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
2817 if (!subdirs)
2818 {
2819 ret = FcFalse;
2820 goto bail;
2821 }
2822
2823 set = FcConfigGetFonts (config, FcSetApplication);
2824 if (!set)
2825 {
2826 set = FcFontSetCreate ();
2827 if (!set)
2828 {
2829 FcStrSetDestroy (subdirs);
2830 ret = FcFalse;
2831 goto bail;
2832 }
2833 FcConfigSetFonts (config, set, FcSetApplication);
2834 }
2835
2836 if (!FcFileScanConfig (set, subdirs, file, config))
2837 {
2838 FcStrSetDestroy (subdirs);
2839 ret = FcFalse;
2840 goto bail;
2841 }
2842 if ((sublist = FcStrListCreate (subdirs)))
2843 {
2844 while ((subdir = FcStrListNext (sublist)))
2845 {
2846 FcConfigAppFontAddDir (config, subdir);
2847 }
2848 FcStrListDone (sublist);
2849 }
2850 FcStrSetDestroy (subdirs);
2851 bail:
2852 FcConfigDestroy (config);
2853
2854 return ret;
2855 }
2856
2857 FcBool
2858 FcConfigAppFontAddDir (FcConfig *config,
2859 const FcChar8 *dir)
2860 {
2861 FcFontSet *set;
2862 FcStrSet *dirs;
2863 FcBool ret = FcTrue;
2864
2865 config = FcConfigReference (config);
2866 if (!config)
2867 return FcFalse;
2868
2869 dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
2870 if (!dirs)
2871 {
2872 ret = FcFalse;
2873 goto bail;
2874 }
2875
2876 set = FcConfigGetFonts (config, FcSetApplication);
2877 if (!set)
2878 {
2879 set = FcFontSetCreate ();
2880 if (!set)
2881 {
2882 FcStrSetDestroy (dirs);
2883 ret = FcFalse;
2884 goto bail;
2885 }
2886 FcConfigSetFonts (config, set, FcSetApplication);
2887 }
2888
2889 FcStrSetAddFilename (dirs, dir);
2890
2891 if (!FcConfigAddDirList (config, FcSetApplication, dirs))
2892 {
2893 FcStrSetDestroy (dirs);
2894 ret = FcFalse;
2895 goto bail;
2896 }
2897 FcStrSetDestroy (dirs);
2898 bail:
2899 FcConfigDestroy (config);
2900
2901 return ret;
2902 }
2903
2904 void
2905 FcConfigAppFontClear (FcConfig *config)
2906 {
2907 config = FcConfigReference (config);
2908 if (!config)
2909 return;
2910
2911 FcConfigSetFonts (config, 0, FcSetApplication);
2912
2913 FcConfigDestroy (config);
2914 }
2915
2916 /*
2917 * Manage filename-based font source selectors
2918 */
2919
2920 FcBool
2921 FcConfigGlobAdd (FcConfig *config,
2922 const FcChar8 *glob,
2923 FcBool accept)
2924 {
2925 FcStrSet *set = accept ? config->acceptGlobs : config->rejectGlobs;
2926 FcChar8 *realglob = FcStrCopyFilename(glob);
2927 if (!realglob)
2928 return FcFalse;
2929
2930 FcBool ret = FcStrSetAdd (set, realglob);
2931 FcStrFree(realglob);
2932 return ret;
2933 }
2934
2935 static FcBool
2936 FcConfigGlobsMatch (const FcStrSet *globs,
2937 const FcChar8 *string)
2938 {
2939 int i;
2940
2941 for (i = 0; i < globs->num; i++)
2942 if (FcStrGlobMatch (globs->strs[i], string))
2943 return FcTrue;
2944 return FcFalse;
2945 }
2946
2947 FcBool
2948 FcConfigAcceptFilename (FcConfig *config,
2949 const FcChar8 *filename)
2950 {
2951 if (FcConfigGlobsMatch (config->acceptGlobs, filename))
2952 return FcTrue;
2953 if (FcConfigGlobsMatch (config->rejectGlobs, filename))
2954 return FcFalse;
2955 return FcTrue;
2956 }
2957
2958 /*
2959 * Manage font-pattern based font source selectors
2960 */
2961
2962 FcBool
2963 FcConfigPatternsAdd (FcConfig *config,
2964 FcPattern *pattern,
2965 FcBool accept)
2966 {
2967 FcFontSet *set = accept ? config->acceptPatterns : config->rejectPatterns;
2968
2969 return FcFontSetAdd (set, pattern);
2970 }
2971
2972 static FcBool
2973 FcConfigPatternsMatch (const FcFontSet *patterns,
2974 const FcPattern *font)
2975 {
2976 int i;
2977
2978 for (i = 0; i < patterns->nfont; i++)
2979 if (FcListPatternMatchAny (patterns->fonts[i], font))
2980 return FcTrue;
2981 return FcFalse;
2982 }
2983
2984 FcBool
2985 FcConfigAcceptFont (FcConfig *config,
2986 const FcPattern *font)
2987 {
2988 if (FcConfigPatternsMatch (config->acceptPatterns, font))
2989 return FcTrue;
2990 if (FcConfigPatternsMatch (config->rejectPatterns, font))
2991 return FcFalse;
2992 return FcTrue;
2993 }
2994
2995 const FcChar8 *
2996 FcConfigGetSysRoot (const FcConfig *config)
2997 {
2998 if (!config)
2999 {
3000 config = FcConfigGetCurrent ();
3001 if (!config)
3002 return NULL;
3003 }
3004 return config->sysRoot;
3005 }
3006
3007 void
3008 FcConfigSetSysRoot (FcConfig *config,
3009 const FcChar8 *sysroot)
3010 {
3011 FcChar8 *s = NULL;
3012 FcBool init = FcFalse;
3013 int nretry = 3;
3014
3015 retry:
3016 if (!config)
3017 {
3018 /* We can't use FcConfigGetCurrent() here to ensure
3019 * the sysroot is set prior to initialize FcConfig,
3020 * to avoid loading caches from non-sysroot dirs.
3021 * So postpone the initialization later.
3022 */
3023 config = fc_atomic_ptr_get (&_fcConfig);
3024 if (!config)
3025 {
3026 config = FcConfigCreate ();
3027 if (!config)
3028 return;
3029 init = FcTrue;
3030 }
3031 }
3032
3033 if (sysroot)
3034 {
3035 s = FcStrRealPath (sysroot);
3036 if (!s)
3037 return;
3038 }
3039
3040 if (config->sysRoot)
3041 FcStrFree (config->sysRoot);
3042
3043 config->sysRoot = s;
3044 if (init)
3045 {
3046 config = FcInitLoadOwnConfigAndFonts (config);
3047 if (!config)
3048 {
3049 /* Something failed. this is usually unlikely. so retrying */
3050 init = FcFalse;
3051 if (--nretry == 0)
3052 {
3053 fprintf (stderr, "Fontconfig warning: Unable to initialize config and retry limit exceeded. sysroot functionality may not work as expected.\n");
3054 return;
3055 }
3056 goto retry;
3057 }
3058 FcConfigSetCurrent (config);
3059 /* FcConfigSetCurrent() increases the refcount.
3060 * decrease it here to avoid the memory leak.
3061 */
3062 FcConfigDestroy (config);
3063 }
3064 }
3065
3066 FcRuleSet *
3067 FcRuleSetCreate (const FcChar8 *name)
3068 {
3069 FcRuleSet *ret = (FcRuleSet *) malloc (sizeof (FcRuleSet));
3070 FcMatchKind k;
3071 const FcChar8 *p;
3072
3073 if (!name)
3074 p = (const FcChar8 *)"";
3075 else
3076 p = name;
3077
3078 if (ret)
3079 {
3080 ret->name = FcStrdup (p);
3081 ret->description = NULL;
3082 ret->domain = NULL;
3083 for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
3084 ret->subst[k] = FcPtrListCreate (FcDestroyAsRule);
3085 FcRefInit (&ret->ref, 1);
3086 }
3087
3088 return ret;
3089 }
3090
3091 void
3092 FcRuleSetDestroy (FcRuleSet *rs)
3093 {
3094 FcMatchKind k;
3095
3096 if (!rs)
3097 return;
3098 if (FcRefDec (&rs->ref) != 1)
3099 return;
3100
3101 if (rs->name)
3102 FcStrFree (rs->name);
3103 if (rs->description)
3104 FcStrFree (rs->description);
3105 if (rs->domain)
3106 FcStrFree (rs->domain);
3107 for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
3108 FcPtrListDestroy (rs->subst[k]);
3109
3110 free (rs);
3111 }
3112
3113 void
3114 FcRuleSetReference (FcRuleSet *rs)
3115 {
3116 if (!FcRefIsConst (&rs->ref))
3117 FcRefInc (&rs->ref);
3118 }
3119
3120 void
3121 FcRuleSetEnable (FcRuleSet *rs,
3122 FcBool flag)
3123 {
3124 if (rs)
3125 {
3126 rs->enabled = flag;
3127 /* XXX: we may want to provide a feature
3128 * to enable/disable rulesets through API
3129 * in the future?
3130 */
3131 }
3132 }
3133
3134 void
3135 FcRuleSetAddDescription (FcRuleSet *rs,
3136 const FcChar8 *domain,
3137 const FcChar8 *description)
3138 {
3139 if (rs->domain)
3140 FcStrFree (rs->domain);
3141 if (rs->description)
3142 FcStrFree (rs->description);
3143
3144 rs->domain = domain ? FcStrdup (domain) : NULL;
3145 rs->description = description ? FcStrdup (description) : NULL;
3146 }
3147
3148 int
3149 FcRuleSetAdd (FcRuleSet *rs,
3150 FcRule *rule,
3151 FcMatchKind kind)
3152 {
3153 FcPtrListIter iter;
3154 FcRule *r;
3155 int n = 0, ret;
3156
3157 if (!rs ||
3158 kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
3159 return -1;
3160 FcPtrListIterInitAtLast (rs->subst[kind], &iter);
3161 if (!FcPtrListIterAdd (rs->subst[kind], &iter, rule))
3162 return -1;
3163
3164 for (r = rule; r; r = r->next)
3165 {
3166 switch (r->type)
3167 {
3168 case FcRuleTest:
3169 if (r->u.test)
3170 {
3171 if (r->u.test->kind == FcMatchDefault)
3172 r->u.test->kind = kind;
3173 if (n < r->u.test->object)
3174 n = r->u.test->object;
3175 }
3176 break;
3177 case FcRuleEdit:
3178 if (n < r->u.edit->object)
3179 n = r->u.edit->object;
3180 break;
3181 default:
3182 break;
3183 }
3184 }
3185 if (FcDebug () & FC_DBG_EDIT)
3186 {
3187 printf ("Add Rule(kind:%d, name: %s) ", kind, rs->name);
3188 FcRulePrint (rule);
3189 }
3190 ret = FC_OBJ_ID (n) - FC_MAX_BASE_OBJECT;
3191
3192 return ret < 0 ? 0 : ret;
3193 }
3194
3195 void
3196 FcConfigFileInfoIterInit (FcConfig *config,
3197 FcConfigFileInfoIter *iter)
3198 {
3199 FcConfig *c;
3200 FcPtrListIter *i = (FcPtrListIter *)iter;
3201
3202 if (!config)
3203 c = FcConfigGetCurrent ();
3204 else
3205 c = config;
3206 FcPtrListIterInit (c->rulesetList, i);
3207 }
3208
3209 FcBool
3210 FcConfigFileInfoIterNext (FcConfig *config,
3211 FcConfigFileInfoIter *iter)
3212 {
3213 FcConfig *c;
3214 FcPtrListIter *i = (FcPtrListIter *)iter;
3215
3216 if (!config)
3217 c = FcConfigGetCurrent ();
3218 else
3219 c = config;
3220 if (FcPtrListIterIsValid (c->rulesetList, i))
3221 {
3222 FcPtrListIterNext (c->rulesetList, i);
3223 }
3224 else
3225 return FcFalse;
3226
3227 return FcTrue;
3228 }
3229
3230 FcBool
3231 FcConfigFileInfoIterGet (FcConfig *config,
3232 FcConfigFileInfoIter *iter,
3233 FcChar8 **name,
3234 FcChar8 **description,
3235 FcBool *enabled)
3236 {
3237 FcConfig *c;
3238 FcRuleSet *r;
3239 FcPtrListIter *i = (FcPtrListIter *)iter;
3240
3241 if (!config)
3242 c = FcConfigGetCurrent ();
3243 else
3244 c = config;
3245 if (!FcPtrListIterIsValid (c->rulesetList, i))
3246 return FcFalse;
3247 r = FcPtrListIterGetValue (c->rulesetList, i);
3248 if (name)
3249 *name = FcStrdup (r->name && r->name[0] ? r->name : (const FcChar8 *) "fonts.conf");
3250 if (description)
3251 *description = FcStrdup (!r->description ? _("No description") :
3252 dgettext (r->domain ? (const char *) r->domain : GETTEXT_PACKAGE "-conf",
3253 (const char *) r->description));
3254 if (enabled)
3255 *enabled = r->enabled;
3256
3257 return FcTrue;
3258 }
3259
3260 #define __fccfg__
3261 #include "fcaliastail.h"
3262 #undef __fccfg__