//	Zinc Application Framework - Z_FDATA1.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA
//
//	This file is a modified version of vfprintf.c obtained from the
//	Berkeley Source Tree administered by The Regents of the University of
//	California (the "Regents")
//
//	Modifications made by Zinc are:
//	  COPYRIGHT (C) 1994-1999.  All Rights Reserved.
//	  Zinc Software Incorporated.  Pleasant Grove, Utah  USA
//
//	Permission from the Regents is conditioned on inclusion of the following
//	notice from the Regents which applies only to vfprintf.c:
//
//	  Copyright (c) 1990 The Regents of the University of California.
//	  All rights reserved.
//
//	  This code is derived from software contributed to Berkeley by
//	  Chris Torek.
//
//	  Redistribution and use in source and binary forms, with or without
//	  modification, are permitted provided that the following conditions
//	  are met:
//	  1. Redistributions of source code must retain the above copyright
//	     notice, this list of conditions and the following disclaimer.
//	  2. Redistributions in binary form must reproduce the above copyright
//	     notice, this list of conditions and the following disclaimer in the
//	     documentation and/or other materials provided with the distribution.
//	  3. All advertising materials mentioning features or use of this software
//	     must display the following acknowledgement:
//	       This product includes software developed by the University of
//	       California, Berkeley and its contributors.
//	  4. Neither the name of the University nor the names of its contributors
//	     may be used to endorse or promote products derived from this software
//	     without specific prior written permission.
//
//	  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
//	  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//	  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//	  ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
//	  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//	  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
//	  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//	  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//	  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//	  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
//	  SUCH DAMAGE.

#include <z_fdata.hpp>
#include <z_ctype.hpp>
#include <z_stdio.hpp>
#include <z_stdarg.hpp>
#include <z_string.hpp>

#undef sprintf
int ZafFormatData::sprintf(ZafIChar *dst, const ZafIChar *format, ...)
{
	va_list args;
	va_start(args, format);
#if defined(ZAF_REARRANGEARGS)
	ZafIChar *newFormat = new ZafIChar[ZAF_MAXPARAMLEN];
	char buff[ZAF_MAXPARAMLEN];	// Borland expects this in the stack.
	va_list newArgs;
	ZafStandardArg::RearrangeArgs(false, buff, format, args, newFormat, &newArgs);
	int i = ZafFormatData::_vsprintf(dst, newFormat, &newArgs);
	delete []newFormat;
#else
	int i = ZafFormatData::_vsprintf(dst, format, &args);
#endif
	va_end(args);
	return (i);
}

/* START BLOCK COMMENT
**	static struct
**	{
**		ZafIChar fmtCH;
**		objectFormat Format;
**	} table[40];
**	static int tableEntries = 0;
END BLOCK COMMENT */

ZafFormatData::FormatTable ZafFormatData::formatTable[40];
int ZafFormatData::formatTableEntries = 0;

void ZafFormatData::SetSprintf(ZafIChar fmtch, objectFormat format)
{
	for (int i = 0; i < formatTableEntries; i++)
		if (formatTable[i].fmtCH == fmtch)
			return;
	formatTable[formatTableEntries].fmtCH = fmtch;
	formatTable[formatTableEntries].Format = format;
	formatTableEntries++;
}

// -- Code below here was derived from BSD

#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)vfprintf.c	5.49 (Berkeley) 6/2/92";
#endif /* LIBC_SCCS and not lint */

/*
 * Actual printf innards.
 *
 * This code is large and complicated...
 */

typedef unsigned long U_QUAD_T;
typedef long QUAD_T;

/*
 * Macros for converting digits to letters and vice versa
 */
// For format string (always in Arabic numerals)
#define to_digit(c)	((c) - '0')
#define is_digit(c)	((unsigned int)to_digit(c) <= 9)

#define	BUF		40

