Coverage Report

Created: 2026-07-12 08:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/codec/webvtt/css_parser.c
Line
Count
Source
1
/*****************************************************************************
2
 * css_parser.c : CSS parser
3
 *****************************************************************************
4
 * Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published by
8
 * the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this program; if not, write to the Free Software Foundation,
18
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19
 *****************************************************************************/
20
#ifdef HAVE_CONFIG_H
21
# include "config.h"
22
#endif
23
24
#include <vlc_common.h>
25
26
#include "css_bridge.h"
27
#include "css_parser.h"
28
#include "CSSGrammar.h"
29
30
#include <ctype.h>
31
32
static void vlc_css_term_Debug( const vlc_css_term_t a, int depth );
33
static void vlc_css_expression_Debug( const vlc_css_expr_t *p_expr, int depth );
34
static void vlc_css_declarations_Debug( const vlc_css_declaration_t *p_decl, int depth );
35
static void vlc_css_selectors_Debug( const vlc_css_selector_t *p_sel, int depth );
36
static void vlc_css_rules_Debug( const vlc_css_rule_t *p_rule, int depth );
37
38
5.92k
#define CHAIN_APPEND_IMPL(n, t) CHAIN_APPEND_DECL(n ,t)\
39
5.92k
{\
40
5.92k
    t ** insert = &p_a->p_next;\
41
24.3k
    while( *insert ) insert = &((*insert)->p_next);\
42
5.92k
    *insert = p_b;\
43
5.92k
}
vlc_css_declarations_Append
Line
Count
Source
38
5.32k
#define CHAIN_APPEND_IMPL(n, t) CHAIN_APPEND_DECL(n ,t)\
39
5.32k
{\
40
5.32k
    t ** insert = &p_a->p_next;\
41
19.1k
    while( *insert ) insert = &((*insert)->p_next);\
42
5.32k
    *insert = p_b;\
43
5.32k
}
vlc_css_selector_Append
Line
Count
Source
38
602
#define CHAIN_APPEND_IMPL(n, t) CHAIN_APPEND_DECL(n ,t)\
39
602
{\
40
602
    t ** insert = &p_a->p_next;\
41
5.18k
    while( *insert ) insert = &((*insert)->p_next);\
42
602
    *insert = p_b;\
43
602
}
44
45
void vlc_css_term_Clean( vlc_css_term_t a )
46
179k
{
47
179k
    if( a.type >= TYPE_STRING )
48
83.1k
        free( a.psz );
49
50
179k
    if( a.type == TYPE_FUNCTION )
51
66.7k
    {
52
66.7k
        if( a.function )
53
60.4k
            vlc_css_expression_Delete( a.function );
54
66.7k
    }
55
179k
}
56
57
static void vlc_css_term_Debug( const vlc_css_term_t a, int depth )
58
0
{
59
0
    for(int i=0;i<depth;i++) printf(" ");
60
0
    printf("term: ");
61
0
    if( a.type >= TYPE_STRING )
62
0
    {
63
0
        printf("%x %s\n", a.type, a.psz);
64
0
        if( a.type == TYPE_FUNCTION && a.function )
65
0
            vlc_css_expression_Debug( a.function, depth + 1 );
66
0
    }
67
0
    else printf("%x %f\n", a.type, a.val);
68
0
}
69
70
bool vlc_css_expression_AddTerm( vlc_css_expr_t *p_expr,
71
                                        char op, vlc_css_term_t a )
