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