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