summaryrefslogtreecommitdiffstats
path: root/mcop/debug.cc
diff options
context:
space:
mode:
Diffstat (limited to 'mcop/debug.cc')
-rw-r--r--mcop/debug.cc821
1 files changed, 821 insertions, 0 deletions
diff --git a/mcop/debug.cc b/mcop/debug.cc
new file mode 100644
index 0000000..208727b
--- /dev/null
+++ b/mcop/debug.cc
@@ -0,0 +1,821 @@
+ /*
+
+ Copyright (C) 2000-2002 Stefan Westerfeld
+ stefan@space.twc.de
+
+ (see also below for details on the copyright of arts_strdup_printf,
+ which is taken from GLib)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "debug.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include "thread.h"
+
+static int arts_debug_level = Arts::Debug::lInfo;
+static bool arts_debug_abort = false;
+static const char *arts_debug_prefix = "";
+static char *messageAppName = 0;
+static Arts::Mutex *arts_debug_mutex = 0;
+
+/* routines for variable length sprintf without buffer overflow (from GLib) */
+static char* arts_strdup_vprintf(const char *format, va_list args1);
+
+namespace Arts {
+
+static char * shell_quote(const char *s)
+{
+ char *result;
+ char *p;
+ p = result = static_cast<char*>( malloc(strlen(s)*5+1) );
+ while(*s)
+ {
+ if (*s == '\'')
+ {
+ *p++ = '\'';
+ *p++ = '"';
+ *p++ = *s++;
+ *p++ = '"';
+ *p++ = '\'';
+ }
+ else
+ {
+ *p++ = *s++;
+ }
+ }
+ *p = '\0';
+ return result;
+}
+
+/*
+ * Call the graphical application to display a message, if
+ * defined. Otherwise, send to standard error. Debug messages are
+ * always sent to standard error because they tend to be very verbose.
+ * Note that the external application is run in the background to
+ * avoid blocking the sound server.
+ */
+static void output_message(Debug::Level level, const char *msg) {
+ char *quoted_msg;
+ char *buff = 0;
+
+ /* default to text output if no message app is defined or if it is a debug message. */
+ if (messageAppName == 0 || !strcmp(messageAppName, "") || (level == Debug::lDebug))
+ {
+ fprintf(stderr, "%s\n", msg);
+ return;
+ }
+
+ quoted_msg = shell_quote(msg);
+ switch (level) {
+ case Debug::lFatal:
+ buff = arts_strdup_printf("%s -e 'Sound server fatal error:\n\n%s' &", messageAppName, quoted_msg);
+ break;
+ case Debug::lWarning:
+ buff = arts_strdup_printf("%s -w 'Sound server warning message:\n\n%s' &", messageAppName, quoted_msg);
+ break;
+ case Debug::lInfo:
+ buff = arts_strdup_printf("%s -i 'Sound server informational message:\n\n%s' &", messageAppName, quoted_msg);
+ break;
+ default:
+ break; // avoid compile warning
+ }
+ free(quoted_msg);
+
+ if(buff != 0)
+ {
+ system(buff);
+ free(buff);
+ }
+}
+
+/*
+ * Display a message using output_message. If the message is the same
+ * as the previous one, just increment a count but don't display
+ * it. This prevents flooding the user with duplicate warnings. If the
+ * message is not the same as the previous one, then we report the
+ * previously repeated message (if any) and reset the last message and
+ * count.
+ */
+static void display_message(Debug::Level level, const char *msg) {
+ static char lastMsg[1024];
+ static Debug::Level lastLevel;
+ static int msgCount = 0;
+
+ if(arts_debug_mutex)
+ arts_debug_mutex->lock();
+
+ if (!strncmp(msg, lastMsg, 1024))
+ {
+ msgCount++;
+ } else {
+ if (msgCount > 0)
+ {
+ char *buff;
+ buff = arts_strdup_printf("%s\n(The previous message was repeated %d times.)", lastMsg, msgCount);
+ output_message(lastLevel, buff);
+ free(buff);
+ }
+ strncpy(lastMsg, msg, 1024);
+ lastMsg[ 1023 ] = '\0';
+ lastLevel = level;
+ msgCount = 0;
+ output_message(level, msg);
+ }
+
+ if(arts_debug_mutex)
+ arts_debug_mutex->unlock();
+}
+
+static class DebugInitFromEnv {
+public:
+ DebugInitFromEnv() {
+ const char *env = getenv("ARTS_DEBUG");
+ if(env)
+ {
+ if(strcmp(env,"debug") == 0)
+ arts_debug_level = Debug::lDebug;
+ else if(strcmp(env,"info") == 0)
+ arts_debug_level = Debug::lInfo;
+ else if(strcmp(env,"warning") == 0)
+ arts_debug_level = Debug::lWarning;
+ else if(strcmp(env,"quiet") == 0)
+ arts_debug_level = Debug::lFatal;
+ else
+ {
+ fprintf(stderr,
+ "ARTS_DEBUG must be one of debug,info,warning,quiet\n");
+ }
+ }
+ env = getenv("ARTS_DEBUG_ABORT");
+ if(env)
+ arts_debug_abort = true;
+ }
+} debugInitFromEnv;
+
+}
+
+void Arts::Debug::init(const char *prefix, Level level)
+{
+ arts_debug_level = level;
+ arts_debug_prefix = prefix;
+}
+
+void Arts::Debug::fatal(const char *fmt, ...)
+{
+ char *buff;
+ va_list ap;
+
+ va_start(ap, fmt);
+ buff = arts_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ display_message(Debug::lFatal, buff);
+ free(buff);
+
+ if(arts_debug_abort) abort();
+ exit(1);
+}
+
+void Arts::Debug::warning(const char *fmt, ...)
+{
+ if(lWarning >= arts_debug_level)
+ {
+ char *buff;
+ va_list ap;
+
+ va_start(ap, fmt);
+ buff = arts_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ display_message(Debug::lWarning, buff);
+ free(buff);
+ }
+}
+
+void Arts::Debug::info(const char *fmt, ...)
+{
+ if(lInfo >= arts_debug_level)
+ {
+ char *buff;
+ va_list ap;
+
+ va_start(ap, fmt);
+ buff = arts_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ display_message(Debug::lInfo, buff);
+ free(buff);
+ }
+}
+
+void Arts::Debug::debug(const char *fmt, ...)
+{
+ if(lDebug >= arts_debug_level)
+ {
+ char *buff;
+ va_list ap;
+
+ va_start(ap, fmt);
+ buff = arts_strdup_vprintf(fmt, ap);
+ va_end(ap);
+
+ display_message(Debug::lDebug, buff);
+ free(buff);
+ }
+}
+
+void Arts::Debug::messageApp(const char *appName)
+{
+ messageAppName = (char*) realloc(messageAppName, strlen(appName)+1);
+ strcpy(messageAppName, appName);
+}
+
+void Arts::Debug::initMutex()
+{
+ arts_return_if_fail(arts_debug_mutex == 0);
+
+ arts_debug_mutex = new Arts::Mutex();
+}
+
+void Arts::Debug::freeMutex()
+{
+ arts_return_if_fail(arts_debug_mutex != 0);
+
+ delete arts_debug_mutex;
+ arts_debug_mutex = 0;
+}
+
+/*
+ * For the sake of portability (snprintf is non-portable), what follows is an
+ * implementation of a variant g_strdup_printf, to format debug messages of
+ * an arbitary length appropriately. This is reduntant with flow/gsl/gslglib.c,
+ * however, as libmcop doesn't necessarily link against gslglib.c, this is a
+ * more-or-less complete copy.
+ */
+
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * GScanner: Flexible lexical scanner for general purpose.
+ * Copyright (C) 1997, 1998 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <string.h>
+
+#define g_warning printf
+#define g_strerror strerror
+
+/*--- gslglib.h ---*/
+
+#include <limits.h>
+#include <float.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+/* --- GLib typedefs --- */
+typedef void* gpointer;
+typedef const void* gconstpointer;
+typedef char gchar;
+typedef unsigned char guchar;
+typedef signed short gshort;
+typedef unsigned short gushort;
+typedef signed int gint;
+typedef unsigned int guint;
+typedef signed long glong;
+typedef unsigned long gulong;
+typedef float gfloat;
+typedef double gdouble;
+typedef size_t gsize;
+typedef gchar gint8;
+typedef guchar guint8;
+typedef gshort gint16;
+typedef gushort guint16;
+typedef gint gint32;
+typedef guint guint32;
+typedef gint gboolean;
+typedef gint32 GTime;
+#ifdef __alpha
+typedef long int gint64;
+typedef unsigned long int guint64;
+#else
+typedef long long int gint64;
+typedef unsigned long long int guint64;
+#endif
+typedef struct _GString GString;
+
+/* --- standard macros --- */
+#ifndef ABS
+#define ABS(a) ((a) > 0 ? (a) : -(a))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef CLAMP
+#define CLAMP(v,l,h) ((v) < (l) ? (l) : (v) > (h) ? (h) : (v))
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+#ifndef NULL
+#define NULL ((void*) 0)
+#endif
+
+/* --- configure stuff!!! --- */
+#ifdef WORDS_BIGENDIAN
+#define G_BYTE_ORDER G_BIG_ENDIAN
+#else
+#define G_BYTE_ORDER G_LITTLE_ENDIAN
+#endif
+
+/* #define GLIB_HAVE_STPCPY 1 */
+/* Define G_VA_COPY() to do the right thing for copying va_list variables.
+ * glibconfig.h may have already defined G_VA_COPY as va_copy or __va_copy.
+ */
+#if !defined (G_VA_COPY)
+# if defined (__GNUC__) && defined (__PPC__) && (defined (_CALL_SYSV) || defined (_WIN32) || defined(WIN32)) || defined(__s390__) || defined(__x86_64__)
+# define G_VA_COPY(ap1, ap2) (*(ap1) = *(ap2))
+# elif defined (G_VA_COPY_AS_ARRAY)
+# define G_VA_COPY(ap1, ap2) g_memmove ((ap1), (ap2), sizeof (va_list))
+# else /* va_list is a pointer */
+# define G_VA_COPY(ap1, ap2) ((ap1) = (ap2))
+# endif /* va_list is a pointer */
+#endif /* !G_VA_COPY */
+
+/* --- glib macros --- */
+#define G_MINFLOAT FLT_MIN
+#define G_MAXFLOAT FLT_MAX
+#define G_MINDOUBLE DBL_MIN
+#define G_MAXDOUBLE DBL_MAX
+#define G_MINSHORT SHRT_MIN
+#define G_MAXSHORT SHRT_MAX
+#define G_MAXUSHORT USHRT_MAX
+#define G_MININT INT_MIN
+#define G_MAXINT INT_MAX
+#define G_MAXUINT UINT_MAX
+#define G_MINLONG LONG_MIN
+#define G_MAXLONG LONG_MAX
+#define G_MAXULONG ULONG_MAX
+#define G_USEC_PER_SEC 1000000
+#define G_LITTLE_ENDIAN 1234
+#define G_BIG_ENDIAN 4321
+
+#define G_STRINGIFY(macro_or_string) G_STRINGIFY_ARG (macro_or_string)
+#define G_STRINGIFY_ARG(contents) #contents
+#if defined __GNUC__ && !defined __cplusplus
+# define G_STRLOC __FILE__ ":" G_STRINGIFY (__LINE__) ":" __PRETTY_FUNCTION__ "()"
+#else
+# define G_STRLOC __FILE__ ":" G_STRINGIFY (__LINE__)
+#endif
+
+/* subtract from biased_exponent to form base2 exponent (normal numbers) */
+typedef union _GDoubleIEEE754 GDoubleIEEE754;
+typedef union _GFloatIEEE754 GFloatIEEE754;
+#define G_IEEE754_FLOAT_BIAS (127)
+#define G_IEEE754_DOUBLE_BIAS (1023)
+/* multiply with base2 exponent to get base10 exponent (nomal numbers) */
+#define G_LOG_2_BASE_10 (0.30102999566398119521)
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+union _GFloatIEEE754
+{
+ gfloat v_float;
+ struct {
+ guint mantissa : 23;
+ guint biased_exponent : 8;
+ guint sign : 1;
+ } mpn;
+};
+union _GDoubleIEEE754
+{
+ gdouble v_double;
+ struct {
+ guint mantissa_low : 32;
+ guint mantissa_high : 20;
+ guint biased_exponent : 11;
+ guint sign : 1;
+ } mpn;
+};
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+union _GFloatIEEE754
+{
+ gfloat v_float;
+ struct {
+ guint sign : 1;
+ guint biased_exponent : 8;
+ guint mantissa : 23;
+ } mpn;
+};
+union _GDoubleIEEE754
+{
+ gdouble v_double;
+ struct {
+ guint sign : 1;
+ guint biased_exponent : 11;
+ guint mantissa_high : 20;
+ guint mantissa_low : 32;
+ } mpn;
+};
+#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+#error unknown ENDIAN type
+#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#define GLIB_SIZEOF_INTMAX (8 /* educated guess */)
+
+typedef struct
+{
+ guint min_width;
+ guint precision;
+ gboolean alternate_format, zero_padding, adjust_left, locale_grouping;
+ gboolean add_space, add_sign, possible_sign, seen_precision;
+ gboolean mod_half, mod_long, mod_extra_long;
+} PrintfArgSpec;
+
+
+static gsize
+printf_string_upper_bound (const gchar *format,
+ gboolean may_warn,
+ va_list args)
+{
+ static gboolean honour_longs = sizeof(long) > 4 || sizeof(void*) > 4;
+ gsize len = 1;
+
+ if (!format)
+ return len;
+
+ while (*format)
+ {
+ register gchar c = *format++;
+
+ if (c != '%')
+ len += 1;
+ else /* (c == '%') */
+ {
+ PrintfArgSpec spec = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ gboolean seen_l = FALSE, conv_done = FALSE;
+ gsize conv_len = 0;
+ const gchar *spec_start = format;
+
+ do
+ {
+ c = *format++;
+ switch (c)
+ {
+ GDoubleIEEE754 u_double;
+ guint v_uint;
+ gint v_int;
+ const gchar *v_string;
+
+ /* beware of positional parameters
+ */
+ case '$':
+ if (may_warn)
+ g_warning (G_STRLOC ": unable to handle positional parameters (%%n$)");
+ len += 1024; /* try adding some safety padding */
+ break;
+
+ /* parse flags
+ */
+ case '#':
+ spec.alternate_format = TRUE;
+ break;
+ case '0':
+ spec.zero_padding = TRUE;
+ break;
+ case '-':
+ spec.adjust_left = TRUE;
+ break;
+ case ' ':
+ spec.add_space = TRUE;
+ break;
+ case '+':
+ spec.add_sign = TRUE;
+ break;
+ case '\'':
+ spec.locale_grouping = TRUE;
+ break;
+
+ /* parse output size specifications
+ */
+ case '.':
+ spec.seen_precision = TRUE;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ v_uint = c - '0';
+ c = *format;
+ while (c >= '0' && c <= '9')
+ {
+ format++;
+ v_uint = v_uint * 10 + c - '0';
+ c = *format;
+ }
+ if (spec.seen_precision)
+ spec.precision = MAX (spec.precision, v_uint);
+ else
+ spec.min_width = MAX (spec.min_width, v_uint);
+ break;
+ case '*':
+ v_int = va_arg (args, int);
+ if (spec.seen_precision)
+ {
+ /* forget about negative precision */
+ if (v_int >= 0)
+ spec.precision = MAX (spec.precision, (unsigned)v_int);
+ }
+ else
+ {
+ if (v_int < 0)
+ {
+ v_int = - v_int;
+ spec.adjust_left = TRUE;
+ }
+ spec.min_width = MAX (spec.min_width, (unsigned)v_int);
+ }
+ break;
+
+ /* parse type modifiers
+ */
+ case 'h':
+ spec.mod_half = TRUE;
+ break;
+ case 'l':
+ if (!seen_l)
+ {
+ spec.mod_long = TRUE;
+ seen_l = TRUE;
+ break;
+ }
+ /* else, fall through */
+ case 'L':
+ case 'q':
+ spec.mod_long = TRUE;
+ spec.mod_extra_long = TRUE;
+ break;
+ case 'z':
+ case 'Z':
+ if (sizeof(size_t))
+ {
+ spec.mod_long = TRUE;
+ spec.mod_extra_long = TRUE;
+ }
+ break;
+ case 't':
+ if (sizeof(ptrdiff_t) > 4)
+ {
+ spec.mod_long = TRUE;
+ spec.mod_extra_long = TRUE;
+ }
+ break;
+ case 'j':
+ if (GLIB_SIZEOF_INTMAX > 4)
+ {
+ spec.mod_long = TRUE;
+ spec.mod_extra_long = TRUE;
+ }
+ break;
+
+ /* parse output conversions
+ */
+ case '%':
+ conv_len += 1;
+ break;
+ case 'O':
+ case 'D':
+ case 'I':
+ case 'U':
+ /* some C libraries feature long variants for these as well? */
+ spec.mod_long = TRUE;
+ /* fall through */
+ case 'o':
+ conv_len += 2;
+ /* fall through */
+ case 'd':
+ case 'i':
+ conv_len += 1; /* sign */
+ /* fall through */
+ case 'u':
+ conv_len += 4;
+ /* fall through */
+ case 'x':
+ case 'X':
+ spec.possible_sign = TRUE;
+ conv_len += 10;
+ if (spec.mod_long && honour_longs)
+ conv_len *= 2;
+ if (spec.mod_extra_long)
+ conv_len *= 2;
+ if (spec.mod_extra_long)
+ {
+ (void) va_arg (args, gint64);
+ }
+ else if (spec.mod_long)
+ (void) va_arg (args, long);
+ else
+ (void) va_arg (args, int);
+ break;
+ case 'A':
+ case 'a':
+ /* 0x */
+ conv_len += 2;
+ /* fall through */
+ case 'g':
+ case 'G':
+ case 'e':
+ case 'E':
+ case 'f':
+ spec.possible_sign = TRUE;
+ /* n . dddddddddddddddddddddddd E +- eeee */
+ conv_len += 1 + 1 + MAX (24, spec.precision) + 1 + 1 + 4;
+ if (may_warn && spec.mod_extra_long)
+ g_warning (G_STRLOC ": unable to handle long double, collecting double only");
+#ifdef HAVE_LONG_DOUBLE
+#error need to implement special handling for long double
+#endif
+ u_double.v_double = va_arg (args, double);
+ /* %f can expand up to all significant digits before '.' (308) */
+ if (c == 'f' &&
+ u_double.mpn.biased_exponent > 0 && u_double.mpn.biased_exponent < 2047)
+ {
+ gint exp = u_double.mpn.biased_exponent;
+
+ exp -= G_IEEE754_DOUBLE_BIAS;
+ exp = (gint)(exp * G_LOG_2_BASE_10 + 1);
+ conv_len += ABS (exp); /* exp can be <0 */
+ }
+ /* some printf() implementations require extra padding for rounding */
+ conv_len += 2;
+ /* we can't really handle locale specific grouping here */
+ if (spec.locale_grouping)
+ conv_len *= 2;
+ break;
+ case 'C':
+ spec.mod_long = TRUE;
+ /* fall through */
+ case 'c':
+ conv_len += spec.mod_long ? MB_LEN_MAX : 1;
+ (void) va_arg (args, int);
+ break;
+ case 'S':
+ spec.mod_long = TRUE;
+ /* fall through */
+ case 's':
+ v_string = va_arg (args, char*);
+ if (!v_string)
+ conv_len += 8; /* hold "(null)" */
+ else if (spec.seen_precision)
+ conv_len += spec.precision;
+ else
+ conv_len += strlen (v_string);
+ conv_done = TRUE;
+ if (spec.mod_long)
+ {
+ if (may_warn)
+ g_warning (G_STRLOC": unable to handle wide char strings");
+ len += 1024; /* try adding some safety padding */
+ }
+ break;
+ case 'P': /* do we actually need this? */
+ /* fall through */
+ case 'p':
+ spec.alternate_format = TRUE;
+ conv_len += 10;
+ if (honour_longs)
+ conv_len *= 2;
+ /* fall through */
+ case 'n':
+ conv_done = TRUE;
+ (void) va_arg (args, void*);
+ break;
+ case 'm':
+ /* there's not much we can do to be clever */
+ v_string = g_strerror (errno);
+ v_uint = v_string ? strlen (v_string) : 0;
+ conv_len += MAX (256, v_uint);
+ break;
+
+ /* handle invalid cases
+ */
+ case '\000':
+ /* no conversion specification, bad bad */
+ conv_len += format - spec_start;
+ break;
+ default:
+ if (may_warn)
+ g_warning (G_STRLOC": unable to handle `%c' while parsing format",
+ c);
+ break;
+ }
+ conv_done |= conv_len > 0;
+ }
+ while (!conv_done);
+ /* handle width specifications */
+ conv_len = MAX (conv_len, MAX (spec.precision, spec.min_width));
+ /* handle flags */
+ conv_len += spec.alternate_format ? 2 : 0;
+ conv_len += (spec.add_space || spec.add_sign || spec.possible_sign);
+ /* finally done */
+ len += conv_len;
+ } /* else (c == '%') */
+ } /* while (*format) */
+
+ return len;
+}
+
+static char*
+#ifdef __GNUC__
+__attribute__ ( (format (printf, 1, 0) ) )
+#endif
+arts_strdup_vprintf (const char *format, va_list args1)
+{
+ gchar *buffer;
+#ifdef HAVE_VASPRINTF
+ if (vasprintf (&buffer, format, args1) < 0)
+ buffer = NULL;
+#else
+ va_list args2;
+
+ G_VA_COPY (args2, args1);
+
+ buffer = (gchar *)malloc (printf_string_upper_bound (format, TRUE, args1));
+
+ vsprintf (buffer, format, args2);
+ va_end (args2);
+#endif
+ return buffer;
+}
+
+char*
+#ifdef __GNUC__
+__attribute__ ( (format (printf, 1, 0) ) )
+#endif
+arts_strdup_printf (const char *format, ...)
+{
+ gchar *buffer;
+ va_list args;
+
+ va_start (args, format);
+ buffer = arts_strdup_vprintf (format, args);
+ va_end (args);
+
+ return buffer;
+}