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 3 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.  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
68static size_t
69strnlen (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
93int
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