]>
git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
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
;
57 FcBool word_allocated
;
61 FcFormatContextInit (FcFormatContext
*c
,
62 const FcChar8
*format
,
66 c
->format_orig
= c
->format
= format
;
67 c
->format_len
= strlen ((const char *) format
);
69 if (c
->format_len
< scratch_len
)
72 c
->word_allocated
= FcFalse
;
76 c
->word
= malloc (c
->format_len
+ 1);
77 c
->word_allocated
= FcTrue
;
80 return c
->word
!= NULL
;
84 FcFormatContextDone (FcFormatContext
*c
)
86 if (c
&& c
->word_allocated
)
93 consume_char (FcFormatContext
*c
,
96 if (*c
->format
!= term
)
104 expect_char (FcFormatContext
*c
,
107 FcBool res
= consume_char (c
, term
);
110 if (c
->format
== c
->format_orig
+ c
->format_len
)
111 message ("format ended while expecting '%c'",
114 message ("expected '%c' at %d",
115 term
, c
->format
- c
->format_orig
+ 1);
121 FcCharIsPunct (const FcChar8 c
)
140 static char escaped_char(const char ch
)
143 case 'a': return '\a';
144 case 'b': return '\b';
145 case 'f': return '\f';
146 case 'n': return '\n';
147 case 'r': return '\r';
148 case 't': return '\t';
149 case 'v': return '\v';
155 read_word (FcFormatContext
*c
)
163 if (*c
->format
== '\\')
167 *p
++ = escaped_char (*c
->format
++);
170 else if (FcCharIsPunct (*c
->format
))
179 message ("expected identifier at %d",
180 c
->format
- c
->format_orig
+ 1);
188 read_chars (FcFormatContext
*c
,
195 while (*c
->format
&& *c
->format
!= '}' && *c
->format
!= term
)
197 if (*c
->format
== '\\')
201 *p
++ = escaped_char (*c
->format
++);
211 message ("expected character data at %d",
212 c
->format
- c
->format_orig
+ 1);
220 interpret_builtin (FcFormatContext
*c
,
224 if (!expect_char (c
, '='))
229 #define BUILTIN(name, func) \
230 else if (0 == strcmp ((const char *) c->word, name))\
231 return func (c, pat, buf)
233 BUILTIN ("unparse", FcNameUnparse
);
234 BUILTIN ("verbose", FcPatternPrint
);
235 BUILTIN2 ("fcmatch", FcStrDirname
);
236 BUILTIN2 ("fclist", FcStrDirname
);
237 BUILTIN2 ("pkgkit", FcStrDirname
);
240 message ("unknown builtin \"%s\"",
246 interpret_expr (FcFormatContext
*c
,
252 interpret_subexpr (FcFormatContext
*c
,
256 return expect_char (c
, '{') &&
257 interpret_expr (c
, pat
, buf
, '}') &&
258 expect_char (c
, '}');
262 maybe_interpret_subexpr (FcFormatContext
*c
,
266 return (*c
->format
== '{') ?
267 interpret_subexpr (c
, pat
, buf
) :
272 skip_subexpr (FcFormatContext
*c
);
275 skip_percent (FcFormatContext
*c
)
279 if (!expect_char (c
, '%'))
282 /* skip an optional width specifier */
283 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
285 if (!expect_char (c
, '{'))
288 while(*c
->format
&& *c
->format
!= '}')
293 c
->format
++; /* skip over '\\' */
298 if (!skip_subexpr (c
))
305 return expect_char (c
, '}');
309 skip_expr (FcFormatContext
*c
)
311 while(*c
->format
&& *c
->format
!= '}')
316 c
->format
++; /* skip over '\\' */
321 if (!skip_percent (c
))
332 skip_subexpr (FcFormatContext
*c
)
334 return expect_char (c
, '{') &&
336 expect_char (c
, '}');
340 maybe_skip_subexpr (FcFormatContext
*c
)
342 return (*c
->format
== '{') ?
348 interpret_filter (FcFormatContext
*c
,
355 if (!expect_char (c
, '+'))
358 os
= FcObjectSetCreate ();
364 if (!read_word (c
) ||
365 !FcObjectSetAdd (os
, (const char *) c
->word
))
367 FcObjectSetDestroy (os
);
371 while (consume_char (c
, ','));
373 subpat
= FcPatternFilter (pat
, os
);
374 FcObjectSetDestroy (os
);
377 !interpret_subexpr (c
, subpat
, buf
))
380 FcPatternDestroy (subpat
);
385 interpret_delete (FcFormatContext
*c
,
391 if (!expect_char (c
, '-'))
394 subpat
= FcPatternDuplicate (pat
);
402 FcPatternDestroy (subpat
);
406 FcPatternDel (subpat
, (const char *) c
->word
);
408 while (consume_char (c
, ','));
410 if (!interpret_subexpr (c
, subpat
, buf
))
413 FcPatternDestroy (subpat
);
418 interpret_cond (FcFormatContext
*c
,
424 if (!expect_char (c
, '?'))
434 negate
= consume_char (c
, '!');
441 (FcResultMatch
== FcPatternGet (pat
,
442 (const char *) c
->word
,
445 while (consume_char (c
, ','));
449 if (!interpret_subexpr (c
, pat
, buf
) ||
450 !maybe_skip_subexpr (c
))
455 if (!skip_subexpr (c
) ||
456 !maybe_interpret_subexpr (c
, pat
, buf
))
464 interpret_count (FcFormatContext
*c
,
470 FcChar8 buf_static
[64];
472 if (!expect_char (c
, '#'))
479 e
= FcPatternObjectFindElt (pat
,
480 FcObjectFromName ((const char *) c
->word
));
485 for (l
= FcPatternEltValues(e
);
491 snprintf ((char *) buf_static
, sizeof (buf_static
), "%d", count
);
492 FcStrBufString (buf
, buf_static
);
498 interpret_simple (FcFormatContext
*c
,
503 FcBool add_colon
= FcFalse
;
504 FcBool add_elt_name
= FcFalse
;
506 if (consume_char (c
, ':'))
512 if (consume_char (c
, '='))
513 add_elt_name
= FcTrue
;
515 e
= FcPatternObjectFindElt (pat
,
516 FcObjectFromName ((const char *) c
->word
));
522 FcStrBufChar (buf
, ':');
525 FcStrBufString (buf
, c
->word
);
526 FcStrBufChar (buf
, '=');
529 l
= FcPatternEltValues(e
);
530 FcNameUnparseValueList (buf
, l
, '\0');
537 cescape (FcFormatContext
*c
,
547 FcStrBufChar (buf
, '\\');
550 FcStrBufChar (buf
, *str
++);
556 shescape (FcFormatContext
*c
,
560 FcStrBufChar (buf
, '\'');
564 FcStrBufString (buf
, (const FcChar8
*) "'\\''");
566 FcStrBufChar (buf
, *str
);
569 FcStrBufChar (buf
, '\'');
574 xmlescape (FcFormatContext
*c
,
582 case '&': FcStrBufString (buf
, (const FcChar8
*) "&"); break;
583 case '<': FcStrBufString (buf
, (const FcChar8
*) "<"); break;
584 case '>': FcStrBufString (buf
, (const FcChar8
*) ">"); break;
585 default: FcStrBufChar (buf
, *str
); break;
593 delete_chars (FcFormatContext
*c
,
597 /* XXX not UTF-8 aware */
599 if (!expect_char (c
, '(') ||
600 !read_chars (c
, ')') ||
601 !expect_char (c
, ')'))
608 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
611 FcStrBufData (buf
, str
, p
- str
);
616 FcStrBufString (buf
, str
);
626 escape_chars (FcFormatContext
*c
,
630 /* XXX not UTF-8 aware */
632 if (!expect_char (c
, '(') ||
633 !read_chars (c
, ')') ||
634 !expect_char (c
, ')'))
641 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
644 FcStrBufData (buf
, str
, p
- str
);
645 FcStrBufChar (buf
, c
->word
[0]);
646 FcStrBufChar (buf
, *p
);
651 FcStrBufString (buf
, str
);
661 translate_chars (FcFormatContext
*c
,
665 char *from
, *to
, repeat
;
666 int from_len
, to_len
;
668 /* XXX not UTF-8 aware */
670 if (!expect_char (c
, '(') ||
671 !read_chars (c
, ',') ||
672 !expect_char (c
, ','))
675 from
= (char *) c
->word
;
676 from_len
= strlen (from
);
677 to
= from
+ from_len
+ 1;
679 /* hack: we temporarily diverge c->word */
680 c
->word
= (FcChar8
*) to
;
681 if (!read_chars (c
, ')'))
683 c
->word
= (FcChar8
*) from
;
686 c
->word
= (FcChar8
*) from
;
688 to_len
= strlen (to
);
689 repeat
= to
[to_len
- 1];
691 if (!expect_char (c
, ')'))
698 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) from
);
702 FcStrBufData (buf
, str
, p
- str
);
703 i
= strchr (from
, *p
) - from
;
704 FcStrBufChar (buf
, i
< to_len
? to
[i
] : repeat
);
709 FcStrBufString (buf
, str
);
719 interpret_convert (FcFormatContext
*c
,
726 FcChar8 buf_static
[8192];
729 if (!expect_char (c
, '|'))
732 /* nul-terminate the buffer */
733 FcStrBufChar (buf
, '\0');
736 str
= buf
->buf
+ start
;
742 /* try simple converters first */
744 #define CONVERTER(name, func) \
745 else if (0 == strcmp ((const char *) c->word, name))\
746 do { new_str = func (str); ret = FcTrue; } while (0)
747 CONVERTER ("downcase", FcStrDowncase
);
748 CONVERTER ("basename", FcStrBasename
);
749 CONVERTER ("dirname", FcStrDirname
);
758 /* replace in the buffer */
759 FcStrBufString (buf
, new_str
);
767 FcStrBufInit (&new_buf
, buf_static
, sizeof (buf_static
));
769 /* now try our custom converters */
771 #define CONVERTER(name, func) \
772 else if (0 == strcmp ((const char *) c->word, name))\
773 ret = func (c, &new_buf, str)
774 CONVERTER ("cescape", cescape
);
775 CONVERTER ("shescape", shescape
);
776 CONVERTER ("xmlescape", xmlescape
);
777 CONVERTER ("delete", delete_chars
);
778 CONVERTER ("escape", escape_chars
);
779 CONVERTER ("translate", translate_chars
);
786 FcStrBufChar (&new_buf
, '\0');
787 FcStrBufString (buf
, new_buf
.buf
);
790 message ("unknown converter \"%s\"",
793 FcStrBufDestroy (&new_buf
);
799 maybe_interpret_converts (FcFormatContext
*c
,
803 while (*c
->format
== '|')
804 if (!interpret_convert (c
, buf
, start
))
811 align_to_width (FcStrBuf
*buf
,
820 len
= buf
->len
- start
;
824 while (len
++ < -width
)
825 FcStrBufChar (buf
, ' ');
827 else if (len
< width
)
832 while (len
++ < width
)
833 FcStrBufChar (buf
, ' ');
837 memmove (buf
->buf
+ buf
->len
- len
,
838 buf
->buf
+ buf
->len
- width
,
840 memset (buf
->buf
+ buf
->len
- width
,
848 interpret_percent (FcFormatContext
*c
,
855 if (!expect_char (c
, '%'))
858 if (consume_char (c
, '%')) /* "%%" */
860 FcStrBufChar (buf
, '%');
864 /* parse an optional width specifier */
865 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
867 if (!expect_char (c
, '{'))
872 switch (*c
->format
) {
873 case '=': ret
= interpret_builtin (c
, pat
, buf
); break;
874 case '{': ret
= interpret_subexpr (c
, pat
, buf
); break;
875 case '+': ret
= interpret_filter (c
, pat
, buf
); break;
876 case '-': ret
= interpret_delete (c
, pat
, buf
); break;
877 case '?': ret
= interpret_cond (c
, pat
, buf
); break;
878 case '#': ret
= interpret_count (c
, pat
, buf
); break;
879 default: ret
= interpret_simple (c
, pat
, buf
); break;
883 maybe_interpret_converts (c
, buf
, start
) &&
884 align_to_width (buf
, start
, width
) &&
885 expect_char (c
, '}');
889 interpret_expr (FcFormatContext
*c
,
894 while (*c
->format
&& *c
->format
!= term
)
899 c
->format
++; /* skip over '\\' */
901 FcStrBufChar (buf
, escaped_char (*c
->format
++));
904 if (!interpret_percent (c
, pat
, buf
))
908 FcStrBufChar (buf
, *c
->format
++);
914 FcPatternFormat (FcPattern
*pat
, const FcChar8
*format
)
917 FcChar8 word_static
[1024];
918 FcChar8 buf_static
[8192 - 1024];
922 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
923 if (!FcFormatContextInit (&c
, format
, word_static
, sizeof (word_static
)))
926 ret
= interpret_expr (&c
, pat
, &buf
, '\0');
928 FcFormatContextDone (&c
);
930 return FcStrBufDone (&buf
);
933 FcStrBufDestroy (&buf
);
939 #include "fcaliastail.h"