123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- /*
- * Do an sprintf(), but into a custom-allocated buffer.
- *
- * Currently I'm doing this via vsnprintf. This has worked so far,
- * but it's not good, because vsnprintf is not available on all
- * platforms. There's an ifdef to use `_vsnprintf', which seems
- * to be the local name for it on Windows. Other platforms may
- * lack it completely, in which case it'll be time to rewrite
- * this function in a totally different way.
- *
- * The only `properly' portable solution I can think of is to
- * implement my own format string scanner, which figures out an
- * upper bound for the length of each formatting directive,
- * allocates the buffer as it goes along, and calls sprintf() to
- * actually process each directive. If I ever need to actually do
- * this, some caveats:
- *
- * - It's very hard to find a reliable upper bound for
- * floating-point values. %f, in particular, when supplied with
- * a number near to the upper or lower limit of representable
- * numbers, could easily take several hundred characters. It's
- * probably feasible to predict this statically using the
- * constants in <float.h>, or even to predict it dynamically by
- * looking at the exponent of the specific float provided, but
- * it won't be fun.
- *
- * - Don't forget to _check_, after calling sprintf, that it's
- * used at most the amount of space we had available.
- *
- * - Fault any formatting directive we don't fully understand. The
- * aim here is to _guarantee_ that we never overflow the buffer,
- * because this is a security-critical function. If we see a
- * directive we don't know about, we should panic and die rather
- * than run any risk.
- */
- #include <stdio.h>
- #include "defs.h"
- #include "misc.h"
- #include "utils/utils.h"
- /* Work around lack of va_copy in old MSC */
- #if defined _MSC_VER && !defined va_copy
- #define va_copy(a, b) TYPECHECK( \
- (va_list *)0 == &(a) && (va_list *)0 == &(b), \
- memcpy(&a, &b, sizeof(va_list)))
- #endif
- /* Also lack of vsnprintf before VS2015 */
- #if defined _WINDOWS && \
- !defined __MINGW32__ && \
- !defined __WINE__ && \
- _MSC_VER < 1900
- #define vsnprintf _vsnprintf
- #endif
- char *dupvprintf_inner(char *buf, size_t oldlen, size_t *sizeptr,
- const char *fmt, va_list ap)
- {
- size_t size = *sizeptr;
- sgrowarrayn_nm(buf, size, oldlen, 512);
- while (1) {
- va_list aq;
- va_copy(aq, ap);
- int len = vsnprintf(buf + oldlen, size - oldlen, fmt, aq);
- va_end(aq);
- if (len >= 0 && len < size) {
- /* This is the C99-specified criterion for snprintf to have
- * been completely successful. */
- *sizeptr = size;
- return buf;
- } else if (len > 0) {
- /* This is the C99 error condition: the returned length is
- * the required buffer size not counting the NUL. */
- sgrowarrayn_nm(buf, size, oldlen + 1, len);
- } else {
- /* This is the pre-C99 glibc error condition: <0 means the
- * buffer wasn't big enough, so we enlarge it a bit and hope. */
- sgrowarray_nm(buf, size, size);
- }
- }
- }
- char *dupvprintf(const char *fmt, va_list ap)
- {
- size_t size = 0;
- return dupvprintf_inner(NULL, 0, &size, fmt, ap);
- }
- char *dupprintf(const char *fmt, ...)
- {
- char *ret;
- va_list ap;
- va_start(ap, fmt);
- ret = dupvprintf(fmt, ap);
- va_end(ap);
- return ret;
- }
|