Browse Source

codec: webvtt: add CSS lexer/grammar/parser

pull/59/merge
Francois Cartegnie 9 years ago
parent
commit
649b3d02f5
  1. 19
      configure.ac
  2. 12
      modules/codec/Makefile.am
  3. 773
      modules/codec/webvtt/CSSGrammar.y
  4. 107
      modules/codec/webvtt/CSSLexer.l
  5. 27
      modules/codec/webvtt/css_bridge.h
  6. 517
      modules/codec/webvtt/css_parser.c
  7. 182
      modules/codec/webvtt/css_parser.h

19
configure.ac

@ -2956,6 +2956,25 @@ AS_IF([test "${enable_tiger}" != "no"], [
])
])
dnl
dnl check if we can build the css selector engine
dnl
AC_ARG_ENABLE(css,
[ --enable-css CSS selector engine (default auto)])
AS_IF([test "${enable_css}" != "no"], [
AC_PROG_LEX
with_css="no"
AS_IF([test "x$LEX" != "x:"], [
AC_PROG_YACC
dnl can only accept bison or we need byacc feature test for %destructor
AS_IF([test "x$YACC" = "xbison -y"], [
AC_DEFINE(HAVE_CSS, 1, [Define if CSS engine is built])]
with_css="yes"
,[AC_MSG_WARN("Bison not found. CSS support disabled.")])
])
])
AM_CONDITIONAL([ENABLE_CSS], [test "${with_css}" = "yes"])
dnl
dnl Video plugins

12
modules/codec/Makefile.am

@ -228,6 +228,18 @@ libwebvtt_plugin_la_SOURCES = codec/webvtt/subsvtt.c \
demux/webvtt.c \
demux/mp4/minibox.h
codec_LTLIBRARIES += libwebvtt_plugin.la
AM_YFLAGS = -d
if ENABLE_CSS
SUFFIXES += .l .y
# dependency: we need grammar build first
BUILT_SOURCES += codec/webvtt/CSSGrammar.h
libwebvtt_plugin_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/codec/webvtt/ -I$(builddir)/codec/webvtt/
libwebvtt_plugin_la_SOURCES += codec/webvtt/CSSGrammar.y \
codec/webvtt/CSSLexer.l \
codec/webvtt/css_parser.c \
codec/webvtt/css_parser.h \
codec/webvtt/css_bridge.h
endif
libsvcdsub_plugin_la_SOURCES = codec/svcdsub.c
codec_LTLIBRARIES += libsvcdsub_plugin.la

773
modules/codec/webvtt/CSSGrammar.y

