]>
Commit | Line | Data |
---|---|---|
b0cacab0 TT |
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 | |
8785aaea | 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
b0cacab0 TT |
17 | USA. */ |
18 | ||
19 | #ifdef HAVE_CONFIG_H | |
20 | # include <config.h> | |
21 | #endif | |
22 | ||
23 | /* Specification. */ | |
24 | #if WIDE_CHAR_VERSION | |
25 | # include "wprintf-parse.h" | |
26 | #else | |
27 | # include "printf-parse.h" | |
28 | #endif | |
29 | ||
30 | /* Get size_t, NULL. */ | |
31 | #include <stddef.h> | |
32 | ||
33 | /* Get intmax_t. */ | |
34 | #if HAVE_STDINT_H_WITH_UINTMAX | |
35 | # include <stdint.h> | |
36 | #endif | |
37 | #if HAVE_INTTYPES_H_WITH_UINTMAX | |
38 | # include <inttypes.h> | |
39 | #endif | |
40 | ||
41 | /* malloc(), realloc(), free(). */ | |
42 | #include <stdlib.h> | |
43 | ||
44 | /* Checked size_t computations. */ | |
45 | #include "xsize.h" | |
46 | ||
47 | #if WIDE_CHAR_VERSION | |
48 | # define PRINTF_PARSE wprintf_parse | |
49 | # define CHAR_T wchar_t | |
50 | # define DIRECTIVE wchar_t_directive | |
51 | # define DIRECTIVES wchar_t_directives | |
52 | #else | |
53 | # define PRINTF_PARSE printf_parse | |
54 | # define CHAR_T char | |
55 | # define DIRECTIVE char_directive | |
56 | # define DIRECTIVES char_directives | |
57 | #endif | |
58 | ||
59 | #ifdef STATIC | |
60 | STATIC | |
61 | #endif | |
62 | int | |
63 | PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a) | |
64 | { | |
65 | const CHAR_T *cp = format; /* pointer into format */ | |
66 | size_t arg_posn = 0; /* number of regular arguments consumed */ | |
67 | size_t d_allocated; /* allocated elements of d->dir */ | |
68 | size_t a_allocated; /* allocated elements of a->arg */ | |
69 | size_t max_width_length = 0; | |
70 | size_t max_precision_length = 0; | |
71 | ||
72 | d->count = 0; | |
73 | d_allocated = 1; | |
74 | d->dir = malloc (d_allocated * sizeof (DIRECTIVE)); | |
75 | if (d->dir == NULL) | |
76 | /* Out of memory. */ | |
77 | return -1; | |
78 | ||
79 | a->count = 0; | |
80 | a_allocated = 0; | |
81 | a->arg = NULL; | |
82 | ||
83 | #define REGISTER_ARG(_index_,_type_) \ | |
84 | { \ | |
85 | size_t n = (_index_); \ | |
86 | if (n >= a_allocated) \ | |
87 | { \ | |
88 | size_t memory_size; \ | |
89 | argument *memory; \ | |
90 | \ | |
91 | a_allocated = xtimes (a_allocated, 2); \ | |
92 | if (a_allocated <= n) \ | |
93 | a_allocated = xsum (n, 1); \ | |
94 | memory_size = xtimes (a_allocated, sizeof (argument)); \ | |
95 | if (size_overflow_p (memory_size)) \ | |
96 | /* Overflow, would lead to out of memory. */ \ | |
97 | goto error; \ | |
98 | memory = (a->arg \ | |
99 | ? realloc (a->arg, memory_size) \ | |
100 | : malloc (memory_size)); \ | |
101 | if (memory == NULL) \ | |
102 | /* Out of memory. */ \ | |
103 | goto error; \ | |
104 | a->arg = memory; \ | |
105 | } \ | |
106 | while (a->count <= n) \ | |
107 | a->arg[a->count++].type = TYPE_NONE; \ | |
108 | if (a->arg[n].type == TYPE_NONE) \ | |
109 | a->arg[n].type = (_type_); \ | |
110 | else if (a->arg[n].type != (_type_)) \ | |
111 | /* Ambiguous type for positional argument. */ \ | |
112 | goto error; \ | |
113 | } | |
114 | ||
115 | while (*cp != '\0') | |
116 | { | |
117 | CHAR_T c = *cp++; | |
118 | if (c == '%') | |
119 | { | |
120 | size_t arg_index = ARG_NONE; | |
121 | DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */ | |
122 | ||
123 | /* Initialize the next directive. */ | |
124 | dp->dir_start = cp - 1; | |
125 | dp->flags = 0; | |
126 | dp->width_start = NULL; | |
127 | dp->width_end = NULL; | |
128 | dp->width_arg_index = ARG_NONE; | |
129 | dp->precision_start = NULL; | |
130 | dp->precision_end = NULL; | |
131 | dp->precision_arg_index = ARG_NONE; | |
132 | dp->arg_index = ARG_NONE; | |
133 | ||
134 | /* Test for positional argument. */ | |
135 | if (*cp >= '0' && *cp <= '9') | |
136 | { | |
137 | const CHAR_T *np; | |
138 | ||
139 | for (np = cp; *np >= '0' && *np <= '9'; np++) | |
140 | ; | |
141 | if (*np == '$') | |
142 | { | |
143 | size_t n = 0; | |
144 | ||
145 | for (np = cp; *np >= '0' && *np <= '9'; np++) | |
146 | n = xsum (xtimes (n, 10), *np - '0'); | |
147 | if (n == 0) | |
148 | /* Positional argument 0. */ | |
149 | goto error; | |
150 | if (size_overflow_p (n)) | |
151 | /* n too large, would lead to out of memory later. */ | |
152 | goto error; | |
153 | arg_index = n - 1; | |
154 | cp = np + 1; | |
155 | } | |
156 | } | |
157 | ||
158 | /* Read the flags. */ | |
159 | for (;;) | |
160 | { | |
161 | if (*cp == '\'') | |
162 | { | |
163 | dp->flags |= FLAG_GROUP; | |
164 | cp++; | |
165 | } | |
166 | else if (*cp == '-') | |
167 | { | |
168 | dp->flags |= FLAG_LEFT; | |
169 | cp++; | |
170 | } | |
171 | else if (*cp == '+') | |
172 | { | |
173 | dp->flags |= FLAG_SHOWSIGN; | |
174 | cp++; | |
175 | } | |
176 | else if (*cp == ' ') | |
177 | { | |
178 | dp->flags |= FLAG_SPACE; | |
179 | cp++; | |
180 | } | |
181 | else if (*cp == '#') | |
182 | { | |
183 | dp->flags |= FLAG_ALT; | |
184 | cp++; | |
185 | } | |
186 | else if (*cp == '0') | |
187 | { | |
188 | dp->flags |= FLAG_ZERO; | |
189 | cp++; | |
190 | } | |
191 | else | |
192 | break; | |
193 | } | |
194 | ||
195 | /* Parse the field width. */ | |
196 | if (*cp == '*') | |
197 | { | |
198 | dp->width_start = cp; | |
199 | cp++; | |
200 | dp->width_end = cp; | |
201 | if (max_width_length < 1) | |
202 | max_width_length = 1; | |
203 | ||
204 | /* Test for positional argument. */ | |
205 | if (*cp >= '0' && *cp <= '9') | |
206 | { | |
207 | const CHAR_T *np; | |
208 | ||
209 | for (np = cp; *np >= '0' && *np <= '9'; np++) | |
210 | ; | |
211 | if (*np == '$') | |
212 | { | |
213 | size_t n = 0; | |
214 | ||
215 | for (np = cp; *np >= '0' && *np <= '9'; np++) | |
216 | n = xsum (xtimes (n, 10), *np - '0'); | |
217 | if (n == 0) | |
218 | /* Positional argument 0. */ | |
219 | goto error; | |
220 | if (size_overflow_p (n)) | |
221 | /* n too large, would lead to out of memory later. */ | |
222 | goto error; | |
223 | dp->width_arg_index = n - 1; | |
224 | cp = np + 1; | |
225 | } | |
226 | } | |
227 | if (dp->width_arg_index == ARG_NONE) | |
228 | { | |
229 | dp->width_arg_index = arg_posn++; | |
230 | if (dp->width_arg_index == ARG_NONE) | |
231 | /* arg_posn wrapped around. */ | |
232 | goto error; | |
233 | } | |
234 | REGISTER_ARG (dp->width_arg_index, TYPE_INT); | |
235 | } | |
236 | else if (*cp >= '0' && *cp <= '9') | |
237 | { | |
238 | size_t width_length; | |
239 | ||
240 | dp->width_start = cp; | |
241 | for (; *cp >= '0' && *cp <= '9'; cp++) | |
242 | ; | |
243 | dp->width_end = cp; | |
244 | width_length = dp->width_end - dp->width_start; | |
245 | if (max_width_length < width_length) | |
246 | max_width_length = width_length; | |
247 | } | |
248 | ||
249 | /* Parse the precision. */ | |
250 | if (*cp == '.') | |
251 | { | |
252 | cp++; | |
253 | if (*cp == '*') | |
254 | { | |
255 | dp->precision_start = cp - 1; | |
256 | cp++; | |
257 | dp->precision_end = cp; | |
258 | if (max_precision_length < 2) | |
259 | max_precision_length = 2; | |
260 | ||
261 | /* Test for positional argument. */ | |
262 | if (*cp >= '0' && *cp <= '9') | |
263 | { | |
264 | const CHAR_T *np; | |
265 | ||
266 | for (np = cp; *np >= '0' && *np <= '9'; np++) | |
267 | ; | |
268 | if (*np == '$') | |
269 | { | |
270 | size_t n = 0; | |
271 | ||
272 | for (np = cp; *np >= '0' && *np <= '9'; np++) | |
273 | n = xsum (xtimes (n, 10), *np - '0'); | |
274 | if (n == 0) | |
275 | /* Positional argument 0. */ | |
276 | goto error; | |
277 | if (size_overflow_p (n)) | |
278 | /* n too large, would lead to out of memory | |
279 | later. */ | |
280 | goto error; | |
281 | dp->precision_arg_index = n - 1; | |
282 | cp = np + 1; | |
283 | } | |
284 | } | |
285 | if (dp->precision_arg_index == ARG_NONE) | |
286 | { | |
287 | dp->precision_arg_index = arg_posn++; | |
288 | if (dp->precision_arg_index == ARG_NONE) | |
289 | /* arg_posn wrapped around. */ | |
290 | goto error; | |
291 | } | |
292 | REGISTER_ARG (dp->precision_arg_index, TYPE_INT); | |
293 | } | |
294 | else | |
295 | { | |
296 | size_t precision_length; | |
297 | ||
298 | dp->precision_start = cp - 1; | |
299 | for (; *cp >= '0' && *cp <= '9'; cp++) | |
300 | ; | |
301 | dp->precision_end = cp; | |
302 | precision_length = dp->precision_end - dp->precision_start; | |
303 | if (max_precision_length < precision_length) | |
304 | max_precision_length = precision_length; | |
305 | } | |
306 | } | |
307 | ||
308 | { | |
309 | arg_type type; | |
310 | ||
311 | /* Parse argument type/size specifiers. */ | |
312 | { | |
313 | int flags = 0; | |
314 | ||
315 | for (;;) | |
316 | { | |
317 | if (*cp == 'h') | |
318 | { | |
319 | flags |= (1 << (flags & 1)); | |
320 | cp++; | |
321 | } | |
322 | else if (*cp == 'L') | |
323 | { | |
324 | flags |= 4; | |
325 | cp++; | |
326 | } | |
327 | else if (*cp == 'l') | |
328 | { | |
329 | flags += 8; | |
330 | cp++; | |
331 | } | |
332 | #ifdef HAVE_INTMAX_T | |
333 | else if (*cp == 'j') | |
334 | { | |
335 | if (sizeof (intmax_t) > sizeof (long)) | |
336 | { | |
337 | /* intmax_t = long long */ | |
338 | flags += 16; | |
339 | } | |
340 | else if (sizeof (intmax_t) > sizeof (int)) | |
341 | { | |
342 | /* intmax_t = long */ | |
343 | flags += 8; | |
344 | } | |
345 | cp++; | |
346 | } | |
347 | #endif | |
348 | else if (*cp == 'z' || *cp == 'Z') | |
349 | { | |
350 | /* 'z' is standardized in ISO C 99, but glibc uses 'Z' | |
351 | because the warning facility in gcc-2.95.2 understands | |
352 | only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ | |
353 | if (sizeof (size_t) > sizeof (long)) | |
354 | { | |
355 | /* size_t = long long */ | |
356 | flags += 16; | |
357 | } | |
358 | else if (sizeof (size_t) > sizeof (int)) | |
359 | { | |
360 | /* size_t = long */ | |
361 | flags += 8; | |
362 | } | |
363 | cp++; | |
364 | } | |
365 | else if (*cp == 't') | |
366 | { | |
367 | if (sizeof (ptrdiff_t) > sizeof (long)) | |
368 | { | |
369 | /* ptrdiff_t = long long */ | |
370 | flags += 16; | |
371 | } | |
372 | else if (sizeof (ptrdiff_t) > sizeof (int)) | |
373 | { | |
374 | /* ptrdiff_t = long */ | |
375 | flags += 8; | |
376 | } | |
377 | cp++; | |
378 | } | |
379 | else | |
380 | break; | |
381 | } | |
382 | ||
383 | /* Read the conversion character. */ | |
384 | c = *cp++; | |
385 | switch (c) | |
386 | { | |
387 | case 'd': case 'i': | |
388 | #ifdef HAVE_LONG_LONG | |
389 | if (flags >= 16 || (flags & 4)) | |
390 | type = TYPE_LONGLONGINT; | |
391 | else | |
392 | #endif | |
393 | if (flags >= 8) | |
394 | type = TYPE_LONGINT; | |
395 | else if (flags & 2) | |
396 | type = TYPE_SCHAR; | |
397 | else if (flags & 1) | |
398 | type = TYPE_SHORT; | |
399 | else | |
400 | type = TYPE_INT; | |
401 | break; | |
402 | case 'o': case 'u': case 'x': case 'X': | |
403 | #ifdef HAVE_LONG_LONG | |
404 | if (flags >= 16 || (flags & 4)) | |
405 | type = TYPE_ULONGLONGINT; | |
406 | else | |
407 | #endif | |
408 | if (flags >= 8) | |
409 | type = TYPE_ULONGINT; | |
410 | else if (flags & 2) | |
411 | type = TYPE_UCHAR; | |
412 | else if (flags & 1) | |
413 | type = TYPE_USHORT; | |
414 | else | |
415 | type = TYPE_UINT; | |
416 | break; | |
417 | case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': | |
418 | case 'a': case 'A': | |
419 | #ifdef HAVE_LONG_DOUBLE | |
420 | if (flags >= 16 || (flags & 4)) | |
421 | type = TYPE_LONGDOUBLE; | |
422 | else | |
423 | #endif | |
424 | type = TYPE_DOUBLE; | |
425 | break; | |
426 | case 'c': | |
427 | if (flags >= 8) | |
428 | #ifdef HAVE_WINT_T | |
429 | type = TYPE_WIDE_CHAR; | |
430 | #else | |
431 | goto error; | |
432 | #endif | |
433 | else | |
434 | type = TYPE_CHAR; | |
435 | break; | |
436 | #ifdef HAVE_WINT_T | |
437 | case 'C': | |
438 | type = TYPE_WIDE_CHAR; | |
439 | c = 'c'; | |
440 | break; | |
441 | #endif | |
442 | case 's': | |
443 | if (flags >= 8) | |
444 | #ifdef HAVE_WCHAR_T | |
445 | type = TYPE_WIDE_STRING; | |
446 | #else | |
447 | goto error; | |
448 | #endif | |
449 | else | |
450 | type = TYPE_STRING; | |
451 | break; | |
452 | #ifdef HAVE_WCHAR_T | |
453 | case 'S': | |
454 | type = TYPE_WIDE_STRING; | |
455 | c = 's'; | |
456 | break; | |
457 | #endif | |
458 | case 'p': | |
459 | type = TYPE_POINTER; | |
460 | break; | |
461 | case 'n': | |
462 | #ifdef HAVE_LONG_LONG | |
463 | if (flags >= 16 || (flags & 4)) | |
464 | type = TYPE_COUNT_LONGLONGINT_POINTER; | |
465 | else | |
466 | #endif | |
467 | if (flags >= 8) | |
468 | type = TYPE_COUNT_LONGINT_POINTER; | |
469 | else if (flags & 2) | |
470 | type = TYPE_COUNT_SCHAR_POINTER; | |
471 | else if (flags & 1) | |
472 | type = TYPE_COUNT_SHORT_POINTER; | |
473 | else | |
474 | type = TYPE_COUNT_INT_POINTER; | |
475 | break; | |
476 | case '%': | |
477 | type = TYPE_NONE; | |
478 | break; | |
479 | default: | |
480 | /* Unknown conversion character. */ | |
481 | goto error; | |
482 | } | |
483 | } | |
484 | ||
485 | if (type != TYPE_NONE) | |
486 | { | |
487 | dp->arg_index = arg_index; | |
488 | if (dp->arg_index == ARG_NONE) | |
489 | { | |
490 | dp->arg_index = arg_posn++; | |
491 | if (dp->arg_index == ARG_NONE) | |
492 | /* arg_posn wrapped around. */ | |
493 | goto error; | |
494 | } | |
495 | REGISTER_ARG (dp->arg_index, type); | |
496 | } | |
497 | dp->conversion = c; | |
498 | dp->dir_end = cp; | |
499 | } | |
500 | ||
501 | d->count++; | |
502 | if (d->count >= d_allocated) | |
503 | { | |
504 | size_t memory_size; | |
505 | DIRECTIVE *memory; | |
506 | ||
507 | d_allocated = xtimes (d_allocated, 2); | |
508 | memory_size = xtimes (d_allocated, sizeof (DIRECTIVE)); | |
509 | if (size_overflow_p (memory_size)) | |
510 | /* Overflow, would lead to out of memory. */ | |
511 | goto error; | |
512 | memory = realloc (d->dir, memory_size); | |
513 | if (memory == NULL) | |
514 | /* Out of memory. */ | |
515 | goto error; | |
516 | d->dir = memory; | |
517 | } | |
518 | } | |
519 | } | |
520 | d->dir[d->count].dir_start = cp; | |
521 | ||
522 | d->max_width_length = max_width_length; | |
523 | d->max_precision_length = max_precision_length; | |
524 | return 0; | |
525 | ||
526 | error: | |
45e338f5 JM |
527 | free (a->arg); |
528 | free (d->dir); | |
b0cacab0 TT |
529 | return -1; |
530 | } | |
531 | ||
532 | #undef DIRECTIVES | |
533 | #undef DIRECTIVE | |
534 | #undef CHAR_T | |
535 | #undef PRINTF_PARSE |