fcformat.c revision a6844aab
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 Keith Packard not be used in 11 * advertising or publicity pertaining to distribution of the software without 12 * specific, written prior permission. Keith Packard makes 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 * pkgkit PackageKit package tag format 66 * 67 * 68 * Some ideas for future syntax extensions: 69 * 70 * - verbose builtin that is like FcPatternPrint 71 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}' 72 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation) 73 */ 74 75 76#define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\"" 77#define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}" 78#define PKGKIT_FORMAT "%{[]family{font(%{family|downcase|delete( )})\n}}%{[]lang{font(:lang=%{lang|downcase|translate(_,-)})\n}}" 79 80 81static void 82message (const char *fmt, ...) 83{ 84 va_list args; 85 va_start (args, fmt); 86 fprintf (stderr, "Fontconfig: Pattern format error: "); 87 vfprintf (stderr, fmt, args); 88 fprintf (stderr, ".\n"); 89 va_end (args); 90} 91 92 93typedef struct _FcFormatContext 94{ 95 const FcChar8 *format_orig; 96 const FcChar8 *format; 97 int format_len; 98 FcChar8 *word; 99 FcBool word_allocated; 100} FcFormatContext; 101 102static FcBool 103FcFormatContextInit (FcFormatContext *c, 104 const FcChar8 *format, 105 FcChar8 *scratch, 106 int scratch_len) 107{ 108 c->format_orig = c->format = format; 109 c->format_len = strlen ((const char *) format); 110 111 if (c->format_len < scratch_len) 112 { 113 c->word = scratch; 114 c->word_allocated = FcFalse; 115 } 116 else 117 { 118 c->word = malloc (c->format_len + 1); 119 c->word_allocated = FcTrue; 120 } 121 122 return c->word != NULL; 123} 124 125static void 126FcFormatContextDone (FcFormatContext *c) 127{ 128 if (c && c->word_allocated) 129 { 130 free (c->word); 131 } 132} 133 134static FcBool 135consume_char (FcFormatContext *c, 136 FcChar8 term) 137{ 138 if (*c->format != term) 139 return FcFalse; 140 141 c->format++; 142 return FcTrue; 143} 144 145static FcBool 146expect_char (FcFormatContext *c, 147 FcChar8 term) 148{ 149 FcBool res = consume_char (c, term); 150 if (!res) 151 { 152 if (c->format == c->format_orig + c->format_len) 153 message ("format ended while expecting '%c'", 154 term); 155 else 156 message ("expected '%c' at %d", 157 term, c->format - c->format_orig + 1); 158 } 159 return res; 160} 161 162static FcBool 163FcCharIsPunct (const FcChar8 c) 164{ 165 if (c < '0') 166 return FcTrue; 167 if (c <= '9') 168 return FcFalse; 169 if (c < 'A') 170 return FcTrue; 171 if (c <= 'Z') 172 return FcFalse; 173 if (c < 'a') 174 return FcTrue; 175 if (c <= 'z') 176 return FcFalse; 177 if (c <= '~') 178 return FcTrue; 179 return FcFalse; 180} 181 182static char escaped_char(const char ch) 183{ 184 switch (ch) { 185 case 'a': return '\a'; 186 case 'b': return '\b'; 187 case 'f': return '\f'; 188 case 'n': return '\n'; 189 case 'r': return '\r'; 190 case 't': return '\t'; 191 case 'v': return '\v'; 192 default: return ch; 193 } 194} 195 196static FcBool 197read_word (FcFormatContext *c) 198{ 199 FcChar8 *p; 200 201 p = c->word; 202 203 while (*c->format) 204 { 205 if (*c->format == '\\') 206 { 207 c->format++; 208 if (*c->format) 209 *p++ = escaped_char (*c->format++); 210 continue; 211 } 212 else if (FcCharIsPunct (*c->format)) 213 break; 214 215 *p++ = *c->format++; 216 } 217 *p = '\0'; 218 219 if (p == c->word) 220 { 221 message ("expected identifier at %d", 222 c->format - c->format_orig + 1); 223 return FcFalse; 224 } 225 226 return FcTrue; 227} 228 229static FcBool 230read_chars (FcFormatContext *c, 231 FcChar8 term) 232{ 233 FcChar8 *p; 234 235 p = c->word; 236 237 while (*c->format && *c->format != '}' && *c->format != term) 238 { 239 if (*c->format == '\\') 240 { 241 c->format++; 242 if (*c->format) 243 *p++ = escaped_char (*c->format++); 244 continue; 245 } 246 247 *p++ = *c->format++; 248 } 249 *p = '\0'; 250 251 if (p == c->word) 252 { 253 message ("expected character data at %d", 254 c->format - c->format_orig + 1); 255 return FcFalse; 256 } 257 258 return FcTrue; 259} 260 261static FcBool 262FcPatternFormatToBuf (FcPattern *pat, 263 const FcChar8 *format, 264 FcStrBuf *buf); 265 266static FcBool 267interpret_builtin (FcFormatContext *c, 268 FcPattern *pat, 269 FcStrBuf *buf) 270{ 271 FcChar8 *new_str; 272 FcBool ret; 273 274 if (!expect_char (c, '=') || 275 !read_word (c)) 276 return FcFalse; 277 278 /* try simple builtins first */ 279 if (0) { } 280#define BUILTIN(name, func) \ 281 else if (0 == strcmp ((const char *) c->word, name))\ 282 do { new_str = func (pat); ret = FcTrue; } while (0) 283 BUILTIN ("unparse", FcNameUnparse); 284 /* BUILTIN ("verbose", FcPatternPrint); XXX */ 285#undef BUILTIN 286 else 287 ret = FcFalse; 288 289 if (ret) 290 { 291 if (new_str) 292 { 293 FcStrBufString (buf, new_str); 294 free (new_str); 295 return FcTrue; 296 } 297 else 298 return FcFalse; 299 } 300 301 /* now try our custom formats */ 302 if (0) { } 303#define BUILTIN(name, format) \ 304 else if (0 == strcmp ((const char *) c->word, name))\ 305 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf) 306 BUILTIN ("fcmatch", FCMATCH_FORMAT); 307 BUILTIN ("fclist", FCLIST_FORMAT); 308 BUILTIN ("pkgkit", PKGKIT_FORMAT); 309#undef BUILTIN 310 else 311 ret = FcFalse; 312 313 if (!ret) 314 message ("unknown builtin \"%s\"", 315 c->word); 316 317 return ret; 318} 319 320static FcBool 321interpret_expr (FcFormatContext *c, 322 FcPattern *pat, 323 FcStrBuf *buf, 324 FcChar8 term); 325 326static FcBool 327interpret_subexpr (FcFormatContext *c, 328 FcPattern *pat, 329 FcStrBuf *buf) 330{ 331 return expect_char (c, '{') && 332 interpret_expr (c, pat, buf, '}') && 333 expect_char (c, '}'); 334} 335 336static FcBool 337maybe_interpret_subexpr (FcFormatContext *c, 338 FcPattern *pat, 339 FcStrBuf *buf) 340{ 341 return (*c->format == '{') ? 342 interpret_subexpr (c, pat, buf) : 343 FcTrue; 344} 345 346static FcBool 347skip_subexpr (FcFormatContext *c); 348 349static FcBool 350skip_percent (FcFormatContext *c) 351{ 352 int width; 353 354 if (!expect_char (c, '%')) 355 return FcFalse; 356 357 /* skip an optional width specifier */ 358 width = strtol ((const char *) c->format, (char **) &c->format, 10); 359 360 if (!expect_char (c, '{')) 361 return FcFalse; 362 363 while(*c->format && *c->format != '}') 364 { 365 switch (*c->format) 366 { 367 case '\\': 368 c->format++; /* skip over '\\' */ 369 if (*c->format) 370 c->format++; 371 continue; 372 case '{': 373 if (!skip_subexpr (c)) 374 return FcFalse; 375 continue; 376 } 377 c->format++; 378 } 379 380 return expect_char (c, '}'); 381} 382 383static FcBool 384skip_expr (FcFormatContext *c) 385{ 386 while(*c->format && *c->format != '}') 387 { 388 switch (*c->format) 389 { 390 case '\\': 391 c->format++; /* skip over '\\' */ 392 if (*c->format) 393 c->format++; 394 continue; 395 case '%': 396 if (!skip_percent (c)) 397 return FcFalse; 398 continue; 399 } 400 c->format++; 401 } 402 403 return FcTrue; 404} 405 406static FcBool 407skip_subexpr (FcFormatContext *c) 408{ 409 return expect_char (c, '{') && 410 skip_expr (c) && 411 expect_char (c, '}'); 412} 413 414static FcBool 415maybe_skip_subexpr (FcFormatContext *c) 416{ 417 return (*c->format == '{') ? 418 skip_subexpr (c) : 419 FcTrue; 420} 421 422static FcBool 423interpret_filter_in (FcFormatContext *c, 424 FcPattern *pat, 425 FcStrBuf *buf) 426{ 427 FcObjectSet *os; 428 FcPattern *subpat; 429 430 if (!expect_char (c, '+')) 431 return FcFalse; 432 433 os = FcObjectSetCreate (); 434 if (!os) 435 return FcFalse; 436 437 do 438 { 439 if (!read_word (c) || 440 !FcObjectSetAdd (os, (const char *) c->word)) 441 { 442 FcObjectSetDestroy (os); 443 return FcFalse; 444 } 445 } 446 while (consume_char (c, ',')); 447 448 subpat = FcPatternFilter (pat, os); 449 FcObjectSetDestroy (os); 450 451 if (!subpat || 452 !interpret_subexpr (c, subpat, buf)) 453 return FcFalse; 454 455 FcPatternDestroy (subpat); 456 return FcTrue; 457} 458 459static FcBool 460interpret_filter_out (FcFormatContext *c, 461 FcPattern *pat, 462 FcStrBuf *buf) 463{ 464 FcPattern *subpat; 465 466 if (!expect_char (c, '-')) 467 return FcFalse; 468 469 subpat = FcPatternDuplicate (pat); 470 if (!subpat) 471 return FcFalse; 472 473 do 474 { 475 if (!read_word (c)) 476 { 477 FcPatternDestroy (subpat); 478 return FcFalse; 479 } 480 481 FcPatternDel (subpat, (const char *) c->word); 482 } 483 while (consume_char (c, ',')); 484 485 if (!interpret_subexpr (c, subpat, buf)) 486 return FcFalse; 487 488 FcPatternDestroy (subpat); 489 return FcTrue; 490} 491 492static FcBool 493interpret_cond (FcFormatContext *c, 494 FcPattern *pat, 495 FcStrBuf *buf) 496{ 497 FcBool pass; 498 499 if (!expect_char (c, '?')) 500 return FcFalse; 501 502 pass = FcTrue; 503 504 do 505 { 506 FcBool negate; 507 FcValue v; 508 509 negate = consume_char (c, '!'); 510 511 if (!read_word (c)) 512 return FcFalse; 513 514 pass = pass && 515 (negate ^ 516 (FcResultMatch == 517 FcPatternGet (pat, (const char *) c->word, 0, &v))); 518 } 519 while (consume_char (c, ',')); 520 521 if (pass) 522 { 523 if (!interpret_subexpr (c, pat, buf) || 524 !maybe_skip_subexpr (c)) 525 return FcFalse; 526 } 527 else 528 { 529 if (!skip_subexpr (c) || 530 !maybe_interpret_subexpr (c, pat, buf)) 531 return FcFalse; 532 } 533 534 return FcTrue; 535} 536 537static FcBool 538interpret_count (FcFormatContext *c, 539 FcPattern *pat, 540 FcStrBuf *buf) 541{ 542 int count; 543 FcPatternElt *e; 544 FcChar8 buf_static[64]; 545 546 if (!expect_char (c, '#')) 547 return FcFalse; 548 549 if (!read_word (c)) 550 return FcFalse; 551 552 count = 0; 553 e = FcPatternObjectFindElt (pat, 554 FcObjectFromName ((const char *) c->word)); 555 if (e) 556 { 557 FcValueListPtr l; 558 count++; 559 for (l = FcPatternEltValues(e); 560 l->next; 561 l = l->next) 562 count++; 563 } 564 565 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count); 566 FcStrBufString (buf, buf_static); 567 568 return FcTrue; 569} 570 571static FcBool 572interpret_enumerate (FcFormatContext *c, 573 FcPattern *pat, 574 FcStrBuf *buf) 575{ 576 FcObjectSet *os; 577 FcPattern *subpat; 578 const FcChar8 *format_save; 579 int idx; 580 FcBool ret, done; 581 FcStrList *lang_strs; 582 583 if (!expect_char (c, '[') || 584 !expect_char (c, ']')) 585 return FcFalse; 586 587 os = FcObjectSetCreate (); 588 if (!os) 589 return FcFalse; 590 591 ret = FcTrue; 592 593 do 594 { 595 if (!read_word (c) || 596 !FcObjectSetAdd (os, (const char *) c->word)) 597 { 598 FcObjectSetDestroy (os); 599 return FcFalse; 600 } 601 } 602 while (consume_char (c, ',')); 603 604 /* If we have one element and it's of type FcLangSet, we want 605 * to enumerate the languages in it. */ 606 lang_strs = NULL; 607 if (os->nobject == 1) 608 { 609 FcLangSet *langset; 610 if (FcResultMatch == 611 FcPatternGetLangSet (pat, os->objects[0], idx, &langset)) 612 { 613 FcStrSet *ss; 614 if (!(ss = FcLangSetGetLangs (langset)) || 615 !(lang_strs = FcStrListCreate (ss))) 616 goto bail0; 617 } 618 } 619 620 subpat = FcPatternDuplicate (pat); 621 if (!subpat) 622 goto bail0; 623 624 format_save = c->format; 625 idx = 0; 626 do 627 { 628 int i; 629 630 done = FcTrue; 631 632 if (lang_strs) 633 { 634 FcChar8 *lang; 635 636 FcPatternDel (subpat, os->objects[0]); 637 if ((lang = FcStrListNext (lang_strs))) 638 { 639 FcPatternAddString (subpat, os->objects[0], lang); 640 done = FcFalse; 641 } 642 } 643 else 644 { 645 for (i = 0; i < os->nobject; i++) 646 { 647 FcValue v; 648 649 /* XXX this can be optimized by accessing valuelist linked lists 650 * directly and remembering where we were. Most (all) value lists 651 * in normal uses are pretty short though (language tags are 652 * stored as a LangSet, not separate values.). */ 653 FcPatternDel (subpat, os->objects[i]); 654 if (FcResultMatch == 655 FcPatternGet (pat, os->objects[i], idx, &v)) 656 { 657 FcPatternAdd (subpat, os->objects[i], v, FcFalse); 658 done = FcFalse; 659 } 660 } 661 } 662 663 if (!done) 664 { 665 c->format = format_save; 666 ret = interpret_subexpr (c, subpat, buf); 667 if (!ret) 668 goto bail; 669 } 670 671 idx++; 672 } while (!done); 673 674 if (c->format == format_save) 675 skip_subexpr (c); 676 677bail: 678 FcPatternDestroy (subpat); 679bail0: 680 if (lang_strs) 681 FcStrListDone (lang_strs); 682 FcObjectSetDestroy (os); 683 684 return ret; 685} 686 687static FcBool 688interpret_simple (FcFormatContext *c, 689 FcPattern *pat, 690 FcStrBuf *buf) 691{ 692 FcPatternElt *e; 693 FcBool add_colon = FcFalse; 694 FcBool add_elt_name = FcFalse; 695 int idx; 696 FcChar8 *else_string; 697 698 if (consume_char (c, ':')) 699 add_colon = FcTrue; 700 701 if (!read_word (c)) 702 return FcFalse; 703 704 idx = -1; 705 if (consume_char (c, '[')) 706 { 707 idx = strtol ((const char *) c->format, (char **) &c->format, 10); 708 if (idx < 0) 709 { 710 message ("expected non-negative number at %d", 711 c->format-1 - c->format_orig + 1); 712 return FcFalse; 713 } 714 if (!expect_char (c, ']')) 715 return FcFalse; 716 } 717 718 if (consume_char (c, '=')) 719 add_elt_name = FcTrue; 720 721 /* modifiers */ 722 else_string = NULL; 723 if (consume_char (c, ':')) 724 { 725 FcChar8 *orig; 726 /* divert the c->word for now */ 727 orig = c->word; 728 c->word = c->word + strlen ((const char *) c->word) + 1; 729 /* for now we just support 'default value' */ 730 if (!expect_char (c, '-') || 731 !read_chars (c, '\0')) 732 { 733 c->word = orig; 734 return FcFalse; 735 } 736 else_string = c->word; 737 c->word = orig; 738 } 739 740 e = FcPatternObjectFindElt (pat, 741 FcObjectFromName ((const char *) c->word)); 742 if (e || else_string) 743 { 744 FcValueListPtr l = NULL; 745 746 if (add_colon) 747 FcStrBufChar (buf, ':'); 748 if (add_elt_name) 749 { 750 FcStrBufString (buf, c->word); 751 FcStrBufChar (buf, '='); 752 } 753 754 if (e) 755 l = FcPatternEltValues(e); 756 757 if (idx != -1) 758 { 759 while (l && idx > 0) 760 { 761 l = FcValueListNext(l); 762 idx--; 763 } 764 if (l && idx == 0) 765 { 766 if (!FcNameUnparseValue (buf, &l->value, '\0')) 767 return FcFalse; 768 } 769 else goto notfound; 770 } 771 else if (l) 772 { 773 FcNameUnparseValueList (buf, l, '\0'); 774 } 775 else 776 { 777 notfound: 778 if (else_string) 779 FcStrBufString (buf, else_string); 780 } 781 } 782 783 return FcTrue; 784} 785 786static FcBool 787cescape (FcFormatContext *c, 788 const FcChar8 *str, 789 FcStrBuf *buf) 790{ 791 while(*str) 792 { 793 switch (*str) 794 { 795 case '\\': 796 case '"': 797 FcStrBufChar (buf, '\\'); 798 break; 799 } 800 FcStrBufChar (buf, *str++); 801 } 802 return FcTrue; 803} 804 805static FcBool 806shescape (FcFormatContext *c, 807 const FcChar8 *str, 808 FcStrBuf *buf) 809{ 810 FcStrBufChar (buf, '\''); 811 while(*str) 812 { 813 if (*str == '\'') 814 FcStrBufString (buf, (const FcChar8 *) "'\\''"); 815 else 816 FcStrBufChar (buf, *str); 817 str++; 818 } 819 FcStrBufChar (buf, '\''); 820 return FcTrue; 821} 822 823static FcBool 824xmlescape (FcFormatContext *c, 825 const FcChar8 *str, 826 FcStrBuf *buf) 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 free (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 FcBool ret; 1187 1188 FcStrBufInit (&buf, buf_static, sizeof (buf_static)); 1189 1190 ret = FcPatternFormatToBuf (pat, format, &buf); 1191 1192 if (ret) 1193 return FcStrBufDone (&buf); 1194 else 1195 { 1196 FcStrBufDestroy (&buf); 1197 return NULL; 1198 } 1199} 1200 1201#define __fcformat__ 1202#include "fcaliastail.h" 1203#undef __fcformat__ 1204