]> git.sesse.net Git - vlc/blobdiff - src/text/strings.c
Input access locking, part 3 (final).
[vlc] / src / text / strings.c
index 497c07735837e8748198d9cf440e6562da39d957..4d0b317c46922c75fdfe9db369a42fb2a818a991 100644 (file)
@@ -27,9 +27,6 @@
  * Preamble
  *****************************************************************************/
 #include <vlc/vlc.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
 #include <assert.h>
 
 /* Needed by str_format_time */
@@ -63,6 +60,8 @@ char *unescape_URI_duplicate( const char *psz )
 void unescape_URI( char *psz )
 {
     unsigned char *in = (unsigned char *)psz, *out = in, c;
+    if( psz == NULL )
+        return;
 
     while( ( c = *in++ ) != '\0' )
     {
@@ -144,6 +143,8 @@ char *decode_URI_duplicate( const char *psz )
 void decode_URI( char *psz )
 {
     unsigned char *in = (unsigned char *)psz, *out = in, c;
+    if( psz == NULL )
+        return;
 
     while( ( c = *in++ ) != '\0' )
     {
@@ -242,30 +243,174 @@ void resolve_xml_special_chars( char *psz_value )
 
     while ( *psz_value )
     {
-        if( !strncmp( psz_value, "&lt;", 4 ) )
+        if( *psz_value == '&' )
         {
-            *p_pos = '<';
-            psz_value += 4;
-        }
-        else if( !strncmp( psz_value, "&gt;", 4 ) )
-        {
-            *p_pos = '>';
-            psz_value += 4;
-        }
-        else if( !strncmp( psz_value, "&amp;", 5 ) )
-        {
-            *p_pos = '&';
-            psz_value += 5;
-        }
-        else if( !strncmp( psz_value, "&quot;", 6 ) )
-        {
-            *p_pos = '\"';
-            psz_value += 6;
-        }
-        else if( !strncmp( psz_value, "&#039;", 6 ) )
-        {
-            *p_pos = '\'';
-            psz_value += 6;
+#define TRY_CHAR( src, len, dst )                   \
+            if( !strncmp( psz_value, src, len ) )   \
+            {                                       \
+                *p_pos = dst;                       \
+                psz_value += len;                   \
+            }
+#define TRY_LONGCHAR( src, len, dst )                   \
+            if( !strncmp( psz_value, src, len ) )       \
+            {                                           \
+                strncpy( p_pos, dst, strlen( dst ) );   \
+                p_pos += strlen( dst ) - 1;             \
+                psz_value += len;                       \
+            }
+            TRY_CHAR( "&lt;", 4, '<' )
+            else TRY_CHAR( "&gt;", 4, '>' )
+            else TRY_CHAR( "&amp;", 5, '&' )
+            else TRY_CHAR( "&quot;", 6, '"' )
+            else TRY_CHAR( "&apos;", 6, '\'' )
+            else if( psz_value[1] == '#' )
+            {
+                char *psz_end;
+                int i = strtol( psz_value+2, &psz_end, 10 );
+                if( *psz_end == ';' )
+                {
+                    if( i >= 32 && i <= 126 )
+                    {
+                        *p_pos = (char)i;
+                        psz_value = psz_end+1;
+                    }
+                    else
+                    {
+                        /* Unhandled code, FIXME */
+                        *p_pos = *psz_value;
+                        psz_value++;
+                    }
+                }
+                else
+                {
+                    /* Invalid entity number */
+                    *p_pos = *psz_value;
+                    psz_value++;
+                }
+            }
+            else TRY_LONGCHAR( "&Agrave;", 8, "À" )
+            else TRY_LONGCHAR( "&Aacute;", 8, "Á" )
+            else TRY_LONGCHAR( "&Acirc;", 7, "Â" )
+            else TRY_LONGCHAR( "&Atilde;", 8, "Ã" )
+            else TRY_LONGCHAR( "&Auml;", 6, "Ä" )
+            else TRY_LONGCHAR( "&Aring;", 7, "Å" )
+            else TRY_LONGCHAR( "&AElig;", 7, "Æ" )
+            else TRY_LONGCHAR( "&Ccedil;", 8, "Ç" )
+            else TRY_LONGCHAR( "&Egrave;", 8, "È" )
+            else TRY_LONGCHAR( "&Eacute;", 8, "É" )
+            else TRY_LONGCHAR( "&Ecirc;", 7, "Ê" )
+            else TRY_LONGCHAR( "&Euml;", 6, "Ë" )
+            else TRY_LONGCHAR( "&Igrave;", 8, "Ì" )
+            else TRY_LONGCHAR( "&Iacute;", 8, "Í" )
+            else TRY_LONGCHAR( "&Icirc;", 7, "Î" )
+            else TRY_LONGCHAR( "&Iuml;", 6, "Ï" )
+            else TRY_LONGCHAR( "&ETH;", 5, "Ð" )
+            else TRY_LONGCHAR( "&Ntilde;", 8, "Ñ" )
+            else TRY_LONGCHAR( "&Ograve;", 8, "Ò" )
+            else TRY_LONGCHAR( "&Oacute;", 8, "Ó" )
+            else TRY_LONGCHAR( "&Ocirc;", 7, "Ô" )
+            else TRY_LONGCHAR( "&Otilde;", 8, "Õ" )
+            else TRY_LONGCHAR( "&Ouml;", 6, "Ö" )
+            else TRY_LONGCHAR( "&Oslash;", 8, "Ø" )
+            else TRY_LONGCHAR( "&Ugrave;", 8, "Ù" )
+            else TRY_LONGCHAR( "&Uacute;", 8, "Ú" )
+            else TRY_LONGCHAR( "&Ucirc;", 7, "Û" )
+            else TRY_LONGCHAR( "&Uuml;", 6, "Ü" )
+            else TRY_LONGCHAR( "&Yacute;", 8, "Ý" )
+            else TRY_LONGCHAR( "&THORN;", 7, "Þ" )
+            else TRY_LONGCHAR( "&szlig;", 7, "ß" )
+            else TRY_LONGCHAR( "&agrave;", 8, "à" )
+            else TRY_LONGCHAR( "&aacute;", 8, "á" )
+            else TRY_LONGCHAR( "&acirc;", 7, "â" )
+            else TRY_LONGCHAR( "&atilde;", 8, "ã" )
+            else TRY_LONGCHAR( "&auml;", 6, "ä" )
+            else TRY_LONGCHAR( "&aring;", 7, "å" )
+            else TRY_LONGCHAR( "&aelig;", 7, "æ" )
+            else TRY_LONGCHAR( "&ccedil;", 8, "ç" )
+            else TRY_LONGCHAR( "&egrave;", 8, "è" )
+            else TRY_LONGCHAR( "&eacute;", 8, "é" )
+            else TRY_LONGCHAR( "&ecirc;", 7, "ê" )
+            else TRY_LONGCHAR( "&euml;", 6, "ë" )
+            else TRY_LONGCHAR( "&igrave;", 8, "ì" )
+            else TRY_LONGCHAR( "&iacute;", 8, "í" )
+            else TRY_LONGCHAR( "&icirc;", 7, "î" )
+            else TRY_LONGCHAR( "&iuml;", 6, "ï" )
+            else TRY_LONGCHAR( "&eth;", 5, "ð" )
+            else TRY_LONGCHAR( "&ntilde;", 8, "ñ" )
+            else TRY_LONGCHAR( "&ograve;", 8, "ò" )
+            else TRY_LONGCHAR( "&oacute;", 8, "ó" )
+            else TRY_LONGCHAR( "&ocirc;", 7, "ô" )
+            else TRY_LONGCHAR( "&otilde;", 8, "õ" )
+            else TRY_LONGCHAR( "&ouml;", 6, "ö" )
+            else TRY_LONGCHAR( "&oslash;", 8, "ø" )
+            else TRY_LONGCHAR( "&ugrave;", 8, "ù" )
+            else TRY_LONGCHAR( "&uacute;", 8, "ú" )
+            else TRY_LONGCHAR( "&ucirc;", 7, "û" )
+            else TRY_LONGCHAR( "&uuml;", 6, "ü" )
+            else TRY_LONGCHAR( "&yacute;", 8, "ý" )
+            else TRY_LONGCHAR( "&thorn;", 7, "þ" )
+            else TRY_LONGCHAR( "&yuml;", 6, "ÿ" )
+            else TRY_LONGCHAR( "&iexcl;", 7, "¡" )
+            else TRY_LONGCHAR( "&curren;", 8, "¤" )
+            else TRY_LONGCHAR( "&cent;", 6, "¢" )
+            else TRY_LONGCHAR( "&pound;", 7, "£" )
+            else TRY_LONGCHAR( "&yen;", 5, "¥" )
+            else TRY_LONGCHAR( "&brvbar;", 8, "¦" )
+            else TRY_LONGCHAR( "&sect;", 6, "§" )
+            else TRY_LONGCHAR( "&uml;", 5, "¨" )
+            else TRY_LONGCHAR( "&copy;", 6, "©" )
+            else TRY_LONGCHAR( "&ordf;", 6, "ª" )
+            else TRY_LONGCHAR( "&laquo;", 7, "«" )
+            else TRY_LONGCHAR( "&not;", 5, "¬" )
+            else TRY_LONGCHAR( "&shy;", 5, "­" )
+            else TRY_LONGCHAR( "&reg;", 5, "®" )
+            else TRY_LONGCHAR( "&trade;", 7, "™" )
+            else TRY_LONGCHAR( "&macr;", 6, "¯" )
+            else TRY_LONGCHAR( "&deg;", 5, "°" )
+            else TRY_LONGCHAR( "&plusmn;", 8, "±" )
+            else TRY_LONGCHAR( "&sup2;", 6, "²" )
+            else TRY_LONGCHAR( "&sup3;", 6, "³" )
+            else TRY_LONGCHAR( "&acute;", 7, "´" )
+            else TRY_LONGCHAR( "&micro;", 7, "µ" )
+            else TRY_LONGCHAR( "&para;", 6, "¶" )
+            else TRY_LONGCHAR( "&middot;", 8, "·" )
+            else TRY_LONGCHAR( "&cedil;", 7, "¸" )
+            else TRY_LONGCHAR( "&sup1;", 6, "¹" )
+            else TRY_LONGCHAR( "&ordm;", 6, "º" )
+            else TRY_LONGCHAR( "&raquo;", 7, "»" )
+            else TRY_LONGCHAR( "&frac14;", 8, "¼" )
+            else TRY_LONGCHAR( "&frac12;", 8, "½" )
+            else TRY_LONGCHAR( "&frac34;", 8, "¾" )
+            else TRY_LONGCHAR( "&iquest;", 8, "¿" )
+            else TRY_LONGCHAR( "&times;", 7, "×" )
+            else TRY_LONGCHAR( "&divide;", 8, "÷" )
+            else TRY_LONGCHAR( "&OElig;", 7, "Œ" )
+            else TRY_LONGCHAR( "&oelig;", 7, "œ" )
+            else TRY_LONGCHAR( "&Scaron;", 8, "Š" )
+            else TRY_LONGCHAR( "&scaron;", 8, "š" )
+            else TRY_LONGCHAR( "&Yuml;", 6, "Ÿ" )
+            else TRY_LONGCHAR( "&circ;", 6, "ˆ" )
+            else TRY_LONGCHAR( "&tilde;", 7, "˜" )
+            else TRY_LONGCHAR( "&ndash;", 7, "–" )
+            else TRY_LONGCHAR( "&mdash;", 7, "—" )
+            else TRY_LONGCHAR( "&lsquo;", 7, "‘" )
+            else TRY_LONGCHAR( "&rsquo;", 7, "’" )
+            else TRY_LONGCHAR( "&sbquo;", 7, "‚" )
+            else TRY_LONGCHAR( "&ldquo;", 7, "“" )
+            else TRY_LONGCHAR( "&rdquo;", 7, "”" )
+            else TRY_LONGCHAR( "&bdquo;", 7, "„" )
+            else TRY_LONGCHAR( "&dagger;", 8, "†" )
+            else TRY_LONGCHAR( "&Dagger;", 8, "‡" )
+            else TRY_LONGCHAR( "&hellip;", 8, "…" )
+            else TRY_LONGCHAR( "&permil;", 8, "‰" )
+            else TRY_LONGCHAR( "&lsaquo;", 8, "‹" )
+            else TRY_LONGCHAR( "&rsaquo;", 8, "›" )
+            else TRY_LONGCHAR( "&euro;", 6, "€" )
+            else
+            {
+                *p_pos = *psz_value;
+                psz_value++;
+            }
         }
         else
         {
@@ -329,46 +474,45 @@ char *convert_xml_special_chars( const char *psz_content )
 }
 
 /* Base64 encoding */
-char *vlc_b64_encode( const char *src )
+char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src )
 {
     static const char b64[] =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-    size_t len = strlen( src );
-    const uint8_t *in = (const uint8_t *)src;
 
-    char *ret;
-    char *dst = (char *)malloc( ( len + 4 ) * 4 / 3 );
+    char *ret = malloc( ( i_src + 4 ) * 4 / 3 );
+    char *dst = ret;
+
     if( dst == NULL )
         return NULL;
 
-    ret = dst;
-
-    while( len > 0 )
+    while( i_src > 0 )
     {
         /* pops (up to) 3 bytes of input, push 4 bytes */
-        uint32_t v = *in++ << 24; // 1/3
-        *dst++ = b64[v >> 26]; // 1/4
+        uint32_t v;
+
+        /* 1/3 -> 1/4 */
+        v = *src++ << 24;
+        *dst++ = b64[v >> 26];
         v = v << 6;
 
-        if( len >= 2 )
-            v |= *in++ << 22; // 2/3
-        *dst++ = b64[v >> 26]; // 2/4
+        /* 2/3 -> 2/4 */
+        if( i_src >= 2 )
+            v |= *src++ << 22;
+        *dst++ = b64[v >> 26];
         v = v << 6;
 
-        if( len >= 3 )
-            v |= *in++ << 20; // 3/3
-        *dst++ = ( len >= 2 ) ? b64[v >> 26] : '='; // 3/4
+        /* 3/3 -> 3/4 */
+        if( i_src >= 3 )
+            v |= *src++ << 20; // 3/3
+        *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4
         v = v << 6;
 
-        *dst++ = ( len >= 3 ) ? b64[v >> 26] : '='; // 4/4
+        /* -> 4/4 */
+        *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4
 
-        len--;
-        if( len > 0 )
-        {
-            len--;
-            if( len > 0 )
-                len--;
-        }
+        if( i_src <= 3 )
+            break;
+        i_src -= 3;
     }
 
     *dst = '\0';
@@ -376,6 +520,93 @@ char *vlc_b64_encode( const char *src )
     return ret;
 }
 
+char *vlc_b64_encode( const char *src )
+{
+    if( src )
+        return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) );
+    else
+        return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
+}
+
+/* Base64 decoding */
+size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src )
+{
+    static const int b64[256] = {
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
+        52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
+        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
+        15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
+        -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
+        41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
+        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
+    };
+    uint8_t *p_start = p_dst;
+    uint8_t *p = (uint8_t *)p_src;
+
+    int i_level;
+    int i_last;
+
+    for( i_level = 0, i_last = 0; i_dst > 0 && *p != '\0'; i_dst--, p++ )
+    {
+        const int c = b64[(unsigned int)*p];
+        if( c == -1 )
+            continue;
+
+        switch( i_level )
+        {
+            case 0:
+                i_level++;
+                break;
+            case 1:
+                *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 );
+                i_level++;
+                break;
+            case 2:
+                *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
+                i_level++;
+                break;
+            case 3:
+                *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c;
+                i_level = 0;
+        }
+        i_last = c;
+    }
+
+    return p_dst - p_start;
+}
+size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
+{
+    const int i_src = strlen( psz_src );
+    uint8_t   *p_dst;
+
+    *pp_dst = p_dst = malloc( i_src );
+    if( !p_dst )
+        return 0;
+    return  vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
+}
+char *vlc_b64_decode( const char *psz_src )
+{
+    const int i_src = strlen( psz_src );
+    char *p_dst = malloc( i_src + 1 );
+    size_t i_dst;
+    if( !p_dst )
+        return NULL;
+
+    i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
+    p_dst[i_dst] = '\0';
+
+    return p_dst;
+}
+
 /****************************************************************************
  * String formating functions
  ****************************************************************************/
