]> git.sesse.net Git - vlc/blobdiff - src/misc/messages.c
decoder: remove unnecessary special case
[vlc] / src / misc / messages.c
index bffe24f95098df6794aba90fb95f34f493b0714c..e22e4fe742fb4587c6c394eb561b150ea8a130d4 100644 (file)
 
 #include <stdlib.h>
 #include <stdarg.h>                                       /* va_list for BSD */
-#ifdef __APPLE__
-# include <xlocale.h>
-#elif defined(HAVE_LOCALE_H)
-# include <locale.h>
-#endif
-#include <errno.h>                                                  /* errno */
-#include <assert.h>
 #include <unistd.h>
+#include <assert.h>
 
 #include <vlc_common.h>
 #include <vlc_interface.h>
-#ifdef _WIN32
-#   include <vlc_network.h>          /* 'net_strerror' and 'WSAGetLastError' */
-#endif
 #include <vlc_charset.h>
+#include <vlc_modules.h>
 #include "../libvlc.h"
 
-/**
- * Emit a log message.
- * \param obj VLC object emitting the message or NULL
- * \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, ... )
+struct vlc_logger_t
+{
+    VLC_COMMON_MEMBERS
+    vlc_rwlock_t lock;
+    vlc_log_cb log;
+    void *sys;
+    module_t *module;
+};
+
+static void vlc_vaLogCallback(libvlc_int_t *vlc, int type,
+                              const vlc_log_t *item, const char *format,
+                              va_list ap)
 {
-    va_list args;
+    vlc_logger_t *logger = libvlc_priv(vlc)->logger;
 
-    va_start (args, format);
-    vlc_vaLog (obj, type, module, format, args);
-    va_end (args);
+    assert(logger != NULL);
+    vlc_rwlock_rdlock(&logger->lock);
+    logger->log(logger->sys, type, item, format, ap);
+    vlc_rwlock_unlock(&logger->lock);
+}
+
+static void vlc_LogCallback(libvlc_int_t *vlc, int type, const vlc_log_t *item,
+                            const char *format, ...)
+{
+    va_list ap;
+
+    va_start(ap, format);
+    vlc_vaLogCallback(vlc, type, item, format, ap);
+    va_end(ap);
 }
 
 #ifdef _WIN32
@@ -79,6 +84,7 @@ static void Win32DebugOutputMsg (void *, int , const vlc_log_t *,
  * to vlc_Log().
  */
 void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
+                const char *file, unsigned line, const char *func,
                 const char *format, va_list args)
 {
     if (obj != NULL && obj->i_flags & OBJECT_FLAGS_QUIET)
@@ -90,7 +96,7 @@ void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
         module = p;
     p = strchr(module, '.');
 
-    size_t modlen = (p != NULL) ? (p - module) : 1;
+    size_t modlen = (p != NULL) ? (p - module) : 0;
     char modulebuf[modlen + 1];
     if (p != NULL)
     {
@@ -99,66 +105,6 @@ void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
         module = modulebuf;
     }
 
-    /* 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);
-
-#ifndef __GLIBC__
-    /* Expand %m to strerror(errno) - only once */
-    char buf[strlen(format) + 2001], *ptr;
-    strcpy (buf, format);
-    ptr = (char*)buf;
-    format = (const char*) buf;
-
-    for( ;; )
-    {
-        ptr = strchr( ptr, '%' );
-        if( ptr == NULL )
-            break;
-
-        if( ptr[1] == 'm' )
-        {
-            char errbuf[2001];
-            size_t errlen;
-
-#ifndef _WIN32
-            strerror_r( errno, errbuf, 1001 );
-#else
-            int sockerr = WSAGetLastError( );
-            if( sockerr )
-            {
-                strncpy( errbuf, net_strerror( sockerr ), 1001 );
-                WSASetLastError( sockerr );
-            }
-            if ((sockerr == 0)
-             || (strcmp ("Unknown network stack error", errbuf) == 0))
-                strncpy( errbuf, strerror( errno ), 1001 );
-#endif
-            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
-
     /* Fill message information fields */
     vlc_log_t msg;
 
@@ -166,6 +112,9 @@ void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
     msg.psz_object_type = (obj != NULL) ? obj->psz_object_type : "generic";
     msg.psz_module = module;
     msg.psz_header = NULL;
+    msg.file = file;
+    msg.line = line;
+    msg.func = func;
 
     for (vlc_object_t *o = obj; o != NULL; o = o->p_parent)
         if (o->psz_header != NULL)
@@ -174,89 +123,44 @@ void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
             break;
         }
 
-    /* Pass message to the callback */
-    libvlc_priv_t *priv = obj ? libvlc_priv (obj->p_libvlc) : NULL;
-
 #ifdef _WIN32
     va_list ap;
 
     va_copy (ap, args);
-    Win32DebugOutputMsg (priv ? &priv->log.verbose : NULL, type, &msg, format, ap);
+    Win32DebugOutputMsg (NULL, type, &msg, format, ap);
     va_end (ap);
 #endif
 
-    if (priv) {
-        vlc_rwlock_rdlock (&priv->log.lock);
-        priv->log.cb (priv->log.opaque, type, &msg, format, args);
-        vlc_rwlock_unlock (&priv->log.lock);
-    }
-
-    uselocale (locale);
-    freelocale (c);
-}
-
-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 };
-
-static void PrintColorMsg (void *d, int type, const vlc_log_t *p_item,
-                           const char *format, va_list ap)
-{
-    FILE *stream = stderr;
-    int verbose = (intptr_t)d;
-
-    if (verbose < 0 || verbose < (type - VLC_MSG_ERR))
-        return;
-
-    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);
-    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);
-#if defined (_WIN32) || defined (__OS2__)
-    fflush (stream);
-#endif
-    funlockfile (stream);
-    vlc_restorecancel (canc);
+    /* Pass message to the callback */
+    if (obj != NULL)
+        vlc_vaLogCallback(obj->p_libvlc, type, &msg, format, args);
 }
 
