in sqlite/sqlite3_debug.c [28486:29180]
SQLITE_API void SQLITE_APICALL sqlite3_str_vappendf(
sqlite3_str *pAccum, /* Accumulate results here */
const char *fmt, /* Format string */
va_list ap /* arguments */
){
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 */
etByte flag_leftjustify; /* True if "-" flag is present */
etByte flag_prefix; /* '+' or ' ' or 0 for prefix */
etByte flag_alternateform; /* True if "#" flag is present */
etByte flag_altform2; /* True if "!" flag is present */
etByte flag_zeropad; /* True if field width constant starts with zero */
etByte flag_long; /* 1 for the "l" flag, 2 for "ll", 0 by default */
etByte done; /* Loop termination flag */
etByte cThousand; /* Thousands separator for %d and %u */
etByte xtype = etINVALID; /* Conversion paradigm */
u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
sqlite_uint64 longvalue; /* Value for integer types */
LONGDOUBLE_TYPE realvalue; /* Value for real types */
const et_info *infop; /* Pointer to the appropriate info structure */
char *zOut; /* Rendering buffer */
int nOut; /* Size of the rendering buffer */
char *zExtra = 0; /* Malloced memory used by some conversion */
#ifndef SQLITE_OMIT_FLOATING_POINT
int exp, e2; /* exponent of real numbers */
int nsd; /* Number of significant digits returned */
double rounder; /* Used for rounding floating point values */
etByte flag_dp; /* True if decimal point should be shown */
etByte flag_rtz; /* True if trailing zeros should be removed */
#endif
PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
char buf[etBUFSIZE]; /* Conversion buffer */
/* pAccum never starts out with an empty buffer that was obtained from
** malloc(). This precondition is required by the mprintf("%z...")
** optimization. */
assert( pAccum->nChar>0 || (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
bufpt = 0;
if( (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC)!=0 ){
pArgList = va_arg(ap, PrintfArguments*);
bArgList = 1;
}else{
bArgList = 0;
}
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
bufpt = (char *)fmt;
#if HAVE_STRCHRNUL
fmt = strchrnul(fmt, '%');
#else
do{ fmt++; }while( *fmt && *fmt != '%' );
#endif
sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
if( *fmt==0 ) break;
}
if( (c=(*++fmt))==0 ){
sqlite3_str_append(pAccum, "%", 1);
break;
}
/* Find out what flags are present */
flag_leftjustify = flag_prefix = cThousand =
flag_alternateform = flag_altform2 = flag_zeropad = 0;
done = 0;
width = 0;
flag_long = 0;
precision = -1;
do{
switch( c ){
case '-': flag_leftjustify = 1; break;
case '+': flag_prefix = '+'; break;
case ' ': flag_prefix = ' '; break;
case '#': flag_alternateform = 1; break;
case '!': flag_altform2 = 1; break;
case '0': flag_zeropad = 1; break;
case ',': cThousand = ','; break;
default: done = 1; break;
case 'l': {
flag_long = 1;
c = *++fmt;
if( c=='l' ){
c = *++fmt;
flag_long = 2;
}
done = 1;
break;
}
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': {
unsigned wx = c - '0';
while( (c = *++fmt)>='0' && c<='9' ){
wx = wx*10 + c - '0';
}
testcase( wx>0x7fffffff );
width = wx & 0x7fffffff;
#ifdef SQLITE_PRINTF_PRECISION_LIMIT
if( width>SQLITE_PRINTF_PRECISION_LIMIT ){
width = SQLITE_PRINTF_PRECISION_LIMIT;
}
#endif
if( c!='.' && c!='l' ){
done = 1;
}else{
fmt--;
}
break;
}
case '*': {
if( bArgList ){
width = (int)getIntArg(pArgList);
}else{
width = va_arg(ap,int);
}
if( width<0 ){
flag_leftjustify = 1;
width = width >= -2147483647 ? -width : 0;
}
#ifdef SQLITE_PRINTF_PRECISION_LIMIT
if( width>SQLITE_PRINTF_PRECISION_LIMIT ){
width = SQLITE_PRINTF_PRECISION_LIMIT;
}
#endif
if( (c = fmt[1])!='.' && c!='l' ){
c = *++fmt;
done = 1;
}
break;
}
case '.': {
c = *++fmt;
if( c=='*' ){
if( bArgList ){
precision = (int)getIntArg(pArgList);
}else{
precision = va_arg(ap,int);
}
if( precision<0 ){
precision = precision >= -2147483647 ? -precision : -1;
}
c = *++fmt;
}else{
unsigned px = 0;
while( c>='0' && c<='9' ){
px = px*10 + c - '0';
c = *++fmt;
}
testcase( px>0x7fffffff );
precision = px & 0x7fffffff;
}
#ifdef SQLITE_PRINTF_PRECISION_LIMIT
if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){
precision = SQLITE_PRINTF_PRECISION_LIMIT;
}
#endif
if( c=='l' ){
--fmt;
}else{
done = 1;
}
break;
}
}
}while( !done && (c=(*++fmt))!=0 );
/* Fetch the info entry for the field */
infop = &fmtinfo[0];
xtype = etINVALID;
for(idx=0; idx<ArraySize(fmtinfo); idx++){
if( c==fmtinfo[idx].fmttype ){
infop = &fmtinfo[idx];
xtype = infop->type;
break;
}
}
/*
** At this point, variables are initialized as follows:
**
** flag_alternateform TRUE if a '#' is present.
** flag_altform2 TRUE if a '!' is present.
** flag_prefix '+' or ' ' or zero
** 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 1 for "l", 2 for "ll"
** 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.
*/
assert( width>=0 );
assert( precision>=(-1) );
switch( xtype ){
case etPOINTER:
flag_long = sizeof(char*)==sizeof(i64) ? 2 :
sizeof(char*)==sizeof(long int) ? 1 : 0;
/* no break */ deliberate_fall_through
case etORDINAL:
case etRADIX:
cThousand = 0;
/* no break */ deliberate_fall_through
case etDECIMAL:
if( infop->flags & FLAG_SIGNED ){
i64 v;
if( bArgList ){
v = getIntArg(pArgList);
}else if( flag_long ){
if( flag_long==2 ){
v = va_arg(ap,i64) ;
}else{
v = va_arg(ap,long int);
}
}else{
v = va_arg(ap,int);
}
if( v<0 ){
testcase( v==SMALLEST_INT64 );
testcase( v==(-1) );
longvalue = ~v;
longvalue++;
prefix = '-';
}else{
longvalue = v;
prefix = flag_prefix;
}
}else{
if( bArgList ){
longvalue = (u64)getIntArg(pArgList);
}else if( flag_long ){
if( flag_long==2 ){
longvalue = va_arg(ap,u64);
}else{
longvalue = va_arg(ap,unsigned long int);
}
}else{
longvalue = va_arg(ap,unsigned int);
}
prefix = 0;
}
if( longvalue==0 ) flag_alternateform = 0;
if( flag_zeropad && precision<width-(prefix!=0) ){
precision = width-(prefix!=0);
}
if( precision<etBUFSIZE-10-etBUFSIZE/3 ){
nOut = etBUFSIZE;
zOut = buf;
}else{
u64 n;
n = (u64)precision + 10;
if( cThousand ) n += precision/3;
zOut = zExtra = printfTempBuf(pAccum, n);
if( zOut==0 ) return;
nOut = (int)n;
}
bufpt = &zOut[nOut-1];
if( xtype==etORDINAL ){
static const char zOrd[] = "thstndrd";
int x = (int)(longvalue % 10);
if( x>=4 || (longvalue/10)%10==1 ){
x = 0;
}
*(--bufpt) = zOrd[x*2+1];
*(--bufpt) = zOrd[x*2];
}
{
const char *cset = &aDigits[infop->charset];
u8 base = infop->base;
do{ /* Convert to ascii */
*(--bufpt) = cset[longvalue%base];
longvalue = longvalue/base;
}while( longvalue>0 );
}
length = (int)(&zOut[nOut-1]-bufpt);
while( precision>length ){
*(--bufpt) = '0'; /* Zero pad */
length++;
}
if( cThousand ){
int nn = (length - 1)/3; /* Number of "," to insert */
int ix = (length - 1)%3 + 1;
bufpt -= nn;
for(idx=0; nn>0; idx++){
bufpt[idx] = bufpt[idx+nn];
ix--;
if( ix==0 ){
bufpt[++idx] = cThousand;
nn--;
ix = 3;
}
}
}
if( prefix ) *(--bufpt) = prefix; /* Add sign */
if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
const char *pre;
char x;
pre = &aPrefix[infop->prefix];
for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
}
length = (int)(&zOut[nOut-1]-bufpt);
break;
case etFLOAT:
case etEXP:
case etGENERIC:
if( bArgList ){
realvalue = getDoubleArg(pArgList);
}else{
realvalue = va_arg(ap,double);
}
#ifdef SQLITE_OMIT_FLOATING_POINT
length = 0;
#else
if( precision<0 ) precision = 6; /* Set default precision */
#ifdef SQLITE_FP_PRECISION_LIMIT
if( precision>SQLITE_FP_PRECISION_LIMIT ){
precision = SQLITE_FP_PRECISION_LIMIT;
}
#endif
if( realvalue<0.0 ){
realvalue = -realvalue;
prefix = '-';
}else{
prefix = flag_prefix;
}
if( xtype==etGENERIC && precision>0 ) precision--;
testcase( precision>0xfff );
idx = precision & 0xfff;
rounder = arRound[idx%10];
while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; }
if( xtype==etFLOAT ){
double rx = (double)realvalue;
sqlite3_uint64 u;
int ex;
memcpy(&u, &rx, sizeof(u));
ex = -1023 + (int)((u>>52)&0x7ff);
if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16;
realvalue += rounder;
}
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
exp = 0;
if( sqlite3IsNaN((double)realvalue) ){
bufpt = "NaN";
length = 3;
break;
}
if( realvalue>0.0 ){
LONGDOUBLE_TYPE scale = 1.0;
while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;}
while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; }
while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
realvalue /= scale;
while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
if( exp>350 ){
bufpt = buf;
buf[0] = prefix;
memcpy(buf+(prefix!=0),"Inf",4);
length = 3+(prefix!=0);
break;
}
}
bufpt = buf;
/*
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
if( xtype!=etFLOAT ){
realvalue += rounder;
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
}
if( xtype==etGENERIC ){
flag_rtz = !flag_alternateform;
if( exp<-4 || exp>precision ){
xtype = etEXP;
}else{
precision = precision - exp;
xtype = etFLOAT;
}
}else{
flag_rtz = flag_altform2;
}
if( xtype==etEXP ){
e2 = 0;
}else{
e2 = exp;
}
{
i64 szBufNeeded; /* Size of a temporary buffer needed */
szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15;
if( szBufNeeded > etBUFSIZE ){
bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded);
if( bufpt==0 ) return;
}
}
zOut = bufpt;
nsd = 16 + flag_altform2*10;
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
/* The sign in front of the number */
if( prefix ){
*(bufpt++) = prefix;
}
/* Digits prior to the decimal point */
if( e2<0 ){
*(bufpt++) = '0';
}else{
for(; e2>=0; e2--){
*(bufpt++) = et_getdigit(&realvalue,&nsd);
}
}
/* The decimal point */
if( flag_dp ){
*(bufpt++) = '.';
}
/* "0" digits after the decimal point but before the first
** significant digit of the number */
for(e2++; e2<0; precision--, e2++){
assert( precision>0 );
*(bufpt++) = '0';
}
/* Significant digits after the decimal point */
while( (precision--)>0 ){
*(bufpt++) = et_getdigit(&realvalue,&nsd);
}
/* Remove trailing zeros and the "." if no digits follow the "." */
if( flag_rtz && flag_dp ){
while( bufpt[-1]=='0' ) *(--bufpt) = 0;
assert( bufpt>zOut );
if( bufpt[-1]=='.' ){
if( flag_altform2 ){
*(bufpt++) = '0';
}else{
*(--bufpt) = 0;
}
}
}
/* Add the "eNNN" suffix */
if( xtype==etEXP ){
*(bufpt++) = aDigits[infop->charset];
if( exp<0 ){
*(bufpt++) = '-'; 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 */
}
*bufpt = 0;
/* 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 = (int)(bufpt-zOut);
bufpt = zOut;
/* 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;
}
#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */
break;
case etSIZE:
if( !bArgList ){
*(va_arg(ap,int*)) = pAccum->nChar;
}
length = width = 0;
break;
case etPERCENT:
buf[0] = '%';
bufpt = buf;
length = 1;
break;
case etCHARX:
if( bArgList ){
bufpt = getTextArg(pArgList);
length = 1;
if( bufpt ){
buf[0] = c = *(bufpt++);
if( (c&0xc0)==0xc0 ){
while( length<4 && (bufpt[0]&0xc0)==0x80 ){
buf[length++] = *(bufpt++);
}
}
}else{
buf[0] = 0;
}
}else{
unsigned int ch = va_arg(ap,unsigned int);
if( ch<0x00080 ){
buf[0] = ch & 0xff;
length = 1;
}else if( ch<0x00800 ){
buf[0] = 0xc0 + (u8)((ch>>6)&0x1f);
buf[1] = 0x80 + (u8)(ch & 0x3f);
length = 2;
}else if( ch<0x10000 ){
buf[0] = 0xe0 + (u8)((ch>>12)&0x0f);
buf[1] = 0x80 + (u8)((ch>>6) & 0x3f);
buf[2] = 0x80 + (u8)(ch & 0x3f);
length = 3;
}else{
buf[0] = 0xf0 + (u8)((ch>>18) & 0x07);
buf[1] = 0x80 + (u8)((ch>>12) & 0x3f);
buf[2] = 0x80 + (u8)((ch>>6) & 0x3f);
buf[3] = 0x80 + (u8)(ch & 0x3f);
length = 4;
}
}
if( precision>1 ){
width -= precision-1;
if( width>1 && !flag_leftjustify ){
sqlite3_str_appendchar(pAccum, width-1, ' ');
width = 0;
}
while( precision-- > 1 ){
sqlite3_str_append(pAccum, buf, length);
}
}
bufpt = buf;
flag_altform2 = 1;
goto adjust_width_for_utf8;
case etSTRING:
case etDYNSTRING:
if( bArgList ){
bufpt = getTextArg(pArgList);
xtype = etSTRING;
}else{
bufpt = va_arg(ap,char*);
}
if( bufpt==0 ){
bufpt = "";
}else if( xtype==etDYNSTRING ){
if( pAccum->nChar==0
&& pAccum->mxAlloc
&& width==0
&& precision<0
&& pAccum->accError==0
){
/* Special optimization for sqlite3_mprintf("%z..."):
** Extend an existing memory allocation rather than creating
** a new one. */
assert( (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
pAccum->zText = bufpt;
pAccum->nAlloc = sqlite3DbMallocSize(pAccum->db, bufpt);
pAccum->nChar = 0x7fffffff & (int)strlen(bufpt);
pAccum->printfFlags |= SQLITE_PRINTF_MALLOCED;
length = 0;
break;
}
zExtra = bufpt;
}
if( precision>=0 ){
if( flag_altform2 ){
/* Set length to the number of bytes needed in order to display
** precision characters */
unsigned char *z = (unsigned char*)bufpt;
while( precision-- > 0 && z[0] ){
SQLITE_SKIP_UTF8(z);
}
length = (int)(z - (unsigned char*)bufpt);
}else{
for(length=0; length<precision && bufpt[length]; length++){}
}
}else{
length = 0x7fffffff & (int)strlen(bufpt);
}
adjust_width_for_utf8:
if( flag_altform2 && width>0 ){
/* Adjust width to account for extra bytes in UTF-8 characters */
int ii = length - 1;
while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++;
}
break;
case etSQLESCAPE: /* %q: Escape ' characters */
case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */
case etSQLESCAPE3: { /* %w: Escape " characters */
int i, j, k, n, isnull;
int needQuote;
char ch;
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
char *escarg;
if( bArgList ){
escarg = getTextArg(pArgList);
}else{
escarg = va_arg(ap,char*);
}
isnull = escarg==0;
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
/* For %q, %Q, and %w, the precision is the number of bytes (or
** characters if the ! flags is present) to use from the input.
** Because of the extra quoting characters inserted, the number
** of output characters may be larger than the precision.
*/
k = precision;
for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){
if( ch==q ) n++;
if( flag_altform2 && (ch&0xc0)==0xc0 ){
while( (escarg[i+1]&0xc0)==0x80 ){ i++; }
}
}
needQuote = !isnull && xtype==etSQLESCAPE2;
n += i + 3;
if( n>etBUFSIZE ){
bufpt = zExtra = printfTempBuf(pAccum, n);
if( bufpt==0 ) return;
}else{
bufpt = buf;
}
j = 0;
if( needQuote ) bufpt[j++] = q;
k = i;
for(i=0; i<k; i++){
bufpt[j++] = ch = escarg[i];
if( ch==q ) bufpt[j++] = ch;
}
if( needQuote ) bufpt[j++] = q;
bufpt[j] = 0;
length = j;
goto adjust_width_for_utf8;
}
case etTOKEN: {
Token *pToken;
if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
pToken = va_arg(ap, Token*);
assert( bArgList==0 );
if( pToken && pToken->n ){
sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n);
}
length = width = 0;
break;
}
case etSRCLIST: {
SrcList *pSrc;
int k;
struct SrcList_item *pItem;
if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
pSrc = va_arg(ap, SrcList*);
k = va_arg(ap, int);
pItem = &pSrc->a[k];
assert( bArgList==0 );
assert( k>=0 && k<pSrc->nSrc );
if( pItem->zDatabase ){
sqlite3_str_appendall(pAccum, pItem->zDatabase);
sqlite3_str_append(pAccum, ".", 1);
}
sqlite3_str_appendall(pAccum, pItem->zName);
length = width = 0;
break;
}
default: {
assert( xtype==etINVALID );
return;
}
}/* 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. Both length and width are in bytes, not characters,
** at this point. If the "!" flag was present on string conversions
** indicating that width and precision should be expressed in characters,
** then the values have been translated prior to reaching this point.
*/
width -= length;
if( width>0 ){
if( !flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
sqlite3_str_append(pAccum, bufpt, length);
if( flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
}else{
sqlite3_str_append(pAccum, bufpt, length);
}
if( zExtra ){
sqlite3DbFree(pAccum->db, zExtra);
zExtra = 0;
}
}/* End for loop over the format string */
} /* End of function */