767 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			767 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/**
 | 
						|
 * @PROJECT     PH7 Engine for the AerScript Interpreter
 | 
						|
 * @COPYRIGHT   See COPYING in the top level directory
 | 
						|
 * @FILE        engine/lib/libfmt.c
 | 
						|
 * @DESCRIPTION Modern formatting library for the PH7 Engine
 | 
						|
 * @DEVELOPERS  Symisc Systems <devel@symisc.net>
 | 
						|
 *              Rafal Kupiec <belliash@codingworkshop.eu.org>
 | 
						|
 */
 | 
						|
#include "ph7int.h"
 | 
						|
 | 
						|
#define SXFMT_BUFSIZ 1024 /* Conversion buffer size */
 | 
						|
/*
 | 
						|
** Conversion types fall into various categories as defined by the
 | 
						|
** following enumeration.
 | 
						|
*/
 | 
						|
#define SXFMT_RADIX       1 /* Integer types.%d, %x, %o, and so forth */
 | 
						|
#define SXFMT_FLOAT       2 /* Floating point.%f */
 | 
						|
#define SXFMT_EXP         3 /* Exponentional notation.%e and %E */
 | 
						|
#define SXFMT_GENERIC     4 /* Floating or exponential, depending on exponent.%g */
 | 
						|
#define SXFMT_SIZE        5 /* Total number of characters processed so far.%n */
 | 
						|
#define SXFMT_STRING      6 /* Strings.%s */
 | 
						|
#define SXFMT_PERCENT     7 /* Percent symbol.%% */
 | 
						|
#define SXFMT_CHARX       8 /* Characters.%c */
 | 
						|
#define SXFMT_ERROR       9 /* Used to indicate no such conversion type */
 | 
						|
/* Extension by Symisc Systems */
 | 
						|
#define SXFMT_RAWSTR     13 /* %z Pointer to raw string (SyString *) */
 | 
						|
#define SXFMT_UNUSED     15
 | 
						|
/*
 | 
						|
** Allowed values for SyFmtInfo.flags
 | 
						|
*/
 | 
						|
#define SXFLAG_SIGNED	0x01
 | 
						|
#define SXFLAG_UNSIGNED 0x02
 | 
						|
/* Allowed values for SyFmtConsumer.nType */
 | 
						|
#define SXFMT_CONS_PROC		1	/* Consumer is a procedure */
 | 
						|
#define SXFMT_CONS_STR		2	/* Consumer is a managed string */
 | 
						|
#define SXFMT_CONS_FILE		5	/* Consumer is an open File */
 | 
						|
#define SXFMT_CONS_BLOB		6	/* Consumer is a BLOB */
 | 
						|
/*
 | 
						|
** Each builtin conversion character (ex: the 'd' in "%d") is described
 | 
						|
** by an instance of the following structure
 | 
						|
*/
 | 
						|
typedef struct SyFmtInfo SyFmtInfo;
 | 
						|
struct SyFmtInfo {
 | 
						|
	char fmttype;  /* The format field code letter [i.e: 'd','s','x'] */
 | 
						|
	sxu8 base;     /* The base for radix conversion */
 | 
						|
	int flags;    /* One or more of SXFLAG_ constants below */
 | 
						|
	sxu8 type;     /* Conversion paradigm */
 | 
						|
	const char *charset; /* The character set for conversion */
 | 
						|
	const char *prefix;  /* Prefix on non-zero values in alt format */
 | 
						|
};
 | 
						|
typedef struct SyFmtConsumer SyFmtConsumer;
 | 
						|
struct SyFmtConsumer {
 | 
						|
	sxu32 nLen; /* Total output length */
 | 
						|
	sxi32 nType; /* Type of the consumer see below */
 | 
						|
	sxi32 rc;	/* Consumer return value;Abort processing if rc != SXRET_OK */
 | 
						|
	union {
 | 
						|
		struct {
 | 
						|
			ProcConsumer xUserConsumer;
 | 
						|
			void *pUserData;
 | 
						|
		} sFunc;
 | 
						|
		SyBlob *pBlob;
 | 
						|
	} uConsumer;
 | 
						|
};
 | 
						|
#ifndef SX_OMIT_FLOATINGPOINT
 | 
						|
static int getdigit(sxlongreal *val, int *cnt) {
 | 
						|
	sxlongreal d;
 | 
						|
	int digit;
 | 
						|
	if((*cnt)++ >= 16) {
 | 
						|
		return '0';
 | 
						|
	}
 | 
						|
	digit = (int) * val;
 | 
						|
	d = digit;
 | 
						|
	*val = (*val - d) * 10.0;
 | 
						|
	return digit + '0' ;
 | 
						|
}
 | 
						|
#endif /* SX_OMIT_FLOATINGPOINT */
 | 
						|
/*
 | 
						|
 * The following routine was taken from the SQLITE2 source tree and was
 | 
						|
 * extended by Symisc Systems to fit its need.
 | 
						|
 * Status: Public Domain
 | 
						|
 */
 | 
						|
