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