xref: /netbsd-src/external/gpl2/gettext/dist/gnulib-local/lib/vasprintf.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
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