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, formated 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  * added 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 #ifndef NO_CONFIG_H
107 /* 08/13/2007 EG changed path to config.h to match NRPE distro */
108 #include "../include/config.h"
109 #else
110 #define NULL 0
111 #endif
112 
113 #ifdef TEST_SNPRINTF /* need math library headers for testing */
114 
115 /* In test mode, we pretend that this system doesn't have any snprintf
116  * functions, regardless of what config.h says. */
117 #  undef HAVE_SNPRINTF
118 #  undef HAVE_VSNPRINTF
119 #  undef HAVE_C99_VSNPRINTF
120 #  undef HAVE_ASPRINTF
121 #  undef HAVE_VASPRINTF
122 #  include <math.h>
123 #endif /* TEST_SNPRINTF */
124 
125 #ifdef HAVE_STRING_H
126 #include <string.h>
127 #endif
128 
129 #ifdef HAVE_STRINGS_H
130 #include <strings.h>
131 #endif
132 #ifdef HAVE_CTYPE_H
133 #include <ctype.h>
134 #endif
135 #include <sys/types.h>
136 #include <stdarg.h>
137 #ifdef HAVE_STDLIB_H
138 #include <stdlib.h>
139 #endif
140 
141 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
142 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
143 #include <stdio.h>
144  /* make the compiler happy with an empty file */
145  void dummy_snprintf(void);
dummy_snprintf(void)146  void dummy_snprintf(void) {}
147 #endif /* HAVE_SNPRINTF, etc */
148 
149 #ifdef HAVE_LONG_DOUBLE
150 #define LDOUBLE long double
151 #else
152 #define LDOUBLE double
153 #endif
154 
155 #ifdef HAVE_LONG_LONG
156 #define LLONG long long
157 #else
158 #define LLONG long
159 #endif
160 
161 #ifndef VA_COPY
162 #ifdef HAVE_VA_COPY
163 #define VA_COPY(dest, src) va_copy(dest, src)
164 #else
165 #ifdef HAVE___VA_COPY
166 #define VA_COPY(dest, src) __va_copy(dest, src)
167 #else
168 #define VA_COPY(dest, src) (dest) = (src)
169 #endif
170 #endif
171 
172 /*
173  * dopr(): poor man's version of doprintf
174  */
175 
176 /* format read states */
177 #define DP_S_DEFAULT 0
178 #define DP_S_FLAGS   1
179 #define DP_S_MIN     2
180 #define DP_S_DOT     3
181 #define DP_S_MAX     4
182 #define DP_S_MOD     5
183 #define DP_S_CONV    6
184 #define DP_S_DONE    7
185 
186 /* format flags - Bits */
187 #define DP_F_MINUS 	(1 << 0)
188 #define DP_F_PLUS  	(1 << 1)
189 #define DP_F_SPACE 	(1 << 2)
190 #define DP_F_NUM   	(1 << 3)
191 #define DP_F_ZERO  	(1 << 4)
192 #define DP_F_UP    	(1 << 5)
193 #define DP_F_UNSIGNED 	(1 << 6)
194 
195 /* Conversion Flags */
196 #define DP_C_CHAR    1
197 #define DP_C_SHORT   2
198 #define DP_C_LONG    3
199 #define DP_C_LDOUBLE 4
200 #define DP_C_LLONG   5
201 
202 /* Chunk types */
203 #define CNK_FMT_STR 0
204 #define CNK_INT     1
205 #define CNK_OCTAL   2
206 #define CNK_UINT    3
207 #define CNK_HEX     4
208 #define CNK_FLOAT   5
209 #define CNK_CHAR    6
210 #define CNK_STRING  7
211 #define CNK_PTR     8
212 #define CNK_NUM     9
213 #define CNK_PRCNT   10
214 
215 #define char_to_int(p) ((p)- '0')
216 #ifndef MAX
217 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
218 #endif
219 
220 /* yes this really must be a ||. Don't muck with this (tridge) */
221 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
222 
223 struct pr_chunk {
224 	int type; /* chunk type */
225 	int num; /* parameter number */
226 	int min;
227 	int max;
228 	int flags;
229 	int cflags;
230 	int start;
231 	int len;
232 	LLONG value;
233 	LDOUBLE fvalue;
234 	char *strvalue;
235 	void *pnum;
236 	struct pr_chunk *min_star;
237 	struct pr_chunk *max_star;
238 	struct pr_chunk *next;
239 };
240 
241 struct pr_chunk_x {
242 	struct pr_chunk **chunks;
243 	int num;
244 };
245 
246 static size_t dopr(char *buffer, size_t maxlen, const char *format,
247 		   va_list args_in);
248 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
249 		    char *value, int flags, int min, int max);
250 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
251 		    long value, int base, int min, int max, int flags);
252 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
253 		   LDOUBLE fvalue, int min, int max, int flags);
254 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
255 static struct pr_chunk *new_chunk(void);
256 static int add_cnk_list_entry(struct pr_chunk_x **list,
257 				int max_num, struct pr_chunk *chunk);
258 
dopr(char * buffer,size_t maxlen,const char * format,va_list args_in)259 static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
260 {
261 	char ch;
262 	int state;
263 	int pflag;
264 	int pnum;
265 	int pfirst;
266 	size_t currlen;
267 	va_list args;
268 	const char *base;
269 	struct pr_chunk *chunks = NULL;
270 	struct pr_chunk *cnk = NULL;
271 	struct pr_chunk_x *clist = NULL;
272 	int max_pos;
273 	size_t ret = (size_t)-1;
274 
275 	VA_COPY(args, args_in);
276 
277 	state = DP_S_DEFAULT;
278 	pfirst = 1;
279 	pflag = 0;
280 	pnum = 0;
281 
282 	max_pos = 0;
283 	base = format;
284 	ch = *format++;
285 
286 	/* retrieve the string structure as chunks */
287 	while (state != DP_S_DONE) {
288 		if (ch == '\0')
289 			state = DP_S_DONE;
290 
291 		switch(state) {
292 		case DP_S_DEFAULT:
293 
294 			if (cnk) {
295 				cnk->next = new_chunk();
296 				cnk = cnk->next;
297 			} else {
298 				cnk = new_chunk();
299 			}
300 			if (!cnk) goto done;
301 			if (!chunks) chunks = cnk;
302 
303 			if (ch == '%') {
304 				state = DP_S_FLAGS;
305 				ch = *format++;
306 			} else {
307 				cnk->type = CNK_FMT_STR;
308 				cnk->start = format - base -1;
309 				while ((ch != '\0') && (ch != '%')) ch = *format++;
310 				cnk->len = format - base - cnk->start -1;
311 			}
312 			break;
313 		case DP_S_FLAGS:
314 			switch (ch) {
315 			case '-':
316 				cnk->flags |= DP_F_MINUS;
317 				ch = *format++;
318 				break;
319 			case '+':
320 				cnk->flags |= DP_F_PLUS;
321 				ch = *format++;
322 				break;
323 			case ' ':
324 				cnk->flags |= DP_F_SPACE;
325 				ch = *format++;
326 				break;
327 			case '#':
328 				cnk->flags |= DP_F_NUM;
329 				ch = *format++;
330 				break;
331 			case '0':
332 				cnk->flags |= DP_F_ZERO;
333 				ch = *format++;
334 				break;
335 			case 'I':
336 				/* internationalization not supported yet */
337 				ch = *format++;
338 				break;
339 			default:
340 				state = DP_S_MIN;
341 				break;
342 			}
343 			break;
344 		case DP_S_MIN:
345 			if (isdigit((unsigned char)ch)) {
346 				cnk->min = 10 * cnk->min + char_to_int (ch);
347 				ch = *format++;
348 			} else if (ch == '$') {
349 				if (!pfirst && !pflag) {
350 					/* parameters must be all positioned or none */
351 					goto done;
352 				}
353 				if (pfirst) {
354 					pfirst = 0;
355 					pflag = 1;
356 				}
357 				if (cnk->min == 0) /* what ?? */
358 					goto done;
359 				cnk->num = cnk->min;
360 				cnk->min = 0;
361 				ch = *format++;
362 			} else if (ch == '*') {
363 				if (pfirst) pfirst = 0;
364 				cnk->min_star = new_chunk();
365 				if (!cnk->min_star) /* out of memory :-( */
366 					goto done;
367 				cnk->min_star->type = CNK_INT;
368 				if (pflag) {
369 					int num;
370 					ch = *format++;
371 					if (!isdigit((unsigned char)ch)) {
372 						/* parameters must be all positioned or none */
373 						goto done;
374 					}
375 					for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
376 						num = 10 * num + char_to_int(ch);
377 					}
378 					cnk->min_star->num = num;
379 					if (ch != '$') /* what ?? */
380 						goto done;
381 				} else {
382 					cnk->min_star->num = ++pnum;
383 				}
384 				max_pos = add_cnk_list_entry(&clist, max_pos, cnk->min_star);
385 				if (max_pos == 0) /* out of memory :-( */
386 					goto done;
387 				ch = *format++;
388 				state = DP_S_DOT;
389 			} else {
390 				if (pfirst) pfirst = 0;
391 				state = DP_S_DOT;
392 			}
393 			break;
394 		case DP_S_DOT:
395 			if (ch == '.') {
396 				state = DP_S_MAX;
397 				ch = *format++;
398 			} else {
399 				state = DP_S_MOD;
400 			}
401 			break;
402 		case DP_S_MAX:
403 			if (isdigit((unsigned char)ch)) {
404 				if (cnk->max < 0)
405 					cnk->max = 0;
406 				cnk->max = 10 * cnk->max + char_to_int (ch);
407 				ch = *format++;
408 			} else if (ch == '$') {
409 				if (!pfirst && !pflag) {
410 					/* parameters must be all positioned or none */
411 					goto done;
412 				}
413 				if (cnk->max <= 0) /* what ?? */
414 					goto done;
415 				cnk->num = cnk->max;
416 				cnk->max = -1;
417 				ch = *format++;
418 			} else if (ch == '*') {
419 				cnk->max_star = new_chunk();
420 				if (!cnk->max_star) /* out of memory :-( */
421 					goto done;
422 				cnk->max_star->type = CNK_INT;
423 				if (pflag) {
424 					int num;
425 					ch = *format++;
426 					if (!isdigit((unsigned char)ch)) {
427 						/* parameters must be all positioned or none */
428 						goto done;
429 					}
430 					for (num = 0; isdigit((unsigned char)ch); ch = *format++) {
431 						num = 10 * num + char_to_int(ch);
432 					}
433 					cnk->max_star->num = num;
434 					if (ch != '$') /* what ?? */
435 						goto done;
436 				} else {
437 					cnk->max_star->num = ++pnum;
438 				}
439 				max_pos = add_cnk_list_entry(&clist, max_pos, cnk->max_star);
440 				if (max_pos == 0) /* out of memory :-( */
441 					goto done;
442 
443 				ch = *format++;
444 				state = DP_S_MOD;
445 			} else {
446 				state = DP_S_MOD;
447 			}
448 			break;
449 		case DP_S_MOD:
450 			switch (ch) {
451 			case 'h':
452 				cnk->cflags = DP_C_SHORT;
453 				ch = *format++;
454 				if (ch == 'h') {
455 					cnk->cflags = DP_C_CHAR;
456 					ch = *format++;
457 				}
458 				break;
459 			case 'l':
460 				cnk->cflags = DP_C_LONG;
461 				ch = *format++;
462 				if (ch == 'l') {	/* It's a long long */
463 					cnk->cflags = DP_C_LLONG;
464 					ch = *format++;
465 				}
466 				break;
467 			case 'L':
468 				cnk->cflags = DP_C_LDOUBLE;
469 				ch = *format++;
470 				break;
471 			default:
472 				break;
473 			}
474 			state = DP_S_CONV;
475 			break;
476 		case DP_S_CONV:
477 			if (cnk->num == 0) cnk->num = ++pnum;
478 			max_pos = add_cnk_list_entry(&clist, max_pos, cnk);
479 			if (max_pos == 0) /* out of memory :-( */
480 				goto done;
481 
482 			switch (ch) {
483 			case 'd':
484 			case 'i':
485 				cnk->type = CNK_INT;
486 				break;
487 			case 'o':
488 				cnk->type = CNK_OCTAL;
489 				cnk->flags |= DP_F_UNSIGNED;
490 				break;
491 			case 'u':
492 				cnk->type = CNK_UINT;
493 				cnk->flags |= DP_F_UNSIGNED;
494 				break;
495 			case 'X':
496 				cnk->flags |= DP_F_UP;
497 			case 'x':
498 				cnk->type = CNK_HEX;
499 				cnk->flags |= DP_F_UNSIGNED;
500 				break;
501 			case 'A':
502 				/* hex float not supported yet */
503 			case 'E':
504 			case 'G':
505 			case 'F':
506 				cnk->flags |= DP_F_UP;
507 			case 'a':
508 				/* hex float not supported yet */
509 			case 'e':
510 			case 'f':
511 			case 'g':
512 				cnk->type = CNK_FLOAT;
513 				break;
514 			case 'c':
515 				cnk->type = CNK_CHAR;
516 				break;
517 			case 's':
518 				cnk->type = CNK_STRING;
519 				break;
520 			case 'p':
521 				cnk->type = CNK_PTR;
522 				break;
523 			case 'n':
524 				cnk->type = CNK_NUM;
525 				break;
526 			case '%':
527 				cnk->type = CNK_PRCNT;
528 				break;
529 			default:
530 				/* Unknown, bail out*/
531 				goto done;
532 			}
533 			ch = *format++;
534 			state = DP_S_DEFAULT;
535 			break;
536 		case DP_S_DONE:
537 			break;
538 		default:
539 			/* hmm? */
540 			break; /* some picky compilers need this */
541 		}
542 	}
543 
544 	/* retieve the format arguments */
545 	for (pnum = 0; pnum < max_pos; pnum++) {
546 		int i;
547 
548 		if (clist[pnum].num == 0) {
549 			/* ignoring a parameter should not be permitted
550 			 * all parameters must be matched at least once
551 			 * BUT seem some system ignore this rule ...
552 			 * at least my glibc based system does --SSS
553 			 */
554 #ifdef DEBUG_SNPRINTF
555 			printf("parameter at position %d not used\n", pnum+1);
556 #endif
557 			/* eat the parameter */
558 			va_arg (args, int);
559 			continue;
560 		}
561 		for (i = 1; i < clist[pnum].num; i++) {
562 			if (clist[pnum].chunks[0]->type != clist[pnum].chunks[i]->type) {
563 				/* nooo noo no!
564 				 * all the references to a parameter
565 				 * must be of the same type
566 				 */
567 				goto done;
568 			}
569 		}
570 		cnk = clist[pnum].chunks[0];
571 		switch (cnk->type) {
572 		case CNK_INT:
573 			if (cnk->cflags == DP_C_SHORT)
574 				cnk->value = va_arg (args, int);
575 			else if (cnk->cflags == DP_C_LONG)
576 				cnk->value = va_arg (args, long int);
577 			else if (cnk->cflags == DP_C_LLONG)
578 				cnk->value = va_arg (args, LLONG);
579 			else
580 				cnk->value = va_arg (args, int);
581 
582 			for (i = 1; i < clist[pnum].num; i++) {
583 				clist[pnum].chunks[i]->value = cnk->value;
584 			}
585 			break;
586 
587 		case CNK_OCTAL:
588 		case CNK_UINT:
589 		case CNK_HEX:
590 			if (cnk->cflags == DP_C_SHORT)
591 				cnk->value = va_arg (args, unsigned int);
592 			else if (cnk->cflags == DP_C_LONG)
593 				cnk->value = (long)va_arg (args, unsigned long int);
594 			else if (cnk->cflags == DP_C_LLONG)
595 				cnk->value = (LLONG)va_arg (args, unsigned LLONG);
596 			else
597 				cnk->value = (long)va_arg (args, unsigned int);
598 
599 			for (i = 1; i < clist[pnum].num; i++) {
600 				clist[pnum].chunks[i]->value = cnk->value;
601 			}
602 			break;
603 
604 		case CNK_FLOAT:
605 			if (cnk->cflags == DP_C_LDOUBLE)
606 				cnk->fvalue = va_arg (args, LDOUBLE);
607 			else
608 				cnk->fvalue = va_arg (args, double);
609 
610 			for (i = 1; i < clist[pnum].num; i++) {
611 				clist[pnum].chunks[i]->fvalue = cnk->fvalue;
612 			}
613 			break;
614 
615 		case CNK_CHAR:
616 			cnk->value = va_arg (args, int);
617 
618 			for (i = 1; i < clist[pnum].num; i++) {
619 				clist[pnum].chunks[i]->value = cnk->value;
620 			}
621 			break;
622 
623 		case CNK_STRING:
624 			cnk->strvalue = va_arg (args, char *);
625 			if (!cnk->strvalue) cnk->strvalue = "(NULL)";
626 
627 			for (i = 1; i < clist[pnum].num; i++) {
628 				clist[pnum].chunks[i]->strvalue = cnk->strvalue;
629 			}
630 			break;
631 
632 		case CNK_PTR:
633 			cnk->strvalue = va_arg (args, void *);
634 			for (i = 1; i < clist[pnum].num; i++) {
635 				clist[pnum].chunks[i]->strvalue = cnk->strvalue;
636 			}
637 			break;
638 
639 		case CNK_NUM:
640 			if (cnk->cflags == DP_C_CHAR)
641 				cnk->pnum = va_arg (args, char *);
642 			else if (cnk->cflags == DP_C_SHORT)
643 				cnk->pnum = va_arg (args, short int *);
644 			else if (cnk->cflags == DP_C_LONG)
645 				cnk->pnum = va_arg (args, long int *);
646 			else if (cnk->cflags == DP_C_LLONG)
647 				cnk->pnum = va_arg (args, LLONG *);
648 			else
649 				cnk->pnum = va_arg (args, int *);
650 
651 			for (i = 1; i < clist[pnum].num; i++) {
652 				clist[pnum].chunks[i]->pnum = cnk->pnum;
653 			}
654 			break;
655 
656 		case CNK_PRCNT:
657 			break;
658 
659 		default:
660 			/* what ?? */
661 			goto done;
662 		}
663 	}
664 	/* print out the actual string from chunks */
665 	currlen = 0;
666 	cnk = chunks;
667 	while (cnk) {
668 		int len, min, max;
669 
670 		if (cnk->min_star) min = cnk->min_star->value;
671 		else min = cnk->min;
672 		if (cnk->max_star) max = cnk->max_star->value;
673 		else max = cnk->max;
674 
675 		switch (cnk->type) {
676 
677 		case CNK_FMT_STR:
678 			if (maxlen != 0 && maxlen > currlen) {
679 				if (maxlen > (currlen + cnk->len)) len = cnk->len;
680 				else len = maxlen - currlen;
681 
682 				memcpy(&(buffer[currlen]), &(base[cnk->start]), len);
683 			}
684 			currlen += cnk->len;
685 
686 			break;
687 
688 		case CNK_INT:
689 		case CNK_UINT:
690 			fmtint (buffer, &currlen, maxlen, cnk->value, 10, min, max, cnk->flags);
691 			break;
692 
693 		case CNK_OCTAL:
694 			fmtint (buffer, &currlen, maxlen, cnk->value, 8, min, max, cnk->flags);
695 			break;
696 
697 		case CNK_HEX:
698 			fmtint (buffer, &currlen, maxlen, cnk->value, 16, min, max, cnk->flags);
699 			break;
700 
701 		case CNK_FLOAT:
702 			fmtfp (buffer, &currlen, maxlen, cnk->fvalue, min, max, cnk->flags);
703 			break;
704 
705 		case CNK_CHAR:
706 			dopr_outch (buffer, &currlen, maxlen, cnk->value);
707 			break;
708 
709 		case CNK_STRING:
710 			if (max == -1) {
711 				max = strlen(cnk->strvalue);
712 			}
713 			fmtstr (buffer, &currlen, maxlen, cnk->strvalue, cnk->flags, min, max);
714 			break;
715 
716 		case CNK_PTR:
717 			fmtint (buffer, &currlen, maxlen, (long)(cnk->strvalue), 16, min, max, cnk->flags);
718 			break;
719 
720 		case CNK_NUM:
721 			if (cnk->cflags == DP_C_CHAR)
722 				*((char *)(cnk->pnum)) = (char)currlen;
723 			else if (cnk->cflags == DP_C_SHORT)
724 				*((short int *)(cnk->pnum)) = (short int)currlen;
725 			else if (cnk->cflags == DP_C_LONG)
726 				*((long int *)(cnk->pnum)) = (long int)currlen;
727 			else if (cnk->cflags == DP_C_LLONG)
728 				*((LLONG *)(cnk->pnum)) = (LLONG)currlen;
729 			else
730 				*((int *)(cnk->pnum)) = (int)currlen;
731 			break;
732 
733 		case CNK_PRCNT:
734 			dopr_outch (buffer, &currlen, maxlen, '%');
735 			break;
736 
737 		default:
738 			/* what ?? */
739 			goto done;
740 		}
741 		cnk = cnk->next;
742 	}
743 	if (maxlen != 0) {
744 		if (currlen < maxlen - 1)
745 			buffer[currlen] = '\0';
746 		else if (maxlen > 0)
747 			buffer[maxlen - 1] = '\0';
748 	}
749 	ret = currlen;
750 
751 done:
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,long value,int base,int min,int max,int flags)802 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
803 		    long value, int base, int min, int max, int flags)
804 {
805 	int signvalue = 0;
806 	unsigned long uvalue;
807 	char convert[20];
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 < 20));
838 	if (place == 20) 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 Justify */
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 	long l;
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 Justify */
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 
smb_vsnprintf(char * str,size_t count,const char * fmt,va_list args)1194  int smb_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
1195 {
1196 	return dopr(str, count, fmt, args);
1197 }
1198 #define vsnprintf smb_vsnprintf
1199 #endif
1200 
1201 /* yes this really must be a ||. Don't muck with this (tridge)
1202  *
1203  * The logic for these two is that we need our own definition if the
1204  * OS *either* has no definition of *sprintf, or if it does have one
1205  * that doesn't work properly according to the autoconf test.
1206  */
1207 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
smb_snprintf(char * str,size_t count,const char * fmt,...)1208 int smb_snprintf(char *str,size_t count,const char *fmt,...)
1209 {
1210 	size_t ret;
1211 	va_list ap;
1212 
1213 	va_start(ap, fmt);
1214 	ret = vsnprintf(str, count, fmt, ap);
1215 	va_end(ap);
1216 	return ret;
1217 }
1218 #define snprintf smb_snprintf
1219 #endif
1220 
1221 #endif
1222 
1223 #ifndef HAVE_VASPRINTF
vasprintf(char ** ptr,const char * format,va_list ap)1224  int vasprintf(char **ptr, const char *format, va_list ap)
1225 {
1226 	int ret;
1227 	va_list ap2;
1228 
1229 	VA_COPY(ap2, ap);
1230 
1231 	ret = vsnprintf(NULL, 0, format, ap2);
1232 	if (ret <= 0) return ret;
1233 
1234 	(*ptr) = (char *)malloc(ret+1);
1235 	if (!*ptr) return -1;
1236 
1237 	VA_COPY(ap2, ap);
1238 
1239 	ret = vsnprintf(*ptr, ret+1, format, ap2);
1240 
1241 	return ret;
1242 }
1243 #endif
1244 
1245 
1246 #ifndef HAVE_ASPRINTF
asprintf(char ** ptr,const char * format,...)1247  int asprintf(char **ptr, const char *format, ...)
1248 {
1249 	va_list ap;
1250 	int ret;
1251 
1252 	*ptr = NULL;
1253 	va_start(ap, format);
1254 	ret = vasprintf(ptr, format, ap);
1255 	va_end(ap);
1256 
1257 	return ret;
1258 }
1259 #endif
1260 
1261 #ifdef TEST_SNPRINTF
1262 
1263  int sprintf(char *str,const char *fmt,...);
1264 
main(void)1265  int main (void)
1266 {
1267 	char buf1[1024];
1268 	char buf2[1024];
1269 	char *buf3;
1270 	char *fp_fmt[] = {
1271 		"%1.1f",
1272 		"%-1.5f",
1273 		"%1.5f",
1274 		"%123.9f",
1275 		"%10.5f",
1276 		"% 10.5f",
1277 		"%+22.9f",
1278 		"%+4.9f",
1279 		"%01.3f",
1280 		"%4f",
1281 		"%3.1f",
1282 		"%3.2f",
1283 		"%.0f",
1284 		"%f",
1285 		"%-8.8f",
1286 		"%-9.9f",
1287 		NULL
1288 	};
1289 	double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
1290 			     0.9996, 1.996, 4.136, 5.030201, 0.00205,
1291 			     /* END LIST */ 0};
1292 	char *int_fmt[] = {
1293 		"%-1.5d",
1294 		"%1.5d",
1295 		"%123.9d",
1296 		"%5.5d",
1297 		"%10.5d",
1298 		"% 10.5d",
1299 		"%+22.33d",
1300 		"%01.3d",
1301 		"%4d",
1302 		"%d",
1303 		NULL
1304 	};
1305 	long int_nums[] = { -1, 134, 91340, 341, 0203, 0, 1234567890};
1306 	char *str_fmt[] = {
1307 		"%10.5s",
1308 		"%-10.5s",
1309 		"%5.10s",
1310 		"%-5.10s",
1311 		"%10.1s",
1312 		"%0.10s",
1313 		"%10.0s",
1314 		"%1.10s",
1315 		"%s",
1316 		"%.1s",
1317 		"%.10s",
1318 		"%10s",
1319 		NULL
1320 	};
1321 	char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
1322 	int x, y;
1323 	int fail = 0;
1324 	int num = 0;
1325 	int l1, l2;
1326 
1327 	printf ("Testing snprintf format codes against system sprintf...\n");
1328 
1329 	for (x = 0; fp_fmt[x] ; x++) {
1330 		for (y = 0; fp_nums[y] != 0 ; y++) {
1331 			buf1[0] = buf2[0] = '\0';
1332 			l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
1333 			l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
1334 			sprintf (buf2, fp_fmt[x], fp_nums[y]);
1335 			buf1[1023] = buf1[1023] = '\0';
1336 			if (strcmp (buf1, buf2) || (l1 != l2)) {
1337 				printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1338 				       fp_fmt[x], l1, buf1, l2, buf2);
1339 				fail++;
1340 			}
1341 			num++;
1342 		}
1343 	}
1344 
1345 	for (x = 0; int_fmt[x] ; x++) {
1346 		for (y = 0; int_nums[y] != 0 ; y++) {
1347 			buf1[0] = buf2[0] = '\0';
1348 			l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
1349 			l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
1350 			sprintf (buf2, int_fmt[x], int_nums[y]);
1351 			buf1[1023] = buf1[1023] = '\0';
1352 			if (strcmp (buf1, buf2) || (l1 != l2)) {
1353 				printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1354 				       int_fmt[x], l1, buf1, l2, buf2);
1355 				fail++;
1356 			}
1357 			num++;
1358 		}
1359 	}
1360 
1361 	for (x = 0; str_fmt[x] ; x++) {
1362 		for (y = 0; str_vals[y] != 0 ; y++) {
1363 			buf1[0] = buf2[0] = '\0';
1364 			l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
1365 			l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
1366 			sprintf (buf2, str_fmt[x], str_vals[y]);
1367 			buf1[1023] = buf1[1023] = '\0';
1368 			if (strcmp (buf1, buf2) || (l1 != l2)) {
1369 				printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1370 				       str_fmt[x], l1, buf1, l2, buf2);
1371 				fail++;
1372 			}
1373 			num++;
1374 		}
1375 	}
1376 
1377 #define BUFSZ 2048
1378 
1379 	buf1[0] = buf2[0] = '\0';
1380 	if ((buf3 = malloc(BUFSZ)) == NULL) {
1381 		fail++;
1382 	} else {
1383 		num++;
1384 		memset(buf3, 'a', BUFSZ);
1385 		snprintf(buf1, sizeof(buf1), "%.*s", 1, buf3);
1386 		buf1[1023] = '\0';
1387 		if (strcmp(buf1, "a") != 0) {
1388 			printf("length limit buf1 '%s' expected 'a'\n", buf1);
1389 			fail++;
1390 		}
1391         }
1392 
1393 	buf1[0] = buf2[0] = '\0';
1394 	l1 = snprintf(buf1, sizeof(buf1), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
1395 	l2 = sprintf(buf2, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
1396 	buf1[1023] = buf1[1023] = '\0';
1397 	if (strcmp(buf1, buf2) || (l1 != l2)) {
1398 		printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1399 				"%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
1400 		fail++;
1401 	}
1402 
1403 	buf1[0] = buf2[0] = '\0';
1404 	l1 = snprintf(buf1, sizeof(buf1), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
1405 	l2 = sprintf(buf2, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
1406 	buf1[1023] = buf1[1023] = '\0';
1407 	if (strcmp(buf1, buf2)) {
1408 		printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1409 				"%4$*1$d %2$s %3$*1$.*1$f", l1, buf1, l2, buf2);
1410 		fail++;
1411 	}
1412 #if 0
1413 	buf1[0] = buf2[0] = '\0';
1414 	l1 = snprintf(buf1, sizeof(buf1), "%lld", (LLONG)1234567890);
1415 	l2 = sprintf(buf2, "%lld", (LLONG)1234567890);
1416 	buf1[1023] = buf1[1023] = '\0';
1417 	if (strcmp(buf1, buf2)) {
1418 		printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1419 				"%lld", l1, buf1, l2, buf2);
1420 		fail++;
1421 	}
1422 
1423 	buf1[0] = buf2[0] = '\0';
1424 	l1 = snprintf(buf1, sizeof(buf1), "%Lf", (LDOUBLE)890.1234567890123);
1425 	l2 = sprintf(buf2, "%Lf", (LDOUBLE)890.1234567890123);
1426 	buf1[1023] = buf1[1023] = '\0';
1427 	if (strcmp(buf1, buf2)) {
1428 		printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1429 				"%Lf", l1, buf1, l2, buf2);
1430 		fail++;
1431 	}
1432 #endif
1433 	printf ("%d tests failed out of %d.\n", fail, num);
1434 
1435 	printf("seeing how many digits we support\n");
1436 	{
1437 		double v0 = 0.12345678901234567890123456789012345678901;
1438 		for (x=0; x<100; x++) {
1439 			double p = pow(10, x);
1440 			double r = v0*p;
1441 			snprintf(buf1, sizeof(buf1), "%1.1f", r);
1442 			sprintf(buf2,                "%1.1f", r);
1443 			if (strcmp(buf1, buf2)) {
1444 				printf("we seem to support %d digits\n", x-1);
1445 				break;
1446 			}
1447 		}
1448 	}
1449 
1450 	return 0;
1451 }
1452 #endif /* TEST_SNPRINTF */
1453