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