]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: standard: add a memprintf() function to build formatted error messages
authorWilly Tarreau <w@1wt.eu>
Fri, 27 Apr 2012 09:16:50 +0000 (11:16 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 30 Apr 2012 09:55:35 +0000 (11:55 +0200)
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);

include/common/standard.h
src/standard.c

index 065a4fd4a36a2fd771a1caefa2735b9f79cc8621..5dcb6c7e9ed60a432898581ef16258fbd3923a3b 100644 (file)
@@ -668,4 +668,33 @@ char *date2str_log(char *dest, struct tm *tm, struct timeval *date, size_t size)
  */
 char *gmt2str_log(char *dst, struct tm *tm, size_t size);
 
+/* 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, ...)
+       __attribute__ ((format(printf, 2, 3)));
+
 #endif /* _COMMON_STANDARD_H */
index d9b585ea15e9e9d5117dec26ba847c15d7d266a3..ea06a87dadf757bdf8d42bdb1f6b548f7cf66dac 100644 (file)
@@ -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 @@ char *gmt2str_log(char *dst, struct tm *tm, size_t size)
        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: