1 /* Formatted output to strings.
2 Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published
6 by the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 USA. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include "gst-printf.h"
24
25 /* Specification. */
26 #include "printf-parse.h"
27
28 /* Get size_t, NULL. */
29 #include <stddef.h>
30
31 /* Get intmax_t. */
32 #ifdef HAVE_STDINT_H_WITH_UINTMAX
33 # include <stdint.h>
34 #endif
35 #ifdef HAVE_INTTYPES_H_WITH_UINTMAX
36 # include <inttypes.h>
37 #endif
38
39 /* malloc(), realloc(), free(). */
40 #include <stdlib.h>
41
42 #ifdef STATIC
43 STATIC
44 #endif
45 int
printf_parse(const char * format,char_directives * d,arguments * a)46 printf_parse (const char *format, char_directives * d, arguments * a)
47 {
48 const char *cp = format; /* pointer into format */
49 int arg_posn = 0; /* number of regular arguments consumed */
50 unsigned int d_allocated; /* allocated elements of d->dir */
51 unsigned int a_allocated; /* allocated elements of a->arg */
52 unsigned int max_width_length = 0;
53 unsigned int max_precision_length = 0;
54
55 d->count = 0;
56 d_allocated = 1;
57 d->dir = malloc (d_allocated * sizeof (char_directive));
58 if (d->dir == NULL)
59 /* Out of memory. */
60 return -1;
61
62 a->count = 0;
63 a_allocated = 0;
64 a->arg = NULL;
65
66 #define REGISTER_ARG(_index_,_type_) \
67 { \
68 unsigned int n = (_index_); \
69 if (n >= a_allocated) \
70 { \
71 argument *memory; \
72 a_allocated = 2 * a_allocated; \
73 if (a_allocated <= n) \
74 a_allocated = n + 1; \
75 memory = (a->arg \
76 ? realloc (a->arg, a_allocated * sizeof (argument)) \
77 : malloc (a_allocated * sizeof (argument))); \
78 if (memory == NULL) \
79 /* Out of memory. */ \
80 goto error; \
81 a->arg = memory; \
82 } \
83 while (a->count <= n) { \
84 a->arg[a->count].type = TYPE_NONE; \
85 a->arg[a->count].ext_string = (char *) 0; \
86 ++a->count; \
87 } \
88 if (a->arg[n].type == TYPE_NONE) \
89 a->arg[n].type = (_type_); \
90 else if (a->arg[n].type != (_type_)) \
91 /* Ambiguous type for positional argument. */ \
92 goto error; \
93 }
94
95 while (*cp != '\0') {
96 char c = *cp++;
97 if (c == '%') {
98 int arg_index = -1;
99 char_directive *dp = &d->dir[d->count]; /* pointer to next directive */
100
101 /* Initialize the next directive. */
102 dp->dir_start = cp - 1;
103 dp->flags = 0;
104 dp->width_start = NULL;
105 dp->width_end = NULL;
106 dp->width_arg_index = -1;
107 dp->precision_start = NULL;
108 dp->precision_end = NULL;
109 dp->precision_arg_index = -1;
110 dp->arg_index = -1;
111
112 /* Test for positional argument. */
113 if (*cp >= '0' && *cp <= '9') {
114 const char *np;
115
116 for (np = cp; *np >= '0' && *np <= '9'; np++);
117 if (*np == '$') {
118 unsigned int n = 0;
119
120 for (np = cp; *np >= '0' && *np <= '9'; np++)
121 n = 10 * n + (*np - '0');
122 if (n == 0)
123 /* Positional argument 0. */
124 goto error;
125 arg_index = n - 1;
126 cp = np + 1;
127 }
128 }
129
130 /* Read the flags. */
131 for (;;) {
132 if (*cp == '\'') {
133 dp->flags |= FLAG_GROUP;
134 cp++;
135 } else if (*cp == '-') {
136 dp->flags |= FLAG_LEFT;
137 cp++;
138 } else if (*cp == '+') {
139 dp->flags |= FLAG_SHOWSIGN;
140 cp++;
141 } else if (*cp == ' ') {
142 dp->flags |= FLAG_SPACE;
143 cp++;
144 } else if (*cp == '#') {
145 dp->flags |= FLAG_ALT;
146 cp++;
147 } else if (*cp == '0') {
148 dp->flags |= FLAG_ZERO;
149 cp++;
150 } else
151 break;
152 }
153
154 /* Parse the field width. */
155 if (*cp == '*') {
156 dp->width_start = cp;
157 cp++;
158 dp->width_end = cp;
159 if (max_width_length < 1)
160 max_width_length = 1;
161
162 /* Test for positional argument. */
163 if (*cp >= '0' && *cp <= '9') {
164 const char *np;
165
166 for (np = cp; *np >= '0' && *np <= '9'; np++);
167 if (*np == '$') {
168 unsigned int n = 0;
169
170 for (np = cp; *np >= '0' && *np <= '9'; np++)
171 n = 10 * n + (*np - '0');
172 if (n == 0)
173 /* Positional argument 0. */
174 goto error;
175 dp->width_arg_index = n - 1;
176 cp = np + 1;
177 }
178 }
179 if (dp->width_arg_index < 0)
180 dp->width_arg_index = arg_posn++;
181 REGISTER_ARG (dp->width_arg_index, TYPE_INT);
182 } else if (*cp >= '0' && *cp <= '9') {
183 unsigned int width_length;
184
185 dp->width_start = cp;
186 for (; *cp >= '0' && *cp <= '9'; cp++);
187 dp->width_end = cp;
188 width_length = dp->width_end - dp->width_start;
189 if (max_width_length < width_length)
190 max_width_length = width_length;
191 }
192
193 /* Parse the precision. */
194 if (*cp == '.') {
195 cp++;
196 if (*cp == '*') {
197 dp->precision_start = cp - 1;
198 cp++;
199 dp->precision_end = cp;
200 if (max_precision_length < 2)
201 max_precision_length = 2;
202
203 /* Test for positional argument. */
204 if (*cp >= '0' && *cp <= '9') {
205 const char *np;
206
207 for (np = cp; *np >= '0' && *np <= '9'; np++);
208 if (*np == '$') {
209 unsigned int n = 0;
210
211 for (np = cp; *np >= '0' && *np <= '9'; np++)
212 n = 10 * n + (*np - '0');
213 if (n == 0)
214 /* Positional argument 0. */
215 goto error;
216 dp->precision_arg_index = n - 1;
217 cp = np + 1;
218 }
219 }
220 if (dp->precision_arg_index < 0)
221 dp->precision_arg_index = arg_posn++;
222 REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
223 } else {
224 unsigned int precision_length;
225
226 dp->precision_start = cp - 1;
227 for (; *cp >= '0' && *cp <= '9'; cp++);
228 dp->precision_end = cp;
229 precision_length = dp->precision_end - dp->precision_start;
230 if (max_precision_length < precision_length)
231 max_precision_length = precision_length;
232 }
233 }
234
235 {
236 arg_type type;
237
238 /* Parse argument type/size specifiers. */
239 {
240 int flags = 0;
241
242 for (;;) {
243 if (*cp == 'h') {
244 flags |= (1 << (flags & 1));
245 cp++;
246 } else if (*cp == 'L') {
247 flags |= 4;
248 cp++;
249 } else if (*cp == 'l') {
250 flags += 8;
251 cp++;
252 }
253 #ifdef HAVE_INT64_AND_I64
254 else if (cp[0] == 'I' && cp[1] == '6' && cp[2] == '4') {
255 flags = 64;
256 cp += 3;
257 }
258 #endif
259 else if (cp[0] == 'I' && cp[1] == '3' && cp[2] == '2') {
260 //flags = 32;
261 cp += 3;
262 }
263 #ifdef HAVE_INTMAX_T
264 else if (*cp == 'j') {
265 if (sizeof (intmax_t) > sizeof (long)) {
266 /* intmax_t = long long */
267 flags += 16;
268 } else if (sizeof (intmax_t) > sizeof (int)) {
269 /* intmax_t = long */
270 flags += 8;
271 }
272 cp++;
273 }
274 #endif
275 else if (*cp == 'z' || *cp == 'Z') {
276 /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
277 because the warning facility in gcc-2.95.2 understands
278 only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */
279 if (sizeof (size_t) > sizeof (long)) {
280 /* size_t = long long */
281 flags += 16;
282 } else if (sizeof (size_t) > sizeof (int)) {
283 /* size_t = long */
284 flags += 8;
285 }
286 cp++;
287 } else if (*cp == 't') {
288 if (sizeof (ptrdiff_t) > sizeof (long)) {
289 /* ptrdiff_t = long long */
290 flags += 16;
291 } else if (sizeof (ptrdiff_t) > sizeof (int)) {
292 /* ptrdiff_t = long */
293 flags += 8;
294 }
295 cp++;
296 } else
297 break;
298 }
299
300 /* Read the conversion character. */
301 c = *cp++;
302 switch (c) {
303 case 'd':
304 case 'i':
305 #ifdef HAVE_INT64_AND_I64
306 if (flags == 64)
307 type = TYPE_INT64;
308 else
309 #endif
310 #ifdef HAVE_LONG_LONG
311 if (flags >= 16 || (flags & 4))
312 type = TYPE_LONGLONGINT;
313 else
314 #endif
315 if (flags >= 8)
316 type = TYPE_LONGINT;
317 else if (flags & 2)
318 type = TYPE_SCHAR;
319 else if (flags & 1)
320 type = TYPE_SHORT;
321 else
322 type = TYPE_INT;
323 break;
324 case 'o':
325 case 'u':
326 case 'x':
327 case 'X':
328 #ifdef HAVE_INT64_AND_I64
329 if (flags == 64)
330 type = TYPE_UINT64;
331 else
332 #endif
333 #ifdef HAVE_LONG_LONG
334 if (flags >= 16 || (flags & 4))
335 type = TYPE_ULONGLONGINT;
336 else
337 #endif
338 if (flags >= 8)
339 type = TYPE_ULONGINT;
340 else if (flags & 2)
341 type = TYPE_UCHAR;
342 else if (flags & 1)
343 type = TYPE_USHORT;
344 else
345 type = TYPE_UINT;
346 break;
347 case 'f':
348 case 'F':
349 case 'e':
350 case 'E':
351 case 'g':
352 case 'G':
353 case 'a':
354 case 'A':
355 #ifdef HAVE_LONG_DOUBLE
356 if (flags >= 16 || (flags & 4))
357 type = TYPE_LONGDOUBLE;
358 else
359 #endif
360 type = TYPE_DOUBLE;
361 break;
362 case 'c':
363 if (flags >= 8)
364 #ifdef HAVE_WINT_T
365 type = TYPE_WIDE_CHAR;
366 #else
367 goto error;
368 #endif
369 else
370 type = TYPE_CHAR;
371 break;
372 #ifdef HAVE_WINT_T
373 case 'C':
374 type = TYPE_WIDE_CHAR;
375 c = 'c';
376 break;
377 #endif
378 case 's':
379 if (flags >= 8)
380 #ifdef HAVE_WCHAR_T
381 type = TYPE_WIDE_STRING;
382 #else
383 goto error;
384 #endif
385 else
386 type = TYPE_STRING;
387 break;
388 #ifdef HAVE_WCHAR_T
389 case 'S':
390 type = TYPE_WIDE_STRING;
391 c = 's';
392 break;
393 #endif
394 /* Old GST_PTR_FORMAT, handle for binary backwards compatibility */
395 case 'P':
396 type = TYPE_POINTER_EXT;
397 dp->flags |= FLAG_PTR_EXT;
398 dp->ptr_ext_char = 'A';
399 c = 'p';
400 break;
401 case 'p':
402 /* Note: cp points already to the char after the 'p' now */
403 if (cp[0] == POINTER_EXT_SIGNIFIER_CHAR && cp[1] != '\0') {
404 type = TYPE_POINTER_EXT;
405 dp->flags |= FLAG_PTR_EXT;
406 dp->ptr_ext_char = cp[1];
407 cp += 2;
408 /* we do not use dp->conversion='s' on purpose here, so we
409 * can fall back to printing just the pointer with %p if the
410 * serialisation function returned NULL for some reason */
411 } else {
412 type = TYPE_POINTER;
413 }
414 break;
415 /* Old GST_SEGMENT_FORMAT, handle for backwards compatibility */
416 case 'Q':
417 type = TYPE_POINTER_EXT;
418 dp->flags |= FLAG_PTR_EXT;
419 dp->ptr_ext_char = 'B';
420 c = 'p';
421 break;
422 case 'n':
423 #ifdef HAVE_LONG_LONG
424 if (flags >= 16 || (flags & 4))
425 type = TYPE_COUNT_LONGLONGINT_POINTER;
426 else
427 #endif
428 if (flags >= 8)
429 type = TYPE_COUNT_LONGINT_POINTER;
430 else if (flags & 2)
431 type = TYPE_COUNT_SCHAR_POINTER;
432 else if (flags & 1)
433 type = TYPE_COUNT_SHORT_POINTER;
434 else
435 type = TYPE_COUNT_INT_POINTER;
436 break;
437 case '%':
438 type = TYPE_NONE;
439 break;
440 default:
441 /* Unknown conversion character. */
442 goto error;
443 }
444 }
445
446 if (type != TYPE_NONE) {
447 dp->arg_index = arg_index;
448 if (dp->arg_index < 0)
449 dp->arg_index = arg_posn++;
450 REGISTER_ARG (dp->arg_index, type);
451 }
452 dp->conversion = c;
453 dp->dir_end = cp;
454 }
455
456 d->count++;
457 if (d->count >= d_allocated) {
458 char_directive *memory;
459
460 d_allocated = 2 * d_allocated;
461 memory = realloc (d->dir, d_allocated * sizeof (char_directive));
462 if (memory == NULL)
463 /* Out of memory. */
464 goto error;
465 d->dir = memory;
466 }
467 }
468 }
469 d->dir[d->count].dir_start = cp;
470
471 d->max_width_length = max_width_length;
472 d->max_precision_length = max_precision_length;
473 return 0;
474
475 error:
476 if (a->arg)
477 free (a->arg);
478 if (d->dir)
479 free (d->dir);
480 return -1;
481 }
482