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