/*
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 . , 51 Franklin Street , Fifth Floor ,
Boston , MA 02110 - 1301 , USA .
*/
# include "debug.h"
# include <config.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 . , 51 Franklin Street , Fifth Floor ,
* Boston , MA 02110 - 1301 , 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 ;
}