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