]> git.sesse.net Git - vlc/blob - modules/control/http/util.c
* src/extras/libc.c: Implemented a wrapper around fork() and execve()
[vlc] / modules / control / http / util.c
1 /*****************************************************************************
2  * util.c : Utility functions for HTTP interface
3  *****************************************************************************
4  * Copyright (C) 2001-2005 the VideoLAN team
5  * $Id: http.c 12225 2005-08-18 10:01:30Z massiot $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Christophe Massiot <massiot@via.ecp.fr>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 #include "http.h"
27
28 /****************************************************************************
29  * File and directory functions
30  ****************************************************************************/
31
32 /* ToUrl: create a good name for an url from filename */
33 char *E_(FileToUrl)( char *name, vlc_bool_t *pb_index )
34 {
35     char *url, *p;
36
37     url = p = malloc( strlen( name ) + 1 );
38
39     *pb_index = VLC_FALSE;
40     if( !url || !p )
41     {
42         return NULL;
43     }
44
45 #ifdef WIN32
46     while( *name == '\\' || *name == '/' )
47 #else
48     while( *name == '/' )
49 #endif
50     {
51         name++;
52     }
53
54     *p++ = '/';
55     strcpy( p, name );
56
57 #ifdef WIN32
58     /* convert '\\' into '/' */
59     name = p;
60     while( *name )
61     {
62         if( *name == '\\' )
63             *name = '/';
64         name++;
65     }
66 #endif
67
68     /* index.* -> / */
69     if( ( p = strrchr( url, '/' ) ) != NULL )
70     {
71         if( !strncmp( p, "/index.", 7 ) )
72         {
73             p[1] = '\0';
74             *pb_index = VLC_TRUE;
75         }
76     }
77     return url;
78 }
79
80 /* Load a file */
81 int E_(FileLoad)( FILE *f, char **pp_data, int *pi_data )
82 {
83     int i_read;
84
85     /* just load the file */
86     *pi_data = 0;
87     *pp_data = malloc( 1025 );  /* +1 for \0 */
88     while( ( i_read = fread( &(*pp_data)[*pi_data], 1, 1024, f ) ) == 1024 )
89     {
90         *pi_data += 1024;
91         *pp_data = realloc( *pp_data, *pi_data  + 1025 );
92     }
93     if( i_read > 0 )
94     {
95         *pi_data += i_read;
96     }
97     (*pp_data)[*pi_data] = '\0';
98
99     return VLC_SUCCESS;
100 }
101
102 /* Parse a directory and recursively add files */
103 int E_(ParseDirectory)( intf_thread_t *p_intf, char *psz_root,
104                         char *psz_dir )
105 {
106     intf_sys_t     *p_sys = p_intf->p_sys;
107     char           dir[MAX_DIR_SIZE];
108 #ifdef HAVE_SYS_STAT_H
109     struct stat   stat_info;
110 #endif
111     DIR           *p_dir;
112     struct dirent *p_dir_content;
113     vlc_acl_t     *p_acl;
114     FILE          *file;
115
116     char          *user = NULL;
117     char          *password = NULL;
118
119     int           i_dirlen;
120
121     char sep;
122
123 #if defined( WIN32 )
124     sep = '\\';
125 #else
126     sep = '/';
127 #endif
128
129 #ifdef HAVE_SYS_STAT_H
130     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
131     {
132         return VLC_EGENERIC;
133     }
134 #endif
135
136     if( ( p_dir = opendir( psz_dir ) ) == NULL )
137     {
138         msg_Err( p_intf, "cannot open dir (%s)", psz_dir );
139         return VLC_EGENERIC;
140     }
141
142     i_dirlen = strlen( psz_dir );
143     if( i_dirlen + 10 > MAX_DIR_SIZE )
144     {
145         msg_Warn( p_intf, "skipping too deep dir (%s)", psz_dir );
146         return 0;
147     }
148
149     msg_Dbg( p_intf, "dir=%s", psz_dir );
150
151     sprintf( dir, "%s%c.access", psz_dir, sep );
152     if( ( file = fopen( dir, "r" ) ) != NULL )
153     {
154         char line[1024];
155         int  i_size;
156
157         msg_Dbg( p_intf, "find .access in dir=%s", psz_dir );
158
159         i_size = fread( line, 1, 1023, file );
160         if( i_size > 0 )
161         {
162             char *p;
163             while( i_size > 0 && ( line[i_size-1] == '\n' ||
164                    line[i_size-1] == '\r' ) )
165             {
166                 i_size--;
167             }
168
169             line[i_size] = '\0';
170
171             p = strchr( line, ':' );
172             if( p )
173             {
174                 *p++ = '\0';
175                 user = strdup( line );
176                 password = strdup( p );
177             }
178         }
179         msg_Dbg( p_intf, "using user=%s password=%s (read=%d)",
180                  user, password, i_size );
181
182         fclose( file );
183     }
184
185     sprintf( dir, "%s%c.hosts", psz_dir, sep );
186     p_acl = ACL_Create( p_intf, VLC_FALSE );
187     if( ACL_LoadFile( p_acl, dir ) )
188     {
189         ACL_Destroy( p_acl );
190         p_acl = NULL;
191     }
192
193     for( ;; )
194     {
195         /* parse psz_src dir */
196         if( ( p_dir_content = readdir( p_dir ) ) == NULL )
197         {
198             break;
199         }
200
201         if( ( p_dir_content->d_name[0] == '.' )
202          || ( i_dirlen + strlen( p_dir_content->d_name ) > MAX_DIR_SIZE ) )
203             continue;
204
205         sprintf( dir, "%s%c%s", psz_dir, sep, p_dir_content->d_name );
206         if( E_(ParseDirectory)( p_intf, psz_root, dir ) )
207         {
208             httpd_file_sys_t *f = NULL;
209             httpd_handler_sys_t *h = NULL;
210             vlc_bool_t b_index;
211             char *psz_tmp, *psz_file, *psz_name, *psz_ext;
212
213             psz_tmp = vlc_fix_readdir_charset( p_intf, dir );
214             psz_file = E_(FromUTF8)( p_intf, psz_tmp );
215             free( psz_tmp );
216             psz_tmp = vlc_fix_readdir_charset( p_intf,
217                                                &dir[strlen( psz_root )] );
218             psz_name = E_(FileToUrl)( psz_tmp, &b_index );
219             free( psz_tmp );
220             psz_ext = strrchr( psz_file, '.' );
221             if( psz_ext != NULL )
222             {
223                 int i;
224                 psz_ext++;
225                 for( i = 0; i < p_sys->i_handlers; i++ )
226                     if( !strcmp( p_sys->pp_handlers[i]->psz_ext, psz_ext ) )
227                         break;
228                 if( i < p_sys->i_handlers )
229                 {
230                     f = malloc( sizeof( httpd_handler_sys_t ) );
231                     h = (httpd_handler_sys_t *)f;
232                     f->b_handler = VLC_TRUE;
233                     h->p_association = p_sys->pp_handlers[i];
234                 }
235             }
236             if( f == NULL )
237             {
238                 f = malloc( sizeof( httpd_file_sys_t ) );
239                 f->b_handler = VLC_FALSE;
240             }
241
242             f->p_intf  = p_intf;
243             f->p_file = NULL;
244             f->p_redir = NULL;
245             f->p_redir2 = NULL;
246             f->file = psz_file;
247             f->name = psz_name;
248             f->b_html = strstr( &dir[strlen( psz_root )], ".htm" ) ? VLC_TRUE : VLC_FALSE;
249
250             if( !f->name )
251             {
252                 msg_Err( p_intf , "unable to parse directory" );
253                 closedir( p_dir );
254                 free( f );
255                 return( VLC_ENOMEM );
256             }
257             msg_Dbg( p_intf, "file=%s (url=%s)",
258                      f->file, f->name );
259
260             if( !f->b_handler )
261             {
262                 f->p_file = httpd_FileNew( p_sys->p_httpd_host,
263                                            f->name,
264                                            f->b_html ? p_sys->psz_html_type :
265                                             NULL,
266                                            user, password, p_acl,
267                                            E_(HttpCallback), f );
268                 if( f->p_file != NULL )
269                 {
270                     TAB_APPEND( p_sys->i_files, p_sys->pp_files, f );
271                 }
272             }
273             else
274             {
275                 h->p_handler = httpd_HandlerNew( p_sys->p_httpd_host,
276                                                  f->name,
277                                                  user, password, p_acl,
278                                                  E_(HandlerCallback), h );
279                 if( h->p_handler != NULL )
280                 {
281                     TAB_APPEND( p_sys->i_files, p_sys->pp_files,
282                                 (httpd_file_sys_t *)h );
283                 }
284             }
285
286             /* for url that ends by / add
287              *  - a redirect from rep to rep/
288              *  - in case of index.* rep/index.html to rep/ */
289             if( f && f->name[strlen(f->name) - 1] == '/' )
290             {
291                 char *psz_redir = strdup( f->name );
292                 char *p;
293                 psz_redir[strlen( psz_redir ) - 1] = '\0';
294
295                 msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name );
296                 f->p_redir = httpd_RedirectNew( p_sys->p_httpd_host, f->name, psz_redir );
297                 free( psz_redir );
298
299                 if( b_index && ( p = strstr( f->file, "index." ) ) )
300                 {
301                     asprintf( &psz_redir, "%s%s", f->name, p );
302
303                     msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name );
304                     f->p_redir2 = httpd_RedirectNew( p_sys->p_httpd_host,
305                                                      f->name, psz_redir );
306
307                     free( psz_redir );
308                 }
309             }
310         }
311     }
312
313     if( user )
314     {
315         free( user );
316     }
317     if( password )
318     {
319         free( password );
320     }
321
322     ACL_Destroy( p_acl );
323     closedir( p_dir );
324
325     return VLC_SUCCESS;
326 }
327
328
329 /**************************************************************************
330  * Locale functions
331  **************************************************************************/
332 char *E_(FromUTF8)( intf_thread_t *p_intf, char *psz_utf8 )
333 {
334     intf_sys_t    *p_sys = p_intf->p_sys;
335
336     if ( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
337     {
338         char *psz_in = psz_utf8;
339         size_t i_in = strlen(psz_in);
340         size_t i_out = i_in * 2;
341         char *psz_local = malloc(i_out + 1);
342         char *psz_out = psz_local;
343
344         size_t i_ret = vlc_iconv( p_sys->iconv_from_utf8, &psz_in, &i_in,
345                                   &psz_out, &i_out );
346         if( i_ret == (size_t)-1 || i_in )
347         {
348             msg_Warn( p_intf,
349                       "failed to convert \"%s\" to desired charset (%s)",
350                       psz_utf8, strerror(errno) );
351             free( psz_local );
352             return strdup( psz_utf8 );
353         }
354
355         *psz_out = '\0';
356         return psz_local;
357     }
358     else
359         return strdup( psz_utf8 );
360 }
361
362 char *E_(ToUTF8)( intf_thread_t *p_intf, char *psz_local )
363 {
364     intf_sys_t    *p_sys = p_intf->p_sys;
365
366     if ( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
367     {
368         char *psz_in = psz_local;
369         size_t i_in = strlen(psz_in);
370         size_t i_out = i_in * 6;
371         char *psz_utf8 = malloc(i_out + 1);
372         char *psz_out = psz_utf8;
373
374         size_t i_ret = vlc_iconv( p_sys->iconv_to_utf8, &psz_in, &i_in,
375                                   &psz_out, &i_out );
376         if( i_ret == (size_t)-1 || i_in )
377         {
378             msg_Warn( p_intf,
379                       "failed to convert \"%s\" to desired charset (%s)",
380                       psz_local, strerror(errno) );
381             free( psz_utf8 );
382             return strdup( psz_local );
383         }
384
385         *psz_out = '\0';
386         return psz_utf8;
387     }
388     else
389         return strdup( psz_local );
390 }
391
392 /*************************************************************************
393  * Playlist stuff
394  *************************************************************************/
395 void E_(PlaylistListNode)( intf_thread_t *p_intf, playlist_t *p_pl,
396                            playlist_item_t *p_node, char *name, mvar_t *s,
397                            int i_depth )
398 {
399     if( p_node != NULL )
400     {
401         if( p_node->i_children == -1 )
402         {
403             char value[512];
404             char *psz;
405             mvar_t *itm = E_(mvar_New)( name, "set" );
406
407             sprintf( value, "%d", ( p_pl->status.p_item == p_node )? 1 : 0 );
408             E_(mvar_AppendNewVar)( itm, "current", value );
409
410             sprintf( value, "%d", p_node->input.i_id );
411             E_(mvar_AppendNewVar)( itm, "index", value );
412
413             psz = E_(FromUTF8)( p_intf, p_node->input.psz_name );
414             E_(mvar_AppendNewVar)( itm, "name", psz );
415             free( psz );
416
417             psz = E_(FromUTF8)( p_intf, p_node->input.psz_uri );
418             E_(mvar_AppendNewVar)( itm, "uri", psz );
419             free( psz );
420
421             sprintf( value, "Item");
422             E_(mvar_AppendNewVar)( itm, "type", value );
423
424             sprintf( value, "%d", i_depth );
425             E_(mvar_AppendNewVar)( itm, "depth", value );
426
427             E_(mvar_AppendVar)( s, itm );
428         }
429         else
430         {
431             char value[512];
432             char *psz;
433             int i_child;
434             mvar_t *itm = E_(mvar_New)( name, "set" );
435
436             psz = E_(FromUTF8)( p_intf, p_node->input.psz_name );
437             E_(mvar_AppendNewVar)( itm, "name", psz );
438             E_(mvar_AppendNewVar)( itm, "uri", psz );
439             free( psz );
440
441             sprintf( value, "Node" );
442             E_(mvar_AppendNewVar)( itm, "type", value );
443
444             sprintf( value, "%d", p_node->input.i_id );
445             E_(mvar_AppendNewVar)( itm, "index", value );
446
447             sprintf( value, "%d", p_node->i_children);
448             E_(mvar_AppendNewVar)( itm, "i_children", value );
449
450             sprintf( value, "%d", i_depth );
451             E_(mvar_AppendNewVar)( itm, "depth", value );
452
453             E_(mvar_AppendVar)( s, itm );
454
455             for (i_child = 0 ; i_child < p_node->i_children ; i_child++)
456                 E_(PlaylistListNode)( p_intf, p_pl,
457                                       p_node->pp_children[i_child],
458                                       name, s, i_depth + 1);
459
460         }
461     }
462 }
463
464 /****************************************************************************
465  * Seek command parsing handling
466  ****************************************************************************/
467 void E_(HandleSeek)( intf_thread_t *p_intf, char *p_value )
468 {
469     intf_sys_t     *p_sys = p_intf->p_sys;
470     vlc_value_t val;
471     int i_stock = 0;
472     uint64_t i_length;
473     int i_value = 0;
474     int i_relative = 0;
475 #define POSITION_ABSOLUTE 12
476 #define POSITION_REL_FOR 13
477 #define POSITION_REL_BACK 11
478 #define VL_TIME_ABSOLUTE 0
479 #define VL_TIME_REL_FOR 1
480 #define VL_TIME_REL_BACK -1
481     if( p_sys->p_input )
482     {
483         var_Get( p_sys->p_input, "length", &val );
484         i_length = val.i_time;
485
486         while( p_value[0] != '\0' )
487         {
488             switch(p_value[0])
489             {
490                 case '+':
491                 {
492                     i_relative = VL_TIME_REL_FOR;
493                     p_value++;
494                     break;
495                 }
496                 case '-':
497                 {
498                     i_relative = VL_TIME_REL_BACK;
499                     p_value++;
500                     break;
501                 }
502                 case '0': case '1': case '2': case '3': case '4':
503                 case '5': case '6': case '7': case '8': case '9':
504                 {
505                     i_stock = strtol( p_value , &p_value , 10 );
506                     break;
507                 }
508                 case '%': /* for percentage ie position */
509                 {
510                     i_relative += POSITION_ABSOLUTE;
511                     i_value = i_stock;
512                     i_stock = 0;
513                     p_value[0] = '\0';
514                     break;
515                 }
516                 case ':':
517                 {
518                     i_value = 60 * (i_value + i_stock) ;
519                     i_stock = 0;
520                     p_value++;
521                     break;
522                 }
523                 case 'h': case 'H': /* hours */
524                 {
525                     i_value += 3600 * i_stock;
526                     i_stock = 0;
527                     /* other characters which are not numbers are not important */
528                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
529                     {
530                         p_value++;
531                     }
532                     break;
533                 }
534                 case 'm': case 'M': case '\'': /* minutes */
535                 {
536                     i_value += 60 * i_stock;
537                     i_stock = 0;
538                     p_value++;
539                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
540                     {
541                         p_value++;
542                     }
543                     break;
544                 }
545                 case 's': case 'S': case '"':  /* seconds */
546                 {
547                     i_value += i_stock;
548                     i_stock = 0;
549                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
550                     {
551                         p_value++;
552                     }
553                     break;
554                 }
555                 default:
556                 {
557                     p_value++;
558                     break;
559                 }
560             }
561         }
562
563         /* if there is no known symbol, I consider it as seconds. Otherwise, i_stock = 0 */
564         i_value += i_stock;
565
566         switch(i_relative)
567         {
568             case VL_TIME_ABSOLUTE:
569             {
570                 if( (uint64_t)( i_value ) * 1000000 <= i_length )
571                     val.i_time = (uint64_t)( i_value ) * 1000000;
572                 else
573                     val.i_time = i_length;
574
575                 var_Set( p_sys->p_input, "time", val );
576                 msg_Dbg( p_intf, "requested seek position: %dsec", i_value );
577                 break;
578             }
579             case VL_TIME_REL_FOR:
580             {
581                 var_Get( p_sys->p_input, "time", &val );
582                 if( (uint64_t)( i_value ) * 1000000 + val.i_time <= i_length )
583                 {
584                     val.i_time = ((uint64_t)( i_value ) * 1000000) + val.i_time;
585                 } else
586                 {
587                     val.i_time = i_length;
588                 }
589                 var_Set( p_sys->p_input, "time", val );
590                 msg_Dbg( p_intf, "requested seek position forward: %dsec", i_value );
591                 break;
592             }
593             case VL_TIME_REL_BACK:
594             {
595                 var_Get( p_sys->p_input, "time", &val );
596                 if( (int64_t)( i_value ) * 1000000 > val.i_time )
597                 {
598                     val.i_time = 0;
599                 } else
600                 {
601                     val.i_time = val.i_time - ((uint64_t)( i_value ) * 1000000);
602                 }
603                 var_Set( p_sys->p_input, "time", val );
604                 msg_Dbg( p_intf, "requested seek position backward: %dsec", i_value );
605                 break;
606             }
607             case POSITION_ABSOLUTE:
608             {
609                 val.f_float = __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
610                 var_Set( p_sys->p_input, "position", val );
611                 msg_Dbg( p_intf, "requested seek percent: %d", i_value );
612                 break;
613             }
614             case POSITION_REL_FOR:
615             {
616                 var_Get( p_sys->p_input, "position", &val );
617                 val.f_float += __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
618                 var_Set( p_sys->p_input, "position", val );
619                 msg_Dbg( p_intf, "requested seek percent forward: %d", i_value );
620                 break;
621             }
622             case POSITION_REL_BACK:
623             {
624                 var_Get( p_sys->p_input, "position", &val );
625                 val.f_float -= __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
626                 var_Set( p_sys->p_input, "position", val );
627                 msg_Dbg( p_intf, "requested seek percent backward: %d", i_value );
628                 break;
629             }
630             default:
631             {
632                 msg_Dbg( p_intf, "requested seek: what the f*** is going on here ?" );
633                 break;
634             }
635         }
636     }
637 #undef POSITION_ABSOLUTE
638 #undef POSITION_REL_FOR
639 #undef POSITION_REL_BACK
640 #undef VL_TIME_ABSOLUTE
641 #undef VL_TIME_REL_FOR
642 #undef VL_TIME_REL_BACK
643 }
644
645
646 /****************************************************************************
647  * URI Parsing functions
648  ****************************************************************************/
649 int E_(TestURIParam)( char *psz_uri, const char *psz_name )
650 {
651     char *p = psz_uri;
652
653     while( (p = strstr( p, psz_name )) )
654     {
655         /* Verify that we are dealing with a post/get argument */
656         if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n')
657               && p[strlen(psz_name)] == '=' )
658         {
659             return VLC_TRUE;
660         }
661         p++;
662     }
663
664     return VLC_FALSE;
665 }
666 char *E_(ExtractURIValue)( char *psz_uri, const char *psz_name,
667                              char *psz_value, int i_value_max )
668 {
669     char *p = psz_uri;
670
671     while( (p = strstr( p, psz_name )) )
672     {
673         /* Verify that we are dealing with a post/get argument */
674         if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n')
675               && p[strlen(psz_name)] == '=' )
676             break;
677         p++;
678     }
679
680     if( p )
681     {
682         int i_len;
683
684         p += strlen( psz_name );
685         if( *p == '=' ) p++;
686
687         if( strchr( p, '&' ) )
688         {
689             i_len = strchr( p, '&' ) - p;
690         }
691         else
692         {
693             /* for POST method */
694             if( strchr( p, '\n' ) )
695             {
696                 i_len = strchr( p, '\n' ) - p;
697                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
698             }
699             else
700             {
701                 i_len = strlen( p );
702             }
703         }
704         i_len = __MIN( i_value_max - 1, i_len );
705         if( i_len > 0 )
706         {
707             strncpy( psz_value, p, i_len );
708             psz_value[i_len] = '\0';
709         }
710         else
711         {
712             strncpy( psz_value, "", i_value_max );
713         }
714         p += i_len;
715     }
716     else
717     {
718         strncpy( psz_value, "", i_value_max );
719     }
720
721     return p;
722 }
723
724 void E_(DecodeEncodedURI)( char *psz )
725 {
726     char *dup = strdup( psz );
727     char *p = dup;
728
729     while( *p )
730     {
731         if( *p == '%' )
732         {
733             char val[3];
734             p++;
735             if( !*p )
736             {
737                 break;
738             }
739
740             val[0] = *p++;
741             val[1] = *p++;
742             val[2] = '\0';
743
744             *psz++ = strtol( val, NULL, 16 );
745         }
746         else if( *p == '+' )
747         {
748             *psz++ = ' ';
749             p++;
750         }
751         else
752         {
753             *psz++ = *p++;
754         }
755     }
756     *psz++ = '\0';
757     free( dup );
758 }
759
760 /* Since the resulting string is smaller we can work in place, so it is
761  * permitted to have psz == new. new points to the first word of the
762  * string, the function returns the remaining string. */
763 char *E_(FirstWord)( char *psz, char *new )
764 {
765     vlc_bool_t b_end;
766
767     while( *psz == ' ' )
768         psz++;
769
770     while( *psz != '\0' && *psz != ' ' )
771     {
772         if( *psz == '\'' )
773         {
774             char c = *psz++;
775             while( *psz != '\0' && *psz != c )
776             {
777                 if( *psz == '\\' && psz[1] != '\0' )
778                     psz++;
779                 *new++ = *psz++;
780             }
781             if( *psz == c )
782                 psz++;
783         }
784         else
785         {
786             if( *psz == '\\' && psz[1] != '\0' )
787                 psz++;
788             *new++ = *psz++;
789         }
790     }
791     b_end = !*psz;
792
793     *new++ = '\0';
794     if( !b_end )
795         return psz + 1;
796     else
797         return NULL;
798 }
799
800 /**********************************************************************
801  * parse_MRL: parse the MRL, find the mrl string and the options,
802  * create an item with all information in it, and return the item.
803  * return NULL if there is an error.
804  **********************************************************************/
805 playlist_item_t *E_(MRLParse)( intf_thread_t *p_intf, char *_psz,
806                                    char *psz_name )
807 {
808     char *psz = strdup( _psz );
809     char *s_mrl = psz;
810     char *s_temp;
811     playlist_item_t * p_item = NULL;
812
813     /* extract the mrl */
814     s_temp = E_(FirstWord)( s_mrl, s_mrl );
815     if( s_temp == NULL )
816     {
817         s_temp = s_mrl + strlen( s_mrl );
818     }
819
820     p_item = playlist_ItemNew( p_intf, s_mrl, psz_name );
821     s_mrl = s_temp;
822
823     /* now we can take care of the options */
824     while( *s_mrl != '\0' )
825     {
826         s_temp = E_(FirstWord)( s_mrl, s_mrl );
827         if( s_mrl == '\0' )
828             break;
829         if( s_temp == NULL )
830         {
831             s_temp = s_mrl + strlen( s_mrl );
832         }
833         if( *s_mrl != ':' )
834             msg_Warn( p_intf, "invalid MRL option: %s", s_mrl );
835         else
836             playlist_ItemAddOption( p_item, s_mrl );
837         s_mrl = s_temp;
838     }
839
840     free( psz );
841     return p_item;
842 }
843
844 /**********************************************************************
845  * RealPath: parse ../, ~ and path stuff
846  **********************************************************************/
847 char *E_(RealPath)( intf_thread_t *p_intf, const char *psz_src )
848 {
849     char *psz_dir;
850     char *p;
851     int i_len = strlen(psz_src);
852     char sep;
853
854 #if defined( WIN32 )
855     sep = '\\';
856 #else
857     sep = '/';
858 #endif
859
860     psz_dir = malloc( i_len + 2 );
861     strcpy( psz_dir, psz_src );
862
863     /* Add a trailing sep to ease the .. step */
864     psz_dir[i_len] = sep;
865     psz_dir[i_len + 1] = '\0';
866
867 #ifdef WIN32
868     /* Convert all / to native separator */
869     p = psz_dir;
870     while( (p = strchr( p, '/' )) != NULL )
871     {
872         *p = sep;
873     }
874 #endif
875
876     /* Remove multiple separators and /./ */
877     p = psz_dir;
878     while( (p = strchr( p, sep )) != NULL )
879     {
880         if( p[1] == sep )
881             memmove( &p[1], &p[2], strlen(&p[2]) + 1 );
882         else if( p[1] == '.' && p[2] == sep )
883             memmove( &p[1], &p[3], strlen(&p[3]) + 1 );
884         else
885             p++;
886     }
887
888     if( psz_dir[0] == '~' )
889     {
890         char *dir = malloc( strlen(psz_dir)
891                              + strlen(p_intf->p_vlc->psz_homedir) );
892         /* This is incomplete : we should also support the ~cmassiot/ syntax. */
893         sprintf( dir, "%s%s", p_intf->p_vlc->psz_homedir, psz_dir + 1 );
894         free( psz_dir );
895         psz_dir = dir;
896     }
897
898     if( strlen(psz_dir) > 2 )
899     {
900         /* Fix all .. dir */
901         p = psz_dir + 3;
902         while( (p = strchr( p, sep )) != NULL )
903         {
904             if( p[-1] == '.' && p[-2] == '.' && p[-3] == sep )
905             {
906                 char *q;
907                 p[-3] = '\0';
908                 if( (q = strrchr( psz_dir, sep )) != NULL )
909                 {
910                     memmove( q + 1, p + 1, strlen(p + 1) + 1 );
911                     p = q + 1;
912                 }
913                 else
914                 {
915                     memmove( psz_dir, p, strlen(p) + 1 );
916                     p = psz_dir + 3;
917                 }
918             }
919             else
920                 p++;
921         }
922     }
923
924     /* Remove trailing sep if there are at least 2 sep in the string
925      * (handles the C:\ stuff) */
926     p = strrchr( psz_dir, sep );
927     if( p != NULL && p[1] == '\0' && p != strchr( psz_dir, sep ) )
928         *p = '\0';
929
930     return psz_dir;
931 }