2 * Copyright © 2008,2009 Red Hat, Inc.
4 * Red Hat Author(s): Behdad Esfahbod
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.
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.
32 * Some ideas for future syntax extensions:
34 * - langset enumeration using same syntax as array enumeration
35 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
36 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
40 #define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
41 #define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
45 message (const char *fmt, ...)
49 fprintf (stderr, "Fontconfig: Pattern format error: ");
50 vfprintf (stderr, fmt, args);
51 fprintf (stderr, ".\n");
56 typedef struct _FcFormatContext
58 const FcChar8 *format_orig;
59 const FcChar8 *format;
62 FcBool word_allocated;
66 FcFormatContextInit (FcFormatContext *c,
67 const FcChar8 *format,
71 c->format_orig = c->format = format;
72 c->format_len = strlen ((const char *) format);
74 if (c->format_len < scratch_len)
77 c->word_allocated = FcFalse;
81 c->word = malloc (c->format_len + 1);
82 c->word_allocated = FcTrue;
85 return c->word != NULL;
89 FcFormatContextDone (FcFormatContext *c)
91 if (c && c->word_allocated)
98 consume_char (FcFormatContext *c,
101 if (*c->format != term)
109 expect_char (FcFormatContext *c,
112 FcBool res = consume_char (c, term);
115 if (c->format == c->format_orig + c->format_len)
116 message ("format ended while expecting '%c'",
119 message ("expected '%c' at %d",
120 term, c->format - c->format_orig + 1);
126 FcCharIsPunct (const FcChar8 c)
145 static char escaped_char(const char 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';
160 read_word (FcFormatContext *c)
168 if (*c->format == '\\')
172 *p++ = escaped_char (*c->format++);
175 else if (FcCharIsPunct (*c->format))
184 message ("expected identifier at %d",
185 c->format - c->format_orig + 1);
193 read_chars (FcFormatContext *c,
200 while (*c->format && *c->format != '}' && *c->format != term)
202 if (*c->format == '\\')
206 *p++ = escaped_char (*c->format++);
216 message ("expected character data at %d",
217 c->format - c->format_orig + 1);
225 FcPatternFormatToBuf (FcPattern *pat,
226 const FcChar8 *format,
230 interpret_builtin (FcFormatContext *c,
237 if (!expect_char (c, '=') ||
241 /* try simple builtins first */
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 */
256 FcStrBufString (buf, new_str);
264 /* now try our custom formats */
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);
276 message ("unknown builtin \"%s\"",
283 interpret_expr (FcFormatContext *c,
289 interpret_subexpr (FcFormatContext *c,
293 return expect_char (c, '{') &&
294 interpret_expr (c, pat, buf, '}') &&
295 expect_char (c, '}');
299 maybe_interpret_subexpr (FcFormatContext *c,
303 return (*c->format == '{') ?
304 interpret_subexpr (c, pat, buf) :
309 skip_subexpr (FcFormatContext *c);
312 skip_percent (FcFormatContext *c)
316 if (!expect_char (c, '%'))
319 /* skip an optional width specifier */
320 width = strtol ((const char *) c->format, (char **) &c->format, 10);
322 if (!expect_char (c, '{'))
325 while(*c->format && *c->format != '}')
330 c->format++; /* skip over '\\' */
335 if (!skip_subexpr (c))
342 return expect_char (c, '}');
346 skip_expr (FcFormatContext *c)
348 while(*c->format && *c->format != '}')
353 c->format++; /* skip over '\\' */
358 if (!skip_percent (c))
369 skip_subexpr (FcFormatContext *c)
371 return expect_char (c, '{') &&
373 expect_char (c, '}');
377 maybe_skip_subexpr (FcFormatContext *c)
379 return (*c->format == '{') ?
385 interpret_filter (FcFormatContext *c,
392 if (!expect_char (c, '+'))
395 os = FcObjectSetCreate ();
401 if (!read_word (c) ||
402 !FcObjectSetAdd (os, (const char *) c->word))
404 FcObjectSetDestroy (os);
408 while (consume_char (c, ','));
410 subpat = FcPatternFilter (pat, os);
411 FcObjectSetDestroy (os);
414 !interpret_subexpr (c, subpat, buf))
417 FcPatternDestroy (subpat);
422 interpret_delete (FcFormatContext *c,
428 if (!expect_char (c, '-'))
431 subpat = FcPatternDuplicate (pat);
439 FcPatternDestroy (subpat);
443 FcPatternDel (subpat, (const char *) c->word);
445 while (consume_char (c, ','));
447 if (!interpret_subexpr (c, subpat, buf))
450 FcPatternDestroy (subpat);
455 interpret_cond (FcFormatContext *c,
461 if (!expect_char (c, '?'))
471 negate = consume_char (c, '!');
479 FcPatternGet (pat, (const char *) c->word, 0, &v)));
481 while (consume_char (c, ','));
485 if (!interpret_subexpr (c, pat, buf) ||
486 !maybe_skip_subexpr (c))
491 if (!skip_subexpr (c) ||
492 !maybe_interpret_subexpr (c, pat, buf))
500 interpret_count (FcFormatContext *c,
506 FcChar8 buf_static[64];
508 if (!expect_char (c, '#'))
515 e = FcPatternObjectFindElt (pat,
516 FcObjectFromName ((const char *) c->word));
521 for (l = FcPatternEltValues(e);
527 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
528 FcStrBufString (buf, buf_static);
534 interpret_array (FcFormatContext *c,
540 const FcChar8 *format_save;
544 if (!expect_char (c, '[') ||
545 !expect_char (c, ']'))
548 os = FcObjectSetCreate ();
556 if (!read_word (c) ||
557 !FcObjectSetAdd (os, (const char *) c->word))
559 FcObjectSetDestroy (os);
563 while (consume_char (c, ','));
565 subpat = FcPatternDuplicate (pat);
569 format_save = c->format;
577 for (i = 0; i < os->nobject; i++)
581 /* XXX this can be optimized by accessing valuelist linked lists
582 * directly and remembering where we were. Most (all) value lists
583 * in normal uses are pretty short though (language tags are
584 * stored as a LangSet, not separate values.). */
585 FcPatternDel (subpat, os->objects[i]);
587 FcPatternGet (pat, os->objects[i], idx, &v))
589 FcPatternAdd (subpat, os->objects[i], v, FcFalse);
596 c->format = format_save;
597 ret = interpret_subexpr (c, subpat, buf);
606 FcPatternDestroy (subpat);
608 FcObjectSetDestroy (os);
614 interpret_simple (FcFormatContext *c,
619 FcBool add_colon = FcFalse;
620 FcBool add_elt_name = FcFalse;
622 FcChar8 *else_string;
624 if (consume_char (c, ':'))
631 if (consume_char (c, '['))
633 idx = strtol ((const char *) c->format, (char **) &c->format, 10);
636 message ("expected non-negative number at %d",
637 c->format-1 - c->format_orig + 1);
640 if (!expect_char (c, ']'))
644 if (consume_char (c, '='))
645 add_elt_name = FcTrue;
649 if (consume_char (c, ':'))
652 /* divert the c->word for now */
654 c->word = c->word + strlen ((const char *) c->word) + 1;
655 /* for now we just support 'default value' */
656 if (!expect_char (c, '-') ||
657 !read_chars (c, '\0'))
662 else_string = c->word;
666 e = FcPatternObjectFindElt (pat,
667 FcObjectFromName ((const char *) c->word));
673 FcStrBufChar (buf, ':');
676 FcStrBufString (buf, c->word);
677 FcStrBufChar (buf, '=');
680 l = FcPatternEltValues(e);
686 l = FcValueListNext(l);
691 if (!FcNameUnparseValue (buf, &l->value, '\0'))
698 FcNameUnparseValueList (buf, l, '\0');
705 printf ("%s", else_string);
712 cescape (FcFormatContext *c,
722 FcStrBufChar (buf, '\\');
725 FcStrBufChar (buf, *str++);
731 shescape (FcFormatContext *c,
735 FcStrBufChar (buf, '\'');
739 FcStrBufString (buf, (const FcChar8 *) "'\\''");
741 FcStrBufChar (buf, *str);
744 FcStrBufChar (buf, '\'');
749 xmlescape (FcFormatContext *c,
757 case '&': FcStrBufString (buf, (const FcChar8 *) "&"); break;
758 case '<': FcStrBufString (buf, (const FcChar8 *) "<"); break;
759 case '>': FcStrBufString (buf, (const FcChar8 *) ">"); break;
760 default: FcStrBufChar (buf, *str); break;
768 delete_chars (FcFormatContext *c,
772 /* XXX not UTF-8 aware */
774 if (!expect_char (c, '(') ||
775 !read_chars (c, ')') ||
776 !expect_char (c, ')'))
783 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
786 FcStrBufData (buf, str, p - str);
791 FcStrBufString (buf, str);
801 escape_chars (FcFormatContext *c,
805 /* XXX not UTF-8 aware */
807 if (!expect_char (c, '(') ||
808 !read_chars (c, ')') ||
809 !expect_char (c, ')'))
816 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
819 FcStrBufData (buf, str, p - str);
820 FcStrBufChar (buf, c->word[0]);
821 FcStrBufChar (buf, *p);
826 FcStrBufString (buf, str);
836 translate_chars (FcFormatContext *c,
840 char *from, *to, repeat;
841 int from_len, to_len;
843 /* XXX not UTF-8 aware */
845 if (!expect_char (c, '(') ||
846 !read_chars (c, ',') ||
847 !expect_char (c, ','))
850 from = (char *) c->word;
851 from_len = strlen (from);
852 to = from + from_len + 1;
854 /* hack: we temporarily divert c->word */
855 c->word = (FcChar8 *) to;
856 if (!read_chars (c, ')'))
858 c->word = (FcChar8 *) from;
861 c->word = (FcChar8 *) from;
863 to_len = strlen (to);
864 repeat = to[to_len - 1];
866 if (!expect_char (c, ')'))
873 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
877 FcStrBufData (buf, str, p - str);
878 i = strchr (from, *p) - from;
879 FcStrBufChar (buf, i < to_len ? to[i] : repeat);
884 FcStrBufString (buf, str);
894 interpret_convert (FcFormatContext *c,
901 FcChar8 buf_static[8192];
904 if (!expect_char (c, '|') ||
908 /* prepare the buffer */
909 FcStrBufChar (buf, '\0');
912 str = buf->buf + start;
915 /* try simple converters first */
917 #define CONVERTER(name, func) \
918 else if (0 == strcmp ((const char *) c->word, name))\
919 do { new_str = func (str); ret = FcTrue; } while (0)
920 CONVERTER ("downcase", FcStrDowncase);
921 CONVERTER ("basename", FcStrBasename);
922 CONVERTER ("dirname", FcStrDirname);
931 FcStrBufString (buf, new_str);
939 FcStrBufInit (&new_buf, buf_static, sizeof (buf_static));
941 /* now try our custom converters */
943 #define CONVERTER(name, func) \
944 else if (0 == strcmp ((const char *) c->word, name))\
945 ret = func (c, str, &new_buf)
946 CONVERTER ("cescape", cescape);
947 CONVERTER ("shescape", shescape);
948 CONVERTER ("xmlescape", xmlescape);
949 CONVERTER ("delete", delete_chars);
950 CONVERTER ("escape", escape_chars);
951 CONVERTER ("translate", translate_chars);
958 FcStrBufChar (&new_buf, '\0');
959 FcStrBufString (buf, new_buf.buf);
962 message ("unknown converter \"%s\"",
965 FcStrBufDestroy (&new_buf);
971 maybe_interpret_converts (FcFormatContext *c,
975 while (*c->format == '|')
976 if (!interpret_convert (c, buf, start))
983 align_to_width (FcStrBuf *buf,
992 len = buf->len - start;
996 while (len++ < -width)
997 FcStrBufChar (buf, ' ');
999 else if (len < width)
1004 while (len++ < width)
1005 FcStrBufChar (buf, ' ');
1009 memmove (buf->buf + buf->len - len,
1010 buf->buf + buf->len - width,
1012 memset (buf->buf + buf->len - width,
1017 return !buf->failed;
1020 interpret_percent (FcFormatContext *c,
1027 if (!expect_char (c, '%'))
1030 if (consume_char (c, '%')) /* "%%" */
1032 FcStrBufChar (buf, '%');
1036 /* parse an optional width specifier */
1037 width = strtol ((const char *) c->format, (char **) &c->format, 10);
1039 if (!expect_char (c, '{'))
1044 switch (*c->format) {
1045 case '=': ret = interpret_builtin (c, pat, buf); break;
1046 case '{': ret = interpret_subexpr (c, pat, buf); break;
1047 case '+': ret = interpret_filter (c, pat, buf); break;
1048 case '-': ret = interpret_delete (c, pat, buf); break;
1049 case '?': ret = interpret_cond (c, pat, buf); break;
1050 case '#': ret = interpret_count (c, pat, buf); break;
1051 case '[': ret = interpret_array (c, pat, buf); break;
1052 default: ret = interpret_simple (c, pat, buf); break;
1056 maybe_interpret_converts (c, buf, start) &&
1057 align_to_width (buf, start, width) &&
1058 expect_char (c, '}');
1062 interpret_expr (FcFormatContext *c,
1067 while (*c->format && *c->format != term)
1072 c->format++; /* skip over '\\' */
1074 FcStrBufChar (buf, escaped_char (*c->format++));
1077 if (!interpret_percent (c, pat, buf))
1081 FcStrBufChar (buf, *c->format++);
1087 FcPatternFormatToBuf (FcPattern *pat,
1088 const FcChar8 *format,
1092 FcChar8 word_static[1024];
1095 if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static)))
1098 ret = interpret_expr (&c, pat, buf, '\0');
1100 FcFormatContextDone (&c);
1106 FcPatternFormat (FcPattern *pat,
1107 const FcChar8 *format)
1110 FcChar8 buf_static[8192 - 1024];
1113 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
1115 ret = FcPatternFormatToBuf (pat, format, &buf);
1118 return FcStrBufDone (&buf);
1121 FcStrBufDestroy (&buf);
1126 #define __fcformat__
1127 #include "fcaliastail.h"