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