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