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