libc.c 29.8 KB
Newer Older
1
2
3
/*****************************************************************************
 * libc.c: Extra libc function for some systems.
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
4
 * Copyright (C) 2002-2006 the VideoLAN team
5
 * $Id$
6
7
8
 *
 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
 *          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>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
12
 *          Rémi Denis-Courmont <rem à videolan.org>
13
14
15
16
17
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
18
 *
19
20
21
22
23
24
25
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
26
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27
 *****************************************************************************/
28
29
30
31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
33
#include <vlc/vlc.h>

34
#include <ctype.h>
35
36


37
38
39
40
41
42
43
44
#undef iconv_t
#undef iconv_open
#undef iconv
#undef iconv_close

#if defined(HAVE_ICONV)
#   include <iconv.h>
#endif
45
46
47
48

#ifdef HAVE_DIRENT_H
#   include <dirent.h>
#endif
49

Rafaël Carré's avatar
Rafaël Carré committed
50
51
52
53
#ifdef HAVE_SIGNAL_H
#   include <signal.h>
#endif

54
55
56
57
58
#ifdef HAVE_FORK
#   include <sys/time.h>
#   include <unistd.h>
#   include <errno.h>
#   include <sys/wait.h>
59
60
61
#   include <fcntl.h>
#   include <sys/socket.h>
#   include <sys/poll.h>
62
63
#endif

64
#if defined(WIN32) || defined(UNDER_CE)
65
66
67
#   undef _wopendir
#   undef _wreaddir
#   undef _wclosedir
68
69
70
#   undef rewinddir
#   undef seekdir
#   undef telldir
71
72
73
74
#   define WIN32_LEAN_AND_MEAN
#   include <windows.h>
#endif

75
76
77
78
#ifdef UNDER_CE
#   define strcoll strcmp
#endif

79
80
81
/*****************************************************************************
 * getenv: just in case, but it should never be called
 *****************************************************************************/
82
#if !defined( HAVE_GETENV )
83
char *vlc_getenv( const char *name )
84
85
86
87
88
89
{
    return NULL;
}
#endif

/*****************************************************************************
90
 * strdup: returns a malloc'd copy of a string
91
 *****************************************************************************/
92
#if !defined( HAVE_STRDUP )
93
char *vlc_strdup( const char *string )
94
95
96
97
98
99
{
    return strndup( string, strlen( string ) );
}
#endif

/*****************************************************************************
100
 * strndup: returns a malloc'd copy of at most n bytes of string
101
102
 * Does anyone know whether or not it will be present in Jaguar?
 *****************************************************************************/
103
#if !defined( HAVE_STRNDUP )
104
char *vlc_strndup( const char *string, size_t n )
105
106
107
108
{
    char *psz;
    size_t len = strlen( string );

109
    len = __MIN( len, n );
110
111
112
113
114
115
116
    psz = (char*)malloc( len + 1 );
    if( psz != NULL )
    {
        memcpy( (void*)psz, (const void*)string, len );
        psz[ len ] = 0;
    }

117
    return psz;
118
119
120
}
#endif

121
/*****************************************************************************
122
 * strnlen:
123
124
125
126
127
 *****************************************************************************/
#if !defined( HAVE_STRNLEN )
size_t vlc_strnlen( const char *psz, size_t n )
{
    const char *psz_end = memchr( psz, 0, n );
128
    return psz_end ? (size_t)( psz_end - psz ) : n;
129
130
131
}
#endif

132
133
134
135
/*****************************************************************************
 * strcasecmp: compare two strings ignoring case
 *****************************************************************************/
#if !defined( HAVE_STRCASECMP ) && !defined( HAVE_STRICMP )
136
int vlc_strcasecmp( const char *s1, const char *s2 )
137
{
138
    int c1, c2;
139
    if( !s1 || !s2 ) return  -1;
140

141
    while( *s1 && *s2 )
142
    {
143
144
        c1 = tolower(*s1);
        c2 = tolower(*s2);
145

146
        if( c1 != c2 ) return (c1 < c2 ? -1 : 1);
147
        s1++; s2++;
148
149
    }

150
151
    if( !*s1 && !*s2 ) return 0;
    else return (*s1 ? 1 : -1);
152
153
154
155
156
157
158
}
#endif

/*****************************************************************************
 * strncasecmp: compare n chars from two strings ignoring case
 *****************************************************************************/
