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