]> git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
[fcformat] Add simple converters
[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 /*
32 * Some ideas for future syntax extensions:
33 *
34 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
35 * - allow indexing simple tags using '%{elt[idx]}'
36 * - conditional/filtering/deletion on binding (using '(w)'/'(s)' notation)
37 */
38
39 static void
40 message (const char *fmt, ...)
41 {
42 va_list args;
43 va_start (args, fmt);
44 fprintf (stderr, "Fontconfig: Pattern format error: ");
45 vfprintf (stderr, fmt, args);
46 fprintf (stderr, ".\n");
47 va_end (args);
48 }
49
50
51 typedef struct _FcFormatContext
52 {
53 const FcChar8 *format_orig;
54 const FcChar8 *format;
55 int format_len;
56 FcChar8 *word;
57 } FcFormatContext;
58
59 static FcBool
60 FcFormatContextInit (FcFormatContext *c,
61 const FcChar8 *format)
62 {
63 c->format_orig = c->format = format;
64 c->format_len = strlen ((const char *) format);
65 c->word = malloc (c->format_len + 1);
66
67 return c->word != NULL;
68 }
69
70 static void
71 FcFormatContextDone (FcFormatContext *c)
72 {
73 if (c)
74 {
75 free (c->word);
76 }
77 }
78
79 static FcBool
80 consume_char (FcFormatContext *c,
81 FcChar8 term)
82 {
83 if (*c->format != term)
84 return FcFalse;
85
86 c->format++;
87 return FcTrue;
88 }
89
90 static FcBool
91 expect_char (FcFormatContext *c,
92 FcChar8 term)
93 {
94 FcBool res = consume_char (c, term);
95 if (!res)
96 {
97 if (c->format == c->format_orig + c->format_len)
98 message ("format ended while expecting '%c'",
99 term);
100 else
101 message ("expected '%c' at %d",
102 term, c->format - c->format_orig + 1);
103 }
104 return res;
105 }
106
107 static FcBool
108 FcCharIsPunct (const FcChar8 c)
109 {
110 if (c < '0')
111 return FcTrue;
112 if (c <= '9')
113 return FcFalse;
114 if (c < 'A')
115 return FcTrue;
116 if (c <= 'Z')
117 return FcFalse;
118 if (c < 'a')
119 return FcTrue;
120 if (c <= 'z')
121 return FcFalse;
122 if (c <= '~')
123 return FcTrue;
124 return FcFalse;
125 }
126
127 static FcBool
128 read_word (FcFormatContext *c)
129 {
130 FcChar8 *p;
131
132 p = c->word;
133
134 while (*c->format)
135 {
136 if (*c->format == '\\')
137 {
138 c->format++;
139 if (*c->format)
140 c->format++;
141 continue;
142 }
143 else if (FcCharIsPunct (*c->format))
144 break;
145
146 *p++ = *c->format++;
147 }
148 *p = '\0';
149
150 if (p == c->word)
151 {
152 message ("expected element name at %d",
153 c->format - c->format_orig + 1);
154 return FcFalse;
155 }
156
157 return FcTrue;
158 }
159
160 static FcBool
161 interpret_expr (FcFormatContext *c,
162 FcPattern *pat,
163 FcStrBuf *buf,
164 FcChar8 term);
165
166 static FcBool
167 interpret_subexpr (FcFormatContext *c,
168 FcPattern *pat,
169 FcStrBuf *buf)
170 {
171 return expect_char (c, '{') &&
172 interpret_expr (c, pat, buf, '}') &&
173 expect_char (c, '}');
174 }
175
176 static FcBool
177 maybe_interpret_subexpr (FcFormatContext *c,
178 FcPattern *pat,
179 FcStrBuf *buf)
180 {
181 return (*c->format == '{') ?
182 interpret_subexpr (c, pat, buf) :
183 FcTrue;
184 }
185
186 static FcBool
187 skip_subexpr (FcFormatContext *c);
188
189 static FcBool
190 skip_percent (FcFormatContext *c)
191 {
192 int width;
193
194 if (!expect_char (c, '%'))
195 return FcFalse;
196
197 /* skip an optional width specifier */
198 width = strtol ((const char *) c->format, (char **) &c->format, 10);
199
200 if (!expect_char (c, '{'))
201 return FcFalse;
202
203 while(*c->format && *c->format != '}')
204 {
205 switch (*c->format)
206 {
207 case '\\':
208 c->format++; /* skip over '\\' */
209 if (*c->format)
210 c->format++;
211 continue;
212 case '{':
213 if (!skip_subexpr (c))
214 return FcFalse;
215 continue;
216 }
217 c->format++;
218 }
219
220 return expect_char (c, '}');
221 }
222
223 static FcBool
224 skip_expr (FcFormatContext *c)
225 {
226 while(*c->format && *c->format != '}')
227 {
228 switch (*c->format)
229 {
230 case '\\':
231 c->format++; /* skip over '\\' */
232 if (*c->format)
233 c->format++;
234 continue;
235 case '%':
236 if (!skip_percent (c))
237 return FcFalse;
238 continue;
239 }
240 c->format++;
241 }
242
243 return FcTrue;
244 }
245
246 static FcBool
247 skip_subexpr (FcFormatContext *c)
248 {
249 return expect_char (c, '{') &&
250 skip_expr (c) &&
251 expect_char (c, '}');
252 }
253
254 static FcBool
255 maybe_skip_subexpr (FcFormatContext *c)
256 {
257 return (*c->format == '{') ?
258 skip_subexpr (c) :
259 FcTrue;
260 }
261
262 static FcBool
263 interpret_simple (FcFormatContext *c,
264 FcPattern *pat,
265 FcStrBuf *buf)
266 {
267 FcPatternElt *e;
268 FcBool add_colon = FcFalse;
269 FcBool add_elt_name = FcFalse;
270
271 if (consume_char (c, ':'))
272 add_colon = FcTrue;
273
274 if (!read_word (c))
275 return FcFalse;
276
277 if (consume_char (c, '='))
278 add_elt_name = FcTrue;
279
280 e = FcPatternObjectFindElt (pat,
281 FcObjectFromName ((const char *) c->word));
282 if (e)
283 {
284 FcValueListPtr l;
285
286 if (add_colon)
287 FcStrBufChar (buf, ':');
288 if (add_elt_name)
289 {
290 FcStrBufString (buf, c->word);
291 FcStrBufChar (buf, '=');
292 }
293
294 l = FcPatternEltValues(e);
295 FcNameUnparseValueList (buf, l, '\0');
296 }
297
298 return FcTrue;
299 }
300
301 static FcBool
302 interpret_filter (FcFormatContext *c,
303 FcPattern *pat,
304 FcStrBuf *buf)
305 {
306 FcObjectSet *os;
307 FcPattern *subpat;
308
309 if (!expect_char (c, '+'))
310 return FcFalse;
311
312 os = FcObjectSetCreate ();
313 if (!os)
314 return FcFalse;
315
316 do
317 {
318 if (!read_word (c) ||
319 !FcObjectSetAdd (os, (const char *) c->word))
320 {
321 FcObjectSetDestroy (os);
322 return FcFalse;
323 }
324 }
325 while (consume_char (c, ','));
326
327 subpat = FcPatternFilter (pat, os);
328 FcObjectSetDestroy (os);
329
330 if (!subpat ||
331 !interpret_subexpr (c, subpat, buf))
332 return FcFalse;
333
334 FcPatternDestroy (subpat);
335 return FcTrue;
336 }
337
338 static FcBool
339 interpret_delete (FcFormatContext *c,
340 FcPattern *pat,
341 FcStrBuf *buf)
342 {
343 FcPattern *subpat;
344
345 if (!expect_char (c, '-'))
346 return FcFalse;
347
348 subpat = FcPatternDuplicate (pat);
349 if (!subpat)
350 return FcFalse;
351
352 do
353 {
354 if (!read_word (c))
355 {
356 FcPatternDestroy (subpat);
357 return FcFalse;
358 }
359
360 FcPatternDel (subpat, (const char *) c->word);
361 }
362 while (consume_char (c, ','));
363
364 if (!interpret_subexpr (c, subpat, buf))
365 return FcFalse;
366
367 FcPatternDestroy (subpat);
368 return FcTrue;
369 }
370
371 static FcBool
372 interpret_cond (FcFormatContext *c,
373 FcPattern *pat,
374 FcStrBuf *buf)
375 {
376 FcBool pass;
377
378 if (!expect_char (c, '?'))
379 return FcFalse;
380
381 pass = FcTrue;
382
383 do
384 {
385 FcBool negate;
386 FcValue v;
387
388 negate = consume_char (c, '!');
389
390 if (!read_word (c))
391 return FcFalse;
392
393 pass = pass &&
394 (negate ^
395 (FcResultMatch == FcPatternGet (pat,
396 (const char *) c->word,
397 0, &v)));
398 }
399 while (consume_char (c, ','));
400
401 if (pass)
402 {
403 if (!interpret_subexpr (c, pat, buf) ||
404 !maybe_skip_subexpr (c))
405 return FcFalse;
406 }
407 else
408 {
409 if (!skip_subexpr (c) ||
410 !maybe_interpret_subexpr (c, pat, buf))
411 return FcFalse;
412 }
413
414 return FcTrue;
415 }
416
417 static FcChar8 *
418 convert (FcFormatContext *c,
419 const FcChar8 *str)
420 {
421 if (!read_word (c))
422 return NULL;
423 else if (0 == strcmp ((const char *) c->word, "downcase"))
424 return FcStrDowncase (str);
425 else if (0 == strcmp ((const char *) c->word, "basename"))
426 return FcStrBasename (str);
427 else if (0 == strcmp ((const char *) c->word, "dirname"))
428 return FcStrDirname (str);
429
430 message ("unknown converter \"%s\"",
431 c->word);
432 return NULL;
433 }
434
435 static FcBool
436 maybe_interpret_converts (FcFormatContext *c,
437 FcStrBuf *buf,
438 int start)
439 {
440 while (consume_char (c, '|'))
441 {
442 const FcChar8 *str;
443 FcChar8 *new_str;
444
445 /* nul-terminate the buffer */
446 FcStrBufChar (buf, '\0');
447 if (buf->failed)
448 return FcFalse;
449 str = buf->buf + start;
450
451 if (!(new_str = convert (c, str)))
452 return FcFalse;
453
454 /* replace in the buffer */
455 buf->len = start;
456 FcStrBufString (buf, new_str);
457 free (new_str);
458 }
459
460 return FcTrue;
461 }
462
463 static FcBool
464 align_to_width (FcStrBuf *buf,
465 int start,
466 int width)
467 {
468 int len;
469
470 if (buf->failed)
471 return FcFalse;
472
473 len = buf->len - start;
474 if (len < -width)
475 {
476 /* left align */
477 while (len++ < -width)
478 FcStrBufChar (buf, ' ');
479 }
480 else if (len < width)
481 {
482 int old_len;
483 old_len = len;
484 /* right align */
485 while (len++ < width)
486 FcStrBufChar (buf, ' ');
487 if (buf->failed)
488 return FcFalse;
489 len = old_len;
490 memmove (buf->buf + buf->len - len,
491 buf->buf + buf->len - width,
492 len);
493 memset (buf->buf + buf->len - width,
494 ' ',
495 width - len);
496 }
497
498 return !buf->failed;
499 }
500 static FcBool
501 interpret_percent (FcFormatContext *c,
502 FcPattern *pat,
503 FcStrBuf *buf)
504 {
505 int width, start;
506 FcBool ret;
507
508 if (!expect_char (c, '%'))
509 return FcFalse;
510
511 if (consume_char (c, '%')) /* "%%" */
512 {
513 FcStrBufChar (buf, '%');
514 return FcTrue;
515 }
516
517 /* parse an optional width specifier */
518 width = strtol ((const char *) c->format, (char **) &c->format, 10);
519
520 if (!expect_char (c, '{'))
521 return FcFalse;
522
523 start = buf->len;
524
525 switch (*c->format) {
526 case '{': ret = interpret_subexpr (c, pat, buf); break;
527 case '+': ret = interpret_filter (c, pat, buf); break;
528 case '-': ret = interpret_delete (c, pat, buf); break;
529 case '?': ret = interpret_cond (c, pat, buf); break;
530 default: ret = interpret_simple (c, pat, buf); break;
531 }
532
533 return ret &&
534 maybe_interpret_converts (c, buf, start) &&
535 align_to_width (buf, start, width) &&
536 expect_char (c, '}');
537 }
538
539 static char escaped_char(const char ch)
540 {
541 switch (ch) {
542 case 'a': return '\a';
543 case 'b': return '\b';
544 case 'f': return '\f';
545 case 'n': return '\n';
546 case 'r': return '\r';
547 case 't': return '\t';
548 case 'v': return '\v';
549 default: return ch;
550 }
551 }
552
553 static FcBool
554 interpret_expr (FcFormatContext *c,
555 FcPattern *pat,
556 FcStrBuf *buf,
557 FcChar8 term)
558 {
559 while (*c->format && *c->format != term)
560 {
561 switch (*c->format)
562 {
563 case '\\':
564 c->format++; /* skip over '\\' */
565 if (*c->format)
566 FcStrBufChar (buf, escaped_char (*c->format++));
567 continue;
568 case '%':
569 if (!interpret_percent (c, pat, buf))
570 return FcFalse;
571 continue;
572 }
573 FcStrBufChar (buf, *c->format++);
574 }
575 return FcTrue;
576 }
577
578 FcChar8 *
579 FcPatternFormat (FcPattern *pat, const FcChar8 *format)
580 {
581 FcStrBuf buf;
582 FcFormatContext c;
583 FcBool ret;
584
585 FcStrBufInit (&buf, 0, 0);
586 if (!FcFormatContextInit (&c, format))
587 return NULL;
588
589 ret = interpret_expr (&c, pat, &buf, '\0');
590 if (buf.failed)
591 ret = FcFalse;
592
593 FcFormatContextDone (&c);
594 if (ret)
595 return FcStrBufDone (&buf);
596 else
597 {
598 FcStrBufDestroy (&buf);
599 return NULL;
600 }
601 }
602
603 #define __fcformat__
604 #include "fcaliastail.h"
605 #undef __fcformat__