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