]> git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
[fcformat] Add element filtering and deletion
[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 FcBool
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 return c->scratch != NULL;
60 }
61
62 static void
63 FcFormatContextDone (FcFormatContext *c)
64 {
65 if (c)
66 {
67 free (c->scratch);
68 }
69 }
70
71 static FcBool
72 consume_char (FcFormatContext *c,
73 FcChar8 term)
74 {
75 if (*c->format != term)
76 return FcFalse;
77
78 c->format++;
79 return FcTrue;
80 }
81
82 static FcBool
83 expect_char (FcFormatContext *c,
84 FcChar8 term)
85 {
86 FcBool res = consume_char (c, term);
87 if (!res)
88 {
89 if (c->format == c->format_orig + c->format_len)
90 message ("format ended while expecting '%c'",
91 term);
92 else
93 message ("expected '%c' at %d",
94 term, c->format - c->format_orig + 1);
95 }
96 return res;
97 }
98
99 static FcBool
100 FcCharIsPunct (const FcChar8 c)
101 {
102 if (c < '0')
103 return FcTrue;
104 if (c <= '9')
105 return FcFalse;
106 if (c < 'A')
107 return FcTrue;
108 if (c <= 'Z')
109 return FcFalse;
110 if (c < 'a')
111 return FcTrue;
112 if (c <= 'z')
113 return FcFalse;
114 if (c <= '~')
115 return FcTrue;
116 return FcFalse;
117 }
118
119 static FcBool
120 read_elt_name_to_scratch (FcFormatContext *c)
121 {
122 FcChar8 *p;
123
124 p = c->scratch;
125
126 while (*c->format)
127 {
128 if (*c->format == '\\')
129 {
130 c->format++;
131 if (*c->format)
132 c->format++;
133 continue;
134 }
135 else if (FcCharIsPunct (*c->format))
136 break;
137
138 *p++ = *c->format++;
139 }
140 *p = '\0';
141
142 if (p == c->scratch)
143 {
144 message ("expected element name at %d",
145 c->format - c->format_orig + 1);
146 return FcFalse;
147 }
148
149 return FcTrue;
150 }
151
152 static FcBool
153 interpret (FcFormatContext *c,
154 FcPattern *pat,
155 FcStrBuf *buf,
156 FcChar8 term);
157
158 static FcBool
159 interpret_subexpr (FcFormatContext *c,
160 FcPattern *pat,
161 FcStrBuf *buf)
162 {
163 return expect_char (c, '{') &&
164 interpret (c, pat, buf, '}') &&
165 expect_char (c, '}');
166 }
167
168 static FcBool
169 interpret_simple_tag (FcFormatContext *c,
170 FcPattern *pat,
171 FcStrBuf *buf)
172 {
173 FcPatternElt *e;
174 FcBool add_colon = FcFalse;
175 FcBool add_elt_name = FcFalse;
176
177 if (consume_char (c, ':'))
178 add_colon = FcTrue;
179
180 if (!read_elt_name_to_scratch (c))
181 return FcFalse;
182
183 if (consume_char (c, '='))
184 add_elt_name = FcTrue;
185
186 e = FcPatternObjectFindElt (pat,
187 FcObjectFromName ((const char *) c->scratch));
188 if (e)
189 {
190 FcValueListPtr l;
191
192 if (add_colon)
193 FcStrBufChar (buf, ':');
194 if (add_elt_name)
195 {
196 FcStrBufString (buf, c->scratch);
197 FcStrBufChar (buf, '=');
198 }
199
200 l = FcPatternEltValues(e);
201 FcNameUnparseValueList (buf, l, '\0');
202 }
203
204 return FcTrue;
205 }
206
207 static FcBool
208 interpret_filter (FcFormatContext *c,
209 FcPattern *pat,
210 FcStrBuf *buf)
211 {
212 FcObjectSet *os;
213 FcPattern *subpat;
214
215 if (!expect_char (c, '+'))
216 return FcFalse;
217
218 os = FcObjectSetCreate ();
219 if (!os)
220 return FcFalse;
221
222 do
223 {
224 if (!read_elt_name_to_scratch (c) ||
225 !FcObjectSetAdd (os, (const char *) c->scratch))
226 {
227 FcObjectSetDestroy (os);
228 return FcFalse;
229 }
230 }
231 while (consume_char (c, ','));
232
233 subpat = FcPatternFilter (pat, os);
234 FcObjectSetDestroy (os);
235
236 if (!subpat ||
237 !interpret_subexpr (c, subpat, buf))
238 return FcFalse;
239
240 FcPatternDestroy (subpat);
241 return FcTrue;
242 }
243
244 static FcBool
245 interpret_delete (FcFormatContext *c,
246 FcPattern *pat,
247 FcStrBuf *buf)
248 {
249 FcPattern *subpat;
250
251 if (!expect_char (c, '-'))
252 return FcFalse;
253
254 subpat = FcPatternDuplicate (pat);
255 if (!subpat)
256 return FcFalse;
257
258 do
259 {
260 if (!read_elt_name_to_scratch (c))
261 {
262 FcPatternDestroy (subpat);
263 return FcFalse;
264 }
265
266 FcPatternDel (subpat, (const char *) c->scratch);
267 }
268 while (consume_char (c, ','));
269
270 if (!interpret_subexpr (c, subpat, buf))
271 return FcFalse;
272
273 FcPatternDestroy (subpat);
274 return FcTrue;
275 }
276
277 static FcBool
278 interpret_percent (FcFormatContext *c,
279 FcPattern *pat,
280 FcStrBuf *buf)
281 {
282 int width, before;
283
284 if (!expect_char (c, '%'))
285 return FcFalse;
286
287 if (consume_char (c, '%')) /* "%%" */
288 {
289 FcStrBufChar (buf, '%');
290 return FcTrue;
291 }
292
293 /* parse an optional width specifier */
294 width = strtol ((const char *) c->format, (char **) &c->format, 10);
295
296 before = buf->len;
297
298 if (!expect_char (c, '{'))
299 return FcFalse;
300
301 switch (*c->format) {
302
303 case '{':
304 /* subexpression */
305 if (!interpret_subexpr (c, pat, buf))
306 return FcFalse;
307 break;
308
309 case '+':
310 /* filtering pattern elements */
311 if (!interpret_filter (c, pat, buf))
312 return FcFalse;
313 break;
314
315 case '-':
316 /* deleting pattern elements */
317 if (!interpret_delete (c, pat, buf))
318 return FcFalse;
319 break;
320
321 default:
322 /* simple tag */
323 if (!interpret_simple_tag (c, pat, buf))
324 return FcFalse;
325 break;
326
327 }
328
329 /* handle filters, if any */
330 /* XXX */
331
332 /* align to width */
333 if (!buf->failed)
334 {
335 int after, len;
336
337 after = buf->len;
338
339 len = after - before;
340
341 if (len < -width)
342 {
343 /* left align */
344 while (len++ < -width)
345 FcStrBufChar (buf, ' ');
346 }
347 else if (len < width)
348 {
349 /* right align */
350 while (len++ < width)
351 FcStrBufChar (buf, ' ');
352 len = after - before;
353 memmove (buf->buf + buf->len - len,
354 buf->buf + buf->len - width,
355 len);
356 memset (buf->buf + buf->len - width,
357 ' ',
358 width - len);
359 }
360 }
361
362 return expect_char (c, '}');
363 }
364
365 static char escaped_char(const char ch)
366 {
367 switch (ch) {
368 case 'a': return '\a';
369 case 'b': return '\b';
370 case 'f': return '\f';
371 case 'n': return '\n';
372 case 'r': return '\r';
373 case 't': return '\t';
374 case 'v': return '\v';
375 default: return ch;
376 }
377 }
378
379 static FcBool
380 interpret (FcFormatContext *c,
381 FcPattern *pat,
382 FcStrBuf *buf,
383 FcChar8 term)
384 {
385 for (; *c->format && *c->format != term;)
386 {
387 switch (*c->format)
388 {
389 case '\\':
390 c->format++; /* skip over '\\' */
391 if (*c->format)
392 FcStrBufChar (buf, escaped_char (*c->format++));
393 continue;
394 case '%':
395 if (!interpret_percent (c, pat, buf))
396 return FcFalse;
397 continue;
398 }
399 FcStrBufChar (buf, *c->format++);
400 }
401 return FcTrue;
402 }
403
404 FcChar8 *
405 FcPatternFormat (FcPattern *pat, const FcChar8 *format)
406 {
407 FcStrBuf buf;
408 FcFormatContext c;
409 FcBool ret;
410
411 FcStrBufInit (&buf, 0, 0);
412 if (!FcFormatContextInit (&c, format))
413 return NULL;
414
415 ret = interpret (&c, pat, &buf, '\0');
416 if (buf.failed)
417 ret = FcFalse;
418
419 FcFormatContextDone (&c);
420 if (ret)
421 return FcStrBufDone (&buf);
422 else
423 {
424 FcStrBufDestroy (&buf);
425 return NULL;
426 }
427 }
428
429 #define __fcformat__
430 #include "fcaliastail.h"
431 #undef __fcformat__