Replace PARA_VSNPRINTF by xvasprintf().
authorAndre Noll <maan@systemlinux.org>
Fri, 6 Jan 2012 03:15:34 +0000 (04:15 +0100)
committerAndre Noll <maan@systemlinux.org>
Fri, 20 Jan 2012 21:57:07 +0000 (22:57 +0100)
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
fd.c
gcc-compat.h
gui.c
para.h
string.c
string.h

index 4e9622e..5ad4d43 100644 (file)
@@ -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 d2f9361..a04232f 100644 (file)
--- 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;
 }
index ba1d82e..5d20728 100644 (file)
@@ -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 b7823f0..0316e36 100644 (file)
--- 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 25cbd16..3b9559e 100644 (file)
--- 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.
  *
index 8c8c50a..a1f2a43 100644 (file)
--- 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;
 }
 
index e5a4f9d..cdc55d2 100644 (file)
--- 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);