Coverage Report

Created: 2025-07-11 06:16

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