Coverage Report

Created: 2025-07-18 07:04

/src/vlc/src/text/strings.c
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * strings.c: String related functions
3
 *****************************************************************************
4
 * Copyright (C) 2006 VLC authors and VideoLAN
5
 * Copyright (C) 2008-2009 Rémi Denis-Courmont
6
 *
7
 * Authors: Antoine Cellerier <dionoea at videolan dot org>
8
 *          Daniel Stranger <vlc at schmaller dot de>
9
 *          Rémi Denis-Courmont
10
 *
11
 * This program is free software; you can redistribute it and/or modify it
12
 * under the terms of the GNU Lesser General Public License as published by
13
 * the Free Software Foundation; either version 2.1 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Lesser General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public License
22
 * along with this program; if not, write to the Free Software Foundation,
23
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
 *****************************************************************************/
25
26
/*****************************************************************************
27
 * Preamble
28
 *****************************************************************************/
29
#ifdef HAVE_CONFIG_H
30
# include "config.h"
31
#endif
32
33
#include <vlc_common.h>
34
#include <assert.h>
35
36
/* Needed by vlc_strftime */
37
#include <time.h>
38
#include <limits.h>
39
#include <math.h>
40
#include <string.h>
41
#ifndef HAVE_STRCOLL
42
# define strcoll strcasecmp
43
#endif
44
45
/* Needed by vlc_strfplayer */
46
#include <vlc_meta.h>
47
#include <vlc_aout.h>
48
#include <vlc_memstream.h>
49
50
#include <vlc_strings.h>
51
#include <vlc_charset.h>
52
#include <vlc_arrays.h>
53
#include <vlc_player.h>
54
#include "../libvlc.h"
55
#include <errno.h>
56
57
static const struct xml_entity_s
58
{
59
    char    psz_entity[8];
60
    char    psz_char[4];
61
} xml_entities[] = {
62
    /* Important: this list has to be in alphabetical order (psz_entity-wise) */
63
    { "AElig;",  "Æ" },
64
    { "Aacute;", "Á" },
65
    { "Acirc;",  "Â" },
66
    { "Agrave;", "À" },
67
    { "Aring;",  "Å" },
68
    { "Atilde;", "Ã" },
69
    { "Auml;",   "Ä" },
70
    { "Ccedil;", "Ç" },
71
    { "Dagger;", "‡" },
72
    { "ETH;",    "Ð" },
73
    { "Eacute;", "É" },
74
    { "Ecirc;",  "Ê" },
75
    { "Egrave;", "È" },
76
    { "Euml;",   "Ë" },
77
    { "Iacute;", "Í" },
78
    { "Icirc;",  "Î" },
79
    { "Igrave;", "Ì" },
80
    { "Iuml;",   "Ï" },
81
    { "Ntilde;", "Ñ" },
82
    { "OElig;",  "Œ" },
83
    { "Oacute;", "Ó" },
84
    { "Ocirc;",  "Ô" },
85
    { "Ograve;", "Ò" },
86
    { "Oslash;", "Ø" },
87
    { "Otilde;", "Õ" },
88
    { "Ouml;",   "Ö" },
89
    { "Scaron;", "Š" },
90
    { "THORN;",  "Þ" },
91
    { "Uacute;", "Ú" },
92
    { "Ucirc;",  "Û" },
93
    { "Ugrave;", "Ù" },
94
    { "Uuml;",   "Ü" },
95
    { "Yacute;", "Ý" },
96
    { "Yuml;",   "Ÿ" },
97
    { "aacute;", "á" },
98
    { "acirc;",  "â" },
99
    { "acute;",  "´" },
100
    { "aelig;",  "æ" },
101
    { "agrave;", "à" },
102
    { "amp;",    "&" },
103
    { "apos;",   "'" },
104
    { "aring;",  "å" },
105
    { "atilde;", "ã" },
106
    { "auml;",   "ä" },
107
    { "bdquo;",  "„" },
108
    { "brvbar;", "¦" },
109
    { "ccedil;", "ç" },
110
    { "cedil;",  "¸" },
111
    { "cent;",   "¢" },
112
    { "circ;",   "ˆ" },
113
    { "copy;",   "©" },
114
    { "curren;", "¤" },
115
    { "dagger;", "†" },
116
    { "deg;",    "°" },
117
    { "divide;", "÷" },
118
    { "eacute;", "é" },
119
    { "ecirc;",  "ê" },
120
    { "egrave;", "è" },
121
    { "eth;",    "ð" },
122
    { "euml;",   "ë" },
123
    { "euro;",   "€" },
124
    { "frac12;", "½" },
125
    { "frac14;", "¼" },
126
    { "frac34;", "¾" },
127
    { "gt;",     ">" },
128
    { "hellip;", "…" },
129
    { "iacute;", "í" },
130
    { "icirc;",  "î" },
131
    { "iexcl;",  "¡" },
132
    { "igrave;", "ì" },
133
    { "iquest;", "¿" },
134
    { "iuml;",   "ï" },
135
    { "laquo;",  "«" },
136
    { "ldquo;",  "“" },
137
    { "lsaquo;", "‹" },
138
    { "lsquo;",  "‘" },
139
    { "lt;",     "<" },
140
    { "macr;",   "¯" },
141
    { "mdash;",  "—" },
142
    { "micro;",  "µ" },
143
    { "middot;", "·" },
144
    { "nbsp;",   "\xc2\xa0" },
145
    { "ndash;",  "–" },
146
    { "not;",    "¬" },
147
    { "ntilde;", "ñ" },
148
    { "oacute;", "ó" },
149
    { "ocirc;",  "ô" },
150
    { "oelig;",  "œ" },
151
    { "ograve;", "ò" },
152
    { "ordf;",   "ª" },
153
    { "ordm;",   "º" },
154
    { "oslash;", "ø" },
155
    { "otilde;", "õ" },
156
    { "ouml;",   "ö" },
157
    { "para;",   "¶" },
158
    { "permil;", "‰" },
159
    { "plusmn;", "±" },
160
    { "pound;",  "£" },
161
    { "quot;",   "\"" },
162
    { "raquo;",  "»" },
163
    { "rdquo;",  "”" },
164
    { "reg;",    "®" },
165
    { "rsaquo;", "›" },
166
    { "rsquo;",  "’" },
167
    { "sbquo;",  "‚" },
168
    { "scaron;", "š" },
169
    { "sect;",   "§" },
170
    { "shy;",    "­" },
171
    { "sup1;",   "¹" },
172
    { "sup2;",   "²" },
173
    { "sup3;",   "³" },
174
    { "szlig;",  "ß" },
175
    { "thorn;",  "þ" },
176
    { "tilde;",  "˜" },
177
    { "times;",  "×" },
178
    { "trade;",  "™" },
179
    { "uacute;", "ú" },
180
    { "ucirc;",  "û" },
181
    { "ugrave;", "ù" },
182
    { "uml;",    "¨" },
183
    { "uuml;",   "ü" },
184
    { "yacute;", "ý" },
185
    { "yen;",    "¥" },
186
    { "yuml;",   "ÿ" },
187
};
188
189
static int cmp_entity (const void *key, const void *elem)
190
0
{
191
0
    const struct xml_entity_s *ent = elem;
192
0
    const char *name = key;
193
194
0
    return strncmp (name, ent->psz_entity, strlen (ent->psz_entity));
195
0
}
196
197
void vlc_xml_decode( char *psz_value )
198
0
{
199
0
    char *p_pos = psz_value;
200
201
0
    while ( *psz_value )
202
0
    {
203
0
        if( *psz_value == '&' )
204
0
        {
205
0
            if( psz_value[1] == '#' )
206
0
            {   /* &#DDD; or &#xHHHH; Unicode code point */
207
0
                char *psz_end;
208
0
                unsigned long cp;
209
210
0
                if( psz_value[2] == 'x' ) /* The x must be lower-case. */
211
0
                    cp = strtoul( psz_value + 3, &psz_end, 16 );
212
0
                else
213
0
                    cp = strtoul( psz_value + 2, &psz_end, 10 );
214
215
0
                if( *psz_end == ';' )
216
0
                {
217
0
                    psz_value = psz_end + 1;
218
0
                    if( cp == 0 )
219
0
                        (void)0; /* skip nulls */
220
0
                    else
221
0
                    if( cp <= 0x7F )
222
0
                    {
223
0
                        *p_pos =            cp;
224
0
                    }
225
0
                    else
226
                    /* Unicode code point outside ASCII.
227
                     * &#xxx; representation is longer than UTF-8 :) */
228
0
                    if( cp <= 0x7FF )
229
0
                    {
230
0
                        *p_pos++ = 0xC0 |  (cp >>  6);
231
0
                        *p_pos   = 0x80 |  (cp        & 0x3F);
232
0
                    }
233
0
                    else
234
0
                    if( cp <= 0xFFFF )
235
0
                    {
236
0
                        *p_pos++ = 0xE0 |  (cp >> 12);
237
0
                        *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
238
0
                        *p_pos   = 0x80 |  (cp        & 0x3F);
239
0
                    }
240
0
                    else
241
0
                    if( cp <= 0x1FFFFF ) /* Outside the BMP */
242
0
                    {   /* Unicode stops at 10FFFF, but who cares? */
243
0
                        *p_pos++ = 0xF0 |  (cp >> 18);
244
0
                        *p_pos++ = 0x80 | ((cp >> 12) & 0x3F);
245
0
                        *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
246
0
                        *p_pos   = 0x80 |  (cp        & 0x3F);
247
0
                    }
248
0
                }
249
0
                else
250
0
                {
251
                    /* Invalid entity number */
252
0
                    *p_pos = *psz_value;
253
0
                    psz_value++;
254
0
                }
255
0
            }
256
0
            else
257
0
            {   /* Well-known XML entity */
258
0
                const struct xml_entity_s *ent;
259
260
0
                ent = bsearch (psz_value + 1, xml_entities,
261
0
                               ARRAY_SIZE (xml_entities),
262
0
                               sizeof (*ent), cmp_entity);
263
0
                if (ent != NULL)
264
0
                {
265
0
                    size_t olen = strlen (ent->psz_char);
266
0
                    memcpy (p_pos, ent->psz_char, olen);
267
0
                    p_pos += olen - 1;
268
0
                    psz_value += strlen (ent->psz_entity) + 1;
269
0
                }
270
0
                else
271
0
                {   /* No match */
272
0
                    *p_pos = *psz_value;
273
0
                    psz_value++;
274
0
                }
275
0
            }
276
0
        }
277
0
        else
278
0
        {
279
0
            *p_pos = *psz_value;
280
0
            psz_value++;
281
0
        }
282
283
0
        p_pos++;
284
0
    }
285
286
0
    *p_pos = '\0';
287
0
}
288
289
char *vlc_xml_encode (const char *str)
290
0
{
291
0
    struct vlc_memstream stream;
292
0
    ssize_t n;
293
0
    uint32_t cp;
294
295
0
    assert(str != NULL);
296
0
    vlc_memstream_open(&stream);
297
298
0
    while ((n = vlc_towc (str, &cp)) != 0)
299
0
    {
300
0
        if (unlikely(n == -1))
301
0
        {
302
0
            if (vlc_memstream_close(&stream) == 0)
303
0
                free(stream.ptr);
304
0
            errno = EILSEQ;
305
0
            return NULL;
306
0
        }
307
308
0
        switch (cp)
309
0
        {
310
0
            case '\"':
311
0
                vlc_memstream_puts(&stream, "&quot;");
312
0
                break;
313
0
            case '&':
314
0
                vlc_memstream_puts(&stream, "&amp;");
315
0
                break;
316
0
            case '\'':
317
0
                vlc_memstream_puts(&stream, "&#39;");
318
0
                break;
319
0
            case '<':
320
0
                vlc_memstream_puts(&stream, "&lt;");
321
0
                break;
322
0
            case '>':
323
0
                vlc_memstream_puts(&stream, "&gt;");
324
0
                break;
325
0
            default:
326
0
                if (cp < 32) /* C0 code not allowed (except 9, 10 and 13) */
327
0
                    break;
328
0
                if (cp >= 128 && cp < 160) /* C1 code encoded (except 133) */
329
0
                {
330
0
                    vlc_memstream_printf(&stream, "&#%"PRIu32";", cp);
331
0
                    break;
332
0
                }
333
                /* fall through */
334
0
            case 9:
335
0
            case 10:
336
0
            case 13:
337
0
            case 133:
338
0
                vlc_memstream_write(&stream, str, n);
339
0
                break;
340
0
        }
341
0
        str += n;
342
0
    }
343
344
0
    if (vlc_memstream_close(&stream))
345
0
        return NULL;
346
0
    return stream.ptr;
347
0
}
348
349
/* Hex encoding */
350
void vlc_hex_encode_binary(const void *input, size_t size, char *output)
351
0
{
352
0
    const unsigned char *buffer = input;
353
354
0
    for (size_t i = 0; i < size; i++) {
355
0
        sprintf(&output[i * 2], "%02hhx", buffer[i]);
356
0
    }
357
0
}
358
359
/* Base64 encoding */
360
char *vlc_b64_encode_binary(const void *src, size_t length)
361
0
{
362
0
    static const char b64[] =
363
0
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
364
0
    const unsigned char *in = src;
365
0
    char *dst = malloc((((length + 2) / 3) * 4) + 1);
366
0
    char *out = dst;
367
368
0
    if (unlikely(dst == NULL))
369
0
        return NULL;
370
371
0
    while (length >= 3) { /* pops (up to) 3 bytes of input, push 4 bytes */
372
0
        uint_fast32_t v = (in[0] << 16) | (in[1] << 8) | in[2];
373
374
0
        *(out++) = b64[(v >> 18)];
375
0
        *(out++) = b64[(v >> 12) & 0x3f];
376
0
        *(out++) = b64[(v >>  6) & 0x3f];
377
0
        *(out++) = b64[(v >>  0) & 0x3f];
378
0
        in += 3;
379
0
        length -= 3;
380
0
    }
381
382
0
    switch (length) {
383
0
        case 2: {
384
0
            uint_fast16_t v = (in[0] << 8) | in[1];
385
386
0
            *(out++) = b64[(v >> 10)];
387
0
            *(out++) = b64[(v >>  4) & 0x3f];
388
0
            *(out++) = b64[(v <<  2) & 0x3f];
389
0
            *(out++) = '=';
390
0
            break;
391
0
        }
392
393
0
        case 1: {
394
0
            uint_fast8_t v = in[0];
395
396
0
            *(out++) = b64[(v >>  2)];
397
0
            *(out++) = b64[(v <<  4) & 0x3f];
398
0
            *(out++) = '=';
399
0
            *(out++) = '=';
400
0
            break;
401
0
        }
402
0
    }
403
404
0
    *out = '\0';
405
0
    return dst;
406
0
}
407
408
char *vlc_b64_encode(const char *src)
409
0
{
410
0
    if (src == NULL)
411
0
        src = "";
412
0
    return vlc_b64_encode_binary(src, strlen(src));
413
0
}
414
415
/* Base64 decoding */
416
size_t vlc_b64_decode_binary_to_buffer(void *dst, size_t size,
417
                                       const char *restrict src)