@ -0,0 +1,773 @@
/*****************************************************************************
* CSSGrammar.y: bison production rules for simplified css parsing
*****************************************************************************
* Copyright © 2017 VideoLabs, VideoLAN and VLC Authors
*
* Adapted from webkit's CSSGrammar.y:
*
* Copyright (C) 2002-2003 Lars Knoll (knoll@kde.org)
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
* Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
* Copyright (C) 2008 Eric Seidel <eric@webkit.org>
* Copyright (C) 2012 Intel Corporation. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
%pure-parser
%parse-param { yyscan_t scanner } { vlc_css_parser_t *css_parser }
%lex-param { yyscan_t scanner } { vlc_css_parser_t *css_parser }
%{
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include "css_parser.h"
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
%}
%union {
bool boolean;
char character;
int integer;
char *string;
enum vlc_css_relation_e relation;
vlc_css_term_t term;
vlc_css_expr_t *expr;
vlc_css_rule_t *rule;
vlc_css_declaration_t *declaration;
vlc_css_declaration_t *declarationList;
vlc_css_selector_t *selector;
vlc_css_selector_t *selectorList;
}
%{
/* See bison pure calling */
int yylex(union YYSTYPE *, yyscan_t, vlc_css_parser_t *);
static int yyerror(yyscan_t scanner, vlc_css_parser_t *p, const char *msg)
{
VLC_UNUSED(scanner);VLC_UNUSED(p);VLC_UNUSED(msg);
return 1;
}
%}
%expect 10
%nonassoc LOWEST_PREC
%left UNIMPORTANT_TOK
%token WHITESPACE SGML_CD
%token TOKEN_EOF 0
%token INCLUDES
%token DASHMATCH
%token BEGINSWITH
%token ENDSWITH
%token CONTAINS
%token <string> STRING
%right <string> IDENT
%nonassoc <string> IDSEL
%nonassoc <string> HASH
%nonassoc ':'
%nonassoc '.'
%nonassoc '['
%nonassoc <character> '*'
%nonassoc error
%left '|'
%token FONT_FACE_SYM
%token CHARSET_SYM
%token IMPORTANT_SYM
%token CDO
%token CDC
%token <term> LENGTH
%token <term> ANGLE
%token <term> TIME
%token <term> FREQ
%token <term> DIMEN
%token <term> PERCENTAGE
%token <term> NUMBER
%destructor { vlc_css_term_Clean($$); } <term>
%token <string> URI
%token <string> FUNCTION
%token <string> UNICODERANGE
%type <relation> combinator
%type <rule> charset
%type <rule> ignored_charset
%type <rule> ruleset
%type <rule> font_face
%type <rule> invalid_rule
%type <rule> rule
%type <rule> valid_rule
%destructor { vlc_css_rules_Delete($$); } <rule>
%type <string> ident_or_string
%type <string> property
%type <selector> specifier
%type <selector> specifier_list
%type <selector> simple_selector
%type <selector> selector
%type <selectorList> selector_list
%type <selector> selector_with_trailing_whitespace
%type <selector> class
%type <selector> attrib
%type <selector> pseudo
%destructor { vlc_css_selectors_Delete($$); } <selector> <selectorList>
%type <declarationList> declaration_list
%type <declarationList> decl_list
%type <declaration> declaration
%destructor { vlc_css_declarations_Delete($$); } <declaration> <declarationList>
%type <boolean> prio
%type <integer> match
%type <integer> unary_operator
%type <integer> maybe_unary_operator
%type <character> operator
%type <expr> expr
%type <term> term
%type <term> unary_term
%type <term> function
%destructor { vlc_css_expression_Delete($$); } <expr>
%type <string> element_name
%type <string> attr_name
%destructor { free($$); } <string>
%%
stylesheet:
maybe_space maybe_charset maybe_sgml rule_list
;
maybe_space:
/* empty */ %prec UNIMPORTANT_TOK
| maybe_space WHITESPACE
;
maybe_sgml:
/* empty */
| maybe_sgml SGML_CD
| maybe_sgml WHITESPACE
;
maybe_charset:
/* empty */
| charset {
vlc_css_rules_Delete($1);
}
;
closing_brace:
'}'
| %prec LOWEST_PREC TOKEN_EOF
;
charset:
CHARSET_SYM maybe_space STRING maybe_space ';' {
free( $3 );
$$ = 0;
}
| CHARSET_SYM error invalid_block {
$$ = 0;
}
| CHARSET_SYM error ';' {
$$ = 0;
}
;
ignored_charset:
CHARSET_SYM maybe_space STRING maybe_space ';' {
// Ignore any @charset rule not at the beginning of the style sheet.
free( $3 );
$$ = 0;
}
| CHARSET_SYM maybe_space ';' {
$$ = 0;
}
;
rule_list:
/* empty */
| rule_list rule maybe_sgml {
if( $2 )
vlc_css_parser_AddRule( css_parser, $2 );
}
;
valid_rule:
ruleset
| font_face
;
rule:
valid_rule {
$$ = $1;
if($$)
$$->b_valid = true;
}
| ignored_charset
| invalid_rule
;
font_face:
FONT_FACE_SYM maybe_space
'{' maybe_space declaration_list closing_brace {
vlc_css_declarations_Delete( $5 );
$$ = NULL;
}
| FONT_FACE_SYM error invalid_block {
$$ = NULL;
}
| FONT_FACE_SYM error ';' {
$$ = NULL;
}
;
combinator:
'+' maybe_space { $$ = RELATION_DIRECTADJACENT; }
| '~' maybe_space { $$ = RELATION_INDIRECTADJACENT; }
| '>' maybe_space { $$ = RELATION_CHILD; }
;
maybe_unary_operator:
unary_operator { $$ = $1; }
| { $$ = 1; }
;
unary_operator:
'-' { $$ = -1; }
| '+' { $$ = 1; }
;
ruleset:
selector_list '{' maybe_space declaration_list closing_brace {
$$ = vlc_css_rule_New();
if($$)
{
$$->p_selectors = $1;
$$->p_declarations = $4;
}
}
;
selector_list:
selector %prec UNIMPORTANT_TOK {
if ($1) {
$$ = $1;
}
}
| selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK {
if ($1 && $4 )
{
$$ = $1;
vlc_css_selector_Append( $$, $4 );
}
else
{
vlc_css_selectors_Delete( $1 );
vlc_css_selectors_Delete( $4 );
$$ = NULL;
}
}
| selector_list error {
vlc_css_selectors_Delete( $1 );
$$ = NULL;
}
;
selector_with_trailing_whitespace:
selector WHITESPACE {
$$ = $1;
}
;
selector:
simple_selector {
$$ = $1;
}
| selector_with_trailing_whitespace
{
$$ = $1;
}
| selector_with_trailing_whitespace simple_selector
{
$$ = $1;
if ($$)
{
vlc_css_selector_AddSpecifier( $$, $2 );
$2->combinator = RELATION_DESCENDENT;
}
else $$ = $2;
}
| selector combinator simple_selector {
$$ = $1;
if ($$)
{
vlc_css_selector_AddSpecifier( $$, $3 );
$3->combinator = $2;
}
else $$ = $3;
}
| selector error {
vlc_css_selectors_Delete( $1 );
$$ = NULL;
}
;
simple_selector:
element_name {
$$ = vlc_css_selector_New( SELECTOR_SIMPLE, $1 );
free( $1 );
}
| element_name specifier_list {
$$ = vlc_css_selector_New( SELECTOR_SIMPLE, $1 );
if( $$ && $2 )
{
vlc_css_selector_AddSpecifier( $$, $2 );
}
else
{
vlc_css_selectors_Delete( $2 );
}
free( $1 );
}
| specifier_list {
$$ = $1;
}
;
element_name:
IDENT
| '*' { $$ = strdup("*"); }
;
specifier_list:
specifier {
$$ = $1;
}
| specifier_list specifier {
if( $1 )
{
$$ = $1;
while( $1->specifiers.p_first )
$1 = $1->specifiers.p_first;
vlc_css_selector_AddSpecifier( $1, $2 );
}
else $$ = $2;
}
| specifier_list error {
vlc_css_selectors_Delete( $1 );
$$ = NULL;
}
;
specifier:
IDSEL {
$$ = vlc_css_selector_New( SPECIFIER_ID, $1 );
free( $1 );
}
/* Case when #fffaaa like token is lexed as HEX instead of IDSEL */
| HASH {
if ($1[0] >= '0' && $1[0] <= '9') {
$$ = NULL;
} else {
$$ = vlc_css_selector_New( SPECIFIER_ID, $1 );
}
free( $1 );
}
| class
| attrib
| pseudo
;
class:
'.' IDENT {
$$ = vlc_css_selector_New( SPECIFIER_CLASS, $2 );
free( $2 );
}
;
attr_name:
IDENT maybe_space {
$$ = $1;
}
;
attrib:
'[' maybe_space attr_name ']' {
$$ = vlc_css_selector_New( SPECIFIER_ATTRIB, $3 );
free( $3 );
}
| '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' {
$$ = vlc_css_selector_New( SPECIFIER_ATTRIB, $3 );
if( $$ )
{
$$->match = $4;
$$->p_matchsel = vlc_css_selector_New( SPECIFIER_ID, $6 );
}
free( $3 );
free( $6 );
}
;
match:
'=' {
$$ = MATCH_EQUALS;
}
| INCLUDES {
$$ = MATCH_INCLUDES;
}
| DASHMATCH {
$$ = MATCH_DASHMATCH;
}
| BEGINSWITH {
$$ = MATCH_BEGINSWITH;
}
| ENDSWITH {
$$ = MATCH_ENDSWITH;
}
| CONTAINS {
$$ = MATCH_CONTAINS;
}
;
ident_or_string:
IDENT
| STRING
;
pseudo:
':' IDENT {
$$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 );
free( $2 );
}
| ':' ':' IDENT {
$$ = vlc_css_selector_New( SELECTOR_PSEUDOELEMENT, $3 );
free( $3 );
}
// used by :nth-*
| ':' FUNCTION maybe_space maybe_unary_operator NUMBER maybe_space ')' {
if(*$2 != 0)
$2[strlen($2) - 1] = 0;
$$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 );
$5.val *= $4;
free( $2 );
vlc_css_term_Clean( $5 );
}
// required for WEBVTT weirdos cue::(::past)
| ':' ':' FUNCTION maybe_space selector maybe_space ')' {
if(*$3 != 0)
$3[strlen($3) - 1] = 0;
$$ = vlc_css_selector_New( SELECTOR_PSEUDOELEMENT, $3 );
free( $3 );
if( $$ && $5 )
{
vlc_css_selector_AddSpecifier( $$, $5 );
$5->combinator = RELATION_SELF;
}
else
vlc_css_selectors_Delete( $5 );
}
// used by :nth-*(odd/even) and :lang
| ':' FUNCTION maybe_space IDENT maybe_space ')' {
if(*$2 != 0)
$2[strlen($2) - 1] = 0;
$$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 );
free( $2 );
free( $4 );
}
;
declaration_list:
declaration {
$$ = $1;
}
| decl_list declaration {
$$ = $1;
if( $$ )
vlc_css_declarations_Append( $$, $2 );
}
| decl_list {
$$ = $1;
}
| error invalid_block_list error {
$$ = NULL;
}
| error {
$$ = NULL;
}
| decl_list error {
$$ = $1;
}
| decl_list invalid_block_list {
$$ = $1;
}
;
decl_list:
declaration ';' maybe_space {
$$ = $1;
}
| declaration invalid_block_list maybe_space {
vlc_css_declarations_Delete( $1 );
$$ = NULL;
}
| declaration invalid_block_list ';' maybe_space {
vlc_css_declarations_Delete( $1 );
$$ = NULL;
}
| error ';' maybe_space {
$$ = NULL;
}
| error invalid_block_list error ';' maybe_space {
$$ = NULL;
}
| decl_list declaration ';' maybe_space {
if( $1 )
{
$$ = $1;
vlc_css_declarations_Append( $$, $2 );
}
else $$ = $2;
}
| decl_list error ';' maybe_space {
$$ = $1;
}
| decl_list error invalid_block_list error ';' maybe_space {
$$ = $1;
}
;
declaration:
property ':' maybe_space expr prio {
$$ = vlc_css_declaration_New( $1 );
if( $$ )
$$->expr = $4;
else
vlc_css_expression_Delete( $4 );
free( $1 );
}
|
property error {
free( $1 );
$$ = NULL;
}
|
property ':' maybe_space error expr prio {
free( $1 );
vlc_css_expression_Delete( $5 );
/* The default movable type template has letter-spacing: .none; Handle this by looking for
error tokens at the start of an expr, recover the expr and then treat as an error, cleaning
up and deleting the shifted expr. */
$$ = NULL;
}
|
property ':' maybe_space expr prio error {
free( $1 );
vlc_css_expression_Delete( $4 );
/* When we encounter something like p {color: red !important fail;} we should drop the declaration */
$$ = NULL;
}
|
IMPORTANT_SYM maybe_space {
/* Handle this case: div { text-align: center; !important } Just reduce away the stray !important. */
$$ = NULL;
}
|
property ':' maybe_space {
free( $1 );
/* div { font-family: } Just reduce away this property with no value. */
$$ = NULL;
}
|
property ':' maybe_space error {
free( $1 );
/* if we come across rules with invalid values like this case: p { weight: *; }, just discard the rule */
$$ = NULL;
}
|
property invalid_block {
/* if we come across: div { color{;color:maroon} }, ignore everything within curly brackets */
free( $1 );
$$ = NULL;
}
;
property:
IDENT maybe_space {
$$ = $1;
}
;
prio:
IMPORTANT_SYM maybe_space { $$ = true; }
| /* empty */ { $$ = false; }
;
expr:
term {
$$ = vlc_css_expression_New( $1 );
if( !$$ )
vlc_css_term_Clean( $1 );
}
| expr operator term {
$$ = $1;
if( !$1 || !vlc_css_expression_AddTerm($1, $2, $3) )
vlc_css_term_Clean( $3 );
}
| expr invalid_block_list {
vlc_css_expression_Delete( $1 );
$$ = NULL;
}
| expr invalid_block_list error {
vlc_css_expression_Delete( $1 );
$$ = NULL;
}
| expr error {
vlc_css_expression_Delete( $1 );
$$ = NULL;
}
;
operator:
'/' maybe_space {
$$ = '/';
}
| ',' maybe_space {
$$ = ',';
}
| /* empty */ {
$$ = 0;
}
;
term:
unary_term { $$ = $1; }
| unary_operator unary_term {
$$ = $2;
$$.val *= $1;
}
| STRING maybe_space { $$.type = TYPE_STRING; $$.psz = $1; }
| IDENT maybe_space { $$.type = TYPE_IDENTIFIER; $$.psz = $1; }
/* We might need to actually parse the number from a dimension, but we can't just put something that uses $$.string into unary_term. */
| DIMEN maybe_space { $$ = $1; }
| unary_operator DIMEN maybe_space { $$ = $2; }
| URI maybe_space { $$.type = TYPE_URI; $$.psz = $1; }
| UNICODERANGE maybe_space { $$.type = TYPE_UNICODERANGE; $$.psz = $1; }
| IDSEL maybe_space { $$.type = TYPE_HEXCOLOR; $$.psz = $1; }
| HASH maybe_space { $$.type = TYPE_HEXCOLOR; $$.psz = $1; }
| '#' maybe_space { $$.type = TYPE_HEXCOLOR; $$.psz = NULL; } /* Handle error case: "color: #;" */
/* FIXME: according to the specs a function can have a unary_operator in front. I know no case where this makes sense */
| function {
$$ = $1;
}
| '%' maybe_space { /* Handle width: %; */
$$.type = TYPE_PERCENT; $$.val = 0;
}
;
unary_term:
NUMBER maybe_space
| PERCENTAGE maybe_space
| LENGTH maybe_space
| ANGLE maybe_space
| TIME maybe_space
| FREQ maybe_space
;
function:
FUNCTION maybe_space expr ')' maybe_space {
$$.type = TYPE_FUNCTION; $$.function = $3;
$$.psz = $1;
if(*$$.psz != 0)
$$.psz[strlen($$.psz) - 1] = 0;
} |
FUNCTION maybe_space expr TOKEN_EOF {
$$.type = TYPE_FUNCTION; $$.function = $3; $$.psz = $1;
if(*$$.psz != 0)
$$.psz[strlen($$.psz) - 1] = 0;
} |
FUNCTION maybe_space ')' maybe_space {
$$.type = TYPE_FUNCTION; $$.function = NULL; $$.psz = $1;
if(*$$.psz != 0)
$$.psz[strlen($$.psz) - 1] = 0;
} |
FUNCTION maybe_space error {
$$.type = TYPE_FUNCTION; $$.function = NULL; $$.psz = $1;
if(*$$.psz != 0)
$$.psz[strlen($$.psz) - 1] = 0;
}
;
/* error handling rules */
invalid_rule:
error invalid_block {
$$ = NULL;
}
/*
Seems like the two rules below are trying too much and violating
http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html
| error ';' {
$$ = 0;
}
| error '}' {
$$ = 0;
}
*/
;
invalid_block:
'{' error invalid_block_list error closing_brace
| '{' error closing_brace
;
invalid_block_list:
invalid_block
| invalid_block_list error invalid_block
;
%%
#ifdef YYDEBUG
int yydebug=1;
#else
int yydebug=0;
#endif

