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 = &prec;
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