1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/utils/dupprintf.c

101 lines
3.4 KiB
C
Raw Normal View History

/*
* 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;
}