fcformat.c revision c9710b42
1/* 2 * Copyright © 2008,2009 Red Hat, Inc. 3 * 4 * Red Hat Author(s): Behdad Esfahbod 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#include "fcint.h" 26#include <stdlib.h> 27#include <string.h> 28#include <stdarg.h> 29 30 31/* The language is documented in doc/fcformat.fncs 32 * These are the features implemented: 33 * 34 * simple %{elt} 35 * width %width{elt} 36 * index %{elt[idx]} 37 * name= %{elt=} 38 * :name= %{:elt} 39 * default %{elt:-word} 40 * count %{#elt} 41 * subexpr %{{expr}} 42 * filter-out %{-elt1,elt2,elt3{expr}} 43 * filter-in %{+elt1,elt2,elt3{expr}} 44 * conditional %{?elt1,elt2,!elt3{}{}} 45 * enumerate %{[]elt1,elt2{expr}} 46 * langset langset enumeration using the same syntax 47 * builtin %{=blt} 48 * convert %{elt|conv1|conv2|conv3} 49 * 50 * converters: 51 * basename FcStrBasename 52 * dirname FcStrDirname 53 * downcase FcStrDowncase 54 * shescape 55 * cescape 56 * xmlescape 57 * delete delete chars 58 * escape escape chars 59 * translate translate chars 60 * 61 * builtins: 62 * unparse FcNameUnparse 63 * fcmatch fc-match default 64 * fclist fc-list default 65 * fccat fc-cat default 66 * pkgkit PackageKit package tag format 67 * 68 * 69 * Some ideas for future syntax extensions: 70 * 71 * - verbose builtin that is like FcPatternPrint 72 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}' 73 * - allow indexing in +, -, ? filtering? 74 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation) 75 */ 76 77 78#define FCCAT_FORMAT "\"%{file|basename|cescape}\" %{index} \"%{-file{%{=unparse|cescape}}}\"" 79#define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\"" 80#define FCLIST_FORMAT "%{?file{%{file}: }}%{-file{%{=unparse}}}" 81#define PKGKIT_FORMAT "%{[]family{font(%{family|downcase|delete( )})\n}}%{[]lang{font(:lang=%{lang|downcase|translate(_,-)})\n}}" 82 83 84static void 85message (const char *fmt, ...) 86{ 87 va_list args; 88 va_start (args, fmt); 89 fprintf (stderr, "Fontconfig: Pattern format error: "); 90 vfprintf (stderr, fmt, args); 91 fprintf (stderr, ".\n"); 92 va_end (args); 93} 94 95 96typedef struct _FcFormatContext 97{ 98 const FcChar8 *format_orig; 99 const FcChar8 *format; 100 int format_len; 101 FcChar8 *word; 102 FcBool word_allocated; 103} FcFormatContext; 104 105static FcBool 106FcFormatContextInit (FcFormatContext *c, 107 const FcChar8 *format, 108 FcChar8 *scratch, 109 int scratch_len) 110{ 111 c->format_orig = c->format = format; 112 c->format_len = strlen ((const char *) format); 113 114 if (c->format_len < scratch_len) 115 { 116 c->word = scratch; 117 c->word_allocated = FcFalse; 118 } 119 else 120 { 121 c->word = malloc (c->format_len + 1); 122 c->word_allocated = FcTrue; 123 } 124 125 return c->word != NULL; 126} 127 128static void 129FcFormatContextDone (FcFormatContext *c) 130{ 131 if (c && c->word_allocated) 132 { 133 free (c->word); 134 } 135} 136 137static FcBool 138consume_char (FcFormatContext *c, 139 FcChar8 term) 140{ 141 if (*c->format != term) 142 return FcFalse; 143 144 c->format++; 145 return FcTrue; 146} 147 148static FcBool 149expect_char (FcFormatContext *c, 150 FcChar8 term) 151{ 152 FcBool res = consume_char (c, term); 153 if (!res) 154 { 155 if (c->format == c->format_orig + c->format_len) 156 message ("format ended while expecting '%c'", 157 term); 158 else 159 message ("expected '%c' at %d", 160 term, c->format - c->format_orig + 1); 161 } 162 return res; 163} 164 165static FcBool 166FcCharIsPunct (const FcChar8 c) 167{ 168 if (c < '0') 169 return FcTrue; 170 if (c <= '9') 171 return FcFalse; 172 if (c < 'A') 173 return FcTrue; 174 if (c <= 'Z') 175 return FcFalse; 176 if (c < 'a') 177 return FcTrue; 178 if (c <= 'z') 179 return FcFalse; 180 if (c <= '~') 181 return FcTrue; 182 return FcFalse; 183} 184 185static char escaped_char(const char ch) 186{ 187 switch (ch) { 188 case 'a': return '\a'; 189 case 'b': return '\b'; 190 case 'f': return '\f'; 191 case 'n': return '\n'; 192 case 'r': return '\r'; 193 case 't': return '\t'; 194 case 'v': return '\v'; 195 default: return ch; 196 } 197} 198 199static FcBool 200read_word (FcFormatContext *c) 201{ 202 FcChar8 *p; 203 204 p = c->word; 205 206 while (*c->format) 207 { 208 if (*c->format == '\\') 209 { 210 c->format++; 211 if (*c->format) 212 *p++ = escaped_char (*c->format++); 213 continue; 214 } 215 else if (FcCharIsPunct (*c->format)) 216 break; 217 218 *p++ = *c->format++; 219 } 220 *p = '\0'; 221 222 if (p == c->word) 223 { 224 message ("expected identifier at %d", 225 c->format - c->format_orig + 1); 226 return FcFalse; 227 } 228 229 return FcTrue; 230} 231 232static FcBool 233read_chars (FcFormatContext *c, 234 FcChar8 term) 235{ 236 FcChar8 *p; 237 238 p = c->word; 239 240 while (*c->format && *c->format != '}' && *c->format != term) 241 { 242 if (*c->format == '\\') 243 { 244 c->format++; 245 if (*c->format) 246 *p++ = escaped_char (*c->format++); 247 continue; 248 } 249 250 *p++ = *c->format++; 251 } 252 *p = '\0'; 253 254 if (p == c->word) 255 { 256 message ("expected character data at %d", 257 c->format - c->format_orig + 1); 258 return FcFalse; 259 } 260 261 return FcTrue; 262} 263 264static FcBool 265FcPatternFormatToBuf (FcPattern *pat, 266 const FcChar8 *format, 267 FcStrBuf *buf); 268 269static FcBool 270interpret_builtin (FcFormatContext *c, 271 FcPattern *pat, 272 FcStrBuf *buf) 273{ 274 FcChar8 *new_str; 275 FcBool ret; 276 277 if (!expect_char (c, '=') || 278 !read_word (c)) 279 return FcFalse; 280 281 /* try simple builtins first */ 282 if (0) { } 283#define BUILTIN(name, func) \ 284 else if (0 == strcmp ((const char *) c->word, name))\ 285 do { new_str = func (pat); ret = FcTrue; } while (0) 286 BUILTIN ("unparse", FcNameUnparse); 287 /* BUILTIN ("verbose", FcPatternPrint); XXX */ 288#undef BUILTIN 289 else 290 ret = FcFalse; 291 292 if (ret) 293 { 294 if (new_str) 295 { 296 FcStrBufString (buf, new_str); 297 FcStrFree (new_str); 298 return FcTrue; 299 } 300 else 301 return FcFalse; 302 } 303 304 /* now try our custom formats */ 305 if (0) { } 306#define BUILTIN(name, format) \ 307 else if (0 == strcmp ((const char *) c->word, name))\ 308 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf) 309 BUILTIN ("fccat", FCCAT_FORMAT); 310 BUILTIN ("fcmatch", FCMATCH_FORMAT); 311 BUILTIN ("fclist", FCLIST_FORMAT); 312 BUILTIN ("pkgkit", PKGKIT_FORMAT); 313#undef BUILTIN 314 else 315 ret = FcFalse; 316 317 if (!ret) 318 message ("unknown builtin \"%s\"", 319 c->word); 320 321 return ret; 322} 323 324static FcBool 325interpret_expr (FcFormatContext *c, 326 FcPattern *pat, 327 FcStrBuf *buf, 328 FcChar8 term); 329 330static FcBool 331interpret_subexpr (FcFormatContext *c, 332 FcPattern *pat, 333 FcStrBuf *buf) 334{ 335 return expect_char (c, '{') && 336 interpret_expr (c, pat, buf, '}') && 337 expect_char (c, '}'); 338} 339 340static FcBool 341maybe_interpret_subexpr (FcFormatContext *c, 342 FcPattern *pat, 343 FcStrBuf *buf) 344{ 345 return (*c->format == '{') ? 346 interpret_subexpr (c, pat, buf) : 347 FcTrue; 348} 349 350static FcBool 351skip_subexpr (FcFormatContext *c); 352 353static FcBool 354skip_percent (FcFormatContext *c) 355{ 356 if (!expect_char (c, '%')) 357 return FcFalse; 358 359 /* skip an optional width specifier */ 360 if (strtol ((const char *) c->format, (char **) &c->format, 10)) 361 {/* don't care */} 362 363 if (!expect_char (c, '{')) 364 return FcFalse; 365 366 while(*c->format && *c->format != '}') 367 { 368 switch (*c->format) 369 { 370 case '\\': 371 c->format++; /* skip over '\\' */ 372 if (*c->format) 373 c->format++; 374 continue; 375 case '{': 376 if (!skip_subexpr (c)) 377 return FcFalse; 378 continue; 379 } 380 c->format++; 381 } 382 383 return expect_char (c, '}'); 384} 385 386static FcBool 387skip_expr (FcFormatContext *c) 388{ 389 while(*c->format && *c->format != '}') 390 { 391 switch (*c->format) 392 { 393 case '\\': 394 c->format++; /* skip over '\\' */ 395 if (*c->format) 396 c->format++; 397 continue; 398 case '%': 399 if (!skip_percent (c)) 400 return FcFalse; 401 continue; 402 } 403 c->format++; 404 } 405 406 return FcTrue; 407} 408 409static FcBool 410skip_subexpr (FcFormatContext *c) 411{ 412 return expect_char (c, '{') && 413 skip_expr (c) && 414 expect_char (c, '}'); 415} 416 417static FcBool 418maybe_skip_subexpr (FcFormatContext *c) 419{ 420 return (*c->format == '{') ? 421 skip_subexpr (c) : 422 FcTrue; 423} 424 425static FcBool 426interpret_filter_in (FcFormatContext *c, 427 FcPattern *pat, 428 FcStrBuf *buf) 429{ 430 FcObjectSet *os; 431 FcPattern *subpat; 432 433 if (!expect_char (c, '+')) 434 return FcFalse; 435 436 os = FcObjectSetCreate (); 437 if (!os) 438 return FcFalse; 439 440 do 441 { 442 /* XXX binding */ 443 if (!read_word (c) || 444 !FcObjectSetAdd (os, (const char *) c->word)) 445 { 446 FcObjectSetDestroy (os); 447 return FcFalse; 448 } 449 } 450 while (consume_char (c, ',')); 451 452 subpat = FcPatternFilter (pat, os); 453 FcObjectSetDestroy (os); 454 455 if (!subpat || 456 !interpret_subexpr (c, subpat, buf)) 457 return FcFalse; 458 459 FcPatternDestroy (subpat); 460 return FcTrue; 461} 462 463static FcBool 464interpret_filter_out (FcFormatContext *c, 465 FcPattern *pat, 466 FcStrBuf *buf) 467{ 468 FcPattern *subpat; 469 470 if (!expect_char (c, '-')) 471 return FcFalse; 472 473 subpat = FcPatternDuplicate (pat); 474 if (!subpat) 475 return FcFalse; 476 477 do 478 { 479 if (!read_word (c)) 480 { 481 FcPatternDestroy (subpat); 482 return FcFalse; 483 } 484 485 FcPatternDel (subpat, (const char *) c->word); 486 } 487 while (consume_char (c, ',')); 488 489 if (!interpret_subexpr (c, subpat, buf)) 490 return FcFalse; 491 492 FcPatternDestroy (subpat); 493 return FcTrue; 494} 495 496static FcBool 497interpret_cond (FcFormatContext *c, 498 FcPattern *pat, 499 FcStrBuf *buf) 500{ 501 FcBool pass; 502 503 if (!expect_char (c, '?')) 504 return FcFalse; 505 506 pass = FcTrue; 507 508 do 509 { 510 FcBool negate; 511 FcValue v; 512 513 negate = consume_char (c, '!'); 514 515 if (!read_word (c)) 516 return FcFalse; 517 518 pass = pass && 519 (negate ^ 520 (FcResultMatch == 521 FcPatternGet (pat, (const char *) c->word, 0, &v))); 522 } 523 while (consume_char (c, ',')); 524 525 if (pass) 526 { 527 if (!interpret_subexpr (c, pat, buf) || 528 !maybe_skip_subexpr (c)) 529 return FcFalse; 530 } 531 else 532 { 533 if (!skip_subexpr (c) || 534 !maybe_interpret_subexpr (c, pat, buf)) 535 return FcFalse; 536 } 537 538 return FcTrue; 539} 540 541static FcBool 542interpret_count (FcFormatContext *c, 543 FcPattern *pat, 544 FcStrBuf *buf) 545{ 546 int count; 547 FcPatternElt *e; 548 FcChar8 buf_static[64]; 549 550 if (!expect_char (c, '#')) 551 return FcFalse; 552 553 if (!read_word (c)) 554 return FcFalse; 555 556 count = 0; 557 e = FcPatternObjectFindElt (pat, 558 FcObjectFromName ((const char *) c->word)); 559 if (e) 560 { 561 FcValueListPtr l; 562 count++; 563 for (l = FcPatternEltValues(e); 564 l->next; 565 l = l->next) 566 count++; 567 } 568 569 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count); 570 FcStrBufString (buf, buf_static); 571 572 return FcTrue; 573} 574 575static FcBool 576interpret_enumerate (FcFormatContext *c, 577 FcPattern *pat, 578 FcStrBuf *buf) 579{ 580 FcObjectSet *os; 581 FcPattern *subpat; 582 const FcChar8 *format_save; 583 int idx; 584 FcBool ret, done; 585 FcStrList *lang_strs; 586 587 if (!expect_char (c, '[') || 588 !expect_char (c, ']')) 589 return FcFalse; 590 591 os = FcObjectSetCreate (); 592 if (!os) 593 return FcFalse; 594 595 ret = FcTrue; 596 597 do 598 { 599 if (!read_word (c) || 600 !FcObjectSetAdd (os, (const char *) c->word)) 601 { 602 FcObjectSetDestroy (os); 603 return FcFalse; 604 } 605 } 606 while (consume_char (c, ',')); 607 608 /* If we have one element and it's of type FcLangSet, we want 609 * to enumerate the languages in it. */ 610 lang_strs = NULL; 611 if (os->nobject == 1) 612 { 613 FcLangSet *langset; 614 if (FcResultMatch == 615 FcPatternGetLangSet (pat, os->objects[0], 0, &langset)) 616 { 617 FcStrSet *ss; 618 if (!(ss = FcLangSetGetLangs (langset)) || 619 !(lang_strs = FcStrListCreate (ss))) 620 goto bail0; 621 } 622 } 623 624 subpat = FcPatternDuplicate (pat); 625 if (!subpat) 626 goto bail0; 627 628 format_save = c->format; 629 idx = 0; 630 do 631 { 632 int i; 633 634 done = FcTrue; 635 636 if (lang_strs) 637 { 638 FcChar8 *lang; 639 640 FcPatternDel (subpat, os->objects[0]); 641 if ((lang = FcStrListNext (lang_strs))) 642 { 643 /* XXX binding? */ 644 FcPatternAddString (subpat, os->objects[0], lang); 645 done = FcFalse; 646 } 647 } 648 else 649 { 650 for (i = 0; i < os->nobject; i++) 651 { 652 FcValue v; 653 654 /* XXX this can be optimized by accessing valuelist linked lists 655 * directly and remembering where we were. Most (all) value lists 656 * in normal uses are pretty short though (language tags are 657 * stored as a LangSet, not separate values.). */ 658 FcPatternDel (subpat, os->objects[i]); 659 if (FcResultMatch == 660 FcPatternGet (pat, os->objects[i], idx, &v)) 661 { 662 /* XXX binding */ 663 FcPatternAdd (subpat, os->objects[i], v, FcFalse); 664 done = FcFalse; 665 } 666 } 667 } 668 669 if (!done) 670 { 671 c->format = format_save; 672 ret = interpret_subexpr (c, subpat, buf); 673 if (!ret) 674 goto bail; 675 } 676 677 idx++; 678 } while (!done); 679 680 if (c->format == format_save) 681 skip_subexpr (c); 682 683bail: 684 FcPatternDestroy (subpat); 685bail0: 686 if (lang_strs) 687 FcStrListDone (lang_strs); 688 FcObjectSetDestroy (os); 689 690 return ret; 691} 692 693static FcBool 694interpret_simple (FcFormatContext *c, 695 FcPattern *pat, 696 FcStrBuf *buf) 697{ 698 FcPatternElt *e; 699 FcBool add_colon = FcFalse; 700 FcBool add_elt_name = FcFalse; 701 int idx; 702 FcChar8 *else_string; 703 704 if (consume_char (c, ':')) 705 add_colon = FcTrue; 706 707 if (!read_word (c)) 708 return FcFalse; 709 710 idx = -1; 711 if (consume_char (c, '[')) 712 { 713 idx = strtol ((const char *) c->format, (char **) &c->format, 10); 714 if (idx < 0) 715 { 716 message ("expected non-negative number at %d", 717 c->format-1 - c->format_orig + 1); 718 return FcFalse; 719 } 720 if (!expect_char (c, ']')) 721 return FcFalse; 722 } 723 724 if (consume_char (c, '=')) 725 add_elt_name = FcTrue; 726 727 /* modifiers */ 728 else_string = NULL; 729 if (consume_char (c, ':')) 730 { 731 FcChar8 *orig; 732 /* divert the c->word for now */ 733 orig = c->word; 734 c->word = c->word + strlen ((const char *) c->word) + 1; 735 /* for now we just support 'default value' */ 736 if (!expect_char (c, '-') || 737 !read_chars (c, '|')) 738 { 739 c->word = orig; 740 return FcFalse; 741 } 742 else_string = c->word; 743 c->word = orig; 744 } 745 746 e = FcPatternObjectFindElt (pat, 747 FcObjectFromName ((const char *) c->word)); 748 if (e || else_string) 749 { 750 FcValueListPtr l = NULL; 751 752 if (add_colon) 753 FcStrBufChar (buf, ':'); 754 if (add_elt_name) 755 { 756 FcStrBufString (buf, c->word); 757 FcStrBufChar (buf, '='); 758 } 759 760 if (e) 761 l = FcPatternEltValues(e); 762 763 if (idx != -1) 764 { 765 while (l && idx > 0) 766 { 767 l = FcValueListNext(l); 768 idx--; 769 } 770 if (l && idx == 0) 771 { 772 if (!FcNameUnparseValue (buf, &l->value, '\0')) 773 return FcFalse; 774 } 775 else goto notfound; 776 } 777 else if (l) 778 { 779 FcNameUnparseValueList (buf, l, '\0'); 780 } 781 else 782 { 783 notfound: 784 if (else_string) 785 FcStrBufString (buf, else_string); 786 } 787 } 788 789 return FcTrue; 790} 791 792static FcBool 793cescape (FcFormatContext *c FC_UNUSED, 794 const FcChar8 *str, 795 FcStrBuf *buf) 796{ 797 /* XXX escape \n etc? */ 798 799 while(*str) 800 { 801 switch (*str) 802 { 803 case '\\': 804 case '"': 805 FcStrBufChar (buf, '\\'); 806 break; 807 } 808 FcStrBufChar (buf, *str++); 809 } 810 return FcTrue; 811} 812 813static FcBool 814shescape (FcFormatContext *c FC_UNUSED, 815 const FcChar8 *str, 816 FcStrBuf *buf) 817{ 818 FcStrBufChar (buf, '\''); 819 while(*str) 820 { 821 if (*str == '\'') 822 FcStrBufString (buf, (const FcChar8 *) "'\\''"); 823 else 824 FcStrBufChar (buf, *str); 825 str++; 826 } 827 FcStrBufChar (buf, '\''); 828 return FcTrue; 829} 830 831static FcBool 832xmlescape (FcFormatContext *c FC_UNUSED, 833 const FcChar8 *str, 834 FcStrBuf *buf) 835{ 836 /* XXX escape \n etc? */ 837 838 while(*str) 839 { 840 switch (*str) 841 { 842 case '&': FcStrBufString (buf, (const FcChar8 *) "&"); break; 843 case '<': FcStrBufString (buf, (const FcChar8 *) "<"); break; 844 case '>': FcStrBufString (buf, (const FcChar8 *) ">"); break; 845 default: FcStrBufChar (buf, *str); break; 846 } 847 str++; 848 } 849 return FcTrue; 850} 851 852static FcBool 853delete_chars (FcFormatContext *c, 854 const FcChar8 *str, 855 FcStrBuf *buf) 856{ 857 /* XXX not UTF-8 aware */ 858 859 if (!expect_char (c, '(') || 860 !read_chars (c, ')') || 861 !expect_char (c, ')')) 862 return FcFalse; 863 864 while(*str) 865 { 866 FcChar8 *p; 867 868 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word); 869 if (p) 870 { 871 FcStrBufData (buf, str, p - str); 872 str = p + 1; 873 } 874 else 875 { 876 FcStrBufString (buf, str); 877 break; 878 } 879 880 } 881 882 return FcTrue; 883} 884 885static FcBool 886escape_chars (FcFormatContext *c, 887 const FcChar8 *str, 888 FcStrBuf *buf) 889{ 890 /* XXX not UTF-8 aware */ 891 892 if (!expect_char (c, '(') || 893 !read_chars (c, ')') || 894 !expect_char (c, ')')) 895 return FcFalse; 896 897 while(*str) 898 { 899 FcChar8 *p; 900 901 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word); 902 if (p) 903 { 904 FcStrBufData (buf, str, p - str); 905 FcStrBufChar (buf, c->word[0]); 906 FcStrBufChar (buf, *p); 907 str = p + 1; 908 } 909 else 910 { 911 FcStrBufString (buf, str); 912 break; 913 } 914 915 } 916 917 return FcTrue; 918} 919 920static FcBool 921translate_chars (FcFormatContext *c, 922 const FcChar8 *str, 923 FcStrBuf *buf) 924{ 925 char *from, *to, repeat; 926 int from_len, to_len; 927 928 /* XXX not UTF-8 aware */ 929 930 if (!expect_char (c, '(') || 931 !read_chars (c, ',') || 932 !expect_char (c, ',')) 933 return FcFalse; 934 935 from = (char *) c->word; 936 from_len = strlen (from); 937 to = from + from_len + 1; 938 939 /* hack: we temporarily divert c->word */ 940 c->word = (FcChar8 *) to; 941 if (!read_chars (c, ')')) 942 { 943 c->word = (FcChar8 *) from; 944 return FcFalse; 945 } 946 c->word = (FcChar8 *) from; 947 948 to_len = strlen (to); 949 repeat = to[to_len - 1]; 950 951 if (!expect_char (c, ')')) 952 return FcFalse; 953 954 while(*str) 955 { 956 FcChar8 *p; 957 958 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from); 959 if (p) 960 { 961 int i; 962 FcStrBufData (buf, str, p - str); 963 i = strchr (from, *p) - from; 964 FcStrBufChar (buf, i < to_len ? to[i] : repeat); 965 str = p + 1; 966 } 967 else 968 { 969 FcStrBufString (buf, str); 970 break; 971 } 972 973 } 974 975 return FcTrue; 976} 977 978static FcBool 979interpret_convert (FcFormatContext *c, 980 FcStrBuf *buf, 981 int start) 982{ 983 const FcChar8 *str; 984 FcChar8 *new_str; 985 FcStrBuf new_buf; 986 FcChar8 buf_static[8192]; 987 FcBool ret; 988 989 if (!expect_char (c, '|') || 990 !read_word (c)) 991 return FcFalse; 992 993 /* prepare the buffer */ 994 FcStrBufChar (buf, '\0'); 995 if (buf->failed) 996 return FcFalse; 997 str = buf->buf + start; 998 buf->len = start; 999 1000 /* try simple converters first */ 1001 if (0) { } 1002#define CONVERTER(name, func) \ 1003 else if (0 == strcmp ((const char *) c->word, name))\ 1004 do { new_str = func (str); ret = FcTrue; } while (0) 1005 CONVERTER ("downcase", FcStrDowncase); 1006 CONVERTER ("basename", FcStrBasename); 1007 CONVERTER ("dirname", FcStrDirname); 1008#undef CONVERTER 1009 else 1010 ret = FcFalse; 1011 1012 if (ret) 1013 { 1014 if (new_str) 1015 { 1016 FcStrBufString (buf, new_str); 1017 FcStrFree (new_str); 1018 return FcTrue; 1019 } 1020 else 1021 return FcFalse; 1022 } 1023 1024 FcStrBufInit (&new_buf, buf_static, sizeof (buf_static)); 1025 1026 /* now try our custom converters */ 1027 if (0) { } 1028#define CONVERTER(name, func) \ 1029 else if (0 == strcmp ((const char *) c->word, name))\ 1030 ret = func (c, str, &new_buf) 1031 CONVERTER ("cescape", cescape); 1032 CONVERTER ("shescape", shescape); 1033 CONVERTER ("xmlescape", xmlescape); 1034 CONVERTER ("delete", delete_chars); 1035 CONVERTER ("escape", escape_chars); 1036 CONVERTER ("translate", translate_chars); 1037#undef CONVERTER 1038 else 1039 ret = FcFalse; 1040 1041 if (ret) 1042 { 1043 FcStrBufChar (&new_buf, '\0'); 1044 FcStrBufString (buf, new_buf.buf); 1045 } 1046 else 1047 message ("unknown converter \"%s\"", 1048 c->word); 1049 1050 FcStrBufDestroy (&new_buf); 1051 1052 return ret; 1053} 1054 1055static FcBool 1056maybe_interpret_converts (FcFormatContext *c, 1057 FcStrBuf *buf, 1058 int start) 1059{ 1060 while (*c->format == '|') 1061 if (!interpret_convert (c, buf, start)) 1062 return FcFalse; 1063 1064 return FcTrue; 1065} 1066 1067static FcBool 1068align_to_width (FcStrBuf *buf, 1069 int start, 1070 int width) 1071{ 1072 int len; 1073 1074 if (buf->failed) 1075 return FcFalse; 1076 1077 len = buf->len - start; 1078 if (len < -width) 1079 { 1080 /* left align */ 1081 while (len++ < -width) 1082 FcStrBufChar (buf, ' '); 1083 } 1084 else if (len < width) 1085 { 1086 int old_len; 1087 old_len = len; 1088 /* right align */ 1089 while (len++ < width) 1090 FcStrBufChar (buf, ' '); 1091 if (buf->failed) 1092 return FcFalse; 1093 len = old_len; 1094 memmove (buf->buf + buf->len - len, 1095 buf->buf + buf->len - width, 1096 len); 1097 memset (buf->buf + buf->len - width, 1098 ' ', 1099 width - len); 1100 } 1101 1102 return !buf->failed; 1103} 1104static FcBool 1105interpret_percent (FcFormatContext *c, 1106 FcPattern *pat, 1107 FcStrBuf *buf) 1108{ 1109 int width, start; 1110 FcBool ret; 1111 1112 if (!expect_char (c, '%')) 1113 return FcFalse; 1114 1115 if (consume_char (c, '%')) /* "%%" */ 1116 { 1117 FcStrBufChar (buf, '%'); 1118 return FcTrue; 1119 } 1120 1121 /* parse an optional width specifier */ 1122 width = strtol ((const char *) c->format, (char **) &c->format, 10); 1123 1124 if (!expect_char (c, '{')) 1125 return FcFalse; 1126 1127 start = buf->len; 1128 1129 switch (*c->format) { 1130 case '=': ret = interpret_builtin (c, pat, buf); break; 1131 case '{': ret = interpret_subexpr (c, pat, buf); break; 1132 case '+': ret = interpret_filter_in (c, pat, buf); break; 1133 case '-': ret = interpret_filter_out (c, pat, buf); break; 1134 case '?': ret = interpret_cond (c, pat, buf); break; 1135 case '#': ret = interpret_count (c, pat, buf); break; 1136 case '[': ret = interpret_enumerate (c, pat, buf); break; 1137 default: ret = interpret_simple (c, pat, buf); break; 1138 } 1139 1140 return ret && 1141 maybe_interpret_converts (c, buf, start) && 1142 align_to_width (buf, start, width) && 1143 expect_char (c, '}'); 1144} 1145 1146static FcBool 1147interpret_expr (FcFormatContext *c, 1148 FcPattern *pat, 1149 FcStrBuf *buf, 1150 FcChar8 term) 1151{ 1152 while (*c->format && *c->format != term) 1153 { 1154 switch (*c->format) 1155 { 1156 case '\\': 1157 c->format++; /* skip over '\\' */ 1158 if (*c->format) 1159 FcStrBufChar (buf, escaped_char (*c->format++)); 1160 continue; 1161 case '%': 1162 if (!interpret_percent (c, pat, buf)) 1163 return FcFalse; 1164 continue; 1165 } 1166 FcStrBufChar (buf, *c->format++); 1167 } 1168 return FcTrue; 1169} 1170 1171static FcBool 1172FcPatternFormatToBuf (FcPattern *pat, 1173 const FcChar8 *format, 1174 FcStrBuf *buf) 1175{ 1176 FcFormatContext c; 1177 FcChar8 word_static[1024]; 1178 FcBool ret; 1179 1180 if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static))) 1181 return FcFalse; 1182 1183 ret = interpret_expr (&c, pat, buf, '\0'); 1184 1185 FcFormatContextDone (&c); 1186 1187 return ret; 1188} 1189 1190FcChar8 * 1191FcPatternFormat (FcPattern *pat, 1192 const FcChar8 *format) 1193{ 1194 FcStrBuf buf; 1195 FcChar8 buf_static[8192 - 1024]; 1196 FcPattern *alloced = NULL; 1197 FcBool ret; 1198 1199 if (!pat) 1200 alloced = pat = FcPatternCreate (); 1201 1202 FcStrBufInit (&buf, buf_static, sizeof (buf_static)); 1203 1204 ret = FcPatternFormatToBuf (pat, format, &buf); 1205 1206 if (alloced) 1207 FcPatternDestroy (alloced); 1208 1209 if (ret) 1210 return FcStrBufDone (&buf); 1211 else 1212 { 1213 FcStrBufDestroy (&buf); 1214 return NULL; 1215 } 1216} 1217 1218#define __fcformat__ 1219#include "fcaliastail.h" 1220#undef __fcformat__ 1221