]> git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
[fcformat] Add conditionals
[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_expr (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_expr (c, pat, buf, '}') &&
165 expect_char (c, '}');
166 }
167
168 static FcBool
169 maybe_interpret_subexpr (FcFormatContext *c,
170 FcPattern *pat,
171 FcStrBuf *buf)
172 {
173 return (*c->format == '{') ?
174 interpret_subexpr (c, pat, buf) :
175 FcTrue;
176 }
177
178 static FcBool
179 skip_subexpr (FcFormatContext *c);
180
181 static FcBool
182 skip_percent (FcFormatContext *c)
183 {
184 if (!expect_char (c, '%'))
185 return FcFalse;
186
187 /* skip an optional width specifier */
188 strtol ((const char *) c->format, (char **) &c->format, 10);
189
190 if (!expect_char (c, '{'))
191 return FcFalse;
192
193 while(*c->format && *c->format != '}')
194 {
195 switch (*c->format)
196 {
197 case '\\':
198 c->format++; /* skip over '\\' */
199 if (*c->format)
200 c->format++;
201 continue;
202 case '{':
203 if (!skip_subexpr (c))
204 return FcFalse;
205 continue;
206 }
207 c->format++;
208 }
209
210 return expect_char (c, '}');
211 }
212
213 static FcBool
214 skip_expr (FcFormatContext *c)
215 {
216 while(*c->format && *c->format != '}')
217 {
218 switch (*c->format)
219 {
220 case '\\':
221 c->format++; /* skip over '\\' */
222 if (*c->format)
223 c->format++;
224 continue;
225 case '%':
226 if (!skip_percent (c))
227 return FcFalse;
228 continue;
229 }
230 c->format++;
231 }
232
233 return FcTrue;
234 }
235
236 static FcBool
237 skip_subexpr (FcFormatContext *c)
238 {
239 return expect_char (c, '{') &&
240 skip_expr (c) &&
241 expect_char (c, '}');
242 }
243
244 static FcBool
245 maybe_skip_subexpr (FcFormatContext *c)
246 {
247 return (*c->format == '{') ?
248 skip_subexpr (c) :
249 FcTrue;
250 }
251
252 static FcBool
253 interpret_simple_tag (FcFormatContext *c,
254 FcPattern *pat,
255 FcStrBuf *buf)
256 {
257 FcPatternElt *e;
258 FcBool add_colon = FcFalse;
259 FcBool add_elt_name = FcFalse;
260
261 if (consume_char (c, ':'))
262 add_colon = FcTrue;
263
264 if (!read_elt_name_to_scratch (c))
265 return FcFalse;
266
267 if (consume_char (c, '='))
268 add_elt_name = FcTrue;
269
270 e = FcPatternObjectFindElt (pat,
271 FcObjectFromName ((const char *) c->scratch));
272 if (e)
273 {
274 FcValueListPtr l;
275
276 if (add_colon)
277 FcStrBufChar (buf, ':');
278 if (add_elt_name)
279 {
280 FcStrBufString (buf, c->scratch);
281 FcStrBufChar (buf, '=');
282 }
283
284 l = FcPatternEltValues(e);
285 FcNameUnparseValueList (buf, l, '\0');
286 }
287
288 return FcTrue;
289 }
290
291 static FcBool
292 interpret_filter (FcFormatContext *c,
293 FcPattern *pat,
294 FcStrBuf *buf)
295 {
296 FcObjectSet *os;
297 FcPattern *subpat;
298
299 if (!expect_char (c, '+'))
300 return FcFalse;
301
302 os = FcObjectSetCreate ();
303 if (!os)
304 return FcFalse;
305
306 do
307 {
308 if (!read_elt_name_to_scratch (c) ||
309 !FcObjectSetAdd (os, (const char *) c->scratch))
310 {
311 FcObjectSetDestroy (os);
312 return FcFalse;
313 }
314 }
315 while (consume_char (c, ','));
316
317 subpat = FcPatternFilter (pat, os);
318 FcObjectSetDestroy (os);
319
320 if (!subpat ||
321 !interpret_subexpr (c, subpat, buf))
322 return FcFalse;
323
324 FcPatternDestroy (subpat);
325 return FcTrue;
326 }
327
328 static FcBool
329 interpret_delete (FcFormatContext *c,
330 FcPattern *pat,
331 FcStrBuf *buf)
332 {
333 FcPattern *subpat;
334
335 if (!expect_char (c, '-'))
336 return FcFalse;
337
338 subpat = FcPatternDuplicate (pat);
339 if (!subpat)
340 return FcFalse;
341
342 do
343 {
344 if (!read_elt_name_to_scratch (c))
345 {
346 FcPatternDestroy (subpat);
347 return FcFalse;
348 }
349
350 FcPatternDel (subpat, (const char *) c->scratch);
351 }
352 while (consume_char (c, ','));
353
354 if (!interpret_subexpr (c, subpat, buf))
355 return FcFalse;
356
357 FcPatternDestroy (subpat);
358 return FcTrue;
359 }
360
361 static FcBool
362 interpret_conditional (FcFormatContext *c,
363 FcPattern *pat,
364 FcStrBuf *buf)
365 {
366 FcBool pass;
367
368 if (!expect_char (c, '?'))
369 return FcFalse;
370
371 pass = FcTrue;
372
373 do
374 {
375 FcBool negate;
376 FcValue v;
377
378 negate = consume_char (c, '!');
379
380 if (!read_elt_name_to_scratch (c))
381 return FcFalse;
382
383 pass = pass &&
384 (negate ^
385 FcResultMatch == FcPatternGet (pat,
386 (const char *) c->scratch,
387 0, &v));
388 }
389 while (consume_char (c, ','));
390
391 if (pass)
392 {
393 if (!interpret_subexpr (c, pat, buf) ||
394 !maybe_skip_subexpr (c))
395 return FcFalse;
396 }
397 else
398 {
399 if (!skip_subexpr (c) ||
400 !maybe_interpret_subexpr (c, pat, buf))
401 return FcFalse;
402 }
403
404 return FcTrue;
405 }
406
407 static FcBool
408 interpret_percent (FcFormatContext *c,
409 FcPattern *pat,
410 FcStrBuf *buf)
411 {
412 int width, before;
413
414 if (!expect_char (c, '%'))
415 return FcFalse;
416
417 if (consume_char (c, '%')) /* "%%" */
418 {
419 FcStrBufChar (buf, '%');
420 return FcTrue;
421 }
422
423 /* parse an optional width specifier */
424 width = strtol ((const char *) c->format, (char **) &c->format, 10);
425
426 before = buf->len;
427
428 if (!expect_char (c, '{'))
429 return FcFalse;
430
431 switch (*c->format) {
432
433 case '{':
434 /* subexpression */
435 if (!interpret_subexpr (c, pat, buf))
436 return FcFalse;
437 break;
438
439 case '+':
440 /* filtering pattern elements */
441 if (!interpret_filter (c, pat, buf))
442 return FcFalse;
443 break;
444
445 case '-':
446 /* deleting pattern elements */
447 if (!interpret_delete (c, pat, buf))
448 return FcFalse;
449 break;
450
451 case '?':
452 /* conditional on pattern elements */
453 if (!interpret_conditional (c, pat, buf))
454 return FcFalse;
455 break;
456
457 default:
458 /* simple tag */
459 if (!interpret_simple_tag (c, pat, buf))
460 return FcFalse;
461 break;
462
463 }
464
465 /* handle filters, if any */
466 /* XXX */
467
468 /* align to width */
469 if (!buf->failed)
470 {
471 int after, len;
472
473 after = buf->len;
474
475 len = after - before;
476
477 if (len < -width)
478 {
479 /* left align */
480 while (len++ < -width)
481 FcStrBufChar (buf, ' ');
482 }
483 else if (len < width)
484 {
485 /* right align */
486 while (len++ < width)
487 FcStrBufChar (buf, ' ');
488 len = after - before;
489 memmove (buf->buf + buf->len - len,
490 buf->buf + buf->len - width,
491 len);
492 memset (buf->buf + buf->len - width,
493 ' ',
494 width - len);
495 }
496 }
497
498 return expect_char (c, '}');
499 }
500
501 static char escaped_char(const char ch)
502 {
503 switch (ch) {
504 case 'a': return '\a';
505 case 'b': return '\b';
506 case 'f': return '\f';
507 case 'n': return '\n';
508 case 'r': return '\r';
509 case 't': return '\t';
510 case 'v': return '\v';
511 default: return ch;
512 }
513 }
514
515 static FcBool
516 interpret_expr (FcFormatContext *c,
517 FcPattern *pat,
518 FcStrBuf *buf,
519 FcChar8 term)
520 {
521 while (*c->format && *c->format != term)
522 {
523 switch (*c->format)
524 {
525 case '\\':
526 c->format++; /* skip over '\\' */
527 if (*c->format)
528 FcStrBufChar (buf, escaped_char (*c->format++));
529 continue;
530 case '%':
531 if (!interpret_percent (c, pat, buf))
532 return FcFalse;
533 continue;
534 }
535 FcStrBufChar (buf, *c->format++);
536 }
537 return FcTrue;
538 }
539
540 FcChar8 *
541 FcPatternFormat (FcPattern *pat, const FcChar8 *format)
542 {
543 FcStrBuf buf;
544 FcFormatContext c;
545 FcBool ret;
546
547 FcStrBufInit (&buf, 0, 0);
548 if (!FcFormatContextInit (&c, format))
549 return NULL;
550
551 ret = interpret_expr (&c, pat, &buf, '\0');
552 if (buf.failed)
553 ret = FcFalse;
554
555 FcFormatContextDone (&c);
556 if (ret)
557 return FcStrBufDone (&buf);
558 else
559 {
560 FcStrBufDestroy (&buf);
561 return NULL;
562 }
563 }
564
565 #define __fcformat__
566 #include "fcaliastail.h"
567 #undef __fcformat__