]> git.sesse.net Git - vlc/blob - src/interface/intf_msg.c
* DirectX plugin by Gildas Bazin <gbazin@netcourrier.com>.
[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, 1999, 2000 VideoLAN
7  * $Id: intf_msg.c,v 1.36 2001/06/02 01:09:03 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 "defs.h"
30
31 #include <errno.h>                                                  /* errno */
32 #include <fcntl.h>                     /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
33 #include <stdio.h>                                               /* required */
34 #include <stdarg.h>                                       /* va_list for BSD */
35 #include <stdlib.h>                                              /* malloc() */
36 #include <string.h>                                            /* strerror() */
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>                                      /* close(), write() */
40 #endif
41
42 #include "config.h"
43 #include "common.h"
44 #include "threads.h"
45 #include "mtime.h"
46
47 #include "intf_msg.h"
48 #include "interface.h"
49
50 #include "main.h"
51
52 #ifdef WIN32
53 #ifndef snprintf
54 #define snprintf _snprintf         /* snprintf not defined in mingw32 (bug?) */
55 #endif
56 #endif
57
58 /*****************************************************************************
59  * intf_msg_item_t
60  *****************************************************************************
61  * Store a single message. Messages have a maximal size of INTF_MSG_MSGSIZE.
62  * If TRACE is defined, messages have a date field and debug messages are
63  * printed with a date to allow more precise profiling.
64  *****************************************************************************/
65 typedef struct
66 {
67     int     i_type;                               /* message type, see below */
68     char *  psz_msg;                                   /* the message itself */
69
70 #ifdef TRACE
71     /* Debugging informations - in TRACE mode, debug messages have calling
72      * location informations printed */
73     mtime_t date;                                     /* date of the message */
74     char *  psz_file;               /* file in which the function was called */
75     char *  psz_function;     /* function from which the function was called */
76     int     i_line;                 /* line at which the function was called */
77 #endif
78 } intf_msg_item_t;
79
80 /* Message types */
81 #define INTF_MSG_STD    0                                /* standard message */
82 #define INTF_MSG_ERR    1                                   /* error message */
83 #define INTF_MSG_DBG    3                                   /* debug message */
84 #define INTF_MSG_WARN   4                                  /* warning message*/
85
86
87 /*****************************************************************************
88  * intf_msg_t
89  *****************************************************************************
90  * Store all data requiered by messages interfaces. It has a single reference
91  * int p_main.
92  *****************************************************************************/
93 typedef struct intf_msg_s
94 {
95 #ifdef INTF_MSG_QUEUE
96     /* Message queue */
97     vlc_mutex_t             lock;                      /* message queue lock */
98     int                     i_count;            /* number of messages stored */
99     intf_msg_item_t         msg[INTF_MSG_QSIZE];            /* message queue */
100 #endif
101
102 #ifdef TRACE_LOG
103     /* Log file */
104     FILE *                  p_log_file;                          /* log file */
105 #endif
106
107 #if !defined(INTF_MSG_QUEUE) && !defined(TRACE_LOG)
108     /* If neither messages queue, neither log file is used, then the structure
109      * is empty. However, empty structures are not allowed in C. Therefore, a
110      * dummy integer is used to fill it. */
111     int                     i_dummy;                        /* unused filler */
112 #endif
113 } intf_msg_t;
114
115 /*****************************************************************************
116  * Local prototypes
117  *****************************************************************************/
118
119 static void QueueMsg        ( intf_msg_t *p_msg, int i_type,
120                               char *psz_format, va_list ap );
121 static void PrintMsg        ( intf_msg_item_t *p_msg );
122 #ifdef TRACE
123 static void QueueDbgMsg     ( intf_msg_t *p_msg, char *psz_file,
124                               char *psz_function, int i_line,
125                               char *psz_format, va_list ap );
126 #endif
127 #ifdef INTF_MSG_QUEUE
128 static void FlushLockedMsg  ( intf_msg_t *p_msg );
129 #endif
130
131
132 /*****************************************************************************
133  * intf_MsgCreate: initialize messages interface                         (ok ?)
134  *****************************************************************************
135  * This functions has to be called before any call to other intf_*Msg functions.
136  * It set up the locks and the message queue if it is used.
137  *****************************************************************************/
138 p_intf_msg_t intf_MsgCreate( void )
139 {
140     p_intf_msg_t p_msg;
141
142     /* Allocate structure */
143     p_msg = malloc( sizeof( intf_msg_t ) );
144     if( p_msg == NULL )
145     {
146         errno = ENOMEM;
147     }
148     else
149     {
150 #ifdef INTF_MSG_QUEUE
151     /* Message queue initialization */
152     vlc_mutex_init( &p_msg->lock );                        /* intialize lock */
153     p_msg->i_count = 0;                                    /* queue is empty */
154 #endif
155
156     
157 #ifdef TRACE_LOG
158         /* Log file initialization - on failure, file pointer will be null,
159          * and no log will be issued, but this is not considered as an
160          * error */
161         p_msg->p_log_file = fopen( TRACE_LOG, "w" );
162 #endif
163     }
164     return( p_msg );
165 }
166
167 /*****************************************************************************
168  * intf_MsgDestroy: free resources allocated by intf_InitMsg            (ok ?)
169  *****************************************************************************
170  * This functions prints all messages remaining in queue, then free all the
171  * resources allocated by intf_InitMsg.
172  * No other messages interface functions should be called after this one.
173  *****************************************************************************/
174 void intf_MsgDestroy( void )
175 {
176     intf_FlushMsg();                         /* print all remaining messages */
177
178 #ifdef TRACE_LOG
179     /* Close log file if any */
180     if( p_main->p_msg->p_log_file != NULL )
181     {
182         fclose( p_main->p_msg->p_log_file );
183     }
184 #endif
185
186 #ifdef INTF_MSG_QUEUE
187     /* destroy lock */
188     vlc_mutex_destroy( &p_main->p_msg->lock );
189 #endif
190     
191     /* Free structure */
192     free( p_main->p_msg );
193 }
194
195 /*****************************************************************************
196  * intf_Msg: print a message                                             (ok ?)
197  *****************************************************************************
198  * This function queue a message for later printing, or print it immediately
199  * if the queue isn't used.
200  *****************************************************************************/
201 void intf_Msg( char *psz_format, ... )
202 {
203     va_list ap;
204
205     va_start( ap, psz_format );
206     QueueMsg( p_main->p_msg, INTF_MSG_STD, psz_format, ap );
207     va_end( ap );
208 }
209
210 /*****************************************************************************
211  * intf_ErrMsg : print an error message                                  (ok ?)
212  *****************************************************************************
213  * This function is the same as intf_Msg, except that it prints its messages
214  * on stderr.
215  *****************************************************************************/
216 void intf_ErrMsg( char *psz_format, ... )
217 {
218     va_list ap;
219
220     va_start( ap, psz_format );
221     QueueMsg( p_main->p_msg, INTF_MSG_ERR, psz_format, ap );
222     va_end( ap );
223 }
224
225 /*****************************************************************************
226  * intf_WarnMsg : print a warning message
227  *****************************************************************************
228  * This function is the same as intf_Msg, except that it concerns warning
229  * messages for testing purpose.
230  *****************************************************************************/
231 void intf_WarnMsg( int i_level, char *psz_format, ... )
232 {
233     va_list ap;
234     
235     if( i_level <= p_main->i_warning_level )
236     {
237         va_start( ap, psz_format );
238         QueueMsg( p_main->p_msg, INTF_MSG_WARN, psz_format, ap );
239         va_end( ap );
240     }
241 }
242
243
244 /*****************************************************************************
245  * _intf_DbgMsg: print a debugging message                               (ok ?)
246  *****************************************************************************
247  * This function prints a debugging message. Compared to other intf_*Msg
248  * functions, it is only defined if TRACE is defined and require a file name,
249  * a function name and a line number as additionnal debugging informations. It
250  * also prints a debugging header for each received line.
251  *****************************************************************************/
252 #ifdef TRACE
253 void _intf_DbgMsg( char *psz_file, char *psz_function, int i_line,
254                    char *psz_format, ...)
255 {
256     va_list ap;
257
258     va_start( ap, psz_format );
259     QueueDbgMsg( p_main->p_msg, psz_file, psz_function, i_line,
260                  psz_format, ap );
261     va_end( ap );
262 }
263 #endif
264
265 /*****************************************************************************
266  * intf_MsgImm: print a message                                          (ok ?)
267  *****************************************************************************
268  * This function prints a message immediately. If the queue is used, all
269  * waiting messages are also printed.
270  *****************************************************************************/
271 void intf_MsgImm( char *psz_format, ... )
272 {
273     va_list ap;
274
275     va_start( ap, psz_format );
276     QueueMsg( p_main->p_msg, INTF_MSG_STD, psz_format, ap );
277     va_end( ap );
278     intf_FlushMsg();
279 }
280
281 /*****************************************************************************
282  * intf_ErrMsgImm: print an error message immediately                    (ok ?)
283  *****************************************************************************
284  * This function is the same as intf_MsgImm, except that it prints its message
285  * on stderr.
286  *****************************************************************************/
287 void intf_ErrMsgImm(char *psz_format, ...)
288 {
289     va_list ap;
290
291     va_start( ap, psz_format );
292     QueueMsg( p_main->p_msg, INTF_MSG_ERR, psz_format, ap );
293     va_end( ap );
294     intf_FlushMsg();
295 }
296
297 /*****************************************************************************
298  * intf_WarnMsgImm : print a warning message
299  *****************************************************************************
300  * This function is the same as intf_MsgImm, except that it concerns warning
301  * messages for testing purpose.
302  *****************************************************************************/
303 void intf_WarnMsgImm( int i_level, char *psz_format, ... )
304 {
305     va_list ap;
306
307     if( i_level <= p_main->i_warning_level )
308     {
309         va_start( ap, psz_format );
310         QueueMsg( p_main->p_msg, INTF_MSG_WARN, psz_format, ap );
311         va_end( ap );
312     }
313     intf_FlushMsg();
314 }
315
316
317
318 /*****************************************************************************
319  * _intf_DbgMsgImm: print a debugging message immediately                (ok ?)
320  *****************************************************************************
321  * This function is the same as intf_DbgMsgImm, except that it prints its
322  * message immediately. It should only be called through the macro
323  * intf_DbgMsgImm().
324  *****************************************************************************/
325 #ifdef TRACE
326 void _intf_DbgMsgImm( char *psz_file, char *psz_function, int i_line,
327                       char *psz_format, ...)
328 {
329     va_list ap;
330
331     va_start( ap, psz_format );
332     QueueDbgMsg( p_main->p_msg, psz_file, psz_function, i_line,
333                  psz_format, ap );
334     va_end( ap );
335     intf_FlushMsg();
336 }
337 #endif
338
339 /*****************************************************************************
340  * intf_WarnHexDump : print a hexadecimal dump of a memory area
341  *****************************************************************************
342  * This is convenient for debugging purposes.
343  *****************************************************************************/
344 void intf_WarnHexDump( int i_level, void *p_data, int i_size )
345 {
346     int   i_index = 0;
347     int   i_subindex;
348     char  p_string[75];
349     u8   *p_area = (u8 *)p_data;
350
351     intf_WarnMsg( i_level, "hexdump: dumping %i bytes at address %p",
352                            i_size, p_data );
353
354     while( i_index < i_size )
355     {
356         i_subindex = 0;
357
358         while( ( i_subindex < 24 ) && ( i_index + i_subindex < i_size ) )
359         {
360             sprintf( p_string + 3 * i_subindex, "%.2x ",
361                      p_area[ i_index + i_subindex ] );
362
363             i_subindex++;
364         }
365
366         /* -1 here is safe because we know we printed at least one */
367         p_string[ 3 * i_subindex - 1 ] = '\0';
368         intf_WarnMsg( i_level, "0x%.4x: %s", i_index, p_string );
369
370         i_index += 24;
371     }
372
373     intf_WarnMsg( i_level, "hexdump: %i bytes dumped", i_size );
374 }
375
376 /*****************************************************************************
377  * intf_FlushMsg                                                        (ok ?)
378  *****************************************************************************
379  * Print all messages remaining in queue: get lock and call FlushLockedMsg.
380  * This function does nothing if the message queue isn't used.
381  * This function is only implemented if message queue is used. If not, it is
382  * an empty macro.
383  *****************************************************************************/
384 #ifdef INTF_MSG_QUEUE
385 void intf_FlushMsg( void )
386 {
387     vlc_mutex_lock( &p_main->p_msg->lock );                      /* get lock */
388     FlushLockedMsg( p_main->p_msg );                       /* flush messages */
389     vlc_mutex_unlock( &p_main->p_msg->lock );              /* give lock back */
390 }
391 #endif
392
393 /* following functions are local */
394
395 /*****************************************************************************
396  * QueueMsg: add a message to a queue
397  *****************************************************************************
398  * This function provide basic functionnalities to other intf_*Msg functions.
399  * It add a message to a queue (after having printed all stored messages if it
400  * is full. If the message can't be converted to string in memory, it exit the
401  * program. If the queue is not used, it prints the message immediately.
402  *****************************************************************************/
403 static void QueueMsg( intf_msg_t *p_msg, int i_type, char *psz_format, va_list ap )
404 {
405     char *                  psz_str;             /* formatted message string */
406     intf_msg_item_t *       p_msg_item;                /* pointer to message */
407
408 #ifndef INTF_MSG_QUEUE /*................................... instant mode ...*/
409     intf_msg_item_t         msg_item;                             /* message */
410     p_msg_item =           &msg_item;
411 #endif /*....................................................................*/
412
413     /*
414      * Convert message to string
415      */
416 #ifdef HAVE_VASPRINTF
417     vasprintf( &psz_str, psz_format, ap );
418 #else
419     psz_str = (char*) malloc( strlen(psz_format) + INTF_MAX_MSG_SIZE );
420 #endif
421     if( psz_str == NULL )
422     {
423         fprintf(stderr, "warning: can't store following message (%s): ",
424                 strerror(errno) );
425         vfprintf(stderr, psz_format, ap );
426         fprintf(stderr, "\n" );
427         exit( errno );
428     }
429 #ifdef HAVE_VASPRINTF
430 #else
431     vsprintf( psz_str, psz_format, ap );
432 #endif
433
434 #ifdef INTF_MSG_QUEUE /*...................................... queue mode ...*/
435     vlc_mutex_lock( &p_msg->lock );                              /* get lock */
436     if( p_msg->i_count == INTF_MSG_QSIZE )          /* flush queue if needed */
437     {
438 #ifdef DEBUG               /* in debug mode, queue overflow causes a warning */
439         fprintf(stderr, "warning: message queue overflow\n" );
440 #endif
441         FlushLockedMsg( p_msg );
442     }
443     p_msg_item = p_msg->msg + p_msg->i_count++;            /* select message */
444 #endif /*.............................................. end of queue mode ...*/
445
446     /*
447      * Fill message information fields
448      */
449     p_msg_item->i_type =     i_type;
450     p_msg_item->psz_msg =    psz_str;
451 #ifdef TRACE    
452     p_msg_item->date =       mdate();
453 #endif
454
455 #ifdef INTF_MSG_QUEUE /*......................................... queue mode */
456     vlc_mutex_unlock( &p_msg->lock );                      /* give lock back */
457 #else /*....................................................... instant mode */
458     PrintMsg( p_msg_item );                                 /* print message */
459     free( psz_str );                                    /* free message data */
460 #endif /*....................................................................*/
461 }
462
463 /*****************************************************************************
464  * QueueDbgMsg: add a message to a queue with debugging informations
465  *****************************************************************************
466  * This function is the same as QueueMsg, except that it is only defined when
467  * TRACE is define, and require additionnal debugging informations.
468  *****************************************************************************/
469 #ifdef TRACE
470 static void QueueDbgMsg(intf_msg_t *p_msg, char *psz_file, char *psz_function,
471                         int i_line, char *psz_format, va_list ap)
472 {
473     char *                  psz_str;             /* formatted message string */
474     intf_msg_item_t *       p_msg_item;                /* pointer to message */
475
476 #ifndef INTF_MSG_QUEUE /*................................... instant mode ...*/
477     intf_msg_item_t         msg_item;                             /* message */
478     p_msg_item =           &msg_item;
479 #endif /*....................................................................*/
480
481     /*
482      * Convert message to string
483      */
484 #ifdef HAVE_VASPRINTF
485     vasprintf( &psz_str, psz_format, ap );
486 #else
487     psz_str = (char*) malloc( INTF_MAX_MSG_SIZE );
488     vsprintf( psz_str, psz_format, ap );
489 #endif
490     if( psz_str == NULL )
491     {
492         fprintf(stderr, "warning: can't store following message (%s): ",
493                 strerror(errno) );
494         fprintf(stderr, INTF_MSG_DBG_FORMAT, psz_file, psz_function, i_line );
495         vfprintf(stderr, psz_format, ap );
496         fprintf(stderr, "\n" );
497         exit( errno );
498     }
499
500 #ifdef INTF_MSG_QUEUE /*...................................... queue mode ...*/
501     vlc_mutex_lock( &p_msg->lock );                              /* get lock */
502     if( p_msg->i_count == INTF_MSG_QSIZE )          /* flush queue if needed */
503     {
504         fprintf(stderr, "warning: message queue overflow\n" );
505         FlushLockedMsg( p_msg );
506     }
507     p_msg_item = p_msg->msg + p_msg->i_count++;            /* select message */
508 #endif /*.............................................. end of queue mode ...*/
509
510     /*
511      * Fill message information fields
512      */
513     p_msg_item->i_type =       INTF_MSG_DBG;
514     p_msg_item->psz_msg =      psz_str;
515     p_msg_item->psz_file =     psz_file;
516     p_msg_item->psz_function = psz_function;
517     p_msg_item->i_line =       i_line;
518     p_msg_item->date =         mdate();
519
520 #ifdef INTF_MSG_QUEUE /*......................................... queue mode */
521     vlc_mutex_unlock( &p_msg->lock );                      /* give lock back */
522 #else /*....................................................... instant mode */
523     PrintMsg( p_msg_item );                                 /* print message */
524     free( psz_str );                                    /* free message data */
525 #endif /*....................................................................*/
526 }
527 #endif
528
529 /*****************************************************************************
530  * FlushLockedMsg                                                       (ok ?)
531  *****************************************************************************
532  * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
533  * this function does not check the lock. This function is only defined if
534  * INTF_MSG_QUEUE is defined.
535  *****************************************************************************/
536 #ifdef INTF_MSG_QUEUE
537 static void FlushLockedMsg ( intf_msg_t *p_msg )
538 {
539     int i_index;
540
541     for( i_index = 0; i_index < p_msg->i_count; i_index++ )
542     {
543         /* Print message and free message data */
544         PrintMsg( &p_msg->msg[i_index] );
545         free( p_msg->msg[i_index].psz_msg );
546     }
547
548     p_msg->i_count = 0;
549 }
550 #endif
551
552 /*****************************************************************************
553  * PrintMsg: print a message                                             (ok ?)
554  *****************************************************************************
555  * Print a single message. The message data is not freed. This function exists
556  * in two version. The TRACE version prints a date with each message, and is
557  * able to log messages (if TRACE_LOG is defined).
558  * The normal one just prints messages to the screen.
559  *****************************************************************************/
560 #ifdef TRACE
561
562 static void PrintMsg( intf_msg_item_t *p_msg )
563 {
564     char    psz_date[MSTRTIME_MAX_SIZE];            /* formatted time buffer */
565     int     i_msg_len = MSTRTIME_MAX_SIZE + strlen(p_msg->psz_msg) + 200;
566     char   *psz_msg;                                       /* message buffer */
567
568     psz_msg = malloc( sizeof( char ) * i_msg_len );
569
570     /* Check if allocation succeeded */
571     if( psz_msg == NULL )
572     {
573         fprintf( stderr, "error: not enough memory for message %s\n",
574                  p_msg->psz_msg );
575         return;
576     }
577
578     /* Format message - the message is formatted here because in case the log
579      * file is used, it avoids another format string parsing */
580     switch( p_msg->i_type )
581     {
582     case INTF_MSG_STD:                                   /* regular messages */
583     case INTF_MSG_ERR:
584         snprintf( psz_msg, i_msg_len, "%s", p_msg->psz_msg );
585         break;
586
587     case INTF_MSG_WARN:                                   /* Warning message */
588         mstrtime( psz_date, p_msg->date );
589         snprintf( psz_msg, i_msg_len, "(%s) %s",
590                   psz_date, p_msg->psz_msg );
591
592         break;
593         
594     case INTF_MSG_DBG:                                     /* debug messages */
595         mstrtime( psz_date, p_msg->date );
596         snprintf( psz_msg, i_msg_len, "(%s) " INTF_MSG_DBG_FORMAT "%s",
597                   psz_date, p_msg->psz_file, p_msg->psz_function, p_msg->i_line,
598                   p_msg->psz_msg );
599         break;
600     }
601
602     /*
603      * Print messages
604      */
605     switch( p_msg->i_type )
606     {
607     case INTF_MSG_STD:                                  /* standard messages */
608         fprintf( stdout, "%s\n", psz_msg );
609         break;
610     case INTF_MSG_ERR:                                     /* error messages */
611     case INTF_MSG_WARN:
612 #ifndef TRACE_LOG_ONLY
613     case INTF_MSG_DBG:                                 /* debugging messages */
614 #endif
615         fprintf( stderr, "%s\n", psz_msg );
616         break;
617     }
618
619 #ifdef TRACE_LOG
620     /* Append all messages to log file */
621     if( p_main->p_msg->p_log_file != NULL )
622     {
623         fwrite( psz_msg, strlen( psz_msg ), 1, p_main->p_msg->p_log_file );
624         fwrite( "\n", 1, 1, p_main->p_msg->p_log_file );
625     }
626 #endif
627
628     /* Free the message */
629     free( psz_msg );
630 }
631
632 #else
633
634 static void PrintMsg( intf_msg_item_t *p_msg )
635 {
636     /*
637      * Print messages on screen
638      */
639     switch( p_msg->i_type )
640     {
641     case INTF_MSG_STD:                                  /* standard messages */
642     case INTF_MSG_DBG:                                     /* debug messages */
643         fprintf( stdout, "%s\n", p_msg->psz_msg );
644         break;
645     case INTF_MSG_ERR:                                     /* error messages */
646     case INTF_MSG_WARN:
647         fprintf( stderr, "%s\n", p_msg->psz_msg );        /* warning message */
648         break;
649     }
650 }
651
652 #endif
653