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 $
9 * Authors: Vincent Seguin <seguin@via.ecp.fr>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
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() */
36 #include <videolan/vlc.h>
39 #include <unistd.h> /* close(), write() */
42 #include "interface.h"
45 /*****************************************************************************
47 *****************************************************************************
48 * Store all data requiered by messages interfaces. It has a single reference
50 *****************************************************************************/
51 typedef struct msg_bank_s
53 /* Message queue lock */
57 msg_item_t msg
[INTF_MSG_QSIZE
]; /* message queue */
63 intf_subscription_t
**pp_sub
;
69 /*****************************************************************************
71 *****************************************************************************/
72 static void QueueMsg ( int, int, char *, va_list );
73 static void FlushMsg ( void );
76 static char *ConvertPrintfFormatString ( char *psz_format
);
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 )
87 /* Message queue initialization */
88 vlc_mutex_init( &msg_bank
.lock
);
93 msg_bank
.pp_sub
= NULL
;
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 )
106 vlc_mutex_destroy( &msg_bank
.lock
);
110 fprintf( stderr
, "intf error: stale interface subscribers\n" );
113 /* Free remaining messages */
117 /*****************************************************************************
118 * intf_MsgSub: subscribe to the message queue.
119 *****************************************************************************/
120 intf_subscription_t
*intf_MsgSub( void )
122 intf_subscription_t
*p_sub
= malloc( sizeof( intf_subscription_t
) );
124 vlc_mutex_lock( &msg_bank
.lock
);
126 /* Add subscription to the list */
128 msg_bank
.pp_sub
= realloc( msg_bank
.pp_sub
,
129 msg_bank
.i_sub
* sizeof( intf_subscription_t
* ) );
131 msg_bank
.pp_sub
[ msg_bank
.i_sub
- 1 ] = p_sub
;
133 p_sub
->i_start
= msg_bank
.i_start
;
134 p_sub
->pi_stop
= &msg_bank
.i_stop
;
136 p_sub
->p_msg
= msg_bank
.msg
;
137 p_sub
->p_lock
= &msg_bank
.lock
;
139 vlc_mutex_unlock( &msg_bank
.lock
);
144 /*****************************************************************************
145 * intf_MsgUnsub: unsubscribe from the message queue.
146 *****************************************************************************/
147 void intf_MsgUnsub( intf_subscription_t
*p_sub
)
151 vlc_mutex_lock( &msg_bank
.lock
);
154 if( !msg_bank
.i_sub
)
156 intf_ErrMsg( "intf error: no subscriber in the list" );
160 /* Look for the appropriate subscription */
161 for( i_index
= 0; i_index
< msg_bank
.i_sub
; i_index
++ )
163 if( msg_bank
.pp_sub
[ i_index
] == p_sub
)
169 if( msg_bank
.pp_sub
[ i_index
] != p_sub
)
171 intf_ErrMsg( "intf error: subscriber not found" );
172 vlc_mutex_unlock( &msg_bank
.lock
);
176 /* Remove this subscription */
177 for( ; i_index
< (msg_bank
.i_sub
- 1); i_index
++ )
179 msg_bank
.pp_sub
[ i_index
] = msg_bank
.pp_sub
[ i_index
+1 ];
185 msg_bank
.pp_sub
= realloc( msg_bank
.pp_sub
,
186 msg_bank
.i_sub
* sizeof( intf_subscription_t
* ) );
190 free( msg_bank
.pp_sub
);
191 msg_bank
.pp_sub
= NULL
;
194 vlc_mutex_unlock( &msg_bank
.lock
);
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
, ... )
207 va_start( ap
, psz_format
);
208 QueueMsg( INTF_MSG_STD
, 0, psz_format
, ap
);
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
217 *****************************************************************************/
218 void intf_ErrMsg( char *psz_format
, ... )
222 va_start( ap
, psz_format
);
223 QueueMsg( INTF_MSG_ERR
, 0, psz_format
, ap
);
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
, ... )
237 va_start( ap
, psz_format
);
238 QueueMsg( INTF_MSG_WARN
, i_level
, psz_format
, ap
);
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
, ... )
252 if( p_main
->b_stats
)
254 va_start( ap
, psz_format
);
255 QueueMsg( INTF_MSG_STAT
, 0, psz_format
, ap
);
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
)
270 u8
*p_area
= (u8
*)p_data
;
272 intf_WarnMsg( i_level
, "hexdump: dumping %i bytes at address %p",
275 while( i_index
< i_size
)
279 while( ( i_subindex
< 24 ) && ( i_index
+ i_subindex
< i_size
) )
281 sprintf( p_string
+ 3 * i_subindex
, "%.2x ",
282 p_area
[ i_index
+ i_subindex
] );
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
);
294 intf_WarnMsg( i_level
, "hexdump: %i bytes dumped", i_size
);
297 /* following functions are local */
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
306 *****************************************************************************/
307 static void QueueMsg( int i_type
, int i_level
, char *psz_format
, va_list ap
)
309 char * psz_str
; /* formatted message string */
310 msg_item_t
* p_item
; /* pointer to message */
316 * Convert message to string
318 #ifdef HAVE_VASPRINTF
319 vasprintf( &psz_str
, psz_format
, ap
);
321 psz_str
= (char*) malloc( strlen(psz_format
) + INTF_MAX_MSG_SIZE
);
324 if( psz_str
== NULL
)
326 fprintf(stderr
, "intf warning: can't store following message (%s): ",
328 vfprintf(stderr
, psz_format
, ap
);
329 fprintf(stderr
, "\n" );
333 #ifndef HAVE_VASPRINTF
335 psz_temp
= ConvertPrintfFormatString(psz_format
);
338 fprintf(stderr
, "intf warning: couldn't print message");
341 vsprintf( psz_str
, psz_temp
, ap
);
344 vsprintf( psz_str
, psz_format
, ap
);
348 /* Put message in queue */
349 vlc_mutex_lock( &msg_bank
.lock
);
351 /* Send the message to stderr */
352 if( i_level
<= p_main
->i_warning_level
)
354 fprintf( stderr
, "%s\n", psz_str
);
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 )
362 if( ((msg_bank
.i_stop
- msg_bank
.i_start
+ 1) % INTF_MSG_QSIZE
) == 0 )
364 fprintf( stderr
, "intf warning: message queue overflow\n" );
365 vlc_mutex_unlock( &msg_bank
.lock
);
370 p_item
= msg_bank
.msg
+ msg_bank
.i_stop
;
371 msg_bank
.i_stop
= (msg_bank
.i_stop
+ 1) % INTF_MSG_QSIZE
;
373 /* Fill message information fields */
374 p_item
->i_type
= i_type
;
375 p_item
->psz_msg
= psz_str
;
377 vlc_mutex_unlock( &msg_bank
.lock
);
380 /*****************************************************************************
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 )
388 int i_index
, i_start
, i_stop
;
390 /* Get the maximum message index that can be freed */
391 i_stop
= msg_bank
.i_stop
;
393 /* Check until which value we can free messages */
394 for( i_index
= 0; i_index
< msg_bank
.i_sub
; i_index
++ )
396 i_start
= msg_bank
.pp_sub
[ i_index
]->i_start
;
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
) ) )
409 /* Free message data */
410 for( i_index
= msg_bank
.i_start
;
412 i_index
= (i_index
+1) % INTF_MSG_QSIZE
)
414 free( msg_bank
.msg
[i_index
].psz_msg
);
417 /* Update the new start value */
418 msg_bank
.i_start
= i_index
;
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 *****************************************************************************/
433 static char *ConvertPrintfFormatString( char *psz_format
)
435 int i
, i_counter
=0, i_pos
=0;
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 */
442 if( strlen( psz_format
) <= 3 )
443 return strdup( psz_format
);
445 for( i
=0; i
<= (strlen(psz_format
) - 3); i
++ )
447 if( !strncmp( (char *)(psz_format
+ i
), "%ll", 3 ) )
453 /* malloc the destination string */
454 psz_dest
= malloc( strlen(psz_format
) + i_counter
+ 1 );
455 if( psz_dest
== NULL
)
457 fprintf( stderr
, "intf warning: ConvertPrintfFormatString failed\n");
461 /* Now build the modified string */
463 for( i
=0; i
<= (strlen(psz_format
) - 3); i
++ )
465 if( !strncmp( (char *)(psz_format
+ i
), "%ll", 3 ) )
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';
475 strcpy( psz_dest
+i_pos
+i_counter
, psz_format
+i_pos
);