bsd-snprintf.c revision 157016
1113908Sdes/*
2113908Sdes * Copyright Patrick Powell 1995
3113908Sdes * This code is based on code written by Patrick Powell (papowell@astart.com)
4113908Sdes * It may be used for any purpose as long as this notice remains intact
5113908Sdes * on all source code distributions
6113908Sdes */
7113908Sdes
898937Sdes/**************************************************************
998937Sdes * Original:
1098937Sdes * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
1198937Sdes * A bombproof version of doprnt (dopr) included.
1298937Sdes * Sigh.  This sort of thing is always nasty do deal with.  Note that
1398937Sdes * the version here does not include floating point...
1498937Sdes *
1598937Sdes * snprintf() is used instead of sprintf() as it does limit checks
1698937Sdes * for string length.  This covers a nasty loophole.
1798937Sdes *
1898937Sdes * The other functions are there to prevent NULL pointers from
1998937Sdes * causing nast effects.
2098937Sdes *
2198937Sdes * More Recently:
2298937Sdes *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
2398937Sdes *  This was ugly.  It is still ugly.  I opted out of floating point
2498937Sdes *  numbers, but the formatter understands just about everything
2598937Sdes *  from the normal C string format, at least as far as I can tell from
2698937Sdes *  the Solaris 2.5 printf(3S) man page.
2798937Sdes *
2898937Sdes *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
2998937Sdes *    Ok, added some minimal floating point support, which means this
3098937Sdes *    probably requires libm on most operating systems.  Don't yet
3198937Sdes *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
3298937Sdes *    was pretty badly broken, it just wasn't being exercised in ways
3398937Sdes *    which showed it, so that's been fixed.  Also, formated the code
3498937Sdes *    to mutt conventions, and removed dead code left over from the
3598937Sdes *    original.  Also, there is now a builtin-test, just compile with:
3698937Sdes *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
3798937Sdes *    and run snprintf for results.
3898937Sdes *
3998937Sdes *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
4098937Sdes *    The PGP code was using unsigned hexadecimal formats.
4198937Sdes *    Unfortunately, unsigned formats simply didn't work.
4298937Sdes *
4398937Sdes *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
4498937Sdes *    The original code assumed that both snprintf() and vsnprintf() were
4598937Sdes *    missing.  Some systems only have snprintf() but not vsnprintf(), so
4698937Sdes *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
4798937Sdes *
48157016Sdes *  Andrew Tridgell (tridge@samba.org) Oct 1998
49157016Sdes *    fixed handling of %.0f
50157016Sdes *    added test for HAVE_LONG_DOUBLE
5198937Sdes *
52157016Sdes * tridge@samba.org, idra@samba.org, April 2001
53157016Sdes *    got rid of fcvt code (twas buggy and made testing harder)
54157016Sdes *    added C99 semantics
55157016Sdes *
56157016Sdes * date: 2002/12/19 19:56:31;  author: herb;  state: Exp;  lines: +2 -0
57157016Sdes * actually print args for %g and %e
58157016Sdes *
59157016Sdes * date: 2002/06/03 13:37:52;  author: jmcd;  state: Exp;  lines: +8 -0
60157016Sdes * Since includes.h isn't included here, VA_COPY has to be defined here.  I don't
61157016Sdes * see any include file that is guaranteed to be here, so I'm defining it
62157016Sdes * locally.  Fixes AIX and Solaris builds.
63157016Sdes *
64157016Sdes * date: 2002/06/03 03:07:24;  author: tridge;  state: Exp;  lines: +5 -13
65157016Sdes * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
66157016Sdes * functions
67157016Sdes *
68157016Sdes * date: 2002/05/17 14:51:22;  author: jmcd;  state: Exp;  lines: +21 -4
69157016Sdes * Fix usage of va_list passed as an arg.  Use __va_copy before using it
70157016Sdes * when it exists.
71157016Sdes *
72157016Sdes * date: 2002/04/16 22:38:04;  author: idra;  state: Exp;  lines: +20 -14
73157016Sdes * Fix incorrect zpadlen handling in fmtfp.
74157016Sdes * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
75157016Sdes * few mods to make it easier to compile the tests.
76157016Sdes * addedd the "Ollie" test to the floating point ones.
77157016Sdes *
78157016Sdes * Martin Pool (mbp@samba.org) April 2003
79157016Sdes *    Remove NO_CONFIG_H so that the test case can be built within a source
80157016Sdes *    tree with less trouble.
81157016Sdes *    Remove unnecessary SAFE_FREE() definition.
82157016Sdes *
83157016Sdes * Martin Pool (mbp@samba.org) May 2003
84157016Sdes *    Put in a prototype for dummy_snprintf() to quiet compiler warnings.
85157016Sdes *
86157016Sdes *    Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
87157016Sdes *    if the C library has some snprintf functions already.
8898937Sdes **************************************************************/
8998937Sdes
9098937Sdes#include "includes.h"
9198937Sdes
92157016SdesRCSID("$Id: bsd-snprintf.c,v 1.11 2005/12/17 11:32:04 dtucker Exp $");
9398937Sdes
9498937Sdes#if defined(BROKEN_SNPRINTF)		/* For those with broken snprintf() */
9598937Sdes# undef HAVE_SNPRINTF
9698937Sdes# undef HAVE_VSNPRINTF
9798937Sdes#endif
9898937Sdes
99157016Sdes#ifndef VA_COPY
100157016Sdes# ifdef HAVE_VA_COPY
101157016Sdes#  define VA_COPY(dest, src) va_copy(dest, src)
102157016Sdes# else
103157016Sdes#  ifdef HAVE___VA_COPY
104157016Sdes#   define VA_COPY(dest, src) __va_copy(dest, src)
105157016Sdes#  else
106157016Sdes#   define VA_COPY(dest, src) (dest) = (src)
107157016Sdes#  endif
108157016Sdes# endif
109157016Sdes#endif
110157016Sdes
11198937Sdes#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
11298937Sdes
113157016Sdes#ifdef HAVE_LONG_DOUBLE
114157016Sdes# define LDOUBLE long double
115157016Sdes#else
116157016Sdes# define LDOUBLE double
117157016Sdes#endif
11898937Sdes
119157016Sdes#ifdef HAVE_LONG_LONG
120157016Sdes# define LLONG long long
121157016Sdes#else
122157016Sdes# define LLONG long
123157016Sdes#endif
12498937Sdes
12598937Sdes/*
12698937Sdes * dopr(): poor man's version of doprintf
12798937Sdes */
12898937Sdes
12998937Sdes/* format read states */
13098937Sdes#define DP_S_DEFAULT 0
13198937Sdes#define DP_S_FLAGS   1
13298937Sdes#define DP_S_MIN     2
13398937Sdes#define DP_S_DOT     3
13498937Sdes#define DP_S_MAX     4
13598937Sdes#define DP_S_MOD     5
13698937Sdes#define DP_S_CONV    6
13798937Sdes#define DP_S_DONE    7
13898937Sdes
13998937Sdes/* format flags - Bits */
14098937Sdes#define DP_F_MINUS 	(1 << 0)
14198937Sdes#define DP_F_PLUS  	(1 << 1)
14298937Sdes#define DP_F_SPACE 	(1 << 2)
14398937Sdes#define DP_F_NUM   	(1 << 3)
14498937Sdes#define DP_F_ZERO  	(1 << 4)
14598937Sdes#define DP_F_UP    	(1 << 5)
14698937Sdes#define DP_F_UNSIGNED 	(1 << 6)
14798937Sdes
14898937Sdes/* Conversion Flags */
149157016Sdes#define DP_C_SHORT   1
150157016Sdes#define DP_C_LONG    2
151157016Sdes#define DP_C_LDOUBLE 3
152157016Sdes#define DP_C_LLONG   4
15398937Sdes
154157016Sdes#define char_to_int(p) ((p)- '0')
155157016Sdes#ifndef MAX
156157016Sdes# define MAX(p,q) (((p) >= (q)) ? (p) : (q))
157157016Sdes#endif
15898937Sdes
159157016Sdesstatic size_t dopr(char *buffer, size_t maxlen, const char *format,
160157016Sdes		   va_list args_in);
161157016Sdesstatic void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
162157016Sdes		    char *value, int flags, int min, int max);
163157016Sdesstatic void fmtint(char *buffer, size_t *currlen, size_t maxlen,
164157016Sdes		    long value, int base, int min, int max, int flags);
165157016Sdesstatic void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
166157016Sdes		   LDOUBLE fvalue, int min, int max, int flags);
167157016Sdesstatic void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
16898937Sdes
169157016Sdesstatic size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in)
17098937Sdes{
171157016Sdes	char ch;
172157016Sdes	LLONG value;
173157016Sdes	LDOUBLE fvalue;
174157016Sdes	char *strvalue;
175157016Sdes	int min;
176157016Sdes	int max;
177157016Sdes	int state;
178157016Sdes	int flags;
179157016Sdes	int cflags;
180157016Sdes	size_t currlen;
181157016Sdes	va_list args;
182157016Sdes
183157016Sdes	VA_COPY(args, args_in);
184157016Sdes
185157016Sdes	state = DP_S_DEFAULT;
186157016Sdes	currlen = flags = cflags = min = 0;
187157016Sdes	max = -1;
18898937Sdes	ch = *format++;
189157016Sdes
19098937Sdes	while (state != DP_S_DONE) {
191157016Sdes		if (ch == '\0')
19298937Sdes			state = DP_S_DONE;
19398937Sdes
19498937Sdes		switch(state) {
195124208Sdes		case DP_S_DEFAULT:
196124208Sdes			if (ch == '%')
197124208Sdes				state = DP_S_FLAGS;
198124208Sdes			else
199157016Sdes				dopr_outch (buffer, &currlen, maxlen, ch);
200124208Sdes			ch = *format++;
201124208Sdes			break;
202124208Sdes		case DP_S_FLAGS:
203124208Sdes			switch (ch) {
204124208Sdes			case '-':
205124208Sdes				flags |= DP_F_MINUS;
20698937Sdes				ch = *format++;
20798937Sdes				break;
208124208Sdes			case '+':
209124208Sdes				flags |= DP_F_PLUS;
210124208Sdes				ch = *format++;
21198937Sdes				break;
212124208Sdes			case ' ':
213124208Sdes				flags |= DP_F_SPACE;
214124208Sdes				ch = *format++;
21598937Sdes				break;
216124208Sdes			case '#':
217124208Sdes				flags |= DP_F_NUM;
218124208Sdes				ch = *format++;
21998937Sdes				break;
220124208Sdes			case '0':
221124208Sdes				flags |= DP_F_ZERO;
222124208Sdes				ch = *format++;
223124208Sdes				break;
224124208Sdes			default:
225124208Sdes				state = DP_S_MIN;
226124208Sdes				break;
227124208Sdes			}
228124208Sdes			break;
229124208Sdes		case DP_S_MIN:
230124208Sdes			if (isdigit((unsigned char)ch)) {
231157016Sdes				min = 10*min + char_to_int (ch);
232124208Sdes				ch = *format++;
233124208Sdes			} else if (ch == '*') {
234124208Sdes				min = va_arg (args, int);
235124208Sdes				ch = *format++;
236124208Sdes				state = DP_S_DOT;
237157016Sdes			} else {
238124208Sdes				state = DP_S_DOT;
239157016Sdes			}
240124208Sdes			break;
241124208Sdes		case DP_S_DOT:
242124208Sdes			if (ch == '.') {
243124208Sdes				state = DP_S_MAX;
244124208Sdes				ch = *format++;
245157016Sdes			} else {
246124208Sdes				state = DP_S_MOD;
247157016Sdes			}
248124208Sdes			break;
249124208Sdes		case DP_S_MAX:
250124208Sdes			if (isdigit((unsigned char)ch)) {
251124208Sdes				if (max < 0)
252124208Sdes					max = 0;
253157016Sdes				max = 10*max + char_to_int (ch);
254124208Sdes				ch = *format++;
255124208Sdes			} else if (ch == '*') {
256124208Sdes				max = va_arg (args, int);
257124208Sdes				ch = *format++;
258124208Sdes				state = DP_S_MOD;
259157016Sdes			} else {
260124208Sdes				state = DP_S_MOD;
261157016Sdes			}
262124208Sdes			break;
263124208Sdes		case DP_S_MOD:
264124208Sdes			switch (ch) {
265124208Sdes			case 'h':
266124208Sdes				cflags = DP_C_SHORT;
267124208Sdes				ch = *format++;
268124208Sdes				break;
269124208Sdes			case 'l':
270124208Sdes				cflags = DP_C_LONG;
271124208Sdes				ch = *format++;
272157016Sdes				if (ch == 'l') {	/* It's a long long */
273157016Sdes					cflags = DP_C_LLONG;
27498937Sdes					ch = *format++;
275124208Sdes				}
27698937Sdes				break;
277124208Sdes			case 'L':
278124208Sdes				cflags = DP_C_LDOUBLE;
279124208Sdes				ch = *format++;
280124208Sdes				break;
281124208Sdes			default:
282124208Sdes				break;
283124208Sdes			}
284124208Sdes			state = DP_S_CONV;
285124208Sdes			break;
286124208Sdes		case DP_S_CONV:
287124208Sdes			switch (ch) {
288124208Sdes			case 'd':
289124208Sdes			case 'i':
290124208Sdes				if (cflags == DP_C_SHORT)
291157016Sdes					value = va_arg (args, int);
292124208Sdes				else if (cflags == DP_C_LONG)
293157016Sdes					value = va_arg (args, long int);
294157016Sdes				else if (cflags == DP_C_LLONG)
295157016Sdes					value = va_arg (args, LLONG);
296124208Sdes				else
297124208Sdes					value = va_arg (args, int);
298157016Sdes				fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
299124208Sdes				break;
300124208Sdes			case 'o':
301124208Sdes				flags |= DP_F_UNSIGNED;
302124208Sdes				if (cflags == DP_C_SHORT)
303157016Sdes					value = va_arg (args, unsigned int);
304124208Sdes				else if (cflags == DP_C_LONG)
305157016Sdes					value = (long)va_arg (args, unsigned long int);
306157016Sdes				else if (cflags == DP_C_LLONG)
307157016Sdes					value = (long)va_arg (args, unsigned LLONG);
308124208Sdes				else
309157016Sdes					value = (long)va_arg (args, unsigned int);
310157016Sdes				fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
311124208Sdes				break;
312124208Sdes			case 'u':
313124208Sdes				flags |= DP_F_UNSIGNED;
314124208Sdes				if (cflags == DP_C_SHORT)
315157016Sdes					value = va_arg (args, unsigned int);
316124208Sdes				else if (cflags == DP_C_LONG)
317157016Sdes					value = (long)va_arg (args, unsigned long int);
318157016Sdes				else if (cflags == DP_C_LLONG)
319157016Sdes					value = (LLONG)va_arg (args, unsigned LLONG);
320124208Sdes				else
321157016Sdes					value = (long)va_arg (args, unsigned int);
322124208Sdes				fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
323124208Sdes				break;
324124208Sdes			case 'X':
325124208Sdes				flags |= DP_F_UP;
326124208Sdes			case 'x':
327124208Sdes				flags |= DP_F_UNSIGNED;
328124208Sdes				if (cflags == DP_C_SHORT)
329157016Sdes					value = va_arg (args, unsigned int);
330124208Sdes				else if (cflags == DP_C_LONG)
331157016Sdes					value = (long)va_arg (args, unsigned long int);
332157016Sdes				else if (cflags == DP_C_LLONG)
333157016Sdes					value = (LLONG)va_arg (args, unsigned LLONG);
334124208Sdes				else
335157016Sdes					value = (long)va_arg (args, unsigned int);
336157016Sdes				fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
337124208Sdes				break;
338124208Sdes			case 'f':
339124208Sdes				if (cflags == DP_C_LDOUBLE)
340157016Sdes					fvalue = va_arg (args, LDOUBLE);
341124208Sdes				else
342157016Sdes					fvalue = va_arg (args, double);
343124208Sdes				/* um, floating point? */
344157016Sdes				fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
345124208Sdes				break;
346124208Sdes			case 'E':
347124208Sdes				flags |= DP_F_UP;
348124208Sdes			case 'e':
349124208Sdes				if (cflags == DP_C_LDOUBLE)
350157016Sdes					fvalue = va_arg (args, LDOUBLE);
351124208Sdes				else
352157016Sdes					fvalue = va_arg (args, double);
353157016Sdes				fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
354124208Sdes				break;
355124208Sdes			case 'G':
356124208Sdes				flags |= DP_F_UP;
357124208Sdes			case 'g':
358124208Sdes				if (cflags == DP_C_LDOUBLE)
359157016Sdes					fvalue = va_arg (args, LDOUBLE);
360124208Sdes				else
361157016Sdes					fvalue = va_arg (args, double);
362157016Sdes				fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
363124208Sdes				break;
364124208Sdes			case 'c':
365157016Sdes				dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
366124208Sdes				break;
367124208Sdes			case 's':
368157016Sdes				strvalue = va_arg (args, char *);
369157016Sdes				if (!strvalue) strvalue = "(NULL)";
370157016Sdes				if (max == -1) {
371157016Sdes					max = strlen(strvalue);
372157016Sdes				}
373157016Sdes				if (min > 0 && max >= 0 && min > max) max = min;
374157016Sdes				fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
375124208Sdes				break;
376124208Sdes			case 'p':
377157016Sdes				strvalue = va_arg (args, void *);
378157016Sdes				fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
379124208Sdes				break;
380124208Sdes			case 'n':
381124208Sdes				if (cflags == DP_C_SHORT) {
382124208Sdes					short int *num;
383157016Sdes					num = va_arg (args, short int *);
384124208Sdes					*num = currlen;
385124208Sdes				} else if (cflags == DP_C_LONG) {
386124208Sdes					long int *num;
387157016Sdes					num = va_arg (args, long int *);
388157016Sdes					*num = (long int)currlen;
389157016Sdes				} else if (cflags == DP_C_LLONG) {
390157016Sdes					LLONG *num;
391157016Sdes					num = va_arg (args, LLONG *);
392157016Sdes					*num = (LLONG)currlen;
393124208Sdes				} else {
394124208Sdes					int *num;
395157016Sdes					num = va_arg (args, int *);
396124208Sdes					*num = currlen;
39798937Sdes				}
39898937Sdes				break;
399124208Sdes			case '%':
400157016Sdes				dopr_outch (buffer, &currlen, maxlen, ch);
401124208Sdes				break;
402157016Sdes			case 'w':
403157016Sdes				/* not supported yet, treat as next char */
40498937Sdes				ch = *format++;
40598937Sdes				break;
406157016Sdes			default:
407157016Sdes				/* Unknown, skip */
408157016Sdes				break;
409124208Sdes			}
410124208Sdes			ch = *format++;
411124208Sdes			state = DP_S_DEFAULT;
412124208Sdes			flags = cflags = min = 0;
413124208Sdes			max = -1;
414124208Sdes			break;
415124208Sdes		case DP_S_DONE:
416124208Sdes			break;
417157016Sdes		default:
418157016Sdes			/* hmm? */
419124208Sdes			break; /* some picky compilers need this */
42098937Sdes		}
42198937Sdes	}
422157016Sdes	if (maxlen != 0) {
423157016Sdes		if (currlen < maxlen - 1)
424157016Sdes			buffer[currlen] = '\0';
425157016Sdes		else if (maxlen > 0)
426157016Sdes			buffer[maxlen - 1] = '\0';
427157016Sdes	}
428157016Sdes
429157016Sdes	return currlen;
43098937Sdes}
43198937Sdes
432157016Sdesstatic void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
433157016Sdes		    char *value, int flags, int min, int max)
43498937Sdes{
435157016Sdes	int padlen, strln;     /* amount to pad */
436157016Sdes	int cnt = 0;
437157016Sdes
438157016Sdes#ifdef DEBUG_SNPRINTF
439157016Sdes	printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
440157016Sdes#endif
441157016Sdes	if (value == 0) {
44298937Sdes		value = "<NULL>";
443157016Sdes	}
44498937Sdes
445146998Sdes	for (strln = 0; strln < max && value[strln]; ++strln); /* strlen */
44698937Sdes	padlen = min - strln;
44798937Sdes	if (padlen < 0)
44898937Sdes		padlen = 0;
44998937Sdes	if (flags & DP_F_MINUS)
45098937Sdes		padlen = -padlen; /* Left Justify */
451157016Sdes
45298937Sdes	while ((padlen > 0) && (cnt < max)) {
453157016Sdes		dopr_outch (buffer, currlen, maxlen, ' ');
45498937Sdes		--padlen;
45598937Sdes		++cnt;
45698937Sdes	}
45798937Sdes	while (*value && (cnt < max)) {
458157016Sdes		dopr_outch (buffer, currlen, maxlen, *value++);
45998937Sdes		++cnt;
46098937Sdes	}
46198937Sdes	while ((padlen < 0) && (cnt < max)) {
462157016Sdes		dopr_outch (buffer, currlen, maxlen, ' ');
46398937Sdes		++padlen;
46498937Sdes		++cnt;
46598937Sdes	}
46698937Sdes}
46798937Sdes
46898937Sdes/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
46998937Sdes
470157016Sdesstatic void fmtint(char *buffer, size_t *currlen, size_t maxlen,
471157016Sdes		    long value, int base, int min, int max, int flags)
47298937Sdes{
473157016Sdes	int signvalue = 0;
47498937Sdes	unsigned long uvalue;
47598937Sdes	char convert[20];
476157016Sdes	int place = 0;
47798937Sdes	int spadlen = 0; /* amount to space pad */
47898937Sdes	int zpadlen = 0; /* amount to zero pad */
479157016Sdes	int caps = 0;
480157016Sdes
48198937Sdes	if (max < 0)
48298937Sdes		max = 0;
483157016Sdes
48498937Sdes	uvalue = value;
485157016Sdes
486157016Sdes	if(!(flags & DP_F_UNSIGNED)) {
487157016Sdes		if( value < 0 ) {
48898937Sdes			signvalue = '-';
48998937Sdes			uvalue = -value;
490157016Sdes		} else {
491157016Sdes			if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
492157016Sdes				signvalue = '+';
493157016Sdes			else if (flags & DP_F_SPACE)
494157016Sdes				signvalue = ' ';
495157016Sdes		}
49698937Sdes	}
49798937Sdes
498157016Sdes	if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
499157016Sdes
50098937Sdes	do {
50198937Sdes		convert[place++] =
502157016Sdes			(caps? "0123456789ABCDEF":"0123456789abcdef")
503157016Sdes			[uvalue % (unsigned)base  ];
50498937Sdes		uvalue = (uvalue / (unsigned)base );
505157016Sdes	} while(uvalue && (place < 20));
506157016Sdes	if (place == 20) place--;
50798937Sdes	convert[place] = 0;
50898937Sdes
50998937Sdes	zpadlen = max - place;
51098937Sdes	spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
511157016Sdes	if (zpadlen < 0) zpadlen = 0;
512157016Sdes	if (spadlen < 0) spadlen = 0;
51398937Sdes	if (flags & DP_F_ZERO) {
51498937Sdes		zpadlen = MAX(zpadlen, spadlen);
51598937Sdes		spadlen = 0;
51698937Sdes	}
51798937Sdes	if (flags & DP_F_MINUS)
51898937Sdes		spadlen = -spadlen; /* Left Justifty */
51998937Sdes
520157016Sdes#ifdef DEBUG_SNPRINTF
521157016Sdes	printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
522157016Sdes	       zpadlen, spadlen, min, max, place);
523157016Sdes#endif
524157016Sdes
52598937Sdes	/* Spaces */
52698937Sdes	while (spadlen > 0) {
527157016Sdes		dopr_outch (buffer, currlen, maxlen, ' ');
52898937Sdes		--spadlen;
52998937Sdes	}
53098937Sdes
53198937Sdes	/* Sign */
53298937Sdes	if (signvalue)
533157016Sdes		dopr_outch (buffer, currlen, maxlen, signvalue);
53498937Sdes
53598937Sdes	/* Zeros */
53698937Sdes	if (zpadlen > 0) {
53798937Sdes		while (zpadlen > 0) {
538157016Sdes			dopr_outch (buffer, currlen, maxlen, '0');
53998937Sdes			--zpadlen;
54098937Sdes		}
54198937Sdes	}
54298937Sdes
54398937Sdes	/* Digits */
54498937Sdes	while (place > 0)
545157016Sdes		dopr_outch (buffer, currlen, maxlen, convert[--place]);
54698937Sdes
54798937Sdes	/* Left Justified spaces */
54898937Sdes	while (spadlen < 0) {
54998937Sdes		dopr_outch (buffer, currlen, maxlen, ' ');
55098937Sdes		++spadlen;
55198937Sdes	}
55298937Sdes}
55398937Sdes
554157016Sdesstatic LDOUBLE abs_val(LDOUBLE value)
55598937Sdes{
556157016Sdes	LDOUBLE result = value;
55798937Sdes
558157016Sdes	if (value < 0)
559157016Sdes		result = -value;
560157016Sdes
561157016Sdes	return result;
562157016Sdes}
563157016Sdes
564157016Sdesstatic LDOUBLE POW10(int exp)
565157016Sdes{
566157016Sdes	LDOUBLE result = 1;
567157016Sdes
56898937Sdes	while (exp) {
56998937Sdes		result *= 10;
57098937Sdes		exp--;
57198937Sdes	}
57298937Sdes
57398937Sdes	return result;
57498937Sdes}
57598937Sdes
576157016Sdesstatic LLONG ROUND(LDOUBLE value)
57798937Sdes{
578157016Sdes	LLONG intpart;
57998937Sdes
580157016Sdes	intpart = (LLONG)value;
581157016Sdes	value = value - intpart;
582157016Sdes	if (value >= 0.5) intpart++;
583157016Sdes
58498937Sdes	return intpart;
58598937Sdes}
58698937Sdes
587157016Sdes/* a replacement for modf that doesn't need the math library. Should
588157016Sdes   be portable, but slow */
589157016Sdesstatic double my_modf(double x0, double *iptr)
59098937Sdes{
591157016Sdes	int i;
592157016Sdes	long l;
593157016Sdes	double x = x0;
594157016Sdes	double f = 1.0;
595157016Sdes
596157016Sdes	for (i=0;i<100;i++) {
597157016Sdes		l = (long)x;
598157016Sdes		if (l <= (x+1) && l >= (x-1)) break;
599157016Sdes		x *= 0.1;
600157016Sdes		f *= 10.0;
601157016Sdes	}
602157016Sdes
603157016Sdes	if (i == 100) {
604157016Sdes		/* yikes! the number is beyond what we can handle. What do we do? */
605157016Sdes		(*iptr) = 0;
606157016Sdes		return 0;
607157016Sdes	}
608157016Sdes
609157016Sdes	if (i != 0) {
610157016Sdes		double i2;
611157016Sdes		double ret;
612157016Sdes
613157016Sdes		ret = my_modf(x0-l*f, &i2);
614157016Sdes		(*iptr) = l*f + i2;
615157016Sdes		return ret;
616157016Sdes	}
617157016Sdes
618157016Sdes	(*iptr) = l;
619157016Sdes	return x - (*iptr);
620157016Sdes}
621157016Sdes
622157016Sdes
623157016Sdesstatic void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
624157016Sdes		   LDOUBLE fvalue, int min, int max, int flags)
625157016Sdes{
626157016Sdes	int signvalue = 0;
627157016Sdes	double ufvalue;
628157016Sdes	char iconvert[311];
629157016Sdes	char fconvert[311];
630157016Sdes	int iplace = 0;
631157016Sdes	int fplace = 0;
63298937Sdes	int padlen = 0; /* amount to pad */
633157016Sdes	int zpadlen = 0;
634157016Sdes	int caps = 0;
635157016Sdes	int idx;
636157016Sdes	double intpart;
637157016Sdes	double fracpart;
638157016Sdes	double temp;
63998937Sdes
64098937Sdes	/*
64198937Sdes	 * AIX manpage says the default is 0, but Solaris says the default
64298937Sdes	 * is 6, and sprintf on AIX defaults to 6
64398937Sdes	 */
64498937Sdes	if (max < 0)
64598937Sdes		max = 6;
64698937Sdes
647157016Sdes	ufvalue = abs_val (fvalue);
64898937Sdes
649157016Sdes	if (fvalue < 0) {
65098937Sdes		signvalue = '-';
651157016Sdes	} else {
652157016Sdes		if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
653157016Sdes			signvalue = '+';
654157016Sdes		} else {
655157016Sdes			if (flags & DP_F_SPACE)
656157016Sdes				signvalue = ' ';
657157016Sdes		}
658157016Sdes	}
65998937Sdes
660157016Sdes#if 0
661157016Sdes	if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
662157016Sdes#endif
66398937Sdes
664157016Sdes#if 0
665157016Sdes	 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
666157016Sdes#endif
667157016Sdes
66898937Sdes	/*
669157016Sdes	 * Sorry, we only support 16 digits past the decimal because of our
67098937Sdes	 * conversion method
67198937Sdes	 */
672157016Sdes	if (max > 16)
673157016Sdes		max = 16;
67498937Sdes
67598937Sdes	/* We "cheat" by converting the fractional part to integer by
67698937Sdes	 * multiplying by a factor of 10
67798937Sdes	 */
67898937Sdes
679157016Sdes	temp = ufvalue;
680157016Sdes	my_modf(temp, &intpart);
681157016Sdes
682157016Sdes	fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
683157016Sdes
684157016Sdes	if (fracpart >= POW10(max)) {
68598937Sdes		intpart++;
686157016Sdes		fracpart -= POW10(max);
68798937Sdes	}
68898937Sdes
68998937Sdes	/* Convert integer part */
69098937Sdes	do {
691157016Sdes		temp = intpart*0.1;
692157016Sdes		my_modf(temp, &intpart);
693157016Sdes		idx = (int) ((temp -intpart +0.05)* 10.0);
694157016Sdes		/* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
695157016Sdes		/* printf ("%llf, %f, %x\n", temp, intpart, idx); */
69698937Sdes		iconvert[iplace++] =
697157016Sdes			(caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
698157016Sdes	} while (intpart && (iplace < 311));
699157016Sdes	if (iplace == 311) iplace--;
70098937Sdes	iconvert[iplace] = 0;
70198937Sdes
70298937Sdes	/* Convert fractional part */
703157016Sdes	if (fracpart)
704157016Sdes	{
705157016Sdes		do {
706157016Sdes			temp = fracpart*0.1;
707157016Sdes			my_modf(temp, &fracpart);
708157016Sdes			idx = (int) ((temp -fracpart +0.05)* 10.0);
709157016Sdes			/* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
710157016Sdes			/* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
711157016Sdes			fconvert[fplace++] =
712157016Sdes			(caps? "0123456789ABCDEF":"0123456789abcdef")[idx];
713157016Sdes		} while(fracpart && (fplace < 311));
714157016Sdes		if (fplace == 311) fplace--;
715157016Sdes	}
71698937Sdes	fconvert[fplace] = 0;
717157016Sdes
71898937Sdes	/* -1 for decimal point, another -1 if we are printing a sign */
71998937Sdes	padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
72098937Sdes	zpadlen = max - fplace;
721157016Sdes	if (zpadlen < 0) zpadlen = 0;
72298937Sdes	if (padlen < 0)
72398937Sdes		padlen = 0;
72498937Sdes	if (flags & DP_F_MINUS)
72598937Sdes		padlen = -padlen; /* Left Justifty */
726157016Sdes
72798937Sdes	if ((flags & DP_F_ZERO) && (padlen > 0)) {
72898937Sdes		if (signvalue) {
729157016Sdes			dopr_outch (buffer, currlen, maxlen, signvalue);
73098937Sdes			--padlen;
73198937Sdes			signvalue = 0;
73298937Sdes		}
73398937Sdes		while (padlen > 0) {
734157016Sdes			dopr_outch (buffer, currlen, maxlen, '0');
73598937Sdes			--padlen;
73698937Sdes		}
73798937Sdes	}
73898937Sdes	while (padlen > 0) {
739157016Sdes		dopr_outch (buffer, currlen, maxlen, ' ');
74098937Sdes		--padlen;
74198937Sdes	}
74298937Sdes	if (signvalue)
743157016Sdes		dopr_outch (buffer, currlen, maxlen, signvalue);
744157016Sdes
74598937Sdes	while (iplace > 0)
746157016Sdes		dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
74798937Sdes
748157016Sdes#ifdef DEBUG_SNPRINTF
749157016Sdes	printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
750157016Sdes#endif
751157016Sdes
75298937Sdes	/*
753157016Sdes	 * Decimal point.  This should probably use locale to find the correct
754157016Sdes	 * char to print out.
75598937Sdes	 */
756157016Sdes	if (max > 0) {
757157016Sdes		dopr_outch (buffer, currlen, maxlen, '.');
758157016Sdes
759157016Sdes		while (zpadlen > 0) {
760157016Sdes			dopr_outch (buffer, currlen, maxlen, '0');
761157016Sdes			--zpadlen;
762157016Sdes		}
76398937Sdes
764157016Sdes		while (fplace > 0)
765157016Sdes			dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
76698937Sdes	}
76798937Sdes
76898937Sdes	while (padlen < 0) {
769157016Sdes		dopr_outch (buffer, currlen, maxlen, ' ');
77098937Sdes		++padlen;
77198937Sdes	}
77298937Sdes}
77398937Sdes
774157016Sdesstatic void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
77598937Sdes{
776157016Sdes	if (*currlen < maxlen) {
777157016Sdes		buffer[(*currlen)] = c;
778157016Sdes	}
779157016Sdes	(*currlen)++;
78098937Sdes}
78198937Sdes#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
78298937Sdes
783157016Sdes#if !defined(HAVE_VSNPRINTF)
784157016Sdesint vsnprintf (char *str, size_t count, const char *fmt, va_list args)
78598937Sdes{
786157016Sdes	return dopr(str, count, fmt, args);
78798937Sdes}
788157016Sdes#endif
78998937Sdes
790157016Sdes#if !defined(HAVE_SNPRINTF)
791157016Sdesint snprintf(char *str, size_t count, SNPRINTF_CONST char *fmt, ...)
79298937Sdes{
793157016Sdes	size_t ret;
79498937Sdes	va_list ap;
79598937Sdes
79698937Sdes	va_start(ap, fmt);
797157016Sdes	ret = vsnprintf(str, count, fmt, ap);
79898937Sdes	va_end(ap);
799157016Sdes	return ret;
80098937Sdes}
801157016Sdes#endif
80298937Sdes
803