]> git.sesse.net Git - vlc/blob - src/text/strings.c
dsm: change item b_net and i_type
[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             {   /* &#DDD; or &#xHHHH; Unicode code point */
206                 char *psz_end;
207                 unsigned long cp;
208
209                 if( psz_value[2] == 'x' ) /* The x must be lower-case. */
210                     cp = strtoul( psz_value + 3, &psz_end, 16 );
211                 else
212                     cp = strtoul( psz_value + 2, &psz_end, 10 );
213
214                 if( *psz_end == ';' )
215                 {
216                     psz_value = psz_end + 1;
217                     if( cp == 0 )
218                         (void)0; /* skip nuls */
219                     else
220                     if( cp <= 0x7F )
221                     {
222                         *p_pos =            cp;
223                     }
224                     else
225                     /* Unicode code point outside ASCII.
226                      * &#xxx; representation is longer than UTF-8 :) */
227                     if( cp <= 0x7FF )
228                     {
229                         *p_pos++ = 0xC0 |  (cp >>  6);
230                         *p_pos   = 0x80 |  (cp        & 0x3F);
231                     }
232                     else
233                     if( cp <= 0xFFFF )
234                     {
235                         *p_pos++ = 0xE0 |  (cp >> 12);
236                         *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
237                         *p_pos   = 0x80 |  (cp        & 0x3F);
238                     }
239                     else
240                     if( cp <= 0x1FFFFF ) /* Outside the BMP */
241                     {   /* Unicode stops at 10FFFF, but who cares? */
242                         *p_pos++ = 0xF0 |  (cp >> 18);
243                         *p_pos++ = 0x80 | ((cp >> 12) & 0x3F);
244                         *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
245                         *p_pos   = 0x80 |  (cp        & 0x3F);
246                     }
247                 }
248                 else
249                 {
250                     /* Invalid entity number */
251                     *p_pos = *psz_value;
252                     psz_value++;
253                 }
254             }
255             else
256             {   /* Well-known XML entity */
257                 const struct xml_entity_s *ent;
258
259                 ent = bsearch (psz_value + 1, xml_entities,
260                                sizeof (xml_entities) / sizeof (*ent),
261                                sizeof (*ent), cmp_entity);
262                 if (ent != NULL)
263                 {
264                     size_t olen = strlen (ent->psz_char);
265                     memcpy (p_pos, ent->psz_char, olen);
266                     p_pos += olen - 1;
267                     psz_value += strlen (ent->psz_entity) + 1;
268                 }
269                 else
270                 {   /* No match */
271                     *p_pos = *psz_value;
272                     psz_value++;
273                 }
274             }
275         }
276         else
277         {
278             *p_pos = *psz_value;
279             psz_value++;
280         }
281
282         p_pos++;
283     }
284
285     *p_pos = '\0';
286 }
287
288 /**
289  * XML-encode an UTF-8 string
290  * \param str nul-terminated UTF-8 byte sequence to XML-encode
291  * \return XML encoded string or NULL on error
292  * (errno is set to ENOMEM or EILSEQ as appropriate)
293  */
294 char *convert_xml_special_chars (const char *str)
295 {
296     assert (str != NULL);
297
298     const size_t len = strlen (str);
299     char *const buf = malloc (6 * len + 1), *ptr = buf;
300     if (unlikely(buf == NULL))
301         return NULL;
302
303     size_t n;
304     uint32_t cp;
305
306     while ((n = vlc_towc (str, &cp)) != 0)
307     {
308         if (unlikely(n == (size_t)-1))
309         {
310             free (buf);
311             errno = EILSEQ;
312             return NULL;
313         }
314
315         if ((cp & ~0x0080) < 32 /* C0/C1 control codes */
316          && memchr ("\x09\x0A\x0D\x85", cp, 4) == NULL)
317             ptr += sprintf (ptr, "&#%"PRIu32";", cp);
318         else
319         switch (cp)
320         {
321             case '\"': strcpy (ptr, "&quot;"); ptr += 6; break;
322             case '&':  strcpy (ptr, "&amp;");  ptr += 5; break;
323             case '\'': strcpy (ptr, "&#39;");  ptr += 5; break;
324             case '<':  strcpy (ptr, "&lt;");   ptr += 4; break;
325             case '>':  strcpy (ptr, "&gt;");   ptr += 4; break;
326             default:   memcpy (ptr, str, n);   ptr += n; break;
327         }
328         str += n;
329     }
330     *(ptr++) = '\0';
331
332     ptr = realloc (buf, ptr - buf);
333     return likely(ptr != NULL) ? ptr : buf; /* cannot fail */
334 }
335
336 /* Base64 encoding */
337 char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src )
338 {
339     static const char b64[] =
340            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
341
342     char *ret = malloc( ( i_src + 4 ) * 4 / 3 );
343     char *dst = ret;
344
345     if( dst == NULL )
346         return NULL;
347
348     while( i_src > 0 )
349     {
350         /* pops (up to) 3 bytes of input, push 4 bytes */
351         uint32_t v;
352
353         /* 1/3 -> 1/4 */
354         v = *src++ << 24;
355         *dst++ = b64[v >> 26];
356         v = v << 6;
357
358         /* 2/3 -> 2/4 */
359         if( i_src >= 2 )
360             v |= *src++ << 22;
361         *dst++ = b64[v >> 26];
362         v = v << 6;
363
364         /* 3/3 -> 3/4 */
365         if( i_src >= 3 )
366             v |= *src++ << 20; // 3/3
367         *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4
368         v = v << 6;
369
370         /* -> 4/4 */
371         *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4
372
373         if( i_src <= 3 )
374             break;
375         i_src -= 3;
376     }
377
378     *dst = '\0';
379
380     return ret;
381 }
382
383 char *vlc_b64_encode( const char *src )
384 {
385     if( src )
386         return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) );
387     else
388         return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
389 }
390
391 /* Base64 decoding */
392 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src )
393 {
394     static const int b64[256] = {
395         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
396         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
397         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
398         52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
399         -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
400         15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
401         -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
402         41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
403         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
404         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
405         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
406         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
407         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
408         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
409         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
410         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
411     };
412     uint8_t *p_start = p_dst;
413     uint8_t *p = (uint8_t *)p_src;
414
415     int i_level;
416     int i_last;
417
418     for( i_level = 0, i_last = 0; (size_t)( p_dst - p_start ) < i_dst && *p != '\0'; p++ )
419     {
420         const int c = b64[(unsigned int)*p];
421         if( c == -1 )
422             break;
423
424         switch( i_level )
425         {
426             case 0:
427                 i_level++;
428                 break;
429             case 1:
430                 *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 );
431                 i_level++;
432                 break;
433             case 2:
434                 *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
435                 i_level++;
436                 break;
437             case 3:
438                 *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c;
439                 i_level = 0;
440         }
441         i_last = c;
442     }
443
444     return p_dst - p_start;
445 }
446 size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
447 {
448     const int i_src = strlen( psz_src );
449     uint8_t   *p_dst;
450
451     *pp_dst = p_dst = malloc( i_src );
452     if( !p_dst )
453         return 0;
454     return  vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
455 }
456 char *vlc_b64_decode( const char *psz_src )
457 {
458     const int i_src = strlen( psz_src );
459     char *p_dst = malloc( i_src + 1 );
460     size_t i_dst;
461     if( !p_dst )
462         return NULL;
463
464     i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
465     p_dst[i_dst] = '\0';
466
467     return p_dst;
468 }
469
470 /**
471  * Formats current time into a heap-allocated string.
472  * @param tformat time format (as with C strftime())
473  * @return an allocated string (must be free()'d), or NULL on memory error.
474  */
475 char *str_format_time( const char *tformat )
476 {
477     time_t curtime;
478     struct tm loctime;
479
480     if (strcmp (tformat, "") == 0)
481         return strdup (""); /* corner case w.r.t. strftime() return value */
482
483     /* Get the current time.  */
484     time( &curtime );
485
486     /* Convert it to local time representation.  */
487     localtime_r( &curtime, &loctime );
488     for (size_t buflen = strlen (tformat) + 32;; buflen += 32)
489     {
490         char *str = malloc (buflen);
491         if (str == NULL)
492             return NULL;
493
494         size_t len = strftime (str, buflen, tformat, &loctime);
495         if (len > 0)
496         {
497             char *ret = realloc (str, len + 1);
498             return ret ? ret : str; /* <- this cannot fail */
499         }
500         free (str);
501     }
502     vlc_assert_unreachable ();
503 }
504
505 static void write_duration(FILE *stream, int64_t duration)
506 {
507     lldiv_t d;
508     long long sec;
509
510     duration /= CLOCK_FREQ;
511     d = lldiv(duration, 60);
512     sec = d.rem;
513     d = lldiv(d.quot, 60);
514     fprintf(stream, "%02lld:%02lld:%02lld", d.quot, d.rem, sec);
515 }
516
517 static int write_meta(FILE *stream, input_item_t *item, vlc_meta_type_t type)
518 {
519     if (item == NULL)
520         return EOF;
521
522     char *value = input_item_GetMeta(item, type);
523     if (value == NULL)
524         return EOF;
525
526     int ret = fputs(value, stream);
527     free(value);
528     return ret;
529 }
530
531 char *str_format_meta(input_thread_t *input, const char *s)
532 {
533     char *str;
534     size_t len;
535 #ifdef HAVE_OPEN_MEMSTREAM
536     FILE *stream = open_memstream(&str, &len);
537 #elif defined( _WIN32 )
538     FILE *stream = vlc_win32_tmpfile();
539 #else
540     FILE *stream = tmpfile();
541 #endif
542     if (stream == NULL)
543         return NULL;
544
545     input_item_t *item = (input != NULL) ? input_GetItem(input) : NULL;
546
547     char c;
548     bool b_is_format = false;
549     bool b_empty_if_na = false;
550
551     while ((c = *s) != '\0')
552     {
553         s++;
554
555         if (!b_is_format)
556         {
557             if (c == '$')
558             {
559                 b_is_format = true;
560                 b_empty_if_na = false;
561                 continue;
562             }
563
564             fputc(c, stream);
565             continue;
566         }
567
568         b_is_format = false;
569
570         switch (c)
571         {
572             case 'a':
573                 write_meta(stream, item, vlc_meta_Artist);
574                 break;
575             case 'b':
576                 write_meta(stream, item, vlc_meta_Album);
577                 break;
578             case 'c':
579                 write_meta(stream, item, vlc_meta_Copyright);
580                 break;
581             case 'd':
582                 write_meta(stream, item, vlc_meta_Description);
583                 break;
584             case 'e':
585                 write_meta(stream, item, vlc_meta_EncodedBy);
586                 break;
587             case 'f':
588                 if (item != NULL && item->p_stats != NULL)
589                 {
590                     vlc_mutex_lock(&item->p_stats->lock);
591                     fprintf(stream, "%"PRIi64,
592                             item->p_stats->i_displayed_pictures);
593                     vlc_mutex_unlock(&item->p_stats->lock);
594                 }
595                 else if (!b_empty_if_na)
596                     fputc('-', stream);
597                 break;
598             case 'g':
599                 write_meta(stream, item, vlc_meta_Genre);
600                 break;
601             case 'l':
602                 write_meta(stream, item, vlc_meta_Language);
603                 break;
604             case 'n':
605                 write_meta(stream, item, vlc_meta_TrackNumber);
606                 break;
607             case 'p':
608                 if (item == NULL)
609                     break;
610                 {
611                     char *value = input_item_GetNowPlayingFb(item);
612                     if (value == NULL)
613                         break;
614
615                     fputs(value, stream);
616                     free(value);
617                 }
618                 break;
619             case 'r':
620                 write_meta(stream, item, vlc_meta_Rating);
621                 break;
622             case 's':
623             {
624                 char *lang = NULL;
625
626                 if (input != NULL)
627                     lang = var_GetNonEmptyString(input, "sub-language");
628                 if (lang != NULL)
629                 {
630                     fputs(lang, stream);
631                     free(lang);
632                 }
633                 else if (!b_empty_if_na)
634                     fputc('-', stream);
635                 break;
636             }
637             case 't':
638                 write_meta(stream, item, vlc_meta_Title);
639                 break;
640             case 'u':
641                 write_meta(stream, item, vlc_meta_URL);
642                 break;
643             case 'A':
644                 write_meta(stream, item, vlc_meta_Date);
645                 break;
646             case 'B':
647                 if (input != NULL)
648                     fprintf(stream, "%"PRId64,
649                             var_GetInteger(input, "bit-rate") / 1000);
650                 else if (!b_empty_if_na)
651                     fputc('-', stream);
652                 break;
653             case 'C':
654                 if (input != NULL)
655                     fprintf(stream, "%"PRId64,
656                             var_GetInteger(input, "chapter"));
657                 else if (!b_empty_if_na)
658                     fputc('-', stream);
659                 break;
660             case 'D':
661                 if (item != NULL)
662                     write_duration(stream, input_item_GetDuration(item));
663                 else if (!b_empty_if_na)
664                     fputs("--:--:--", stream);
665                 break;
666             case 'F':
667                 if (item != NULL)
668                 {
669                     char *uri = input_item_GetURI(item);
670                     if (uri != NULL)
671                     {
672                         fputs(uri, stream);
673                         free(uri);
674                     }
675                 }
676                 break;
677             case 'I':
678                 if (input != NULL)
679                     fprintf(stream, "%"PRId64, var_GetInteger(input, "title"));
680                 else if (!b_empty_if_na)
681                     fputc('-', stream);
682                 break;
683             case 'L':
684                 if (item != NULL)
685                 {
686                     assert(input != NULL);
687                     write_duration(stream, input_item_GetDuration(item)
688                                    - var_GetTime(input, "time"));
689                 }
690                 else if (!b_empty_if_na)
691                     fputs("--:--:--", stream);
692                 break;
693             case 'N':
694                 if (item != NULL)
695                 {
696                     char *name = input_item_GetName(item);
697                     if (name != NULL)
698                     {
699                         fputs(name, stream);
700                         free(name);
701                     }
702                 }
703                 break;
704             case 'O':
705             {
706                 char *lang = NULL;
707
708                 if (input != NULL)
709                     lang = var_GetNonEmptyString(input, "audio-language");
710                 if (lang != NULL)
711                 {
712                     fputs(lang, stream);
713                     free(lang);
714                 }
715                 else if (!b_empty_if_na)
716                     fputc('-', stream);
717                 break;
718             }
719             case 'P':
720                 if (input != NULL)
721                     fprintf(stream, "%2.1f",
722                             var_GetFloat(input, "position") * 100.f);
723                 else if (!b_empty_if_na)
724                     fputs("--.-%", stream);
725                 break;
726             case 'R':
727                 if (input != NULL)
728                     fprintf(stream, "%.3f", var_GetFloat(input, "rate"));
729                 else if (!b_empty_if_na)
730                     fputc('-', stream);
731                 break;
732             case 'S':
733                 if (input != NULL)
734                 {
735                     int rate = var_GetInteger(input, "sample-rate");
736                     div_t dr = div((rate + 50) / 100, 10);
737
738                     fprintf(stream, "%d.%01d", dr.quot, dr.rem);
739                 }
740                 else if (!b_empty_if_na)
741                     fputc('-', stream);
742                 break;
743             case 'T':
744                 if (input != NULL)
745                     write_duration(stream, var_GetTime(input, "time"));
746                 else if (!b_empty_if_na)
747                     fputs("--:--:--", stream);
748                 break;
749             case 'U':
750                 write_meta(stream, item, vlc_meta_Publisher);
751                 break;
752             case 'V':
753             {
754                 float vol = 0.f;
755
756                 if (input != NULL)
757                 {
758                     audio_output_t *aout = input_GetAout(input);
759                     if (aout != NULL)
760                     {
761                         vol = aout_VolumeGet(aout);
762                         vlc_object_release(aout);
763                     }
764                 }
765                 if (vol >= 0.f)
766                     fprintf(stream, "%ld", lroundf(vol * 256.f));
767                 else if (!b_empty_if_na)
768                     fputs("---", stream);
769                 break;
770             }
771             case '_':
772                 fputc('\n', stream);
773                 break;
774             case 'Z':
775                 if (item == NULL)
776                     break;
777                 {
778                     char *value = input_item_GetNowPlayingFb(item);
779                     if (value != NULL)
780                     {
781                         fputs(value, stream);
782                         free(value);
783                     }
784                     else
785                     {
786                         char *title = input_item_GetTitleFbName(item);
787
788                         if (write_meta(stream, item, vlc_meta_Artist) >= 0
789                             && title != NULL)
790                             fputs(" - ", stream);
791
792                         if (title != NULL)
793                         {
794                             fputs(title, stream);
795                             free(title);
796                         }
797                     }
798                 }
799                 break;
800             case ' ':
801                 b_empty_if_na = true;
802                 b_is_format = true;
803                 break;
804             default:
805                 fputc(c, stream);
806                 break;
807         }
808     }
809
810 #ifdef HAVE_OPEN_MEMSTREAM
811     return (fclose(stream) == 0) ? str : NULL;
812 #else
813     len = ftell(stream);
814     if (len != (size_t)-1)
815     {
816         rewind(stream);
817         str = xmalloc(len + 1);
818         fread(str, len, 1, stream);
819         str[len] = '\0';
820     }
821     else
822         str = NULL;
823     fclose(stream);
824     return str;
825 #endif
826 }
827
828 /**
829  * Remove forbidden, potentially forbidden and otherwise evil characters from
830  * filenames. This includes slashes, and popular characters like colon
831  * (on Unix anyway), so this should only be used for automatically generated
832  * filenames.
833  * \warning Do not use this on full paths,
834  * only single file names without any directory separator!
835  */
836 void filename_sanitize( char *str )
837 {
838     unsigned char c;
839
840     /* Special file names, not allowed */
841     if( !strcmp( str, "." ) || !strcmp( str, ".." ) )
842     {
843         while( *str )
844             *(str++) = '_';
845         return;
846     }
847
848     /* On platforms not using UTF-7, VLC cannot access non-Unicode paths.
849      * Also, some file systems require Unicode file names.
850      * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
851     EnsureUTF8( str );
852
853     /* Avoid leading spaces to please Windows. */
854     while( (c = *str) != '\0' )
855     {
856         if( c != ' ' )
857             break;
858         *(str++) = '_';
859     }
860
861     char *start = str;
862
863     while( (c = *str) != '\0' )
864     {
865         /* Non-printable characters are not a good idea */
866         if( c < 32 )
867             *str = '_';
868         /* This is the list of characters not allowed by Microsoft.
869          * We also black-list them on Unix as they may be confusing, and are
870          * not supported by some file system types (notably CIFS). */
871         else if( strchr( "/:\\*\"?|<>", c ) != NULL )
872             *str = '_';
873         str++;
874     }
875
876     /* Avoid trailing spaces also to please Windows. */
877     while( str > start )
878     {
879         if( *(--str) != ' ' )
880             break;
881         *str = '_';
882     }
883 }
884
885 /**
886  * Remove forbidden characters from full paths (leaves slashes)
887  */
888 void path_sanitize( char *str )
889 {
890 #if defined( _WIN32 ) || defined( __OS2__ )
891     /* check drive prefix if path is absolute */
892     if( (((unsigned char)(str[0] - 'A') < 26)
893       || ((unsigned char)(str[0] - 'a') < 26)) && (':' == str[1]) )
894         str += 2;
895 #endif
896     while( *str )
897     {
898 #if defined( __APPLE__ )
899         if( *str == ':' )
900             *str = '_';
901 #elif defined( _WIN32 ) || defined( __OS2__ )
902         if( strchr( "*\"?:|<>", *str ) )
903             *str = '_';
904         if( *str == '/' )
905             *str = DIR_SEP_CHAR;
906 #endif
907         str++;
908     }
909 }
910
911 /*
912   Decodes a duration as defined by ISO 8601
913   http://en.wikipedia.org/wiki/ISO_8601#Durations
914   @param str A null-terminated string to convert
915   @return: The duration in seconds. -1 if an error occurred.
916
917   Exemple input string: "PT0H9M56.46S"
918  */
919 time_t str_duration( const char *psz_duration )
920 {
921     bool        timeDesignatorReached = false;
922     time_t      res = 0;
923     char*       end_ptr;
924
925     if ( psz_duration == NULL )
926         return -1;
927     if ( ( *(psz_duration++) ) != 'P' )
928         return -1;
929     do
930     {
931         double number = us_strtod( psz_duration, &end_ptr );
932         double      mul = 0;
933         if ( psz_duration != end_ptr )
934             psz_duration = end_ptr;
935         switch( *psz_duration )
936         {
937             case 'M':
938             {
939                 //M can mean month or minutes, if the 'T' flag has been reached.
940                 //We don't handle months though.
941                 if ( timeDesignatorReached == true )
942                     mul = 60.0;
943                 break ;
944             }
945             case 'Y':
946             case 'W':
947                 break ; //Don't handle this duration.
948             case 'D':
949                 mul = 86400.0;
950                 break ;
951             case 'T':
952                 timeDesignatorReached = true;
953                 break ;
954             case 'H':
955                 mul = 3600.0;
956                 break ;
957             case 'S':
958                 mul = 1.0;
959                 break ;
960             default:
961                 break ;
962         }
963         res += (time_t)(mul * number);
964         if ( *psz_duration )
965             psz_duration++;
966     } while ( *psz_duration );
967     return res;
968 }