Upload Tizen:Base source
[external/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 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 */