]> git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
[fcformat] Add support for subexpressions
[fontconfig.git] / src / fcformat.c
1 /*
2 * Copyright © 2008 Red Hat, Inc.
3 *
4 * Red Hat Author(s): Behdad Esfahbod
5 *
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.
15 *
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.
23 */
24
25 #include "fcint.h"
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdarg.h>
29
30
31 static void
32 message (const char *fmt, ...)
33 {
34 va_list args;
35 va_start (args, fmt);
36 fprintf (stderr, "Fontconfig: Pattern format error:");
37 vfprintf (stderr, fmt, args);
38 fprintf (stderr, ".\n");
39 va_end (args);
40 }
41
42
43 typedef struct _FcFormatContext
44 {
45 const FcChar8 *format_orig;
46 const FcChar8 *format;
47 int format_len;
48 FcChar8 *scratch;
49 } FcFormatContext;
50
51 static void
52 FcFormatContextInit (FcFormatContext *c,
53 const FcChar8 *format)
54 {
55 c->format_orig = c->format = format;
56 c->format_len = strlen ((const char *) format);
57 c->scratch = malloc (c->format_len + 1);
58 }
59
60 static void
61 FcFormatContextDone (FcFormatContext *c)
62 {
63 if (c)
64 {
65 free (c->scratch);
66 }
67 }
68
69 static FcBool
70 consume_char (FcFormatContext *c,
71 FcChar8 term)
72 {
73 if (*c->format != term)
74 return FcFalse;
75
76 c->format++;
77 return FcTrue;
78 }
79
80 static FcBool
81 expect_char (FcFormatContext *c,
82 FcChar8 term)
83 {
84 FcBool res = consume_char (c, term);
85 if (!res)
86 {
87 if (c->format == c->format_orig + c->format_len)
88 message ("format ended while expecting '%c'",
89 term);
90 else
91 message ("expected '%c' at %d",
92 term, c->format - c->format_orig + 1);
93 }
94 return res;
95 }
96
97 static FcBool
98 FcCharIsPunct (const FcChar8 c)
99 {
100 if (c < '0')
101 return FcTrue;
102 if (c <= '9')
103 return FcFalse;
104 if (c < 'A')
105 return FcTrue;
106 if (c <= 'Z')
107 return FcFalse;
108 if (c < 'a')
109 return FcTrue;
110 if (c <= 'z')
111 return FcFalse;
112 if (c <= '~')
113 return FcTrue;
114 return FcFalse;
115 }
116
117 static FcBool
118 read_elt_name_to_scratch (FcFormatContext *c)
119 {
120 FcChar8 *p;
121
122 p = c->scratch;
123
124 while (*c->format)
125 {
126 if (*c->format == '\\')
127 {
128 c->format++;
129 if (*c->format)
130 c->format++;
131 continue;
132 }
133 else if (FcCharIsPunct (*c->format))
134 break;
135
136 *p++ = *c->format++;
137 }
138 *p = '\0';
139
140 if (p == c->scratch)
141 {
142 message ("expected element name at %d",
143 c->format - c->format_orig + 1);
144 return FcFalse;
145 }
146
147 return FcTrue;
148 }
149
150 static void
151 interpret (FcFormatContext *c,
152 FcPattern *pat,
153 FcStrBuf *buf,
154 FcChar8 term);
155
156 static void
157 interpret_percent (FcFormatContext *c,
158 FcPattern *pat,
159 FcStrBuf *buf)
160 {
161 int width, before;
162 FcChar8 *p;
163 FcPatternElt *e;
164
165 FcPattern *subpat = pat;
166 FcBool add_colon = FcFalse;
167 FcBool add_elt_name = FcFalse;
168
169 if (!expect_char (c, '%'))
170 return;
171
172 if (consume_char (c, '%')) /* "%%" */
173 {
174 FcStrBufChar (buf, '%');
175 return;
176 }
177
178 /* parse an optional width specifier */
179 width = strtol ((const char *) c->format, (char **) &c->format, 10);
180
181 before = buf->len;
182
183 if (!expect_char (c, '{'))
184 goto bail;
185
186 if (consume_char (c, '{'))
187 {
188 /* it's just a subexpression. no tag involved */
189 interpret (c, pat, buf, '}');
190 expect_char (c, '}');
191 goto filter;
192 }
193
194 switch (*c->format) {
195 case ':':
196 add_colon = FcTrue;
197 consume_char (c, ':');
198 break;
199 }
200
201 parse_tag:
202 if (!read_elt_name_to_scratch (c))
203 goto bail;
204
205 if (consume_char (c, '='))
206 add_elt_name = FcTrue;
207
208 e = FcPatternObjectFindElt (pat,
209 FcObjectFromName ((const char *) c->scratch));
210 if (e)
211 {
212 FcValueListPtr l;
213
214 if (add_colon)
215 FcStrBufChar (buf, ':');
216 if (add_elt_name)
217 {
218 FcStrBufString (buf, c->scratch);
219 FcStrBufChar (buf, '=');
220 }
221
222 l = FcPatternEltValues(e);
223 FcNameUnparseValueList (buf, l, '\0');
224 }
225
226 filter:
227 /* handle filters, if any */
228 /* XXX */
229
230 /* align to width */
231 if (!buf->failed)
232 {
233 int after, len;
234
235 after = buf->len;
236
237 len = after - before;
238
239 if (len < -width)
240 {
241 /* left align */
242 while (len++ < -width)
243 FcStrBufChar (buf, ' ');
244 }
245 else if (len < width)
246 {
247 /* right align */
248 while (len++ < width)
249 FcStrBufChar (buf, ' ');
250 len = after - before;
251 memmove (buf->buf + buf->len - len,
252 buf->buf + buf->len - width,
253 len);
254 memset (buf->buf + buf->len - width,
255 ' ',
256 width - len);
257 }
258 }
259
260 expect_char (c, '}');
261
262 bail:
263 if (subpat != pat)
264 FcPatternDestroy (subpat);
265 }
266
267 static char escaped_char(const char ch)
268 {
269 switch (ch) {
270 case 'a': return '\a';
271 case 'b': return '\b';
272 case 'f': return '\f';
273 case 'n': return '\n';
274 case 'r': return '\r';
275 case 't': return '\t';
276 case 'v': return '\v';
277 default: return ch;
278 }
279 }
280
281 static void
282 interpret (FcFormatContext *c,
283 FcPattern *pat,
284 FcStrBuf *buf,
285 FcChar8 term)
286 {
287 for (; *c->format && *c->format != term;)
288 {
289 switch (*c->format)
290 {
291 case '\\':
292 c->format++; /* skip over '\\' */
293 if (*c->format)
294 FcStrBufChar (buf, escaped_char (*c->format++));
295 continue;
296 case '%':
297 interpret_percent (c, pat, buf);
298 continue;
299 }
300 FcStrBufChar (buf, *c->format++);
301 }
302 }
303
304 FcChar8 *
305 FcPatternFormat (FcPattern *pat, const FcChar8 *format)
306 {
307 FcStrBuf buf;
308 FcFormatContext c;
309
310 FcStrBufInit (&buf, 0, 0);
311 FcFormatContextInit (&c, format);
312
313 interpret (&c, pat, &buf, '\0');
314
315 FcFormatContextDone (&c);
316 return FcStrBufDone (&buf);
317 }
318
319 #define __fcformat__
320 #include "fcaliastail.h"
321 #undef __fcformat__