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