messages.c 10 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-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
6
 * Copyright (C) 1998-2005 VLC authors and VideoLAN
zorglub's avatar
zorglub committed
7
 * $Id$
8
 *
Sam Hocevar's avatar
   
Sam Hocevar committed
9
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
10
 *          Samuel Hocevar <sam@zoy.org>
11
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
12
13
14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
15
 * (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
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
19
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
21
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
22
23
24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software 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 <stdarg.h>                                       /* va_list for BSD */
36
#ifdef __APPLE__
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
37
# include <xlocale.h>
38
#elif defined(HAVE_LOCALE_H)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
39
# include <locale.h>
40
#endif
41
#include <errno.h>                                                  /* errno */
42
#include <assert.h>
43

44
45
#include <vlc_common.h>
#include <vlc_interface.h>
46
47
48
#ifdef WIN32
#   include <vlc_network.h>          /* 'net_strerror' and 'WSAGetLastError' */
#endif
zorglub's avatar
zorglub committed
49
#include <vlc_charset.h>
50
#include "../libvlc.h"
51

52
53
54
/**
 * Store all data required by messages interfaces.
 */
55
56
vlc_rwlock_t msg_lock = VLC_STATIC_RWLOCK;
msg_subscription_t *msg_head;
57
58
59

struct msg_subscription_t
{
60
    msg_subscription_t *prev, *next;
61
    msg_callback_t  func;
62
    void           *opaque;
63
64
};

sigmunau's avatar
all:    
sigmunau committed
65
/**
66
67
68
69
70
71
72
 * Subscribe to the message queue.
 * Whenever a message is emitted, a callback will be called.
 * Callback invocation are serialized within a subscription.
 *
 * @param cb callback function
 * @param opaque data for the callback function
 * @return a subscription pointer, or NULL in case of failure
sigmunau's avatar
all:    
sigmunau committed
73
 */
74
msg_subscription_t *vlc_Subscribe (msg_callback_t cb, void *opaque)
Sam Hocevar's avatar
   
Sam Hocevar committed
75
{
76
77
    msg_subscription_t *sub = malloc (sizeof (*sub));
    if (sub == NULL)
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
78
79
        return NULL;

80
    sub->prev = NULL;
81
82
    sub->func = cb;
    sub->opaque = opaque;
Sam Hocevar's avatar
   
Sam Hocevar committed
83

84
85
86
87
    vlc_rwlock_wrlock (&msg_lock);
    sub->next = msg_head;
    msg_head = sub;
    vlc_rwlock_unlock (&msg_lock);
Sam Hocevar's avatar
   
Sam Hocevar committed
88

89
    return sub;
Sam Hocevar's avatar
   
Sam Hocevar committed
90
91
}

sigmunau's avatar
all:    
sigmunau committed
92
/**
93
94
 * Unsubscribe from the message queue.
 * This function waits for the message callback to return if needed.
sigmunau's avatar
all:    
sigmunau committed
95
 */
96
void vlc_Unsubscribe (msg_subscription_t *sub)
Sam Hocevar's avatar
   
Sam Hocevar committed
97
{
98
99
100
101
102
103
104
105
106
107
108
    vlc_rwlock_wrlock (&msg_lock);
    if (sub->next != NULL)
        sub->next->prev = sub->prev;
    if (sub->prev != NULL)
        sub->prev->next = sub->next;
    else
    {
        assert (msg_head == sub);
        msg_head = sub->next;
    }
    vlc_rwlock_unlock (&msg_lock);
109
    free (sub);
Michel Kaempf's avatar
Michel Kaempf committed
110
111
}

112
113
/**
 * Emit a log message.
114
 * \param obj VLC object emitting the message or NULL
115
116
117
118
119
120
121
 * \param type VLC_MSG_* message type (info, error, warning or debug)
 * \param module name of module from which the message come
 *               (normally MODULE_STRING)
 * \param format printf-like message format
 */
void vlc_Log (vlc_object_t *obj, int type, const char *module,
              const char *format, ... )
Michel Kaempf's avatar
Michel Kaempf committed
122
{
123
    va_list args;
Michel Kaempf's avatar
Michel Kaempf committed
124

125
126
127
    va_start (args, format);
    vlc_vaLog (obj, type, module, format, args);
    va_end (args);
Michel Kaempf's avatar
Michel Kaempf committed
128
}
129

130
131
132
static void PrintColorMsg (void *, int, const msg_item_t *,
                           const char *, va_list);
static void PrintMsg (void *, int, const msg_item_t *, const char *, va_list);
133
134
135
136
#ifdef WIN32
static void Win32DebugOutputMsg (void *, int , const msg_item_t *,
                                 const char *, va_list);
#endif
137

sigmunau's avatar
all:    
sigmunau committed
138
/**
139
140
 * Emit a log message. This function is the variable argument list equivalent
 * to vlc_Log().
sigmunau's avatar
all:    
sigmunau committed
141
 */
142
143
void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
                const char *format, va_list args)
Michel Kaempf's avatar
Michel Kaempf committed
144
{
145
    if (obj != NULL && obj->i_flags & OBJECT_FLAGS_QUIET)
146
        return;
147

148
149
150
    /* C locale to get error messages in English in the logs */
    locale_t c = newlocale (LC_MESSAGES_MASK, "C", (locale_t)0);
    locale_t locale = uselocale (c);
151

152
153
#ifndef __GLIBC__
    /* Expand %m to strerror(errno) - only once */
154
155
    char buf[strlen(format) + 2001], *ptr;
    strcpy (buf, format);
Rafaël Carré's avatar
Rafaël Carré committed
156
    ptr = (char*)buf;
157
    format = (const char*) buf;
158
159
160
161
162
163
164
165
166
167
168
169

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

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

170
#ifndef WIN32
171
            strerror_r( errno, errbuf, 1001 );
172
173
174
175
176
177
178
#else
            int sockerr = WSAGetLastError( );
            if( sockerr )
            {
                strncpy( errbuf, net_strerror( sockerr ), 1001 );
                WSASetLastError( sockerr );
            }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
179
180
            if ((sockerr == 0)
             || (strcmp ("Unknown network stack error", errbuf) == 0))
181
182
                strncpy( errbuf, strerror( errno ), 1001 );
#endif
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
            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

208
209
    /* Fill message information fields */
    msg_item_t msg;
210

211
    msg.i_object_id = (uintptr_t)obj;
212
    msg.psz_object_type = (obj != NULL) ? obj->psz_object_type : "generic";
213
    msg.psz_module = module;
214
    msg.psz_header = NULL;
215

216
    for (vlc_object_t *o = obj; o != NULL; o = o->p_parent)
217
        if (o->psz_header != NULL)
218
        {
219
            msg.psz_header = o->psz_header;
220
            break;
221
        }
222

223
224
225
    /* Pass message to subscribers */
    libvlc_priv_t *priv = libvlc_priv (obj->p_libvlc);

226
227
228
    va_list ap;

    va_copy (ap, args);
229
    if (priv->b_color)
230
        PrintColorMsg (&priv->i_verbose, type, &msg, format, ap);
231
    else
232
233
        PrintMsg (&priv->i_verbose, type, &msg, format, ap);
    va_end (ap);
234

235
#ifdef WIN32
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
236
237
238
    va_copy (ap, args);
    Win32DebugOutputMsg (&priv->i_verbose, type, &msg, format, ap);
    va_end (ap);
239
240
#endif

241
242
    vlc_rwlock_rdlock (&msg_lock);
    for (msg_subscription_t *sub = msg_head; sub != NULL; sub = sub->next)
243
244
245
246
247
    {
        va_copy (ap, args);
        sub->func (sub->opaque, type, &msg, format, ap);
        va_end (ap);
    }
248
    vlc_rwlock_unlock (&msg_lock);
249

250
251
    uselocale (locale);
    freelocale (c);
Michel Kaempf's avatar
Michel Kaempf committed
252
253
}

254
255
256
257
258
259
260
261
262
static const char msg_type[4][9] = { "", " error", " warning", " debug" };
#define COL(x,y)  "\033[" #x ";" #y "m"
#define RED     COL(31,1)
#define GREEN   COL(32,1)
#define YELLOW  COL(0,33)
#define WHITE   COL(0,1)
#define GRAY    "\033[0m"
static const char msg_color[4][8] = { WHITE, RED, YELLOW, GRAY };

263
264
static void PrintColorMsg (void *d, int type, const msg_item_t *p_item,
                           const char *format, va_list ap)
265
{
266
    const signed char *pverbose = d;
267
    FILE *stream = stderr;
268

269
    if (*pverbose < 0 || *pverbose < (type - VLC_MSG_ERR))
270
        return;
271

272
273
274
275
276
277
    int canc = vlc_savecancel ();

    flockfile (stream);
    fprintf (stream, "["GREEN"%p"GRAY"] ", (void *)p_item->i_object_id);
    if (p_item->psz_header != NULL)
        utf8_fprintf (stream, "[%s] ", p_item->psz_header);
278
279
280
281
    utf8_fprintf (stream, "%s %s%s: %s", p_item->psz_module,
                  p_item->psz_object_type, msg_type[type], msg_color[type]);
    utf8_vfprintf (stream, format, ap);
    fputs (GRAY"\n", stream);
282
283
284
285
286
287
288
#if defined (WIN32) || defined (__OS2__)
    fflush (stream);
#endif
    funlockfile (stream);
    vlc_restorecancel (canc);
}

289
290
static void PrintMsg (void *d, int type, const msg_item_t *p_item,
                      const char *format, va_list ap)
291
{
292
    const signed char *pverbose = d;
293
    FILE *stream = stderr;
294
295
296
297

    if (*pverbose < 0 || *pverbose < (type - VLC_MSG_ERR))
        return;

298
299
300
    int canc = vlc_savecancel ();

    flockfile (stream);
301
    fprintf (stream, "[%p] ", (void *)p_item->i_object_id);
302
    if (p_item->psz_header != NULL)
303
        utf8_fprintf (stream, "[%s] ", p_item->psz_header);
304
305
306
    utf8_fprintf (stream, "%s %s%s: ", p_item->psz_module,
                  p_item->psz_object_type, msg_type[type]);
    utf8_vfprintf (stream, format, ap);
307
    putc_unlocked ('\n', stream);
308
309
#if defined (WIN32) || defined (__OS2__)
    fflush (stream);
310
#endif
311
    funlockfile (stream);
312
    vlc_restorecancel (canc);
313
}
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345

#ifdef WIN32
static void Win32DebugOutputMsg (void* d, int type, const msg_item_t *p_item,
                                 const char *format, va_list dol)
{
    const signed char *pverbose = d;
    if (pverbose && (*pverbose < 0 || *pverbose < (type - VLC_MSG_ERR)))
        return;

    va_list dol2;
    va_copy (dol2, dol);
    int msg_len = vsnprintf(NULL, 0, format, dol2);
    va_end (dol2);

    if(msg_len <= 0)
        return;

    char *msg = malloc(msg_len + 1 + 1);
    if (!msg)
        return;

    msg_len = vsnprintf(msg, msg_len+1, format, dol);
    if (msg_len > 0){
        if(msg[msg_len-1] != '\n'){
            msg[msg_len] = '\n';
            msg[msg_len + 1] = '\0';
        }
        OutputDebugString(msg);
    }
    free(msg);
}
#endif