1 /*
2 * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $"
3 *
4 * String functions for Mini-XML, a small XML-like file parsing library.
5 *
6 * Copyright 2003-2010 by Michael R Sweet.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Michael R Sweet and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "COPYING"
11 * which should have been included with this file. If this file is
12 * missing or damaged, see the license at:
13 *
14 * http://www.minixml.org/
15 *
16 * Contents:
17 *
18 * _mxml_snprintf() - Format a string.
19 * _mxml_strdup() - Duplicate a string.
20 * _mxml_strdupf() - Format and duplicate a string.
21 * _mxml_vsnprintf() - Format a string into a fixed size buffer.
22 * _mxml_vstrdupf() - Format and duplicate a string.
23 */
25 /*
26 * Include necessary headers...
27 */
29 #include "config.h"
32 /*
33 * The va_copy macro is part of C99, but many compilers don't implement it.
34 * Provide a "direct assignment" implmentation when va_copy isn't defined...
35 */
37 #ifndef va_copy
38 # ifdef __va_copy
39 # define va_copy(dst,src) __va_copy(dst,src)
40 # else
41 # define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list))
42 # endif /* __va_copy */
43 #endif /* va_copy */
46 #ifndef HAVE_SNPRINTF
47 /*
48 * '_mxml_snprintf()' - Format a string.
49 */
51 int /* O - Number of bytes formatted */
52 _mxml_snprintf(char *buffer, /* I - Output buffer */
53 size_t bufsize, /* I - Size of output buffer */
54 const char *format, /* I - Printf-style format string */
55 ...) /* I - Additional arguments as needed */
56 {
57 va_list ap; /* Argument list */
58 int bytes; /* Number of bytes formatted */
61 va_start(ap, format);
62 bytes = vsnprintf(buffer, bufsize, format, ap);
63 va_end(ap);
65 return (bytes);
66 }
67 #endif /* !HAVE_SNPRINTF */
70 /*
71 * '_mxml_strdup()' - Duplicate a string.
72 */
74 #ifndef HAVE_STRDUP
75 char * /* O - New string pointer */
76 _mxml_strdup(const char *s) /* I - String to duplicate */
77 {
78 char *t; /* New string pointer */
81 if (s == NULL)
82 return (NULL);
84 if ((t = malloc(strlen(s) + 1)) == NULL)
85 return (NULL);
87 return (strcpy(t, s));
88 }
89 #endif /* !HAVE_STRDUP */
92 /*
93 * '_mxml_strdupf()' - Format and duplicate a string.
94 */
96 char * /* O - New string pointer */
97 _mxml_strdupf(const char *format, /* I - Printf-style format string */
98 ...) /* I - Additional arguments as needed */
99 {
100 va_list ap; /* Pointer to additional arguments */
101 char *s; /* Pointer to formatted string */
104 /*
105 * Get a pointer to the additional arguments, format the string,
106 * and return it...
107 */
109 va_start(ap, format);
110 s = _mxml_vstrdupf(format, ap);
111 va_end(ap);
113 return (s);
114 }
117 #ifndef HAVE_VSNPRINTF
118 /*
119 * '_mxml_vsnprintf()' - Format a string into a fixed size buffer.
120 */
122 int /* O - Number of bytes formatted */
123 _mxml_vsnprintf(char *buffer, /* O - Output buffer */
124 size_t bufsize, /* O - Size of output buffer */
125 const char *format, /* I - Printf-style format string */
126 va_list ap) /* I - Pointer to additional arguments */
127 {
128 char *bufptr, /* Pointer to position in buffer */
129 *bufend, /* Pointer to end of buffer */
130 sign, /* Sign of format width */
131 size, /* Size character (h, l, L) */
132 type; /* Format type character */
133 int width, /* Width of field */
134 prec; /* Number of characters of precision */
135 char tformat[100], /* Temporary format string for sprintf() */
136 *tptr, /* Pointer into temporary format */
137 temp[1024]; /* Buffer for formatted numbers */
138 char *s; /* Pointer to string */
139 int slen; /* Length of string */
140 int bytes; /* Total number of bytes needed */
143 /*
144 * Loop through the format string, formatting as needed...
145 */
147 bufptr = buffer;
148 bufend = buffer + bufsize - 1;
149 bytes = 0;
151 while (*format)
152 {
153 if (*format == '%')
154 {
155 tptr = tformat;
156 *tptr++ = *format++;
158 if (*format == '%')
159 {
160 if (bufptr && bufptr < bufend) *bufptr++ = *format;
161 bytes ++;
162 format ++;
163 continue;
164 }
165 else if (strchr(" -+#\'", *format))
166 {
167 *tptr++ = *format;
168 sign = *format++;
169 }
170 else
171 sign = 0;
173 if (*format == '*')
174 {
175 /*
176 * Get width from argument...
177 */
179 format ++;
180 width = va_arg(ap, int);
182 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
183 tptr += strlen(tptr);
184 }
185 else
186 {
187 width = 0;
189 while (isdigit(*format & 255))
190 {
191 if (tptr < (tformat + sizeof(tformat) - 1))
192 *tptr++ = *format;
194 width = width * 10 + *format++ - '0';
195 }
196 }
198 if (*format == '.')
199 {
200 if (tptr < (tformat + sizeof(tformat) - 1))
201 *tptr++ = *format;
203 format ++;
205 if (*format == '*')
206 {
207 /*
208 * Get precision from argument...
209 */
211 format ++;
212 prec = va_arg(ap, int);
214 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
215 tptr += strlen(tptr);
216 }
217 else
218 {
219 prec = 0;
221 while (isdigit(*format & 255))
222 {
223 if (tptr < (tformat + sizeof(tformat) - 1))
224 *tptr++ = *format;
226 prec = prec * 10 + *format++ - '0';
227 }
228 }
229 }
230 else
231 prec = -1;
233 if (*format == 'l' && format[1] == 'l')
234 {
235 size = 'L';
237 if (tptr < (tformat + sizeof(tformat) - 2))
238 {
239 *tptr++ = 'l';
240 *tptr++ = 'l';
241 }
243 format += 2;
244 }
245 else if (*format == 'h' || *format == 'l' || *format == 'L')
246 {
247 if (tptr < (tformat + sizeof(tformat) - 1))
248 *tptr++ = *format;
250 size = *format++;
251 }
253 if (!*format)
254 break;
256 if (tptr < (tformat + sizeof(tformat) - 1))
257 *tptr++ = *format;
259 type = *format++;
260 *tptr = '\0';
262 switch (type)
263 {
264 case 'E' : /* Floating point formats */
265 case 'G' :
266 case 'e' :
267 case 'f' :
268 case 'g' :
269 if ((width + 2) > sizeof(temp))
270 break;
272 sprintf(temp, tformat, va_arg(ap, double));
274 bytes += strlen(temp);
276 if (bufptr)
277 {
278 if ((bufptr + strlen(temp)) > bufend)
279 {
280 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
281 bufptr = bufend;
282 }
283 else
284 {
285 strcpy(bufptr, temp);
286 bufptr += strlen(temp);
287 }
288 }
289 break;
291 case 'B' : /* Integer formats */
292 case 'X' :
293 case 'b' :
294 case 'd' :
295 case 'i' :
296 case 'o' :
297 case 'u' :
298 case 'x' :
299 if ((width + 2) > sizeof(temp))
300 break;
302 #ifdef HAVE_LONG_LONG
303 if (size == 'L')
304 sprintf(temp, tformat, va_arg(ap, long long));
305 else
306 #endif /* HAVE_LONG_LONG */
307 sprintf(temp, tformat, va_arg(ap, int));
309 bytes += strlen(temp);
311 if (bufptr)
312 {
313 if ((bufptr + strlen(temp)) > bufend)
314 {
315 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
316 bufptr = bufend;
317 }
318 else
319 {
320 strcpy(bufptr, temp);
321 bufptr += strlen(temp);
322 }
323 }
324 break;
326 case 'p' : /* Pointer value */
327 if ((width + 2) > sizeof(temp))
328 break;
330 sprintf(temp, tformat, va_arg(ap, void *));
332 bytes += strlen(temp);
334 if (bufptr)
335 {
336 if ((bufptr + strlen(temp)) > bufend)
337 {
338 strncpy(bufptr, temp, (size_t)(bufend - bufptr));
339 bufptr = bufend;
340 }
341 else
342 {
343 strcpy(bufptr, temp);
344 bufptr += strlen(temp);
345 }
346 }
347 break;
349 case 'c' : /* Character or character array */
350 bytes += width;
352 if (bufptr)
353 {
354 if (width <= 1)
355 *bufptr++ = va_arg(ap, int);
356 else
357 {
358 if ((bufptr + width) > bufend)
359 width = bufend - bufptr;
361 memcpy(bufptr, va_arg(ap, char *), (size_t)width);
362 bufptr += width;
363 }
364 }
365 break;
367 case 's' : /* String */
368 if ((s = va_arg(ap, char *)) == NULL)
369 s = "(null)";
371 slen = strlen(s);
372 if (slen > width && prec != width)
373 width = slen;
375 bytes += width;
377 if (bufptr)
378 {
379 if ((bufptr + width) > bufend)
380 width = bufend - bufptr;
382 if (slen > width)
383 slen = width;
385 if (sign == '-')
386 {
387 strncpy(bufptr, s, (size_t)slen);
388 memset(bufptr + slen, ' ', (size_t)(width - slen));
389 }
390 else
391 {
392 memset(bufptr, ' ', (size_t)(width - slen));
393 strncpy(bufptr + width - slen, s, (size_t)slen);
394 }
396 bufptr += width;
397 }
398 break;
400 case 'n' : /* Output number of chars so far */
401 *(va_arg(ap, int *)) = bytes;
402 break;
403 }
404 }
405 else
406 {
407 bytes ++;
409 if (bufptr && bufptr < bufend)
410 *bufptr++ = *format;
412 format ++;
413 }
414 }
416 /*
417 * Nul-terminate the string and return the number of characters needed.
418 */
420 *bufptr = '\0';
422 return (bytes);
423 }
424 #endif /* !HAVE_VSNPRINTF */
427 /*
428 * '_mxml_vstrdupf()' - Format and duplicate a string.
429 */
431 char * /* O - New string pointer */
432 _mxml_vstrdupf(const char *format, /* I - Printf-style format string */
433 va_list ap) /* I - Pointer to additional arguments */
434 {
435 int bytes; /* Number of bytes required */
436 char *buffer, /* String buffer */
437 temp[256]; /* Small buffer for first vsnprintf */
438 va_list apcopy; /* Copy of argument list */
441 /*
442 * First format with a tiny buffer; this will tell us how many bytes are
443 * needed...
444 */
446 va_copy(apcopy, ap);
447 bytes = vsnprintf(temp, sizeof(temp), format, apcopy);
449 if (bytes < sizeof(temp))
450 {
451 /*
452 * Hey, the formatted string fits in the tiny buffer, so just dup that...
453 */
455 return (strdup(temp));
456 }
458 /*
459 * Allocate memory for the whole thing and reformat to the new, larger
460 * buffer...
461 */
463 if ((buffer = calloc(1, bytes + 1)) != NULL)
464 vsnprintf(buffer, bytes + 1, format, ap);
466 /*
467 * Return the new string...
468 */
470 return (buffer);
471 }
474 /*
475 * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $".
476 */