mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
101 lines
3.4 KiB
C
101 lines
3.4 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|