Aer Interpreter Source
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

745 lines
22 KiB

/**
* @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;
};
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' ;
}
/*
* 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 },
{ '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 },
{ '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)
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 */
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:
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;
}
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(xUserCons == 0) {
return SXERR_EMPTY;
}
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(SX_EMPTY_STR(zFormat)) {
return SXERR_EMPTY;
}
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(SX_EMPTY_STR(zFormat)) {
return 0;
}
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(SX_EMPTY_STR(zFormat)) {
return 0;
}
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(SX_EMPTY_STR(zFormat)) {
return 0;
}
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;
}