]> git.sesse.net Git - vlc/blob - src/text/strings.c
stream: add STREAM_GET_META
[vlc] / src / text / strings.c
1 /*****************************************************************************
2  * strings.c: String related functions
3  *****************************************************************************
4  * Copyright (C) 2006 VLC authors and VideoLAN
5  * Copyright (C) 2008-2009 Rémi Denis-Courmont
6  * $Id$
7  *
8  * Authors: Antoine Cellerier <dionoea at videolan dot org>
9  *          Daniel Stranger <vlc at schmaller dot de>
10  *          Rémi Denis-Courmont <rem # videolan org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35 #include <assert.h>
36
37 /* Needed by str_format_time */
38 #include <time.h>
39 #include <limits.h>
40 #include <math.h>
41
42 /* Needed by str_format_meta */
43 #include <vlc_input.h>
44 #include <vlc_meta.h>
45 #include <vlc_playlist.h>
46
47 #include <vlc_strings.h>
48 #include <vlc_charset.h>
49 #include <vlc_fs.h>
50 #include <libvlc.h>
51 #include <errno.h>
52
53 static const struct xml_entity_s
54 {
55     char    psz_entity[8];
56     char    psz_char[4];
57 } xml_entities[] = {
58     /* Important: this list has to be in alphabetical order (psz_entity-wise) */
59     { "AElig;",  "Æ" },
60     { "Aacute;", "Á" },
61     { "Acirc;",  "Â" },
62     { "Agrave;", "À" },
63     { "Aring;",  "Å" },
64     { "Atilde;", "Ã" },
65     { "Auml;",   "Ä" },
66     { "Ccedil;", "Ç" },
67     { "Dagger;", "‡" },
68     { "ETH;",    "Ð" },
69     { "Eacute;", "É" },
70     { "Ecirc;",  "Ê" },
71     { "Egrave;", "È" },
72     { "Euml;",   "Ë" },
73     { "Iacute;", "Í" },
74     { "Icirc;",  "Î" },
75     { "Igrave;", "Ì" },
76     { "Iuml;",   "Ï" },
77     { "Ntilde;", "Ñ" },
78     { "OElig;",  "Œ" },
79     { "Oacute;", "Ó" },
80     { "Ocirc;",  "Ô" },
81     { "Ograve;", "Ò" },
82     { "Oslash;", "Ø" },
83     { "Otilde;", "Õ" },
84     { "Ouml;",   "Ö" },
85     { "Scaron;", "Š" },
86     { "THORN;",  "Þ" },
87     { "Uacute;", "Ú" },
88     { "Ucirc;",  "Û" },
89     { "Ugrave;", "Ù" },
90     { "Uuml;",   "Ü" },
91     { "Yacute;", "Ý" },
92     { "Yuml;",   "Ÿ" },
93     { "aacute;", "á" },
94     { "acirc;",  "â" },
95     { "acute;",  "´" },
96     { "aelig;",  "æ" },
97     { "agrave;", "à" },
98     { "amp;",    "&" },
99     { "apos;",   "'" },
100     { "aring;",  "å" },
101     { "atilde;", "ã" },
102     { "auml;",   "ä" },
103     { "bdquo;",  "„" },
104     { "brvbar;", "¦" },
105     { "ccedil;", "ç" },
106     { "cedil;",  "¸" },
107     { "cent;",   "¢" },
108     { "circ;",   "ˆ" },
109     { "copy;",   "©" },
110     { "curren;", "¤" },
111     { "dagger;", "†" },
112     { "deg;",    "°" },
113     { "divide;", "÷" },
114     { "eacute;", "é" },
115     { "ecirc;",  "ê" },
116     { "egrave;", "è" },
117     { "eth;",    "ð" },
118     { "euml;",   "ë" },
119     { "euro;",   "€" },
120     { "frac12;", "½" },
121     { "frac14;", "¼" },
122     { "frac34;", "¾" },
123     { "gt;",     ">" },
124     { "hellip;", "…" },
125     { "iacute;", "í" },
126     { "icirc;",  "î" },
127     { "iexcl;",  "¡" },
128     { "igrave;", "ì" },
129     { "iquest;", "¿" },
130     { "iuml;",   "ï" },
131     { "laquo;",  "«" },
132     { "ldquo;",  "“" },
133     { "lsaquo;", "‹" },
134     { "lsquo;",  "‘" },
135     { "lt;",     "<" },
136     { "macr;",   "¯" },
137     { "mdash;",  "—" },
138     { "micro;",  "µ" },
139     { "middot;", "·" },
140     { "nbsp;",   "\xc2\xa0" },
141     { "ndash;",  "–" },
142     { "not;",    "¬" },
143     { "ntilde;", "ñ" },
144     { "oacute;", "ó" },
145     { "ocirc;",  "ô" },
146     { "oelig;",  "œ" },
147     { "ograve;", "ò" },
148     { "ordf;",   "ª" },
149     { "ordm;",   "º" },
150     { "oslash;", "ø" },
151     { "otilde;", "õ" },
152     { "ouml;",   "ö" },
153     { "para;",   "¶" },
154     { "permil;", "‰" },
155     { "plusmn;", "±" },
156     { "pound;",  "£" },
157     { "quot;",   "\"" },
158     { "raquo;",  "»" },
159     { "rdquo;",  "”" },
160     { "reg;",    "®" },
161     { "rsaquo;", "›" },
162     { "rsquo;",  "’" },
163     { "sbquo;",  "‚" },
164     { "scaron;", "š" },
165     { "sect;",   "§" },
166     { "shy;",    "­" },
167     { "sup1;",   "¹" },
168     { "sup2;",   "²" },
169     { "sup3;",   "³" },
170     { "szlig;",  "ß" },
171     { "thorn;",  "þ" },
172     { "tilde;",  "˜" },
173     { "times;",  "×" },
174     { "trade;",  "™" },
175     { "uacute;", "ú" },
176     { "ucirc;",  "û" },
177     { "ugrave;", "ù" },
178     { "uml;",    "¨" },
179     { "uuml;",   "ü" },
180     { "yacute;", "ý" },
181     { "yen;",    "¥" },
182     { "yuml;",   "ÿ" },
183 };
184
185 static int cmp_entity (const void *key, const void *elem)
186 {
187     const struct xml_entity_s *ent = elem;
188     const char *name = key;
189
190     return strncmp (name, ent->psz_entity, strlen (ent->psz_entity));
191 }
192
193 /**
194  * Converts "&lt;", "&gt;" and "&amp;" to "<", ">" and "&"
195  * \param string to convert
196  */
197 void resolve_xml_special_chars( char *psz_value )
198 {
199     char *p_pos = psz_value;
200
201     while ( *psz_value )
202     {
203         if( *psz_value == '&' )
204         {
205             if( psz_value[1] == '#' )
206             {   /* &#xxx; Unicode code point */
207                 char *psz_end;
208                 unsigned long cp = strtoul( psz_value+2, &psz_end, 10 );
209                 if( *psz_end == ';' )
210                 {
211                     psz_value = psz_end + 1;
212                     if( cp == 0 )
213                         (void)0; /* skip nuls */
214                     else
215                     if( cp <= 0x7F )
216                     {
217                         *p_pos =            cp;
218                     }
219                     else
220                     /* Unicode code point outside ASCII.
221                      * &#xxx; representation is longer than UTF-8 :) */
222                     if( cp <= 0x7FF )
223                     {
224                         *p_pos++ = 0xC0 |  (cp >>  6);
225                         *p_pos   = 0x80 |  (cp        & 0x3F);
226                     }
227                     else
228                     if( cp <= 0xFFFF )
229                     {
230                         *p_pos++ = 0xE0 |  (cp >> 12);
231                         *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
232                         *p_pos   = 0x80 |  (cp        & 0x3F);
233                     }
234                     else
235                     if( cp <= 0x1FFFFF ) /* Outside the BMP */
236                     {   /* Unicode stops at 10FFFF, but who cares? */
237                         *p_pos++ = 0xF0 |  (cp >> 18);
238                         *p_pos++ = 0x80 | ((cp >> 12) & 0x3F);
239                         *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
240                         *p_pos   = 0x80 |  (cp        & 0x3F);
241                     }
242                 }
243                 else
244                 {
245                     /* Invalid entity number */
246                     *p_pos = *psz_value;
247                     psz_value++;
248                 }
249             }
250             else
251             {   /* Well-known XML entity */
252                 const struct xml_entity_s *ent;
253
254                 ent = bsearch (psz_value + 1, xml_entities,
255                                sizeof (xml_entities) / sizeof (*ent),
256                                sizeof (*ent), cmp_entity);
257                 if (ent != NULL)
258                 {
259                     size_t olen = strlen (ent->psz_char);
260                     memcpy (p_pos, ent->psz_char, olen);
261                     p_pos += olen - 1;
262                     psz_value += strlen (ent->psz_entity) + 1;
263                 }
264                 else
265                 {   /* No match */
266                     *p_pos = *psz_value;
267                     psz_value++;
268                 }
269             }
270         }
271         else
272         {
273             *p_pos = *psz_value;
274             psz_value++;
275         }
276
277         p_pos++;
278     }
279
280     *p_pos = '\0';
281 }
282
283 /**
284  * XML-encode an UTF-8 string
285  * \param str nul-terminated UTF-8 byte sequence to XML-encode
286  * \return XML encoded string or NULL on error
287  * (errno is set to ENOMEM or EILSEQ as appropriate)
288  */
289 char *convert_xml_special_chars (const char *str)
290 {
291     assert (str != NULL);
292
293     const size_t len = strlen (str);
294     char *const buf = malloc (6 * len + 1), *ptr = buf;
295     if (unlikely(buf == NULL))
296         return NULL;
297
298     size_t n;
299     uint32_t cp;
300
301     while ((n = vlc_towc (str, &cp)) != 0)
302     {
303         if (unlikely(n == (size_t)-1))
304         {
305             free (buf);
306             errno = EILSEQ;
307             return NULL;
308         }
309
310         if ((cp & ~0x0080) < 32 /* C0/C1 control codes */
311          && memchr ("\x09\x0A\x0D\x85", cp, 4) == NULL)
312             ptr += sprintf (ptr, "&#%"PRIu32";", cp);
313         else
314         switch (cp)
315         {
316             case '\"': strcpy (ptr, "&quot;"); ptr += 6; break;
317             case '&':  strcpy (ptr, "&amp;");  ptr += 5; break;
318             case '\'': strcpy (ptr, "&#39;");  ptr += 5; break;
319             case '<':  strcpy (ptr, "&lt;");   ptr += 4; break;
320             case '>':  strcpy (ptr, "&gt;");   ptr += 4; break;
321             default:   memcpy (ptr, str, n);   ptr += n; break;
322         }
323         str += n;
324     }
325     *(ptr++) = '\0';
326
327     ptr = realloc (buf, ptr - buf);
328     return likely(ptr != NULL) ? ptr : buf; /* cannot fail */
329 }
330
331 /* Base64 encoding */
332 char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src )
333 {
334     static const char b64[] =
335            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
336
337     char *ret = malloc( ( i_src + 4 ) * 4 / 3 );
338     char *dst = ret;
339
340     if( dst == NULL )
341         return NULL;
342
343     while( i_src > 0 )
344     {
345         /* pops (up to) 3 bytes of input, push 4 bytes */
346         uint32_t v;
347
348         /* 1/3 -> 1/4 */
349         v = *src++ << 24;
350         *dst++ = b64[v >> 26];
351         v = v << 6;
352
353         /* 2/3 -> 2/4 */
354         if( i_src >= 2 )
355             v |= *src++ << 22;
356         *dst++ = b64[v >> 26];
357         v = v << 6;
358
359         /* 3/3 -> 3/4 */
360         if( i_src >= 3 )
361             v |= *src++ << 20; // 3/3
362         *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4
363         v = v << 6;
364
365         /* -> 4/4 */
366         *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4
367
368         if( i_src <= 3 )
369             break;
370         i_src -= 3;
371     }
372
373     *dst = '\0';
374
375     return ret;
376 }
377
378 char *vlc_b64_encode( const char *src )
379 {
380     if( src )
381         return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) );
382     else
383         return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
384 }
385
386 /* Base64 decoding */
387 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src )
388 {
389     static const int b64[256] = {
390         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
391         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
392         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
393         52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
394         -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
395         15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
396         -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
397         41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
398         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
399         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
400         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
401         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
402         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
403         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
404         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
405         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
406     };
407     uint8_t *p_start = p_dst;
408     uint8_t *p = (uint8_t *)p_src;
409
410     int i_level;
411     int i_last;
412
413     for( i_level = 0, i_last = 0; (size_t)( p_dst - p_start ) < i_dst && *p != '\0'; p++ )
414     {
415         const int c = b64[(unsigned int)*p];
416         if( c == -1 )
417             break;
418
419         switch( i_level )
420         {
421             case 0:
422                 i_level++;
423                 break;
424             case 1:
425                 *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 );
426                 i_level++;
427                 break;
428             case 2:
429                 *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
430                 i_level++;
431                 break;
432             case 3:
433                 *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c;
434                 i_level = 0;
435         }
436         i_last = c;
437     }
438
439     return p_dst - p_start;
440 }
441 size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
442 {
443     const int i_src = strlen( psz_src );
444     uint8_t   *p_dst;
445
446     *pp_dst = p_dst = malloc( i_src );
447     if( !p_dst )
448         return 0;
449     return  vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
450 }
451 char *vlc_b64_decode( const char *psz_src )
452 {
453     const int i_src = strlen( psz_src );
454     char *p_dst = malloc( i_src + 1 );
455     size_t i_dst;
456     if( !p_dst )
457         return NULL;
458
459     i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
460     p_dst[i_dst] = '\0';
461
462     return p_dst;
463 }
464
465 /**
466  * Formats current time into a heap-allocated string.
467  * @param tformat time format (as with C strftime())
468  * @return an allocated string (must be free()'d), or NULL on memory error.
469  */
470 char *str_format_time( const char *tformat )
471 {
472     time_t curtime;
473     struct tm loctime;
474
475     if (strcmp (tformat, "") == 0)
476         return strdup (""); /* corner case w.r.t. strftime() return value */
477
478     /* Get the current time.  */
479     time( &curtime );
480
481     /* Convert it to local time representation.  */
482     localtime_r( &curtime, &loctime );
483     for (size_t buflen = strlen (tformat) + 32;; buflen += 32)
484     {
485         char *str = malloc (buflen);
486         if (str == NULL)
487             return NULL;
488
489         size_t len = strftime (str, buflen, tformat, &loctime);
490         if (len > 0)
491         {
492             char *ret = realloc (str, len + 1);
493             return ret ? ret : str; /* <- this cannot fail */
494         }
495     }
496     assert (0);
497 }
498
499 static void format_duration (char *buf, size_t len, int64_t duration)
500 {
501     lldiv_t d;
502     int sec;
503
504     duration /= CLOCK_FREQ;
505     d = lldiv (duration, 60);
506     sec = d.rem;
507     d = lldiv (d.quot, 60);
508     snprintf (buf, len, "%02lld:%02d:%02d", d.quot, (int)d.rem, sec);
509 }
510
511 #define INSERT_STRING( string )                                     \
512                     if( string != NULL )                            \
513                     {                                               \
514                         int len = strlen( string );                 \
515                         dst = xrealloc( dst, i_size = i_size + len );\
516                         memcpy( (dst+d), string, len );             \
517                         d += len;                                   \
518                         free( string );                             \
519                     }
520
521 /* same than INSERT_STRING, except that string won't be freed */
522 #define INSERT_STRING_NO_FREE( string )                             \
523                     {                                               \
524                         int len = strlen( string );                 \
525                         dst = xrealloc( dst, i_size = i_size + len );\
526                         memcpy( dst+d, string, len );               \
527                         d += len;                                   \
528                     }
529 char *str_format_meta( playlist_t *p_object, const char *string )
530 {
531     const char *s = string;
532     bool b_is_format = false;
533     bool b_empty_if_na = false;
534     char buf[10];
535     int i_size = strlen( string ) + 1; /* +1 to store '\0' */
536     char *dst = strdup( string );
537     if( !dst ) return NULL;
538     int d = 0;
539
540     input_thread_t *p_input = playlist_CurrentInput( p_object );
541     input_item_t *p_item = NULL;
542     if( p_input )
543     {
544         p_item = input_GetItem(p_input);
545     }
546
547     while( *s )
548     {
549         if( b_is_format )
550         {
551             switch( *s )
552             {
553                 case 'a':
554                     if( p_item )
555                     {
556                         INSERT_STRING( input_item_GetArtist( p_item ) );
557                     }
558                     break;
559                 case 'b':
560                     if( p_item )
561                     {
562                         INSERT_STRING( input_item_GetAlbum( p_item ) );
563                     }
564                     break;
565                 case 'c':
566                     if( p_item )
567                     {
568                         INSERT_STRING( input_item_GetCopyright( p_item ) );
569                     }
570                     break;
571                 case 'd':
572                     if( p_item )
573                     {
574                         INSERT_STRING( input_item_GetDescription( p_item ) );
575                     }
576                     break;
577                 case 'e':
578                     if( p_item )
579                     {
580                         INSERT_STRING( input_item_GetEncodedBy( p_item ) );
581                     }
582                     break;
583                 case 'f':
584                     if( p_item && p_item->p_stats )
585                     {
586                         vlc_mutex_lock( &p_item->p_stats->lock );
587                         snprintf( buf, 10, "%"PRIi64,
588                                   p_item->p_stats->i_displayed_pictures );
589                         vlc_mutex_unlock( &p_item->p_stats->lock );
590                     }
591                     else
592                         strcpy( buf, b_empty_if_na ? "" : "-" );
593                     INSERT_STRING_NO_FREE( buf );
594                     break;
595                 case 'g':
596                     if( p_item )
597                     {
598                         INSERT_STRING( input_item_GetGenre( p_item ) );
599                     }
600                     break;
601                 case 'l':
602                     if( p_item )
603                     {
604                         INSERT_STRING( input_item_GetLanguage( p_item ) );
605                     }
606                     break;
607                 case 'n':
608                     if( p_item )
609                     {
610                         INSERT_STRING( input_item_GetTrackNum( p_item ) );
611                     }
612                     break;
613                 case 'p':
614                     if( p_item )
615                     {
616                         INSERT_STRING( input_item_GetNowPlaying( p_item ) );
617                     }
618                     break;
619                 case 'r':
620                     if( p_item )
621                     {
622                         INSERT_STRING( input_item_GetRating( p_item ) );
623                     }
624                     break;
625                 case 's':
626                     {
627                         char *psz_lang = NULL;
628                         if( p_input )
629                             psz_lang = var_GetNonEmptyString( p_input, "sub-language" );
630                         if( psz_lang == NULL )
631                             psz_lang = strdup( b_empty_if_na ? "" : "-" );
632                         INSERT_STRING( psz_lang );
633                         break;
634                     }
635                 case 't':
636                     if( p_item )
637                     {
638                         INSERT_STRING( input_item_GetTitle( p_item ) );
639                     }
640                     break;
641                 case 'u':
642                     if( p_item )
643                     {
644                         INSERT_STRING( input_item_GetURL( p_item ) );
645                     }
646                     break;
647                 case 'A':
648                     if( p_item )
649                     {
650                         INSERT_STRING( input_item_GetDate( p_item ) );
651                     }
652                     break;
653                 case 'B':
654                     if( p_input )
655                     {
656                         snprintf( buf, 10, "%"PRId64,
657                                   var_GetInteger( p_input, "bit-rate" )/1000 );
658                     }
659                     else
660                         strcpy( buf, b_empty_if_na ? "" : "-" );
661                     INSERT_STRING_NO_FREE( buf );
662                     break;
663                 case 'C':
664                     if( p_input )
665                     {
666                         snprintf( buf, 10, "%"PRId64,
667                                   var_GetInteger( p_input, "chapter" ) );
668                     }
669                     else
670                         strcpy( buf, b_empty_if_na ? "" : "-" );
671                     INSERT_STRING_NO_FREE( buf );
672                     break;
673                 case 'D':
674                     if( p_item )
675                     {
676                         mtime_t i_duration = input_item_GetDuration( p_item );
677                         format_duration (buf, sizeof (buf), i_duration);
678                     }
679                     else
680                         strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
681                     INSERT_STRING_NO_FREE( buf );
682                     break;
683                 case 'F':
684                     if( p_item )
685                     {
686                         INSERT_STRING( input_item_GetURI( p_item ) );
687                     }
688                     break;
689                 case 'I':
690                     if( p_input )
691                     {
692                         snprintf( buf, 10, "%"PRId64,
693                                   var_GetInteger( p_input, "title" ) );
694                     }
695                     else
696                         strcpy( buf, b_empty_if_na ? "" : "-" );
697                     INSERT_STRING_NO_FREE( buf );
698                     break;
699                 case 'L':
700                     if( p_item && p_input )
701                     {
702                         mtime_t i_duration = input_item_GetDuration( p_item );
703                         int64_t i_time = var_GetTime( p_input, "time" );
704                         format_duration( buf, sizeof(buf),
705                                          i_duration - i_time );
706                     }
707                     else
708                         strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
709                     INSERT_STRING_NO_FREE( buf );
710                     break;
711                 case 'N':
712                     if( p_item )
713                     {
714                         INSERT_STRING( input_item_GetName( p_item ) );
715                     }
716                     break;
717                 case 'O':
718                     {
719                         char *lang = NULL;
720                         if( p_input )
721                             lang = var_GetNonEmptyString( p_input,
722                                                           "audio-language" );
723                         if( lang == NULL )
724                             lang = strdup( b_empty_if_na ? "" : "-" );
725                         INSERT_STRING( lang );
726                         break;
727                     }
728                 case 'P':
729                     if( p_input )
730                     {
731                         snprintf( buf, 10, "%2.1lf",
732                                   var_GetFloat( p_input, "position" ) * 100. );
733                     }
734                     else
735                     {
736                         snprintf( buf, 10, b_empty_if_na ? "" : "--.-%%" );
737                     }
738                     INSERT_STRING_NO_FREE( buf );
739                     break;
740                 case 'R':
741                     if( p_input )
742                     {
743                         float f = var_GetFloat( p_input, "rate" );
744                         snprintf( buf, 10, "%.3f", f );
745                     }
746                     else
747                         strcpy( buf, b_empty_if_na ? "" : "-" );
748                     INSERT_STRING_NO_FREE( buf );
749                     break;
750                 case 'S':
751                     if( p_input )
752                     {
753                         int r = var_GetInteger( p_input, "sample-rate" );
754                         snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
755                     }
756                     else
757                         strcpy( buf, b_empty_if_na ? "" : "-" );
758                     INSERT_STRING_NO_FREE( buf );
759                     break;
760                 case 'T':
761                     if( p_input )
762                     {
763                         int64_t i_time = var_GetTime( p_input, "time" );
764                         format_duration( buf, sizeof(buf), i_time );
765                     }
766                     else
767                         strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
768                     INSERT_STRING_NO_FREE( buf );
769                     break;
770                 case 'U':
771                     if( p_item )
772                     {
773                         INSERT_STRING( input_item_GetPublisher( p_item ) );
774                     }
775                     break;
776                 case 'V':
777                 {
778                     float vol = playlist_VolumeGet( p_object );
779                     if( vol >= 0. )
780                     {
781                         snprintf( buf, 10, "%ld",
782                                   lroundf(vol * AOUT_VOLUME_DEFAULT ) );
783                         INSERT_STRING_NO_FREE( buf );
784                     }
785                     else
786                          INSERT_STRING_NO_FREE( "---" );
787                     break;
788                 }
789                 case '_':
790                     *(dst+d) = '\n';
791                     d++;
792                     break;
793                 case 'Z':
794                     if( p_item )
795                     {
796                         char *psz_now_playing = input_item_GetNowPlaying( p_item );
797                         if( EMPTY_STR( psz_now_playing ) )
798                         {
799                             char *psz_temp = input_item_GetTitleFbName( p_item );
800                             char *psz_artist = input_item_GetArtist( p_item );
801                             if( !EMPTY_STR( psz_artist ) )
802                             {
803                                 INSERT_STRING( psz_artist );
804                                 if ( !EMPTY_STR( psz_temp ) )
805                                     INSERT_STRING_NO_FREE( " - " );
806                             }
807                             INSERT_STRING( psz_temp );
808                         }
809                         else
810                             INSERT_STRING( psz_now_playing );
811                     }
812                     break;
813
814                 case ' ':
815                     b_empty_if_na = true;
816                     break;
817
818                 default:
819                     *(dst+d) = *s;
820                     d++;
821                     break;
822             }
823             if( *s != ' ' )
824                 b_is_format = false;
825         }
826         else if( *s == '$' )
827         {
828             b_is_format = true;
829             b_empty_if_na = false;
830         }
831         else
832         {
833             *(dst+d) = *s;
834             d++;
835         }
836         s++;
837     }
838     *(dst+d) = '\0';
839
840     if( p_input )
841         vlc_object_release( p_input );
842
843     return dst;
844 }
845 #undef INSERT_STRING
846 #undef INSERT_STRING_NO_FREE
847
848 /**
849  * Remove forbidden, potentially forbidden and otherwise evil characters from
850  * filenames. This includes slashes, and popular characters like colon
851  * (on Unix anyway), so this should only be used for automatically generated
852  * filenames.
853  * \warning Do not use this on full paths,
854  * only single file names without any directory separator!
855  */
856 void filename_sanitize( char *str )
857 {
858     unsigned char c;
859
860     /* Special file names, not allowed */
861     if( !strcmp( str, "." ) || !strcmp( str, ".." ) )
862     {
863         while( *str )
864             *(str++) = '_';
865         return;
866     }
867
868     /* On platforms not using UTF-7, VLC cannot access non-Unicode paths.
869      * Also, some file systems require Unicode file names.
870      * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
871     EnsureUTF8( str );
872
873     /* Avoid leading spaces to please Windows. */
874     while( (c = *str) != '\0' )
875     {
876         if( c != ' ' )
877             break;
878         *(str++) = '_';
879     }
880
881     char *start = str;
882
883     while( (c = *str) != '\0' )
884     {
885         /* Non-printable characters are not a good idea */
886         if( c < 32 )
887             *str = '_';
888         /* This is the list of characters not allowed by Microsoft.
889          * We also black-list them on Unix as they may be confusing, and are
890          * not supported by some file system types (notably CIFS). */
891         else if( strchr( "/:\\*\"?|<>", c ) != NULL )
892             *str = '_';
893         str++;
894     }
895
896     /* Avoid trailing spaces also to please Windows. */
897     while( str > start )
898     {
899         if( *(--str) != ' ' )
900             break;
901         *str = '_';
902     }
903 }
904
905 /**
906  * Remove forbidden characters from full paths (leaves slashes)
907  */
908 void path_sanitize( char *str )
909 {
910 #if defined( WIN32 ) || defined( __OS2__ )
911     /* check drive prefix if path is absolute */
912     if( (((unsigned char)(str[0] - 'A') < 26)
913       || ((unsigned char)(str[0] - 'a') < 26)) && (':' == str[1]) )
914         str += 2;
915 #endif
916     while( *str )
917     {
918 #if defined( __APPLE__ )
919         if( *str == ':' )
920             *str = '_';
921 #elif defined( WIN32 ) || defined( __OS2__ )
922         if( strchr( "*\"?:|<>", *str ) )
923             *str = '_';
924         if( *str == '/' )
925             *str = DIR_SEP_CHAR;
926 #endif
927         str++;
928     }
929 }
930
931 /*
932   Decodes a duration as defined by ISO 8601
933   http://en.wikipedia.org/wiki/ISO_8601#Durations
934   @param str A null-terminated string to convert
935   @return: The duration in seconds. -1 if an error occurred.
936
937   Exemple input string: "PT0H9M56.46S"
938  */
939 time_t str_duration( const char *psz_duration )
940 {
941     bool        timeDesignatorReached = false;
942     time_t      res = 0;
943     char*       end_ptr;
944
945     if ( psz_duration == NULL )
946         return -1;
947     if ( ( *(psz_duration++) ) != 'P' )
948         return -1;
949     do
950     {
951         double number = strtod( psz_duration, &end_ptr );
952         double      mul = 0;
953         if ( psz_duration != end_ptr )
954             psz_duration = end_ptr;
955         switch( *psz_duration )
956         {
957             case 'M':
958             {
959                 //M can mean month or minutes, if the 'T' flag has been reached.
960                 //We don't handle months though.
961                 if ( timeDesignatorReached == true )
962                     mul = 60.0;
963                 break ;
964             }
965             case 'Y':
966             case 'W':
967                 break ; //Don't handle this duration.
968             case 'D':
969                 mul = 86400.0;
970                 break ;
971             case 'T':
972                 timeDesignatorReached = true;
973                 break ;
974             case 'H':
975                 mul = 3600.0;
976                 break ;
977             case 'S':
978                 mul = 1.0;
979                 break ;
980             default:
981                 break ;
982         }
983         res += (time_t)(mul * number);
984         if ( *psz_duration )
985             psz_duration++;
986     } while ( *psz_duration );
987     return res;
988 }