@@ -404,18 +635,39 @@ char *str_format_time( const char *tformat )
 }
 
 #define INSERT_STRING( check, string )                              \
+                    if( check )                                     \
+                    {                                               \
+                        psz_meta = string;                          \
+                        if( psz_meta )                              \
+                        {                                           \
+                            int len = strlen( string );             \
+                            dst = realloc( dst,                     \
+                                   i_size = i_size + len + 1 );     \
+                            strncpy( d, psz_meta, len+1 );          \
+                            d += len;                               \
+                        }                                           \
+                        else                                        \
+                        {                                           \
+                                *d = '-';                           \
+                                d++;                                \
+                        }                                           \
+                    }
+
+/* same than INSERT_STRING, except that string won't be freed */
+#define INSERT_STRING_NO_FREE( check, string )                           \
                     if( check && string )                           \
                     {                                               \
-                        int len = strlen( string );                 \
-                        dst = realloc( dst,                         \
-                                       i_size = i_size + len + 1 ); \
-                        strncpy( d, string, len+1 );                \
-                        d += len;                                   \
+                            int len = strlen( string );             \
+                            dst = realloc( dst,                     \
+                                   i_size = i_size + len + 1 );     \
+                            strncpy( d, string, len+1 );            \
+                            d += len;                               \
+                            free( string );                         \
                     }                                               \
                     else                                            \
                     {                                               \
-                        *d = '-';                                   \
-                        d++;                                        \
+                            *d = '-';                               \
+                            d++;                                    \
                     }
 char *__str_format_meta( vlc_object_t *p_object, const char *string )
 {
@@ -423,6 +675,7 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
     char *dst = malloc( 1000 );
     char *d = dst;
     int b_is_format = 0;
+    int b_empty_if_na = 0;
     char buf[10];
     int i_size = strlen( string );
 
@@ -434,8 +687,6 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
     {
         vlc_object_yield( p_input );
         p_item = input_GetItem(p_input);
-        if( p_item )
-            vlc_mutex_lock( &p_item->lock );
     }
 
     sprintf( dst, string );
@@ -446,45 +697,36 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
         {
             switch( *s )
             {
+                char *psz_meta; /* used by INSERT_STRING */
                 case 'a':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_artist );
+                    INSERT_STRING( p_item, input_item_GetArtist(p_item) );
                     break;
                 case 'b':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_album );
+                    INSERT_STRING( p_item, input_item_GetAlbum(p_item) );
                     break;
                 case 'c':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_copyright );
