fcformat.c revision 1887081f
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 FcPatternIter iter; 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 if (FcPatternFindIter (pat, &iter, (const char *) c->word)) 558 { 559 count = FcPatternIterValueCount (pat, &iter); 560 } 561 562 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count); 563 FcStrBufString (buf, buf_static); 564 565 return FcTrue; 566} 567 568static FcBool 569interpret_enumerate (FcFormatContext *c, 570 FcPattern *pat, 571 FcStrBuf *buf) 572{ 573 FcObjectSet *os; 574 FcPattern *subpat; 575 const FcChar8 *format_save; 576 int idx; 577 FcBool ret, done; 578 FcStrList *lang_strs; 579 580 if (!expect_char (c, '[') || 581 !expect_char (c, ']')) 582 return FcFalse; 583 584 os = FcObjectSetCreate (); 585 if (!os) 586 return FcFalse; 587 588 ret = FcTrue; 589 590 do 591 { 592 if (!read_word (c) || 593 !FcObjectSetAdd (os, (const char *) c->word)) 594 { 595 FcObjectSetDestroy (os); 596 return FcFalse; 597 } 598 } 599 while (consume_char (c, ',')); 600 601 /* If we have one element and it's of type FcLangSet, we want 602 * to enumerate the languages in it. */ 603 lang_strs = NULL; 604 if (os->nobject == 1) 605 { 606 FcLangSet *langset; 607 if (FcResultMatch == 608 FcPatternGetLangSet (pat, os->objects[0], 0, &langset)) 609 { 610 FcStrSet *ss; 611 if (!(ss = FcLangSetGetLangs (langset)) || 612 !(lang_strs = FcStrListCreate (ss))) 613 goto bail0; 614 } 615 } 616 617 subpat = FcPatternDuplicate (pat); 618 if (!subpat) 619 goto bail0; 620 621 format_save = c->format; 622 idx = 0; 623 do 624 { 625 int i; 626 627 done = FcTrue; 628 629 if (lang_strs) 630 { 631 FcChar8 *lang; 632 633 FcPatternDel (subpat, os->objects[0]); 634 if ((lang = FcStrListNext (lang_strs))) 635 { 636 /* XXX binding? */ 637 FcPatternAddString (subpat, os->objects[0], lang); 638 done = FcFalse; 639 } 640 } 641 else 642 { 643 for (i = 0; i < os->nobject; i++) 644 { 645 FcValue v; 646 647 /* XXX this can be optimized by accessing valuelist linked lists 648 * directly and remembering where we were. Most (all) value lists 649 * in normal uses are pretty short though (language tags are 650 * stored as a LangSet, not separate values.). */ 651 FcPatternDel (subpat, os->objects[i]); 652 if (FcResultMatch == 653 FcPatternGet (pat, os->objects[i], idx, &v)) 654 { 655 /* XXX binding */ 656 FcPatternAdd (subpat, os->objects[i], v, FcFalse); 657 done = FcFalse; 658 } 659 } 660 } 661 662 if (!done) 663 { 664 c->format = format_save; 665 ret = interpret_subexpr (c, subpat, buf); 666 if (!ret) 667 goto bail; 668 } 669 670 idx++; 671 } while (!done); 672 673 if (c->format == format_save) 674 skip_subexpr (c); 675 676bail: 677 FcPatternDestroy (subpat); 678bail0: 679 if (lang_strs) 680 FcStrListDone (lang_strs); 681 FcObjectSetDestroy (os); 682 683 return ret; 684} 685 686static FcBool 687interpret_simple (FcFormatContext *c, 688 FcPattern *pat, 689 FcStrBuf *buf) 690{ 691 FcPatternIter iter; 692 FcBool add_colon = FcFalse; 693 FcBool add_elt_name = FcFalse; 694 int idx; 695 FcChar8 *else_string; 696 697 if (consume_char (c, ':')) 698 add_colon = FcTrue; 699 700 if (!read_word (c)) 701 return FcFalse; 702 703 idx = -1; 704 if (consume_char (c, '[')) 705 { 706 idx = strtol ((const char *) c->format, (char **) &c->format, 10); 707 if (idx < 0) 708 { 709 message ("expected non-negative number at %d", 710 c->format-1 - c->format_orig + 1); 711 return FcFalse; 712 } 713 if (!expect_char (c, ']')) 714 return FcFalse; 715 } 716 717 if (consume_char (c, '=')) 718 add_elt_name = FcTrue; 719 720 /* modifiers */ 721 else_string = NULL; 722 if (consume_char (c, ':')) 723 { 724 FcChar8 *orig; 725 /* divert the c->word for now */ 726 orig = c->word; 727 c->word = c->word + strlen ((const char *) c->word) + 1; 728 /* for now we just support 'default value' */ 729 if (!expect_char (c, '-') || 730 !read_chars (c, '|')) 731 { 732 c->word = orig; 733 return FcFalse; 734 } 735 else_string = c->word; 736 c->word = orig; 737 } 738 739 if (FcPatternFindIter (pat, &iter, (const char *) c->word) || else_string) 740 { 741 FcValueListPtr l = NULL; 742 743 if (add_colon) 744 FcStrBufChar (buf, ':'); 745 if (add_elt_name) 746 { 747 FcStrBufString (buf, c->word); 748 FcStrBufChar (buf, '='); 749 } 750 751 l = FcPatternIterGetValues (pat, &iter); 752 753 if (idx != -1) 754 { 755 while (l && idx > 0) 756 { 757 l = FcValueListNext(l); 758 idx--; 759 } 760 if (l && idx == 0) 761 { 762 if (!FcNameUnparseValue (buf, &l->value, NULL)) 763 return FcFalse; 764 } 765 else goto notfound; 766 } 767 else if (l) 768 { 769 FcNameUnparseValueList (buf, l, NULL); 770 } 771 else 772 { 773 notfound: 774 if (else_string) 775 FcStrBufString (buf, else_string); 776 } 777 } 778 779 return FcTrue; 780} 781 782static FcBool 783cescape (FcFormatContext *c FC_UNUSED, 784 const FcChar8 *str, 785 FcStrBuf *buf) 786{ 787 /* XXX escape \n etc? */ 788 789 while(*str) 790 { 791 switch (*str) 792 { 793 case '\\': 794 case '"': 795 FcStrBufChar (buf, '\\'); 796 break; 797 } 798 FcStrBufChar (buf, *str++); 799 } 800 return FcTrue; 801} 802 803static FcBool 804shescape (FcFormatContext *c FC_UNUSED, 805 const FcChar8 *str, 806 FcStrBuf *buf) 807{ 808 FcStrBufChar (buf, '\''); 809 while(*str) 810 { 811 if (*str == '\'') 812 FcStrBufString (buf, (const FcChar8 *) "'\\''"); 813 else 814 FcStrBufChar (buf, *str); 815 str++; 816 } 817 FcStrBufChar (buf, '\''); 818 return FcTrue; 819} 820 821static FcBool 822xmlescape (FcFormatContext *c FC_UNUSED, 823 const FcChar8 *str, 824 FcStrBuf *buf) 825{ 826 /* XXX escape \n etc? */ 827 828 while(*str) 829 { 830 switch (*str) 831 { 832 case '&': FcStrBufString (buf, (const FcChar8 *) "&"); break; 833 case '<': FcStrBufString (buf, (const FcChar8 *) "<"); break; 834 case '>': FcStrBufString (buf, (const FcChar8 *) ">"); break; 835 default: FcStrBufChar (buf, *str); break; 836 } 837 str++; 838 } 839 return FcTrue; 840} 841 842static FcBool 843delete_chars (FcFormatContext *c, 844 const FcChar8 *str, 845 FcStrBuf *buf) 846{ 847 /* XXX not UTF-8 aware */ 848 849 if (!expect_char (c, '(') || 850 !read_chars (c, ')') || 851 !expect_char (c, ')')) 852 return FcFalse; 853 854 while(*str) 855 { 856 FcChar8 *p; 857 858 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word); 859 if (p) 860 { 861 FcStrBufData (buf, str, p - str); 862 str = p + 1; 863 } 864 else 865 { 866 FcStrBufString (buf, str); 867 break; 868 } 869 870 } 871 872 return FcTrue; 873} 874 875static FcBool 876escape_chars (FcFormatContext *c, 877 const FcChar8 *str, 878 FcStrBuf *buf) 879{ 880 /* XXX not UTF-8 aware */ 881 882 if (!expect_char (c, '(') || 883 !read_chars (c, ')') || 884 !expect_char (c, ')')) 885 return FcFalse; 886 887 while(*str) 888 { 889 FcChar8 *p; 890 891 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word); 892 if (p) 893 { 894 FcStrBufData (buf, str, p - str); 895 FcStrBufChar (buf, c->word[0]); 896 FcStrBufChar (buf, *p); 897 str = p + 1; 898 } 899 else 900 { 901 FcStrBufString (buf, str); 902 break; 903 } 904 905 } 906 907 return FcTrue; 908} 909 910static FcBool 911translate_chars (FcFormatContext *c, 912 const FcChar8 *str, 913 FcStrBuf *buf) 914{ 915 char *from, *to, repeat; 916 int from_len, to_len; 917 918 /* XXX not UTF-8 aware */ 919 920 if (!expect_char (c, '(') || 921 !read_chars (c, ',') || 922 !expect_char (c, ',')) 923 return FcFalse; 924 925 from = (char *) c->word; 926 from_len = strlen (from); 927 to = from + from_len + 1; 928 929 /* hack: we temporarily divert c->word */ 930 c->word = (FcChar8 *) to; 931 if (!read_chars (c, ')')) 932 { 933 c->word = (FcChar8 *) from; 934 return FcFalse; 935 } 936 c->word = (FcChar8 *) from; 937 938 to_len = strlen (to); 939 repeat = to[to_len - 1]; 940 941 if (!expect_char (c, ')')) 942 return FcFalse; 943 944 while(*str) 945 { 946 FcChar8 *p; 947 948 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from); 949 if (p) 950 { 951 int i; 952 FcStrBufData (buf, str, p - str); 953 i = strchr (from, *p) - from; 954 FcStrBufChar (buf, i < to_len ? to[i] : repeat); 955 str = p + 1; 956 } 957 else 958 { 959 FcStrBufString (buf, str); 960 break; 961 } 962 963 } 964 965 return FcTrue; 966} 967 968static FcBool 969interpret_convert (FcFormatContext *c, 970 FcStrBuf *buf, 971 int start) 972{ 973 const FcChar8 *str; 974 FcChar8 *new_str; 975 FcStrBuf new_buf; 976 FcChar8 buf_static[8192]; 977 FcBool ret; 978 979 if (!expect_char (c, '|') || 980 !read_word (c)) 981 return FcFalse; 982 983 /* prepare the buffer */ 984 FcStrBufChar (buf, '\0'); 985 if (buf->failed) 986 return FcFalse; 987 str = buf->buf + start; 988 buf->len = start; 989 990 /* try simple converters first */ 991 if (0) { } 992#define CONVERTER(name, func) \ 993 else if (0 == strcmp ((const char *) c->word, name))\ 994 do { new_str = func (str); ret = FcTrue; } while (0) 995 CONVERTER ("downcase", FcStrDowncase); 996 CONVERTER ("basename", FcStrBasename); 997 CONVERTER ("dirname", FcStrDirname); 998#undef CONVERTER 999 else 1000 ret = FcFalse; 1001 1002 if (ret) 1003 { 1004 if (new_str) 1005 { 1006 FcStrBufString (buf, new_str); 1007 FcStrFree (new_str); 1008 return FcTrue; 1009 } 1010 else 1011 return FcFalse; 1012 } 1013 1014 FcStrBufInit (&new_buf, buf_static, sizeof (buf_static)); 1015 1016 /* now try our custom converters */ 1017 if (0) { } 1018#define CONVERTER(name, func) \ 1019 else if (0 == strcmp ((const char *) c->word, name))\ 1020 ret = func (c, str, &new_buf) 1021 CONVERTER ("cescape", cescape); 1022 CONVERTER ("shescape", shescape); 1023 CONVERTER ("xmlescape", xmlescape); 1024 CONVERTER ("delete", delete_chars); 1025 CONVERTER ("escape", escape_chars); 1026 CONVERTER ("translate", translate_chars); 1027#undef CONVERTER 1028 else 1029 ret = FcFalse; 1030 1031 if (ret) 1032 { 1033 FcStrBufChar (&new_buf, '\0'); 1034 FcStrBufString (buf, new_buf.buf); 1035 } 1036 else 1037 message ("unknown converter \"%s\"", 1038 c->word); 1039 1040 FcStrBufDestroy (&new_buf); 1041 1042 return ret; 1043} 1044 1045static FcBool 1046maybe_interpret_converts (FcFormatContext *c, 1047 FcStrBuf *buf, 1048 int start) 1049{ 1050 while (*c->format == '|') 1051 if (!interpret_convert (c, buf, start)) 1052 return FcFalse; 1053 1054 return FcTrue; 1055} 1056 1057static FcBool 1058align_to_width (FcStrBuf *buf, 1059 int start, 1060 int width) 1061{ 1062 int len; 1063 1064 if (buf->failed) 1065 return FcFalse; 1066 1067 len = buf->len - start; 1068 if (len < -width) 1069 { 1070 /* left align */ 1071 while (len++ < -width) 1072 FcStrBufChar (buf, ' '); 1073 } 1074 else if (len < width) 1075 { 1076 int old_len; 1077 old_len = len; 1078 /* right align */ 1079 while (len++ < width) 1080 FcStrBufChar (buf, ' '); 1081 if (buf->failed) 1082 return FcFalse; 1083 len = old_len; 1084 memmove (buf->buf + buf->len - len, 1085 buf->buf + buf->len - width, 1086 len); 1087 memset (buf->buf + buf->len - width, 1088 ' ', 1089 width - len); 1090 } 1091 1092 return !buf->failed; 1093} 1094static FcBool 1095interpret_percent (FcFormatContext *c, 1096 FcPattern *pat, 1097 FcStrBuf *buf) 1098{ 1099 int width, start; 1100 FcBool ret; 1101 1102 if (!expect_char (c, '%')) 1103 return FcFalse; 1104 1105 if (consume_char (c, '%')) /* "%%" */ 1106 { 1107 FcStrBufChar (buf, '%'); 1108 return FcTrue; 1109 } 1110 1111 /* parse an optional width specifier */ 1112 width = strtol ((const char *) c->format, (char **) &c->format, 10); 1113 1114 if (!expect_char (c, '{')) 1115 return FcFalse; 1116 1117 start = buf->len; 1118 1119 switch (*c->format) { 1120 case '=': ret = interpret_builtin (c, pat, buf); break; 1121 case '{': ret = interpret_subexpr (c, pat, buf); break; 1122 case '+': ret = interpret_filter_in (c, pat, buf); break; 1123 case '-': ret = interpret_filter_out (c, pat, buf); break; 1124 case '?': ret = interpret_cond (c, pat, buf); break; 1125 case '#': ret = interpret_count (c, pat, buf); break; 1126 case '[': ret = interpret_enumerate (c, pat, buf); break; 1127 default: ret = interpret_simple (c, pat, buf); break; 1128 } 1129 1130 return ret && 1131 maybe_interpret_converts (c, buf, start) && 1132 align_to_width (buf, start, width) && 1133 expect_char (c, '}'); 1134} 1135 1136static FcBool 1137interpret_expr (FcFormatContext *c, 1138 FcPattern *pat, 1139 FcStrBuf *buf, 1140 FcChar8 term) 1141{ 1142 while (*c->format && *c->format != term) 1143 { 1144 switch (*c->format) 1145 { 1146 case '\\': 1147 c->format++; /* skip over '\\' */ 1148 if (*c->format) 1149 FcStrBufChar (buf, escaped_char (*c->format++)); 1150 continue; 1151 case '%': 1152 if (!interpret_percent (c, pat, buf)) 1153 return FcFalse; 1154 continue; 1155 } 1156 FcStrBufChar (buf, *c->format++); 1157 } 1158 return FcTrue; 1159} 1160 1161static FcBool 1162FcPatternFormatToBuf (FcPattern *pat, 1163 const FcChar8 *format, 1164 FcStrBuf *buf) 1165{ 1166 FcFormatContext c; 1167 FcChar8 word_static[1024]; 1168 FcBool ret; 1169 1170 if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static))) 1171 return FcFalse; 1172 1173 ret = interpret_expr (&c, pat, buf, '\0'); 1174 1175 FcFormatContextDone (&c); 1176 1177 return ret; 1178} 1179 1180FcChar8 * 1181FcPatternFormat (FcPattern *pat, 1182 const FcChar8 *format) 1183{ 1184 FcStrBuf buf; 1185 FcChar8 buf_static[8192 - 1024]; 1186 FcPattern *alloced = NULL; 1187 FcBool ret; 1188 1189 if (!pat) 1190 alloced = pat = FcPatternCreate (); 1191 1192 FcStrBufInit (&buf, buf_static, sizeof (buf_static)); 1193 1194 ret = FcPatternFormatToBuf (pat, format, &buf); 1195 1196 if (alloced) 1197 FcPatternDestroy (alloced); 1198 1199 if (ret) 1200 return FcStrBufDone (&buf); 1201 else 1202 { 1203 FcStrBufDestroy (&buf); 1204 return NULL; 1205 } 1206} 1207 1208#define __fcformat__ 1209#include "fcaliastail.h" 1210#undef __fcformat__ 1211