int ZafFormatData::_vsprintf(ZafIChar *fp, const ZafIChar *fmt, va_list *ap)
{
	int ch;			/* character from fmt */
	int n;			/* handy integer (short term usage) */
	ZafIChar *cp;		/* handy char pointer (short term usage) */
	int flags;		/* flags as above */
	int ret;		/* return value accumulator */
	int width;		/* width from format (%8d), or 0 */
	int prec;		/* precision from format (%.3d), or -1 */

	/*
	 * BEWARE, these `goto error' on error, and PAD uses `n'.
	 */
#define	PRINT(ptr, len) strncpy(fp, ptr, (size_t)len), fp += len;
#define	FLUSH() *fp = 0

	ret = 0;
	/*
	 * Scan the format for conversions (`%' character).
	 */
	for (;;) {
		for (cp = (ZafIChar *)fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
			/* void */;
		if ((n = (int)(fmt - cp)) != 0) {
			PRINT(cp, n);
			ret += n;
		}
		if (ch == '\0')
			goto done;
		fmt++;		/* skip over '%' */

		flags = 0;
		width = 0;
		prec = -1;

rflag:		ch = *fmt++;
reswitch:	switch (ch) {
		case ' ':
			/*
			 * ``If the space and + flags both appear, the space
			 * flag will be ignored.''
			 *	-- ANSI X3J11
			 */
			if (!(flags & ZAF_PLUS))
				flags |= ZAF_SPACEx;
			goto rflag;
		case '#':
			flags |= ZAF_ALT;
			goto rflag;
		case '*':
			/*
			 * ``A negative field width argument is taken as a
			 * - flag followed by a positive field width.''
			 *	-- ANSI X3J11
			 * They don't exclude field widths read from args.
			 */
			if ((width = va_arg(*ap, int)) >= 0)
				goto rflag;
			width = -width;
			/* FALLTHROUGH */
		case '-':
			flags |= ZAF_LADJUST;
			goto rflag;
		case '$':
			flags |= ZAF_CURRENCYx;
			goto rflag;
		case '@':
			flags |= ZAF_CREDIT;
			goto rflag;
		case ',':
			flags |= ZAF_COMMAS;
			goto rflag;
		case '+':
			flags |= ZAF_PLUS;
			flags &= ~ZAF_SPACEx;	// See case ' ':
			goto rflag;
		case '.':
			if ((ch = *fmt++) == '*') {
				n = va_arg(*ap, int);
				prec = n < 0 ? -1 : n;
				goto rflag;
			}
			n = 0;
			while (is_digit(ch)) {
				n = 10 * n + to_digit(ch);
				ch = *fmt++;
			}
			prec = n < 0 ? -1 : n;
			goto reswitch;
		case 'Z':
			flags |= ZAF_OBJ;
			goto rflag;
		case '0':
			/*
			 * ``Note that 0 is taken as a flag, not as the
			 * beginning of a field width.''
			 *	-- ANSI X3J11
			 */
			flags |= ZAF_ZEROPAD;
			goto rflag;
		case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			n = 0;
			do {
				n = 10 * n + to_digit(ch);
				ch = *fmt++;
			} while (is_digit(ch));
			width = n;
			goto reswitch;
		case 'L':
			flags |= ZAF_LONGDBL;
			goto rflag;
		case 'h':
			flags |= ZAF_SHORTINT;
			goto rflag;
		case 'l':
			flags |= ZAF_LONGINT;
			goto rflag;
		case 'q':
			flags |= ZAF_QUADINT;
			goto rflag;
		case 'a':
			flags |= ZAF_ALT_DIGITS;
			goto rflag;
		case 'n':
			if (flags & ZAF_QUADINT)
				*va_arg(*ap, QUAD_T *) = ret;
			else if (flags & ZAF_LONGINT)
				*va_arg(*ap, long *) = ret;
			else if (flags & ZAF_SHORTINT)
				*va_arg(*ap, short *) = (short)ret;
			else
				*va_arg(*ap, int *) = ret;
			continue;	/* no output */
/* START BLOCK COMMENT
**			case 'p':
**				{
**				#*
**				 * ``The argument shall be a pointer to void.  The
**				 * value of the pointer is converted to a sequence
**				 * of printable characters, in an implementation-
**				 * defined manner.''
**				 *	-- ANSI X3J11
**				 #/
**				ZafIChar *buffer = fp;
**				ZafIntegerData::Format(ap, &buffer, 'x', flags | ZAF_HEXPREFIX | ZAF_LONGINT, width, prec);
**				ret += (int)(buffer - fp);
**				fp = buffer;
**				}
**				break;
END BLOCK COMMENT */
		default:	/* "%?" prints ?, unless ? is NUL */
			{
			if (ch == '\0')
				goto done;
			for (int i=0; i < formatTableEntries; i++)
				if (formatTable[i].fmtCH == ch)
				{
					ZafIChar *buffer = fp;
					formatTable[i].Format(ap, &buffer, (ZafIChar)ch, flags, width, prec);
					ret += (int)(buffer - fp);
					fp = buffer;
					goto next;
				}
			/* pretend it was %c with argument ch */
				*fp++ = (char)ch;
				ret++;
			}
			break;
		}
next:
		FLUSH();	/* copy out the I/O vectors */
	}
done:
	FLUSH();
	return (ret);
}

