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