1 /*****************************************************************************
2 * messages.c: messages interface
3 * This library provides an interface to the message queue to be used by other
4 * modules, especially intf modules. See config.h for output configuration.
5 *****************************************************************************
6 * Copyright (C) 1998-2002 VideoLAN
7 * $Id: messages.c,v 1.3 2002/06/27 19:05:17 sam Exp $
9 * Authors: Vincent Seguin <seguin@via.ecp.fr>
10 * Samuel Hocevar <sam@zoy.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
30 #include <errno.h> /* errno */
31 #include <fcntl.h> /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
32 #include <stdio.h> /* required */
33 #include <stdarg.h> /* va_list for BSD */
34 #include <stdlib.h> /* malloc() */
35 #include <string.h> /* strerror() */
40 #include <unistd.h> /* close(), write() */
43 #include "interface.h"
45 /*****************************************************************************
47 *****************************************************************************/
48 static void QueueMsg ( vlc_object_t *, int , const char *,
49 const char *, va_list );
50 static void FlushMsg ( msg_bank_t * );
53 static char *ConvertPrintfFormatString ( const char *psz_format );
56 /*****************************************************************************
57 * msg_Create: initialize messages interface
58 *****************************************************************************
59 * This functions has to be called before any call to other msg_* functions.
60 * It set up the locks and the message queue if it is used.
61 *****************************************************************************/
62 void __msg_Create( vlc_object_t *p_this )
64 /* Message queue initialization */
65 vlc_mutex_init( p_this, &p_this->p_vlc->msg_bank.lock );
66 p_this->p_vlc->msg_bank.i_start = 0;
67 p_this->p_vlc->msg_bank.i_stop = 0;
69 p_this->p_vlc->msg_bank.i_sub = 0;
70 p_this->p_vlc->msg_bank.pp_sub = NULL;
73 /*****************************************************************************
74 * msg_Destroy: free resources allocated by msg_Create
75 *****************************************************************************
76 * This functions prints all messages remaining in queue, then free all the
77 * resources allocated by msg_Create.
78 * No other messages interface functions should be called after this one.
79 *****************************************************************************/
80 void __msg_Destroy( vlc_object_t *p_this )
83 vlc_mutex_destroy( &p_this->p_vlc->msg_bank.lock );
85 if( p_this->p_vlc->msg_bank.i_sub )
87 fprintf( stderr, "main error: stale interface subscribers\n" );
90 /* Free remaining messages */
91 FlushMsg( &p_this->p_vlc->msg_bank );
94 /*****************************************************************************
95 * msg_Subscribe: subscribe to the message queue.
96 *****************************************************************************/
97 msg_subscription_t *__msg_Subscribe( vlc_object_t *p_this )
99 msg_subscription_t *p_sub = malloc( sizeof( msg_subscription_t ) );
101 vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
103 /* Add subscription to the list */
104 p_this->p_vlc->msg_bank.i_sub++;
105 p_this->p_vlc->msg_bank.pp_sub = realloc( p_this->p_vlc->msg_bank.pp_sub,
106 p_this->p_vlc->msg_bank.i_sub * sizeof( msg_subscription_t* ) );
108 p_this->p_vlc->msg_bank.pp_sub[ p_this->p_vlc->msg_bank.i_sub - 1 ] = p_sub;
110 p_sub->i_start = p_this->p_vlc->msg_bank.i_start;
111 p_sub->pi_stop = &p_this->p_vlc->msg_bank.i_stop;
113 p_sub->p_msg = p_this->p_vlc->msg_bank.msg;
114 p_sub->p_lock = &p_this->p_vlc->msg_bank.lock;
116 vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
121 /*****************************************************************************
122 * msg_Unsubscribe: unsubscribe from the message queue.
123 *****************************************************************************/
124 void __msg_Unsubscribe( vlc_object_t *p_this, msg_subscription_t *p_sub )
128 vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
131 if( !p_this->p_vlc->msg_bank.i_sub )
133 msg_Err( p_this, "no subscriber in the list" );
137 /* Look for the appropriate subscription */
138 for( i_index = 0; i_index < p_this->p_vlc->msg_bank.i_sub; i_index++ )
140 if( p_this->p_vlc->msg_bank.pp_sub[ i_index ] == p_sub )
146 if( p_this->p_vlc->msg_bank.pp_sub[ i_index ] != p_sub )
148 msg_Err( p_this, "subscriber not found" );
149 vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
153 /* Remove this subscription */
154 for( ; i_index < (p_this->p_vlc->msg_bank.i_sub - 1); i_index++ )
156 p_this->p_vlc->msg_bank.pp_sub[ i_index ] = p_this->p_vlc->msg_bank.pp_sub[ i_index+1 ];
159 p_this->p_vlc->msg_bank.i_sub--;
160 if( p_this->p_vlc->msg_bank.i_sub )
162 p_this->p_vlc->msg_bank.pp_sub = realloc( p_this->p_vlc->msg_bank.pp_sub,
163 p_this->p_vlc->msg_bank.i_sub * sizeof( msg_subscription_t* ) );
167 free( p_this->p_vlc->msg_bank.pp_sub );
168 p_this->p_vlc->msg_bank.pp_sub = NULL;
172 vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
175 /*****************************************************************************
176 * __msg_*: print a message
177 *****************************************************************************
178 * These functions queue a message for later printing.
179 *****************************************************************************/
180 void __msg_Generic( vlc_object_t *p_this, int i_type, const char *psz_module,
181 const char *psz_format, ... )
185 va_start( args, psz_format );
186 QueueMsg( p_this, i_type, psz_module, psz_format, args );
190 void __msg_Info( void *p_this, const char *psz_format, ... )
193 va_start( args, psz_format );
194 QueueMsg( (vlc_object_t *)p_this, VLC_MSG_INFO, "unknown",
199 void __msg_Err( void *p_this, const char *psz_format, ... )
202 va_start( args, psz_format );
203 QueueMsg( (vlc_object_t *)p_this, VLC_MSG_ERR, "unknown",
208 void __msg_Warn( void *p_this, const char *psz_format, ... )
211 va_start( args, psz_format );
212 QueueMsg( (vlc_object_t *)p_this, VLC_MSG_WARN, "unknown",
217 void __msg_Dbg( void *p_this, const char *psz_format, ... )
220 va_start( args, psz_format );
221 QueueMsg( (vlc_object_t *)p_this, VLC_MSG_DBG, "unknown",
227 /*****************************************************************************
228 * intf_WarnHexDump : print a hexadecimal dump of a memory area
229 *****************************************************************************
230 * This is convenient for debugging purposes.
231 *****************************************************************************/
232 void intf_WarnHexDump( int i_level, void *p_data, int i_size )
237 u8 *p_area = (u8 *)p_data;
239 msg_Dbg( "dumping %i bytes at address %p", i_size, p_data );
241 while( i_index < i_size )
245 while( ( i_subindex < 24 ) && ( i_index + i_subindex < i_size ) )
247 sprintf( p_string + 3 * i_subindex, "%.2x ",
248 p_area[ i_index + i_subindex ] );
253 /* -1 here is safe because we know we printed at least one */
254 p_string[ 3 * i_subindex - 1 ] = '\0';
255 msg_Dbg( "0x%.4x: %s", i_index, p_string );
260 msg_Dbg( "hexdump: %i bytes dumped", i_size );
264 /*****************************************************************************
265 * QueueMsg: add a message to a queue
266 *****************************************************************************
267 * This function provides basic functionnalities to other msg_* functions.
268 * It adds a message to a queue (after having printed all stored messages if it
269 * is full). If the message can't be converted to string in memory, it issues
271 *****************************************************************************/
272 static void QueueMsg( vlc_object_t *p_this, int i_type, const char *psz_module,
273 const char *psz_format, va_list args )
275 static const char * ppsz_type[4] = { "", " error", " warning", " debug" };
276 char * psz_str; /* formatted message string */
277 msg_item_t * p_item; /* pointer to message */
283 * Convert message to string
285 #ifdef HAVE_VASPRINTF
286 vasprintf( &psz_str, psz_format, args );
288 psz_str = (char*) malloc( strlen(psz_format) + INTF_MAX_MSG_SIZE );
291 if( psz_str == NULL )
293 fprintf( stderr, "main warning: can't store message (%s): ",
295 vfprintf( stderr, psz_format, args );
296 fprintf( stderr, "\n" );
300 #ifndef HAVE_VASPRINTF
302 psz_temp = ConvertPrintfFormatString( psz_format );
305 fprintf( stderr, "main warning: couldn't print message\n" );
308 vsprintf( psz_str, psz_temp, args );
311 vsprintf( psz_str, psz_format, args );
315 /* Put message in queue */
316 vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
318 /* Send the message to stderr */
319 if( (i_type == VLC_MSG_ERR)
320 || ( (i_type == VLC_MSG_INFO) && !p_this->p_vlc->b_quiet )
321 || ( (i_type == VLC_MSG_WARN) && p_this->p_vlc->b_verbose
322 && !p_this->p_vlc->b_quiet )
323 || ( (i_type == VLC_MSG_DBG) && p_this->p_vlc->b_verbose
324 && !p_this->p_vlc->b_quiet ) )
326 if( p_this->p_vlc->b_color )
328 # define COL(x) "\033[" #x ";1m"
330 # define GREEN COL(32)
331 # define YELLOW COL(33)
332 # define WHITE COL(37)
333 # define GRAY "\033[0m"
334 static const char *ppsz_color[4] = { WHITE, RED, YELLOW, GRAY };
336 fprintf( stderr, "[" GREEN "%.2x" GRAY ":" GREEN "%.6x" GRAY "] "
337 "%s%s: %s%s" GRAY "\n", p_this->p_vlc->i_unique,
338 p_this->i_object_id, psz_module,
339 ppsz_type[i_type], ppsz_color[i_type], psz_str );
343 fprintf( stderr, "[%.2x:%.6x] %s%s: %s\n",
344 p_this->p_vlc->i_unique, p_this->i_object_id,
345 psz_module, ppsz_type[i_type], psz_str );
349 /* Put the message in the queue if there is room for it */
350 if( ((p_this->p_vlc->msg_bank.i_stop - p_this->p_vlc->msg_bank.i_start + 1) % VLC_MSG_QSIZE) == 0 )
352 FlushMsg( &p_this->p_vlc->msg_bank );
354 if( ((p_this->p_vlc->msg_bank.i_stop - p_this->p_vlc->msg_bank.i_start + 1) % VLC_MSG_QSIZE) == 0 )
356 fprintf( stderr, "main warning: message queue overflow\n" );
357 vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
362 p_item = p_this->p_vlc->msg_bank.msg + p_this->p_vlc->msg_bank.i_stop;
363 p_this->p_vlc->msg_bank.i_stop = (p_this->p_vlc->msg_bank.i_stop + 1) % VLC_MSG_QSIZE;
365 /* Fill message information fields */
366 p_item->i_type = i_type;
367 p_item->psz_module = strdup( psz_module );
368 p_item->psz_msg = psz_str;
370 vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
373 /* following functions are local */
375 /*****************************************************************************
377 *****************************************************************************
378 * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
379 * this function does not check the lock.
380 *****************************************************************************/
381 static void FlushMsg ( msg_bank_t *p_bank )
383 int i_index, i_start, i_stop;
385 /* Get the maximum message index that can be freed */
386 i_stop = p_bank->i_stop;
388 /* Check until which value we can free messages */
389 for( i_index = 0; i_index < p_bank->i_sub; i_index++ )
391 i_start = p_bank->pp_sub[ i_index ]->i_start;
393 /* If this subscriber is late, we don't free messages before
394 * his i_start value, otherwise he'll miss messages */
395 if( ( i_start < i_stop
396 && (p_bank->i_stop <= i_start || i_stop <= p_bank->i_stop) )
397 || ( i_stop < i_start
398 && (i_stop <= p_bank->i_stop && p_bank->i_stop <= i_start) ) )
404 /* Free message data */
405 for( i_index = p_bank->i_start;
407 i_index = (i_index+1) % VLC_MSG_QSIZE )
409 free( p_bank->msg[i_index].psz_msg );
410 free( p_bank->msg[i_index].psz_module );
413 /* Update the new start value */
414 p_bank->i_start = i_index;
417 /*****************************************************************************
418 * ConvertPrintfFormatString: replace all occurrences of %ll with %I64 in the
419 * printf format string.
420 *****************************************************************************
421 * Win32 doesn't recognize the "%ll" format in a printf string, so we have
422 * to convert this string to something that win32 can handle.
423 * This is a REALLY UGLY HACK which won't even work in every situation,
424 * but hey I don't want to put an ifdef WIN32 each time I use printf with
425 * a "long long" type!!!
426 * By the way, if we don't do this we can sometimes end up with segfaults.
427 *****************************************************************************/
429 static char *ConvertPrintfFormatString( const char *psz_format )
431 int i, i_counter=0, i_pos=0;
434 /* We first need to check how many occurences of %ll there are in the
435 * psz_format string. Once we'll know that we'll be able to malloc the
436 * destination string */
438 if( strlen( psz_format ) <= 3 )
439 return strdup( psz_format );
441 for( i=0; i <= (strlen(psz_format) - 3); i++ )
443 if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
449 /* malloc the destination string */
450 psz_dest = malloc( strlen(psz_format) + i_counter + 1 );
451 if( psz_dest == NULL )
453 fprintf( stderr, "main warning: ConvertPrintfFormatString failed\n" );
457 /* Now build the modified string */
459 for( i=0; i <= (strlen(psz_format) - 3); i++ )
461 if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
463 memcpy( psz_dest+i_pos+i_counter, psz_format+i_pos, i-i_pos+1);
464 *(psz_dest+i+i_counter+1)='I';
465 *(psz_dest+i+i_counter+2)='6';
466 *(psz_dest+i+i_counter+3)='4';
471 strcpy( psz_dest+i_pos+i_counter, psz_format+i_pos );