]> git.sesse.net Git - vlc/blob - src/text/strings.c
Misc: remove leading underscores
[vlc] / src / text / strings.c
1 /*****************************************************************************
2  * strings.c: String related functions
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
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
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 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 General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, 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
41 /* Needed by str_format_meta */
42 #include <vlc_input.h>
43 #include <vlc_meta.h>
44 #include <vlc_playlist.h>
45 #include <vlc_aout.h>
46
47 #include <vlc_strings.h>
48 #include <vlc_url.h>
49 #include <vlc_charset.h>
50
51 /**
52  * Decode encoded URI component. See also decode_URI().
53  * \return decoded duplicated string
54  */
55 char *decode_URI_duplicate( const char *psz )
56 {
57     char *psz_dup = strdup( psz );
58     decode_URI( psz_dup );
59     return psz_dup;
60 }
61
62 /**
63  * Decode an encoded URI component in place.
64  * <b>This function does NOT decode entire URIs.</b>
65  * It decodes components (e.g. host name, directory, file name).
66  * Decoded URIs do not exist in the real world (see RFC3986 §2.4).
67  * Complete URIs are always "encoded" (or they are syntaxically invalid).
68  *
69  * Note that URI encoding is different from Javascript escaping. Especially,
70  * white spaces and Unicode non-ASCII code points are encoded differently.
71  *
72  * \return psz on success, NULL if it was not properly encoded
73  */
74 char *decode_URI( char *psz )
75 {
76     unsigned char *in = (unsigned char *)psz, *out = in, c;
77
78     if( psz == NULL )
79         return NULL;
80
81     while( ( c = *in++ ) != '\0' )
82     {
83         switch( c )
84         {
85             case '%':
86             {
87                 char hex[3];
88
89                 if( ( ( hex[0] = *in++ ) == 0 )
90                  || ( ( hex[1] = *in++ ) == 0 ) )
91                     return NULL;
92
93                 hex[2] = '\0';
94                 *out++ = (unsigned char)strtoul( hex, NULL, 0x10 );
95                 break;
96             }
97
98             case '+': /* This is HTTP forms, not URI decoding... */
99                 *out++ = ' ';
100                 break;
101
102             default:
103                 /* Inserting non-ASCII or non-printable characters is unsafe,
104                  * and no sane browser will send these unencoded */
105                 if( ( c < 32 ) || ( c > 127 ) )
106                     *out++ = '?';
107                 else
108                     *out++ = c;
109         }
110     }
111     *out = '\0';
112     EnsureUTF8( psz );
113     return psz;
114 }
115
116 static inline bool isurisafe( int c )
117 {
118     /* These are the _unreserved_ URI characters (RFC3986 §2.3) */
119     return ( (unsigned char)( c - 'a' ) < 26 )
120             || ( (unsigned char)( c - 'A' ) < 26 )
121             || ( (unsigned char)( c - '0' ) < 10 )
122             || ( strchr( "-._~", c ) != NULL );
123 }
124
125 static char *encode_URI_bytes (const char *psz_uri, size_t len)
126 {
127     char *psz_enc = malloc (3 * len + 1), *out = psz_enc;
128     if (psz_enc == NULL)
129         return NULL;
130
131     for (size_t i = 0; i < len; i++)
132     {
133         static const char hex[16] = "0123456789ABCDEF";
134         uint8_t c = *psz_uri;
135
136         if( isurisafe( c ) )
137             *out++ = c;
138         /* This is URI encoding, not HTTP forms:
139          * Space is encoded as '%20', not '+'. */
140         else
141         {
142             *out++ = '%';
143             *out++ = hex[c >> 4];
144             *out++ = hex[c & 0xf];
145         }
146         psz_uri++;
147     }
148     *out++ = '\0';
149
150     out = realloc (psz_enc, out - psz_enc);
151     return out ? out : psz_enc; /* realloc() can fail (safe) */
152 }
153
154 /**
155  * Encodes an URI component (RFC3986 §2).
156  *
157  * @param psz_uri nul-terminated UTF-8 representation of the component.
158  * Obviously, you can't pass an URI containing a nul character, but you don't
159  * want to do that, do you?
160  *
161  * @return encoded string (must be free()'d), or NULL for ENOMEM.
162  */
163 char *encode_URI_component( const char *psz_uri )
164 {
165     return encode_URI_bytes (psz_uri, strlen (psz_uri));
166 }
167
168
169 static const struct xml_entity_s
170 {
171     char    psz_entity[8];
172     char    psz_char[4];
173 } xml_entities[] = {
174     /* Important: this list has to be in alphabetical order (psz_entity-wise) */
175     { "AElig;",  "Æ" },
176     { "Aacute;", "Á" },
177     { "Acirc;",  "Â" },
178     { "Agrave;", "À" },
179     { "Aring;",  "Å" },
180     { "Atilde;", "Ã" },
181     { "Auml;",   "Ä" },
182     { "Ccedil;", "Ç" },
183     { "Dagger;", "‡" },
184     { "ETH;",    "Ð" },
185     { "Eacute;", "É" },
186     { "Ecirc;",  "Ê" },
187     { "Egrave;", "È" },
188     { "Euml;",   "Ë" },
189     { "Iacute;", "Í" },
190     { "Icirc;",  "Î" },
191     { "Igrave;", "Ì" },
192     { "Iuml;",   "Ï" },
193     { "Ntilde;", "Ñ" },
194     { "OElig;",  "Œ" },
195     { "Oacute;", "Ó" },
196     { "Ocirc;",  "Ô" },
197     { "Ograve;", "Ò" },
198     { "Oslash;", "Ø" },
199     { "Otilde;", "Õ" },
200     { "Ouml;",   "Ö" },
201     { "Scaron;", "Š" },
202     { "THORN;",  "Þ" },
203     { "Uacute;", "Ú" },
204     { "Ucirc;",  "Û" },
205     { "Ugrave;", "Ù" },
206     { "Uuml;",   "Ü" },
207     { "Yacute;", "Ý" },
208     { "Yuml;",   "Ÿ" },
209     { "aacute;", "á" },
210     { "acirc;",  "â" },
211     { "acute;",  "´" },
212     { "aelig;",  "æ" },
213     { "agrave;", "à" },
214     { "amp;",    "&" },
215     { "apos;",   "'" },
216     { "aring;",  "å" },
217     { "atilde;", "ã" },
218     { "auml;",   "ä" },
219     { "bdquo;",  "„" },
220     { "brvbar;", "¦" },
221     { "ccedil;", "ç" },
222     { "cedil;",  "¸" },
223     { "cent;",   "¢" },
224     { "circ;",   "ˆ" },
225     { "copy;",   "©" },
226     { "curren;", "¤" },
227     { "dagger;", "†" },
228     { "deg;",    "°" },
229     { "divide;", "÷" },
230     { "eacute;", "é" },
231     { "ecirc;",  "ê" },
232     { "egrave;", "è" },
233     { "eth;",    "ð" },
234     { "euml;",   "ë" },
235     { "euro;",   "€" },
236     { "frac12;", "½" },
237     { "frac14;", "¼" },
238     { "frac34;", "¾" },
239     { "gt;",     ">" },
240     { "hellip;", "…" },
241     { "iacute;", "í" },
242     { "icirc;",  "î" },
243     { "iexcl;",  "¡" },
244     { "igrave;", "ì" },
245     { "iquest;", "¿" },
246     { "iuml;",   "ï" },
247     { "laquo;",  "«" },
248     { "ldquo;",  "“" },
249     { "lsaquo;", "‹" },
250     { "lsquo;",  "‘" },
251     { "lt;",     "<" },
252     { "macr;",   "¯" },
253     { "mdash;",  "—" },
254     { "micro;",  "µ" },
255     { "middot;", "·" },
256     { "nbsp;",   "\xc2\xa0" },
257     { "ndash;",  "–" },
258     { "not;",    "¬" },
259     { "ntilde;", "ñ" },
260     { "oacute;", "ó" },
261     { "ocirc;",  "ô" },
262     { "oelig;",  "œ" },
263     { "ograve;", "ò" },
264     { "ordf;",   "ª" },
265     { "ordm;",   "º" },
266     { "oslash;", "ø" },
267     { "otilde;", "õ" },
268     { "ouml;",   "ö" },
269     { "para;",   "¶" },
270     { "permil;", "‰" },
271     { "plusmn;", "±" },
272     { "pound;",  "£" },
273     { "quot;",   "\"" },
274     { "raquo;",  "»" },
275     { "rdquo;",  "”" },
276     { "reg;",    "®" },
277     { "rsaquo;", "›" },
278     { "rsquo;",  "’" },
279     { "sbquo;",  "‚" },
280     { "scaron;", "š" },
281     { "sect;",   "§" },
282     { "shy;",    "­" },
283     { "sup1;",   "¹" },
284     { "sup2;",   "²" },
285     { "sup3;",   "³" },
286     { "szlig;",  "ß" },
287     { "thorn;",  "þ" },
288     { "tilde;",  "˜" },
289     { "times;",  "×" },
290     { "trade;",  "™" },
291     { "uacute;", "ú" },
292     { "ucirc;",  "û" },
293     { "ugrave;", "ù" },
294     { "uml;",    "¨" },
295     { "uuml;",   "ü" },
296     { "yacute;", "ý" },
297     { "yen;",    "¥" },
298     { "yuml;",   "ÿ" },
299 };
300
301 static int cmp_entity (const void *key, const void *elem)
302 {
303     const struct xml_entity_s *ent = elem;
304     const char *name = key;
305
306     return strncmp (name, ent->psz_entity, strlen (ent->psz_entity));
307 }
308
309 /**
310  * Converts "&lt;", "&gt;" and "&amp;" to "<", ">" and "&"
311  * \param string to convert
312  */
313 void resolve_xml_special_chars( char *psz_value )
314 {
315     char *p_pos = psz_value;
316
317     while ( *psz_value )
318     {
319         if( *psz_value == '&' )
320         {
321             if( psz_value[1] == '#' )
322             {   /* &#xxx; Unicode code point */
323                 char *psz_end;
324                 unsigned long cp = strtoul( psz_value+2, &psz_end, 10 );
325                 if( *psz_end == ';' )
326                 {
327                     psz_value = psz_end + 1;
328                     if( cp == 0 )
329                         (void)0; /* skip nuls */
330                     else
331                     if( cp <= 0x7F )
332                     {
333                         *p_pos =            cp;
334                     }
335                     else
336                     /* Unicode code point outside ASCII.
337                      * &#xxx; representation is longer than UTF-8 :) */
338                     if( cp <= 0x7FF )
339                     {
340                         *p_pos++ = 0xC0 |  (cp >>  6);
341                         *p_pos   = 0x80 |  (cp        & 0x3F);
342                     }
343                     else
344                     if( cp <= 0xFFFF )
345                     {
346                         *p_pos++ = 0xE0 |  (cp >> 12);
347                         *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
348                         *p_pos   = 0x80 |  (cp        & 0x3F);
349                     }
350                     else
351                     if( cp <= 0x1FFFFF ) /* Outside the BMP */
352                     {   /* Unicode stops at 10FFFF, but who cares? */
353                         *p_pos++ = 0xF0 |  (cp >> 18);
354                         *p_pos++ = 0x80 | ((cp >> 12) & 0x3F);
355                         *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
356                         *p_pos   = 0x80 |  (cp        & 0x3F);
357                     }
358                 }
359                 else
360                 {
361                     /* Invalid entity number */
362                     *p_pos = *psz_value;
363                     psz_value++;
364                 }
365             }
366             else
367             {   /* Well-known XML entity */
368                 const struct xml_entity_s *ent;
369
370                 ent = bsearch (psz_value + 1, xml_entities,
371                                sizeof (xml_entities) / sizeof (*ent),
372                                sizeof (*ent), cmp_entity);
373                 if (ent != NULL)
374                 {
375                     size_t olen = strlen (ent->psz_char);
376                     memcpy (p_pos, ent->psz_char, olen);
377                     p_pos += olen - 1;
378                     psz_value += strlen (ent->psz_entity) + 1;
379                 }
380                 else
381                 {   /* No match */
382                     *p_pos = *psz_value;
383                     psz_value++;
384                 }
385             }
386         }
387         else
388         {
389             *p_pos = *psz_value;
390             psz_value++;
391         }
392
393         p_pos++;
394     }
395
396     *p_pos = '\0';
397 }
398
399 /**
400  * Converts '<', '>', '\"', '\'' and '&' to their html entities
401  * \param psz_content simple element content that is to be converted
402  */
403 char *convert_xml_special_chars( const char *psz_content )
404 {
405     assert( psz_content );
406
407     const size_t len = strlen( psz_content );
408     char *const psz_temp = malloc( 6 * len + 1 );
409     char *p_to   = psz_temp;
410
411     if( psz_temp == NULL )
412         return NULL;
413     for( size_t i = 0; i < len; i++ )
414     {
415         const char *str;
416         char c = psz_content[i];
417
418         switch ( c )
419         {
420             case '\"': str = "quot"; break;
421             case '&':  str = "amp";  break;
422             case '\'': str = "#39";  break;
423             case '<':  str = "lt";   break;
424             case '>':  str = "gt";   break;
425             default:
426                 *(p_to++) = c;
427                 continue;
428         }
429         p_to += sprintf( p_to, "&%s;", str );
430     }
431     *(p_to++) = '\0';
432
433     p_to = realloc( psz_temp, p_to - psz_temp );
434     return p_to ? p_to : psz_temp; /* cannot fail */
435 }
436
437 /* Base64 encoding */
438 char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src )
439 {
440     static const char b64[] =
441            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
442
443     char *ret = malloc( ( i_src + 4 ) * 4 / 3 );
444     char *dst = ret;
445
446     if( dst == NULL )
447         return NULL;
448
449     while( i_src > 0 )
450     {
451         /* pops (up to) 3 bytes of input, push 4 bytes */
452         uint32_t v;
453
454         /* 1/3 -> 1/4 */
455         v = *src++ << 24;
456         *dst++ = b64[v >> 26];
457         v = v << 6;
458
459         /* 2/3 -> 2/4 */
460         if( i_src >= 2 )
461             v |= *src++ << 22;
462         *dst++ = b64[v >> 26];
463         v = v << 6;
464
465         /* 3/3 -> 3/4 */
466         if( i_src >= 3 )
467             v |= *src++ << 20; // 3/3
468         *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4
469         v = v << 6;
470
471         /* -> 4/4 */
472         *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4
473
474         if( i_src <= 3 )
475             break;
476         i_src -= 3;
477     }
478
479     *dst = '\0';
480
481     return ret;
482 }
483
484 char *vlc_b64_encode( const char *src )
485 {
486     if( src )
487         return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) );
488     else
489         return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
490 }
491
492 /* Base64 decoding */
493 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src )
494 {
495     static const int b64[256] = {
496         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
497         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
498         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
499         52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
500         -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
501         15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
502         -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
503         41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
504         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
505         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
506         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
507         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
508         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
509         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
510         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
511         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
512     };
513     uint8_t *p_start = p_dst;
514     uint8_t *p = (uint8_t *)p_src;
515
516     int i_level;
517     int i_last;
518
519     for( i_level = 0, i_last = 0; (size_t)( p_dst - p_start ) < i_dst && *p != '\0'; p++ )
520     {
521         const int c = b64[(unsigned int)*p];
522         if( c == -1 )
523             continue;
524
525         switch( i_level )
526         {
527             case 0:
528                 i_level++;
529                 break;
530             case 1:
531                 *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 );
532                 i_level++;
533                 break;
534             case 2:
535                 *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
536                 i_level++;
537                 break;
538             case 3:
539                 *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c;
540                 i_level = 0;
541         }
542         i_last = c;
543     }
544
545     return p_dst - p_start;
546 }
547 size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
548 {
549     const int i_src = strlen( psz_src );
550     uint8_t   *p_dst;
551
552     *pp_dst = p_dst = malloc( i_src );
553     if( !p_dst )
554         return 0;
555     return  vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
556 }
557 char *vlc_b64_decode( const char *psz_src )
558 {
559     const int i_src = strlen( psz_src );
560     char *p_dst = malloc( i_src + 1 );
561     size_t i_dst;
562     if( !p_dst )
563         return NULL;
564
565     i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
566     p_dst[i_dst] = '\0';
567
568     return p_dst;
569 }
570
571 /**
572  * Formats current time into a heap-allocated string.
573  * @param tformat time format (as with C strftime())
574  * @return an allocated string (must be free()'d), or NULL on memory error.
575  */
576 char *str_format_time( const char *tformat )
577 {
578     time_t curtime;
579     struct tm loctime;
580
581     if (strcmp (tformat, "") == 0)
582         return strdup (""); /* corner case w.r.t. strftime() return value */
583
584     /* Get the current time.  */
585     time( &curtime );
586
587     /* Convert it to local time representation.  */
588     localtime_r( &curtime, &loctime );
589     for (size_t buflen = strlen (tformat) + 32;; buflen += 32)
590     {
591         char *str = malloc (buflen);
592         if (str == NULL)
593             return NULL;
594
595         size_t len = strftime (str, buflen, tformat, &loctime);
596         if (len > 0)
597         {
598             char *ret = realloc (str, len + 1);
599             return ret ? ret : str; /* <- this cannot fail */
600         }
601     }
602     assert (0);
603 }
604
605 #define INSERT_STRING( string )                                     \
606                     if( string != NULL )                            \
607                     {                                               \
608                         int len = strlen( string );                 \
609                         dst = xrealloc( dst, i_size = i_size + len );\
610                         memcpy( (dst+d), string, len );             \
611                         d += len;                                   \
612                         free( string );                             \
613                     }                                               \
614                     else if( !b_empty_if_na )                       \
615                     {                                               \
616                         *(dst+d) = '-';                             \
617                         d++;                                        \
618                     }                                               \
619
620 /* same than INSERT_STRING, except that string won't be freed */
621 #define INSERT_STRING_NO_FREE( string )                             \
622                     {                                               \
623                         int len = strlen( string );                 \
624                         dst = xrealloc( dst, i_size = i_size + len );\
625                         memcpy( dst+d, string, len );               \
626                         d += len;                                   \
627                     }
628 #undef str_format_meta
629 char *str_format_meta( vlc_object_t *p_object, const char *string )
630 {
631     const char *s = string;
632     bool b_is_format = false;
633     bool b_empty_if_na = false;
634     char buf[10];
635     int i_size = strlen( string ) + 1; /* +1 to store '\0' */
636     char *dst = strdup( string );
637     if( !dst ) return NULL;
638     int d = 0;
639
640     playlist_t *p_playlist = pl_Hold( p_object );
641     input_thread_t *p_input = playlist_CurrentInput( p_playlist );
642     input_item_t *p_item = NULL;
643     pl_Release( p_object );
644     if( p_input )
645     {
646         p_item = input_GetItem(p_input);
647     }
648
649     while( *s )
650     {
651         if( b_is_format )
652         {
653             switch( *s )
654             {
655                 case 'a':
656                     if( p_item )
657                     {
658                         INSERT_STRING( input_item_GetArtist( p_item ) );
659                     }
660                     break;
661                 case 'b':
662                     if( p_item )
663                     {
664                         INSERT_STRING( input_item_GetAlbum( p_item ) );
665                     }
666                     break;
667                 case 'c':
668                     if( p_item )
669                     {
670                         INSERT_STRING( input_item_GetCopyright( p_item ) );
671                     }
672                     break;
673                 case 'd':
674                     if( p_item )
675                     {
676                         INSERT_STRING( input_item_GetDescription( p_item ) );
677                     }
678                     break;
679                 case 'e':
680                     if( p_item )
681                     {
682                         INSERT_STRING( input_item_GetEncodedBy( p_item ) );
683                     }
684                     break;
685                 case 'f':
686                     if( p_item && p_item->p_stats )
687                     {
688                         vlc_mutex_lock( &p_item->p_stats->lock );
689                         snprintf( buf, 10, "%d",
690                                   p_item->p_stats->i_displayed_pictures );
691                         vlc_mutex_unlock( &p_item->p_stats->lock );
692                     }
693                     else
694                     {
695                         sprintf( buf, b_empty_if_na ? "" : "-" );
696                     }
697                     INSERT_STRING_NO_FREE( buf );
698                     break;
699                 case 'g':
700                     if( p_item )
701                     {
702                         INSERT_STRING( input_item_GetGenre( p_item ) );
703                     }
704                     break;
705                 case 'l':
706                     if( p_item )
707                     {
708                         INSERT_STRING( input_item_GetLanguage( p_item ) );
709                     }
710                     break;
711                 case 'n':
712                     if( p_item )
713                     {
714                         INSERT_STRING( input_item_GetTrackNum( p_item ) );
715                     }
716                     break;
717                 case 'p':
718                     if( p_item )
719                     {
720                         INSERT_STRING( input_item_GetNowPlaying( p_item ) );
721                     }
722                     break;
723                 case 'r':
724                     if( p_item )
725                     {
726                         INSERT_STRING( input_item_GetRating( p_item ) );
727                     }
728                     break;
729                 case 's':
730                 {
731                     char *lang = NULL;
732                     if( p_input )
733                         lang = var_GetNonEmptyString( p_input, "sub-language" );
734                     if( lang == NULL )
735                         lang = strdup( b_empty_if_na ? "" : "-" );
736                     INSERT_STRING( lang );
737                     break;
738                 }
739                 case 't':
740                     if( p_item )
741                     {
742                         INSERT_STRING( input_item_GetTitle( p_item ) );
743                     }
744                     break;
745                 case 'u':
746                     if( p_item )
747                     {
748                         INSERT_STRING( input_item_GetURL( p_item ) );
749                     }
750                     break;
751                 case 'A':
752                     if( p_item )
753                     {
754                         INSERT_STRING( input_item_GetDate( p_item ) );
755                     }
756                     break;
757                 case 'B':
758                     if( p_input )
759                     {
760                         snprintf( buf, 10, "%d",
761                                   var_GetInteger( p_input, "bit-rate" )/1000 );
762                     }
763                     else
764                     {
765                         sprintf( buf, b_empty_if_na ? "" : "-" );
766                     }
767                     INSERT_STRING_NO_FREE( buf );
768                     break;
769                 case 'C':
770                     if( p_input )
771                     {
772                         snprintf( buf, 10, "%d",
773                                   var_GetInteger( p_input, "chapter" ) );
774                     }
775                     else
776                     {
777                         sprintf( buf, b_empty_if_na ? "" : "-" );
778                     }
779                     INSERT_STRING_NO_FREE( buf );
780                     break;
781                 case 'D':
782                     if( p_item )
783                     {
784                         mtime_t i_duration = input_item_GetDuration( p_item );
785                         snprintf( buf, 10, "%02d:%02d:%02d",
786                                  (int)(i_duration/(3600000000)),
787                                  (int)((i_duration/(60000000))%60),
788                                  (int)((i_duration/1000000)%60) );
789                     }
790                     else
791                     {
792                         snprintf( buf, 10, b_empty_if_na ? "" : "--:--:--" );
793                     }
794                     INSERT_STRING_NO_FREE( buf );
795                     break;
796                 case 'F':
797                     if( p_item )
798                     {
799                         INSERT_STRING( input_item_GetURI( p_item ) );
800                     }
801                     break;
802                 case 'I':
803                     if( p_input )
804                     {
805                         snprintf( buf, 10, "%d",
806                                   var_GetInteger( p_input, "title" ) );
807                     }
808                     else
809                     {
810                         sprintf( buf, b_empty_if_na ? "" : "-" );
811                     }
812                     INSERT_STRING_NO_FREE( buf );
813                     break;
814                 case 'L':
815                     if( p_item && p_input )
816                     {
817                         mtime_t i_duration = input_item_GetDuration( p_item );
818                         int64_t i_time = var_GetTime( p_input, "time" );
819                         snprintf( buf, 10, "%02d:%02d:%02d",
820                      (int)( ( i_duration - i_time ) / 3600000000 ),
821                      (int)( ( ( i_duration - i_time ) / 60000000 ) % 60 ),
822                      (int)( ( ( i_duration - i_time ) / 1000000 ) % 60 ) );
823                     }
824                     else
825                     {
826                         snprintf( buf, 10, b_empty_if_na ? "" : "--:--:--" );
827                     }
828                     INSERT_STRING_NO_FREE( buf );
829                     break;
830                 case 'N':
831                     if( p_item )
832                     {
833                         INSERT_STRING( input_item_GetName( p_item ) );
834                     }
835                     break;
836                 case 'O':
837                 {
838                     char *lang = NULL;
839                     if( p_input )
840                         lang = var_GetNonEmptyString( p_input,
841                                                       "audio-language" );
842                     if( lang == NULL )
843                         lang = strdup( b_empty_if_na ? "" : "-" );
844                     INSERT_STRING( lang );
845                     break;
846                 }
847                 case 'P':
848                     if( p_input )
849                     {
850                         snprintf( buf, 10, "%2.1lf",
851                                   var_GetFloat( p_input, "position" ) * 100. );
852                     }
853                     else
854                     {
855                         snprintf( buf, 10, b_empty_if_na ? "" : "--.-%%" );
856                     }
857                     INSERT_STRING_NO_FREE( buf );
858                     break;
859                 case 'R':
860                     if( p_input )
861                     {
862                         float f = var_GetFloat( p_input, "rate" );
863                         snprintf( buf, 10, "%.3f", f );
864                     }
865                     else
866                     {
867                         sprintf( buf, b_empty_if_na ? "" : "-" );
868                     }
869                     INSERT_STRING_NO_FREE( buf );
870                     break;
871                 case 'S':
872                     if( p_input )
873                     {
874                         int r = var_GetInteger( p_input, "sample-rate" );
875                         snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
876                     }
877                     else
878                     {
879                         sprintf( buf, b_empty_if_na ? "" : "-" );
880                     }
881                     INSERT_STRING_NO_FREE( buf );
882                     break;
883                 case 'T':
884                     if( p_input )
885                     {
886                         int64_t i_time = var_GetTime( p_input, "time" );
887                         snprintf( buf, 10, "%02d:%02d:%02d",
888                             (int)( i_time / ( 3600000000 ) ),
889                             (int)( ( i_time / ( 60000000 ) ) % 60 ),
890                             (int)( ( i_time / 1000000 ) % 60 ) );
891                     }
892                     else
893                     {
894                         snprintf( buf, 10, b_empty_if_na ? "" :  "--:--:--" );
895                     }
896                     INSERT_STRING_NO_FREE( buf );
897                     break;
898                 case 'U':
899                     if( p_item )
900                     {
901                         INSERT_STRING( input_item_GetPublisher( p_item ) );
902                     }
903                     break;
904                 case 'V':
905                 {
906                     audio_volume_t volume;
907                     aout_VolumeGet( p_object, &volume );
908                     snprintf( buf, 10, "%d", volume );
909                     INSERT_STRING_NO_FREE( buf );
910                     break;
911                 }
912                 case '_':
913                     *(dst+d) = '\n';
914                     d++;
915                     break;
916
917                 case ' ':
918                     b_empty_if_na = true;
919                     break;
920
921                 default:
922                     *(dst+d) = *s;
923                     d++;
924                     break;
925             }
926             if( *s != ' ' )
927                 b_is_format = false;
928         }
929         else if( *s == '$' )
930         {
931             b_is_format = true;
932             b_empty_if_na = false;
933         }
934         else
935         {
936             *(dst+d) = *s;
937             d++;
938         }
939         s++;
940     }
941     *(dst+d) = '\0';
942
943     if( p_input )
944         vlc_object_release( p_input );
945
946     return dst;
947 }
948 #undef INSERT_STRING
949 #undef INSERT_STRING_NO_FREE
950
951 #undef str_format
952 /**
953  * Apply str format time and str format meta
954  */
955 char *str_format( vlc_object_t *p_this, const char *psz_src )
956 {
957     char *psz_buf1, *psz_buf2;
958     psz_buf1 = str_format_time( psz_src );
959     psz_buf2 = str_format_meta( p_this, psz_buf1 );
960     free( psz_buf1 );
961     return psz_buf2;
962 }
963
964 /**
965  * Remove forbidden characters from filenames (including slashes)
966  */
967 char* filename_sanitize( const char *str_origin )
968 {
969     char *str = strdup( str_origin );
970     char *str_base = str;
971     if( *str == '.' && (str[1] == '\0' || (str[1] == '.' && str[2] == '\0' ) ) )
972     {
973         while( *str )
974         {
975             *str = '_';
976             str++;
977         }
978         return str_base;
979     }
980
981 #if defined( WIN32 )
982     // Change leading spaces into underscores
983     while( *str && *str == ' ' )
984         *str++ = '_';
985 #endif
986
987     while( *str )
988     {
989         switch( *str )
990         {
991             case '/':
992 #if defined( __APPLE__ )
993             case ':':
994 #elif defined( WIN32 )
995             case '\\':
996             case '*':
997             case '"':
998             case '?':
999             case ':':
1000             case '|':
1001             case '<':
1002             case '>':
1003 #endif
1004                 *str = '_';
1005         }
1006         str++;
1007     }
1008
1009 #if defined( WIN32 )
1010     // Change trailing spaces into underscores
1011     str--;
1012     while( str != str_base )
1013     {
1014         if( *str != ' ' )
1015             break;
1016         *str-- = '_';
1017     }
1018 #endif
1019
1020     return str_base;
1021 }
1022
1023 /**
1024  * Remove forbidden characters from full paths (leaves slashes)
1025  */
1026 void path_sanitize( char *str )
1027 {
1028 #ifdef WIN32
1029     /* check drive prefix if path is absolute */
1030     if( (((unsigned char)(str[0] - 'A') < 26)
1031       || ((unsigned char)(str[0] - 'a') < 26)) && (':' == str[1]) )
1032         str += 2;
1033 #endif
1034     while( *str )
1035     {
1036 #if defined( __APPLE__ )
1037         if( *str == ':' )
1038             *str = '_';
1039 #elif defined( WIN32 )
1040         if( strchr( "*\"?:|<>", *str ) )
1041             *str = '_';
1042         if( *str == '/' )
1043             *str = DIR_SEP_CHAR;
1044 #endif
1045         str++;
1046     }
1047 }
1048
1049 #include <vlc_url.h>
1050
1051 /**
1052  * Convert a file path to an URI.
1053  * If already an URI, return a copy of the string.
1054  * @path path path to convert (or URI to copy)
1055  * @return a nul-terminated URI string (use free() to release it),
1056  * or NULL in case of error
1057  */
1058 char *make_URI (const char *path)
1059 {
1060     if (path == NULL)
1061         return NULL;
1062     if (!strcmp (path, "-"))
1063         return strdup ("fd://0"); // standard input
1064     if (strstr (path, "://") != NULL)
1065         return strdup (path); /* Already an URI */
1066     /* Note: VLC cannot handle URI schemes without double slash after the
1067      * scheme name (such as mailto: or news:). */
1068
1069     char *buf;
1070 #ifdef WIN32
1071     if (isalpha (path[0]) && (path[1] == ':'))
1072     {
1073         if (asprintf (&buf, "file:///%c:", path[0]) == -1)
1074             buf = NULL;
1075         path += 2;
1076     }
1077     else
1078 #endif
1079     if (!strncmp (path, "\\\\", 2))
1080     {   /* Windows UNC paths */
1081 #ifndef WIN32
1082         /* \\host\share\path -> smb://host/share/path */
1083         if (strchr (path + 2, '\\') != NULL)
1084         {   /* Convert antislashes to slashes */
1085             char *dup = strdup (path);
1086             if (dup == NULL)
1087                 return NULL;
1088             for (size_t i = 2; dup[i]; i++)
1089                 if (dup[i] == '\\')
1090                     dup[i] = DIR_SEP_CHAR;
1091
1092             char *ret = make_URI (dup);
1093             free (dup);
1094             return ret;
1095         }
1096 # define SMB_SCHEME "smb"
1097 #else
1098         /* \\host\share\path -> file://host/share/path */
1099 # define SMB_SCHEME "file"
1100 #endif
1101         size_t hostlen = strcspn (path + 2, DIR_SEP);
1102
1103         buf = malloc (sizeof (SMB_SCHEME) + 3 + hostlen);
1104         if (buf != NULL)
1105             snprintf (buf, sizeof (SMB_SCHEME) + 3 + hostlen,
1106                       SMB_SCHEME"://%s", path + 2);
1107         path += 2 + hostlen;
1108     }
1109     else
1110     if (path[0] != DIR_SEP_CHAR)
1111     {   /* Relative path: prepend the current working directory */
1112         char cwd[PATH_MAX];
1113
1114         if (getcwd (cwd, sizeof (cwd)) == NULL) /* FIXME: UTF8? */
1115             return NULL;
1116         if (asprintf (&buf, "%s/%s", cwd, path) == -1)
1117             return NULL;
1118         char *ret = make_URI (buf);
1119         free (buf);
1120         return ret;
1121     }
1122     else
1123         buf = strdup ("file://");
1124     if (buf == NULL)
1125         return NULL;
1126
1127     assert (path[0] == DIR_SEP_CHAR);
1128
1129     /* Absolute file path */
1130     for (const char *ptr = path + 1;; ptr++)
1131     {
1132         size_t len = strcspn (ptr, DIR_SEP);
1133         char *component = encode_URI_bytes (ptr, len);
1134         if (component == NULL)
1135         {
1136             free (buf);
1137             return NULL;
1138         }
1139         char *uri;
1140         int val = asprintf (&uri, "%s/%s", buf, component);
1141         free (component);
1142         free (buf);
1143         if (val == -1)
1144             return NULL;
1145         buf = uri;
1146         ptr += len;
1147         if (*ptr == '\0')
1148             return buf;
1149     }
1150 }
1151
1152 /**
1153  * Tries to convert an URI to a local (UTF-8-encoded) file path.
1154  * @param url URI to convert
1155  * @return NULL on error, a nul-terminated string otherwise
1156  * (use free() to release it)
1157  */
1158 char *make_path (const char *url)
1159 {
1160     char *ret = NULL;
1161     char *end;
1162
1163     char *path = strstr (url, "://");
1164     if (path == NULL)
1165         return NULL; /* unsupported scheme or invalid syntax */
1166
1167     end = memchr (url, '/', path - url);
1168     size_t schemelen = ((end != NULL) ? end : path) - url;
1169     path += 3; /* skip "://" */
1170
1171     /* Remove HTML anchor if present */
1172     end = strchr (path, '#');
1173     if (end)
1174         path = strndup (path, end - path);
1175     else
1176         path = strdup (path);
1177     if (unlikely(path == NULL))
1178         return NULL; /* boom! */
1179
1180     /* Decode path */
1181     decode_URI (path);
1182
1183     if (schemelen == 4 && !strncasecmp (url, "file", 4))
1184     {
1185 #if (DIR_SEP_CHAR != '/')
1186         for (char *p = strchr (path, '/'); p; p = strchr (p, '/'))
1187             *p == DIR_SEP_CHAR;
1188 #endif
1189         if (*path == DIR_SEP_CHAR)
1190             return path;
1191
1192         /* Local path disguised as a remote one (MacOS X) */
1193         if (!strncasecmp (path, "localhost"DIR_SEP, 10))
1194         {
1195             memmove (path, path + 9, strlen (path + 9) + 1);
1196             return path;
1197         }
1198
1199 #ifdef WIN32
1200         if (*path && asprintf (&ret, "\\\\%s", path) == -1)
1201             ret = NULL;
1202 #endif
1203         /* non-local path :-( */
1204     }
1205     else
1206     if (schemelen == 2 && !strncasecmp (url, "fd", 2))
1207     {
1208         int fd = strtol (path, &end, 0);
1209
1210         if (*end)
1211             goto out;
1212
1213 #ifndef WIN32
1214         switch (fd)
1215         {
1216             case 0:
1217                 ret = strdup ("/dev/stdin");
1218                 break;
1219             case 1:
1220                 ret = strdup ("/dev/stdout");
1221                 break;
1222             case 2:
1223                 ret = strdup ("/dev/strerr");
1224                 break;
1225             default:
1226                 if (asprintf (&ret, "/dev/fd/%d", fd) == -1)
1227                     ret = NULL;
1228         }
1229 #else
1230         if (fd < 2)
1231             ret = strdup ("CON");
1232         else
1233             ret = NULL;
1234 #endif
1235     }
1236
1237 out:
1238     free (path);
1239     return ret; /* unknown scheme */
1240 }