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