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