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