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