Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'utils/dupprintf.c')
-rw-r--r--utils/dupprintf.c100
1 files changed, 100 insertions, 0 deletions
diff --git a/utils/dupprintf.c b/utils/dupprintf.c
new file mode 100644
index 00000000..aa9f330b
--- /dev/null
+++ b/utils/dupprintf.c
@@ -0,0 +1,100 @@
+/*
+ * 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;
+}