#if !defined( HAVE_STRNCASECMP ) && !defined( HAVE_STRNICMP )
159
int vlc_strncasecmp( const char *s1, const char *s2, size_t n )
160
{
161
    int c1, c2;
162
    if( !s1 || !s2 ) return  -1;
163

164
    while( n > 0 && *s1 && *s2 )
165
    {
166
167
        c1 = tolower(*s1);
        c2 = tolower(*s2);
168

169
170
        if( c1 != c2 ) return (c1 < c2 ? -1 : 1);
        s1++; s2++; n--;
171
172
    }

173
174
    if( !n || (!*s1 && !*s2) ) return 0;
    else return (*s1 ? 1 : -1);
175
176
177
}
#endif

178
179
180
181
182
/******************************************************************************
 * strcasestr: find a substring (little) in another substring (big)
 * Case sensitive. Return NULL if not found, return big if little == null
 *****************************************************************************/
#if !defined( HAVE_STRCASESTR ) && !defined( HAVE_STRISTR )
hartman's avatar
hartman committed
183
char * vlc_strcasestr( const char *psz_big, const char *psz_little )
184
{
185
    char *p_pos = (char *)psz_big;
186

187
    if( !psz_big || !psz_little || !*psz_little ) return p_pos;
188
 
189
    while( *p_pos )
190
191
192
193
    {
        if( toupper( *p_pos ) == toupper( *psz_little ) )
        {
            char * psz_cur1 = p_pos + 1;
194
            char * psz_cur2 = (char *)psz_little + 1;
195
196
            while( *psz_cur1 && *psz_cur2 &&
                   toupper( *psz_cur1 ) == toupper( *psz_cur2 ) )
197
198
199
200
201
202
203
204
205
206
207
208
            {
                psz_cur1++;
                psz_cur2++;
            }
            if( !*psz_cur2 ) return p_pos;
        }
        p_pos++;
    }
    return NULL;
}
#endif

209
210
211
/*****************************************************************************
 * vasprintf:
 *****************************************************************************/
212
#if !defined(HAVE_VASPRINTF) || defined(__APPLE__) || defined(SYS_BEOS)
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
int vlc_vasprintf(char **strp, const char *fmt, va_list ap)
{
    /* Guess we need no more than 100 bytes. */
    int     i_size = 100;
    char    *p = malloc( i_size );
    int     n;

    if( p == NULL )
    {
        *strp = NULL;
        return -1;
    }

    for( ;; )
    {
        /* Try to print in the allocated space. */
229
        n = vsnprintf( p, i_size, fmt, ap );
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

        /* If that worked, return the string. */
        if (n > -1 && n < i_size)
        {
            *strp = p;
            return strlen( p );
        }
        /* Else try again with more space. */
        if (n > -1)    /* glibc 2.1 */
        {
           i_size = n+1; /* precisely what is needed */
        }
        else           /* glibc 2.0 */
        {
           i_size *= 2;  /* twice the old size */
        }
        if( (p = realloc( p, i_size ) ) == NULL)
        {
            *strp = NULL;
            return -1;
        }
    }
}
#endif

gbazin's avatar
   
gbazin committed
255
256
257
/*****************************************************************************
 * asprintf:
 *****************************************************************************/
258
#if !defined(HAVE_ASPRINTF) || defined(__APPLE__) || defined(SYS_BEOS)
gbazin's avatar
   
gbazin committed
259
260
261
int vlc_asprintf( char **strp, const char *fmt, ... )
{
    va_list args;
gbazin's avatar
   
gbazin committed
262
263
    int i_ret;

gbazin's avatar
   
gbazin committed
264
    va_start( args, fmt );
gbazin's avatar
   
gbazin committed
265
    i_ret = vasprintf( strp, fmt, args );
gbazin's avatar
   
gbazin committed
266
    va_end( args );
gbazin's avatar
   
gbazin committed
267
268

    return i_ret;
gbazin's avatar
   
gbazin committed
269
270
271
}
#endif

272
273
274
/*****************************************************************************
 * atof: convert a string to a double.
 *****************************************************************************/
275
#if !defined( HAVE_ATOF )
276
double vlc_atof( const char *nptr )
277
{
278
    double f_result;
279
    wchar_t *psz_tmp = NULL;
280
    int i_len = strlen( nptr ) + 1;
281

282
    psz_tmp = malloc( i_len * sizeof(wchar_t) );
283
284
    if( !psz_tmp )
        return NULL;
285
286
287
288
289
290
291
292
    MultiByteToWideChar( CP_ACP, 0, nptr, -1, psz_tmp, i_len );
    f_result = wcstod( psz_tmp, NULL );
    free( psz_tmp );

    return f_result;
}
#endif

293
/*****************************************************************************
294
 * strtoll: convert a string to a 64 bits int.
295
 *****************************************************************************/