+                    INSERT_STRING( p_item, input_item_GetCopyright(p_item) );
                     break;
                 case 'd':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_description );
+                    INSERT_STRING( p_item, input_item_GetDescription(p_item) );
                     break;
                 case 'e':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_encodedby );
+                    INSERT_STRING( p_item, input_item_GetEncodedBy(p_item) );
                     break;
                 case 'g':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_genre );
+                    INSERT_STRING( p_item, input_item_GetGenre(p_item) );
                     break;
                 case 'l':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_language );
+                    INSERT_STRING( p_item, input_item_GetLanguage(p_item) );
                     break;
                 case 'n':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_tracknum );
+                    INSERT_STRING( p_item, input_item_GetTrackNum(p_item) );
                     break;
                 case 'p':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_nowplaying );
+                    INSERT_STRING( p_item, input_item_GetNowPlaying(p_item) );
                     break;
                 case 'r':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_rating );
+                    INSERT_STRING( p_item, input_item_GetRating(p_item) );
                     break;
                 case 's':
                 {
@@ -495,23 +737,19 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     }
                     else
                     {
-                        lang = strdup( "-" );
+                        lang = strdup( b_empty_if_na ? "" : "-" );
                     }
                     INSERT_STRING( 1, lang );
-                    free( lang );
                     break;
                 }
                 case 't':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_title );
