messages.c 20.3 KB
Newer Older
1
/*****************************************************************************
2 3
 * messages.c: messages interface
 * This library provides an interface to the message queue to be used by other
4
 * modules, especially intf modules. See vlc_config.h for output configuration.
5
 *****************************************************************************
Jean-Paul Saman's avatar
Jean-Paul Saman committed
6
 * Copyright (C) 1998-2005 the VideoLAN team
7
 * $Id$
8
 *
9
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
10
 *          Samuel Hocevar <sam@zoy.org>
11 12 13 14 15
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
16
 *
17 18
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
21
 *
22 23
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
26

27
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
28
 * Preamble
29
 *****************************************************************************/
30

31 32 33 34
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

35
#include <vlc_common.h>
36

37
#include <stdarg.h>                                       /* va_list for BSD */
38

39 40 41 42
#ifdef HAVE_FCNTL_H
#   include <fcntl.h>                  /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
#endif

43
#include <errno.h>                                                  /* errno */
44

45 46 47 48
#ifdef WIN32
#   include <vlc_network.h>          /* 'net_strerror' and 'WSAGetLastError' */
#endif

49
#ifdef HAVE_UNISTD_H
50
#   include <unistd.h>                                   /* close(), write() */
51
#endif
Michel Kaempf's avatar
Michel Kaempf committed
52

53 54
#include <assert.h>

55
#include <vlc_charset.h>
56
#include "../libvlc.h"
57

58 59 60 61 62 63 64 65 66 67 68
/*****************************************************************************
 * Local macros
 *****************************************************************************/
#if defined(HAVE_VA_COPY)
#   define vlc_va_copy(dest,src) va_copy(dest,src)
#elif defined(HAVE___VA_COPY)
#   define vlc_va_copy(dest,src) __va_copy(dest,src)
#else
#   define vlc_va_copy(dest,src) (dest)=(src)
#endif

69
#define QUEUE priv->msg_bank.queue
70 71
#define LOCK_BANK vlc_mutex_lock( &priv->msg_bank.lock );
#define UNLOCK_BANK vlc_mutex_unlock( &priv->msg_bank.lock );
72

Vincent Seguin's avatar
Vincent Seguin committed
73
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
74
 * Local prototypes
Vincent Seguin's avatar
Vincent Seguin committed
75
 *****************************************************************************/
76
static void QueueMsg ( vlc_object_t *, int, const char *,
77
                       const char *, va_list );
78
static void FlushMsg ( msg_queue_t * );
79
static void PrintMsg ( vlc_object_t *, msg_item_t * );
Michel Kaempf's avatar
Michel Kaempf committed
80

81 82 83 84 85 86 87 88
static inline char * object_description( vlc_object_t * p_this )
{
    if( p_this->i_object_type == VLC_OBJECT_GENERIC
        && p_this->psz_object_name )
        return strdup( p_this->psz_object_name );
    return strdup( p_this->psz_object_type );
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
89
/**
90 91
 * Initialize messages queues
 * This function initializes all message queues
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
92
 */
93
void msg_Create (libvlc_int_t *p_libvlc)
Michel Kaempf's avatar
Michel Kaempf committed
94
{
95
    libvlc_priv_t *priv = libvlc_priv (p_libvlc);
96
    vlc_mutex_init( &priv->msg_bank.lock );
97 98 99 100 101 102
    vlc_mutex_init( &QUEUE.lock );
    QUEUE.b_overflow = false;
    QUEUE.i_start = 0;
    QUEUE.i_stop = 0;
    QUEUE.i_sub = 0;
    QUEUE.pp_sub = 0;
103 104

#ifdef UNDER_CE
105
    QUEUE.logfile =
106 107
        CreateFile( L"vlc-log.txt", GENERIC_WRITE,
                    FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
108
                    CREATE_ALWAYS, 0, NULL );
109
    SetFilePointer( QUEUE.logfile, 0, NULL, FILE_END );
110
#endif
Michel Kaempf's avatar
Michel Kaempf committed
111 112
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
113
/**
114
 * Flush all message queues
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
115
 */
116
void msg_Flush (libvlc_int_t *p_libvlc)
117
{
118
    libvlc_priv_t *priv = libvlc_priv (p_libvlc);
119 120 121
    vlc_mutex_lock( &QUEUE.lock );
    FlushMsg( &QUEUE );
    vlc_mutex_unlock( &QUEUE.lock );
122 123
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
124
/**
125
 * Destroy the message queues
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
126
 *
127
 * This functions prints all messages remaining in the queues,
128
 * then frees all the allocated ressources
Michel Kaempf's avatar
Michel Kaempf committed
129
 * No other messages interface functions should be called after this one.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
130
 */
131
void msg_Destroy (libvlc_int_t *p_libvlc)
Michel Kaempf's avatar
Michel Kaempf committed
132
{
133
    libvlc_priv_t *priv = libvlc_priv (p_libvlc);
134

135 136
    if( QUEUE.i_sub )
        msg_Err( p_libvlc, "stale interface subscribers" );
137

138
    FlushMsg( &QUEUE );
139

140
#ifdef UNDER_CE
141
    CloseHandle( QUEUE.logfile );
142
#endif
143 144
    /* Destroy lock */
    vlc_mutex_destroy( &QUEUE.lock );
145
    vlc_mutex_destroy( &priv->msg_bank.lock);
146 147
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
148
/**
149
 * Subscribe to a message queue.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
150
 */
151
msg_subscription_t *__msg_Subscribe( vlc_object_t *p_this )
152
{
153
    libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
154
    msg_subscription_t *p_sub = malloc( sizeof( msg_subscription_t ) );
155

156 157 158
    if (p_sub == NULL)
        return NULL;

159
    LOCK_BANK;
160
    vlc_mutex_lock( &QUEUE.lock );
161

162
    TAB_APPEND( QUEUE.i_sub, QUEUE.pp_sub, p_sub );
163

164 165 166 167
    p_sub->i_start = QUEUE.i_start;
    p_sub->pi_stop = &QUEUE.i_stop;
    p_sub->p_msg   = QUEUE.msg;
    p_sub->p_lock  = &QUEUE.lock;
168

169
    vlc_mutex_unlock( &QUEUE.lock );
170
    UNLOCK_BANK;
171 172 173 174

    return p_sub;
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
175
/**
176
 * Unsubscribe from a message queue.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
177
 */
178
void __msg_Unsubscribe( vlc_object_t *p_this, msg_subscription_t *p_sub )
179
{
180
    libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
181

182
    LOCK_BANK;
183 184
    vlc_mutex_lock( &QUEUE.lock );
    for( int j = 0 ; j< QUEUE.i_sub ; j++ )
185
    {
186
        if( QUEUE.pp_sub[j] == p_sub )
187
        {
188 189
            REMOVE_ELEM( QUEUE.pp_sub, QUEUE.i_sub, j );
            free( p_sub );
190 191
        }
    }
192
    vlc_mutex_unlock( &QUEUE.lock );
193
    UNLOCK_BANK;
Michel Kaempf's avatar
Michel Kaempf committed
194 195
}

196
/*****************************************************************************
197
 * __msg_*: print a message
198
 *****************************************************************************
199
 * These functions queue a message for later printing.
200
 *****************************************************************************/
201
void __msg_Generic( vlc_object_t *p_this, int i_type, const char *psz_module,
202
                    const char *psz_format, ... )
Michel Kaempf's avatar
Michel Kaempf committed
203
{
204
    va_list args;
Michel Kaempf's avatar
Michel Kaempf committed
205

206
    va_start( args, psz_format );
207
    QueueMsg( p_this, i_type, psz_module, psz_format, args );
208
    va_end( args );
Michel Kaempf's avatar
Michel Kaempf committed
209
}
210

211
void __msg_GenericVa( vlc_object_t *p_this, int i_type, const char *psz_module,
212 213
                      const char *psz_format, va_list args )
{
214
    QueueMsg( p_this, i_type, psz_module, psz_format, args );
215 216
}

217 218
/* Generic functions used when variadic macros are not available. */
#define DECLARE_MSG_FN( FN_NAME, FN_TYPE ) \
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
219
    void FN_NAME( vlc_object_t *p_this, const char *psz_format, ... ) \
220 221 222
    { \
        va_list args; \
        va_start( args, psz_format ); \
223
        QueueMsg( p_this, FN_TYPE, "unknown", psz_format, args ); \
224
        va_end( args ); \
225 226
    } \
    struct _
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
227 228 229 230 231
/**
 * Output an informational message.
 * \note Do not use this for debug messages
 * \see input_AddInfo
 */
232
DECLARE_MSG_FN( __msg_Info, VLC_MSG_INFO );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
233 234 235
/**
 * Output an error message.
 */
236
DECLARE_MSG_FN( __msg_Err,  VLC_MSG_ERR );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
237 238 239
/**
 * Output a waring message
 */
240
DECLARE_MSG_FN( __msg_Warn, VLC_MSG_WARN );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
241 242 243
/**
 * Output a debug message
 */
244
DECLARE_MSG_FN( __msg_Dbg,  VLC_MSG_DBG );
Michel Kaempf's avatar
Michel Kaempf committed
245

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
246 247 248
/**
 * Add a message to a queue
 *
249
 * This function provides basic functionnalities to other msg_* functions.
250 251 252
 * It adds a message to a queue (after having printed all stored messages if it
 * is full). If the message can't be converted to string in memory, it issues
 * a warning.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
253
 */
254
static void QueueMsg( vlc_object_t *p_this, int i_type, const char *psz_module,
255
                      const char *psz_format, va_list _args )
Michel Kaempf's avatar
Michel Kaempf committed
256
{
257
    assert (p_this);
258
    libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
259
    int         i_header_size;             /* Size of the additionnal header */
260
    vlc_object_t *p_obj;
261
    char *       psz_str = NULL;                 /* formatted message string */
262
    char *       psz_header = NULL;
263
    va_list      args;
264 265
    msg_item_t * p_item = NULL;                        /* pointer to message */
    msg_item_t   item;                    /* message in case of a full queue */
266
    msg_queue_t *p_queue;
267

268
#if !defined(HAVE_VASPRINTF) || defined(__APPLE__) || defined(SYS_BEOS)
269
    int          i_size = strlen(psz_format) + INTF_MAX_MSG_SIZE;
270
#endif
Vincent Seguin's avatar
Vincent Seguin committed
271

272 273 274 275
    if( p_this->i_flags & OBJECT_FLAGS_QUIET ||
        (p_this->i_flags & OBJECT_FLAGS_NODBG && i_type == VLC_MSG_DBG) )
        return;

276 277 278 279
#ifndef __GLIBC__
    /* Expand %m to strerror(errno) - only once */
    char buf[strlen( psz_format ) + 2001], *ptr;
    strcpy( buf, psz_format );
Rafaël Carré's avatar
Rafaël Carré committed
280 281
    ptr = (char*)buf;
    psz_format = (const char*) buf;
282 283 284 285 286 287 288 289 290 291 292 293

    for( ;; )
    {
        ptr = strchr( ptr, '%' );
        if( ptr == NULL )
            break;

        if( ptr[1] == 'm' )
        {
            char errbuf[2001];
            size_t errlen;

294
#ifndef WIN32
295
            strerror_r( errno, errbuf, 1001 );
296 297 298 299 300 301 302
#else
            int sockerr = WSAGetLastError( );
            if( sockerr )
            {
                strncpy( errbuf, net_strerror( sockerr ), 1001 );
                WSASetLastError( sockerr );
            }
303 304
            if ((sockerr == 0)
             || (strcmp ("Unknown network stack error", errbuf) == 0))
305 306
                strncpy( errbuf, strerror( errno ), 1001 );
#endif
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
            errbuf[1000] = 0;

            /* Escape '%' from the error string */
            for( char *percent = strchr( errbuf, '%' );
                 percent != NULL;
                 percent = strchr( percent + 2, '%' ) )
            {
                memmove( percent + 1, percent, strlen( percent ) + 1 );
            }

            errlen = strlen( errbuf );
            memmove( ptr + errlen, ptr + 2, strlen( ptr + 2 ) + 1 );
            memcpy( ptr, errbuf, errlen );
            break; /* Only once, so we don't overflow */
        }

        /* Looks for conversion specifier... */
        do
            ptr++;
        while( *ptr && ( strchr( "diouxXeEfFgGaAcspn%", *ptr ) == NULL ) );
        if( *ptr )
            ptr++; /* ...and skip it */
    }
#endif

332
    /* Convert message to string  */
333
#if defined(HAVE_VASPRINTF) && !defined(__APPLE__) && !defined( SYS_BEOS )
334
    vlc_va_copy( args, _args );
335 336
    if( vasprintf( &psz_str, psz_format, args ) == -1 )
        psz_str = NULL;
337
    va_end( args );
338
#else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
339
    psz_str = (char*) malloc( i_size );
340
#endif
341

Michel Kaempf's avatar
Michel Kaempf committed
342 343
    if( psz_str == NULL )
    {
344 345 346
#ifdef __GLIBC__
        fprintf( stderr, "main warning: can't store message (%m): " );
#else
347
        char psz_err[1001];
348 349 350 351 352
#ifndef WIN32
        /* we're not using GLIBC, so we are sure that the error description
         * will be stored in the buffer we provide to strerror_r() */
        strerror_r( errno, psz_err, 1001 );
#else
353
        strncpy( psz_err, strerror( errno ), 1001 );
354 355 356 357
#endif
        psz_err[1000] = '\0';
        fprintf( stderr, "main warning: can't store message (%s): ", psz_err );
#endif
358
        vlc_va_copy( args, _args );
359
        /* We should use utf8_vfprintf - but it calls malloc()... */
360
        vfprintf( stderr, psz_format, args );
361
        va_end( args );
362
        fputs( "\n", stderr );
363
        return;
Michel Kaempf's avatar
Michel Kaempf committed
364
    }
365

366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    i_header_size = 0;
    p_obj = p_this;
    while( p_obj != NULL )
    {
        char *psz_old = NULL;
        if( p_obj->psz_header )
        {
            i_header_size += strlen( p_obj->psz_header ) + 4;
            if( psz_header )
            {
                psz_old = strdup( psz_header );
                psz_header = (char*)realloc( psz_header, i_header_size );
                snprintf( psz_header, i_header_size , "[%s] %s",
                          p_obj->psz_header, psz_old );
            }
            else
            {
                psz_header = (char *)malloc( i_header_size );
                snprintf( psz_header, i_header_size, "[%s]",
                          p_obj->psz_header );
            }
        }
388
        free( psz_old );
389 390 391
        p_obj = p_obj->p_parent;
    }

392
#if !defined(HAVE_VASPRINTF) || defined(__APPLE__) || defined(SYS_BEOS)
393
    vlc_va_copy( args, _args );
394
    vsnprintf( psz_str, i_size, psz_format, args );
395
    va_end( args );
396
    psz_str[ i_size - 1 ] = 0; /* Just in case */
397
#endif
Vincent Seguin's avatar
Vincent Seguin committed
398

399
    LOCK_BANK;
400
    p_queue = &QUEUE;
401
    vlc_mutex_lock( &p_queue->lock );
Vincent Seguin's avatar
Vincent Seguin committed
402

403
    /* Check there is room in the queue for our message */
404
    if( p_queue->b_overflow )
405
    {
406
        FlushMsg( p_queue );
407

408
        if( ((p_queue->i_stop - p_queue->i_start + 1) % VLC_MSG_QSIZE) == 0 )
409 410 411 412 413 414 415
        {
            /* Still in overflow mode, print from a dummy item */
            p_item = &item;
        }
        else
        {
            /* Pheeew, at last, there is room in the queue! */
416
            p_queue->b_overflow = false;
417 418
        }
    }
419
    else if( ((p_queue->i_stop - p_queue->i_start + 2) % VLC_MSG_QSIZE) == 0 )
Vincent Seguin's avatar
Vincent Seguin committed
420
    {
421
        FlushMsg( p_queue );
Michel Kaempf's avatar
Michel Kaempf committed
422

423
        if( ((p_queue->i_stop - p_queue->i_start + 2) % VLC_MSG_QSIZE) == 0 )
424
        {
425
            p_queue->b_overflow = true;
426

427 428 429 430 431 432
            /* Put the overflow message in the queue */
            p_item = p_queue->msg + p_queue->i_stop;
            p_queue->i_stop = (p_queue->i_stop + 1) % VLC_MSG_QSIZE;

            p_item->i_type =        VLC_MSG_WARN;
            p_item->i_object_id =   p_this->i_object_id;
433
            p_item->psz_object =    object_description( p_this );
434 435 436 437 438 439 440
            p_item->psz_module =    strdup( "message" );
            p_item->psz_msg =       strdup( "message queue overflowed" );
            p_item->psz_header =    NULL;

            PrintMsg( p_this, p_item );
            /* We print from a dummy item */
            p_item = &item;
441
        }
Michel Kaempf's avatar
Michel Kaempf committed
442 443
    }

444
    if( !p_queue->b_overflow )
445 446
    {
        /* Put the message in the queue */
447 448
        p_item = p_queue->msg + p_queue->i_stop;
        p_queue->i_stop = (p_queue->i_stop + 1) % VLC_MSG_QSIZE;
449
    }
Michel Kaempf's avatar
Michel Kaempf committed
450

451
    /* Fill message information fields */
452 453
    p_item->i_type =        i_type;
    p_item->i_object_id =   p_this->i_object_id;
454
    p_item->psz_object =    object_description( p_this );
455 456
    p_item->psz_module =    strdup( psz_module );
    p_item->psz_msg =       psz_str;
457
    p_item->psz_header =    psz_header;
458

459
    PrintMsg( p_this, p_item );
Michel Kaempf's avatar
Michel Kaempf committed
460

461
    if( p_queue->b_overflow )
462
    {
463 464 465
        free( p_item->psz_module );
        free( p_item->psz_msg );
        free( p_item->psz_header );
466
        free( p_item->psz_object );
467 468
    }

469
    vlc_mutex_unlock ( &p_queue->lock );
470
    UNLOCK_BANK;
Michel Kaempf's avatar
Michel Kaempf committed
471 472
}

473 474
/* following functions are local */

475
/*****************************************************************************
476
 * FlushMsg
477
 *****************************************************************************
478 479
 * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
 * this function does not check the lock.
480
 *****************************************************************************/
481
static void FlushMsg ( msg_queue_t *p_queue )
Michel Kaempf's avatar
Michel Kaempf committed
482
{
483
    int i_index, i_start, i_stop;
484

485
    /* Get the maximum message index that can be freed */
486
    i_stop = p_queue->i_stop;
487

488
    /* Check until which value we can free messages */
489
    for( i_index = 0; i_index < p_queue->i_sub; i_index++ )
490
    {
491
        i_start = p_queue->pp_sub[ i_index ]->i_start;
492 493 494 495

        /* If this subscriber is late, we don't free messages before
         * his i_start value, otherwise he'll miss messages */
        if(   ( i_start < i_stop
496
               && (p_queue->i_stop <= i_start || i_stop <= p_queue->i_stop) )
497
           || ( i_stop < i_start
498
               && (i_stop <= p_queue->i_stop && p_queue->i_stop <= i_start) ) )
499 500 501
        {
            i_stop = i_start;
        }
502
    }
503

504
    /* Free message data */
505
    for( i_index = p_queue->i_start;
506
         i_index != i_stop;
507
         i_index = (i_index+1) % VLC_MSG_QSIZE )
Michel Kaempf's avatar
Michel Kaempf committed
508
    {
509 510 511
        free( p_queue->msg[i_index].psz_msg );
        free( p_queue->msg[i_index].psz_module );
        free( p_queue->msg[i_index].psz_header );
512
        free( p_queue->msg[i_index].psz_object );
513
    }
514

515
    /* Update the new start value */
516
    p_queue->i_start = i_index;
517
}
Gildas Bazin's avatar
Gildas Bazin committed
518

519
/*****************************************************************************
520
 * PrintMsg: output a standard message item to stderr
521 522 523 524 525 526 527 528 529
 *****************************************************************************
 * Print a message to stderr, with colour formatting if needed.
 *****************************************************************************/
static void PrintMsg ( vlc_object_t * p_this, msg_item_t * p_item )
{
#   define COL(x)  "\033[" #x ";1m"
#   define RED     COL(31)
#   define GREEN   COL(32)
#   define YELLOW  COL(33)
530
#   define WHITE   COL(0)
531 532
#   define GRAY    "\033[0m"

533 534 535
#ifdef UNDER_CE
    int i_dummy;
#endif
536 537
    static const char ppsz_type[4][9] = { "", " error", " warning", " debug" };
    static const char ppsz_color[4][8] = { WHITE, RED, YELLOW, GRAY };
538
    const char *psz_object;
539
    libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc);
540 541
    int i_type = p_item->i_type;

542
    switch( i_type )
543
    {
544
        case VLC_MSG_ERR:
545
            if( priv->i_verbose < 0 ) return;
546 547
            break;
        case VLC_MSG_INFO:
548
            if( priv->i_verbose < 0 ) return;
549 550
            break;
        case VLC_MSG_WARN:
551
            if( priv->i_verbose < 1 ) return;
552 553
            break;
        case VLC_MSG_DBG:
554
            if( priv->i_verbose < 2 ) return;
555
            break;
556 557
    }

558
    psz_object = p_item->psz_object;
559

560
#ifdef UNDER_CE
561
#   define CE_WRITE(str) WriteFile( QUEUE.logfile, \
562 563 564 565 566 567 568
                                    str, strlen(str), &i_dummy, NULL );
    CE_WRITE( p_item->psz_module );
    CE_WRITE( " " );
    CE_WRITE( psz_object );
    CE_WRITE( ppsz_type[i_type] );
    CE_WRITE( ": " );
    CE_WRITE( p_item->psz_msg );
569
    CE_WRITE( "\r\n" );
570
    FlushFileBuffers( QUEUE.logfile );
571 572

#else
573
    /* Send the message to stderr */
574
    if( priv->b_color )
575
    {
576 577
        if( p_item->psz_header )
        {
578
            utf8_fprintf( stderr, "[" GREEN "%.8i" GRAY "] %s %s %s%s: %s%s" GRAY
579 580 581 582 583 584 585 586
                              "\n",
                         p_item->i_object_id, p_item->psz_header,
                         p_item->psz_module, psz_object,
                         ppsz_type[i_type], ppsz_color[i_type],
                         p_item->psz_msg );
        }
        else
        {
587
             utf8_fprintf( stderr, "[" GREEN "%.8i" GRAY "] %s %s%s: %s%s" GRAY "\n",
588
                         p_item->i_object_id, p_item->psz_module, psz_object,
589
                         ppsz_type[i_type], ppsz_color[i_type],
Clément Stenac's avatar
Clément Stenac committed
590
                         p_item->psz_msg );
591
        }
592 593 594
    }
    else
    {
595 596
        if( p_item->psz_header )
        {
597
            utf8_fprintf( stderr, "[%.8i] %s %s %s%s: %s\n", p_item->i_object_id,
598 599 600 601 602
                         p_item->psz_header, p_item->psz_module,
                         psz_object, ppsz_type[i_type], p_item->psz_msg );
        }
        else
        {
603
            utf8_fprintf( stderr, "[%.8i] %s %s%s: %s\n", p_item->i_object_id,
604
                         p_item->psz_module, psz_object, ppsz_type[i_type],
605
                         p_item->psz_msg );
606
        }
607
    }
608

609
#   if defined(WIN32)
610
    fflush( stderr );
611
#   endif
612
#endif
613
}
614 615 616 617 618 619 620 621 622 623 624 625 626

static msg_context_t* GetContext(void)
{
    msg_context_t *p_ctx = vlc_threadvar_get( &msg_context_global_key );
    if( p_ctx == NULL )
    {
        MALLOC_NULL( p_ctx, msg_context_t );
        p_ctx->psz_message = NULL;
        vlc_threadvar_set( &msg_context_global_key, p_ctx );
    }
    return p_ctx;
}

627 628 629 630 631 632 633 634
void msg_StackDestroy (void *data)
{
    msg_context_t *p_ctx = data;

    free (p_ctx->psz_message);
    free (p_ctx);
}

635 636 637 638
void msg_StackSet( int i_code, const char *psz_message, ... )
{
    va_list ap;
    msg_context_t *p_ctx = GetContext();
639 640 641

    if( p_ctx == NULL )
        return;
642
    free( p_ctx->psz_message );
643

644
    va_start( ap, psz_message );
645 646
    if( vasprintf( &p_ctx->psz_message, psz_message, ap ) == -1 )
        p_ctx->psz_message = NULL;
647
    va_end( ap );
648

649 650 651 652 653 654 655 656
    p_ctx->i_code = i_code;
}

void msg_StackAdd( const char *psz_message, ... )
{
    char *psz_tmp;
    va_list ap;
    msg_context_t *p_ctx = GetContext();
657 658 659

    if( p_ctx == NULL )
        return;
660 661

    va_start( ap, psz_message );
662 663
    if( vasprintf( &psz_tmp, psz_message, ap ) == -1 )
        psz_tmp = NULL;
664 665 666 667 668 669
    va_end( ap );

    if( !p_ctx->psz_message )
        p_ctx->psz_message = psz_tmp;
    else
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
670 671 672 673 674 675 676
        char *psz_new;
        if( asprintf( &psz_new, "%s: %s", psz_tmp, p_ctx->psz_message ) == -1 )
            psz_new = NULL;

        free( p_ctx->psz_message );
        p_ctx->psz_message = psz_new;
        free( psz_tmp );
677 678 679 680 681 682 683 684 685
    }
}

const char* msg_StackMsg( void )
{
    msg_context_t *p_ctx = GetContext();
    assert( p_ctx );
    return p_ctx->psz_message;
}