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