]> git.sesse.net Git - vlc/blob - src/extras/libc.c
* src/extras/libc.c: Implemented vlc_execve() for the Win32 platform.
[vlc] / src / extras / libc.c
1 /*****************************************************************************
2  * libc.c: Extra libc function for some systems.
3  *****************************************************************************
4  * Copyright (C) 2002 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Gildas Bazin <gbazin@videolan.org>
10  *          Derk-Jan Hartman <hartman at videolan dot org>
11  *          Christophe Massiot <massiot@via.ecp.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27 #include <string.h>                                              /* strdup() */
28 #include <stdlib.h>
29 #include <ctype.h>
30
31 #include <vlc/vlc.h>
32
33 #undef iconv_t
34 #undef iconv_open
35 #undef iconv
36 #undef iconv_close
37
38 #if defined(HAVE_ICONV)
39 #   include <iconv.h>
40 #endif
41
42 #ifdef HAVE_DIRENT_H
43 #   include <dirent.h>
44 #endif
45
46 #ifdef HAVE_FORK
47 #   include <sys/time.h>
48 #   include <unistd.h>
49 #   include <errno.h>
50 #   include <sys/wait.h>
51 #endif
52
53 #if defined(WIN32) || defined(UNDER_CE)
54 #   define WIN32_LEAN_AND_MEAN
55 #   include <windows.h>
56 #endif
57
58 /*****************************************************************************
59  * getenv: just in case, but it should never be called
60  *****************************************************************************/
61 #if !defined( HAVE_GETENV )
62 char *vlc_getenv( const char *name )
63 {
64     return NULL;
65 }
66 #endif
67
68 /*****************************************************************************
69  * strdup: returns a malloc'd copy of a string
70  *****************************************************************************/
71 #if !defined( HAVE_STRDUP )
72 char *vlc_strdup( const char *string )
73 {
74     return strndup( string, strlen( string ) );
75 }
76 #endif
77
78 /*****************************************************************************
79  * strndup: returns a malloc'd copy of at most n bytes of string
80  * Does anyone know whether or not it will be present in Jaguar?
81  *****************************************************************************/
82 #if !defined( HAVE_STRNDUP )
83 char *vlc_strndup( const char *string, size_t n )
84 {
85     char *psz;
86     size_t len = strlen( string );
87
88     len = __MIN( len, n );
89     psz = (char*)malloc( len + 1 );
90
91     if( psz != NULL )
92     {
93         memcpy( (void*)psz, (const void*)string, len );
94         psz[ len ] = 0;
95     }
96
97     return psz;
98 }
99 #endif
100
101 /*****************************************************************************
102  * strcasecmp: compare two strings ignoring case
103  *****************************************************************************/
104 #if !defined( HAVE_STRCASECMP ) && !defined( HAVE_STRICMP )
105 int vlc_strcasecmp( const char *s1, const char *s2 )
106 {
107     int c1, c2;
108     if( !s1 || !s2 ) return  -1;
109
110     while( *s1 && *s2 )
111     {
112         c1 = tolower(*s1);
113         c2 = tolower(*s2);
114
115         if( c1 != c2 ) return (c1 < c2 ? -1 : 1);
116         s1++; s2++;
117     }
118
119     if( !*s1 && !*s2 ) return 0;
120     else return (*s1 ? 1 : -1);
121 }
122 #endif
123
124 /*****************************************************************************
125  * strncasecmp: compare n chars from two strings ignoring case
126  *****************************************************************************/
127 #if !defined( HAVE_STRNCASECMP ) && !defined( HAVE_STRNICMP )
128 int vlc_strncasecmp( const char *s1, const char *s2, size_t n )
129 {
130     int c1, c2;
131     if( !s1 || !s2 ) return  -1;
132
133     while( n > 0 && *s1 && *s2 )
134     {
135         c1 = tolower(*s1);
136         c2 = tolower(*s2);
137
138         if( c1 != c2 ) return (c1 < c2 ? -1 : 1);
139         s1++; s2++; n--;
140     }
141
142     if( !n || (!*s1 && !*s2) ) return 0;
143     else return (*s1 ? 1 : -1);
144 }
145 #endif
146
147 /******************************************************************************
148  * strcasestr: find a substring (little) in another substring (big)
149  * Case sensitive. Return NULL if not found, return big if little == null
150  *****************************************************************************/
151 #if !defined( HAVE_STRCASESTR ) && !defined( HAVE_STRISTR )
152 char * vlc_strcasestr( const char *psz_big, const char *psz_little )
153 {
154     char *p_pos = (char *)psz_big;
155
156     if( !psz_big || !psz_little || !*psz_little ) return p_pos;
157  
158     while( *p_pos ) 
159     {
160         if( toupper( *p_pos ) == toupper( *psz_little ) )
161         {
162             char * psz_cur1 = p_pos + 1;
163             char * psz_cur2 = (char *)psz_little + 1;
164             while( *psz_cur1 && *psz_cur2 &&
165                    toupper( *psz_cur1 ) == toupper( *psz_cur2 ) )
166             {
167                 psz_cur1++;
168                 psz_cur2++;
169             }
170             if( !*psz_cur2 ) return p_pos;
171         }
172         p_pos++;
173     }
174     return NULL;
175 }
176 #endif
177
178 /*****************************************************************************
179  * vasprintf:
180  *****************************************************************************/
181 #if !defined(HAVE_VASPRINTF) || defined(SYS_DARWIN) || defined(SYS_BEOS)
182 int vlc_vasprintf(char **strp, const char *fmt, va_list ap)
183 {
184     /* Guess we need no more than 100 bytes. */
185     int     i_size = 100;
186     char    *p = malloc( i_size );
187     int     n;
188
189     if( p == NULL )
190     {
191         *strp = NULL;
192         return -1;
193     }
194
195     for( ;; )
196     {
197         /* Try to print in the allocated space. */
198         n = vsnprintf( p, i_size, fmt, ap );
199
200         /* If that worked, return the string. */
201         if (n > -1 && n < i_size)
202         {
203             *strp = p;
204             return strlen( p );
205         }
206         /* Else try again with more space. */
207         if (n > -1)    /* glibc 2.1 */
208         {
209            i_size = n+1; /* precisely what is needed */
210         }
211         else           /* glibc 2.0 */
212         {
213            i_size *= 2;  /* twice the old size */
214         }
215         if( (p = realloc( p, i_size ) ) == NULL)
216         {
217             *strp = NULL;
218             return -1;
219         }
220     }
221 }
222 #endif
223
224 /*****************************************************************************
225  * asprintf:
226  *****************************************************************************/
227 #if !defined(HAVE_ASPRINTF) || defined(SYS_DARWIN) || defined(SYS_BEOS)
228 int vlc_asprintf( char **strp, const char *fmt, ... )
229 {
230     va_list args;
231     int i_ret;
232
233     va_start( args, fmt );
234     i_ret = vasprintf( strp, fmt, args );
235     va_end( args );
236
237     return i_ret;
238 }
239 #endif
240
241 /*****************************************************************************
242  * atof: convert a string to a double.
243  *****************************************************************************/
244 #if !defined( HAVE_ATOF )
245 double vlc_atof( const char *nptr )
246 {
247     double f_result;
248     wchar_t *psz_tmp;
249     int i_len = strlen( nptr ) + 1;
250
251     psz_tmp = malloc( i_len * sizeof(wchar_t) );
252     MultiByteToWideChar( CP_ACP, 0, nptr, -1, psz_tmp, i_len );
253     f_result = wcstod( psz_tmp, NULL );
254     free( psz_tmp );
255
256     return f_result;
257 }
258 #endif
259
260 /*****************************************************************************
261  * strtoll: convert a string to a 64 bits int.
262  *****************************************************************************/
263 #if !defined( HAVE_STRTOLL )
264 int64_t vlc_strtoll( const char *nptr, char **endptr, int base )
265 {
266     int64_t i_value = 0;
267     int sign = 1, newbase = base ? base : 10;
268
269     while( isspace(*nptr) ) nptr++;
270
271     if( *nptr == '-' )
272     {
273         sign = -1;
274         nptr++;
275     }
276
277     /* Try to detect base */
278     if( *nptr == '0' )
279     {
280         newbase = 8;
281         nptr++;
282
283         if( *nptr == 'x' )
284         {
285             newbase = 16;
286             nptr++;
287         }
288     }
289
290     if( base && newbase != base )
291     {
292         if( endptr ) *endptr = (char *)nptr;
293         return i_value;
294     }
295
296     switch( newbase )
297     {
298         case 10:
299             while( *nptr >= '0' && *nptr <= '9' )
300             {
301                 i_value *= 10;
302                 i_value += ( *nptr++ - '0' );
303             }
304             if( endptr ) *endptr = (char *)nptr;
305             break;
306
307         case 16:
308             while( (*nptr >= '0' && *nptr <= '9') ||
309                    (*nptr >= 'a' && *nptr <= 'f') ||
310                    (*nptr >= 'A' && *nptr <= 'F') )
311             {
312                 int i_valc = 0;
313                 if(*nptr >= '0' && *nptr <= '9') i_valc = *nptr - '0';
314                 else if(*nptr >= 'a' && *nptr <= 'f') i_valc = *nptr - 'a' +10;
315                 else if(*nptr >= 'A' && *nptr <= 'F') i_valc = *nptr - 'A' +10;
316                 i_value *= 16;
317                 i_value += i_valc;
318                 nptr++;
319             }
320             if( endptr ) *endptr = (char *)nptr;
321             break;
322
323         default:
324             i_value = strtol( nptr, endptr, newbase );
325             break;
326     }
327
328     return i_value * sign;
329 }
330 #endif
331
332 /*****************************************************************************
333  * atoll: convert a string to a 64 bits int.
334  *****************************************************************************/
335 #if !defined( HAVE_ATOLL )
336 int64_t vlc_atoll( const char *nptr )
337 {
338     return strtoll( nptr, (char **)NULL, 10 );
339 }
340 #endif
341
342 /*****************************************************************************
343  * vlc_*dir_wrapper: wrapper under Windows to return the list of drive letters
344  * when called with an empty argument or just '\'
345  *****************************************************************************/
346 #if defined(WIN32) || defined(UNDER_CE)
347 typedef struct vlc_DIR
348 {
349     DIR *p_real_dir;
350     int i_drives;
351     struct dirent dd_dir;
352     vlc_bool_t b_insert_back;
353 } vlc_DIR;
354
355 void *vlc_opendir_wrapper( const char *psz_path )
356 {
357     vlc_DIR *p_dir;
358     DIR *p_real_dir;
359
360     if ( psz_path == NULL || psz_path[0] == '\0'
361           || (psz_path[0] == '\\' && psz_path[1] == '\0') )
362     {
363         /* Special mode to list drive letters */
364         p_dir = malloc( sizeof(vlc_DIR) );
365         p_dir->p_real_dir = NULL;
366         p_dir->i_drives = GetLogicalDrives();
367         return (void *)p_dir;
368     }
369
370     p_real_dir = opendir( psz_path );
371     if ( p_real_dir == NULL )
372         return NULL;
373
374     p_dir = malloc( sizeof(vlc_DIR) );
375     p_dir->p_real_dir = p_real_dir;
376     p_dir->b_insert_back = ( psz_path[1] == ':' && psz_path[2] == '\\'
377                               && psz_path[3] =='\0' );
378     return (void *)p_dir;
379 }
380
381 struct dirent *vlc_readdir_wrapper( void *_p_dir )
382 {
383     vlc_DIR *p_dir = (vlc_DIR *)_p_dir;
384     unsigned int i;
385     DWORD i_drives;
386
387     if ( p_dir->p_real_dir != NULL )
388     {
389         if ( p_dir->b_insert_back )
390         {
391             p_dir->dd_dir.d_ino = 0;
392             p_dir->dd_dir.d_reclen = 0;
393             p_dir->dd_dir.d_namlen = 2;
394             strcpy( p_dir->dd_dir.d_name, ".." );
395             p_dir->b_insert_back = VLC_FALSE;
396             return &p_dir->dd_dir;
397         }
398
399         return readdir( p_dir->p_real_dir );
400     }
401
402     /* Drive letters mode */
403     i_drives = p_dir->i_drives;
404     if ( !i_drives )
405         return NULL; /* end */
406
407     for ( i = 0; i < sizeof(DWORD)*8; i++, i_drives >>= 1 )
408         if ( i_drives & 1 ) break;
409
410     if ( i >= 26 )
411         return NULL; /* this should not happen */
412
413     sprintf( p_dir->dd_dir.d_name, "%c:\\", 'A' + i );
414     p_dir->dd_dir.d_namlen = strlen(p_dir->dd_dir.d_name);
415     p_dir->i_drives &= ~(1UL << i);
416     return &p_dir->dd_dir;
417 }
418
419 int vlc_closedir_wrapper( void *_p_dir )
420 {
421     vlc_DIR *p_dir = (vlc_DIR *)_p_dir;
422
423     if ( p_dir->p_real_dir != NULL )
424     {
425         int i_ret = closedir( p_dir->p_real_dir );
426         free( p_dir );
427         return i_ret;
428     }
429
430     free( p_dir );
431     return 0;
432 }
433 #else
434 void *vlc_opendir_wrapper( const char *psz_path )
435 {
436     return (void *)opendir( psz_path );
437 }
438 struct dirent *vlc_readdir_wrapper( void *_p_dir )
439 {
440     return readdir( (DIR *)_p_dir );
441 }
442 int vlc_closedir_wrapper( void *_p_dir )
443 {
444     return closedir( (DIR *)_p_dir );
445 }
446 #endif
447
448 /*****************************************************************************
449  * scandir: scan a directory alpha-sorted
450  *****************************************************************************/
451 #if !defined( HAVE_SCANDIR )
452 int vlc_alphasort( const struct dirent **a, const struct dirent **b )
453 {
454     return strcoll( (*a)->d_name, (*b)->d_name );
455 }
456
457 int vlc_scandir( const char *name, struct dirent ***namelist,
458                     int (*filter) ( const struct dirent * ),
459                     int (*compar) ( const struct dirent **,
460                                     const struct dirent ** ) )
461 {
462     DIR            * p_dir;
463     struct dirent  * p_content;
464     struct dirent ** pp_list;
465     int              ret, size;
466
467     if( !namelist || !( p_dir = vlc_opendir_wrapper( name ) ) ) return -1;
468
469     ret     = 0;
470     pp_list = NULL;
471     while( ( p_content = vlc_readdir_wrapper( p_dir ) ) )
472     {
473         if( filter && !filter( p_content ) )
474         {
475             continue;
476         }
477         pp_list = realloc( pp_list, ( ret + 1 ) * sizeof( struct dirent * ) );
478         size = sizeof( struct dirent ) + strlen( p_content->d_name ) + 1;
479         pp_list[ret] = malloc( size );
480         memcpy( pp_list[ret], p_content, size );
481         ret++;
482     }
483
484     vlc_closedir_wrapper( p_dir );
485
486     if( compar )
487     {
488         qsort( pp_list, ret, sizeof( struct dirent * ),
489                (int (*)(const void *, const void *)) compar );
490     }
491
492     *namelist = pp_list;
493     return ret;
494 }
495 #endif
496
497 /*****************************************************************************
498  * dgettext: gettext for plugins.
499  *****************************************************************************/
500 char *vlc_dgettext( const char *package, const char *msgid )
501 {
502 #if defined( ENABLE_NLS ) \
503      && ( defined(HAVE_GETTEXT) || defined(HAVE_INCLUDED_GETTEXT) )
504     return dgettext( package, msgid );
505 #else
506     return (char *)msgid;
507 #endif
508 }
509
510 /*****************************************************************************
511  * count_utf8_string: returns the number of characters in the string.
512  *****************************************************************************/
513 static int count_utf8_string( const char *psz_string )
514 {
515     int i = 0, i_count = 0;
516     while( psz_string[ i ] != 0 )
517     {
518         if( ((unsigned char *)psz_string)[ i ] <  0x80UL ) i_count++;
519         i++;
520     }
521     return i_count;
522 }
523
524 /*****************************************************************************
525  * wraptext: inserts \n at convenient places to wrap the text.
526  *           Returns the modified string in a new buffer.
527  *****************************************************************************/
528 char *vlc_wraptext( const char *psz_text, int i_line, vlc_bool_t b_utf8 )
529 {
530     int i_len;
531     char *psz_line, *psz_new_text;
532
533     psz_line = psz_new_text = strdup( psz_text );
534
535     if( b_utf8 )
536         i_len = count_utf8_string( psz_text );
537     else
538         i_len = strlen( psz_text );
539
540     while( i_len > i_line )
541     {
542         /* Look if there is a newline somewhere. */
543         char *psz_parser = psz_line;
544         int i_count = 0;
545         while( i_count <= i_line && *psz_parser != '\n' )
546         {
547             if( b_utf8 )
548             {
549                 while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser++;
550             }
551             psz_parser++;
552             i_count++;
553         }
554         if( *psz_parser == '\n' )
555         {
556             i_len -= (i_count + 1);
557             psz_line = psz_parser + 1;
558             continue;
559         }
560
561         /* Find the furthest space. */
562         while( psz_parser > psz_line && *psz_parser != ' ' )
563         {
564             if( b_utf8 )
565             {
566                 while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser--;
567             }
568             psz_parser--;
569             i_count--;
570         }
571         if( *psz_parser == ' ' )
572         {
573             *psz_parser = '\n';
574             i_len -= (i_count + 1);
575             psz_line = psz_parser + 1;
576             continue;
577         }
578
579         /* Wrapping has failed. Find the first space or newline */
580         while( i_count < i_len && *psz_parser != ' ' && *psz_parser != '\n' )
581         {
582             if( b_utf8 )
583             {
584                 while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser++;
585             }
586             psz_parser++;
587             i_count++;
588         }
589         if( i_count < i_len ) *psz_parser = '\n';
590         i_len -= (i_count + 1);
591         psz_line = psz_parser + 1;
592     }
593
594     return psz_new_text;
595 }
596
597 /*****************************************************************************
598  * iconv wrapper
599  *****************************************************************************/
600 vlc_iconv_t vlc_iconv_open( const char *tocode, const char *fromcode )
601 {
602 #if defined(HAVE_ICONV)
603     return iconv_open( tocode, fromcode );
604 #else
605     return NULL;
606 #endif
607 }
608
609 size_t vlc_iconv( vlc_iconv_t cd, char **inbuf, size_t *inbytesleft,
610                   char **outbuf, size_t *outbytesleft )
611 {
612 #if defined(HAVE_ICONV)
613     return iconv( cd, inbuf, inbytesleft, outbuf, outbytesleft );
614 #else
615     int i_bytes = __MIN(*inbytesleft, *outbytesleft);
616     if( !inbuf || !outbuf || !i_bytes ) return (size_t)(-1);
617     memcpy( *outbuf, *inbuf, i_bytes );
618     inbuf += i_bytes;
619     outbuf += i_bytes;
620     inbytesleft -= i_bytes;
621     outbytesleft -= i_bytes;
622     return i_bytes;
623 #endif
624 }
625
626 int vlc_iconv_close( vlc_iconv_t cd )
627 {
628 #if defined(HAVE_ICONV)
629     return iconv_close( cd );
630 #else
631     return 0;
632 #endif
633 }
634
635 /*****************************************************************************
636  * reduce a fraction
637  *   (adapted from libavcodec, author Michael Niedermayer <michaelni@gmx.at>)
638  *****************************************************************************/
639 vlc_bool_t vlc_ureduce( unsigned *pi_dst_nom, unsigned *pi_dst_den,
640                         uint64_t i_nom, uint64_t i_den, uint64_t i_max )
641 {
642     vlc_bool_t b_exact = 1;
643     uint64_t i_gcd;
644
645     if( i_den == 0 )
646     {
647         *pi_dst_nom = 0;
648         *pi_dst_den = 1;
649         return 1;
650     }
651
652     i_gcd = GCD( i_nom, i_den );
653     i_nom /= i_gcd;
654     i_den /= i_gcd;
655
656     if( i_max == 0 ) i_max = I64C(0xFFFFFFFF);
657
658     if( i_nom > i_max || i_den > i_max )
659     {
660         uint64_t i_a0_num = 0, i_a0_den = 1, i_a1_num = 1, i_a1_den = 0;
661         b_exact = 0;
662
663         for( ; ; )
664         {
665             uint64_t i_x = i_nom / i_den;
666             uint64_t i_a2n = i_x * i_a1_num + i_a0_num;
667             uint64_t i_a2d = i_x * i_a1_den + i_a0_den;
668
669             if( i_a2n > i_max || i_a2d > i_max ) break;
670
671             i_nom %= i_den;
672
673             i_a0_num = i_a1_num; i_a0_den = i_a1_den;
674             i_a1_num = i_a2n; i_a1_den = i_a2d;
675             if( i_nom == 0 ) break;
676             i_x = i_nom; i_nom = i_den; i_den = i_x;
677         }
678         i_nom = i_a1_num;
679         i_den = i_a1_den;
680     }
681
682     *pi_dst_nom = i_nom;
683     *pi_dst_den = i_den;
684
685     return b_exact;
686 }
687
688 /*************************************************************************
689  * vlc_parse_cmdline: Command line parsing into elements.
690  *
691  * The command line is composed of space/tab separated arguments.
692  * Quotes can be used as argument delimiters and a backslash can be used to
693  * escape a quote.
694  *************************************************************************/
695 static void find_end_quote( char **s, char **ppsz_parser, int i_quote )
696 {
697     int i_bcount = 0;
698
699     while( **s )
700     {
701         if( **s == '\\' )
702         {
703             **ppsz_parser = **s;
704             (*ppsz_parser)++; (*s)++;
705             i_bcount++;
706         }
707         else if( **s == '"' || **s == '\'' )
708         {
709             /* Preceeded by a number of '\' which we erase. */
710             *ppsz_parser -= i_bcount / 2;
711             if( i_bcount & 1 )
712             {
713                 /* '\\' followed by a '"' or '\'' */
714                 *ppsz_parser -= 1;
715                 **ppsz_parser = **s;
716                 (*ppsz_parser)++; (*s)++;
717                 i_bcount = 0;
718                 continue;
719             }
720
721             if( **s == i_quote )
722             {
723                 /* End */
724                 return;
725             }
726             else
727             {
728                 /* Different quoting */
729                 int i_quote = **s;
730                 **ppsz_parser = **s;
731                 (*ppsz_parser)++; (*s)++;
732                 find_end_quote( s, ppsz_parser, i_quote );
733                 **ppsz_parser = **s;
734                 (*ppsz_parser)++; (*s)++;
735             }
736
737             i_bcount = 0;
738         }
739         else
740         {
741             /* A regular character */
742             **ppsz_parser = **s;
743             (*ppsz_parser)++; (*s)++;
744             i_bcount = 0;
745         }
746     }
747 }
748
749 char **vlc_parse_cmdline( const char *psz_cmdline, int *i_args )
750 {
751     int argc = 0;
752     char **argv = 0;
753     char *s, *psz_parser, *psz_arg, *psz_orig;
754     int i_bcount = 0;
755
756     if( !psz_cmdline ) return 0;
757     psz_orig = strdup( psz_cmdline );
758     psz_arg = psz_parser = s = psz_orig;
759
760     while( *s )
761     {
762         if( *s == '\t' || *s == ' ' )
763         {
764             /* We have a complete argument */
765             *psz_parser = 0;
766             TAB_APPEND( argc, argv, strdup(psz_arg) );
767
768             /* Skip trailing spaces/tabs */
769             do{ s++; } while( *s == '\t' || *s == ' ' );
770
771             /* New argument */
772             psz_arg = psz_parser = s;
773             i_bcount = 0;
774         }
775         else if( *s == '\\' )
776         {
777             *psz_parser++ = *s++;
778             i_bcount++;
779         }
780         else if( *s == '"' || *s == '\'' )
781         {
782             if( ( i_bcount & 1 ) == 0 )
783             {
784                 /* Preceeded by an even number of '\', this is half that
785                  * number of '\', plus a quote which we erase. */
786                 int i_quote = *s;
787                 psz_parser -= i_bcount / 2;
788                 s++;
789                 find_end_quote( &s, &psz_parser, i_quote );
790                 s++;
791             }
792             else
793             {
794                 /* Preceeded by an odd number of '\', this is half that
795                  * number of '\' followed by a '"' */
796                 psz_parser = psz_parser - i_bcount/2 - 1;
797                 *psz_parser++ = '"';
798                 s++;
799             }
800             i_bcount = 0;
801         }
802         else
803         {
804             /* A regular character */
805             *psz_parser++ = *s++;
806             i_bcount = 0;
807         }
808     }
809
810     /* Take care of the last arg */
811     if( *psz_arg )
812     {
813         *psz_parser = '\0';
814         TAB_APPEND( argc, argv, strdup(psz_arg) );
815     }
816
817     if( i_args ) *i_args = argc;
818     free( psz_orig );
819     return argv;
820 }
821
822 /*************************************************************************
823  * vlc_execve: Execute an external program with a given environment,
824  * wait until it finishes and return its standard output
825  *************************************************************************/
826 int __vlc_execve( vlc_object_t *p_object, int i_argc, char **ppsz_argv,
827                   char **ppsz_env, char *psz_cwd, char *p_in, int i_in,
828                   char **pp_data, int *pi_data )
829 {
830 #ifdef HAVE_FORK
831     int pi_stdin[2];
832     int pi_stdout[2];
833     pid_t i_child_pid;
834
835     pipe( pi_stdin );
836     pipe( pi_stdout );
837
838     if ( (i_child_pid = fork()) == -1 )
839     {
840         msg_Err( p_object, "unable to fork (%s)", strerror(errno) );
841         return -1;
842     }
843
844     if ( i_child_pid == 0 )
845     {
846         close(0);
847         dup(pi_stdin[1]);
848         close(pi_stdin[0]);
849
850         close(1);
851         dup(pi_stdout[1]);
852         close(pi_stdout[0]);
853
854         close(2);
855
856         if ( psz_cwd != NULL )
857             chdir( psz_cwd );
858         execve( ppsz_argv[0], ppsz_argv, ppsz_env );
859         exit(1);
860     }
861
862     close(pi_stdin[1]);
863     close(pi_stdout[1]);
864     if ( !i_in )
865         close( pi_stdin[0] );
866
867     *pi_data = 0;
868     *pp_data = malloc( 1025 );  /* +1 for \0 */
869
870     while ( !p_object->b_die )
871     {
872         int i_ret, i_status;
873         fd_set readfds, writefds;
874         struct timeval tv;
875
876         FD_ZERO( &readfds );
877         FD_ZERO( &writefds );
878         FD_SET( pi_stdout[0], &readfds );
879         if ( i_in )
880             FD_SET( pi_stdin[0], &writefds );
881
882         tv.tv_sec = 0;
883         tv.tv_usec = 10000;
884         
885         i_ret = select( pi_stdin[0] > pi_stdout[0] ? pi_stdin[0] + 1 :
886                         pi_stdout[0] + 1, &readfds, &writefds, NULL, &tv );
887         if ( i_ret > 0 )
888         {
889             if ( FD_ISSET( pi_stdout[0], &readfds ) )
890             {
891                 ssize_t i_read = read( pi_stdout[0], &(*pp_data)[*pi_data],
892                                        1024 );
893                 if ( i_read > 0 )
894                 {
895                     *pi_data += i_read;
896                     *pp_data = realloc( *pp_data, *pi_data + 1025 );
897                 }
898             }
899             if ( FD_ISSET( pi_stdin[0], &writefds ) )
900             {
901                 ssize_t i_write = write( pi_stdin[0], p_in, __MIN(i_in, 1024) );
902
903                 if ( i_write > 0 )
904                 {
905                     p_in += i_write;
906                     i_in -= i_write;
907                 }
908                 if ( !i_in )
909                     close( pi_stdin[0] );
910             }
911         }
912
913         if ( waitpid( i_child_pid, &i_status, WNOHANG ) == i_child_pid )
914         {
915             if ( WIFEXITED( i_status ) )
916             {
917                 if ( WEXITSTATUS( i_status ) )
918                 {
919                     msg_Warn( p_object,
920                               "child %s returned with error code %d",
921                               ppsz_argv[0], WEXITSTATUS( i_status ) );
922                 }
923             }
924             else
925             {
926                 if ( WIFSIGNALED( i_status ) )
927                 {
928                     msg_Warn( p_object,
929                               "child %s quit on signal %d", ppsz_argv[0],
930                               WTERMSIG( i_status ) );
931                 }
932             }
933             if ( i_in )
934                 close( pi_stdin[0] );
935             close( pi_stdout[0] );
936             break;
937         }
938
939         if ( i_ret < 0 && errno != EINTR )
940         {
941             msg_Warn( p_object, "select failed (%s)", strerror(errno) );
942         }
943     }
944
945 #elif defined( WIN32 )
946     SECURITY_ATTRIBUTES saAttr; 
947     PROCESS_INFORMATION piProcInfo; 
948     STARTUPINFO siStartInfo;
949     BOOL bFuncRetn = FALSE; 
950     HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr;
951     DWORD i_status;
952     char *psz_cmd, *p_env, *p;
953     char **ppsz_parser;
954     int i_size;
955
956     /* Set the bInheritHandle flag so pipe handles are inherited. */
957     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
958     saAttr.bInheritHandle = TRUE; 
959     saAttr.lpSecurityDescriptor = NULL; 
960
961     /* Create a pipe for the child process's STDOUT. */
962     if ( !CreatePipe( &hChildStdoutRd, &hChildStdoutWr, &saAttr, 0 ) ) 
963     {
964         msg_Err( p_object, "stdout pipe creation failed" ); 
965         return -1;
966     }
967
968     /* Ensure the read handle to the pipe for STDOUT is not inherited. */
969     SetHandleInformation( hChildStdoutRd, HANDLE_FLAG_INHERIT, 0 );
970
971     /* Create a pipe for the child process's STDIN. */
972     if ( !CreatePipe( &hChildStdinRd, &hChildStdinWr, &saAttr, 0 ) ) 
973     {
974         msg_Err( p_object, "stdin pipe creation failed" ); 
975         return -1;
976     }
977
978     /* Ensure the write handle to the pipe for STDIN is not inherited. */
979     SetHandleInformation( hChildStdinWr, HANDLE_FLAG_INHERIT, 0 );
980
981     /* Set up members of the PROCESS_INFORMATION structure. */
982     ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
983  
984     /* Set up members of the STARTUPINFO structure. */
985     ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
986     siStartInfo.cb = sizeof(STARTUPINFO); 
987     siStartInfo.hStdError = hChildStdoutWr;
988     siStartInfo.hStdOutput = hChildStdoutWr;
989     siStartInfo.hStdInput = hChildStdinRd;
990     siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
991
992     /* Set up the command line. */
993     psz_cmd = malloc(32768);
994     psz_cmd[0] = '\0';
995     i_size = 32768;
996     ppsz_parser = &ppsz_argv[0];
997     while ( ppsz_parser[0] != NULL && i_size > 0 )
998     {
999         /* Protect the last argument with quotes ; the other arguments
1000          * are supposed to be already protected because they have been
1001          * passed as a command-line option. */
1002         if ( ppsz_parser[1] == NULL )
1003         {
1004             strncat( psz_cmd, "\"", i_size );
1005             i_size--;
1006         }
1007         strncat( psz_cmd, *ppsz_parser, i_size );
1008         i_size -= strlen( *ppsz_parser );
1009         if ( ppsz_parser[1] == NULL )
1010         {
1011             strncat( psz_cmd, "\"", i_size );
1012             i_size--;
1013         }
1014         strncat( psz_cmd, " ", i_size );
1015         i_size--;
1016         ppsz_parser++;
1017     }
1018
1019     /* Set up the environment. */
1020     p = p_env = malloc(32768);
1021     i_size = 32768;
1022     ppsz_parser = &ppsz_env[0];
1023     while ( *ppsz_parser != NULL && i_size > 0 )
1024     {
1025         memcpy( p, *ppsz_parser,
1026                 __MIN((int)(strlen(*ppsz_parser) + 1), i_size) );
1027         p += strlen(*ppsz_parser) + 1;
1028         i_size -= strlen(*ppsz_parser) + 1;
1029         ppsz_parser++;
1030     }
1031     *p = '\0';
1032  
1033     /* Create the child process. */
1034     bFuncRetn = CreateProcess( NULL,
1035           psz_cmd,       // command line 
1036           NULL,          // process security attributes 
1037           NULL,          // primary thread security attributes 
1038           TRUE,          // handles are inherited 
1039           0,             // creation flags 
1040           p_env,
1041           psz_cwd,
1042           &siStartInfo,  // STARTUPINFO pointer 
1043           &piProcInfo ); // receives PROCESS_INFORMATION 
1044
1045     free( psz_cmd );
1046     free( p_env );
1047    
1048     if ( bFuncRetn == 0 ) 
1049     {
1050         msg_Err( p_object, "child creation failed" ); 
1051         return -1;
1052     }
1053
1054     /* Read from a file and write its contents to a pipe. */
1055     while ( i_in > 0 && !p_object->b_die )
1056     {
1057         DWORD i_written;
1058         if ( !WriteFile( hChildStdinWr, p_in, i_in, &i_written, NULL ) )
1059             break;
1060         i_in -= i_written;
1061         p_in += i_written;
1062     }
1063
1064     /* Close the pipe handle so the child process stops reading. */
1065     CloseHandle(hChildStdinWr);
1066
1067     /* Close the write end of the pipe before reading from the
1068      * read end of the pipe. */
1069     CloseHandle(hChildStdoutWr);
1070  
1071     /* Read output from the child process. */
1072     *pi_data = 0;
1073     *pp_data = malloc( 1025 );  /* +1 for \0 */
1074
1075     while ( !p_object->b_die )
1076     {
1077         DWORD i_read;
1078         if ( !ReadFile( hChildStdoutRd, &(*pp_data)[*pi_data], 1024, &i_read, 
1079                         NULL )
1080               || i_read == 0 )
1081             break; 
1082         *pi_data += i_read;
1083         *pp_data = realloc( *pp_data, *pi_data + 1025 );
1084     }
1085
1086     while ( !p_object->b_die
1087              && !GetExitCodeProcess( piProcInfo.hProcess, &i_status )
1088              && i_status != STILL_ACTIVE )
1089         msleep( 10000 );
1090
1091     CloseHandle(piProcInfo.hProcess);
1092     CloseHandle(piProcInfo.hThread);
1093
1094     if ( i_status )
1095         msg_Warn( p_object,
1096                   "child %s returned with error code %ld",
1097                   ppsz_argv[0], i_status );
1098
1099 #else
1100     msg_Err( p_object, "vlc_execve called but no implementation is available" );
1101     return -1;
1102
1103 #endif
1104
1105     (*pp_data)[*pi_data] = '\0';
1106
1107     return 0;
1108 }