]> git.wh0rd.org - fontconfig.git/blame - src/fcformat.c
[fcformat] Add element filtering and deletion
[fontconfig.git] / src / fcformat.c
CommitLineData
0c93b91d
BE
1/*
2 * Copyright © 2008 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 * 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
31static void
32message (const char *fmt, ...)
33{
34 va_list args;
35 va_start (args, fmt);
8c31a243 36 fprintf (stderr, "Fontconfig: Pattern format error: ");
0c93b91d 37 vfprintf (stderr, fmt, args);
d6506ff6 38 fprintf (stderr, ".\n");
0c93b91d
BE
39 va_end (args);
40}
41
42
27b3e2dd 43typedef struct _FcFormatContext
0c93b91d 44{
27b3e2dd
BE
45 const FcChar8 *format_orig;
46 const FcChar8 *format;
47 int format_len;
48 FcChar8 *scratch;
49} FcFormatContext;
c493c3b7 50
8c31a243 51static FcBool
27b3e2dd
BE
52FcFormatContextInit (FcFormatContext *c,
53 const FcChar8 *format)
54{
55 c->format_orig = c->format = format;
56 c->format_len = strlen ((const char *) format);
57 c->scratch = malloc (c->format_len + 1);
8c31a243
BE
58
59 return c->scratch != NULL;
27b3e2dd 60}
c493c3b7 61
27b3e2dd
BE
62static void
63FcFormatContextDone (FcFormatContext *c)
64{
65 if (c)
66 {
67 free (c->scratch);
68 }
69}
c493c3b7 70
27b3e2dd
BE
71static FcBool
72consume_char (FcFormatContext *c,
73 FcChar8 term)
74{
75 if (*c->format != term)
d6506ff6
BE
76 return FcFalse;
77
78 c->format++;
79 return FcTrue;
80}
81
82static FcBool
83expect_char (FcFormatContext *c,
84 FcChar8 term)
85{
86 FcBool res = consume_char (c, term);
87 if (!res)
88 {
89 if (c->format == c->format_orig + c->format_len)
90 message ("format ended while expecting '%c'",
91 term);
92 else
93 message ("expected '%c' at %d",
94 term, c->format - c->format_orig + 1);
95 }
96 return res;
97}
98
99static FcBool
100FcCharIsPunct (const FcChar8 c)
101{
102 if (c < '0')
103 return FcTrue;
104 if (c <= '9')
105 return FcFalse;
106 if (c < 'A')
107 return FcTrue;
108 if (c <= 'Z')
109 return FcFalse;
110 if (c < 'a')
111 return FcTrue;
112 if (c <= 'z')
113 return FcFalse;
114 if (c <= '~')
115 return FcTrue;
116 return FcFalse;
117}
118
119static FcBool
120read_elt_name_to_scratch (FcFormatContext *c)
121{
122 FcChar8 *p;
123
124 p = c->scratch;
125
126 while (*c->format)
127 {
128 if (*c->format == '\\')
129 {
130 c->format++;
131 if (*c->format)
132 c->format++;
133 continue;
134 }
135 else if (FcCharIsPunct (*c->format))
136 break;
137
138 *p++ = *c->format++;
139 }
140 *p = '\0';
141
142 if (p == c->scratch)
0c93b91d 143 {
d6506ff6
BE
144 message ("expected element name at %d",
145 c->format - c->format_orig + 1);
27b3e2dd
BE
146 return FcFalse;
147 }
0c93b91d 148
27b3e2dd
BE
149 return FcTrue;
150}
0c93b91d 151
8c31a243 152static FcBool
d6506ff6
BE
153interpret (FcFormatContext *c,
154 FcPattern *pat,
155 FcStrBuf *buf,
156 FcChar8 term);
157
8c31a243
BE
158static FcBool
159interpret_subexpr (FcFormatContext *c,
27b3e2dd
BE
160 FcPattern *pat,
161 FcStrBuf *buf)
162{
8c31a243
BE
163 return expect_char (c, '{') &&
164 interpret (c, pat, buf, '}') &&
165 expect_char (c, '}');
166}
0c93b91d 167
8c31a243
BE
168static FcBool
169interpret_simple_tag (FcFormatContext *c,
170 FcPattern *pat,
171 FcStrBuf *buf)
172{
173 FcPatternElt *e;
d6506ff6
BE
174 FcBool add_colon = FcFalse;
175 FcBool add_elt_name = FcFalse;
176
8c31a243 177 if (consume_char (c, ':'))
d6506ff6 178 add_colon = FcTrue;
d6506ff6 179
d6506ff6 180 if (!read_elt_name_to_scratch (c))
8c31a243 181 return FcFalse;
d6506ff6
BE
182
183 if (consume_char (c, '='))
184 add_elt_name = FcTrue;
185
186 e = FcPatternObjectFindElt (pat,
187 FcObjectFromName ((const char *) c->scratch));
27b3e2dd
BE
188 if (e)
189 {
190 FcValueListPtr l;
d6506ff6
BE
191
192 if (add_colon)
193 FcStrBufChar (buf, ':');
194 if (add_elt_name)
195 {
196 FcStrBufString (buf, c->scratch);
197 FcStrBufChar (buf, '=');
198 }
199
27b3e2dd
BE
200 l = FcPatternEltValues(e);
201 FcNameUnparseValueList (buf, l, '\0');
c493c3b7
BE
202 }
203
8c31a243
BE
204 return FcTrue;
205}
206
207static FcBool
208interpret_filter (FcFormatContext *c,
209 FcPattern *pat,
210 FcStrBuf *buf)
211{
212 FcObjectSet *os;
213 FcPattern *subpat;
214
215 if (!expect_char (c, '+'))
216 return FcFalse;
217
218 os = FcObjectSetCreate ();
219 if (!os)
220 return FcFalse;
221
222 do
223 {
224 if (!read_elt_name_to_scratch (c) ||
225 !FcObjectSetAdd (os, (const char *) c->scratch))
226 {
227 FcObjectSetDestroy (os);
228 return FcFalse;
229 }
230 }
231 while (consume_char (c, ','));
232
233 subpat = FcPatternFilter (pat, os);
234 FcObjectSetDestroy (os);
235
236 if (!subpat ||
237 !interpret_subexpr (c, subpat, buf))
238 return FcFalse;
239
240 FcPatternDestroy (subpat);
241 return FcTrue;
242}
243
244static FcBool
245interpret_delete (FcFormatContext *c,
246 FcPattern *pat,
247 FcStrBuf *buf)
248{
249 FcPattern *subpat;
250
251 if (!expect_char (c, '-'))
252 return FcFalse;
253
254 subpat = FcPatternDuplicate (pat);
255 if (!subpat)
256 return FcFalse;
257
258 do
259 {
260 if (!read_elt_name_to_scratch (c))
261 {
262 FcPatternDestroy (subpat);
263 return FcFalse;
264 }
265
266 FcPatternDel (subpat, (const char *) c->scratch);
267 }
268 while (consume_char (c, ','));
269
270 if (!interpret_subexpr (c, subpat, buf))
271 return FcFalse;
272
273 FcPatternDestroy (subpat);
274 return FcTrue;
275}
276
277static FcBool
278interpret_percent (FcFormatContext *c,
279 FcPattern *pat,
280 FcStrBuf *buf)
281{
282 int width, before;
283
284 if (!expect_char (c, '%'))
285 return FcFalse;
286
287 if (consume_char (c, '%')) /* "%%" */
288 {
289 FcStrBufChar (buf, '%');
290 return FcTrue;
291 }
292
293 /* parse an optional width specifier */
294 width = strtol ((const char *) c->format, (char **) &c->format, 10);
295
296 before = buf->len;
297
298 if (!expect_char (c, '{'))
299 return FcFalse;
300
301 switch (*c->format) {
302
303 case '{':
304 /* subexpression */
305 if (!interpret_subexpr (c, pat, buf))
306 return FcFalse;
307 break;
308
309 case '+':
310 /* filtering pattern elements */
311 if (!interpret_filter (c, pat, buf))
312 return FcFalse;
313 break;
314
315 case '-':
316 /* deleting pattern elements */
317 if (!interpret_delete (c, pat, buf))
318 return FcFalse;
319 break;
320
321 default:
322 /* simple tag */
323 if (!interpret_simple_tag (c, pat, buf))
324 return FcFalse;
325 break;
326
327 }
328
27b3e2dd
BE
329 /* handle filters, if any */
330 /* XXX */
331
c493c3b7
BE
332 /* align to width */
333 if (!buf->failed)
334 {
335 int after, len;
336
337 after = buf->len;
338
339 len = after - before;
340
341 if (len < -width)
342 {
343 /* left align */
344 while (len++ < -width)
345 FcStrBufChar (buf, ' ');
346 }
347 else if (len < width)
348 {
349 /* right align */
350 while (len++ < width)
351 FcStrBufChar (buf, ' ');
352 len = after - before;
353 memmove (buf->buf + buf->len - len,
354 buf->buf + buf->len - width,
355 len);
356 memset (buf->buf + buf->len - width,
357 ' ',
358 width - len);
359 }
0c93b91d 360 }
c493c3b7 361
8c31a243 362 return expect_char (c, '}');
0c93b91d
BE
363}
364
365static char escaped_char(const char ch)
366{
367 switch (ch) {
368 case 'a': return '\a';
369 case 'b': return '\b';
370 case 'f': return '\f';
371 case 'n': return '\n';
372 case 'r': return '\r';
373 case 't': return '\t';
374 case 'v': return '\v';
375 default: return ch;
376 }
377}
378
8c31a243 379static FcBool
27b3e2dd
BE
380interpret (FcFormatContext *c,
381 FcPattern *pat,
382 FcStrBuf *buf,
383 FcChar8 term)
0c93b91d 384{
27b3e2dd 385 for (; *c->format && *c->format != term;)
0c93b91d 386 {
27b3e2dd 387 switch (*c->format)
0c93b91d
BE
388 {
389 case '\\':
27b3e2dd
BE
390 c->format++; /* skip over '\\' */
391 if (*c->format)
392 FcStrBufChar (buf, escaped_char (*c->format++));
0c93b91d
BE
393 continue;
394 case '%':
8c31a243
BE
395 if (!interpret_percent (c, pat, buf))
396 return FcFalse;
0c93b91d
BE
397 continue;
398 }
27b3e2dd 399 FcStrBufChar (buf, *c->format++);
0c93b91d 400 }
8c31a243 401 return FcTrue;
0c93b91d
BE
402}
403
404FcChar8 *
405FcPatternFormat (FcPattern *pat, const FcChar8 *format)
406{
0c93b91d 407 FcStrBuf buf;
27b3e2dd 408 FcFormatContext c;
8c31a243 409 FcBool ret;
0c93b91d
BE
410
411 FcStrBufInit (&buf, 0, 0);
8c31a243
BE
412 if (!FcFormatContextInit (&c, format))
413 return NULL;
0c93b91d 414
8c31a243
BE
415 ret = interpret (&c, pat, &buf, '\0');
416 if (buf.failed)
417 ret = FcFalse;
0c93b91d 418
27b3e2dd 419 FcFormatContextDone (&c);
8c31a243
BE
420 if (ret)
421 return FcStrBufDone (&buf);
422 else
423 {
424 FcStrBufDestroy (&buf);
425 return NULL;
426 }
0c93b91d
BE
427}
428
429#define __fcformat__
430#include "fcaliastail.h"
431#undef __fcformat__