]> git.wh0rd.org - fontconfig.git/blame - src/fcformat.c
[fcformat] Add conditionals
[fontconfig.git] / src / fcformat.c
CommitLineData
0c93b91d
BE
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
31static void
32message (const char *fmt, ...)
33{
34 va_list args;
35 va_start (args, fmt);
8c31a243 36 fprintf (stderr, "Fontconfig: Pattern format error: ");
0c93b91d 37 vfprintf (stderr, fmt, args);
d6506ff6 38 fprintf (stderr, ".\n");
0c93b91d
BE
39 va_end (args);
40}
41
42
27b3e2dd 43typedef struct _FcFormatContext
0c93b91d 44{
27b3e2dd
BE
45 const FcChar8 *format_orig;
46 const FcChar8 *format;
47 int format_len;
48 FcChar8 *scratch;
49} FcFormatContext;
c493c3b7 50
8c31a243 51static FcBool
27b3e2dd
BE
52FcFormatContextInit (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);
8c31a243
BE
58
59 return c->scratch != NULL;
27b3e2dd 60}
c493c3b7 61
27b3e2dd
BE
62static void
63FcFormatContextDone (FcFormatContext *c)
64{
65 if (c)
66 {
67 free (c->scratch);
68 }
69}
c493c3b7 70
27b3e2dd
BE
71static FcBool
72consume_char (FcFormatContext *c,
73 FcChar8 term)
74{
75 if (*c->format != term)
d6506ff6
BE
76 return FcFalse;
77
78 c->format++;
79 return FcTrue;
80}
81
82static FcBool
83expect_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
99static FcBool
100FcCharIsPunct (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
119static FcBool
120read_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)
0c93b91d 143 {
d6506ff6
BE
144 message ("expected element name at %d",
145 c->format - c->format_orig + 1);
27b3e2dd
BE
146 return FcFalse;
147 }
0c93b91d 148
27b3e2dd
BE
149 return FcTrue;
150}
0c93b91d 151
8c31a243 152static FcBool
7717b25f
BE
153interpret_expr (FcFormatContext *c,
154 FcPattern *pat,
155 FcStrBuf *buf,
156 FcChar8 term);
d6506ff6 157
8c31a243
BE
158static FcBool
159interpret_subexpr (FcFormatContext *c,
27b3e2dd
BE
160 FcPattern *pat,
161 FcStrBuf *buf)
162{
8c31a243 163 return expect_char (c, '{') &&
7717b25f 164 interpret_expr (c, pat, buf, '}') &&
8c31a243
BE
165 expect_char (c, '}');
166}
0c93b91d 167
7717b25f
BE
168static FcBool
169maybe_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
178static FcBool
179skip_subexpr (FcFormatContext *c);
180
181static FcBool
182skip_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
213static FcBool
214skip_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
236static FcBool
237skip_subexpr (FcFormatContext *c)
238{
239 return expect_char (c, '{') &&
240 skip_expr (c) &&
241 expect_char (c, '}');
242}
243
244static FcBool
245maybe_skip_subexpr (FcFormatContext *c)
246{
247 return (*c->format == '{') ?
248 skip_subexpr (c) :
249 FcTrue;
250}
251
8c31a243
BE
252static FcBool
253interpret_simple_tag (FcFormatContext *c,
254 FcPattern *pat,
255 FcStrBuf *buf)
256{
257 FcPatternElt *e;
d6506ff6
BE
258 FcBool add_colon = FcFalse;
259 FcBool add_elt_name = FcFalse;
260
8c31a243 261 if (consume_char (c, ':'))
d6506ff6 262 add_colon = FcTrue;
d6506ff6 263
d6506ff6 264 if (!read_elt_name_to_scratch (c))
8c31a243 265 return FcFalse;
d6506ff6
BE
266
267 if (consume_char (c, '='))
268 add_elt_name = FcTrue;
269
270 e = FcPatternObjectFindElt (pat,
271 FcObjectFromName ((const char *) c->scratch));
27b3e2dd
BE
272 if (e)
273 {
274 FcValueListPtr l;
d6506ff6
BE
275
276 if (add_colon)
277 FcStrBufChar (buf, ':');
278 if (add_elt_name)
279 {
280 FcStrBufString (buf, c->scratch);
281 FcStrBufChar (buf, '=');
282 }
283
27b3e2dd
BE
284 l = FcPatternEltValues(e);
285 FcNameUnparseValueList (buf, l, '\0');
c493c3b7
BE
286 }
287
8c31a243
BE
288 return FcTrue;
289}
290
291static FcBool
292interpret_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
328static FcBool
329interpret_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
7717b25f
BE
361static FcBool
362interpret_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
8c31a243
BE
407static FcBool
408interpret_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
7717b25f
BE
451 case '?':
452 /* conditional on pattern elements */
453 if (!interpret_conditional (c, pat, buf))
454 return FcFalse;
455 break;
456
8c31a243
BE
457 default:
458 /* simple tag */
459 if (!interpret_simple_tag (c, pat, buf))
460 return FcFalse;
461 break;
462
463 }
464
27b3e2dd
BE
465 /* handle filters, if any */
466 /* XXX */
467
c493c3b7
BE
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 }
0c93b91d 496 }
c493c3b7 497
8c31a243 498 return expect_char (c, '}');
0c93b91d
BE
499}
500
501static 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
8c31a243 515static FcBool
7717b25f
BE
516interpret_expr (FcFormatContext *c,
517 FcPattern *pat,
518 FcStrBuf *buf,
519 FcChar8 term)
0c93b91d 520{
7717b25f 521 while (*c->format && *c->format != term)
0c93b91d 522 {
27b3e2dd 523 switch (*c->format)
0c93b91d
BE
524 {
525 case '\\':
27b3e2dd
BE
526 c->format++; /* skip over '\\' */
527 if (*c->format)
528 FcStrBufChar (buf, escaped_char (*c->format++));
0c93b91d
BE
529 continue;
530 case '%':
8c31a243
BE
531 if (!interpret_percent (c, pat, buf))
532 return FcFalse;
0c93b91d
BE
533 continue;
534 }
27b3e2dd 535 FcStrBufChar (buf, *c->format++);
0c93b91d 536 }
8c31a243 537 return FcTrue;
0c93b91d
BE
538}
539
540FcChar8 *
541FcPatternFormat (FcPattern *pat, const FcChar8 *format)
542{
0c93b91d 543 FcStrBuf buf;
27b3e2dd 544 FcFormatContext c;
8c31a243 545 FcBool ret;
0c93b91d
BE
546
547 FcStrBufInit (&buf, 0, 0);
8c31a243
BE
548 if (!FcFormatContextInit (&c, format))
549 return NULL;
0c93b91d 550
7717b25f 551 ret = interpret_expr (&c, pat, &buf, '\0');
8c31a243
BE
552 if (buf.failed)
553 ret = FcFalse;
0c93b91d 554
27b3e2dd 555 FcFormatContextDone (&c);
8c31a243
BE
556 if (ret)
557 return FcStrBufDone (&buf);
558 else
559 {
560 FcStrBufDestroy (&buf);
561 return NULL;
562 }
0c93b91d
BE
563}
564
565#define __fcformat__
566#include "fcaliastail.h"
567#undef __fcformat__