296
297
#if !defined( HAVE_STRTOLL )
int64_t vlc_strtoll( const char *nptr, char **endptr, int base )
298
299
{
    int64_t i_value = 0;
300
    int sign = 1, newbase = base ? base : 10;
301

302
303
304
    while( isspace(*nptr) ) nptr++;

    if( *nptr == '-' )
305
306
    {
        sign = -1;
307
        nptr++;
308
309
    }

310
311
    /* Try to detect base */
    if( *nptr == '0' )
312
    {
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
        newbase = 8;
        nptr++;

        if( *nptr == 'x' )
        {
            newbase = 16;
            nptr++;
        }
    }

    if( base && newbase != base )
    {
        if( endptr ) *endptr = (char *)nptr;
        return i_value;
    }

    switch( newbase )
    {
        case 10:
            while( *nptr >= '0' && *nptr <= '9' )
            {
                i_value *= 10;
                i_value += ( *nptr++ - '0' );
            }
            if( endptr ) *endptr = (char *)nptr;
            break;

        case 16:
            while( (*nptr >= '0' && *nptr <= '9') ||
                   (*nptr >= 'a' && *nptr <= 'f') ||
                   (*nptr >= 'A' && *nptr <= 'F') )
            {
                int i_valc = 0;
                if(*nptr >= '0' && *nptr <= '9') i_valc = *nptr - '0';
                else if(*nptr >= 'a' && *nptr <= 'f') i_valc = *nptr - 'a' +10;
                else if(*nptr >= 'A' && *nptr <= 'F') i_valc = *nptr - 'A' +10;
                i_value *= 16;
                i_value += i_valc;
                nptr++;
            }
            if( endptr ) *endptr = (char *)nptr;
            break;

        default:
            i_value = strtol( nptr, endptr, newbase );
            break;
359
360
361
362
363
364
    }

    return i_value * sign;
}
#endif

365
366
367
368
369
370
371
372
373
374
/*****************************************************************************
 * atoll: convert a string to a 64 bits int.
 *****************************************************************************/
#if !defined( HAVE_ATOLL )
int64_t vlc_atoll( const char *nptr )
{
    return strtoll( nptr, (char **)NULL, 10 );
}
#endif

375
376
377
/*****************************************************************************
 * lldiv: returns quotient and remainder
 *****************************************************************************/
378
#if !defined( HAVE_LLDIV )
379
380
381
382
383
384
385
386
387
lldiv_t vlc_lldiv( long long numer, long long denom )
{
    lldiv_t d;
    d.quot = numer / denom;
    d.rem  = numer % denom;
    return d;
}
#endif

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417

/**
 * Copy a string to a sized buffer. The result is always nul-terminated
 * (contrary to strncpy()).
 *
 * @param dest destination buffer
 * @param src string to be copied
 * @param len maximum number of characters to be copied plus one for the
 * terminating nul.
 *
 * @return strlen(src)
 */
#ifndef HAVE_STRLCPY
extern size_t vlc_strlcpy (char *tgt, const char *src, size_t bufsize)
{
    size_t length;

    for (length = 1; (length < bufsize) && *src; length++)
        *tgt++ = *src++;

    if (bufsize)
        *tgt = '\0';

    while (*src++)
        length++;

    return length - 1;
}
#endif

418
419
420
421
/*****************************************************************************
 * vlc_*dir_wrapper: wrapper under Windows to return the list of drive letters
 * when called with an empty argument or just '\'
 *****************************************************************************/
422
#if defined(WIN32) && !defined(UNDER_CE)
423
424
#   include <assert.h>

425
426
typedef struct vlc_DIR
{
427
    _WDIR *p_real_dir;
428
    int i_drives;
429
    struct _wdirent dd_dir;
430
    bool b_insert_back;
431
432
} vlc_DIR;

433
void *vlc_wopendir( const wchar_t *wpath )
434
{
435
436
    vlc_DIR *p_dir = NULL;
    _WDIR *p_real_dir = NULL;
437

438
439
    if ( wpath == NULL || wpath[0] == '\0'
          || (wcscmp (wpath, L"\\") == 0) )
440
441
442
    {
        /* Special mode to list drive letters */
        p_dir = malloc( sizeof(vlc_DIR) );
443
444
        if( !p_dir )
            return NULL;
445
446
447
448
449
        p_dir->p_real_dir = NULL;
        p_dir->i_drives = GetLogicalDrives();
        return (void *)p_dir;
    }

450
    p_real_dir = _wopendir( wpath );
451
452
453
454
    if ( p_real_dir == NULL )
        return NULL;

    p_dir = malloc( sizeof(vlc_DIR) );
455
456
457
458
459
    if( !p_dir )
    {
        _wclosedir( p_real_dir );
        return NULL;
    }
460
    p_dir->p_real_dir = p_real_dir;
461
462
463

    assert (wpath[0]); // wpath[1] is defined
    p_dir->b_insert_back = !wcscmp (wpath + 1, L":\\");
464
465
466
    return (void *)p_dir;
}

