]>
Commit | Line | Data |
---|---|---|
b0cacab0 TT |
1 | /* vsprintf with automatic memory allocation. |
2 | Copyright (C) 1999, 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 | /* Tell glibc's <stdio.h> to provide a prototype for snprintf(). | |
20 | This must come before <config.h> because <config.h> may include | |
21 | <features.h>, and once <features.h> has been included, it's too late. */ | |
22 | #ifndef _GNU_SOURCE | |
23 | # define _GNU_SOURCE 1 | |
24 | #endif | |
25 | ||
26 | #ifdef HAVE_CONFIG_H | |
27 | # include <config.h> | |
28 | #endif | |
29 | #ifndef IN_LIBINTL | |
30 | # include <alloca.h> | |
31 | #endif | |
32 | ||
33 | /* Specification. */ | |
34 | #if WIDE_CHAR_VERSION | |
35 | # include "vasnwprintf.h" | |
36 | #else | |
37 | # include "vasnprintf.h" | |
38 | #endif | |
39 | ||
40 | #include <stdio.h> /* snprintf(), sprintf() */ | |
41 | #include <stdlib.h> /* abort(), malloc(), realloc(), free() */ | |
42 | #include <string.h> /* memcpy(), strlen() */ | |
43 | #include <errno.h> /* errno */ | |
44 | #include <limits.h> /* CHAR_BIT */ | |
45 | #include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */ | |
46 | #if WIDE_CHAR_VERSION | |
47 | # include "wprintf-parse.h" | |
48 | #else | |
49 | # include "printf-parse.h" | |
50 | #endif | |
51 | ||
52 | /* Checked size_t computations. */ | |
53 | #include "xsize.h" | |
54 | ||
55 | #ifdef HAVE_WCHAR_T | |
56 | # ifdef HAVE_WCSLEN | |
57 | # define local_wcslen wcslen | |
58 | # else | |
59 | /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid | |
60 | a dependency towards this library, here is a local substitute. | |
61 | Define this substitute only once, even if this file is included | |
62 | twice in the same compilation unit. */ | |
63 | # ifndef local_wcslen_defined | |
64 | # define local_wcslen_defined 1 | |
65 | static size_t | |
66 | local_wcslen (const wchar_t *s) | |
67 | { | |
68 | const wchar_t *ptr; | |
69 | ||
70 | for (ptr = s; *ptr != (wchar_t) 0; ptr++) | |
71 | ; | |
72 | return ptr - s; | |
73 | } | |
74 | # endif | |
75 | # endif | |
76 | #endif | |
77 | ||
78 | #if WIDE_CHAR_VERSION | |
79 | # define VASNPRINTF vasnwprintf | |
80 | # define CHAR_T wchar_t | |
81 | # define DIRECTIVE wchar_t_directive | |
82 | # define DIRECTIVES wchar_t_directives | |
83 | # define PRINTF_PARSE wprintf_parse | |
84 | # define USE_SNPRINTF 1 | |
85 | # if HAVE_DECL__SNWPRINTF | |
86 | /* On Windows, the function swprintf() has a different signature than | |
87 | on Unix; we use the _snwprintf() function instead. */ | |
88 | # define SNPRINTF _snwprintf | |
89 | # else | |
90 | /* Unix. */ | |
91 | # define SNPRINTF swprintf | |
92 | # endif | |
93 | #else | |
94 | # define VASNPRINTF vasnprintf | |
95 | # define CHAR_T char | |
96 | # define DIRECTIVE char_directive | |
97 | # define DIRECTIVES char_directives | |
98 | # define PRINTF_PARSE printf_parse | |
99 | # define USE_SNPRINTF (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) | |
100 | # if HAVE_DECL__SNPRINTF | |
101 | /* Windows. */ | |
102 | # define SNPRINTF _snprintf | |
103 | # else | |
104 | /* Unix. */ | |
105 | # define SNPRINTF snprintf | |
106 | # endif | |
107 | #endif | |
108 | ||
109 | CHAR_T * | |
110 | VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list args) | |
111 | { | |
112 | DIRECTIVES d; | |
113 | arguments a; | |
114 | ||
115 | if (PRINTF_PARSE (format, &d, &a) < 0) | |
116 | { | |
117 | errno = EINVAL; | |
118 | return NULL; | |
119 | } | |
120 | ||
121 | #define CLEANUP() \ | |
122 | free (d.dir); \ | |
123 | if (a.arg) \ | |
124 | free (a.arg); | |
125 | ||
126 | if (printf_fetchargs (args, &a) < 0) | |
127 | { | |
128 | CLEANUP (); | |
129 | errno = EINVAL; | |
130 | return NULL; | |
131 | } | |
132 | ||
133 | { | |
134 | size_t buf_neededlength; | |
135 | CHAR_T *buf; | |
136 | CHAR_T *buf_malloced; | |
137 | const CHAR_T *cp; | |
138 | size_t i; | |
139 | DIRECTIVE *dp; | |
140 | /* Output string accumulator. */ | |
141 | CHAR_T *result; | |
142 | size_t allocated; | |
143 | size_t length; | |
144 | ||
145 | /* Allocate a small buffer that will hold a directive passed to | |
146 | sprintf or snprintf. */ | |
147 | buf_neededlength = | |
148 | xsum4 (7, d.max_width_length, d.max_precision_length, 6); | |
149 | #if HAVE_ALLOCA | |
150 | if (buf_neededlength < 4000 / sizeof (CHAR_T)) | |
151 | { | |
152 | buf = (CHAR_T *) alloca (buf_neededlength * sizeof (CHAR_T)); | |
153 | buf_malloced = NULL; | |
154 | } | |
155 | else | |
156 | #endif | |
157 | { | |
158 | size_t buf_memsize = xtimes (buf_neededlength, sizeof (CHAR_T)); | |
159 | if (size_overflow_p (buf_memsize)) | |
160 | goto out_of_memory_1; | |
161 | buf = (CHAR_T *) malloc (buf_memsize); | |
162 | if (buf == NULL) | |
163 | goto out_of_memory_1; | |
164 | buf_malloced = buf; | |
165 | } | |
166 | ||
167 | if (resultbuf != NULL) | |
168 | { | |
169 | result = resultbuf; | |
170 | allocated = *lengthp; | |
171 | } | |
172 | else | |
173 | { | |
174 | result = NULL; | |
175 | allocated = 0; | |
176 | } | |
177 | length = 0; | |
178 | /* Invariants: | |
179 | result is either == resultbuf or == NULL or malloc-allocated. | |
180 | If length > 0, then result != NULL. */ | |
181 | ||
182 | /* Ensures that allocated >= needed. Aborts through a jump to | |
183 | out_of_memory if needed is SIZE_MAX or otherwise too big. */ | |
184 | #define ENSURE_ALLOCATION(needed) \ | |
185 | if ((needed) > allocated) \ | |
186 | { \ | |
187 | size_t memory_size; \ | |
188 | CHAR_T *memory; \ | |
189 | \ | |
190 | allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ | |
191 | if ((needed) > allocated) \ | |
192 | allocated = (needed); \ | |
193 | memory_size = xtimes (allocated, sizeof (CHAR_T)); \ | |
194 | if (size_overflow_p (memory_size)) \ | |
195 | goto out_of_memory; \ | |
196 | if (result == resultbuf || result == NULL) \ | |
197 | memory = (CHAR_T *) malloc (memory_size); \ | |
198 | else \ | |
199 | memory = (CHAR_T *) realloc (result, memory_size); \ | |
200 | if (memory == NULL) \ | |
201 | goto out_of_memory; \ | |
202 | if (result == resultbuf && length > 0) \ | |
203 | memcpy (memory, result, length * sizeof (CHAR_T)); \ | |
204 | result = memory; \ | |
205 | } | |
206 | ||
207 | for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++) | |
208 | { | |
209 | if (cp != dp->dir_start) | |
210 | { | |
211 | size_t n = dp->dir_start - cp; | |
212 | size_t augmented_length = xsum (length, n); | |
213 | ||
214 | ENSURE_ALLOCATION (augmented_length); | |
215 | memcpy (result + length, cp, n * sizeof (CHAR_T)); | |
216 | length = augmented_length; | |
217 | } | |
218 | if (i == d.count) | |
219 | break; | |
220 | ||
221 | /* Execute a single directive. */ | |
222 | if (dp->conversion == '%') | |
223 | { | |
224 | size_t augmented_length; | |
225 | ||
226 | if (!(dp->arg_index == ARG_NONE)) | |
227 | abort (); | |
228 | augmented_length = xsum (length, 1); | |
229 | ENSURE_ALLOCATION (augmented_length); | |
230 | result[length] = '%'; | |
231 | length = augmented_length; | |
232 | } | |
233 | else | |
234 | { | |
235 | if (!(dp->arg_index != ARG_NONE)) | |
236 | abort (); | |
237 | ||
238 | if (dp->conversion == 'n') | |
239 | { | |
240 | switch (a.arg[dp->arg_index].type) | |
241 | { | |
242 | case TYPE_COUNT_SCHAR_POINTER: | |
243 | *a.arg[dp->arg_index].a.a_count_schar_pointer = length; | |
244 | break; | |
245 | case TYPE_COUNT_SHORT_POINTER: | |
246 | *a.arg[dp->arg_index].a.a_count_short_pointer = length; | |
247 | break; | |
248 | case TYPE_COUNT_INT_POINTER: | |
249 | *a.arg[dp->arg_index].a.a_count_int_pointer = length; | |
250 | break; | |
251 | case TYPE_COUNT_LONGINT_POINTER: | |
252 | *a.arg[dp->arg_index].a.a_count_longint_pointer = length; | |
253 | break; | |
254 | #ifdef HAVE_LONG_LONG | |
255 | case TYPE_COUNT_LONGLONGINT_POINTER: | |
256 | *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; | |
257 | break; | |
258 | #endif | |
259 | default: | |
260 | abort (); | |
261 | } | |
262 | } | |
263 | else | |
264 | { | |
265 | arg_type type = a.arg[dp->arg_index].type; | |
266 | CHAR_T *p; | |
267 | unsigned int prefix_count; | |
268 | int prefixes[2]; | |
269 | #if !USE_SNPRINTF | |
270 | size_t tmp_length; | |
271 | CHAR_T tmpbuf[700]; | |
272 | CHAR_T *tmp; | |
273 | ||
274 | /* Allocate a temporary buffer of sufficient size for calling | |
275 | sprintf. */ | |
276 | { | |
277 | size_t width; | |
278 | size_t precision; | |
279 | ||
280 | width = 0; | |
281 | if (dp->width_start != dp->width_end) | |
282 | { | |
283 | if (dp->width_arg_index != ARG_NONE) | |
284 | { | |
285 | int arg; | |
286 | ||
287 | if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
288 | abort (); | |
289 | arg = a.arg[dp->width_arg_index].a.a_int; | |
290 | width = (arg < 0 ? (unsigned int) (-arg) : arg); | |
291 | } | |
292 | else | |
293 | { | |
294 | const CHAR_T *digitp = dp->width_start; | |
295 | ||
296 | do | |
297 | width = xsum (xtimes (width, 10), *digitp++ - '0'); | |
298 | while (digitp != dp->width_end); | |
299 | } | |
300 | } | |
301 | ||
302 | precision = 6; | |
303 | if (dp->precision_start != dp->precision_end) | |
304 | { | |
305 | if (dp->precision_arg_index != ARG_NONE) | |
306 | { | |
307 | int arg; | |
308 | ||
309 | if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
310 | abort (); | |
311 | arg = a.arg[dp->precision_arg_index].a.a_int; | |
312 | precision = (arg < 0 ? 0 : arg); | |
313 | } | |
314 | else | |
315 | { | |
316 | const CHAR_T *digitp = dp->precision_start + 1; | |
317 | ||
318 | precision = 0; | |
319 | do | |
320 | precision = xsum (xtimes (precision, 10), *digitp++ - '0'); | |
321 | while (digitp != dp->precision_end); | |
322 | } | |
323 | } | |
324 | ||
325 | switch (dp->conversion) | |
326 | { | |
327 | ||
328 | case 'd': case 'i': case 'u': | |
329 | # ifdef HAVE_LONG_LONG | |
330 | if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) | |
331 | tmp_length = | |
332 | (unsigned int) (sizeof (unsigned long long) * CHAR_BIT | |
333 | * 0.30103 /* binary -> decimal */ | |
334 | * 2 /* estimate for FLAG_GROUP */ | |
335 | ) | |
336 | + 1 /* turn floor into ceil */ | |
337 | + 1; /* account for leading sign */ | |
338 | else | |
339 | # endif | |
340 | if (type == TYPE_LONGINT || type == TYPE_ULONGINT) | |
341 | tmp_length = | |
342 | (unsigned int) (sizeof (unsigned long) * CHAR_BIT | |
343 | * 0.30103 /* binary -> decimal */ | |
344 | * 2 /* estimate for FLAG_GROUP */ | |
345 | ) | |
346 | + 1 /* turn floor into ceil */ | |
347 | + 1; /* account for leading sign */ | |
348 | else | |
349 | tmp_length = | |
350 | (unsigned int) (sizeof (unsigned int) * CHAR_BIT | |
351 | * 0.30103 /* binary -> decimal */ | |
352 | * 2 /* estimate for FLAG_GROUP */ | |
353 | ) | |
354 | + 1 /* turn floor into ceil */ | |
355 | + 1; /* account for leading sign */ | |
356 | break; | |
357 | ||
358 | case 'o': | |
359 | # ifdef HAVE_LONG_LONG | |
360 | if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) | |
361 | tmp_length = | |
362 | (unsigned int) (sizeof (unsigned long long) * CHAR_BIT | |
363 | * 0.333334 /* binary -> octal */ | |
364 | ) | |
365 | + 1 /* turn floor into ceil */ | |
366 | + 1; /* account for leading sign */ | |
367 | else | |
368 | # endif | |
369 | if (type == TYPE_LONGINT || type == TYPE_ULONGINT) | |
370 | tmp_length = | |
371 | (unsigned int) (sizeof (unsigned long) * CHAR_BIT | |
372 | * 0.333334 /* binary -> octal */ | |
373 | ) | |
374 | + 1 /* turn floor into ceil */ | |
375 | + 1; /* account for leading sign */ | |
376 | else | |
377 | tmp_length = | |
378 | (unsigned int) (sizeof (unsigned int) * CHAR_BIT | |
379 | * 0.333334 /* binary -> octal */ | |
380 | ) | |
381 | + 1 /* turn floor into ceil */ | |
382 | + 1; /* account for leading sign */ | |
383 | break; | |
384 | ||
385 | case 'x': case 'X': | |
386 | # ifdef HAVE_LONG_LONG | |
387 | if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) | |
388 | tmp_length = | |
389 | (unsigned int) (sizeof (unsigned long long) * CHAR_BIT | |
390 | * 0.25 /* binary -> hexadecimal */ | |
391 | ) | |
392 | + 1 /* turn floor into ceil */ | |
393 | + 2; /* account for leading sign or alternate form */ | |
394 | else | |
395 | # endif | |
396 | if (type == TYPE_LONGINT || type == TYPE_ULONGINT) | |
397 | tmp_length = | |
398 | (unsigned int) (sizeof (unsigned long) * CHAR_BIT | |
399 | * 0.25 /* binary -> hexadecimal */ | |
400 | ) | |
401 | + 1 /* turn floor into ceil */ | |
402 | + 2; /* account for leading sign or alternate form */ | |
403 | else | |
404 | tmp_length = | |
405 | (unsigned int) (sizeof (unsigned int) * CHAR_BIT | |
406 | * 0.25 /* binary -> hexadecimal */ | |
407 | ) | |
408 | + 1 /* turn floor into ceil */ | |
409 | + 2; /* account for leading sign or alternate form */ | |
410 | break; | |
411 | ||
412 | case 'f': case 'F': | |
413 | # ifdef HAVE_LONG_DOUBLE | |
414 | if (type == TYPE_LONGDOUBLE) | |
415 | tmp_length = | |
416 | (unsigned int) (LDBL_MAX_EXP | |
417 | * 0.30103 /* binary -> decimal */ | |
418 | * 2 /* estimate for FLAG_GROUP */ | |
419 | ) | |
420 | + 1 /* turn floor into ceil */ | |
421 | + 10; /* sign, decimal point etc. */ | |
422 | else | |
423 | # endif | |
424 | tmp_length = | |
425 | (unsigned int) (DBL_MAX_EXP | |
426 | * 0.30103 /* binary -> decimal */ | |
427 | * 2 /* estimate for FLAG_GROUP */ | |
428 | ) | |
429 | + 1 /* turn floor into ceil */ | |
430 | + 10; /* sign, decimal point etc. */ | |
431 | tmp_length = xsum (tmp_length, precision); | |
432 | break; | |
433 | ||
434 | case 'e': case 'E': case 'g': case 'G': | |
435 | case 'a': case 'A': | |
436 | tmp_length = | |
437 | 12; /* sign, decimal point, exponent etc. */ | |
438 | tmp_length = xsum (tmp_length, precision); | |
439 | break; | |
440 | ||
441 | case 'c': | |
442 | # if defined HAVE_WINT_T && !WIDE_CHAR_VERSION | |
443 | if (type == TYPE_WIDE_CHAR) | |
444 | tmp_length = MB_CUR_MAX; | |
445 | else | |
446 | # endif | |
447 | tmp_length = 1; | |
448 | break; | |
449 | ||
450 | case 's': | |
451 | # ifdef HAVE_WCHAR_T | |
452 | if (type == TYPE_WIDE_STRING) | |
453 | { | |
454 | tmp_length = | |
455 | local_wcslen (a.arg[dp->arg_index].a.a_wide_string); | |
456 | ||
457 | # if !WIDE_CHAR_VERSION | |
458 | tmp_length = xtimes (tmp_length, MB_CUR_MAX); | |
459 | # endif | |
460 | } | |
461 | else | |
462 | # endif | |
463 | tmp_length = strlen (a.arg[dp->arg_index].a.a_string); | |
464 | break; | |
465 | ||
466 | case 'p': | |
467 | tmp_length = | |
468 | (unsigned int) (sizeof (void *) * CHAR_BIT | |
469 | * 0.25 /* binary -> hexadecimal */ | |
470 | ) | |
471 | + 1 /* turn floor into ceil */ | |
472 | + 2; /* account for leading 0x */ | |
473 | break; | |
474 | ||
475 | default: | |
476 | abort (); | |
477 | } | |
478 | ||
479 | if (tmp_length < width) | |
480 | tmp_length = width; | |
481 | ||
482 | tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ | |
483 | } | |
484 | ||
485 | if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T)) | |
486 | tmp = tmpbuf; | |
487 | else | |
488 | { | |
489 | size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T)); | |
490 | ||
491 | if (size_overflow_p (tmp_memsize)) | |
492 | /* Overflow, would lead to out of memory. */ | |
493 | goto out_of_memory; | |
494 | tmp = (CHAR_T *) malloc (tmp_memsize); | |
495 | if (tmp == NULL) | |
496 | /* Out of memory. */ | |
497 | goto out_of_memory; | |
498 | } | |
499 | #endif | |
500 | ||
501 | /* Construct the format string for calling snprintf or | |
502 | sprintf. */ | |
503 | p = buf; | |
504 | *p++ = '%'; | |
505 | if (dp->flags & FLAG_GROUP) | |
506 | *p++ = '\''; | |
507 | if (dp->flags & FLAG_LEFT) | |
508 | *p++ = '-'; | |
509 | if (dp->flags & FLAG_SHOWSIGN) | |
510 | *p++ = '+'; | |
511 | if (dp->flags & FLAG_SPACE) | |
512 | *p++ = ' '; | |
513 | if (dp->flags & FLAG_ALT) | |
514 | *p++ = '#'; | |
515 | if (dp->flags & FLAG_ZERO) | |
516 | *p++ = '0'; | |
517 | if (dp->width_start != dp->width_end) | |
518 | { | |
519 | size_t n = dp->width_end - dp->width_start; | |
520 | memcpy (p, dp->width_start, n * sizeof (CHAR_T)); | |
521 | p += n; | |
522 | } | |
523 | if (dp->precision_start != dp->precision_end) | |
524 | { | |
525 | size_t n = dp->precision_end - dp->precision_start; | |
526 | memcpy (p, dp->precision_start, n * sizeof (CHAR_T)); | |
527 | p += n; | |
528 | } | |
529 | ||
530 | switch (type) | |
531 | { | |
532 | #ifdef HAVE_LONG_LONG | |
533 | case TYPE_LONGLONGINT: | |
534 | case TYPE_ULONGLONGINT: | |
535 | *p++ = 'l'; | |
536 | /*FALLTHROUGH*/ | |
537 | #endif | |
538 | case TYPE_LONGINT: | |
539 | case TYPE_ULONGINT: | |
540 | #ifdef HAVE_WINT_T | |
541 | case TYPE_WIDE_CHAR: | |
542 | #endif | |
543 | #ifdef HAVE_WCHAR_T | |
544 | case TYPE_WIDE_STRING: | |
545 | #endif | |
546 | *p++ = 'l'; | |
547 | break; | |
548 | #ifdef HAVE_LONG_DOUBLE | |
549 | case TYPE_LONGDOUBLE: | |
550 | *p++ = 'L'; | |
551 | break; | |
552 | #endif | |
553 | default: | |
554 | break; | |
555 | } | |
556 | *p = dp->conversion; | |
557 | #if USE_SNPRINTF | |
558 | p[1] = '%'; | |
559 | p[2] = 'n'; | |
560 | p[3] = '\0'; | |
561 | #else | |
562 | p[1] = '\0'; | |
563 | #endif | |
564 | ||
565 | /* Construct the arguments for calling snprintf or sprintf. */ | |
566 | prefix_count = 0; | |
567 | if (dp->width_arg_index != ARG_NONE) | |
568 | { | |
569 | if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) | |
570 | abort (); | |
571 | prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; | |
572 | } | |
573 | if (dp->precision_arg_index != ARG_NONE) | |
574 | { | |
575 | if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) | |
576 | abort (); | |
577 | prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int; | |
578 | } | |
579 | ||
580 | #if USE_SNPRINTF | |
581 | /* Prepare checking whether snprintf returns the count | |
582 | via %n. */ | |
583 | ENSURE_ALLOCATION (xsum (length, 1)); | |
584 | result[length] = '\0'; | |
585 | #endif | |
586 | ||
587 | for (;;) | |
588 | { | |
589 | size_t maxlen; | |
590 | int count; | |
591 | int retcount; | |
592 | ||
593 | maxlen = allocated - length; | |
594 | count = -1; | |
595 | retcount = 0; | |
596 | ||
597 | #if USE_SNPRINTF | |
598 | # define SNPRINTF_BUF(arg) \ | |
599 | switch (prefix_count) \ | |
600 | { \ | |
601 | case 0: \ | |
602 | retcount = SNPRINTF (result + length, maxlen, buf, \ | |
603 | arg, &count); \ | |
604 | break; \ | |
605 | case 1: \ | |
606 | retcount = SNPRINTF (result + length, maxlen, buf, \ | |
607 | prefixes[0], arg, &count); \ | |
608 | break; \ | |
609 | case 2: \ | |
610 | retcount = SNPRINTF (result + length, maxlen, buf, \ | |
611 | prefixes[0], prefixes[1], arg, \ | |
612 | &count); \ | |
613 | break; \ | |
614 | default: \ | |
615 | abort (); \ | |
616 | } | |
617 | #else | |
618 | # define SNPRINTF_BUF(arg) \ | |
619 | switch (prefix_count) \ | |
620 | { \ | |
621 | case 0: \ | |
622 | count = sprintf (tmp, buf, arg); \ | |
623 | break; \ | |
624 | case 1: \ | |
625 | count = sprintf (tmp, buf, prefixes[0], arg); \ | |
626 | break; \ | |
627 | case 2: \ | |
628 | count = sprintf (tmp, buf, prefixes[0], prefixes[1],\ | |
629 | arg); \ | |
630 | break; \ | |
631 | default: \ | |
632 | abort (); \ | |
633 | } | |
634 | #endif | |
635 | ||
636 | switch (type) | |
637 | { | |
638 | case TYPE_SCHAR: | |
639 | { | |
640 | int arg = a.arg[dp->arg_index].a.a_schar; | |
641 | SNPRINTF_BUF (arg); | |
642 | } | |
643 | break; | |
644 | case TYPE_UCHAR: | |
645 | { | |
646 | unsigned int arg = a.arg[dp->arg_index].a.a_uchar; | |
647 | SNPRINTF_BUF (arg); | |
648 | } | |
649 | break; | |
650 | case TYPE_SHORT: | |
651 | { | |
652 | int arg = a.arg[dp->arg_index].a.a_short; | |
653 | SNPRINTF_BUF (arg); | |
654 | } | |
655 | break; | |
656 | case TYPE_USHORT: | |
657 | { | |
658 | unsigned int arg = a.arg[dp->arg_index].a.a_ushort; | |
659 | SNPRINTF_BUF (arg); | |
660 | } | |
661 | break; | |
662 | case TYPE_INT: | |
663 | { | |
664 | int arg = a.arg[dp->arg_index].a.a_int; | |
665 | SNPRINTF_BUF (arg); | |
666 | } | |
667 | break; | |
668 | case TYPE_UINT: | |
669 | { | |
670 | unsigned int arg = a.arg[dp->arg_index].a.a_uint; | |
671 | SNPRINTF_BUF (arg); | |
672 | } | |
673 | break; | |
674 | case TYPE_LONGINT: | |
675 | { | |
676 | long int arg = a.arg[dp->arg_index].a.a_longint; | |
677 | SNPRINTF_BUF (arg); | |
678 | } | |
679 | break; | |
680 | case TYPE_ULONGINT: | |
681 | { | |
682 | unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint; | |
683 | SNPRINTF_BUF (arg); | |
684 | } | |
685 | break; | |
686 | #ifdef HAVE_LONG_LONG | |
687 | case TYPE_LONGLONGINT: | |
688 | { | |
689 | long long int arg = a.arg[dp->arg_index].a.a_longlongint; | |
690 | SNPRINTF_BUF (arg); | |
691 | } | |
692 | break; | |
693 | case TYPE_ULONGLONGINT: | |
694 | { | |
695 | unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint; | |
696 | SNPRINTF_BUF (arg); | |
697 | } | |
698 | break; | |
699 | #endif | |
700 | case TYPE_DOUBLE: | |
701 | { | |
702 | double arg = a.arg[dp->arg_index].a.a_double; | |
703 | SNPRINTF_BUF (arg); | |
704 | } | |
705 | break; | |
706 | #ifdef HAVE_LONG_DOUBLE | |
707 | case TYPE_LONGDOUBLE: | |
708 | { | |
709 | long double arg = a.arg[dp->arg_index].a.a_longdouble; | |
710 | SNPRINTF_BUF (arg); | |
711 | } | |
712 | break; | |
713 | #endif | |
714 | case TYPE_CHAR: | |
715 | { | |
716 | int arg = a.arg[dp->arg_index].a.a_char; | |
717 | SNPRINTF_BUF (arg); | |
718 | } | |
719 | break; | |
720 | #ifdef HAVE_WINT_T | |
721 | case TYPE_WIDE_CHAR: | |
722 | { | |
723 | wint_t arg = a.arg[dp->arg_index].a.a_wide_char; | |
724 | SNPRINTF_BUF (arg); | |
725 | } | |
726 | break; | |
727 | #endif | |
728 | case TYPE_STRING: | |
729 | { | |
730 | const char *arg = a.arg[dp->arg_index].a.a_string; | |
731 | SNPRINTF_BUF (arg); | |
732 | } | |
733 | break; | |
734 | #ifdef HAVE_WCHAR_T | |
735 | case TYPE_WIDE_STRING: | |
736 | { | |
737 | const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string; | |
738 | SNPRINTF_BUF (arg); | |
739 | } | |
740 | break; | |
741 | #endif | |
742 | case TYPE_POINTER: | |
743 | { | |
744 | void *arg = a.arg[dp->arg_index].a.a_pointer; | |
745 | SNPRINTF_BUF (arg); | |
746 | } | |
747 | break; | |
748 | default: | |
749 | abort (); | |
750 | } | |
751 | ||
752 | #if USE_SNPRINTF | |
753 | /* Portability: Not all implementations of snprintf() | |
754 | are ISO C 99 compliant. Determine the number of | |
755 | bytes that snprintf() has produced or would have | |
756 | produced. */ | |
757 | if (count >= 0) | |
758 | { | |
759 | /* Verify that snprintf() has NUL-terminated its | |
760 | result. */ | |
761 | if (count < maxlen && result[length + count] != '\0') | |
762 | abort (); | |
763 | /* Portability hack. */ | |
764 | if (retcount > count) | |
765 | count = retcount; | |
766 | } | |
767 | else | |
768 | { | |
769 | /* snprintf() doesn't understand the '%n' | |
770 | directive. */ | |
771 | if (p[1] != '\0') | |
772 | { | |
773 | /* Don't use the '%n' directive; instead, look | |
774 | at the snprintf() return value. */ | |
775 | p[1] = '\0'; | |
776 | continue; | |
777 | } | |
778 | else | |
779 | { | |
780 | /* Look at the snprintf() return value. */ | |
781 | if (retcount < 0) | |
782 | { | |
783 | /* HP-UX 10.20 snprintf() is doubly deficient: | |
784 | It doesn't understand the '%n' directive, | |
785 | *and* it returns -1 (rather than the length | |
786 | that would have been required) when the | |
787 | buffer is too small. */ | |
788 | size_t bigger_need = | |
789 | xsum (xtimes (allocated, 2), 12); | |
790 | ENSURE_ALLOCATION (bigger_need); | |
791 | continue; | |
792 | } | |
793 | else | |
794 | count = retcount; | |
795 | } | |
796 | } | |
797 | #endif | |
798 | ||
799 | /* Attempt to handle failure. */ | |
800 | if (count < 0) | |
801 | { | |
802 | if (!(result == resultbuf || result == NULL)) | |
803 | free (result); | |
45e338f5 | 804 | free (buf_malloced); |
b0cacab0 TT |
805 | CLEANUP (); |
806 | errno = EINVAL; | |
807 | return NULL; | |
808 | } | |
809 | ||
810 | #if !USE_SNPRINTF | |
811 | if (count >= tmp_length) | |
812 | /* tmp_length was incorrectly calculated - fix the | |
813 | code above! */ | |
814 | abort (); | |
815 | #endif | |
816 | ||
817 | /* Make room for the result. */ | |
818 | if (count >= maxlen) | |
819 | { | |
820 | /* Need at least count bytes. But allocate | |
821 | proportionally, to avoid looping eternally if | |
822 | snprintf() reports a too small count. */ | |
823 | size_t n = | |
824 | xmax (xsum (length, count), xtimes (allocated, 2)); | |
825 | ||
826 | ENSURE_ALLOCATION (n); | |
827 | #if USE_SNPRINTF | |
828 | continue; | |
829 | #endif | |
830 | } | |
831 | ||
832 | #if USE_SNPRINTF | |
833 | /* The snprintf() result did fit. */ | |
834 | #else | |
835 | /* Append the sprintf() result. */ | |
836 | memcpy (result + length, tmp, count * sizeof (CHAR_T)); | |
837 | if (tmp != tmpbuf) | |
838 | free (tmp); | |
839 | #endif | |
840 | ||
841 | length += count; | |
842 | break; | |
843 | } | |
844 | } | |
845 | } | |
846 | } | |
847 | ||
848 | /* Add the final NUL. */ | |
849 | ENSURE_ALLOCATION (xsum (length, 1)); | |
850 | result[length] = '\0'; | |
851 | ||
852 | if (result != resultbuf && length + 1 < allocated) | |
853 | { | |
854 | /* Shrink the allocated memory if possible. */ | |
855 | CHAR_T *memory; | |
856 | ||
857 | memory = (CHAR_T *) realloc (result, (length + 1) * sizeof (CHAR_T)); | |
858 | if (memory != NULL) | |
859 | result = memory; | |
860 | } | |
861 | ||
45e338f5 | 862 | free (buf_malloced); |
b0cacab0 TT |
863 | CLEANUP (); |
864 | *lengthp = length; | |
865 | return result; | |
866 | ||
867 | out_of_memory: | |
868 | if (!(result == resultbuf || result == NULL)) | |
869 | free (result); | |
45e338f5 | 870 | free (buf_malloced); |
b0cacab0 TT |
871 | out_of_memory_1: |
872 | CLEANUP (); | |
873 | errno = ENOMEM; | |
874 | return NULL; | |
875 | } | |
876 | } | |
877 | ||
878 | #undef SNPRINTF | |
879 | #undef USE_SNPRINTF | |
880 | #undef PRINTF_PARSE | |
881 | #undef DIRECTIVES | |
882 | #undef DIRECTIVE | |
883 | #undef CHAR_T | |
884 | #undef VASNPRINTF |