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