diff --git a/mongoose.c b/mongoose.c index 48076686..db714e1b 100644 --- a/mongoose.c +++ b/mongoose.c @@ -1467,15 +1467,38 @@ int mg_write(struct mg_connection *conn, const void *buf, size_t len) { } int mg_printf(struct mg_connection *conn, const char *fmt, ...) { - char buf[MG_BUF_LEN]; + char mem[MG_BUF_LEN], *buf = mem; int len; va_list ap; + // Print in a local buffer first, hoping that it is large enough to + // hold the whole message va_start(ap, fmt); - len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap); + len = vsnprintf(mem, sizeof(mem), fmt, ap); va_end(ap); - return mg_write(conn, buf, (size_t)len); + if (len <= 0) { + // vsnprintf() error, give up + len = -1; + cry(conn, "%s(%s, ...): vsnprintf() error", __func__, fmt); + } else if (len > (int) sizeof(mem) && (buf = malloc(len + 1)) != NULL) { + // Local buffer is not large enough, allocate big buffer on heap + va_start(ap, fmt); + vsnprintf(buf, len + 1, fmt, ap); + va_end(ap); + len = mg_write(conn, buf, (size_t) len); + free(buf); + } else if (len > (int) sizeof(mem)) { + // Failed to allocate large enough buffer, give up + cry(conn, "%s(%s, ...): Can't allocate %d bytes, not printing anything", + __func__, fmt, len); + len = -1; + } else { + // Copy to the local buffer succeeded + len = mg_write(conn, buf, (size_t) len); + } + + return len; } // URL-decode input buffer into destination buffer. diff --git a/mongoose.h b/mongoose.h index f48cdb05..446a5c13 100644 --- a/mongoose.h +++ b/mongoose.h @@ -163,14 +163,29 @@ int mg_write(struct mg_connection *, const void *buf, size_t len); // Send data to the browser using printf() semantics. // // Works exactly like mg_write(), but allows to do message formatting. -// Note that mg_printf() uses internal buffer of size IO_BUF_SIZE -// (8 Kb by default) as temporary message storage for formatting. Do not -// print data that is bigger than that, otherwise it will be truncated. -int mg_printf(struct mg_connection *, const char *fmt, ...) -#ifdef __GNUC__ -__attribute__((format(printf, 2, 3))) +// Below are the macros for enabling compiler-specific checks for +// printf-like arguments. + +#undef PRINTF_FORMAT_STRING +#if _MSC_VER >= 1400 +#include +#if _MSC_VER > 1400 +#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s +#else +#define PRINTF_FORMAT_STRING(s) __format_string s #endif -; +#else +#define PRINTF_FORMAT_STRING(s) s +#endif + +#ifdef __GNUC__ +#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y))) +#else +#define PRINTF_ARGS(x, y) +#endif + +int mg_printf(struct mg_connection *, + PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); // Send contents of the entire file together with HTTP headers.