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