]> git.sesse.net Git - vlc/blob - src/extras/libc.c
I shall compile before commiting * 100 for monday.
[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;
616
617     if (inbytesleft == NULL || outbytesleft == NULL)
618     {
619         return 0;
620     }
621
622     i_bytes = __MIN(*inbytesleft, *outbytesleft);
623     if( !inbuf || !outbuf || !i_bytes ) return (size_t)(-1);
624     memcpy( *outbuf, *inbuf, i_bytes );
625     inbuf += i_bytes;
626     outbuf += i_bytes;
627     inbytesleft -= i_bytes;
628     outbytesleft -= i_bytes;
629     return i_bytes;
630 #endif
631 }
632
633 int vlc_iconv_close( vlc_iconv_t cd )
634 {
635 #if defined(HAVE_ICONV)
636     return iconv_close( cd );
637 #else
638     return 0;
639 #endif
640 }
641
642 /*****************************************************************************
643  * reduce a fraction
644  *   (adapted from libavcodec, author Michael Niedermayer <michaelni@gmx.at>)
645  *****************************************************************************/
646 vlc_bool_t vlc_ureduce( unsigned *pi_dst_nom, unsigned *pi_dst_den,
647                         uint64_t i_nom, uint64_t i_den, uint64_t i_max )
648 {
649     vlc_bool_t b_exact = 1;
650     uint64_t i_gcd;
651
652     if( i_den == 0 )
653     {
654         *pi_dst_nom = 0;
655         *pi_dst_den = 1;
656         return 1;
657     }
658
659     i_gcd = GCD( i_nom, i_den );
660     i_nom /= i_gcd;
661     i_den /= i_gcd;
662
663     if( i_max == 0 ) i_max = I64C(0xFFFFFFFF);
664
665     if( i_nom > i_max || i_den > i_max )
666     {
667         uint64_t i_a0_num = 0, i_a0_den = 1, i_a1_num = 1, i_a1_den = 0;
668         b_exact = 0;
669
670         for( ; ; )
671         {
672             uint64_t i_x = i_nom / i_den;
673             uint64_t i_a2n = i_x * i_a1_num + i_a0_num;
674             uint64_t i_a2d = i_x * i_a1_den + i_a0_den;
675
676             if( i_a2n > i_max || i_a2d > i_max ) break;
677
678             i_nom %= i_den;
679
680             i_a0_num = i_a1_num; i_a0_den = i_a1_den;
681             i_a1_num = i_a2n; i_a1_den = i_a2d;
682             if( i_nom == 0 ) break;
683             i_x = i_nom; i_nom = i_den; i_den = i_x;
684         }
685         i_nom = i_a1_num;
686         i_den = i_a1_den;
687     }
688
689     *pi_dst_nom = i_nom;
690     *pi_dst_den = i_den;
691
692     return b_exact;
693 }
694
695 /*************************************************************************
696  * vlc_parse_cmdline: Command line parsing into elements.
697  *
698  * The command line is composed of space/tab separated arguments.
699  * Quotes can be used as argument delimiters and a backslash can be used to
700  * escape a quote.
701  *************************************************************************/
702 static void find_end_quote( char **s, char **ppsz_parser, int i_quote )
703 {
704     int i_bcount = 0;
705
706     while( **s )
707     {
708         if( **s == '\\' )
709         {
710             **ppsz_parser = **s;
711             (*ppsz_parser)++; (*s)++;
712             i_bcount++;
713         }
714         else if( **s == '"' || **s == '\'' )
715         {
716             /* Preceeded by a number of '\' which we erase. */
717             *ppsz_parser -= i_bcount / 2;
718             if( i_bcount & 1 )
719             {
720                 /* '\\' followed by a '"' or '\'' */
721                 *ppsz_parser -= 1;
722                 **ppsz_parser = **s;
723                 (*ppsz_parser)++; (*s)++;
724                 i_bcount = 0;
725                 continue;
726             }
727
728             if( **s == i_quote )
729             {
730                 /* End */
731                 return;
732             }
733             else
734             {
735                 /* Different quoting */
736                 int i_quote = **s;
737                 **ppsz_parser = **s;
738                 (*ppsz_parser)++; (*s)++;
739                 find_end_quote( s, ppsz_parser, i_quote );
740                 **ppsz_parser = **s;
741                 (*ppsz_parser)++; (*s)++;
742             }
743
744             i_bcount = 0;
745         }
746         else
747         {
748             /* A regular character */
749             **ppsz_parser = **s;
750             (*ppsz_parser)++; (*s)++;
751             i_bcount = 0;
752         }
753     }
754 }
755
756 char **vlc_parse_cmdline( const char *psz_cmdline, int *i_args )
757 {
758     int argc = 0;
759     char **argv = 0;
760     char *s, *psz_parser, *psz_arg, *psz_orig;
761     int i_bcount = 0;
762
763     if( !psz_cmdline ) return 0;
764     psz_orig = strdup( psz_cmdline );
765     psz_arg = psz_parser = s = psz_orig;
766
767     while( *s )
768     {
769         if( *s == '\t' || *s == ' ' )
770         {
771             /* We have a complete argument */
772             *psz_parser = 0;
773             TAB_APPEND( argc, argv, strdup(psz_arg) );
774
775             /* Skip trailing spaces/tabs */
776             do{ s++; } while( *s == '\t' || *s == ' ' );
777
778             /* New argument */
779             psz_arg = psz_parser = s;
780             i_bcount = 0;
781         }
782         else if( *s == '\\' )
783         {
784             *psz_parser++ = *s++;
785             i_bcount++;
786         }
787         else if( *s == '"' || *s == '\'' )
788         {
789             if( ( i_bcount & 1 ) == 0 )
790             {
791                 /* Preceeded by an even number of '\', this is half that
792                  * number of '\', plus a quote which we erase. */
793                 int i_quote = *s;
794                 psz_parser -= i_bcount / 2;
795                 s++;
796                 find_end_quote( &s, &psz_parser, i_quote );
797                 s++;
798             }
799             else
800             {
801                 /* Preceeded by an odd number of '\', this is half that
802                  * number of '\' followed by a '"' */
803                 psz_parser = psz_parser - i_bcount/2 - 1;
804                 *psz_parser++ = '"';
805                 s++;
806             }
807             i_bcount = 0;
808         }
809         else
810         {
811             /* A regular character */
812             *psz_parser++ = *s++;
813             i_bcount = 0;
814         }
815     }
816
817     /* Take care of the last arg */
818     if( *psz_arg )
819     {
820         *psz_parser = '\0';
821         TAB_APPEND( argc, argv, strdup(psz_arg) );
822     }
823
824     if( i_args ) *i_args = argc;
825     free( psz_orig );
826     return argv;
827 }
828
829 /*************************************************************************
830  * vlc_execve: Execute an external program with a given environment,
831  * wait until it finishes and return its standard output
832  *************************************************************************/
833 int __vlc_execve( vlc_object_t *p_object, int i_argc, char **ppsz_argv,
834                   char **ppsz_env, char *psz_cwd, char *p_in, int i_in,
835                   char **pp_data, int *pi_data )
836 {
837 #ifdef HAVE_FORK
838     int pi_stdin[2];
839     int pi_stdout[2];
840     pid_t i_child_pid;
841
842     pipe( pi_stdin );
843     pipe( pi_stdout );
844
845     if ( (i_child_pid = fork()) == -1 )
846     {
847         msg_Err( p_object, "unable to fork (%s)", strerror(errno) );
848         return -1;
849     }
850
851     if ( i_child_pid == 0 )
852     {
853         close(0);
854         dup(pi_stdin[1]);
855         close(pi_stdin[0]);
856
857         close(1);
858         dup(pi_stdout[1]);
859         close(pi_stdout[0]);
860
861         close(2);
862
863         if ( psz_cwd != NULL )
864             chdir( psz_cwd );
865         execve( ppsz_argv[0], ppsz_argv, ppsz_env );
866         exit(1);
867     }
868
869     close(pi_stdin[1]);
870     close(pi_stdout[1]);
871     if ( !i_in )
872         close( pi_stdin[0] );
873
874     *pi_data = 0;
875     *pp_data = malloc( 1025 );  /* +1 for \0 */
876
877     while ( !p_object->b_die )
878     {
879         int i_ret, i_status;
880         fd_set readfds, writefds;
881         struct timeval tv;
882
883         FD_ZERO( &readfds );
884         FD_ZERO( &writefds );
885         FD_SET( pi_stdout[0], &readfds );
886         if ( i_in )
887             FD_SET( pi_stdin[0], &writefds );
888
889         tv.tv_sec = 0;
890         tv.tv_usec = 10000;
891         
892         i_ret = select( pi_stdin[0] > pi_stdout[0] ? pi_stdin[0] + 1 :
893                         pi_stdout[0] + 1, &readfds, &writefds, NULL, &tv );
894         if ( i_ret > 0 )
895         {
896             if ( FD_ISSET( pi_stdout[0], &readfds ) )
897             {
898                 ssize_t i_read = read( pi_stdout[0], &(*pp_data)[*pi_data],
899                                        1024 );
900                 if ( i_read > 0 )
901                 {
902                     *pi_data += i_read;
903                     *pp_data = realloc( *pp_data, *pi_data + 1025 );
904                 }
905             }
906             if ( FD_ISSET( pi_stdin[0], &writefds ) )
907             {
908                 ssize_t i_write = write( pi_stdin[0], p_in, __MIN(i_in, 1024) );
909
910                 if ( i_write > 0 )
911                 {
912                     p_in += i_write;
913                     i_in -= i_write;
914                 }
915                 if ( !i_in )
916                     close( pi_stdin[0] );
917             }
918         }
919
920         if ( waitpid( i_child_pid, &i_status, WNOHANG ) == i_child_pid )
921         {
922             if ( WIFEXITED( i_status ) )
923             {
924                 if ( WEXITSTATUS( i_status ) )
925                 {
926                     msg_Warn( p_object,
927                               "child %s returned with error code %d",
928                               ppsz_argv[0], WEXITSTATUS( i_status ) );
929                 }
930             }
931             else
932             {
933                 if ( WIFSIGNALED( i_status ) )
934                 {
935                     msg_Warn( p_object,
936                               "child %s quit on signal %d", ppsz_argv[0],
937                               WTERMSIG( i_status ) );
938                 }
939             }
940             if ( i_in )
941                 close( pi_stdin[0] );
942             close( pi_stdout[0] );
943             break;
944         }
945
946         if ( i_ret < 0 && errno != EINTR )
947         {
948             msg_Warn( p_object, "select failed (%s)", strerror(errno) );
949         }
950     }
951
952 #elif defined( WIN32 )
953     SECURITY_ATTRIBUTES saAttr; 
954     PROCESS_INFORMATION piProcInfo; 
955     STARTUPINFO siStartInfo;
956     BOOL bFuncRetn = FALSE; 
957     HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr;
958     DWORD i_status;
959     char *psz_cmd, *p_env, *p;
960     char **ppsz_parser;
961     int i_size;
962
963     /* Set the bInheritHandle flag so pipe handles are inherited. */
964     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
965     saAttr.bInheritHandle = TRUE; 
966     saAttr.lpSecurityDescriptor = NULL; 
967
968     /* Create a pipe for the child process's STDOUT. */
969     if ( !CreatePipe( &hChildStdoutRd, &hChildStdoutWr, &saAttr, 0 ) ) 
970     {
971         msg_Err( p_object, "stdout pipe creation failed" ); 
972         return -1;
973     }
974
975     /* Ensure the read handle to the pipe for STDOUT is not inherited. */
976     SetHandleInformation( hChildStdoutRd, HANDLE_FLAG_INHERIT, 0 );
977
978     /* Create a pipe for the child process's STDIN. */
979     if ( !CreatePipe( &hChildStdinRd, &hChildStdinWr, &saAttr, 0 ) ) 
980     {
981         msg_Err( p_object, "stdin pipe creation failed" ); 
982         return -1;
983     }
984
985     /* Ensure the write handle to the pipe for STDIN is not inherited. */
986     SetHandleInformation( hChildStdinWr, HANDLE_FLAG_INHERIT, 0 );
987
988     /* Set up members of the PROCESS_INFORMATION structure. */
989     ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
990  
991     /* Set up members of the STARTUPINFO structure. */
992     ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
993     siStartInfo.cb = sizeof(STARTUPINFO); 
994     siStartInfo.hStdError = hChildStdoutWr;
995     siStartInfo.hStdOutput = hChildStdoutWr;
996     siStartInfo.hStdInput = hChildStdinRd;
997     siStartInfo.wShowWindow = SW_HIDE;
998     siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
999
1000     /* Set up the command line. */
1001     psz_cmd = malloc(32768);
1002     psz_cmd[0] = '\0';
1003     i_size = 32768;
1004     ppsz_parser = &ppsz_argv[0];
1005     while ( ppsz_parser[0] != NULL && i_size > 0 )
1006     {
1007         /* Protect the last argument with quotes ; the other arguments
1008          * are supposed to be already protected because they have been
1009          * passed as a command-line option. */
1010         if ( ppsz_parser[1] == NULL )
1011         {
1012             strncat( psz_cmd, "\"", i_size );
1013             i_size--;
1014         }
1015         strncat( psz_cmd, *ppsz_parser, i_size );
1016         i_size -= strlen( *ppsz_parser );
1017         if ( ppsz_parser[1] == NULL )
1018         {
1019             strncat( psz_cmd, "\"", i_size );
1020             i_size--;
1021         }
1022         strncat( psz_cmd, " ", i_size );
1023         i_size--;
1024         ppsz_parser++;
1025     }
1026
1027     /* Set up the environment. */
1028     p = p_env = malloc(32768);
1029     i_size = 32768;
1030     ppsz_parser = &ppsz_env[0];
1031     while ( *ppsz_parser != NULL && i_size > 0 )
1032     {
1033         memcpy( p, *ppsz_parser,
1034                 __MIN((int)(strlen(*ppsz_parser) + 1), i_size) );
1035         p += strlen(*ppsz_parser) + 1;
1036         i_size -= strlen(*ppsz_parser) + 1;
1037         ppsz_parser++;
1038     }
1039     *p = '\0';
1040  
1041     /* Create the child process. */
1042     bFuncRetn = CreateProcess( NULL,
1043           psz_cmd,       // command line 
1044           NULL,          // process security attributes 
1045           NULL,          // primary thread security attributes 
1046           TRUE,          // handles are inherited 
1047           0,             // creation flags 
1048           p_env,
1049           psz_cwd,
1050           &siStartInfo,  // STARTUPINFO pointer 
1051           &piProcInfo ); // receives PROCESS_INFORMATION 
1052
1053     free( psz_cmd );
1054     free( p_env );
1055    
1056     if ( bFuncRetn == 0 ) 
1057     {
1058         msg_Err( p_object, "child creation failed" ); 
1059         return -1;
1060     }
1061
1062     /* Read from a file and write its contents to a pipe. */
1063     while ( i_in > 0 && !p_object->b_die )
1064     {
1065         DWORD i_written;
1066         if ( !WriteFile( hChildStdinWr, p_in, i_in, &i_written, NULL ) )
1067             break;
1068         i_in -= i_written;
1069         p_in += i_written;
1070     }
1071
1072     /* Close the pipe handle so the child process stops reading. */
1073     CloseHandle(hChildStdinWr);
1074
1075     /* Close the write end of the pipe before reading from the
1076      * read end of the pipe. */
1077     CloseHandle(hChildStdoutWr);
1078  
1079     /* Read output from the child process. */
1080     *pi_data = 0;
1081     *pp_data = malloc( 1025 );  /* +1 for \0 */
1082
1083     while ( !p_object->b_die )
1084     {
1085         DWORD i_read;
1086         if ( !ReadFile( hChildStdoutRd, &(*pp_data)[*pi_data], 1024, &i_read, 
1087                         NULL )
1088               || i_read == 0 )
1089             break; 
1090         *pi_data += i_read;
1091         *pp_data = realloc( *pp_data, *pi_data + 1025 );
1092     }
1093
1094     while ( !p_object->b_die
1095              && !GetExitCodeProcess( piProcInfo.hProcess, &i_status )
1096              && i_status != STILL_ACTIVE )
1097         msleep( 10000 );
1098
1099     CloseHandle(piProcInfo.hProcess);
1100     CloseHandle(piProcInfo.hThread);
1101
1102     if ( i_status )
1103         msg_Warn( p_object,
1104                   "child %s returned with error code %ld",
1105                   ppsz_argv[0], i_status );
1106
1107 #else
1108     msg_Err( p_object, "vlc_execve called but no implementation is available" );
1109     return -1;
1110
1111 #endif
1112
1113     (*pp_data)[*pi_data] = '\0';
1114
1115     return 0;
1116 }