/* * snprintf() / vsnprintf() re-implementation by Frank Denis * * These functions understand : * - characters ("%c") . * - space padding ("%3d", "%-3s") . * - zero padding ("%04d") . * - explicit '+' ("%+d", "%+3.2f") . * - length restrictions ("%.30s", "%-42.30s", "%.4d") . * - int, long, long long types ("%lld", "%-3ld", "%i") . * - unsigned int, long, long long types ("%llu", "%-3lu", "%u") . * - hex and octal unsigned types ("%llX", "%04x", "%-3o") . * - double and long double types ("%f", "%Lf") . * - floating point frac restrictions ("%.2f") . * - combinations of everything ("%-8.5llo") . * * Nothing more. Return value is if an overflow occured, or the * copied size if no overflow occured (mostly compatible with C99 * snprintf() behavior, except that it doesn't return any value larger * than ). * * These functions are portable, they are twice faster than their BSD and GNU * implementations, and they don't tamper with errno. But they only know * a limited subset of what a full-implementation is supposed to do. * * It's enough for our needs, though. */ #include #include "qscan.h" #ifdef WITH_DMALLOC # include #endif #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) /* * add a string to the buffer * \param zero if this is non-zero, we pad with zeroes, else we pad * with a blank. * \param maxlen sets the maximum size of the string to be added */ static void fakesnprintf_addstr(char **str, size_t *size, const char *pnt, size_t maxlen, size_t padlen, unsigned char zero, unsigned char minuspad) { size_t maxlenc; /* prepare to cut off string if longer than maxlen */ maxlenc = strlen(pnt); if (maxlen > 0U && maxlen < maxlenc) { maxlenc = maxlen; } if (padlen > 0U && minuspad == 0U && padlen > maxlenc) { size_t maxlenp = padlen - maxlenc; if (maxlenp > *size) { maxlenp = *size; } if (maxlenp > 0U) { memset(*str, zero != 0 ? '0' : ' ', maxlenp); (*size) -= maxlenp; (*str) += maxlenp; } } if (maxlenc > *size) { maxlenc = *size; } if (maxlenc > 0U) { memcpy(*str, pnt, maxlenc); (*size) -= maxlenc; (*str) += maxlenc; } if (padlen > 0U && minuspad > 0U && padlen > maxlenc) { size_t maxlenp = padlen - maxlenc; if (maxlenp > *size) { maxlenp = *size; } if (maxlenp > 0U) { memset(*str, ' ', maxlenp); (*size) -= maxlenp; (*str) += maxlenp; } } } int fakesnprintf_vsnprintf(char * const str_, const size_t size_, const char *format, va_list va) { char *str; size_t size; size_t maxlen; size_t padlen; unsigned char longs; unsigned char zero; unsigned char minuspad; unsigned char hasmaxlen; unsigned char plussign; str = str_; size = size_; str_[size_ - 1U] = 1; while (size > 0U && *format != 0) { if (*format != '%') { *str++ = *format++; size--; continue; } longs = 0U; zero = 0U; minuspad = 0U; maxlen = 0U; padlen = 0U; hasmaxlen = 0U; plussign = 0U; for (;;) { breakpoint_nextspecial_inc: format++; breakpoint_nextspecial_noinc: switch (*format) { case 0: goto breakpoint_end; case '%': *str++ = '%'; size--; goto breakpoint_nextspecial_inc; case 'c': { int val; val = va_arg(va, int); *str++ = (char) val; size--; goto breakpoint_nextspecial_inc; } case 'l': case 'L': longs++; goto breakpoint_nextspecial_inc; case '0': zero++; goto breakpoint_nextspecial_inc; case '.': format++; hasmaxlen = 1U; while ((unsigned char) *format >= '0' && (unsigned char) *format <= '9') { maxlen *= 10U; maxlen += (*format - '0'); format++; } goto breakpoint_nextspecial_noinc; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': do { padlen *= 10U; padlen += *format - '0'; format++; } while ((unsigned char) *format >= '0' && (unsigned char) *format <= '9'); goto breakpoint_nextspecial_noinc; case '-': minuspad++; format++; goto breakpoint_nextspecial_noinc; case '+': plussign++; format++; goto breakpoint_nextspecial_noinc; case 's': { const char *pnt; pnt = va_arg(va, const char *); if (pnt == NULL) { pnt = ""; } fakesnprintf_addstr(&str, &size, pnt, maxlen, padlen, zero, minuspad); goto breakpoint_next; } case 'u': case 'o': case 'x': case 'X': { unsigned long long val; char vals[256]; char *valspnt = vals + sizeof vals; const char *basics; unsigned int base; switch (longs) { case 2: val = va_arg(va, unsigned long long); break; case 1: val = (unsigned long long) va_arg(va, unsigned long); break; default: val = (unsigned long long) va_arg(va, unsigned int); } basics = "0123456789abcdef"; switch (*format) { case 'o': base = 8U; break; case 'X': basics = "0123456789ABCDEF"; case 'x': base = 16U; break; default: base = 10U; } *--valspnt = 0; do { *--valspnt = basics[val % base]; val /= base; } while (valspnt != &vals[0] && val > 0ULL); fakesnprintf_addstr(&str, &size, valspnt, maxlen, padlen, zero, minuspad); goto breakpoint_next; } case 'd': case 'i': { long long val; unsigned char minussign = 0U; char vals[256]; char *valspnt = vals + sizeof vals; switch (longs) { case 2: val = va_arg(va, long long); break; case 1: val = (long long) va_arg(va, long); break; default: val = (long long) va_arg(va, int); } if (val < 0LL) { minussign++; val = -val; } *--valspnt = 0; do { *--valspnt = "0123456789"[val % 10LL]; val /= 10LL; } while (valspnt != &vals[1] && val > 0LL); if (minussign != 0) { *--valspnt = '-'; } else if (plussign != 0) { *--valspnt = '+'; } fakesnprintf_addstr(&str, &size, valspnt, maxlen, padlen, zero, minuspad); goto breakpoint_next; } case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': { unsigned int nfrac = 6U; long double val; unsigned long long vali; unsigned char minussign = 0U; char vals[512]; char *valspnt = vals + sizeof vals / 2U; char *valsleft; if (longs != 0) { val = va_arg(va, long double); } else { val = va_arg(va, double); } if (val < 0.0L) { minussign++; val = -val; } vali = (unsigned long long) val; do { *--valspnt = '0' + vali % 10ULL; vali /= 10ULL; } while (valspnt != &vals[1] && vali > 0ULL); if (minussign != 0) { *--valspnt = '-'; } else if (plussign != 0) { *--valspnt = '+'; } valsleft = valspnt; valspnt = vals + sizeof vals / 2U; if (maxlen > (sizeof vals / 2U) - 3U) { nfrac = (sizeof vals / 2U) - 3U; } else if (hasmaxlen != 0U) { nfrac = maxlen; } if (nfrac > 0U) { *valspnt++ = '.'; } while (nfrac > 0U) { nfrac--; val *= 10.0L; *valspnt++ = '0' + (((unsigned long long) val) % 10U); } *valspnt = 0; fakesnprintf_addstr(&str, &size, valsleft, sizeof vals, padlen, zero, minuspad); goto breakpoint_next; } } } breakpoint_next: format++; } breakpoint_end: if (str_[size_ - 1U] != 1) { str_[size_ - 1U] = 0; return (int) size_; } *str = 0; return (int) (size_ - size); } int fakesnprintf_snprintf(char * const str, const size_t size, const char * const format, ...) { int ret; va_list va; va_start(va, format); ret = fakesnprintf_vsnprintf(str, size, format, va); va_end(va); return ret; } #endif /* !HAVE_SNPRINTF */