467
struct _wdirent *vlc_wreaddir( void *_p_dir )
468
469
470
471
472
473
474
475
476
{
    vlc_DIR *p_dir = (vlc_DIR *)_p_dir;
    unsigned int i;
    DWORD i_drives;

    if ( p_dir->p_real_dir != NULL )
    {
        if ( p_dir->b_insert_back )
        {
477
            /* Adds "..", gruik! */
478
479
480
            p_dir->dd_dir.d_ino = 0;
            p_dir->dd_dir.d_reclen = 0;
            p_dir->dd_dir.d_namlen = 2;
481
            wcscpy( p_dir->dd_dir.d_name, L".." );
482
            p_dir->b_insert_back = false;
483
484
            return &p_dir->dd_dir;
        }
485

486
        return _wreaddir( p_dir->p_real_dir );
487
488
489
490
491
492
493
494
495
496
497
498
499
    }

    /* Drive letters mode */
    i_drives = p_dir->i_drives;
    if ( !i_drives )
        return NULL; /* end */

    for ( i = 0; i < sizeof(DWORD)*8; i++, i_drives >>= 1 )
        if ( i_drives & 1 ) break;

    if ( i >= 26 )
        return NULL; /* this should not happen */

500
501
    swprintf( p_dir->dd_dir.d_name, L"%c:\\", 'A' + i );
    p_dir->dd_dir.d_namlen = wcslen(p_dir->dd_dir.d_name);
502
503
504
505
    p_dir->i_drives &= ~(1UL << i);
    return &p_dir->dd_dir;
}

506
int vlc_wclosedir( void *_p_dir )
507
508
{
    vlc_DIR *p_dir = (vlc_DIR *)_p_dir;
509
    int i_ret = 0;
510
511

    if ( p_dir->p_real_dir != NULL )
512
        i_ret = _wclosedir( p_dir->p_real_dir );
513
514

    free( p_dir );
515
    return i_ret;
516
}
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541

void vlc_rewinddir( void *_p_dir )
{
    vlc_DIR *p_dir = (vlc_DIR *)_p_dir;

    if ( p_dir->p_real_dir != NULL )
        _wrewinddir( p_dir->p_real_dir );
}

void vlc_seekdir( void *_p_dir, long loc)
{
    vlc_DIR *p_dir = (vlc_DIR *)_p_dir;

    if ( p_dir->p_real_dir != NULL )
        _wseekdir( p_dir->p_real_dir, loc );
}

long vlc_telldir( void *_p_dir )
{
    vlc_DIR *p_dir = (vlc_DIR *)_p_dir;

    if ( p_dir->p_real_dir != NULL )
        return _wtelldir( p_dir->p_real_dir );
    return 0;
}
542
543
#endif

544
545
546
547
/*****************************************************************************
 * scandir: scan a directory alpha-sorted
 *****************************************************************************/
#if !defined( HAVE_SCANDIR )
548
549
550
551
552
553
/* FIXME: I suspect this is dead code -> utf8_scandir */
#ifdef WIN32
# undef opendir
# undef readdir
# undef closedir
#endif
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
int vlc_alphasort( const struct dirent **a, const struct dirent **b )
{
    return strcoll( (*a)->d_name, (*b)->d_name );
}

int vlc_scandir( const char *name, struct dirent ***namelist,
                    int (*filter) ( const struct dirent * ),
                    int (*compar) ( const struct dirent **,
                                    const struct dirent ** ) )
{
    DIR            * p_dir;
    struct dirent  * p_content;
    struct dirent ** pp_list;
    int              ret, size;

569
    if( !namelist || !( p_dir = opendir( name ) ) ) return -1;
570
571
572

    ret     = 0;
    pp_list = NULL;
573
    while( ( p_content = readdir( p_dir ) ) )
574
575
576
577
578
579
580
581
    {
        if( filter && !filter( p_content ) )
        {
            continue;
        }
        pp_list = realloc( pp_list, ( ret + 1 ) * sizeof( struct dirent * ) );
        size = sizeof( struct dirent ) + strlen( p_content->d_name ) + 1;
        pp_list[ret] = malloc( size );
582
583
584
585
586
587
588
589
590
591
592
593
594
        if( pp_list[ret] )
        {
            memcpy( pp_list[ret], p_content, size );
            ret++;
        }
        else
        {
            /* Continuing is useless when no more memory can be allocted,
             * so better return what we have found.
             */
            ret = -1;
            break;
        }
595
596
    }

597
    closedir( p_dir );
598
599
600
601
602
603
604
605
606
607
608
609

    if( compar )
    {
        qsort( pp_list, ret, sizeof( struct dirent * ),
               (int (*)(const void *, const void *)) compar );
    }

    *namelist = pp_list;
    return ret;
}
#endif

