Coverage Report

Created: 2026-11-12 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/src/input/item.c
Line
Count
Source
1
/*****************************************************************************
2
 * item.c: input_item management
3
 *****************************************************************************
4
 * Copyright (C) 1998-2004 VLC authors and VideoLAN
5
 *
6
 * Authors: Clément Stenac <zorglub@videolan.org>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program; if not, write to the Free Software Foundation,
20
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21
 *****************************************************************************/
22
23
#ifdef HAVE_CONFIG_H
24
# include "config.h"
25
#endif
26
#include <assert.h>
27
#include <time.h>
28
#include <limits.h>
29
#include <ctype.h>
30
31
#include <vlc_common.h>
32
#include <vlc_arrays.h>
33
#include <vlc_url.h>
34
#include <vlc_strings.h>
35
36
#include "item.h"
37
#include "info.h"
38
#include "input_internal.h"
39
40
#include <vlc_charset.h>
41
42
struct input_item_opaque
43
{
44
    struct input_item_opaque *next;
45
    void *value;
46
    char name[1];
47
};
48
49
static enum input_item_type_e GuessType( const input_item_t *p_item, bool *p_net );
50
51
void input_item_SetPreparsed( input_item_t *p_i )
52
0
{
53
0
    vlc_mutex_lock( &p_i->lock );
54
55
0
    int status = vlc_meta_GetStatus(p_i->p_meta);
56
0
    int new_status = status | ITEM_PREPARSED;
57
0
    if( status != new_status )
58
0
        vlc_meta_SetStatus(p_i->p_meta, new_status);
59
60
0
    vlc_mutex_unlock( &p_i->lock );
61
0
}
62
63
void input_item_SetArtNotFound( input_item_t *p_i, bool b_not_found )
64
0
{
65
0
    vlc_mutex_lock( &p_i->lock );
66
67
0
    int status = vlc_meta_GetStatus(p_i->p_meta);
68
69
0
    if( b_not_found )
70
0
        status |= ITEM_ART_NOTFOUND;
71
0
    else
72
0
        status &= ~ITEM_ART_NOTFOUND;
73
74
0
    vlc_meta_SetStatus(p_i->p_meta, status);
75
76
0
    vlc_mutex_unlock( &p_i->lock );
77
0
}
78
79
void input_item_SetArtFetched( input_item_t *p_i, bool b_art_fetched )
80
0
{
81
0
    vlc_mutex_lock( &p_i->lock );
82
83
0
    int status = vlc_meta_GetStatus(p_i->p_meta);
84
85
0
    if( b_art_fetched )
86
0
        status |= ITEM_ART_FETCHED;
87
0
    else
88
0
        status &= ~ITEM_ART_FETCHED;
89
90
0
    vlc_meta_SetStatus(p_i->p_meta, status);
91
92
0
    vlc_mutex_unlock( &p_i->lock );
93
0
}
94
95
void input_item_SetMeta( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz_val )
96
0
{
97
0
    vlc_mutex_lock( &p_i->lock );
98
0
    vlc_meta_Set( p_i->p_meta, meta_type, psz_val );
99
0
    vlc_mutex_unlock( &p_i->lock );
100
0
}
101
102
void input_item_SetMetaExtra( input_item_t *p_i, const char *psz_name, const char *psz_value )
103
0
{
104
0
    vlc_mutex_lock( &p_i->lock );
105
0
    vlc_meta_SetExtra( p_i->p_meta, psz_name, psz_value );
106
0
    vlc_mutex_unlock( &p_i->lock );
107
0
}
108
109
void input_item_CopyOptions( input_item_t *p_child,
110
                             input_item_t *p_parent )
111
0
{
112
0
    char **optv = NULL;
113
0
    uint8_t *flagv = NULL;
114
0
    int optc = 0;
115
0
    char **optv_realloc = NULL;
116
0
    uint8_t *flagv_realloc = NULL;
117
118
0
    vlc_mutex_lock( &p_parent->lock );
119
120
0
    if( p_parent->i_options > 0 )
121
0
    {
122
0
        optv = vlc_alloc( p_parent->i_options, sizeof (*optv) );
123
0
        if( likely(optv) )
124
0
            flagv = vlc_alloc( p_parent->i_options, sizeof (*flagv) );
125
126
0
        if( likely(flagv) )
127
0
        {
128
0
            for( int i = 0; i < p_parent->i_options; i++ )
129
0
            {
130
0
                char *psz_dup = strdup( p_parent->ppsz_options[i] );
131
0
                if( likely(psz_dup) )
132
0
                {
133
0
                    flagv[optc] = p_parent->optflagv[i];
134
0
                    optv[optc++] = psz_dup;
135
0
                }
136
0
            }
137
0
        }
138
0
    }
139
140
0
    vlc_mutex_unlock( &p_parent->lock );
141
142
0
    if( likely(optv && flagv && optc ) )
143
0
    {
144
0
        vlc_mutex_lock( &p_child->lock );
145
146
0
        if( INT_MAX - p_child->i_options >= optc &&
147
0
            SIZE_MAX / sizeof (*flagv) >= (size_t) (p_child->i_options + optc) )
148
0
            flagv_realloc = realloc( p_child->optflagv,
149
0
                                    (p_child->i_options + optc) * sizeof (*flagv) );
150
0
        if( likely(flagv_realloc) )
151
0
        {
152
0
            p_child->optflagv = flagv_realloc;
153
0
            if( SIZE_MAX / sizeof (*optv) >= (size_t) (p_child->i_options + optc) )
154
0
                optv_realloc = realloc( p_child->ppsz_options,
155
0
                                       (p_child->i_options + optc) * sizeof (*optv) );
156
0
            if( likely(optv_realloc) )
157
0
            {
158
0
                p_child->ppsz_options = optv_realloc;
159
0
                memcpy( p_child->ppsz_options + p_child->i_options, optv,
160
0
                        optc * sizeof (*optv) );
161
0
                memcpy( p_child->optflagv + p_child->i_options, flagv,
162
0
                        optc * sizeof (*flagv) );
163
0
                p_child->i_options += optc;
164
0
                p_child->optflagc += optc;
165
0
            }
166
0
        }
167
168
0
        vlc_mutex_unlock( &p_child->lock );
169
0
    }
170
171
0
    if( unlikely(!flagv_realloc || !optv_realloc) )
172
0
    {
173
        /* Didn't copy pointers, so need to free the strdup() */
174
0
        for( int i=0; i<optc; i++ )
175
0
            free( optv[i] );
176
0
    }
177
178
0
    free( flagv );
179
0
    free( optv );
180
0
}
181
182
bool input_item_MetaMatch( input_item_t *p_i,
183
                           vlc_meta_type_t meta_type, const char *psz )
184
0
{
185
0
    vlc_mutex_lock( &p_i->lock );
186
187
0
    const char *psz_meta = vlc_meta_Get( p_i->p_meta, meta_type );
188
0
    bool b_ret = psz_meta && strcasestr( psz_meta, psz );
189
190
0
    vlc_mutex_unlock( &p_i->lock );
191
192
0
    return b_ret;
193
0
}
194
195
const char *input_item_GetMetaLocked(input_item_t *item,
196
                                     vlc_meta_type_t meta_type)
197
0
{
198
0
    vlc_mutex_assert(&item->lock);
199
200
0
    return vlc_meta_Get(item->p_meta, meta_type);
201
0
}
202
203
char *input_item_GetMeta( input_item_t *p_i, vlc_meta_type_t meta_type )
204
0
{
205
0
    vlc_mutex_lock( &p_i->lock );
206
0
    const char *value = input_item_GetMetaLocked( p_i, meta_type );
207
0
    char *psz = value ? strdup( value ) : NULL;
208
0
    vlc_mutex_unlock( &p_i->lock );
209
0
    return psz;
210
0
}
211
212
static const char *input_item_GetMetaExtraLocked( input_item_t *item, const char *psz_name )
213
0
{
214
0
    vlc_mutex_assert( &item->lock );
215
0
    return vlc_meta_GetExtra( item->p_meta, psz_name );
216
0
}
217
218
char *input_item_GetMetaExtra( input_item_t *p_i, const char *psz_name )
219
0
{
220
0
    vlc_mutex_lock( &p_i->lock );
221
0
    const char *value = input_item_GetMetaExtraLocked( p_i, psz_name );
222
0
    char *psz = value ? strdup( value ) : NULL;
223
0
    vlc_mutex_unlock( &p_i->lock );
224
0
    return psz;
225
0
}
226
227
unsigned input_item_GetMetaExtraNames( input_item_t *p_i, char ***pppsz_names )
228
0
{
229
0
    vlc_mutex_lock( &p_i->lock );
230
0
    unsigned count = vlc_meta_GetExtraCount( p_i->p_meta );
231
0
    *pppsz_names = vlc_meta_CopyExtraNames( p_i->p_meta );
232
0
    vlc_mutex_unlock( &p_i->lock );
233
0
    return count;
234
0
}
235
236
/* Get the title of a given item or fallback to the name if the title is empty */
237
char *input_item_GetTitleFbName( input_item_t *p_item )
238
0
{
239
0
    char *psz_ret;
240
0
    vlc_mutex_lock( &p_item->lock );
241
242
0
    const char *psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title );
243
0
    if( !EMPTY_STR( psz_title ) )
244
0
        psz_ret = strdup( psz_title );
245
0
    else
246
0
        psz_ret = p_item->psz_name ? strdup( p_item->psz_name ) : NULL;
247
248
0
    vlc_mutex_unlock( &p_item->lock );
249
0
    return psz_ret;
