]> git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
[fcformat] Enumerate langsets like we do arrays of values
[fontconfig.git] / src / fcformat.c
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
43 static void
44 message (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
55 typedef 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
64 static FcBool
65 FcFormatContextInit (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
87 static void
88 FcFormatContextDone (FcFormatContext *c)
89 {
90 if (c && c->word_allocated)
91 {
92 free (c->word);
93 }
94 }
95
96 static FcBool
97 consume_char (FcFormatContext *c,
98 FcChar8 term)
99 {
100 if (*c->format != term)
101 return FcFalse;
102
103 c->format++;
104 return FcTrue;
105 }
106
107 static FcBool
108 expect_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
124 static FcBool
125 FcCharIsPunct (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
144 static 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
158 static FcBool
159 read_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
191 static FcBool
192 read_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
223 static FcBool
224 FcPatternFormatToBuf (FcPattern *pat,
225 const FcChar8 *format,
226 FcStrBuf *buf);
227
228 static FcBool
229 interpret_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
281 static FcBool
282 interpret_expr (FcFormatContext *c,
283 FcPattern *pat,
284 FcStrBuf *buf,
285 FcChar8 term);
286
287 static FcBool
288 interpret_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
297 static FcBool
298 maybe_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
307 static FcBool
308 skip_subexpr (FcFormatContext *c);
309
310 static FcBool
311 skip_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
344 static FcBool
345 skip_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
367 static FcBool
368 skip_subexpr (FcFormatContext *c)
369 {
370 return expect_char (c, '{') &&
371 skip_expr (c) &&
372 expect_char (c, '}');
373 }
374
375 static FcBool
376 maybe_skip_subexpr (FcFormatContext *c)
377 {
378 return (*c->format == '{') ?
379 skip_subexpr (c) :
380 FcTrue;
381 }
382
383 static FcBool
384 interpret_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
420 static FcBool
421 interpret_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
453 static FcBool
454 interpret_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
498 static FcBool
499 interpret_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
532 static FcBool
533 interpret_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
638 bail:
639 FcPatternDestroy (subpat);
640 bail0:
641 if (lang_strs)
642 FcStrListDone (lang_strs);
643 FcObjectSetDestroy (os);
644
645 return ret;
646 }
647
648 static FcBool
649 interpret_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
737 notfound:
738 {
739 if (else_string)
740 printf ("%s", else_string);
741 }
742
743 return FcTrue;
744 }
745
746 static FcBool
747 cescape (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
765 static FcBool
766 shescape (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
783 static FcBool
784 xmlescape (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
802 static FcBool
803 delete_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
835 static FcBool
836 escape_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
870 static FcBool
871 translate_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
928 static FcBool
929 interpret_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
1005 static FcBool
1006 maybe_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
1017 static FcBool
1018 align_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 }
1054 static FcBool
1055 interpret_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
1096 static FcBool
1097 interpret_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
1121 static FcBool
1122 FcPatternFormatToBuf (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
1140 FcChar8 *
1141 FcPatternFormat (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__