107
modules/codec/webvtt/CSSLexer.l

@ -0,0 +1,107 @@
/*****************************************************************************
* CSSLexer.l : lexer for simplified CSS, based on W3C spec
*****************************************************************************
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
%option case-insensitive
%option reentrant
%option bison-bridge
%option noyywrap
%option nounput
%option noinput
%option never-interactive
%option nostdinit
%{
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include "css_parser.h"
#include "CSSGrammar.h"
#define VAL(a, b) { yylval->term.val = (a); yylval->term.type = TYPE_ ## b; }
#define us_strtof strtof
char *d;
%}
h [0-9a-f]
nonascii [\200-\377]
unicode \\{h}{1,6}[ \t\r\n\f]?
escape {unicode}|\\[ -~\200-\377]
nmstart [a-z]|{nonascii}|{escape}
nmchar [a-z0-9-]|{nonascii}|{escape}
string1 \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
string2 \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
ident [-]?{nmstart}{nmchar}*
name {nmchar}+
num [0-9]+|[0-9]*"."[0-9]+
string {string1}|{string2}
url ([!#$%&*-~]|{nonascii}|{escape})*
w [ \t\r\n\f]*
nl \n|\r\n|\r|\f
range \?{1,6}|{h}(\?{0,5}|{h}(\?{0,4}|{h}(\?{0,3}|{h}(\?{0,2}|{h}(\??|{h})))))
%%
[ \t\r\n\f]+ {return WHITESPACE;}
\/\*[^*]*\*+([^/][^*]*\*+)*\/ /* ignore comments */
"<!--" {return CDO;}
"-->" {return CDC;}
"~=" {return INCLUDES;}
"|=" {return DASHMATCH;}
{string} { yylval->string = vlc_css_unquotedunescaped(yytext); return STRING;}
{ident} { yylval->string = vlc_css_unescaped(yytext); return IDENT;}
"@font-face" {return FONT_FACE_SYM;}
"!{w}important" {return IMPORTANT_SYM;}
{num}em { VAL( us_strtof(yytext, &d), EMS ); return LENGTH;}
{num}ex { VAL( atoi(yytext), EXS ); return LENGTH;}
{num}px { VAL( atoi(yytext), PIXELS ); return LENGTH;}
{num}cm { VAL( us_strtof(yytext, &d) * 10, MILLIMETERS ); return LENGTH;}
{num}mm { VAL( atoi(yytext), MILLIMETERS ); return LENGTH;}
{num}in { VAL( us_strtof(yytext, &d) * 25.4, MILLIMETERS ); return LENGTH;}
{num}pt { VAL( us_strtof(yytext, &d), POINTS ); return LENGTH;}
{num}pc { VAL( us_strtof(yytext, &d), POINTS ); return LENGTH;}
{num}deg { VAL( us_strtof(yytext, &d), DEGREES ); return ANGLE;}
{num}rad { VAL( us_strtof(yytext, &d) * 0.0174533, DEGREES ); return ANGLE;}
{num}grad { VAL( us_strtof(yytext, &d) * 1.1111111, DEGREES ); return ANGLE;}
{num}ms { VAL( atoi(yytext), MILLISECONDS ); return TIME;}
{num}s { VAL( atoi(yytext) * 1000, MILLISECONDS ); return TIME;}
{num}Hz { VAL( atoi(yytext), HERTZ ); return FREQ;}
{num}kHz { VAL( atoi(yytext) * 1000, HERTZ ); return FREQ;}
{num}{ident} { VAL( 0, DIMENSION ); return DIMEN;}
{num}% { VAL( atoi(yytext), PERCENT ); return PERCENTAGE;}
{num} { VAL( us_strtof(yytext, &d), NONE ); return NUMBER;}
"url("{w}{string}{w}")" { yylval->string = vlc_css_unquotedunescaped(yytext); return URI;}
"url("{w}{url}{w}")" { yylval->string = vlc_css_unquotedunescaped(yytext); return URI;}
{ident}"(" { yylval->string = vlc_css_unescaped(yytext); return FUNCTION;}
"#"{ident} {yylval->string = vlc_css_unescaped(yytext); return IDSEL;}
"#"{name} {yylval->string = vlc_css_unescaped(yytext); return HASH;}
U\+{range} { yylval->string = strdup(yytext); return UNICODERANGE;}
U\+{h}{1,6}-{h}{1,6} { yylval->string = strdup(yytext); return UNICODERANGE;}
. {return *yytext;}

