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