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