Coverage Report

Created: 2026-01-10 07:00

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
2.64k
#define CHAIN_APPEND_IMPL(n, t) CHAIN_APPEND_DECL(n ,t)\
39
2.64k
{\
40
2.64k
    t ** insert = &p_a->p_next;\
41
37.3k
    while( *insert ) insert = &((*insert)->p_next);\
42
2.64k
    *insert = p_b;\
43
2.64k
}
vlc_css_declarations_Append
Line
Count
Source
38
2.07k
#define CHAIN_APPEND_IMPL(n, t) CHAIN_APPEND_DECL(n ,t)\
39
2.07k
{\
40
2.07k
    t ** insert = &p_a->p_next;\
41
21.0k
    while( *insert ) insert = &((*insert)->p_next);\
42
2.07k
    *insert = p_b;\
43
2.07k
}
vlc_css_selector_Append
Line
Count
Source
38
575
#define CHAIN_APPEND_IMPL(n, t) CHAIN_APPEND_DECL(n ,t)\
39
575
{\
40
575
    t ** insert = &p_a->p_next;\
41
16.3k
    while( *insert ) insert = &((*insert)->p_next);\
42
575
    *insert = p_b;\
43
575
}
44
45
void vlc_css_term_Clean( vlc_css_term_t a )
46
269k
{
47
269k
    if( a.type >= TYPE_STRING )
48
215k
        free( a.psz );
49
50
269k
    if( a.type == TYPE_FUNCTION )
51
78.3k
    {
52
78.3k
        if( a.function )
53
64.6k
            vlc_css_expression_Delete( a.function );
54
78.3k
    }
55
269k
}
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
223k
{
73
223k
    if( p_expr->i_count >= p_expr->i_alloc )
74
125k
    {
75
125k
        size_t i_realloc = (p_expr->i_alloc == 0) ? 1 : p_expr->i_alloc + 4;
76
125k
        void *reac = realloc( p_expr->seq, i_realloc * sizeof(p_expr->seq[0]) );
77
125k
        if( reac )
78
125k
        {
79
125k
            p_expr->seq = reac;
80
125k
            p_expr->i_alloc = i_realloc;
81
125k
        }
82
125k
    }
83
84
223k
    if( p_expr->i_count >= p_expr->i_alloc )
85
0
        return false;
86
87
223k
    p_expr->seq[p_expr->i_count].op = op;
88
223k
    p_expr->seq[p_expr->i_count++].term = a;
89
223k
    return true;
90
223k
}
91
92
void vlc_css_expression_Delete( vlc_css_expr_t *p_expr )
93
85.4k
{
94
85.4k
    if( p_expr )
95
85.4k
    {
96
308k
        for(size_t i=0; i<p_expr->i_count; i++)
97
223k
            vlc_css_term_Clean( p_expr->seq[i].term );
98
85.4k
        free( p_expr->seq );
99
85.4k
    }
100
85.4k
    free( p_expr );
101
85.4k
}
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
85.6k
{
116
85.6k
    vlc_css_expr_t *p_expr = calloc(1, sizeof(*p_expr));
117
85.6k
    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
85.6k
    return p_expr;
123
85.6k
}
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
441k
{
129
448k
    while( p_decl )
130
6.52k
    {
131
6.52k
        vlc_css_declaration_t *p_next = p_decl->p_next;
132
6.52k
        vlc_css_expression_Delete( p_decl->expr );
133
6.52k
        free( p_decl->psz_property );
134
6.52k
        free( p_decl );
135
6.52k
        p_decl = p_next;
136
6.52k
    }
137
441k
}
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
6.75k
{
152
6.75k
    vlc_css_declaration_t *p_decl = calloc(1, sizeof(*p_decl));
153
6.75k
    if (likely(p_decl != NULL))
154
6.75k
    {
155
6.75k
        p_decl->psz_property = strdup(psz);
156
6.75k
        if (unlikely( p_decl->psz_property == NULL ))
157
0
        {
158
0
            free(p_decl);
159
0
            p_decl = NULL;
160
0
        }
161
6.75k
    }
162
6.75k
    return p_decl;
163
6.75k
}
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
15.7k
{
170
15.7k
    *p_sel->specifiers.pp_append = p_spec;
171
31.5k
    while(p_spec)
172
15.7k
    {
173
15.7k
        p_sel->specifiers.pp_append = &p_spec->p_next;
174
15.7k
        p_spec = p_spec->p_next;
175
15.7k
    }
176
15.7k
}
177
178
void vlc_css_selectors_Delete( vlc_css_selector_t *p_sel )
179
516k
{
180
578k
    while( p_sel )
181
61.8k
    {
182
61.8k
        vlc_css_selector_t *p_next = p_sel->p_next;
183
61.8k
        free( p_sel->psz_name );
184
61.8k
        vlc_css_selectors_Delete( p_sel->specifiers.p_first );
185
61.8k
        vlc_css_selectors_Delete( p_sel->p_matchsel );
186
61.8k
        free( p_sel );
187
61.8k
        p_sel = p_next;
188
61.8k
    }
189
516k
}
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
61.8k
{
204
61.8k
    vlc_css_selector_t *p_sel = calloc(1, sizeof(*p_sel));
205
61.8k
    if (unlikely(p_sel == NULL))
206
0
        return NULL;
207
61.8k
    p_sel->psz_name = strdup(psz);
208
61.8k
    p_sel->type = type;
209
61.8k
    p_sel->combinator = RELATION_SELF;
210
61.8k
    p_sel->specifiers.pp_append = &p_sel->specifiers.p_first;
211
61.8k
    return p_sel;
212
61.8k
}
213
214
void vlc_css_rules_Delete( vlc_css_rule_t *p_rule )
215
21.5k
{
216
36.1k
    while(p_rule)
217
14.5k
    {
218
14.5k
        vlc_css_rule_t *p_next = p_rule->p_next;
219
14.5k
        vlc_css_selectors_Delete( p_rule->p_selectors );
220
14.5k
        vlc_css_declarations_Delete( p_rule->p_declarations );
221
14.5k
        free(p_rule);
222
14.5k
        p_rule = p_next;
223
14.5k
    }
224
21.5k
}
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
14.5k
{
240
14.5k
    vlc_css_rule_t *p_rule = calloc(1, sizeof(*p_rule));
241
14.5k
    return p_rule;
242
14.5k
}
243
244
void vlc_css_parser_AddRule( vlc_css_parser_t *p_parser,
245
                                           vlc_css_rule_t *p_rule )
246
14.5k
{
247
14.5k
    (*p_parser->rules.pp_append) = p_rule;
248
14.5k
    p_parser->rules.pp_append = &p_rule->p_next;
249
14.5k
}
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
12.4k
{
258
12.4k
    vlc_css_rules_Delete( p_parser->rules.p_first );
259
12.4k
}
260
261
void vlc_css_parser_Init( vlc_css_parser_t *p_parser )
262
12.4k
{
263
12.4k
    memset(p_parser, 0, sizeof(vlc_css_parser_t));
264
12.4k
    p_parser->rules.pp_append = &p_parser->rules.p_first;
265
12.4k
}
266
267
bool vlc_css_parser_ParseBytes( vlc_css_parser_t *p_parser, const uint8_t *p_data, size_t i_data )
268
12.4k
{
269
12.4k
    yyscan_t yy;
270
12.4k
    csslex_init(&yy);
271
272
12.4k
    YY_BUFFER_STATE buf = css_scan_bytes( (const char*) p_data, i_data, yy );
273
274
12.4k
    bool b_ret = !cssparse( yy, p_parser );
275
276
12.4k
    css_delete_buffer( buf, yy );
277
12.4k
    csslex_destroy( yy );
278
279
12.4k
    return b_ret;
280
12.4k
}
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
34.3k
{
299
    /* adapted from codepoint conversion from strings.h */
300
34.3k
    if( ucs4 <= 0x7F )
301
24.8k
    {
302
24.8k
        p[0] = ucs4;
303
24.8k
        return 1;
304
24.8k
    }
305
9.57k
    else if( ucs4 <= 0x7FF )
306
2.57k
    {
307
2.57k
        p[0] = 0xC0 |  (ucs4 >>  6);
308
2.57k
        p[1] = 0x80 |  (ucs4        & 0x3F);
309
2.57k
        return 2;
310
2.57k
    }
311
7.00k
    else if( ucs4 <= 0xFFFF )
312
3.68k
    {
313
3.68k
        p[0] = 0xE0 |  (ucs4 >> 12);
314
3.68k
        p[1] = 0x80 | ((ucs4 >>  6) & 0x3F);
315
3.68k
        p[2] = 0x80 |  (ucs4        & 0x3F);
316
3.68k
        return 3;
317
3.68k
    }
318
3.31k
    else if( ucs4 <= 0x1FFFFF )
319
2.22k
    {
320
2.22k
        p[0] = 0xF0 |  (ucs4 >> 18);
321
2.22k
        p[1] = 0x80 | ((ucs4 >> 12) & 0x3F);
322
2.22k
        p[2] = 0x80 | ((ucs4 >>  6) & 0x3F);
323
2.22k
        p[3] = 0x80 |  (ucs4        & 0x3F);
324
2.22k
        return 4;
325
2.22k
    }
326
1.08k
    else if( ucs4 <= 0x3FFFFFF )
327
1.08k
    {
328
1.08k
        p[0] = 0xF8 |  (ucs4 >> 24);
329
1.08k
        p[1] = 0x80 | ((ucs4 >> 18) & 0x3F);
330
1.08k
        p[2] = 0x80 | ((ucs4 >> 12) & 0x3F);
331
1.08k
        p[3] = 0x80 | ((ucs4 >>  6) & 0x3F);
332
1.08k
        p[4] = 0x80 |  (ucs4        & 0x3F);
333
1.08k
        return 5;
334
1.08k
    }
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
34.3k
}
346
347
void vlc_css_unescape( char *psz )
348
769k
{
349
769k
    if( !psz )
350
0
        return;
351
769k
    char *r = psz;
352
769k
    char *w = psz;
353
354
3.36M
    while( *r )
355
2.59M
    {
356
2.59M
        if( *r == '\\' )
357
160k
        {
358
160k
            r++;
359
            /* newlines */
360
160k
            if( *r == 0 )
361
230
            {
362
230
                break;
363
230
            }
364
160k
            else if( strchr( "nfr", *r ) )
365
116k
            {
366
116k
                switch( r[0] )
367
116k
                {
368
19.4k
                    case 'n':
369
19.4k
                        *w++ = '\n';
370
19.4k
                        r++;
371
19.4k
                        break;
372
69.9k
                    case 'r':
373
69.9k
                        *w++ = '\r';
374
69.9k
                        if( r[1] && r[1] == 'n' )
375
230
                        {
376
230
                            *w++ = '\n';
377
230
                            r++;
378
230
                        }
379
69.9k
                        r++;
380
69.9k
                        break;
381
27.2k
                    case 'f':
382
27.2k
                        *w++ = '\f';
383
27.2k
                        r++;
384
27.2k
                        break;
385
116k
                }
386
116k
            }
387
43.8k
            else if( isxdigit( *r ) )
388
34.3k
            {
389
34.3k
                const char *p_start = r;
390
34.3k
                int i;
391
98.8k
                for( i=0; i<6 && *r && isxdigit( *r ); i++ )
392
64.5k
                    r++;
393
34.3k
                const char backup = *r;
394
34.3k
                *r = 0;
395
34.3k
                unsigned i_value = strtoul( p_start, NULL, 16 );
396
34.3k
                *r = backup;
397
34.3k
                if( i < 6 && *r && *r == ' ' )
398
245
                    r++;
399
34.3k
                w += CodePointToUTF8( i_value, w );
400
34.3k
            }
401
160k
        }
402
2.43M
        else
403
2.43M
        {
404
2.43M
            *w++ = *r++;
405
2.43M
        }
406
2.59M
    }
407
408
769k
    *w = 0;
409
769k
}
410
411
char * vlc_css_unescaped( const char *psz )
412
758k
{
413
758k
    char *psz_ret = strdup( psz );
414
758k
    vlc_css_unescape( psz_ret );
415
758k
    return psz_ret;
416
758k
}
417
418
char * vlc_css_unquoted( const char *psz )
419
10.7k
{
420
10.7k
    char *psz_ret;
421
10.7k
    if( *psz == '\'' || *psz == '\"' )
422
9.98k
    {
423
9.98k
        size_t i_len = strlen(psz);
424
9.98k
        if( psz[i_len - 1] == psz[0] )
425
9.98k
            psz_ret = strndup( psz + 1, i_len - 2 );
426
0
        else
427
0
            psz_ret = strdup( psz );
428
9.98k
    }
429
795
    else
430
795
    {
431
795
        psz_ret = strdup( psz );
432
795
    }
433
10.7k
    return psz_ret;
434
10.7k
}
435
436
437
char * vlc_css_unquotedunescaped( const char *psz )
438
10.7k
{
439
10.7k
    char *psz_ret = vlc_css_unquoted( psz );
440
10.7k
    if( psz_ret )
441
10.7k
        vlc_css_unescape( psz_ret );
442
10.7k
    return psz_ret;
443
10.7k
}
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