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