+                    INSERT_STRING( p_item, input_item_GetTitle(p_item) );
                     break;
                 case 'u':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_url );
+                    INSERT_STRING( p_item, input_item_GetURL(p_item) );
                     break;
                 case 'A':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_date );
+                    INSERT_STRING( p_item, input_item_GetDate(p_item) );
                     break;
                 case 'B':
                     if( p_input )
@@ -521,9 +759,9 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     }
                     else
                     {
-                        sprintf( buf, "-" );
+                        sprintf( buf, b_empty_if_na ? "" : "-" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'C':
                     if( p_input )
@@ -533,26 +771,27 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     }
                     else
                     {
-                        sprintf( buf, "-" );
+                        sprintf( buf, b_empty_if_na ? "" : "-" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'D':
                     if( p_item )
                     {
+                        mtime_t i_duration = input_item_GetDuration( p_item );
                         sprintf( buf, "%02d:%02d:%02d",
-                                 (int)(p_item->i_duration/(3600000000)),
-                                 (int)((p_item->i_duration/(60000000))%60),
-                                 (int)((p_item->i_duration/1000000)%60) );
+                                 (int)(i_duration/(3600000000)),
+                                 (int)((i_duration/(60000000))%60),
+                                 (int)((i_duration/1000000)%60) );
                     }
                     else
                     {
-                        sprintf( buf, "--:--:--" );
+                        sprintf( buf, b_empty_if_na ? "" : "--:--:--" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'F':
-                    INSERT_STRING( p_item, p_item->psz_uri );
+                    INSERT_STRING( p_item, input_item_GetURI( p_item ) );
                     break;
                 case 'I':
                     if( p_input )
@@ -562,26 +801,28 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     }
                     else
                     {
-                        sprintf( buf, "-" );
+                        sprintf( buf, b_empty_if_na ? "" : "-" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'L':
                     if( p_item && p_input )
                     {
+                        mtime_t i_duration = input_item_GetDuration( p_item );
+                        int64_t i_time = p_input->i_time;
                         sprintf( buf, "%02d:%02d:%02d",
-                     (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
-                     (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
-                     (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
+                     (int)( ( i_duration - i_time ) / 3600000000 ),
+                     (int)( ( ( i_duration - i_time ) / 60000000 ) % 60 ),
+                     (int)( ( ( i_duration - i_time ) / 1000000 ) % 60 ) );
                     }
                     else
                     {
-                        sprintf( buf, "--:--:--" );
+                        sprintf( buf, b_empty_if_na ? "" : "--:--:--" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'N':
-                    INSERT_STRING( p_item, p_item->psz_name );
+                    INSERT_STRING( p_item, input_item_GetName( p_item ) );
                     break;
                 case 'O':
                 {
@@ -592,10 +833,9 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     }
                     else
                     {
-                        lang = strdup( "-" );
+                        lang = strdup( b_empty_if_na ? "" : "-" );
                     }
                     INSERT_STRING( 1, lang );
-                    free( lang );
                     break;
                 }
                 case 'P':
@@ -606,9 +846,9 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     }
                     else
                     {
-                        sprintf( buf, "--.-%%" );
+                        sprintf( buf, b_empty_if_na ? "" : "--.-%%" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'R':
                     if( p_input )
@@ -618,9 +858,9 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     }
                     else
                     {
-                        sprintf( buf, "-" );
+                        sprintf( buf, b_empty_if_na ? "" : "-" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'S':
                     if( p_input )
@@ -630,34 +870,33 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     }
                     else
                     {
-                        sprintf( buf, "-" );
+                        sprintf( buf, b_empty_if_na ? "" : "-" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'T':
                     if( p_input )
                     {
                         sprintf( buf, "%02d:%02d:%02d",
-                                 (int)(p_input->i_time/(3600000000)),
-                                 (int)((p_input->i_time/(60000000))%60),
-                                 (int)((p_input->i_time/1000000)%60) );
+                            (int)( p_input->i_time / ( 3600000000 ) ),
+                            (int)( ( p_input->i_time / ( 60000000 ) ) % 60 ),
+                            (int)( ( p_input->i_time / 1000000 ) % 60 ) );
                     }
                     else
                     {
-                        sprintf( buf, "--:--:--" );
+                        sprintf( buf, b_empty_if_na ? "" :  "--:--:--" );
                     }
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 case 'U':
-                    INSERT_STRING( p_item && p_item->p_meta,
-                                   p_item->p_meta->psz_publisher );
+                    INSERT_STRING( p_item, input_item_GetPublisher(p_item) );
                     break;
                 case 'V':
                 {
                     audio_volume_t volume;
                     aout_VolumeGet( p_object, &volume );
                     snprintf( buf, 10, "%d", volume );
-                    INSERT_STRING( 1, buf );
+                    INSERT_STRING_NO_FREE( 1, buf );
                     break;
                 }
                 case '_':
@@ -665,16 +904,22 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
                     d++;
                     break;
 
+                case ' ':
+                    b_empty_if_na = 1;
+                    break;
+
                 default:
                     *d = *s;
                     d++;
                     break;
             }
-            b_is_format = 0;
+            if( *s != ' ' )
+                b_is_format = 0;
         }
         else if( *s == '$' )
         {
             b_is_format = 1;
+            b_empty_if_na = 0;
         }
         else
         {
@@ -686,11 +931,7 @@ char *__str_format_meta( vlc_object_t *p_object, const char *string )
     *d = '\0';
 
     if( p_input )
-    {
         vlc_object_release( p_input );
-        if( p_item )
-            vlc_mutex_unlock( &p_item->lock );
-    }
 
     return dst;
 }
@@ -712,22 +953,30 @@ char *__str_format( vlc_object_t *p_this, const char *psz_src )
  */
 void filename_sanitize( char *str )
 {
+    if( *str == '.' && (str[1] == '\0' || (str[1] == '.' && str[2] == '\0' ) ) )
+    {
+        while( *str )
+        {
+            *str = '_';
+            str++;
+        }
+        return;
+    }
+
     while( *str )
     {
         switch( *str )
         {
             case '/':
 #ifdef WIN32
+            case '\\':
             case '*':
-            case '.':
             case '"':
-            case '\\':
-            case '[':
-            case ']':
+            case '?':
             case ':':
-            case ';':
             case '|':
-            case '=':
+            case '<':
+            case '>':
 #endif
                 *str = '_';
         }
@@ -740,23 +989,52 @@ void filename_sanitize( char *str )
  */
 void path_sanitize( char *str )
 {
+#if 0
+    /*
+     * Uncomment the two blocks to prevent /../ or /./, i'm not sure that we
+     * want to.
+     */
+    char *prev = str - 1;
+#endif
+#ifdef WIN32
+    /* check drive prefix if path is absolute */
+    if( isalpha(*str) && (':' == *(str+1)) )
+        str += 2;
+#endif
     while( *str )
     {
+#ifdef WIN32
         switch( *str )
         {
-#ifdef WIN32
             case '*':
-            case '.':
             case '"':
-            case '[':
-            case ']':
+            case '?':
             case ':':
-            case ';':
             case '|':
-            case '=':
-#endif
+            case '<':
+            case '>':
                 *str = '_';
         }
+#endif
+#if 0
+        if( *str == '/'
+#ifdef WIN32
+            || *str == '\\'
+#endif
+            )
+        {
+            if( str - prev == 2 && prev[1] == '.' )
+            {
+                prev[1] = '.';
+            }
+            else if( str - prev == 3 && prev[1] == '.' && prev[2] == '.' )
+            {
+                prev[1] = '_';
+                prev[2] = '_';
+            }
+            prev = str;
+        }
+#endif
         str++;
     }
 }