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

« back to all changes in this revision

Viewing changes to src/fcformat.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathieu Trudel
  • Date: 2010-01-14 13:23:47 UTC
  • mfrom: (1.1.7 upstream) (0.1.8 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100114132347-eokx9c6dvdf3q40b
Tags: 2.8.0-2ubuntu1
* Merge from Debian testing, remaining changes: (LP: #490326)
  - fix buildd bustage by ignoring errors in postinst and prerm (taken
    from debian NMU by Riku Voipio <riku.voipio@iki.fi>)
    - add debian/fontconfig.prerm
    - update debian/fontconfig.postinst
  - debian/fontconfig-config.postinst: drop debconf transition;
    we remove conf.d links for hinting, subpixel and bitmap configs
    and stop looking at debconf, excluding 70-no-bitmaps.conf.
  - debian/fontconfig-config.postinst: also exclude 70-no-bitmaps.conf
    from "drop debconf transition" to ensure that it doesn't get removed
    automatically
    - keep adding it in CONF_FILES through 07_no_bitmaps.patch as well.
  - debian/patches/00_old_diff_gz.patch:
    - add MgOpen Moderna family to 40-nonlatin.conf
    - reorder Bitstream Vera and DejaVu families in 60-latin.conf
  - debian/patches/00_old_diff_gz.patch,
    debian/patches/04_ubuntu_monospace_lcd_filter_conf.patch,
    debian/patches/05_ubuntu_add_hinting_and_antialiasing_confs.patch:
    - add extra config files:
      - antialias and hinting.
      - 52-languageselector.conf
      - 53-monospace-lcd-filter.conf
  - debian/patches/05_lcdfilterlegacy.patch: Recognize const value 
    "lcdfilterlegacy", introduced before upstream did introduce "lcdlegacy"
  - debian/patches/06_ubuntu_lcddefault.patch: set lcddefault as default
  - drop debian/fontconfig-config.templates, debian/fontconfig-config.config,
    and associtated po files.
* purge debconf database when upgrading from previous versions.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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__