/* ** The "printf" code that follows dates from the 1980's. It is in ** the public domain. The original comments are included here for ** completeness. They are very out-of-date but might be useful as ** an historical reference. Most of the "enhancements" have been backed ** out so that the functionality is now the same as standard printf(). ** ************************************************************************** ** ** The following modules is an enhanced replacement for the "printf" subroutines ** found in the standard C library. The following enhancements are ** supported: ** ** + Additional functions. The standard set of "printf" functions ** includes printf, fprintf, sprintf, vprintf, vfprintf, and ** vsprintf. This module adds the following: ** ** * snprintf -- Works like sprintf, but has an extra argument ** which is the size of the buffer written to. ** ** * mprintf -- Similar to sprintf. Writes output to memory ** obtained from malloc. ** ** * xprintf -- Calls a function to dispose of output. ** ** * nprintf -- No output, but returns the number of characters ** that would have been output by printf. ** ** * A v- version (ex: vsnprintf) of every function is also ** supplied. ** ** + A few extensions to the formatting notation are supported: ** ** * The "=" flag (similar to "-") causes the output to be ** be centered in the appropriately sized field. ** ** * The %b field outputs an integer in binary notation. ** ** * The %c field now accepts a precision. The character output ** is repeated by the number of times the precision specifies. ** ** * The %' field works like %c, but takes as its character the ** next character of the format string, instead of the next ** argument. For example, printf("%.78'-") prints 78 minus ** signs, the same as printf("%.78c",'-'). ** ** + When compiled using GCC on a SPARC, this version of printf is ** faster than the library printf for SUN OS 4.1. ** ** + All functions are fully reentrant. ** */ /* * 20090210 (stkn): * Taken from sqlite-3.3.x, * renamed SQLITE_ -> SWITCH_, * renamed visible functions to switch_* * disabled functions without extra conversion specifiers */ #include #define LONGDOUBLE_TYPE long double /* ** Conversion types fall into various categories as defined by the ** following enumeration. */ #define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ #define etFLOAT 2 /* Floating point. %f */ #define etEXP 3 /* Exponentional notation. %e and %E */ #define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */ #define etSIZE 5 /* Return number of characters processed so far. %n */ #define etSTRING 6 /* Strings. %s */ #define etDYNSTRING 7 /* Dynamically allocated strings. %z */ #define etPERCENT 8 /* Percent symbol. %% */ #define etCHARX 9 /* Characters. %c */ /* The rest are extensions, not normally found in printf() */ #define etCHARLIT 10 /* Literal characters. %' */ #define etSQLESCAPE 11 /* Strings with '\'' doubled. %q */ #define etSQLESCAPE2 12 /* Strings with '\'' doubled and enclosed in '', NULL pointers replaced by SQL NULL. %Q */ #ifdef __UNSUPPORTED__ #define etTOKEN 13 /* a pointer to a Token structure */ #define etSRCLIST 14 /* a pointer to a SrcList */ #endif #define etPOINTER 15 /* The %p conversion */ #define etSQLESCAPE3 16 #define etSQLESCAPE4 17 /* ** An "etByte" is an 8-bit unsigned value. */ typedef unsigned char etByte; /* ** Each builtin conversion character (ex: the 'd' in "%d") is described ** by an instance of the following structure */ typedef struct et_info { /* Information about each format field */ char fmttype; /* The format field code letter */ etByte base; /* The base for radix conversion */ etByte flags; /* One or more of FLAG_ constants below */ etByte type; /* Conversion paradigm */ etByte charset; /* Offset into aDigits[] of the digits string */ etByte prefix; /* Offset into aPrefix[] of the prefix string */ } et_info; /* ** Allowed values for et_info.flags */ #define FLAG_SIGNED 1 /* True if the value to convert is signed */ #define FLAG_INTERN 2 /* True if for internal use only */ #define FLAG_STRING 4 /* Allow infinity precision */ /* ** The following table is searched linearly, so it is good to put the ** most frequently used conversion types first. */ static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; static const char aPrefix[] = "-x0\000X0"; static const et_info fmtinfo[] = { {'d', 10, 1, etRADIX, 0, 0}, {'s', 0, 4, etSTRING, 0, 0}, {'g', 0, 1, etGENERIC, 30, 0}, {'z', 0, 6, etDYNSTRING, 0, 0}, {'q', 0, 4, etSQLESCAPE, 0, 0}, {'Q', 0, 4, etSQLESCAPE2, 0, 0}, {'w', 0, 4, etSQLESCAPE3, 0, 0}, {'y', 0, 4, etSQLESCAPE4, 0, 0}, {'c', 0, 0, etCHARX, 0, 0}, {'o', 8, 0, etRADIX, 0, 2}, {'u', 10, 0, etRADIX, 0, 0}, {'x', 16, 0, etRADIX, 16, 1}, {'X', 16, 0, etRADIX, 0, 4}, #ifndef SWITCH_OMIT_FLOATING_POINT {'f', 0, 1, etFLOAT, 0, 0}, {'e', 0, 1, etEXP, 30, 0}, {'E', 0, 1, etEXP, 14, 0}, {'G', 0, 1, etGENERIC, 14, 0}, #endif {'i', 10, 1, etRADIX, 0, 0}, {'n', 0, 0, etSIZE, 0, 0}, {'%', 0, 0, etPERCENT, 0, 0}, {'p', 16, 0, etPOINTER, 0, 1}, #ifdef __UNSUPPORTED__ {'T', 0, 2, etTOKEN, 0, 0}, {'S', 0, 2, etSRCLIST, 0, 0}, #endif }; #define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0])) /* ** If SWITCH_OMIT_FLOATING_POINT is defined, then none of the floating point ** conversions will work. */ #ifndef SWITCH_OMIT_FLOATING_POINT /* ** "*val" is a double such that 0.1 <= *val < 10.0 ** Return the ascii code for the leading digit of *val, then ** multiply "*val" by 10.0 to renormalize. ** ** Example: ** input: *val = 3.14159 ** output: *val = 1.4159 function return = '3' ** ** The counter *cnt is incremented each time. After counter exceeds ** 16 (the number of significant digits in a 64-bit float) '0' is ** always returned. */ static int et_getdigit(LONGDOUBLE_TYPE * val, int *cnt) { int digit; LONGDOUBLE_TYPE d; if ((*cnt)++ >= 16) return '0'; digit = (int) *val; d = digit; digit += '0'; *val = (*val - d) * 10.0; return digit; } #endif /* SWITCH_OMIT_FLOATING_POINT */ /* ** On machines with a small stack size, you can redefine the ** SWITCH_PRINT_BUF_SIZE to be less than 350. But beware - for ** smaller values some %f conversions may go into an infinite loop. */ #ifndef SWITCH_PRINT_BUF_SIZE # define SWITCH_PRINT_BUF_SIZE 350 #endif #define etBUFSIZE SWITCH_PRINT_BUF_SIZE /* Size of the output buffer */ /* ** The root program. All variations call this core. ** ** INPUTS: ** func This is a pointer to a function taking three arguments ** 1. A pointer to anything. Same as the "arg" parameter. ** 2. A pointer to the list of characters to be output ** (Note, this list is NOT null terminated.) ** 3. An integer number of characters to be output. ** (Note: This number might be zero.) ** ** arg This is the pointer to anything which will be passed as the ** first argument to "func". Use it for whatever you like. ** ** fmt This is the format string, as in the usual print. ** ** ap This is a pointer to a list of arguments. Same as in ** vfprint. ** ** OUTPUTS: ** The return value is the total number of characters sent to ** the function "func". Returns -1 on a error. ** ** Note that the order in which automatic variables are declared below ** seems to make a big difference in determining how fast this beast ** will run. */ static int vxprintf(void (*func) (void *, const char *, int), /* Consumer of text */ void *arg, /* First argument to the consumer */ int useExtended, /* Allow extended %-conversions */ 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 count; /* Total number of characters output */ int width; /* Width of the current field */ etByte flag_leftjustify; /* True if "-" flag is present */ etByte flag_plussign; /* True if "+" flag is present */ etByte flag_blanksign; /* True if " " flag is present */ 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; /* True if "l" flag is present */ etByte flag_longlong; /* True if the "ll" flag is present */ etByte done; /* Loop termination flag */ uint64_t longvalue; /* Value for integer types */ LONGDOUBLE_TYPE realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ char buf[etBUFSIZE]; /* Conversion buffer */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ etByte errorflag = 0; /* True if an error is encountered */ etByte xtype = 0; /* Conversion paradigm */ char *zExtra; /* Extra memory used for etTCLESCAPE conversions */ static const char spaces[] = " "; #define etSPACESIZE (sizeof(spaces)-1) #ifndef SWITCH_OMIT_FLOATING_POINT int exp, e2; /* exponent of real numbers */ 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 */ etByte flag_exp; /* True to force display of the exponent */ int nsd; /* Number of significant digits returned */ #endif func(arg, "", 0); count = length = 0; bufpt = 0; for (; (c = (*fmt)) != 0; ++fmt) { if (c != '%') { int amt; bufpt = (char *) fmt; amt = 1; while ((c = (*++fmt)) != '%' && c != 0) amt++; (*func) (arg, bufpt, amt); count += amt; if (c == 0) break; } if ((c = (*++fmt)) == 0) { errorflag = 1; (*func) (arg, "%", 1); count++; break; } /* Find out what flags are present */ flag_leftjustify = flag_plussign = flag_blanksign = flag_alternateform = flag_altform2 = flag_zeropad = 0; done = 0; do { switch (c) { case '-': flag_leftjustify = 1; break; case '+': flag_plussign = 1; break; case ' ': flag_blanksign = 1; break; case '#': flag_alternateform = 1; break; case '!': flag_altform2 = 1; break; case '0': flag_zeropad = 1; break; default: done = 1; break; } } while (!done && (c = (*++fmt)) != 0); /* Get the field width */ width = 0; if (c == '*') { width = va_arg(ap, int); if (width < 0) { flag_leftjustify = 1; width = -width; } c = *++fmt; } else { while (c >= '0' && c <= '9') { width = width * 10 + c - '0'; c = *++fmt; } } if (width > etBUFSIZE - 10) { width = etBUFSIZE - 10; } /* Get the precision */ if (c == '.') { precision = 0; c = *++fmt; if (c == '*') { precision = va_arg(ap, int); if (precision < 0) precision = -precision; c = *++fmt; } else { while (c >= '0' && c <= '9') { precision = precision * 10 + c - '0'; c = *++fmt; } } } else { precision = -1; } /* Get the conversion type modifier */ if (c == 'l') { flag_long = 1; c = *++fmt; if (c == 'l') { flag_longlong = 1; c = *++fmt; } else { flag_longlong = 0; } } else { flag_long = flag_longlong = 0; } /* Fetch the info entry for the field */ infop = 0; for (idx = 0; idx < etNINFO; idx++) { if (c == fmtinfo[idx].fmttype) { infop = &fmtinfo[idx]; if (useExtended || (infop->flags & FLAG_INTERN) == 0) { xtype = infop->type; } else { return -1; } break; } } zExtra = 0; if (infop == 0) { return -1; } /* Limit the precision to prevent overflowing buf[] during conversion */ if (precision > etBUFSIZE - 40 && (infop->flags & FLAG_STRING) == 0) { precision = etBUFSIZE - 40; } /* ** At this point, variables are initialized as follows: ** ** flag_alternateform TRUE if a '#' is present. ** flag_altform2 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) prefixed ** the conversion character. ** flag_longlong TRUE if the letter 'll' (ell ell) 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 etPOINTER: flag_longlong = sizeof(char *) == sizeof(int64_t); flag_long = sizeof(char *) == sizeof(long int); /* Fall through into the next case */ case etRADIX: if (infop->flags & FLAG_SIGNED) { int64_t v; if (flag_longlong) v = va_arg(ap, int64_t); else if (flag_long) v = va_arg(ap, long int); else v = va_arg(ap, int); if (v < 0) { longvalue = -v; prefix = '-'; } else { longvalue = v; if (flag_plussign) prefix = '+'; else if (flag_blanksign) prefix = ' '; else prefix = 0; } } else { if (flag_longlong) longvalue = va_arg(ap, uint64_t); else if (flag_long) 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); } bufpt = &buf[etBUFSIZE - 1]; { register const char *cset; /* Use registers for speed */ register int base; cset = &aDigits[infop->charset]; base = infop->base; do { /* Convert to ascii */ *(--bufpt) = cset[longvalue % base]; longvalue = longvalue / base; } while (longvalue > 0); } length = (int)(&buf[etBUFSIZE - 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 = &aPrefix[infop->prefix]; if (*bufpt != pre[0]) { for (; (x = (*pre)) != 0; pre++) *(--bufpt) = x; } } length = (int)(&buf[etBUFSIZE - 1] - bufpt); break; case etFLOAT: case etEXP: case etGENERIC: realvalue = va_arg(ap, double); #ifndef SWITCH_OMIT_FLOATING_POINT if (precision < 0) precision = 6; /* Set default precision */ if (precision > etBUFSIZE / 2 - 10) precision = etBUFSIZE / 2 - 10; if (realvalue < 0.0) { realvalue = -realvalue; prefix = '-'; } else { if (flag_plussign) prefix = '+'; else if (flag_blanksign) prefix = ' '; else prefix = 0; } if (xtype == etGENERIC && precision > 0) precision--; #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 (xtype == etFLOAT) realvalue += rounder; /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ exp = 0; if (realvalue > 0.0) { while (realvalue >= 1e32 && exp <= 350) { realvalue *= 1e-32; exp += 32; } 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 == etEXP; 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 = 0; } if (xtype == etEXP) { e2 = 0; } else { e2 = exp; } nsd = 0; flag_dp = (precision > 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++) = (char) 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 > 0; precision--, e2++) { *(bufpt++) = '0'; } /* Significant digits after the decimal point */ while ((precision--) > 0) { *(bufpt++) = (char) 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 > buf); if (bufpt[-1] == '.') { if (flag_altform2) { *(bufpt++) = '0'; } else { *(--bufpt) = 0; } } } /* Add the "eNNN" suffix */ if (flag_exp || (xtype == etEXP && exp)) { *(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++) = 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 - 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; } #endif break; case etSIZE: *(va_arg(ap, int *)) = count; length = width = 0; break; case etPERCENT: buf[0] = '%'; bufpt = buf; length = 1; break; case etCHARLIT: case etCHARX: c = buf[0] = (char) (xtype == etCHARX ? va_arg(ap, int) : *++fmt); if (precision >= 0) { for (idx = 1; idx < precision; idx++) buf[idx] = (char) c; length = precision; } else { length = 1; } bufpt = buf; break; case etSTRING: case etDYNSTRING: bufpt = va_arg(ap, char *); if (bufpt == 0) { bufpt = ""; } else if (xtype == etDYNSTRING) { zExtra = bufpt; } length = (int)strlen(bufpt); if (precision >= 0 && precision < length) length = precision; break; case etSQLESCAPE: case etSQLESCAPE2: case etSQLESCAPE4: case etSQLESCAPE3:{ int i, j, n, ch, isnull; int needQuote; char *escarg = va_arg(ap, char *); isnull = escarg == 0; if (isnull) escarg = (xtype == etSQLESCAPE2 ? "NULL" : "(NULL)"); for (i = n = 0; (ch = escarg[i]) != 0; i++) { if (ch == '\'' || (xtype == etSQLESCAPE3 && ch == '\\')) n++; } needQuote = !isnull && xtype == etSQLESCAPE2; n += i + 1 + needQuote * 2; if (n > etBUFSIZE) { bufpt = zExtra = malloc(n); if (bufpt == 0) return -1; } else { bufpt = buf; } j = 0; if (needQuote) bufpt[j++] = '\''; for (i = 0; (ch = escarg[i]) != 0; i++) { bufpt[j++] = (char) ch; if (xtype == etSQLESCAPE4) { if (ch == '\'' || (xtype == etSQLESCAPE3 && ch == '\\')) { bufpt[j] = (char) ch; bufpt[j-1] = (char) '\\'; j++; } } else { if (ch == '\'' || (xtype == etSQLESCAPE3 && ch == '\\')) bufpt[j++] = (char) ch; } } if (needQuote) bufpt[j++] = '\''; bufpt[j] = 0; length = j; /* The precision is ignored on %q and %Q */ /* if ( precision>=0 && precisionz) { (*func) (arg, (char *) pToken->z, pToken->n); } length = width = 0; break; } case etSRCLIST:{ SrcList *pSrc = va_arg(ap, SrcList *); int k = va_arg(ap, int); struct SrcList_item *pItem = &pSrc->a[k]; assert(k >= 0 && k < pSrc->nSrc); if (pItem->zDatabase && pItem->zDatabase[0]) { (*func) (arg, pItem->zDatabase, strlen(pItem->zDatabase)); (*func) (arg, ".", 1); } (*func) (arg, pItem->zName, strlen(pItem->zName)); length = width = 0; break; } #endif } /* 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) { count += nspace; while (nspace >= etSPACESIZE) { (*func) (arg, spaces, etSPACESIZE); nspace -= etSPACESIZE; } if (nspace > 0) (*func) (arg, spaces, nspace); } } if (length > 0) { (*func) (arg, bufpt, length); count += length; } if (flag_leftjustify) { register int nspace; nspace = width - length; if (nspace > 0) { count += nspace; while (nspace >= etSPACESIZE) { (*func) (arg, spaces, etSPACESIZE); nspace -= etSPACESIZE; } if (nspace > 0) (*func) (arg, spaces, nspace); } } if (zExtra) { free(zExtra); } } /* End for loop over the format string */ return errorflag ? -1 : count; } /* End of function */ /* This structure is used to store state information about the ** write to memory that is currently in progress. */ struct sgMprintf { char *zBase; /* A base allocation */ char *zText; /* The string collected so far */ int nChar; /* Length of the string so far */ int nTotal; /* Output size if unconstrained */ int nAlloc; /* Amount of space allocated in zText */ void *(*xRealloc) (void *, int); /* Function used to realloc memory */ }; /* ** This function implements the callback from vxprintf. ** ** This routine add nNewChar characters of text in zNewText to ** the sgMprintf structure pointed to by "arg". */ static void mout(void *arg, const char *zNewText, int nNewChar) { struct sgMprintf *pM = (struct sgMprintf *) arg; pM->nTotal += nNewChar; if (pM->nChar + nNewChar + 1 > pM->nAlloc) { if (pM->xRealloc == 0) { nNewChar = pM->nAlloc - pM->nChar - 1; } else { pM->nAlloc = pM->nChar + nNewChar * 2 + 1; if (pM->zText == pM->zBase) { pM->zText = pM->xRealloc(0, pM->nAlloc); if (pM->zText && pM->nChar) { memcpy(pM->zText, pM->zBase, pM->nChar); } } else { char *zNew; zNew = pM->xRealloc(pM->zText, pM->nAlloc); if (zNew) { pM->zText = zNew; } } } } if (pM->zText) { if (nNewChar > 0) { memcpy(&pM->zText[pM->nChar], zNewText, nNewChar); pM->nChar += nNewChar; } pM->zText[pM->nChar] = 0; } } /* ** This routine is a wrapper around xprintf() that invokes mout() as ** the consumer. */ static char *base_vprintf(void *(*xRealloc) (void *, int), /* Routine to realloc memory. May be NULL */ int useInternal, /* Use internal %-conversions if true */ char *zInitBuf, /* Initially write here, before mallocing */ int nInitBuf, /* Size of zInitBuf[] */ const char *zFormat, /* format string */ va_list ap /* arguments */ ) { struct sgMprintf sM; sM.zBase = sM.zText = zInitBuf; sM.nChar = sM.nTotal = 0; sM.nAlloc = nInitBuf; sM.xRealloc = xRealloc; vxprintf(mout, &sM, useInternal, zFormat, ap); if (xRealloc) { if (sM.zText == sM.zBase) { sM.zText = xRealloc(0, sM.nChar + 1); if (sM.zText) { memcpy(sM.zText, sM.zBase, sM.nChar + 1); } } else if (sM.nAlloc > sM.nChar + 10) { char *zNew = xRealloc(sM.zText, sM.nChar + 1); if (zNew) { sM.zText = zNew; } } } return sM.zText; } /* ** Realloc that is a real function, not a macro. */ static void *printf_realloc(void *old, int size) { return realloc(old, size); } /* ** Print into memory. Omit the internal %-conversion extensions. */ SWITCH_DECLARE(char *) switch_vmprintf(const char *zFormat, va_list ap) { char zBase[SWITCH_PRINT_BUF_SIZE]; return base_vprintf(printf_realloc, 0, zBase, sizeof(zBase), zFormat, ap); } /* ** Print into memory. Omit the internal %-conversion extensions. */ SWITCH_DECLARE(char *) switch_mprintf(const char *zFormat, ...) { va_list ap; char *z; char zBase[SWITCH_PRINT_BUF_SIZE]; va_start(ap, zFormat); z = base_vprintf(printf_realloc, 0, zBase, sizeof(zBase), zFormat, ap); va_end(ap); return z; } /* ** sqlite3_snprintf() works like snprintf() except that it ignores the ** current locale settings. This is important for SQLite because we ** are not able to use a "," as the decimal point in place of "." as ** specified by some locales. */ SWITCH_DECLARE(char *) switch_snprintfv(char *zBuf, int n, const char *zFormat, ...) { char *z; va_list ap; va_start(ap, zFormat); z = base_vprintf(0, 0, zBuf, n, zFormat, ap); va_end(ap); return z; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:t * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: */