]>
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
;
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_expr (FcFormatContext
*c
,
213 interpret_subexpr (FcFormatContext
*c
,
217 return expect_char (c
, '{') &&
218 interpret_expr (c
, pat
, buf
, '}') &&
219 expect_char (c
, '}');
223 maybe_interpret_subexpr (FcFormatContext
*c
,
227 return (*c
->format
== '{') ?
228 interpret_subexpr (c
, pat
, buf
) :
233 skip_subexpr (FcFormatContext
*c
);
236 skip_percent (FcFormatContext
*c
)
240 if (!expect_char (c
, '%'))
243 /* skip an optional width specifier */
244 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
246 if (!expect_char (c
, '{'))
249 while(*c
->format
&& *c
->format
!= '}')
254 c
->format
++; /* skip over '\\' */
259 if (!skip_subexpr (c
))
266 return expect_char (c
, '}');
270 skip_expr (FcFormatContext
*c
)
272 while(*c
->format
&& *c
->format
!= '}')
277 c
->format
++; /* skip over '\\' */
282 if (!skip_percent (c
))
293 skip_subexpr (FcFormatContext
*c
)
295 return expect_char (c
, '{') &&
297 expect_char (c
, '}');
301 maybe_skip_subexpr (FcFormatContext
*c
)
303 return (*c
->format
== '{') ?
309 interpret_filter (FcFormatContext
*c
,
316 if (!expect_char (c
, '+'))
319 os
= FcObjectSetCreate ();
325 if (!read_word (c
) ||
326 !FcObjectSetAdd (os
, (const char *) c
->word
))
328 FcObjectSetDestroy (os
);
332 while (consume_char (c
, ','));
334 subpat
= FcPatternFilter (pat
, os
);
335 FcObjectSetDestroy (os
);
338 !interpret_subexpr (c
, subpat
, buf
))
341 FcPatternDestroy (subpat
);
346 interpret_delete (FcFormatContext
*c
,
352 if (!expect_char (c
, '-'))
355 subpat
= FcPatternDuplicate (pat
);
363 FcPatternDestroy (subpat
);
367 FcPatternDel (subpat
, (const char *) c
->word
);
369 while (consume_char (c
, ','));
371 if (!interpret_subexpr (c
, subpat
, buf
))
374 FcPatternDestroy (subpat
);
379 interpret_cond (FcFormatContext
*c
,
385 if (!expect_char (c
, '?'))
395 negate
= consume_char (c
, '!');
402 (FcResultMatch
== FcPatternGet (pat
,
403 (const char *) c
->word
,
406 while (consume_char (c
, ','));
410 if (!interpret_subexpr (c
, pat
, buf
) ||
411 !maybe_skip_subexpr (c
))
416 if (!skip_subexpr (c
) ||
417 !maybe_interpret_subexpr (c
, pat
, buf
))
425 interpret_count (FcFormatContext
*c
,
431 FcChar8 buf_static
[64];
433 if (!expect_char (c
, '#'))
440 e
= FcPatternObjectFindElt (pat
,
441 FcObjectFromName ((const char *) c
->word
));
446 for (l
= FcPatternEltValues(e
);
452 snprintf ((char *) buf_static
, sizeof (buf_static
), "%d", count
);
453 FcStrBufString (buf
, buf_static
);
459 interpret_simple (FcFormatContext
*c
,
464 FcBool add_colon
= FcFalse
;
465 FcBool add_elt_name
= FcFalse
;
467 if (consume_char (c
, ':'))
473 if (consume_char (c
, '='))
474 add_elt_name
= FcTrue
;
476 e
= FcPatternObjectFindElt (pat
,
477 FcObjectFromName ((const char *) c
->word
));
483 FcStrBufChar (buf
, ':');
486 FcStrBufString (buf
, c
->word
);
487 FcStrBufChar (buf
, '=');
490 l
= FcPatternEltValues(e
);
491 FcNameUnparseValueList (buf
, l
, '\0');
498 cescape (const FcChar8
*str
)
501 FcChar8 buf_static
[8192];
503 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
510 FcStrBufChar (&buf
, '\\');
513 FcStrBufChar (&buf
, *str
++);
515 return FcStrBufDone (&buf
);
519 shescape (const FcChar8
*str
)
522 FcChar8 buf_static
[8192];
524 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
525 FcStrBufChar (&buf
, '\'');
529 FcStrBufString (&buf
, (const FcChar8
*) "'\\''");
531 FcStrBufChar (&buf
, *str
);
534 FcStrBufChar (&buf
, '\'');
535 return FcStrBufDone (&buf
);
539 xmlescape (const FcChar8
*str
)
542 FcChar8 buf_static
[8192];
544 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
549 case '&': FcStrBufString (&buf
, (const FcChar8
*) "&"); break;
550 case '<': FcStrBufString (&buf
, (const FcChar8
*) "<"); break;
551 case '>': FcStrBufString (&buf
, (const FcChar8
*) ">"); break;
552 default: FcStrBufChar (&buf
, *str
); break;
556 return FcStrBufDone (&buf
);
560 delete_chars (FcFormatContext
*c
,
564 FcChar8 buf_static
[8192];
566 /* XXX not UTF-8 aware */
568 if (!expect_char (c
, '(') ||
569 !read_chars (c
, ')') ||
570 !expect_char (c
, ')'))
573 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
578 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
581 FcStrBufData (&buf
, str
, p
- str
);
586 FcStrBufString (&buf
, str
);
591 return FcStrBufDone (&buf
);
595 escape_chars (FcFormatContext
*c
,
599 FcChar8 buf_static
[8192];
601 /* XXX not UTF-8 aware */
603 if (!expect_char (c
, '(') ||
604 !read_chars (c
, ')') ||
605 !expect_char (c
, ')'))
608 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
613 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
616 FcStrBufData (&buf
, str
, p
- str
);
617 FcStrBufChar (&buf
, c
->word
[0]);
618 FcStrBufChar (&buf
, *p
);
623 FcStrBufString (&buf
, str
);
628 return FcStrBufDone (&buf
);
632 translate_chars (FcFormatContext
*c
,
636 FcChar8 buf_static
[8192];
637 char *from
, *to
, repeat
;
638 int from_len
, to_len
;
640 /* XXX not UTF-8 aware */
642 if (!expect_char (c
, '(') ||
643 !read_chars (c
, ',') ||
644 !expect_char (c
, ','))
647 from
= (char *) c
->word
;
648 from_len
= strlen (from
);
649 to
= from
+ from_len
+ 1;
651 /* hack: we temporarily diverge c->word */
652 c
->word
= (FcChar8
*) to
;
653 if (!read_chars (c
, ')'))
655 c
->word
= (FcChar8
*) from
;
658 c
->word
= (FcChar8
*) from
;
660 to_len
= strlen (to
);
661 repeat
= to
[to_len
- 1];
663 if (!expect_char (c
, ')'))
666 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
671 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) from
);
675 FcStrBufData (&buf
, str
, p
- str
);
676 i
= strchr (from
, *p
) - from
;
677 FcStrBufChar (&buf
, i
< to_len
? to
[i
] : repeat
);
682 FcStrBufString (&buf
, str
);
687 return FcStrBufDone (&buf
);
691 convert (FcFormatContext
*c
,
696 #define CONVERTER(name, func) \
697 else if (0 == strcmp ((const char *) c->word, name))\
699 #define CONVERTER2(name, func) \
700 else if (0 == strcmp ((const char *) c->word, name))\
702 CONVERTER ("downcase", FcStrDowncase
);
703 CONVERTER ("basename", FcStrBasename
);
704 CONVERTER ("dirname", FcStrDirname
);
705 CONVERTER ("cescape", cescape
);
706 CONVERTER ("shescape", shescape
);
707 CONVERTER ("xmlescape", xmlescape
);
708 CONVERTER2 ("delete", delete_chars
);
709 CONVERTER2 ("escape", escape_chars
);
710 CONVERTER2 ("translate", translate_chars
);
712 message ("unknown converter \"%s\"",
718 maybe_interpret_converts (FcFormatContext
*c
,
722 while (consume_char (c
, '|'))
727 /* nul-terminate the buffer */
728 FcStrBufChar (buf
, '\0');
731 str
= buf
->buf
+ start
;
733 if (!(new_str
= convert (c
, str
)))
736 /* replace in the buffer */
738 FcStrBufString (buf
, new_str
);
746 align_to_width (FcStrBuf
*buf
,
755 len
= buf
->len
- start
;
759 while (len
++ < -width
)
760 FcStrBufChar (buf
, ' ');
762 else if (len
< width
)
767 while (len
++ < width
)
768 FcStrBufChar (buf
, ' ');
772 memmove (buf
->buf
+ buf
->len
- len
,
773 buf
->buf
+ buf
->len
- width
,
775 memset (buf
->buf
+ buf
->len
- width
,
783 interpret_percent (FcFormatContext
*c
,
790 if (!expect_char (c
, '%'))
793 if (consume_char (c
, '%')) /* "%%" */
795 FcStrBufChar (buf
, '%');
799 /* parse an optional width specifier */
800 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
802 if (!expect_char (c
, '{'))
807 switch (*c
->format
) {
808 case '{': ret
= interpret_subexpr (c
, pat
, buf
); break;
809 case '+': ret
= interpret_filter (c
, pat
, buf
); break;
810 case '-': ret
= interpret_delete (c
, pat
, buf
); break;
811 case '?': ret
= interpret_cond (c
, pat
, buf
); break;
812 case '#': ret
= interpret_count (c
, pat
, buf
); break;
813 default: ret
= interpret_simple (c
, pat
, buf
); break;
817 maybe_interpret_converts (c
, buf
, start
) &&
818 align_to_width (buf
, start
, width
) &&
819 expect_char (c
, '}');
823 interpret_expr (FcFormatContext
*c
,
828 while (*c
->format
&& *c
->format
!= term
)
833 c
->format
++; /* skip over '\\' */
835 FcStrBufChar (buf
, escaped_char (*c
->format
++));
838 if (!interpret_percent (c
, pat
, buf
))
842 FcStrBufChar (buf
, *c
->format
++);
848 FcPatternFormat (FcPattern
*pat
, const FcChar8
*format
)
851 FcChar8 buf_static
[8192];
855 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
856 if (!FcFormatContextInit (&c
, format
))
859 ret
= interpret_expr (&c
, pat
, &buf
, '\0');
861 FcFormatContextDone (&c
);
863 return FcStrBufDone (&buf
);
866 FcStrBufDestroy (&buf
);
872 #include "fcaliastail.h"