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