1 /* -*- mode: C; mode: fold; -*- */
2 /*
3 This file is part of SLRN.
4
5 Copyright (c) 2001-2006 Thomas Schultz <tststs@gmx.de>
6
7 Based on code from glib 1.2.8; original copyright notice:
8 Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald
9 Modified by the GLib Team and others 1997-1999. See the AUTHORS
10 file for a list of people on the GLib Team. See the ChangeLog
11 files for a list of changes. These files are distributed with
12 GLib at ftp://ftp.gtk.org/pub/gtk/.
13
14 This program is free software; you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by the Free
16 Software Foundation; either version 2 of the License, or (at your option)
17 any later version.
18
19 This program is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
22 more details.
23
24 You should have received a copy of the GNU General Public License along
25 with this program; if not, write to the Free Software Foundation, Inc.,
26 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 */
28
29 /*{{{ include files */
30 #include "config.h"
31 #include "slrnfeat.h"
32
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <string.h>
36 #ifdef HAVE_STDLIB_H
37 # include <stdlib.h>
38 #endif
39
40 #include "jdmacros.h"
41 #include "snprintf.h"
42 #include "util.h"
43 #include "strutil.h"
44
45 /*}}}*/
46
47 /*{{{ static function declarations and defines */
48 static unsigned int printf_string_upper_bound (const char*, va_list);
49
50 /* Define VA_COPY() to do the right thing for copying va_list variables.
51 * config.h may have already defined VA_COPY as va_copy or __va_copy.
52 */
53 #ifndef VA_COPY
54 # if (defined (__GNUC__) && defined (__PPC__) && (defined (_CALL_SYSV) || defined (__WIN32__))) || defined (__WATCOMC__)
55 # define VA_COPY(ap1, ap2) (*(ap1) = *(ap2))
56 # elif defined (VA_COPY_AS_ARRAY)
57 # define VA_COPY(ap1, ap2) memmove ((ap1), (ap2), sizeof (va_list))
58 # else /* va_list is a pointer */
59 # define VA_COPY(ap1, ap2) ((ap1) = (ap2))
60 # endif /* va_list is a pointer */
61 #endif /* !VA_COPY */
62
63 /*}}}*/
64
65 /* Remember that you explicitly need to pass NULL as the final argument! */
slrn_strdup_strcat(const char * str,...)66 char *slrn_strdup_strcat (const char *str, ...) /*{{{*/
67 {
68 char *buffer, *cur;
69 const char *p;
70 unsigned int len = 0;
71 va_list args;
72
73 if ((p = str) == NULL) return NULL;
74
75 va_start (args, str);
76 while (p != NULL)
77 {
78 len += strlen (p);
79 p = va_arg (args, const char *);
80 }
81 va_end (args);
82
83 cur = buffer = slrn_safe_malloc (len + 1);
84
85 va_start (args, str);
86 p = str;
87 while (p != NULL)
88 {
89 strcpy (cur, p); /* safe */
90 p = va_arg (args, const char *);
91 cur += strlen (cur);
92 }
93 va_end (args);
94
95 return buffer;
96 }
97 /*}}}*/
98
slrn_strdup_vprintf(const char * format,va_list args1)99 char *slrn_strdup_vprintf (const char *format, va_list args1) /*{{{*/
100 {
101 char *buffer;
102 va_list args2;
103
104 if (format == NULL) return NULL;
105
106 VA_COPY (args2, args1);
107
108 buffer = slrn_safe_malloc (printf_string_upper_bound (format, args1));
109
110 vsprintf (buffer, format, args2); /* safe */
111 va_end (args2);
112
113 return buffer;
114 }
115
116 /*}}}*/
117
slrn_strdup_printf(const char * format,...)118 char *slrn_strdup_printf (const char *format, ... ) /*{{{*/
119 {
120 va_list args;
121 char *retval;
122
123 va_start (args, format);
124 retval = slrn_strdup_vprintf (format, args);
125 va_end (args);
126
127 return retval;
128 }
129
130 /*}}}*/
131
slrn_vsnprintf(char * str,size_t n,const char * format,va_list ap)132 int slrn_vsnprintf (char *str, size_t n, const char *format, /*{{{*/
133 va_list ap)
134 {
135 int retval;
136
137 retval = vsnprintf (str, n, format, ap);
138
139 if ((retval == -1) || (retval > (int) n))
140 {
141 str[n-1] = '\0';
142 retval = (int) n;
143 }
144
145 return retval;
146 }
147
148 /*}}}*/
149
slrn_snprintf(char * str,size_t n,const char * format,...)150 int slrn_snprintf (char *str, size_t n, const char *format, ... ) /*{{{*/
151 {
152 va_list args;
153 int retval;
154
155 va_start (args, format);
156 retval = vsnprintf (str, n, format, args);
157 va_end (args);
158
159 if ((retval == -1) || (retval > (int) n))
160 {
161 str[n-1] = '\0';
162 retval = (int) n;
163 }
164
165 return retval;
166 }
167
168 /*}}}*/
169
170 #ifndef HAVE_VSNPRINTF
snprintf(char * str,size_t n,const char * format,...)171 int snprintf (char *str, size_t n, const char *format, ... ) /*{{{*/
172 {
173 va_list args;
174 int retval;
175 char *printed;
176
177 if (str == NULL) return 0;
178 if (n <= 0) return 0;
179 if (format == NULL) return 0;
180
181 va_start (args, format);
182 printed = slrn_strdup_vprintf (format, args);
183 va_end (args);
184
185 strncpy (str, printed, n);
186 retval = strlen (printed); /* behave like glibc 2.1 */
187
188 slrn_free (printed);
189
190 return retval;
191 }
192
193 /*}}}*/
194
vsnprintf(char * str,size_t n,const char * format,va_list ap)195 int vsnprintf (char *str, size_t n, const char *format, va_list ap) /*{{{*/
196 {
197 char *printed;
198 int retval;
199
200 if (str == NULL) return 0;
201 if (n <= 0) return 0;
202 if (format == NULL) return 0;
203
204 printed = slrn_strdup_vprintf (format, ap);
205 strncpy (str, printed, n);
206 retval = strlen (printed); /* behave like glibc 2.1 */
207
208 slrn_free (printed);
209
210 return retval;
211 }
212
213 /*}}}*/
214 #endif /* !HAVE_VSNPRINTF */
215
216 /* Note: This function is completely rewritten in glib 1.2.9 and later.
217 * In the long run, I should use their new code; currently, I believe slrn
218 * uses no format strings that could cause problems with this version. */
219
printf_string_upper_bound(const char * format,va_list args)220 static unsigned int printf_string_upper_bound (const char* format, /*{{{*/
221 va_list args)
222 {
223 unsigned int len = 1;
224
225 while (*format)
226 {
227 int long_int = 0;
228 int extra_long = 0;
229 char c;
230
231 c = *format++;
232
233 if (c == '%')
234 {
235 int done = 0;
236
237 while (*format && !done)
238 {
239 switch (*format++)
240 {
241 char *string_arg;
242
243 case '*':
244 len += va_arg (args, int);
245 break;
246 case '1':
247 case '2':
248 case '3':
249 case '4':
250 case '5':
251 case '6':
252 case '7':
253 case '8':
254 case '9':
255 /* add specified format length, since it might exceed
256 * the size we assume it to have. */
257 format -= 1;
258 len += strtol (format, (char **)&format, 10);
259 break;
260 case 'h':
261 /* ignore short int flag, since all args have at least
262 * the same size as an int */
263 break;
264 case 'l':
265 if (long_int)
266 extra_long = 1; /* linux specific */
267 else
268 long_int = 1;
269 break;
270 case 'q':
271 case 'L':
272 long_int = 1;
273 extra_long = 1;
274 break;
275 case 's':
276 string_arg = va_arg (args, char *);
277 if (NULL != string_arg)
278 len += strlen (string_arg);
279 else
280 {
281 /* add enough padding to hold "(null)" identifier */
282 len += 16;
283 }
284 done = 1;
285 break;
286 case 'd':
287 case 'i':
288 case 'o':
289 case 'u':
290 case 'x':
291 case 'X':
292 {
293 if (long_int)
294 (void) va_arg (args, long);
295 else
296 (void) va_arg (args, int);
297 }
298 len += extra_long ? 64 : 32;
299 done = 1;
300 break;
301 case 'D':
302 case 'O':
303 case 'U':
304 (void) va_arg (args, long);
305 len += 32;
306 done = 1;
307 break;
308 case 'e':
309 case 'E':
310 case 'f':
311 case 'g':
312 #ifdef HAVE_LONG_DOUBLE
313 /* Warning: There is currently no test in ./configure to enable this */
314 if (extra_long)
315 (void) va_arg (args, long double);
316 else
317 #endif /* HAVE_LONG_DOUBLE */
318 (void) va_arg (args, double);
319 len += extra_long ? 128 : 64;
320 done = 1;
321 break;
322 case 'c':
323 (void) va_arg (args, int);
324 len += 1;
325 done = 1;
326 break;
327 case 'p':
328 case 'n':
329 (void) va_arg (args, void*);
330 len += 32;
331 done = 1;
332 break;
333 case '%':
334 len += 1;
335 done = 1;
336 break;
337 default:
338 /* ignore unknow/invalid flags */
339 break;
340 }
341 }
342 }
343 else
344 len += 1;
345 }
346
347 return len;
348 }
349
350 /*}}}*/
351