27
modules/codec/webvtt/css_bridge.h

@ -0,0 +1,27 @@
/*****************************************************************************
* css_bridge.h : CSS grammar/lexer bridge
*****************************************************************************
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#define yyconst const
typedef void* yyscan_t;
typedef struct yy_buffer_state *YY_BUFFER_STATE;
int yylex_init (yyscan_t* scanner);
YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes, int, yyscan_t yyscanner );
int yylex_destroy (yyscan_t yyscanner );
void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner);

517
modules/codec/webvtt/css_parser.c

@ -0,0 +1,517 @@
/*****************************************************************************
* css_parser.c : CSS parser
*****************************************************************************
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include "css_bridge.h"
#include "css_parser.h"
#include "CSSGrammar.h"
#include <ctype.h>
static void vlc_css_term_Debug( const vlc_css_term_t a, int depth );
static void vlc_css_expression_Debug( const vlc_css_expr_t *p_expr, int depth );
static void vlc_css_declarations_Debug( const vlc_css_declaration_t *p_decl, int depth );
static void vlc_css_selectors_Debug( const vlc_css_selector_t *p_sel, int depth );
static void vlc_css_rules_Debug( const vlc_css_rule_t *p_rule, int depth );
#define CHAIN_APPEND_IMPL(n, t) CHAIN_APPEND_DECL(n ,t)\
{\
t ** insert = &p_a->p_next;\
while( *insert ) insert = &((*insert)->p_next);\
*insert = p_b;\
}
void vlc_css_term_Clean( vlc_css_term_t a )
{
if( a.type >= TYPE_STRING )
free( a.psz );
if( a.type == TYPE_FUNCTION )
{
if( a.function )
vlc_css_expression_Delete( a.function );
}
}
static void vlc_css_term_Debug( const vlc_css_term_t a, int depth )
{
for(int i=0;i<depth;i++) printf(" ");
printf("term: ");
if( a.type >= TYPE_STRING )
{
printf("%x %s\n", a.type, a.psz);
if( a.type == TYPE_FUNCTION && a.function )
vlc_css_expression_Debug( a.function, depth + 1 );
}
else printf("%x %f\n", a.type, a.val);
}
bool vlc_css_expression_AddTerm( vlc_css_expr_t *p_expr,
char op, vlc_css_term_t a )
{
if( p_expr->i_count >= p_expr->i_alloc )
{
size_t i_realloc = (p_expr->i_alloc == 0) ? 1 : p_expr->i_alloc + 4;
void *reac = realloc( p_expr->seq, i_realloc * sizeof(p_expr->seq[0]) );
if( reac )
{
p_expr->seq = reac;
p_expr->i_alloc = i_realloc;
}
}
if( p_expr->i_count >= p_expr->i_alloc )
return false;
p_expr->seq[p_expr->i_count].op = op;
p_expr->seq[p_expr->i_count++].term = a;
return true;
}
void vlc_css_expression_Delete( vlc_css_expr_t *p_expr )
{
if( p_expr )
{
for(size_t i=0; i<p_expr->i_count; i++)
vlc_css_term_Clean( p_expr->seq[i].term );
free( p_expr->seq );
}
free( p_expr );
}
static void vlc_css_expression_Debug( const vlc_css_expr_t *p_expr, int depth )
{
if( p_expr )
{
for(int i=0;i<depth;i++) printf(" ");
printf("expression: \n");
for(size_t i=0; i<p_expr->i_count; i++)
vlc_css_term_Debug( p_expr->seq[i].term, depth + 1 );
}
}
vlc_css_expr_t * vlc_css_expression_New( vlc_css_term_t term )
{
vlc_css_expr_t *p_expr = calloc(1, sizeof(*p_expr));
if(!vlc_css_expression_AddTerm( p_expr, 0, term ))
{
free(p_expr);
p_expr = NULL;
}
return p_expr;
}
CHAIN_APPEND_IMPL(vlc_css_declarations_Append, vlc_css_declaration_t)
void vlc_css_declarations_Delete( vlc_css_declaration_t *p_decl )
{
while( p_decl )
{
vlc_css_declaration_t *p_next = p_decl->p_next;
vlc_css_expression_Delete( p_decl->expr );
free( p_decl->psz_property );
free( p_decl );
p_decl = p_next;
}
}
static void vlc_css_declarations_Debug( const vlc_css_declaration_t *p_decl, int depth )
{
while( p_decl )
{
for(int i=0;i<depth;i++) printf(" ");
printf("declaration: %s\n", p_decl->psz_property );
vlc_css_expression_Debug( p_decl->expr, depth + 1 );
p_decl = p_decl->p_next;
}
}
vlc_css_declaration_t * vlc_css_declaration_New( const char *psz )
{
vlc_css_declaration_t *p_decl = calloc(1, sizeof(*p_decl));
p_decl->psz_property = strdup(psz);
return p_decl;
}
CHAIN_APPEND_IMPL(vlc_css_selector_Append, vlc_css_selector_t)
void
vlc_css_selector_AddSpecifier( vlc_css_selector_t *p_sel, vlc_css_selector_t *p_spec )
{
*p_sel->specifiers.pp_append = p_spec;
while(p_spec)
{
p_sel->specifiers.pp_append = &p_spec->p_next;
p_spec = p_spec->p_next;
}
}
void vlc_css_selectors_Delete( vlc_css_selector_t *p_sel )
{
while( p_sel )
{
vlc_css_selector_t *p_next = p_sel->p_next;
free( p_sel->psz_name );
vlc_css_selectors_Delete( p_sel->specifiers.p_first );
vlc_css_selectors_Delete( p_sel->p_matchsel );
free( p_sel );
p_sel = p_next;
}
}
static void vlc_css_selectors_Debug( const vlc_css_selector_t *p_sel, int depth )
{
while( p_sel )
{
for(int i=0;i<depth;i++) printf(" "); printf("selector %c%s:\n", p_sel->combinator, p_sel->psz_name );
vlc_css_selectors_Debug( p_sel->p_matchsel, depth + 1 );
vlc_css_selectors_Debug( p_sel->specifiers.p_first, depth + 1 );
p_sel = p_sel->p_next;
}
}
vlc_css_selector_t * vlc_css_selector_New( int type, const char *psz )
{
vlc_css_selector_t *p_sel = calloc(1, sizeof(*p_sel));
p_sel->psz_name = strdup(psz);
p_sel->type = type;
p_sel->combinator = RELATION_SELF;
p_sel->specifiers.pp_append = &p_sel->specifiers.p_first;
return p_sel;
}
void vlc_css_rules_Delete( vlc_css_rule_t *p_rule )
{
while(p_rule)
{
vlc_css_rule_t *p_next = p_rule->p_next;
vlc_css_selectors_Delete( p_rule->p_selectors );
vlc_css_declarations_Delete( p_rule->p_declarations );
free(p_rule);
p_rule = p_next;
}
}
static void vlc_css_rules_Debug( const vlc_css_rule_t *p_rule, int depth )
{
int j = 0;
while(p_rule)
{
for(int i=0;i<depth;i++) printf(" "); printf("rule %d:\n", j++);
vlc_css_selectors_Debug( p_rule->p_selectors, depth + 1 );
vlc_css_declarations_Debug( p_rule->p_declarations, depth + 1 );
p_rule = p_rule->p_next;
}
}
vlc_css_rule_t * vlc_css_rule_New( void )
{
vlc_css_rule_t *p_rule = calloc(1, sizeof(*p_rule));
return p_rule;
}
void vlc_css_parser_AddRule( vlc_css_parser_t *p_parser,
vlc_css_rule_t *p_rule )
{
(*p_parser->rules.pp_append) = p_rule;
p_parser->rules.pp_append = &p_rule->p_next;
}
void vlc_css_parser_Debug( const vlc_css_parser_t *p_parser )
{
vlc_css_rules_Debug( p_parser->rules.p_first, 0 );
}
void vlc_css_parser_Clean( vlc_css_parser_t *p_parser )
{
vlc_css_rules_Delete( p_parser->rules.p_first );
}
void vlc_css_parser_Init( vlc_css_parser_t *p_parser )
{
memset(p_parser, 0, sizeof(vlc_css_parser_t));
p_parser->rules.pp_append = &p_parser->rules.p_first;
}
bool vlc_css_parser_ParseBytes( vlc_css_parser_t *p_parser, const uint8_t *p_data, size_t i_data )
{
yyscan_t yy;
yylex_init(&yy);
YY_BUFFER_STATE buf = yy_scan_bytes( (const char*) p_data, i_data, yy );
bool b_ret = !yyparse( yy, p_parser );
yy_delete_buffer( buf, yy );
yylex_destroy( yy );
return b_ret;
}
bool vlc_css_parser_ParseString( vlc_css_parser_t *p_parser, const char *psz_css )
{
yyscan_t yy;
yylex_init(&yy);
YY_BUFFER_STATE buf = yy_scan_string( psz_css, yy );
bool b_ret = !yyparse( yy, p_parser );
yy_delete_buffer( buf, yy );
yylex_destroy( yy );
return b_ret;
}
static int CodePointToUTF8( uint32_t ucs4, char *p )
{
/* adapted from codepoint conversion from strings.h */
if( ucs4 <= 0x7F )
{
p[0] = ucs4;
return 1;
}
else if( ucs4 <= 0x7FF )
{
p[0] = 0xC0 | (ucs4 >> 6);
p[1] = 0x80 | (ucs4 & 0x3F);
return 2;
}
else if( ucs4 <= 0xFFFF )
{
p[0] = 0xE0 | (ucs4 >> 12);
p[1] = 0x80 | ((ucs4 >> 6) & 0x3F);
p[2] = 0x80 | (ucs4 & 0x3F);
return 3;
}
else if( ucs4 <= 0x1FFFFF )
{
p[0] = 0xF0 | (ucs4 >> 18);
p[1] = 0x80 | ((ucs4 >> 12) & 0x3F);
p[2] = 0x80 | ((ucs4 >> 6) & 0x3F);
p[3] = 0x80 | (ucs4 & 0x3F);
return 4;
}
else if( ucs4 <= 0x3FFFFFF )
{
p[0] = 0xF8 | (ucs4 >> 24);
p[1] = 0x80 | ((ucs4 >> 18) & 0x3F);
p[2] = 0x80 | ((ucs4 >> 12) & 0x3F);
p[3] = 0x80 | ((ucs4 >> 6) & 0x3F);
p[4] = 0x80 | (ucs4 & 0x3F);
return 5;
}
else
{
p[0] = 0xFC | (ucs4 >> 30);
p[1] = 0x80 | ((ucs4 >> 24) & 0x3F);
p[2] = 0x80 | ((ucs4 >> 18) & 0x3F);
p[3] = 0x80 | ((ucs4 >> 12) & 0x3F);
p[4] = 0x80 | ((ucs4 >> 6) & 0x3F);
p[5] = 0x80 | (ucs4 & 0x3F);
return 6;
}
}
void vlc_css_unescape( char *psz )
{
if( !psz )
return;
char *r = psz;
char *w = psz;
while( *r )
{
if( *r == '\\' )
{
r++;
/* newlines */
if( *r == 0 )
{
break;
}
else if( strchr( "nfr", *r ) )
{
switch( r[0] )
{
case 'n':
*w++ = '\n';
r++;
break;
case 'r':
*w++ = '\r';
if( r[1] && r[1] == 'n' )
{
*w++ = '\n';
r++;
}
r++;
break;
case 'f':
*w++ = '\f';
r++;
break;
}
}
else if( isxdigit( *r ) )
{
const char *p_start = r;
int i;
for( i=0; i<6 && *r && isxdigit( *r ); i++ )
r++;
const char backup = *r;
*r = 0;
unsigned i_value = strtoul( p_start, NULL, 16 );
*r = backup;
if( i < 6 && *r && *r == ' ' )
r++;
w += CodePointToUTF8( i_value, w );
}
}
else
{
*w++ = *r++;
}
}
*w = 0;
}
char * vlc_css_unescaped( const char *psz )
{
char *psz_ret = strdup( psz );
vlc_css_unescape( psz_ret );
return psz_ret;
}
char * vlc_css_unquoted( const char *psz )
{
char *psz_ret;
if( *psz == '\'' || *psz == '\"' )
{
size_t i_len = strlen(psz);
if( psz[i_len - 1] == psz[0] )
psz_ret = strndup( psz + 1, i_len - 2 );
else
psz_ret = strdup( psz );
}
else
{
psz_ret = strdup( psz );
}
return psz_ret;
}
char * vlc_css_unquotedunescaped( const char *psz )
{
char *psz_ret = vlc_css_unquoted( psz );
if( psz_ret )
vlc_css_unescape( psz_ret );
return psz_ret;
}
#ifdef CSS_PARSER_DEBUG
static void css_properties_Debug( const vlc_css_declaration_t *p_decl )
{
printf("set %s to ", p_decl->psz_property);
for( size_t i=0; i<p_decl->expr->i_count; i++ )
{
printf("term %s ", p_decl->expr->seq[i].term.psz);
}
printf("\n");
}
void css_selector_Debug( const vlc_css_selector_t *p_sel )
{
printf("select its ");
switch( p_sel->combinator )
{
case RELATION_DESCENDENT:
printf("descendent");
break;
case RELATION_DIRECTADJACENT:
printf("adjacent");
break;
case RELATION_INDIRECTADJACENT:
printf("indirect adjacent");
break;
case RELATION_CHILD:
printf("child");
break;
case RELATION_SELF:
break;
}
printf(" nodes matching filter: ");
switch( p_sel->type )
{
case SELECTOR_SIMPLE:
printf("<%s>\n", p_sel->psz_name);
break;
case SELECTOR_PSEUDOCLASS:
printf(":%s\n", p_sel->psz_name);
break;
case SELECTOR_PSEUDOELEMENT:
printf("::%s\n", p_sel->psz_name);
break;
case SPECIFIER_ID:
printf("%s\n", p_sel->psz_name);
break;
case SPECIFIER_CLASS:
printf(".%s\n", p_sel->psz_name);
break;
case SPECIFIER_ATTRIB:
printf("[%s]\n", p_sel->psz_name);
break;
}
}
void css_rule_Debug( const vlc_css_rule_t *p_rule )
{
if( p_rule == NULL )
return;
printf("add for rule nodes:\n");
for( const vlc_css_selector_t *p_sel = p_rule->p_selectors;
p_sel; p_sel = p_sel->p_next )
{
css_selector_Debug( p_sel );
for( const vlc_css_selector_t *p_spec = p_sel->specifiers.p_first;
p_spec; p_spec = p_spec->p_next )
css_selector_Debug( p_spec );
if( p_sel->p_next )
printf("add nodes\n");
}
for( const vlc_css_declaration_t *p_decl = p_rule->p_declarations;
p_decl; p_decl = p_decl->p_next )
{
css_properties_Debug( p_decl );
}
}
#endif