610
611
612
613
614
#if defined (WIN32)
/**
 * gettext callbacks for plugins.
 * LibVLC links libintl statically on Windows.
 */
615
616
617
618
char *vlc_dgettext( const char *package, const char *msgid )
{
    return dgettext( package, msgid );
}
619
#endif
620

621
622
623
624
625
626
627
628
/**
 * In-tree plugins share their gettext domain with LibVLC.
 */
char *vlc_gettext( const char *msgid )
{
    return dgettext( PACKAGE_NAME, msgid );
}

gbazin's avatar
   
gbazin committed
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
/*****************************************************************************
 * count_utf8_string: returns the number of characters in the string.
 *****************************************************************************/
static int count_utf8_string( const char *psz_string )
{
    int i = 0, i_count = 0;
    while( psz_string[ i ] != 0 )
    {
        if( ((unsigned char *)psz_string)[ i ] <  0x80UL ) i_count++;
        i++;
    }
    return i_count;
}

/*****************************************************************************
 * wraptext: inserts \n at convenient places to wrap the text.
 *           Returns the modified string in a new buffer.
 *****************************************************************************/
647
char *vlc_wraptext( const char *psz_text, int i_line )
gbazin's avatar
   
gbazin committed
648
649
650
651
652
653
{
    int i_len;
    char *psz_line, *psz_new_text;

    psz_line = psz_new_text = strdup( psz_text );

654
    i_len = count_utf8_string( psz_text );
gbazin's avatar
   
gbazin committed
655
656
657
658
659
660
661
662

    while( i_len > i_line )
    {
        /* Look if there is a newline somewhere. */
        char *psz_parser = psz_line;
        int i_count = 0;
        while( i_count <= i_line && *psz_parser != '\n' )
        {
663
            while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser++;
gbazin's avatar
   
gbazin committed
664
665
666
667
668
669
670
671
672
673
674
675
676
            psz_parser++;
            i_count++;
        }
        if( *psz_parser == '\n' )
        {
            i_len -= (i_count + 1);
            psz_line = psz_parser + 1;
            continue;
        }

        /* Find the furthest space. */
        while( psz_parser > psz_line && *psz_parser != ' ' )
        {
677
            while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser--;
gbazin's avatar
   
gbazin committed
678
679
680
681
682
683
684
685
686
687
688
689
690
691
            psz_parser--;
            i_count--;
        }
        if( *psz_parser == ' ' )
        {
            *psz_parser = '\n';
            i_len -= (i_count + 1);
            psz_line = psz_parser + 1;
            continue;
        }

        /* Wrapping has failed. Find the first space or newline */
        while( i_count < i_len && *psz_parser != ' ' && *psz_parser != '\n' )
        {
692
            while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser++;
gbazin's avatar
   
gbazin committed
693
694
695
696
697
698
699
700
701
702
            psz_parser++;
            i_count++;
        }
        if( i_count < i_len ) *psz_parser = '\n';
        i_len -= (i_count + 1);
        psz_line = psz_parser + 1;
    }

    return psz_new_text;
}
703
704
705
706
707
708
709
710
711
712
713
714
715

/*****************************************************************************
 * iconv wrapper
 *****************************************************************************/
vlc_iconv_t vlc_iconv_open( const char *tocode, const char *fromcode )
{
#if defined(HAVE_ICONV)
    return iconv_open( tocode, fromcode );
#else
    return NULL;
#endif
}

716
size_t vlc_iconv( vlc_iconv_t cd, const char **inbuf, size_t *inbytesleft,
717
718
719
                  char **outbuf, size_t *outbytesleft )
{
#if defined(HAVE_ICONV)
720
721
    return iconv( cd, (ICONV_CONST char **)inbuf, inbytesleft,
                  outbuf, outbytesleft );
722
#else
723
724
725
726
727
728
729
730
    int i_bytes;

    if (inbytesleft == NULL || outbytesleft == NULL)
    {
        return 0;
    }

    i_bytes = __MIN(*inbytesleft, *outbytesleft);
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
    if( !inbuf || !outbuf || !i_bytes ) return (size_t)(-1);
    memcpy( *outbuf, *inbuf, i_bytes );
    inbuf += i_bytes;
    outbuf += i_bytes;
    inbytesleft -= i_bytes;
    outbytesleft -= i_bytes;
    return i_bytes;
#endif
}

