]>
Commit | Line | Data |
---|---|---|
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 |
40 | static void |
41 | message (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 | 52 | typedef 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 | 60 | static FcBool |
27b3e2dd BE |
61 | FcFormatContextInit (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 |
71 | static void |
72 | FcFormatContextDone (FcFormatContext *c) | |
73 | { | |
74 | if (c) | |
75 | { | |
2017a5eb | 76 | free (c->word); |
27b3e2dd BE |
77 | } |
78 | } | |
c493c3b7 | 79 | |
27b3e2dd BE |
80 | static FcBool |
81 | consume_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 | ||
91 | static FcBool | |
92 | expect_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 | ||
108 | static FcBool | |
109 | FcCharIsPunct (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 | ||
128 | static FcBool | |
2017a5eb | 129 | read_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 | 161 | static FcBool |
7717b25f BE |
162 | interpret_expr (FcFormatContext *c, |
163 | FcPattern *pat, | |
164 | FcStrBuf *buf, | |
165 | FcChar8 term); | |
d6506ff6 | 166 | |
8c31a243 BE |
167 | static FcBool |
168 | interpret_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 |
177 | static FcBool |
178 | maybe_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 | ||
187 | static FcBool | |
188 | skip_subexpr (FcFormatContext *c); | |
189 | ||
190 | static FcBool | |
191 | skip_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 | ||
224 | static FcBool | |
225 | skip_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 | ||
247 | static FcBool | |
248 | skip_subexpr (FcFormatContext *c) | |
249 | { | |
250 | return expect_char (c, '{') && | |
251 | skip_expr (c) && | |
252 | expect_char (c, '}'); | |
253 | } | |
254 | ||
255 | static FcBool | |
256 | maybe_skip_subexpr (FcFormatContext *c) | |
257 | { | |
258 | return (*c->format == '{') ? | |
259 | skip_subexpr (c) : | |
260 | FcTrue; | |
261 | } | |
262 | ||
8c31a243 | 263 | static FcBool |
2017a5eb BE |
264 | interpret_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 | ||
302 | static FcBool | |
303 | interpret_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 | ||
339 | static FcBool | |
340 | interpret_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 | 372 | static FcBool |
2017a5eb BE |
373 | interpret_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 |
418 | static FcChar8 * |
419 | cescape (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 | ||
439 | static FcChar8 * | |
440 | shescape (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 | ||
459 | static FcChar8 * | |
460 | xmlescape (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 *) "&"); break; | |
471 | case '<': FcStrBufString (&buf, (const FcChar8 *) "<"); break; | |
472 | case '>': FcStrBufString (&buf, (const FcChar8 *) ">"); break; | |
473 | default: FcStrBufChar (&buf, *str); break; | |
474 | } | |
475 | str++; | |
476 | } | |
477 | return FcStrBufDone (&buf); | |
478 | } | |
479 | ||
2017a5eb BE |
480 | static FcChar8 * |
481 | convert (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 | ||
501 | static FcBool | |
502 | maybe_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 | ||
529 | static FcBool | |
530 | align_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 |
566 | static FcBool |
567 | interpret_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 | ||
605 | static 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 | 619 | static FcBool |
7717b25f BE |
620 | interpret_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 | ||
644 | FcChar8 * | |
645 | FcPatternFormat (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__ |