182
modules/codec/webvtt/css_parser.h

@ -0,0 +1,182 @@
/*****************************************************************************
* css_parser.h : CSS parser interface
*****************************************************************************
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef CSS_PARSER_H
#define CSS_PARSER_H
//#define YYDEBUG 1
//#define CSS_PARSER_DEBUG
typedef struct vlc_css_parser_t vlc_css_parser_t;
typedef struct vlc_css_selector_t vlc_css_selector_t;
typedef struct vlc_css_declaration_t vlc_css_declaration_t;
typedef struct vlc_css_rule_t vlc_css_rule_t;
typedef struct vlc_css_expr_t vlc_css_expr_t;
typedef struct
{
float val;
char *psz;
vlc_css_expr_t *function;
enum
{
TYPE_NONE = 0,
TYPE_EMS,
TYPE_EXS,
TYPE_PIXELS,
TYPE_POINTS,
TYPE_MILLIMETERS,
TYPE_PERCENT,
TYPE_MILLISECONDS,
TYPE_HERTZ,
TYPE_DEGREES,
TYPE_DIMENSION,
TYPE_STRING = 0x20,
TYPE_FUNCTION,
TYPE_IDENTIFIER,
TYPE_HEXCOLOR,
TYPE_UNICODERANGE,
TYPE_URI,
} type;
} vlc_css_term_t;
struct vlc_css_expr_t
{
struct
{
char op;
vlc_css_term_t term;
} *seq;
size_t i_alloc;
size_t i_count;
};
struct vlc_css_declaration_t
{
char *psz_property;
vlc_css_expr_t *expr;
vlc_css_declaration_t *p_next;
};
enum vlc_css_match_e
{
MATCH_EQUALS,
MATCH_INCLUDES,
MATCH_DASHMATCH,
MATCH_BEGINSWITH,
MATCH_ENDSWITH,
MATCH_CONTAINS,
};
enum vlc_css_relation_e
{
RELATION_SELF = 0,
RELATION_DESCENDENT = ' ',
RELATION_DIRECTADJACENT = '+',
RELATION_INDIRECTADJACENT = '~',
RELATION_CHILD = '>',
};
struct vlc_css_selector_t
{
char *psz_name;
enum
{
SELECTOR_SIMPLE = 0,
SELECTOR_PSEUDOCLASS,
SELECTOR_PSEUDOELEMENT,
/* SELECTOR_PSEUDONTHCHILD,
SELECTOR_PSEUDONTHTYPE,
SELECTOR_PSEUDONTHLASTCHILD,
SELECTOR_PSEUDONTHLASTTYPE,*/
SPECIFIER_ID,
SPECIFIER_CLASS,
SPECIFIER_ATTRIB,
} type;
struct
{
vlc_css_selector_t *p_first;
vlc_css_selector_t **pp_append;
} specifiers;
enum vlc_css_match_e match;
vlc_css_selector_t *p_matchsel;
enum vlc_css_relation_e combinator;
vlc_css_selector_t *p_next;
};
struct vlc_css_rule_t
{
bool b_valid;
vlc_css_selector_t *p_selectors;
vlc_css_declaration_t *p_declarations;
vlc_css_rule_t *p_next;
};
struct vlc_css_parser_t
{
struct
{
vlc_css_rule_t *p_first;
vlc_css_rule_t **pp_append;
} rules;
};
#define CHAIN_APPEND_DECL(n, t) void n( t *p_a, t *p_b )
void vlc_css_term_Clean( vlc_css_term_t a );
bool vlc_css_expression_AddTerm( vlc_css_expr_t *p_expr, char op, vlc_css_term_t a );
void vlc_css_expression_Delete( vlc_css_expr_t *p_expr );
vlc_css_expr_t * vlc_css_expression_New( vlc_css_term_t term );
CHAIN_APPEND_DECL(vlc_css_declarations_Append, vlc_css_declaration_t);
void vlc_css_declarations_Delete( vlc_css_declaration_t *p_decl );
vlc_css_declaration_t * vlc_css_declaration_New( const char *psz );
CHAIN_APPEND_DECL(vlc_css_selector_Append, vlc_css_selector_t);
void vlc_css_selector_AddSpecifier( vlc_css_selector_t *p_sel, vlc_css_selector_t *p_spec );
void vlc_css_selectors_Delete( vlc_css_selector_t *p_sel );
vlc_css_selector_t * vlc_css_selector_New( int type, const char *psz );
void vlc_css_rules_Delete( vlc_css_rule_t *p_rule );
vlc_css_rule_t * vlc_css_rule_New( void );
void vlc_css_parser_AddRule( vlc_css_parser_t *p_parser, vlc_css_rule_t *p_rule );
void vlc_css_parser_Debug( const vlc_css_parser_t *p_parser );
void vlc_css_parser_Clean( vlc_css_parser_t *p_parser );
void vlc_css_parser_Init( vlc_css_parser_t *p_parser );
bool vlc_css_parser_ParseBytes( vlc_css_parser_t *p_parser, const uint8_t *, size_t );
bool vlc_css_parser_ParseString( vlc_css_parser_t *p_parser, const char * );
void vlc_css_unescape( char *psz );
char * vlc_css_unquoted( const char *psz );
char * vlc_css_unescaped( const char *psz );
char * vlc_css_unquotedunescaped( const char *psz );
# ifdef CSS_PARSER_DEBUG
void css_selector_Debug( const vlc_css_selector_t *p_sel );
void css_rule_Debug( const vlc_css_rule_t *p_rule );
# endif
#endif
Loading…
Cancel
Save