MINOR: standard: add a memprintf() function to build formatted error messages

memprintf() is just like snprintf() except that it always returns a properly
sized allocated string that the caller is responsible for freeing. NULL is
returned on serious errors. It also supports stackable calls over the same
pointer since it offers support for automatically freeing a previous one :

     memprintf(&err, "invalid argument: '%s'", arg);
     ...
     memprintf(&err, "keyword parser said: <%s>", *err);
     ...
     memprintf(&err, "line parser said: %s\n", *err);
     ...
     free(*err);
diff --git a/src/standard.c b/src/standard.c
index d9b585e..ea06a87 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -12,6 +12,7 @@
 
 #include <ctype.h>
 #include <netdb.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -1728,6 +1729,68 @@
 	return dst;
 }
 
+/* Dynamically allocates a string of the proper length to hold the formatted
+ * output. NULL is returned on error. The caller is responsible for freeing the
+ * memory area using free(). The resulting string is returned in <out> if the
+ * pointer is not NULL. A previous version of <out> might be used to build the
+ * new string, and it will be freed before returning if it is not NULL, which
+ * makes it possible to build complex strings from iterative calls without
+ * having to care about freeing intermediate values, as in the example below :
+ *
+ *     memprintf(&err, "invalid argument: '%s'", arg);
+ *     ...
+ *     memprintf(&err, "parser said : <%s>\n", *err);
+ *     ...
+ *     free(*err);
+ *
+ * This means that <err> must be initialized to NULL before first invocation.
+ * The return value also holds the allocated string, which eases error checking
+ * and immediate consumption. If the output pointer is not used, NULL must be
+ * passed instead and it will be ignored.
+ *
+ * It is also convenient to use it without any free except the last one :
+ *    err = NULL;
+ *    if (!fct1(err)) report(*err);
+ *    if (!fct2(err)) report(*err);
+ *    if (!fct3(err)) report(*err);
+ *    free(*err);
+ */
+char *memprintf(char **out, const char *format, ...)
+{
+	va_list args;
+	char *ret = NULL;
+	int allocated = 0;
+	int needed = 0;
+
+	do {
+		/* vsnprintf() will return the required length even when the
+		 * target buffer is NULL. We do this in a loop just in case
+		 * intermediate evaluations get wrong.
+		 */
+		va_start(args, format);
+		needed = vsnprintf(ret, allocated, format, args) + 1;
+		va_end(args);
+
+		if (needed <= allocated)
+			break;
+
+		allocated = needed;
+		ret = realloc(ret, allocated);
+	} while (ret);
+
+	if (needed < 0) {
+		/* an error was encountered */
+		free(ret);
+		ret = NULL;
+	}
+
+	if (out) {
+		free(*out);
+		*out = ret;
+	}
+
+	return ret;
+}
 
 /*
  * Local variables: