source: trunk/third/gmp/printf/repl-vsnprintf.c @ 18191

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