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