| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 | /* * 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 WINSCP) && !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#endifchar *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);#if defined _DEBUG && defined IDE    // WORKAROUND    // CodeGuard breaks execution when vsnprintf function returns -1.    // Prevent that by making the buffer large enough not to ever return -1.    // (particularly when called from verify_ssh_host_key for keydisp)    sgrowarrayn_nm(buf, size, oldlen, 4096);#endif    while (1) {        va_list aq;        va_copy(aq, ap);        { // WINSCP        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);        }        } // WINSCP    }}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;}
 |