From: Willy Tarreau Date: Fri, 27 Apr 2012 09:16:50 +0000 (+0200) Subject: MINOR: standard: add a memprintf() function to build formatted error messages X-Git-Tag: v1.5-dev9~98 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9a7bea52b12b612e83524037d20b941d1d97cfc4;p=thirdparty%2Fhaproxy.git 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/include/common/standard.h b/include/common/standard.h index 065a4fd4a3..5dcb6c7e9e 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -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 if the + * pointer is not NULL. A previous version of 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 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 */ diff --git a/src/standard.c b/src/standard.c index d9b585ea15..ea06a87dad 100644 --- a/src/standard.c +++ b/src/standard.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -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 if the + * pointer is not NULL. A previous version of 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 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: