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.
{
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;
}
{
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;
}
#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)
__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);
__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);
}
{
int color;
char *msg;
+ va_list ap;
if (ll < loglevel || !curses_active)
return;
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);
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.
*
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.
*
* \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;
}
__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);