]> git.wh0rd.org - fontconfig.git/blame - src/fcformat.c
FcStat: change to FcChar8 for first arg
[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{
356 if (!expect_char (c, '%'))
357 return FcFalse;
358
359 /* skip an optional width specifier */
e3a66c29
MF
360 if (strtol ((const char *) c->format, (char **) &c->format, 10))
361 /* don't care */;
7717b25f
BE
362
363 if (!expect_char (c, '{'))
364 return FcFalse;
365
366 while(*c->format && *c->format != '}')
367 {
368 switch (*c->format)
369 {
370 case '\\':
371 c->format++; /* skip over '\\' */
372 if (*c->format)
373 c->format++;
374 continue;
375 case '{':
376 if (!skip_subexpr (c))
377 return FcFalse;
378 continue;
379 }
380 c->format++;
381 }
382
383 return expect_char (c, '}');
384}
385
386static FcBool
387skip_expr (FcFormatContext *c)
388{
389 while(*c->format && *c->format != '}')
390 {
391 switch (*c->format)
392 {
393 case '\\':
394 c->format++; /* skip over '\\' */
395 if (*c->format)
396 c->format++;
397 continue;
398 case '%':
399 if (!skip_percent (c))
400 return FcFalse;
401 continue;
402 }
403 c->format++;
404 }
405
406 return FcTrue;
407}
408
409static FcBool
410skip_subexpr (FcFormatContext *c)
411{
412 return expect_char (c, '{') &&
413 skip_expr (c) &&
414 expect_char (c, '}');
415}
416
417static FcBool
418maybe_skip_subexpr (FcFormatContext *c)
419{
420 return (*c->format == '{') ?
421 skip_subexpr (c) :
422 FcTrue;
423}
424
8c31a243 425static FcBool
caeea376
BE
426interpret_filter_in (FcFormatContext *c,
427 FcPattern *pat,
428 FcStrBuf *buf)
8c31a243
BE
429{
430 FcObjectSet *os;
431 FcPattern *subpat;
432
433 if (!expect_char (c, '+'))
434 return FcFalse;
435
436 os = FcObjectSetCreate ();
437 if (!os)
438 return FcFalse;
439
440 do
441 {
25afea87 442 /* XXX binding */
2017a5eb
BE
443 if (!read_word (c) ||
444 !FcObjectSetAdd (os, (const char *) c->word))
8c31a243
BE
445 {
446 FcObjectSetDestroy (os);
447 return FcFalse;
448 }
449 }
450 while (consume_char (c, ','));
451
452 subpat = FcPatternFilter (pat, os);
453 FcObjectSetDestroy (os);
454
455 if (!subpat ||
456 !interpret_subexpr (c, subpat, buf))
457 return FcFalse;
458
459 FcPatternDestroy (subpat);
460 return FcTrue;
461}
462
463static FcBool
caeea376
BE
464interpret_filter_out (FcFormatContext *c,
465 FcPattern *pat,
466 FcStrBuf *buf)
8c31a243
BE
467{
468 FcPattern *subpat;
469
470 if (!expect_char (c, '-'))
471 return FcFalse;
472
473 subpat = FcPatternDuplicate (pat);
474 if (!subpat)
475 return FcFalse;
476
477 do
478 {
2017a5eb 479 if (!read_word (c))
8c31a243
BE
480 {
481 FcPatternDestroy (subpat);
482 return FcFalse;
483 }
484
2017a5eb 485 FcPatternDel (subpat, (const char *) c->word);
8c31a243
BE
486 }
487 while (consume_char (c, ','));
488
489 if (!interpret_subexpr (c, subpat, buf))
490 return FcFalse;
491
492 FcPatternDestroy (subpat);
493 return FcTrue;
494}
495
7717b25f 496static FcBool
2017a5eb
BE
497interpret_cond (FcFormatContext *c,
498 FcPattern *pat,
499 FcStrBuf *buf)
7717b25f
BE
500{
501 FcBool pass;
502
503 if (!expect_char (c, '?'))
504 return FcFalse;
505
506 pass = FcTrue;
507
508 do
509 {
510 FcBool negate;
511 FcValue v;
512
513 negate = consume_char (c, '!');
514
2017a5eb 515 if (!read_word (c))
7717b25f
BE
516 return FcFalse;
517
518 pass = pass &&
519 (negate ^
cdfb7658
BE
520 (FcResultMatch ==
521 FcPatternGet (pat, (const char *) c->word, 0, &v)));
7717b25f
BE
522 }
523 while (consume_char (c, ','));
524
525 if (pass)
526 {
527 if (!interpret_subexpr (c, pat, buf) ||
528 !maybe_skip_subexpr (c))
529 return FcFalse;
530 }
531 else
532 {
533 if (!skip_subexpr (c) ||
534 !maybe_interpret_subexpr (c, pat, buf))
535 return FcFalse;
536 }
537
538 return FcTrue;
539}
540
b6a23028
BE
541static FcBool
542interpret_count (FcFormatContext *c,
543 FcPattern *pat,
544 FcStrBuf *buf)
545{
546 int count;
547 FcPatternElt *e;
548 FcChar8 buf_static[64];
549
550 if (!expect_char (c, '#'))
551 return FcFalse;
552
553 if (!read_word (c))
554 return FcFalse;
555
556 count = 0;
557 e = FcPatternObjectFindElt (pat,
558 FcObjectFromName ((const char *) c->word));
559 if (e)
560 {
561 FcValueListPtr l;
562 count++;
563 for (l = FcPatternEltValues(e);
564 l->next;
565 l = l->next)
566 count++;
567 }
568
c8f5933d 569 snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
b6a23028
BE
570 FcStrBufString (buf, buf_static);
571
572 return FcTrue;
573}
574
cdfb7658 575static FcBool
caeea376
BE
576interpret_enumerate (FcFormatContext *c,
577 FcPattern *pat,
578 FcStrBuf *buf)
cdfb7658
BE
579{
580 FcObjectSet *os;
581 FcPattern *subpat;
582 const FcChar8 *format_save;
583 int idx;
584 FcBool ret, done;
9d58d01c 585 FcStrList *lang_strs;
cdfb7658
BE
586
587 if (!expect_char (c, '[') ||
588 !expect_char (c, ']'))
589 return FcFalse;
590
591 os = FcObjectSetCreate ();
592 if (!os)
593 return FcFalse;
594
595 ret = FcTrue;
596
597 do
598 {
599 if (!read_word (c) ||
600 !FcObjectSetAdd (os, (const char *) c->word))
601 {
602 FcObjectSetDestroy (os);
603 return FcFalse;
604 }
605 }
606 while (consume_char (c, ','));
607
9d58d01c
BE
608 /* If we have one element and it's of type FcLangSet, we want
609 * to enumerate the languages in it. */
610 lang_strs = NULL;
611 if (os->nobject == 1)
612 {
613 FcLangSet *langset;
614 if (FcResultMatch ==
c76ed777 615 FcPatternGetLangSet (pat, os->objects[0], 0, &langset))
9d58d01c
BE
616 {
617 FcStrSet *ss;
618 if (!(ss = FcLangSetGetLangs (langset)) ||
619 !(lang_strs = FcStrListCreate (ss)))
620 goto bail0;
621 }
622 }
623
cdfb7658
BE
624 subpat = FcPatternDuplicate (pat);
625 if (!subpat)
626 goto bail0;
627
628 format_save = c->format;
629 idx = 0;
630 do
631 {
632 int i;
633
634 done = FcTrue;
635
9d58d01c 636 if (lang_strs)
cdfb7658 637 {
9d58d01c
BE
638 FcChar8 *lang;
639
640 FcPatternDel (subpat, os->objects[0]);
641 if ((lang = FcStrListNext (lang_strs)))
cdfb7658 642 {
25afea87 643 /* XXX binding? */
9d58d01c 644 FcPatternAddString (subpat, os->objects[0], lang);
cdfb7658
BE
645 done = FcFalse;
646 }
647 }
9d58d01c
BE
648 else
649 {
650 for (i = 0; i < os->nobject; i++)
651 {
652 FcValue v;
653
654 /* XXX this can be optimized by accessing valuelist linked lists
655 * directly and remembering where we were. Most (all) value lists
656 * in normal uses are pretty short though (language tags are
657 * stored as a LangSet, not separate values.). */
658 FcPatternDel (subpat, os->objects[i]);
659 if (FcResultMatch ==
660 FcPatternGet (pat, os->objects[i], idx, &v))
661 {
25afea87 662 /* XXX binding */
9d58d01c
BE
663 FcPatternAdd (subpat, os->objects[i], v, FcFalse);
664 done = FcFalse;
665 }
666 }
667 }
cdfb7658
BE
668
669 if (!done)
670 {
671 c->format = format_save;
672 ret = interpret_subexpr (c, subpat, buf);
673 if (!ret)
674 goto bail;
675 }
676
677 idx++;
678 } while (!done);
679
9d58d01c
BE
680 if (c->format == format_save)
681 skip_subexpr (c);
682
cdfb7658
BE
683bail:
684 FcPatternDestroy (subpat);
685bail0:
9d58d01c
BE
686 if (lang_strs)
687 FcStrListDone (lang_strs);
cdfb7658
BE
688 FcObjectSetDestroy (os);
689
690 return ret;
691}
692
b6a23028
BE
693static FcBool
694interpret_simple (FcFormatContext *c,
695 FcPattern *pat,
696 FcStrBuf *buf)
697{
698 FcPatternElt *e;
699 FcBool add_colon = FcFalse;
700 FcBool add_elt_name = FcFalse;
9c83a837 701 int idx;
0673ef38 702 FcChar8 *else_string;
b6a23028
BE
703
704 if (consume_char (c, ':'))
705 add_colon = FcTrue;
706
707 if (!read_word (c))
708 return FcFalse;
709
9c83a837
BE
710 idx = -1;
711 if (consume_char (c, '['))
712 {
713 idx = strtol ((const char *) c->format, (char **) &c->format, 10);
714 if (idx < 0)
715 {
716 message ("expected non-negative number at %d",
717 c->format-1 - c->format_orig + 1);
718 return FcFalse;
719 }
cdfb7658
BE
720 if (!expect_char (c, ']'))
721 return FcFalse;
9c83a837
BE
722 }
723
b6a23028
BE
724 if (consume_char (c, '='))
725 add_elt_name = FcTrue;
726
0673ef38
BE
727 /* modifiers */
728 else_string = NULL;
729 if (consume_char (c, ':'))
730 {
731 FcChar8 *orig;
732 /* divert the c->word for now */
733 orig = c->word;
734 c->word = c->word + strlen ((const char *) c->word) + 1;
735 /* for now we just support 'default value' */
736 if (!expect_char (c, '-') ||
a15ac5d3 737 !read_chars (c, '|'))
0673ef38
BE
738 {
739 c->word = orig;
740 return FcFalse;
741 }
742 else_string = c->word;
743 c->word = orig;
744 }
745
b6a23028
BE
746 e = FcPatternObjectFindElt (pat,
747 FcObjectFromName ((const char *) c->word));
f6d83439 748 if (e || else_string)
b6a23028 749 {
f6d83439 750 FcValueListPtr l = NULL;
b6a23028
BE
751
752 if (add_colon)
753 FcStrBufChar (buf, ':');
754 if (add_elt_name)
755 {
756 FcStrBufString (buf, c->word);
757 FcStrBufChar (buf, '=');
758 }
759
f6d83439
BE
760 if (e)
761 l = FcPatternEltValues(e);
9c83a837
BE
762
763 if (idx != -1)
764 {
765 while (l && idx > 0)
766 {
767 l = FcValueListNext(l);
768 idx--;
769 }
770 if (l && idx == 0)
771 {
772 if (!FcNameUnparseValue (buf, &l->value, '\0'))
773 return FcFalse;
774 }
775 else goto notfound;
776 }
f6d83439 777 else if (l)
9c83a837
BE
778 {
779 FcNameUnparseValueList (buf, l, '\0');
780 }
f6d83439
BE
781 else
782 {
783 notfound:
784 if (else_string)
785 FcStrBufString (buf, else_string);
786 }
b6a23028
BE
787 }
788
789 return FcTrue;
790}
791
85c7fb67
BE
792static FcBool
793cescape (FcFormatContext *c,
d04a7507
BE
794 const FcChar8 *str,
795 FcStrBuf *buf)
ced38254 796{
25afea87
BE
797 /* XXX escape \n etc? */
798
ced38254
BE
799 while(*str)
800 {
801 switch (*str)
802 {
803 case '\\':
804 case '"':
85c7fb67 805 FcStrBufChar (buf, '\\');
ced38254
BE
806 break;
807 }
85c7fb67 808 FcStrBufChar (buf, *str++);
ced38254 809 }
85c7fb67 810 return FcTrue;
ced38254
BE
811}
812
85c7fb67
BE
813static FcBool
814shescape (FcFormatContext *c,
d04a7507
BE
815 const FcChar8 *str,
816 FcStrBuf *buf)
ced38254 817{
85c7fb67 818 FcStrBufChar (buf, '\'');
ced38254
BE
819 while(*str)
820 {
821 if (*str == '\'')
85c7fb67 822 FcStrBufString (buf, (const FcChar8 *) "'\\''");
ced38254 823 else
85c7fb67 824 FcStrBufChar (buf, *str);
ced38254
BE
825 str++;
826 }
85c7fb67
BE
827 FcStrBufChar (buf, '\'');
828 return FcTrue;
ced38254
BE
829}
830
85c7fb67
BE
831static FcBool
832xmlescape (FcFormatContext *c,
d04a7507
BE
833 const FcChar8 *str,
834 FcStrBuf *buf)
ced38254 835{
25afea87
BE
836 /* XXX escape \n etc? */
837
ced38254
BE
838 while(*str)
839 {
840 switch (*str)
841 {
85c7fb67
BE
842 case '&': FcStrBufString (buf, (const FcChar8 *) "&amp;"); break;
843 case '<': FcStrBufString (buf, (const FcChar8 *) "&lt;"); break;
844 case '>': FcStrBufString (buf, (const FcChar8 *) "&gt;"); break;
845 default: FcStrBufChar (buf, *str); break;
ced38254
BE
846 }
847 str++;
848 }
85c7fb67 849 return FcTrue;
ced38254
BE
850}
851
85c7fb67 852static FcBool
c8f5933d 853delete_chars (FcFormatContext *c,
d04a7507
BE
854 const FcChar8 *str,
855 FcStrBuf *buf)
c8f5933d 856{
c8f5933d
BE
857 /* XXX not UTF-8 aware */
858
859 if (!expect_char (c, '(') ||
860 !read_chars (c, ')') ||
861 !expect_char (c, ')'))
85c7fb67 862 return FcFalse;
c8f5933d 863
c8f5933d
BE
864 while(*str)
865 {
866 FcChar8 *p;
867
868 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
869 if (p)
870 {
85c7fb67 871 FcStrBufData (buf, str, p - str);
c8f5933d
BE
872 str = p + 1;
873 }
874 else
875 {
85c7fb67 876 FcStrBufString (buf, str);
c8f5933d
BE
877 break;
878 }
879
880 }
85c7fb67
BE
881
882 return FcTrue;
c8f5933d
BE
883}
884
85c7fb67 885static FcBool
c8f5933d 886escape_chars (FcFormatContext *c,
d04a7507
BE
887 const FcChar8 *str,
888 FcStrBuf *buf)
c8f5933d 889{
c8f5933d
BE
890 /* XXX not UTF-8 aware */
891
892 if (!expect_char (c, '(') ||
893 !read_chars (c, ')') ||
894 !expect_char (c, ')'))
85c7fb67 895 return FcFalse;
c8f5933d 896
c8f5933d
BE
897 while(*str)
898 {
899 FcChar8 *p;
900
901 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
902 if (p)
903 {
85c7fb67
BE
904 FcStrBufData (buf, str, p - str);
905 FcStrBufChar (buf, c->word[0]);
906 FcStrBufChar (buf, *p);
c8f5933d
BE
907 str = p + 1;
908 }
909 else
910 {
85c7fb67 911 FcStrBufString (buf, str);
c8f5933d
BE
912 break;
913 }
914
915 }
85c7fb67
BE
916
917 return FcTrue;
c8f5933d
BE
918}
919
85c7fb67 920static FcBool
c8f5933d 921translate_chars (FcFormatContext *c,
d04a7507
BE
922 const FcChar8 *str,
923 FcStrBuf *buf)
c8f5933d 924{
c8f5933d
BE
925 char *from, *to, repeat;
926 int from_len, to_len;
927
928 /* XXX not UTF-8 aware */
929
930 if (!expect_char (c, '(') ||
931 !read_chars (c, ',') ||
932 !expect_char (c, ','))
85c7fb67 933 return FcFalse;
c8f5933d
BE
934
935 from = (char *) c->word;
936 from_len = strlen (from);
937 to = from + from_len + 1;
938
0673ef38 939 /* hack: we temporarily divert c->word */
c8f5933d
BE
940 c->word = (FcChar8 *) to;
941 if (!read_chars (c, ')'))
942 {
943 c->word = (FcChar8 *) from;
944 return FcFalse;
945 }
946 c->word = (FcChar8 *) from;
947
948 to_len = strlen (to);
949 repeat = to[to_len - 1];
950
951 if (!expect_char (c, ')'))
952 return FcFalse;
953
c8f5933d
BE
954 while(*str)
955 {
956 FcChar8 *p;
957
958 p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
959 if (p)
960 {
961 int i;
85c7fb67 962 FcStrBufData (buf, str, p - str);
c8f5933d 963 i = strchr (from, *p) - from;
85c7fb67 964 FcStrBufChar (buf, i < to_len ? to[i] : repeat);
c8f5933d
BE
965 str = p + 1;
966 }
967 else
968 {
85c7fb67 969 FcStrBufString (buf, str);
c8f5933d
BE
970 break;
971 }
972
973 }
85c7fb67
BE
974
975 return FcTrue;
c8f5933d
BE
976}
977
85c7fb67
BE
978static FcBool
979interpret_convert (FcFormatContext *c,
980 FcStrBuf *buf,
981 int start)
2017a5eb 982{
85c7fb67
BE
983 const FcChar8 *str;
984 FcChar8 *new_str;
985 FcStrBuf new_buf;
986 FcChar8 buf_static[8192];
987 FcBool ret;
988
d04a7507
BE
989 if (!expect_char (c, '|') ||
990 !read_word (c))
85c7fb67
BE
991 return FcFalse;
992
d04a7507 993 /* prepare the buffer */
85c7fb67
BE
994 FcStrBufChar (buf, '\0');
995 if (buf->failed)
996 return FcFalse;
997 str = buf->buf + start;
998 buf->len = start;
999
85c7fb67
BE
1000 /* try simple converters first */
1001 if (0) { }
ced38254
BE
1002#define CONVERTER(name, func) \
1003 else if (0 == strcmp ((const char *) c->word, name))\
85c7fb67 1004 do { new_str = func (str); ret = FcTrue; } while (0)
c8f5933d
BE
1005 CONVERTER ("downcase", FcStrDowncase);
1006 CONVERTER ("basename", FcStrBasename);
1007 CONVERTER ("dirname", FcStrDirname);
85c7fb67
BE
1008#undef CONVERTER
1009 else
1010 ret = FcFalse;
1011
1012 if (ret)
1013 {
1014 if (new_str)
1015 {
85c7fb67
BE
1016 FcStrBufString (buf, new_str);
1017 free (new_str);
1018 return FcTrue;
1019 }
1020 else
1021 return FcFalse;
1022 }
1023
1024 FcStrBufInit (&new_buf, buf_static, sizeof (buf_static));
1025
1026 /* now try our custom converters */
1027 if (0) { }
1028#define CONVERTER(name, func) \
1029 else if (0 == strcmp ((const char *) c->word, name))\
d04a7507 1030 ret = func (c, str, &new_buf)
85c7fb67
BE
1031 CONVERTER ("cescape", cescape);
1032 CONVERTER ("shescape", shescape);
1033 CONVERTER ("xmlescape", xmlescape);
1034 CONVERTER ("delete", delete_chars);
1035 CONVERTER ("escape", escape_chars);
1036 CONVERTER ("translate", translate_chars);
1037#undef CONVERTER
1038 else
1039 ret = FcFalse;
1040
1041 if (ret)
1042 {
1043 FcStrBufChar (&new_buf, '\0');
1044 FcStrBufString (buf, new_buf.buf);
1045 }
1046 else
1047 message ("unknown converter \"%s\"",
1048 c->word);
1049
1050 FcStrBufDestroy (&new_buf);
1051
1052 return ret;
2017a5eb
BE
1053}
1054
1055static FcBool
1056maybe_interpret_converts (FcFormatContext *c,
1057 FcStrBuf *buf,
1058 int start)
1059{
85c7fb67
BE
1060 while (*c->format == '|')
1061 if (!interpret_convert (c, buf, start))
2017a5eb
BE
1062 return FcFalse;
1063
2017a5eb
BE
1064 return FcTrue;
1065}
1066
1067static FcBool
1068align_to_width (FcStrBuf *buf,
1069 int start,
1070 int width)
1071{
1072 int len;
1073
1074 if (buf->failed)
1075 return FcFalse;
1076
1077 len = buf->len - start;
1078 if (len < -width)
1079 {
1080 /* left align */
1081 while (len++ < -width)
1082 FcStrBufChar (buf, ' ');
1083 }
1084 else if (len < width)
1085 {
1086 int old_len;
1087 old_len = len;
1088 /* right align */
1089 while (len++ < width)
1090 FcStrBufChar (buf, ' ');
1091 if (buf->failed)
1092 return FcFalse;
1093 len = old_len;
1094 memmove (buf->buf + buf->len - len,
1095 buf->buf + buf->len - width,
1096 len);
1097 memset (buf->buf + buf->len - width,
1098 ' ',
1099 width - len);
1100 }
1101
1102 return !buf->failed;
1103}
8c31a243
BE
1104static FcBool
1105interpret_percent (FcFormatContext *c,
1106 FcPattern *pat,
1107 FcStrBuf *buf)
1108{
2017a5eb
BE
1109 int width, start;
1110 FcBool ret;
8c31a243
BE
1111
1112 if (!expect_char (c, '%'))
1113 return FcFalse;
1114
1115 if (consume_char (c, '%')) /* "%%" */
1116 {
1117 FcStrBufChar (buf, '%');
1118 return FcTrue;
1119 }
1120
1121 /* parse an optional width specifier */
1122 width = strtol ((const char *) c->format, (char **) &c->format, 10);
1123
8c31a243
BE
1124 if (!expect_char (c, '{'))
1125 return FcFalse;
1126
2017a5eb 1127 start = buf->len;
7717b25f 1128
2017a5eb 1129 switch (*c->format) {
caeea376
BE
1130 case '=': ret = interpret_builtin (c, pat, buf); break;
1131 case '{': ret = interpret_subexpr (c, pat, buf); break;
1132 case '+': ret = interpret_filter_in (c, pat, buf); break;
1133 case '-': ret = interpret_filter_out (c, pat, buf); break;
1134 case '?': ret = interpret_cond (c, pat, buf); break;
1135 case '#': ret = interpret_count (c, pat, buf); break;
1136 case '[': ret = interpret_enumerate (c, pat, buf); break;
1137 default: ret = interpret_simple (c, pat, buf); break;
0c93b91d 1138 }
c493c3b7 1139
2017a5eb
BE
1140 return ret &&
1141 maybe_interpret_converts (c, buf, start) &&
1142 align_to_width (buf, start, width) &&
1143 expect_char (c, '}');
0c93b91d
BE
1144}
1145
8c31a243 1146static FcBool
7717b25f
BE
1147interpret_expr (FcFormatContext *c,
1148 FcPattern *pat,
1149 FcStrBuf *buf,
1150 FcChar8 term)
0c93b91d 1151{
7717b25f 1152 while (*c->format && *c->format != term)
0c93b91d 1153 {
27b3e2dd 1154 switch (*c->format)
0c93b91d
BE
1155 {
1156 case '\\':
27b3e2dd
BE
1157 c->format++; /* skip over '\\' */
1158 if (*c->format)
1159 FcStrBufChar (buf, escaped_char (*c->format++));
0c93b91d
BE
1160 continue;
1161 case '%':
8c31a243
BE
1162 if (!interpret_percent (c, pat, buf))
1163 return FcFalse;
0c93b91d
BE
1164 continue;
1165 }
27b3e2dd 1166 FcStrBufChar (buf, *c->format++);
0c93b91d 1167 }
8c31a243 1168 return FcTrue;
0c93b91d
BE
1169}
1170
d04a7507
BE
1171static FcBool
1172FcPatternFormatToBuf (FcPattern *pat,
1173 const FcChar8 *format,
1174 FcStrBuf *buf)
1175{
1176 FcFormatContext c;
1177 FcChar8 word_static[1024];
1178 FcBool ret;
1179
1180 if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static)))
1181 return FcFalse;
1182
1183 ret = interpret_expr (&c, pat, buf, '\0');
1184
1185 FcFormatContextDone (&c);
1186
1187 return ret;
1188}
1189
0c93b91d 1190FcChar8 *
d04a7507
BE
1191FcPatternFormat (FcPattern *pat,
1192 const FcChar8 *format)
0c93b91d 1193{
ced38254 1194 FcStrBuf buf;
85c7fb67 1195 FcChar8 buf_static[8192 - 1024];
ced38254 1196 FcBool ret;
0c93b91d 1197
ced38254 1198 FcStrBufInit (&buf, buf_static, sizeof (buf_static));
0c93b91d 1199
d04a7507 1200 ret = FcPatternFormatToBuf (pat, format, &buf);
0c93b91d 1201
8c31a243
BE
1202 if (ret)
1203 return FcStrBufDone (&buf);
1204 else
1205 {
1206 FcStrBufDestroy (&buf);
1207 return NULL;
1208 }
0c93b91d
BE
1209}
1210
1211#define __fcformat__
1212#include "fcaliastail.h"
1213#undef __fcformat__