]> git.sesse.net Git - vlc/blob - src/interface/intf_msg.c
* ./src/interface/main.c: we no longer segfault if argc == 0.
[vlc] / src / interface / intf_msg.c
1 /*****************************************************************************
2  * intf_msg.c: messages interface
3  * This library provides basic functions for threads to interact with user
4  * interface, such as message output. See config.h for output configuration.
5  *****************************************************************************
6  * Copyright (C) 1998-2001 VideoLAN
7  * $Id: intf_msg.c,v 1.48 2002/04/24 00:36:24 sam Exp $
8  *
9  * Authors: Vincent Seguin <seguin@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                  /* errno */
30 #include <fcntl.h>                     /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
31 #include <stdio.h>                                               /* required */
32 #include <stdarg.h>                                       /* va_list for BSD */
33 #include <stdlib.h>                                              /* malloc() */
34 #include <string.h>                                            /* strerror() */
35
36 #include <videolan/vlc.h>
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>                                      /* close(), write() */
40 #endif
41
42 #include "interface.h"
43
44
45 /*****************************************************************************
46  * intf_msg_t
47  *****************************************************************************
48  * Store all data requiered by messages interfaces. It has a single reference
49  * int p_main.
50  *****************************************************************************/
51 typedef struct msg_bank_s
52 {
53     /* Message queue lock */
54     vlc_mutex_t             lock;
55
56     /* Message queue */
57     msg_item_t              msg[INTF_MSG_QSIZE];            /* message queue */
58     int i_start;
59     int i_stop;
60
61     /* Subscribers */
62     int i_sub;
63     intf_subscription_t **pp_sub;
64
65 } msg_bank_t;
66
67 msg_bank_t msg_bank;
68
69 /*****************************************************************************
70  * Local prototypes
71  *****************************************************************************/
72 static void QueueMsg ( int, int, char *, va_list );
73 static void FlushMsg ( void );
74
75 #if defined( WIN32 )
76 static char *ConvertPrintfFormatString ( char *psz_format );
77 #endif
78
79 /*****************************************************************************
80  * intf_MsgCreate: initialize messages interface                         (ok ?)
81  *****************************************************************************
82  * This functions has to be called before any call to other intf_*Msg functions.
83  * It set up the locks and the message queue if it is used.
84  *****************************************************************************/
85 void intf_MsgCreate( void )
86 {
87     /* Message queue initialization */
88     vlc_mutex_init( &msg_bank.lock );
89     msg_bank.i_start = 0;
90     msg_bank.i_stop = 0;
91
92     msg_bank.i_sub = 0;
93     msg_bank.pp_sub = NULL;
94 }
95
96 /*****************************************************************************
97  * intf_MsgDestroy: free resources allocated by intf_InitMsg            (ok ?)
98  *****************************************************************************
99  * This functions prints all messages remaining in queue, then free all the
100  * resources allocated by intf_InitMsg.
101  * No other messages interface functions should be called after this one.
102  *****************************************************************************/
103 void intf_MsgDestroy( void )
104 {
105     /* Destroy lock */
106     vlc_mutex_destroy( &msg_bank.lock );
107
108     if( msg_bank.i_sub )
109     {
110         fprintf( stderr, "intf error: stale interface subscribers\n" );
111     }
112
113     /* Free remaining messages */
114     FlushMsg( );
115 }
116
117 /*****************************************************************************
118  * intf_MsgSub: subscribe to the message queue.
119  *****************************************************************************/
120 intf_subscription_t *intf_MsgSub( void )
121 {
122     intf_subscription_t *p_sub = malloc( sizeof( intf_subscription_t ) );
123
124     vlc_mutex_lock( &msg_bank.lock );
125
126     /* Add subscription to the list */
127     msg_bank.i_sub++;
128     msg_bank.pp_sub = realloc( msg_bank.pp_sub,
129         msg_bank.i_sub * sizeof( intf_subscription_t* ) );
130
131     msg_bank.pp_sub[ msg_bank.i_sub - 1 ] = p_sub;
132
133     p_sub->i_start = msg_bank.i_start;
134     p_sub->pi_stop = &msg_bank.i_stop;
135
136     p_sub->p_msg   = msg_bank.msg;
137     p_sub->p_lock  = &msg_bank.lock;
138
139     vlc_mutex_unlock( &msg_bank.lock );
140
141     return p_sub;
142 }
143
144 /*****************************************************************************
145  * intf_MsgUnsub: unsubscribe from the message queue.
146  *****************************************************************************/
147 void intf_MsgUnsub( intf_subscription_t *p_sub )
148 {
149     int i_index;
150
151     vlc_mutex_lock( &msg_bank.lock );
152
153     /* Sanity check */
154     if( !msg_bank.i_sub )
155     {
156         intf_ErrMsg( "intf error: no subscriber in the list" );
157         return;
158     }
159
160     /* Look for the appropriate subscription */
161     for( i_index = 0; i_index < msg_bank.i_sub; i_index++ )
162     {
163         if( msg_bank.pp_sub[ i_index ] == p_sub )
164         {
165             break;
166         }
167     }
168
169     if( msg_bank.pp_sub[ i_index ] != p_sub )
170     {
171         intf_ErrMsg( "intf error: subscriber not found" );
172         vlc_mutex_unlock( &msg_bank.lock );
173         return;
174     }
175
176     /* Remove this subscription */
177     for( ; i_index < (msg_bank.i_sub - 1); i_index++ )
178     {
179         msg_bank.pp_sub[ i_index ] = msg_bank.pp_sub[ i_index+1 ];
180     }
181
182     msg_bank.i_sub--;
183     msg_bank.pp_sub = realloc( msg_bank.pp_sub,
184         msg_bank.i_sub * sizeof( intf_subscription_t* ) );
185
186     vlc_mutex_unlock( &msg_bank.lock );
187 }
188
189 /*****************************************************************************
190  * intf_Msg: print a message                                             (ok ?)
191  *****************************************************************************
192  * This function queue a message for later printing, or print it immediately
193  * if the queue isn't used.
194  *****************************************************************************/
195 void intf_Msg( char *psz_format, ... )
196 {
197     va_list ap;
198
199     va_start( ap, psz_format );
200     QueueMsg( INTF_MSG_STD, 0, psz_format, ap );
201     va_end( ap );
202 }
203
204 /*****************************************************************************
205  * intf_ErrMsg : print an error message                                  (ok ?)
206  *****************************************************************************
207  * This function is the same as intf_Msg, except that it prints its messages
208  * on stderr.
209  *****************************************************************************/
210 void intf_ErrMsg( char *psz_format, ... )
211 {
212     va_list ap;
213
214     va_start( ap, psz_format );
215     QueueMsg( INTF_MSG_ERR, 0, psz_format, ap );
216     va_end( ap );
217 }
218
219 /*****************************************************************************
220  * intf_WarnMsg : print a warning message
221  *****************************************************************************
222  * This function is the same as intf_Msg, except that it concerns warning
223  * messages for testing purpose.
224  *****************************************************************************/
225 void intf_WarnMsg( int i_level, char *psz_format, ... )
226 {
227     va_list ap;
228     
229     va_start( ap, psz_format );
230     QueueMsg( INTF_MSG_WARN, i_level, psz_format, ap );
231     va_end( ap );
232 }
233
234 /*****************************************************************************
235  * intf_StatMsg : print a statistic message
236  *****************************************************************************
237  * This function is the same as intf_Msg, except that it concerns statistic
238  * messages for testing purpose.
239  *****************************************************************************/
240 void intf_StatMsg( char *psz_format, ... )
241 {
242     va_list ap;
243     
244     if( p_main->b_stats )
245     {
246         va_start( ap, psz_format );
247         QueueMsg( INTF_MSG_STAT, 0, psz_format, ap );
248         va_end( ap );
249     }
250 }
251
252 /*****************************************************************************
253  * intf_WarnHexDump : print a hexadecimal dump of a memory area
254  *****************************************************************************
255  * This is convenient for debugging purposes.
256  *****************************************************************************/
257 void intf_WarnHexDump( int i_level, void *p_data, int i_size )
258 {
259     int   i_index = 0;
260     int   i_subindex;
261     char  p_string[75];
262     u8   *p_area = (u8 *)p_data;
263
264     intf_WarnMsg( i_level, "hexdump: dumping %i bytes at address %p",
265                            i_size, p_data );
266
267     while( i_index < i_size )
268     {
269         i_subindex = 0;
270
271         while( ( i_subindex < 24 ) && ( i_index + i_subindex < i_size ) )
272         {
273             sprintf( p_string + 3 * i_subindex, "%.2x ",
274                      p_area[ i_index + i_subindex ] );
275
276             i_subindex++;
277         }
278
279         /* -1 here is safe because we know we printed at least one */
280         p_string[ 3 * i_subindex - 1 ] = '\0';
281         intf_WarnMsg( i_level, "0x%.4x: %s", i_index, p_string );
282
283         i_index += 24;
284     }
285
286     intf_WarnMsg( i_level, "hexdump: %i bytes dumped", i_size );
287 }
288
289 /* following functions are local */
290
291 /*****************************************************************************
292  * QueueMsg: add a message to a queue
293  *****************************************************************************
294  * This function provides basic functionnalities to other intf_*Msg functions.
295  * It adds a message to a queue (after having printed all stored messages if it
296  * is full). If the message can't be converted to string in memory, it issues
297  * a warning.
298  *****************************************************************************/
299 static void QueueMsg( int i_type, int i_level, char *psz_format, va_list ap )
300 {
301     char *                  psz_str;             /* formatted message string */
302     msg_item_t *            p_item;                /* pointer to message */
303 #ifdef WIN32
304     char *                  psz_temp;
305 #endif
306
307     /*
308      * Convert message to string
309      */
310 #ifdef HAVE_VASPRINTF
311     vasprintf( &psz_str, psz_format, ap );
312 #else
313     psz_str = (char*) malloc( strlen(psz_format) + INTF_MAX_MSG_SIZE );
314 #endif
315
316     if( psz_str == NULL )
317     {
318         fprintf(stderr, "intf warning: can't store following message (%s): ",
319                 strerror(errno) );
320         vfprintf(stderr, psz_format, ap );
321         fprintf(stderr, "\n" );
322         return;
323     }
324
325 #ifndef HAVE_VASPRINTF
326 #   ifdef WIN32
327     psz_temp = ConvertPrintfFormatString(psz_format);
328     if( !psz_temp )
329     {
330         fprintf(stderr, "intf warning: couldn't print message");
331         return;
332     }
333     vsprintf( psz_str, psz_temp, ap );
334     free( psz_temp );
335 #   else
336     vsprintf( psz_str, psz_format, ap );
337 #   endif
338 #endif
339
340     /* Put message in queue */
341     vlc_mutex_lock( &msg_bank.lock );
342
343     /* Send the message to stderr */
344     if( i_level <= p_main->i_warning_level )
345     {
346         fprintf( stderr, "%s\n", psz_str );
347     }
348
349     /* Put the message in the queue if there is room for it */
350     if( ((msg_bank.i_stop - msg_bank.i_start + 1) % INTF_MSG_QSIZE) == 0 )
351     {
352         FlushMsg( );
353
354         if( ((msg_bank.i_stop - msg_bank.i_start + 1) % INTF_MSG_QSIZE) == 0 )
355         {
356             fprintf( stderr, "intf warning: message queue overflow\n" );
357             vlc_mutex_unlock( &msg_bank.lock );
358             return;
359         }
360     }
361
362     p_item = msg_bank.msg + msg_bank.i_stop;
363     msg_bank.i_stop = (msg_bank.i_stop + 1) % INTF_MSG_QSIZE;
364
365     /* Fill message information fields */
366     p_item->i_type =     i_type;
367     p_item->psz_msg =    psz_str;
368
369     vlc_mutex_unlock( &msg_bank.lock );
370 }
371
372 /*****************************************************************************
373  * FlushMsg
374  *****************************************************************************
375  * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
376  * this function does not check the lock.
377  *****************************************************************************/
378 static void FlushMsg ( void )
379 {
380     int i_index, i_start, i_stop;
381
382     /* Get the maximum message index that can be freed */
383     i_stop = msg_bank.i_stop;
384
385     /* Check until which value we can free messages */
386     for( i_index = 0; i_index < msg_bank.i_sub; i_index++ )
387     {
388         i_start = msg_bank.pp_sub[ i_index ]->i_start;
389
390         /* If this subscriber is late, we don't free messages before
391          * his i_start value, otherwise he'll miss messages */
392         if(   ( i_start < i_stop
393                && (msg_bank.i_stop <= i_start || i_stop <= msg_bank.i_stop) )
394            || ( i_stop < i_start
395                && (i_stop <= msg_bank.i_stop && msg_bank.i_stop <= i_start) ) )
396         {
397             i_stop = i_start;
398         }
399     }
400
401     /* Free message data */
402     for( i_index = msg_bank.i_start;
403          i_index != i_stop;
404          i_index = (i_index+1) % INTF_MSG_QSIZE )
405     {
406         free( msg_bank.msg[i_index].psz_msg );
407     }
408
409     /* Update the new start value */
410     msg_bank.i_start = i_index;
411 }
412
413 /*****************************************************************************
414  * ConvertPrintfFormatString: replace all occurrences of %ll with %I64 in the
415  *                            printf format string.
416  *****************************************************************************
417  * Win32 doesn't recognize the "%ll" format in a printf string, so we have
418  * to convert this string to something that win32 can handle.
419  * This is a REALLY UGLY HACK which won't even work in every situation,
420  * but hey I don't want to put an ifdef WIN32 each time I use printf with
421  * a "long long" type!!!
422  * By the way, if we don't do this we can sometimes end up with segfaults.
423  *****************************************************************************/
424 #if defined( WIN32 )
425 static char *ConvertPrintfFormatString( char *psz_format )
426 {
427   int i, i_counter=0, i_pos=0;
428   char *psz_dest;
429
430   /* We first need to check how many occurences of %ll there are in the
431    * psz_format string. Once we'll know that we'll be able to malloc the
432    * destination string */
433
434   if( strlen( psz_format ) <= 3 )
435       return strdup( psz_format );
436
437   for( i=0; i <= (strlen(psz_format) - 3); i++ )
438   {
439       if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
440       {
441           i_counter++;
442       }
443   }
444
445   /* malloc the destination string */
446   psz_dest = malloc( strlen(psz_format) + i_counter + 1 );
447   if( psz_dest == NULL )
448   {
449       fprintf( stderr, "intf warning: ConvertPrintfFormatString failed\n");
450       return NULL;
451   }
452
453   /* Now build the modified string */
454   i_counter = 0;
455   for( i=0; i <= (strlen(psz_format) - 3); i++ )
456   {
457       if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
458       {
459           memcpy( psz_dest+i_pos+i_counter, psz_format+i_pos, i-i_pos+1);
460           *(psz_dest+i+i_counter+1)='I';
461           *(psz_dest+i+i_counter+2)='6';
462           *(psz_dest+i+i_counter+3)='4';
463           i_pos = i+3;
464           i_counter++;
465       }
466   }
467   strcpy( psz_dest+i_pos+i_counter, psz_format+i_pos );
468
469   return psz_dest;
470 }
471 #endif