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