Below follows some code I hacked together to do my own snprintf()
*without* having to parse the format string. The performance freaks
among you will frown on my waste of cycles, I'm sure :)
--Sten
/*
* Variants of snprintf() and vsnprintf() with a definable callback
* handler function to catch buffer overflows.
*
* Use it only if you have the "spare" cycles needed to effectively
* do every snprintf operation twice! Why is that? Because everything
* is first vfprintf()'d to /dev/null to determine the number of bytes.
* Perhaps a bit slow for demanding applications on slow machines,
* no problem for a fast machine with some spare cycles.
*
* You don't have a /dev/null? Every Linux contains one for free!
*
* On my Ultra-1 (143Mhz) this implementation of snprintf() runs
* at 45% of the speed of native sprintf(). This leaves my with
* "just" under 100k snprintf()'s per second to play with :-)
*
* Because the format string is never even looked at, all current and
* possible future printf-conversions should be handled just fine.
*
* Written July 1997 by Sten Gunterberg (gunterberg@ergon.ch)
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
static void
default_handler (const char *func, int max, const char *fmt)
{
int len;
fprintf(stderr,
"*** short buffer in %s() with max=%d and format=%s",
func, max, fmt);
if ((len = strlen(fmt)) > 0 && fmt[len-1] != '\n')
fputc('\n', stderr);
abort();
}
static void (*overflow_handler)() = default_handler;
void
set_nprintf_overflow_handler (void (*arg)(const char *, int, const char *))
{
overflow_handler = arg;
}
static int
needed (const char *fmt, va_list argp)
{
static FILE *sink = NULL;
if (sink == NULL)
{
if ((sink = fopen("/dev/null", "w")) == NULL)
{
/* Hmm. Find a better way to handle this! */
fprintf(stderr, "*** failed to open /dev/null ***\n");
abort();
}
}
return vfprintf(sink, fmt, argp);
}
int
snprintf (char *buf, int max, const char *fmt, ...)
{
va_list argp;
int bytes;
va_start(argp, fmt);
if (needed(fmt, argp) > max)
{
(*overflow_handler)("snprintf", max, fmt);
exit(1); /* should never get here, really */
}
bytes = vsprintf(buf, fmt, argp);
va_end(argp);
return bytes;
}
int
vsnprintf (char *buf, int max, const char *fmt, va_list argp)
{
if (needed(fmt, argp) > max)
{
(*overflow_handler)("vsnprintf", max, fmt);
exit(1); /* should never get here, really */
}
return vsprintf(buf, fmt, argp);
}
#ifdef STANDALONE
int
main (int argc, char **argv)
{
char buf[10];
snprintf(buf, 10, "test %d\n", 123); /* works */
snprintf(buf, 10, "test %d\n", 123456); /* fails */
}
#endif