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 * - 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)
40 message (const char *fmt, ...)
44 fprintf (stderr, "Fontconfig: Pattern format error: ");
45 vfprintf (stderr, fmt, args);
46 fprintf (stderr, ".\n");
51 typedef struct _FcFormatContext
53 const FcChar8 *format_orig;
54 const FcChar8 *format;
60 FcFormatContextInit (FcFormatContext *c,
61 const FcChar8 *format)
63 c->format_orig = c->format = format;
64 c->format_len = strlen ((const char *) format);
65 c->word = malloc (c->format_len + 1);
67 return c->word != NULL;
71 FcFormatContextDone (FcFormatContext *c)
80 consume_char (FcFormatContext *c,
83 if (*c->format != term)
91 expect_char (FcFormatContext *c,
94 FcBool res = consume_char (c, term);
97 if (c->format == c->format_orig + c->format_len)
98 message ("format ended while expecting '%c'",
101 message ("expected '%c' at %d",
102 term, c->format - c->format_orig + 1);
108 FcCharIsPunct (const FcChar8 c)
127 static char escaped_char(const char ch)
130 case 'a': return '\a';
131 case 'b': return '\b';
132 case 'f': return '\f';
133 case 'n': return '\n';
134 case 'r': return '\r';
135 case 't': return '\t';
136 case 'v': return '\v';
142 read_word (FcFormatContext *c)
150 if (*c->format == '\\')
154 *p++ = escaped_char (*c->format++);
157 else if (FcCharIsPunct (*c->format))
166 message ("expected element name at %d",
167 c->format - c->format_orig + 1);
175 read_chars (FcFormatContext *c,
182 while (*c->format && *c->format != '}' && *c->format != term)
184 if (*c->format == '\\')
188 *p++ = escaped_char (*c->format++);
198 message ("expected character data at %d",
199 c->format - c->format_orig + 1);
207 interpret_builtin (FcFormatContext *c,
211 if (!expect_char (c, '='))
216 #define BUILTIN(name, func) \
217 else if (0 == strcmp ((const char *) c->word, name))\
218 return func (c, pat, buf)
219 BUILTIN ("unparse", FcNameUnparse);
220 BUILTIN ("verbose", FcPatternPrint);
221 BUILTIN2 ("fcmatch", FcStrDirname);
222 BUILTIN2 ("fclist", FcStrDirname);
223 BUILTIN2 ("pkgkit", FcStrDirname);
225 message ("unknown builtin \"%s\"",
231 interpret_expr (FcFormatContext *c,
237 interpret_subexpr (FcFormatContext *c,
241 return expect_char (c, '{') &&
242 interpret_expr (c, pat, buf, '}') &&
243 expect_char (c, '}');
247 maybe_interpret_subexpr (FcFormatContext *c,
251 return (*c->format == '{') ?
252 interpret_subexpr (c, pat, buf) :
257 skip_subexpr (FcFormatContext *c);
260 skip_percent (FcFormatContext *c)
264 if (!expect_char (c, '%'))
267 /* skip an optional width specifier */
268 width = strtol ((const char *) c->format, (char **) &c->format, 10);
270 if (!expect_char (c, '{'))
273 while(*c->format && *c->format != '}')
278 c->format++; /* skip over '\\' */
283 if (!skip_subexpr (c))
290 return expect_char (c, '}');
294 skip_expr (FcFormatContext *c)
296 while(*c->format && *c->format != '}')
301 c->format++; /* skip over '\\' */
306 if (!skip_percent (c))
317 skip_subexpr (FcFormatContext *c)
319 return expect_char (c, '{') &&
321 expect_char (c, '}');
325 maybe_skip_subexpr (FcFormatContext *c)
327 return (*c->format == '{') ?
333 interpret_filter (FcFormatContext *c,
340 if (!expect_char (c, '+'))
343 os = FcObjectSetCreate ();
349 if (!read_word (c) ||
350 !FcObjectSetAdd (os, (const char *) c->word))
352 FcObjectSetDestroy (os);
356 while (consume_char (c, ','));
358 subpat = FcPatternFilter (pat, os);
359 FcObjectSetDestroy (os);
362 !interpret_subexpr (c, subpat, buf))
365 FcPatternDestroy (subpat);
370 interpret_delete (FcFormatContext *c,
376 if (!expect_char (c, '-'))
379 subpat = FcPatternDuplicate (pat);
387 FcPatternDestroy (subpat);
391 FcPatternDel (subpat, (const char *) c->word);
393 while (consume_char (c, ','));
395 if (!interpret_subexpr (c, subpat, buf))
398 FcPatternDestroy (subpat);
403 interpret_cond (FcFormatContext *c,
409 if (!expect_char (c, '?'))
419 negate = consume_char (c, '!');
426 (FcResultMatch == FcPatternGet (pat,
427 (const char *) c->word,
430 while (consume_char (c, ','));
434 if (!interpret_subexpr (c, pat, buf) ||
435 !maybe_skip_subexpr (c))
440 if (!skip_subexpr (c) ||
441 !maybe_interpret_subexpr (c, pat, buf))
449 interpret_count (FcFormatContext *c,
455 FcChar8 buf_static[64];
457 if (!expect_char (c, '#'))
464 e = FcPatternObjectFindElt (pat,
465 FcObjectFromName ((const char *) c->word));
470 for (l = FcPatternEltValues(e);
476 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
477 FcStrBufString (buf, buf_static);
483 interpret_simple (FcFormatContext *c,
488 FcBool add_colon = FcFalse;
489 FcBool add_elt_name = FcFalse;
491 if (consume_char (c, ':'))
497 if (consume_char (c, '='))
498 add_elt_name = FcTrue;
500 e = FcPatternObjectFindElt (pat,
501 FcObjectFromName ((const char *) c->word));
507 FcStrBufChar (buf, ':');
510 FcStrBufString (buf, c->word);
511 FcStrBufChar (buf, '=');
514 l = FcPatternEltValues(e);
515 FcNameUnparseValueList (buf, l, '\0');
522 cescape (const FcChar8 *str)
525 FcChar8 buf_static[8192];
527 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
534 FcStrBufChar (&buf, '\\');
537 FcStrBufChar (&buf, *str++);
539 return FcStrBufDone (&buf);
543 shescape (const FcChar8 *str)
546 FcChar8 buf_static[8192];
548 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
549 FcStrBufChar (&buf, '\'');
553 FcStrBufString (&buf, (const FcChar8 *) "'\\''");
555 FcStrBufChar (&buf, *str);
558 FcStrBufChar (&buf, '\'');
559 return FcStrBufDone (&buf);
563 xmlescape (const FcChar8 *str)
566 FcChar8 buf_static[8192];
568 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
573 case '&': FcStrBufString (&buf, (const FcChar8 *) "&"); break;
574 case '<': FcStrBufString (&buf, (const FcChar8 *) "<"); break;
575 case '>': FcStrBufString (&buf, (const FcChar8 *) ">"); break;
576 default: FcStrBufChar (&buf, *str); break;
580 return FcStrBufDone (&buf);
584 delete_chars (FcFormatContext *c,
588 FcChar8 buf_static[8192];
590 /* XXX not UTF-8 aware */
592 if (!expect_char (c, '(') ||
593 !read_chars (c, ')') ||
594 !expect_char (c, ')'))
597 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
602 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
605 FcStrBufData (&buf, str, p - str);
610 FcStrBufString (&buf, str);
615 return FcStrBufDone (&buf);
619 escape_chars (FcFormatContext *c,
623 FcChar8 buf_static[8192];
625 /* XXX not UTF-8 aware */
627 if (!expect_char (c, '(') ||
628 !read_chars (c, ')') ||
629 !expect_char (c, ')'))
632 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
637 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
640 FcStrBufData (&buf, str, p - str);
641 FcStrBufChar (&buf, c->word[0]);
642 FcStrBufChar (&buf, *p);
647 FcStrBufString (&buf, str);
652 return FcStrBufDone (&buf);
656 translate_chars (FcFormatContext *c,
660 FcChar8 buf_static[8192];
661 char *from, *to, repeat;
662 int from_len, to_len;
664 /* XXX not UTF-8 aware */
666 if (!expect_char (c, '(') ||
667 !read_chars (c, ',') ||
668 !expect_char (c, ','))
671 from = (char *) c->word;
672 from_len = strlen (from);
673 to = from + from_len + 1;
675 /* hack: we temporarily diverge c->word */
676 c->word = (FcChar8 *) to;
677 if (!read_chars (c, ')'))
679 c->word = (FcChar8 *) from;
682 c->word = (FcChar8 *) from;
684 to_len = strlen (to);
685 repeat = to[to_len - 1];
687 if (!expect_char (c, ')'))
690 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
695 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
699 FcStrBufData (&buf, str, p - str);
700 i = strchr (from, *p) - from;
701 FcStrBufChar (&buf, i < to_len ? to[i] : repeat);
706 FcStrBufString (&buf, str);
711 return FcStrBufDone (&buf);
715 convert (FcFormatContext *c,
720 #define CONVERTER(name, func) \
721 else if (0 == strcmp ((const char *) c->word, name))\
723 #define CONVERTER2(name, func) \
724 else if (0 == strcmp ((const char *) c->word, name))\
726 CONVERTER ("downcase", FcStrDowncase);
727 CONVERTER ("basename", FcStrBasename);
728 CONVERTER ("dirname", FcStrDirname);
729 CONVERTER ("cescape", cescape);
730 CONVERTER ("shescape", shescape);
731 CONVERTER ("xmlescape", xmlescape);
732 CONVERTER2 ("delete", delete_chars);
733 CONVERTER2 ("escape", escape_chars);
734 CONVERTER2 ("translate", translate_chars);
736 message ("unknown converter \"%s\"",
742 maybe_interpret_converts (FcFormatContext *c,
746 while (consume_char (c, '|'))
751 /* nul-terminate the buffer */
752 FcStrBufChar (buf, '\0');
755 str = buf->buf + start;
757 if (!(new_str = convert (c, str)))
760 /* replace in the buffer */
762 FcStrBufString (buf, new_str);
770 align_to_width (FcStrBuf *buf,
779 len = buf->len - start;
783 while (len++ < -width)
784 FcStrBufChar (buf, ' ');
786 else if (len < width)
791 while (len++ < width)
792 FcStrBufChar (buf, ' ');
796 memmove (buf->buf + buf->len - len,
797 buf->buf + buf->len - width,
799 memset (buf->buf + buf->len - width,
807 interpret_percent (FcFormatContext *c,
814 if (!expect_char (c, '%'))
817 if (consume_char (c, '%')) /* "%%" */
819 FcStrBufChar (buf, '%');
823 /* parse an optional width specifier */
824 width = strtol ((const char *) c->format, (char **) &c->format, 10);
826 if (!expect_char (c, '{'))
831 switch (*c->format) {
832 case '=': ret = interpret_builtin (c, pat, buf); break;
833 case '{': ret = interpret_subexpr (c, pat, buf); break;
834 case '+': ret = interpret_filter (c, pat, buf); break;
835 case '-': ret = interpret_delete (c, pat, buf); break;
836 case '?': ret = interpret_cond (c, pat, buf); break;
837 case '#': ret = interpret_count (c, pat, buf); break;
838 default: ret = interpret_simple (c, pat, buf); break;
842 maybe_interpret_converts (c, buf, start) &&
843 align_to_width (buf, start, width) &&
844 expect_char (c, '}');
848 interpret_expr (FcFormatContext *c,
853 while (*c->format && *c->format != term)
858 c->format++; /* skip over '\\' */
860 FcStrBufChar (buf, escaped_char (*c->format++));
863 if (!interpret_percent (c, pat, buf))
867 FcStrBufChar (buf, *c->format++);
873 FcPatternFormat (FcPattern *pat, const FcChar8 *format)
876 FcChar8 buf_static[8192];
880 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
881 if (!FcFormatContextInit (&c, format))
884 ret = interpret_expr (&c, pat, &buf, '\0');
886 FcFormatContextDone (&c);
888 return FcStrBufDone (&buf);
891 FcStrBufDestroy (&buf);
897 #include "fcaliastail.h"