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