Imported Upstream version 6.0.0
[platform/upstream/gmp.git] / printf / repl-vsnprintf.c
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 */