1 /* __gmp_replacement_vsnprintf -- for systems which don't have vsnprintf, or
2 only have a broken one.
3
4 THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY. THEY'RE ALMOST
5 CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
6 FUTURE GNU MP RELEASES.
7
8 Copyright 2001, 2002 Free Software Foundation, Inc.
9
10 This file is part of the GNU MP Library.
11
12 The GNU MP Library is free software; you can redistribute it and/or modify
13 it under the terms of the GNU Lesser General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or (at your
15 option) any later version.
16
17 The GNU MP Library is distributed in the hope that it will be useful, but
18 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
20 License for more details.
21
22 You should have received a copy of the GNU Lesser General Public License
23 along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
24
25 #include "config.h"
26
27 #if ! HAVE_VSNPRINTF /* only need this file if we don't have vsnprintf */
28
29
30 #define _GNU_SOURCE /* for strnlen prototype */
31
32 #if HAVE_STDARG
33 #include <stdarg.h>
34 #else
35 #include <varargs.h>
36 #endif
37
38 #include <ctype.h> /* for isdigit */
39 #include <stddef.h> /* for ptrdiff_t */
40 #include <string.h>
41 #include <stdio.h> /* for NULL */
42 #include <stdlib.h>
43
44 #if HAVE_FLOAT_H
45 #include <float.h> /* for DBL_MAX_10_EXP etc */
46 #endif
47
48 #if HAVE_INTTYPES_H
49 # include <inttypes.h> /* for intmax_t */
50 #else
51 # if HAVE_STDINT_H
52 # include <stdint.h>
53 # endif
54 #endif
55
56 #if HAVE_SYS_TYPES_H
57 #include <sys/types.h> /* for quad_t */
58 #endif
59
60 #include "gmp.h"
61 #include "gmp-impl.h"
62
63
64 /* Autoconf notes that AIX 4.3 has a broken strnlen, but fortunately it
65 doesn't affect us since __gmp_replacement_vsnprintf is not required on
66 that system. */
67 #if ! HAVE_STRNLEN
68 static size_t
strnlen(const char * s,size_t n)69 strnlen (const char *s, size_t n)
70 {
71 size_t i;
72 for (i = 0; i < n; i++)
73 if (s[i] == '\0')
74 break;
75 return i;
76 }
77 #endif
78
79
80 /* The approach here is to parse the fmt string, and decide how much space
81 it requires, then use vsprintf into a big enough buffer. The space
82 calculated isn't an exact amount, but it's certainly no less than
83 required.
84
85 This code was inspired by GNU libiberty/vasprintf.c but we support more
86 datatypes, when available.
87
88 mingw32 - doesn't have vsnprintf, it seems. Because gcc is used a full
89 set of types are available, but "long double" is just a plain IEEE
90 64-bit "double" and LDBL_MAX_EXP_10 is correspondingly defined, so we
91 avoid the big 15-bit exponent estimate. */
92
93 int
__gmp_replacement_vsnprintf(char * buf,size_t buf_size,const char * orig_fmt,va_list orig_ap)94 __gmp_replacement_vsnprintf (char *buf, size_t buf_size,
95 const char *orig_fmt, va_list orig_ap)
96 {
97 va_list ap;
98 const char *fmt;
99 size_t total_width, integer_sizeof, floating_sizeof, len;
100 char fchar, type;
101 int width, prec, seen_prec, double_digits, long_double_digits;
102 int *value;
103
104 /* preserve orig_ap for use after size estimation */
105 va_copy (ap, orig_ap);
106
107 fmt = orig_fmt;
108 total_width = strlen (fmt) + 1; /* 1 extra for the '\0' */
109
110 integer_sizeof = sizeof (long);
111 #if HAVE_LONG_LONG
112 integer_sizeof = MAX (integer_sizeof, sizeof (long long));
113 #endif
114 #if HAVE_QUAD_T
115 integer_sizeof = MAX (integer_sizeof, sizeof (quad_t));
116 #endif
117
118 floating_sizeof = sizeof (double);
119 #if HAVE_LONG_DOUBLE
120 floating_sizeof = MAX (floating_sizeof, sizeof (long double));
121 #endif
122
123 /* IEEE double or VAX G floats have an 11 bit exponent, so the default is
124 a maximum 308 decimal digits. VAX D floats have only an 8 bit
125 exponent, but we don't bother trying to detect that directly. */
126 double_digits = 308;
127 #ifdef DBL_MAX_10_EXP
128 /* but in any case prefer a value the compiler says */
129 double_digits = DBL_MAX_10_EXP;
130 #endif
131
132 /* IEEE 128-bit quad, Intel 80-bit temporary, or VAX H floats all have 15
133 bit exponents, so the default is a maximum 4932 decimal digits. */
134 long_double_digits = 4932;
135 /* but if double == long double, then go with that size */
136 #if HAVE_LONG_DOUBLE
137 if (sizeof (double) == sizeof (long double))
138 long_double_digits = double_digits;
139 #endif
140 #ifdef LDBL_MAX_10_EXP
141 /* but in any case prefer a value the compiler says */
142 long_double_digits = LDBL_MAX_10_EXP;
143 #endif
144
145 for (;;)
146 {
147 fmt = strchr (fmt, '%');
148 if (fmt == NULL)
149 break;
150 fmt++;
151
152 type = '\0';
153 width = 0;
154 prec = 6;
155 seen_prec = 0;
156 value = &width;
157
158 for (;;)
159 {
160 fchar = *fmt++;
161 switch (fchar) {
162
163 case 'c':
164 /* char, already accounted for by strlen(fmt) */
165 goto next;
166
167 case 'd':
168 case 'i':
169 case 'o':
170 case 'x':
171 case 'X':
172 case 'u':
173 /* at most 3 digits per byte in hex, dec or octal, plus a sign */
174 total_width += 3 * integer_sizeof + 1;
175
176 switch (type) {
177 case 'j':
178 /* Let's assume uintmax_t is the same size as intmax_t. */
179 #if HAVE_INTMAX_T
180 (void) va_arg (ap, intmax_t);
181 #else
182 ASSERT_FAIL (intmax_t not available);
183 #endif
184 break;
185 case 'l':
186 (void) va_arg (ap, long);
187 break;
188 case 'L':
189 #if HAVE_LONG_LONG
190 (void) va_arg (ap, long long);
191 #else
192 ASSERT_FAIL (long long not available);
193 #endif
194 break;
195 case 'q':
196 /* quad_t is probably the same as long long, but let's treat
197 it separately just to be sure. Also let's assume u_quad_t
198 will be the same size as quad_t. */
199 #if HAVE_QUAD_T
200 (void) va_arg (ap, quad_t);
201 #else
202 ASSERT_FAIL (quad_t not available);
203 #endif
204 break;
205 case 't':
206 #if HAVE_PTRDIFF_T
207 (void) va_arg (ap, ptrdiff_t);
208 #else
209 ASSERT_FAIL (ptrdiff_t not available);
210 #endif
211 break;
212 case 'z':
213 (void) va_arg (ap, size_t);
214 break;
215 default:
216 /* default is an "int", and this includes h=short and hh=char
217 since they're promoted to int in a function call */
218 (void) va_arg (ap, int);
219 break;
220 }
221 goto next;
222
223 case 'E':
224 case 'e':
225 case 'G':
226 case 'g':
227 /* Requested decimals, sign, point and e, plus an overestimate
228 of exponent digits (the assumption is all the float is
229 exponent!). */
230 total_width += prec + 3 + floating_sizeof * 3;
231 if (type == 'L')
232 {
233 #if HAVE_LONG_DOUBLE
234 (void) va_arg (ap, long double);
235 #else
236 ASSERT_FAIL (long double not available);
237 #endif
238 }
239 else
240 (void) va_arg (ap, double);
241 break;
242
243 case 'f':
244 /* Requested decimals, sign and point, and a margin for error,
245 then add the maximum digits that can be in the integer part,
246 based on the maximum exponent value. */
247 total_width += prec + 2 + 10;
248 if (type == 'L')
249 {
250 #if HAVE_LONG_DOUBLE
251 (void) va_arg (ap, long double);
252 total_width += long_double_digits;
253 #else
254 ASSERT_FAIL (long double not available);
255 #endif
256 }
257 else
258 {
259 (void) va_arg (ap, double);
260 total_width += double_digits;
261 }
262 break;
263
264 case 'h': /* short or char */
265 case 'j': /* intmax_t */
266 case 'L': /* long long or long double */
267 case 'q': /* quad_t */
268 case 't': /* ptrdiff_t */
269 set_type:
270 type = fchar;
271 break;
272
273 case 'l':
274 /* long or long long */
275 if (type != 'l')
276 goto set_type;
277 type = 'L'; /* "ll" means "L" */
278 break;
279
280 case 'n':
281 /* bytes written, no output as such */
282 (void) va_arg (ap, void *);
283 goto next;
284
285 case 's':
286 /* If no precision was given, then determine the string length
287 and put it there, to be added to the total under "next". If
288 a precision was given then that's already the maximum from
289 this field, but see whether the string is shorter than that,
290 in case the limit was very big. */
291 {
292 const char *s = va_arg (ap, const char *);
293 prec = (seen_prec ? strnlen (s, prec) : strlen (s));
294 }
295 goto next;
296
297 case 'p':
298 /* pointer, let's assume at worst it's octal with some padding */
299 (void) va_arg (ap, const void *);
300 total_width += 3 * sizeof (void *) + 16;
301 goto next;
302
303 case '%':
304 /* literal %, already accounted for by strlen(fmt) */
305 goto next;
306
307 case '#':
308 /* showbase, at most 2 for "0x" */
309 total_width += 2;
310 break;
311
312 case '+':
313 case ' ':
314 /* sign, already accounted for under numerics */
315 break;
316
317 case '-':
318 /* left justify, no effect on total width */
319 break;
320
321 case '.':
322 seen_prec = 1;
323 value = ≺
324 break;
325
326 case '*':
327 {
328 /* negative width means left justify which can be ignored,
329 negative prec would be invalid, just use absolute value */
330 int n = va_arg (ap, int);
331 *value = ABS (n);
332 }
333 break;
334
335 case '0': case '1': case '2': case '3': case '4':
336 case '5': case '6': case '7': case '8': case '9':
337 /* process all digits to form a value */
338 {
339 int n = 0;
340 do {
341 n = n * 10 + (fchar-'0');
342 fchar = *fmt++;
343 } while (isascii (fchar) && isdigit (fchar));
344 fmt--; /* unget the non-digit */
345 *value = n;
346 }
347 break;
348
349 default:
350 /* incomplete or invalid % sequence */
351 ASSERT (0);
352 goto next;
353 }
354 }
355
356 next:
357 total_width += width;
358 total_width += prec;
359 }
360
361 if (total_width <= buf_size)
362 {
363 vsprintf (buf, orig_fmt, orig_ap);
364 len = strlen (buf);
365 }
366 else
367 {
368 char *s;
369
370 s = __GMP_ALLOCATE_FUNC_TYPE (total_width, char);
371 vsprintf (s, orig_fmt, orig_ap);
372 len = strlen (s);
373 if (buf_size != 0)
374 {
375 size_t copylen = MIN (len, buf_size-1);
376 memcpy (buf, s, copylen);
377 buf[copylen] = '\0';
378 }
379 (*__gmp_free_func) (s, total_width);
380 }
381
382 /* If total_width was somehow wrong then chances are we've already
383 clobbered memory, but maybe this check will still work. */
384 ASSERT_ALWAYS (len < total_width);
385
386 return len;
387 }
388
389 #endif /* ! HAVE_VSNPRINTF */
390