#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 */
	char *zExtra;
	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;
			}
		}
		zExtra = 0;
		/*
		** 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;
#if 0
				/* Rounding works like BSD when the constant 0.4999 is used.Wierd! */
				for(idx = precision, rounder = 0.4999; idx > 0; idx--, rounder *= 0.1);
#else
				/* It makes more sense to use 0.5 */
				for(idx = precision, rounder = 0.5; idx > 0; idx--, rounder *= 0.1);
#endif
				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;
}