72
150k
{
73
150k
    if( p_expr->i_count >= p_expr->i_alloc )
74
95.9k
    {
75
95.9k
        size_t i_realloc = (p_expr->i_alloc == 0) ? 1 : p_expr->i_alloc + 4;
76
95.9k
        void *reac = realloc( p_expr->seq, i_realloc * sizeof(p_expr->seq[0]) );
77
95.9k
        if( reac )
78
95.9k
        {
79
95.9k
            p_expr->seq = reac;
80
95.9k
            p_expr->i_alloc = i_realloc;
81
95.9k
        }
82
95.9k
    }
83
84
150k
    if( p_expr->i_count >= p_expr->i_alloc )
85
0
        return false;
86
87
150k
    p_expr->seq[p_expr->i_count].op = op;
88
150k
    p_expr->seq[p_expr->i_count++].term = a;
89
150k
    return true;
90
150k
}
91
92
void vlc_css_expression_Delete( vlc_css_expr_t *p_expr )
93
72.9k
{
94
72.9k
    if( p_expr )
95
72.9k
    {
96
223k
        for(size_t i=0; i<p_expr->i_count; i++)
97
150k
            vlc_css_term_Clean( p_expr->seq[i].term );
98
72.9k
        free( p_expr->seq );
99
72.9k
    }
100
72.9k
    free( p_expr );
101
72.9k
}
102
103
static void vlc_css_expression_Debug( const vlc_css_expr_t *p_expr, int depth )
104
0
{
105
0
    if( p_expr )
106
0
    {
107
0
        for(int i=0;i<depth;i++) printf(" ");
108
0
        printf("expression: \n");
109
0
        for(size_t i=0; i<p_expr->i_count; i++)
110
0
            vlc_css_term_Debug( p_expr->seq[i].term, depth + 1 );
111
0
    }
112
0
}
113
114
vlc_css_expr_t * vlc_css_expression_New( vlc_css_term_t term )
115
73.4k
{
116
73.4k
    vlc_css_expr_t *p_expr = calloc(1, sizeof(*p_expr));
117
73.4k
    if(p_expr && !vlc_css_expression_AddTerm( p_expr, 0, term ))
118
0
    {
119
0
        free(p_expr);
120
0
        p_expr = NULL;
121
0
    }
122
73.4k
    return p_expr;
123
73.4k
}
124
125
CHAIN_APPEND_IMPL(vlc_css_declarations_Append, vlc_css_declaration_t)
126
127
void vlc_css_declarations_Delete( vlc_css_declaration_t *p_decl )
128
269k
{
129
274k
    while( p_decl )
130
5.40k
    {
131
5.40k
        vlc_css_declaration_t *p_next = p_decl->p_next;
132
5.40k
        vlc_css_expression_Delete( p_decl->expr );
133
5.40k
        free( p_decl->psz_property );
134
5.40k
        free( p_decl );
135
5.40k
        p_decl = p_next;
136
5.40k
    }
137
269k
}
138
139
static void vlc_css_declarations_Debug( const vlc_css_declaration_t *p_decl, int depth )
140
0
{
141
0
    while( p_decl )
142
0
    {
143
0
        for(int i=0;i<depth;i++) printf(" ");
144
0
        printf("declaration: %s\n", p_decl->psz_property );
145
0
        vlc_css_expression_Debug( p_decl->expr, depth + 1 );
146
0
        p_decl = p_decl->p_next;
147
0
    }
148
0
}
149
150
vlc_css_declaration_t * vlc_css_declaration_New( const char *psz )
151
5.68k
{
152
5.68k
    vlc_css_declaration_t *p_decl = calloc(1, sizeof(*p_decl));
153
5.68k
    if (likely(p_decl != NULL))
154
5.68k
    {
155
5.68k
        p_decl->psz_property = strdup(psz);
156
5.68k
        if (unlikely( p_decl->psz_property == NULL ))
157
0
        {
158
0
            free(p_decl);
159
0
            p_decl = NULL;
160
0
        }
161
5.68k
    }
162
5.68k
    return p_decl;
163
5.68k
}
164
165
CHAIN_APPEND_IMPL(vlc_css_selector_Append, vlc_css_selector_t)
166
167
void
168
vlc_css_selector_AddSpecifier( vlc_css_selector_t *p_sel, vlc_css_selector_t *p_spec )
169
10.7k
{
170
10.7k
    *p_sel->specifiers.pp_append = p_spec;
171
21.5k
    while(p_spec)
172
10.7k
    {
173
10.7k
        p_sel->specifiers.pp_append = &p_spec->p_next;
174
10.7k
        p_spec = p_spec->p_next;
175
10.7k
    }
176
10.7k
}
177
178
void vlc_css_selectors_Delete( vlc_css_selector_t *p_sel )
179
693k
{
180
728k
    while( p_sel )
181
35.5k
    {
182
35.5k
        vlc_css_selector_t *p_next = p_sel->p_next;
183
35.5k
        free( p_sel->psz_name );
184
35.5k
        vlc_css_selectors_Delete( p_sel->specifiers.p_first );
185
35.5k
        vlc_css_selectors_Delete( p_sel->p_matchsel );
186
35.5k
        free( p_sel );
187
35.5k
        p_sel = p_next;
188
35.5k
    }
189
693k
}
190
191
static void vlc_css_selectors_Debug( const vlc_css_selector_t *p_sel, int depth )
192
0
{
193
0
    while( p_sel )
194
0
    {
195
0
        for(int i=0;i<depth;i++) printf(" "); printf("selector %c%s:\n", p_sel->combinator, p_sel->psz_name );
196
0
        vlc_css_selectors_Debug( p_sel->p_matchsel, depth + 1 );
197
0
        vlc_css_selectors_Debug( p_sel->specifiers.p_first, depth + 1 );
198
0
        p_sel = p_sel->p_next;
199
0
    }
200
0
}
201
202
vlc_css_selector_t * vlc_css_selector_New( int type, const char *psz )
203
35.5k
{
204
35.5k
    vlc_css_selector_t *p_sel = calloc(1, sizeof(*p_sel));
205
35.5k
    if (unlikely(p_sel == NULL))
206
0
        return NULL;
207
35.5k
    p_sel->psz_name = strdup(psz);
208
35.5k
    p_sel->type = type;
209
35.5k
    p_sel->combinator = RELATION_SELF;
210
35.5k
    p_sel->specifiers.pp_append = &p_sel->specifiers.p_first;
211
35.5k
    return p_sel;
212
35.5k
}
213
214
void vlc_css_rules_Delete( vlc_css_rule_t *p_rule )
215
18.1k
{
216
26.4k
    while(p_rule)
217
8.31k
    {
218
8.31k
        vlc_css_rule_t *p_next = p_rule->p_next;
219
8.31k
        vlc_css_selectors_Delete( p_rule->p_selectors );
220
8.31k
        vlc_css_declarations_Delete( p_rule->p_declarations );
221
8.31k
        free(p_rule);
222
8.31k
        p_rule = p_next;
223
8.31k
    }
224
18.1k
}
225
226
static void vlc_css_rules_Debug( const vlc_css_rule_t *p_rule, int depth )
227
0
{
228
0
    int j = 0;
229
0
    while(p_rule)
230
0
    {
231
0
        for(int i=0;i<depth;i++) printf(" "); printf("rule %d:\n", j++);
232
0
        vlc_css_selectors_Debug( p_rule->p_selectors, depth + 1 );
233
0
        vlc_css_declarations_Debug( p_rule->p_declarations, depth + 1 );
234
0
        p_rule = p_rule->p_next;
235
0
    }
236
0
}
237
238
vlc_css_rule_t * vlc_css_rule_New( void )
239
8.31k
{
240
8.31k
    vlc_css_rule_t *p_rule = calloc(1, sizeof(*p_rule));
241
8.31k
    return p_rule;
242
8.31k
}
243
244
void vlc_css_parser_AddRule( vlc_css_parser_t *p_parser,
245
                                           vlc_css_rule_t *p_rule )
