fccfg.c revision 18bd4a06
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#include <dirent.h> 29#include <sys/types.h> 30#include "../fc-blanks/fcblanks.h" 31 32#if defined (_WIN32) && !defined (R_OK) 33#define R_OK 4 34#endif 35 36static FcConfig *_fcConfig; /* MT-safe */ 37 38static FcConfig * 39FcConfigEnsure (void) 40{ 41 FcConfig *config; 42retry: 43 config = fc_atomic_ptr_get (&_fcConfig); 44 if (!config) 45 { 46 config = FcInitLoadConfigAndFonts (); 47 48 if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) { 49 FcConfigDestroy (config); 50 goto retry; 51 } 52 } 53 return config; 54} 55 56FcBool 57FcConfigInit (void) 58{ 59 return FcConfigEnsure () ? FcTrue : FcFalse; 60} 61 62void 63FcConfigFini (void) 64{ 65 FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig); 66 if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL)) 67 FcConfigDestroy (cfg); 68} 69 70 71FcConfig * 72FcConfigCreate (void) 73{ 74 FcSetName set; 75 FcConfig *config; 76 77 config = malloc (sizeof (FcConfig)); 78 if (!config) 79 goto bail0; 80 81 config->configDirs = FcStrSetCreate (); 82 if (!config->configDirs) 83 goto bail1; 84 85 config->configFiles = FcStrSetCreate (); 86 if (!config->configFiles) 87 goto bail2; 88 89 config->fontDirs = FcStrSetCreate (); 90 if (!config->fontDirs) 91 goto bail3; 92 93 config->acceptGlobs = FcStrSetCreate (); 94 if (!config->acceptGlobs) 95 goto bail4; 96 97 config->rejectGlobs = FcStrSetCreate (); 98 if (!config->rejectGlobs) 99 goto bail5; 100 101 config->acceptPatterns = FcFontSetCreate (); 102 if (!config->acceptPatterns) 103 goto bail6; 104 105 config->rejectPatterns = FcFontSetCreate (); 106 if (!config->rejectPatterns) 107 goto bail7; 108 109 config->cacheDirs = FcStrSetCreate (); 110 if (!config->cacheDirs) 111 goto bail8; 112 113 config->blanks = &fcBlanks; 114 115 config->substPattern = 0; 116 config->substFont = 0; 117 config->substScan = 0; 118 config->maxObjects = 0; 119 for (set = FcSetSystem; set <= FcSetApplication; set++) 120 config->fonts[set] = 0; 121 122 config->rescanTime = time(0); 123 config->rescanInterval = 30; 124 125 config->expr_pool = NULL; 126 127 config->sysRoot = NULL; 128 129 FcRefInit (&config->ref, 1); 130 131 return config; 132 133bail8: 134 FcFontSetDestroy (config->rejectPatterns); 135bail7: 136 FcFontSetDestroy (config->acceptPatterns); 137bail6: 138 FcStrSetDestroy (config->rejectGlobs); 139bail5: 140 FcStrSetDestroy (config->acceptGlobs); 141bail4: 142 FcStrSetDestroy (config->fontDirs); 143bail3: 144 FcStrSetDestroy (config->configFiles); 145bail2: 146 FcStrSetDestroy (config->configDirs); 147bail1: 148 free (config); 149bail0: 150 return 0; 151} 152 153static FcFileTime 154FcConfigNewestFile (FcStrSet *files) 155{ 156 FcStrList *list = FcStrListCreate (files); 157 FcFileTime newest = { 0, FcFalse }; 158 FcChar8 *file; 159 struct stat statb; 160 161 if (list) 162 { 163 while ((file = FcStrListNext (list))) 164 if (FcStat (file, &statb) == 0) 165 if (!newest.set || statb.st_mtime - newest.time > 0) 166 { 167 newest.set = FcTrue; 168 newest.time = statb.st_mtime; 169 } 170 FcStrListDone (list); 171 } 172 return newest; 173} 174 175FcBool 176FcConfigUptoDate (FcConfig *config) 177{ 178 FcFileTime config_time, config_dir_time, font_time; 179 time_t now = time(0); 180 if (!config) 181 { 182 config = FcConfigGetCurrent (); 183 if (!config) 184 return FcFalse; 185 } 186 config_time = FcConfigNewestFile (config->configFiles); 187 config_dir_time = FcConfigNewestFile (config->configDirs); 188 font_time = FcConfigNewestFile (config->fontDirs); 189 if ((config_time.set && config_time.time - config->rescanTime > 0) || 190 (config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) || 191 (font_time.set && (font_time.time - config->rescanTime) > 0)) 192 { 193 /* We need to check for potential clock problems here (OLPC ticket #6046) */ 194 if ((config_time.set && (config_time.time - now) > 0) || 195 (config_dir_time.set && (config_dir_time.time - now) > 0) || 196 (font_time.set && (font_time.time - now) > 0)) 197 { 198 fprintf (stderr, 199 "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n"); 200 config->rescanTime = now; 201 return FcTrue; 202 } 203 else 204 return FcFalse; 205 } 206 config->rescanTime = now; 207 return FcTrue; 208} 209 210static void 211FcSubstDestroy (FcSubst *s) 212{ 213 FcSubst *n; 214 215 while (s) 216 { 217 n = s->next; 218 if (s->rule) 219 FcRuleDestroy (s->rule); 220 free (s); 221 s = n; 222 } 223} 224 225FcExpr * 226FcConfigAllocExpr (FcConfig *config) 227{ 228 if (!config->expr_pool || config->expr_pool->next == config->expr_pool->end) 229 { 230 FcExprPage *new_page; 231 232 new_page = malloc (sizeof (FcExprPage)); 233 if (!new_page) 234 return 0; 235 236 new_page->next_page = config->expr_pool; 237 new_page->next = new_page->exprs; 238 config->expr_pool = new_page; 239 } 240 241 return config->expr_pool->next++; 242} 243 244FcConfig * 245FcConfigReference (FcConfig *config) 246{ 247 if (!config) 248 { 249 config = FcConfigGetCurrent (); 250 if (!config) 251 return 0; 252 } 253 254 FcRefInc (&config->ref); 255 256 return config; 257} 258 259void 260FcConfigDestroy (FcConfig *config) 261{ 262 FcSetName set; 263 FcExprPage *page; 264 265 if (FcRefDec (&config->ref) != 1) 266 return; 267 268 (void) fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL); 269 270 FcStrSetDestroy (config->configDirs); 271 FcStrSetDestroy (config->fontDirs); 272 FcStrSetDestroy (config->cacheDirs); 273 FcStrSetDestroy (config->configFiles); 274 FcStrSetDestroy (config->acceptGlobs); 275 FcStrSetDestroy (config->rejectGlobs); 276 FcFontSetDestroy (config->acceptPatterns); 277 FcFontSetDestroy (config->rejectPatterns); 278 279 if (config->blanks) 280 FcBlanksDestroy (config->blanks); 281 282 FcSubstDestroy (config->substPattern); 283 FcSubstDestroy (config->substFont); 284 FcSubstDestroy (config->substScan); 285 for (set = FcSetSystem; set <= FcSetApplication; set++) 286 if (config->fonts[set]) 287 FcFontSetDestroy (config->fonts[set]); 288 289 page = config->expr_pool; 290 while (page) 291 { 292 FcExprPage *next = page->next_page; 293 free (page); 294 page = next; 295 } 296 if (config->sysRoot) 297 FcStrFree (config->sysRoot); 298 299 free (config); 300} 301 302/* 303 * Add cache to configuration, adding fonts and directories 304 */ 305 306FcBool 307FcConfigAddCache (FcConfig *config, FcCache *cache, 308 FcSetName set, FcStrSet *dirSet) 309{ 310 FcFontSet *fs; 311 intptr_t *dirs; 312 int i; 313 314 /* 315 * Add fonts 316 */ 317 fs = FcCacheSet (cache); 318 if (fs) 319 { 320 int nref = 0; 321 322 for (i = 0; i < fs->nfont; i++) 323 { 324 FcPattern *font = FcFontSetFont (fs, i); 325 FcChar8 *font_file; 326 327 /* 328 * Check to see if font is banned by filename 329 */ 330 if (FcPatternObjectGetString (font, FC_FILE_OBJECT, 331 0, &font_file) == FcResultMatch && 332 !FcConfigAcceptFilename (config, font_file)) 333 { 334 continue; 335 } 336 337 /* 338 * Check to see if font is banned by pattern 339 */ 340 if (!FcConfigAcceptFont (config, font)) 341 continue; 342 343 if (FcFontSetAdd (config->fonts[set], font)) 344 nref++; 345 } 346 FcDirCacheReference (cache, nref); 347 } 348 349 /* 350 * Add directories 351 */ 352 dirs = FcCacheDirs (cache); 353 if (dirs) 354 { 355 for (i = 0; i < cache->dirs_count; i++) 356 { 357 FcChar8 *dir = FcOffsetToPtr (dirs, dirs[i], FcChar8); 358 if (FcConfigAcceptFilename (config, dir)) 359 FcStrSetAddFilename (dirSet, dir); 360 } 361 } 362 return FcTrue; 363} 364 365static FcBool 366FcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet) 367{ 368 FcStrList *dirlist; 369 FcChar8 *dir; 370 FcCache *cache; 371 372 dirlist = FcStrListCreate (dirSet); 373 if (!dirlist) 374 return FcFalse; 375 376 while ((dir = FcStrListNext (dirlist))) 377 { 378 if (FcDebug () & FC_DBG_FONTSET) 379 printf ("adding fonts from %s\n", dir); 380 cache = FcDirCacheRead (dir, FcFalse, config); 381 if (!cache) 382 continue; 383 FcConfigAddCache (config, cache, set, dirSet); 384 FcDirCacheUnload (cache); 385 } 386 FcStrListDone (dirlist); 387 return FcTrue; 388} 389 390/* 391 * Scan the current list of directories in the configuration 392 * and build the set of available fonts. 393 */ 394 395FcBool 396FcConfigBuildFonts (FcConfig *config) 397{ 398 FcFontSet *fonts; 399 400 if (!config) 401 { 402 config = FcConfigGetCurrent (); 403 if (!config) 404 return FcFalse; 405 } 406 407 fonts = FcFontSetCreate (); 408 if (!fonts) 409 return FcFalse; 410 411 FcConfigSetFonts (config, fonts, FcSetSystem); 412 413 if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs)) 414 return FcFalse; 415 if (FcDebug () & FC_DBG_FONTSET) 416 FcFontSetPrint (fonts); 417 return FcTrue; 418} 419 420FcBool 421FcConfigSetCurrent (FcConfig *config) 422{ 423 FcConfig *cfg; 424 425retry: 426 cfg = fc_atomic_ptr_get (&_fcConfig); 427 428 if (config == cfg) 429 return FcTrue; 430 431 if (config && !config->fonts[FcSetSystem]) 432 if (!FcConfigBuildFonts (config)) 433 return FcFalse; 434 435 if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config)) 436 goto retry; 437 438 FcConfigReference (config); 439 if (cfg) 440 FcConfigDestroy (cfg); 441 442 return FcTrue; 443} 444 445FcConfig * 446FcConfigGetCurrent (void) 447{ 448 return FcConfigEnsure (); 449} 450 451FcBool 452FcConfigAddConfigDir (FcConfig *config, 453 const FcChar8 *d) 454{ 455 return FcStrSetAddFilename (config->configDirs, d); 456} 457 458FcStrList * 459FcConfigGetConfigDirs (FcConfig *config) 460{ 461 if (!config) 462 { 463 config = FcConfigGetCurrent (); 464 if (!config) 465 return 0; 466 } 467 return FcStrListCreate (config->configDirs); 468} 469 470FcBool 471FcConfigAddFontDir (FcConfig *config, 472 const FcChar8 *d) 473{ 474 return FcStrSetAddFilename (config->fontDirs, d); 475} 476 477FcBool 478FcConfigAddDir (FcConfig *config, 479 const FcChar8 *d) 480{ 481 return (FcConfigAddConfigDir (config, d) && 482 FcConfigAddFontDir (config, d)); 483} 484 485FcStrList * 486FcConfigGetFontDirs (FcConfig *config) 487{ 488 if (!config) 489 { 490 config = FcConfigGetCurrent (); 491 if (!config) 492 return 0; 493 } 494 return FcStrListCreate (config->fontDirs); 495} 496 497FcBool 498FcConfigAddCacheDir (FcConfig *config, 499 const FcChar8 *d) 500{ 501 return FcStrSetAddFilename (config->cacheDirs, d); 502} 503 504FcStrList * 505FcConfigGetCacheDirs (const FcConfig *config) 506{ 507 if (!config) 508 { 509 config = FcConfigGetCurrent (); 510 if (!config) 511 return 0; 512 } 513 return FcStrListCreate (config->cacheDirs); 514} 515 516FcBool 517FcConfigAddConfigFile (FcConfig *config, 518 const FcChar8 *f) 519{ 520 FcBool ret; 521 FcChar8 *file = FcConfigFilename (f); 522 523 if (!file) 524 return FcFalse; 525 526 ret = FcStrSetAdd (config->configFiles, file); 527 FcStrFree (file); 528 return ret; 529} 530 531FcStrList * 532FcConfigGetConfigFiles (FcConfig *config) 533{ 534 if (!config) 535 { 536 config = FcConfigGetCurrent (); 537 if (!config) 538 return 0; 539 } 540 return FcStrListCreate (config->configFiles); 541} 542 543FcChar8 * 544FcConfigGetCache (FcConfig *config FC_UNUSED) 545{ 546 return NULL; 547} 548 549FcFontSet * 550FcConfigGetFonts (FcConfig *config, 551 FcSetName set) 552{ 553 if (!config) 554 { 555 config = FcConfigGetCurrent (); 556 if (!config) 557 return 0; 558 } 559 return config->fonts[set]; 560} 561 562void 563FcConfigSetFonts (FcConfig *config, 564 FcFontSet *fonts, 565 FcSetName set) 566{ 567 if (config->fonts[set]) 568 FcFontSetDestroy (config->fonts[set]); 569 config->fonts[set] = fonts; 570} 571 572FcBlanks * 573FcConfigGetBlanks (FcConfig *config) 574{ 575 if (!config) 576 { 577 config = FcConfigGetCurrent (); 578 if (!config) 579 return 0; 580 } 581 return config->blanks; 582} 583 584FcBool 585FcConfigAddBlank (FcConfig *config, 586 FcChar32 blank) 587{ 588 FcBlanks *b, *freeme = 0; 589 590 b = config->blanks; 591 if (!b) 592 { 593 freeme = b = FcBlanksCreate (); 594 if (!b) 595 return FcFalse; 596 } 597 if (!FcBlanksAdd (b, blank)) 598 { 599 if (freeme) 600 FcBlanksDestroy (freeme); 601 return FcFalse; 602 } 603 config->blanks = b; 604 return FcTrue; 605} 606 607int 608FcConfigGetRescanInterval (FcConfig *config) 609{ 610 if (!config) 611 { 612 config = FcConfigGetCurrent (); 613 if (!config) 614 return 0; 615 } 616 return config->rescanInterval; 617} 618 619FcBool 620FcConfigSetRescanInterval (FcConfig *config, int rescanInterval) 621{ 622 if (!config) 623 { 624 config = FcConfigGetCurrent (); 625 if (!config) 626 return FcFalse; 627 } 628 config->rescanInterval = rescanInterval; 629 return FcTrue; 630} 631 632/* 633 * A couple of typos escaped into the library 634 */ 635int 636FcConfigGetRescanInverval (FcConfig *config) 637{ 638 return FcConfigGetRescanInterval (config); 639} 640 641FcBool 642FcConfigSetRescanInverval (FcConfig *config, int rescanInterval) 643{ 644 return FcConfigSetRescanInterval (config, rescanInterval); 645} 646 647FcBool 648FcConfigAddRule (FcConfig *config, 649 FcRule *rule, 650 FcMatchKind kind) 651{ 652 FcSubst *subst, **prev; 653 FcRule *r; 654 int n = 0; 655 656 if (!rule) 657 return FcFalse; 658 switch (kind) { 659 case FcMatchPattern: 660 prev = &config->substPattern; 661 break; 662 case FcMatchFont: 663 prev = &config->substFont; 664 break; 665 case FcMatchScan: 666 prev = &config->substScan; 667 break; 668 default: 669 return FcFalse; 670 } 671 subst = (FcSubst *) malloc (sizeof (FcSubst)); 672 if (!subst) 673 return FcFalse; 674 for (; *prev; prev = &(*prev)->next); 675 *prev = subst; 676 subst->next = NULL; 677 subst->rule = rule; 678 for (r = rule; r; r = r->next) 679 { 680 switch (r->type) 681 { 682 case FcRuleTest: 683 if (r->u.test && 684 r->u.test->kind == FcMatchDefault) 685 r->u.test->kind = kind; 686 687 if (n < r->u.test->object) 688 n = r->u.test->object; 689 break; 690 case FcRuleEdit: 691 if (n < r->u.edit->object) 692 n = r->u.edit->object; 693 break; 694 default: 695 break; 696 } 697 } 698 n = FC_OBJ_ID (n) - FC_MAX_BASE_OBJECT; 699 if (config->maxObjects < n) 700 config->maxObjects = n; 701 if (FcDebug () & FC_DBG_EDIT) 702 { 703 printf ("Add Subst "); 704 FcSubstPrint (subst); 705 } 706 return FcTrue; 707} 708 709static FcValue 710FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf) 711{ 712 if (v.type == FcTypeInteger) 713 { 714 v.type = FcTypeDouble; 715 v.u.d = (double) v.u.i; 716 } 717 else if (v.type == FcTypeVoid && u.type == FcTypeMatrix) 718 { 719 v.u.m = &FcIdentityMatrix; 720 v.type = FcTypeMatrix; 721 } 722 else if (buf && v.type == FcTypeString && u.type == FcTypeLangSet) 723 { 724 v.u.l = FcLangSetPromote (v.u.s, buf); 725 v.type = FcTypeLangSet; 726 } 727 else if (v.type == FcTypeVoid && u.type == FcTypeLangSet) 728 { 729 v.u.l = FcLangSetPromote (NULL, buf); 730 v.type = FcTypeLangSet; 731 } 732 else if (v.type == FcTypeVoid && u.type == FcTypeCharSet) 733 { 734 v.u.c = FcCharSetPromote (buf); 735 v.type = FcTypeCharSet; 736 } 737 if (buf && v.type == FcTypeDouble && u.type == FcTypeRange) 738 { 739 v.u.r = FcRangePromote (v.u.d, buf); 740 v.type = FcTypeRange; 741 } 742 return v; 743} 744 745FcBool 746FcConfigCompareValue (const FcValue *left_o, 747 unsigned int op_, 748 const FcValue *right_o) 749{ 750 FcValue left = FcValueCanonicalize(left_o); 751 FcValue right = FcValueCanonicalize(right_o); 752 FcBool ret = FcFalse; 753 FcOp op = FC_OP_GET_OP (op_); 754 int flags = FC_OP_GET_FLAGS (op_); 755 FcValuePromotionBuffer buf1, buf2; 756 757 left = FcConfigPromote (left, right, &buf1); 758 right = FcConfigPromote (right, left, &buf2); 759 if (left.type == right.type) 760 { 761 switch (left.type) { 762 case FcTypeUnknown: 763 break; /* No way to guess how to compare for this object */ 764 case FcTypeInteger: 765 break; /* FcConfigPromote prevents this from happening */ 766 case FcTypeDouble: 767 switch ((int) op) { 768 case FcOpEqual: 769 case FcOpContains: 770 case FcOpListing: 771 ret = left.u.d == right.u.d; 772 break; 773 case FcOpNotEqual: 774 case FcOpNotContains: 775 ret = left.u.d != right.u.d; 776 break; 777 case FcOpLess: 778 ret = left.u.d < right.u.d; 779 break; 780 case FcOpLessEqual: 781 ret = left.u.d <= right.u.d; 782 break; 783 case FcOpMore: 784 ret = left.u.d > right.u.d; 785 break; 786 case FcOpMoreEqual: 787 ret = left.u.d >= right.u.d; 788 break; 789 default: 790 break; 791 } 792 break; 793 case FcTypeBool: 794 switch ((int) op) { 795 case FcOpEqual: 796 case FcOpContains: 797 case FcOpListing: 798 ret = left.u.b == right.u.b; 799 break; 800 case FcOpNotEqual: 801 case FcOpNotContains: 802 ret = left.u.b != right.u.b; 803 break; 804 default: 805 break; 806 } 807 break; 808 case FcTypeString: 809 switch ((int) op) { 810 case FcOpEqual: 811 case FcOpListing: 812 if (flags & FcOpFlagIgnoreBlanks) 813 ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) == 0; 814 else 815 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0; 816 break; 817 case FcOpContains: 818 ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0; 819 break; 820 case FcOpNotEqual: 821 if (flags & FcOpFlagIgnoreBlanks) 822 ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) != 0; 823 else 824 ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0; 825 break; 826 case FcOpNotContains: 827 ret = FcStrStrIgnoreCase (left.u.s, right.u.s) == 0; 828 break; 829 default: 830 break; 831 } 832 break; 833 case FcTypeMatrix: 834 switch ((int) op) { 835 case FcOpEqual: 836 case FcOpContains: 837 case FcOpListing: 838 ret = FcMatrixEqual (left.u.m, right.u.m); 839 break; 840 case FcOpNotEqual: 841 case FcOpNotContains: 842 ret = !FcMatrixEqual (left.u.m, right.u.m); 843 break; 844 default: 845 break; 846 } 847 break; 848 case FcTypeCharSet: 849 switch ((int) op) { 850 case FcOpContains: 851 case FcOpListing: 852 /* left contains right if right is a subset of left */ 853 ret = FcCharSetIsSubset (right.u.c, left.u.c); 854 break; 855 case FcOpNotContains: 856 /* left contains right if right is a subset of left */ 857 ret = !FcCharSetIsSubset (right.u.c, left.u.c); 858 break; 859 case FcOpEqual: 860 ret = FcCharSetEqual (left.u.c, right.u.c); 861 break; 862 case FcOpNotEqual: 863 ret = !FcCharSetEqual (left.u.c, right.u.c); 864 break; 865 default: 866 break; 867 } 868 break; 869 case FcTypeLangSet: 870 switch ((int) op) { 871 case FcOpContains: 872 case FcOpListing: 873 ret = FcLangSetContains (left.u.l, right.u.l); 874 break; 875 case FcOpNotContains: 876 ret = !FcLangSetContains (left.u.l, right.u.l); 877 break; 878 case FcOpEqual: 879 ret = FcLangSetEqual (left.u.l, right.u.l); 880 break; 881 case FcOpNotEqual: 882 ret = !FcLangSetEqual (left.u.l, right.u.l); 883 break; 884 default: 885 break; 886 } 887 break; 888 case FcTypeVoid: 889 switch ((int) op) { 890 case FcOpEqual: 891 case FcOpContains: 892 case FcOpListing: 893 ret = FcTrue; 894 break; 895 default: 896 break; 897 } 898 break; 899 case FcTypeFTFace: 900 switch ((int) op) { 901 case FcOpEqual: 902 case FcOpContains: 903 case FcOpListing: 904 ret = left.u.f == right.u.f; 905 break; 906 case FcOpNotEqual: 907 case FcOpNotContains: 908 ret = left.u.f != right.u.f; 909 break; 910 default: 911 break; 912 } 913 break; 914 case FcTypeRange: 915 ret = FcRangeCompare (op, left.u.r, right.u.r); 916 break; 917 } 918 } 919 else 920 { 921 if (op == FcOpNotEqual || op == FcOpNotContains) 922 ret = FcTrue; 923 } 924 return ret; 925} 926 927 928#define _FcDoubleFloor(d) ((int) (d)) 929#define _FcDoubleCeil(d) ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1)) 930#define FcDoubleFloor(d) ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d))) 931#define FcDoubleCeil(d) ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d))) 932#define FcDoubleRound(d) FcDoubleFloor ((d) + 0.5) 933#define FcDoubleTrunc(d) ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d))) 934 935static FcValue 936FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e) 937{ 938 FcValue v, vl, vr, vle, vre; 939 FcMatrix *m; 940 FcChar8 *str; 941 FcOp op = FC_OP_GET_OP (e->op); 942 FcValuePromotionBuffer buf1, buf2; 943 944 switch ((int) op) { 945 case FcOpInteger: 946 v.type = FcTypeInteger; 947 v.u.i = e->u.ival; 948 break; 949 case FcOpDouble: 950 v.type = FcTypeDouble; 951 v.u.d = e->u.dval; 952 break; 953 case FcOpString: 954 v.type = FcTypeString; 955 v.u.s = e->u.sval; 956 v = FcValueSave (v); 957 break; 958 case FcOpMatrix: 959 { 960 FcMatrix m; 961 FcValue xx, xy, yx, yy; 962 v.type = FcTypeMatrix; 963 xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xx), v, NULL); 964 xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xy), v, NULL); 965 yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yx), v, NULL); 966 yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yy), v, NULL); 967 if (xx.type == FcTypeDouble && xy.type == FcTypeDouble && 968 yx.type == FcTypeDouble && yy.type == FcTypeDouble) 969 { 970 m.xx = xx.u.d; 971 m.xy = xy.u.d; 972 m.yx = yx.u.d; 973 m.yy = yy.u.d; 974 v.u.m = &m; 975 } 976 else 977 v.type = FcTypeVoid; 978 v = FcValueSave (v); 979 } 980 break; 981 case FcOpCharSet: 982 v.type = FcTypeCharSet; 983 v.u.c = e->u.cval; 984 v = FcValueSave (v); 985 break; 986 case FcOpLangSet: 987 v.type = FcTypeLangSet; 988 v.u.l = e->u.lval; 989 v = FcValueSave (v); 990 break; 991 case FcOpRange: 992 v.type = FcTypeRange; 993 v.u.r = e->u.rval; 994 v = FcValueSave (v); 995 break; 996 case FcOpBool: 997 v.type = FcTypeBool; 998 v.u.b = e->u.bval; 999 break; 1000 case FcOpField: 1001 if (kind == FcMatchFont && e->u.name.kind == FcMatchPattern) 1002 { 1003 if (FcResultMatch != FcPatternObjectGet (p_pat, e->u.name.object, 0, &v)) 1004 v.type = FcTypeVoid; 1005 } 1006 else if (kind == FcMatchPattern && e->u.name.kind == FcMatchFont) 1007 { 1008 fprintf (stderr, 1009 "Fontconfig warning: <name> tag has target=\"font\" in a <match target=\"pattern\">.\n"); 1010 v.type = FcTypeVoid; 1011 } 1012 else 1013 { 1014 if (FcResultMatch != FcPatternObjectGet (p, e->u.name.object, 0, &v)) 1015 v.type = FcTypeVoid; 1016 } 1017 v = FcValueSave (v); 1018 break; 1019 case FcOpConst: 1020 if (FcNameConstant (e->u.constant, &v.u.i)) 1021 v.type = FcTypeInteger; 1022 else 1023 v.type = FcTypeVoid; 1024 break; 1025 case FcOpQuest: 1026 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1027 if (vl.type == FcTypeBool) 1028 { 1029 if (vl.u.b) 1030 v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.left); 1031 else 1032 v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.right); 1033 } 1034 else 1035 v.type = FcTypeVoid; 1036 FcValueDestroy (vl); 1037 break; 1038 case FcOpEqual: 1039 case FcOpNotEqual: 1040 case FcOpLess: 1041 case FcOpLessEqual: 1042 case FcOpMore: 1043 case FcOpMoreEqual: 1044 case FcOpContains: 1045 case FcOpNotContains: 1046 case FcOpListing: 1047 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1048 vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right); 1049 v.type = FcTypeBool; 1050 v.u.b = FcConfigCompareValue (&vl, e->op, &vr); 1051 FcValueDestroy (vl); 1052 FcValueDestroy (vr); 1053 break; 1054 case FcOpOr: 1055 case FcOpAnd: 1056 case FcOpPlus: 1057 case FcOpMinus: 1058 case FcOpTimes: 1059 case FcOpDivide: 1060 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1061 vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right); 1062 vle = FcConfigPromote (vl, vr, &buf1); 1063 vre = FcConfigPromote (vr, vle, &buf2); 1064 if (vle.type == vre.type) 1065 { 1066 switch ((int) vle.type) { 1067 case FcTypeDouble: 1068 switch ((int) op) { 1069 case FcOpPlus: 1070 v.type = FcTypeDouble; 1071 v.u.d = vle.u.d + vre.u.d; 1072 break; 1073 case FcOpMinus: 1074 v.type = FcTypeDouble; 1075 v.u.d = vle.u.d - vre.u.d; 1076 break; 1077 case FcOpTimes: 1078 v.type = FcTypeDouble; 1079 v.u.d = vle.u.d * vre.u.d; 1080 break; 1081 case FcOpDivide: 1082 v.type = FcTypeDouble; 1083 v.u.d = vle.u.d / vre.u.d; 1084 break; 1085 default: 1086 v.type = FcTypeVoid; 1087 break; 1088 } 1089 if (v.type == FcTypeDouble && 1090 v.u.d == (double) (int) v.u.d) 1091 { 1092 v.type = FcTypeInteger; 1093 v.u.i = (int) v.u.d; 1094 } 1095 break; 1096 case FcTypeBool: 1097 switch ((int) op) { 1098 case FcOpOr: 1099 v.type = FcTypeBool; 1100 v.u.b = vle.u.b || vre.u.b; 1101 break; 1102 case FcOpAnd: 1103 v.type = FcTypeBool; 1104 v.u.b = vle.u.b && vre.u.b; 1105 break; 1106 default: 1107 v.type = FcTypeVoid; 1108 break; 1109 } 1110 break; 1111 case FcTypeString: 1112 switch ((int) op) { 1113 case FcOpPlus: 1114 v.type = FcTypeString; 1115 str = FcStrPlus (vle.u.s, vre.u.s); 1116 v.u.s = FcStrdup (str); 1117 FcStrFree (str); 1118 1119 if (!v.u.s) 1120 v.type = FcTypeVoid; 1121 break; 1122 default: 1123 v.type = FcTypeVoid; 1124 break; 1125 } 1126 break; 1127 case FcTypeMatrix: 1128 switch ((int) op) { 1129 case FcOpTimes: 1130 v.type = FcTypeMatrix; 1131 m = malloc (sizeof (FcMatrix)); 1132 if (m) 1133 { 1134 FcMatrixMultiply (m, vle.u.m, vre.u.m); 1135 v.u.m = m; 1136 } 1137 else 1138 { 1139 v.type = FcTypeVoid; 1140 } 1141 break; 1142 default: 1143 v.type = FcTypeVoid; 1144 break; 1145 } 1146 break; 1147 case FcTypeCharSet: 1148 switch ((int) op) { 1149 case FcOpPlus: 1150 v.type = FcTypeCharSet; 1151 v.u.c = FcCharSetUnion (vle.u.c, vre.u.c); 1152 if (!v.u.c) 1153 v.type = FcTypeVoid; 1154 break; 1155 case FcOpMinus: 1156 v.type = FcTypeCharSet; 1157 v.u.c = FcCharSetSubtract (vle.u.c, vre.u.c); 1158 if (!v.u.c) 1159 v.type = FcTypeVoid; 1160 break; 1161 default: 1162 v.type = FcTypeVoid; 1163 break; 1164 } 1165 break; 1166 case FcTypeLangSet: 1167 switch ((int) op) { 1168 case FcOpPlus: 1169 v.type = FcTypeLangSet; 1170 v.u.l = FcLangSetUnion (vle.u.l, vre.u.l); 1171 if (!v.u.l) 1172 v.type = FcTypeVoid; 1173 break; 1174 case FcOpMinus: 1175 v.type = FcTypeLangSet; 1176 v.u.l = FcLangSetSubtract (vle.u.l, vre.u.l); 1177 if (!v.u.l) 1178 v.type = FcTypeVoid; 1179 break; 1180 default: 1181 v.type = FcTypeVoid; 1182 break; 1183 } 1184 break; 1185 default: 1186 v.type = FcTypeVoid; 1187 break; 1188 } 1189 } 1190 else 1191 v.type = FcTypeVoid; 1192 FcValueDestroy (vl); 1193 FcValueDestroy (vr); 1194 break; 1195 case FcOpNot: 1196 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1197 switch ((int) vl.type) { 1198 case FcTypeBool: 1199 v.type = FcTypeBool; 1200 v.u.b = !vl.u.b; 1201 break; 1202 default: 1203 v.type = FcTypeVoid; 1204 break; 1205 } 1206 FcValueDestroy (vl); 1207 break; 1208 case FcOpFloor: 1209 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1210 switch ((int) vl.type) { 1211 case FcTypeInteger: 1212 v = vl; 1213 break; 1214 case FcTypeDouble: 1215 v.type = FcTypeInteger; 1216 v.u.i = FcDoubleFloor (vl.u.d); 1217 break; 1218 default: 1219 v.type = FcTypeVoid; 1220 break; 1221 } 1222 FcValueDestroy (vl); 1223 break; 1224 case FcOpCeil: 1225 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1226 switch ((int) vl.type) { 1227 case FcTypeInteger: 1228 v = vl; 1229 break; 1230 case FcTypeDouble: 1231 v.type = FcTypeInteger; 1232 v.u.i = FcDoubleCeil (vl.u.d); 1233 break; 1234 default: 1235 v.type = FcTypeVoid; 1236 break; 1237 } 1238 FcValueDestroy (vl); 1239 break; 1240 case FcOpRound: 1241 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1242 switch ((int) vl.type) { 1243 case FcTypeInteger: 1244 v = vl; 1245 break; 1246 case FcTypeDouble: 1247 v.type = FcTypeInteger; 1248 v.u.i = FcDoubleRound (vl.u.d); 1249 break; 1250 default: 1251 v.type = FcTypeVoid; 1252 break; 1253 } 1254 FcValueDestroy (vl); 1255 break; 1256 case FcOpTrunc: 1257 vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1258 switch ((int) vl.type) { 1259 case FcTypeInteger: 1260 v = vl; 1261 break; 1262 case FcTypeDouble: 1263 v.type = FcTypeInteger; 1264 v.u.i = FcDoubleTrunc (vl.u.d); 1265 break; 1266 default: 1267 v.type = FcTypeVoid; 1268 break; 1269 } 1270 FcValueDestroy (vl); 1271 break; 1272 default: 1273 v.type = FcTypeVoid; 1274 break; 1275 } 1276 return v; 1277} 1278 1279static FcValueList * 1280FcConfigMatchValueList (FcPattern *p, 1281 FcPattern *p_pat, 1282 FcMatchKind kind, 1283 FcTest *t, 1284 FcValueList *values) 1285{ 1286 FcValueList *ret = 0; 1287 FcExpr *e = t->expr; 1288 FcValue value; 1289 FcValueList *v; 1290 1291 while (e) 1292 { 1293 /* Compute the value of the match expression */ 1294 if (FC_OP_GET_OP (e->op) == FcOpComma) 1295 { 1296 value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1297 e = e->u.tree.right; 1298 } 1299 else 1300 { 1301 value = FcConfigEvaluate (p, p_pat, kind, e); 1302 e = 0; 1303 } 1304 1305 for (v = values; v; v = FcValueListNext(v)) 1306 { 1307 /* Compare the pattern value to the match expression value */ 1308 if (FcConfigCompareValue (&v->value, t->op, &value)) 1309 { 1310 if (!ret) 1311 ret = v; 1312 } 1313 else 1314 { 1315 if (t->qual == FcQualAll) 1316 { 1317 ret = 0; 1318 break; 1319 } 1320 } 1321 } 1322 FcValueDestroy (value); 1323 } 1324 return ret; 1325} 1326 1327static FcValueList * 1328FcConfigValues (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e, FcValueBinding binding) 1329{ 1330 FcValueList *l; 1331 1332 if (!e) 1333 return 0; 1334 l = (FcValueList *) malloc (sizeof (FcValueList)); 1335 if (!l) 1336 return 0; 1337 if (FC_OP_GET_OP (e->op) == FcOpComma) 1338 { 1339 l->value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left); 1340 l->next = FcConfigValues (p, p_pat, kind, e->u.tree.right, binding); 1341 } 1342 else 1343 { 1344 l->value = FcConfigEvaluate (p, p_pat, kind, e); 1345 l->next = NULL; 1346 } 1347 l->binding = binding; 1348 if (l->value.type == FcTypeVoid) 1349 { 1350 FcValueList *next = FcValueListNext(l); 1351 1352 free (l); 1353 l = next; 1354 } 1355 1356 return l; 1357} 1358 1359static FcBool 1360FcConfigAdd (FcValueListPtr *head, 1361 FcValueList *position, 1362 FcBool append, 1363 FcValueList *new, 1364 FcObject object) 1365{ 1366 FcValueListPtr *prev, l, last, v; 1367 FcValueBinding sameBinding; 1368 1369 /* 1370 * Make sure the stored type is valid for built-in objects 1371 */ 1372 for (l = new; l != NULL; l = FcValueListNext (l)) 1373 { 1374 if (!FcObjectValidType (object, l->value.type)) 1375 { 1376 fprintf (stderr, 1377 "Fontconfig warning: FcPattern object %s does not accept value", FcObjectName (object)); 1378 FcValuePrintFile (stderr, l->value); 1379 fprintf (stderr, "\n"); 1380 1381 if (FcDebug () & FC_DBG_EDIT) 1382 { 1383 printf ("Not adding\n"); 1384 } 1385 1386 return FcFalse; 1387 } 1388 } 1389 1390 if (position) 1391 sameBinding = position->binding; 1392 else 1393 sameBinding = FcValueBindingWeak; 1394 for (v = new; v != NULL; v = FcValueListNext(v)) 1395 if (v->binding == FcValueBindingSame) 1396 v->binding = sameBinding; 1397 if (append) 1398 { 1399 if (position) 1400 prev = &position->next; 1401 else 1402 for (prev = head; *prev != NULL; 1403 prev = &(*prev)->next) 1404 ; 1405 } 1406 else 1407 { 1408 if (position) 1409 { 1410 for (prev = head; *prev != NULL; 1411 prev = &(*prev)->next) 1412 { 1413 if (*prev == position) 1414 break; 1415 } 1416 } 1417 else 1418 prev = head; 1419 1420 if (FcDebug () & FC_DBG_EDIT) 1421 { 1422 if (*prev == NULL) 1423 printf ("position not on list\n"); 1424 } 1425 } 1426 1427 if (FcDebug () & FC_DBG_EDIT) 1428 { 1429 printf ("%s list before ", append ? "Append" : "Prepend"); 1430 FcValueListPrintWithPosition (*head, *prev); 1431 printf ("\n"); 1432 } 1433 1434 if (new) 1435 { 1436 last = new; 1437 while (last->next != NULL) 1438 last = last->next; 1439 1440 last->next = *prev; 1441 *prev = new; 1442 } 1443 1444 if (FcDebug () & FC_DBG_EDIT) 1445 { 1446 printf ("%s list after ", append ? "Append" : "Prepend"); 1447 FcValueListPrint (*head); 1448 printf ("\n"); 1449 } 1450 1451 return FcTrue; 1452} 1453 1454static void 1455FcConfigDel (FcValueListPtr *head, 1456 FcValueList *position) 1457{ 1458 FcValueListPtr *prev; 1459 1460 for (prev = head; *prev != NULL; prev = &(*prev)->next) 1461 { 1462 if (*prev == position) 1463 { 1464 *prev = position->next; 1465 position->next = NULL; 1466 FcValueListDestroy (position); 1467 break; 1468 } 1469 } 1470} 1471 1472static void 1473FcConfigPatternAdd (FcPattern *p, 1474 FcObject object, 1475 FcValueList *list, 1476 FcBool append) 1477{ 1478 if (list) 1479 { 1480 FcPatternElt *e = FcPatternObjectInsertElt (p, object); 1481 1482 if (!e) 1483 return; 1484 FcConfigAdd (&e->values, 0, append, list, object); 1485 } 1486} 1487 1488/* 1489 * Delete all values associated with a field 1490 */ 1491static void 1492FcConfigPatternDel (FcPattern *p, 1493 FcObject object) 1494{ 1495 FcPatternElt *e = FcPatternObjectFindElt (p, object); 1496 if (!e) 1497 return; 1498 while (e->values != NULL) 1499 FcConfigDel (&e->values, e->values); 1500} 1501 1502static void 1503FcConfigPatternCanon (FcPattern *p, 1504 FcObject object) 1505{ 1506 FcPatternElt *e = FcPatternObjectFindElt (p, object); 1507 if (!e) 1508 return; 1509 if (e->values == NULL) 1510 FcPatternObjectDel (p, object); 1511} 1512 1513FcBool 1514FcConfigSubstituteWithPat (FcConfig *config, 1515 FcPattern *p, 1516 FcPattern *p_pat, 1517 FcMatchKind kind) 1518{ 1519 FcValue v; 1520 FcSubst *s; 1521 FcRule *r; 1522 FcValueList *l, **value = NULL, *vl; 1523 FcPattern *m; 1524 FcStrSet *strs; 1525 FcObject object = FC_INVALID_OBJECT; 1526 FcPatternElt **elt = NULL, *e; 1527 int i, nobjs; 1528 FcBool retval = FcTrue; 1529 FcTest **tst = NULL; 1530 1531 if (!config) 1532 { 1533 config = FcConfigGetCurrent (); 1534 if (!config) 1535 return FcFalse; 1536 } 1537 1538 switch (kind) { 1539 case FcMatchPattern: 1540 s = config->substPattern; 1541 strs = FcGetDefaultLangs (); 1542 if (strs) 1543 { 1544 FcStrList *l = FcStrListCreate (strs); 1545 FcChar8 *lang; 1546 FcValue v; 1547 FcLangSet *lsund = FcLangSetCreate (); 1548 1549 FcLangSetAdd (lsund, (const FcChar8 *)"und"); 1550 FcStrSetDestroy (strs); 1551 while (l && (lang = FcStrListNext (l))) 1552 { 1553 FcPatternElt *e = FcPatternObjectFindElt (p, FC_LANG_OBJECT); 1554 1555 if (e) 1556 { 1557 FcValueListPtr ll; 1558 1559 for (ll = FcPatternEltValues (e); ll; ll = FcValueListNext (ll)) 1560 { 1561 FcValue vv = FcValueCanonicalize (&ll->value); 1562 1563 if (vv.type == FcTypeLangSet) 1564 { 1565 FcLangSet *ls = FcLangSetCreate (); 1566 FcBool b; 1567 1568 FcLangSetAdd (ls, lang); 1569 b = FcLangSetContains (vv.u.l, ls); 1570 FcLangSetDestroy (ls); 1571 if (b) 1572 goto bail_lang; 1573 if (FcLangSetContains (vv.u.l, lsund)) 1574 goto bail_lang; 1575 } 1576 else 1577 { 1578 if (FcStrCmpIgnoreCase (vv.u.s, lang) == 0) 1579 goto bail_lang; 1580 if (FcStrCmpIgnoreCase (vv.u.s, (const FcChar8 *)"und") == 0) 1581 goto bail_lang; 1582 } 1583 } 1584 } 1585 v.type = FcTypeString; 1586 v.u.s = lang; 1587 1588 FcPatternObjectAddWithBinding (p, FC_LANG_OBJECT, v, FcValueBindingWeak, FcTrue); 1589 } 1590 bail_lang: 1591 FcStrListDone (l); 1592 FcLangSetDestroy (lsund); 1593 } 1594 if (FcPatternObjectGet (p, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch) 1595 { 1596 FcChar8 *prgname = FcGetPrgname (); 1597 if (prgname) 1598 FcPatternObjectAddString (p, FC_PRGNAME_OBJECT, prgname); 1599 } 1600 break; 1601 case FcMatchFont: 1602 s = config->substFont; 1603 break; 1604 case FcMatchScan: 1605 s = config->substScan; 1606 break; 1607 default: 1608 return FcFalse; 1609 } 1610 1611 nobjs = FC_MAX_BASE_OBJECT + config->maxObjects + 2; 1612 value = (FcValueList **) malloc (sizeof(void *) * nobjs); 1613 if (!value) 1614 { 1615 retval = FcFalse; 1616 goto bail1; 1617 } 1618 elt = (FcPatternElt **) malloc (sizeof(void *) * nobjs); 1619 if (!elt) 1620 { 1621 retval = FcFalse; 1622 goto bail1; 1623 } 1624 tst = (FcTest **) malloc (sizeof(void *) * nobjs); 1625 if (!tst) 1626 { 1627 retval = FcFalse; 1628 goto bail1; 1629 } 1630 1631 if (FcDebug () & FC_DBG_EDIT) 1632 { 1633 printf ("FcConfigSubstitute "); 1634 FcPatternPrint (p); 1635 } 1636 for (; s; s = s->next) 1637 { 1638 r = s->rule; 1639 for (i = 0; i < nobjs; i++) 1640 { 1641 elt[i] = NULL; 1642 value[i] = NULL; 1643 tst[i] = NULL; 1644 } 1645 for (; r; r = r->next) 1646 { 1647 switch (r->type) { 1648 case FcRuleUnknown: 1649 /* shouldn't be reached */ 1650 break; 1651 case FcRuleTest: 1652 object = FC_OBJ_ID (r->u.test->object); 1653 /* 1654 * Check the tests to see if 1655 * they all match the pattern 1656 */ 1657 if (FcDebug () & FC_DBG_EDIT) 1658 { 1659 printf ("FcConfigSubstitute test "); 1660 FcTestPrint (r->u.test); 1661 } 1662 if (kind == FcMatchFont && r->u.test->kind == FcMatchPattern) 1663 m = p_pat; 1664 else 1665 m = p; 1666 if (m) 1667 e = FcPatternObjectFindElt (m, r->u.test->object); 1668 else 1669 e = NULL; 1670 /* different 'kind' won't be the target of edit */ 1671 if (!elt[object] && kind == r->u.test->kind) 1672 { 1673 elt[object] = e; 1674 tst[object] = r->u.test; 1675 } 1676 /* 1677 * If there's no such field in the font, 1678 * then FcQualAll matches while FcQualAny does not 1679 */ 1680 if (!e) 1681 { 1682 if (r->u.test->qual == FcQualAll) 1683 { 1684 value[object] = NULL; 1685 continue; 1686 } 1687 else 1688 { 1689 if (FcDebug () & FC_DBG_EDIT) 1690 printf ("No match\n"); 1691 goto bail; 1692 } 1693 } 1694 /* 1695 * Check to see if there is a match, mark the location 1696 * to apply match-relative edits 1697 */ 1698 vl = FcConfigMatchValueList (m, p_pat, kind, r->u.test, e->values); 1699 /* different 'kind' won't be the target of edit */ 1700 if (!value[object] && kind == r->u.test->kind) 1701 value[object] = vl; 1702 if (!vl || 1703 (r->u.test->qual == FcQualFirst && vl != e->values) || 1704 (r->u.test->qual == FcQualNotFirst && vl == e->values)) 1705 { 1706 if (FcDebug () & FC_DBG_EDIT) 1707 printf ("No match\n"); 1708 goto bail; 1709 } 1710 break; 1711 case FcRuleEdit: 1712 object = FC_OBJ_ID (r->u.edit->object); 1713 if (FcDebug () & FC_DBG_EDIT) 1714 { 1715 printf ("Substitute "); 1716 FcEditPrint (r->u.edit); 1717 printf ("\n\n"); 1718 } 1719 /* 1720 * Evaluate the list of expressions 1721 */ 1722 l = FcConfigValues (p, p_pat,kind, r->u.edit->expr, r->u.edit->binding); 1723 if (tst[object] && (tst[object]->kind == FcMatchFont || kind == FcMatchPattern)) 1724 elt[object] = FcPatternObjectFindElt (p, tst[object]->object); 1725 1726 switch (FC_OP_GET_OP (r->u.edit->op)) { 1727 case FcOpAssign: 1728 /* 1729 * If there was a test, then replace the matched 1730 * value with the new list of values 1731 */ 1732 if (value[object]) 1733 { 1734 FcValueList *thisValue = value[object]; 1735 FcValueList *nextValue = l; 1736 1737 /* 1738 * Append the new list of values after the current value 1739 */ 1740 FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object); 1741 /* 1742 * Delete the marked value 1743 */ 1744 if (thisValue) 1745 FcConfigDel (&elt[object]->values, thisValue); 1746 /* 1747 * Adjust a pointer into the value list to ensure 1748 * future edits occur at the same place 1749 */ 1750 value[object] = nextValue; 1751 break; 1752 } 1753 /* fall through ... */ 1754 case FcOpAssignReplace: 1755 /* 1756 * Delete all of the values and insert 1757 * the new set 1758 */ 1759 FcConfigPatternDel (p, r->u.edit->object); 1760 FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue); 1761 /* 1762 * Adjust a pointer into the value list as they no 1763 * longer point to anything valid 1764 */ 1765 value[object] = NULL; 1766 break; 1767 case FcOpPrepend: 1768 if (value[object]) 1769 { 1770 FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object); 1771 break; 1772 } 1773 /* fall through ... */ 1774 case FcOpPrependFirst: 1775 FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse); 1776 break; 1777 case FcOpAppend: 1778 if (value[object]) 1779 { 1780 FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object); 1781 break; 1782 } 1783 /* fall through ... */ 1784 case FcOpAppendLast: 1785 FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue); 1786 break; 1787 case FcOpDelete: 1788 if (value[object]) 1789 { 1790 FcConfigDel (&elt[object]->values, value[object]); 1791 break; 1792 } 1793 /* fall through ... */ 1794 case FcOpDeleteAll: 1795 FcConfigPatternDel (p, r->u.edit->object); 1796 break; 1797 default: 1798 FcValueListDestroy (l); 1799 break; 1800 } 1801 /* 1802 * Now go through the pattern and eliminate 1803 * any properties without data 1804 */ 1805 FcConfigPatternCanon (p, r->u.edit->object); 1806 1807 if (FcDebug () & FC_DBG_EDIT) 1808 { 1809 printf ("FcConfigSubstitute edit"); 1810 FcPatternPrint (p); 1811 } 1812 break; 1813 } 1814 } 1815 bail:; 1816 } 1817 if (FcDebug () & FC_DBG_EDIT) 1818 { 1819 printf ("FcConfigSubstitute done"); 1820 FcPatternPrint (p); 1821 } 1822bail1: 1823 if (elt) 1824 free (elt); 1825 if (value) 1826 free (value); 1827 if (tst) 1828 free (tst); 1829 1830 return retval; 1831} 1832 1833FcBool 1834FcConfigSubstitute (FcConfig *config, 1835 FcPattern *p, 1836 FcMatchKind kind) 1837{ 1838 return FcConfigSubstituteWithPat (config, p, 0, kind); 1839} 1840 1841#if defined (_WIN32) 1842 1843static FcChar8 fontconfig_path[1000] = ""; /* MT-dontcare */ 1844FcChar8 fontconfig_instprefix[1000] = ""; /* MT-dontcare */ 1845 1846# if (defined (PIC) || defined (DLL_EXPORT)) 1847 1848BOOL WINAPI 1849DllMain (HINSTANCE hinstDLL, 1850 DWORD fdwReason, 1851 LPVOID lpvReserved); 1852 1853BOOL WINAPI 1854DllMain (HINSTANCE hinstDLL, 1855 DWORD fdwReason, 1856 LPVOID lpvReserved) 1857{ 1858 FcChar8 *p; 1859 1860 switch (fdwReason) { 1861 case DLL_PROCESS_ATTACH: 1862 if (!GetModuleFileName ((HMODULE) hinstDLL, (LPCH) fontconfig_path, 1863 sizeof (fontconfig_path))) 1864 break; 1865 1866 /* If the fontconfig DLL is in a "bin" or "lib" subfolder, 1867 * assume it's a Unix-style installation tree, and use 1868 * "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the 1869 * folder where the DLL is as FONTCONFIG_PATH. 1870 */ 1871 p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\'); 1872 if (p) 1873 { 1874 *p = '\0'; 1875 p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\'); 1876 if (p && (FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "bin") == 0 || 1877 FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "lib") == 0)) 1878 *p = '\0'; 1879 strcat ((char *) fontconfig_instprefix, (char *) fontconfig_path); 1880 strcat ((char *) fontconfig_path, "\\etc\\fonts"); 1881 } 1882 else 1883 fontconfig_path[0] = '\0'; 1884 1885 break; 1886 } 1887 1888 return TRUE; 1889} 1890 1891# endif /* !PIC */ 1892 1893#undef FONTCONFIG_PATH 1894#define FONTCONFIG_PATH fontconfig_path 1895 1896#endif /* !_WIN32 */ 1897 1898#ifndef FONTCONFIG_FILE 1899#define FONTCONFIG_FILE "fonts.conf" 1900#endif 1901 1902static FcChar8 * 1903FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file) 1904{ 1905 FcChar8 *path; 1906 int size, osize; 1907 1908 if (!dir) 1909 dir = (FcChar8 *) ""; 1910 1911 osize = strlen ((char *) dir) + 1 + strlen ((char *) file) + 1; 1912 /* 1913 * workaround valgrind warning because glibc takes advantage of how it knows memory is 1914 * allocated to implement strlen by reading in groups of 4 1915 */ 1916 size = (osize + 3) & ~3; 1917 1918 path = malloc (size); 1919 if (!path) 1920 return 0; 1921 1922 strcpy ((char *) path, (const char *) dir); 1923 /* make sure there's a single separator */ 1924#ifdef _WIN32 1925 if ((!path[0] || (path[strlen((char *) path)-1] != '/' && 1926 path[strlen((char *) path)-1] != '\\')) && 1927 !(file[0] == '/' || 1928 file[0] == '\\' || 1929 (isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\')))) 1930 strcat ((char *) path, "\\"); 1931#else 1932 if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/') 1933 strcat ((char *) path, "/"); 1934 else 1935 osize--; 1936#endif 1937 strcat ((char *) path, (char *) file); 1938 1939 if (access ((char *) path, R_OK) == 0) 1940 return path; 1941 1942 FcStrFree (path); 1943 1944 return 0; 1945} 1946 1947static FcChar8 ** 1948FcConfigGetPath (void) 1949{ 1950 FcChar8 **path; 1951 FcChar8 *env, *e, *colon; 1952 FcChar8 *dir; 1953 int npath; 1954 int i; 1955 1956 npath = 2; /* default dir + null */ 1957 env = (FcChar8 *) getenv ("FONTCONFIG_PATH"); 1958 if (env) 1959 { 1960 e = env; 1961 npath++; 1962 while (*e) 1963 if (*e++ == FC_SEARCH_PATH_SEPARATOR) 1964 npath++; 1965 } 1966 path = calloc (npath, sizeof (FcChar8 *)); 1967 if (!path) 1968 goto bail0; 1969 i = 0; 1970 1971 if (env) 1972 { 1973 e = env; 1974 while (*e) 1975 { 1976 colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR); 1977 if (!colon) 1978 colon = e + strlen ((char *) e); 1979 path[i] = malloc (colon - e + 1); 1980 if (!path[i]) 1981 goto bail1; 1982 strncpy ((char *) path[i], (const char *) e, colon - e); 1983 path[i][colon - e] = '\0'; 1984 if (*colon) 1985 e = colon + 1; 1986 else 1987 e = colon; 1988 i++; 1989 } 1990 } 1991 1992#ifdef _WIN32 1993 if (fontconfig_path[0] == '\0') 1994 { 1995 char *p; 1996 if(!GetModuleFileName(NULL, (LPCH) fontconfig_path, sizeof(fontconfig_path))) 1997 goto bail1; 1998 p = strrchr ((const char *) fontconfig_path, '\\'); 1999 if (p) *p = '\0'; 2000 strcat ((char *) fontconfig_path, "\\fonts"); 2001 } 2002#endif 2003 dir = (FcChar8 *) FONTCONFIG_PATH; 2004 path[i] = malloc (strlen ((char *) dir) + 1); 2005 if (!path[i]) 2006 goto bail1; 2007 strcpy ((char *) path[i], (const char *) dir); 2008 return path; 2009 2010bail1: 2011 for (i = 0; path[i]; i++) 2012 free (path[i]); 2013 free (path); 2014bail0: 2015 return 0; 2016} 2017 2018static void 2019FcConfigFreePath (FcChar8 **path) 2020{ 2021 FcChar8 **p; 2022 2023 for (p = path; *p; p++) 2024 free (*p); 2025 free (path); 2026} 2027 2028static FcBool _FcConfigHomeEnabled = FcTrue; /* MT-goodenough */ 2029 2030FcChar8 * 2031FcConfigHome (void) 2032{ 2033 if (_FcConfigHomeEnabled) 2034 { 2035 char *home = getenv ("HOME"); 2036 2037#ifdef _WIN32 2038 if (home == NULL) 2039 home = getenv ("USERPROFILE"); 2040#endif 2041 2042 return (FcChar8 *) home; 2043 } 2044 return 0; 2045} 2046 2047FcChar8 * 2048FcConfigXdgCacheHome (void) 2049{ 2050 const char *env = getenv ("XDG_CACHE_HOME"); 2051 FcChar8 *ret = NULL; 2052 2053 if (!_FcConfigHomeEnabled) 2054 return NULL; 2055 if (env) 2056 ret = FcStrCopy ((const FcChar8 *)env); 2057 else 2058 { 2059 const FcChar8 *home = FcConfigHome (); 2060 size_t len = home ? strlen ((const char *)home) : 0; 2061 2062 ret = malloc (len + 7 + 1); 2063 if (ret) 2064 { 2065 memcpy (ret, home, len); 2066 memcpy (&ret[len], FC_DIR_SEPARATOR_S ".cache", 7); 2067 ret[len + 7] = 0; 2068 } 2069 } 2070 2071 return ret; 2072} 2073 2074FcChar8 * 2075FcConfigXdgConfigHome (void) 2076{ 2077 const char *env = getenv ("XDG_CONFIG_HOME"); 2078 FcChar8 *ret = NULL; 2079 2080 if (!_FcConfigHomeEnabled) 2081 return NULL; 2082 if (env) 2083 ret = FcStrCopy ((const FcChar8 *)env); 2084 else 2085 { 2086 const FcChar8 *home = FcConfigHome (); 2087 size_t len = home ? strlen ((const char *)home) : 0; 2088 2089 ret = malloc (len + 8 + 1); 2090 if (ret) 2091 { 2092 memcpy (ret, home, len); 2093 memcpy (&ret[len], FC_DIR_SEPARATOR_S ".config", 8); 2094 ret[len + 8] = 0; 2095 } 2096 } 2097 2098 return ret; 2099} 2100 2101FcChar8 * 2102FcConfigXdgDataHome (void) 2103{ 2104 const char *env = getenv ("XDG_DATA_HOME"); 2105 FcChar8 *ret = NULL; 2106 2107 if (!_FcConfigHomeEnabled) 2108 return NULL; 2109 if (env) 2110 ret = FcStrCopy ((const FcChar8 *)env); 2111 else 2112 { 2113 const FcChar8 *home = FcConfigHome (); 2114 size_t len = home ? strlen ((const char *)home) : 0; 2115 2116 ret = malloc (len + 13 + 1); 2117 if (ret) 2118 { 2119 memcpy (ret, home, len); 2120 memcpy (&ret[len], FC_DIR_SEPARATOR_S ".local" FC_DIR_SEPARATOR_S "share", 13); 2121 ret[len + 13] = 0; 2122 } 2123 } 2124 2125 return ret; 2126} 2127 2128FcBool 2129FcConfigEnableHome (FcBool enable) 2130{ 2131 FcBool prev = _FcConfigHomeEnabled; 2132 _FcConfigHomeEnabled = enable; 2133 return prev; 2134} 2135 2136FcChar8 * 2137FcConfigFilename (const FcChar8 *url) 2138{ 2139 FcChar8 *file, *dir, **path, **p; 2140 2141 if (!url || !*url) 2142 { 2143 url = (FcChar8 *) getenv ("FONTCONFIG_FILE"); 2144 if (!url) 2145 url = (FcChar8 *) FONTCONFIG_FILE; 2146 } 2147 file = 0; 2148 2149#ifdef _WIN32 2150 if (isalpha (*url) && 2151 url[1] == ':' && 2152 (url[2] == '/' || url[2] == '\\')) 2153 goto absolute_path; 2154#endif 2155 2156 switch (*url) { 2157 case '~': 2158 dir = FcConfigHome (); 2159 if (dir) 2160 file = FcConfigFileExists (dir, url + 1); 2161 else 2162 file = 0; 2163 break; 2164#ifdef _WIN32 2165 case '\\': 2166 absolute_path: 2167#endif 2168 case '/': 2169 file = FcConfigFileExists (0, url); 2170 break; 2171 default: 2172 path = FcConfigGetPath (); 2173 if (!path) 2174 return NULL; 2175 for (p = path; *p; p++) 2176 { 2177 file = FcConfigFileExists (*p, url); 2178 if (file) 2179 break; 2180 } 2181 FcConfigFreePath (path); 2182 break; 2183 } 2184 2185 return file; 2186} 2187 2188/* 2189 * Manage the application-specific fonts 2190 */ 2191 2192FcBool 2193FcConfigAppFontAddFile (FcConfig *config, 2194 const FcChar8 *file) 2195{ 2196 FcFontSet *set; 2197 FcStrSet *subdirs; 2198 FcStrList *sublist; 2199 FcChar8 *subdir; 2200 2201 if (!config) 2202 { 2203 config = FcConfigGetCurrent (); 2204 if (!config) 2205 return FcFalse; 2206 } 2207 2208 subdirs = FcStrSetCreateEx (FCSS_GROW_BY_64); 2209 if (!subdirs) 2210 return FcFalse; 2211 2212 set = FcConfigGetFonts (config, FcSetApplication); 2213 if (!set) 2214 { 2215 set = FcFontSetCreate (); 2216 if (!set) 2217 { 2218 FcStrSetDestroy (subdirs); 2219 return FcFalse; 2220 } 2221 FcConfigSetFonts (config, set, FcSetApplication); 2222 } 2223 2224 if (!FcFileScanConfig (set, subdirs, config->blanks, file, config)) 2225 { 2226 FcStrSetDestroy (subdirs); 2227 return FcFalse; 2228 } 2229 if ((sublist = FcStrListCreate (subdirs))) 2230 { 2231 while ((subdir = FcStrListNext (sublist))) 2232 { 2233 FcConfigAppFontAddDir (config, subdir); 2234 } 2235 FcStrListDone (sublist); 2236 } 2237 FcStrSetDestroy (subdirs); 2238 return FcTrue; 2239} 2240 2241FcBool 2242FcConfigAppFontAddDir (FcConfig *config, 2243 const FcChar8 *dir) 2244{ 2245 FcFontSet *set; 2246 FcStrSet *dirs; 2247 2248 if (!config) 2249 { 2250 config = FcConfigGetCurrent (); 2251 if (!config) 2252 return FcFalse; 2253 } 2254 2255 dirs = FcStrSetCreateEx (FCSS_GROW_BY_64); 2256 if (!dirs) 2257 return FcFalse; 2258 2259 set = FcConfigGetFonts (config, FcSetApplication); 2260 if (!set) 2261 { 2262 set = FcFontSetCreate (); 2263 if (!set) 2264 { 2265 FcStrSetDestroy (dirs); 2266 return FcFalse; 2267 } 2268 FcConfigSetFonts (config, set, FcSetApplication); 2269 } 2270 2271 FcStrSetAddFilename (dirs, dir); 2272 2273 if (!FcConfigAddDirList (config, FcSetApplication, dirs)) 2274 { 2275 FcStrSetDestroy (dirs); 2276 return FcFalse; 2277 } 2278 FcStrSetDestroy (dirs); 2279 return FcTrue; 2280} 2281 2282void 2283FcConfigAppFontClear (FcConfig *config) 2284{ 2285 if (!config) 2286 { 2287 config = FcConfigGetCurrent (); 2288 if (!config) 2289 return; 2290 } 2291 2292 FcConfigSetFonts (config, 0, FcSetApplication); 2293} 2294 2295/* 2296 * Manage filename-based font source selectors 2297 */ 2298 2299FcBool 2300FcConfigGlobAdd (FcConfig *config, 2301 const FcChar8 *glob, 2302 FcBool accept) 2303{ 2304 FcStrSet *set = accept ? config->acceptGlobs : config->rejectGlobs; 2305 2306 return FcStrSetAdd (set, glob); 2307} 2308 2309static FcBool 2310FcConfigGlobsMatch (const FcStrSet *globs, 2311 const FcChar8 *string) 2312{ 2313 int i; 2314 2315 for (i = 0; i < globs->num; i++) 2316 if (FcStrGlobMatch (globs->strs[i], string)) 2317 return FcTrue; 2318 return FcFalse; 2319} 2320 2321FcBool 2322FcConfigAcceptFilename (FcConfig *config, 2323 const FcChar8 *filename) 2324{ 2325 if (FcConfigGlobsMatch (config->acceptGlobs, filename)) 2326 return FcTrue; 2327 if (FcConfigGlobsMatch (config->rejectGlobs, filename)) 2328 return FcFalse; 2329 return FcTrue; 2330} 2331 2332/* 2333 * Manage font-pattern based font source selectors 2334 */ 2335 2336FcBool 2337FcConfigPatternsAdd (FcConfig *config, 2338 FcPattern *pattern, 2339 FcBool accept) 2340{ 2341 FcFontSet *set = accept ? config->acceptPatterns : config->rejectPatterns; 2342 2343 return FcFontSetAdd (set, pattern); 2344} 2345 2346static FcBool 2347FcConfigPatternsMatch (const FcFontSet *patterns, 2348 const FcPattern *font) 2349{ 2350 int i; 2351 2352 for (i = 0; i < patterns->nfont; i++) 2353 if (FcListPatternMatchAny (patterns->fonts[i], font)) 2354 return FcTrue; 2355 return FcFalse; 2356} 2357 2358FcBool 2359FcConfigAcceptFont (FcConfig *config, 2360 const FcPattern *font) 2361{ 2362 if (FcConfigPatternsMatch (config->acceptPatterns, font)) 2363 return FcTrue; 2364 if (FcConfigPatternsMatch (config->rejectPatterns, font)) 2365 return FcFalse; 2366 return FcTrue; 2367} 2368 2369const FcChar8 * 2370FcConfigGetSysRoot (const FcConfig *config) 2371{ 2372 if (!config) 2373 { 2374 config = FcConfigGetCurrent (); 2375 if (!config) 2376 return NULL; 2377 } 2378 2379 return config->sysRoot; 2380} 2381 2382void 2383FcConfigSetSysRoot (FcConfig *config, 2384 const FcChar8 *sysroot) 2385{ 2386 FcChar8 *s = NULL; 2387 FcBool init = FcFalse; 2388 2389 if (!config) 2390 { 2391 /* We can't use FcConfigGetCurrent() here to ensure 2392 * the sysroot is set prior to initialize FcConfig, 2393 * to avoid loading caches from non-sysroot dirs. 2394 * So postpone the initialization later. 2395 */ 2396 config = fc_atomic_ptr_get (&_fcConfig); 2397 if (!config) 2398 { 2399 config = FcConfigCreate (); 2400 if (!config) 2401 return; 2402 init = FcTrue; 2403 } 2404 } 2405 2406 if (sysroot) 2407 { 2408 s = FcStrCopyFilename (sysroot); 2409 if (!s) 2410 return; 2411 } 2412 2413 if (config->sysRoot) 2414 FcStrFree (config->sysRoot); 2415 2416 config->sysRoot = s; 2417 if (init) 2418 { 2419 config = FcInitLoadOwnConfigAndFonts (config); 2420 FcConfigSetCurrent (config); 2421 /* FcConfigSetCurrent() increases the refcount. 2422 * decrease it here to avoid the memory leak. 2423 */ 2424 FcConfigDestroy (config); 2425 } 2426} 2427 2428#define __fccfg__ 2429#include "fcaliastail.h" 2430#undef __fccfg__ 2431