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 "g-gnulib.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 #if HAVE_STDINT_H_WITH_UINTMAX
33 # include <stdint.h>
34 #endif
35 #if 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 if (a->arg[n].type == TYPE_NONE) \
86 a->arg[n].type = (_type_); \
87 else if (a->arg[n].type != (_type_)) \
88 /* Ambiguous type for positional argument. */ \
89 goto error; \
90 }
91
92 while (*cp != '\0')
93 {
94 char c = *cp++;
95 if (c == '%')
96 {
97 int arg_index = -1;
98 char_directive *dp = &d->dir[d->count];/* pointer to next directive */
99
100 /* Initialize the next directive. */
101 dp->dir_start = cp - 1;
102 dp->flags = 0;
103 dp->width_start = NULL;
104 dp->width_end = NULL;
105 dp->width_arg_index = -1;
106 dp->precision_start = NULL;
107 dp->precision_end = NULL;
108 dp->precision_arg_index = -1;
109 dp->arg_index = -1;
110
111 /* Test for positional argument. */
112 if (*cp >= '0' && *cp <= '9')
113 {
114 const char *np;
115
116 for (np = cp; *np >= '0' && *np <= '9'; np++)
117 ;
118 if (*np == '$')
119 {
120 unsigned int n = 0;
121
122 for (np = cp; *np >= '0' && *np <= '9'; np++)
123 n = 10 * n + (*np - '0');
124 if (n == 0)
125 /* Positional argument 0. */
126 goto error;
127 arg_index = n - 1;
128 cp = np + 1;
129 }
130 }
131
132 /* Read the flags. */
133 for (;;)
134 {
135 if (*cp == '\'')
136 {
137 dp->flags |= FLAG_GROUP;
138 cp++;
139 }
140 else if (*cp == '-')
141 {
142 dp->flags |= FLAG_LEFT;
143 cp++;
144 }
145 else if (*cp == '+')
146 {
147 dp->flags |= FLAG_SHOWSIGN;
148 cp++;
149 }
150 else if (*cp == ' ')
151 {
152 dp->flags |= FLAG_SPACE;
153 cp++;
154 }
155 else if (*cp == '#')
156 {
157 dp->flags |= FLAG_ALT;
158 cp++;
159 }
160 else if (*cp == '0')
161 {
162 dp->flags |= FLAG_ZERO;
163 cp++;
164 }
165 else
166 break;
167 }
168
169 /* Parse the field width. */
170 if (*cp == '*')
171 {
172 dp->width_start = cp;
173 cp++;
174 dp->width_end = cp;
175 if (max_width_length < 1)
176 max_width_length = 1;
177
178 /* Test for positional argument. */
179 if (*cp >= '0' && *cp <= '9')
180 {
181 const char *np;
182
183 for (np = cp; *np >= '0' && *np <= '9'; np++)
184 ;
185 if (*np == '$')
186 {
187 unsigned int n = 0;
188
189 for (np = cp; *np >= '0' && *np <= '9'; np++)
190 n = 10 * n + (*np - '0');
191 if (n == 0)
192 /* Positional argument 0. */
193 goto error;
194 dp->width_arg_index = n - 1;
195 cp = np + 1;
196 }
197 }
198 if (dp->width_arg_index < 0)
199 dp->width_arg_index = arg_posn++;
200 REGISTER_ARG (dp->width_arg_index, TYPE_INT);
201 }
202 else if (*cp >= '0' && *cp <= '9')
203 {
204 unsigned int width_length;
205
206 dp->width_start = cp;
207 for (; *cp >= '0' && *cp <= '9'; cp++)
208 ;
209 dp->width_end = cp;
210 width_length = dp->width_end - dp->width_start;
211 if (max_width_length < width_length)
212 max_width_length = width_length;
213 }
214
215 /* Parse the precision. */
216 if (*cp == '.')
217 {
218 cp++;
219 if (*cp == '*')
220 {
221 dp->precision_start = cp - 1;
222 cp++;
223 dp->precision_end = cp;
224 if (max_precision_length < 2)
225 max_precision_length = 2;
226
227 /* Test for positional argument. */
228 if (*cp >= '0' && *cp <= '9')
229 {
230 const char *np;
231
232 for (np = cp; *np >= '0' && *np <= '9'; np++)
233 ;
234 if (*np == '$')
235 {
236 unsigned int n = 0;
237
238 for (np = cp; *np >= '0' && *np <= '9'; np++)
239 n = 10 * n + (*np - '0');
240 if (n == 0)
241 /* Positional argument 0. */
242 goto error;
243 dp->precision_arg_index = n - 1;
244 cp = np + 1;
245 }
246 }
247 if (dp->precision_arg_index < 0)
248 dp->precision_arg_index = arg_posn++;
249 REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
250 }
251 else
252 {
253 unsigned int precision_length;
254
255 dp->precision_start = cp - 1;
256 for (; *cp >= '0' && *cp <= '9'; cp++)
257 ;
258 dp->precision_end = cp;
259 precision_length = dp->precision_end - dp->precision_start;
260 if (max_precision_length < precision_length)
261 max_precision_length = precision_length;
262 }
263 }
264
265 {
266 arg_type type;
267
268 /* Parse argument type/size specifiers. */
269 {
270 int flags = 0;
271
272 for (;;)
273 {
274 if (*cp == 'h')
275 {
276 flags |= (1 << (flags & 1));
277 cp++;
278 }
279 else if (*cp == 'L')
280 {
281 flags |= 4;
282 cp++;
283 }
284 else if (*cp == 'l')
285 {
286 flags += 8;
287 cp++;
288 }
289 #ifdef HAVE_INT64_AND_I64
290 else if (cp[0] == 'I' &&
291 cp[1] == '6' &&
292 cp[2] == '4')
293 {
294 flags = 64;
295 cp += 3;
296 }
297 #endif
298 #ifdef HAVE_INTMAX_T
299 else if (*cp == 'j')
300 {
301 if (sizeof (intmax_t) > sizeof (long))
302 {
303 /* intmax_t = long long */
304 flags += 16;
305 }
306 else if (sizeof (intmax_t) > sizeof (int))
307 {
308 /* intmax_t = long */
309 flags += 8;
310 }
311 cp++;
312 }
313 #endif
314 else if (*cp == 'z' || *cp == 'Z')
315 {
316 /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
317 because the warning facility in gcc-2.95.2 understands
318 only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */
319 if (sizeof (size_t) > sizeof (long))
320 {
321 /* size_t = long long */
322 flags += 16;
323 }
324 else if (sizeof (size_t) > sizeof (int))
325 {
326 /* size_t = long */
327 flags += 8;
328 }
329 cp++;
330 }
331 else if (*cp == 't')
332 {
333 if (sizeof (ptrdiff_t) > sizeof (long))
334 {
335 /* ptrdiff_t = long long */
336 flags += 16;
337 }
338 else if (sizeof (ptrdiff_t) > sizeof (int))
339 {
340 /* ptrdiff_t = long */
341 flags += 8;
342 }
343 cp++;
344 }
345 else
346 break;
347 }
348
349 /* Read the conversion character. */
350 c = *cp++;
351 switch (c)
352 {
353 case 'd': case 'i':
354 #ifdef HAVE_INT64_AND_I64
355 if (flags == 64)
356 type = TYPE_INT64;
357 else
358 #endif
359 #ifdef HAVE_LONG_LONG
360 if (flags >= 16 || (flags & 4))
361 type = TYPE_LONGLONGINT;
362 else
363 #endif
364 if (flags >= 8)
365 type = TYPE_LONGINT;
366 else if (flags & 2)
367 type = TYPE_SCHAR;
368 else if (flags & 1)
369 type = TYPE_SHORT;
370 else
371 type = TYPE_INT;
372 break;
373 case 'o': case 'u': case 'x': case 'X':
374 #ifdef HAVE_INT64_AND_I64
375 if (flags == 64)
376 type = TYPE_UINT64;
377 else
378 #endif
379 #ifdef HAVE_LONG_LONG
380 if (flags >= 16 || (flags & 4))
381 type = TYPE_ULONGLONGINT;
382 else
383 #endif
384 if (flags >= 8)
385 type = TYPE_ULONGINT;
386 else if (flags & 2)
387 type = TYPE_UCHAR;
388 else if (flags & 1)
389 type = TYPE_USHORT;
390 else
391 type = TYPE_UINT;
392 break;
393 case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
394 case 'a': case 'A':
395 #ifdef HAVE_LONG_DOUBLE
396 if (flags >= 16 || (flags & 4))
397 type = TYPE_LONGDOUBLE;
398 else
399 #endif
400 type = TYPE_DOUBLE;
401 break;
402 case 'c':
403 if (flags >= 8)
404 #ifdef HAVE_WINT_T
405 type = TYPE_WIDE_CHAR;
406 #else
407 goto error;
408 #endif
409 else
410 type = TYPE_CHAR;
411 break;
412 #ifdef HAVE_WINT_T
413 case 'C':
414 type = TYPE_WIDE_CHAR;
415 c = 'c';
416 break;
417 #endif
418 case 's':
419 if (flags >= 8)
420 #ifdef HAVE_WCHAR_T
421 type = TYPE_WIDE_STRING;
422 #else
423 goto error;
424 #endif
425 else
426 type = TYPE_STRING;
427 break;
428 #ifdef HAVE_WCHAR_T
429 case 'S':
430 type = TYPE_WIDE_STRING;
431 c = 's';
432 break;
433 #endif
434 case 'p':
435 type = TYPE_POINTER;
436 break;
437 case 'n':
438 #ifdef HAVE_LONG_LONG
439 if (flags >= 16 || (flags & 4))
440 type = TYPE_COUNT_LONGLONGINT_POINTER;
441 else
442 #endif
443 if (flags >= 8)
444 type = TYPE_COUNT_LONGINT_POINTER;
445 else if (flags & 2)
446 type = TYPE_COUNT_SCHAR_POINTER;
447 else if (flags & 1)
448 type = TYPE_COUNT_SHORT_POINTER;
449 else
450 type = TYPE_COUNT_INT_POINTER;
451 break;
452 case '%':
453 type = TYPE_NONE;
454 break;
455 default:
456 /* Unknown conversion character. */
457 goto error;
458 }
459 }
460
461 if (type != TYPE_NONE)
462 {
463 dp->arg_index = arg_index;
464 if (dp->arg_index < 0)
465 dp->arg_index = arg_posn++;
466 REGISTER_ARG (dp->arg_index, type);
467 }
468 dp->conversion = c;
469 dp->dir_end = cp;
470 }
471
472 d->count++;
473 if (d->count >= d_allocated)
474 {
475 char_directive *memory;
476
477 d_allocated = 2 * d_allocated;
478 memory = realloc (d->dir, d_allocated * sizeof (char_directive));
479 if (memory == NULL)
480 /* Out of memory. */
481 goto error;
482 d->dir = memory;
483 }
484 }
485 }
486 d->dir[d->count].dir_start = cp;
487
488 d->max_width_length = max_width_length;
489 d->max_precision_length = max_precision_length;
490 return 0;
491
492 error:
493 if (a->arg)
494 free (a->arg);
495 if (d->dir)
496 free (d->dir);
497 return -1;
498 }
499