]> git.wh0rd.org - fontconfig.git/blame - src/fcformat.c
Bug 44826 - <alias> must contain only a single <family>
[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
5aaf466d 10 * documentation, and that the name of the author(s) not be used in
0c93b91d 11 * advertising or publicity pertaining to distribution of the software without
5aaf466d 12 * specific, written prior permission. The authors make no
0c93b91d
BE
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
15 *
3074a73b 16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
0c93b91d 17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
3074a73b 18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
0c93b91d
BE
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
caeea376
BE
31/* The language is documented in doc/fcformat.fncs
32 * These are the features implemented:
04ac14fc 33 *
caeea376
BE
34 * simple %{elt}
35 * width %width{elt}
36 * index %{elt[idx]}
37 * name= %{elt=}
38 * :name= %{:elt}
39 * default %{elt:-word}
40 * count %{#elt}
41 * subexpr %{{expr}}
42 * filter-out %{-elt1,elt2,elt3{expr}}
43 * filter-in %{+elt1,elt2,elt3{expr}}
44 * conditional %{?elt1,elt2,!elt3{}{}}
45 * enumerate %{[]elt1,elt2{expr}}
46 * langset langset enumeration using the same syntax
47 * builtin %{=blt}
48 * convert %{elt|conv1|conv2|conv3}
04ac14fc 49 *
caeea376
BE
50 * converters:
51 * basename FcStrBasename
52 * dirname FcStrDirname
53 * downcase FcStrDowncase
04ac14fc
BE
54 * shescape
55 * cescape
56 * xmlescape
caeea376
BE
57 * delete delete chars
58 * escape escape chars
59 * translate translate chars
04ac14fc
BE
60 *
61 * builtins:
caeea376
BE
62 * unparse FcNameUnparse
63 * fcmatch fc-match default
64 * fclist fc-list default
e61eba94 65 * fccat fc-cat default
caeea376
BE
66 * pkgkit PackageKit package tag format
67 *
68 *
2017a5eb
BE
69 * Some ideas for future syntax extensions:
70 *
caeea376 71 * - verbose builtin that is like FcPatternPrint
d04a7507 72 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
25afea87 73 * - allow indexing in +, -, ? filtering?
d4f7a4c6 74 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
2017a5eb
BE
75 */
76
d04a7507 77
e61eba94 78#define FCCAT_FORMAT "\"%{file|basename|cescape}\" %{index} \"%{-file{%{=unparse|cescape}}}\""
0673ef38 79#define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
e0be405a 80#define FCLIST_FORMAT "%{?file{%{file}: }}%{-file{%{=unparse}}}"
384542fa 81#define PKGKIT_FORMAT "%{[]family{font(%{family|downcase|delete( )})\n}}%{[]lang{font(:lang=%{lang|downcase|translate(_,-)})\n}}"
d04a7507
BE
82
83
0c93b91d
BE
84static void
85message (const char *fmt, ...)
86{
87 va_list args;
88 va_start (args, fmt);
8c31a243 89 fprintf (stderr, "Fontconfig: Pattern format error: ");
0c93b91d 90 vfprintf (stderr, fmt, args);
d6506ff6 91 fprintf (stderr, ".\n");
0c93b91d
BE
92 va_end (args);
93}
94
95
27b3e2dd 96typedef struct _FcFormatContext
0c93b91d 97{
27b3e2dd
BE
98 const FcChar8 *format_orig;
99 const FcChar8 *format;
100 int format_len;
2017a5eb 101 FcChar8 *word;
85c7fb67 102 FcBool word_allocated;
27b3e2dd 103} FcFormatContext;
c493c3b7 104
8c31a243 105static FcBool
27b3e2dd 106FcFormatContextInit (FcFormatContext *c,
85c7fb67
BE
107 const FcChar8 *format,
108 FcChar8 *scratch,
109 int scratch_len)
27b3e2dd
BE
110{
111 c->format_orig = c->format = format;
112 c->format_len = strlen ((const char *) format);
85c7fb67
BE
113
114 if (c->format_len < scratch_len)
115 {
116 c->word = scratch;
117 c->word_allocated = FcFalse;
118 }
119 else
120 {
121 c->word = malloc (c->format_len + 1);
122 c->word_allocated = FcTrue;
123 }
8c31a243 124
2017a5eb 125 return c->word != NULL;
27b3e2dd 126}
c493c3b7 127
27b3e2dd
BE
128static void
129FcFormatContextDone (FcFormatContext *c)
130{
85c7fb67 131 if (c && c->word_allocated)
27b3e2dd 132 {
2017a5eb 133 free (c->word);
27b3e2dd
BE
134 }
135}
c493c3b7 136
27b3e2dd
BE
137static FcBool
138consume_char (FcFormatContext *c,
139 FcChar8 term)
140{
141 if (*c->format != term)
d6506ff6
BE
142 return FcFalse;
143
144 c->format++;
145 return FcTrue;
146}
147
148static FcBool
149expect_char (FcFormatContext *c,
150 FcChar8 term)
151{
152 FcBool res = consume_char (c, term);
153 if (!res)
154 {
155 if (c->format == c->format_orig + c->format_len)
156 message ("format ended while expecting '%c'",
157 term);
158 else
159 message ("expected '%c' at %d",
160 term, c->format - c->format_orig + 1);
161 }
162 return res;
163}
164
165static FcBool
166FcCharIsPunct (const FcChar8 c)
167{
168 if (c < '0')
169 return FcTrue;
170 if (c <= '9')
171 return FcFalse;
172 if (c < 'A')
173 return FcTrue;
174 if (c <= 'Z')
175 return FcFalse;
176 if (c < 'a')
177 return FcTrue;
178 if (c <= 'z')
179 return FcFalse;
180 if (c <= '~')
181 return FcTrue;
182 return FcFalse;
183}
184
c8f5933d
BE
185static char escaped_char(const char ch)
186{
187 switch (ch) {
188 case 'a': return '\a';
189 case 'b': return '\b';
190 case 'f': return '\f';
191 case 'n': return '\n';
192 case 'r': return '\r';
193 case 't': return '\t';
194 case 'v': return '\v';
195 default: return ch;
196 }
197}
198
d6506ff6 199static FcBool
2017a5eb 200read_word (FcFormatContext *c)
d6506ff6
BE
201{
202 FcChar8 *p;
203
2017a5eb 204 p = c->word;
d6506ff6
BE
205
206 while (*c->format)
207 {
208 if (*c->format == '\\')
209 {
210 c->format++;
211 if (*c->format)
c8f5933d 212 *p++ = escaped_char (*c->format++);
d6506ff6
BE
213 continue;
214 }
215 else if (FcCharIsPunct (*c->format))
216 break;
217
218 *p++ = *c->format++;
219 }
220 *p = '\0';
221
2017a5eb 222 if (p == c->word)
0c93b91d 223 {
85c7fb67 224 message ("expected identifier at %d",
d6506ff6 225 c->format - c->format_orig + 1);
27b3e2dd
BE
226 return FcFalse;
227 }
0c93b91d 228
27b3e2dd
BE
229 return FcTrue;
230}
0c93b91d 231
c8f5933d
BE
232static FcBool
233read_chars (FcFormatContext *c,
234 FcChar8 term)
235{
236 FcChar8 *p;
237
238 p = c->word;
239
240 while (*c->format && *c->format != '}' && *c->format != term)
241 {
242 if (*c->format == '\\')
243 {
244 c->format++;
245 if (*c->format)
246 *p++ = escaped_char (*c->format++);
247 continue;
248 }
249
250 *p++ = *c->format++;
251 }
252 *p = '\0';
253
254 if (p == c->word)
255 {
256 message ("expected character data at %d",
257 c->format - c->format_orig + 1);
258 return FcFalse;
259 }
260
261 return FcTrue;
262}
263
d04a7507
BE
264static FcBool
265FcPatternFormatToBuf (FcPattern *pat,
266 const FcChar8 *format,
267 FcStrBuf *buf);
268
d4f7a4c6
BE
269static FcBool
270interpret_builtin (FcFormatContext *c,
271 FcPattern *pat,
272 FcStrBuf *buf)
273{
d04a7507
BE
274 FcChar8 *new_str;
275 FcBool ret;
d4f7a4c6 276
d04a7507
BE
277 if (!expect_char (c, '=') ||
278 !read_word (c))
d4f7a4c6 279 return FcFalse;
d04a7507
BE
280
281 /* try simple builtins first */
282 if (0) { }
d4f7a4c6
BE
283#define BUILTIN(name, func) \
284 else if (0 == strcmp ((const char *) c->word, name))\
d04a7507
BE
285 do { new_str = func (pat); ret = FcTrue; } while (0)
286 BUILTIN ("unparse", FcNameUnparse);
0673ef38 287 /* BUILTIN ("verbose", FcPatternPrint); XXX */
d04a7507
BE
288#undef BUILTIN
289 else
290 ret = FcFalse;
291
292 if (ret)
293 {
294 if (new_str)
295 {
296 FcStrBufString (buf, new_str);
297 free (new_str);
298 return FcTrue;
299 }
300 else
301 return FcFalse;
302 }
303
304 /* now try our custom formats */
305 if (0) { }
306#define BUILTIN(name, format) \
307 else if (0 == strcmp ((const char *) c->word, name))\
308 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
e61eba94 309 BUILTIN ("fccat", FCCAT_FORMAT);
d04a7507
BE
310 BUILTIN ("fcmatch", FCMATCH_FORMAT);
311 BUILTIN ("fclist", FCLIST_FORMAT);
384542fa 312 BUILTIN ("pkgkit", PKGKIT_FORMAT);
d04a7507
BE
313#undef BUILTIN
314 else
315 ret = FcFalse;
316
317 if (!ret)
318 message ("unknown builtin \"%s\"",
319 c->word);
320
321 return ret;
d4f7a4c6
BE
322}
323
8c31a243 324static FcBool
7717b25f
BE
325interpret_expr (FcFormatContext *c,
326 FcPattern *pat,
327 FcStrBuf *buf,
328 FcChar8 term);
d6506ff6 329
8c31a243
BE
330static FcBool
331interpret_subexpr (FcFormatContext *c,
27b3e2dd
BE
332 FcPattern *pat,
333 FcStrBuf *buf)
334{
8c31a243 335 return expect_char (c, '{') &&
7717b25f 336 interpret_expr (c, pat, buf, '}') &&
8c31a243
BE
337 expect_char (c, '}');
338}
0c93b91d 339
7717b25f
BE
340static FcBool
341maybe_interpret_subexpr (FcFormatContext *c,
342 FcPattern *pat,
343 FcStrBuf *buf)
344{
345 return (*c->format == '{') ?
346 interpret_subexpr (c, pat, buf) :
347 FcTrue;
348}
349
350static FcBool
351skip_subexpr (FcFormatContext *c);
352
353static FcBool
354skip_percent (FcFormatContext *c)
355{
2017a5eb
BE
356 int width;
357
7717b25f
BE
358 if (!expect_char (c, '%'))
359 return FcFalse;
360
361 /* skip an optional width specifier */
2017a5eb 362 width = strtol ((const char *) c->format, (char **) &c->format, 10);
7717b25f
BE
363
364 if (!expect_char (c, '{'))
365 return FcFalse;
366
367 while(*c->format && *c->format != '}')
368 {
369 switch (*c->format)
370 {
371 case '\\':
372 c->format++; /* skip over '\\' */
373 if (*c->format)
374 c->format++;
375 continue;
376 case '{':
377 if (!skip_subexpr (c))
378 return FcFalse;
379 continue;
380 }
381 c->format++;
382 }
383
384 return expect_char (c, '}');
385}
386
387static FcBool
388skip_expr (FcFormatContext *c)
389{
390 while(*c->format && *c->format != '}')
391 {
392 switch (*c->format)
393 {
394 case '\\':
395 c->format++; /* skip over '\\' */
396 if (*c->format)
397 c->format++;
398 continue;
399 case '%':
400 if (!skip_percent (c))
401 return FcFalse;
402 continue;
403 }
404 c->format++;
405 }
406
407 return FcTrue;
408}
409
410static FcBool
411skip_subexpr (FcFormatContext *c)
412{
413 return expect_char (c, '{') &&
414 skip_expr (c) &&
415 expect_char (c, '}');
416}
417
418static FcBool
419maybe_skip_subexpr (FcFormatContext *c)
420{
421 return (*c->format == '{') ?
422 skip_subexpr (c) :
423 FcTrue;
424}
425
8c31a243 426static FcBool
caeea376
BE
427interpret_filter_in (FcFormatContext *c,
428 FcPattern *pat,
429 FcStrBuf *buf)
8c31a243
BE
430{
431 FcObjectSet *os;
432 FcPattern *subpat;
433
434 if (!expect_char (c, '+'))
435 return FcFalse;
436
437 os = FcObjectSetCreate ();
438 if (!os)
439 return FcFalse;
440
441 do
442 {
25afea87 443 /* XXX binding */
2017a5eb
BE
444 if (!read_word (c) ||
445 !FcObjectSetAdd (os, (const char *) c->word))
8c31a243
BE
446 {
447 FcObjectSetDestroy (os);
448 return FcFalse;
449 }
450 }
451 while (consume_char (c, ','));
452
453 subpat = FcPatternFilter (pat, os);
454 FcObjectSetDestroy (os);
455
456 if (!subpat ||
457 !interpret_subexpr (c, subpat, buf))
458 return FcFalse;
459
460 FcPatternDestroy (subpat);
461 return FcTrue;
462}
463
464static FcBool
caeea376
BE
465interpret_filter_out (FcFormatContext *c,
466 FcPattern *pat,
467 FcStrBuf *buf)
8c31a243
BE
468{
469 FcPattern *subpat;
470
471 if (!expect_char (c, '-'))
472 return FcFalse;
473
474 subpat = FcPatternDuplicate (pat);
475 if (!subpat)
476 return FcFalse;
477
478 do
479 {
2017a5eb 480 if (!read_word (c))
8c31a243
BE
481 {
482 FcPatternDestroy (subpat);
483 return FcFalse;
484 }
485
2017a5eb 486 FcPatternDel (subpat, (const char *) c->word);
8c31a243
BE
487 }
488 while (consume_char (c, ','));
489
490 if (!interpret_subexpr (c, subpat, buf))
491 return FcFalse;
492
493 FcPatternDestroy (subpat);
494 return FcTrue;
495}
496
7717b25f 497static FcBool
2017a5eb
BE
498interpret_cond (FcFormatContext *c,
499 FcPattern *pat,
500 FcStrBuf *buf)
7717b25f
BE
501{
502 FcBool pass;
503
504 if (!expect_char (c, '?'))
505 return FcFalse;
506
507 pass = FcTrue;
508
509 do
510 {
511 FcBool negate;
512 FcValue v;
513
514 negate = consume_char (c, '!');
515
2017a5eb 516 if (!read_word (c))
7717b25f
BE
517 return FcFalse;
518
519 pass = pass &&
520 (negate ^
cdfb7658
BE
521 (FcResultMatch ==
522 FcPatternGet (pat, (const char *) c->word, 0, &v)));
7717b25f
BE
523 }
524 while (consume_char (c, ','));
525
526 if (pass)
527 {
528 if (!interpret_subexpr (c, pat, buf) ||
529 !maybe_skip_subexpr (c))
530 return FcFalse;
531 }
532 else
533 {
534 if (!skip_subexpr (c) ||
535 !maybe_interpret_subexpr (c, pat, buf))
536 return FcFalse;
537 }
538
539 return FcTrue;
540}
541
b6a23028
BE
542static FcBool
543interpret_count (FcFormatContext *c,
544 FcPattern *pat,
545 FcStrBuf *buf)
546{
547 int count;
548 FcPatternElt *e;
549 FcChar8 buf_static[64];
550
551 if (!expect_char (c, '#'))
552 return FcFalse;
553
554 if (!read_word (c))
555 return FcFalse;
556
557 count = 0;
558 e = FcPatternObjectFindElt (pat,
559 FcObjectFromName ((const char *) c->word));
560 if (e)
561 {
562 FcValueListPtr l;
563 count++;
564 for (l = FcPatternEltValues(e);
565 l->next;
566 l = l->next)
567 count++;
568 }
569
c8f5933d 570 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
b6a23028
BE
571 FcStrBufString (buf, buf_static);
572
573 return FcTrue;
574}
575
cdfb7658 576static FcBool
caeea376
BE
577interpret_enumerate (FcFormatContext *c,
578 FcPattern *pat,
579 FcStrBuf *buf)
cdfb7658
BE
580{
581 FcObjectSet *os;
582 FcPattern *subpat;
583 const FcChar8 *format_save;
584 int idx;
585 FcBool ret, done;
9d58d01c 586 FcStrList *lang_strs;
cdfb7658
BE
587
588 if (!expect_char (c, '[') ||
589 !expect_char (c, ']'))
590 return FcFalse;
591
592 os = FcObjectSetCreate ();
593 if (!os)
594 return FcFalse;
595
596 ret = FcTrue;
597
598 do
599 {
600 if (!read_word (c) ||
601 !FcObjectSetAdd (os, (const char *) c->word))
602 {
603 FcObjectSetDestroy (os);
604 return FcFalse;
605 }
606 }
607 while (consume_char (c, ','));
608
9d58d01c
BE
609 /* If we have one element and it's of type FcLangSet, we want
610 * to enumerate the languages in it. */
611 lang_strs = NULL;
612 if (os->nobject == 1)
613 {
614 FcLangSet *langset;
615 if (FcResultMatch ==
c76ed777 616 FcPatternGetLangSet (pat, os->objects[0], 0, &langset))
9d58d01c
BE
617 {
618 FcStrSet *ss;
619 if (!(ss = FcLangSetGetLangs (langset)) ||
620 !(lang_strs = FcStrListCreate (ss)))
621 goto bail0;
622 }
623 }
624
cdfb7658
BE
625 subpat = FcPatternDuplicate (pat);
626 if (!subpat)
627 goto bail0;
628
629 format_save = c->format;
630 idx = 0;
631 do
632 {
633 int i;
634
635 done = FcTrue;
636
9d58d01c 637 if (lang_strs)
cdfb7658 638 {
9d58d01c
BE
639 FcChar8 *lang;
640
641 FcPatternDel (subpat, os->objects[0]);
642 if ((lang = FcStrListNext (lang_strs)))
cdfb7658 643 {
25afea87 644 /* XXX binding? */
9d58d01c 645 FcPatternAddString (subpat, os->objects[0], lang);
cdfb7658
BE
646 done = FcFalse;
647 }
648 }
9d58d01c
BE
649 else
650 {
651 for (i = 0; i < os->nobject; i++)
652 {
653 FcValue v;
654
655 /* XXX this can be optimized by accessing valuelist linked lists
656 * directly and remembering where we were. Most (all) value lists
657 * in normal uses are pretty short though (language tags are
658 * stored as a LangSet, not separate values.). */
659 FcPatternDel (subpat, os->objects[i]);
660 if (FcResultMatch ==
661 FcPatternGet (pat, os->objects[i], idx, &v))
662 {
25afea87 663 /* XXX binding */
9d58d01c
BE
664 FcPatternAdd (subpat, os->objects[i], v, FcFalse);
665 done = FcFalse;
666 }
667 }
668 }
cdfb7658
BE
669
670 if (!done)
671 {
672 c->format = format_save;
673 ret = interpret_subexpr (c, subpat, buf);
674 if (!ret)
675 goto bail;
676 }
677
678 idx++;
679 } while (!done);
680
9d58d01c
BE
681 if (c->format == format_save)
682 skip_subexpr (c);
683
cdfb7658
BE
684bail:
685 FcPatternDestroy (subpat);
686bail0:
9d58d01c
BE
687 if (lang_strs)
688 FcStrListDone (lang_strs);
cdfb7658
BE
689 FcObjectSetDestroy (os);
690
691 return ret;
692}
693
b6a23028
BE
694static FcBool
695interpret_simple (FcFormatContext *c,
696 FcPattern *pat,
697 FcStrBuf *buf)
698{
699 FcPatternElt *e;
700 FcBool add_colon = FcFalse;
701 FcBool add_elt_name = FcFalse;
9c83a837 702 int idx;
0673ef38 703 FcChar8 *else_string;
b6a23028
BE
704
705 if (consume_char (c, ':'))
706 add_colon = FcTrue;
707
708 if (!read_word (c))
709 return FcFalse;
710
9c83a837
BE
711 idx = -1;
712 if (consume_char (c, '['))
713 {
714 idx = strtol ((const char *) c->format, (char **) &c->format, 10);
715 if (idx < 0)
716 {
717 message ("expected non-negative number at %d",
718 c->format-1 - c->format_orig + 1);
719 return FcFalse;
720 }
cdfb7658
BE
721 if (!expect_char (c, ']'))
722 return FcFalse;
9c83a837
BE
723 }
724
b6a23028
BE
725 if (consume_char (c, '='))
726 add_elt_name = FcTrue;
727
0673ef38
BE
728 /* modifiers */
729 else_string = NULL;
730 if (consume_char (c, ':'))
731 {
732 FcChar8 *orig;
733 /* divert the c->word for now */
734 orig = c->word;
735 c->word = c->word + strlen ((const char *) c->word) + 1;
736 /* for now we just support 'default value' */
737 if (!expect_char (c, '-') ||
a15ac5d3 738 !read_chars (c, '|'))
0673ef38
BE
739 {
740 c->word = orig;
741 return FcFalse;
742 }
743 else_string = c->word;
744 c->word = orig;
745 }
746
b6a23028
BE
747 e = FcPatternObjectFindElt (pat,
748 FcObjectFromName ((const char *) c->word));
f6d83439 749 if (e || else_string)
b6a23028 750 {
f6d83439 751 FcValueListPtr l = NULL;
b6a23028
BE
752
753 if (add_colon)
754 FcStrBufChar (buf, ':');
755 if (add_elt_name)
756 {
757 FcStrBufString (buf, c->word);
758 FcStrBufChar (buf, '=');
759 }
760
f6d83439
BE
761 if (e)
762 l = FcPatternEltValues(e);
9c83a837
BE
763
764 if (idx != -1)
765 {
766 while (l && idx > 0)
767 {
768 l = FcValueListNext(l);
769 idx--;
770 }
771 if (l && idx == 0)
772 {
773 if (!FcNameUnparseValue (buf, &l->value, '\0'))
774 return FcFalse;
775 }
776 else goto notfound;
777 }
f6d83439 778 else if (l)
9c83a837
BE
779 {
780 FcNameUnparseValueList (buf, l, '\0');
781 }
f6d83439
BE
782 else
783 {
784 notfound:
785 if (else_string)
786 FcStrBufString (buf, else_string);
787 }
b6a23028
BE
788 }
789
790 return FcTrue;
791}
792
85c7fb67
BE
793static FcBool
794cescape (FcFormatContext *c,
d04a7507
BE
795 const FcChar8 *str,
796 FcStrBuf *buf)
ced38254 797{
25afea87
BE
798 /* XXX escape \n etc? */
799
ced38254
BE
800 while(*str)
801 {
802 switch (*str)
803 {
804 case '\\':
805 case '"':
85c7fb67 806 FcStrBufChar (buf, '\\');
ced38254
BE
807 break;
808 }
85c7fb67 809 FcStrBufChar (buf, *str++);
ced38254 810 }
85c7fb67 811 return FcTrue;
ced38254
BE
812}
813
85c7fb67
BE
814static FcBool
815shescape (FcFormatContext *c,
d04a7507
BE
816 const FcChar8 *str,
817 FcStrBuf *buf)
ced38254 818{
85c7fb67 819 FcStrBufChar (buf, '\'');
ced38254
BE
820 while(*str)
821 {
822 if (*str == '\'')
85c7fb67 823 FcStrBufString (buf, (const FcChar8 *) "'\\''");
ced38254 824 else
85c7fb67 825 FcStrBufChar (buf, *str);
ced38254
BE
826 str++;
827 }
85c7fb67
BE
828 FcStrBufChar (buf, '\'');
829 return FcTrue;
ced38254
BE
830}
831
85c7fb67
BE
832static FcBool
833xmlescape (FcFormatContext *c,
d04a7507
BE
834 const FcChar8 *str,
835 FcStrBuf *buf)
ced38254 836{
25afea87
BE
837 /* XXX escape \n etc? */
838
ced38254
BE
839 while(*str)
840 {
841 switch (*str)
842 {
85c7fb67
BE
843 case '&': FcStrBufString (buf, (const FcChar8 *) "&amp;"); break;
844 case '<': FcStrBufString (buf, (const FcChar8 *) "&lt;"); break;
845 case '>': FcStrBufString (buf, (const FcChar8 *) "&gt;"); break;
846 default: FcStrBufChar (buf, *str); break;
ced38254
BE
847 }
848 str++;
849 }
85c7fb67 850 return FcTrue;
ced38254
BE
851}
852
85c7fb67 853static FcBool
c8f5933d 854delete_chars (FcFormatContext *c,
d04a7507
BE
855 const FcChar8 *str,
856 FcStrBuf *buf)
c8f5933d 857{
c8f5933d
BE
858 /* XXX not UTF-8 aware */
859
860 if (!expect_char (c, '(') ||
861 !read_chars (c, ')') ||
862 !expect_char (c, ')'))
85c7fb67 863 return FcFalse;
c8f5933d 864
c8f5933d
BE
865 while(*str)
866 {
867 FcChar8 *p;
868
869 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
870 if (p)
871 {
85c7fb67 872 FcStrBufData (buf, str, p - str);
c8f5933d
BE
873 str = p + 1;
874 }
875 else
876 {
85c7fb67 877 FcStrBufString (buf, str);
c8f5933d
BE
878 break;
879 }
880
881 }
85c7fb67
BE
882
883 return FcTrue;
c8f5933d
BE
884}
885
85c7fb67 886static FcBool
c8f5933d 887escape_chars (FcFormatContext *c,
d04a7507
BE
888 const FcChar8 *str,
889 FcStrBuf *buf)
c8f5933d 890{
c8f5933d
BE
891 /* XXX not UTF-8 aware */
892
893 if (!expect_char (c, '(') ||
894 !read_chars (c, ')') ||
895 !expect_char (c, ')'))
85c7fb67 896 return FcFalse;
c8f5933d 897
c8f5933d
BE
898 while(*str)
899 {
900 FcChar8 *p;
901
902 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
903 if (p)
904 {
85c7fb67
BE
905 FcStrBufData (buf, str, p - str);
906 FcStrBufChar (buf, c->word[0]);
907 FcStrBufChar (buf, *p);
c8f5933d
BE
908 str = p + 1;
909 }
910 else
911 {
85c7fb67 912 FcStrBufString (buf, str);
c8f5933d
BE
913 break;
914 }
915
916 }
85c7fb67
BE
917
918 return FcTrue;
c8f5933d
BE
919}
920
85c7fb67 921static FcBool
c8f5933d 922translate_chars (FcFormatContext *c,
d04a7507
BE
923 const FcChar8 *str,
924 FcStrBuf *buf)
c8f5933d 925{
c8f5933d
BE
926 char *from, *to, repeat;
927 int from_len, to_len;
928
929 /* XXX not UTF-8 aware */
930
931 if (!expect_char (c, '(') ||
932 !read_chars (c, ',') ||
933 !expect_char (c, ','))
85c7fb67 934 return FcFalse;
c8f5933d
BE
935
936 from = (char *) c->word;
937 from_len = strlen (from);
938 to = from + from_len + 1;
939
0673ef38 940 /* hack: we temporarily divert c->word */
c8f5933d
BE
941 c->word = (FcChar8 *) to;
942 if (!read_chars (c, ')'))
943 {
944 c->word = (FcChar8 *) from;
945 return FcFalse;
946 }
947 c->word = (FcChar8 *) from;
948
949 to_len = strlen (to);
950 repeat = to[to_len - 1];
951
952 if (!expect_char (c, ')'))
953 return FcFalse;
954
c8f5933d
BE
955 while(*str)
956 {
957 FcChar8 *p;
958
959 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
960 if (p)
961 {
962 int i;
85c7fb67 963 FcStrBufData (buf, str, p - str);
c8f5933d 964 i = strchr (from, *p) - from;
85c7fb67 965 FcStrBufChar (buf, i < to_len ? to[i] : repeat);
c8f5933d
BE
966 str = p + 1;
967 }
968 else
969 {
85c7fb67 970 FcStrBufString (buf, str);
c8f5933d
BE
971 break;
972 }
973
974 }
85c7fb67
BE
975
976 return FcTrue;
c8f5933d
BE
977}
978
85c7fb67
BE
979static FcBool
980interpret_convert (FcFormatContext *c,
981 FcStrBuf *buf,
982 int start)
2017a5eb 983{
85c7fb67
BE
984 const FcChar8 *str;
985 FcChar8 *new_str;
986 FcStrBuf new_buf;
987 FcChar8 buf_static[8192];
988 FcBool ret;
989
d04a7507
BE
990 if (!expect_char (c, '|') ||
991 !read_word (c))
85c7fb67
BE
992 return FcFalse;
993
d04a7507 994 /* prepare the buffer */
85c7fb67
BE
995 FcStrBufChar (buf, '\0');
996 if (buf->failed)
997 return FcFalse;
998 str = buf->buf + start;
999 buf->len = start;
1000
85c7fb67
BE
1001 /* try simple converters first */
1002 if (0) { }
ced38254
BE
1003#define CONVERTER(name, func) \
1004 else if (0 == strcmp ((const char *) c->word, name))\
85c7fb67 1005 do { new_str = func (str); ret = FcTrue; } while (0)
c8f5933d
BE
1006 CONVERTER ("downcase", FcStrDowncase);
1007 CONVERTER ("basename", FcStrBasename);
1008 CONVERTER ("dirname", FcStrDirname);
85c7fb67
BE
1009#undef CONVERTER
1010 else
1011 ret = FcFalse;
1012
1013 if (ret)
1014 {
1015 if (new_str)
1016 {
85c7fb67
BE
1017 FcStrBufString (buf, new_str);
1018 free (new_str);
1019 return FcTrue;
1020 }
1021 else
1022 return FcFalse;
1023 }
1024
1025 FcStrBufInit (&new_buf, buf_static, sizeof (buf_static));
1026
1027 /* now try our custom converters */
1028 if (0) { }
1029#define CONVERTER(name, func) \
1030 else if (0 == strcmp ((const char *) c->word, name))\
d04a7507 1031 ret = func (c, str, &new_buf)
85c7fb67
BE
1032 CONVERTER ("cescape", cescape);
1033 CONVERTER ("shescape", shescape);
1034 CONVERTER ("xmlescape", xmlescape);
1035 CONVERTER ("delete", delete_chars);
1036 CONVERTER ("escape", escape_chars);
1037 CONVERTER ("translate", translate_chars);
1038#undef CONVERTER
1039 else
1040 ret = FcFalse;
1041
1042 if (ret)
1043 {
1044 FcStrBufChar (&new_buf, '\0');
1045 FcStrBufString (buf, new_buf.buf);
1046 }
1047 else
1048 message ("unknown converter \"%s\"",
1049 c->word);
1050
1051 FcStrBufDestroy (&new_buf);
1052
1053 return ret;
2017a5eb
BE
1054}
1055
1056static FcBool
1057maybe_interpret_converts (FcFormatContext *c,
1058 FcStrBuf *buf,
1059 int start)
1060{
85c7fb67
BE
1061 while (*c->format == '|')
1062 if (!interpret_convert (c, buf, start))
2017a5eb
BE
1063 return FcFalse;
1064
2017a5eb
BE
1065 return FcTrue;
1066}
1067
1068static FcBool
1069align_to_width (FcStrBuf *buf,
1070 int start,
1071 int width)
1072{
1073 int len;
1074
1075 if (buf->failed)
1076 return FcFalse;
1077
1078 len = buf->len - start;
1079 if (len < -width)
1080 {
1081 /* left align */
1082 while (len++ < -width)
1083 FcStrBufChar (buf, ' ');
1084 }
1085 else if (len < width)
1086 {
1087 int old_len;
1088 old_len = len;
1089 /* right align */
1090 while (len++ < width)
1091 FcStrBufChar (buf, ' ');
1092 if (buf->failed)
1093 return FcFalse;
1094 len = old_len;
1095 memmove (buf->buf + buf->len - len,
1096 buf->buf + buf->len - width,
1097 len);
1098 memset (buf->buf + buf->len - width,
1099 ' ',
1100 width - len);
1101 }
1102
1103 return !buf->failed;
1104}
8c31a243
BE
1105static FcBool
1106interpret_percent (FcFormatContext *c,
1107 FcPattern *pat,
1108 FcStrBuf *buf)
1109{
2017a5eb
BE
1110 int width, start;
1111 FcBool ret;
8c31a243
BE
1112
1113 if (!expect_char (c, '%'))
1114 return FcFalse;
1115
1116 if (consume_char (c, '%')) /* "%%" */
1117 {
1118 FcStrBufChar (buf, '%');
1119 return FcTrue;
1120 }
1121
1122 /* parse an optional width specifier */
1123 width = strtol ((const char *) c->format, (char **) &c->format, 10);
1124
8c31a243
BE
1125 if (!expect_char (c, '{'))
1126 return FcFalse;
1127
2017a5eb 1128 start = buf->len;
7717b25f 1129
2017a5eb 1130 switch (*c->format) {
caeea376
BE
1131 case '=': ret = interpret_builtin (c, pat, buf); break;
1132 case '{': ret = interpret_subexpr (c, pat, buf); break;
1133 case '+': ret = interpret_filter_in (c, pat, buf); break;
1134 case '-': ret = interpret_filter_out (c, pat, buf); break;
1135 case '?': ret = interpret_cond (c, pat, buf); break;
1136 case '#': ret = interpret_count (c, pat, buf); break;
1137 case '[': ret = interpret_enumerate (c, pat, buf); break;
1138 default: ret = interpret_simple (c, pat, buf); break;
0c93b91d 1139 }
c493c3b7 1140
2017a5eb
BE
1141 return ret &&
1142 maybe_interpret_converts (c, buf, start) &&
1143 align_to_width (buf, start, width) &&
1144 expect_char (c, '}');
0c93b91d
BE
1145}
1146
8c31a243 1147static FcBool
7717b25f
BE
1148interpret_expr (FcFormatContext *c,
1149 FcPattern *pat,
1150 FcStrBuf *buf,
1151 FcChar8 term)
0c93b91d 1152{
7717b25f 1153 while (*c->format && *c->format != term)
0c93b91d 1154 {
27b3e2dd 1155 switch (*c->format)
0c93b91d
BE
1156 {
1157 case '\\':
27b3e2dd
BE
1158 c->format++; /* skip over '\\' */
1159 if (*c->format)
1160 FcStrBufChar (buf, escaped_char (*c->format++));
0c93b91d
BE
1161 continue;
1162 case '%':
8c31a243
BE
1163 if (!interpret_percent (c, pat, buf))
1164 return FcFalse;
0c93b91d
BE
1165 continue;
1166 }
27b3e2dd 1167 FcStrBufChar (buf, *c->format++);
0c93b91d 1168 }
8c31a243 1169 return FcTrue;
0c93b91d
BE
1170}
1171
d04a7507
BE
1172static FcBool
1173FcPatternFormatToBuf (FcPattern *pat,
1174 const FcChar8 *format,
1175 FcStrBuf *buf)
1176{
1177 FcFormatContext c;
1178 FcChar8 word_static[1024];
1179 FcBool ret;
1180
1181 if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static)))
1182 return FcFalse;
1183
1184 ret = interpret_expr (&c, pat, buf, '\0');
1185
1186 FcFormatContextDone (&c);
1187
1188 return ret;
1189}
1190
0c93b91d 1191FcChar8 *
d04a7507
BE
1192FcPatternFormat (FcPattern *pat,
1193 const FcChar8 *format)
0c93b91d 1194{
ced38254 1195 FcStrBuf buf;
85c7fb67 1196 FcChar8 buf_static[8192 - 1024];
ced38254 1197 FcBool ret;
0c93b91d 1198
ced38254 1199 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
0c93b91d 1200
d04a7507 1201 ret = FcPatternFormatToBuf (pat, format, &buf);
0c93b91d 1202
8c31a243
BE
1203 if (ret)
1204 return FcStrBufDone (&buf);
1205 else
1206 {
1207 FcStrBufDestroy (&buf);
1208 return NULL;
1209 }
0c93b91d
BE
1210}
1211
1212#define __fcformat__
1213#include "fcaliastail.h"
1214#undef __fcformat__