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