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