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