diff --git a/include/common/standard.h b/include/common/standard.h index 065a4fd4a..5dcb6c7e9 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 d9b585ea1..ea06a87da 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: