~ubuntu-branches/ubuntu/lucid/fontconfig/lucid

0.2.2 by Keith Packard
Import upstream version 2.8.0
1
/*
2
 * Copyright © 2008,2009 Red Hat, Inc.
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
 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18
 * EVENT SHALL THE AUTHOR(S) 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
31
/* The language is documented in doc/fcformat.fncs
32
 * These are the features implemented:
33
 *
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}
49
 *
50
 * converters:
51
 * basename	FcStrBasename
52
 * dirname	FcStrDirname
53
 * downcase	FcStrDowncase
54
 * shescape
55
 * cescape
56
 * xmlescape
57
 * delete	delete chars
58
 * escape	escape chars
59
 * translate	translate chars
60
 *
61
 * builtins:
62
 * unparse	FcNameUnparse
63
 * fcmatch	fc-match default
64
 * fclist	fc-list default
65
 * pkgkit	PackageKit package tag format
66
 *
67
 *
68
 * Some ideas for future syntax extensions:
69
 *
70
 * - verbose builtin that is like FcPatternPrint
71
 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
72
 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
73
 */
74
75
76
#define FCMATCH_FORMAT	"%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
77
#define FCLIST_FORMAT	"%{?file{%{file}: }}%{=unparse}"
78
#define PKGKIT_FORMAT	"%{[]family{font(%{family|downcase|delete( )})\n}}%{[]lang{font(:lang=%{lang|downcase|translate(_,-)})\n}}"
79
80
81
static void
82
message (const char *fmt, ...)
83
{
84
    va_list	args;
85
    va_start (args, fmt);
86
    fprintf (stderr, "Fontconfig: Pattern format error: ");
87
    vfprintf (stderr, fmt, args);
88
    fprintf (stderr, ".\n");
89
    va_end (args);
90
}
91
92
93
typedef struct _FcFormatContext
94
{
95
    const FcChar8 *format_orig;
96
    const FcChar8 *format;
97
    int            format_len;
98
    FcChar8       *word;
99
    FcBool         word_allocated;
100
} FcFormatContext;
101
102
static FcBool
103
FcFormatContextInit (FcFormatContext *c,
104
		     const FcChar8   *format,
105
		     FcChar8         *scratch,
106
		     int              scratch_len)
107
{
108
    c->format_orig = c->format = format;
109
    c->format_len = strlen ((const char *) format);
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
    }
121
122
    return c->word != NULL;
123
}
124
125
static void
126
FcFormatContextDone (FcFormatContext *c)
127
{
128
    if (c && c->word_allocated)
129
    {
130
	free (c->word);
131
    }
132
}
133
134
static FcBool
135
consume_char (FcFormatContext *c,
136
	      FcChar8          term)
137
{
138
    if (*c->format != term)
139
	return FcFalse;
140
141
    c->format++;
142
    return FcTrue;
143
}
144
145
static FcBool
146
expect_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
162
static FcBool
163
FcCharIsPunct (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
182
static 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
196
static FcBool
197
read_word (FcFormatContext *c)
198
{
199
    FcChar8 *p;
200
201
    p = c->word;
202
203
    while (*c->format)
204
    {
205
	if (*c->format == '\\')
206
	{
207
	    c->format++;
208
	    if (*c->format)
209
	      *p++ = escaped_char (*c->format++);
210
	    continue;
211
	}
212
	else if (FcCharIsPunct (*c->format))
213
	    break;
214
215
	*p++ = *c->format++;
216
    }
217
    *p = '\0';
218
219
    if (p == c->word)
220
    {
221
	message ("expected identifier at %d",
222
		 c->format - c->format_orig + 1);
223
	return FcFalse;
224
    }
225
226
    return FcTrue;
227
}
228
229
static FcBool
230
read_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
261
static FcBool
262
FcPatternFormatToBuf (FcPattern     *pat,
263
		      const FcChar8 *format,
264
		      FcStrBuf      *buf);
265
266
static FcBool
267
interpret_builtin (FcFormatContext *c,
268
		   FcPattern       *pat,
269
		   FcStrBuf        *buf)
270
{
271
    FcChar8       *new_str;
272
    FcBool         ret;
273
274
    if (!expect_char (c, '=') ||
275
	!read_word (c))
276
	return FcFalse;
277
278
    /* try simple builtins first */
279
    if (0) { }
280
#define BUILTIN(name, func) \
281
    else if (0 == strcmp ((const char *) c->word, name))\
282
	do { new_str = func (pat); ret = FcTrue; } while (0)
283
    BUILTIN ("unparse",  FcNameUnparse);
284
 /* BUILTIN ("verbose",  FcPatternPrint); XXX */
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);
308
    BUILTIN ("pkgkit",   PKGKIT_FORMAT);
309
#undef BUILTIN
310
    else
311
	ret = FcFalse;
312
313
    if (!ret)
314
	message ("unknown builtin \"%s\"",
315
		 c->word);
316
317
    return ret;
318
}
319
320
static FcBool
321
interpret_expr (FcFormatContext *c,
322
		FcPattern       *pat,
323
		FcStrBuf        *buf,
324
		FcChar8          term);
325
326
static FcBool
327
interpret_subexpr (FcFormatContext *c,
328
		   FcPattern       *pat,
329
		   FcStrBuf        *buf)
330
{
331
    return expect_char (c, '{') &&
332
	   interpret_expr (c, pat, buf, '}') &&
333
	   expect_char (c, '}');
334
}
335
336
static FcBool
337
maybe_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
346
static FcBool
347
skip_subexpr (FcFormatContext *c);
348
349
static FcBool
350
skip_percent (FcFormatContext *c)
351
{
352
    int width;
353
354
    if (!expect_char (c, '%'))
355
	return FcFalse;
356
357
    /* skip an optional width specifier */
358
    width = strtol ((const char *) c->format, (char **) &c->format, 10);
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
383
static FcBool
384
skip_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
406
static FcBool
407
skip_subexpr (FcFormatContext *c)
408
{
409
    return expect_char (c, '{') &&
410
	   skip_expr (c) &&
411
	   expect_char (c, '}');
412
}
413
414
static FcBool
415
maybe_skip_subexpr (FcFormatContext *c)
416
{
417
    return (*c->format == '{') ?
418
	   skip_subexpr (c) :
419
	   FcTrue;
420
}
421
422
static FcBool
423
interpret_filter_in (FcFormatContext *c,
424
		     FcPattern       *pat,
425
		     FcStrBuf        *buf)
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
    {
439
	if (!read_word (c) ||
440
	    !FcObjectSetAdd (os, (const char *) c->word))
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
459
static FcBool
460
interpret_filter_out (FcFormatContext *c,
461
		      FcPattern       *pat,
462
		      FcStrBuf        *buf)
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
    {
475
	if (!read_word (c))
476
	{
477
	    FcPatternDestroy (subpat);
478
	    return FcFalse;
479
	}
480
481
	FcPatternDel (subpat, (const char *) c->word);
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
492
static FcBool
493
interpret_cond (FcFormatContext *c,
494
		FcPattern       *pat,
495
		FcStrBuf        *buf)
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
511
	if (!read_word (c))
512
	    return FcFalse;
513
514
	pass = pass &&
515
	       (negate ^
516
		(FcResultMatch ==
517
		 FcPatternGet (pat, (const char *) c->word, 0, &v)));
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
537
static FcBool
538
interpret_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
565
    snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
566
    FcStrBufString (buf, buf_static);
567
568
    return FcTrue;
569
}
570
571
static FcBool
572
interpret_enumerate (FcFormatContext *c,
573
		     FcPattern       *pat,
574
		     FcStrBuf        *buf)
575
{
576
    FcObjectSet   *os;
577
    FcPattern     *subpat;
578
    const FcChar8 *format_save;
579
    int            idx;
580
    FcBool         ret, done;
581
    FcStrList      *lang_strs;
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
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
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
632
	if (lang_strs)
633
	{
634
	    FcChar8 *lang;
635
636
	    FcPatternDel (subpat, os->objects[0]);
637
	    if ((lang = FcStrListNext (lang_strs)))
638
	    {
639
		FcPatternAddString (subpat, os->objects[0], lang);
640
		done = FcFalse;
641
	    }
642
	}
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
	}
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
674
    if (c->format == format_save)
675
	skip_subexpr (c);
676
677
bail:
678
    FcPatternDestroy (subpat);
679
bail0:
680
    if (lang_strs)
681
	FcStrListDone (lang_strs);
682
    FcObjectSetDestroy (os);
683
684
    return ret;
685
}
686
687
static FcBool
688
interpret_simple (FcFormatContext *c,
689
		  FcPattern       *pat,
690
		  FcStrBuf        *buf)
691
{
692
    FcPatternElt *e;
693
    FcBool        add_colon = FcFalse;
694
    FcBool        add_elt_name = FcFalse;
695
    int           idx;
696
    FcChar8      *else_string;
697
698
    if (consume_char (c, ':'))
699
	add_colon = FcTrue;
700
701
    if (!read_word (c))
702
	return FcFalse;
703
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
	}
714
	if (!expect_char (c, ']'))
715
	    return FcFalse;
716
    }
717
718
    if (consume_char (c, '='))
719
	add_elt_name = FcTrue;
720
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
740
    e = FcPatternObjectFindElt (pat,
741
				FcObjectFromName ((const char *) c->word));
742
    if (e || else_string)
743
    {
744
	FcValueListPtr l = NULL;
745
746
	if (add_colon)
747
	    FcStrBufChar (buf, ':');
748
	if (add_elt_name)
749
	{
750
	    FcStrBufString (buf, c->word);
751
	    FcStrBufChar (buf, '=');
752
	}
753
754
	if (e)
755
	    l = FcPatternEltValues(e);
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
        }
771
	else if (l)
772
	{
773
	    FcNameUnparseValueList (buf, l, '\0');
774
	}
775
	else
776
	{
777
    notfound:
778
	    if (else_string)
779
		FcStrBufString (buf, else_string);
780
	}
781
    }
782
783
    return FcTrue;
784
}
785
786
static FcBool
787
cescape (FcFormatContext *c,
788
	 const FcChar8   *str,
789
	 FcStrBuf        *buf)
790
{
791
    while(*str)
792
    {
793
	switch (*str)
794
	{
795
	case '\\':
796
	case '"':
797
	    FcStrBufChar (buf, '\\');
798
	    break;
799
	}
800
	FcStrBufChar (buf, *str++);
801
    }
802
    return FcTrue;
803
}
804
805
static FcBool
806
shescape (FcFormatContext *c,
807
	  const FcChar8   *str,
808
	  FcStrBuf        *buf)
809
{
810
    FcStrBufChar (buf, '\'');
811
    while(*str)
812
    {
813
	if (*str == '\'')
814
	    FcStrBufString (buf, (const FcChar8 *) "'\\''");
815
	else
816
	    FcStrBufChar (buf, *str);
817
	str++;
818
    }
819
    FcStrBufChar (buf, '\'');
820
    return FcTrue;
821
}
822
823
static FcBool
824
xmlescape (FcFormatContext *c,
825
	   const FcChar8   *str,
826
	   FcStrBuf        *buf)
827
{
828
    while(*str)
829
    {
830
	switch (*str)
831
	{
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;
836
	}
837
	str++;
838
    }
839
    return FcTrue;
840
}
841
842
static FcBool
843
delete_chars (FcFormatContext *c,
844
	      const FcChar8   *str,
845
	      FcStrBuf        *buf)
846
{
847
    /* XXX not UTF-8 aware */
848
849
    if (!expect_char (c, '(') ||
850
	!read_chars (c, ')') ||
851
	!expect_char (c, ')'))
852
	return FcFalse;
853
854
    while(*str)
855
    {
856
	FcChar8 *p;
857
858
	p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
859
	if (p)
860
	{
861
	    FcStrBufData (buf, str, p - str);
862
	    str = p + 1;
863
	}
864
	else
865
	{
866
	    FcStrBufString (buf, str);
867
	    break;
868
	}
869
870
    }
871
872
    return FcTrue;
873
}
874
875
static FcBool
876
escape_chars (FcFormatContext *c,
877
	      const FcChar8   *str,
878
	      FcStrBuf        *buf)
879
{
880
    /* XXX not UTF-8 aware */
881
882
    if (!expect_char (c, '(') ||
883
	!read_chars (c, ')') ||
884
	!expect_char (c, ')'))
885
	return FcFalse;
886
887
    while(*str)
888
    {
889
	FcChar8 *p;
890
891
	p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
892
	if (p)
893
	{
894
	    FcStrBufData (buf, str, p - str);
895
	    FcStrBufChar (buf, c->word[0]);
896
	    FcStrBufChar (buf, *p);
897
	    str = p + 1;
898
	}
899
	else
900
	{
901
	    FcStrBufString (buf, str);
902
	    break;
903
	}
904
905
    }
906
907
    return FcTrue;
908
}
909
910
static FcBool
911
translate_chars (FcFormatContext *c,
912
		 const FcChar8   *str,
913
		 FcStrBuf        *buf)
914
{
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, ','))
923
	return FcFalse;
924
925
    from = (char *) c->word;
926
    from_len = strlen (from);
927
    to = from + from_len + 1;
