1/* Like vsprintf but provides a pointer to malloc'd storage, which must
2   be freed by the caller.
3   Copyright (C) 1994, 1998, 1999, 2000-2003, 2006 Free Software Foundation, Inc.
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2, or (at your option)
8any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software Foundation,
17Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19#include <config.h>
20
21/* Specification.  */
22#include "vasprintf.h"
23
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include <stdarg.h>
28#include <math.h>
29
30#ifdef TEST
31size_t global_total_width;
32#endif
33
34static int
35int_vasprintf (char **result, const char *format, va_list *args)
36{
37  const char *p = format;
38  /* Add one to make sure that it is never zero, which might cause malloc
39     to return NULL.  */
40  size_t total_width = strlen (format) + 1;
41  va_list ap = *args;
42
43  while (*p != '\0')
44    {
45      if (*p++ == '%')
46	{
47	  while (strchr ("-+ #0", *p))
48	    ++p;
49	  if (*p == '*')
50	    {
51	      ++p;
52	      total_width += abs (va_arg (ap, int));
53	    }
54	  else
55	    total_width += strtoul (p, (char **) &p, 10);
56	  if (*p == '.')
57	    {
58	      ++p;
59	      if (*p == '*')
60		{
61		  ++p;
62		  total_width += abs (va_arg (ap, int));
63		}
64	      else
65		total_width += strtoul (p, (char **) &p, 10);
66	    }
67	  while (strchr ("hlLjtz", *p))
68	    ++p;
69	  /* Should be big enough for any format specifier except %s
70	     and floats.  */
71	  total_width += 30;
72	  switch (*p)
73	    {
74	    case 'd':
75	    case 'i':
76	    case 'o':
77	    case 'u':
78	    case 'x':
79	    case 'X':
80	    case 'c':
81	      (void) va_arg (ap, int);
82	      break;
83	    case 'f':
84	    case 'F':
85	      {
86		double arg = va_arg (ap, double);
87		if (arg >= 1.0 || arg <= -1.0)
88		  /* Since an ieee double can have an exponent of 307, we'll
89		     make the buffer wide enough to cover the gross case. */
90		  total_width += 307;
91	      }
92	      break;
93	    case 'e':
94	    case 'E':
95	    case 'g':
96	    case 'G':
97	      (void) va_arg (ap, double);
98	      break;
99	    case 's':
100	      total_width += strlen (va_arg (ap, char *));
101	      break;
102	    case 'p':
103	    case 'n':
104	      (void) va_arg (ap, char *);
105	      break;
106	    }
107	  p++;
108	}
109    }
110#ifdef TEST
111  global_total_width = total_width;
112#endif
113  *result = malloc (total_width);
114  if (*result != NULL)
115    return vsprintf (*result, format, *args);
116  else
117    return -1;
118}
119
120int
121vasprintf (char **result, const char *format, va_list args)
122{
123  return int_vasprintf (result, format, &args);
124}
125
126int
127asprintf (char **result, const char *format, ...)
128{
129  va_list args;
130  int done;
131
132  va_start (args, format);
133  done = vasprintf (result, format, args);
134  va_end (args);
135
136  return done;
137}
138
139/* ========================= test program ========================= */
140
141#ifdef TEST
142
143#include <float.h>
144
145void
146checkit (const char* format, ...)
147{
148  va_list args;
149  char *result;
150
151  va_start (args, format);
152  vasprintf (&result, format, args);
153  if (strlen (result) < global_total_width)
154    printf ("PASS: ");
155  else
156    printf ("FAIL: ");
157  printf ("%lu %s\n", (unsigned long) global_total_width, result);
158}
159
160int
161main ()
162{
163  checkit ("%d", 0x12345678);
164  checkit ("%200d", 5);
165  checkit ("%.300d", 6);
166  checkit ("%100.150d", 7);
167  checkit ("%s", "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
168777777777777777777333333333333366666666666622222222222777777777777733333");
169  checkit ("%f%s%d%s", 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx");
170  checkit ("%e", DBL_MIN);
171  checkit ("%e", DBL_MAX);
172  checkit ("%.400f", DBL_MIN);
173  checkit ("%f", DBL_MAX);
174  checkit ("%g", DBL_MIN);
175  checkit ("%g", DBL_MAX);
176  return 0;
177}
178
179#endif /* TEST */
180