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