Make it possible to force a distro.mak to use. This is helpfull in cross-compile...
[vlc.git] / src / text / strings.c
1 /*****************************************************************************
2  * strings.c: String related functions
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan dot org>
8  *          Daniel Stranger <vlc at schmaller dot de>
9  *          RĂ©mi Denis-Courmont <rem # videolan org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <vlc/vlc.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <assert.h>
34
35 /* Needed by str_format_time */
36 #include <time.h>
37
38 /* Needed by str_format_meta */
39 #include <vlc_input.h>
40 #include <vlc_meta.h>
41 #include <vlc_playlist.h>
42 #include <vlc_aout.h>
43
44 #include <vlc_strings.h>
45 #include <vlc_url.h>
46 #include <vlc_charset.h>
47
48 /**
49  * Unescape URI encoded string
50  * \return decoded duplicated string
51  */
52 char *unescape_URI_duplicate( const char *psz )
53 {
54     char *psz_dup = strdup( psz );
55     unescape_URI( psz_dup );
56     return psz_dup;
57 }
58
59 /**
60  * Unescape URI encoded string in place
61  * \return nothing
62  */
63 void unescape_URI( char *psz )
64 {
65     unsigned char *in = (unsigned char *)psz, *out = in, c;
66     if( psz == NULL )
67         return;
68
69     while( ( c = *in++ ) != '\0' )
70     {
71         switch( c )
72         {
73             case '%':
74             {
75                 char val[5], *pval = val;
76                 unsigned long cp;
77
78                 switch( c = *in++ )
79                 {
80                     case '\0':
81                         return;
82
83                     case 'u':
84                     case 'U':
85                         if( ( *pval++ = *in++ ) == '\0' )
86                             return;
87                         if( ( *pval++ = *in++ ) == '\0' )
88                             return;
89                         c = *in++;
90
91                     default:
92                         *pval++ = c;
93                         if( ( *pval++ = *in++ ) == '\0' )
94                             return;
95                         *pval = '\0';
96                 }
97
98                 cp = strtoul( val, NULL, 0x10 );
99                 if( cp < 0x80 )
100                     *out++ = cp;
101                 else
102                 if( cp < 0x800 )
103                 {
104                     *out++ = (( cp >>  6)         | 0xc0);
105                     *out++ = (( cp        & 0x3f) | 0x80);
106                 }
107                 else
108                 {
109                     assert( cp < 0x10000 );
110                     *out++ = (( cp >> 12)         | 0xe0);
111                     *out++ = (((cp >>  6) & 0x3f) | 0x80);
112                     *out++ = (( cp        & 0x3f) | 0x80);
113                 }
114                 break;
115             }
116
117             /* + is not a special case - it means plus, not space. */
118
119             default:
120                 /* Inserting non-ASCII or non-printable characters is unsafe,
121                  * and no sane browser will send these unencoded */
122                 if( ( c < 32 ) || ( c > 127 ) )
123                     *out++ = '?';
124                 else
125                     *out++ = c;
126         }
127     }
128     *out = '\0';
129 }
130
131 /**
132  * Decode encoded URI string
133  * \return decoded duplicated string
134  */
135 char *decode_URI_duplicate( const char *psz )
136 {
137     char *psz_dup = strdup( psz );
138     decode_URI( psz_dup );
139     return psz_dup;
140 }
141
142 /**
143  * Decode encoded URI string in place
144  * \return nothing
145  */
146 void decode_URI( char *psz )
147 {
148     unsigned char *in = (unsigned char *)psz, *out = in, c;
149     if( psz == NULL )
150         return;
151
152     while( ( c = *in++ ) != '\0' )
153     {
154         switch( c )
155         {
156             case '%':
157             {
158                 char hex[3];
159
160                 if( ( ( hex[0] = *in++ ) == 0 )
161                  || ( ( hex[1] = *in++ ) == 0 ) )
162                     return;
163
164                 hex[2] = '\0';
165                 *out++ = (unsigned char)strtoul( hex, NULL, 0x10 );
166                 break;
167             }
168
169             case '+':
170                 *out++ = ' ';
171                 break;
172
173             default:
174                 /* Inserting non-ASCII or non-printable characters is unsafe,
175                  * and no sane browser will send these unencoded */
176                 if( ( c < 32 ) || ( c > 127 ) )
177                     *out++ = '?';
178                 else
179                     *out++ = c;
180         }
181     }
182     *out = '\0';
183     EnsureUTF8( psz );
184 }
185
186 static inline int isurlsafe( int c )
187 {
188     return ( (unsigned char)( c - 'a' ) < 26 )
189             || ( (unsigned char)( c - 'A' ) < 26 )
190             || ( (unsigned char)( c - '0' ) < 10 )
191         /* Hmm, we should not encode character that are allowed in URLs
192          * (even if they are not URL-safe), nor URL-safe characters.
193          * We still encode some of them because of Microsoft's crap browser.
194          */
195             || ( strchr( "-_.", c ) != NULL );
196 }
197
198 static inline char url_hexchar( int c )
199 {
200     return ( c < 10 ) ? c + '0' : c + 'A' - 10;
201 }
202
203 /**
204  * encode_URI_component
205  * Encodes an URI component.
206  *
207  * @param psz_url nul-terminated UTF-8 representation of the component.
208  * Obviously, you can't pass an URI containing a nul character, but you don't
209  * want to do that, do you?
210  *
211  * @return encoded string (must be free()'d)
212  */
213 char *encode_URI_component( const char *psz_url )
214 {
215     char psz_enc[3 * strlen( psz_url ) + 1], *out = psz_enc;
216     const uint8_t *in;
217
218     for( in = (const uint8_t *)psz_url; *in; in++ )
219     {
220         uint8_t c = *in;
221
222         if( isurlsafe( c ) )
223             *out++ = (char)c;
224         else
225         if ( c == ' ')
226             *out++ = '+';
227         else
228         {
229             *out++ = '%';
230             *out++ = url_hexchar( c >> 4 );
231             *out++ = url_hexchar( c & 0xf );
232         }
233     }
234     *out++ = '\0';
235
236     return strdup( psz_enc );
237 }
238
239 /**
240  * Converts "&lt;", "&gt;" and "&amp;" to "<", ">" and "&"
241  * \param string to convert
242  */
243 void resolve_xml_special_chars( char *psz_value )
244 {
245     char *p_pos = psz_value;
246
247     while ( *psz_value )
248     {
249         if( !strncmp( psz_value, "&lt;", 4 ) )
250         {
251             *p_pos = '<';
252             psz_value += 4;
253         }
254         else if( !strncmp( psz_value, "&gt;", 4 ) )
255         {
256             *p_pos = '>';
257             psz_value += 4;
258         }
259         else if( !strncmp( psz_value, "&amp;", 5 ) )
260         {
261             *p_pos = '&';
262             psz_value += 5;
263         }
264         else if( !strncmp( psz_value, "&quot;", 6 ) )
265         {
266             *p_pos = '\"';
267             psz_value += 6;
268         }
269         else if( !strncmp( psz_value, "&#039;", 6 ) )
270         {
271             *p_pos = '\'';
272             psz_value += 6;
273         }
274         else
275         {
276             *p_pos = *psz_value;
277             psz_value++;
278         }
279
280         p_pos++;
281     }
282
283     *p_pos = '\0';
284 }
285
286 /**
287  * Converts '<', '>', '\"', '\'' and '&' to their html entities
288  * \param psz_content simple element content that is to be converted
289  */
290 char *convert_xml_special_chars( const char *psz_content )
291 {
292     char *psz_temp = malloc( 6 * strlen( psz_content ) + 1 );
293     const char *p_from = psz_content;
294     char *p_to   = psz_temp;
295
296     while ( *p_from )
297     {
298         if ( *p_from == '<' )
299         {
300             strcpy( p_to, "&lt;" );
301             p_to += 4;
302         }
303         else if ( *p_from == '>' )
304         {
305             strcpy( p_to, "&gt;" );
306             p_to += 4;
307         }
308         else if ( *p_from == '&' )
309         {
310             strcpy( p_to, "&amp;" );
311             p_to += 5;
312         }
313         else if( *p_from == '\"' )
314         {
315             strcpy( p_to, "&quot;" );
316             p_to += 6;
317         }
318         else if( *p_from == '\'' )
319         {
320             strcpy( p_to, "&#039;" );
321             p_to += 6;
322         }
323         else
324         {
325             *p_to = *p_from;
326             p_to++;
327         }
328         p_from++;
329     }
330     *p_to = '\0';
331
332     return psz_temp;
333 }
334
335 /* Base64 encoding */
336 char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src )
337 {
338     static const char b64[] =
339            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
340
341     char *ret = malloc( ( i_src + 4 ) * 4 / 3 );
342     char *dst = ret;
343
344     if( dst == NULL )
345         return NULL;
346
347     while( i_src > 0 )
348     {
349         /* pops (up to) 3 bytes of input, push 4 bytes */
350         uint32_t v;
351
352         /* 1/3 -> 1/4 */
353         v = *src++ << 24;
354         *dst++ = b64[v >> 26];
355         v = v << 6;
356
357         /* 2/3 -> 2/4 */
358         if( i_src >= 2 )
359             v |= *src++ << 22;
360         *dst++ = b64[v >> 26];
361         v = v << 6;
362
363         /* 3/3 -> 3/4 */
364         if( i_src >= 3 )
365             v |= *src++ << 20; // 3/3
366         *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4
367         v = v << 6;
368
369         /* -> 4/4 */
370         *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4
371
372         if( i_src <= 3 )
373             break;
374         i_src -= 3;
375     }
376
377     *dst = '\0';
378
379     return ret;
380 }
381
382 char *vlc_b64_encode( const char *src )
383 {
384     if( src )
385         return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) );
386     else
387         return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
388 }
389
390 /* Base64 decoding */
391 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src )
392 {
393     static const int b64[256] = {
394         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
395         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
396         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
397         52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
398         -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
399         15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
400         -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
401         41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
402         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
403         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
404         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
405         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
406         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
407         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
408         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
409         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
410     };
411     uint8_t *p_start = p_dst;
412     uint8_t *p = (uint8_t *)p_src;
413
414     int i_level;
415     int i_last;
416
417     for( i_level = 0, i_last = 0; i_dst > 0 && *p != '\0'; i_dst--, p++ )
418     {
419         const int c = b64[(unsigned int)*p];
420         if( c == -1 )
421             continue;
422
423         switch( i_level )
424         {
425             case 0:
426                 i_level++;
427                 break;
428             case 1:
429                 *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 );
430                 i_level++;
431                 break;
432             case 2:
433                 *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
434                 i_level++;
435                 break;
436             case 3:
437                 *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c;
438                 i_level = 0;
439         }
440         i_last = c;
441     }
442
443     return p_dst - p_start;
444 }
445 size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
446 {
447     const int i_src = strlen( psz_src );
448     uint8_t   *p_dst;
449
450     *pp_dst = p_dst = malloc( i_src );
451     if( !p_dst )
452         return 0;
453     return  vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
454 }
455 char *vlc_b64_decode( const char *psz_src )
456 {
457     const int i_src = strlen( psz_src );
458     char *p_dst = malloc( i_src + 1 );
459     size_t i_dst;
460     if( !p_dst )
461         return NULL;
462
463     i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
464     p_dst[i_dst] = '\0';
465
466     return p_dst;
467 }
468
469 /****************************************************************************
470  * String formating functions
471  ****************************************************************************/
472 char *str_format_time( const char *tformat )
473 {
474     char buffer[255];
475     time_t curtime;
476 #if defined(HAVE_LOCALTIME_R)
477     struct tm loctime;
478 #else
479     struct tm *loctime;
480 #endif
481
482     /* Get the current time.  */
483     curtime = time( NULL );
484
485     /* Convert it to local time representation.  */
486 #if defined(HAVE_LOCALTIME_R)
487     localtime_r( &curtime, &loctime );
488     strftime( buffer, 255, tformat, &loctime );
489 #else
490     loctime = localtime( &curtime );
491     strftime( buffer, 255, tformat, loctime );
492 #endif
493     return strdup( buffer );
494 }
495
496 #define INSERT_STRING( check, string )                              \
497                     if( check && string )                           \
498                     {                                               \
499                         int len = strlen( string );                 \
500                         dst = realloc( dst,                         \
501                                        i_size = i_size + len + 1 ); \
502                         strncpy( d, string, len+1 );                \
503                         d += len;                                   \
504                     }                                               \
505                     else                                            \
506                     {                                               \
507                         *d = '-';                                   \
508                         d++;                                        \
509                     }
510 char *__str_format_meta( vlc_object_t *p_object, const char *string )
511 {
512     const char *s = string;
513     char *dst = malloc( 1000 );
514     char *d = dst;
515     int b_is_format = 0;
516     char buf[10];
517     int i_size = strlen( string );
518
519     playlist_t *p_playlist = pl_Yield( p_object );
520     input_thread_t *p_input = p_playlist->p_input;
521     input_item_t *p_item = NULL;
522     pl_Release( p_object );
523     if( p_input )
524     {
525         vlc_object_yield( p_input );
526         p_item = input_GetItem(p_input);
527         if( p_item )
528             vlc_mutex_lock( &p_item->lock );
529     }
530
531     sprintf( dst, string );
532
533     while( *s )
534     {
535         if( b_is_format )
536         {
537             switch( *s )
538             {
539                 case 'a':
540                     INSERT_STRING( p_item && p_item->p_meta,
541                                    p_item->p_meta->psz_artist );
542                     break;
543                 case 'b':
544                     INSERT_STRING( p_item && p_item->p_meta,
545                                    p_item->p_meta->psz_album );
546                     break;
547                 case 'c':
548                     INSERT_STRING( p_item && p_item->p_meta,
549                                    p_item->p_meta->psz_copyright );
550                     break;
551                 case 'd':
552                     INSERT_STRING( p_item && p_item->p_meta,
553                                    p_item->p_meta->psz_description );
554                     break;
555                 case 'e':
556                     INSERT_STRING( p_item && p_item->p_meta,
557                                    p_item->p_meta->psz_encodedby );
558                     break;
559                 case 'g':
560                     INSERT_STRING( p_item && p_item->p_meta,
561                                    p_item->p_meta->psz_genre );
562                     break;
563                 case 'l':
564                     INSERT_STRING( p_item && p_item->p_meta,
565                                    p_item->p_meta->psz_language );
566                     break;
567                 case 'n':
568                     INSERT_STRING( p_item && p_item->p_meta,
569                                    p_item->p_meta->psz_tracknum );
570                     break;
571                 case 'p':
572                     INSERT_STRING( p_item && p_item->p_meta,
573                                    p_item->p_meta->psz_nowplaying );
574                     break;
575                 case 'r':
576                     INSERT_STRING( p_item && p_item->p_meta,
577                                    p_item->p_meta->psz_rating );
578                     break;
579                 case 's':
580                 {
581                     char *lang;
582                     if( p_input )
583                     {
584                         lang = var_GetString( p_input, "sub-language" );
585                     }
586                     else
587                     {
588                         lang = strdup( "-" );
589                     }
590                     INSERT_STRING( 1, lang );
591                     free( lang );
592                     break;
593                 }
594                 case 't':
595                     INSERT_STRING( p_item && p_item->p_meta,
596                                    p_item->p_meta->psz_title );
597                     break;
598                 case 'u':
599                     INSERT_STRING( p_item && p_item->p_meta,
600                                    p_item->p_meta->psz_url );
601                     break;
602                 case 'A':
603                     INSERT_STRING( p_item && p_item->p_meta,
604                                    p_item->p_meta->psz_date );
605                     break;
606                 case 'B':
607                     if( p_input )
608                     {
609                         snprintf( buf, 10, "%d",
610                                   var_GetInteger( p_input, "bit-rate" )/1000 );
611                     }
612                     else
613                     {
614                         sprintf( buf, "-" );
615                     }
616                     INSERT_STRING( 1, buf );
617                     break;
618                 case 'C':
619                     if( p_input )
620                     {
621                         snprintf( buf, 10, "%d",
622                                   var_GetInteger( p_input, "chapter" ) );
623                     }
624                     else
625                     {
626                         sprintf( buf, "-" );
627                     }
628                     INSERT_STRING( 1, buf );
629                     break;
630                 case 'D':
631                     if( p_item )
632                     {
633                         sprintf( buf, "%02d:%02d:%02d",
634                                  (int)(p_item->i_duration/(3600000000)),
635                                  (int)((p_item->i_duration/(60000000))%60),
636                                  (int)((p_item->i_duration/1000000)%60) );
637                     }
638                     else
639                     {
640                         sprintf( buf, "--:--:--" );
641                     }
642                     INSERT_STRING( 1, buf );
643                     break;
644                 case 'F':
645                     INSERT_STRING( p_item, p_item->psz_uri );
646                     break;
647                 case 'I':
648                     if( p_input )
649                     {
650                         snprintf( buf, 10, "%d",
651                                   var_GetInteger( p_input, "title" ) );
652                     }
653                     else
654                     {
655                         sprintf( buf, "-" );
656                     }
657                     INSERT_STRING( 1, buf );
658                     break;
659                 case 'L':
660                     if( p_item && p_input )
661                     {
662                         sprintf( buf, "%02d:%02d:%02d",
663                      (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
664                      (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
665                      (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
666                     }
667                     else
668                     {
669                         sprintf( buf, "--:--:--" );
670                     }
671                     INSERT_STRING( 1, buf );
672                     break;
673                 case 'N':
674                     INSERT_STRING( p_item, p_item->psz_name );
675                     break;
676                 case 'O':
677                 {
678                     char *lang;
679                     if( p_input )
680                     {
681                         lang = var_GetString( p_input, "audio-language" );
682                     }
683                     else
684                     {
685                         lang = strdup( "-" );
686                     }
687                     INSERT_STRING( 1, lang );
688                     free( lang );
689                     break;
690                 }
691                 case 'P':
692                     if( p_input )
693                     {
694                         snprintf( buf, 10, "%2.1lf",
695                                   var_GetFloat( p_input, "position" ) * 100. );
696                     }
697                     else
698                     {
699                         sprintf( buf, "--.-%%" );
700                     }
701                     INSERT_STRING( 1, buf );
702                     break;
703                 case 'R':
704                     if( p_input )
705                     {
706                         int r = var_GetInteger( p_input, "rate" );
707                         snprintf( buf, 10, "%d.%d", r/1000, r%1000 );
708                     }
709                     else
710                     {
711                         sprintf( buf, "-" );
712                     }
713                     INSERT_STRING( 1, buf );
714                     break;
715                 case 'S':
716                     if( p_input )
717                     {
718                         int r = var_GetInteger( p_input, "sample-rate" );
719                         snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
720                     }
721                     else
722                     {
723                         sprintf( buf, "-" );
724                     }
725                     INSERT_STRING( 1, buf );
726                     break;
727                 case 'T':
728                     if( p_input )
729                     {
730                         sprintf( buf, "%02d:%02d:%02d",
731                                  (int)(p_input->i_time/(3600000000)),
732                                  (int)((p_input->i_time/(60000000))%60),
733                                  (int)((p_input->i_time/1000000)%60) );
734                     }
735                     else
736                     {
737                         sprintf( buf, "--:--:--" );
738                     }
739                     INSERT_STRING( 1, buf );
740                     break;
741                 case 'U':
742                     INSERT_STRING( p_item && p_item->p_meta,
743                                    p_item->p_meta->psz_publisher );
744                     break;
745                 case 'V':
746                 {
747                     audio_volume_t volume;
748                     aout_VolumeGet( p_object, &volume );
749                     snprintf( buf, 10, "%d", volume );
750                     INSERT_STRING( 1, buf );
751                     break;
752                 }
753                 case '_':
754                     *d = '\n';
755                     d++;
756                     break;
757
758                 default:
759                     *d = *s;
760                     d++;
761                     break;
762             }
763             b_is_format = 0;
764         }
765         else if( *s == '$' )
766         {
767             b_is_format = 1;
768         }
769         else
770         {
771             *d = *s;
772             d++;
773         }
774         s++;
775     }
776     *d = '\0';
777
778     if( p_input )
779     {
780         vlc_object_release( p_input );
781         if( p_item )
782             vlc_mutex_unlock( &p_item->lock );
783     }
784
785     return dst;
786 }
787
788 /**
789  * Apply str format time and str format meta
790  */
791 char *__str_format( vlc_object_t *p_this, const char *psz_src )
792 {
793     char *psz_buf1, *psz_buf2;
794     psz_buf1 = str_format_time( psz_src );
795     psz_buf2 = str_format_meta( p_this, psz_buf1 );
796     free( psz_buf1 );
797     return psz_buf2;
798 }
799
800 /**
801  * Remove forbidden characters from filenames (including slashes)
802  */
803 void filename_sanitize( char *str )
804 {
805     if( *str == '.' && (str[1] == '\0' || (str[1] == '.' && str[2] == '\0' ) ) )
806     {
807         while( *str )
808         {
809             *str = '_';
810             str++;
811         }
812         return;
813     }
814
815     while( *str )
816     {
817         switch( *str )
818         {
819             case '/':
820 #ifdef WIN32
821             case '\\':
822             case '*':
823             case '"':
824             case '?':
825             case ':':
826             case '|':
827             case '<':
828             case '>':
829 #endif
830                 *str = '_';
831         }
832         str++;
833     }
834 }
835
836 /**
837  * Remove forbidden characters from full paths (leaves slashes)
838  */
839 void path_sanitize( char *str )
840 {
841 #if 0
842     /*
843      * Uncomment the two blocks to prevent /../ or /./, i'm not sure that we
844      * want to.
845      */
846     char *prev = str - 1;
847 #endif
848 #ifdef WIN32
849     /* check drive prefix if path is absolute */
850     if( isalpha(*str) && (':' == *(str+1)) )
851         str += 2;
852 #endif
853     while( *str )
854     {
855 #ifdef WIN32
856         switch( *str )
857         {
858             case '*':
859             case '"':
860             case '?':
861             case ':':
862             case '|':
863             case '<':
864             case '>':
865                 *str = '_';
866         }
867 #endif
868 #if 0
869         if( *str == '/'
870 #ifdef WIN32
871             || *str == '\\'
872 #endif
873             )
874         {
875             if( str - prev == 2 && prev[1] == '.' )
876             {
877                 prev[1] = '.';
878             }
879             else if( str - prev == 3 && prev[1] == '.' && prev[2] == '.' )
880             {
881                 prev[1] = '_';
882                 prev[2] = '_';
883             }
884             prev = str;
885         }
886 #endif
887         str++;
888     }
889 }