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 subexprs using '%{[idx]elt1,elt2{subexpr}}'
37 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
41 #define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
42 #define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
46 message (const char *fmt, ...)
50 fprintf (stderr, "Fontconfig: Pattern format error: ");
51 vfprintf (stderr, fmt, args);
52 fprintf (stderr, ".\n");
57 typedef struct _FcFormatContext
59 const FcChar8 *format_orig;
60 const FcChar8 *format;
63 FcBool word_allocated;
67 FcFormatContextInit (FcFormatContext *c,
68 const FcChar8 *format,
72 c->format_orig = c->format = format;
73 c->format_len = strlen ((const char *) format);
75 if (c->format_len < scratch_len)
78 c->word_allocated = FcFalse;
82 c->word = malloc (c->format_len + 1);
83 c->word_allocated = FcTrue;
86 return c->word != NULL;
90 FcFormatContextDone (FcFormatContext *c)
92 if (c && c->word_allocated)
99 consume_char (FcFormatContext *c,
102 if (*c->format != term)
110 expect_char (FcFormatContext *c,
113 FcBool res = consume_char (c, term);
116 if (c->format == c->format_orig + c->format_len)
117 message ("format ended while expecting '%c'",
120 message ("expected '%c' at %d",
121 term, c->format - c->format_orig + 1);
127 FcCharIsPunct (const FcChar8 c)
146 static char escaped_char(const char ch)
149 case 'a': return '\a';
150 case 'b': return '\b';
151 case 'f': return '\f';
152 case 'n': return '\n';
153 case 'r': return '\r';
154 case 't': return '\t';
155 case 'v': return '\v';
161 read_word (FcFormatContext *c)
169 if (*c->format == '\\')
173 *p++ = escaped_char (*c->format++);
176 else if (FcCharIsPunct (*c->format))
185 message ("expected identifier at %d",
186 c->format - c->format_orig + 1);
194 read_chars (FcFormatContext *c,
201 while (*c->format && *c->format != '}' && *c->format != term)
203 if (*c->format == '\\')
207 *p++ = escaped_char (*c->format++);
217 message ("expected character data at %d",
218 c->format - c->format_orig + 1);
226 FcPatternFormatToBuf (FcPattern *pat,
227 const FcChar8 *format,
231 interpret_builtin (FcFormatContext *c,
238 if (!expect_char (c, '=') ||
242 /* try simple builtins first */
244 #define BUILTIN(name, func) \
245 else if (0 == strcmp ((const char *) c->word, name))\
246 do { new_str = func (pat); ret = FcTrue; } while (0)
247 BUILTIN ("unparse", FcNameUnparse);
248 /* BUILTIN ("verbose", FcPatternPrint); XXX */
257 FcStrBufString (buf, new_str);
265 /* now try our custom formats */
267 #define BUILTIN(name, format) \
268 else if (0 == strcmp ((const char *) c->word, name))\
269 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
270 BUILTIN ("fcmatch", FCMATCH_FORMAT);
271 BUILTIN ("fclist", FCLIST_FORMAT);
277 message ("unknown builtin \"%s\"",
284 interpret_expr (FcFormatContext *c,
290 interpret_subexpr (FcFormatContext *c,
294 return expect_char (c, '{') &&
295 interpret_expr (c, pat, buf, '}') &&
296 expect_char (c, '}');
300 maybe_interpret_subexpr (FcFormatContext *c,
304 return (*c->format == '{') ?
305 interpret_subexpr (c, pat, buf) :
310 skip_subexpr (FcFormatContext *c);
313 skip_percent (FcFormatContext *c)
317 if (!expect_char (c, '%'))
320 /* skip an optional width specifier */
321 width = strtol ((const char *) c->format, (char **) &c->format, 10);
323 if (!expect_char (c, '{'))
326 while(*c->format && *c->format != '}')
331 c->format++; /* skip over '\\' */
336 if (!skip_subexpr (c))
343 return expect_char (c, '}');
347 skip_expr (FcFormatContext *c)
349 while(*c->format && *c->format != '}')
354 c->format++; /* skip over '\\' */
359 if (!skip_percent (c))
370 skip_subexpr (FcFormatContext *c)
372 return expect_char (c, '{') &&
374 expect_char (c, '}');
378 maybe_skip_subexpr (FcFormatContext *c)
380 return (*c->format == '{') ?
386 interpret_filter (FcFormatContext *c,
393 if (!expect_char (c, '+'))
396 os = FcObjectSetCreate ();
402 if (!read_word (c) ||
403 !FcObjectSetAdd (os, (const char *) c->word))
405 FcObjectSetDestroy (os);
409 while (consume_char (c, ','));
411 subpat = FcPatternFilter (pat, os);
412 FcObjectSetDestroy (os);
415 !interpret_subexpr (c, subpat, buf))
418 FcPatternDestroy (subpat);
423 interpret_delete (FcFormatContext *c,
429 if (!expect_char (c, '-'))
432 subpat = FcPatternDuplicate (pat);
440 FcPatternDestroy (subpat);
444 FcPatternDel (subpat, (const char *) c->word);
446 while (consume_char (c, ','));
448 if (!interpret_subexpr (c, subpat, buf))
451 FcPatternDestroy (subpat);
456 interpret_cond (FcFormatContext *c,
462 if (!expect_char (c, '?'))
472 negate = consume_char (c, '!');
479 (FcResultMatch == FcPatternGet (pat,
480 (const char *) c->word,
483 while (consume_char (c, ','));
487 if (!interpret_subexpr (c, pat, buf) ||
488 !maybe_skip_subexpr (c))
493 if (!skip_subexpr (c) ||
494 !maybe_interpret_subexpr (c, pat, buf))
502 interpret_count (FcFormatContext *c,
508 FcChar8 buf_static[64];
510 if (!expect_char (c, '#'))
517 e = FcPatternObjectFindElt (pat,
518 FcObjectFromName ((const char *) c->word));
523 for (l = FcPatternEltValues(e);
529 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
530 FcStrBufString (buf, buf_static);
536 interpret_simple (FcFormatContext *c,
541 FcBool add_colon = FcFalse;
542 FcBool add_elt_name = FcFalse;
544 FcChar8 *else_string;
546 if (consume_char (c, ':'))
553 if (consume_char (c, '['))
555 idx = strtol ((const char *) c->format, (char **) &c->format, 10);
558 message ("expected non-negative number at %d",
559 c->format-1 - c->format_orig + 1);
562 expect_char (c, ']');
565 if (consume_char (c, '='))
566 add_elt_name = FcTrue;
570 if (consume_char (c, ':'))
573 /* divert the c->word for now */
575 c->word = c->word + strlen ((const char *) c->word) + 1;
576 /* for now we just support 'default value' */
577 if (!expect_char (c, '-') ||
578 !read_chars (c, '\0'))
583 else_string = c->word;
587 e = FcPatternObjectFindElt (pat,
588 FcObjectFromName ((const char *) c->word));
594 FcStrBufChar (buf, ':');
597 FcStrBufString (buf, c->word);
598 FcStrBufChar (buf, '=');
601 l = FcPatternEltValues(e);
607 l = FcValueListNext(l);
612 if (!FcNameUnparseValue (buf, &l->value, '\0'))
619 FcNameUnparseValueList (buf, l, '\0');
626 printf ("%s", else_string);
633 cescape (FcFormatContext *c,
643 FcStrBufChar (buf, '\\');
646 FcStrBufChar (buf, *str++);
652 shescape (FcFormatContext *c,
656 FcStrBufChar (buf, '\'');
660 FcStrBufString (buf, (const FcChar8 *) "'\\''");
662 FcStrBufChar (buf, *str);
665 FcStrBufChar (buf, '\'');
670 xmlescape (FcFormatContext *c,
678 case '&': FcStrBufString (buf, (const FcChar8 *) "&"); break;
679 case '<': FcStrBufString (buf, (const FcChar8 *) "<"); break;
680 case '>': FcStrBufString (buf, (const FcChar8 *) ">"); break;
681 default: FcStrBufChar (buf, *str); break;
689 delete_chars (FcFormatContext *c,
693 /* XXX not UTF-8 aware */
695 if (!expect_char (c, '(') ||
696 !read_chars (c, ')') ||
697 !expect_char (c, ')'))
704 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
707 FcStrBufData (buf, str, p - str);
712 FcStrBufString (buf, str);
722 escape_chars (FcFormatContext *c,
726 /* XXX not UTF-8 aware */
728 if (!expect_char (c, '(') ||
729 !read_chars (c, ')') ||
730 !expect_char (c, ')'))
737 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
740 FcStrBufData (buf, str, p - str);
741 FcStrBufChar (buf, c->word[0]);
742 FcStrBufChar (buf, *p);
747 FcStrBufString (buf, str);
757 translate_chars (FcFormatContext *c,
761 char *from, *to, repeat;
762 int from_len, to_len;
764 /* XXX not UTF-8 aware */
766 if (!expect_char (c, '(') ||
767 !read_chars (c, ',') ||
768 !expect_char (c, ','))
771 from = (char *) c->word;
772 from_len = strlen (from);
773 to = from + from_len + 1;
775 /* hack: we temporarily divert c->word */
776 c->word = (FcChar8 *) to;
777 if (!read_chars (c, ')'))
779 c->word = (FcChar8 *) from;
782 c->word = (FcChar8 *) from;
784 to_len = strlen (to);
785 repeat = to[to_len - 1];
787 if (!expect_char (c, ')'))
794 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
798 FcStrBufData (buf, str, p - str);
799 i = strchr (from, *p) - from;
800 FcStrBufChar (buf, i < to_len ? to[i] : repeat);
805 FcStrBufString (buf, str);
815 interpret_convert (FcFormatContext *c,
822 FcChar8 buf_static[8192];
825 if (!expect_char (c, '|') ||
829 /* prepare the buffer */
830 FcStrBufChar (buf, '\0');
833 str = buf->buf + start;
836 /* try simple converters first */
838 #define CONVERTER(name, func) \
839 else if (0 == strcmp ((const char *) c->word, name))\
840 do { new_str = func (str); ret = FcTrue; } while (0)
841 CONVERTER ("downcase", FcStrDowncase);
842 CONVERTER ("basename", FcStrBasename);
843 CONVERTER ("dirname", FcStrDirname);
852 FcStrBufString (buf, new_str);
860 FcStrBufInit (&new_buf, buf_static, sizeof (buf_static));
862 /* now try our custom converters */
864 #define CONVERTER(name, func) \
865 else if (0 == strcmp ((const char *) c->word, name))\
866 ret = func (c, str, &new_buf)
867 CONVERTER ("cescape", cescape);
868 CONVERTER ("shescape", shescape);
869 CONVERTER ("xmlescape", xmlescape);
870 CONVERTER ("delete", delete_chars);
871 CONVERTER ("escape", escape_chars);
872 CONVERTER ("translate", translate_chars);
879 FcStrBufChar (&new_buf, '\0');
880 FcStrBufString (buf, new_buf.buf);
883 message ("unknown converter \"%s\"",
886 FcStrBufDestroy (&new_buf);
892 maybe_interpret_converts (FcFormatContext *c,
896 while (*c->format == '|')
897 if (!interpret_convert (c, buf, start))
904 align_to_width (FcStrBuf *buf,
913 len = buf->len - start;
917 while (len++ < -width)
918 FcStrBufChar (buf, ' ');
920 else if (len < width)
925 while (len++ < width)
926 FcStrBufChar (buf, ' ');
930 memmove (buf->buf + buf->len - len,
931 buf->buf + buf->len - width,
933 memset (buf->buf + buf->len - width,
941 interpret_percent (FcFormatContext *c,
948 if (!expect_char (c, '%'))
951 if (consume_char (c, '%')) /* "%%" */
953 FcStrBufChar (buf, '%');
957 /* parse an optional width specifier */
958 width = strtol ((const char *) c->format, (char **) &c->format, 10);
960 if (!expect_char (c, '{'))
965 switch (*c->format) {
966 case '=': ret = interpret_builtin (c, pat, buf); break;
967 case '{': ret = interpret_subexpr (c, pat, buf); break;
968 case '+': ret = interpret_filter (c, pat, buf); break;
969 case '-': ret = interpret_delete (c, pat, buf); break;
970 case '?': ret = interpret_cond (c, pat, buf); break;
971 case '#': ret = interpret_count (c, pat, buf); break;
972 default: ret = interpret_simple (c, pat, buf); break;
976 maybe_interpret_converts (c, buf, start) &&
977 align_to_width (buf, start, width) &&
978 expect_char (c, '}');
982 interpret_expr (FcFormatContext *c,
987 while (*c->format && *c->format != term)
992 c->format++; /* skip over '\\' */
994 FcStrBufChar (buf, escaped_char (*c->format++));
997 if (!interpret_percent (c, pat, buf))
1001 FcStrBufChar (buf, *c->format++);
1007 FcPatternFormatToBuf (FcPattern *pat,
1008 const FcChar8 *format,
1012 FcChar8 word_static[1024];
1015 if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static)))
1018 ret = interpret_expr (&c, pat, buf, '\0');
1020 FcFormatContextDone (&c);
1026 FcPatternFormat (FcPattern *pat,
1027 const FcChar8 *format)
1030 FcChar8 buf_static[8192 - 1024];
1033 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
1035 ret = FcPatternFormatToBuf (pat, format, &buf);
1038 return FcStrBufDone (&buf);
1041 FcStrBufDestroy (&buf);
1046 #define __fcformat__
1047 #include "fcaliastail.h"