418
0
{
419
0
    static const signed char b64[256] = {
420
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
421
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
422
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
423
0
        52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
424
0
        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
425
0
        15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
426
0
        -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
427
0
        41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
428
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
429
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
430
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
431
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
432
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
433
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
434
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
435
0
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* F0-FF */
436
0
    };
437
0
    const unsigned char *in = (const unsigned char *)src;
438
0
    unsigned char *out = dst;
439
0
    signed char prev;
440
0
    int shift = 0;
441
442
0
    static_assert (CHAR_BIT == 8, "Oops");
443
444
0
    while (size > 0) {
445
0
        const signed char cur = b64[*(in++)];
446
0
        if (cur < 0)
447
0
            break;
448
449
0
        if (shift != 0) {
450
0
            *(out++) = (prev << shift) | (cur >> (6 - shift));
451
0
            size--;
452
0
        }
453
454
0
        prev = cur;
455
0
        shift = (shift + 2) & 7;
456
0
    }
457
458
0
    return out - (unsigned char *)dst;
459
0
}
460
461
size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
462
0
{
463
0
    const int i_src = strlen( psz_src );
464
0
    uint8_t   *p_dst;
465
466
0
    *pp_dst = p_dst = malloc( i_src );
467
0
    if( !p_dst )
468
0
        return 0;
469
0
    return  vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
470
0
}
471
char *vlc_b64_decode( const char *psz_src )
472
0
{
473
0
    const int i_src = strlen( psz_src );
474
0
    char *p_dst = malloc( i_src + 1 );
475
0
    size_t i_dst;
476
0
    if( !p_dst )
477
0
        return NULL;
478
479
0
    i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
480
0
    p_dst[i_dst] = '\0';
481
482
0
    return p_dst;
483
0
}
484
485
char *vlc_strftime( const char *tformat )
486
0
{
487
0
    time_t curtime;
488
0
    struct tm loctime;
489
490
0
    if (strcmp (tformat, "") == 0)
491
0
        return strdup (""); /* corner case w.r.t. strftime() return value */
492
493
    /* Get the current time.  */
494
0
    time( &curtime );
495
496
    /* Convert it to local time representation.  */
497
0
    localtime_r( &curtime, &loctime );
498
0
    for (size_t buflen = strlen (tformat) + 32;; buflen += 32)
499
0
    {
500
0
        char *str = malloc (buflen);
501
0
        if (str == NULL)
502
0
            return NULL;
503
504
0
        size_t len = strftime (str, buflen, tformat, &loctime);
505
0
        if (len > 0)
506
0
        {
507
0
            char *ret = realloc (str, len + 1);
508
0
            return ret ? ret : str; /* <- this cannot fail */
509
0
        }
510
0
        free (str);
511
0
    }
512
0
    vlc_assert_unreachable ();
513
0
}
514
515
static void write_duration(struct vlc_memstream *stream, vlc_tick_t duration)
516
0
{
517
0
    lldiv_t d;
518
0
    long long sec;
519
520
0
    duration /= CLOCK_FREQ;
521
0
    d = lldiv(duration, 60);
522
0
    sec = d.rem;
523
0
    d = lldiv(d.quot, 60);
524
0
    vlc_memstream_printf(stream, "%02lld:%02lld:%02lld", d.quot, d.rem, sec);
525
0
}
526
527
static int write_meta(struct vlc_memstream *stream, input_item_t *item,
528
                      vlc_meta_type_t type)
