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