int vlc_iconv_close( vlc_iconv_t cd )
{
#if defined(HAVE_ICONV)
    return iconv_close( cd );
#else
    return 0;
#endif
}
749
750
751
752
753

/*****************************************************************************
 * reduce a fraction
 *   (adapted from libavcodec, author Michael Niedermayer <michaelni@gmx.at>)
 *****************************************************************************/
754
bool vlc_ureduce( unsigned *pi_dst_nom, unsigned *pi_dst_den,
755
                        uint64_t i_nom, uint64_t i_den, uint64_t i_max )
756
{
757
    bool b_exact = 1;
Rémi Denis-Courmont's avatar
fix fix    
Rémi Denis-Courmont committed
758
    uint64_t i_gcd;
759
760
761

    if( i_den == 0 )
    {
762
763
        *pi_dst_nom = 0;
        *pi_dst_den = 1;
764
765
766
767
768
769
770
        return 1;
    }

    i_gcd = GCD( i_nom, i_den );
    i_nom /= i_gcd;
    i_den /= i_gcd;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
771
    if( i_max == 0 ) i_max = INT64_C(0xFFFFFFFF);
772

773
774
    if( i_nom > i_max || i_den > i_max )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
775
        uint64_t i_a0_num = 0, i_a0_den = 1, i_a1_num = 1, i_a1_den = 0;
776
777
778
779
        b_exact = 0;

        for( ; ; )
        {
Rémi Denis-Courmont's avatar
fix fix    
Rémi Denis-Courmont committed
780
781
782
            uint64_t i_x = i_nom / i_den;
            uint64_t i_a2n = i_x * i_a1_num + i_a0_num;
            uint64_t i_a2d = i_x * i_a1_den + i_a0_den;
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801

            if( i_a2n > i_max || i_a2d > i_max ) break;

            i_nom %= i_den;

            i_a0_num = i_a1_num; i_a0_den = i_a1_den;
            i_a1_num = i_a2n; i_a1_den = i_a2d;
            if( i_nom == 0 ) break;
            i_x = i_nom; i_nom = i_den; i_den = i_x;
        }
        i_nom = i_a1_num;
        i_den = i_a1_den;
    }

    *pi_dst_nom = i_nom;
    *pi_dst_den = i_den;

    return b_exact;
}
802

803
804
805
806
/*************************************************************************
 * vlc_execve: Execute an external program with a given environment,
 * wait until it finishes and return its standard output
 *************************************************************************/
807
808
809
810
int __vlc_execve( vlc_object_t *p_object, int i_argc, char *const *ppsz_argv,
                  char *const *ppsz_env, const char *psz_cwd,
                  const char *p_in, size_t i_in,
                  char **pp_data, size_t *pi_data )