246
8.31k
{
247
8.31k
    (*p_parser->rules.pp_append) = p_rule;
248
8.31k
    p_parser->rules.pp_append = &p_rule->p_next;
249
8.31k
}
250
251
void vlc_css_parser_Debug( const vlc_css_parser_t *p_parser )
252
0
{
253
0
    vlc_css_rules_Debug( p_parser->rules.p_first, 0 );
254
0
}
255
256
void vlc_css_parser_Clean( vlc_css_parser_t *p_parser )
257
10.3k
{
258
10.3k
    vlc_css_rules_Delete( p_parser->rules.p_first );
259
10.3k
}
260
261
void vlc_css_parser_Init( vlc_css_parser_t *p_parser )
262
10.3k
{
263
10.3k
    memset(p_parser, 0, sizeof(vlc_css_parser_t));
264
10.3k
    p_parser->rules.pp_append = &p_parser->rules.p_first;
265
10.3k
}
266
267
bool vlc_css_parser_ParseBytes( vlc_css_parser_t *p_parser, const uint8_t *p_data, size_t i_data )
268
10.3k
{
269
10.3k
    yyscan_t yy;
270
10.3k
    csslex_init(&yy);
271
272
10.3k
    YY_BUFFER_STATE buf = css_scan_bytes( (const char*) p_data, i_data, yy );
273
274
10.3k
    bool b_ret = !cssparse( yy, p_parser );
275
276
10.3k
    css_delete_buffer( buf, yy );
277
10.3k
    csslex_destroy( yy );
278
279
10.3k
    return b_ret;
280
10.3k
}
281
282
bool vlc_css_parser_ParseString( vlc_css_parser_t *p_parser, const char *psz_css )
283
0
{
284
0
    yyscan_t yy;
285
0
    csslex_init(&yy);
286
287
0
    YY_BUFFER_STATE buf = css_scan_string( psz_css, yy );
288
289
0
    bool b_ret = !cssparse( yy, p_parser );
290
291
0
    css_delete_buffer( buf, yy );
292
0
    csslex_destroy( yy );
293
294
0
    return b_ret;
295
0
}
296
297
static int CodePointToUTF8( uint32_t ucs4, char *p )
298
13.3k
{
299
    /* adapted from codepoint conversion from strings.h */
300
13.3k
    if( ucs4 <= 0x7F )
301
8.82k
    {
302
8.82k
        p[0] = ucs4;
303
8.82k
        return 1;
304
8.82k
    }
305
4.56k
    else if( ucs4 <= 0x7FF )
306
1.11k
    {
307
1.11k
        p[0] = 0xC0 |  (ucs4 >>  6);
308
1.11k
        p[1] = 0x80 |  (ucs4        & 0x3F);
309
1.11k
        return 2;
310
1.11k
    }
311
3.45k
    else if( ucs4 <= 0xFFFF )
312
1.93k
    {
313
1.93k
        p[0] = 0xE0 |  (ucs4 >> 12);
314
1.93k
        p[1] = 0x80 | ((ucs4 >>  6) & 0x3F);
315
1.93k
        p[2] = 0x80 |  (ucs4        & 0x3F);
316
1.93k
        return 3;
317
1.93k
    }
318
1.51k
    else if( ucs4 <= 0x1FFFFF )
319
884
    {
320
884
        p[0] = 0xF0 |  (ucs4 >> 18);
321
884
        p[1] = 0x80 | ((ucs4 >> 12) & 0x3F);
322
884
        p[2] = 0x80 | ((ucs4 >>  6) & 0x3F);
323
884
        p[3] = 0x80 |  (ucs4        & 0x3F);
324
884
        return 4;
325
884
    }
326
634
    else if( ucs4 <= 0x3FFFFFF )
327
634
    {
328
634
        p[0] = 0xF8 |  (ucs4 >> 24);
329
634
        p[1] = 0x80 | ((ucs4 >> 18) & 0x3F);
330
634
        p[2] = 0x80 | ((ucs4 >> 12) & 0x3F);
331
634
        p[3] = 0x80 | ((ucs4 >>  6) & 0x3F);
332
634
        p[4] = 0x80 |  (ucs4        & 0x3F);
333
634
        return 5;
334
634
    }
335
0
    else
336
0
    {
337
0
        p[0] = 0xFC |  (ucs4 >> 30);
338
0
        p[1] = 0x80 | ((ucs4 >> 24) & 0x3F);
339
0
        p[2] = 0x80 | ((ucs4 >> 18) & 0x3F);
340
0
        p[3] = 0x80 | ((ucs4 >> 12) & 0x3F);
341
0
        p[4] = 0x80 | ((ucs4 >>  6) & 0x3F);
342
0
        p[5] = 0x80 |  (ucs4        & 0x3F);
343
0
        return 6;
344
0
    }
345
13.3k
}
346
347
void vlc_css_unescape( char *psz )
348
606k
{
349
606k
    if( !psz )
350
0
        return;
351
606k
    char *r = psz;
352
606k
    char *w = psz;
353
354
2.37M
    while( *r )
355
1.76M
    {
356
1.76M
        if( *r == '\\' )
357
35.1k
        {
358
35.1k
            r++;
359
            /* newlines */
360
35.1k
            if( *r == 0 )
361
112
            {
362
112
                break;
363
112
            }
364
34.9k
            else if( strchr( "nfr", *r ) )
365
17.2k
            {
366
17.2k
                switch( r[0] )
367
17.2k
                {
368
243
                    case 'n':
369
243
                        *w++ = '\n';
370
243
                        r++;
371
243
                        break;
372
16.8k
                    case 'r':
373
16.8k
                        *w++ = '\r';
374
16.8k
                        if( r[1] && r[1] == 'n' )
375
210
                        {
376
210
                            *w++ = '\n';
377
210
                            r++;
378
210
                        }
379
16.8k
                        r++;
380
16.8k
                        break;
381
103
                    case 'f':
382
103
                        *w++ = '\f';
383
103
                        r++;
384
103
                        break;
385
17.2k
                }
386
17.2k
            }
387
17.7k
            else if( isxdigit( *r ) )
388
13.3k
            {
389
13.3k
                const char *p_start = r;
390
13.3k
                int i;
391
42.2k
                for( i=0; i<6 && *r && isxdigit( *r ); i++ )
392
28.9k
                    r++;
393
13.3k
                const char backup = *r;
394
13.3k
                *r = 0;
395
13.3k
                unsigned i_value = strtoul( p_start, NULL, 16 );
396
13.3k
                *r = backup;
397
13.3k
                if( i < 6 && *r && *r == ' ' )
398
249
                    r++;
399
13.3k
                w += CodePointToUTF8( i_value, w );
400
13.3k
            }
401
35.1k
        }
402
1.72M
        else
403
1.72M
        {
404
1.72M
            *w++ = *r++;
405
1.72M
        }
406
1.76M
    }
407
408
606k
    *w = 0;
409
606k
}
410
411
char * vlc_css_unescaped( const char *psz )
412
0
{
413
0
    char *psz_ret = strdup( psz );
414
0
    vlc_css_unescape( psz_ret );
415
0
    return psz_ret;
416
0
}
417
418
char * vlc_css_unquoted( const char *psz )
419
606k
{
420
606k
    char *psz_ret;
421
606k
    if( *psz == '\'' || *psz == '\"' )
422
4.55k
    {
423
4.55k
        size_t i_len = strlen(psz);
424
4.55k
        if( psz[i_len - 1] == psz[0] )
425
4.55k
            psz_ret = strndup( psz + 1, i_len - 2 );
426
0
        else
427
0
            psz_ret = strdup( psz );
428
4.55k
    }
429
601k
    else
430
601k
    {
431
601k
        psz_ret = strdup( psz );
432
601k
    }
433
606k
    return psz_ret;
434
606k
}
435
436
437
char * vlc_css_unquotedunescaped( const char *psz )
438
606k
{
439
606k
    char *psz_ret = vlc_css_unquoted( psz );
440
606k
    if( psz_ret )
441
606k
        vlc_css_unescape( psz_ret );
442
606k
    return psz_ret;
443
606k
}
444
445
#ifdef CSS_PARSER_DEBUG
446
447
448
static void css_properties_Debug( const vlc_css_declaration_t *p_decl )
449
{
450
    printf("set %s to ", p_decl->psz_property);
451
    for( size_t i=0; i<p_decl->expr->i_count; i++ )
452
    {
453
        printf("term %s ", p_decl->expr->seq[i].term.psz);
454
    }
455
    printf("\n");
456
}
457
458
void css_selector_Debug( const vlc_css_selector_t *p_sel )
459
{
460
    printf("select its ");
461
    switch( p_sel->combinator )
462
    {
463
        case RELATION_DESCENDENT:
464
            printf("descendent");
465
            break;
466
        case RELATION_DIRECTADJACENT:
467
            printf("adjacent");
468
            break;
469
        case RELATION_INDIRECTADJACENT:
470
            printf("indirect adjacent");
471
            break;
472
        case RELATION_CHILD:
473
            printf("child");
474
            break;
475
        case RELATION_SELF:
476
            break;
477
    }
478
479
    printf(" nodes matching filter: ");
480
    switch( p_sel->type )
481
    {
482
        case SELECTOR_SIMPLE:
483
            printf("<%s>\n", p_sel->psz_name);
484
            break;
485
        case SELECTOR_PSEUDOCLASS:
486
            printf(":%s\n", p_sel->psz_name);
487
            break;
488
        case SELECTOR_PSEUDOELEMENT:
489
            printf("::%s\n", p_sel->psz_name);
490
            break;
491
        case SPECIFIER_ID:
492
            printf("%s\n", p_sel->psz_name);
493
            break;
494
        case SPECIFIER_CLASS:
495
            printf(".%s\n", p_sel->psz_name);
496
            break;
497
        case SPECIFIER_ATTRIB:
498
            printf("[%s]\n", p_sel->psz_name);
499
            break;
500
    }
501
}
502
503
void css_rule_Debug( const vlc_css_rule_t *p_rule )
504
{
505
    if( p_rule == NULL )
506
    return;
507
    printf("add for rule nodes:\n");
508
    for( const vlc_css_selector_t *p_sel = p_rule->p_selectors;
509
                                   p_sel; p_sel = p_sel->p_next )
510
    {
511
        css_selector_Debug( p_sel );
512
        for( const vlc_css_selector_t *p_spec = p_sel->specifiers.p_first;
513
                                       p_spec; p_spec = p_spec->p_next )
514
            css_selector_Debug( p_spec );
515
516
        if( p_sel->p_next )
517
            printf("add nodes\n");
518
    }
519
520
    for( const vlc_css_declaration_t *p_decl = p_rule->p_declarations;
521
                                      p_decl; p_decl = p_decl->p_next )
522
    {
523
        css_properties_Debug( p_decl );
524
    }
525
}
526
527
#endif