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