From c9036f3ac091a31f25ae10e10384a885fb5e9a82 Mon Sep 17 00:00:00 2001
From: Sergey Lyubka <valenok@gmail.com>
Date: Wed, 5 Feb 2014 12:33:36 +0000
Subject: [PATCH] Added workaround for non-compliant runtimes in
 alloc_vprintf()

---
 mongoose.c  | 37 ++++++++++++++++++++++++-------------
 unit_test.c | 25 +++++++++++++++++++++++++
 2 files changed, 49 insertions(+), 13 deletions(-)

diff --git a/mongoose.c b/mongoose.c
index a186418de..01cd72ca9 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -731,21 +731,32 @@ static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) {
   va_list ap_copy;
   int len;
 
-  // Windows is not standard-compliant, and vsnprintf() returns -1 if
-  // buffer is too small. Also, older versions of msvcrt.dll do not have
-  // _vscprintf().  However, if size is 0, vsnprintf() behaves correctly.
-  // Therefore, we make two passes: on first pass, get required message length.
-  // On second pass, actually print the message.
   va_copy(ap_copy, ap);
-  len = vsnprintf(NULL, 0, fmt, ap_copy);
+  len = vsnprintf(*buf, size, fmt, ap_copy);
+  va_end(ap_copy);
 
-  if (len > (int) size &&
-      (size = len + 1) > 0 &&
-      (*buf = (char *) malloc(size)) == NULL) {
-    len = -1;  // Allocation failed, mark failure
-  } else {
-    va_copy(ap_copy, ap);
-    vsnprintf(*buf, size, fmt, ap_copy);
+  if (len < 0) {
+    // eCos and Windows are not standard-compliant and return -1 when
+    // the buffer is too small. Keep allocating larger buffers until we
+    // succeed or out of memory.
+    *buf = NULL;
+    while (len < 0) {
+      if (*buf) free(*buf);
+      size *= 2;
+      if ((*buf = (char *) malloc(size)) == NULL) break;
+      va_copy(ap_copy, ap);
+      len = vsnprintf(*buf, size, fmt, ap_copy);
+      va_end(ap_copy);
+    }
+  } else if (len > (int) size) {
+    // Standard-compliant code path. Allocate a buffer that is large enough.
+    if ((*buf = (char *) malloc(len + 1)) == NULL) {
+      len = -1;
+    } else {
+      va_copy(ap_copy, ap);
+      len = vsnprintf(*buf, len + 1, fmt, ap_copy);
+      va_end(ap_copy);
+    }
   }
 
   return len;
diff --git a/unit_test.c b/unit_test.c
index b3d034492..3f9a39ed4 100644
--- a/unit_test.c
+++ b/unit_test.c
@@ -613,6 +613,30 @@ static const char *test_rewrites(void) {
   return NULL;
 }
 
+static int avt(char **buf, size_t buf_size, const char *fmt, ...) {
+  int result;
+  va_list ap;
+  va_start(ap, fmt);
+  result = alloc_vprintf(buf, buf_size, fmt, ap);
+  va_end(ap);
+  return result;
+}
+
+static const char *test_alloc_vprintf(void) {
+  char buf[5], *p = buf;
+
+  ASSERT(avt(&p, sizeof(buf), "%d", 123) == 3);
+  ASSERT(p == buf);
+  ASSERT(strcmp(p, "123") == 0);
+
+  ASSERT(avt(&p, sizeof(buf), "%d", 123456789) == 9);
+  ASSERT(p != buf);
+  ASSERT(strcmp(p, "123456789") == 0);
+  free(p);
+
+  return NULL;
+}
+
 static const char *run_all_tests(void) {
   RUN_TEST(test_should_keep_alive);
   RUN_TEST(test_match_prefix);
@@ -630,6 +654,7 @@ static const char *run_all_tests(void) {
   RUN_TEST(test_mg_connect);
   RUN_TEST(test_mg_set_option);
   RUN_TEST(test_rewrites);
+  RUN_TEST(test_alloc_vprintf);
 #ifdef MONGOOSE_USE_SSL
   RUN_TEST(test_ssl);
 #endif
-- 
GitLab