529
0
{
530
0
    if (item == NULL)
531
0
        return EOF;
532
533
0
    char *value = input_item_GetMeta(item, type);
534
0
    if (value == NULL)
535
0
        return EOF;
536
537
0
    vlc_memstream_puts(stream, value);
538
0
    free(value);
539
0
    return 0;
540
0
}
541
542
char *vlc_strfplayer(vlc_player_t *player, input_item_t *item, const char *s)
543
0
{
544
0
    struct vlc_memstream stream[1];
545
546
0
    char c;
547
0
    bool b_is_format = false;
548
0
    bool b_empty_if_na = false;
549
550
0
    assert(s != NULL);
551
552
0
    if (!item && player)
553
0
        item = vlc_player_GetCurrentMedia(player);
554
555
0
    vlc_memstream_open(stream);
556
557
0
    while ((c = *s) != '\0')
558
0
    {
559
0
        s++;
560
561
0
        if (!b_is_format)
562
0
        {
563
0
            if (c == '$')
564
0
            {
565
0
                b_is_format = true;
566
0
                b_empty_if_na = false;
567
0
                continue;
568
0
            }
569
570
0
            vlc_memstream_putc(stream, c);
571
0
            continue;
572
0
        }
573
574
0
        b_is_format = false;
575
576
0
        switch (c)
577
0
        {
578
0
            case 'a':
579
0
                write_meta(stream, item, vlc_meta_Artist);
580
0
                break;
581
0
            case 'b':
582
0
                write_meta(stream, item, vlc_meta_Album);
583
0
                break;
584
0
            case 'c':
585
0
                write_meta(stream, item, vlc_meta_Copyright);
586
0
                break;
587
0
            case 'd':
588
0
                write_meta(stream, item, vlc_meta_Description);
589
0
                break;
590
0
            case 'e':
591
0
                write_meta(stream, item, vlc_meta_EncodedBy);
592
0
                break;
593
0
            case 'f':
594
0
                if (item != NULL)
595
0
                {
596
0
                    vlc_mutex_lock(&item->lock);
597
0
                    if (item->p_stats != NULL)
598
0
                        vlc_memstream_printf(stream, "%"PRIi64,
599
0
                            item->p_stats->i_displayed_pictures);
600
0
                    else if (!b_empty_if_na)
601
0
                        vlc_memstream_putc(stream, '-');
602
0
                    vlc_mutex_unlock(&item->lock);
603
0
                }
604
0
                else if (!b_empty_if_na)
605
0
                    vlc_memstream_putc(stream, '-');
606
0
                break;
607
0
            case 'g':
608
0
                write_meta(stream, item, vlc_meta_Genre);
609
0
                break;
610
0
            case 'l':
611
0
                write_meta(stream, item, vlc_meta_Language);
612
0
                break;
613
0
            case 'n':
614
0
                write_meta(stream, item, vlc_meta_TrackNumber);
615
0
                break;
616
0
            case 'o':
617
0
                write_meta(stream, item, vlc_meta_TrackTotal);
618
0
                break;
619
0
            case 'p':
620
0
                if (item == NULL)
621
0
                    break;
622
0
                {
623
0
                    char *value = input_item_GetNowPlayingFb(item);
624
0
                    if (value == NULL)
625
0
                        break;
626
627
0
                    vlc_memstream_puts(stream, value);
628
0
                    free(value);
629
0
                }
630
0
                break;
631
0
            case 'r':
632
0
                write_meta(stream, item, vlc_meta_Rating);
633
0
                break;
634
0
            case 's':
635
0
            {
636
0
                char *lang = NULL;
637
638
0
                if (player != NULL)
639
0
                    lang = vlc_player_GetCategoryLanguage(player, SPU_ES);
640
0
                if (lang != NULL)
641
0
                {
642
0
                    vlc_memstream_puts(stream, lang);
643
0
                    free(lang);
644
0
                }
645
0
                else if (!b_empty_if_na)
646
0
                    vlc_memstream_putc(stream, '-');
647
0
                break;
648
0
            }
649
0
            case 't':
650
0
                write_meta(stream, item, vlc_meta_Title);
651
0
                break;
652
0
            case 'u':
653
0
                write_meta(stream, item, vlc_meta_URL);
654
0
                break;
655
0
            case 'A':
656
0
                write_meta(stream, item, vlc_meta_Date);
657
0
                break;
658
0
            case 'B':
659
0
            {
660
0
                if (player)
661
0
                {
662
0
                    const struct vlc_player_track *track =
663
0
                        vlc_player_GetSelectedTrack(player, AUDIO_ES);
664
0
                    if (track)
665
0
                    {
666
0
                        vlc_memstream_printf(stream, "%u",
667
0
                                             track->fmt.i_bitrate);
668
0
                        break;
669
0
                    }
670
0
                }
671
0
                if (!b_empty_if_na)
672
0
                    vlc_memstream_putc(stream, '-');
673
0
                break;
674
0
            }
675
0
            case 'C':
676
0
                if (player)
677
0
                {
678
0
                    ssize_t chapter = vlc_player_GetSelectedChapterIdx(player);
679
0
                    if (chapter != -1)
680
0
                    {
681
0
                        vlc_memstream_printf(stream, "%zd", chapter);
682
0
                        break;
683
0
                    }
684
0
                }
685
0
                if (!b_empty_if_na)
686
0
                    vlc_memstream_putc(stream, '-');
687
0
                break;
688
0
            case 'D':
689
0
                if (item != NULL)
690
0
                    write_duration(stream, input_item_GetDuration(item));
691
0
                else if (!b_empty_if_na)
692
0
                    vlc_memstream_puts(stream, "--:--:--");
693
0
                break;
694
0
            case 'F':
695
0
                if (item != NULL)
696
0
                {
697
0
                    char *uri = input_item_GetURI(item);
698
0
                    if (uri != NULL)
699
0
                    {
700
0
                        vlc_memstream_puts(stream, uri);
701
0
                        free(uri);
702
0
                    }
703
0
                }
704
0
                break;
705
0
            case 'I':
706
0
                if (player)
707
0
                {
708
0
                    ssize_t title = vlc_player_GetSelectedTitleIdx(player);
709
0
                    if (title != -1)
710
0
                    {
711
0
                        vlc_memstream_printf(stream, "%zd", title);
712
0
                        break;
713
0
                    }
714
0
                }
715
0
                if (!b_empty_if_na)
716
0
                    vlc_memstream_putc(stream, '-');
717
0
                break;
718
0
            case 'L':
719
0
                if (player)
720
0
                {
721
0
                    vlc_tick_t length = vlc_player_GetLength(player);
722
0
                    vlc_tick_t time = vlc_player_GetTime(player);
723
0
                    if (length != VLC_TICK_INVALID && time != VLC_TICK_INVALID)
724
0
                        write_duration(stream, length - time);
725
0
                }
726
0
                if (!b_empty_if_na)
727
0
                    vlc_memstream_puts(stream, "--:--:--");
728
0
                break;
729
0
            case 'N':
730
0
                if (item != NULL)
731
0
                {
732
0
                    char *name = input_item_GetName(item);
733
0
                    if (name != NULL)
734
0
                    {
735
0
                        vlc_memstream_puts(stream, name);
736
0
                        free(name);
737
0
                    }
738
0
                }
739
0
                break;
740
0
            case 'O':
741
0
            {
742
0
                char *lang = NULL;
743
744
0
                if (player != NULL)
745
0
                    lang = vlc_player_GetCategoryLanguage(player, AUDIO_ES);
746
0
                if (lang != NULL)
747
0
                {
748
0
                    vlc_memstream_puts(stream, lang);
749
0
                    free(lang);
750
0
                }
751
0
                else if (!b_empty_if_na)
752
0
                    vlc_memstream_putc(stream, '-');
753
0
                break;
754
0
            }
755
0
            case 'P':
756
0
                if (player)
757
0
                {
758
0
                    float pos = vlc_player_GetPosition(player);
759
0
                    if (pos >= 0)
760
0
                    {
761
0
                        vlc_memstream_printf(stream, "%2.1f", pos);
762
0
                        break;
763
0
                    }
764
0
                }
765
0
                if (!b_empty_if_na)
766
0
                    vlc_memstream_puts(stream, "--.-%");
767
0
                break;
768
0
            case 'R':
769
0
                if (player)
770
0
                    vlc_memstream_printf(stream, "%.3f",
771
0
                                         vlc_player_GetRate(player));
772
0
                else if (!b_empty_if_na)
773
0
                    vlc_memstream_putc(stream, '-');
774
0
                break;
775
0
            case 'S':
776
0
                if (player)
777
0
                {
778
0
                    const struct vlc_player_track *track =
779
0
                        vlc_player_GetSelectedTrack(player, AUDIO_ES);
780
0
                    if (track)
781
0
                    {
782
0
                        div_t dr = div((track->fmt.audio.i_rate + 50) / 100, 10);
783
0
                        vlc_memstream_printf(stream, "%d.%01d", dr.quot, dr.rem);
784
0
                        break;
785
0
                    }
786
0
                }
787
0
                if (!b_empty_if_na)
788
0
                    vlc_memstream_putc(stream, '-');
789
0
                break;
790
0
            case 'T':
791
0
                if (player)
792
0
                {
793
0
                    vlc_tick_t time = vlc_player_GetTime(player);
794
0
                    if (time != VLC_TICK_INVALID)
795
0
                    {
796
0
                        write_duration(stream, time);
797
0
                        break;
798
0
                    }
799
0
                }
800
0
                if (!b_empty_if_na)
801
0
                    vlc_memstream_puts(stream, "--:--:--");
802
0
                break;
803
0
            case 'U':
804
0
                write_meta(stream, item, vlc_meta_Publisher);
805
0
                break;
806
0
            case 'V':
807
0
            {
808
0
                float vol = 0.f;
809
810
0
                if (player)
811
0
                {
812
0
                    audio_output_t *aout = vlc_player_aout_Hold(player);
813
0
                    if (aout != NULL)
814
0
                    {
815
0
                        vol = aout_VolumeGet(aout);
816
0
                        aout_Release(aout);
817
0
                    }
818
0
                }
819
0
                if (vol >= 0.f)
820
0
                    vlc_memstream_printf(stream, "%ld", lroundf(vol * 256.f));
821
0
                else if (!b_empty_if_na)
822
0
                    vlc_memstream_puts(stream, "---");
823
0
                break;
824
0
            }
825
0
            case '_':
826
0
                vlc_memstream_putc(stream, '\n');
827
0
                break;
828
0
            case 'Z':
829
0
                if (item == NULL)
830
0
                    break;
831
0
                {
832
0
                    char *value = input_item_GetNowPlayingFb(item);
833
0
                    if (value != NULL)
834
0
                    {
835
0
                        vlc_memstream_puts(stream, value);
836
0
                        free(value);
837
0
                    }
838
0
                    else
839
0
                    {
840
0
                        char *title = input_item_GetTitleFbName(item);
841
842
0
                        if (write_meta(stream, item, vlc_meta_Artist) >= 0
843
0
                            && title != NULL)
844
0
                            vlc_memstream_puts(stream, " - ");
845
846
0
                        if (title != NULL)
847
0
                        {
848
0
                            vlc_memstream_puts(stream, title);
849
0
                            free(title);
850
0
                        }
851
0
                    }
852
0
                }
853
0
                break;
854
0
            case ' ':
855
0
                b_empty_if_na = true;
856
0
                b_is_format = true;
857
0
                break;
858
0
            default:
859
0
                vlc_memstream_putc(stream, c);
860
0
                break;
861
0
        }
862
0
    }
863
864
0
    if (vlc_memstream_close(stream))
865
0
        return NULL;
866
0
    return stream->ptr;
867
0
}
868
869
int vlc_filenamecmp(const char *a, const char *b)
870
0
{
871
0
    size_t i;
872
0
    char ca, cb;
873
874
    /* Attempt to guess if the sorting algorithm should be alphabetic
875
     * (i.e. collation) or numeric:
876
     * - If the first mismatching characters are not both digits,
877
     *   then collation is the only option.
878
     * - If one of the first mismatching characters is 0 and the other is also
879
     *   a digit, the comparands are probably left-padded numerical values.
880
     *   It does not matter which algorithm is used: the zero will be smaller
881
     *   than non-zero either way.
882
     * - Otherwise, the comparands are numerical values, and might not be
883
     *   aligned (i.e. not same order of magnitude). If so, collation would
884
     *   fail. So numerical comparison is performed. */
885
0
    for (i = 0; (ca = a[i]) == (cb = b[i]); i++)
886
0
        if (ca == '\0')
887
0
            return 0; /* strings are exactly identical */
888
889
0
    if ((unsigned)(ca - '0') > 9 || (unsigned)(cb - '0') > 9)
890
0
        return strcoll(a, b);
891
892
0
    unsigned long long ua = strtoull(a + i, NULL, 10);
893
0
    unsigned long long ub = strtoull(b + i, NULL, 10);
894
895
    /* The number may be identical in two cases:
896
     * - leading zero (e.g. "012" and "12")
897
     * - overflow on both sides (#ULLONG_MAX) */
898
0
    if (ua == ub)
899
0
        return strcoll(a, b);
900
901
0
    return (ua > ub) ? +1 : -1;
902
0
}
903
904
/**
905
 * Sanitize a file name.
906
 *
907
 * Remove forbidden, potentially forbidden and otherwise evil characters from
908
 * file names. That includes slashes, and popular characters like colon
909
 * (on Unix anyway).
910
 *
911
 * \warning This function should only be used for automatically generated
912
 * file names. Do not use this on full paths, only single file names without
913
 * any directory separator!
914
 */
