1 /* snprintf - compatibility implementation of snprintf, vsnprintf
2  *
3  * Copyright (c) 2013, NLnet Labs. All rights reserved.
4  *
5  * This software is open source.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * Redistributions of source code must retain the above copyright notice,
12  * this list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution.
17  *
18  * Neither the name of the NLNET LABS nor the names of its contributors may
19  * be used to endorse or promote products derived from this software without
20  * specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "config.h"
36 
37 #ifndef HAVE_VSNPRINTF
38 
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <errno.h>
45 #ifdef HAVE_STDINT_H
46 #include <stdint.h>
47 #endif
48 #include <limits.h>
49 
50 /* for test */
51 /* #define SNPRINTF_TEST 1 */
52 #ifdef SNPRINTF_TEST
53 #define snprintf my_snprintf
54 #define vsnprintf my_vsnprintf
55 #endif				/* SNPRINTF_TEST */
56 
57 int snprintf(char *str, size_t size, const char *format, ...);
58 int vsnprintf(char *str, size_t size, const char *format, va_list arg);
59 
60 /**
61  * Very portable snprintf implementation, limited in functionality,
62  * esp. for %[capital] %[nonportable] and so on.  Reduced float functionality,
63  * mostly in formatting and range (e+-16), for %f and %g.
64  *
65  * %s, %d, %u, %i, %x, %c, %n and %% are fully supported.
66  *   This includes width, precision, flags 0- +, and *(arg for wid,prec).
67  * %f, %g, %m, %p have reduced support, support for wid,prec,flags,*, but
68  *   less floating point range, no %e formatting for %g.
69  */
snprintf(char * str,size_t size,const char * format,...)70 int snprintf(char *str, size_t size, const char *format, ...)
71 {
72     int r;
73     va_list args;
74     va_start(args, format);
75     r = vsnprintf(str, size, format, args);
76     va_end(args);
77     return r;
78 }
79 
80 /** add padding to string */
print_pad(char ** at,size_t * left,int * ret,char p,int num)81 static void print_pad(char **at, size_t * left, int *ret, char p, int num)
82 {
83     while (num--) {
84 	if (*left > 1) {
85 	    *(*at)++ = p;
86 	    (*left)--;
87 	}
88 	(*ret)++;
89     }
90 }
91 
92 /** get negative symbol, 0 if none */
get_negsign(int negative,int plus,int space)93 static char get_negsign(int negative, int plus, int space)
94 {
95     if (negative)
96 	return '-';
97     if (plus)
98 	return '+';
99     if (space)
100 	return ' ';
101     return 0;
102 }
103 
104 #define PRINT_DEC_BUFSZ 32	/* 20 is enough for 64 bit decimals */
105 /** print decimal into buffer, returns length */
print_dec(char * buf,int max,unsigned int value)106 static int print_dec(char *buf, int max, unsigned int value)
107 {
108     int i = 0;
109     if (value == 0) {
110 	if (max > 0) {
111 	    buf[0] = '0';
112 	    i = 1;
113 	}
114     } else
115 	while (value && i < max) {
116 	    buf[i++] = '0' + value % 10;
117 	    value /= 10;
118 	}
119     return i;
120 }
121 
122 /** print long decimal into buffer, returns length */
print_dec_l(char * buf,int max,unsigned long value)123 static int print_dec_l(char *buf, int max, unsigned long value)
124 {
125     int i = 0;
126     if (value == 0) {
127 	if (max > 0) {
128 	    buf[0] = '0';
129 	    i = 1;
130 	}
131     } else
132 	while (value && i < max) {
133 	    buf[i++] = '0' + value % 10;
134 	    value /= 10;
135 	}
136     return i;
137 }
138 
139 /** print long decimal into buffer, returns length */
print_dec_ll(char * buf,int max,unsigned long long value)140 static int print_dec_ll(char *buf, int max, unsigned long long value)
141 {
142     int i = 0;
143     if (value == 0) {
144 	if (max > 0) {
145 	    buf[0] = '0';
146 	    i = 1;
147 	}
148     } else
149 	while (value && i < max) {
150 	    buf[i++] = '0' + value % 10;
151 	    value /= 10;
152 	}
153     return i;
154 }
155 
156 /** print hex into buffer, returns length */
print_hex(char * buf,int max,unsigned int value)157 static int print_hex(char *buf, int max, unsigned int value)
158 {
159     const char *h = "0123456789abcdef";
160     int i = 0;
161     if (value == 0) {
162 	if (max > 0) {
163 	    buf[0] = '0';
164 	    i = 1;
165 	}
166     } else
167 	while (value && i < max) {
168 	    buf[i++] = h[value & 0x0f];
169 	    value >>= 4;
170 	}
171     return i;
172 }
173 
174 /** print long hex into buffer, returns length */
print_hex_l(char * buf,int max,unsigned long value)175 static int print_hex_l(char *buf, int max, unsigned long value)
176 {
177     const char *h = "0123456789abcdef";
178     int i = 0;
179     if (value == 0) {
180 	if (max > 0) {
181 	    buf[0] = '0';
182 	    i = 1;
183 	}
184     } else
185 	while (value && i < max) {
186 	    buf[i++] = h[value & 0x0f];
187 	    value >>= 4;
188 	}
189     return i;
190 }
191 
192 /** print long long hex into buffer, returns length */
print_hex_ll(char * buf,int max,unsigned long long value)193 static int print_hex_ll(char *buf, int max, unsigned long long value)
194 {
195     const char *h = "0123456789abcdef";
196     int i = 0;
197     if (value == 0) {
198 	if (max > 0) {
199 	    buf[0] = '0';
200 	    i = 1;
201 	}
202     } else
203 	while (value && i < max) {
204 	    buf[i++] = h[value & 0x0f];
205 	    value >>= 4;
206 	}
207     return i;
208 }
209 
210 /** copy string into result, reversed */
211 static void
spool_str_rev(char ** at,size_t * left,int * ret,const char * buf,int len)212 spool_str_rev(char **at, size_t * left, int *ret, const char *buf, int len)
213 {
214     int i = len;
215     while (i) {
216 	if (*left > 1) {
217 	    *(*at)++ = buf[--i];
218 	    (*left)--;
219 	} else
220 	    --i;
221 	(*ret)++;
222     }
223 }
224 
225 /** copy string into result */
226 static void
spool_str(char ** at,size_t * left,int * ret,const char * buf,int len)227 spool_str(char **at, size_t * left, int *ret, const char *buf, int len)
228 {
229     int i;
230     for (i = 0; i < len; i++) {
231 	if (*left > 1) {
232 	    *(*at)++ = buf[i];
233 	    (*left)--;
234 	}
235 	(*ret)++;
236     }
237 }
238 
239 /** print number formatted */
240 static void
print_num(char ** at,size_t * left,int * ret,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space,int zero,int negative,char * buf,int len)241 print_num(char **at, size_t * left, int *ret, int minw, int precision,
242 	  int prgiven, int zeropad, int minus, int plus, int space,
243 	  int zero, int negative, char *buf, int len)
244 {
245     int w = len;		/* excludes minus sign */
246     char s = get_negsign(negative, plus, space);
247     if (minus) {
248 	/* left adjust the number into the field, space padding */
249 	/* calc numw = [sign][zeroes][number] */
250 	int numw = w;
251 	if (precision == 0 && zero)
252 	    numw = 0;
253 	if (numw < precision)
254 	    numw = precision;
255 	if (s)
256 	    numw++;
257 
258 	/* sign */
259 	if (s)
260 	    print_pad(at, left, ret, s, 1);
261 
262 	/* number */
263 	if (precision == 0 && zero) {
264 	    /* "" for the number */
265 	} else {
266 	    if (w < precision)
267 		print_pad(at, left, ret, '0', precision - w);
268 	    spool_str_rev(at, left, ret, buf, len);
269 	}
270 	/* spaces */
271 	if (numw < minw)
272 	    print_pad(at, left, ret, ' ', minw - numw);
273     } else {
274 	/* pad on the left of the number */
275 	/* calculate numw has width of [sign][zeroes][number] */
276 	int numw = w;
277 	if (precision == 0 && zero)
278 	    numw = 0;
279 	if (numw < precision)
280 	    numw = precision;
281 	if (!prgiven && zeropad && numw < minw)
282 	    numw = minw;
283 	else if (s)
284 	    numw++;
285 
286 	/* pad with spaces */
287 	if (numw < minw)
288 	    print_pad(at, left, ret, ' ', minw - numw);
289 	/* print sign (and one less zeropad if so) */
290 	if (s) {
291 	    print_pad(at, left, ret, s, 1);
292 	    numw--;
293 	}
294 	/* pad with zeroes */
295 	if (w < numw)
296 	    print_pad(at, left, ret, '0', numw - w);
297 	if (precision == 0 && zero)
298 	    return;
299 	/* print the characters for the value */
300 	spool_str_rev(at, left, ret, buf, len);
301     }
302 }
303 
304 /** print %d and %i */
305 static void
print_num_d(char ** at,size_t * left,int * ret,int value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)306 print_num_d(char **at, size_t * left, int *ret, int value,
307 	    int minw, int precision, int prgiven, int zeropad, int minus,
308 	    int plus, int space)
309 {
310     char buf[PRINT_DEC_BUFSZ];
311     int negative = (value < 0);
312     int zero = (value == 0);
313     int len = print_dec(buf, (int)sizeof(buf),
314 			(unsigned int)(negative ? -value : value));
315     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
316 	      plus, space, zero, negative, buf, len);
317 }
318 
319 /** print %ld and %li */
320 static void
print_num_ld(char ** at,size_t * left,int * ret,long value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)321 print_num_ld(char **at, size_t * left, int *ret, long value,
322 	     int minw, int precision, int prgiven, int zeropad, int minus,
323 	     int plus, int space)
324 {
325     char buf[PRINT_DEC_BUFSZ];
326     int negative = (value < 0);
327     int zero = (value == 0);
328     int len = print_dec_l(buf, (int)sizeof(buf),
329 			  (unsigned long)(negative ? -value : value));
330     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
331 	      plus, space, zero, negative, buf, len);
332 }
333 
334 /** print %lld and %lli */
335 static void
print_num_lld(char ** at,size_t * left,int * ret,long long value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)336 print_num_lld(char **at, size_t * left, int *ret, long long value,
337 	      int minw, int precision, int prgiven, int zeropad, int minus,
338 	      int plus, int space)
339 {
340     char buf[PRINT_DEC_BUFSZ];
341     int negative = (value < 0);
342     int zero = (value == 0);
343     int len = print_dec_ll(buf, (int)sizeof(buf),
344 			   (unsigned long long)(negative ? -value : value));
345     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
346 	      plus, space, zero, negative, buf, len);
347 }
348 
349 /** print %u */
350 static void
print_num_u(char ** at,size_t * left,int * ret,unsigned int value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)351 print_num_u(char **at, size_t * left, int *ret, unsigned int value,
352 	    int minw, int precision, int prgiven, int zeropad, int minus,
353 	    int plus, int space)
354 {
355     char buf[PRINT_DEC_BUFSZ];
356     int negative = 0;
357     int zero = (value == 0);
358     int len = print_dec(buf, (int)sizeof(buf), value);
359     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
360 	      plus, space, zero, negative, buf, len);
361 }
362 
363 /** print %lu */
364 static void
print_num_lu(char ** at,size_t * left,int * ret,unsigned long value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)365 print_num_lu(char **at, size_t * left, int *ret, unsigned long value,
366 	     int minw, int precision, int prgiven, int zeropad, int minus,
367 	     int plus, int space)
368 {
369     char buf[PRINT_DEC_BUFSZ];
370     int negative = 0;
371     int zero = (value == 0);
372     int len = print_dec_l(buf, (int)sizeof(buf), value);
373     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
374 	      plus, space, zero, negative, buf, len);
375 }
376 
377 /** print %llu */
378 static void
print_num_llu(char ** at,size_t * left,int * ret,unsigned long long value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)379 print_num_llu(char **at, size_t * left, int *ret, unsigned long long value,
380 	      int minw, int precision, int prgiven, int zeropad, int minus,
381 	      int plus, int space)
382 {
383     char buf[PRINT_DEC_BUFSZ];
384     int negative = 0;
385     int zero = (value == 0);
386     int len = print_dec_ll(buf, (int)sizeof(buf), value);
387     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
388 	      plus, space, zero, negative, buf, len);
389 }
390 
391 /** print %x */
392 static void
print_num_x(char ** at,size_t * left,int * ret,unsigned int value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)393 print_num_x(char **at, size_t * left, int *ret, unsigned int value,
394 	    int minw, int precision, int prgiven, int zeropad, int minus,
395 	    int plus, int space)
396 {
397     char buf[PRINT_DEC_BUFSZ];
398     int negative = 0;
399     int zero = (value == 0);
400     int len = print_hex(buf, (int)sizeof(buf), value);
401     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
402 	      plus, space, zero, negative, buf, len);
403 }
404 
405 /** print %lx */
406 static void
print_num_lx(char ** at,size_t * left,int * ret,unsigned long value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)407 print_num_lx(char **at, size_t * left, int *ret, unsigned long value,
408 	     int minw, int precision, int prgiven, int zeropad, int minus,
409 	     int plus, int space)
410 {
411     char buf[PRINT_DEC_BUFSZ];
412     int negative = 0;
413     int zero = (value == 0);
414     int len = print_hex_l(buf, (int)sizeof(buf), value);
415     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
416 	      plus, space, zero, negative, buf, len);
417 }
418 
419 /** print %llx */
420 static void
print_num_llx(char ** at,size_t * left,int * ret,unsigned long long value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)421 print_num_llx(char **at, size_t * left, int *ret, unsigned long long value,
422 	      int minw, int precision, int prgiven, int zeropad, int minus,
423 	      int plus, int space)
424 {
425     char buf[PRINT_DEC_BUFSZ];
426     int negative = 0;
427     int zero = (value == 0);
428     int len = print_hex_ll(buf, (int)sizeof(buf), value);
429     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
430 	      plus, space, zero, negative, buf, len);
431 }
432 
433 /** print %llp */
434 static void
print_num_llp(char ** at,size_t * left,int * ret,void * value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)435 print_num_llp(char **at, size_t * left, int *ret, void *value,
436 	      int minw, int precision, int prgiven, int zeropad, int minus,
437 	      int plus, int space)
438 {
439     char buf[PRINT_DEC_BUFSZ];
440     int negative = 0;
441     int zero = (value == 0);
442 #if defined(SIZE_MAX) && defined(UINT32_MAX) && (UINT32_MAX == SIZE_MAX || INT32_MAX == SIZE_MAX)
443     /* avoid warning about upcast on 32bit systems */
444     unsigned long long llvalue = (unsigned long)value;
445 #else
446     unsigned long long llvalue = (unsigned long long)value;
447 #endif
448     int len = print_hex_ll(buf, (int)sizeof(buf), llvalue);
449     if (zero) {
450 	buf[0] = ')';
451 	buf[1] = 'l';
452 	buf[2] = 'i';
453 	buf[3] = 'n';
454 	buf[4] = '(';
455 	len = 5;
456     } else {
457 	/* put '0x' in front of the (reversed) buffer result */
458 	if (len < PRINT_DEC_BUFSZ)
459 	    buf[len++] = 'x';
460 	if (len < PRINT_DEC_BUFSZ)
461 	    buf[len++] = '0';
462     }
463     print_num(at, left, ret, minw, precision, prgiven, zeropad, minus,
464 	      plus, space, zero, negative, buf, len);
465 }
466 
467 #define PRINT_FLOAT_BUFSZ 64	/* xx.yy with 20.20 about the max */
468 /** spool remainder after the decimal point to buffer, in reverse */
print_remainder(char * buf,int max,double r,int prec)469 static int print_remainder(char *buf, int max, double r, int prec)
470 {
471     unsigned long long cap = 1;
472     unsigned long long value;
473     int len, i;
474     if (prec > 19)
475 	prec = 19;		/* max we can do */
476     if (max < prec)
477 	return 0;
478     for (i = 0; i < prec; i++) {
479 	cap *= 10;
480     }
481     r *= (double)cap;
482     value = (unsigned long long)r;
483     /* see if we need to round up */
484     if (((unsigned long long)((r - (double)value) * 10.0)) >= 5) {
485 	value++;
486 	/* that might carry to numbers before the comma, if so,
487 	 * just ignore that rounding. failure because 64bitprintout */
488 	if (value >= cap)
489 	    value = cap - 1;
490     }
491     len = print_dec_ll(buf, max, value);
492     while (len < prec) {	/* pad with zeroes, e.g. if 0.0012 */
493 	buf[len++] = '0';
494     }
495     if (len < max)
496 	buf[len++] = '.';
497     return len;
498 }
499 
500 /** spool floating point to buffer */
print_float(char * buf,int max,double value,int prec)501 static int print_float(char *buf, int max, double value, int prec)
502 {
503     /* as xxx.xxx  if prec==0, no '.', with prec decimals after . */
504     /* no conversion for NAN and INF, because we do not want to require
505        linking with -lm. */
506     /* Thus, the conversions use 64bit integers to convert the numbers,
507      * which makes 19 digits before and after the decimal point the max */
508     unsigned long long whole = (unsigned long long)value;
509     double remain = value - (double)whole;
510     int len = 0;
511     if (prec != 0)
512 	len = print_remainder(buf, max, remain, prec);
513     len += print_dec_ll(buf + len, max - len, whole);
514     return len;
515 }
516 
517 /** print %f */
518 static void
print_num_f(char ** at,size_t * left,int * ret,double value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)519 print_num_f(char **at, size_t * left, int *ret, double value,
520 	    int minw, int precision, int prgiven, int zeropad, int minus,
521 	    int plus, int space)
522 {
523     char buf[PRINT_FLOAT_BUFSZ];
524     int negative = (value < 0);
525     int zero = 0;
526     int len;
527     if (!prgiven)
528 	precision = 6;
529     len = print_float(buf, (int)sizeof(buf), negative ? -value : value,
530 		      precision);
531     print_num(at, left, ret, minw, 1, 0, zeropad, minus,
532 	      plus, space, zero, negative, buf, len);
533 }
534 
535 /* rudimentary %g support */
print_float_g(char * buf,int max,double value,int prec)536 static int print_float_g(char *buf, int max, double value, int prec)
537 {
538     unsigned long long whole = (unsigned long long)value;
539     double remain = value - (double)whole;
540     int before = 0;
541     int len = 0;
542 
543     /* number of digits before the decimal point */
544     while (whole > 0) {
545 	before++;
546 	whole /= 10;
547     }
548     whole = (unsigned long long)value;
549 
550     if (prec > before && remain != 0.0) {
551 	/* see if the last decimals are zero, if so, skip them */
552 	len = print_remainder(buf, max, remain, prec - before);
553 	while (len > 0 && buf[0] == '0') {
554 	    memmove(buf, buf + 1, --len);
555 	}
556     }
557     len += print_dec_ll(buf + len, max - len, whole);
558     return len;
559 }
560 
561 /** print %g */
562 static void
print_num_g(char ** at,size_t * left,int * ret,double value,int minw,int precision,int prgiven,int zeropad,int minus,int plus,int space)563 print_num_g(char **at, size_t * left, int *ret, double value,
564 	    int minw, int precision, int prgiven, int zeropad, int minus,
565 	    int plus, int space)
566 {
567     char buf[PRINT_FLOAT_BUFSZ];
568     int negative = (value < 0);
569     int zero = 0;
570     int len;
571     if (!prgiven)
572 	precision = 6;
573     if (precision == 0)
574 	precision = 1;
575     len = print_float_g(buf, (int)sizeof(buf), negative ? -value : value,
576 			precision);
577     print_num(at, left, ret, minw, 1, 0, zeropad, minus,
578 	      plus, space, zero, negative, buf, len);
579 }
580 
581 /** strnlen (compat implementation) */
my_strnlen(const char * s,int max)582 static int my_strnlen(const char *s, int max)
583 {
584     int i;
585     for (i = 0; i < max; i++)
586 	if (s[i] == 0)
587 	    return i;
588     return max;
589 }
590 
591 /** print %s */
592 static void
print_str(char ** at,size_t * left,int * ret,char * s,int minw,int precision,int prgiven,int minus)593 print_str(char **at, size_t * left, int *ret, char *s,
594 	  int minw, int precision, int prgiven, int minus)
595 {
596     int w;
597     /* with prec: no more than x characters from this string, stop at 0 */
598     if (prgiven)
599 	w = my_strnlen(s, precision);
600     else
601 	w = (int)strlen(s);	/* up to the nul */
602     if (w < minw && !minus)
603 	print_pad(at, left, ret, ' ', minw - w);
604     spool_str(at, left, ret, s, w);
605     if (w < minw && minus)
606 	print_pad(at, left, ret, ' ', minw - w);
607 }
608 
609 /** print %c */
610 static void
print_char(char ** at,size_t * left,int * ret,int c,int minw,int minus)611 print_char(char **at, size_t * left, int *ret, int c, int minw, int minus)
612 {
613     if (1 < minw && !minus)
614 	print_pad(at, left, ret, ' ', minw - 1);
615     print_pad(at, left, ret, c, 1);
616     if (1 < minw && minus)
617 	print_pad(at, left, ret, ' ', minw - 1);
618 }
619 
620 /**
621  * Print to string.
622  * str: string buffer for result. result will be null terminated.
623  * size: size of the buffer. null is put inside buffer.
624  * format: printf format string.
625  * arg: '...' arguments to print.
626  * returns number of characters. a null is printed after this.
627  * return number of bytes that would have been written
628  *	   if the buffer had been large enough.
629  *
630  * supported format specifiers:
631  * 	%s, %u, %d, %x, %i, %f, %g, %c, %p, %n.
632  * 	length: l, ll (for d, u, x).
633  * 	precision: 6.6d (for d, u, x)
634  * 		%f, %g precisions, 0.3f
635  * 		%20s, '.*s'
636  * 	and %%.
637  */
vsnprintf(char * str,size_t size,const char * format,va_list arg)638 int vsnprintf(char *str, size_t size, const char *format, va_list arg)
639 {
640     char *at = str;
641     size_t left = size;
642     int ret = 0;
643     const char *fmt = format;
644     int conv, minw, precision, prgiven, zeropad, minus, plus, space, length;
645     while (*fmt) {
646 	/* copy string before % */
647 	while (*fmt && *fmt != '%') {
648 	    if (left > 1) {
649 		*at++ = *fmt++;
650 		left--;
651 	    } else
652 		fmt++;
653 	    ret++;
654 	}
655 
656 	/* see if we are at end */
657 	if (!*fmt)
658 	    break;
659 
660 	/* fetch next argument % designation from format string */
661 	fmt++;			/* skip the '%' */
662 
663 		/********************************/
664 	/* get the argument designation */
665 		/********************************/
666 	/* we must do this vararg stuff inside this function for
667 	 * portability.  Hence, get_designation, and print_designation
668 	 * are not their own functions. */
669 
670 	/* printout designation:
671 	 * conversion specifier: x, d, u, s, c, n, m, p
672 	 * flags: # not supported
673 	 *        0 zeropad (on the left)
674 	 *        - left adjust (right by default)
675 	 *        ' ' printspace for positive number (in - position).
676 	 *        + alwayssign
677 	 * fieldwidth: [1-9][0-9]* minimum field width.
678 	 *      if this is * then type int next argument specifies the minwidth.
679 	 *      if this is negative, the - flag is set (with positive width).
680 	 * precision: period[digits]*, %.2x.
681 	 *      if this is * then type int next argument specifies the precision.
682 	 *      just '.' or negative value means precision=0.
683 	 *              this is mindigits to print for d, i, u, x
684 	 *              this is aftercomma digits for f
685 	 *              this is max number significant digits for g
686 	 *              maxnumber characters to be printed for s
687 	 * length: 0-none (int), 1-l (long), 2-ll (long long)
688 	 *      notsupported: hh (char), h (short), L (long double), q, j, z, t
689 	 * Does not support %m$ and *m$ argument designation as array indices.
690 	 * Does not support %#x
691 	 *
692 	 */
693 	minw = 0;
694 	precision = 1;
695 	prgiven = 0;
696 	zeropad = 0;
697 	minus = 0;
698 	plus = 0;
699 	space = 0;
700 	length = 0;
701 
702 	/* get flags in any order */
703 	for (;;) {
704 	    if (*fmt == '0')
705 		zeropad = 1;
706 	    else if (*fmt == '-')
707 		minus = 1;
708 	    else if (*fmt == '+')
709 		plus = 1;
710 	    else if (*fmt == ' ')
711 		space = 1;
712 	    else
713 		break;
714 	    fmt++;
715 	}
716 
717 	/* field width */
718 	if (*fmt == '*') {
719 	    fmt++;		/* skip char */
720 	    minw = va_arg(arg, int);
721 	    if (minw < 0) {
722 		minus = 1;
723 		minw = -minw;
724 	    }
725 	} else
726 	    while (*fmt >= '0' && *fmt <= '9') {
727 		minw = minw * 10 + (*fmt++) - '0';
728 	    }
729 
730 	/* precision */
731 	if (*fmt == '.') {
732 	    fmt++;		/* skip period */
733 	    prgiven = 1;
734 	    precision = 0;
735 	    if (*fmt == '*') {
736 		fmt++;		/* skip char */
737 		precision = va_arg(arg, int);
738 		if (precision < 0)
739 		    precision = 0;
740 	    } else
741 		while (*fmt >= '0' && *fmt <= '9') {
742 		    precision = precision * 10 + (*fmt++) - '0';
743 		}
744 	}
745 
746 	/* length */
747 	if (*fmt == 'l') {
748 	    fmt++;		/* skip char */
749 	    length = 1;
750 	    if (*fmt == 'l') {
751 		fmt++;		/* skip char */
752 		length = 2;
753 	    }
754 	}
755 
756 	/* get the conversion */
757 	if (!*fmt)
758 	    conv = 0;
759 	else
760 	    conv = *fmt++;
761 
762 		/***********************************/
763 	/* print that argument designation */
764 		/***********************************/
765 	switch (conv) {
766 	case 'i':
767 	case 'd':
768 	    if (length == 0)
769 		print_num_d(&at, &left, &ret, va_arg(arg, int),
770 			    minw, precision, prgiven, zeropad, minus, plus,
771 			    space);
772 	    else if (length == 1)
773 		print_num_ld(&at, &left, &ret, va_arg(arg, long),
774 			     minw, precision, prgiven, zeropad, minus, plus,
775 			     space);
776 	    else if (length == 2)
777 		print_num_lld(&at, &left, &ret,
778 			      va_arg(arg, long long),
779 			      minw, precision, prgiven, zeropad, minus, plus,
780 			      space);
781 	    break;
782 	case 'u':
783 	    if (length == 0)
784 		print_num_u(&at, &left, &ret,
785 			    va_arg(arg, unsigned int),
786 			    minw, precision, prgiven, zeropad, minus, plus,
787 			    space);
788 	    else if (length == 1)
789 		print_num_lu(&at, &left, &ret,
790 			     va_arg(arg, unsigned long),
791 			     minw, precision, prgiven, zeropad, minus, plus,
792 			     space);
793 	    else if (length == 2)
794 		print_num_llu(&at, &left, &ret,
795 			      va_arg(arg, unsigned long long),
796 			      minw, precision, prgiven, zeropad, minus, plus,
797 			      space);
798 	    break;
799 	case 'x':
800 	    if (length == 0)
801 		print_num_x(&at, &left, &ret,
802 			    va_arg(arg, unsigned int),
803 			    minw, precision, prgiven, zeropad, minus, plus,
804 			    space);
805 	    else if (length == 1)
806 		print_num_lx(&at, &left, &ret,
807 			     va_arg(arg, unsigned long),
808 			     minw, precision, prgiven, zeropad, minus, plus,
809 			     space);
810 	    else if (length == 2)
811 		print_num_llx(&at, &left, &ret,
812 			      va_arg(arg, unsigned long long),
813 			      minw, precision, prgiven, zeropad, minus, plus,
814 			      space);
815 	    break;
816 	case 's':
817 	    print_str(&at, &left, &ret, va_arg(arg, char *),
818 		      minw, precision, prgiven, minus);
819 	    break;
820 	case 'c':
821 	    print_char(&at, &left, &ret, va_arg(arg, int), minw, minus);
822 	    break;
823 	case 'n':
824 	    *va_arg(arg, int *) = ret;
825 	    break;
826 	case 'm':
827 	    print_str(&at, &left, &ret, strerror(errno),
828 		      minw, precision, prgiven, minus);
829 	    break;
830 	case 'p':
831 	    print_num_llp(&at, &left, &ret, va_arg(arg, void *),
832 			  minw, precision, prgiven, zeropad, minus, plus,
833 			  space);
834 	    break;
835 	case '%':
836 	    print_pad(&at, &left, &ret, '%', 1);
837 	    break;
838 	case 'f':
839 	    print_num_f(&at, &left, &ret, va_arg(arg, double),
840 			minw, precision, prgiven, zeropad, minus, plus, space);
841 	    break;
842 	case 'g':
843 	    print_num_g(&at, &left, &ret, va_arg(arg, double),
844 			minw, precision, prgiven, zeropad, minus, plus, space);
845 	    break;
846 	    /* unknown */
847 	default:
848 	case 0:
849 	    break;
850 	}
851     }
852 
853     /* zero terminate */
854     if (left > 0)
855 	*at = 0;
856     return ret;
857 }
858 
859 #endif				/* !HAVE_VSNPRINTF */
860 
861 #ifdef SNPRINTF_TEST
862 
863 /** do tests */
864 #undef snprintf
865 #define DOTEST(bufsz, result, retval, ...) do { \
866 	char buf[bufsz]; \
867 	printf("now test %s\n", #__VA_ARGS__); \
868 	int r=my_snprintf(buf, sizeof(buf), __VA_ARGS__); \
869 	if(r != retval || strcmp(buf, result) != 0) { \
870 		printf("error test(%s) was \"%s\":%d\n", \
871 			""#bufsz", "#result", "#retval", "#__VA_ARGS__, \
872 			buf, r); \
873 		exit(1); \
874 		} \
875 	r=snprintf(buf, sizeof(buf), __VA_ARGS__); \
876 	if(r != retval || strcmp(buf, result) != 0) { \
877 		printf("error test(%s) differs with system, \"%s\":%d\n", \
878 			""#bufsz", "#result", "#retval", "#__VA_ARGS__, \
879 			buf, r); \
880 		exit(1); \
881 		} \
882 	printf("test(\"%s\":%d) passed\n", buf, r); \
883 	} while(0);
884 
885 /** test program */
main(void)886 int main(void)
887 {
888     int x = 0;
889 
890     /* bufsize, expectedstring, expectedretval, snprintf arguments */
891     DOTEST(1024, "hello", 5, "hello");
892     DOTEST(1024, "h", 1, "h");
893     /* warning from gcc for format string, but it does work
894      * DOTEST(1024, "", 0, ""); */
895 
896     DOTEST(3, "he", 5, "hello");
897     DOTEST(1, "", 7, "%d", 7823089);
898 
899     /* test positive numbers */
900     DOTEST(1024, "0", 1, "%d", 0);
901     DOTEST(1024, "1", 1, "%d", 1);
902     DOTEST(1024, "9", 1, "%d", 9);
903     DOTEST(1024, "15", 2, "%d", 15);
904     DOTEST(1024, "ab15cd", 6, "ab%dcd", 15);
905     DOTEST(1024, "167", 3, "%d", 167);
906     DOTEST(1024, "7823089", 7, "%d", 7823089);
907     DOTEST(1024, " 12", 3, "%3d", 12);
908     DOTEST(1024, "012", 3, "%.3d", 12);
909     DOTEST(1024, "012", 3, "%3.3d", 12);
910     DOTEST(1024, "012", 3, "%03d", 12);
911     DOTEST(1024, " 012", 4, "%4.3d", 12);
912     DOTEST(1024, "", 0, "%.0d", 0);
913 
914     /* test negative numbers */
915     DOTEST(1024, "-1", 2, "%d", -1);
916     DOTEST(1024, "-12", 3, "%3d", -12);
917     DOTEST(1024, " -2", 3, "%3d", -2);
918     DOTEST(1024, "-012", 4, "%.3d", -12);
919     DOTEST(1024, "-012", 4, "%3.3d", -12);
920     DOTEST(1024, "-012", 4, "%4.3d", -12);
921     DOTEST(1024, " -012", 5, "%5.3d", -12);
922     DOTEST(1024, "-12", 3, "%03d", -12);
923     DOTEST(1024, "-02", 3, "%03d", -2);
924     DOTEST(1024, "-15", 3, "%d", -15);
925     DOTEST(1024, "-7307", 5, "%d", -7307);
926     DOTEST(1024, "-12  ", 5, "%-5d", -12);
927     DOTEST(1024, "-00012", 6, "%-.5d", -12);
928 
929     /* test + and space flags */
930     DOTEST(1024, "+12", 3, "%+d", 12);
931     DOTEST(1024, " 12", 3, "% d", 12);
932 
933     /* test %u */
934     DOTEST(1024, "12", 2, "%u", 12);
935     DOTEST(1024, "0", 1, "%u", 0);
936     DOTEST(1024, "4294967295", 10, "%u", 0xffffffff);
937 
938     /* test %x */
939     DOTEST(1024, "0", 1, "%x", 0);
940     DOTEST(1024, "c", 1, "%x", 12);
941     DOTEST(1024, "12ab34cd", 8, "%x", 0x12ab34cd);
942 
943     /* test %llu, %lld */
944     DOTEST(1024, "18446744073709551615", 20, "%llu",
945 	   (long long)0xffffffffffffffff);
946     DOTEST(1024, "-9223372036854775808", 20, "%lld",
947 	   (long long)0x8000000000000000);
948     DOTEST(1024, "9223372036854775808", 19, "%llu",
949 	   (long long)0x8000000000000000);
950 
951     /* test %s */
952     DOTEST(1024, "hello", 5, "%s", "hello");
953     DOTEST(1024, "     hello", 10, "%10s", "hello");
954     DOTEST(1024, "hello     ", 10, "%-10s", "hello");
955     DOTEST(1024, "he", 2, "%.2s", "hello");
956     DOTEST(1024, "  he", 4, "%4.2s", "hello");
957     DOTEST(1024, "   h", 4, "%4.2s", "h");
958 
959     /* test %c */
960     DOTEST(1024, "a", 1, "%c", 'a');
961     /* warning from gcc for format string, but it does work
962        DOTEST(1024, "    a", 5, "%5c", 'a');
963        DOTEST(1024, "a", 1, "%.0c", 'a'); */
964 
965     /* test %n */
966     DOTEST(1024, "hello", 5, "hello%n", &x);
967     if (x != 5) {
968 	printf("the %%n failed\n");
969 	exit(1);
970     }
971 
972     /* test %m */
973     errno = 0;
974     DOTEST(1024, "Success", 7, "%m");
975 
976     /* test %p */
977     DOTEST(1024, "0x10", 4, "%p", (void *)0x10);
978     DOTEST(1024, "(nil)", 5, "%p", (void *)0x0);
979 
980     /* test %% */
981     DOTEST(1024, "%", 1, "%%");
982 
983     /* test %f */
984     DOTEST(1024, "0.000000", 8, "%f", 0.0);
985     DOTEST(1024, "0.00", 4, "%.2f", 0.0);
986     /* differs, "-0.00" DOTEST(1024, "0.00", 4, "%.2f", -0.0); */
987     DOTEST(1024, "234.00", 6, "%.2f", 234.005);
988     DOTEST(1024, "8973497.1246", 12, "%.4f", 8973497.12456);
989     DOTEST(1024, "-12.000000", 10, "%f", -12.0);
990     DOTEST(1024, "6", 1, "%.0f", 6.0);
991 
992     DOTEST(1024, "6", 1, "%g", 6.0);
993     DOTEST(1024, "6.1", 3, "%g", 6.1);
994     DOTEST(1024, "6.15", 4, "%g", 6.15);
995 
996     /* These format strings are from the code of NSD, Unbound, ldns */
997 
998     DOTEST(1024, "abcdef", 6, "%s", "abcdef");
999     DOTEST(1024, "005", 3, "%03u", 5);
1000     DOTEST(1024, "12345", 5, "%03u", 12345);
1001     DOTEST(1024, "5", 1, "%d", 5);
1002     DOTEST(1024, "(nil)", 5, "%p", NULL);
1003     DOTEST(1024, "12345", 5, "%ld", (long)12345);
1004     DOTEST(1024, "12345", 5, "%lu", (long)12345);
1005     DOTEST(1024, "       12345", 12, "%12u", (unsigned)12345);
1006     DOTEST(1024, "12345", 5, "%u", (unsigned)12345);
1007     DOTEST(1024, "12345", 5, "%llu", (unsigned long long)12345);
1008     DOTEST(1024, "12345", 5, "%x", 0x12345);
1009     DOTEST(1024, "12345", 5, "%llx", (long long)0x12345);
1010     DOTEST(1024, "012345", 6, "%6.6d", 12345);
1011     DOTEST(1024, "012345", 6, "%6.6u", 12345);
1012     DOTEST(1024, "1234.54", 7, "%g", 1234.54);
1013     DOTEST(1024, "123456789.54", 12, "%.12g", 123456789.54);
1014     DOTEST(1024, "3456789123456.54", 16, "%.16g", 3456789123456.54);
1015     /* %24g does not work with 24 digits, not enough accuracy,
1016      * the first 16 digits are correct */
1017     DOTEST(1024, "12345", 5, "%3.3d", 12345);
1018     DOTEST(1024, "000", 3, "%3.3d", 0);
1019     DOTEST(1024, "001", 3, "%3.3d", 1);
1020     DOTEST(1024, "012", 3, "%3.3d", 12);
1021     DOTEST(1024, "-012", 4, "%3.3d", -12);
1022     DOTEST(1024, "he", 2, "%.2s", "hello");
1023     DOTEST(1024, "helloworld", 10, "%s%s", "hello", "world");
1024     DOTEST(1024, "he", 2, "%.*s", 2, "hello");
1025     DOTEST(1024, "  hello", 7, "%*s", 7, "hello");
1026     DOTEST(1024, "hello  ", 7, "%*s", -7, "hello");
1027     DOTEST(1024, "0", 1, "%c", '0');
1028     DOTEST(1024, "A", 1, "%c", 'A');
1029     DOTEST(1024, "", 1, "%c", 0);
1030     DOTEST(1024, "\010", 1, "%c", 8);
1031     DOTEST(1024, "%", 1, "%%");
1032     DOTEST(1024, "0a", 2, "%02x", 0x0a);
1033     DOTEST(1024, "bd", 2, "%02x", 0xbd);
1034     DOTEST(1024, "12", 2, "%02ld", (long)12);
1035     DOTEST(1024, "02", 2, "%02ld", (long)2);
1036     DOTEST(1024, "02", 2, "%02u", (unsigned)2);
1037     DOTEST(1024, "765432", 6, "%05u", (unsigned)765432);
1038     DOTEST(1024, "10.234", 6, "%0.3f", 10.23421);
1039     DOTEST(1024, "123456.234", 10, "%0.3f", 123456.23421);
1040     DOTEST(1024, "123456789.234", 13, "%0.3f", 123456789.23421);
1041     DOTEST(1024, "123456.23", 9, "%.2f", 123456.23421);
1042     DOTEST(1024, "123456", 6, "%.0f", 123456.23421);
1043     DOTEST(1024, "0123", 4, "%.4x", 0x0123);
1044     DOTEST(1024, "00000123", 8, "%.8x", 0x0123);
1045     DOTEST(1024, "ffeb0cde", 8, "%.8x", 0xffeb0cde);
1046     DOTEST(1024, " 987654321", 10, "%10lu", (unsigned long)987654321);
1047     DOTEST(1024, "   987654321", 12, "%12lu", (unsigned long)987654321);
1048     DOTEST(1024, "987654321", 9, "%i", 987654321);
1049     DOTEST(1024, "-87654321", 9, "%i", -87654321);
1050     DOTEST(1024, "hello           ", 16, "%-16s", "hello");
1051     DOTEST(1024, "                ", 16, "%-16s", "");
1052     DOTEST(1024, "a               ", 16, "%-16s", "a");
1053     DOTEST(1024, "foobarfoobar    ", 16, "%-16s", "foobarfoobar");
1054     DOTEST(1024, "foobarfoobarfoobar", 18, "%-16s", "foobarfoobarfoobar");
1055 
1056     /* combined expressions */
1057     DOTEST(1024, "foo 1.0 size 512 edns", 21,
1058 	   "foo %s size %d %s%s", "1.0", 512, "", "edns");
1059     DOTEST(15, "foo 1.0 size 5", 21,
1060 	   "foo %s size %d %s%s", "1.0", 512, "", "edns");
1061     DOTEST(1024, "packet 1203ceff id", 18,
1062 	   "packet %2.2x%2.2x%2.2x%2.2x id", 0x12, 0x03, 0xce, 0xff);
1063     DOTEST(1024, "/tmp/testbound_123abcd.tmp", 26, "/tmp/testbound_%u%s%s.tmp",
1064 	   123, "ab", "cd");
1065 
1066     return 0;
1067 }
1068 #endif				/* SNPRINTF_TEST */
1069