1 /*
2  * NOTE: If you change this file, please merge it into rsync, samba, etc.
3  */
4 
5 /*
6  * Copyright Patrick Powell 1995
7  * This code is based on code written by Patrick Powell (papowell@astart.com)
8  * It may be used for any purpose as long as this notice remains intact
9  * on all source code distributions
10  */
11 
12 /**************************************************************
13  * Original:
14  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
15  * A bombproof version of doprnt (dopr) included.
16  * Sigh.  This sort of thing is always nasty do deal with.  Note that
17  * the version here does not include floating point...
18  *
19  * snprintf() is used instead of sprintf() as it does limit checks
20  * for string length.  This covers a nasty loophole.
21  *
22  * The other functions are there to prevent NULL pointers from
23  * causing nast effects.
24  *
25  * More Recently:
26  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
27  *  This was ugly.  It is still ugly.  I opted out of floating point
28  *  numbers, but the formatter understands just about everything
29  *  from the normal C string format, at least as far as I can tell from
30  *  the Solaris 2.5 printf(3S) man page.
31  *
32  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
33  *    Ok, added some minimal floating point support, which means this
34  *    probably requires libm on most operating systems.  Don't yet
35  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
36  *    was pretty badly broken, it just wasn't being exercised in ways
37  *    which showed it, so that's been fixed.  Also, formatted the code
38  *    to mutt conventions, and removed dead code left over from the
39  *    original.  Also, there is now a builtin-test, just compile with:
40  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
41  *    and run snprintf for results.
42  *
43  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
44  *    The PGP code was using unsigned hexadecimal formats.
45  *    Unfortunately, unsigned formats simply didn't work.
46  *
47  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
48  *    The original code assumed that both snprintf() and vsnprintf() were
49  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
50  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
51  *
52  *  Andrew Tridgell (tridge@samba.org) Oct 1998
53  *    fixed handling of %.0f
54  *    added test for HAVE_LONG_DOUBLE
55  *
56  * tridge@samba.org, idra@samba.org, April 2001
57  *    got rid of fcvt code (twas buggy and made testing harder)
58  *    added C99 semantics
59  *
60  * date: 2002/12/19 19:56:31;  author: herb;  state: Exp;  lines: +2 -0
61  * actually print args for %g and %e
62  *
63  * date: 2002/06/03 13:37:52;  author: jmcd;  state: Exp;  lines: +8 -0
64  * Since includes.h isn't included here, VA_COPY has to be defined here.  I don't
65  * see any include file that is guaranteed to be here, so I'm defining it
66  * locally.  Fixes AIX and Solaris builds.
67  *
68  * date: 2002/06/03 03:07:24;  author: tridge;  state: Exp;  lines: +5 -13
69  * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
70  * functions
71  *
72  * date: 2002/05/17 14:51:22;  author: jmcd;  state: Exp;  lines: +21 -4
73  * Fix usage of va_list passed as an arg.  Use __va_copy before using it
74  * when it exists.
75  *
76  * date: 2002/04/16 22:38:04;  author: idra;  state: Exp;  lines: +20 -14
77  * Fix incorrect zpadlen handling in fmtfp.
78  * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
79  * few mods to make it easier to compile the tests.
80  * addedd the "Ollie" test to the floating point ones.
81  *
82  * Martin Pool (mbp@samba.org) April 2003
83  *    Remove NO_CONFIG_H so that the test case can be built within a source
84  *    tree with less trouble.
85  *    Remove unnecessary SAFE_FREE() definition.
86  *
87  * Martin Pool (mbp@samba.org) May 2003
88  *    Put in a prototype for dummy_snprintf() to quiet compiler warnings.
89  *
90  *    Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
91  *    if the C library has some snprintf functions already.
92  *
93  * Darren Tucker (dtucker@zip.com.au) 2005
94  *    Fix bug allowing read overruns of the source string with "%.*s"
95  *    Usually harmless unless the read runs outside the process' allocation
96  *    (eg if your malloc does guard pages) in which case it will segfault.
97  *    From OpenSSH.  Also added test for same.
98  *
99  * Simo Sorce (idra@samba.org) Jan 2006
100  *
101  *    Add support for position independent parameters
102  *    fix fmtstr now it conforms to sprintf wrt min.max
103  *
104  **************************************************************/
105 
106 #include "replace.h"
107 #include "system/locale.h"
108 
109 #ifdef TEST_SNPRINTF /* need math library headers for testing */
110 
111 /* In test mode, we pretend that this system doesn't have any snprintf
112  * functions, regardless of what config.h says. */
113 #  undef HAVE_SNPRINTF
114 #  undef HAVE_VSNPRINTF
115 #  undef HAVE_C99_VSNPRINTF
116 #  undef HAVE_ASPRINTF
117 #  undef HAVE_VASPRINTF
118 #  include <math.h>
119 #endif /* TEST_SNPRINTF */
120 
121 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
122 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
123 #include <stdio.h>
124  /* make the compiler happy with an empty file */
125  void dummy_snprintf(void);
dummy_snprintf(void)126  void dummy_snprintf(void) {}
127 #endif /* HAVE_SNPRINTF, etc */
128 
129 /* yes this really must be a ||. Don't muck with this (tridge) */
130 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
131 
132 #ifdef HAVE_LONG_DOUBLE
133 #define LDOUBLE long double
134 #else
135 #define LDOUBLE double
136 #endif
137 
138 #ifdef HAVE_LONG_LONG
139 #define LLONG long long
140 #else
141 #define LLONG long
142 #endif
143 
144 #ifndef VA_COPY
145 #ifdef HAVE_VA_COPY
146 #define VA_COPY(dest, src) va_copy(dest, src)
147 #else
148 #ifdef HAVE___VA_COPY
149 #define VA_COPY(dest, src) __va_copy(dest, src)
150 #else
151 #define VA_COPY(dest, src) (dest) = (src)
152 #endif
153 #endif
154 
155 /*
156  * dopr(): poor man's version of doprintf
157  */
158 
159 /* format read states */
160 #define DP_S_DEFAULT 0
161 #define DP_S_FLAGS   1
162 #define DP_S_MIN     2
163 #define DP_S_DOT     3
164 #define DP_S_MAX     4
165 #define DP_S_MOD     5
166 #define DP_S_CONV    6
167 #define DP_S_DONE    7
168 
169 /* format flags - Bits */
170 #define DP_F_MINUS	(1 << 0)
171 #define DP_F_PLUS	(1 << 1)
172 #define DP_F_SPACE	(1 << 2)
173 #define DP_F_NUM	(1 << 3)
174 #define DP_F_ZERO	(1 << 4)
175 #define DP_F_UP		(1 << 5)
176 #define DP_F_UNSIGNED	(1 << 6)
177 
178 /* Conversion Flags */
179 #define DP_C_CHAR    1
180 #define DP_C_SHORT   2
181 #define DP_C_LONG    3
182 #define DP_C_LDOUBLE 4
183 #define DP_C_LLONG   5
184 #define DP_C_SIZET   6
185 
186 /* Chunk types */
187 #define CNK_FMT_STR 0
188 #define CNK_INT     1
189 #define CNK_OCTAL   2
190 #define CNK_UINT    3
191 #define CNK_HEX     4
192 #define CNK_FLOAT   5
193 #define CNK_CHAR    6
194 #define CNK_STRING  7
195 #define CNK_PTR     8
196 #define CNK_NUM     9
197 #define CNK_PRCNT   10
198 
199 #define char_to_int(p) ((p)- '0')
200 #ifndef MAX
201 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
202 #endif
203 
204 struct pr_chunk {
205 	int type; /* chunk type */
206 	int num; /* parameter number */
207 	int min;
208 	int max;
209 	int flags;
210 	int cflags;
211 	int start;
212 	int len;
213 	LLONG value;
214 	LDOUBLE fvalue;
215 	char *strvalue;
216 	void *pnum;
217 	struct pr_chunk *min_star;
218 	struct pr_chunk *max_star;
219 	struct pr_chunk *next;
220 };
221 
222 struct pr_chunk_x {
223 	struct pr_chunk **chunks;
224 	int num;
225 };
226 
227 static int dopr(char *buffer, size_t maxlen, const char *format,
228 		   va_list args_in);
229 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
230 		    char *value, int flags, int min, int max);
231 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
232 		    LLONG value, int base, int min, int max, int flags);
233 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
234 		   LDOUBLE fvalue, int min, int max, int flags);
235 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
236 static struct pr_chunk *new_chunk(void);
237 static int add_cnk_list_entry(struct pr_chunk_x **list,
238 				int max_num, struct pr_chunk *chunk);
239 
dopr(char * buffer,size_t maxlen,const char * format,va_list args_in)240 static int dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
241 {
242 	char ch;
243 	int state;
244 	int pflag;
245 	int pnum;
246 	int pfirst;
247 	size_t currlen;
248 	va_list args;
249 	const char *base;
250 	struct pr_chunk *chunks = NULL;
251 	struct pr_chunk *cnk = NULL;
252 	struct pr_chunk_x *clist = NULL;
253 	int max_pos;
254 	int ret = -1;
255 
256 	VA_COPY(args, args_in);
257 
258 	state = DP_S_DEFAULT;
259 	pfirst = 1;
260 	pflag = 0;
261 	pnum = 0;
262 
263 	max_pos = 0;
264 	base = format;
265 	ch = *format++;
266 
267 	/* retrieve the string structure as chunks */
268 	while (state != DP_S_DONE) {
269 		if (ch == '\0')
270 			state = DP_S_DONE;
271 
272 		switch(state) {
273 		case DP_S_DEFAULT:
274 
275 			if (cnk) {
276 				cnk->next = new_chunk();
277 				cnk = cnk->next;
278 			} else {
279 				cnk = new_chunk();
280 			}
281 			if (!cnk) goto done;
282 			if (!chunks) chunks = cnk;
283 
284 			if (ch == '%') {
285 				state = DP_S_FLAGS;
286 				ch = *format++;
287 			} else {
288 				cnk->type = CNK_FMT_STR;
289 				cnk->start = format - base -1;
290 				while ((ch != '\0') && (ch != '%')) ch = *format++;
291 				cnk->len = format - base - cnk->start -1;
292 			}
293 			break;
294 		case DP_S_FLAGS:
295 			switch (ch) {
296 			case '-':
297 				cnk->flags |= DP_F_MINUS;
298 				ch = *format++;
299 				break;
300 			case '+':
301 				cnk->flags |= DP_F_PLUS;
302 				ch = *format++;
303 				break;
304 			case ' ':
305 				cnk->flags |= DP_F_SPACE;
306 				ch = *format++;
307 				break;
308 			case '#':
309 				cnk->flags |= DP_F_NUM;
310 				ch = *format++;
311 				break;
312 			case '0':
313 				cnk->flags |= DP_F_ZERO;
314 				ch = *format++;
315 				break;
316 			case 'I':
317 				/* internationalization not supported yet */
318 				ch = *format++;
319 				break;
320 			default:
321 				state = DP_S_MIN;
322 				break;
323 			}
324 			break;
325 		case DP_S_MIN:
326 			if (isdigit((unsigned char)ch)) {
327 				cnk->min = 10 * cnk->min + char_to_int (ch);
328 				ch = *format++;
329 			} else if (ch == '$') {
330 				if (!pfirst && !pflag) {
331 					/* parameters must be all positioned or none */
332 					goto done;
333 				}
334 				if (pfirst) {
335 					pfirst = 0;
336 					pflag = 1;
337 				}
338 				if (cnk->min == 0) /* what ?? */
339 					goto done;
340 				cnk->num = cnk->min;
341 				cnk->min = 0;
342 				ch = *format++;
343 			} else if (ch == '*') {
344 				if (pfirst) pfirst = 0;
345 				cnk->min_star = new_chunk();
346 				if (!cnk->min_star) /* out of memory :-( */
347 					goto done;
348 				cnk->min_star->type = CNK_INT;
349 				if (pflag) {
350 					int num;
351 					ch = *format++;
352 					if (!isdigit((unsigned char)ch)) {
353 						/* parameters must be all positioned or none */
354 						goto done;
355 					}
356 					for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
357 						num = 10 * num + char_to_int(ch);
358 					}
359 					cnk->min_star->num = num;
360 					if (ch != '$') /* what ?? */
361 						goto done;
362 				} else {
363 					cnk->min_star->num = ++pnum;
364 				}
365 				max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star);
366 				if (max_pos == 0) /* out of memory :-( */
367 					goto done;
368 				ch = *format++;
369 				state = DP_S_DOT;
370 			} else {
371 				if (pfirst) pfirst = 0;
372 				state = DP_S_DOT;
373 			}
374 			break;
375 		case DP_S_DOT:
376 			if (ch == '.') {
377 				state = DP_S_MAX;
378 				ch = *format++;
379 			} else {
380 				state = DP_S_MOD;
381 			}
382 			break;
383 		case DP_S_MAX:
384 			if (isdigit((unsigned char)ch)) {
385 				if (cnk->max < 0)
386 					cnk->max = 0;
387 				cnk->max = 10 * cnk->max + char_to_int (ch);
388 				ch = *format++;
389 			} else if (ch == '$') {
390 				if (!pfirst && !pflag) {
391 					/* parameters must be all positioned or none */
392 					goto done;
393 				}
394 				if (cnk->max <= 0) /* what ?? */
395 					goto done;
396 				cnk->num = cnk->max;
397 				cnk->max = -1;
398 				ch = *format++;
399 			} else if (ch == '*') {
400 				cnk->max_star = new_chunk();
401 				if (!cnk->max_star) /* out of memory :-( */
402 					goto done;
403 				cnk->max_star->type = CNK_INT;
404 				if (pflag) {
405 					int num;
406 					ch = *format++;
407 					if (!isdigit((unsigned char)ch)) {
408 						/* parameters must be all positioned or none */
409 						goto done;
410 					}
411 					for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
412 						num = 10 * num + char_to_int(ch);
413 					}
414 					cnk->max_star->num = num;
415 					if (ch != '$') /* what ?? */
416 						goto done;
417 				} else {
418 					cnk->max_star->num = ++pnum;
419 				}
420 				max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star);
421 				if (max_pos == 0) /* out of memory :-( */
422 					goto done;
423 
424 				ch = *format++;
425 				state = DP_S_MOD;
426 			} else {
427 				state = DP_S_MOD;
428 			}
429 			break;
430 		case DP_S_MOD:
431 			switch (ch) {
432 			case 'h':
433 				cnk->cflags = DP_C_SHORT;
434 				ch = *format++;
435 				if (ch == 'h') {
436 					cnk->cflags = DP_C_CHAR;
437 					ch = *format++;
438 				}
439 				break;
440 			case 'l':
441 				cnk->cflags = DP_C_LONG;
442 				ch = *format++;
443 				if (ch == 'l') {	/* It's a long long */
444 					cnk->cflags = DP_C_LLONG;
445 					ch = *format++;
446 				}
447 				break;
448 			case 'j':
449 				cnk->cflags = DP_C_LLONG;
450 				ch = *format++;
451 				break;
452 			case 'L':
453 				cnk->cflags = DP_C_LDOUBLE;
454 				ch = *format++;
455 				break;
456 			case 'z':
457 				cnk->cflags = DP_C_SIZET;
458 				ch = *format++;
459 				break;
460 			default:
461 				break;
462 			}
463 			state = DP_S_CONV;
464 			break;
465 		case DP_S_CONV:
466 			if (cnk->num == 0) cnk->num = ++pnum;
467 			max_pos = add_cnk_list_entry(&clist, max_pos, cnk);
468 			if (max_pos == 0) /* out of memory :-( */
469 				goto done;
470 
471 			switch (ch) {
472 			case 'd':
473 			case 'i':
474 				cnk->type = CNK_INT;
475 				break;
476 			case 'o':
477 				cnk->type = CNK_OCTAL;
478 				cnk->flags |= DP_F_UNSIGNED;
479 				break;
480 			case 'u':
481 				cnk->type = CNK_UINT;
482 				cnk->flags |= DP_F_UNSIGNED;
483 				break;
484 			case 'X':
485 				cnk->flags |= DP_F_UP;
486 			case 'x':
487 				cnk->type = CNK_HEX;
488 				cnk->flags |= DP_F_UNSIGNED;
489 				break;
490 			case 'A':
491 				/* hex float not supported yet */
492 			case 'E':
493 			case 'G':
494 			case 'F':
495 				cnk->flags |= DP_F_UP;
496 			case 'a':
497 				/* hex float not supported yet */
498 			case 'e':
499 			case 'f':
500 			case 'g':
501 				cnk->type = CNK_FLOAT;
502 				break;
503 			case 'c':
504 				cnk->type = CNK_CHAR;
505 				break;
506 			case 's':
507 				cnk->type = CNK_STRING;
508 				break;
509 			case 'p':
510 				cnk->type = CNK_PTR;
511 				cnk->flags |= DP_F_UNSIGNED;
512 				break;
513 			case 'n':
514 				cnk->type = CNK_NUM;
515 				break;
516 			case '%':
517 				cnk->type = CNK_PRCNT;
518 				break;
519 			default:
520 				/* Unknown, bail out*/
521 				goto done;
522 			}
523 			ch = *format++;
524 			state = DP_S_DEFAULT;
525 			break;
526 		case DP_S_DONE:
527 			break;
528 		default:
529 			/* hmm? */
530 			break; /* some picky compilers need this */
531 		}
532 	}
533 
534 	/* retrieve the format arguments */
535 	for (pnum = 0; pnum < max_pos; pnum++) {
536 		int i;
537 
538 		if (clist[pnum].num == 0) {
539 			/* ignoring a parameter should not be permitted
540 			 * all parameters must be matched at least once
541 			 * BUT seem some system ignore this rule ...
542 			 * at least my glibc based system does --SSS
543 			 */
544 #ifdef DEBUG_SNPRINTF
545 			printf("parameter at position %d not used\n", pnum+1);
546 #endif
547 			/* eat the parameter */
548 			va_arg (args, int);
549 			continue;
550 		}
551 		for (i = 1; i < clist[pnum].num; i++) {
552 			if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) {
553 				/* nooo noo no!
554 				 * all the references to a parameter
555 				 * must be of the same type
556 				 */
557 				goto done;
558 			}
559 		}
560 		cnk = clist[pnum].chunks[0];
561 		switch (cnk->type) {
562 		case CNK_INT:
563 			if (cnk->cflags == DP_C_SHORT)
564 				cnk->value = va_arg (args, int);
565 			else if (cnk->cflags == DP_C_LONG)
566 				cnk->value = va_arg (args, long int);
567 			else if (cnk->cflags == DP_C_LLONG)
568 				cnk->value = va_arg (args, LLONG);
569 			else if (cnk->cflags == DP_C_SIZET)
570 				cnk->value = va_arg (args, ssize_t);
571 			else
572 				cnk->value = va_arg (args, int);
573 
574 			for (i = 1; i < clist[pnum].num; i++) {
575 				clist[pnum].chunks[i]->value = cnk->value;
576 			}
577 			break;
578 
579 		case CNK_OCTAL:
580 		case CNK_UINT:
581 		case CNK_HEX:
582 			if (cnk->cflags == DP_C_SHORT)
583 				cnk->value = va_arg (args, unsigned int);
584 			else if (cnk->cflags == DP_C_LONG)
585 				cnk->value = (unsigned long int)va_arg (args, unsigned long int);
586 			else if (cnk->cflags == DP_C_LLONG)
587 				cnk->value = (LLONG)va_arg (args, unsigned LLONG);
588 			else if (cnk->cflags == DP_C_SIZET)
589 				cnk->value = (size_t)va_arg (args, size_t);
590 			else
591 				cnk->value = (unsigned int)va_arg (args, unsigned int);
592 
593 			for (i = 1; i < clist[pnum].num; i++) {
594 				clist[pnum].chunks[i]->value = cnk->value;
595 			}
596 			break;
597 
598 		case CNK_FLOAT:
599 			if (cnk->cflags == DP_C_LDOUBLE)
600 				cnk->fvalue = va_arg (args, LDOUBLE);
601 			else
602 				cnk->fvalue = va_arg (args, double);
603 
604 			for (i = 1; i < clist[pnum].num; i++) {
605 				clist[pnum].chunks[i]->fvalue = cnk->fvalue;
606 			}
607 			break;
608 
609 		case CNK_CHAR:
610 			cnk->value = va_arg (args, int);
611 
612 			for (i = 1; i < clist[pnum].num; i++) {
613 				clist[pnum].chunks[i]->value = cnk->value;
614 			}
615 			break;
616 
617 		case CNK_STRING:
618 			cnk->strvalue = va_arg (args, char *);
619 			if (!cnk->strvalue) cnk->strvalue = "(NULL)";
620 
621 			for (i = 1; i < clist[pnum].num; i++) {
622 				clist[pnum].chunks[i]->strvalue = cnk->strvalue;
623 			}
624 			break;
625 
626 		case CNK_PTR:
627 			cnk->strvalue = va_arg (args, void *);
628 			for (i = 1; i < clist[pnum].num; i++) {
629 				clist[pnum].chunks[i]->strvalue = cnk->strvalue;
630 			}
631 			break;
632 
633 		case CNK_NUM:
634 			if (cnk->cflags == DP_C_CHAR)
635 				cnk->pnum = va_arg (args, char *);
636 			else if (cnk->cflags == DP_C_SHORT)
637 				cnk->pnum = va_arg (args, short int *);
638 			else if (cnk->cflags == DP_C_LONG)
639 				cnk->pnum = va_arg (args, long int *);
640 			else if (cnk->cflags == DP_C_LLONG)
641 				cnk->pnum = va_arg (args, LLONG *);
642 			else if (cnk->cflags == DP_C_SIZET)
643 				cnk->pnum = va_arg (args, ssize_t *);
644 			else
645 				cnk->pnum = va_arg (args, int *);
646 
647 			for (i = 1; i < clist[pnum].num; i++) {
648 				clist[pnum].chunks[i]->pnum = cnk->pnum;
649 			}
650 			break;
651 
652 		case CNK_PRCNT:
653 			break;
654 
655 		default:
656 			/* what ?? */
657 			goto done;
658 		}
659 	}
660 	/* print out the actual string from chunks */
661 	currlen = 0;
662 	cnk = chunks;
663 	while (cnk) {
664 		int len, min, max;
665 
666 		if (cnk->min_star) min = cnk->min_star->value;
667 		else min = cnk->min;
668 		if (cnk->max_star) max = cnk->max_star->value;
669 		else max = cnk->max;
670 
671 		switch (cnk->type) {
672 
673 		case CNK_FMT_STR:
674 			if (maxlen != 0 && maxlen > currlen) {
675 				if (maxlen > (currlen + cnk->len)) len = cnk->len;
676 				else len = maxlen - currlen;
677 
678 				memcpy(&(buffer[currlen]), &(base[cnk->start]), len);
679 			}
680 			currlen += cnk->len;
681 
682 			break;
683 
684 		case CNK_INT:
685 		case CNK_UINT:
686 			fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags);
687 			break;
688 
689 		case CNK_OCTAL:
690 			fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags);
691 			break;
692 
693 		case CNK_HEX:
694 			fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags);
695 			break;
696 
697 		case CNK_FLOAT:
698 			fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags);
699 			break;
700 
701 		case CNK_CHAR:
702 			dopr_outch (buffer, &currlen, maxlen, cnk->value);
703 			break;
704 
705 		case CNK_STRING:
706 			if (max == -1) {
707 				max = strlen(cnk->strvalue);
708 			}
709 			fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max);
710 			break;
711 
712 		case CNK_PTR:
713 			fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags);
714 			break;
715 
716 		case CNK_NUM:
717 			if (cnk->cflags == DP_C_CHAR)
718 				*((char *)(cnk->pnum)) = (char)currlen;
719 			else if (cnk->cflags == DP_C_SHORT)
720 				*((short int *)(cnk->pnum)) = (short int)currlen;
721 			else if (cnk->cflags == DP_C_LONG)
722 				*((long int *)(cnk->pnum)) = (long int)currlen;
723 			else if (cnk->cflags == DP_C_LLONG)
724 				*((LLONG *)(cnk->pnum)) = (LLONG)currlen;
725 			else if (cnk->cflags == DP_C_SIZET)
726 				*((ssize_t *)(cnk->pnum)) = (ssize_t)currlen;
727 			else
728 				*((int *)(cnk->pnum)) = (int)currlen;
729 			break;
730 
731 		case CNK_PRCNT:
732 			dopr_outch (buffer, &currlen, maxlen, '%');
733 			break;
734 
735 		default:
736 			/* what ?? */
737 			goto done;
738 		}
739 		cnk = cnk->next;
740 	}
741 	if (maxlen != 0) {
742 		if (currlen < maxlen - 1)
743 			buffer[currlen] = '\0';
744 		else if (maxlen > 0)
745 			buffer[maxlen - 1] = '\0';
746 	}
747 	ret = currlen;
748 
749 done:
750 	va_end(args);
751 
752 	while (chunks) {
753 		cnk = chunks->next;
754 		free(chunks);
755 		chunks = cnk;
756 	}
757 	if (clist) {
758 		for (pnum = 0; pnum < max_pos; pnum++) {
759 			if (clist[pnum].chunks) free(clist[pnum].chunks);
760 		}
761 		free(clist);
762 	}
763 	return ret;
764 }
765 
fmtstr(char * buffer,size_t * currlen,size_t maxlen,char * value,int flags,int min,int max)766 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
767 		    char *value, int flags, int min, int max)
768 {
769 	int padlen, strln;     /* amount to pad */
770 	int cnt = 0;
771 
772 #ifdef DEBUG_SNPRINTF
773 	printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
774 #endif
775 	if (value == 0) {
776 		value = "<NULL>";
777 	}
778 
779 	for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */
780 	padlen = min - strln;
781 	if (padlen < 0)
782 		padlen = 0;
783 	if (flags & DP_F_MINUS)
784 		padlen = -padlen; /* Left Justify */
785 
786 	while (padlen > 0) {
787 		dopr_outch (buffer, currlen, maxlen, ' ');
788 		--padlen;
789 	}
790 	while (*value && (cnt < max)) {
791 		dopr_outch (buffer, currlen, maxlen, *value++);
792 		++cnt;
793 	}
794 	while (padlen < 0) {
795 		dopr_outch (buffer, currlen, maxlen, ' ');
796 		++padlen;
797 	}
798 }
799 
800 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
801 
fmtint(char * buffer,size_t * currlen,size_t maxlen,LLONG value,int base,int min,int max,int flags)802 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
803 		    LLONG value, int base, int min, int max, int flags)
804 {
805 	int signvalue = 0;
806 	unsigned LLONG uvalue;
807 	char convert[22+1]; /* 64-bit value in octal: 22 digits + \0 */
808 	int place = 0;
809 	int spadlen = 0; /* amount to space pad */
810 	int zpadlen = 0; /* amount to zero pad */
811 	int caps = 0;
812 
813 	if (max < 0)
814 		max = 0;
815 
816 	uvalue = value;
817 
818 	if(!(flags & DP_F_UNSIGNED)) {
819 		if( value < 0 ) {
820 			signvalue = '-';
821 			uvalue = -value;
822 		} else {
823 			if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
824 				signvalue = '+';
825 			else if (flags & DP_F_SPACE)
826 				signvalue = ' ';
827 		}
828 	}
829 
830 	if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
831 
832 	do {
833 		convert[place++] =
834 			(caps? "0123456789ABCDEF":"0123456789abcdef")
835 			[uvalue % (unsigned)base  ];
836 		uvalue = (uvalue / (unsigned)base );
837 	} while(uvalue && (place < sizeof(convert)));
838 	if (place == sizeof(convert)) place--;
839 	convert[place] = 0;
840 
841 	zpadlen = max - place;
842 	spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
843 	if (zpadlen < 0) zpadlen = 0;
844 	if (spadlen < 0) spadlen = 0;
845 	if (flags & DP_F_ZERO) {
846 		zpadlen = MAX(zpadlen, spadlen);
847 		spadlen = 0;
848 	}
849 	if (flags & DP_F_MINUS)
850 		spadlen = -spadlen; /* Left Justifty */
851 
852 #ifdef DEBUG_SNPRINTF
853 	printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
854 	       zpadlen, spadlen, min, max, place);
855 #endif
856 
857 	/* Spaces */
858 	while (spadlen > 0) {
859 		dopr_outch (buffer, currlen, maxlen, ' ');
860 		--spadlen;
861 	}
862 
863 	/* Sign */
864 	if (signvalue)
865 		dopr_outch (buffer, currlen, maxlen, signvalue);
866 
867 	/* Zeros */
868 	if (zpadlen > 0) {
869 		while (zpadlen > 0) {
870 			dopr_outch (buffer, currlen, maxlen, '0');
871 			--zpadlen;
872 		}
873 	}
874 
875 	/* Digits */
876 	while (place > 0)
877 		dopr_outch (buffer, currlen, maxlen, convert[--place]);
878 
879 	/* Left Justified spaces */
880 	while (spadlen < 0) {
881 		dopr_outch (buffer, currlen, maxlen, ' ');
882 		++spadlen;
883 	}
884 }
885 
abs_val(LDOUBLE value)886 static LDOUBLE abs_val(LDOUBLE value)
887 {
888 	LDOUBLE result = value;
889 
890 	if (value < 0)
891 		result = -value;
892 
893 	return result;
894 }
895 
POW10(int exp)896 static LDOUBLE POW10(int exp)
897 {
898 	LDOUBLE result = 1;
899 
900 	while (exp) {
901 		result *= 10;
902 		exp--;
903 	}
904 
905 	return result;
906 }
907 
ROUND(LDOUBLE value)908 static LLONG ROUND(LDOUBLE value)
909 {
910 	LLONG intpart;
911 
912 	intpart = (LLONG)value;
913 	value = value - intpart;
914 	if (value >= 0.5) intpart++;
915 
916 	return intpart;
917 }
918 
919 /* a replacement for modf that doesn't need the math library. Should
920    be portable, but slow */
my_modf(double x0,double * iptr)921 static double my_modf(double x0, double *iptr)
922 {
923 	int i;
924 	LLONG l=0;
925 	double x = x0;
926 	double f = 1.0;
927 
928 	for (i=0;i<100;i++) {
929 		l = (long)x;
930 		if (l <= (x+1) && l >= (x-1)) break;
931 		x *= 0.1;
932 		f *= 10.0;
933 	}
934 
935 	if (i == 100) {
936 		/* yikes! the number is beyond what we can handle. What do we do? */
937 		(*iptr) = 0;
938 		return 0;
939 	}
940 
941 	if (i != 0) {
942 		double i2;
943 		double ret;
944 
945 		ret = my_modf(x0-l*f, &i2);
946 		(*iptr) = l*f + i2;
947 		return ret;
948 	}
949 
950 	(*iptr) = l;
951 	return x - (*iptr);
952 }
953 
954 
fmtfp(char * buffer,size_t * currlen,size_t maxlen,LDOUBLE fvalue,int min,int max,int flags)955 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
956 		   LDOUBLE fvalue, int min, int max, int flags)
957 {
958 	int signvalue = 0;
959 	double ufvalue;
960 	char iconvert[311];
961 	char fconvert[311];
962 	int iplace = 0;
963 	int fplace = 0;
964 	int padlen = 0; /* amount to pad */
965 	int zpadlen = 0;
966 	int caps = 0;
967 	int idx;
968 	double intpart;
969 	double fracpart;
970 	double temp;
971 
972 	/*
973 	 * AIX manpage says the default is 0, but Solaris says the default
974 	 * is 6, and sprintf on AIX defaults to 6
975 	 */
976 	if (max < 0)
977 		max = 6;
978 
979 	ufvalue = abs_val (fvalue);
980 
981 	if (fvalue < 0) {
982 		signvalue = '-';
983 	} else {
984 		if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
985 			signvalue = '+';
986 		} else {
987 			if (flags & DP_F_SPACE)
988 				signvalue = ' ';
989 		}
990 	}
991 
992 #if 0
993 	if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
994 #endif
995 
996 #if 0
997 	 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
998 #endif
999 
1000 	/*
1001 	 * Sorry, we only support 9 digits past the decimal because of our
1002 	 * conversion method
1003 	 */
1004 	if (max > 9)
1005 		max = 9;
1006 
1007 	/* We "cheat" by converting the fractional part to integer by
1008 	 * multiplying by a factor of 10
1009 	 */
1010 
1011 	temp = ufvalue;
1012 	my_modf(temp, &intpart);
1013 
1014 	fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
1015 
1016 	if (fracpart >= POW10(max)) {
1017 		intpart++;
1018 		fracpart -= POW10(max);
1019 	}
1020 
1021 
1022 	/* Convert integer part */
1023 	do {
1024 		temp = intpart*0.1;
1025 		my_modf(temp, &intpart);
1026 		idx = (int) ((temp -intpart +0.05)* 10.0);
1027 		/* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
1028 		/* printf ("%llf, %f, %x\n", temp, intpart, idx); */
1029 		iconvert[iplace++] =
1030 			(caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
1031 	} while (intpart && (iplace < 311));
1032 	if (iplace == 311) iplace--;
1033 	iconvert[iplace] = 0;
1034 
1035 	/* Convert fractional part */
1036 	if (fracpart)
1037 	{
1038 		do {
1039 			temp = fracpart*0.1;
1040 			my_modf(temp, &fracpart);
1041 			idx = (int) ((temp -fracpart +0.05)* 10.0);
1042 			/* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
1043 			/* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
1044 			fconvert[fplace++] =
1045 			(caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
1046 		} while(fracpart && (fplace < 311));
1047 		if (fplace == 311) fplace--;
1048 	}
1049 	fconvert[fplace] = 0;
1050 
1051 	/* -1 for decimal point, another -1 if we are printing a sign */
1052 	padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
1053 	zpadlen = max - fplace;
1054 	if (zpadlen < 0) zpadlen = 0;
1055 	if (padlen < 0)
1056 		padlen = 0;
1057 	if (flags & DP_F_MINUS)
1058 		padlen = -padlen; /* Left Justifty */
1059 
1060 	if ((flags & DP_F_ZERO) && (padlen > 0)) {
1061 		if (signvalue) {
1062 			dopr_outch (buffer, currlen, maxlen, signvalue);
1063 			--padlen;
1064 			signvalue = 0;
1065 		}
1066 		while (padlen > 0) {
1067 			dopr_outch (buffer, currlen, maxlen, '0');
1068 			--padlen;
1069 		}
1070 	}
1071 	while (padlen > 0) {
1072 		dopr_outch (buffer, currlen, maxlen, ' ');
1073 		--padlen;
1074 	}
1075 	if (signvalue)
1076 		dopr_outch (buffer, currlen, maxlen, signvalue);
1077 
1078 	while (iplace > 0)
1079 		dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
1080 
1081 #ifdef DEBUG_SNPRINTF
1082 	printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
1083 #endif
1084 
1085 	/*
1086 	 * Decimal point.  This should probably use locale to find the correct
1087 	 * char to print out.
1088 	 */
1089 	if (max > 0) {
1090 		dopr_outch (buffer, currlen, maxlen, '.');
1091 
1092 		while (zpadlen > 0) {
1093 			dopr_outch (buffer, currlen, maxlen, '0');
1094 			--zpadlen;
1095 		}
1096 
1097 		while (fplace > 0)
1098 			dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
1099 	}
1100 
1101 	while (padlen < 0) {
1102 		dopr_outch (buffer, currlen, maxlen, ' ');
1103 		++padlen;
1104 	}
1105 }
1106 
dopr_outch(char * buffer,size_t * currlen,size_t maxlen,char c)1107 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
1108 {
1109 	if (*currlen < maxlen) {
1110 		buffer[(*currlen)] = c;
1111 	}
1112 	(*currlen)++;
1113 }
1114 
new_chunk(void)1115 static struct pr_chunk *new_chunk(void) {
1116 	struct pr_chunk *new_c = (struct pr_chunk *)malloc(sizeof(struct pr_chunk));
1117 
1118 	if (!new_c)
1119 		return NULL;
1120 
1121 	new_c->type = 0;
1122 	new_c->num = 0;
1123 	new_c->min = 0;
1124 	new_c->min_star = NULL;
1125 	new_c->max = -1;
1126 	new_c->max_star = NULL;
1127 	new_c->flags = 0;
1128 	new_c->cflags = 0;
1129 	new_c->start = 0;
1130 	new_c->len = 0;
1131 	new_c->value = 0;
1132 	new_c->fvalue = 0;
1133 	new_c->strvalue = NULL;
1134 	new_c->pnum = NULL;
1135 	new_c->next = NULL;
1136 
1137 	return new_c;
1138 }
1139 
add_cnk_list_entry(struct pr_chunk_x ** list,int max_num,struct pr_chunk * chunk)1140 static int add_cnk_list_entry(struct pr_chunk_x **list,
1141 				int max_num, struct pr_chunk *chunk) {
1142 	struct pr_chunk_x *l;
1143 	struct pr_chunk **c;
1144 	int max;
1145 	int cnum;
1146 	int i, pos;
1147 
1148 	if (chunk->num > max_num) {
1149 		max = chunk->num;
1150 
1151 		if (*list == NULL) {
1152 			l = (struct pr_chunk_x *)malloc(sizeof(struct pr_chunk_x) * max);
1153 			pos = 0;
1154 		} else {
1155 			l = (struct pr_chunk_x *)realloc(*list, sizeof(struct pr_chunk_x) * max);
1156 			pos = max_num;
1157 		}
1158 		if (l == NULL) {
1159 			for (i = 0; i < max; i++) {
1160 				if ((*list)[i].chunks) free((*list)[i].chunks);
1161 			}
1162 			return 0;
1163 		}
1164 		for (i = pos; i < max; i++) {
1165 			l[i].chunks = NULL;
1166 			l[i].num = 0;
1167 		}
1168 	} else {
1169 		l = *list;
1170 		max = max_num;
1171 	}
1172 
1173 	i = chunk->num - 1;
1174 	cnum = l[i].num + 1;
1175 	if (l[i].chunks == NULL) {
1176 		c = (struct pr_chunk **)malloc(sizeof(struct pr_chunk *) * cnum);
1177 	} else {
1178 		c = (struct pr_chunk **)realloc(l[i].chunks, sizeof(struct pr_chunk *) * cnum);
1179 	}
1180 	if (c == NULL) {
1181 		for (i = 0; i < max; i++) {
1182 			if (l[i].chunks) free(l[i].chunks);
1183 		}
1184 		return 0;
1185 	}
1186 	c[l[i].num] = chunk;
1187 	l[i].chunks = c;
1188 	l[i].num = cnum;
1189 
1190 	*list = l;
1191 	return max;
1192 }
1193 
rep_vsnprintf(char * str,size_t count,const char * fmt,va_list args)1194  int rep_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
1195 {
1196 	return dopr(str, count, fmt, args);
1197 }
1198 #endif
1199 
1200 /* yes this really must be a ||. Don't muck with this (tridge)
1201  *
1202  * The logic for these two is that we need our own definition if the
1203  * OS *either* has no definition of *sprintf, or if it does have one
1204  * that doesn't work properly according to the autoconf test.
1205  */
1206 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
rep_snprintf(char * str,size_t count,const char * fmt,...)1207  int rep_snprintf(char *str,size_t count,const char *fmt,...)
1208 {
1209 	size_t ret;
1210 	va_list ap;
1211 
1212 	va_start(ap, fmt);
1213 	ret = vsnprintf(str, count, fmt, ap);
1214 	va_end(ap);
1215 	return ret;
1216 }
1217 #endif
1218 
1219 #ifndef HAVE_C99_VSNPRINTF
rep_printf(const char * fmt,...)1220  int rep_printf(const char *fmt, ...)
1221 {
1222 	va_list ap;
1223 	int ret;
1224 	char *s;
1225 
1226 	s = NULL;
1227 	va_start(ap, fmt);
1228 	ret = vasprintf(&s, fmt, ap);
1229 	va_end(ap);
1230 
1231 	if (s) {
1232 		fwrite(s, 1, strlen(s), stdout);
1233 	}
1234 	free(s);
1235 
1236 	return ret;
1237 }
1238 #endif
1239 
1240 #ifndef HAVE_C99_VSNPRINTF
rep_fprintf(FILE * stream,const char * fmt,...)1241  int rep_fprintf(FILE *stream, const char *fmt, ...)
1242 {
1243 	va_list ap;
1244 	int ret;
1245 	char *s;
1246 
1247 	s = NULL;
1248 	va_start(ap, fmt);
1249 	ret = vasprintf(&s, fmt, ap);
1250 	va_end(ap);
1251 
1252 	if (s) {
1253 		fwrite(s, 1, strlen(s), stream);
1254 	}
1255 	free(s);
1256 
1257 	return ret;
1258 }
1259 #endif
1260 
1261 #endif
1262 
1263 #if !defined(HAVE_VASPRINTF) || !defined(HAVE_C99_VSNPRINTF)
rep_vasprintf(char ** ptr,const char * format,va_list ap)1264  int rep_vasprintf(char **ptr, const char *format, va_list ap)
1265 {
1266 	int ret;
1267 	va_list ap2;
1268 
1269 	VA_COPY(ap2, ap);
1270 	ret = vsnprintf(NULL, 0, format, ap2);
1271 	va_end(ap2);
1272 	if (ret < 0) return ret;
1273 
1274 	(*ptr) = (char *)malloc(ret+1);
1275 	if (!*ptr) return -1;
1276 
1277 	VA_COPY(ap2, ap);
1278 	ret = vsnprintf(*ptr, ret+1, format, ap2);
1279 	va_end(ap2);
1280 
1281 	return ret;
1282 }
1283 #endif
1284 
1285 #if !defined(HAVE_ASPRINTF) || !defined(HAVE_C99_VSNPRINTF)
rep_asprintf(char ** ptr,const char * format,...)1286  int rep_asprintf(char **ptr, const char *format, ...)
1287 {
1288 	va_list ap;
1289 	int ret;
1290 
1291 	*ptr = NULL;
1292 	va_start(ap, format);
1293 	ret = vasprintf(ptr, format, ap);
1294 	va_end(ap);
1295 
1296 	return ret;
1297 }
1298 #endif
1299 
1300 #ifdef TEST_SNPRINTF
1301 
1302  int sprintf(char *str,const char *fmt,...);
1303  int printf(const char *fmt,...);
1304 
main(void)1305  int main (void)
1306 {
1307 	char buf1[1024];
1308 	char buf2[1024];
1309 	char *buf3;
1310 	char *fp_fmt[] = {
1311 		"%1.1f",
1312 		"%-1.5f",
1313 		"%1.5f",
1314 		"%123.9f",
1315 		"%10.5f",
1316 		"% 10.5f",
1317 		"%+22.9f",
1318 		"%+4.9f",
1319 		"%01.3f",
1320 		"%4f",
1321 		"%3.1f",
1322 		"%3.2f",
1323 		"%.0f",
1324 		"%f",
1325 		"%-8.8f",
1326 		"%-9.9f",
1327 		NULL
1328 	};
1329 	double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
1330 			     0.9996, 1.996, 4.136, 5.030201, 0.00205,
1331 			     /* END LIST */ 0};
1332 	char *int_fmt[] = {
1333 		"%-1.5d",
1334 		"%1.5d",
1335 		"%123.9d",
1336 		"%5.5d",
1337 		"%10.5d",
1338 		"% 10.5d",
1339 		"%+22.33d",
1340 		"%01.3d",
1341 		"%4d",
1342 		"%d",
1343 		NULL
1344 	};
1345 	long int_nums[] = { -1, 134, 91340, 341, 0203, 1234567890, 0};
1346 	char *str_fmt[] = {
1347 		"%10.5s",
1348 		"%-10.5s",
1349 		"%5.10s",
1350 		"%-5.10s",
1351 		"%10.1s",
1352 		"%0.10s",
1353 		"%10.0s",
1354 		"%1.10s",
1355 		"%s",
1356 		"%.1s",
1357 		"%.10s",
1358 		"%10s",
1359 		NULL
1360 	};
1361 	char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
1362 #ifdef HAVE_LONG_LONG
1363 	char *ll_fmt[] = {
1364 		"%llu",
1365 		NULL
1366 	};
1367 	LLONG ll_nums[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0};
1368 #endif
1369 	int x, y;
1370 	int fail = 0;
1371 	int num = 0;
1372 	int l1, l2;
1373 	char *ss_fmt[] = {
1374 		"%zd",
1375 		"%zu",
1376 		NULL
1377 	};
1378 	size_t ss_nums[] = {134, 91340, 123456789, 0203, 1234567890, 0};
1379 
1380 	printf ("Testing snprintf format codes against system sprintf...\n");
1381 
1382 	for (x = 0; fp_fmt[x] ; x++) {
1383 		for (y = 0; fp_nums[y] != 0 ; y++) {
1384 			buf1[0] = buf2[0] = '\0';
1385 			l1 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
1386 			l2 = sprintf (buf2, fp_fmt[x], fp_nums[y]);
1387 			buf1[1023] = buf2[1023] = '\0';
1388 			if (strcmp (buf1, buf2) || (l1 != l2)) {
1389 				printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1390 				       fp_fmt[x], l1, buf1, l2, buf2);
1391 				fail++;
1392 			}
1393 			num++;
1394 		}
1395 	}
1396 
1397 	for (x = 0; int_fmt[x] ; x++) {
1398 		for (y = 0; int_nums[y] != 0 ; y++) {
1399 			buf1[0] = buf2[0] = '\0';
1400 			l1 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
1401 			l2 = sprintf (buf2, int_fmt[x], int_nums[y]);
1402 			buf1[1023] = buf2[1023] = '\0';
1403 			if (strcmp (buf1, buf2) || (l1 != l2)) {
1404 				printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1405 				       int_fmt[x], l1, buf1, l2, buf2);
1406 				fail++;
1407 			}
1408 			num++;
1409 		}
1410 	}
1411 
1412 	for (x = 0; str_fmt[x] ; x++) {
1413 		for (y = 0; str_vals[y] != 0 ; y++) {
1414 			buf1[0] = buf2[0] = '\0';
1415 			l1 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
1416 			l2 = sprintf (buf2, str_fmt[x], str_vals[y]);
1417 			buf1[1023] = buf2[1023] = '\0';
1418 			if (strcmp (buf1, buf2) || (l1 != l2)) {
1419 				printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1420 				       str_fmt[x], l1, buf1, l2, buf2);
1421 				fail++;
1422 			}
1423 			num++;
1424 		}
1425 	}
1426 
1427 #ifdef HAVE_LONG_LONG
1428 	for (x = 0; ll_fmt[x] ; x++) {
1429 		for (y = 0; ll_nums[y] != 0 ; y++) {
1430 			buf1[0] = buf2[0] = '\0';
1431 			l1 = snprintf(buf1, sizeof(buf1), ll_fmt[x], ll_nums[y]);
1432 			l2 = sprintf (buf2, ll_fmt[x], ll_nums[y]);
1433 			buf1[1023] = buf2[1023] = '\0';
1434 			if (strcmp (buf1, buf2) || (l1 != l2)) {
1435 				printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1436 				       ll_fmt[x], l1, buf1, l2, buf2);
1437 				fail++;
1438 			}
1439 			num++;
1440 		}
1441 	}
1442 #endif
1443 
1444 #define BUFSZ 2048
1445 
1446 	buf1[0] = buf2[0] = '\0';
1447 	if ((buf3 = malloc(BUFSZ)) == NULL) {
1448 		fail++;
1449 	} else {
1450 		num++;
1451 		memset(buf3, 'a', BUFSZ);
1452 		snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3);
1453 		buf1[1023] = '\0';
1454 		if (strcmp(buf1, "a") != 0) {
1455 			printf("length limit buf1 '%s' expected 'a'\n", buf1);
1456 			fail++;
1457 		}
1458         }
1459 
1460 	buf1[0] = buf2[0] = '\0';
1461 	l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
1462 	l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
1463 	buf1[1023] = buf2[1023] = '\0';
1464 	if (strcmp(buf1, buf2) || (l1 != l2)) {
1465 		printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1466 				"%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
1467 		fail++;
1468 	}
1469 
1470 	buf1[0] = buf2[0] = '\0';
1471 	l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
1472 	l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
1473 	buf1[1023] = buf2[1023] = '\0';
1474 	if (strcmp(buf1, buf2)) {
1475 		printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1476 				"%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
1477 		fail++;
1478 	}
1479 
1480 	for (x = 0; ss_fmt[x] ; x++) {
1481 		for (y = 0; ss_nums[y] != 0 ; y++) {
1482 			buf1[0] = buf2[0] = '\0';
1483 			l1 = snprintf(buf1, sizeof(buf1), ss_fmt[x], ss_nums[y]);
1484 			l2 = sprintf (buf2, ss_fmt[x], ss_nums[y]);
1485 			buf1[1023] = buf2[1023] = '\0';
1486 			if (strcmp (buf1, buf2) || (l1 != l2)) {
1487 				printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1488 				       ss_fmt[x], l1, buf1, l2, buf2);
1489 				fail++;
1490 			}
1491 			num++;
1492 		}
1493 	}
1494 #if 0
1495 	buf1[0] = buf2[0] = '\0';
1496 	l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890);
1497 	l2 = sprintf(buf2, "%lld", (LLONG)1234567890);
1498 	buf1[1023] = buf2[1023] = '\0';
1499 	if (strcmp(buf1, buf2)) {
1500 		printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1501 				"%lld", l1, buf1, l2, buf2);
1502 		fail++;
1503 	}
1504 
1505 	buf1[0] = buf2[0] = '\0';
1506 	l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123);
1507 	l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123);
1508 	buf1[1023] = buf2[1023] = '\0';
1509 	if (strcmp(buf1, buf2)) {
1510 		printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1511 				"%Lf", l1, buf1, l2, buf2);
1512 		fail++;
1513 	}
1514 #endif
1515 	printf ("%d tests failed out of %d.\n", fail, num);
1516 
1517 	printf("seeing how many digits we support\n");
1518 	{
1519 		double v0 = 0.12345678901234567890123456789012345678901;
1520 		for (x=0; x<100; x++) {
1521 			double p = pow(10, x);
1522 			double r = v0*p;
1523 			snprintf(buf1, sizeof(buf1), "%1.1f", r);
1524 			sprintf(buf2,                "%1.1f", r);
1525 			if (strcmp(buf1, buf2)) {
1526 				printf("we seem to support %d digits\n", x-1);
1527 				break;
1528 			}
1529 		}
1530 	}
1531 
1532 	return 0;
1533 }
1534 #endif /* TEST_SNPRINTF */
1535