]> git.wh0rd.org - fontconfig.git/blame - src/fcformat.c
[fcformat] Implement 'delete', 'escape', and 'translate' filter functions
[fontconfig.git] / src / fcformat.c
CommitLineData
0c93b91d 1/*
b6a23028 2 * Copyright © 2008,2009 Red Hat, Inc.
0c93b91d
BE
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 *
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
0c93b91d
BE
39static void
40message (const char *fmt, ...)
41{
42 va_list args;
43 va_start (args, fmt);
8c31a243 44 fprintf (stderr, "Fontconfig: Pattern format error: ");
0c93b91d 45 vfprintf (stderr, fmt, args);
d6506ff6 46 fprintf (stderr, ".\n");
0c93b91d
BE
47 va_end (args);
48}
49
50
27b3e2dd 51typedef struct _FcFormatContext
0c93b91d 52{
27b3e2dd
BE
53 const FcChar8 *format_orig;
54 const FcChar8 *format;
55 int format_len;
2017a5eb 56 FcChar8 *word;
27b3e2dd 57} FcFormatContext;
c493c3b7 58
8c31a243 59static FcBool
27b3e2dd
BE
60FcFormatContextInit (FcFormatContext *c,
61 const FcChar8 *format)
62{
63 c->format_orig = c->format = format;
64 c->format_len = strlen ((const char *) format);
2017a5eb 65 c->word = malloc (c->format_len + 1);
8c31a243 66
2017a5eb 67 return c->word != NULL;
27b3e2dd 68}
c493c3b7 69
27b3e2dd
BE
70static void
71FcFormatContextDone (FcFormatContext *c)
72{
73 if (c)
74 {
2017a5eb 75 free (c->word);
27b3e2dd
BE
76 }
77}
c493c3b7 78
27b3e2dd
BE
79static FcBool
80consume_char (FcFormatContext *c,
81 FcChar8 term)
82{
83 if (*c->format != term)
d6506ff6
BE
84 return FcFalse;
85
86 c->format++;
87 return FcTrue;
88}
89
90static FcBool
91expect_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
107static FcBool
108FcCharIsPunct (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
c8f5933d
BE
127static char escaped_char(const char ch)
128{
129 switch (ch) {
130 case 'a': return '\a';
131 case 'b': return '\b';
132 case 'f': return '\f';
133 case 'n': return '\n';
134 case 'r': return '\r';
135 case 't': return '\t';
136 case 'v': return '\v';
137 default: return ch;
138 }
139}
140
d6506ff6 141static FcBool
2017a5eb 142read_word (FcFormatContext *c)
d6506ff6
BE
143{
144 FcChar8 *p;
145
2017a5eb 146 p = c->word;
d6506ff6
BE
147
148 while (*c->format)
149 {
150 if (*c->format == '\\')
151 {
152 c->format++;
153 if (*c->format)
c8f5933d 154 *p++ = escaped_char (*c->format++);
d6506ff6
BE
155 continue;
156 }
157 else if (FcCharIsPunct (*c->format))
158 break;
159
160 *p++ = *c->format++;
161 }
162 *p = '\0';
163
2017a5eb 164 if (p == c->word)
0c93b91d 165 {
d6506ff6
BE
166 message ("expected element name at %d",
167 c->format - c->format_orig + 1);
27b3e2dd
BE
168 return FcFalse;
169 }
0c93b91d 170
27b3e2dd
BE
171 return FcTrue;
172}
0c93b91d 173
c8f5933d
BE
174static FcBool
175read_chars (FcFormatContext *c,
176 FcChar8 term)
177{
178 FcChar8 *p;
179
180 p = c->word;
181
182 while (*c->format && *c->format != '}' && *c->format != term)
183 {
184 if (*c->format == '\\')
185 {
186 c->format++;
187 if (*c->format)
188 *p++ = escaped_char (*c->format++);
189 continue;
190 }
191
192 *p++ = *c->format++;
193 }
194 *p = '\0';
195
196 if (p == c->word)
197 {
198 message ("expected character data at %d",
199 c->format - c->format_orig + 1);
200 return FcFalse;
201 }
202
203 return FcTrue;
204}
205
8c31a243 206static FcBool
7717b25f
BE
207interpret_expr (FcFormatContext *c,
208 FcPattern *pat,
209 FcStrBuf *buf,
210 FcChar8 term);
d6506ff6 211
8c31a243
BE
212static FcBool
213interpret_subexpr (FcFormatContext *c,
27b3e2dd
BE
214 FcPattern *pat,
215 FcStrBuf *buf)
216{
8c31a243 217 return expect_char (c, '{') &&
7717b25f 218 interpret_expr (c, pat, buf, '}') &&
8c31a243
BE
219 expect_char (c, '}');
220}
0c93b91d 221
7717b25f
BE
222static FcBool
223maybe_interpret_subexpr (FcFormatContext *c,
224 FcPattern *pat,
225 FcStrBuf *buf)
226{
227 return (*c->format == '{') ?
228 interpret_subexpr (c, pat, buf) :
229 FcTrue;
230}
231
232static FcBool
233skip_subexpr (FcFormatContext *c);
234
235static FcBool
236skip_percent (FcFormatContext *c)
237{
2017a5eb
BE
238 int width;
239
7717b25f
BE
240 if (!expect_char (c, '%'))
241 return FcFalse;
242
243 /* skip an optional width specifier */
2017a5eb 244 width = strtol ((const char *) c->format, (char **) &c->format, 10);
7717b25f
BE
245
246 if (!expect_char (c, '{'))
247 return FcFalse;
248
249 while(*c->format && *c->format != '}')
250 {
251 switch (*c->format)
252 {
253 case '\\':
254 c->format++; /* skip over '\\' */
255 if (*c->format)
256 c->format++;
257 continue;
258 case '{':
259 if (!skip_subexpr (c))
260 return FcFalse;
261 continue;
262 }
263 c->format++;
264 }
265
266 return expect_char (c, '}');
267}
268
269static FcBool
270skip_expr (FcFormatContext *c)
271{
272 while(*c->format && *c->format != '}')
273 {
274 switch (*c->format)
275 {
276 case '\\':
277 c->format++; /* skip over '\\' */
278 if (*c->format)
279 c->format++;
280 continue;
281 case '%':
282 if (!skip_percent (c))
283 return FcFalse;
284 continue;
285 }
286 c->format++;
287 }
288
289 return FcTrue;
290}
291
292static FcBool
293skip_subexpr (FcFormatContext *c)
294{
295 return expect_char (c, '{') &&
296 skip_expr (c) &&
297 expect_char (c, '}');
298}
299
300static FcBool
301maybe_skip_subexpr (FcFormatContext *c)
302{
303 return (*c->format == '{') ?
304 skip_subexpr (c) :
305 FcTrue;
306}
307
8c31a243
BE
308static FcBool
309interpret_filter (FcFormatContext *c,
310 FcPattern *pat,
311 FcStrBuf *buf)
312{
313 FcObjectSet *os;
314 FcPattern *subpat;
315
316 if (!expect_char (c, '+'))
317 return FcFalse;
318
319 os = FcObjectSetCreate ();
320 if (!os)
321 return FcFalse;
322
323 do
324 {
2017a5eb
BE
325 if (!read_word (c) ||
326 !FcObjectSetAdd (os, (const char *) c->word))
8c31a243
BE
327 {
328 FcObjectSetDestroy (os);
329 return FcFalse;
330 }
331 }
332 while (consume_char (c, ','));
333
334 subpat = FcPatternFilter (pat, os);
335 FcObjectSetDestroy (os);
336
337 if (!subpat ||
338 !interpret_subexpr (c, subpat, buf))
339 return FcFalse;
340
341 FcPatternDestroy (subpat);
342 return FcTrue;
343}
344
345static FcBool
346interpret_delete (FcFormatContext *c,
347 FcPattern *pat,
348 FcStrBuf *buf)
349{
350 FcPattern *subpat;
351
352 if (!expect_char (c, '-'))
353 return FcFalse;
354
355 subpat = FcPatternDuplicate (pat);
356 if (!subpat)
357 return FcFalse;
358
359 do
360 {
2017a5eb 361 if (!read_word (c))
8c31a243
BE
362 {
363 FcPatternDestroy (subpat);
364 return FcFalse;
365 }
366
2017a5eb 367 FcPatternDel (subpat, (const char *) c->word);
8c31a243
BE
368 }
369 while (consume_char (c, ','));
370
371 if (!interpret_subexpr (c, subpat, buf))
372 return FcFalse;
373
374 FcPatternDestroy (subpat);
375 return FcTrue;
376}
377
7717b25f 378static FcBool
2017a5eb
BE
379interpret_cond (FcFormatContext *c,
380 FcPattern *pat,
381 FcStrBuf *buf)
7717b25f
BE
382{
383 FcBool pass;
384
385 if (!expect_char (c, '?'))
386 return FcFalse;
387
388 pass = FcTrue;
389
390 do
391 {
392 FcBool negate;
393 FcValue v;
394
395 negate = consume_char (c, '!');
396
2017a5eb 397 if (!read_word (c))
7717b25f
BE
398 return FcFalse;
399
400 pass = pass &&
401 (negate ^
2017a5eb
BE
402 (FcResultMatch == FcPatternGet (pat,
403 (const char *) c->word,
404 0, &v)));
7717b25f
BE
405 }
406 while (consume_char (c, ','));
407
408 if (pass)
409 {
410 if (!interpret_subexpr (c, pat, buf) ||
411 !maybe_skip_subexpr (c))
412 return FcFalse;
413 }
414 else
415 {
416 if (!skip_subexpr (c) ||
417 !maybe_interpret_subexpr (c, pat, buf))
418 return FcFalse;
419 }
420
421 return FcTrue;
422}
423
b6a23028
BE
424static FcBool
425interpret_count (FcFormatContext *c,
426 FcPattern *pat,
427 FcStrBuf *buf)
428{
429 int count;
430 FcPatternElt *e;
431 FcChar8 buf_static[64];
432
433 if (!expect_char (c, '#'))
434 return FcFalse;
435
436 if (!read_word (c))
437 return FcFalse;
438
439 count = 0;
440 e = FcPatternObjectFindElt (pat,
441 FcObjectFromName ((const char *) c->word));
442 if (e)
443 {
444 FcValueListPtr l;
445 count++;
446 for (l = FcPatternEltValues(e);
447 l->next;
448 l = l->next)
449 count++;
450 }
451
c8f5933d 452 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
b6a23028
BE
453 FcStrBufString (buf, buf_static);
454
455 return FcTrue;
456}
457
458static FcBool
459interpret_simple (FcFormatContext *c,
460 FcPattern *pat,
461 FcStrBuf *buf)
462{
463 FcPatternElt *e;
464 FcBool add_colon = FcFalse;
465 FcBool add_elt_name = FcFalse;
466
467 if (consume_char (c, ':'))
468 add_colon = FcTrue;
469
470 if (!read_word (c))
471 return FcFalse;
472
473 if (consume_char (c, '='))
474 add_elt_name = FcTrue;
475
476 e = FcPatternObjectFindElt (pat,
477 FcObjectFromName ((const char *) c->word));
478 if (e)
479 {
480 FcValueListPtr l;
481
482 if (add_colon)
483 FcStrBufChar (buf, ':');
484 if (add_elt_name)
485 {
486 FcStrBufString (buf, c->word);
487 FcStrBufChar (buf, '=');
488 }
489
490 l = FcPatternEltValues(e);
491 FcNameUnparseValueList (buf, l, '\0');
492 }
493
494 return FcTrue;
495}
496
ced38254
BE
497static FcChar8 *
498cescape (const FcChar8 *str)
499{
500 FcStrBuf buf;
c8f5933d 501 FcChar8 buf_static[8192];
ced38254
BE
502
503 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
504 while(*str)
505 {
506 switch (*str)
507 {
508 case '\\':
509 case '"':
510 FcStrBufChar (&buf, '\\');
511 break;
512 }
513 FcStrBufChar (&buf, *str++);
514 }
515 return FcStrBufDone (&buf);
516}
517
518static FcChar8 *
519shescape (const FcChar8 *str)
520{
521 FcStrBuf buf;
c8f5933d 522 FcChar8 buf_static[8192];
ced38254
BE
523
524 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
525 FcStrBufChar (&buf, '\'');
526 while(*str)
527 {
528 if (*str == '\'')
529 FcStrBufString (&buf, (const FcChar8 *) "'\\''");
530 else
531 FcStrBufChar (&buf, *str);
532 str++;
533 }
534 FcStrBufChar (&buf, '\'');
535 return FcStrBufDone (&buf);
536}
537
538static FcChar8 *
539xmlescape (const FcChar8 *str)
540{
541 FcStrBuf buf;
c8f5933d 542 FcChar8 buf_static[8192];
ced38254
BE
543
544 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
545 while(*str)
546 {
547 switch (*str)
548 {
549 case '&': FcStrBufString (&buf, (const FcChar8 *) "&amp;"); break;
550 case '<': FcStrBufString (&buf, (const FcChar8 *) "&lt;"); break;
551 case '>': FcStrBufString (&buf, (const FcChar8 *) "&gt;"); break;
552 default: FcStrBufChar (&buf, *str); break;
553 }
554 str++;
555 }
556 return FcStrBufDone (&buf);
557}
558
c8f5933d
BE
559static FcChar8 *
560delete_chars (FcFormatContext *c,
561 const FcChar8 *str)
562{
563 FcStrBuf buf;
564 FcChar8 buf_static[8192];
565
566 /* XXX not UTF-8 aware */
567
568 if (!expect_char (c, '(') ||
569 !read_chars (c, ')') ||
570 !expect_char (c, ')'))
571 return NULL;
572
573 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
574 while(*str)
575 {
576 FcChar8 *p;
577
578 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
579 if (p)
580 {
581 FcStrBufData (&buf, str, p - str);
582 str = p + 1;
583 }
584 else
585 {
586 FcStrBufString (&buf, str);
587 break;
588 }
589
590 }
591 return FcStrBufDone (&buf);
592}
593
594static FcChar8 *
595escape_chars (FcFormatContext *c,
596 const FcChar8 *str)
597{
598 FcStrBuf buf;
599 FcChar8 buf_static[8192];
600
601 /* XXX not UTF-8 aware */
602
603 if (!expect_char (c, '(') ||
604 !read_chars (c, ')') ||
605 !expect_char (c, ')'))
606 return NULL;
607
608 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
609 while(*str)
610 {
611 FcChar8 *p;
612
613 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
614 if (p)
615 {
616 FcStrBufData (&buf, str, p - str);
617 FcStrBufChar (&buf, c->word[0]);
618 FcStrBufChar (&buf, *p);
619 str = p + 1;
620 }
621 else
622 {
623 FcStrBufString (&buf, str);
624 break;
625 }
626
627 }
628 return FcStrBufDone (&buf);
629}
630
631static FcChar8 *
632translate_chars (FcFormatContext *c,
633 const FcChar8 *str)
634{
635 FcStrBuf buf;
636 FcChar8 buf_static[8192];
637 char *from, *to, repeat;
638 int from_len, to_len;
639
640 /* XXX not UTF-8 aware */
641
642 if (!expect_char (c, '(') ||
643 !read_chars (c, ',') ||
644 !expect_char (c, ','))
645 return NULL;
646
647 from = (char *) c->word;
648 from_len = strlen (from);
649 to = from + from_len + 1;
650
651 /* hack: we temporarily diverge c->word */
652 c->word = (FcChar8 *) to;
653 if (!read_chars (c, ')'))
654 {
655 c->word = (FcChar8 *) from;
656 return FcFalse;
657 }
658 c->word = (FcChar8 *) from;
659
660 to_len = strlen (to);
661 repeat = to[to_len - 1];
662
663 if (!expect_char (c, ')'))
664 return FcFalse;
665
666 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
667 while(*str)
668 {
669 FcChar8 *p;
670
671 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
672 if (p)
673 {
674 int i;
675 FcStrBufData (&buf, str, p - str);
676 i = strchr (from, *p) - from;
677 FcStrBufChar (&buf, i < to_len ? to[i] : repeat);
678 str = p + 1;
679 }
680 else
681 {
682 FcStrBufString (&buf, str);
683 break;
684 }
685
686 }
687 return FcStrBufDone (&buf);
688}
689
2017a5eb
BE
690static FcChar8 *
691convert (FcFormatContext *c,
692 const FcChar8 *str)
693{
694 if (!read_word (c))
695 return NULL;
ced38254
BE
696#define CONVERTER(name, func) \
697 else if (0 == strcmp ((const char *) c->word, name))\
698 return func (str)
c8f5933d
BE
699#define CONVERTER2(name, func) \
700 else if (0 == strcmp ((const char *) c->word, name))\
701 return func (c, str)
702 CONVERTER ("downcase", FcStrDowncase);
703 CONVERTER ("basename", FcStrBasename);
704 CONVERTER ("dirname", FcStrDirname);
705 CONVERTER ("cescape", cescape);
706 CONVERTER ("shescape", shescape);
707 CONVERTER ("xmlescape", xmlescape);
708 CONVERTER2 ("delete", delete_chars);
709 CONVERTER2 ("escape", escape_chars);
710 CONVERTER2 ("translate", translate_chars);
2017a5eb
BE
711
712 message ("unknown converter \"%s\"",
713 c->word);
714 return NULL;
715}
716
717static FcBool
718maybe_interpret_converts (FcFormatContext *c,
719 FcStrBuf *buf,
720 int start)
721{
722 while (consume_char (c, '|'))
723 {
724 const FcChar8 *str;
725 FcChar8 *new_str;
726
727 /* nul-terminate the buffer */
728 FcStrBufChar (buf, '\0');
729 if (buf->failed)
730 return FcFalse;
731 str = buf->buf + start;
732
733 if (!(new_str = convert (c, str)))
734 return FcFalse;
735
736 /* replace in the buffer */
737 buf->len = start;
738 FcStrBufString (buf, new_str);
739 free (new_str);
740 }
741
742 return FcTrue;
743}
744
745static FcBool
746align_to_width (FcStrBuf *buf,
747 int start,
748 int width)
749{
750 int len;
751
752 if (buf->failed)
753 return FcFalse;
754
755 len = buf->len - start;
756 if (len < -width)
757 {
758 /* left align */
759 while (len++ < -width)
760 FcStrBufChar (buf, ' ');
761 }
762 else if (len < width)
763 {
764 int old_len;
765 old_len = len;
766 /* right align */
767 while (len++ < width)
768 FcStrBufChar (buf, ' ');
769 if (buf->failed)
770 return FcFalse;
771 len = old_len;
772 memmove (buf->buf + buf->len - len,
773 buf->buf + buf->len - width,
774 len);
775 memset (buf->buf + buf->len - width,
776 ' ',
777 width - len);
778 }
779
780 return !buf->failed;
781}
8c31a243
BE
782static FcBool
783interpret_percent (FcFormatContext *c,
784 FcPattern *pat,
785 FcStrBuf *buf)
786{
2017a5eb
BE
787 int width, start;
788 FcBool ret;
8c31a243
BE
789
790 if (!expect_char (c, '%'))
791 return FcFalse;
792
793 if (consume_char (c, '%')) /* "%%" */
794 {
795 FcStrBufChar (buf, '%');
796 return FcTrue;
797 }
798
799 /* parse an optional width specifier */
800 width = strtol ((const char *) c->format, (char **) &c->format, 10);
801
8c31a243
BE
802 if (!expect_char (c, '{'))
803 return FcFalse;
804
2017a5eb 805 start = buf->len;
7717b25f 806
2017a5eb
BE
807 switch (*c->format) {
808 case '{': ret = interpret_subexpr (c, pat, buf); break;
809 case '+': ret = interpret_filter (c, pat, buf); break;
810 case '-': ret = interpret_delete (c, pat, buf); break;
811 case '?': ret = interpret_cond (c, pat, buf); break;
b6a23028 812 case '#': ret = interpret_count (c, pat, buf); break;
2017a5eb 813 default: ret = interpret_simple (c, pat, buf); break;
0c93b91d 814 }
c493c3b7 815
2017a5eb
BE
816 return ret &&
817 maybe_interpret_converts (c, buf, start) &&
818 align_to_width (buf, start, width) &&
819 expect_char (c, '}');
0c93b91d
BE
820}
821
8c31a243 822static FcBool
7717b25f
BE
823interpret_expr (FcFormatContext *c,
824 FcPattern *pat,
825 FcStrBuf *buf,
826 FcChar8 term)
0c93b91d 827{
7717b25f 828 while (*c->format && *c->format != term)
0c93b91d 829 {
27b3e2dd 830 switch (*c->format)
0c93b91d
BE
831 {
832 case '\\':
27b3e2dd
BE
833 c->format++; /* skip over '\\' */
834 if (*c->format)
835 FcStrBufChar (buf, escaped_char (*c->format++));
0c93b91d
BE
836 continue;
837 case '%':
8c31a243
BE
838 if (!interpret_percent (c, pat, buf))
839 return FcFalse;
0c93b91d
BE
840 continue;
841 }
27b3e2dd 842 FcStrBufChar (buf, *c->format++);
0c93b91d 843 }
8c31a243 844 return FcTrue;
0c93b91d
BE
845}
846
847FcChar8 *
848FcPatternFormat (FcPattern *pat, const FcChar8 *format)
849{
ced38254
BE
850 FcStrBuf buf;
851 FcChar8 buf_static[8192];
27b3e2dd 852 FcFormatContext c;
ced38254 853 FcBool ret;
0c93b91d 854
ced38254 855 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
8c31a243
BE
856 if (!FcFormatContextInit (&c, format))
857 return NULL;
0c93b91d 858
7717b25f 859 ret = interpret_expr (&c, pat, &buf, '\0');
0c93b91d 860
27b3e2dd 861 FcFormatContextDone (&c);
8c31a243
BE
862 if (ret)
863 return FcStrBufDone (&buf);
864 else
865 {
866 FcStrBufDestroy (&buf);
867 return NULL;
868 }
0c93b91d
BE
869}
870
871#define __fcformat__
872#include "fcaliastail.h"
873#undef __fcformat__