]>
git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
334eee6a71bc09ab27cde3a62d9d9eb5649e4f9e
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 simple tags using '%{elt[idx]}'
37 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
38 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
42 /* fc-match needs '<unknown filename>', etc handling, as well as printing
43 * printing first value only. */
44 #define FCMATCH_FORMAT "%{file|basename}: \"%{family}\" \"%{style}\""
45 #define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
49 message (const char *fmt
, ...)
53 fprintf (stderr
, "Fontconfig: Pattern format error: ");
54 vfprintf (stderr
, fmt
, args
);
55 fprintf (stderr
, ".\n");
60 typedef struct _FcFormatContext
62 const FcChar8
*format_orig
;
63 const FcChar8
*format
;
66 FcBool word_allocated
;
70 FcFormatContextInit (FcFormatContext
*c
,
71 const FcChar8
*format
,
75 c
->format_orig
= c
->format
= format
;
76 c
->format_len
= strlen ((const char *) format
);
78 if (c
->format_len
< scratch_len
)
81 c
->word_allocated
= FcFalse
;
85 c
->word
= malloc (c
->format_len
+ 1);
86 c
->word_allocated
= FcTrue
;
89 return c
->word
!= NULL
;
93 FcFormatContextDone (FcFormatContext
*c
)
95 if (c
&& c
->word_allocated
)
102 consume_char (FcFormatContext
*c
,
105 if (*c
->format
!= term
)
113 expect_char (FcFormatContext
*c
,
116 FcBool res
= consume_char (c
, term
);
119 if (c
->format
== c
->format_orig
+ c
->format_len
)
120 message ("format ended while expecting '%c'",
123 message ("expected '%c' at %d",
124 term
, c
->format
- c
->format_orig
+ 1);
130 FcCharIsPunct (const FcChar8 c
)
149 static char escaped_char(const char ch
)
152 case 'a': return '\a';
153 case 'b': return '\b';
154 case 'f': return '\f';
155 case 'n': return '\n';
156 case 'r': return '\r';
157 case 't': return '\t';
158 case 'v': return '\v';
164 read_word (FcFormatContext
*c
)
172 if (*c
->format
== '\\')
176 *p
++ = escaped_char (*c
->format
++);
179 else if (FcCharIsPunct (*c
->format
))
188 message ("expected identifier at %d",
189 c
->format
- c
->format_orig
+ 1);
197 read_chars (FcFormatContext
*c
,
204 while (*c
->format
&& *c
->format
!= '}' && *c
->format
!= term
)
206 if (*c
->format
== '\\')
210 *p
++ = escaped_char (*c
->format
++);
220 message ("expected character data at %d",
221 c
->format
- c
->format_orig
+ 1);
229 FcPatternFormatToBuf (FcPattern
*pat
,
230 const FcChar8
*format
,
234 interpret_builtin (FcFormatContext
*c
,
241 if (!expect_char (c
, '=') ||
245 /* try simple builtins first */
247 #define BUILTIN(name, func) \
248 else if (0 == strcmp ((const char *) c->word, name))\
249 do { new_str = func (pat); ret = FcTrue; } while (0)
250 BUILTIN ("unparse", FcNameUnparse
);
251 /* BUILTIN ("verbose", FcPatternPrint); */
260 FcStrBufString (buf
, new_str
);
268 /* now try our custom formats */
270 #define BUILTIN(name, format) \
271 else if (0 == strcmp ((const char *) c->word, name))\
272 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
273 BUILTIN ("fcmatch", FCMATCH_FORMAT
);
274 BUILTIN ("fclist", FCLIST_FORMAT
);
280 message ("unknown builtin \"%s\"",
287 interpret_expr (FcFormatContext
*c
,
293 interpret_subexpr (FcFormatContext
*c
,
297 return expect_char (c
, '{') &&
298 interpret_expr (c
, pat
, buf
, '}') &&
299 expect_char (c
, '}');
303 maybe_interpret_subexpr (FcFormatContext
*c
,
307 return (*c
->format
== '{') ?
308 interpret_subexpr (c
, pat
, buf
) :
313 skip_subexpr (FcFormatContext
*c
);
316 skip_percent (FcFormatContext
*c
)
320 if (!expect_char (c
, '%'))
323 /* skip an optional width specifier */
324 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
326 if (!expect_char (c
, '{'))
329 while(*c
->format
&& *c
->format
!= '}')
334 c
->format
++; /* skip over '\\' */
339 if (!skip_subexpr (c
))
346 return expect_char (c
, '}');
350 skip_expr (FcFormatContext
*c
)
352 while(*c
->format
&& *c
->format
!= '}')
357 c
->format
++; /* skip over '\\' */
362 if (!skip_percent (c
))
373 skip_subexpr (FcFormatContext
*c
)
375 return expect_char (c
, '{') &&
377 expect_char (c
, '}');
381 maybe_skip_subexpr (FcFormatContext
*c
)
383 return (*c
->format
== '{') ?
389 interpret_filter (FcFormatContext
*c
,
396 if (!expect_char (c
, '+'))
399 os
= FcObjectSetCreate ();
405 if (!read_word (c
) ||
406 !FcObjectSetAdd (os
, (const char *) c
->word
))
408 FcObjectSetDestroy (os
);
412 while (consume_char (c
, ','));
414 subpat
= FcPatternFilter (pat
, os
);
415 FcObjectSetDestroy (os
);
418 !interpret_subexpr (c
, subpat
, buf
))
421 FcPatternDestroy (subpat
);
426 interpret_delete (FcFormatContext
*c
,
432 if (!expect_char (c
, '-'))
435 subpat
= FcPatternDuplicate (pat
);
443 FcPatternDestroy (subpat
);
447 FcPatternDel (subpat
, (const char *) c
->word
);
449 while (consume_char (c
, ','));
451 if (!interpret_subexpr (c
, subpat
, buf
))
454 FcPatternDestroy (subpat
);
459 interpret_cond (FcFormatContext
*c
,
465 if (!expect_char (c
, '?'))
475 negate
= consume_char (c
, '!');
482 (FcResultMatch
== FcPatternGet (pat
,
483 (const char *) c
->word
,
486 while (consume_char (c
, ','));
490 if (!interpret_subexpr (c
, pat
, buf
) ||
491 !maybe_skip_subexpr (c
))
496 if (!skip_subexpr (c
) ||
497 !maybe_interpret_subexpr (c
, pat
, buf
))
505 interpret_count (FcFormatContext
*c
,
511 FcChar8 buf_static
[64];
513 if (!expect_char (c
, '#'))
520 e
= FcPatternObjectFindElt (pat
,
521 FcObjectFromName ((const char *) c
->word
));
526 for (l
= FcPatternEltValues(e
);
532 snprintf ((char *) buf_static
, sizeof (buf_static
), "%d", count
);
533 FcStrBufString (buf
, buf_static
);
539 interpret_simple (FcFormatContext
*c
,
544 FcBool add_colon
= FcFalse
;
545 FcBool add_elt_name
= FcFalse
;
547 if (consume_char (c
, ':'))
553 if (consume_char (c
, '='))
554 add_elt_name
= FcTrue
;
556 e
= FcPatternObjectFindElt (pat
,
557 FcObjectFromName ((const char *) c
->word
));
563 FcStrBufChar (buf
, ':');
566 FcStrBufString (buf
, c
->word
);
567 FcStrBufChar (buf
, '=');
570 l
= FcPatternEltValues(e
);
571 FcNameUnparseValueList (buf
, l
, '\0');
578 cescape (FcFormatContext
*c
,
588 FcStrBufChar (buf
, '\\');
591 FcStrBufChar (buf
, *str
++);
597 shescape (FcFormatContext
*c
,
601 FcStrBufChar (buf
, '\'');
605 FcStrBufString (buf
, (const FcChar8
*) "'\\''");
607 FcStrBufChar (buf
, *str
);
610 FcStrBufChar (buf
, '\'');
615 xmlescape (FcFormatContext
*c
,
623 case '&': FcStrBufString (buf
, (const FcChar8
*) "&"); break;
624 case '<': FcStrBufString (buf
, (const FcChar8
*) "<"); break;
625 case '>': FcStrBufString (buf
, (const FcChar8
*) ">"); break;
626 default: FcStrBufChar (buf
, *str
); break;
634 delete_chars (FcFormatContext
*c
,
638 /* XXX not UTF-8 aware */
640 if (!expect_char (c
, '(') ||
641 !read_chars (c
, ')') ||
642 !expect_char (c
, ')'))
649 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
652 FcStrBufData (buf
, str
, p
- str
);
657 FcStrBufString (buf
, str
);
667 escape_chars (FcFormatContext
*c
,
671 /* XXX not UTF-8 aware */
673 if (!expect_char (c
, '(') ||
674 !read_chars (c
, ')') ||
675 !expect_char (c
, ')'))
682 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
685 FcStrBufData (buf
, str
, p
- str
);
686 FcStrBufChar (buf
, c
->word
[0]);
687 FcStrBufChar (buf
, *p
);
692 FcStrBufString (buf
, str
);
702 translate_chars (FcFormatContext
*c
,
706 char *from
, *to
, repeat
;
707 int from_len
, to_len
;
709 /* XXX not UTF-8 aware */
711 if (!expect_char (c
, '(') ||
712 !read_chars (c
, ',') ||
713 !expect_char (c
, ','))
716 from
= (char *) c
->word
;
717 from_len
= strlen (from
);
718 to
= from
+ from_len
+ 1;
720 /* hack: we temporarily diverge c->word */
721 c
->word
= (FcChar8
*) to
;
722 if (!read_chars (c
, ')'))
724 c
->word
= (FcChar8
*) from
;
727 c
->word
= (FcChar8
*) from
;
729 to_len
= strlen (to
);
730 repeat
= to
[to_len
- 1];
732 if (!expect_char (c
, ')'))
739 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) from
);
743 FcStrBufData (buf
, str
, p
- str
);
744 i
= strchr (from
, *p
) - from
;
745 FcStrBufChar (buf
, i
< to_len
? to
[i
] : repeat
);
750 FcStrBufString (buf
, str
);
760 interpret_convert (FcFormatContext
*c
,
767 FcChar8 buf_static
[8192];
770 if (!expect_char (c
, '|') ||
774 /* prepare the buffer */
775 FcStrBufChar (buf
, '\0');
778 str
= buf
->buf
+ start
;
781 /* try simple converters first */
783 #define CONVERTER(name, func) \
784 else if (0 == strcmp ((const char *) c->word, name))\
785 do { new_str = func (str); ret = FcTrue; } while (0)
786 CONVERTER ("downcase", FcStrDowncase
);
787 CONVERTER ("basename", FcStrBasename
);
788 CONVERTER ("dirname", FcStrDirname
);
797 FcStrBufString (buf
, new_str
);
805 FcStrBufInit (&new_buf
, buf_static
, sizeof (buf_static
));
807 /* now try our custom converters */
809 #define CONVERTER(name, func) \
810 else if (0 == strcmp ((const char *) c->word, name))\
811 ret = func (c, str, &new_buf)
812 CONVERTER ("cescape", cescape
);
813 CONVERTER ("shescape", shescape
);
814 CONVERTER ("xmlescape", xmlescape
);
815 CONVERTER ("delete", delete_chars
);
816 CONVERTER ("escape", escape_chars
);
817 CONVERTER ("translate", translate_chars
);
824 FcStrBufChar (&new_buf
, '\0');
825 FcStrBufString (buf
, new_buf
.buf
);
828 message ("unknown converter \"%s\"",
831 FcStrBufDestroy (&new_buf
);
837 maybe_interpret_converts (FcFormatContext
*c
,
841 while (*c
->format
== '|')
842 if (!interpret_convert (c
, buf
, start
))
849 align_to_width (FcStrBuf
*buf
,
858 len
= buf
->len
- start
;
862 while (len
++ < -width
)
863 FcStrBufChar (buf
, ' ');
865 else if (len
< width
)
870 while (len
++ < width
)
871 FcStrBufChar (buf
, ' ');
875 memmove (buf
->buf
+ buf
->len
- len
,
876 buf
->buf
+ buf
->len
- width
,
878 memset (buf
->buf
+ buf
->len
- width
,
886 interpret_percent (FcFormatContext
*c
,
893 if (!expect_char (c
, '%'))
896 if (consume_char (c
, '%')) /* "%%" */
898 FcStrBufChar (buf
, '%');
902 /* parse an optional width specifier */
903 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
905 if (!expect_char (c
, '{'))
910 switch (*c
->format
) {
911 case '=': ret
= interpret_builtin (c
, pat
, buf
); break;
912 case '{': ret
= interpret_subexpr (c
, pat
, buf
); break;
913 case '+': ret
= interpret_filter (c
, pat
, buf
); break;
914 case '-': ret
= interpret_delete (c
, pat
, buf
); break;
915 case '?': ret
= interpret_cond (c
, pat
, buf
); break;
916 case '#': ret
= interpret_count (c
, pat
, buf
); break;
917 default: ret
= interpret_simple (c
, pat
, buf
); break;
921 maybe_interpret_converts (c
, buf
, start
) &&
922 align_to_width (buf
, start
, width
) &&
923 expect_char (c
, '}');
927 interpret_expr (FcFormatContext
*c
,
932 while (*c
->format
&& *c
->format
!= term
)
937 c
->format
++; /* skip over '\\' */
939 FcStrBufChar (buf
, escaped_char (*c
->format
++));
942 if (!interpret_percent (c
, pat
, buf
))
946 FcStrBufChar (buf
, *c
->format
++);
952 FcPatternFormatToBuf (FcPattern
*pat
,
953 const FcChar8
*format
,
957 FcChar8 word_static
[1024];
960 if (!FcFormatContextInit (&c
, format
, word_static
, sizeof (word_static
)))
963 ret
= interpret_expr (&c
, pat
, buf
, '\0');
965 FcFormatContextDone (&c
);
971 FcPatternFormat (FcPattern
*pat
,
972 const FcChar8
*format
)
975 FcChar8 buf_static
[8192 - 1024];
978 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
980 ret
= FcPatternFormatToBuf (pat
, format
, &buf
);
983 return FcStrBufDone (&buf
);
986 FcStrBufDestroy (&buf
);
992 #include "fcaliastail.h"