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