]>
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 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
39 #define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
40 #define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
44 message (const char *fmt
, ...)
48 fprintf (stderr
, "Fontconfig: Pattern format error: ");
49 vfprintf (stderr
, fmt
, args
);
50 fprintf (stderr
, ".\n");
55 typedef struct _FcFormatContext
57 const FcChar8
*format_orig
;
58 const FcChar8
*format
;
61 FcBool word_allocated
;
65 FcFormatContextInit (FcFormatContext
*c
,
66 const FcChar8
*format
,
70 c
->format_orig
= c
->format
= format
;
71 c
->format_len
= strlen ((const char *) format
);
73 if (c
->format_len
< scratch_len
)
76 c
->word_allocated
= FcFalse
;
80 c
->word
= malloc (c
->format_len
+ 1);
81 c
->word_allocated
= FcTrue
;
84 return c
->word
!= NULL
;
88 FcFormatContextDone (FcFormatContext
*c
)
90 if (c
&& c
->word_allocated
)
97 consume_char (FcFormatContext
*c
,
100 if (*c
->format
!= term
)
108 expect_char (FcFormatContext
*c
,
111 FcBool res
= consume_char (c
, term
);
114 if (c
->format
== c
->format_orig
+ c
->format_len
)
115 message ("format ended while expecting '%c'",
118 message ("expected '%c' at %d",
119 term
, c
->format
- c
->format_orig
+ 1);
125 FcCharIsPunct (const FcChar8 c
)
144 static char escaped_char(const char ch
)
147 case 'a': return '\a';
148 case 'b': return '\b';
149 case 'f': return '\f';
150 case 'n': return '\n';
151 case 'r': return '\r';
152 case 't': return '\t';
153 case 'v': return '\v';
159 read_word (FcFormatContext
*c
)
167 if (*c
->format
== '\\')
171 *p
++ = escaped_char (*c
->format
++);
174 else if (FcCharIsPunct (*c
->format
))
183 message ("expected identifier at %d",
184 c
->format
- c
->format_orig
+ 1);
192 read_chars (FcFormatContext
*c
,
199 while (*c
->format
&& *c
->format
!= '}' && *c
->format
!= term
)
201 if (*c
->format
== '\\')
205 *p
++ = escaped_char (*c
->format
++);
215 message ("expected character data at %d",
216 c
->format
- c
->format_orig
+ 1);
224 FcPatternFormatToBuf (FcPattern
*pat
,
225 const FcChar8
*format
,
229 interpret_builtin (FcFormatContext
*c
,
236 if (!expect_char (c
, '=') ||
240 /* try simple builtins first */
242 #define BUILTIN(name, func) \
243 else if (0 == strcmp ((const char *) c->word, name))\
244 do { new_str = func (pat); ret = FcTrue; } while (0)
245 BUILTIN ("unparse", FcNameUnparse
);
246 /* BUILTIN ("verbose", FcPatternPrint); XXX */
255 FcStrBufString (buf
, new_str
);
263 /* now try our custom formats */
265 #define BUILTIN(name, format) \
266 else if (0 == strcmp ((const char *) c->word, name))\
267 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
268 BUILTIN ("fcmatch", FCMATCH_FORMAT
);
269 BUILTIN ("fclist", FCLIST_FORMAT
);
275 message ("unknown builtin \"%s\"",
282 interpret_expr (FcFormatContext
*c
,
288 interpret_subexpr (FcFormatContext
*c
,
292 return expect_char (c
, '{') &&
293 interpret_expr (c
, pat
, buf
, '}') &&
294 expect_char (c
, '}');
298 maybe_interpret_subexpr (FcFormatContext
*c
,
302 return (*c
->format
== '{') ?
303 interpret_subexpr (c
, pat
, buf
) :
308 skip_subexpr (FcFormatContext
*c
);
311 skip_percent (FcFormatContext
*c
)
315 if (!expect_char (c
, '%'))
318 /* skip an optional width specifier */
319 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
321 if (!expect_char (c
, '{'))
324 while(*c
->format
&& *c
->format
!= '}')
329 c
->format
++; /* skip over '\\' */
334 if (!skip_subexpr (c
))
341 return expect_char (c
, '}');
345 skip_expr (FcFormatContext
*c
)
347 while(*c
->format
&& *c
->format
!= '}')
352 c
->format
++; /* skip over '\\' */
357 if (!skip_percent (c
))
368 skip_subexpr (FcFormatContext
*c
)
370 return expect_char (c
, '{') &&
372 expect_char (c
, '}');
376 maybe_skip_subexpr (FcFormatContext
*c
)
378 return (*c
->format
== '{') ?
384 interpret_filter (FcFormatContext
*c
,
391 if (!expect_char (c
, '+'))
394 os
= FcObjectSetCreate ();
400 if (!read_word (c
) ||
401 !FcObjectSetAdd (os
, (const char *) c
->word
))
403 FcObjectSetDestroy (os
);
407 while (consume_char (c
, ','));
409 subpat
= FcPatternFilter (pat
, os
);
410 FcObjectSetDestroy (os
);
413 !interpret_subexpr (c
, subpat
, buf
))
416 FcPatternDestroy (subpat
);
421 interpret_delete (FcFormatContext
*c
,
427 if (!expect_char (c
, '-'))
430 subpat
= FcPatternDuplicate (pat
);
438 FcPatternDestroy (subpat
);
442 FcPatternDel (subpat
, (const char *) c
->word
);
444 while (consume_char (c
, ','));
446 if (!interpret_subexpr (c
, subpat
, buf
))
449 FcPatternDestroy (subpat
);
454 interpret_cond (FcFormatContext
*c
,
460 if (!expect_char (c
, '?'))
470 negate
= consume_char (c
, '!');
478 FcPatternGet (pat
, (const char *) c
->word
, 0, &v
)));
480 while (consume_char (c
, ','));
484 if (!interpret_subexpr (c
, pat
, buf
) ||
485 !maybe_skip_subexpr (c
))
490 if (!skip_subexpr (c
) ||
491 !maybe_interpret_subexpr (c
, pat
, buf
))
499 interpret_count (FcFormatContext
*c
,
505 FcChar8 buf_static
[64];
507 if (!expect_char (c
, '#'))
514 e
= FcPatternObjectFindElt (pat
,
515 FcObjectFromName ((const char *) c
->word
));
520 for (l
= FcPatternEltValues(e
);
526 snprintf ((char *) buf_static
, sizeof (buf_static
), "%d", count
);
527 FcStrBufString (buf
, buf_static
);
533 interpret_array (FcFormatContext
*c
,
539 const FcChar8
*format_save
;
542 FcStrList
*lang_strs
;
544 if (!expect_char (c
, '[') ||
545 !expect_char (c
, ']'))
548 os
= FcObjectSetCreate ();
556 if (!read_word (c
) ||
557 !FcObjectSetAdd (os
, (const char *) c
->word
))
559 FcObjectSetDestroy (os
);
563 while (consume_char (c
, ','));
565 /* If we have one element and it's of type FcLangSet, we want
566 * to enumerate the languages in it. */
568 if (os
->nobject
== 1)
572 FcPatternGetLangSet (pat
, os
->objects
[0], idx
, &langset
))
575 if (!(ss
= FcLangSetGetLangs (langset
)) ||
576 !(lang_strs
= FcStrListCreate (ss
)))
581 subpat
= FcPatternDuplicate (pat
);
585 format_save
= c
->format
;
597 FcPatternDel (subpat
, os
->objects
[0]);
598 if ((lang
= FcStrListNext (lang_strs
)))
600 FcPatternAddString (subpat
, os
->objects
[0], lang
);
606 for (i
= 0; i
< os
->nobject
; i
++)
610 /* XXX this can be optimized by accessing valuelist linked lists
611 * directly and remembering where we were. Most (all) value lists
612 * in normal uses are pretty short though (language tags are
613 * stored as a LangSet, not separate values.). */
614 FcPatternDel (subpat
, os
->objects
[i
]);
616 FcPatternGet (pat
, os
->objects
[i
], idx
, &v
))
618 FcPatternAdd (subpat
, os
->objects
[i
], v
, FcFalse
);
626 c
->format
= format_save
;
627 ret
= interpret_subexpr (c
, subpat
, buf
);
635 if (c
->format
== format_save
)
639 FcPatternDestroy (subpat
);
642 FcStrListDone (lang_strs
);
643 FcObjectSetDestroy (os
);
649 interpret_simple (FcFormatContext
*c
,
654 FcBool add_colon
= FcFalse
;
655 FcBool add_elt_name
= FcFalse
;
657 FcChar8
*else_string
;
659 if (consume_char (c
, ':'))
666 if (consume_char (c
, '['))
668 idx
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
671 message ("expected non-negative number at %d",
672 c
->format
-1 - c
->format_orig
+ 1);
675 if (!expect_char (c
, ']'))
679 if (consume_char (c
, '='))
680 add_elt_name
= FcTrue
;
684 if (consume_char (c
, ':'))
687 /* divert the c->word for now */
689 c
->word
= c
->word
+ strlen ((const char *) c
->word
) + 1;
690 /* for now we just support 'default value' */
691 if (!expect_char (c
, '-') ||
692 !read_chars (c
, '\0'))
697 else_string
= c
->word
;
701 e
= FcPatternObjectFindElt (pat
,
702 FcObjectFromName ((const char *) c
->word
));
708 FcStrBufChar (buf
, ':');
711 FcStrBufString (buf
, c
->word
);
712 FcStrBufChar (buf
, '=');
715 l
= FcPatternEltValues(e
);
721 l
= FcValueListNext(l
);
726 if (!FcNameUnparseValue (buf
, &l
->value
, '\0'))
733 FcNameUnparseValueList (buf
, l
, '\0');
740 printf ("%s", else_string
);
747 cescape (FcFormatContext
*c
,
757 FcStrBufChar (buf
, '\\');
760 FcStrBufChar (buf
, *str
++);
766 shescape (FcFormatContext
*c
,
770 FcStrBufChar (buf
, '\'');
774 FcStrBufString (buf
, (const FcChar8
*) "'\\''");
776 FcStrBufChar (buf
, *str
);
779 FcStrBufChar (buf
, '\'');
784 xmlescape (FcFormatContext
*c
,
792 case '&': FcStrBufString (buf
, (const FcChar8
*) "&"); break;
793 case '<': FcStrBufString (buf
, (const FcChar8
*) "<"); break;
794 case '>': FcStrBufString (buf
, (const FcChar8
*) ">"); break;
795 default: FcStrBufChar (buf
, *str
); break;
803 delete_chars (FcFormatContext
*c
,
807 /* XXX not UTF-8 aware */
809 if (!expect_char (c
, '(') ||
810 !read_chars (c
, ')') ||
811 !expect_char (c
, ')'))
818 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
821 FcStrBufData (buf
, str
, p
- str
);
826 FcStrBufString (buf
, str
);
836 escape_chars (FcFormatContext
*c
,
840 /* XXX not UTF-8 aware */
842 if (!expect_char (c
, '(') ||
843 !read_chars (c
, ')') ||
844 !expect_char (c
, ')'))
851 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
854 FcStrBufData (buf
, str
, p
- str
);
855 FcStrBufChar (buf
, c
->word
[0]);
856 FcStrBufChar (buf
, *p
);
861 FcStrBufString (buf
, str
);
871 translate_chars (FcFormatContext
*c
,
875 char *from
, *to
, repeat
;
876 int from_len
, to_len
;
878 /* XXX not UTF-8 aware */
880 if (!expect_char (c
, '(') ||
881 !read_chars (c
, ',') ||
882 !expect_char (c
, ','))
885 from
= (char *) c
->word
;
886 from_len
= strlen (from
);
887 to
= from
+ from_len
+ 1;
889 /* hack: we temporarily divert c->word */
890 c
->word
= (FcChar8
*) to
;
891 if (!read_chars (c
, ')'))
893 c
->word
= (FcChar8
*) from
;
896 c
->word
= (FcChar8
*) from
;
898 to_len
= strlen (to
);
899 repeat
= to
[to_len
- 1];
901 if (!expect_char (c
, ')'))
908 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) from
);
912 FcStrBufData (buf
, str
, p
- str
);
913 i
= strchr (from
, *p
) - from
;
914 FcStrBufChar (buf
, i
< to_len
? to
[i
] : repeat
);
919 FcStrBufString (buf
, str
);
929 interpret_convert (FcFormatContext
*c
,
936 FcChar8 buf_static
[8192];
939 if (!expect_char (c
, '|') ||
943 /* prepare the buffer */
944 FcStrBufChar (buf
, '\0');
947 str
= buf
->buf
+ start
;
950 /* try simple converters first */
952 #define CONVERTER(name, func) \
953 else if (0 == strcmp ((const char *) c->word, name))\
954 do { new_str = func (str); ret = FcTrue; } while (0)
955 CONVERTER ("downcase", FcStrDowncase
);
956 CONVERTER ("basename", FcStrBasename
);
957 CONVERTER ("dirname", FcStrDirname
);
966 FcStrBufString (buf
, new_str
);
974 FcStrBufInit (&new_buf
, buf_static
, sizeof (buf_static
));
976 /* now try our custom converters */
978 #define CONVERTER(name, func) \
979 else if (0 == strcmp ((const char *) c->word, name))\
980 ret = func (c, str, &new_buf)
981 CONVERTER ("cescape", cescape
);
982 CONVERTER ("shescape", shescape
);
983 CONVERTER ("xmlescape", xmlescape
);
984 CONVERTER ("delete", delete_chars
);
985 CONVERTER ("escape", escape_chars
);
986 CONVERTER ("translate", translate_chars
);
993 FcStrBufChar (&new_buf
, '\0');
994 FcStrBufString (buf
, new_buf
.buf
);
997 message ("unknown converter \"%s\"",
1000 FcStrBufDestroy (&new_buf
);
1006 maybe_interpret_converts (FcFormatContext
*c
,
1010 while (*c
->format
== '|')
1011 if (!interpret_convert (c
, buf
, start
))
1018 align_to_width (FcStrBuf
*buf
,
1027 len
= buf
->len
- start
;
1031 while (len
++ < -width
)
1032 FcStrBufChar (buf
, ' ');
1034 else if (len
< width
)
1039 while (len
++ < width
)
1040 FcStrBufChar (buf
, ' ');
1044 memmove (buf
->buf
+ buf
->len
- len
,
1045 buf
->buf
+ buf
->len
- width
,
1047 memset (buf
->buf
+ buf
->len
- width
,
1052 return !buf
->failed
;
1055 interpret_percent (FcFormatContext
*c
,
1062 if (!expect_char (c
, '%'))
1065 if (consume_char (c
, '%')) /* "%%" */
1067 FcStrBufChar (buf
, '%');
1071 /* parse an optional width specifier */
1072 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
1074 if (!expect_char (c
, '{'))
1079 switch (*c
->format
) {
1080 case '=': ret
= interpret_builtin (c
, pat
, buf
); break;
1081 case '{': ret
= interpret_subexpr (c
, pat
, buf
); break;
1082 case '+': ret
= interpret_filter (c
, pat
, buf
); break;
1083 case '-': ret
= interpret_delete (c
, pat
, buf
); break;
1084 case '?': ret
= interpret_cond (c
, pat
, buf
); break;
1085 case '#': ret
= interpret_count (c
, pat
, buf
); break;
1086 case '[': ret
= interpret_array (c
, pat
, buf
); break;
1087 default: ret
= interpret_simple (c
, pat
, buf
); break;
1091 maybe_interpret_converts (c
, buf
, start
) &&
1092 align_to_width (buf
, start
, width
) &&
1093 expect_char (c
, '}');
1097 interpret_expr (FcFormatContext
*c
,
1102 while (*c
->format
&& *c
->format
!= term
)
1107 c
->format
++; /* skip over '\\' */
1109 FcStrBufChar (buf
, escaped_char (*c
->format
++));
1112 if (!interpret_percent (c
, pat
, buf
))
1116 FcStrBufChar (buf
, *c
->format
++);
1122 FcPatternFormatToBuf (FcPattern
*pat
,
1123 const FcChar8
*format
,
1127 FcChar8 word_static
[1024];
1130 if (!FcFormatContextInit (&c
, format
, word_static
, sizeof (word_static
)))
1133 ret
= interpret_expr (&c
, pat
, buf
, '\0');
1135 FcFormatContextDone (&c
);
1141 FcPatternFormat (FcPattern
*pat
,
1142 const FcChar8
*format
)
1145 FcChar8 buf_static
[8192 - 1024];
1148 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
1150 ret
= FcPatternFormatToBuf (pat
, format
, &buf
);
1153 return FcStrBufDone (&buf
);
1156 FcStrBufDestroy (&buf
);
1161 #define __fcformat__
1162 #include "fcaliastail.h"