]> git.wh0rd.org - fontconfig.git/blame - src/fcformat.c
[fcformat] Support indexing simple tags
[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 41
9c83a837
BE
42/* fc-match needs '<unknown filename>', etc handling. */
43#define FCMATCH_FORMAT "%{file|basename}: \"%{family[0]}\" \"%{style[0]}\""
d04a7507
BE
44#define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
45
46
0c93b91d
BE
47static void
48message (const char *fmt, ...)
49{
50 va_list args;
51 va_start (args, fmt);
8c31a243 52 fprintf (stderr, "Fontconfig: Pattern format error: ");
0c93b91d 53 vfprintf (stderr, fmt, args);
d6506ff6 54 fprintf (stderr, ".\n");
0c93b91d
BE
55 va_end (args);
56}
57
58
27b3e2dd 59typedef struct _FcFormatContext
0c93b91d 60{
27b3e2dd
BE
61 const FcChar8 *format_orig;
62 const FcChar8 *format;
63 int format_len;
2017a5eb 64 FcChar8 *word;
85c7fb67 65 FcBool word_allocated;
27b3e2dd 66} FcFormatContext;
c493c3b7 67
8c31a243 68static FcBool
27b3e2dd 69FcFormatContextInit (FcFormatContext *c,
85c7fb67
BE
70 const FcChar8 *format,
71 FcChar8 *scratch,
72 int scratch_len)
27b3e2dd
BE
73{
74 c->format_orig = c->format = format;
75 c->format_len = strlen ((const char *) format);
85c7fb67
BE
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 }
8c31a243 87
2017a5eb 88 return c->word != NULL;
27b3e2dd 89}
c493c3b7 90
27b3e2dd
BE
91static void
92FcFormatContextDone (FcFormatContext *c)
93{
85c7fb67 94 if (c && c->word_allocated)
27b3e2dd 95 {
2017a5eb 96 free (c->word);
27b3e2dd
BE
97 }
98}
c493c3b7 99
27b3e2dd
BE
100static FcBool
101consume_char (FcFormatContext *c,
102 FcChar8 term)
103{
104 if (*c->format != term)
d6506ff6
BE
105 return FcFalse;
106
107 c->format++;
108 return FcTrue;
109}
110
111static FcBool
112expect_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
128static FcBool
129FcCharIsPunct (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
c8f5933d
BE
148static 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
d6506ff6 162static FcBool
2017a5eb 163read_word (FcFormatContext *c)
d6506ff6
BE
164{
165 FcChar8 *p;
166
2017a5eb 167 p = c->word;
d6506ff6
BE
168
169 while (*c->format)
170 {
171 if (*c->format == '\\')
172 {
173 c->format++;
174 if (*c->format)
c8f5933d 175 *p++ = escaped_char (*c->format++);
d6506ff6
BE
176 continue;
177 }
178 else if (FcCharIsPunct (*c->format))
179 break;
180
181 *p++ = *c->format++;
182 }
183 *p = '\0';
184
2017a5eb 185 if (p == c->word)
0c93b91d 186 {
85c7fb67 187 message ("expected identifier at %d",
d6506ff6 188 c->format - c->format_orig + 1);
27b3e2dd
BE
189 return FcFalse;
190 }
0c93b91d 191
27b3e2dd
BE
192 return FcTrue;
193}
0c93b91d 194
c8f5933d
BE
195static FcBool
196read_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
d04a7507
BE
227static FcBool
228FcPatternFormatToBuf (FcPattern *pat,
229 const FcChar8 *format,
230 FcStrBuf *buf);
231
d4f7a4c6
BE
232static FcBool
233interpret_builtin (FcFormatContext *c,
234 FcPattern *pat,
235 FcStrBuf *buf)
236{
d04a7507
BE
237 FcChar8 *new_str;
238 FcBool ret;
d4f7a4c6 239
d04a7507
BE
240 if (!expect_char (c, '=') ||
241 !read_word (c))
d4f7a4c6 242 return FcFalse;
d04a7507
BE
243
244 /* try simple builtins first */
245 if (0) { }
d4f7a4c6
BE
246#define BUILTIN(name, func) \
247 else if (0 == strcmp ((const char *) c->word, name))\
d04a7507
BE
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;
d4f7a4c6
BE
283}
284
8c31a243 285static FcBool
7717b25f
BE
286interpret_expr (FcFormatContext *c,
287 FcPattern *pat,
288 FcStrBuf *buf,
289 FcChar8 term);
d6506ff6 290
8c31a243
BE
291static FcBool
292interpret_subexpr (FcFormatContext *c,
27b3e2dd
BE
293 FcPattern *pat,
294 FcStrBuf *buf)
295{
8c31a243 296 return expect_char (c, '{') &&
7717b25f 297 interpret_expr (c, pat, buf, '}') &&
8c31a243
BE
298 expect_char (c, '}');
299}
0c93b91d 300
7717b25f
BE
301static FcBool
302maybe_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
311static FcBool
312skip_subexpr (FcFormatContext *c);
313
314static FcBool
315skip_percent (FcFormatContext *c)
316{
2017a5eb
BE
317 int width;
318
7717b25f
BE
319 if (!expect_char (c, '%'))
320 return FcFalse;
321
322 /* skip an optional width specifier */
2017a5eb 323 width = strtol ((const char *) c->format, (char **) &c->format, 10);
7717b25f
BE
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
348static FcBool
349skip_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
371static FcBool
372skip_subexpr (FcFormatContext *c)
373{
374 return expect_char (c, '{') &&
375 skip_expr (c) &&
376 expect_char (c, '}');
377}
378
379static FcBool
380maybe_skip_subexpr (FcFormatContext *c)
381{
382 return (*c->format == '{') ?
383 skip_subexpr (c) :
384 FcTrue;
385}
386
8c31a243
BE
387static FcBool
388interpret_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 {
2017a5eb
BE
404 if (!read_word (c) ||
405 !FcObjectSetAdd (os, (const char *) c->word))
8c31a243
BE
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
424static FcBool
425interpret_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 {
2017a5eb 440 if (!read_word (c))
8c31a243
BE
441 {
442 FcPatternDestroy (subpat);
443 return FcFalse;
444 }
445
2017a5eb 446 FcPatternDel (subpat, (const char *) c->word);
8c31a243
BE
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
7717b25f 457static FcBool
2017a5eb
BE
458interpret_cond (FcFormatContext *c,
459 FcPattern *pat,
460 FcStrBuf *buf)
7717b25f
BE
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
2017a5eb 476 if (!read_word (c))
7717b25f
BE
477 return FcFalse;
478
479 pass = pass &&
480 (negate ^
2017a5eb
BE
481 (FcResultMatch == FcPatternGet (pat,
482 (const char *) c->word,
483 0, &v)));
7717b25f
BE
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
b6a23028
BE
503static FcBool
504interpret_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
c8f5933d 531 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
b6a23028
BE
532 FcStrBufString (buf, buf_static);
533
534 return FcTrue;
535}
536
537static FcBool
538interpret_simple (FcFormatContext *c,
539 FcPattern *pat,
540 FcStrBuf *buf)
541{
542 FcPatternElt *e;
543 FcBool add_colon = FcFalse;
544 FcBool add_elt_name = FcFalse;
9c83a837 545 int idx;
b6a23028
BE
546
547 if (consume_char (c, ':'))
548 add_colon = FcTrue;
549
550 if (!read_word (c))
551 return FcFalse;
552
9c83a837
BE
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
b6a23028
BE
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);
9c83a837
BE
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
605notfound:
606 {
b6a23028
BE
607 }
608
609 return FcTrue;
610}
611
85c7fb67
BE
612static FcBool
613cescape (FcFormatContext *c,
d04a7507
BE
614 const FcChar8 *str,
615 FcStrBuf *buf)
ced38254 616{
ced38254
BE
617 while(*str)
618 {
619 switch (*str)
620 {
621 case '\\':
622 case '"':
85c7fb67 623 FcStrBufChar (buf, '\\');
ced38254
BE
624 break;
625 }
85c7fb67 626 FcStrBufChar (buf, *str++);
ced38254 627 }
85c7fb67 628 return FcTrue;
ced38254
BE
629}
630
85c7fb67
BE
631static FcBool
632shescape (FcFormatContext *c,
d04a7507
BE
633 const FcChar8 *str,
634 FcStrBuf *buf)
ced38254 635{
85c7fb67 636 FcStrBufChar (buf, '\'');
ced38254
BE
637 while(*str)
638 {
639 if (*str == '\'')
85c7fb67 640 FcStrBufString (buf, (const FcChar8 *) "'\\''");
ced38254 641 else
85c7fb67 642 FcStrBufChar (buf, *str);
ced38254
BE
643 str++;
644 }
85c7fb67
BE
645 FcStrBufChar (buf, '\'');
646 return FcTrue;
ced38254
BE
647}
648
85c7fb67
BE
649static FcBool
650xmlescape (FcFormatContext *c,
d04a7507
BE
651 const FcChar8 *str,
652 FcStrBuf *buf)
ced38254 653{
ced38254
BE
654 while(*str)
655 {
656 switch (*str)
657 {
85c7fb67
BE
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;
ced38254
BE
662 }
663 str++;
664 }
85c7fb67 665 return FcTrue;
ced38254
BE
666}
667
85c7fb67 668static FcBool
c8f5933d 669delete_chars (FcFormatContext *c,
d04a7507
BE
670 const FcChar8 *str,
671 FcStrBuf *buf)
c8f5933d 672{
c8f5933d
BE
673 /* XXX not UTF-8 aware */
674
675 if (!expect_char (c, '(') ||
676 !read_chars (c, ')') ||
677 !expect_char (c, ')'))
85c7fb67 678 return FcFalse;
c8f5933d 679
c8f5933d
BE
680 while(*str)
681 {
682 FcChar8 *p;
683
684 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
685 if (p)
686 {
85c7fb67 687 FcStrBufData (buf, str, p - str);
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 702escape_chars (FcFormatContext *c,
d04a7507
BE
703 const FcChar8 *str,
704 FcStrBuf *buf)
c8f5933d 705{
c8f5933d
BE
706 /* XXX not UTF-8 aware */
707
708 if (!expect_char (c, '(') ||
709 !read_chars (c, ')') ||
710 !expect_char (c, ')'))
85c7fb67 711 return FcFalse;
c8f5933d 712
c8f5933d
BE
713 while(*str)
714 {
715 FcChar8 *p;
716
717 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
718 if (p)
719 {
85c7fb67
BE
720 FcStrBufData (buf, str, p - str);
721 FcStrBufChar (buf, c->word[0]);
722 FcStrBufChar (buf, *p);
c8f5933d
BE
723 str = p + 1;
724 }
725 else
726 {
85c7fb67 727 FcStrBufString (buf, str);
c8f5933d
BE
728 break;
729 }
730
731 }
85c7fb67
BE
732
733 return FcTrue;
c8f5933d
BE
734}
735
85c7fb67 736static FcBool
c8f5933d 737translate_chars (FcFormatContext *c,
d04a7507
BE
738 const FcChar8 *str,
739 FcStrBuf *buf)
c8f5933d 740{
c8f5933d
BE
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, ','))
85c7fb67 749 return FcFalse;
c8f5933d
BE
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
c8f5933d
BE
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;
85c7fb67 778 FcStrBufData (buf, str, p - str);
c8f5933d 779 i = strchr (from, *p) - from;
85c7fb67 780 FcStrBufChar (buf, i < to_len ? to[i] : repeat);
c8f5933d
BE
781 str = p + 1;
782 }
783 else
784 {
85c7fb67 785 FcStrBufString (buf, str);
c8f5933d
BE
786 break;
787 }
788
789 }
85c7fb67
BE
790
791 return FcTrue;
c8f5933d
BE
792}
793
85c7fb67
BE
794static FcBool
795interpret_convert (FcFormatContext *c,
796 FcStrBuf *buf,
797 int start)
2017a5eb 798{
85c7fb67
BE
799 const FcChar8 *str;
800 FcChar8 *new_str;
801 FcStrBuf new_buf;
802 FcChar8 buf_static[8192];
803 FcBool ret;
804
d04a7507
BE
805 if (!expect_char (c, '|') ||
806 !read_word (c))
85c7fb67
BE
807 return FcFalse;
808
d04a7507 809 /* prepare the buffer */
85c7fb67
BE
810 FcStrBufChar (buf, '\0');
811 if (buf->failed)
812 return FcFalse;
813 str = buf->buf + start;
814 buf->len = start;
815
85c7fb67
BE
816 /* try simple converters first */
817 if (0) { }
ced38254
BE
818#define CONVERTER(name, func) \
819 else if (0 == strcmp ((const char *) c->word, name))\
85c7fb67 820 do { new_str = func (str); ret = FcTrue; } while (0)
c8f5933d
BE
821 CONVERTER ("downcase", FcStrDowncase);
822 CONVERTER ("basename", FcStrBasename);
823 CONVERTER ("dirname", FcStrDirname);
85c7fb67
BE
824#undef CONVERTER
825 else
826 ret = FcFalse;
827
828 if (ret)
829 {
830 if (new_str)
831 {
85c7fb67
BE
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))\
d04a7507 846 ret = func (c, str, &new_buf)
85c7fb67
BE
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;
2017a5eb
BE
869}
870
871static FcBool
872maybe_interpret_converts (FcFormatContext *c,
873 FcStrBuf *buf,
874 int start)
875{
85c7fb67
BE
876 while (*c->format == '|')
877 if (!interpret_convert (c, buf, start))
2017a5eb
BE
878 return FcFalse;
879
2017a5eb
BE
880 return FcTrue;
881}
882
883static FcBool
884align_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}
8c31a243
BE
920static FcBool
921interpret_percent (FcFormatContext *c,
922 FcPattern *pat,
923 FcStrBuf *buf)
924{
2017a5eb
BE
925 int width, start;
926 FcBool ret;
8c31a243
BE
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
8c31a243
BE
940 if (!expect_char (c, '{'))
941 return FcFalse;
942
2017a5eb 943 start = buf->len;
7717b25f 944
2017a5eb 945 switch (*c->format) {
d4f7a4c6 946 case '=': ret = interpret_builtin (c, pat, buf); break;
2017a5eb
BE
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;
b6a23028 951 case '#': ret = interpret_count (c, pat, buf); break;
2017a5eb 952 default: ret = interpret_simple (c, pat, buf); break;
0c93b91d 953 }
c493c3b7 954
2017a5eb
BE
955 return ret &&
956 maybe_interpret_converts (c, buf, start) &&
957 align_to_width (buf, start, width) &&
958 expect_char (c, '}');
0c93b91d
BE
959}
960
8c31a243 961static FcBool
7717b25f
BE
962interpret_expr (FcFormatContext *c,
963 FcPattern *pat,
964 FcStrBuf *buf,
965 FcChar8 term)
0c93b91d 966{
7717b25f 967 while (*c->format && *c->format != term)
0c93b91d 968 {
27b3e2dd 969 switch (*c->format)
0c93b91d
BE
970 {
971 case '\\':
27b3e2dd
BE
972 c->format++; /* skip over '\\' */
973 if (*c->format)
974 FcStrBufChar (buf, escaped_char (*c->format++));
0c93b91d
BE
975 continue;
976 case '%':
8c31a243
BE
977 if (!interpret_percent (c, pat, buf))
978 return FcFalse;
0c93b91d
BE
979 continue;
980 }
27b3e2dd 981 FcStrBufChar (buf, *c->format++);
0c93b91d 982 }
8c31a243 983 return FcTrue;
0c93b91d
BE
984}
985
d04a7507
BE
986static FcBool
987FcPatternFormatToBuf (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
0c93b91d 1005FcChar8 *
d04a7507
BE
1006FcPatternFormat (FcPattern *pat,
1007 const FcChar8 *format)
0c93b91d 1008{
ced38254 1009 FcStrBuf buf;
85c7fb67 1010 FcChar8 buf_static[8192 - 1024];
ced38254 1011 FcBool ret;
0c93b91d 1012
ced38254 1013 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
0c93b91d 1014
d04a7507 1015 ret = FcPatternFormatToBuf (pat, format, &buf);
0c93b91d 1016
8c31a243
BE
1017 if (ret)
1018 return FcStrBufDone (&buf);
1019 else
1020 {
1021 FcStrBufDestroy (&buf);
1022 return NULL;
1023 }
0c93b91d
BE
1024}
1025
1026#define __fcformat__
1027#include "fcaliastail.h"
1028#undef __fcformat__