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