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