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