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