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 * - array enumeration using '%{[]family,familylang{expr}|decorator}'
35 * - langset enumeration using same syntax as array enumeration
36 * - allow indexing simple tags using '%{elt[idx]}'
37 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
38 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
42 /* fc-match needs '<unknown filename>', etc handling. */
43 #define FCMATCH_FORMAT "%{file|basename}: \"%{family[0]}\" \"%{style[0]}\""
44 #define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
48 message (const char *fmt, ...)
52 fprintf (stderr, "Fontconfig: Pattern format error: ");
53 vfprintf (stderr, fmt, args);
54 fprintf (stderr, ".\n");
59 typedef struct _FcFormatContext
61 const FcChar8 *format_orig;
62 const FcChar8 *format;
65 FcBool word_allocated;
69 FcFormatContextInit (FcFormatContext *c,
70 const FcChar8 *format,
74 c->format_orig = c->format = format;
75 c->format_len = strlen ((const char *) format);
77 if (c->format_len < scratch_len)
80 c->word_allocated = FcFalse;
84 c->word = malloc (c->format_len + 1);
85 c->word_allocated = FcTrue;
88 return c->word != NULL;
92 FcFormatContextDone (FcFormatContext *c)
94 if (c && c->word_allocated)
101 consume_char (FcFormatContext *c,
104 if (*c->format != term)
112 expect_char (FcFormatContext *c,
115 FcBool res = consume_char (c, term);
118 if (c->format == c->format_orig + c->format_len)
119 message ("format ended while expecting '%c'",
122 message ("expected '%c' at %d",
123 term, c->format - c->format_orig + 1);
129 FcCharIsPunct (const FcChar8 c)
148 static char escaped_char(const char ch)
151 case 'a': return '\a';
152 case 'b': return '\b';
153 case 'f': return '\f';
154 case 'n': return '\n';
155 case 'r': return '\r';
156 case 't': return '\t';
157 case 'v': return '\v';
163 read_word (FcFormatContext *c)
171 if (*c->format == '\\')
175 *p++ = escaped_char (*c->format++);
178 else if (FcCharIsPunct (*c->format))
187 message ("expected identifier at %d",
188 c->format - c->format_orig + 1);
196 read_chars (FcFormatContext *c,
203 while (*c->format && *c->format != '}' && *c->format != term)
205 if (*c->format == '\\')
209 *p++ = escaped_char (*c->format++);
219 message ("expected character data at %d",
220 c->format - c->format_orig + 1);
228 FcPatternFormatToBuf (FcPattern *pat,
229 const FcChar8 *format,
233 interpret_builtin (FcFormatContext *c,
240 if (!expect_char (c, '=') ||
244 /* try simple builtins first */
246 #define BUILTIN(name, func) \
247 else if (0 == strcmp ((const char *) c->word, name))\
248 do { new_str = func (pat); ret = FcTrue; } while (0)
249 BUILTIN ("unparse", FcNameUnparse);
250 /* BUILTIN ("verbose", FcPatternPrint); */
259 FcStrBufString (buf, new_str);
267 /* now try our custom formats */
269 #define BUILTIN(name, format) \
270 else if (0 == strcmp ((const char *) c->word, name))\
271 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
272 BUILTIN ("fcmatch", FCMATCH_FORMAT);
273 BUILTIN ("fclist", FCLIST_FORMAT);
279 message ("unknown builtin \"%s\"",
286 interpret_expr (FcFormatContext *c,
292 interpret_subexpr (FcFormatContext *c,
296 return expect_char (c, '{') &&
297 interpret_expr (c, pat, buf, '}') &&
298 expect_char (c, '}');
302 maybe_interpret_subexpr (FcFormatContext *c,
306 return (*c->format == '{') ?
307 interpret_subexpr (c, pat, buf) :
312 skip_subexpr (FcFormatContext *c);
315 skip_percent (FcFormatContext *c)
319 if (!expect_char (c, '%'))
322 /* skip an optional width specifier */
323 width = strtol ((const char *) c->format, (char **) &c->format, 10);
325 if (!expect_char (c, '{'))
328 while(*c->format && *c->format != '}')
333 c->format++; /* skip over '\\' */
338 if (!skip_subexpr (c))
345 return expect_char (c, '}');
349 skip_expr (FcFormatContext *c)
351 while(*c->format && *c->format != '}')
356 c->format++; /* skip over '\\' */
361 if (!skip_percent (c))
372 skip_subexpr (FcFormatContext *c)
374 return expect_char (c, '{') &&
376 expect_char (c, '}');
380 maybe_skip_subexpr (FcFormatContext *c)
382 return (*c->format == '{') ?
388 interpret_filter (FcFormatContext *c,
395 if (!expect_char (c, '+'))
398 os = FcObjectSetCreate ();
404 if (!read_word (c) ||
405 !FcObjectSetAdd (os, (const char *) c->word))
407 FcObjectSetDestroy (os);
411 while (consume_char (c, ','));
413 subpat = FcPatternFilter (pat, os);
414 FcObjectSetDestroy (os);
417 !interpret_subexpr (c, subpat, buf))
420 FcPatternDestroy (subpat);
425 interpret_delete (FcFormatContext *c,
431 if (!expect_char (c, '-'))
434 subpat = FcPatternDuplicate (pat);
442 FcPatternDestroy (subpat);
446 FcPatternDel (subpat, (const char *) c->word);
448 while (consume_char (c, ','));
450 if (!interpret_subexpr (c, subpat, buf))
453 FcPatternDestroy (subpat);
458 interpret_cond (FcFormatContext *c,
464 if (!expect_char (c, '?'))
474 negate = consume_char (c, '!');
481 (FcResultMatch == FcPatternGet (pat,
482 (const char *) c->word,
485 while (consume_char (c, ','));
489 if (!interpret_subexpr (c, pat, buf) ||
490 !maybe_skip_subexpr (c))
495 if (!skip_subexpr (c) ||
496 !maybe_interpret_subexpr (c, pat, buf))
504 interpret_count (FcFormatContext *c,
510 FcChar8 buf_static[64];
512 if (!expect_char (c, '#'))
519 e = FcPatternObjectFindElt (pat,
520 FcObjectFromName ((const char *) c->word));
525 for (l = FcPatternEltValues(e);
531 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
532 FcStrBufString (buf, buf_static);
538 interpret_simple (FcFormatContext *c,
543 FcBool add_colon = FcFalse;
544 FcBool add_elt_name = FcFalse;
547 if (consume_char (c, ':'))
554 if (consume_char (c, '['))
556 idx = strtol ((const char *) c->format, (char **) &c->format, 10);
559 message ("expected non-negative number at %d",
560 c->format-1 - c->format_orig + 1);
563 expect_char (c, ']');
566 if (consume_char (c, '='))
567 add_elt_name = FcTrue;
569 e = FcPatternObjectFindElt (pat,
570 FcObjectFromName ((const char *) c->word));
576 FcStrBufChar (buf, ':');
579 FcStrBufString (buf, c->word);
580 FcStrBufChar (buf, '=');
583 l = FcPatternEltValues(e);
589 l = FcValueListNext(l);
594 if (!FcNameUnparseValue (buf, &l->value, '\0'))
601 FcNameUnparseValueList (buf, l, '\0');
613 cescape (FcFormatContext *c,
623 FcStrBufChar (buf, '\\');
626 FcStrBufChar (buf, *str++);
632 shescape (FcFormatContext *c,
636 FcStrBufChar (buf, '\'');
640 FcStrBufString (buf, (const FcChar8 *) "'\\''");
642 FcStrBufChar (buf, *str);
645 FcStrBufChar (buf, '\'');
650 xmlescape (FcFormatContext *c,
658 case '&': FcStrBufString (buf, (const FcChar8 *) "&"); break;
659 case '<': FcStrBufString (buf, (const FcChar8 *) "<"); break;
660 case '>': FcStrBufString (buf, (const FcChar8 *) ">"); break;
661 default: FcStrBufChar (buf, *str); break;
669 delete_chars (FcFormatContext *c,
673 /* XXX not UTF-8 aware */
675 if (!expect_char (c, '(') ||
676 !read_chars (c, ')') ||
677 !expect_char (c, ')'))
684 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
687 FcStrBufData (buf, str, p - str);
692 FcStrBufString (buf, str);
702 escape_chars (FcFormatContext *c,
706 /* XXX not UTF-8 aware */
708 if (!expect_char (c, '(') ||
709 !read_chars (c, ')') ||
710 !expect_char (c, ')'))
717 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
720 FcStrBufData (buf, str, p - str);
721 FcStrBufChar (buf, c->word[0]);
722 FcStrBufChar (buf, *p);
727 FcStrBufString (buf, str);
737 translate_chars (FcFormatContext *c,
741 char *from, *to, repeat;
742 int from_len, to_len;
744 /* XXX not UTF-8 aware */
746 if (!expect_char (c, '(') ||
747 !read_chars (c, ',') ||
748 !expect_char (c, ','))
751 from = (char *) c->word;
752 from_len = strlen (from);
753 to = from + from_len + 1;
755 /* hack: we temporarily diverge c->word */
756 c->word = (FcChar8 *) to;
757 if (!read_chars (c, ')'))
759 c->word = (FcChar8 *) from;
762 c->word = (FcChar8 *) from;
764 to_len = strlen (to);
765 repeat = to[to_len - 1];
767 if (!expect_char (c, ')'))
774 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
778 FcStrBufData (buf, str, p - str);
779 i = strchr (from, *p) - from;
780 FcStrBufChar (buf, i < to_len ? to[i] : repeat);
785 FcStrBufString (buf, str);
795 interpret_convert (FcFormatContext *c,
802 FcChar8 buf_static[8192];
805 if (!expect_char (c, '|') ||
809 /* prepare the buffer */
810 FcStrBufChar (buf, '\0');
813 str = buf->buf + start;
816 /* try simple converters first */
818 #define CONVERTER(name, func) \
819 else if (0 == strcmp ((const char *) c->word, name))\
820 do { new_str = func (str); ret = FcTrue; } while (0)
821 CONVERTER ("downcase", FcStrDowncase);
822 CONVERTER ("basename", FcStrBasename);
823 CONVERTER ("dirname", FcStrDirname);
832 FcStrBufString (buf, new_str);
840 FcStrBufInit (&new_buf, buf_static, sizeof (buf_static));
842 /* now try our custom converters */
844 #define CONVERTER(name, func) \
845 else if (0 == strcmp ((const char *) c->word, name))\
846 ret = func (c, str, &new_buf)
847 CONVERTER ("cescape", cescape);
848 CONVERTER ("shescape", shescape);
849 CONVERTER ("xmlescape", xmlescape);
850 CONVERTER ("delete", delete_chars);
851 CONVERTER ("escape", escape_chars);
852 CONVERTER ("translate", translate_chars);
859 FcStrBufChar (&new_buf, '\0');
860 FcStrBufString (buf, new_buf.buf);
863 message ("unknown converter \"%s\"",
866 FcStrBufDestroy (&new_buf);
872 maybe_interpret_converts (FcFormatContext *c,
876 while (*c->format == '|')
877 if (!interpret_convert (c, buf, start))
884 align_to_width (FcStrBuf *buf,
893 len = buf->len - start;
897 while (len++ < -width)
898 FcStrBufChar (buf, ' ');
900 else if (len < width)
905 while (len++ < width)
906 FcStrBufChar (buf, ' ');
910 memmove (buf->buf + buf->len - len,
911 buf->buf + buf->len - width,
913 memset (buf->buf + buf->len - width,
921 interpret_percent (FcFormatContext *c,
928 if (!expect_char (c, '%'))
931 if (consume_char (c, '%')) /* "%%" */
933 FcStrBufChar (buf, '%');
937 /* parse an optional width specifier */
938 width = strtol ((const char *) c->format, (char **) &c->format, 10);
940 if (!expect_char (c, '{'))
945 switch (*c->format) {
946 case '=': ret = interpret_builtin (c, pat, buf); break;
947 case '{': ret = interpret_subexpr (c, pat, buf); break;
948 case '+': ret = interpret_filter (c, pat, buf); break;
949 case '-': ret = interpret_delete (c, pat, buf); break;
950 case '?': ret = interpret_cond (c, pat, buf); break;
951 case '#': ret = interpret_count (c, pat, buf); break;
952 default: ret = interpret_simple (c, pat, buf); break;
956 maybe_interpret_converts (c, buf, start) &&
957 align_to_width (buf, start, width) &&
958 expect_char (c, '}');
962 interpret_expr (FcFormatContext *c,
967 while (*c->format && *c->format != term)
972 c->format++; /* skip over '\\' */
974 FcStrBufChar (buf, escaped_char (*c->format++));
977 if (!interpret_percent (c, pat, buf))
981 FcStrBufChar (buf, *c->format++);
987 FcPatternFormatToBuf (FcPattern *pat,
988 const FcChar8 *format,
992 FcChar8 word_static[1024];
995 if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static)))
998 ret = interpret_expr (&c, pat, buf, '\0');
1000 FcFormatContextDone (&c);
1006 FcPatternFormat (FcPattern *pat,
1007 const FcChar8 *format)
1010 FcChar8 buf_static[8192 - 1024];
1013 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
1015 ret = FcPatternFormatToBuf (pat, format, &buf);
1018 return FcStrBufDone (&buf);
1021 FcStrBufDestroy (&buf);
1026 #define __fcformat__
1027 #include "fcaliastail.h"