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