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