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 * 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
94#ifndef NO_CONFIG_H
95#include "config.h"
96#else
97#define NULL 0
98#endif
99
100#ifdef TEST_SNPRINTF /* need math library headers for testing */
101
102/* In test mode, we pretend that this system doesn't have any snprintf
103 * functions, regardless of what config.h says. */
104#  undef HAVE_SNPRINTF
105#  undef HAVE_VSNPRINTF
106#  undef HAVE_C99_VSNPRINTF
107#  undef HAVE_ASPRINTF
108#  undef HAVE_VASPRINTF
109#  include <math.h>
110#endif /* TEST_SNPRINTF */
111
112#ifdef HAVE_STRING_H
113#include <string.h>
114#endif
115
116#ifdef HAVE_STRINGS_H
117#include <strings.h>
118#endif
119#ifdef HAVE_CTYPE_H
120#include <ctype.h>
121#endif
122#include <sys/types.h>
123#include <stdarg.h>
124#ifdef HAVE_STDLIB_H
125#include <stdlib.h>
126#endif
127
128#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
129/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
130#include <stdio.h>
131 /* make the compiler happy with an empty file */
132 void dummy_snprintf(void);
133 void dummy_snprintf(void) {}
134#endif /* HAVE_SNPRINTF, etc */
135
136#ifdef HAVE_LONG_DOUBLE
137#define LDOUBLE long double
138#else
139#define LDOUBLE double
140#endif
141
142#if SIZEOF_LONG_LONG
143#define LLONG long long
144#else
145#define LLONG long
146#endif
147
148#ifndef VA_COPY
149#if defined HAVE_VA_COPY || defined va_copy
150#define VA_COPY(dest, src) va_copy(dest, src)
151#else
152#ifdef HAVE___VA_COPY
153#define VA_COPY(dest, src) __va_copy(dest, src)
154#else
155#define VA_COPY(dest, src) (dest) = (src)
156#endif
157#endif
158
159/*
160 * dopr(): poor man's version of doprintf
161 */
162
163/* format read states */
164#define DP_S_DEFAULT 0
165#define DP_S_FLAGS   1
166#define DP_S_MIN     2
167#define DP_S_DOT     3
168#define DP_S_MAX     4
169#define DP_S_MOD     5
170#define DP_S_CONV    6
171#define DP_S_DONE    7
172
173/* format flags - Bits */
174#define DP_F_MINUS 	(1 << 0)
175#define DP_F_PLUS  	(1 << 1)
176#define DP_F_SPACE 	(1 << 2)
177#define DP_F_NUM   	(1 << 3)
178#define DP_F_ZERO  	(1 << 4)
179#define DP_F_UP    	(1 << 5)
180#define DP_F_UNSIGNED 	(1 << 6)
181
182/* Conversion Flags */
183#define DP_C_SHORT   1
184#define DP_C_LONG    2
185#define DP_C_LDOUBLE 3
186#define DP_C_LLONG   4
187
188#define char_to_int(p) ((p)- '0')
189#ifndef MAX
190#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
191#endif
192
193/* yes this really must be a ||. Don't muck with this (tridge) */
194#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
195
196static size_t dopr(char *buffer, size_t maxlen, const char *format,
197		   va_list args_in);
198static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
199		    char *value, int flags, int min, int max);
200static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
201		    long value, int base, int min, int max, int flags);
202static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
203		   LDOUBLE fvalue, int min, int max, int flags);
204static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
205
206static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
207{
208	char ch;
209	LLONG value;
210	LDOUBLE fvalue;
211	char *strvalue;
212	int min;
213	int max;
214	int state;
215	int flags;
216	int cflags;
217	size_t currlen;
218	va_list args;
219
220	VA_COPY(args, args_in);
221
222	state = DP_S_DEFAULT;
223	currlen = flags = cflags = min = 0;
224	max = -1;
225	ch = *format++;
226
227	while (state != DP_S_DONE) {
228		if (ch == '\0')
229			state = DP_S_DONE;
230
231		switch(state) {
232		case DP_S_DEFAULT:
233			if (ch == '%')
234				state = DP_S_FLAGS;
235			else
236				dopr_outch (buffer, &currlen, maxlen, ch);
237			ch = *format++;
238			break;
239		case DP_S_FLAGS:
240			switch (ch) {
241			case '-':
242				flags |= DP_F_MINUS;
243				ch = *format++;
244				break;
245			case '+':
246				flags |= DP_F_PLUS;
247				ch = *format++;
248				break;
249			case ' ':
250				flags |= DP_F_SPACE;
251				ch = *format++;
252				break;
253			case '#':
254				flags |= DP_F_NUM;
255				ch = *format++;
256				break;
257			case '0':
258				flags |= DP_F_ZERO;
259				ch = *format++;
260				break;
261			default:
262				state = DP_S_MIN;
263				break;
264			}
265			break;
266		case DP_S_MIN:
267			if (isdigit((unsigned char)ch)) {
268				min = 10*min + char_to_int (ch);
269				ch = *format++;
270			} else if (ch == '*') {
271				min = va_arg (args, int);
272				ch = *format++;
273				state = DP_S_DOT;
274			} else {
275				state = DP_S_DOT;
276			}
277			break;
278		case DP_S_DOT:
279			if (ch == '.') {
280				state = DP_S_MAX;
281				ch = *format++;
282			} else {
283				state = DP_S_MOD;
284			}
285			break;
286		case DP_S_MAX:
287			if (isdigit((unsigned char)ch)) {
288				if (max < 0)
289					max = 0;
290				max = 10*max + char_to_int (ch);
291				ch = *format++;
292			} else if (ch == '*') {
293				max = va_arg (args, int);
294				ch = *format++;
295				state = DP_S_MOD;
296			} else {
297				state = DP_S_MOD;
298			}
299			break;
300		case DP_S_MOD:
301			switch (ch) {
302			case 'h':
303				cflags = DP_C_SHORT;
304				ch = *format++;
305				break;
306			case 'l':
307				cflags = DP_C_LONG;
308				ch = *format++;
309				if (ch == 'l') {	/* It's a long long */
310					cflags = DP_C_LLONG;
311					ch = *format++;
312				}
313				break;
314			case 'L':
315				cflags = DP_C_LDOUBLE;
316				ch = *format++;
317				break;
318			default:
319				break;
320			}
321			state = DP_S_CONV;
322			break;
323		case DP_S_CONV:
324			switch (ch) {
325			case 'd':
326			case 'i':
327				if (cflags == DP_C_SHORT)
328					value = va_arg (args, int);
329				else if (cflags == DP_C_LONG)
330					value = va_arg (args, long int);
331				else if (cflags == DP_C_LLONG)
332					value = va_arg (args, LLONG);
333				else
334					value = va_arg (args, int);
335				fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
336				break;
337			case 'o':
338				flags |= DP_F_UNSIGNED;
339				if (cflags == DP_C_SHORT)
340					value = va_arg (args, unsigned int);
341				else if (cflags == DP_C_LONG)
342					value = (long)va_arg (args, unsigned long int);
343				else if (cflags == DP_C_LLONG)
344					value = (long)va_arg (args, unsigned LLONG);
345				else
346					value = (long)va_arg (args, unsigned int);
347				fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
348				break;
349			case 'u':
350				flags |= DP_F_UNSIGNED;
351				if (cflags == DP_C_SHORT)
352					value = va_arg (args, unsigned int);
353				else if (cflags == DP_C_LONG)
354					value = (long)va_arg (args, unsigned long int);
355				else if (cflags == DP_C_LLONG)
356					value = (LLONG)va_arg (args, unsigned LLONG);
357				else
358					value = (long)va_arg (args, unsigned int);
359				fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
360				break;
361			case 'X':
362				flags |= DP_F_UP;
363			case 'x':
364				flags |= DP_F_UNSIGNED;
365				if (cflags == DP_C_SHORT)
366					value = va_arg (args, unsigned int);
367				else if (cflags == DP_C_LONG)
368					value = (long)va_arg (args, unsigned long int);
369				else if (cflags == DP_C_LLONG)
370					value = (LLONG)va_arg (args, unsigned LLONG);
371				else
372					value = (long)va_arg (args, unsigned int);
373				fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
374				break;
375			case 'f':
376				if (cflags == DP_C_LDOUBLE)
377					fvalue = va_arg (args, LDOUBLE);
378				else
379					fvalue = va_arg (args, double);
380				/* um, floating point? */
381				fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
382				break;
383			case 'E':
384				flags |= DP_F_UP;
385			case 'e':
386				if (cflags == DP_C_LDOUBLE)
387					fvalue = va_arg (args, LDOUBLE);
388				else
389					fvalue = va_arg (args, double);
390				fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
391				break;
392			case 'G':
393				flags |= DP_F_UP;
394			case 'g':
395				if (cflags == DP_C_LDOUBLE)
396					fvalue = va_arg (args, LDOUBLE);
397				else
398					fvalue = va_arg (args, double);
399				fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
400				break;
401			case 'c':
402				dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
403				break;
404			case 's':
405				strvalue = va_arg (args, char *);
406				if (!strvalue) strvalue = "(NULL)";
407				if (max == -1) {
408					max = strlen(strvalue);
409				}
410				if (min > 0 && max >= 0 && min > max) max = min;
411				fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
412				break;
413			case 'p':
414				strvalue = va_arg (args, void *);
415				fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
416				break;
417			case 'n':
418				if (cflags == DP_C_SHORT) {
419					short int *num;
420					num = va_arg (args, short int *);
421					*num = currlen;
422				} else if (cflags == DP_C_LONG) {
423					long int *num;
424					num = va_arg (args, long int *);
425					*num = (long int)currlen;
426				} else if (cflags == DP_C_LLONG) {
427					LLONG *num;
428					num = va_arg (args, LLONG *);
429					*num = (LLONG)currlen;
430				} else {
431					int *num;
432					num = va_arg (args, int *);
433					*num = currlen;
434				}
435				break;
436			case '%':
437				dopr_outch (buffer, &currlen, maxlen, ch);
438				break;
439			case 'w':
440				/* not supported yet, treat as next char */
441				ch = *format++;
442				break;
443			default:
444				/* Unknown, skip */
445				break;
446			}
447			ch = *format++;
448			state = DP_S_DEFAULT;
449			flags = cflags = min = 0;
450			max = -1;
451			break;
452		case DP_S_DONE:
453			break;
454		default:
455			/* hmm? */
456			break; /* some picky compilers need this */
457		}
458	}
459	if (maxlen != 0) {
460		if (currlen < maxlen - 1)
461			buffer[currlen] = '\0';
462		else if (maxlen > 0)
463			buffer[maxlen - 1] = '\0';
464	}
465
466	return currlen;
467}
468
469static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
470		    char *value, int flags, int min, int max)
471{
472	int padlen, strln;     /* amount to pad */
473	int cnt = 0;
474
475#ifdef DEBUG_SNPRINTF
476	printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
477#endif
478	if (value == 0) {
479		value = "<NULL>";
480	}
481
482	for (strln = 0; value[strln]; ++strln); /* strlen */
483	padlen = min - strln;
484	if (padlen < 0)
485		padlen = 0;
486	if (flags & DP_F_MINUS)
487		padlen = -padlen; /* Left Justify */
488
489	while ((padlen > 0) && (cnt < max)) {
490		dopr_outch (buffer, currlen, maxlen, ' ');
491		--padlen;
492		++cnt;
493	}
494	while (*value && (cnt < max)) {
495		dopr_outch (buffer, currlen, maxlen, *value++);
496		++cnt;
497	}
498	while ((padlen < 0) && (cnt < max)) {
499		dopr_outch (buffer, currlen, maxlen, ' ');
500		++padlen;
501		++cnt;
502	}
503}
504
505/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
506
507static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
508		    long value, int base, int min, int max, int flags)
509{
510	int signvalue = 0;
511	unsigned long uvalue;
512	char convert[20];
513	int place = 0;
514	int spadlen = 0; /* amount to space pad */
515	int zpadlen = 0; /* amount to zero pad */
516	int caps = 0;
517
518	if (max < 0)
519		max = 0;
520
521	uvalue = value;
522
523	if(!(flags & DP_F_UNSIGNED)) {
524		if( value < 0 ) {
525			signvalue = '-';
526			uvalue = -value;
527		} else {
528			if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
529				signvalue = '+';
530			else if (flags & DP_F_SPACE)
531				signvalue = ' ';
532		}
533	}
534
535	if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
536
537	do {
538		convert[place++] =
539			(caps? "0123456789ABCDEF":"0123456789abcdef")
540			[uvalue % (unsigned)base  ];
541		uvalue = (uvalue / (unsigned)base );
542	} while(uvalue && (place < 20));
543	if (place == 20) place--;
544	convert[place] = 0;
545
546	zpadlen = max - place;
547	spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
548	if (zpadlen < 0) zpadlen = 0;
549	if (spadlen < 0) spadlen = 0;
550	if (flags & DP_F_ZERO) {
551		zpadlen = MAX(zpadlen, spadlen);
552		spadlen = 0;
553	}
554	if (flags & DP_F_MINUS)
555		spadlen = -spadlen; /* Left Justifty */
556
557#ifdef DEBUG_SNPRINTF
558	printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
559	       zpadlen, spadlen, min, max, place);
560#endif
561
562	/* Spaces */
563	while (spadlen > 0) {
564		dopr_outch (buffer, currlen, maxlen, ' ');
565		--spadlen;
566	}
567
568	/* Sign */
569	if (signvalue)
570		dopr_outch (buffer, currlen, maxlen, signvalue);
571
572	/* Zeros */
573	if (zpadlen > 0) {
574		while (zpadlen > 0) {
575			dopr_outch (buffer, currlen, maxlen, '0');
576			--zpadlen;
577		}
578	}
579
580	/* Digits */
581	while (place > 0)
582		dopr_outch (buffer, currlen, maxlen, convert[--place]);
583
584	/* Left Justified spaces */
585	while (spadlen < 0) {
586		dopr_outch (buffer, currlen, maxlen, ' ');
587		++spadlen;
588	}
589}
590
591static LDOUBLE abs_val(LDOUBLE value)
592{
593	LDOUBLE result = value;
594
595	if (value < 0)
596		result = -value;
597
598	return result;
599}
600
601static LDOUBLE POW10(int exp)
602{
603	LDOUBLE result = 1;
604
605	while (exp) {
606		result *= 10;
607		exp--;
608	}
609
610	return result;
611}
612
613static LLONG ROUND(LDOUBLE value)
614{
615	LLONG intpart;
616
617	intpart = (LLONG)value;
618	value = value - intpart;
619	if (value >= 0.5) intpart++;
620
621	return intpart;
622}
623
624/* a replacement for modf that doesn't need the math library. Should
625   be portable, but slow */
626static double my_modf(double x0, double *iptr)
627{
628	int i;
629	long l;
630	double x = x0;
631	double f = 1.0;
632
633	for (i=0;i<100;i++) {
634		l = (long)x;
635		if (l <= (x+1) && l >= (x-1)) {
636			if (i != 0) {
637				double i2;
638				double ret;
639
640				ret = my_modf(x0-l*f, &i2);
641				(*iptr) = l*f + i2;
642				return ret;
643			}
644
645			(*iptr) = l;
646			return x - (*iptr);
647		}
648		x *= 0.1;
649		f *= 10.0;
650	}
651
652	/* yikes! the number is beyond what we can handle. What do we do? */
653	(*iptr) = 0;
654	return 0;
655}
656
657
658static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
659		   LDOUBLE fvalue, int min, int max, int flags)
660{
661	int signvalue = 0;
662	double ufvalue;
663	char iconvert[311];
664	char fconvert[311];
665	int iplace = 0;
666	int fplace = 0;
667	int padlen = 0; /* amount to pad */
668	int zpadlen = 0;
669	int caps = 0;
670	int idx;
671	double intpart;
672	double fracpart;
673	double temp;
674
675	/*
676	 * AIX manpage says the default is 0, but Solaris says the default
677	 * is 6, and sprintf on AIX defaults to 6
678	 */
679	if (max < 0)
680		max = 6;
681
682	ufvalue = abs_val (fvalue);
683
684	if (fvalue < 0) {
685		signvalue = '-';
686	} else {
687		if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
688			signvalue = '+';
689		} else {
690			if (flags & DP_F_SPACE)
691				signvalue = ' ';
692		}
693	}
694
695#if 0
696	if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
697#endif
698
699#if 0
700	 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
701#endif
702
703	/*
704	 * Sorry, we only support 16 digits past the decimal because of our
705	 * conversion method
706	 */
707	if (max > 16)
708		max = 16;
709
710	/* We "cheat" by converting the fractional part to integer by
711	 * multiplying by a factor of 10
712	 */
713
714	temp = ufvalue;
715	my_modf(temp, &intpart);
716
717	fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
718
719	if (fracpart >= POW10(max)) {
720		intpart++;
721		fracpart -= POW10(max);
722	}
723
724
725	/* Convert integer part */
726	do {
727		temp = intpart*0.1;
728		my_modf(temp, &intpart);
729		idx = (int) ((temp -intpart +0.05)* 10.0);
730		/* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
731		/* printf ("%llf, %f, %x\n", temp, intpart, idx); */
732		iconvert[iplace++] =
733			(caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
734	} while (intpart && (iplace < 311));
735	if (iplace == 311) iplace--;
736	iconvert[iplace] = 0;
737
738	/* Convert fractional part */
739	if (fracpart)
740	{
741		do {
742			temp = fracpart*0.1;
743			my_modf(temp, &fracpart);
744			idx = (int) ((temp -fracpart +0.05)* 10.0);
745			/* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
746			/* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
747			fconvert[fplace++] =
748			(caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
749		} while(fracpart && (fplace < 311));
750		if (fplace == 311) fplace--;
751	}
752	fconvert[fplace] = 0;
753
754	/* -1 for decimal point, another -1 if we are printing a sign */
755	padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
756	zpadlen = max - fplace;
757	if (zpadlen < 0) zpadlen = 0;
758	if (padlen < 0)
759		padlen = 0;
760	if (flags & DP_F_MINUS)
761		padlen = -padlen; /* Left Justifty */
762
763	if ((flags & DP_F_ZERO) && (padlen > 0)) {
764		if (signvalue) {
765			dopr_outch (buffer, currlen, maxlen, signvalue);
766			--padlen;
767			signvalue = 0;
768		}
769		while (padlen > 0) {
770			dopr_outch (buffer, currlen, maxlen, '0');
771			--padlen;
772		}
773	}
774	while (padlen > 0) {
775		dopr_outch (buffer, currlen, maxlen, ' ');
776		--padlen;
777	}
778	if (signvalue)
779		dopr_outch (buffer, currlen, maxlen, signvalue);
780
781	while (iplace > 0)
782		dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
783
784#ifdef DEBUG_SNPRINTF
785	printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
786#endif
787
788	/*
789	 * Decimal point.  This should probably use locale to find the correct
790	 * char to print out.
791	 */
792	if (max > 0) {
793		dopr_outch (buffer, currlen, maxlen, '.');
794
795		while (zpadlen > 0) {
796			dopr_outch (buffer, currlen, maxlen, '0');
797			--zpadlen;
798		}
799
800		while (fplace > 0)
801			dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
802	}
803
804	while (padlen < 0) {
805		dopr_outch (buffer, currlen, maxlen, ' ');
806		++padlen;
807	}
808}
809
810static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
811{
812	if (*currlen < maxlen) {
813		buffer[(*currlen)] = c;
814	}
815	(*currlen)++;
816}
817
818 int rsync_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
819{
820	return dopr(str, count, fmt, args);
821}
822#define vsnprintf rsync_vsnprintf
823#endif
824
825/* yes this really must be a ||. Don't muck with this (tridge)
826 *
827 * The logic for these two is that we need our own definition if the
828 * OS *either* has no definition of *sprintf, or if it does have one
829 * that doesn't work properly according to the autoconf test.
830 */
831#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
832int rsync_snprintf(char *str,size_t count,const char *fmt,...)
833{
834	size_t ret;
835	va_list ap;
836
837	va_start(ap, fmt);
838	ret = vsnprintf(str, count, fmt, ap);
839	va_end(ap);
840	return ret;
841}
842#define snprintf rsync_snprintf
843#endif
844
845#endif
846
847#ifndef HAVE_VASPRINTF
848 int vasprintf(char **ptr, const char *format, va_list ap)
849{
850	int ret;
851	va_list ap2;
852
853	VA_COPY(ap2, ap);
854
855	ret = vsnprintf(NULL, 0, format, ap2);
856	if (ret <= 0) return ret;
857
858	(*ptr) = (char *)malloc(ret+1);
859	if (!*ptr) return -1;
860
861	VA_COPY(ap2, ap);
862
863	ret = vsnprintf(*ptr, ret+1, format, ap2);
864
865	return ret;
866}
867#endif
868
869
870#ifndef HAVE_ASPRINTF
871 int asprintf(char **ptr, const char *format, ...)
872{
873	va_list ap;
874	int ret;
875
876	*ptr = NULL;
877	va_start(ap, format);
878	ret = vasprintf(ptr, format, ap);
879	va_end(ap);
880
881	return ret;
882}
883#endif
884
885#ifdef TEST_SNPRINTF
886
887 int sprintf(char *str,const char *fmt,...);
888
889 int main (void)
890{
891	char buf1[1024];
892	char buf2[1024];
893	char *fp_fmt[] = {
894		"%1.1f",
895		"%-1.5f",
896		"%1.5f",
897		"%123.9f",
898		"%10.5f",
899		"% 10.5f",
900		"%+22.9f",
901		"%+4.9f",
902		"%01.3f",
903		"%4f",
904		"%3.1f",
905		"%3.2f",
906		"%.0f",
907		"%f",
908		"-16.16f",
909		NULL
910	};
911	double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
912			     0.9996, 1.996, 4.136, 5.030201, 0.00205,
913			     /* END LIST */ 0};
914	char *int_fmt[] = {
915		"%-1.5d",
916		"%1.5d",
917		"%123.9d",
918		"%5.5d",
919		"%10.5d",
920		"% 10.5d",
921		"%+22.33d",
922		"%01.3d",
923		"%4d",
924		"%d",
925		NULL
926	};
927	long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
928	char *str_fmt[] = {
929		"10.5s",
930		"5.10s",
931		"10.1s",
932		"0.10s",
933		"10.0s",
934		"1.10s",
935		"%s",
936		"%.1s",
937		"%.10s",
938		"%10s",
939		NULL
940	};
941	char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
942	int x, y;
943	int fail = 0;
944	int num = 0;
945
946	printf ("Testing snprintf format codes against system sprintf...\n");
947
948	for (x = 0; fp_fmt[x] ; x++) {
949		for (y = 0; fp_nums[y] != 0 ; y++) {
950			int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
951			int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
952			sprintf (buf2, fp_fmt[x], fp_nums[y]);
953			if (strcmp (buf1, buf2)) {
954				printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
955				       fp_fmt[x], buf1, buf2);
956				fail++;
957			}
958			if (l1 != l2) {
959				printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
960				fail++;
961			}
962			num++;
963		}
964	}
965
966	for (x = 0; int_fmt[x] ; x++) {
967		for (y = 0; int_nums[y] != 0 ; y++) {
968			int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
969			int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
970			sprintf (buf2, int_fmt[x], int_nums[y]);
971			if (strcmp (buf1, buf2)) {
972				printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
973				       int_fmt[x], buf1, buf2);
974				fail++;
975			}
976			if (l1 != l2) {
977				printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
978				fail++;
979			}
980			num++;
981		}
982	}
983
984	for (x = 0; str_fmt[x] ; x++) {
985		for (y = 0; str_vals[y] != 0 ; y++) {
986			int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
987			int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
988			sprintf (buf2, str_fmt[x], str_vals[y]);
989			if (strcmp (buf1, buf2)) {
990				printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
991				       str_fmt[x], buf1, buf2);
992				fail++;
993			}
994			if (l1 != l2) {
995				printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
996				fail++;
997			}
998			num++;
999		}
1000	}
1001
1002	printf ("%d tests failed out of %d.\n", fail, num);
1003
1004	printf("seeing how many digits we support\n");
1005	{
1006		double v0 = 0.12345678901234567890123456789012345678901;
1007		for (x=0; x<100; x++) {
1008			double p = pow(10, x);
1009			double r = v0*p;
1010			snprintf(buf1, sizeof(buf1), "%1.1f", r);
1011			sprintf(buf2,                "%1.1f", r);
1012			if (strcmp(buf1, buf2)) {
1013				printf("we seem to support %d digits\n", x-1);
1014				break;
1015			}
1016		}
1017	}
1018
1019	return 0;
1020}
1021#endif /* TEST_SNPRINTF */
1022