]> git.sesse.net Git - vlc/blob - src/misc/messages.c
* ./modules/audio_output/oss.c: compilation fixes.
[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.7 2002/08/08 00:35:11 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 static void PrintMsg ( vlc_object_t *, msg_item_t * );
52
53 #if defined( WIN32 )
54 static char *ConvertPrintfFormatString ( const char *psz_format );
55 #endif
56
57 /*****************************************************************************
58  * msg_Create: initialize messages interface
59  *****************************************************************************
60  * This functions has to be called before any call to other msg_* functions.
61  * It set up the locks and the message queue if it is used.
62  *****************************************************************************/
63 void __msg_Create( vlc_object_t *p_this )
64 {
65     /* Message queue initialization */
66     vlc_mutex_init( p_this, &p_this->p_vlc->msg_bank.lock );
67
68     p_this->p_vlc->msg_bank.b_configured = VLC_FALSE;
69     p_this->p_vlc->msg_bank.b_overflow = VLC_FALSE;
70
71     p_this->p_vlc->msg_bank.i_start = 0;
72     p_this->p_vlc->msg_bank.i_stop = 0;
73
74     p_this->p_vlc->msg_bank.i_sub = 0;
75     p_this->p_vlc->msg_bank.pp_sub = NULL;
76 }
77
78 /*****************************************************************************
79  * msg_Flush: flush the message queue
80  *****************************************************************************/
81 void __msg_Flush( vlc_object_t *p_this )
82 {
83     int i_index;
84
85     vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
86
87     p_this->p_vlc->msg_bank.b_configured = VLC_TRUE;
88
89     for( i_index = p_this->p_vlc->msg_bank.i_start;
90          i_index != p_this->p_vlc->msg_bank.i_stop;
91          i_index = (i_index+1) % VLC_MSG_QSIZE )
92     {
93         PrintMsg( p_this, &p_this->p_vlc->msg_bank.msg[i_index] );
94     }
95
96     FlushMsg( &p_this->p_vlc->msg_bank );
97
98     vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
99 }
100
101 /*****************************************************************************
102  * msg_Destroy: free resources allocated by msg_Create
103  *****************************************************************************
104  * This functions prints all messages remaining in queue, then free all the
105  * resources allocated by msg_Create.
106  * No other messages interface functions should be called after this one.
107  *****************************************************************************/
108 void __msg_Destroy( vlc_object_t *p_this )
109 {
110     if( p_this->p_vlc->msg_bank.i_sub )
111     {
112         msg_Err( p_this, "stale interface subscribers" );
113     }
114
115     /* Flush the queue */
116     if( !p_this->p_vlc->msg_bank.b_configured )
117     {
118         msg_Flush( p_this );
119     }
120     else
121     {
122         FlushMsg( &p_this->p_vlc->msg_bank );
123     }
124
125     /* Destroy lock */
126     vlc_mutex_destroy( &p_this->p_vlc->msg_bank.lock );
127 }
128
129 /*****************************************************************************
130  * msg_Subscribe: subscribe to the message queue.
131  *****************************************************************************/
132 msg_subscription_t *__msg_Subscribe( vlc_object_t *p_this )
133 {
134     msg_subscription_t *p_sub = malloc( sizeof( msg_subscription_t ) );
135
136     vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
137
138     /* Add subscription to the list */
139     p_this->p_vlc->msg_bank.i_sub++;
140     p_this->p_vlc->msg_bank.pp_sub = realloc( p_this->p_vlc->msg_bank.pp_sub,
141         p_this->p_vlc->msg_bank.i_sub * sizeof( msg_subscription_t* ) );
142
143     p_this->p_vlc->msg_bank.pp_sub[ p_this->p_vlc->msg_bank.i_sub - 1 ] = p_sub;
144
145     p_sub->i_start = p_this->p_vlc->msg_bank.i_start;
146     p_sub->pi_stop = &p_this->p_vlc->msg_bank.i_stop;
147
148     p_sub->p_msg   = p_this->p_vlc->msg_bank.msg;
149     p_sub->p_lock  = &p_this->p_vlc->msg_bank.lock;
150
151     vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
152
153     return p_sub;
154 }
155
156 /*****************************************************************************
157  * msg_Unsubscribe: unsubscribe from the message queue.
158  *****************************************************************************/
159 void __msg_Unsubscribe( vlc_object_t *p_this, msg_subscription_t *p_sub )
160 {
161     int i_index;
162
163     vlc_mutex_lock( &p_this->p_vlc->msg_bank.lock );
164
165     /* Sanity check */
166     if( !p_this->p_vlc->msg_bank.i_sub )
167     {
168         msg_Err( p_this, "no subscriber in the list" );
169         return;
170     }
171
172     /* Look for the appropriate subscription */
173     for( i_index = 0; i_index < p_this->p_vlc->msg_bank.i_sub; i_index++ )
174     {
175         if( p_this->p_vlc->msg_bank.pp_sub[ i_index ] == p_sub )
176         {
177             break;
178         }
179     }
180
181     if( p_this->p_vlc->msg_bank.pp_sub[ i_index ] != p_sub )
182     {
183         msg_Err( p_this, "subscriber not found" );
184         vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
185         return;
186     }
187
188     /* Remove this subscription */
189     for( ; i_index < (p_this->p_vlc->msg_bank.i_sub - 1); i_index++ )
190     {
191         p_this->p_vlc->msg_bank.pp_sub[ i_index ] = p_this->p_vlc->msg_bank.pp_sub[ i_index+1 ];
192     }
193
194     p_this->p_vlc->msg_bank.i_sub--;
195     if( p_this->p_vlc->msg_bank.i_sub )
196     {
197         p_this->p_vlc->msg_bank.pp_sub = realloc( p_this->p_vlc->msg_bank.pp_sub,
198             p_this->p_vlc->msg_bank.i_sub * sizeof( msg_subscription_t* ) );
199     }
200     else
201     {
202         free( p_this->p_vlc->msg_bank.pp_sub );
203         p_this->p_vlc->msg_bank.pp_sub = NULL;
204     }
205
206
207     vlc_mutex_unlock( &p_this->p_vlc->msg_bank.lock );
208 }
209
210 /*****************************************************************************
211  * __msg_*: print a message
212  *****************************************************************************
213  * These functions queue a message for later printing.
214  *****************************************************************************/
215 void __msg_Generic( vlc_object_t *p_this, int i_type, const char *psz_module,
216                     const char *psz_format, ... )
217 {
218     va_list args;
219
220     va_start( args, psz_format );
221     QueueMsg( p_this, i_type, psz_module, psz_format, args );
222     va_end( args );
223 }
224
225 /* Generic functions used when variadic macros are not available. */
226 #define DECLARE_MSG_FN( FN_NAME, FN_TYPE ) \
227     void FN_NAME( void *p_this, const char *psz_format, ... ) \
228     { \
229         va_list args; \
230         va_start( args, psz_format ); \
231         QueueMsg( (vlc_object_t *)p_this, FN_TYPE, "unknown", \
232                   psz_format, args ); \
233         va_end( args ); \
234     } \
235     struct _
236
237 DECLARE_MSG_FN( __msg_Info, VLC_MSG_INFO );
238 DECLARE_MSG_FN( __msg_Err,  VLC_MSG_ERR );
239 DECLARE_MSG_FN( __msg_Warn, VLC_MSG_WARN );
240 DECLARE_MSG_FN( __msg_Dbg,  VLC_MSG_DBG );
241
242 /*****************************************************************************
243  * QueueMsg: add a message to a queue
244  *****************************************************************************
245  * This function provides basic functionnalities to other msg_* functions.
246  * It adds a message to a queue (after having printed all stored messages if it
247  * is full). If the message can't be converted to string in memory, it issues
248  * a warning.
249  *****************************************************************************/
250 static void QueueMsg( vlc_object_t *p_this, int i_type, const char *psz_module,
251                       const char *psz_format, va_list args )
252 {
253     msg_bank_t *        p_bank = &p_this->p_vlc->msg_bank;   /* message bank */
254     char *              psz_str = NULL;          /* formatted message string */
255     msg_item_t *        p_item = NULL;                 /* pointer to message */
256     msg_item_t          item;             /* message in case of a full queue */
257 #ifdef WIN32
258     char *              psz_temp;
259 #endif
260
261     /*
262      * Convert message to string
263      */
264 #ifdef HAVE_VASPRINTF
265     vasprintf( &psz_str, psz_format, args );
266 #else
267     psz_str = (char*) malloc( strlen(psz_format) + INTF_MAX_MSG_SIZE );
268 #endif
269
270     if( psz_str == NULL )
271     {
272         fprintf( stderr, "main warning: can't store message (%s): ",
273                  strerror(errno) );
274         vfprintf( stderr, psz_format, args );
275         fprintf( stderr, "\n" );
276         return;
277     }
278
279 #ifndef HAVE_VASPRINTF
280 #   ifdef WIN32
281     psz_temp = ConvertPrintfFormatString( psz_format );
282     if( !psz_temp )
283     {
284         fprintf( stderr, "main warning: couldn't print message\n" );
285         return;
286     }
287     vsprintf( psz_str, psz_temp, args );
288     free( psz_temp );
289 #   else
290     vsprintf( psz_str, psz_format, args );
291 #   endif
292 #endif
293
294     /* Put message in queue */
295     vlc_mutex_lock( &p_bank->lock );
296
297     /* Check there is room in the queue for our message */
298     if( p_bank->b_overflow )
299     {
300         FlushMsg( &p_this->p_vlc->msg_bank );
301
302         if( ((p_bank->i_stop - p_bank->i_start + 1) % VLC_MSG_QSIZE) == 0 )
303         {
304             /* Still in overflow mode, print from a dummy item */
305             p_item = &item;
306         }
307         else
308         {
309             /* Pheeew, at last, there is room in the queue! */
310             p_bank->b_overflow = VLC_FALSE;
311         }
312     }
313     else if( ((p_bank->i_stop - p_bank->i_start + 2) % VLC_MSG_QSIZE) == 0 )
314     {
315         FlushMsg( &p_this->p_vlc->msg_bank );
316
317         if( ((p_bank->i_stop - p_bank->i_start + 2) % VLC_MSG_QSIZE) == 0 )
318         {
319             p_bank->b_overflow = VLC_TRUE;
320
321             /* Put the overflow message in the queue */
322             p_item = p_bank->msg + p_bank->i_stop;
323             p_bank->i_stop = (p_bank->i_stop + 1) % VLC_MSG_QSIZE;
324
325             p_item->i_type =      VLC_MSG_ERR;
326             p_item->i_object_id = p_this->i_object_id;
327             p_item->psz_module =  strdup( "message" );
328             p_item->psz_msg =     strdup( "message queue overflowed" );
329
330             PrintMsg( p_this, p_item );
331
332             /* We print from a dummy item */
333             p_item = &item;
334         }
335     }
336
337     if( !p_bank->b_overflow )
338     {
339         /* Put the message in the queue */
340         p_item = p_bank->msg + p_bank->i_stop;
341         p_bank->i_stop = (p_bank->i_stop + 1) % VLC_MSG_QSIZE;
342     }
343
344     /* Fill message information fields */
345     p_item->i_type =      i_type;
346     p_item->i_object_id = p_this->i_object_id;
347     p_item->psz_module =  strdup( psz_module );
348     p_item->psz_msg =     psz_str;
349
350     PrintMsg( p_this, p_item );
351
352     if( p_bank->b_overflow )
353     {
354         free( p_item->psz_module );
355         free( p_item->psz_msg );
356     }
357
358     vlc_mutex_unlock( &p_bank->lock );
359 }
360
361 /* following functions are local */
362
363 /*****************************************************************************
364  * FlushMsg
365  *****************************************************************************
366  * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
367  * this function does not check the lock.
368  *****************************************************************************/
369 static void FlushMsg ( msg_bank_t *p_bank )
370 {
371     int i_index, i_start, i_stop;
372
373     /* Only flush the queue if it has been properly configured */
374     if( !p_bank->b_configured )
375     {
376         return;
377     }
378
379     /* Get the maximum message index that can be freed */
380     i_stop = p_bank->i_stop;
381
382     /* Check until which value we can free messages */
383     for( i_index = 0; i_index < p_bank->i_sub; i_index++ )
384     {
385         i_start = p_bank->pp_sub[ i_index ]->i_start;
386
387         /* If this subscriber is late, we don't free messages before
388          * his i_start value, otherwise he'll miss messages */
389         if(   ( i_start < i_stop
390                && (p_bank->i_stop <= i_start || i_stop <= p_bank->i_stop) )
391            || ( i_stop < i_start
392                && (i_stop <= p_bank->i_stop && p_bank->i_stop <= i_start) ) )
393         {
394             i_stop = i_start;
395         }
396     }
397
398     /* Free message data */
399     for( i_index = p_bank->i_start;
400          i_index != i_stop;
401          i_index = (i_index+1) % VLC_MSG_QSIZE )
402     {
403         free( p_bank->msg[i_index].psz_msg );
404         free( p_bank->msg[i_index].psz_module );
405     }
406
407     /* Update the new start value */
408     p_bank->i_start = i_index;
409 }
410
411 /*****************************************************************************
412  * PrintMsg: output a message item to stderr
413  *****************************************************************************
414  * Print a message to stderr, with colour formatting if needed.
415  *****************************************************************************/
416 static void PrintMsg ( vlc_object_t * p_this, msg_item_t * p_item )
417 {
418 #   define COL(x)  "\033[" #x ";1m"
419 #   define RED     COL(31)
420 #   define GREEN   COL(32)
421 #   define YELLOW  COL(33)
422 #   define WHITE   COL(37)
423 #   define GRAY    "\033[0m"
424
425     static const char * ppsz_type[4] = { "", " error", " warning", " debug" };
426     static const char *ppsz_color[4] = { WHITE, RED, YELLOW, GRAY };
427     int i_type = p_item->i_type;
428
429     if( p_this->p_vlc->b_quiet || !p_this->p_vlc->msg_bank.b_configured )
430     {
431         return;
432     }
433
434     if( !p_this->p_vlc->b_verbose &&
435          ( (i_type == VLC_MSG_WARN) || (i_type == VLC_MSG_DBG) ) )
436     {
437         return;
438     }
439
440     /* Send the message to stderr */
441     if( p_this->p_vlc->b_color )
442     {
443         fprintf( stderr, "[" GREEN "%.2x" GRAY ":" GREEN "%.6x" GRAY "] "
444                          "%s%s: %s%s" GRAY "\n", p_this->p_vlc->i_unique,
445                          p_item->i_object_id, p_item->psz_module,
446                          ppsz_type[i_type], ppsz_color[i_type],
447                          p_item->psz_msg );
448     }
449     else
450     {
451         fprintf( stderr, "[%.2x:%.6x] %s%s: %s\n",
452                          p_this->p_vlc->i_unique, p_item->i_object_id,
453                          p_item->psz_module, ppsz_type[i_type],
454                          p_item->psz_msg );
455     }
456 }
457
458 /*****************************************************************************
459  * ConvertPrintfFormatString: replace all occurrences of %ll with %I64 in the
460  *                            printf format string.
461  *****************************************************************************
462  * Win32 doesn't recognize the "%ll" format in a printf string, so we have
463  * to convert this string to something that win32 can handle.
464  * This is a REALLY UGLY HACK which won't even work in every situation,
465  * but hey I don't want to put an ifdef WIN32 each time I use printf with
466  * a "long long" type!!!
467  * By the way, if we don't do this we can sometimes end up with segfaults.
468  *****************************************************************************/
469 #if defined( WIN32 )
470 static char *ConvertPrintfFormatString( const char *psz_format )
471 {
472   int i, i_counter=0, i_pos=0;
473   char *psz_dest;
474
475   /* We first need to check how many occurences of %ll there are in the
476    * psz_format string. Once we'll know that we'll be able to malloc the
477    * destination string */
478
479   if( strlen( psz_format ) <= 3 )
480       return strdup( psz_format );
481
482   for( i=0; i <= (strlen(psz_format) - 3); i++ )
483   {
484       if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
485       {
486           i_counter++;
487       }
488   }
489
490   /* malloc the destination string */
491   psz_dest = malloc( strlen(psz_format) + i_counter + 1 );
492   if( psz_dest == NULL )
493   {
494       fprintf( stderr, "main warning: ConvertPrintfFormatString failed\n" );
495       return NULL;
496   }
497
498   /* Now build the modified string */
499   i_counter = 0;
500   for( i=0; i <= (strlen(psz_format) - 3); i++ )
501   {
502       if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
503       {
504           memcpy( psz_dest+i_pos+i_counter, psz_format+i_pos, i-i_pos+1);
505           *(psz_dest+i+i_counter+1)='I';
506           *(psz_dest+i+i_counter+2)='6';
507           *(psz_dest+i+i_counter+3)='4';
508           i_pos = i+3;
509           i_counter++;
510       }
511   }
512   strcpy( psz_dest+i_pos+i_counter, psz_format+i_pos );
513
514   return psz_dest;
515 }
516 #endif