]> git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
[fcformat] Support indexing simple tags
[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 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 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL KEITH PACKARD 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 /*
32 * Some ideas for future syntax extensions:
33 *
34 * - array enumeration using '%{[]family,familylang{expr}|decorator}'
35 * - langset enumeration using same syntax as array enumeration
36 * - allow indexing simple tags using '%{elt[idx]}'
37 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
38 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
39 */
40
41
42 /* fc-match needs '<unknown filename>', etc handling. */
43 #define FCMATCH_FORMAT "%{file|basename}: \"%{family[0]}\" \"%{style[0]}\""
44 #define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
45
46
47 static void
48 message (const char *fmt, ...)
49 {
50 va_list args;
51 va_start (args, fmt);
52 fprintf (stderr, "Fontconfig: Pattern format error: ");
53 vfprintf (stderr, fmt, args);
54 fprintf (stderr, ".\n");
55 va_end (args);
56 }
57
58
59 typedef struct _FcFormatContext
60 {
61 const FcChar8 *format_orig;
62 const FcChar8 *format;
63 int format_len;
64 FcChar8 *word;
65 FcBool word_allocated;
66 } FcFormatContext;
67
68 static FcBool
69 FcFormatContextInit (FcFormatContext *c,
70 const FcChar8 *format,
71 FcChar8 *scratch,
72 int scratch_len)
73 {
74 c->format_orig = c->format = format;
75 c->format_len = strlen ((const char *) format);
76
77 if (c->format_len < scratch_len)
78 {
79 c->word = scratch;
80 c->word_allocated = FcFalse;
81 }
82 else
83 {
84 c->word = malloc (c->format_len + 1);
85 c->word_allocated = FcTrue;
86 }
87
88 return c->word != NULL;
89 }
90
91 static void
92 FcFormatContextDone (FcFormatContext *c)
93 {
94 if (c && c->word_allocated)
95 {
96 free (c->word);
97 }
98 }
99
100 static FcBool
101 consume_char (FcFormatContext *c,
102 FcChar8 term)
103 {
104 if (*c->format != term)
105 return FcFalse;
106
107 c->format++;
108 return FcTrue;
109 }
110
111 static FcBool
112 expect_char (FcFormatContext *c,
113 FcChar8 term)
114 {
115 FcBool res = consume_char (c, term);
116 if (!res)
117 {
118 if (c->format == c->format_orig + c->format_len)
119 message ("format ended while expecting '%c'",
120 term);
121 else
122 message ("expected '%c' at %d",
123 term, c->format - c->format_orig + 1);
124 }
125 return res;
126 }
127
128 static FcBool
129 FcCharIsPunct (const FcChar8 c)
130 {
131 if (c < '0')
132 return FcTrue;
133 if (c <= '9')
134 return FcFalse;
135 if (c < 'A')
136 return FcTrue;
137 if (c <= 'Z')
138 return FcFalse;
139 if (c < 'a')
140 return FcTrue;
141 if (c <= 'z')
142 return FcFalse;
143 if (c <= '~')
144 return FcTrue;
145 return FcFalse;
146 }
147
148 static char escaped_char(const char ch)
149 {
150 switch (ch) {
151 case 'a': return '\a';
152 case 'b': return '\b';
153 case 'f': return '\f';
154 case 'n': return '\n';
155 case 'r': return '\r';
156 case 't': return '\t';
157 case 'v': return '\v';
158 default: return ch;
159 }
160 }
161
162 static FcBool
163 read_word (FcFormatContext *c)
164 {
165 FcChar8 *p;
166
167 p = c->word;
168
169 while (*c->format)
170 {
171 if (*c->format == '\\')
172 {
173 c->format++;
174 if (*c->format)
175 *p++ = escaped_char (*c->format++);
176 continue;
177 }
178 else if (FcCharIsPunct (*c->format))
179 break;
180
181 *p++ = *c->format++;
182 }
183 *p = '\0';
184
185 if (p == c->word)
186 {
187 message ("expected identifier at %d",
188 c->format - c->format_orig + 1);
189 return FcFalse;
190 }
191
192 return FcTrue;
193 }
194
195 static FcBool
196 read_chars (FcFormatContext *c,
197 FcChar8 term)
198 {
199 FcChar8 *p;
200
201 p = c->word;
202
203 while (*c->format && *c->format != '}' && *c->format != term)
204 {
205 if (*c->format == '\\')
206 {
207 c->format++;
208 if (*c->format)
209 *p++ = escaped_char (*c->format++);
210 continue;
211 }
212
213 *p++ = *c->format++;
214 }
215 *p = '\0';
216
217 if (p == c->word)
218 {
219 message ("expected character data at %d",
220 c->format - c->format_orig + 1);
221 return FcFalse;
222 }
223
224 return FcTrue;
225 }
226
227 static FcBool
228 FcPatternFormatToBuf (FcPattern *pat,
229 const FcChar8 *format,
230 FcStrBuf *buf);
231
232 static FcBool
233 interpret_builtin (FcFormatContext *c,
234 FcPattern *pat,
235 FcStrBuf *buf)
236 {
237 FcChar8 *new_str;
238 FcBool ret;
239
240 if (!expect_char (c, '=') ||
241 !read_word (c))
242 return FcFalse;
243
244 /* try simple builtins first */
245 if (0) { }
246 #define BUILTIN(name, func) \
247 else if (0 == strcmp ((const char *) c->word, name))\
248 do { new_str = func (pat); ret = FcTrue; } while (0)
249 BUILTIN ("unparse", FcNameUnparse);
250 /* BUILTIN ("verbose", FcPatternPrint); */
251 #undef BUILTIN
252 else
253 ret = FcFalse;
254
255 if (ret)
256 {
257 if (new_str)
258 {
259 FcStrBufString (buf, new_str);
260 free (new_str);
261 return FcTrue;
262 }
263 else
264 return FcFalse;
265 }
266
267 /* now try our custom formats */
268 if (0) { }
269 #define BUILTIN(name, format) \
270 else if (0 == strcmp ((const char *) c->word, name))\
271 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
272 BUILTIN ("fcmatch", FCMATCH_FORMAT);
273 BUILTIN ("fclist", FCLIST_FORMAT);
274 #undef BUILTIN
275 else
276 ret = FcFalse;
277
278 if (!ret)
279 message ("unknown builtin \"%s\"",
280 c->word);
281
282 return ret;
283 }
284
285 static FcBool
286 interpret_expr (FcFormatContext *c,
287 FcPattern *pat,
288 FcStrBuf *buf,
289 FcChar8 term);
290
291 static FcBool
292 interpret_subexpr (FcFormatContext *c,
293 FcPattern *pat,
294 FcStrBuf *buf)
295 {
296 return expect_char (c, '{') &&
297 interpret_expr (c, pat, buf, '}') &&
298 expect_char (c, '}');
299 }
300
301 static FcBool
302 maybe_interpret_subexpr (FcFormatContext *c,
303 FcPattern *pat,
304 FcStrBuf *buf)
305 {
306 return (*c->format == '{') ?
307 interpret_subexpr (c, pat, buf) :
308 FcTrue;
309 }
310
311 static FcBool
312 skip_subexpr (FcFormatContext *c);
313
314 static FcBool
315 skip_percent (FcFormatContext *c)
316 {
317 int width;
318
319 if (!expect_char (c, '%'))
320 return FcFalse;
321
322 /* skip an optional width specifier */
323 width = strtol ((const char *) c->format, (char **) &c->format, 10);
324
325 if (!expect_char (c, '{'))
326 return FcFalse;
327
328 while(*c->format && *c->format != '}')
329 {
330 switch (*c->format)
331 {
332 case '\\':
333 c->format++; /* skip over '\\' */
334 if (*c->format)
335 c->format++;
336 continue;
337 case '{':
338 if (!skip_subexpr (c))
339 return FcFalse;
340 continue;
341 }
342 c->format++;
343 }
344
345 return expect_char (c, '}');
346 }
347
348 static FcBool
349 skip_expr (FcFormatContext *c)
350 {
351 while(*c->format && *c->format != '}')
352 {
353 switch (*c->format)
354 {
355 case '\\':
356 c->format++; /* skip over '\\' */
357 if (*c->format)
358 c->format++;
359 continue;
360 case '%':
361 if (!skip_percent (c))
362 return FcFalse;
363 continue;
364 }
365 c->format++;
366 }
367
368 return FcTrue;
369 }
370
371 static FcBool
372 skip_subexpr (FcFormatContext *c)
373 {
374 return expect_char (c, '{') &&
375 skip_expr (c) &&
376 expect_char (c, '}');
377 }
378
379 static FcBool
380 maybe_skip_subexpr (FcFormatContext *c)
381 {
382 return (*c->format == '{') ?
383 skip_subexpr (c) :
384 FcTrue;
385 }
386
387 static FcBool
388 interpret_filter (FcFormatContext *c,
389 FcPattern *pat,
390 FcStrBuf *buf)
391 {
392 FcObjectSet *os;
393 FcPattern *subpat;
394
395 if (!expect_char (c, '+'))
396 return FcFalse;
397
398 os = FcObjectSetCreate ();
399 if (!os)
400 return FcFalse;
401
402 do
403 {
404 if (!read_word (c) ||
405 !FcObjectSetAdd (os, (const char *) c->word))
406 {
407 FcObjectSetDestroy (os);
408 return FcFalse;
409 }
410 }
411 while (consume_char (c, ','));
412
413 subpat = FcPatternFilter (pat, os);
414 FcObjectSetDestroy (os);
415
416 if (!subpat ||
417 !interpret_subexpr (c, subpat, buf))
418 return FcFalse;
419
420 FcPatternDestroy (subpat);
421 return FcTrue;
422 }
423
424 static FcBool
425 interpret_delete (FcFormatContext *c,
426 FcPattern *pat,
427 FcStrBuf *buf)
428 {
429 FcPattern *subpat;
430
431 if (!expect_char (c, '-'))
432 return FcFalse;
433
434 subpat = FcPatternDuplicate (pat);
435 if (!subpat)
436 return FcFalse;
437
438 do
439 {
440 if (!read_word (c))
441 {
442 FcPatternDestroy (subpat);
443 return FcFalse;
444 }
445
446 FcPatternDel (subpat, (const char *) c->word);
447 }
448 while (consume_char (c, ','));
449
450 if (!interpret_subexpr (c, subpat, buf))
451 return FcFalse;
452
453 FcPatternDestroy (subpat);
454 return FcTrue;
455 }
456
457 static FcBool
458 interpret_cond (FcFormatContext *c,
459 FcPattern *pat,
460 FcStrBuf *buf)
461 {
462 FcBool pass;
463
464 if (!expect_char (c, '?'))
465 return FcFalse;
466
467 pass = FcTrue;
468
469 do
470 {
471 FcBool negate;
472 FcValue v;
473
474 negate = consume_char (c, '!');
475
476 if (!read_word (c))
477 return FcFalse;
478
479 pass = pass &&
480 (negate ^
481 (FcResultMatch == FcPatternGet (pat,
482 (const char *) c->word,
483 0, &v)));
484 }
485 while (consume_char (c, ','));
486
487 if (pass)
488 {
489 if (!interpret_subexpr (c, pat, buf) ||
490 !maybe_skip_subexpr (c))
491 return FcFalse;
492 }
493 else
494 {
495 if (!skip_subexpr (c) ||
496 !maybe_interpret_subexpr (c, pat, buf))
497 return FcFalse;
498 }
499
500 return FcTrue;
501 }
502
503 static FcBool
504 interpret_count (FcFormatContext *c,
505 FcPattern *pat,
506 FcStrBuf *buf)
507 {
508 int count;
509 FcPatternElt *e;
510 FcChar8 buf_static[64];
511
512 if (!expect_char (c, '#'))
513 return FcFalse;
514
515 if (!read_word (c))
516 return FcFalse;
517
518 count = 0;
519 e = FcPatternObjectFindElt (pat,
520 FcObjectFromName ((const char *) c->word));
521 if (e)
522 {
523 FcValueListPtr l;
524 count++;
525 for (l = FcPatternEltValues(e);
526 l->next;
527 l = l->next)
528 count++;
529 }
530
531 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
532 FcStrBufString (buf, buf_static);
533
534 return FcTrue;
535 }
536
537 static FcBool
538 interpret_simple (FcFormatContext *c,
539 FcPattern *pat,
540 FcStrBuf *buf)
541 {
542 FcPatternElt *e;
543 FcBool add_colon = FcFalse;
544 FcBool add_elt_name = FcFalse;
545 int idx;
546
547 if (consume_char (c, ':'))
548 add_colon = FcTrue;
549
550 if (!read_word (c))
551 return FcFalse;
552
553 idx = -1;
554 if (consume_char (c, '['))
555 {
556 idx = strtol ((const char *) c->format, (char **) &c->format, 10);
557 if (idx < 0)
558 {
559 message ("expected non-negative number at %d",
560 c->format-1 - c->format_orig + 1);
561 return FcFalse;
562 }
563 expect_char (c, ']');
564 }
565
566 if (consume_char (c, '='))
567 add_elt_name = FcTrue;
568
569 e = FcPatternObjectFindElt (pat,
570 FcObjectFromName ((const char *) c->word));
571 if (e)
572 {
573 FcValueListPtr l;
574
575 if (add_colon)
576 FcStrBufChar (buf, ':');
577 if (add_elt_name)
578 {
579 FcStrBufString (buf, c->word);
580 FcStrBufChar (buf, '=');
581 }
582
583 l = FcPatternEltValues(e);
584
585 if (idx != -1)
586 {
587 while (l && idx > 0)
588 {
589 l = FcValueListNext(l);
590 idx--;
591 }
592 if (l && idx == 0)
593 {
594 if (!FcNameUnparseValue (buf, &l->value, '\0'))
595 return FcFalse;
596 }
597 else goto notfound;
598 }
599 else
600 {
601 FcNameUnparseValueList (buf, l, '\0');
602 }
603 }
604 else
605 notfound:
606 {
607 }
608
609 return FcTrue;
610 }
611
612 static FcBool
613 cescape (FcFormatContext *c,
614 const FcChar8 *str,
615 FcStrBuf *buf)
616 {
617 while(*str)
618 {
619 switch (*str)
620 {
621 case '\\':
622 case '"':
623 FcStrBufChar (buf, '\\');
624 break;
625 }
626 FcStrBufChar (buf, *str++);
627 }
628 return FcTrue;
629 }
630
631 static FcBool
632 shescape (FcFormatContext *c,
633 const FcChar8 *str,
634 FcStrBuf *buf)
635 {
636 FcStrBufChar (buf, '\'');
637 while(*str)
638 {
639 if (*str == '\'')
640 FcStrBufString (buf, (const FcChar8 *) "'\\''");
641 else
642 FcStrBufChar (buf, *str);
643 str++;
644 }
645 FcStrBufChar (buf, '\'');
646 return FcTrue;
647 }
648
649 static FcBool
650 xmlescape (FcFormatContext *c,
651 const FcChar8 *str,
652 FcStrBuf *buf)
653 {
654 while(*str)
655 {
656 switch (*str)
657 {
658 case '&': FcStrBufString (buf, (const FcChar8 *) "&amp;"); break;
659 case '<': FcStrBufString (buf, (const FcChar8 *) "&lt;"); break;
660 case '>': FcStrBufString (buf, (const FcChar8 *) "&gt;"); break;
661 default: FcStrBufChar (buf, *str); break;
662 }
663 str++;
664 }
665 return FcTrue;
666 }
667
668 static FcBool
669 delete_chars (FcFormatContext *c,
670 const FcChar8 *str,
671 FcStrBuf *buf)
672 {
673 /* XXX not UTF-8 aware */
674
675 if (!expect_char (c, '(') ||
676 !read_chars (c, ')') ||
677 !expect_char (c, ')'))
678 return FcFalse;
679
680 while(*str)
681 {
682 FcChar8 *p;
683
684 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
685 if (p)
686 {
687 FcStrBufData (buf, str, p - str);
688 str = p + 1;
689 }
690 else
691 {
692 FcStrBufString (buf, str);
693 break;
694 }
695
696 }
697
698 return FcTrue;
699 }
700
701 static FcBool
702 escape_chars (FcFormatContext *c,
703 const FcChar8 *str,
704 FcStrBuf *buf)
705 {
706 /* XXX not UTF-8 aware */
707
708 if (!expect_char (c, '(') ||
709 !read_chars (c, ')') ||
710 !expect_char (c, ')'))
711 return FcFalse;
712
713 while(*str)
714 {
715 FcChar8 *p;
716
717 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
718 if (p)
719 {
720 FcStrBufData (buf, str, p - str);
721 FcStrBufChar (buf, c->word[0]);
722 FcStrBufChar (buf, *p);
723 str = p + 1;
724 }
725 else
726 {
727 FcStrBufString (buf, str);
728 break;
729 }
730
731 }
732
733 return FcTrue;
734 }
735
736 static FcBool
737 translate_chars (FcFormatContext *c,
738 const FcChar8 *str,
739 FcStrBuf *buf)
740 {
741 char *from, *to, repeat;
742 int from_len, to_len;
743
744 /* XXX not UTF-8 aware */
745
746 if (!expect_char (c, '(') ||
747 !read_chars (c, ',') ||
748 !expect_char (c, ','))
749 return FcFalse;
750
751 from = (char *) c->word;
752 from_len = strlen (from);
753 to = from + from_len + 1;
754
755 /* hack: we temporarily diverge c->word */
756 c->word = (FcChar8 *) to;
757 if (!read_chars (c, ')'))
758 {
759 c->word = (FcChar8 *) from;
760 return FcFalse;
761 }
762 c->word = (FcChar8 *) from;
763
764 to_len = strlen (to);
765 repeat = to[to_len - 1];
766
767 if (!expect_char (c, ')'))
768 return FcFalse;
769
770 while(*str)
771 {
772 FcChar8 *p;
773
774 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
775 if (p)
776 {
777 int i;
778 FcStrBufData (buf, str, p - str);
779 i = strchr (from, *p) - from;
780 FcStrBufChar (buf, i < to_len ? to[i] : repeat);
781 str = p + 1;
782 }
783 else
784 {
785 FcStrBufString (buf, str);
786 break;
787 }
788
789 }
790
791 return FcTrue;
792 }
793
794 static FcBool
795 interpret_convert (FcFormatContext *c,
796 FcStrBuf *buf,
797 int start)
798 {
799 const FcChar8 *str;
800 FcChar8 *new_str;
801 FcStrBuf new_buf;
802 FcChar8 buf_static[8192];
803 FcBool ret;
804
805 if (!expect_char (c, '|') ||
806 !read_word (c))
807 return FcFalse;
808
809 /* prepare the buffer */
810 FcStrBufChar (buf, '\0');
811 if (buf->failed)
812 return FcFalse;
813 str = buf->buf + start;
814 buf->len = start;
815
816 /* try simple converters first */
817 if (0) { }
818 #define CONVERTER(name, func) \
819 else if (0 == strcmp ((const char *) c->word, name))\
820 do { new_str = func (str); ret = FcTrue; } while (0)
821 CONVERTER ("downcase", FcStrDowncase);
822 CONVERTER ("basename", FcStrBasename);
823 CONVERTER ("dirname", FcStrDirname);
824 #undef CONVERTER
825 else
826 ret = FcFalse;
827
828 if (ret)
829 {
830 if (new_str)
831 {
832 FcStrBufString (buf, new_str);
833 free (new_str);
834 return FcTrue;
835 }
836 else
837 return FcFalse;
838 }
839
840 FcStrBufInit (&new_buf, buf_static, sizeof (buf_static));
841
842 /* now try our custom converters */
843 if (0) { }
844 #define CONVERTER(name, func) \
845 else if (0 == strcmp ((const char *) c->word, name))\
846 ret = func (c, str, &new_buf)
847 CONVERTER ("cescape", cescape);
848 CONVERTER ("shescape", shescape);
849 CONVERTER ("xmlescape", xmlescape);
850 CONVERTER ("delete", delete_chars);
851 CONVERTER ("escape", escape_chars);
852 CONVERTER ("translate", translate_chars);
853 #undef CONVERTER
854 else
855 ret = FcFalse;
856
857 if (ret)
858 {
859 FcStrBufChar (&new_buf, '\0');
860 FcStrBufString (buf, new_buf.buf);
861 }
862 else
863 message ("unknown converter \"%s\"",
864 c->word);
865
866 FcStrBufDestroy (&new_buf);
867
868 return ret;
869 }
870
871 static FcBool
872 maybe_interpret_converts (FcFormatContext *c,
873 FcStrBuf *buf,
874 int start)
875 {
876 while (*c->format == '|')
877 if (!interpret_convert (c, buf, start))
878 return FcFalse;
879
880 return FcTrue;
881 }
882
883 static FcBool
884 align_to_width (FcStrBuf *buf,
885 int start,
886 int width)
887 {
888 int len;
889
890 if (buf->failed)
891 return FcFalse;
892
893 len = buf->len - start;
894 if (len < -width)
895 {
896 /* left align */
897 while (len++ < -width)
898 FcStrBufChar (buf, ' ');
899 }
900 else if (len < width)
901 {
902 int old_len;
903 old_len = len;
904 /* right align */
905 while (len++ < width)
906 FcStrBufChar (buf, ' ');
907 if (buf->failed)
908 return FcFalse;
909 len = old_len;
910 memmove (buf->buf + buf->len - len,
911 buf->buf + buf->len - width,
912 len);
913 memset (buf->buf + buf->len - width,
914 ' ',
915 width - len);
916 }
917
918 return !buf->failed;
919 }
920 static FcBool
921 interpret_percent (FcFormatContext *c,
922 FcPattern *pat,
923 FcStrBuf *buf)
924 {
925 int width, start;
926 FcBool ret;
927
928 if (!expect_char (c, '%'))
929 return FcFalse;
930
931 if (consume_char (c, '%')) /* "%%" */
932 {
933 FcStrBufChar (buf, '%');
934 return FcTrue;
935 }
936
937 /* parse an optional width specifier */
938 width = strtol ((const char *) c->format, (char **) &c->format, 10);
939
940 if (!expect_char (c, '{'))
941 return FcFalse;
942
943 start = buf->len;
944
945 switch (*c->format) {
946 case '=': ret = interpret_builtin (c, pat, buf); break;
947 case '{': ret = interpret_subexpr (c, pat, buf); break;
948 case '+': ret = interpret_filter (c, pat, buf); break;
949 case '-': ret = interpret_delete (c, pat, buf); break;
950 case '?': ret = interpret_cond (c, pat, buf); break;
951 case '#': ret = interpret_count (c, pat, buf); break;
952 default: ret = interpret_simple (c, pat, buf); break;
953 }
954
955 return ret &&
956 maybe_interpret_converts (c, buf, start) &&
957 align_to_width (buf, start, width) &&
958 expect_char (c, '}');
959 }
960
961 static FcBool
962 interpret_expr (FcFormatContext *c,
963 FcPattern *pat,
964 FcStrBuf *buf,
965 FcChar8 term)
966 {
967 while (*c->format && *c->format != term)
968 {
969 switch (*c->format)
970 {
971 case '\\':
972 c->format++; /* skip over '\\' */
973 if (*c->format)
974 FcStrBufChar (buf, escaped_char (*c->format++));
975 continue;
976 case '%':
977 if (!interpret_percent (c, pat, buf))
978 return FcFalse;
979 continue;
980 }
981 FcStrBufChar (buf, *c->format++);
982 }
983 return FcTrue;
984 }
985
986 static FcBool
987 FcPatternFormatToBuf (FcPattern *pat,
988 const FcChar8 *format,
989 FcStrBuf *buf)
990 {
991 FcFormatContext c;
992 FcChar8 word_static[1024];
993 FcBool ret;
994
995 if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static)))
996 return FcFalse;
997
998 ret = interpret_expr (&c, pat, buf, '\0');
999
1000 FcFormatContextDone (&c);
1001
1002 return ret;
1003 }
1004
1005 FcChar8 *
1006 FcPatternFormat (FcPattern *pat,
1007 const FcChar8 *format)
1008 {
1009 FcStrBuf buf;
1010 FcChar8 buf_static[8192 - 1024];
1011 FcBool ret;
1012
1013 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
1014
1015 ret = FcPatternFormatToBuf (pat, format, &buf);
1016
1017 if (ret)
1018 return FcStrBufDone (&buf);
1019 else
1020 {
1021 FcStrBufDestroy (&buf);
1022 return NULL;
1023 }
1024 }
1025
1026 #define __fcformat__
1027 #include "fcaliastail.h"
1028 #undef __fcformat__