250
0
}
251
252
char *input_item_GetName( input_item_t *p_item )
253
0
{
254
0
    vlc_mutex_lock( &p_item->lock );
255
256
0
    char *psz_name = p_item->psz_name ? strdup( p_item->psz_name ) : NULL;
257
258
0
    vlc_mutex_unlock( &p_item->lock );
259
0
    return psz_name;
260
0
}
261
void input_item_SetName( input_item_t *p_item, const char *psz_name )
262
0
{
263
0
    vlc_mutex_lock( &p_item->lock );
264
265
0
    free( p_item->psz_name );
266
0
    p_item->psz_name = strdup( psz_name );
267
268
0
    vlc_mutex_unlock( &p_item->lock );
269
0
}
270
271
char *input_item_GetURI( input_item_t *p_i )
272
0
{
273
0
    vlc_mutex_lock( &p_i->lock );
274
275
0
    char *psz_s = p_i->psz_uri ? strdup( p_i->psz_uri ) : NULL;
276
277
0
    vlc_mutex_unlock( &p_i->lock );
278
0
    return psz_s;
279
0
}
280
281
void input_item_SetURI( input_item_t *p_i, const char *psz_uri )
282
0
{
283
0
    assert( psz_uri );
284
0
#ifndef NDEBUG
285
0
    if( !strchr( psz_uri, ':' )
286
0
     || strchr( psz_uri, ' ' ) || strchr( psz_uri, '"' ) )
287
0
        fprintf( stderr, "Warning: %s(\"%s\"): file path instead of URL.\n",
288
0
                 __func__, psz_uri );
289
0
#endif
290
0
    vlc_mutex_lock( &p_i->lock );
291
0
    free( p_i->psz_uri );
292
0
    p_i->psz_uri = strdup( psz_uri );
293
294
0
    p_i->i_type = GuessType( p_i, &p_i->b_net );
295
296
0
    if( p_i->psz_name )
297
0
        ;
298
0
    else
299
0
    if( p_i->i_type == ITEM_TYPE_FILE || p_i->i_type == ITEM_TYPE_DIRECTORY )
300
0
    {
301
0
        const char *psz_filename = strrchr( p_i->psz_uri, '/' );
302
303
0
        if( psz_filename && *psz_filename == '/' )
304
0
            psz_filename++;
305
0
        if( psz_filename && *psz_filename )
306
0
            p_i->psz_name = strdup( psz_filename );
307
308
        /* Make the name more readable */
309
0
        if( p_i->psz_name )
310
0
        {
311
0
            vlc_uri_decode( p_i->psz_name );
312
0
            EnsureUTF8( p_i->psz_name );
313
0
        }
314
0
    }
315
0
    else
316
0
    {   /* Strip login and password from title */
317
0
        int r;
318
0
        vlc_url_t url;
319
320
0
        vlc_UrlParse( &url, psz_uri );
321
0
        if( url.psz_protocol )
322
0
        {
323
0
            if( url.i_port > 0 )
324
0
                r=asprintf( &p_i->psz_name, "%s://%s:%u%s", url.psz_protocol,
325
0
                          url.psz_host, url.i_port,
326
0
                          url.psz_path ? url.psz_path : "" );
327
0
            else
328
0
                r=asprintf( &p_i->psz_name, "%s://%s%s", url.psz_protocol,
329
0
                          url.psz_host ? url.psz_host : "",
330
0
                          url.psz_path ? url.psz_path : "" );
331
0
        }
332
0
        else
333
0
        {
334
0
            if( url.i_port > 0 )
335
0
                r=asprintf( &p_i->psz_name, "%s:%u%s", url.psz_host, url.i_port,
336
0
                          url.psz_path ? url.psz_path : "" );
337
0
            else
338
0
                r=asprintf( &p_i->psz_name, "%s%s", url.psz_host,
339
0
                          url.psz_path ? url.psz_path : "" );
340
0
        }
341
0
        vlc_UrlClean( &url );
342
0
        if( -1==r )
343
0
            p_i->psz_name=NULL; /* recover from undefined value */
344
0
    }
345
346
0
    vlc_mutex_unlock( &p_i->lock );
347
0
}
348
349
vlc_tick_t input_item_GetDuration( input_item_t *p_i )
350
0
{
351
0
    vlc_mutex_lock( &p_i->lock );
352
353
0
    vlc_tick_t i_duration = p_i->i_duration;
354
355
0
    vlc_mutex_unlock( &p_i->lock );
356
0
    if (i_duration == INPUT_DURATION_INDEFINITE)
357
0
        i_duration = 0;
358
0
    else if (i_duration == INPUT_DURATION_UNSET)
359
0
        i_duration = 0;
360
0
    return i_duration;
361
0
}
362
363
void input_item_SetDuration( input_item_t *p_i, vlc_tick_t i_duration )
364
0
{
365
0
    vlc_mutex_lock( &p_i->lock );
366
0
    if( p_i->i_duration != i_duration )
367
0
        p_i->i_duration = i_duration;
368
0
    vlc_mutex_unlock( &p_i->lock );
369
0
}
370
371
char *input_item_GetNowPlayingFb( input_item_t *p_item )
372
0
{
373
0
    char *psz_meta = input_item_GetMeta( p_item, vlc_meta_NowPlaying );
374
0
    if( !psz_meta || strlen( psz_meta ) == 0 )
375
0
    {
376
0
        free( psz_meta );
377
0
        return input_item_GetMeta( p_item, vlc_meta_ESNowPlaying );
378
0
    }
379
380
0
    return psz_meta;
381
0
}
382
383
bool input_item_IsPreparsed( input_item_t *p_item )
384
0
{
385
0
    vlc_mutex_lock( &p_item->lock );
386
0
    bool b_preparsed = vlc_meta_GetStatus(p_item->p_meta) & ITEM_PREPARSED;
387
0
    vlc_mutex_unlock( &p_item->lock );
388
389
0
    return b_preparsed;
390
0
}
391
392
bool input_item_IsArtFetched( input_item_t *p_item )
393
0
{
394
0
    vlc_mutex_lock( &p_item->lock );
395
0
    bool b_fetched = vlc_meta_GetStatus(p_item->p_meta) & ITEM_ART_FETCHED;
396
0
    vlc_mutex_unlock( &p_item->lock );
397
398
0
    return b_fetched;
399
0
}
400
401
input_item_t *input_item_Hold( input_item_t *p_item )
402
0
{
403
0
    input_item_owner_t *owner = item_owner(p_item);
404
405
0
    vlc_atomic_rc_inc( &owner->rc );
406
0
    return p_item;
407
0
}
408
409
void *
410
input_item_GetLibvlcOwner(input_item_t *item)
411
0
{
412
0
    input_item_owner_t *owner = item_owner(item);
413
414
0
    return owner->libvlc_owner;
415
0
}
416
417
void
418
input_item_SetLibvlcOwner(input_item_t *item, void *libvlc_owner,
419
                          void (*release)(void *))
420
0
{
421
0
    input_item_owner_t *owner = item_owner(item);
422
423
0
    owner->libvlc_owner = libvlc_owner;
424
0
    owner->libvlc_owner_release = release;
425
0
}
426
427
void input_item_Release( input_item_t *p_item )
428
0
{
429
0
    input_item_owner_t *owner = item_owner(p_item);
430
431
0
    if( !vlc_atomic_rc_dec( &owner->rc ) )
432
0
        return;
433
434
0
    if( owner->libvlc_owner_release != NULL )
435
0
        owner->libvlc_owner_release( owner->libvlc_owner );
436
437
0
    free( p_item->psz_name );
438
0
    free( p_item->psz_uri );
439
0
    free( p_item->p_stats );
440
0
    vlc_meta_Delete( p_item->p_meta );
441
442
0
    for( input_item_opaque_t *o = p_item->opaques, *next; o != NULL; o = next )
443
0
    {
444
0
        next = o->next;
445
0
        free( o );
446
0
    }
447
448
0
    for( int i = 0; i < p_item->i_options; i++ )
449
0
        free( p_item->ppsz_options[i] );
450
0
    TAB_CLEAN( p_item->i_options, p_item->ppsz_options );
451
0
    free( p_item->optflagv );
452
453
0
    for( size_t i = 0; i < p_item->es_vec.size; ++i )
454
0
    {
455
0
        struct input_item_es *item_es = &p_item->es_vec.data[i];
456
0
        es_format_Clean( &item_es->es );
457
0
        free( item_es->id );
458
0
    }
459
0
    vlc_vector_destroy( &p_item->es_vec );
460
461
0
    for( int i = 0; i < p_item->i_epg; i++ )
462
0
        vlc_epg_Delete( p_item->pp_epg[i] );
463
0
    TAB_CLEAN( p_item->i_epg, p_item->pp_epg );
464
465
0
    info_category_t *cat;
466
0
    vlc_list_foreach( cat, &p_item->categories, node )
467
0
        info_category_Delete( cat );
468
469
0
    for( int i = 0; i < p_item->i_slaves; i++ )
470
0
        input_item_slave_Delete( p_item->pp_slaves[i] );
471
0
    TAB_CLEAN( p_item->i_slaves, p_item->pp_slaves );
472
473
0
    free( owner );
474
0
}
475
476
int input_item_AddOption( input_item_t *p_input, const char *psz_option,
477
                          unsigned flags )
478
0
{
479
0
    int err = VLC_SUCCESS;
480
481
0
    if( psz_option == NULL )
482
0
        return VLC_EGENERIC;
483
484
0
    vlc_mutex_lock( &p_input->lock );
485
0
    if (flags & VLC_INPUT_OPTION_UNIQUE)
486
0
    {
487
0
        for (int i = 0 ; i < p_input->i_options; i++)
488
0
            if( !strcmp( p_input->ppsz_options[i], psz_option ) )
489
0
                goto out;
490
0
    }
491
492
0
    uint8_t *flagv = realloc (p_input->optflagv, p_input->optflagc + 1);
493
0
    if (flagv == NULL)
494
0
    {
495
0
        err = VLC_ENOMEM;
496
0
        goto out;
497
0
    }
498
499
0
    p_input->optflagv = flagv;
500
501
0
    char* psz_option_dup = strdup( psz_option );
502
0
    if( unlikely( !psz_option_dup ) )
503
0
    {
504
0
        err = VLC_ENOMEM;
505
0
        goto out;
506
0
    }
507
508
0
    TAB_APPEND(p_input->i_options, p_input->ppsz_options, psz_option_dup);
509
510
0
    flagv[p_input->optflagc++] = flags;
511
512
0
out:
513
0
    vlc_mutex_unlock( &p_input->lock );
514
0
    return err;
515
0
}
516
517
int input_item_AddOptions( input_item_t *p_item, int i_options,
518
                           const char *const *ppsz_options,
519
                           unsigned i_flags )