811
{
812
    (void)i_argc; // <-- hmph
813
#ifdef HAVE_FORK
814
815
# define BUFSIZE 1024
    int fds[2], i_status;
816

817
    if (socketpair (AF_LOCAL, SOCK_STREAM, 0, fds))
818
819
        return -1;

820
821
822
    pid_t pid = -1;
    if ((fds[0] > 2) && (fds[1] > 2))
        pid = fork ();
823

824
825
826
    switch (pid)
    {
        case -1:
827
            msg_Err (p_object, "unable to fork (%m)");
828
829
830
            close (fds[0]);
            close (fds[1]);
            return -1;
831

832
        case 0:
833
834
835
836
837
        {
            sigset_t set;
            sigemptyset (&set);
            pthread_sigmask (SIG_SETMASK, &set, NULL);

838
839
840
841
842
843
844
845
            /* NOTE:
             * Like it or not, close can fail (and not only with EBADF)
             */
            if ((close (0) == 0) && (close (1) == 0) && (close (2) == 0)
             && (dup (fds[1]) == 0) && (dup (fds[1]) == 1)
             && (open ("/dev/null", O_RDONLY) == 2)
             && ((psz_cwd == NULL) || (chdir (psz_cwd) == 0)))
                execve (ppsz_argv[0], ppsz_argv, ppsz_env);
846

847
848
            exit (EXIT_FAILURE);
        }
849
850
    }

851
    close (fds[1]);
852
853

    *pi_data = 0;
854
855
    if (*pp_data)
        free (*pp_data);
856
    *pp_data = NULL;
857

858
859
860
861
    if (i_in == 0)
        shutdown (fds[0], SHUT_WR);

    while (!p_object->b_die)
862
    {
863
864
865
866
867
868
869
870
871
872
873
874
        struct pollfd ufd[1];
        memset (ufd, 0, sizeof (ufd));
        ufd[0].fd = fds[0];
        ufd[0].events = POLLIN;

        if (i_in > 0)
            ufd[0].events |= POLLOUT;

        if (poll (ufd, 1, 10) <= 0)
            continue;

        if (ufd[0].revents & ~POLLOUT)
875
        {
876
877
878
879
880
881
882
883
            char *ptr = realloc (*pp_data, *pi_data + BUFSIZE + 1);
            if (ptr == NULL)
                break; /* safely abort */

            *pp_data = ptr;

            ssize_t val = read (fds[0], ptr + *pi_data, BUFSIZE);
            switch (val)
884
            {
885
886
887
888
889
890
891
                case -1:
                case 0:
                    shutdown (fds[0], SHUT_RD);
                    break;

                default:
                    *pi_data += val;
892
893
894
            }
        }

895
        if (ufd[0].revents & POLLOUT)
896
        {
897
898
            ssize_t val = write (fds[0], p_in, i_in);
            switch (val)
899
            {
900
901
902
903
904
905
906
907
908
                case -1:
                case 0:
                    i_in = 0;
                    shutdown (fds[0], SHUT_WR);
                    break;

                default:
                    i_in -= val;
                    p_in += val;
909
910
            }
        }
911
    }
912

913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
    close (fds[0]);

    while (waitpid (pid, &i_status, 0) == -1);

    if (WIFEXITED (i_status))
    {
        i_status = WEXITSTATUS (i_status);
        if (i_status)
            msg_Warn (p_object,  "child %s (PID %d) exited with error code %d",
                      ppsz_argv[0], (int)pid, i_status);
    }
    else
    if (WIFSIGNALED (i_status)) // <-- this should be redumdant a check
    {
        i_status = WTERMSIG (i_status);
        msg_Warn (p_object, "child %s (PID %d) exited on signal %d (%s)",
                  ppsz_argv[0], (int)pid, i_status, strsignal (i_status));
930
931
    }

932
#elif defined( WIN32 ) && !defined( UNDER_CE )
zorglub's avatar
zorglub committed
933
934
    SECURITY_ATTRIBUTES saAttr;
    PROCESS_INFORMATION piProcInfo;
935
    STARTUPINFO siStartInfo;
zorglub's avatar
zorglub committed
936
    BOOL bFuncRetn = FALSE;
937
938
    HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr;
    DWORD i_status;
939
    char *psz_cmd = NULL, *p_env = NULL, *p = NULL;
940
941
942
943
    char **ppsz_parser;
    int i_size;

    /* Set the bInheritHandle flag so pipe handles are inherited. */
zorglub's avatar
zorglub committed
944
945
946
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;
947
948

    /* Create a pipe for the child process's STDOUT. */
zorglub's avatar
zorglub committed
949
    if ( !CreatePipe( &hChildStdoutRd, &hChildStdoutWr, &saAttr, 0 ) )
950
    {
zorglub's avatar
zorglub committed
951
        msg_Err( p_object, "stdout pipe creation failed" );
952
953
954
955
956
957
958
        return -1;
    }

    /* Ensure the read handle to the pipe for STDOUT is not inherited. */
    SetHandleInformation( hChildStdoutRd, HANDLE_FLAG_INHERIT, 0 );

    /* Create a pipe for the child process's STDIN. */
zorglub's avatar
zorglub committed
959
    if ( !CreatePipe( &hChildStdinRd, &hChildStdinWr, &saAttr, 0 ) )
960
    {
zorglub's avatar
zorglub committed
961
        msg_Err( p_object, "stdin pipe creation failed" );
962
963
964
965
966
967
968
969
        return -1;
    }

    /* Ensure the write handle to the pipe for STDIN is not inherited. */
    SetHandleInformation( hChildStdinWr, HANDLE_FLAG_INHERIT, 0 );

    /* Set up members of the PROCESS_INFORMATION structure. */
    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
zorglub's avatar
zorglub committed
970

971
972
    /* Set up members of the STARTUPINFO structure. */
    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
zorglub's avatar
zorglub committed
973
    siStartInfo.cb = sizeof(STARTUPINFO);
974
975
976
    siStartInfo.hStdError = hChildStdoutWr;
    siStartInfo.hStdOutput = hChildStdoutWr;
    siStartInfo.hStdInput = hChildStdinRd;
977
978
    siStartInfo.wShowWindow = SW_HIDE;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
979
980
981

    /* Set up the command line. */
    psz_cmd = malloc(32768);
982
983
    if( !psz_cmd )
        return -1;
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
    psz_cmd[0] = '\0';
    i_size = 32768;
    ppsz_parser = &ppsz_argv[0];
    while ( ppsz_parser[0] != NULL && i_size > 0 )
    {
        /* Protect the last argument with quotes ; the other arguments
         * are supposed to be already protected because they have been
         * passed as a command-line option. */
        if ( ppsz_parser[1] == NULL )
        {
            strncat( psz_cmd, "\"", i_size );
            i_size--;
        }
        strncat( psz_cmd, *ppsz_parser, i_size );
        i_size -= strlen( *ppsz_parser );
        if ( ppsz_parser[1] == NULL )
        {
            strncat( psz_cmd, "\"", i_size );
            i_size--;
        }
        strncat( psz_cmd, " ", i_size );
        i_size--;
        ppsz_parser++;
    }

    /* Set up the environment. */
    p = p_env = malloc(32768);
1011
1012
1013
1014
1015
1016
    if( !p )
    {
        free( psz_cmd );
        return -1;
    }

1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
    i_size = 32768;
    ppsz_parser = &ppsz_env[0];
    while ( *ppsz_parser != NULL && i_size > 0 )
    {
        memcpy( p, *ppsz_parser,
                __MIN((int)(strlen(*ppsz_parser) + 1), i_size) );
        p += strlen(*ppsz_parser) + 1;
        i_size -= strlen(*ppsz_parser) + 1;
        ppsz_parser++;
    }
    *p = '\0';
zorglub's avatar
zorglub committed
1028

1029
1030
    /* Create the child process. */
    bFuncRetn = CreateProcess( NULL,
zorglub's avatar
zorglub committed
1031
1032
1033
1034
1035
          psz_cmd,       // command line
          NULL,          // process security attributes
          NULL,          // primary thread security attributes
          TRUE,          // handles are inherited
          0,             // creation flags
1036
1037
          p_env,
          psz_cwd,
zorglub's avatar
zorglub committed
1038
1039
          &siStartInfo,  // STARTUPINFO pointer
          &piProcInfo ); // receives PROCESS_INFORMATION
1040
1041
1042

    free( psz_cmd );
    free( p_env );
zorglub's avatar
zorglub committed
1043
1044

    if ( bFuncRetn == 0 )
1045
    {
zorglub's avatar
zorglub committed
1046
        msg_Err( p_object, "child creation failed" );
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
        return -1;
    }

    /* Read from a file and write its contents to a pipe. */
    while ( i_in > 0 && !p_object->b_die )
    {
        DWORD i_written;
        if ( !WriteFile( hChildStdinWr, p_in, i_in, &i_written, NULL ) )
            break;
        i_in -= i_written;
        p_in += i_written;
    }

    /* Close the pipe handle so the child process stops reading. */
    CloseHandle(hChildStdinWr);

    /* Close the write end of the pipe before reading from the
     * read end of the pipe. */
    CloseHandle(hChildStdoutWr);
zorglub's avatar
zorglub committed
1066

1067
1068
    /* Read output from the child process. */
    *pi_data = 0;
1069
1070
1071
    if( *pp_data )
        free( pp_data );
    *pp_data = NULL;
1072
1073
1074
1075
1076
    *pp_data = malloc( 1025 );  /* +1 for \0 */

    while ( !p_object->b_die )
    {
        DWORD i_read;
zorglub's avatar
zorglub committed
1077
        if ( !ReadFile( hChildStdoutRd, &(*pp_data)[*pi_data], 1024, &i_read,
1078
1079
                        NULL )
              || i_read == 0 )
zorglub's avatar
zorglub committed
1080
            break;
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
        *pi_data += i_read;
        *pp_data = realloc( *pp_data, *pi_data + 1025 );
    }

    while ( !p_object->b_die
             && !GetExitCodeProcess( piProcInfo.hProcess, &i_status )
             && i_status != STILL_ACTIVE )
        msleep( 10000 );

    CloseHandle(piProcInfo.hProcess);
    CloseHandle(piProcInfo.hThread);

    if ( i_status )
        msg_Warn( p_object,
                  "child %s returned with error code %ld",
                  ppsz_argv[0], i_status );

1098
#else
1099
    msg_Err( p_object, "vlc_execve called but no implementation is available" );
1100
1101
1102
1103
    return -1;

#endif

1104
1105
1106
    if (*pp_data == NULL)
        return -1;

1107
1108
1109
    (*pp_data)[*pi_data] = '\0';
    return 0;
}