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