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