]> git.sesse.net Git - vlc/blob - src/interface/intf_msg.c
* ./plugins/macosx/intf_vlc_wrapper.m: fix for non-ASCII filenames in the
[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.49 2002/05/17 00:58:13 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     if( msg_bank.i_sub )
184     {
185         msg_bank.pp_sub = realloc( msg_bank.pp_sub,
186             msg_bank.i_sub * sizeof( intf_subscription_t* ) );
187     }
188     else
189     {
190         free( msg_bank.pp_sub );
191         msg_bank.pp_sub = NULL;
192     }
193
194     vlc_mutex_unlock( &msg_bank.lock );
195 }
196
197 /*****************************************************************************
198  * intf_Msg: print a message                                             (ok ?)
199  *****************************************************************************
200  * This function queue a message for later printing, or print it immediately
201  * if the queue isn't used.
202  *****************************************************************************/
203 void intf_Msg( char *psz_format, ... )
204 {
205     va_list ap;
206
207     va_start( ap, psz_format );
208     QueueMsg( INTF_MSG_STD, 0, psz_format, ap );
209     va_end( ap );
210 }
211
212 /*****************************************************************************
213  * intf_ErrMsg : print an error message                                  (ok ?)
214  *****************************************************************************
215  * This function is the same as intf_Msg, except that it prints its messages
216  * on stderr.
217  *****************************************************************************/
218 void intf_ErrMsg( char *psz_format, ... )
219 {
220     va_list ap;
221
222     va_start( ap, psz_format );
223     QueueMsg( INTF_MSG_ERR, 0, psz_format, ap );
224     va_end( ap );
225 }
226
227 /*****************************************************************************
228  * intf_WarnMsg : print a warning message
229  *****************************************************************************
230  * This function is the same as intf_Msg, except that it concerns warning
231  * messages for testing purpose.
232  *****************************************************************************/
233 void intf_WarnMsg( int i_level, char *psz_format, ... )
234 {
235     va_list ap;
236     
237     va_start( ap, psz_format );
238     QueueMsg( INTF_MSG_WARN, i_level, psz_format, ap );
239     va_end( ap );
240 }
241
242 /*****************************************************************************
243  * intf_StatMsg : print a statistic message
244  *****************************************************************************
245  * This function is the same as intf_Msg, except that it concerns statistic
246  * messages for testing purpose.
247  *****************************************************************************/
248 void intf_StatMsg( char *psz_format, ... )
249 {
250     va_list ap;
251     
252     if( p_main->b_stats )
253     {
254         va_start( ap, psz_format );
255         QueueMsg( INTF_MSG_STAT, 0, psz_format, ap );
256         va_end( ap );
257     }
258 }
259
260 /*****************************************************************************
261  * intf_WarnHexDump : print a hexadecimal dump of a memory area
262  *****************************************************************************
263  * This is convenient for debugging purposes.
264  *****************************************************************************/
265 void intf_WarnHexDump( int i_level, void *p_data, int i_size )
266 {
267     int   i_index = 0;
268     int   i_subindex;
269     char  p_string[75];
270     u8   *p_area = (u8 *)p_data;
271
272     intf_WarnMsg( i_level, "hexdump: dumping %i bytes at address %p",
273                            i_size, p_data );
274
275     while( i_index < i_size )
276     {
277         i_subindex = 0;
278
279         while( ( i_subindex < 24 ) && ( i_index + i_subindex < i_size ) )
280         {
281             sprintf( p_string + 3 * i_subindex, "%.2x ",
282                      p_area[ i_index + i_subindex ] );
283
284             i_subindex++;
285         }
286
287         /* -1 here is safe because we know we printed at least one */
288         p_string[ 3 * i_subindex - 1 ] = '\0';
289         intf_WarnMsg( i_level, "0x%.4x: %s", i_index, p_string );
290
291         i_index += 24;
292     }
293
294     intf_WarnMsg( i_level, "hexdump: %i bytes dumped", i_size );
295 }
296
297 /* following functions are local */
298
299 /*****************************************************************************
300  * QueueMsg: add a message to a queue
301  *****************************************************************************
302  * This function provides basic functionnalities to other intf_*Msg functions.
303  * It adds a message to a queue (after having printed all stored messages if it
304  * is full). If the message can't be converted to string in memory, it issues
305  * a warning.
306  *****************************************************************************/
307 static void QueueMsg( int i_type, int i_level, char *psz_format, va_list ap )
308 {
309     char *                  psz_str;             /* formatted message string */
310     msg_item_t *            p_item;                /* pointer to message */
311 #ifdef WIN32
312     char *                  psz_temp;
313 #endif
314
315     /*
316      * Convert message to string
317      */
318 #ifdef HAVE_VASPRINTF
319     vasprintf( &psz_str, psz_format, ap );
320 #else
321     psz_str = (char*) malloc( strlen(psz_format) + INTF_MAX_MSG_SIZE );
322 #endif
323
324     if( psz_str == NULL )
325     {
326         fprintf(stderr, "intf warning: can't store following message (%s): ",
327                 strerror(errno) );
328         vfprintf(stderr, psz_format, ap );
329         fprintf(stderr, "\n" );
330         return;
331     }
332
333 #ifndef HAVE_VASPRINTF
334 #   ifdef WIN32
335     psz_temp = ConvertPrintfFormatString(psz_format);
336     if( !psz_temp )
337     {
338         fprintf(stderr, "intf warning: couldn't print message");
339         return;
340     }
341     vsprintf( psz_str, psz_temp, ap );
342     free( psz_temp );
343 #   else
344     vsprintf( psz_str, psz_format, ap );
345 #   endif
346 #endif
347
348     /* Put message in queue */
349     vlc_mutex_lock( &msg_bank.lock );
350
351     /* Send the message to stderr */
352     if( i_level <= p_main->i_warning_level )
353     {
354         fprintf( stderr, "%s\n", psz_str );
355     }
356
357     /* Put the message in the queue if there is room for it */
358     if( ((msg_bank.i_stop - msg_bank.i_start + 1) % INTF_MSG_QSIZE) == 0 )
359     {
360         FlushMsg( );
361
362         if( ((msg_bank.i_stop - msg_bank.i_start + 1) % INTF_MSG_QSIZE) == 0 )
363         {
364             fprintf( stderr, "intf warning: message queue overflow\n" );
365             vlc_mutex_unlock( &msg_bank.lock );
366             return;
367         }
368     }
369
370     p_item = msg_bank.msg + msg_bank.i_stop;
371     msg_bank.i_stop = (msg_bank.i_stop + 1) % INTF_MSG_QSIZE;
372
373     /* Fill message information fields */
374     p_item->i_type =     i_type;
375     p_item->psz_msg =    psz_str;
376
377     vlc_mutex_unlock( &msg_bank.lock );
378 }
379
380 /*****************************************************************************
381  * FlushMsg
382  *****************************************************************************
383  * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
384  * this function does not check the lock.
385  *****************************************************************************/
386 static void FlushMsg ( void )
387 {
388     int i_index, i_start, i_stop;
389
390     /* Get the maximum message index that can be freed */
391     i_stop = msg_bank.i_stop;
392
393     /* Check until which value we can free messages */
394     for( i_index = 0; i_index < msg_bank.i_sub; i_index++ )
395     {
396         i_start = msg_bank.pp_sub[ i_index ]->i_start;
397
398         /* If this subscriber is late, we don't free messages before
399          * his i_start value, otherwise he'll miss messages */
400         if(   ( i_start < i_stop
401                && (msg_bank.i_stop <= i_start || i_stop <= msg_bank.i_stop) )
402            || ( i_stop < i_start
403                && (i_stop <= msg_bank.i_stop && msg_bank.i_stop <= i_start) ) )
404         {
405             i_stop = i_start;
406         }
407     }
408
409     /* Free message data */
410     for( i_index = msg_bank.i_start;
411          i_index != i_stop;
412          i_index = (i_index+1) % INTF_MSG_QSIZE )
413     {
414         free( msg_bank.msg[i_index].psz_msg );
415     }
416
417     /* Update the new start value */
418     msg_bank.i_start = i_index;
419 }
420
421 /*****************************************************************************
422  * ConvertPrintfFormatString: replace all occurrences of %ll with %I64 in the
423  *                            printf format string.
424  *****************************************************************************
425  * Win32 doesn't recognize the "%ll" format in a printf string, so we have
426  * to convert this string to something that win32 can handle.
427  * This is a REALLY UGLY HACK which won't even work in every situation,
428  * but hey I don't want to put an ifdef WIN32 each time I use printf with
429  * a "long long" type!!!
430  * By the way, if we don't do this we can sometimes end up with segfaults.
431  *****************************************************************************/
432 #if defined( WIN32 )
433 static char *ConvertPrintfFormatString( char *psz_format )
434 {
435   int i, i_counter=0, i_pos=0;
436   char *psz_dest;
437
438   /* We first need to check how many occurences of %ll there are in the
439    * psz_format string. Once we'll know that we'll be able to malloc the
440    * destination string */
441
442   if( strlen( psz_format ) <= 3 )
443       return strdup( psz_format );
444
445   for( i=0; i <= (strlen(psz_format) - 3); i++ )
446   {
447       if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
448       {
449           i_counter++;
450       }
451   }
452
453   /* malloc the destination string */
454   psz_dest = malloc( strlen(psz_format) + i_counter + 1 );
455   if( psz_dest == NULL )
456   {
457       fprintf( stderr, "intf warning: ConvertPrintfFormatString failed\n");
458       return NULL;
459   }
460
461   /* Now build the modified string */
462   i_counter = 0;
463   for( i=0; i <= (strlen(psz_format) - 3); i++ )
464   {
465       if( !strncmp( (char *)(psz_format + i), "%ll", 3 ) )
466       {
467           memcpy( psz_dest+i_pos+i_counter, psz_format+i_pos, i-i_pos+1);
468           *(psz_dest+i+i_counter+1)='I';
469           *(psz_dest+i+i_counter+2)='6';
470           *(psz_dest+i+i_counter+3)='4';
471           i_pos = i+3;
472           i_counter++;
473       }
474   }
475   strcpy( psz_dest+i_pos+i_counter, psz_format+i_pos );
476
477   return psz_dest;
478 }
479 #endif