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