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