520
0
{
521
0
    int i_ret = VLC_SUCCESS;
522
0
    for( int i = 0; i < i_options && i_ret == VLC_SUCCESS; i++ )
523
0
        i_ret = input_item_AddOption( p_item, ppsz_options[i], i_flags );
524
0
    return i_ret;
525
0
}
526
527
int input_item_AddOpaque(input_item_t *item, const char *name, void *value)
528
0
{
529
0
    assert(name != NULL);
530
531
0
    size_t namelen = strlen(name);
532
0
    input_item_opaque_t *entry = malloc(sizeof (*entry) + namelen);
533
0
    if (unlikely(entry == NULL))
534
0
        return VLC_ENOMEM;
535
536
0
    memcpy(entry->name, name, namelen + 1);
537
0
    entry->value = value;
538
539
0
    vlc_mutex_lock(&item->lock);
540
0
    entry->next = item->opaques;
541
0
    item->opaques = entry;
542
0
    vlc_mutex_unlock(&item->lock);
543
0
    return VLC_SUCCESS;
544
0
}
545
546
void input_item_ApplyOptions(vlc_object_t *obj, input_item_t *item)
547
0
{
548
0
    vlc_mutex_lock(&item->lock);
549
0
    assert(item->optflagc == (unsigned)item->i_options);
550
551
0
    for (unsigned i = 0; i < (unsigned)item->i_options; i++)
552
0
        var_OptionParse(obj, item->ppsz_options[i],
553
0
                        !!(item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED));
554
555
0
    for (const input_item_opaque_t *o = item->opaques; o != NULL; o = o->next)
556
0
    {
557
0
        var_Create(obj, o->name, VLC_VAR_ADDRESS);
558
0
        var_SetAddress(obj, o->name, o->value);
559
0
    }
560
561
0
    vlc_mutex_unlock(&item->lock);
562
0
}
563
564
static int bsearch_strcmp_cb(const void *a, const void *b)
565
0
{
566
0
    const char *const *entry = b;
567
0
    return strcasecmp(a, *entry);
568
0
}
569
570
static bool input_item_IsMaster(const char *psz_filename)
571
0
{
572
0
    static const char *const ppsz_master_exts[] = { MASTER_EXTENSIONS };
573
574
0
    const char *psz_ext = strrchr(psz_filename, '.');
575
0
    if (psz_ext == NULL || *(++psz_ext) == '\0')
576
0
        return false;
577
578
0
    return bsearch(psz_ext, ppsz_master_exts, ARRAY_SIZE(ppsz_master_exts),
579
0
                   sizeof(const char *), bsearch_strcmp_cb) != NULL;
580
0
}
581
582
bool input_item_slave_GetType(const char *psz_filename,
583
                              enum slave_type *p_slave_type)
584
0
{
585
0
    static const char *const ppsz_sub_exts[] = { SLAVE_SPU_EXTENSIONS };
586
0
    static const char *const ppsz_audio_exts[] = { SLAVE_AUDIO_EXTENSIONS };
587
588
0
    static struct {
589
0
        enum slave_type i_type;
590
0
        const char *const *ppsz_exts;
591
0
        size_t nmemb;
592
0
    } p_slave_list[] = {
593
0
        { SLAVE_TYPE_SPU, ppsz_sub_exts, ARRAY_SIZE(ppsz_sub_exts) },
594
0
        { SLAVE_TYPE_GENERIC, ppsz_audio_exts, ARRAY_SIZE(ppsz_audio_exts) },
595
0
    };
596
597
0
    const char *psz_ext = strrchr(psz_filename, '.');
598
0
    if (psz_ext == NULL || *(++psz_ext) == '\0')
599
0
        return false;
600
601
0
    for (unsigned int i = 0; i < sizeof(p_slave_list) / sizeof(*p_slave_list); ++i)
602
0
    {
603
0
        if (bsearch(psz_ext, p_slave_list[i].ppsz_exts, p_slave_list[i].nmemb,
604
0
                    sizeof(const char *), bsearch_strcmp_cb))
605
0
        {
606
0
            *p_slave_type = p_slave_list[i].i_type;
607
0
            return true;
608
0
        }
609
0
    }
610
0
    return false;
611
0
}
612
613
input_item_slave_t *input_item_slave_New(const char *psz_uri, enum slave_type i_type,
614
                                       enum slave_priority i_priority)
615
0
{
616
0
    if( !psz_uri )
617
0
        return NULL;
618
619
0
    input_item_slave_t *p_slave = malloc( sizeof( *p_slave ) + strlen( psz_uri ) + 1 );
620
0
    if( !p_slave )
621
0
        return NULL;
622
623
0
    p_slave->i_type = i_type;
624
0
    p_slave->i_priority = i_priority;
625
0
    p_slave->b_forced = false;
626
0
    strcpy( p_slave->psz_uri, psz_uri );
627
628
0
    return p_slave;
629
0
}
630
631
int input_item_AddSlave(input_item_t *p_item, input_item_slave_t *p_slave)
632
0
{
633
0
    if( p_item == NULL || p_slave == NULL
634
0
     || p_slave->i_priority < SLAVE_PRIORITY_MATCH_NONE )
635
0
        return VLC_EGENERIC;
636
637
0
    vlc_mutex_lock( &p_item->lock );
638
639
0
    TAB_APPEND(p_item->i_slaves, p_item->pp_slaves, p_slave);
640
641
0
    vlc_mutex_unlock( &p_item->lock );
642
0
    return VLC_SUCCESS;
643
0
}
644
645
static info_category_t *InputItemFindCat( input_item_t *p_item,
646
                                          const char *psz_cat )
647
0
{
648
0
    vlc_mutex_assert( &p_item->lock );
649
0
    if( !psz_cat )
650
0
        return NULL;
651
652
0
    info_category_t *p_cat;
653
0
    vlc_list_foreach( p_cat, &p_item->categories, node )
654
0
    {
655
0
        if( !strcmp( p_cat->psz_name, psz_cat ) )
656
0
            return p_cat;
657
0
    }
658
0
    return NULL;
659
0
}
660
661
bool input_item_Playable(const char *psz_filename)
662
0
{
663
0
    if (psz_filename == NULL) {
664
0
        return false;
665
0
    }
666
667
0
    enum slave_type unused;
668
0
    return input_item_slave_GetType(psz_filename, &unused) || input_item_IsMaster(psz_filename);
669
0
}
670
671
/**
672
 * Get a info item from a given category in a given input item.
673
 *
674
 * \param p_i The input item to get info from
675
 * \param psz_cat String representing the category for the info
676
 * \param psz_name String representing the name of the desired info
677
 * \return A pointer to the string with the given info if found, or an
678
 *         empty string otherwise. The caller should free the returned
679
 *         pointer.
680
 */
681
char *input_item_GetInfo( input_item_t *p_i,
682
                          const char *psz_cat,
683
                          const char *psz_name )
684
0
{
685
0
    vlc_mutex_lock( &p_i->lock );
686
687
0
    char * result = input_item_GetInfoLocked( p_i, psz_cat, psz_name );
688
689
0
    vlc_mutex_unlock( &p_i->lock );
690
691
0
    return result;
692
0
}
693
694
char *input_item_GetInfoLocked( input_item_t *p_i,
695
                                const char *psz_cat,
696
                                const char *psz_name )
697
0
{
698
0
    vlc_mutex_assert( &p_i->lock );
699
700
0
    info_category_t *p_cat = InputItemFindCat( p_i, psz_cat );
701
0
    if( p_cat )
702
0
    {
703
0
        info_t *p_info = info_category_FindInfo( p_cat, psz_name );
704
0
        if( p_info && p_info->psz_value )
705
0
        {
706
0
            char *psz_ret = strdup( p_info->psz_value );
707
0
            return psz_ret;
708
0
        }
709
0
    }
710
711
0
    return strdup( "" );
712
0
}
713
714
static int InputItemVaAddInfo( input_item_t *p_i,
715
                               const char *psz_cat,
716
                               const char *psz_name,
717
                               const char *psz_format, va_list args )
718
0
{
719
0
    vlc_mutex_assert( &p_i->lock );
720
721
0
    info_category_t *p_cat = InputItemFindCat( p_i, psz_cat );
722
0
    if( !p_cat )
723
0
    {
724
0
        p_cat = info_category_New( psz_cat );
725
0
        if( !p_cat )
726
0
            return VLC_ENOMEM;
727
0
        vlc_list_append( &p_cat->node, &p_i->categories );
728
0
    }
729
0
    info_t *p_info = info_category_VaAddInfo( p_cat, psz_name, psz_format, args );
730
0
    if( !p_info || !p_info->psz_value )
731
0
        return VLC_EGENERIC;
732
0
    return VLC_SUCCESS;
733
0
}
734
735
int input_item_AddInfo( input_item_t *p_i,
736
                        const char *psz_cat,
737
                        const char *psz_name,
738
                        const char *psz_format, ... )
739
0
{
740
0
    va_list args;
741
742
0
    vlc_mutex_lock( &p_i->lock );
743
744
0
    va_start( args, psz_format );
745
0
    const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args );
746
0
    va_end( args );
747
748
0
    vlc_mutex_unlock( &p_i->lock );
749
750
0
    return i_ret;
751
0
}
752
753
int input_item_DelInfo( input_item_t *p_i,
754
                        const char *psz_cat,
755
                        const char *psz_name )
