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