vasprintf.c revision 1.1 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
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 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
31 size_t global_total_width;
32 #endif
33
34 static int
35 int_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
120 int
121 vasprintf (char **result, const char *format, va_list args)
122 {
123 return int_vasprintf (result, format, &args);
124 }
125
126 int
127 asprintf (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
145 void
146 checkit (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
160 int
161 main ()
162 {
163 checkit ("%d", 0x12345678);
164 checkit ("%200d", 5);
165 checkit ("%.300d", 6);
166 checkit ("%100.150d", 7);
167 checkit ("%s", "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
168 777777777777777777333333333333366666666666622222222222777777777777733333");
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