915
void filename_sanitize( char *str )
916
0
{
917
0
    unsigned char c;
918
919
    /* Special file names, not allowed */
920
0
    if( !strcmp( str, "." ) || !strcmp( str, ".." ) )
921
0
    {
922
0
        while( *str )
923
0
            *(str++) = '_';
924
0
        return;
925
0
    }
926
927
    /* On platforms not using UTF-8, VLC cannot access non-Unicode paths.
928
     * Also, some file systems require Unicode file names.
929
     * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
930
0
    EnsureUTF8( str );
931
932
    /* Avoid leading spaces to please Windows. */
933
0
    while( (c = *str) != '\0' )
934
0
    {
935
0
        if( c != ' ' )
936
0
            break;
937
0
        *(str++) = '_';
938
0
    }
939
940
0
    char *start = str;
941
942
0
    while( (c = *str) != '\0' )
943
0
    {
944
        /* Non-printable characters are not a good idea */
945
0
        if( c < 32 )
946
0
            *str = '_';
947
        /* This is the list of characters not allowed by Microsoft.
948
         * We also black-list them on Unix as they may be confusing, and are
949
         * not supported by some file system types (notably CIFS). */
950
0
        else if( strchr( "/:\\*\"?|<>", c ) != NULL )
951
0
            *str = '_';
952
0
        str++;
953
0
    }
954
955
    /* Avoid trailing spaces also to please Windows. */
956
0
    while( str > start )
957
0
    {
958
0
        if( *(--str) != ' ' )
959
0
            break;
960
0
        *str = '_';
961
0
    }
962
0
}