diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h index 889017d2a..1eb6d0bb8 100644 --- a/include/grub/script_sh.h +++ b/include/grub/script_sh.h @@ -157,8 +157,10 @@ struct grub_lexer_param int merge_start; int merge_end; - /* Text of current token. */ + /* Part of a multi-part token. */ char *text; + unsigned used; + unsigned size; /* Type of text. */ grub_script_arg_type_t type; @@ -168,12 +170,9 @@ struct grub_lexer_param /* Flex scanner buffer. */ void *buffer; - - /* Length of current token text. */ - unsigned size; }; -#define GRUB_LEXER_TOKEN_MAX 256 +#define GRUB_LEXER_INITIAL_TEXT_SIZE 32 #define GRUB_LEXER_RECORD_INCREMENT 256 /* State of the parser as passes to the parser. */ diff --git a/script/lexer.c b/script/lexer.c index d43c9f157..179e1d1d2 100644 --- a/script/lexer.c +++ b/script/lexer.c @@ -213,7 +213,8 @@ grub_script_lexer_init (struct grub_parser_param *parser, char *script, if (!lexerstate) return 0; - lexerstate->text = grub_malloc (GRUB_LEXER_TOKEN_MAX); + lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE; + lexerstate->text = grub_malloc (lexerstate->size); if (!lexerstate->text) { grub_free (lexerstate); @@ -301,7 +302,7 @@ grub_script_yylex (union YYSTYPE *value, do { /* Empty lexerstate->text. */ - lexerstate->size = 0; + lexerstate->used = 1; lexerstate->text[0] = '\0'; token = yylex (value, lexerstate->yyscanner); @@ -311,7 +312,6 @@ grub_script_yylex (union YYSTYPE *value, /* Merging feature uses lexerstate->text instead of yytext. */ if (lexerstate->merge_start) { - lexerstate->text[lexerstate->size] = '\0'; str = lexerstate->text; type = lexerstate->type; } diff --git a/script/yylex.l b/script/yylex.l index 4f6c00e21..c19461c9a 100644 --- a/script/yylex.l +++ b/script/yylex.l @@ -37,31 +37,19 @@ grub_printf ("fatal error: %s\n", msg); \ } while (0) -#define PUSH(c) \ - do { \ - if (yyextra->lexerstate->size >= GRUB_LEXER_TOKEN_MAX - 1) \ - grub_script_yyerror (yyextra, "token too long"); \ - else \ - yyextra->lexerstate->text[yyextra->lexerstate->size++] = c; \ - } while (0) - -#define COPY(str) \ - do { \ - char *ptr = str; \ - while (*ptr && ! yyextra->err) \ - { \ - PUSH (*ptr); \ - ptr++; \ - } \ +#define COPY(str, hint) \ + do { \ + copy_string (yyextra, str, hint); \ } while (0) -#define RECORD \ - do { \ - grub_script_lexer_record (yyextra, yytext); \ + +#define RECORD \ + do { \ + grub_script_lexer_record (yyextra, yytext); \ } while (0) -#define ARG(t) \ - do { \ +#define ARG(t) \ + do { \ yyextra->lexerstate->type = t; \ return GRUB_PARSER_TOKEN_WORD; \ } while (0) @@ -73,6 +61,8 @@ static void grub_lexer_yyfree (void *, yyscan_t yyscanner); static void* grub_lexer_yyalloc (yy_size_t, yyscan_t yyscanner); static void* grub_lexer_yyrealloc (void*, yy_size_t, yyscan_t yyscanner); +static void copy_string (struct grub_parser_param *, const char *, + unsigned hint); %} @@ -201,52 +191,47 @@ WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+ RECORD; /* resplit yytext */ yypush_buffer_state (YY_CURRENT_BUFFER, yyscanner); - if (yy_scan_string (yytext, yyscanner)) - { - yyextra->lexerstate->merge_start = 1; - yy_push_state (SPLIT, yyscanner); - } - else - { - grub_script_yyerror (yyextra, 0); - yypop_buffer_state (yyscanner); - return GRUB_PARSER_TOKEN_WORD; - } + if (yy_scan_string (yytext, yyscanner)) + { + yyextra->lexerstate->merge_start = 1; + yy_push_state (SPLIT, yyscanner); + } + else + { + grub_script_yyerror (yyextra, 0); + yypop_buffer_state (yyscanner); + return GRUB_PARSER_TOKEN_WORD; + } } .|\n { grub_script_yyerror (yyextra, "unrecognized token"); - return GRUB_PARSER_TOKEN_BAD; + return GRUB_PARSER_TOKEN_BAD; } /* Split word into multiple args */ { - \\. { PUSH (yytext[1]); } + \\. { COPY (yytext + 1, yyleng - 1); } \" { - yy_push_state (DQUOTE, yyscanner); + yy_push_state (DQUOTE, yyscanner); ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); } \' { - yy_push_state (SQUOTE, yyscanner); + yy_push_state (SQUOTE, yyscanner); ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); } \$ { yy_push_state (VAR, yyscanner); ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); - } - {CHAR} { PUSH (yytext[0]); } - .|\n { - /* This cannot happen. */ - grub_script_yyerror (yyextra, "internal error: unexpected characters in a word"); - return GRUB_PARSER_TOKEN_BAD; - } - + } + \\ | + [^\"\'$\\]+ { COPY (yytext, yyleng); } <> { - yy_pop_state (yyscanner); - yypop_buffer_state (yyscanner); - yyextra->lexerstate->merge_end = 1; + yy_pop_state (yyscanner); + yypop_buffer_state (yyscanner); + yyextra->lexerstate->merge_end = 1; ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); } } @@ -255,23 +240,23 @@ WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+ \? | {DIGITS} | {NAME} { - COPY (yytext); - yy_pop_state (yyscanner); - if (YY_START == SPLIT) - ARG (GRUB_SCRIPT_ARG_TYPE_VAR); - else - ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); - } + COPY (yytext, yyleng); + yy_pop_state (yyscanner); + if (YY_START == SPLIT) + ARG (GRUB_SCRIPT_ARG_TYPE_VAR); + else + ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); + } \{\?\} | \{{DIGITS}\} | \{{NAME}\} { yytext[yyleng - 1] = '\0'; - COPY (yytext + 1); + COPY (yytext + 1, yyleng - 2); yy_pop_state (yyscanner); - if (YY_START == SPLIT) - ARG (GRUB_SCRIPT_ARG_TYPE_VAR); - else - ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); + if (YY_START == SPLIT) + ARG (GRUB_SCRIPT_ARG_TYPE_VAR); + else + ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); } .|\n { return GRUB_PARSER_TOKEN_BAD; } } @@ -279,33 +264,34 @@ WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+ { \' { yy_pop_state (yyscanner); - ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR); + ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR); } - (.|\n) { PUSH (yytext[0]); } + [^\']+ { COPY (yytext, yyleng); } } { \" { yy_pop_state (yyscanner); - ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); + ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); } \$ { yy_push_state (VAR, yyscanner); - ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); - } - \\\\ { PUSH ('\\'); } - \\\" { PUSH ('\"'); } + ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); + } + \\\\ { COPY ("\\", 1); } + \\\" { COPY ("\"", 1); } \\\n { /* ignore */ } - (.|\n) { PUSH (yytext[0]); } + [^\"$\\\n]+ { COPY (yytext, yyleng); } + (.|\n) { COPY (yytext, yyleng); } } <> { - yypop_buffer_state (yyscanner); - if (! grub_script_lexer_yywrap (yyextra)) - { - yyextra->lexerstate->eof = 1; - return GRUB_PARSER_TOKEN_EOF; - } + yypop_buffer_state (yyscanner); + if (! grub_script_lexer_yywrap (yyextra)) + { + yyextra->lexerstate->eof = 1; + return GRUB_PARSER_TOKEN_EOF; + } } %% @@ -329,3 +315,26 @@ grub_lexer_yyrealloc (void *ptr, yy_size_t size, return grub_realloc (ptr, size); } +static void copy_string (struct grub_parser_param *parser, const char *str, unsigned hint) +{ + int len; + int size; + char *ptr; + + len = hint ? hint : grub_strlen (str); + if (parser->lexerstate->used + len >= parser->lexerstate->size) + { + size = parser->lexerstate->size * 2; + ptr = grub_realloc (parser->lexerstate->text, size); + if (!ptr) + { + grub_script_yyerror (parser, 0); + return; + } + + parser->lexerstate->text = ptr; + parser->lexerstate->size = size; + } + grub_strcpy (parser->lexerstate->text + parser->lexerstate->used - 1, str); + parser->lexerstate->used += len; +}