static sxi32 InternFormat(ProcConsumer xConsumer, void *pUserData, const char *zFormat, va_list ap) {
 | 
						|
	/*
 | 
						|
	 * The following table is searched linearly, so it is good to put the most frequently
 | 
						|
	 * used conversion types first.
 | 
						|
	 */
 | 
						|
	static const SyFmtInfo aFmt[] = {
 | 
						|
		{  'd', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0    },
 | 
						|
		{  's',  0, 0, SXFMT_STRING,     0,                  0    },
 | 
						|
		{  'c',  0, 0, SXFMT_CHARX,      0,                  0    },
 | 
						|
		{  'x', 16, 0, SXFMT_RADIX,      "0123456789abcdef", "x0" },
 | 
						|
		{  'X', 16, 0, SXFMT_RADIX,      "0123456789ABCDEF", "X0" },
 | 
						|
		/* -- Extensions by Symisc Systems -- */
 | 
						|
		{  'z',  0, 0, SXFMT_RAWSTR,     0,                   0   }, /* Pointer to a raw string (SyString *) */
 | 
						|
		{  'B',  2, 0, SXFMT_RADIX,      "01",                "b0"},
 | 
						|
		/* -- End of Extensions -- */
 | 
						|
		{  'o',  8, 0, SXFMT_RADIX,      "01234567",         "0"  },
 | 
						|
		{  'u', 10, 0, SXFMT_RADIX,      "0123456789",       0    },
 | 
						|
#ifndef SX_OMIT_FLOATINGPOINT
 | 
						|
		{  'f',  0, SXFLAG_SIGNED, SXFMT_FLOAT,       0,     0    },
 | 
						|
		{  'e',  0, SXFLAG_SIGNED, SXFMT_EXP,        "e",    0    },
 | 
						|
		{  'E',  0, SXFLAG_SIGNED, SXFMT_EXP,        "E",    0    },
 | 
						|
		{  'g',  0, SXFLAG_SIGNED, SXFMT_GENERIC,    "e",    0    },
 | 
						|
		{  'G',  0, SXFLAG_SIGNED, SXFMT_GENERIC,    "E",    0    },
 | 
						|
#endif
 | 
						|
		{  'i', 10, SXFLAG_SIGNED, SXFMT_RADIX, "0123456789", 0    },
 | 
						|
		{  'n',  0, 0, SXFMT_SIZE,       0,                  0    },
 | 
						|
		{  '%',  0, 0, SXFMT_PERCENT,    0,                  0    },
 | 
						|
		{  'p', 10, 0, SXFMT_RADIX,      "0123456789",       0    }
 | 
						|
	};
 | 
						|
	int c;                     /* Next character in the format string */
 | 
						|
	char *bufpt;               /* Pointer to the conversion buffer */
 | 
						|
	int precision;             /* Precision of the current field */
 | 
						|
	int length;                /* Length of the field */
 | 
						|
	int idx;                   /* A general purpose loop counter */
 | 
						|
	int width;                 /* Width of the current field */
 | 
						|
	sxu8 flag_leftjustify;   /* True if "-" flag is present */
 | 
						|
	sxu8 flag_plussign;      /* True if "+" flag is present */
 | 
						|
	sxu8 flag_blanksign;     /* True if " " flag is present */
 | 
						|
	sxu8 flag_alternateform; /* True if "#" flag is present */
 | 
						|
	sxu8 flag_zeropad;       /* True if field width constant starts with zero */
 | 
						|
	sxu8 flag_long;          /* True if "l" flag is present */
 | 
						|
	sxi64 longvalue;         /* Value for integer types */
 | 
						|
	const SyFmtInfo *infop;  /* Pointer to the appropriate info structure */
 | 
						|
	char buf[SXFMT_BUFSIZ];  /* Conversion buffer */
 | 
						|
	char prefix;             /* Prefix character."+" or "-" or " " or '\0'.*/
 | 
						|
	sxu8 errorflag = 0;      /* True if an error is encountered */
 | 
						|
	sxu8 xtype;              /* Conversion paradigm */
 | 
						|
	static char spaces[] = "                                                  ";
 | 
						|
#define etSPACESIZE ((int)sizeof(spaces)-1)
 | 
						|
#ifndef SX_OMIT_FLOATINGPOINT
 | 
						|
	sxlongreal realvalue;    /* Value for real types */
 | 
						|
	int  exp;                /* exponent of real numbers */
 | 
						|
	double rounder;          /* Used for rounding floating point values */
 | 
						|
	sxu8 flag_dp;            /* True if decimal point should be shown */
 | 
						|
	sxu8 flag_rtz;           /* True if trailing zeros should be removed */
 | 
						|
	sxu8 flag_exp;           /* True to force display of the exponent */
 | 
						|
	int nsd;                 /* Number of significant digits returned */
 | 
						|
#endif
 | 
						|
	int rc;
 | 
						|
	length = 0;
 | 
						|
	bufpt = 0;
 | 
						|
	for(; (c = (*zFormat)) != 0; ++zFormat) {
 | 
						|
		if(c != '%') {
 | 
						|
			unsigned int amt;
 | 
						|
			bufpt = (char *)zFormat;
 | 
						|
			amt = 1;
 | 
						|
			while((c = (*++zFormat)) != '%' && c != 0) {
 | 
						|
				amt++;
 | 
						|
			}
 | 
						|
			rc = xConsumer((const void *)bufpt, amt, pUserData);
 | 
						|
			if(rc != SXRET_OK) {
 | 
						|
				return SXERR_ABORT; /* Consumer routine request an operation abort */
 | 
						|
			}
 | 
						|
			if(c == 0) {
 | 
						|
				return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if((c = (*++zFormat)) == 0) {
 | 
						|
			errorflag = 1;
 | 
						|
			rc = xConsumer("%", sizeof("%") - 1, pUserData);
 | 
						|
			if(rc != SXRET_OK) {
 | 
						|
				return SXERR_ABORT; /* Consumer routine request an operation abort */
 | 
						|
			}
 | 
						|
			return errorflag > 0 ? SXERR_FORMAT : SXRET_OK;
 | 
						|
		}
 | 
						|
		/* Find out what flags are present */
 | 
						|
		flag_leftjustify = flag_plussign = flag_blanksign =
 | 
						|
											   flag_alternateform = flag_zeropad = 0;
 | 
						|
		do {
 | 
						|
			switch(c) {
 | 
						|
				case '-':
 | 
						|
					flag_leftjustify = 1;
 | 
						|
					c = 0;
 | 
						|
					break;
 | 
						|
				case '+':
 | 
						|
					flag_plussign = 1;
 | 
						|
					c = 0;
 | 
						|
					break;
 | 
						|
				case ' ':
 | 
						|
					flag_blanksign = 1;
 | 
						|
					c = 0;
 | 
						|
					break;
 | 
						|
				case '#':
 | 
						|
					flag_alternateform = 1;
 | 
						|
					c = 0;
 | 
						|
					break;
 | 
						|
				case '0':
 | 
						|
					flag_zeropad = 1;
 | 
						|
					c = 0;
 | 
						|
					break;
 | 
						|
				default:
 | 
						|
					break;
 | 
						|
			}
 | 
						|
		} while(c == 0 && (c = (*++zFormat)) != 0);
 | 
						|
		/* Get the field width */
 | 
						|
		width = 0;
 | 
						|
		if(c == '*') {
 | 
						|
			width = va_arg(ap, int);
 | 
						|
			if(width < 0) {
 | 
						|
				flag_leftjustify = 1;
 | 
						|
				width = -width;
 | 
						|
			}
 | 
						|
			c = *++zFormat;
 | 
						|
		} else {
 | 
						|
			while(c >= '0' && c <= '9') {
 | 
						|
				width = width * 10 + c - '0';
 | 
						|
				c = *++zFormat;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(width > SXFMT_BUFSIZ - 10) {
 | 
						|
			width = SXFMT_BUFSIZ - 10;
 | 
						|
		}
 | 
						|
		/* Get the precision */
 | 
						|
		precision = -1;
 | 
						|
		if(c == '.') {
 | 
						|
			precision = 0;
 | 
						|
			c = *++zFormat;
 | 
						|
			if(c == '*') {
 | 
						|
				precision = va_arg(ap, int);
 | 
						|
				if(precision < 0) {
 | 
						|
					precision = -precision;
 | 
						|
				}
 | 
						|
				c = *++zFormat;
 | 
						|
			} else {
 | 
						|
				while(c >= '0' && c <= '9') {
 | 
						|
					precision = precision * 10 + c - '0';
 | 
						|
					c = *++zFormat;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Get the conversion type modifier */
 | 
						|
		flag_long = 0;
 | 
						|
		if(c == 'l' || c == 'q' /* BSD quad (expect a 64-bit integer) */) {
 | 
						|
			flag_long = (c == 'q') ? 2 : 1;
 | 
						|
			c = *++zFormat;
 | 
						|
			if(c == 'l') {
 | 
						|
				/* Standard printf emulation 'lld' (expect a 64bit integer) */
 | 
						|
				flag_long = 2;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/* Fetch the info entry for the field */
 | 
						|
		infop = 0;
 | 
						|
		xtype = SXFMT_ERROR;
 | 
						|
		for(idx = 0; idx < (int)SX_ARRAYSIZE(aFmt); idx++) {
 | 
						|
			if(c == aFmt[idx].fmttype) {
 | 
						|
				infop = &aFmt[idx];
 | 
						|
				xtype = infop->type;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		** At this point, variables are initialized as follows:
 | 
						|
		**
 | 
						|
		**   flag_alternateform          TRUE if a '#' is present.
 | 
						|
		**   flag_plussign               TRUE if a '+' is present.
 | 
						|
		**   flag_leftjustify            TRUE if a '-' is present or if the
 | 
						|
		**                               field width was negative.
 | 
						|
		**   flag_zeropad                TRUE if the width began with 0.
 | 
						|
		**   flag_long                   TRUE if the letter 'l' (ell) or 'q'(BSD quad) prefixed
 | 
						|
		**                               the conversion character.
 | 
						|
		**   flag_blanksign              TRUE if a ' ' is present.
 | 
						|
		**   width                       The specified field width.This is
 | 
						|
		**                               always non-negative.Zero is the default.
 | 
						|
		**   precision                   The specified precision.The default
 | 
						|
		**                               is -1.
 | 
						|
		**   xtype                       The class of the conversion.
 | 
						|
		**   infop                       Pointer to the appropriate info struct.
 | 
						|
		*/
 | 
						|
		switch(xtype) {
 | 
						|
			case SXFMT_RADIX:
 | 
						|
				if(flag_long > 0) {
 | 
						|
					if(flag_long > 1) {
 | 
						|
						/* BSD quad: expect a 64-bit integer */
 | 
						|
						longvalue = va_arg(ap, sxi64);
 | 
						|
					} else {
 | 
						|
						longvalue = va_arg(ap, sxlong);
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					if(infop->flags & SXFLAG_SIGNED) {
 | 
						|
						longvalue = va_arg(ap, sxi32);
 | 
						|
					} else {
 | 
						|
						longvalue = va_arg(ap, sxu32);
 | 
						|
					}
 | 
						|
				}
 | 
						|
				/* Limit the precision to prevent overflowing buf[] during conversion */
 | 
						|
				if(precision > SXFMT_BUFSIZ - 40) {
 | 
						|
					precision = SXFMT_BUFSIZ - 40;
 | 
						|
				}
 | 
						|
#if 1
 | 
						|
				/* For the format %#x, the value zero is printed "0" not "0x0".
 | 
						|
				** I think this is stupid.*/
 | 
						|
				if(longvalue == 0) {
 | 
						|
					flag_alternateform = 0;
 | 
						|
				}
 | 
						|
#else
 | 
						|
				/* More sensible: turn off the prefix for octal (to prevent "00"),
 | 
						|
				** but leave the prefix for hex.*/
 | 
						|
				if(longvalue == 0 && infop->base == 8) {
 | 
						|
					flag_alternateform = 0;
 | 
						|
				}
 | 
						|
#endif
 | 
						|
				if(infop->flags & SXFLAG_SIGNED) {
 | 
						|
					if(longvalue < 0) {
 | 
						|
						longvalue = -longvalue;
 | 
						|
						/* Ticket 1433-003 */
 | 
						|
						if(longvalue < 0) {
 | 
						|
							/* Overflow */
 | 
						|
							longvalue = SXI64_HIGH;
 | 
						|
						}
 | 
						|
						prefix = '-';
 | 
						|
					} else if(flag_plussign) {
 | 
						|
						prefix = '+';
 | 
						|
					} else if(flag_blanksign) {
 | 
						|
						prefix = ' ';
 | 
						|
					} else {
 | 
						|
						prefix = 0;
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					if(longvalue < 0) {
 | 
						|
						longvalue = -longvalue;
 | 
						|
						/* Ticket 1433-003 */
 | 
						|
						if(longvalue < 0) {
 | 
						|
							/* Overflow */
 | 
						|
							longvalue = SXI64_HIGH;
 | 
						|
						}
 | 
						|
					}
 | 
						|
					prefix = 0;
 | 
						|
				}
 | 
						|
				if(flag_zeropad && precision < width - (prefix != 0)) {
 | 
						|
					precision = width - (prefix != 0);
 | 
						|
				}
 | 
						|
				bufpt = &buf[SXFMT_BUFSIZ - 1];
 | 
						|
				{
 | 
						|
					register const char *cset;      /* Use registers for speed */
 | 
						|
					register int base;
 | 
						|
					cset = infop->charset;
 | 
						|
					base = infop->base;
 | 
						|
					do {                                          /* Convert to ascii */
 | 
						|
						*(--bufpt) = cset[longvalue % base];
 | 
						|
						longvalue = longvalue / base;
 | 
						|
					} while(longvalue > 0);
 | 
						|
				}
 | 
						|
				length = &buf[SXFMT_BUFSIZ - 1] - bufpt;
 | 
						|
				for(idx = precision - length; idx > 0; idx--) {
 | 
						|
					*(--bufpt) = '0';                             /* Zero pad */
 | 
						|
				}
 | 
						|
				if(prefix) {
 | 
						|
					*(--bufpt) = prefix;    /* Add sign */
 | 
						|
				}
 | 
						|
				if(flag_alternateform && infop->prefix) {       /* Add "0" or "0x" */
 | 
						|
					const char *pre;
 | 
						|
					char x;
 | 
						|
					pre = infop->prefix;
 | 
						|
					if(*bufpt != pre[0]) {
 | 
						|
						for(pre = infop->prefix; (x = (*pre)) != 0; pre++) {
 | 
						|
							*(--bufpt) = x;
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
				length = &buf[SXFMT_BUFSIZ - 1] - bufpt;
 | 
						|
				break;
 | 
						|
			case SXFMT_FLOAT:
 | 
						|
			case SXFMT_EXP:
 | 
						|
			case SXFMT_GENERIC:
 | 
						|
#ifndef SX_OMIT_FLOATINGPOINT
 | 
						|
				realvalue = va_arg(ap, double);
 | 
						|
				if(precision < 0) {
 | 
						|
					precision = 6;    /* Set default precision */
 | 
						|
				}
 | 
						|
				if(precision > SXFMT_BUFSIZ - 40) {
 | 
						|
					precision = SXFMT_BUFSIZ - 40;
 | 
						|
				}
 | 
						|
				if(realvalue < 0.0) {
 | 
						|
					realvalue = -realvalue;
 | 
						|
					prefix = '-';
 | 
						|
				} else {
 | 
						|
					if(flag_plussign) {
 | 
						|
						prefix = '+';
 | 
						|
					} else if(flag_blanksign) {
 | 
						|
						prefix = ' ';
 | 
						|
					} else {
 | 
						|
						prefix = 0;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				if(infop->type == SXFMT_GENERIC && precision > 0) {
 | 
						|
					precision--;
 | 
						|
				}
 | 
						|
				rounder = 0.0;
 | 
						|
				/* Rounding works like BSD when the constant 0.4999 is used. Wierd!
 | 
						|
				 * It makes more sense to use 0.5 instead. */
 | 
						|
				for(idx = precision, rounder = 0.5; idx > 0; idx--, rounder *= 0.1);
 | 
						|
				if(infop->type == SXFMT_FLOAT) {
 | 
						|
					realvalue += rounder;
 | 
						|
				}
 | 
						|
				/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
 | 
						|
				exp = 0;
 | 
						|
				if(realvalue > 0.0) {
 | 
						|
					while(realvalue >= 1e8 && exp <= 350) {
 | 
						|
						realvalue *= 1e-8;
 | 
						|
						exp += 8;
 | 
						|
					}
 | 
						|
					while(realvalue >= 10.0 && exp <= 350) {
 | 
						|
						realvalue *= 0.1;
 | 
						|
						exp++;
 | 
						|
					}
 | 
						|
					while(realvalue < 1e-8 && exp >= -350) {
 | 
						|
						realvalue *= 1e8;
 | 
						|
						exp -= 8;
 | 
						|
					}
 | 
						|
					while(realvalue < 1.0 && exp >= -350) {
 | 
						|
						realvalue *= 10.0;
 | 
						|
						exp--;
 | 
						|
					}
 | 
						|
					if(exp > 350 || exp < -350) {
 | 
						|
						bufpt = "NaN";
 | 
						|
						length = 3;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				bufpt = buf;
 | 
						|
				/*
 | 
						|
				** If the field type is etGENERIC, then convert to either etEXP
 | 
						|
				** or etFLOAT, as appropriate.
 | 
						|
				*/
 | 
						|
				flag_exp = xtype == SXFMT_EXP;
 | 
						|
				if(xtype != SXFMT_FLOAT) {
 | 
						|
					realvalue += rounder;
 | 
						|
					if(realvalue >= 10.0) {
 | 
						|
						realvalue *= 0.1;
 | 
						|
						exp++;
 | 
						|
					}
 | 
						|
				}
 | 
						|
				if(xtype == SXFMT_GENERIC) {
 | 
						|
					flag_rtz = !flag_alternateform;
 | 
						|
					if(exp < -4 || exp > precision) {
 | 
						|
						xtype = SXFMT_EXP;
 | 
						|
					} else {
 | 
						|
						precision = precision - exp;
 | 
						|
						xtype = SXFMT_FLOAT;
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					flag_rtz = 0;
 | 
						|
				}
 | 
						|
				/*
 | 
						|
				** The "exp+precision" test causes output to be of type etEXP if
 | 
						|
				** the precision is too large to fit in buf[].
 | 
						|
				*/
 | 
						|
				nsd = 0;
 | 
						|
				if(xtype == SXFMT_FLOAT && exp + precision < SXFMT_BUFSIZ - 30) {
 | 
						|
					flag_dp = (precision > 0 || flag_alternateform);
 | 
						|
					if(prefix) {
 | 
						|
						*(bufpt++) = prefix;    /* Sign */
 | 
						|
					}
 | 
						|
					if(exp < 0) {
 | 
						|
						*(bufpt++) = '0';    /* Digits before "." */
 | 
						|
					} else
 | 
						|
						for(; exp >= 0; exp--) {
 | 
						|
							*(bufpt++) = (char)getdigit(&realvalue, &nsd);
 | 
						|
						}
 | 
						|
					if(flag_dp) {
 | 
						|
						*(bufpt++) = '.';    /* The decimal point */
 | 
						|
					}
 | 
						|
					for(exp++; exp < 0 && precision > 0; precision--, exp++) {
 | 
						|
						*(bufpt++) = '0';
 | 
						|
					}
 | 
						|
					while((precision--) > 0) {
 | 
						|
						*(bufpt++) = (char)getdigit(&realvalue, &nsd);
 | 
						|
					}
 | 
						|
					*(bufpt--) = 0;                           /* Null terminate */
 | 
						|
					if(flag_rtz && flag_dp) {      /* Remove trailing zeros and "." */
 | 
						|
						while(bufpt >= buf && *bufpt == '0') {
 | 
						|
							*(bufpt--) = 0;
 | 
						|
						}
 | 
						|
						if(bufpt >= buf && *bufpt == '.') {
 | 
						|
							*(bufpt--) = 0;
 | 
						|
						}
 | 
						|
					}
 | 
						|
					bufpt++;                            /* point to next free slot */
 | 
						|
				} else {   /* etEXP or etGENERIC */
 | 
						|
					flag_dp = (precision > 0 || flag_alternateform);
 | 
						|
					if(prefix) {
 | 
						|
						*(bufpt++) = prefix;    /* Sign */
 | 
						|
					}
 | 
						|
					*(bufpt++) = (char)getdigit(&realvalue, &nsd); /* First digit */
 | 
						|
					if(flag_dp) {
 | 
						|
						*(bufpt++) = '.';    /* Decimal point */
 | 
						|
					}
 | 
						|
					while((precision--) > 0) {
 | 
						|
						*(bufpt++) = (char)getdigit(&realvalue, &nsd);
 | 
						|
					}
 | 
						|
					bufpt--;                            /* point to last digit */
 | 
						|
					if(flag_rtz && flag_dp) {           /* Remove tail zeros */
 | 
						|
						while(bufpt >= buf && *bufpt == '0') {
 | 
						|
							*(bufpt--) = 0;
 | 
						|
						}
 | 
						|
						if(bufpt >= buf && *bufpt == '.') {
 | 
						|
							*(bufpt--) = 0;
 | 
						|
						}
 | 
						|
					}
 | 
						|
					bufpt++;                            /* point to next free slot */
 | 
						|
					if(exp || flag_exp) {
 | 
						|
						*(bufpt++) = infop->charset[0];
 | 
						|
						if(exp < 0) {
 | 
						|
							*(bufpt++) = '-';    /* sign of exp */
 | 
						|
							exp = -exp;
 | 
						|
						} else       {
 | 
						|
							*(bufpt++) = '+';
 | 
						|
						}
 | 
						|
						if(exp >= 100) {
 | 
						|
							*(bufpt++) = (char)((exp / 100) + '0');            /* 100's digit */
 | 
						|
							exp %= 100;
 | 
						|
						}
 | 
						|
						*(bufpt++) = (char)(exp / 10 + '0');                 /* 10's digit */
 | 
						|
						*(bufpt++) = (char)(exp % 10 + '0');                 /* 1's digit */
 | 
						|
					}
 | 
						|
				}
 | 
						|
				/* The converted number is in buf[] and zero terminated.Output it.
 | 
						|
				** Note that the number is in the usual order, not reversed as with
 | 
						|
				** integer conversions.*/
 | 
						|
				length = bufpt - buf;
 | 
						|
				bufpt = buf;
 | 
						|
				/* Special case:  Add leading zeros if the flag_zeropad flag is
 | 
						|
				** set and we are not left justified */
 | 
						|
				if(flag_zeropad && !flag_leftjustify && length < width) {
 | 
						|
					int i;
 | 
						|
					int nPad = width - length;
 | 
						|
					for(i = width; i >= nPad; i--) {
 | 
						|
						bufpt[i] = bufpt[i - nPad];
 | 
						|
					}
 | 
						|
					i = prefix != 0;
 | 
						|
					while(nPad--) {
 | 
						|
						bufpt[i++] = '0';
 | 
						|
					}
 | 
						|
					length = width;
 | 
						|
				}
 | 
						|
#else
 | 
						|
				bufpt = " ";
 | 
						|
				length = (int)sizeof(" ") - 1;
 | 
						|
#endif /* SX_OMIT_FLOATINGPOINT */
 | 
						|
				break;
 | 
						|
			case SXFMT_SIZE: {
 | 
						|
					int *pSize = va_arg(ap, int *);
 | 
						|
					*pSize = ((SyFmtConsumer *)pUserData)->nLen;
 | 
						|
					length = width = 0;
 | 
						|
				}
 | 
						|
				break;
 | 
						|
			case SXFMT_PERCENT:
 | 
						|
				buf[0] = '%';
 | 
						|
				bufpt = buf;
 | 
						|
				length = 1;
 | 
						|
				break;
 | 
						|
			case SXFMT_CHARX:
 | 
						|
				c = va_arg(ap, int);
 | 
						|
				buf[0] = (char)c;
 | 
						|
				/* Limit the precision to prevent overflowing buf[] during conversion */
 | 
						|
				if(precision > SXFMT_BUFSIZ - 40) {
 | 
						|
					precision = SXFMT_BUFSIZ - 40;
 | 
						|
				}
 | 
						|
				if(precision >= 0) {
 | 
						|
					for(idx = 1; idx < precision; idx++) {
 | 
						|
						buf[idx] = (char)c;
 | 
						|
					}
 | 
						|
					length = precision;
 | 
						|
				} else {
 | 
						|
					length = 1;
 | 
						|
				}
 | 
						|
				bufpt = buf;
 | 
						|
				break;
 | 
						|
			case SXFMT_STRING:
 | 
						|
				bufpt = va_arg(ap, char *);
 | 
						|
				if(bufpt == 0) {
 | 
						|
					bufpt = " ";
 | 
						|
					length = (int)sizeof(" ") - 1;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				length = precision;
 | 
						|
				if(precision < 0) {
 | 
						|
					/* Symisc extension */
 | 
						|
					length = (int)SyStrlen(bufpt);
 | 
						|
				}
 | 
						|
				if(precision >= 0 && precision < length) {
 | 
						|
					length = precision;
 | 
						|
				}
 | 
						|
				break;
 | 
						|
			case SXFMT_RAWSTR: {
 | 
						|
					/* Symisc extension */
 | 
						|
					SyString *pStr = va_arg(ap, SyString *);
 | 
						|
					if(pStr == 0 || pStr->zString == 0) {
 | 
						|
						bufpt = " ";
 | 
						|
						length = (int)sizeof(char);
 | 
						|
						break;
 | 
						|
					}
 | 
						|
					bufpt = (char *)pStr->zString;
 | 
						|
					length = (int)pStr->nByte;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			case SXFMT_ERROR:
 | 
						|
				buf[0] = '?';
 | 
						|
				bufpt = buf;
 | 
						|
				length = (int)sizeof(char);
 | 
						|
				if(c == 0) {
 | 
						|
					zFormat--;
 | 
						|
				}
 | 
						|
				break;
 | 
						|
		}/* End switch over the format type */
 | 
						|
		/*
 | 
						|
		** The text of the conversion is pointed to by "bufpt" and is
 | 
						|
		** "length" characters long.The field width is "width".Do
 | 
						|
		** the output.
 | 
						|
		*/
 | 
						|
		if(!flag_leftjustify) {
 | 
						|
			register int nspace;
 | 
						|
			nspace = width - length;
 | 
						|
			if(nspace > 0) {
 | 
						|
				while(nspace >= etSPACESIZE) {
 | 
						|
					rc = xConsumer(spaces, etSPACESIZE, pUserData);
 | 
						|
					if(rc != SXRET_OK) {
 | 
						|
						return SXERR_ABORT; /* Consumer routine request an operation abort */
 | 
						|
					}
 | 
						|
					nspace -= etSPACESIZE;
 | 
						|
				}
 | 
						|
				if(nspace > 0) {
 | 
						|
					rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
 | 
						|
					if(rc != SXRET_OK) {
 | 
						|
						return SXERR_ABORT; /* Consumer routine request an operation abort */
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(length > 0) {
 | 
						|
			rc = xConsumer(bufpt, (unsigned int)length, pUserData);
 | 
						|
			if(rc != SXRET_OK) {
 | 
						|
				return SXERR_ABORT; /* Consumer routine request an operation abort */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if(flag_leftjustify) {
 | 
						|
			register int nspace;
 | 
						|
			nspace = width - length;
 | 
						|
			if(nspace > 0) {
 | 
						|
				while(nspace >= etSPACESIZE) {
 | 
						|
					rc = xConsumer(spaces, etSPACESIZE, pUserData);
 | 
						|
					if(rc != SXRET_OK) {
 | 
						|
						return SXERR_ABORT; /* Consumer routine request an operation abort */
 | 
						|
					}
 | 
						|
					nspace -= etSPACESIZE;
 | 
						|
				}
 | 
						|
				if(nspace > 0) {
 | 
						|
					rc = xConsumer(spaces, (unsigned int)nspace, pUserData);
 | 
						|
					if(rc != SXRET_OK) {
 | 
						|
						return SXERR_ABORT; /* Consumer routine request an operation abort */
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}/* End for loop over the format string */
 | 
						|
	return errorflag ? SXERR_FORMAT : SXRET_OK;
 | 
						|
}
 | 
						|
static sxi32 FormatConsumer(const void *pSrc, unsigned int nLen, void *pData) {
 | 
						|
	SyFmtConsumer *pConsumer = (SyFmtConsumer *)pData;
 | 
						|
	sxi32 rc = SXERR_ABORT;
 | 
						|
	switch(pConsumer->nType) {
 | 
						|
		case SXFMT_CONS_PROC:
 | 
						|
			/* User callback */
 | 
						|
			rc = pConsumer->uConsumer.sFunc.xUserConsumer(pSrc, nLen, pConsumer->uConsumer.sFunc.pUserData);
 | 
						|
			break;
 | 
						|
		case SXFMT_CONS_BLOB:
 | 
						|
			/* Blob consumer */
 | 
						|
			rc = SyBlobAppend(pConsumer->uConsumer.pBlob, pSrc, (sxu32)nLen);
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			/* Unknown consumer */
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	/* Update total number of bytes consumed so far */
 | 
						|
	pConsumer->nLen += nLen;
 | 
						|
	pConsumer->rc = rc;
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
static sxi32 FormatMount(sxi32 nType, void *pConsumer, ProcConsumer xUserCons, void *pUserData, sxu32 *pOutLen, const char *zFormat, va_list ap) {
 | 
						|
	SyFmtConsumer sCons;
 | 
						|
	sCons.nType = nType;
 | 
						|
	sCons.rc = SXRET_OK;
 | 
						|
	sCons.nLen = 0;
 | 
						|
	if(pOutLen) {
 | 
						|
		*pOutLen = 0;
 | 
						|
	}
 | 
						|
	switch(nType) {
 | 
						|
		case SXFMT_CONS_PROC:
 | 
						|
#if defined(UNTRUST)
 | 
						|
			if(xUserCons == 0) {
 | 
						|
				return SXERR_EMPTY;
 | 
						|
			}
 | 
						|
#endif
 | 
						|
			sCons.uConsumer.sFunc.xUserConsumer = xUserCons;
 | 
						|
			sCons.uConsumer.sFunc.pUserData	    = pUserData;
 | 
						|
			break;
 | 
						|
		case SXFMT_CONS_BLOB:
 | 
						|
			sCons.uConsumer.pBlob = (SyBlob *)pConsumer;
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			return SXERR_UNKNOWN;
 | 
						|
	}
 | 
						|
	InternFormat(FormatConsumer, &sCons, zFormat, ap);
 | 
						|
	if(pOutLen) {
 | 
						|
		*pOutLen = sCons.nLen;
 | 
						|
	}
 | 
						|
	return sCons.rc;
 | 
						|
}
 | 
						|
PH7_PRIVATE sxi32 SyProcFormat(ProcConsumer xConsumer, void *pData, const char *zFormat, ...) {
 | 
						|
	va_list ap;
 | 
						|
	sxi32 rc;
 | 
						|
#if defined(UNTRUST)
 | 
						|
	if(SX_EMPTY_STR(zFormat)) {
 | 
						|
		return SXERR_EMPTY;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	va_start(ap, zFormat);
 | 
						|
	rc = FormatMount(SXFMT_CONS_PROC, 0, xConsumer, pData, 0, zFormat, ap);
 | 
						|
	va_end(ap);
 | 
						|
	return rc;
 | 
						|
}
 | 
						|
PH7_PRIVATE sxu32 SyBlobFormat(SyBlob *pBlob, const char *zFormat, ...) {
 | 
						|
	va_list ap;
 | 
						|
	sxu32 n;
 | 
						|
#if defined(UNTRUST)
 | 
						|
	if(SX_EMPTY_STR(zFormat)) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	va_start(ap, zFormat);
 | 
						|
	FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
 | 
						|
	va_end(ap);
 | 
						|
	return n;
 | 
						|
}
 | 
						|
PH7_PRIVATE sxu32 SyBlobFormatAp(SyBlob *pBlob, const char *zFormat, va_list ap) {
 | 
						|
	sxu32 n = 0; /* cc warning */
 | 
						|
#if defined(UNTRUST)
 | 
						|
	if(SX_EMPTY_STR(zFormat)) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	FormatMount(SXFMT_CONS_BLOB, &(*pBlob), 0, 0, &n, zFormat, ap);
 | 
						|
	return n;
 | 
						|
}
 | 
						|
PH7_PRIVATE sxu32 SyBufferFormat(char *zBuf, sxu32 nLen, const char *zFormat, ...) {
 | 
						|
	SyBlob sBlob;
 | 
						|
	va_list ap;
 | 
						|
	sxu32 n;
 | 
						|
#if defined(UNTRUST)
 | 
						|
	if(SX_EMPTY_STR(zFormat)) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	if(SXRET_OK != SyBlobInitFromBuf(&sBlob, zBuf, nLen - 1)) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
	va_start(ap, zFormat);
 | 
						|
	FormatMount(SXFMT_CONS_BLOB, &sBlob, 0, 0, 0, zFormat, ap);
 | 
						|
	va_end(ap);
 | 
						|
	n = SyBlobLength(&sBlob);
 | 
						|
	/* Append the null terminator */
 | 
						|
	sBlob.mByte++;
 | 
						|
	SyBlobAppend(&sBlob, "\0", sizeof(char));
 | 
						|
	return n;
 | 
						|
}
 |