]> git.wh0rd.org - fontconfig.git/blame - src/fcformat.c
[fcformat] Add support for subexpressions
[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);
d6506ff6 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
27b3e2dd
BE
51static void
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);
58}
c493c3b7 59
27b3e2dd
BE
60static void
61FcFormatContextDone (FcFormatContext *c)
62{
63 if (c)
64 {
65 free (c->scratch);
66 }
67}
c493c3b7 68
27b3e2dd
BE
69static FcBool
70consume_char (FcFormatContext *c,
71 FcChar8 term)
72{
73 if (*c->format != term)
d6506ff6
BE
74 return FcFalse;
75
76 c->format++;
77 return FcTrue;
78}
79
80static FcBool
81expect_char (FcFormatContext *c,
82 FcChar8 term)
83{
84 FcBool res = consume_char (c, term);
85 if (!res)
86 {
87 if (c->format == c->format_orig + c->format_len)
88 message ("format ended while expecting '%c'",
89 term);
90 else
91 message ("expected '%c' at %d",
92 term, c->format - c->format_orig + 1);
93 }
94 return res;
95}
96
97static FcBool
98FcCharIsPunct (const FcChar8 c)
99{
100 if (c < '0')
101 return FcTrue;
102 if (c <= '9')
103 return FcFalse;
104 if (c < 'A')
105 return FcTrue;
106 if (c <= 'Z')
107 return FcFalse;
108 if (c < 'a')
109 return FcTrue;
110 if (c <= 'z')
111 return FcFalse;
112 if (c <= '~')
113 return FcTrue;
114 return FcFalse;
115}
116
117static FcBool
118read_elt_name_to_scratch (FcFormatContext *c)
119{
120 FcChar8 *p;
121
122 p = c->scratch;
123
124 while (*c->format)
125 {
126 if (*c->format == '\\')
127 {
128 c->format++;
129 if (*c->format)
130 c->format++;
131 continue;
132 }
133 else if (FcCharIsPunct (*c->format))
134 break;
135
136 *p++ = *c->format++;
137 }
138 *p = '\0';
139
140 if (p == c->scratch)
0c93b91d 141 {
d6506ff6
BE
142 message ("expected element name at %d",
143 c->format - c->format_orig + 1);
27b3e2dd
BE
144 return FcFalse;
145 }
0c93b91d 146
27b3e2dd
BE
147 return FcTrue;
148}
0c93b91d 149
d6506ff6
BE
150static void
151interpret (FcFormatContext *c,
152 FcPattern *pat,
153 FcStrBuf *buf,
154 FcChar8 term);
155
27b3e2dd
BE
156static void
157interpret_percent (FcFormatContext *c,
158 FcPattern *pat,
159 FcStrBuf *buf)
160{
d6506ff6
BE
161 int width, before;
162 FcChar8 *p;
27b3e2dd 163 FcPatternElt *e;
0c93b91d 164
d6506ff6
BE
165 FcPattern *subpat = pat;
166 FcBool add_colon = FcFalse;
167 FcBool add_elt_name = FcFalse;
168
169 if (!expect_char (c, '%'))
27b3e2dd
BE
170 return;
171
d6506ff6 172 if (consume_char (c, '%')) /* "%%" */
27b3e2dd 173 {
d6506ff6 174 FcStrBufChar (buf, '%');
27b3e2dd
BE
175 return;
176 }
177
178 /* parse an optional width specifier */
179 width = strtol ((const char *) c->format, (char **) &c->format, 10);
180
181 before = buf->len;
182
d6506ff6
BE
183 if (!expect_char (c, '{'))
184 goto bail;
0c93b91d 185
d6506ff6 186 if (consume_char (c, '{'))
27b3e2dd 187 {
d6506ff6
BE
188 /* it's just a subexpression. no tag involved */
189 interpret (c, pat, buf, '}');
190 expect_char (c, '}');
191 goto filter;
0c93b91d 192 }
27b3e2dd 193
d6506ff6
BE
194 switch (*c->format) {
195 case ':':
196 add_colon = FcTrue;
197 consume_char (c, ':');
198 break;
199 }
200
201parse_tag:
202 if (!read_elt_name_to_scratch (c))
203 goto bail;
204
205 if (consume_char (c, '='))
206 add_elt_name = FcTrue;
207
208 e = FcPatternObjectFindElt (pat,
209 FcObjectFromName ((const char *) c->scratch));
27b3e2dd
BE
210 if (e)
211 {
212 FcValueListPtr l;
d6506ff6
BE
213
214 if (add_colon)
215 FcStrBufChar (buf, ':');
216 if (add_elt_name)
217 {
218 FcStrBufString (buf, c->scratch);
219 FcStrBufChar (buf, '=');
220 }
221
27b3e2dd
BE
222 l = FcPatternEltValues(e);
223 FcNameUnparseValueList (buf, l, '\0');
c493c3b7
BE
224 }
225
d6506ff6 226filter:
27b3e2dd
BE
227 /* handle filters, if any */
228 /* XXX */
229
c493c3b7
BE
230 /* align to width */
231 if (!buf->failed)
232 {
233 int after, len;
234
235 after = buf->len;
236
237 len = after - before;
238
239 if (len < -width)
240 {
241 /* left align */
242 while (len++ < -width)
243 FcStrBufChar (buf, ' ');
244 }
245 else if (len < width)
246 {
247 /* right align */
248 while (len++ < width)
249 FcStrBufChar (buf, ' ');
250 len = after - before;
251 memmove (buf->buf + buf->len - len,
252 buf->buf + buf->len - width,
253 len);
254 memset (buf->buf + buf->len - width,
255 ' ',
256 width - len);
257 }
0c93b91d 258 }
c493c3b7 259
d6506ff6
BE
260 expect_char (c, '}');
261
262bail:
263 if (subpat != pat)
264 FcPatternDestroy (subpat);
0c93b91d
BE
265}
266
267static char escaped_char(const char ch)
268{
269 switch (ch) {
270 case 'a': return '\a';
271 case 'b': return '\b';
272 case 'f': return '\f';
273 case 'n': return '\n';
274 case 'r': return '\r';
275 case 't': return '\t';
276 case 'v': return '\v';
277 default: return ch;
278 }
279}
280
27b3e2dd
BE
281static void
282interpret (FcFormatContext *c,
283 FcPattern *pat,
284 FcStrBuf *buf,
285 FcChar8 term)
0c93b91d 286{
27b3e2dd 287 for (; *c->format && *c->format != term;)
0c93b91d 288 {
27b3e2dd 289 switch (*c->format)
0c93b91d
BE
290 {
291 case '\\':
27b3e2dd
BE
292 c->format++; /* skip over '\\' */
293 if (*c->format)
294 FcStrBufChar (buf, escaped_char (*c->format++));
0c93b91d
BE
295 continue;
296 case '%':
27b3e2dd 297 interpret_percent (c, pat, buf);
0c93b91d
BE
298 continue;
299 }
27b3e2dd 300 FcStrBufChar (buf, *c->format++);
0c93b91d 301 }
0c93b91d
BE
302}
303
304FcChar8 *
305FcPatternFormat (FcPattern *pat, const FcChar8 *format)
306{
0c93b91d 307 FcStrBuf buf;
27b3e2dd 308 FcFormatContext c;
0c93b91d
BE
309
310 FcStrBufInit (&buf, 0, 0);
27b3e2dd 311 FcFormatContextInit (&c, format);
0c93b91d 312
27b3e2dd 313 interpret (&c, pat, &buf, '\0');
0c93b91d 314
27b3e2dd 315 FcFormatContextDone (&c);
0c93b91d
BE
316 return FcStrBufDone (&buf);
317}
318
319#define __fcformat__
320#include "fcaliastail.h"
321#undef __fcformat__