2 * Copyright © 2008 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 * - number of values for element using '%{#elt}'
35 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
36 * - allow indexing simple tags using '%{elt[idx]}'
37 * - conditional/filtering/deletion on binding (using '(w)'/'(s)' notation)
41 message (const char *fmt, ...)
45 fprintf (stderr, "Fontconfig: Pattern format error: ");
46 vfprintf (stderr, fmt, args);
47 fprintf (stderr, ".\n");
52 typedef struct _FcFormatContext
54 const FcChar8 *format_orig;
55 const FcChar8 *format;
61 FcFormatContextInit (FcFormatContext *c,
62 const FcChar8 *format)
64 c->format_orig = c->format = format;
65 c->format_len = strlen ((const char *) format);
66 c->word = malloc (c->format_len + 1);
68 return c->word != NULL;
72 FcFormatContextDone (FcFormatContext *c)
81 consume_char (FcFormatContext *c,
84 if (*c->format != term)
92 expect_char (FcFormatContext *c,
95 FcBool res = consume_char (c, term);
98 if (c->format == c->format_orig + c->format_len)
99 message ("format ended while expecting '%c'",
102 message ("expected '%c' at %d",
103 term, c->format - c->format_orig + 1);
109 FcCharIsPunct (const FcChar8 c)
129 read_word (FcFormatContext *c)
137 if (*c->format == '\\')
144 else if (FcCharIsPunct (*c->format))
153 message ("expected element name at %d",
154 c->format - c->format_orig + 1);
162 interpret_expr (FcFormatContext *c,
168 interpret_subexpr (FcFormatContext *c,
172 return expect_char (c, '{') &&
173 interpret_expr (c, pat, buf, '}') &&
174 expect_char (c, '}');
178 maybe_interpret_subexpr (FcFormatContext *c,
182 return (*c->format == '{') ?
183 interpret_subexpr (c, pat, buf) :
188 skip_subexpr (FcFormatContext *c);
191 skip_percent (FcFormatContext *c)
195 if (!expect_char (c, '%'))
198 /* skip an optional width specifier */
199 width = strtol ((const char *) c->format, (char **) &c->format, 10);
201 if (!expect_char (c, '{'))
204 while(*c->format && *c->format != '}')
209 c->format++; /* skip over '\\' */
214 if (!skip_subexpr (c))
221 return expect_char (c, '}');
225 skip_expr (FcFormatContext *c)
227 while(*c->format && *c->format != '}')
232 c->format++; /* skip over '\\' */
237 if (!skip_percent (c))
248 skip_subexpr (FcFormatContext *c)
250 return expect_char (c, '{') &&
252 expect_char (c, '}');
256 maybe_skip_subexpr (FcFormatContext *c)
258 return (*c->format == '{') ?
264 interpret_simple (FcFormatContext *c,
269 FcBool add_colon = FcFalse;
270 FcBool add_elt_name = FcFalse;
272 if (consume_char (c, ':'))
278 if (consume_char (c, '='))
279 add_elt_name = FcTrue;
281 e = FcPatternObjectFindElt (pat,
282 FcObjectFromName ((const char *) c->word));
288 FcStrBufChar (buf, ':');
291 FcStrBufString (buf, c->word);
292 FcStrBufChar (buf, '=');
295 l = FcPatternEltValues(e);
296 FcNameUnparseValueList (buf, l, '\0');
303 interpret_filter (FcFormatContext *c,
310 if (!expect_char (c, '+'))
313 os = FcObjectSetCreate ();
319 if (!read_word (c) ||
320 !FcObjectSetAdd (os, (const char *) c->word))
322 FcObjectSetDestroy (os);
326 while (consume_char (c, ','));
328 subpat = FcPatternFilter (pat, os);
329 FcObjectSetDestroy (os);
332 !interpret_subexpr (c, subpat, buf))
335 FcPatternDestroy (subpat);
340 interpret_delete (FcFormatContext *c,
346 if (!expect_char (c, '-'))
349 subpat = FcPatternDuplicate (pat);
357 FcPatternDestroy (subpat);
361 FcPatternDel (subpat, (const char *) c->word);
363 while (consume_char (c, ','));
365 if (!interpret_subexpr (c, subpat, buf))
368 FcPatternDestroy (subpat);
373 interpret_cond (FcFormatContext *c,
379 if (!expect_char (c, '?'))
389 negate = consume_char (c, '!');
396 (FcResultMatch == FcPatternGet (pat,
397 (const char *) c->word,
400 while (consume_char (c, ','));
404 if (!interpret_subexpr (c, pat, buf) ||
405 !maybe_skip_subexpr (c))
410 if (!skip_subexpr (c) ||
411 !maybe_interpret_subexpr (c, pat, buf))
419 cescape (const FcChar8 *str)
422 FcChar8 buf_static[8192];
424 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
431 FcStrBufChar (&buf, '\\');
434 FcStrBufChar (&buf, *str++);
436 return FcStrBufDone (&buf);
440 shescape (const FcChar8 *str)
443 FcChar8 buf_static[8192];
445 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
446 FcStrBufChar (&buf, '\'');
450 FcStrBufString (&buf, (const FcChar8 *) "'\\''");
452 FcStrBufChar (&buf, *str);
455 FcStrBufChar (&buf, '\'');
456 return FcStrBufDone (&buf);
460 xmlescape (const FcChar8 *str)
463 FcChar8 buf_static[8192];
465 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
470 case '&': FcStrBufString (&buf, (const FcChar8 *) "&"); break;
471 case '<': FcStrBufString (&buf, (const FcChar8 *) "<"); break;
472 case '>': FcStrBufString (&buf, (const FcChar8 *) ">"); break;
473 default: FcStrBufChar (&buf, *str); break;
477 return FcStrBufDone (&buf);
481 convert (FcFormatContext *c,
486 #define CONVERTER(name, func) \
487 else if (0 == strcmp ((const char *) c->word, name))\
489 CONVERTER ("downcase", FcStrDowncase);
490 CONVERTER ("basename", FcStrBasename);
491 CONVERTER ("dirname", FcStrDirname);
492 CONVERTER ("cescape", cescape);
493 CONVERTER ("shescape", shescape);
494 CONVERTER ("xmlescape", xmlescape);
496 message ("unknown converter \"%s\"",
502 maybe_interpret_converts (FcFormatContext *c,
506 while (consume_char (c, '|'))
511 /* nul-terminate the buffer */
512 FcStrBufChar (buf, '\0');
515 str = buf->buf + start;
517 if (!(new_str = convert (c, str)))
520 /* replace in the buffer */
522 FcStrBufString (buf, new_str);
530 align_to_width (FcStrBuf *buf,
539 len = buf->len - start;
543 while (len++ < -width)
544 FcStrBufChar (buf, ' ');
546 else if (len < width)
551 while (len++ < width)
552 FcStrBufChar (buf, ' ');
556 memmove (buf->buf + buf->len - len,
557 buf->buf + buf->len - width,
559 memset (buf->buf + buf->len - width,
567 interpret_percent (FcFormatContext *c,
574 if (!expect_char (c, '%'))
577 if (consume_char (c, '%')) /* "%%" */
579 FcStrBufChar (buf, '%');
583 /* parse an optional width specifier */
584 width = strtol ((const char *) c->format, (char **) &c->format, 10);
586 if (!expect_char (c, '{'))
591 switch (*c->format) {
592 case '{': ret = interpret_subexpr (c, pat, buf); break;
593 case '+': ret = interpret_filter (c, pat, buf); break;
594 case '-': ret = interpret_delete (c, pat, buf); break;
595 case '?': ret = interpret_cond (c, pat, buf); break;
596 default: ret = interpret_simple (c, pat, buf); break;
600 maybe_interpret_converts (c, buf, start) &&
601 align_to_width (buf, start, width) &&
602 expect_char (c, '}');
605 static char escaped_char(const char ch)
608 case 'a': return '\a';
609 case 'b': return '\b';
610 case 'f': return '\f';
611 case 'n': return '\n';
612 case 'r': return '\r';
613 case 't': return '\t';
614 case 'v': return '\v';
620 interpret_expr (FcFormatContext *c,
625 while (*c->format && *c->format != term)
630 c->format++; /* skip over '\\' */
632 FcStrBufChar (buf, escaped_char (*c->format++));
635 if (!interpret_percent (c, pat, buf))
639 FcStrBufChar (buf, *c->format++);
645 FcPatternFormat (FcPattern *pat, const FcChar8 *format)
648 FcChar8 buf_static[8192];
652 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
653 if (!FcFormatContextInit (&c, format))
656 ret = interpret_expr (&c, pat, &buf, '\0');
658 FcFormatContextDone (&c);
660 return FcStrBufDone (&buf);
663 FcStrBufDestroy (&buf);
669 #include "fcaliastail.h"