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
84 static void
85 message (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
96 typedef 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
105 static FcBool
106 FcFormatContextInit (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
128 static void
129 FcFormatContextDone (FcFormatContext *c)
130 {
131 if (c && c->word_allocated)
132 {
133 free (c->word);
134 }
135 }
136
137 static FcBool
138 consume_char (FcFormatContext *c,
139 FcChar8 term)
140 {
141 if (*c->format != term)
142 return FcFalse;
143
144 c->format++;
145 return FcTrue;
146 }
147
148 static FcBool
149 expect_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
165 static FcBool
166 FcCharIsPunct (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
185 static 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
199 static FcBool
200 read_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
232 static FcBool
233 read_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
264 static FcBool
265 FcPatternFormatToBuf (FcPattern *pat,
266 const FcChar8 *format,
267 FcStrBuf *buf);
268
269 static FcBool
270 interpret_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
324 static FcBool
325 interpret_expr (FcFormatContext *c,
326 FcPattern *pat,
327 FcStrBuf *buf,
328 FcChar8 term);
329
330 static FcBool
331 interpret_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
340 static FcBool
341 maybe_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
350 static FcBool
351 skip_subexpr (FcFormatContext *c);
352
353 static FcBool
354 skip_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
386 static FcBool
387 skip_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
409 static FcBool
410 skip_subexpr (FcFormatContext *c)
411 {
412 return expect_char (c, '{') &&
413 skip_expr (c) &&
414 expect_char (c, '}');
415 }
416
417 static FcBool
418 maybe_skip_subexpr (FcFormatContext *c)
419 {
420 return (*c->format == '{') ?
421 skip_subexpr (c) :
422 FcTrue;
423 }
424
425 static FcBool
426 interpret_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
463 static FcBool
464 interpret_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
496 static FcBool
497 interpret_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
541 static FcBool
542 interpret_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
568 static FcBool
569 interpret_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
676 bail:
677 FcPatternDestroy (subpat);
678 bail0:
679 if (lang_strs)
680 FcStrListDone (lang_strs);
681 FcObjectSetDestroy (os);
682
683 return ret;
684 }
685
686 static FcBool
687 interpret_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
782 static FcBool
783 cescape (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
803 static FcBool
804 shescape (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
821 static FcBool
822 xmlescape (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
842 static FcBool
843 delete_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
875 static FcBool
876 escape_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
910 static FcBool
911 translate_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
968 static FcBool
969 interpret_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
1045 static FcBool
1046 maybe_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
1057 static FcBool
1058 align_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 }
1094 static FcBool
1095 interpret_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
1136 static FcBool
1137 interpret_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
1161 static FcBool
1162 FcPatternFormatToBuf (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
1180 FcChar8 *
1181 FcPatternFormat (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__