]> git.sesse.net Git - vlc/blob - src/misc/messages.c
* ./src/misc/modules.c: added the --plugin-path option to give vlc an
[vlc] / src / misc / messages.c
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 $
8  *
9  * Authors: Vincent Seguin <seguin@via.ecp.fr>
10  *          Samuel Hocevar <sam@zoy.org>
11  *
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.
16  * 
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.
21  *
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  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
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() */
36
37 #include <vlc/vlc.h>
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>                                      /* close(), write() */
41 #endif
42
43 #include "interface.h"
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48 static void QueueMsg ( vlc_object_t *, int , const char *,
49                        const char *, va_list );
50 static void FlushMsg ( msg_bank_t * );
51
52 #if defined( WIN32 )
53 static char *ConvertPrintfFormatString ( const char *psz_format );
54 #endif
55
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 )
63 {
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;
68
69     p_this->p_vlc->msg_bank.i_sub = 0;
70     p_this->p_vlc->msg_bank.pp_sub = NULL;
71 }
72
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 )
81 {
82     /* Destroy lock */
83     vlc_mutex_destroy( &p_this->p_vlc->msg_bank.lock );
84
85     if( p_this->p_vlc->msg_bank.i_sub )
86     {
87         fprintf( stderr, "main error: stale interface subscribers\n" );
88     }
89
90     /* Free remaining messages */
91     FlushMsg( &p_this->p_vlc->msg_bank );
92 }
93
94 /*****************************************************************************
95  * msg_Subscribe: subscribe to the message queue.
96  *****************************************************************************/
97 msg_subscription_t *__msg_Subscribe( vlc_object_t *p_this )
98 {
99     msg_subscription_t *p_sub = malloc( sizeof( msg_subscription_t ) );
100
101     vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
102
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* ) );
107
108     p_this->p_vlc->msg_bank.pp_sub[ p_this->p_vlc->msg_bank.i_sub - 1 ] = p_sub;
109
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;
112
113     p_sub->p_msg   = p_this->p_vlc->msg_bank.msg;
114     p_sub->p_lock  = &p_this->p_vlc->msg_bank.lock;
115
116     vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
117
118     return p_sub;
119 }
120
121 /*****************************************************************************
122  * msg_Unsubscribe: unsubscribe from the message queue.
123  *****************************************************************************/
124 void __msg_Unsubscribe( vlc_object_t *p_this, msg_subscription_t *p_sub )
125 {
126     int i_index;
127
128     vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
129
130     /* Sanity check */
131     if( !p_this->p_vlc->msg_bank.i_sub )
132     {
133         msg_Err( p_this, "no subscriber in the list" );
134         return;
135     }
136
137     /* Look for the appropriate subscription */
138     for( i_index = 0; i_index < p_this->p_vlc->msg_bank.i_sub; i_index++ )
139     {
140         if( p_this->p_vlc->msg_bank.pp_sub[ i_index ] == p_sub )
141         {
142             break;
143         }
144     }
145
146     if( p_this->p_vlc->msg_bank.pp_sub[ i_index ] != p_sub )
147     {
148         msg_Err( p_this, "subscriber not found" );
149         vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
150         return;
151     }
152
153     /* Remove this subscription */
154     for( ; i_index < (p_this->p_vlc->msg_bank.i_sub - 1); i_index++ )
155     {
156         p_this->p_vlc->msg_bank.pp_sub[ i_index ] = p_this->p_vlc->msg_bank.pp_sub[ i_index+1 ];
157     }
158
159     p_this->p_vlc->msg_bank.i_sub--;
160     if( p_this->p_vlc->msg_bank.i_sub )
161     {
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* ) );
164     }
165     else
166     {
167         free( p_this->p_vlc->msg_bank.pp_sub );
168         p_this->p_vlc->msg_bank.pp_sub = NULL;
169     }
170
171
172     vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
173 }
174
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, ... )
182 {
183     va_list args;
184
185     va_start( args, psz_format );
186     QueueMsg( p_this, i_type, psz_module, psz_format, args );
187     va_end( args );
188 }
189
190 void __msg_Info( void *p_this, const char *psz_format, ... )
191 {
192     va_list args;
193     va_start( args, psz_format );
194     QueueMsg( (vlc_object_t *)p_this, VLC_MSG_INFO, "unknown",
195               psz_format, args );
196     va_end( args );
197 }
198
199 void __msg_Err( void *p_this, const char *psz_format, ... )
200 {
201     va_list args;
202     va_start( args, psz_format );
203     QueueMsg( (vlc_object_t *)p_this, VLC_MSG_ERR, "unknown",
204               psz_format, args );
205     va_end( args );
206 }
207
208 void __msg_Warn( void *p_this, const char *psz_format, ... )
209 {
210     va_list args;
211     va_start( args, psz_format );
212     QueueMsg( (vlc_object_t *)p_this, VLC_MSG_WARN, "unknown",
213               psz_format, args );
214     va_end( args );
215 }
216
217 void __msg_Dbg( void *p_this, const char *psz_format, ... )
218 {
219     va_list args;
220     va_start( args, psz_format );
221     QueueMsg( (vlc_object_t *)p_this, VLC_MSG_DBG, "unknown",
222               psz_format, args );
223     va_end( args );
224 }
225
226 #if 0
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 )
233 {
234     int   i_index = 0;
235     int   i_subindex;
236     char  p_string[75];
237     u8   *p_area = (u8 *)p_data;
238
239     msg_Dbg( "dumping %i bytes at address %p", i_size, p_data );
240
241     while( i_index < i_size )
242     {
243         i_subindex = 0;
244
245         while( ( i_subindex < 24 ) && ( i_index + i_subindex < i_size ) )
246         {
247             sprintf( p_string + 3 * i_subindex, "%.2x ",
248                      p_area[ i_index + i_subindex ] );
249
250             i_subindex++;
251         }
252
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 );
256
257         i_index += 24;
258     }
259
260     msg_Dbg( "hexdump: %i bytes dumped", i_size );
261 }
262 #endif
263
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
270  * a warning.
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 )
274 {
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 */
278 #ifdef WIN32
279     char *              psz_temp;
280 #endif
281
282     /*
283      * Convert message to string
284      */
285 #ifdef HAVE_VASPRINTF
286     vasprintf( &psz_str, psz_format, args );
287 #else
288     psz_str = (char*) malloc( strlen(psz_format) + INTF_MAX_MSG_SIZE );
289 #endif
290
291     if( psz_str == NULL )
292     {
293         fprintf( stderr, "main warning: can't store message (%s): ",
294                  strerror(errno) );
295         vfprintf( stderr, psz_format, args );
296         fprintf( stderr, "\n" );
297         return;
298     }
299
300 #ifndef HAVE_VASPRINTF
301 #   ifdef WIN32
302     psz_temp = ConvertPrintfFormatString( psz_format );
303     if( !psz_temp )
304     {
305         fprintf( stderr, "main warning: couldn't print message\n" );
306         return;
307     }
308     vsprintf( psz_str, psz_temp, args );
309     free( psz_temp );
310 #   else
311     vsprintf( psz_str, psz_format, args );
312 #   endif
313 #endif
314
315     /* Put message in queue */
316     vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
317
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 ) )
325     {
326         if( p_this->p_vlc->b_color )
327         {
328 #           define COL(x)  "\033[" #x ";1m"
329 #           define RED     COL(31)
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 };
335
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 );
340         }
341         else
342         {
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 );
346         }
347     }
348
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 )
351     {
352         FlushMsg( &p_this->p_vlc->msg_bank );
353
354         if( ((p_this->p_vlc->msg_bank.i_stop - p_this->p_vlc->msg_bank.i_start + 1) % VLC_MSG_QSIZE) == 0 )
355         {
356             fprintf( stderr, "main warning: message queue overflow\n" );
357             vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
358             return;
359         }
360     }
361
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;
364
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;
369
370     vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
371 }
372
373 /* following functions are local */
374
375 /*****************************************************************************
376  * FlushMsg
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 )
382 {
383     int i_index, i_start, i_stop;
384
385     /* Get the maximum message index that can be freed */
386     i_stop = p_bank->i_stop;
387
388     /* Check until which value we can free messages */
389     for( i_index = 0; i_index < p_bank->i_sub; i_index++ )
390     {
391         i_start = p_bank->pp_sub[ i_index ]->i_start;
392
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) ) )
399         {
400             i_stop = i_start;
401         }
402     }
403
404     /* Free message data */
405     for( i_index = p_bank->i_start;
406          i_index != i_stop;
407          i_index = (i_index+1) % VLC_MSG_QSIZE )
408     {
409         free( p_bank->msg[i_index].psz_msg );
410         free( p_bank->msg[i_index].psz_module );
411     }
412
413     /* Update the new start value */
414     p_bank->i_start = i_index;
415 }
416
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  *****************************************************************************/
428 #if defined( WIN32 )
429 static char *ConvertPrintfFormatString( const char *psz_format )
430 {
431   int i, i_counter=0, i_pos=0;
432   char *psz_dest;
433
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 */
437
438   if( strlen( psz_format ) <= 3 )
439       return strdup( psz_format );
440
441   for( i=0; i <= (strlen(psz_format) - 3); i++ )
442   {
443       if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
444       {
445           i_counter++;
446       }
447   }
448
449   /* malloc the destination string */
450   psz_dest = malloc( strlen(psz_format) + i_counter + 1 );
451   if( psz_dest == NULL )
452   {
453       fprintf( stderr, "main warning: ConvertPrintfFormatString failed\n" );
454       return NULL;
455   }
456
457   /* Now build the modified string */
458   i_counter = 0;
459   for( i=0; i <= (strlen(psz_format) - 3); i++ )
460   {
461       if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
462       {
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';
467           i_pos = i+3;
468           i_counter++;
469       }
470   }
471   strcpy( psz_dest+i_pos+i_counter, psz_format+i_pos );
472
473   return psz_dest;
474 }
475 #endif