928
929
    /* hack: we temporarily divert c->word */
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
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;
952
	    FcStrBufData (buf, str, p - str);
953
	    i = strchr (from, *p) - from;
954
	    FcStrBufChar (buf, i < to_len ? to[i] : repeat);
955
	    str = p + 1;
956
	}
957
	else
958
	{
959
	    FcStrBufString (buf, str);
960
	    break;
961
	}
962
963
    }
964
965
    return FcTrue;
966
}
967
968
static FcBool
969
interpret_convert (FcFormatContext *c,
970
		   FcStrBuf        *buf,
971
		   int              start)
972
{
973
    const FcChar8 *str;
974
    FcChar8       *new_str;
975
    FcStrBuf       new_buf;
976
    FcChar8        buf_static[8192];
977
    FcBool         ret;
978
979
    if (!expect_char (c, '|') ||
980
	!read_word (c))
981
	return FcFalse;
982
983
    /* prepare the buffer */
984
    FcStrBufChar (buf, '\0');
985
    if (buf->failed)
986
	return FcFalse;
987
    str = buf->buf + start;
988
    buf->len = start;
989
990
    /* try simple converters first */
991
    if (0) { }
992
#define CONVERTER(name, func) \
993
    else if (0 == strcmp ((const char *) c->word, name))\
994
	do { new_str = func (str); ret = FcTrue; } while (0)
995
    CONVERTER  ("downcase",  FcStrDowncase);
996
    CONVERTER  ("basename",  FcStrBasename);
997
    CONVERTER  ("dirname",   FcStrDirname);
998
#undef CONVERTER
999
    else
1000
	ret = FcFalse;
1001
1002
    if (ret)
1003
    {
1004
	if (new_str)
1005
	{
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))\
1020
	ret = func (c, str, &new_buf)
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;
1043
}
1044
1045
static FcBool
1046
maybe_interpret_converts (FcFormatContext *c,
1047
			   FcStrBuf        *buf,
1048
			   int              start)
1049
{
1050
    while (*c->format == '|')
1051
	if (!interpret_convert (c, buf, start))
1052
	    return FcFalse;
1053
1054
    return FcTrue;
1055
}
1056
1057
static FcBool
1058
align_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
}
1094
static FcBool
1095
interpret_percent (FcFormatContext *c,
1096
		   FcPattern       *pat,
1097
		   FcStrBuf        *buf)
1098
{
1099
    int width, start;
1100
    FcBool ret;
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
1114
    if (!expect_char (c, '{'))
1115
	return FcFalse;
1116
1117
    start = buf->len;
1118
1119
    switch (*c->format) {
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;
1128
    }
1129
1130
    return ret &&
1131
	   maybe_interpret_converts (c, buf, start) &&
1132
	   align_to_width (buf, start, width) &&
1133
	   expect_char (c, '}');
1134
}
1135
1136
static FcBool
1137
interpret_expr (FcFormatContext *c,
1138
		FcPattern       *pat,
1139
		FcStrBuf        *buf,
1140
		FcChar8          term)
1141
{
1142
    while (*c->format && *c->format != term)
1143
    {
1144
	switch (*c->format)
1145
	{
1146
	case '\\':
1147
	    c->format++; /* skip over '\\' */
1148
	    if (*c->format)
1149
		FcStrBufChar (buf, escaped_char (*c->format++));
1150
	    continue;
1151
	case '%':
1152
	    if (!interpret_percent (c, pat, buf))
1153
		return FcFalse;
1154
	    continue;
1155
	}
1156
	FcStrBufChar (buf, *c->format++);
1157
    }
1158
    return FcTrue;
1159
}
1160
1161
static FcBool
1162
FcPatternFormatToBuf (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
1180
FcChar8 *
1181
FcPatternFormat (FcPattern *pat,
1182
		 const FcChar8 *format)
1183
{
1184
    FcStrBuf        buf;
1185
    FcChar8         buf_static[8192 - 1024];
1186
    FcBool          ret;
1187
1188
    FcStrBufInit (&buf, buf_static, sizeof (buf_static));
1189
1190
    ret = FcPatternFormatToBuf (pat, format, &buf);
1191
1192
    if (ret)
1193
	return FcStrBufDone (&buf);
1194
    else
1195
    {
1196
	FcStrBufDestroy (&buf);
1197
	return NULL;
1198
    }
1199
}
1200
1201
#define __fcformat__
1202
#include "fcaliastail.h"
1203
#undef __fcformat__