756
0
{
757
0
    vlc_mutex_lock( &p_i->lock );
758
0
    info_category_t *p_cat = InputItemFindCat( p_i, psz_cat );
759
0
    if( !p_cat )
760
0
    {
761
0
        vlc_mutex_unlock( &p_i->lock );
762
0
        return VLC_EGENERIC;
763
0
    }
764
765
0
    if( psz_name )
766
0
    {
767
        /* Remove a specific info */
768
0
        int i_ret = info_category_DeleteInfo( p_cat, psz_name );
769
0
        if( i_ret )
770
0
        {
771
0
            vlc_mutex_unlock( &p_i->lock );
772
0
            return VLC_EGENERIC;
773
0
        }
774
0
    }
775
0
    else
776
0
    {
777
        /* Remove the complete categorie */
778
0
        vlc_list_remove( &p_cat->node );
779
0
        info_category_Delete( p_cat );
780
0
    }
781
0
    vlc_mutex_unlock( &p_i->lock );
782
783
0
    return VLC_SUCCESS;
784
0
}
785
void input_item_ReplaceInfos( input_item_t *p_item, info_category_t *p_cat )
786
0
{
787
0
    vlc_mutex_lock( &p_item->lock );
788
0
    info_category_t *p_old = InputItemFindCat( p_item, p_cat->psz_name );
789
0
    if( p_old )
790
0
    {
791
0
        vlc_list_add_after( &p_cat->node, &p_old->node );
792
0
        vlc_list_remove( &p_old->node );
793
0
        info_category_Delete( p_old );
794
0
    }
795
0
    else
796
0
        vlc_list_append( &p_cat->node, &p_item->categories );
797
0
    vlc_mutex_unlock( &p_item->lock );
798
0
}
799
800
void input_item_MergeInfos( input_item_t *p_item, info_category_t *p_cat )
801
0
{
802
0
    vlc_mutex_lock( &p_item->lock );
803
0
    info_category_t *p_old = InputItemFindCat( p_item, p_cat->psz_name );
804
0
    if( p_old )
805
0
    {
806
0
        info_t *info;
807
808
0
        info_foreach(info, &p_cat->infos)
809
0
            info_category_ReplaceInfo( p_old, info );
810
0
        vlc_list_init( &p_cat->infos );
811
0
        info_category_Delete( p_cat );
812
0
    }
813
0
    else
814
0
        vlc_list_append( &p_cat->node, &p_item->categories );
815
0
    vlc_mutex_unlock( &p_item->lock );
816
0
}
817
818
void input_item_SetEpgEvent( input_item_t *p_item, const vlc_epg_event_t *p_epg_evt )
819
0
{
820
0
    vlc_mutex_lock( &p_item->lock );
821
822
0
    for( int i = 0; i < p_item->i_epg; i++ )
823
0
    {
824
0
        vlc_epg_t *p_epg = p_item->pp_epg[i];
825
0
        for( size_t j = 0; j < p_epg->i_event; j++ )
826
0
        {
827
            /* Same event can exist in more than one table */
828
0
            if( p_epg->pp_event[j]->i_id == p_epg_evt->i_id )
829
0
            {
830
0
                vlc_epg_event_t *p_dup = vlc_epg_event_Duplicate( p_epg_evt );
831
0
                if( p_dup )
832
0
                {
833
0
                    if( p_epg->p_current == p_epg->pp_event[j] )
834
0
                        p_epg->p_current = p_dup;
835
0
                    vlc_epg_event_Delete( p_epg->pp_event[j] );
836
0
                    p_epg->pp_event[j] = p_dup;
837
0
                }
838
0
                break;
839
0
            }
840
0
        }
841
0
    }
842
0
    vlc_mutex_unlock( &p_item->lock );
843
0
}
844
845
//#define EPG_DEBUG
846
#ifdef EPG_DEBUG
847
static int InputItemAddInfo( input_item_t *p_i,
848
                             const char *psz_cat,
849
                             const char *psz_name,
850
                             const char *psz_format, ... )
