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