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