851
{
852
    va_list args;
853
854
    va_start( args, psz_format );
855
    const int i_ret = InputItemVaAddInfo( p_i, psz_cat, psz_name, psz_format, args );
856
    va_end( args );
857
858
    return i_ret;
859
}
860
#endif
861
862
void input_item_SetEpg( input_item_t *p_item, const vlc_epg_t *p_update, bool b_current_source )
863
0
{
864
0
    vlc_epg_t *p_epg = vlc_epg_Duplicate( p_update );
865
0
    if( !p_epg )
866
0
        return;
867
868
0
    vlc_mutex_lock( &p_item->lock );
869
870
    /* */
871
0
    vlc_epg_t **pp_epg = NULL;
872
0
    for( int i = 0; i < p_item->i_epg; i++ )
873
0
    {
874
0
        if( p_item->pp_epg[i]->i_source_id == p_update->i_source_id &&
875
0
            p_item->pp_epg[i]->i_id == p_update->i_id )
876
0
        {
877
0
            pp_epg = &p_item->pp_epg[i];
878
0
            break;
879
0
        }
880
0
    }
881
882
    /* replace with new version */
883
0
    if( pp_epg )
884
0
    {
885
0
        vlc_epg_Delete( *pp_epg );
886
0
        if( *pp_epg == p_item->p_epg_table ) /* current table can have changed */
887
0
            p_item->p_epg_table = NULL;
888
0
        *pp_epg = p_epg;
889
0
    }
890
0
    else
891
0
    {
892
0
        TAB_APPEND( p_item->i_epg, p_item->pp_epg, p_epg );
893
0
    }
894
895
0
    if( b_current_source && p_epg->b_present )
896
0
        p_item->p_epg_table = p_epg;
897
898
0
    vlc_mutex_unlock( &p_item->lock );
899
900
#ifdef EPG_DEBUG
901
    char *psz_epg;
902
    if( asprintf( &psz_epg, "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 )
903
        return;
904
905
    input_item_DelInfo( p_item, psz_epg, NULL );
906
907
    vlc_mutex_lock( &p_item->lock );
908
    for( size_t i = 0; i < p_epg->i_event; i++ )
909
    {
910
        const vlc_epg_event_t *p_evt = p_epg->pp_event[i];
911
        time_t t_start = (time_t)p_evt->i_start;
912
        struct tm tm_start;
913
        char psz_start[128];
914
915
        localtime_r( &t_start, &tm_start );
916
917
        snprintf( psz_start, sizeof(psz_start), "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
918
                  1900 + tm_start.tm_year, 1 + tm_start.tm_mon, tm_start.tm_mday,
919
                  tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec );
920
        if( p_evt->psz_short_description || p_evt->psz_description )
921
            InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s %s",
922
                              p_evt->psz_name,
923
                              p_evt->i_duration/60/60, (p_evt->i_duration/60)%60,
924
                              p_evt->psz_short_description ? p_evt->psz_short_description : "" ,
925
                              p_evt->psz_description ? p_evt->psz_description : "" );
926
        else
927
            InputItemAddInfo( p_item, psz_epg, psz_start, "%s (%2.2d:%2.2d)",
928
                              p_evt->psz_name,
929
                              p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 );
930
    }
931
    vlc_mutex_unlock( &p_item->lock );
932
    free( psz_epg );
933
#endif
934
0
}
935
936
void input_item_ChangeEPGSource( input_item_t *p_item, int i_source_id )
937
0
{
938
0
    vlc_mutex_lock( &p_item->lock );
939
0
    p_item->p_epg_table = NULL;
940
0
    if( i_source_id > 0 )
941
0
    {
942
        /* Update pointer to current/next table in the full schedule */
943
0
        for( int i = 0; i < p_item->i_epg; i++ )
944
0
        {
945
0
            if( p_item->pp_epg[i]->i_source_id == i_source_id &&
946
0
                p_item->pp_epg[i]->b_present )
947
0
            {
948
0
                p_item->p_epg_table = p_item->pp_epg[i];
949
0
                break;
950
0
            }
951
0
        }
952
0
    }
953
0
    vlc_mutex_unlock( &p_item->lock );
954
0
}
955
956
void input_item_SetEpgTime( input_item_t *p_item, int64_t i_time )
957
0
{
958
0
    vlc_mutex_lock( &p_item->lock );
959
0
    p_item->i_epg_time = i_time;
960
0
    vlc_mutex_unlock( &p_item->lock );
961
0
}
962
963
void input_item_SetEpgOffline( input_item_t *p_item )
964
0
{
965
0
    input_item_ChangeEPGSource( p_item, -1 );
966
967
#ifdef EPG_DEBUG
968
    vlc_mutex_lock( &p_item->lock );
969
    const int i_epg_info = p_item->i_epg;
970
    if( i_epg_info > 0 )
971
    {
972
        char *ppsz_epg_info[i_epg_info];
973
        for( int i = 0; i < p_item->i_epg; i++ )
974
        {
975
            const vlc_epg_t *p_epg = p_item->pp_epg[i];
976
            if( asprintf( &ppsz_epg_info[i], "EPG %s", p_epg->psz_name ? p_epg->psz_name : "unknown" ) < 0 )
977
                ppsz_epg_info[i] = NULL;
978
        }
979
        vlc_mutex_unlock( &p_item->lock );
980
981
        for( int i = 0; i < i_epg_info; i++ )
982
        {
983
            if( !ppsz_epg_info[i] )
984
                continue;
985
            input_item_DelInfo( p_item, ppsz_epg_info[i], NULL );
986
            free( ppsz_epg_info[i] );
987
        }
988
    }
989
    else
990
        vlc_mutex_unlock( &p_item->lock );
991
#endif
992
0
}
993
994
input_item_t *
995
input_item_NewExt( const char *psz_uri, const char *psz_name,
996
                   vlc_tick_t duration, enum input_item_type_e type, enum input_item_net_type i_net )
997
0
{
998
0
    input_item_owner_t *owner = calloc( 1, sizeof( *owner ) );
999
0
    if( unlikely(owner == NULL) )
1000
0
        return NULL;
1001
1002
0
    vlc_atomic_rc_init( &owner->rc );
1003
0
    owner->libvlc_owner = NULL;
1004
0
    owner->libvlc_owner_release = NULL;
1005
1006
0
    input_item_t *p_input = &owner->item;
1007
1008
0
    p_input->p_meta = vlc_meta_New();
1009
0
    if( unlikely(p_input->p_meta == NULL) )
1010
0
    {
1011
0
        free(owner);
1012
0
        return NULL;
1013
0
    }
1014
1015
0
    vlc_mutex_init( &p_input->lock );
1016
1017
0
    p_input->psz_name = NULL;
1018
0
    if( psz_name )
1019
0
        input_item_SetName( p_input, psz_name );
1020
1021
0
    p_input->psz_uri = NULL;
1022
0
    if( psz_uri )
1023
0
        input_item_SetURI( p_input, psz_uri );
1024
0
    else
1025
0
    {
1026
0
        p_input->i_type = ITEM_TYPE_UNKNOWN;
1027
0
        p_input->b_net = false;
1028
0
    }
1029
1030
0
    TAB_INIT( p_input->i_options, p_input->ppsz_options );
1031
0
    p_input->optflagc = 0;
1032
0
    p_input->optflagv = NULL;
1033
0
    p_input->opaques = NULL;
1034
1035
0
    p_input->i_duration = duration;
1036
0
    vlc_list_init( &p_input->categories );
1037
0
    vlc_vector_init( &p_input->es_vec );
1038
0
    p_input->p_stats = NULL;
1039
0
    TAB_INIT( p_input->i_epg, p_input->pp_epg );
1040
0
    TAB_INIT( p_input->i_slaves, p_input->pp_slaves );
1041
1042
0
    if( type != ITEM_TYPE_UNKNOWN )
1043
0
        p_input->i_type = type;
1044
1045
0
    if( i_net != ITEM_NET_UNKNOWN )
1046
0
        p_input->b_net = i_net == ITEM_NET;
1047
0
    return p_input;
1048
0
}
1049
1050
input_item_t *input_item_Copy( input_item_t *p_input )
1051
0
{
1052
0
    input_item_t *item;
1053
1054
0
    vlc_mutex_lock( &p_input->lock );
1055
1056
0
    item = input_item_NewExt( p_input->psz_uri, p_input->psz_name,
1057
0
                              p_input->i_duration, p_input->i_type,
1058
0
                              ITEM_NET_UNKNOWN );
1059
0
    if( unlikely(item == NULL) )
1060
0
    {
1061
0
        vlc_mutex_unlock( &p_input->lock );
1062
0
        return NULL;
1063
0
    }
1064
1065
0
    vlc_meta_Merge( item->p_meta, p_input->p_meta );
1066
0
    item->b_net = p_input->b_net;
1067
1068
0
    if( p_input->i_slaves > 0 )
1069
0
    {
1070
0
        for( int i = 0; i < p_input->i_slaves; i++ )
1071
0
        {
1072
0
            input_item_slave_t* slave = input_item_slave_New(
1073
0
                        p_input->pp_slaves[i]->psz_uri,
1074
0
                        p_input->pp_slaves[i]->i_type,
1075
0
                        p_input->pp_slaves[i]->i_priority);
1076
0
            if( unlikely(slave != NULL) )
1077
0
            {
1078
0
                TAB_APPEND(item->i_slaves, item->pp_slaves, slave);
1079
0
            }
1080
0
        }
1081
0
    }
1082
1083
0
    struct input_item_es item_es;
1084
0
    struct input_item_es cpy_item_es;
1085
0
    vlc_vector_foreach(item_es, &p_input->es_vec) {
1086
0
        cpy_item_es.id_stable = item_es.id_stable;
1087
0
        cpy_item_es.id = strdup(item_es.id);
1088
0
        if (cpy_item_es.id == NULL) {
1089
0
            goto error;
1090
0
        }
1091
0
        es_format_Init(&cpy_item_es.es, UNKNOWN_ES, 0);
1092
0
        if (es_format_Copy(&cpy_item_es.es, &item_es.es) != VLC_SUCCESS) {
1093
            /* es_format_Copy didn't stop if an allocation fail so we need to
1094
             * clear all memory allocated */
1095
0
            es_format_Clean(&cpy_item_es.es);
1096
0
            free(cpy_item_es.id);
1097
0
            goto error;
1098
0
        }
1099
0
        vlc_vector_push(&item->es_vec, cpy_item_es);
1100
0
    }
1101
1102
0
    vlc_mutex_unlock( &p_input->lock );
1103
1104
    /* No need to lock; no other thread has seen this new item yet. */
1105
0
    input_item_CopyOptions( item, p_input );
1106
0
    return item;
1107
1108
0
error:
1109
0
    vlc_mutex_unlock(&p_input->lock);
1110
0
    input_item_Release(item);
1111
0
    return NULL;
1112
0
}
1113
1114
int input_item_Update(input_item_t *dst, input_item_t *src)
1115
0
{
1116
0
    assert(dst != NULL);
1117
0
    assert(src != NULL);
1118
1119
0
    if (strcmp(dst->psz_uri, src->psz_uri)) {
1120
0
        return VLC_EGENERIC;
1121
0
    }
1122
1123
0
    input_item_t *tmp = input_item_Copy(src);
1124
0
    if (tmp == NULL) {
1125
0
        return VLC_EGENERIC;
1126
0
    }
1127
1128
0
    vlc_mutex_lock(&dst->lock);
1129
0
    if (dst->psz_name == NULL) {
1130
0
        dst->psz_name = tmp->psz_name;
1131
0
        tmp->psz_name = NULL;
1132
0
    }
1133
0
    dst->i_duration = tmp->i_duration;
1134
1135
0
    struct input_item_es item_es;
1136
0
    vlc_vector_foreach(item_es, &dst->es_vec) {
1137
0
        free(item_es.id);
1138
0
        es_format_Clean(&item_es.es);
1139
0
    }
1140
0
    vlc_vector_clear(&dst->es_vec);
1141
0
    dst->es_vec = tmp->es_vec;
1142
0
    vlc_vector_init(&tmp->es_vec);
1143
1144
0
    if (tmp->p_meta != NULL) {
1145
0
        if (dst->p_meta != NULL) {
1146
0
            vlc_meta_Merge(dst->p_meta, tmp->p_meta);
1147
0
        } else {
1148
0
            dst->p_meta = tmp->p_meta;
1149
0
            tmp->p_meta = NULL;
1150
0
        }
1151
0
    }
1152
1153
0
    for( int i = 0; i < dst->i_slaves; i++ )
1154
0
        input_item_slave_Delete( dst->pp_slaves[i] );
1155
0
    TAB_CLEAN(dst->i_slaves, dst->pp_slaves );
1156
0
    dst->i_slaves = tmp->i_slaves;
1157
0
    tmp->i_slaves = 0;
1158
0
    dst->pp_slaves = tmp->pp_slaves;
1159
0
    tmp->pp_slaves = NULL;
1160
0
    vlc_mutex_unlock(&dst->lock);
1161
1162
0
    input_item_Release(tmp);
1163
0
    return VLC_SUCCESS;
1164
0
}
1165
1166
struct item_type_entry
1167
{
1168
    const char *psz_scheme;
1169
    enum input_item_type_e i_type;
1170
    bool       b_net;
1171
};
1172
1173
static int typecmp( const void *key, const void *entry )
1174
0
{
1175
0
    const struct item_type_entry *type = entry;
1176
0
    const char *uri = key, *scheme = type->psz_scheme;
1177
1178
0
    return strncmp( uri, scheme, strlen( scheme ) );
1179
0
}
1180
1181
/* Guess the type of the item using the beginning of the mrl */
1182
static enum input_item_type_e GuessType( const input_item_t *p_item, bool *p_net )
1183
0
{
1184
0
    static const struct item_type_entry tab[] =
1185
0
    {   /* /!\ Alphabetical order /!\ */
1186
        /* Short match work, not just exact match */
1187
0
        { "alsa",   ITEM_TYPE_CARD, false },
1188
0
        { "atsc",   ITEM_TYPE_CARD, false },
1189
0
        { "avcapt", ITEM_TYPE_CARD, false }, /* AVCapture */
1190
0
        { "bd",     ITEM_TYPE_DISC, false },
1191
0
        { "bluray", ITEM_TYPE_DISC, false },
1192
0
        { "cable",  ITEM_TYPE_CARD, false },
1193
0
        { "cdda",   ITEM_TYPE_DISC, false },
1194
0
        { "cqam",   ITEM_TYPE_CARD, false },
1195
0
        { "dc1394", ITEM_TYPE_CARD, false },
1196
0
        { "dccp",   ITEM_TYPE_STREAM, true },
1197
0
        { "deckli", ITEM_TYPE_CARD, false }, /* decklink */
1198
0
        { "dir",    ITEM_TYPE_DIRECTORY, false },
1199
0
        { "dshow",  ITEM_TYPE_CARD, false },
1200
0
        { "dtv",    ITEM_TYPE_CARD, false },
1201
0
        { "dvb",    ITEM_TYPE_CARD, false },
1202
0
        { "dvd",    ITEM_TYPE_DISC, false },
1203
0
        { "fd",     ITEM_TYPE_UNKNOWN, false },
1204
0
        { "file",   ITEM_TYPE_FILE, false },
1205
0
        { "ftp",    ITEM_TYPE_FILE, true },
1206
0
        { "gopher", ITEM_TYPE_STREAM, true },
1207
0
        { "http",   ITEM_TYPE_FILE, true },
1208
0
        { "icyx",   ITEM_TYPE_STREAM, true },
1209
0
        { "imem",   ITEM_TYPE_UNKNOWN, false },
1210
0
        { "isdb-",  ITEM_TYPE_CARD, false },
1211
0
        { "itpc",   ITEM_TYPE_PLAYLIST, true },
1212
0
        { "jack",   ITEM_TYPE_CARD, false },
1213
0
        { "linsys", ITEM_TYPE_CARD, false },
1214
0
        { "live",   ITEM_TYPE_STREAM, true }, /* livedotcom */
1215
0
        { "mms",    ITEM_TYPE_STREAM, true },
1216
0
        { "mtp",    ITEM_TYPE_DISC, false },
1217
0
        { "ndi",    ITEM_TYPE_STREAM, true},
1218
0
        { "nfs",    ITEM_TYPE_FILE, true },
1219
0
        { "ofdm",   ITEM_TYPE_CARD, false },
1220
0
        { "oss",    ITEM_TYPE_CARD, false },
1221
0
        { "pnm",    ITEM_TYPE_STREAM, true },
1222
0
        { "pulse",  ITEM_TYPE_CARD, false },
1223
0
        { "qam",    ITEM_TYPE_CARD, false },
1224
0
        { "qpsk",   ITEM_TYPE_CARD, false },
1225
0
        { "qtsound",ITEM_TYPE_CARD, false },
1226
0
        { "raw139", ITEM_TYPE_CARD, false }, /* raw1394 */
1227
0
        { "rt",     ITEM_TYPE_STREAM, true }, /* rtp, rtsp, rtmp */
1228
0
        { "satell", ITEM_TYPE_CARD, false }, /* satellite */
1229
0
        { "satip",  ITEM_TYPE_STREAM, true }, /* satellite over ip */
1230
0
        { "screen", ITEM_TYPE_CARD, false },
1231
0
        { "sdp",    ITEM_TYPE_STREAM, true },
1232
0
        { "sftp",   ITEM_TYPE_FILE, true },
1233
0
        { "shm",    ITEM_TYPE_CARD, false },
1234
0
        { "smb",    ITEM_TYPE_FILE, true },
1235
0
        { "stream", ITEM_TYPE_STREAM, false },
1236
0
        { "svcd",   ITEM_TYPE_DISC, false },
1237
0
        { "tcp",    ITEM_TYPE_STREAM, true },
1238
0
        { "terres", ITEM_TYPE_CARD, false }, /* terrestrial */
1239
0
        { "udp",    ITEM_TYPE_STREAM, true },
1240
0
        { "unsv",   ITEM_TYPE_STREAM, true },
1241
0
        { "upnp",   ITEM_TYPE_FILE, true },
1242
0
        { "v4l",    ITEM_TYPE_CARD, false },
1243
0
        { "vcd",    ITEM_TYPE_DISC, false },
1244
0
        { "vdr",    ITEM_TYPE_STREAM, true },
1245
0
        { "vlctest_",ITEM_TYPE_FILE, false },
1246
0
        { "wasapi", ITEM_TYPE_CARD, false },
1247
0
        { "window", ITEM_TYPE_CARD, false },
1248
0
    };
1249
1250
0
#ifndef NDEBUG
1251
0
    for( size_t i = 1; i < ARRAY_SIZE( tab ); i++ )
1252
0
        assert( typecmp( (tab + i)->psz_scheme, tab + i - 1 ) > 0 );
1253
0
#endif
1254
1255
0
    *p_net = false;
1256
1257
0
    if( strchr( p_item->psz_uri, ':' ) == NULL )
1258
0
        return ITEM_TYPE_UNKNOWN; /* invalid URI */
1259
1260
0
    const struct item_type_entry *e =
1261
0
        bsearch( p_item->psz_uri, tab, ARRAY_SIZE( tab ),
1262
0
                 sizeof( tab[0] ), typecmp );
1263
0
    if( e == NULL )
1264
0
        return ITEM_TYPE_UNKNOWN;
1265
1266
0
    *p_net = e->b_net;
1267
0
    return e->i_type;
1268
0
}
1269
1270
input_item_node_t *input_item_node_Create( input_item_t *p_input )
1271
0
{
1272
0
    input_item_node_t* p_node = malloc( sizeof( input_item_node_t ) );
1273
0
    if( !p_node )
1274
0
        return NULL;
1275
1276
0
    assert( p_input );
1277
1278
0
    p_node->p_item = p_input;
1279
0
    input_item_Hold( p_input );
1280
1281
0
    p_node->i_children = 0;
1282
0
    p_node->pp_children = NULL;
1283
1284
0
    return p_node;
1285
0
}
1286
1287
void input_item_node_Delete( input_item_node_t *p_node )
1288
0
{
1289
0
    for( int i = 0; i < p_node->i_children; i++ )
1290
0
        input_item_node_Delete( p_node->pp_children[i] );
1291
1292
0
    input_item_Release( p_node->p_item );
1293
0
    free( p_node->pp_children );
1294
0
    free( p_node );
1295
0
}
1296
1297
input_item_node_t *input_item_node_AppendItem( input_item_node_t *p_node, input_item_t *p_item )
1298
0
{
1299
0
    input_item_node_t *p_new_child = input_item_node_Create( p_item );
1300
0
    if( !p_new_child ) return NULL;
1301
1302
0
    input_item_node_AppendNode( p_node, p_new_child );
1303
0
    return p_new_child;
1304
0
}
1305
1306
void input_item_node_AppendNode( input_item_node_t *p_parent,
1307
                                 input_item_node_t *p_child )
1308
0
{
1309
0
    assert(p_parent != NULL);
1310
0
    assert(p_child != NULL);
1311
0
    TAB_APPEND(p_parent->i_children, p_parent->pp_children, p_child);
1312
0
}
1313
1314
void input_item_node_RemoveNode( input_item_node_t *parent,
1315
                                 input_item_node_t *child )
1316
0
{
1317
0
    TAB_REMOVE(parent->i_children, parent->pp_children, child);
1318
0
}
1319
1320
/* Called by es_out when a new Elementary Stream is added or updated. */
1321
void input_item_UpdateTracksInfo(input_item_t *item, const es_format_t *fmt,
1322
                                 const char *es_id, bool es_id_stable)
1323
0
{
1324
0
    assert( es_id != NULL );
1325
0
    vlc_mutex_lock( &item->lock );
1326
1327
0
    for( size_t i = 0; i < item->es_vec.size; ++i )
1328
0
    {
1329
0
        struct input_item_es *item_es = &item->es_vec.data[i];
1330
0
        if (strcmp(item_es->id, es_id) != 0)
1331
0
            continue;
1332
1333
        /* We've found the right ES, replace it */
1334
0
        es_format_Clean(&item_es->es);
1335
0
        es_format_Copy(&item_es->es, fmt);
1336
0
        vlc_mutex_unlock( &item->lock );
1337
0
        return;
1338
0
    }
1339
1340
0
    char *es_id_dup = strdup( es_id );
1341
0
    if( es_id_dup == NULL )
1342
0
    {
1343
0
        vlc_mutex_unlock( &item->lock );
1344
0
        return;
1345
0
    }
1346
1347
    /* ES not found, insert it */
1348
0
    if (vlc_vector_push_hole( &item->es_vec, 1 ))
1349
0
    {
1350
0
        struct input_item_es *item_es = &item->es_vec.data[item->es_vec.size - 1];
1351
0
        item_es->id = es_id_dup;
1352
0
        item_es->id_stable = es_id_stable;
1353
0
        es_format_Copy( &item_es->es, fmt );
1354
0
    }
1355
0
    else
1356
0
        free( es_id_dup );
1357
1358
0
    vlc_mutex_unlock( &item->lock );
1359
0
}
1360
1361
char *input_item_CreateFilename(input_item_t *item,
1362
                                const char *dir, const char *filenamefmt,
1363
                                const char *ext)
1364
0
{
1365
0
    char *path;
1366
0
    char *filename = str_format(NULL, item, filenamefmt);
1367
0
    if (unlikely(filename == NULL))
1368
0
        return NULL;
1369
1370
0
    filename_sanitize(filename);
1371
1372
0
    if (((ext != NULL)
1373
0
            ? asprintf(&path, "%s"DIR_SEP"%s.%s", dir, filename, ext)
1374
0
            : asprintf(&path, "%s"DIR_SEP"%s", dir, filename)) < 0)
1375
0
        path = NULL;
1376
1377
0
    free(filename);
1378
0
    return path;
1379
0
}
1380
1381
static int rdh_compar_type(input_item_t *p1, input_item_t *p2)
1382
0
{
1383
0
    if (p1->i_type != p2->i_type)
1384
0
    {
1385
0
        if (p1->i_type == ITEM_TYPE_DIRECTORY)
1386
0
            return -1;
1387
0
        if (p2->i_type == ITEM_TYPE_DIRECTORY)
1388
0
            return 1;
1389
0
    }
1390
0
    return 0;
1391
0
}
1392
1393
static int rdh_compar_filename(const void *a, const void *b)
1394
0
{
1395
0
    input_item_node_t *const *na = a, *const *nb = b;
1396
0
    input_item_t *ia = (*na)->p_item, *ib = (*nb)->p_item;
1397
1398
0
    int i_ret = rdh_compar_type(ia, ib);
1399
0
    if (i_ret != 0)
1400
0
        return i_ret;
1401
1402
0
    return vlc_filenamecmp(ia->psz_name, ib->psz_name);
1403
0
}
1404
1405
static void rdh_sort(input_item_node_t *p_node)
1406
0
{
1407
0
    if (p_node->i_children <= 0)
1408
0
        return;
1409
1410
    /* Sort current node */
1411
0
    qsort(p_node->pp_children, p_node->i_children,
1412
0
          sizeof(input_item_node_t *), rdh_compar_filename);
1413
1414
    /* Sort all children */
1415
0
    for (int i = 0; i < p_node->i_children; i++)
1416
0
        rdh_sort(p_node->pp_children[i]);
1417
0
}
1418
1419
/**
1420
 * Does the provided file name has one of the extension provided ?
1421
 */
1422
static bool rdh_file_has_ext(const char *psz_filename,
1423
                             const char *psz_ignored_exts)
1424
0
{
1425
0
    if (psz_ignored_exts == NULL)
1426
0
        return false;
1427
1428
0
    const char *ext = strrchr(psz_filename, '.');
1429
0
    if (ext == NULL)
1430
0
        return false;
1431
1432
0
    size_t extlen = strlen(++ext);
1433
1434
0
    for (const char *type = psz_ignored_exts, *end; type[0]; type = end + 1)
1435
0
    {
1436
0
        end = strchr(type, ',');
1437
0
        if (end == NULL)
1438
0
            end = type + strlen(type);
1439
1440
0
        if (type + extlen == end && !strncasecmp(ext, type, extlen))
1441
0
            return true;
1442
1443
0
        if (*end == '\0')
1444
0
            break;
1445
0
    }
1446
1447
0
    return false;
1448
0
}
1449
1450
static bool rdh_file_is_ignored(struct vlc_readdir_helper *p_rdh,
1451
                                const char *psz_filename)
1452
0
{
1453
0
    return (psz_filename[0] == '\0'
1454
0
         || strcmp(psz_filename, ".") == 0
1455
0
         || strcmp(psz_filename, "..") == 0
1456
0
         || (!p_rdh->b_show_hiddenfiles && psz_filename[0] == '.')
1457
0
         || rdh_file_has_ext(psz_filename, p_rdh->psz_ignored_exts));
1458
0
}
1459
1460
struct rdh_slave
1461
{
1462
    input_item_slave_t *p_slave;
1463
    char *psz_filename;
1464
    input_item_node_t *p_node;
1465
};
1466
1467
struct rdh_dir
1468
{
1469
    input_item_node_t *p_node;
1470
    char psz_path[];
1471
};
1472
1473
static char *rdh_name_from_filename(const char *psz_filename)
1474
0
{
1475
    /* remove leading white spaces */
1476
0
    while (*psz_filename != '\0' && *psz_filename == ' ')
1477
0
        psz_filename++;
1478
1479
0
    char *psz_name = strdup(psz_filename);
1480
0
    if (!psz_name)
1481
0
        return NULL;
1482
1483
    /* remove extension */
1484
0
    char *psz_ptr = strrchr(psz_name, '.');
1485
0
    if (psz_ptr && psz_ptr != psz_name)
1486
0
        *psz_ptr = '\0';
1487
1488
    /* remove trailing white spaces */
1489
0
    int i = strlen(psz_name) - 1;
1490
0
    while (psz_name[i] == ' ' && i >= 0)
1491
0
        psz_name[i--] = '\0';
1492
1493
    /* convert to lower case */
1494
0
    psz_ptr = psz_name;
1495
0
    while (*psz_ptr != '\0')
1496
0
    {
1497
0
        *psz_ptr = tolower(*psz_ptr);
1498
0
        psz_ptr++;
1499
0
    }
1500
1501
0
    return psz_name;
1502
0
}
1503
1504
static uint8_t rdh_get_slave_priority(input_item_t *p_item,
1505
                                      input_item_slave_t *p_slave,
1506
                                      const char *psz_slave_filename)
1507
0
{
1508
0
    uint8_t i_priority = SLAVE_PRIORITY_MATCH_NONE;
1509
0
    char *psz_item_name = rdh_name_from_filename(p_item->psz_name);
1510
0
    char *psz_slave_name = rdh_name_from_filename(psz_slave_filename);
1511
1512
0
    if (!psz_item_name || !psz_slave_name)
1513
0
        goto done;
1514
1515
0
    size_t i_item_len = strlen(psz_item_name);
1516
0
    size_t i_slave_len = strlen(psz_slave_name);
1517
1518
    /* The slave name len should not be twice longer than the item name len. */
1519
0
    if (i_item_len > i_slave_len || i_slave_len > 2 * i_item_len)
1520
0
        goto done;
1521
1522
    /* check if the names match exactly */
1523
0
    if (!strcmp(psz_item_name, psz_slave_name))
1524
0
    {
1525
0
        i_priority = SLAVE_PRIORITY_MATCH_ALL;
1526
0
        goto done;
1527
0
    }
1528
1529
    /* "cdg" slaves have to be a full match */
1530
0
    if (p_slave->i_type == SLAVE_TYPE_SPU)
1531
0
    {
1532
0
        char *psz_ext = strrchr(psz_slave_name, '.');
1533
0
        if (psz_ext != NULL && strcasecmp(++psz_ext, "cdg") == 0)
1534
0
            goto done;
1535
0
    }
1536
1537
    /* check if the item name is a substring of the slave name */
1538
0
    const char *psz_sub = strstr(psz_slave_name, psz_item_name);
1539
1540
0
    if (psz_sub)
1541
0
    {
1542
        /* check if the item name was found at the end of the slave name */
1543
0
        if (strlen(psz_sub + strlen(psz_item_name)) == 0)
1544
0
        {
1545
0
            i_priority = SLAVE_PRIORITY_MATCH_RIGHT;
1546
0
            goto done;
1547
0
        }
1548
0
        else
1549
0
        {
1550
0
            i_priority = SLAVE_PRIORITY_MATCH_LEFT;
1551
0
            goto done;
1552
0
        }
1553
0
    }
1554
1555
0
done:
1556
0
    free(psz_item_name);
1557
0
    free(psz_slave_name);
1558
0
    return i_priority;
1559
0
}
1560
1561
static int rdh_should_match_idx(struct vlc_readdir_helper *p_rdh,
1562
                                struct rdh_slave *p_rdh_sub)
1563
0
{
1564
0
    char *psz_ext = strrchr(p_rdh_sub->psz_filename, '.');
1565
0
    if (!psz_ext)
1566
0
        return false;
1567
0
    psz_ext++;
1568
1569
0
    if (strcasecmp(psz_ext, "sub") != 0)
1570
0
        return false;
1571
1572
0
    for (size_t i = 0; i < p_rdh->i_slaves; i++)
1573
0
    {
1574
0
        struct rdh_slave *p_rdh_slave = p_rdh->pp_slaves[i];
1575
1576
0
        if (p_rdh_slave == NULL || p_rdh_slave == p_rdh_sub)
1577
0
            continue;
1578
1579
        /* check that priorities match */
1580
0
        if (p_rdh_slave->p_slave->i_priority !=
1581
0
            p_rdh_sub->p_slave->i_priority)
1582
0
            continue;
1583
1584
        /* check that the filenames without extension match */
1585
0
        if (strncasecmp(p_rdh_sub->psz_filename, p_rdh_slave->psz_filename,
1586
0
                        strlen(p_rdh_sub->psz_filename) - 3 ) != 0)
1587
0
            continue;
1588
1589
        /* check that we have an idx file */
1590
0
        char *psz_ext_idx = strrchr(p_rdh_slave->psz_filename, '.');
1591
0
        if (psz_ext_idx == NULL)
1592
0
            continue;
1593
0
        psz_ext_idx++;
1594
0
        if (strcasecmp(psz_ext_idx, "idx" ) == 0)
1595
0
            return true;
1596
0
    }
1597
0
    return false;
1598
0
}
1599
1600
static void rdh_attach_slaves(struct vlc_readdir_helper *p_rdh,
1601
                              input_item_node_t *p_parent_node)
1602
0
{
1603
0
    if (p_rdh->i_sub_autodetect_fuzzy == 0)
1604
0
        return;
1605
1606
    /* Try to match slaves for each items of the node */
1607
0
    for (int i = 0; i < p_parent_node->i_children; i++)
1608
0
    {
1609
0
        input_item_node_t *p_node = p_parent_node->pp_children[i];
1610
0
        input_item_t *p_item = p_node->p_item;
1611
1612
0
        enum slave_type unused;
1613
0
        if (!input_item_IsMaster(p_item->psz_name)
1614
0
         || input_item_slave_GetType(p_item->psz_name, &unused))
1615
0
            continue; /* don't match 2 possible slaves between each others */
1616
1617
0
        for (size_t j = 0; j < p_rdh->i_slaves; j++)
1618
0
        {
1619
0
            struct rdh_slave *p_rdh_slave = p_rdh->pp_slaves[j];
1620
1621
            /* Don't try to match slaves with themselves or slaves already
1622
             * attached with the higher priority */
1623
0
            if (p_rdh_slave->p_node == p_node
1624
0
             || p_rdh_slave->p_slave->i_priority == SLAVE_PRIORITY_MATCH_ALL)
1625
0
                continue;
1626
1627
0
            uint8_t i_priority =
1628
0
                rdh_get_slave_priority(p_item, p_rdh_slave->p_slave,
1629
0
                                         p_rdh_slave->psz_filename);
1630
1631
0
            if (i_priority < p_rdh->i_sub_autodetect_fuzzy)
1632
0
                continue;
1633
1634
            /* Drop the ".sub" slave if a ".idx" slave matches */
1635
0
            if (p_rdh_slave->p_slave->i_type == SLAVE_TYPE_SPU
1636
0
             && rdh_should_match_idx(p_rdh, p_rdh_slave))
1637
0
                continue;
1638
1639
0
            input_item_slave_t *p_slave =
1640
0
                input_item_slave_New(p_rdh_slave->p_slave->psz_uri,
1641
0
                                     p_rdh_slave->p_slave->i_type,
1642
0
                                     i_priority);
1643
0
            if (p_slave == NULL)
1644
0
                break;
1645
1646
0
            if (input_item_AddSlave(p_item, p_slave) != VLC_SUCCESS)
1647
0
            {
1648
0
                input_item_slave_Delete(p_slave);
1649
0
                break;
1650
0
            }
1651
1652
            /* Remove the corresponding node if any: This slave won't be
1653
             * added in the parent node */
1654
0
            if (p_rdh_slave->p_node != NULL)
1655
0
            {
1656
0
                input_item_node_RemoveNode(p_parent_node, p_rdh_slave->p_node);
1657
0
                input_item_node_Delete(p_rdh_slave->p_node);
1658
0
                p_rdh_slave->p_node = NULL;
1659
0
            }
1660
1661
0
            p_rdh_slave->p_slave->i_priority = i_priority;
1662
0
        }
1663
0
    }
1664
1665
    /* Attach all children */
1666
0
    for (int i = 0; i < p_parent_node->i_children; i++)
1667
0
        rdh_attach_slaves(p_rdh, p_parent_node->pp_children[i]);
1668
0
}
1669
1670
static int rdh_unflatten(struct vlc_readdir_helper *p_rdh,
1671
                         input_item_node_t **pp_node, const char *psz_path,
1672
                         int i_net)
1673
0
{
1674
    /* Create an input input for each sub folders that is contained in the full
1675
     * path. Update pp_node to point to the direct parent of the future item to
1676
     * add. */
1677
1678
0
    assert(psz_path != NULL);
1679
0
    const char *psz_subpaths = psz_path;
1680
1681
0
    while ((psz_subpaths = strchr(psz_subpaths, '/')))
1682
0
    {
1683
0
        input_item_node_t *p_subnode = NULL;
1684
1685
        /* Check if this sub folder item was already added */
1686
0
        for (size_t i = 0; i < p_rdh->i_dirs && p_subnode == NULL; i++)
1687
0
        {
1688
0
            struct rdh_dir *rdh_dir = p_rdh->pp_dirs[i];
1689
0
            if (!strncmp(rdh_dir->psz_path, psz_path, psz_subpaths - psz_path))
1690
0
                p_subnode = rdh_dir->p_node;
1691
0
        }
1692
1693
        /* The sub folder item doesn't exist, so create it */
1694
0
        if (p_subnode == NULL)
1695
0
        {
1696
0
            size_t i_sub_path_len = psz_subpaths - psz_path;
1697
0
            struct rdh_dir *p_rdh_dir =
1698
0
                malloc(sizeof(struct rdh_dir) + 1 + i_sub_path_len);
1699
0
            if (p_rdh_dir == NULL)
1700
0
                return VLC_ENOMEM;
1701
0
            strncpy(p_rdh_dir->psz_path, psz_path, i_sub_path_len);
1702
0
            p_rdh_dir->psz_path[i_sub_path_len] = 0;
1703
1704
0
            const char *psz_subpathname = strrchr(p_rdh_dir->psz_path, '/');
1705
0
            if (psz_subpathname != NULL)
1706
0
                ++psz_subpathname;
1707
0
            else
1708
0
                psz_subpathname = p_rdh_dir->psz_path;
1709
1710
0
            input_item_t *p_item =
1711
0
                input_item_NewExt(INPUT_ITEM_URI_NOP, psz_subpathname, INPUT_DURATION_UNSET,
1712
0
                                  ITEM_TYPE_DIRECTORY, i_net);
1713
0
            if (p_item == NULL)
1714
0
            {
1715
0
                free(p_rdh_dir);
1716
0
                return VLC_ENOMEM;
1717
0
            }
1718
0
            input_item_CopyOptions(p_item, (*pp_node)->p_item);
1719
0
            *pp_node = input_item_node_AppendItem(*pp_node, p_item);
1720
0
            input_item_Release(p_item);
1721
0
            if (*pp_node == NULL)
1722
0
            {
1723
0
                free(p_rdh_dir);
1724
0
                return VLC_ENOMEM;
1725
0
            }
1726
0
            p_rdh_dir->p_node = *pp_node;
1727
0
            TAB_APPEND(p_rdh->i_dirs, p_rdh->pp_dirs, p_rdh_dir);
1728
0
        }
1729
0
        else
1730
0
            *pp_node = p_subnode;
1731
0
        psz_subpaths++;
1732
0
    }
1733
0
    return VLC_SUCCESS;
1734
0
}
1735
1736
#undef vlc_readdir_helper_init
1737
void vlc_readdir_helper_init(struct vlc_readdir_helper *p_rdh,
1738
                             vlc_object_t *p_obj, input_item_node_t *p_node)
1739
0
{
1740
    /* Read options from the parent item. This allows vlc_stream_ReadDir()
1741
     * users to specify options without affecting any existing vlc_object_t.
1742
     * Apply options on a temporary object in order to not apply options (which
1743
     * can be insecure) to the current object. */
1744
0
    vlc_object_t *p_var_obj = vlc_object_create(p_obj, sizeof(vlc_object_t));
1745
0
    if (p_var_obj != NULL)
1746
0
    {
1747
0
        input_item_ApplyOptions(p_var_obj, p_node->p_item);
1748
0
        p_obj = p_var_obj;
1749
0
    }
1750
1751
0
    p_rdh->p_node = p_node;
1752
0
    p_rdh->b_show_hiddenfiles = var_InheritBool(p_obj, "show-hiddenfiles");
1753
0
    p_rdh->psz_ignored_exts = var_InheritString(p_obj, "ignore-filetypes");
1754
0
    bool b_autodetect = var_InheritBool(p_obj, "sub-autodetect-file");
1755
0
    p_rdh->i_sub_autodetect_fuzzy = !b_autodetect ? 0 :
1756
0
        var_InheritInteger(p_obj, "sub-autodetect-fuzzy");
1757
0
    p_rdh->b_flatten = var_InheritBool(p_obj, "extractor-flatten");
1758
0
    TAB_INIT(p_rdh->i_slaves, p_rdh->pp_slaves);
1759
0
    TAB_INIT(p_rdh->i_dirs, p_rdh->pp_dirs);
1760
1761
0
    if (p_var_obj != NULL)
1762
0
        vlc_object_delete(p_var_obj);
1763
0
}
1764
1765
void vlc_readdir_helper_finish(struct vlc_readdir_helper *p_rdh, bool b_success)
1766
0
{
1767
0
    if (b_success)
1768
0
    {
1769
0
        rdh_sort(p_rdh->p_node);
1770
0
        rdh_attach_slaves(p_rdh, p_rdh->p_node);
1771
0
    }
1772
0
    free(p_rdh->psz_ignored_exts);
1773
1774
    /* Remove unmatched slaves */
1775
0
    for (size_t i = 0; i < p_rdh->i_slaves; i++)
1776
0
    {
1777
0
        struct rdh_slave *p_rdh_slave = p_rdh->pp_slaves[i];
1778
0
        if (p_rdh_slave != NULL)
1779
0
        {
1780
0
            input_item_slave_Delete(p_rdh_slave->p_slave);
1781
0
            free(p_rdh_slave->psz_filename);
1782
0
            free(p_rdh_slave);
1783
0
        }
1784
0
    }
1785
0
    TAB_CLEAN(p_rdh->i_slaves, p_rdh->pp_slaves);
1786
1787
0
    for (size_t i = 0; i < p_rdh->i_dirs; i++)
1788
0
        free(p_rdh->pp_dirs[i]);
1789
0
    TAB_CLEAN(p_rdh->i_dirs, p_rdh->pp_dirs);
1790
0
}
1791
1792
int vlc_readdir_helper_additem(struct vlc_readdir_helper *p_rdh,
1793
                               const char *psz_uri, const char *psz_flatpath,
1794
                               const char *psz_filename, int i_type, int i_net,
1795
                               input_item_t **restrict created_item)
1796
0
{
1797
0
    enum slave_type i_slave_type;
1798
0
    struct rdh_slave *p_rdh_slave = NULL;
1799
0
    assert(psz_flatpath || psz_filename);
1800
1801
0
    if (!p_rdh->b_flatten)
1802
0
    {
1803
0
        if (psz_filename == NULL)
1804
0
        {
1805
0
            psz_filename = strrchr(psz_flatpath, '/');
1806
0
            if (psz_filename != NULL)
1807
0
                ++psz_filename;
1808
0
            else
1809
0
                psz_filename = psz_flatpath;
1810
0
        }
1811
0
    }
1812
0
    else
1813
0
    {
1814
0
        if (psz_filename == NULL)
1815
0
            psz_filename = psz_flatpath;
1816
0
        psz_flatpath = NULL;
1817
0
    }
1818
1819
0
    if (p_rdh->i_sub_autodetect_fuzzy != 0
1820
0
     && input_item_slave_GetType(psz_filename, &i_slave_type))
1821
0
    {
1822
0
        p_rdh_slave = malloc(sizeof(*p_rdh_slave));
1823
0
        if (!p_rdh_slave)
1824
0
            return VLC_ENOMEM;
1825
1826
0
        p_rdh_slave->p_node = NULL;
1827
0
        p_rdh_slave->psz_filename = strdup(psz_filename);
1828
0
        p_rdh_slave->p_slave = input_item_slave_New(psz_uri, i_slave_type,
1829
0
                                                      SLAVE_PRIORITY_MATCH_NONE);
1830
0
        if (!p_rdh_slave->p_slave || !p_rdh_slave->psz_filename)
1831
0
        {
1832
0
            free(p_rdh_slave->psz_filename);
1833
0
            free(p_rdh_slave);
1834
0
            return VLC_ENOMEM;
1835
0
        }
1836
1837
0
        TAB_APPEND(p_rdh->i_slaves, p_rdh->pp_slaves, p_rdh_slave);
1838
0
    }
1839
1840
0
    if (rdh_file_is_ignored(p_rdh, psz_filename))
1841
0
    {
1842
0
        if (created_item != NULL)
1843
0
            *created_item = NULL;
1844
0
        return VLC_SUCCESS;
1845
0
    }
1846
1847
0
    input_item_node_t *p_node = p_rdh->p_node;
1848
1849
0
    if (psz_flatpath != NULL)
1850
0
    {
1851
0
        int i_ret = rdh_unflatten(p_rdh, &p_node, psz_flatpath, i_net);
1852
0
        if (i_ret != VLC_SUCCESS)
1853
0
            return i_ret;
1854
0
    }
1855
1856
0
    input_item_t *p_item = input_item_NewExt(psz_uri, psz_filename, INPUT_DURATION_UNSET, i_type,
1857
0
                                             i_net);
1858
0
    if (p_item == NULL)
1859
0
        return VLC_ENOMEM;
1860
1861
0
    input_item_CopyOptions(p_item, p_node->p_item);
1862
0
    p_node = input_item_node_AppendItem(p_node, p_item);
1863
0
    input_item_Release(p_item);
1864
0
    if (p_node == NULL)
1865
0
        return VLC_ENOMEM;
1866
1867
    /* A slave can also be an item. If there is a match, this item will be
1868
     * removed from the parent node. This is not a common case, since most
1869
     * slaves will be ignored by rdh_file_is_ignored() */
1870
0
    if (p_rdh_slave != NULL)
1871
0
        p_rdh_slave->p_node = p_node;
1872
1873
0
    if (created_item != NULL)
1874
0
        *created_item = p_item;
1875
0
    return VLC_SUCCESS;
1876
0
}