From 3e6bba778c867a8977704b5c15e52fa18fb0d900 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Fri, 6 Jan 2012 04:15:34 +0100 Subject: [PATCH] Replace PARA_VSNPRINTF by xvasprintf(). The PARA_VSNPRINTF macro is rather clumsy, and too large to be inlined. Moreover, it does not return the length of the formated string, so users have to call strlen() after the call to PARA_VSNPRINTF(). This is extra work which can easily be avoided since the number of bytes written is returned by the underlying call to vsnprintf(). This patch replaces the macro by the public function xvasprintf(), which is similar to the non-standard vasprintf() on GNU systems. It also adds xasprintf(), a similar variant which takes a variable number of arguments. Unlike PARA_VSNPRINTF, xasprintf() and xvasprintf() return the number of bytes written. This relies on vsnprintf() conforming to the C99 standard and breaks in particular on glibc 2.0 systems. Since glibc 2.0 is about 15 years old, this is unlikely to cause problems on real systems. All users which called strlen() right after xvasprintf() are changed to use the return value of xvasprintf() instead. --- crypt_common.c | 7 ++++-- fd.c | 6 +++-- gcc-compat.h | 11 +++++---- gui.c | 15 +++++++++--- para.h | 31 +----------------------- string.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++-- string.h | 3 +++ 7 files changed, 95 insertions(+), 43 deletions(-) diff --git a/crypt_common.c b/crypt_common.c index 4e9622e4..5ad4d43d 100644 --- a/crypt_common.c +++ b/crypt_common.c @@ -350,9 +350,12 @@ __printf_2_3 int sc_send_va_buffer(struct stream_cipher_context *scc, { char *msg; int ret; + va_list ap; - PARA_VSPRINTF(fmt, msg); - ret = sc_send_buffer(scc, msg); + va_start(ap, fmt); + ret = xvasprintf(&msg, fmt, ap); + va_end(ap); + ret = sc_send_bin_buffer(scc, msg, ret); free(msg); return ret; } diff --git a/fd.c b/fd.c index d2f93611..a04232f8 100644 --- a/fd.c +++ b/fd.c @@ -142,9 +142,11 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...) { char *msg; int ret; + va_list ap; - PARA_VSPRINTF(fmt, msg); - ret = write_buffer(fd, msg); + va_start(ap, fmt); + ret = xvasprintf(&msg, fmt, ap); + ret = write_all(fd, msg, ret); free(msg); return ret; } diff --git a/gcc-compat.h b/gcc-compat.h index ba1d82ea..5d207288 100644 --- a/gcc-compat.h +++ b/gcc-compat.h @@ -5,15 +5,18 @@ #define __a_aligned(alignment) __attribute__((__aligned__(alignment))) /* - * p is the number of the "format string" parameter, and q is - * the number of the first variadic parameter. + * p is the number of the "format string" parameter, and q is the number of the + * first variadic parameter. If q is specified as zero, the compiler only + * checks the format string for consistency. */ # define __printf(p,q) __attribute__ ((format (printf, p, q))) # define __a_const __attribute__ ((const)) + /* - * as direct use of __printf(p,q) confuses doxygen, here are two extra macros - * for those values p,q that are actually used by paraslash. + * As direct use of __printf(p,q) confuses doxygen, here are some extra macros + * for those values p,q that are actually used. */ +#define __printf_2_0 __printf(2,0) #define __printf_1_2 __printf(1,2) #define __printf_2_3 __printf(2,3) diff --git a/gui.c b/gui.c index b7823f0d..0316e365 100644 --- a/gui.c +++ b/gui.c @@ -336,11 +336,14 @@ static int align_str(WINDOW* win, char *str, unsigned int len, __printf_2_3 static void print_in_bar(int color, const char *fmt,...) { char *msg; + va_list ap; if (!curses_active) return; wattron(in.win, COLOR_PAIR(color)); - PARA_VSPRINTF(fmt, msg); + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); wmove(in.win, 0, 0); align_str(in.win, msg, sb.cols, LEFT); free(msg); @@ -471,10 +474,13 @@ static void rb_add_entry(int color, char *msg) __printf_2_3 static void outputf(int color, const char* fmt,...) { char *msg; + va_list ap; if (!curses_active) return; - PARA_VSPRINTF(fmt, msg); + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); rb_add_entry(color, msg); wrefresh(bot.win); } @@ -493,6 +499,7 @@ __printf_2_3 void curses_log(int ll, const char *fmt,...) { int color; char *msg; + va_list ap; if (ll < loglevel || !curses_active) return; @@ -505,7 +512,9 @@ __printf_2_3 void curses_log(int ll, const char *fmt,...) default: color = COLOR_ERRMSG; } - PARA_VSPRINTF(fmt, msg); + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); chop(msg); rb_add_entry(color, msg); wrefresh(bot.win); diff --git a/para.h b/para.h index 25cbd16a..3b9559e1 100644 --- a/para.h +++ b/para.h @@ -113,38 +113,9 @@ extern const char *status_item_list[]; int for_each_stat_item(char *item_buf, size_t num_bytes, int (*item_handler)(int, char *)); -/** - * Write a log message to a dynamically allocated string. - * - * \param fmt Usual format string. - * \param p Result pointer. - * - * \sa printf(3). */ -#define PARA_VSPRINTF(fmt, p) \ -{ \ - int n; \ - size_t size = 100; \ - p = para_malloc(size); \ - while (1) { \ - va_list ap; \ - /* Try to print in the allocated space. */ \ - va_start(ap, fmt); \ - n = vsnprintf(p, size, fmt, ap); \ - va_end(ap); \ - /* If that worked, return the string. */ \ - if (n > -1 && n < size) \ - break; \ - /* Else try again with more space. */ \ - if (n > -1) /* glibc 2.1 */ \ - size = n + 1; /* precisely what is needed */ \ - else /* glibc 2.0 */ \ - size *= 2; /* twice the old size */ \ - p = para_realloc(p, size); \ - } \ -} /** - * Return a random non-negative integer in an interval. + * Return a random non-negative integer in an interval. * * \param max Determines maximal possible return value. * diff --git a/string.c b/string.c index 8c8c50ac..a1f2a43c 100644 --- a/string.c +++ b/string.c @@ -114,6 +114,64 @@ __must_check __malloc char *para_strdup(const char *s) exit(EXIT_FAILURE); } +/** + * Print a formated message to a dynamically allocated string. + * + * \param result The formated string is returned here. + * \param fmt The format string. + * \param ap Initialized list of arguments. + * + * This function is similar to vasprintf(), a GNU extension which is not in C + * or POSIX. It allocates a string large enough to hold the output including + * the terminating null byte. The allocated string is returned via the first + * argument and must be freed by the caller. However, unlike vasprintf(), this + * function calls exit() if insufficient memory is available, while vasprintf() + * returns -1 in this case. + * + * \return Number of bytes written, not including the terminating '\0'. + * + * \sa printf(3), vsnprintf(3), va_start(3), vasprintf(3), \ref xasprintf(). + */ +__printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap) +{ + int ret; + size_t size; + va_list aq; + + va_copy(aq, ap); + ret = vsnprintf(NULL, 0, fmt, aq); + va_end(aq); + assert(ret >= 0); + size = ret + 1; + *result = para_malloc(size); + va_copy(aq, ap); + ret = vsnprintf(*result, size, fmt, aq); + va_end(aq); + assert(ret >= 0 && ret < size); + return ret; +} + +/** + * Print to a dynamically allocated string, variable number of arguments. + * + * \param result See \ref xvasprintf(). + * \param fmt Usual format string. + * + * \return The return value of the underlying call to \ref xvasprintf(). + * + * \sa \ref xvasprintf() and the references mentioned there. + */ +__printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...) +{ + va_list ap; + unsigned ret; + + va_start(ap, fmt); + ret = xvasprintf(result, fmt, ap); + va_end(ap); + return ret; +} + /** * Allocate a sufficiently large string and print into it. * @@ -125,13 +183,16 @@ __must_check __malloc char *para_strdup(const char *s) * \return This function either returns a pointer to a string that must be * freed by the caller or aborts without returning. * - * \sa printf(3). + * \sa printf(3), xasprintf(). */ __must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...) { char *msg; + va_list ap; - PARA_VSPRINTF(fmt, msg); + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); return msg; } diff --git a/string.h b/string.h index e5a4f9db..cdc55d2d 100644 --- a/string.h +++ b/string.h @@ -60,6 +60,9 @@ __must_check __malloc void *para_realloc(void *p, size_t size); __must_check __malloc void *para_malloc(size_t size); __must_check __malloc void *para_calloc(size_t size); __must_check __malloc char *para_strdup(const char *s); + +__printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap); +__printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...); __must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...); __must_check __malloc char *para_strcat(char *a, const char *b); __must_check __malloc char *para_dirname(const char *name); -- 2.39.5