-static void PrintMsg (void *d, int type, const vlc_log_t *p_item,
-                      const char *format, va_list ap)
+/**
+ * Emit a log message.
+ * \param obj VLC object emitting the message or NULL
+ * \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 file source module file name (normally __FILE__) or NULL
+ * \param line function call source line number (normally __LINE__) or 0
+ * \param func calling function name (normally __func__) or NULL
+ * \param format printf-like message format
+ */
+void vlc_Log(vlc_object_t *obj, int type, const char *module,
+             const char *file, unsigned line, const char *func,
+             const char *format, ... )
 {
-    FILE *stream = stderr;
-    int verbose = (intptr_t)d;
-
-    if (verbose < 0 || verbose < (type - VLC_MSG_ERR))
-        return;
+    va_list ap;
 
-    int canc = vlc_savecancel ();
-
-    flockfile (stream);
-    fprintf (stream, "[%p] ", (void *)p_item->i_object_id);
-    if (p_item->psz_header != NULL)
-        utf8_fprintf (stream, "[%s] ", p_item->psz_header);
-    utf8_fprintf (stream, "%s %s%s: ", p_item->psz_module,
-                  p_item->psz_object_type, msg_type[type]);
-    utf8_vfprintf (stream, format, ap);
-    putc_unlocked ('\n', stream);
-#if defined (_WIN32) || defined (__OS2__)
-    fflush (stream);
-#endif
-    funlockfile (stream);
-    vlc_restorecancel (canc);
+    va_start(ap, format);
+    vlc_vaLog(obj, type, module, file, line, func, format, ap);
+    va_end(ap);
 }
 
 #ifdef _WIN32
+static const char msg_type[4][9] = { "", " error", " warning", " debug" };
+
 static void Win32DebugOutputMsg (void* d, int type, const vlc_log_t *p_item,
                                  const char *format, va_list dol)
 {
@@ -297,30 +201,214 @@ static void Win32DebugOutputMsg (void* d, int type, const vlc_log_t *p_item,
 }
 #endif
 
+typedef struct vlc_log_early_t
+{
+    struct vlc_log_early_t *next;
+    int type;
+    vlc_log_t meta;
+    char *msg;
+} vlc_log_early_t;
+
+typedef struct
+{
+    vlc_mutex_t lock;
+    vlc_log_early_t *head;
+    vlc_log_early_t **tailp;
+} vlc_logger_early_t;
+
+static void vlc_vaLogEarly(void *d, int type, const vlc_log_t *item,
+                           const char *format, va_list ap)
+{
+    vlc_logger_early_t *sys = d;
+
+    vlc_log_early_t *log = malloc(sizeof (*log));
+    if (unlikely(log == NULL))
+        return;
+
+    log->next = NULL;
+    log->type = type;
+    log->meta.i_object_id = item->i_object_id;
+    /* NOTE: Object types MUST be static constant - no need to copy them. */
+    log->meta.psz_object_type = item->psz_object_type;
+    log->meta.psz_module = item->psz_module; /* Ditto. */
+    log->meta.psz_header = item->psz_header ? strdup(item->psz_header) : NULL;
+    log->meta.file = item->file;
+    log->meta.line = item->line;
+    log->meta.func = item->func;
+
+    int canc = vlc_savecancel(); /* XXX: needed for vasprintf() ? */
+    if (vasprintf(&log->msg, format, ap) == -1)
+        log->msg = NULL;
+    vlc_restorecancel(canc);
+
+    vlc_mutex_lock(&sys->lock);
+    assert(sys->tailp != NULL);
+    assert(*(sys->tailp) == NULL);
+    *(sys->tailp) = log;
+    sys->tailp = &log->next;
+    vlc_mutex_unlock(&sys->lock);
+}
+
+static int vlc_LogEarlyOpen(vlc_logger_t *logger)
+{
+    vlc_logger_early_t *sys = malloc(sizeof (*sys));
+
+    if (unlikely(sys == NULL))
+        return -1;
+
+    vlc_mutex_init(&sys->lock);
+    sys->head = NULL;
+    sys->tailp = &sys->head;
+
+    logger->log = vlc_vaLogEarly;
+    logger->sys = sys;
+    return 0;
+}
+
+static void vlc_LogEarlyClose(vlc_logger_t *logger, void *d)
+{
+    libvlc_int_t *vlc = logger->p_libvlc;
+    vlc_logger_early_t *sys = d;
+
+    /* Drain early log messages */
+    for (vlc_log_early_t *log = sys->head, *next; log != NULL; log = next)
+    {
+        vlc_LogCallback(vlc, log->type, &log->meta, "%s",
+                        (log->msg != NULL) ? log->msg : "message lost");
+        free(log->msg);
+        next = log->next;
+        free(log);
+    }
+
+    vlc_mutex_destroy(&sys->lock);
+    free(sys);
+}
+
+static void vlc_vaLogDiscard(void *d, int type, const vlc_log_t *item,
+                             const char *format, va_list ap)
+{
+    (void) d; (void) type; (void) item; (void) format; (void) ap;
+}
+
+static int vlc_logger_load(void *func, va_list ap)
+{
+    vlc_log_cb (*activate)(vlc_object_t *, void **) = func;
+    vlc_logger_t *logger = va_arg(ap, vlc_logger_t *);
+    vlc_log_cb *cb = va_arg(ap, vlc_log_cb *);
+    void **sys = va_arg(ap, void **);
+
+    *cb = activate(VLC_OBJECT(logger), sys);
+    return (*cb != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static void vlc_logger_unload(void *func, va_list ap)
+{
+    void (*deactivate)(vlc_logger_t *) = func;
+    void *sys = va_arg(ap, void *);
+
+    deactivate(sys);
+}
+
+/**
+ * Performs preinitialization of the messages logging subsystem.
+ *
+ * Early log messages will be stored in memory until the subsystem is fully
+ * initialized with vlc_LogInit(). This enables logging before the
+ * configuration and modules bank are ready.
+ *
+ * \return 0 on success, -1 on error.
+ */
+int vlc_LogPreinit(libvlc_int_t *vlc)
+{
+    vlc_logger_t *logger = vlc_custom_create(vlc, sizeof (*logger), "logger");
+
+    libvlc_priv(vlc)->logger = logger;
+
+    if (unlikely(logger == NULL))
+        return -1;
+
+    vlc_rwlock_init(&logger->lock);
+
+    if (vlc_LogEarlyOpen(logger))
+    {
+        logger->log = vlc_vaLogDiscard;
+        return -1;
+    }
+
+    /* Announce who we are */
+    msg_Dbg(vlc, "VLC media player - %s", VERSION_MESSAGE);
+    msg_Dbg(vlc, "%s", COPYRIGHT_MESSAGE);
+    msg_Dbg(vlc, "revision %s", psz_vlc_changeset);
+    msg_Dbg(vlc, "configured with %s", CONFIGURE_LINE);
+    return 0;
+}
+
+/**
+ * Initializes the messages logging subsystem and drain the early messages to
+ * the configured log.
+ *
+ * \return 0 on success, -1 on error.
+ */
+int vlc_LogInit(libvlc_int_t *vlc)
+{
+    vlc_logger_t *logger = libvlc_priv(vlc)->logger;
+    if (unlikely(logger == NULL))
+        return -1;
+
+    vlc_log_cb cb;
+    void *sys, *early_sys = NULL;
+
+    /* TODO: module configuration item */
+    module_t *module = vlc_module_load(logger, "logger", NULL, false,
+                                       vlc_logger_load, logger, &cb, &sys);
+    if (module == NULL)
+        cb = vlc_vaLogDiscard;
+
+    vlc_rwlock_wrlock(&logger->lock);
+    if (logger->log == vlc_vaLogEarly)
+        early_sys = logger->sys;
+
+    logger->log = cb;
+    logger->sys = sys;
+    assert(logger->module == NULL); /* Only one call to vlc_LogInit()! */
+    logger->module = module;
+    vlc_rwlock_unlock(&logger->lock);
+
+    if (early_sys != NULL)
+        vlc_LogEarlyClose(logger, early_sys);
+
+    return 0;
+}
+
 /**
  * Sets the message logging callback.
- * \param cb message callback, or NULL to reset
+ * \param cb message callback, or NULL to clear
  * \param data data pointer for the message callback
  */
-void vlc_LogSet (libvlc_int_t *vlc, vlc_log_cb cb, void *opaque)
+void vlc_LogSet(libvlc_int_t *vlc, vlc_log_cb cb, void *opaque)
 {
-    libvlc_priv_t *priv = libvlc_priv (vlc);
+    vlc_logger_t *logger = libvlc_priv(vlc)->logger;
+
+    if (unlikely(logger == NULL))
+        return;
+
+    module_t *module;
+    void *sys;
 
     if (cb == NULL)
-    {
-#if defined (HAVE_ISATTY) && !defined (_WIN32)
-        if (isatty (STDERR_FILENO) && var_InheritBool (vlc, "color"))
-            cb = PrintColorMsg;
-        else
-#endif
-            cb = PrintMsg;
-        opaque = (void *)(intptr_t)priv->log.verbose;
-    }
+        cb = vlc_vaLogDiscard;
+
+    vlc_rwlock_wrlock(&logger->lock);
+    sys = logger->sys;
+    module = logger->module;
 
-    vlc_rwlock_wrlock (&priv->log.lock);
-    priv->log.cb = cb;
-    priv->log.opaque = opaque;
-    vlc_rwlock_unlock (&priv->log.lock);
+    logger->log = cb;
+    logger->sys = opaque;
+    logger->module = NULL;
+    vlc_rwlock_unlock(&logger->lock);
+
+    if (module != NULL)
+        vlc_module_unload(module, vlc_logger_unload, sys);
 
     /* Announce who we are */
     msg_Dbg (vlc, "VLC media player - %s", VERSION_MESSAGE);
@@ -329,26 +417,24 @@ void vlc_LogSet (libvlc_int_t *vlc, vlc_log_cb cb, void *opaque)
     msg_Dbg (vlc, "configured with %s", CONFIGURE_LINE);
 }
 
-void vlc_LogInit (libvlc_int_t *vlc)
+void vlc_LogDeinit(libvlc_int_t *vlc)
 {
-    libvlc_priv_t *priv = libvlc_priv (vlc);
-    const char *str;
+    vlc_logger_t *logger = libvlc_priv(vlc)->logger;
 
-    if (var_InheritBool (vlc, "quiet"))
-        priv->log.verbose = -1;
-    else
-    if ((str = getenv ("VLC_VERBOSE")) != NULL)
-        priv->log.verbose = atoi (str);
-    else
-        priv->log.verbose = var_InheritInteger (vlc, "verbose");
-
-    vlc_rwlock_init (&priv->log.lock);
-    vlc_LogSet (vlc, NULL, NULL);
-}
+    if (unlikely(logger == NULL))
+        return;
 
-void vlc_LogDeinit (libvlc_int_t *vlc)
-{
-    libvlc_priv_t *priv = libvlc_priv (vlc);
+    if (logger->module != NULL)
+        vlc_module_unload(logger->module, vlc_logger_unload, logger->sys);
+    else
+    /* Flush early log messages (corner case: no call to vlc_LogInit()) */
+    if (logger->log == vlc_vaLogEarly)
+    {
+        logger->log = vlc_vaLogDiscard;
+        vlc_LogEarlyClose(logger, logger->sys);
+    }
 
-    vlc_rwlock_destroy (&priv->log.lock);
+    vlc_rwlock_destroy(&logger->lock);
+    vlc_object_release(logger);
+    libvlc_priv(vlc)->logger = NULL;
 }