lexer-rewrite rebase commit

This commit is contained in:
BVK Chaitanya 2010-01-22 19:07:27 +05:30
parent bf86e59a76
commit 547e494f1b
25 changed files with 1214 additions and 652 deletions

70
ChangeLog.lexer-rewrite Normal file
View file

@ -0,0 +1,70 @@
2010-01-10 BVK Chaitanya <bvk.groups@gmail.com>
* conf/any-emu.rmk: Build rule updates.
* conf/common.rmk: Likewise.
* conf/i386-coreboot.rmk: Likewise.
* conf/i386-efi.rmk: Likewise.
* conf/i386-ieee1275.rmk: Likewise.
* conf/i386-pc.rmk: Likewise.
* conf/powerpc-ieee1275.rmk: Likewise.
* conf/x86_64-efi.rmk: Likewise.
* configure.ac: Configure check for flex.
* include/grub/script_sh.h (grub_script_arg_type_t): More argument
types.
(grub_lexer_param): Struct member updates.
(grub_parser_param): Likewise.
(GRUB_LEXER_TOKEN_MAX): Maximum token size.
(GRUB_LEXER_RECORD_INCREMENT): Memory increments' size.
(grub_script_lexer_init): Prototype update.
(grub_script_lexer_record_start): Likewise.
(grub_script_lexer_record_stop): Likewise.
(grub_script_lexer_yywrap): New function prototype.
(grub_script_lexer_fini): Likewise.
(grub_script_execute_argument_to_string): Removed by...
(grub_script_execute_argument_to_argv): ...better version.
* script/execute.c (ROUND_UPTO): New macro.
(grub_script_execute_cmdline): Out of memory fixes.
(grub_script_execute_menuentry): Likewise.
(grub_script_execute_argument_to_string): Removed. Update all
users by...
(grub_script_execute_argument_to_argv): ...better version.
* script/function.c (grub_script_function_create): Use
grub_script_execute_argument_to_argv instead of
grub_script_execute_argument_to_string.
* script/lexer.c (check_varstate): Removed.
(check_textstate): Removed.
(grub_script_lexer_record_start): Likewise.
(grub_script_lexer_record_stop): Likewise.
(recordchar): Replaced with...
(grub_script_lexer_record): ...new function.
(nextchar): Removed.
(grub_script_lexer_init): Rewritten.
(grub_script_yylex): Rewritten.
(append_newline): New function.
(grub_script_lexer_yywrap): New function.
(grub_script_lexer_fini): New function.
(grub_script_yyerror): Sets error flag.
* script/yylex.l: New file.
(grub_lexer_yyfree): Wrapper for flex yyffre.
(grub_lexer_yyalloc): Likewise.
(grub_lexer_yyrealloc): Likewise.
* script/parser.y: Refactored.
* script/script.c (grub_script_arg_add): Out of memory fixes.
(grub_script_add_arglist): Likewise.
(grub_script_create_cmdline): Likewise.
(grub_script_create_cmdmenu): Likewise.
(grub_script_add_cmd): Likewise.
(grub_script_parse): Use grub_script_lexer_fini to deallocated.
* util/grub-script-check.c (grub_script_execute_menuentry): Remove
unnecessary code.
* tests/grub_script_echo1.in: New testcase.
* tests/grub_script_vars1.in: New testcase.
* tests/grub_script_echo_keywords.in: New testcase.

View file

@ -1,7 +1,7 @@
# -*- makefile -*- # -*- makefile -*-
# Used by various components. These rules need to precede them. # Used by various components. These rules need to precede them.
script/lexer.c_DEPENDENCIES = grub_script.tab.h script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h
sbin_UTILITIES += grub-emu sbin_UTILITIES += grub-emu
util/grub-emu.c_DEPENDENCIES = grub_emu_init.h util/grub-emu.c_DEPENDENCIES = grub_emu_init.h
@ -33,6 +33,8 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \
commands/terminal.c normal/context.c lib/charset.c \ commands/terminal.c normal/context.c lib/charset.c \
script/main.c script/execute.c script/function.c \ script/main.c script/execute.c script/function.c \
script/lexer.c script/script.c grub_script.tab.c \ script/lexer.c script/script.c grub_script.tab.c \
grub_script.yy.c \
\
partmap/amiga.c partmap/apple.c partmap/msdos.c partmap/sun.c \ partmap/amiga.c partmap/apple.c partmap/msdos.c partmap/sun.c \
partmap/acorn.c partmap/gpt.c \ partmap/acorn.c partmap/gpt.c \
\ \
@ -99,5 +101,11 @@ grub_script.tab.c grub_script.tab.h: script/parser.y
$(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/script/parser.y $(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/script/parser.y
DISTCLEANFILES += grub_script.tab.c grub_script.tab.h DISTCLEANFILES += grub_script.tab.c grub_script.tab.h
grub_script.yy.c grub_script.yy.h: script/yylex.l
$(LEX) -o grub_script.yy.c --header-file=grub_script.yy.h $(srcdir)/script/yylex.l
sed -i 's/^#include.*\(<stdio\.h>\|<string\.h>\|<errno\.h>\|<stdlib\.h>\|<unistd\.h>\)//g' grub_script.yy.h
sed -i 's/^#include.*\(<stdio\.h>\|<string\.h>\|<errno\.h>\|<stdlib\.h>\|<unistd\.h>\)//g' grub_script.yy.c
DISTCLEANFILES += grub_script.yy.c grub_script.yy.h
bin_UTILITIES += grub-bin2h bin_UTILITIES += grub-bin2h
grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c

View file

@ -91,13 +91,21 @@ grub_mkrelpath_SOURCES = gnulib/progname.c util/grub-mkrelpath.c util/misc.c
bin_UTILITIES += grub-bin2h bin_UTILITIES += grub-bin2h
grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c
# For the lexer.
grub_script.yy.c grub_script.yy.h: script/yylex.l
$(LEX) -o grub_script.yy.c --header-file=grub_script.yy.h $(srcdir)/script/yylex.l
sed -i 's/^#include.*\(<stdio\.h>\|<string\.h>\|<errno\.h>\|<stdlib\.h>\|<unistd\.h>\)//g' grub_script.yy.h
sed -i 's/^#include.*\(<stdio\.h>\|<string\.h>\|<errno\.h>\|<stdlib\.h>\|<unistd\.h>\)//g' grub_script.yy.c
DISTCLEANFILES += grub_script.yy.c grub_script.yy.h
# For grub-script-check. # For grub-script-check.
bin_UTILITIES += grub-script-check bin_UTILITIES += grub-script-check
util/grub-script-check.c_DEPENDENCIES = grub_script_check_init.h util/grub-script-check.c_DEPENDENCIES = grub_script_check_init.h
grub_script_check_SOURCES = gnulib/progname.c util/grub-script-check.c util/misc.c \ grub_script_check_SOURCES = gnulib/progname.c util/grub-script-check.c util/misc.c \
script/main.c script/script.c script/function.c script/lexer.c \ script/main.c script/script.c script/function.c script/lexer.c \
kern/handler.c kern/err.c kern/parser.c kern/list.c \ kern/handler.c kern/err.c kern/parser.c kern/list.c \
kern/misc.c kern/env.c grub_script_check_init.c grub_script.tab.c kern/misc.c kern/env.c grub_script_check_init.c grub_script.tab.c \
grub_script.yy.c
# For the parser. # For the parser.
grub_script.tab.c grub_script.tab.h: script/parser.y grub_script.tab.c grub_script.tab.h: script/parser.y
@ -594,7 +602,7 @@ normal_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For sh.mod. # For sh.mod.
sh_mod_SOURCES = script/main.c script/script.c script/execute.c \ sh_mod_SOURCES = script/main.c script/script.c script/execute.c \
script/function.c script/lexer.c grub_script.tab.c script/function.c script/lexer.c grub_script.tab.c grub_script.yy.c
sh_mod_CFLAGS = $(COMMON_CFLAGS) sh_mod_CFLAGS = $(COMMON_CFLAGS)
sh_mod_LDFLAGS = $(COMMON_LDFLAGS) sh_mod_LDFLAGS = $(COMMON_LDFLAGS)

View file

@ -5,7 +5,7 @@ COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32
COMMON_LDFLAGS = -m32 -nostdlib COMMON_LDFLAGS = -m32 -nostdlib
# Used by various components. These rules need to precede them. # Used by various components. These rules need to precede them.
script/lexer.c_DEPENDENCIES = grub_script.tab.h script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h
# Images. # Images.

View file

@ -5,7 +5,7 @@ COMMON_CFLAGS = -fno-builtin -m32
COMMON_LDFLAGS = -melf_i386 -nostdlib COMMON_LDFLAGS = -melf_i386 -nostdlib
# Used by various components. These rules need to precede them. # Used by various components. These rules need to precede them.
script/lexer.c_DEPENDENCIES = grub_script.tab.h script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h
# Utilities. # Utilities.
bin_UTILITIES = grub-mkimage bin_UTILITIES = grub-mkimage

View file

@ -5,7 +5,7 @@ COMMON_CFLAGS = -ffreestanding -mrtd -mregparm=3
COMMON_LDFLAGS = -nostdlib COMMON_LDFLAGS = -nostdlib
# Used by various components. These rules need to precede them. # Used by various components. These rules need to precede them.
script/lexer.c_DEPENDENCIES = grub_script.tab.h script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h
# Images. # Images.
pkglib_PROGRAMS = kernel.img pkglib_PROGRAMS = kernel.img

View file

@ -7,7 +7,7 @@ COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32
COMMON_LDFLAGS = -m32 -nostdlib COMMON_LDFLAGS = -m32 -nostdlib
# Used by various components. These rules need to precede them. # Used by various components. These rules need to precede them.
script/lexer.c_DEPENDENCIES = grub_script.tab.h script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h
# Images. # Images.
pkglib_IMAGES = boot.img cdboot.img diskboot.img kernel.img lnxboot.img \ pkglib_IMAGES = boot.img cdboot.img diskboot.img kernel.img lnxboot.img \

View file

@ -6,7 +6,7 @@ COMMON_CFLAGS = -ffreestanding
COMMON_LDFLAGS += -nostdlib COMMON_LDFLAGS += -nostdlib
# Used by various components. These rules need to precede them. # Used by various components. These rules need to precede them.
script/lexer.c_DEPENDENCIES = grub_script.tab.h script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h
# Images. # Images.

View file

@ -6,7 +6,7 @@ COMMON_CFLAGS = -ffreestanding -m64 -mno-app-regs
COMMON_LDFLAGS = -melf64_sparc -nostdlib -mno-relax COMMON_LDFLAGS = -melf64_sparc -nostdlib -mno-relax
# Used by various components. These rules need to precede them. # Used by various components. These rules need to precede them.
script/lexer.c_DEPENDENCIES = grub_script.tab.h script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h
# Images. # Images.
pkglib_IMAGES = boot.img diskboot.img kernel.img pkglib_IMAGES = boot.img diskboot.img kernel.img

View file

@ -37,12 +37,28 @@ example_scripted_test_SOURCES = tests/example_scripted_test.in
check_SCRIPTS += example_grub_script_test check_SCRIPTS += example_grub_script_test
example_grub_script_test_SOURCES = tests/example_grub_script_test.in example_grub_script_test_SOURCES = tests/example_grub_script_test.in
#
# Rules for real tests
#
check_SCRIPTS += grub_script_echo1
grub_script_echo1_SOURCES = tests/grub_script_echo1.in
check_SCRIPTS += grub_script_echo_keywords
grub_script_echo_keywords_SOURCES = tests/grub_script_echo_keywords.in
check_SCRIPTS += grub_script_vars1
grub_script_vars1_SOURCES = tests/grub_script_vars1.in
# List of tests to execute on "make check" # List of tests to execute on "make check"
SCRIPTED_TESTS = example_scripted_test # SCRIPTED_TESTS = example_scripted_test
SCRIPTED_TESTS += example_grub_script_test # SCRIPTED_TESTS += example_grub_script_test
UNIT_TESTS = example_unit_test # UNIT_TESTS = example_unit_test
FUNCTIONAL_TESTS = example_functional_test.mod # FUNCTIONAL_TESTS = example_functional_test.mod
SCRIPTED_TESTS = grub_script_echo1
SCRIPTED_TESTS += grub_script_echo_keywords
SCRIPTED_TESTS += grub_script_vars1
# dependencies between tests and testing-tools # dependencies between tests and testing-tools
$(SCRIPTED_TESTS): grub-shell grub-shell-tester $(SCRIPTED_TESTS): grub-shell grub-shell-tester

View file

@ -5,7 +5,7 @@ COMMON_CFLAGS = -fno-builtin -m64
COMMON_LDFLAGS = -melf_x86_64 -nostdlib COMMON_LDFLAGS = -melf_x86_64 -nostdlib
# Used by various components. These rules need to precede them. # Used by various components. These rules need to precede them.
script/lexer.c_DEPENDENCIES = grub_script.tab.h script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h
# Utilities. # Utilities.
bin_UTILITIES = grub-mkimage bin_UTILITIES = grub-mkimage

View file

@ -163,6 +163,11 @@ if test "x$CMP" = x; then
AC_MSG_ERROR([cmp is not found]) AC_MSG_ERROR([cmp is not found])
fi fi
AC_CHECK_PROGS([LEX], [flex])
if test "x$LEX" = x; then
AC_MSG_ERROR([flex is not found])
fi
AC_CHECK_PROGS([YACC], [bison]) AC_CHECK_PROGS([YACC], [bison])
if test "x$YACC" = x; then if test "x$YACC" = x; then
AC_MSG_ERROR([bison is not found]) AC_MSG_ERROR([bison is not found])

View file

@ -1,7 +1,7 @@
/* normal_parser.h */ /* normal_parser.h */
/* /*
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. * Copyright (C) 2005,2007,2009,2010 Free Software Foundation, Inc.
* *
* GRUB is free software: you can redistribute it and/or modify * GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -45,8 +45,11 @@ struct grub_script
typedef enum typedef enum
{ {
GRUB_SCRIPT_ARG_TYPE_STR, GRUB_SCRIPT_ARG_TYPE_VAR,
GRUB_SCRIPT_ARG_TYPE_VAR GRUB_SCRIPT_ARG_TYPE_TEXT,
GRUB_SCRIPT_ARG_TYPE_DQVAR,
GRUB_SCRIPT_ARG_TYPE_DQSTR,
GRUB_SCRIPT_ARG_TYPE_SQSTR
} grub_script_arg_type_t; } grub_script_arg_type_t;
/* A part of an argument. */ /* A part of an argument. */
@ -121,12 +124,6 @@ struct grub_script_cmd_menuentry
/* State of the lexer as passed to the lexer. */ /* State of the lexer as passed to the lexer. */
struct grub_lexer_param struct grub_lexer_param
{ {
/* Set to 0 when the lexer is done. */
int done;
/* State of the state machine. */
grub_parser_state_t state;
/* Function used by the lexer to get a new line when more input is /* Function used by the lexer to get a new line when more input is
expected, but not available. */ expected, but not available. */
grub_reader_getline_t getline; grub_reader_getline_t getline;
@ -137,10 +134,6 @@ struct grub_lexer_param
depleted. */ depleted. */
int refs; int refs;
/* The character stream that has to be parsed. */
char *script;
char *newscript; /* XXX */
/* While walking through the databuffer, `record' the characters to /* While walking through the databuffer, `record' the characters to
this other buffer. It can be used to edit the menu entry at a this other buffer. It can be used to edit the menu entry at a
later moment. */ later moment. */
@ -157,13 +150,32 @@ struct grub_lexer_param
/* Size of RECORDING. */ /* Size of RECORDING. */
int recordlen; int recordlen;
/* The token that is already parsed but not yet returned. */ /* End of file reached. */
int tokenonhold; int eof;
/* Was the last token a newline? */ /* Merge multiple word tokens. */
int was_newline; int merge_start;
int merge_end;
/* Text of current token. */
char *text;
/* Type of text. */
grub_script_arg_type_t type;
/* Flex scanner. */
void *yyscanner;
/* Flex scanner buffer. */
void *buffer;
/* Length of current token text. */
unsigned size;
}; };
#define GRUB_LEXER_TOKEN_MAX 256
#define GRUB_LEXER_RECORD_INCREMENT 256
/* State of the parser as passes to the parser. */ /* State of the parser as passes to the parser. */
struct grub_parser_param struct grub_parser_param
{ {
@ -223,12 +235,16 @@ void grub_script_free (struct grub_script *script);
struct grub_script *grub_script_create (struct grub_script_cmd *cmd, struct grub_script *grub_script_create (struct grub_script_cmd *cmd,
struct grub_script_mem *mem); struct grub_script_mem *mem);
struct grub_lexer_param *grub_script_lexer_init (char *s, struct grub_lexer_param *grub_script_lexer_init (struct grub_parser_param *parser,
char *script,
grub_reader_getline_t getline); grub_reader_getline_t getline);
void grub_script_lexer_fini (struct grub_lexer_param *);
void grub_script_lexer_ref (struct grub_lexer_param *); void grub_script_lexer_ref (struct grub_lexer_param *);
void grub_script_lexer_deref (struct grub_lexer_param *); void grub_script_lexer_deref (struct grub_lexer_param *);
void grub_script_lexer_record_start (struct grub_lexer_param *); void grub_script_lexer_record_start (struct grub_parser_param *);
char *grub_script_lexer_record_stop (struct grub_lexer_param *); char *grub_script_lexer_record_stop (struct grub_parser_param *);
int grub_script_lexer_yywrap (struct grub_parser_param *);
void grub_script_lexer_record (struct grub_parser_param *, char *);
/* Functions to track allocated memory. */ /* Functions to track allocated memory. */
struct grub_script_mem *grub_script_mem_record (struct grub_parser_param *state); struct grub_script_mem *grub_script_mem_record (struct grub_parser_param *state);
@ -284,7 +300,7 @@ int grub_script_function_iterate (int (*iterate) (grub_script_function_t));
int grub_script_function_call (grub_script_function_t func, int grub_script_function_call (grub_script_function_t func,
int argc, char **args); int argc, char **args);
char * char **
grub_script_execute_argument_to_string (struct grub_script_arg *arg); grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist);
#endif /* ! GRUB_NORMAL_PARSER_HEADER */ #endif /* ! GRUB_NORMAL_PARSER_HEADER */

View file

@ -1,7 +1,7 @@
/* execute.c -- Execute a GRUB script. */ /* execute.c -- Execute a GRUB script. */
/* /*
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2007,2008,2009 Free Software Foundation, Inc. * Copyright (C) 2005,2007,2008,2009,2010 Free Software Foundation, Inc.
* *
* GRUB is free software: you can redistribute it and/or modify * GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -35,49 +35,146 @@ grub_script_execute_cmd (struct grub_script_cmd *cmd)
return cmd->exec (cmd); return cmd->exec (cmd);
} }
/* Parse ARG and return the textual representation. Add strings are #define ROUND_UPTO(sz,up) (((sz) + (up) - 1) / (up) * (up))
concatenated and all values of the variables are filled in. */
char *
grub_script_execute_argument_to_string (struct grub_script_arg *arg)
{
int size = 0;
char *val;
char *chararg;
struct grub_script_arg *argi;
/* First determine the size of the argument. */ /* Expand arguments in ARGLIST into multiple arguments. */
for (argi = arg; argi; argi = argi->next) char **
grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist)
{ {
if (argi->type == 1) int i;
int oom;
int argc;
int empty;
char *ptr;
char **argv;
char *value;
struct grub_script_arg *arg;
auto void push (char *str);
void push (char *str)
{ {
val = grub_env_get (argi->str); char **p;
if (val)
size += grub_strlen (val); if (oom)
} return;
p = grub_realloc (argv, ROUND_UPTO (sizeof(char*) * (argc + 1), 32));
if (!p)
oom = 1;
else else
size += grub_strlen (argi->str); {
p[argc++] = str;
argv = p;
}
} }
/* Create the argument. */ auto char* append (const char *str, grub_size_t nchar);
chararg = grub_malloc (size + 1); char* append (const char *str, grub_size_t nchar)
if (! chararg) {
int len;
int old;
char *p;
if (oom || !str)
return 0; return 0;
*chararg = '\0'; len = nchar ?: grub_strlen (str);
/* First determine the size of the argument. */ old = argv[argc - 1] ? grub_strlen (argv[argc - 1]) : 0;
for (argi = arg; argi; argi = argi->next) p = grub_realloc (argv[argc - 1], ROUND_UPTO(old + len + 1, 32));
if (p)
{ {
if (argi->type == 1) grub_strncpy (p + old, str, len);
{ p[old + len] = '\0';
val = grub_env_get (argi->str);
if (val)
grub_strcat (chararg, val);
} }
else else
grub_strcat (chararg, argi->str); {
oom = 1;
grub_free (argv[argc - 1]);
}
argv[argc - 1] = p;
return argv[argc - 1];
} }
return chararg; /* Move *STR to the begining of next word, but return current word. */
auto char* move_to_next (char **str);
char* move_to_next (char **str)
{
char *end;
char *start;
if (oom || !str || !*str)
return 0;
start = *str;
while (*start && grub_isspace (*start)) start++;
if (*start == '\0')
return 0;
end = start + 1;
while (*end && !grub_isspace (*end)) end++;
*str = end;
return start;
}
oom = 0;
argv = 0;
argc = 0;
push (0);
for (; arglist; arglist = arglist->next)
{
empty = 1;
arg = arglist->arg;
while (arg)
{
switch (arg->type)
{
case GRUB_SCRIPT_ARG_TYPE_VAR:
value = grub_env_get (arg->str);
while (*value && (ptr = move_to_next(&value)))
{
empty = 0;
append (ptr, value - ptr);
if (*value) push(0);
}
break;
case GRUB_SCRIPT_ARG_TYPE_TEXT:
if (grub_strlen (arg->str) > 0)
{
empty = 0;
append (arg->str, 0);
}
break;
case GRUB_SCRIPT_ARG_TYPE_DQSTR:
case GRUB_SCRIPT_ARG_TYPE_SQSTR:
empty = 0;
append (arg->str, 0);
break;
case GRUB_SCRIPT_ARG_TYPE_DQVAR:
empty = 0;
append (grub_env_get (arg->str), 0);
break;
}
arg = arg->next;
}
if (!empty)
push (0);
}
push (0); /* Ensure argv[argc] == 0. */
if (oom)
{
for (i = 0; i < argc; i++)
grub_free (argv[i]);
grub_free (argv);
argv = 0;
}
return argv;
} }
/* Execute a single command line. */ /* Execute a single command line. */
@ -85,7 +182,6 @@ grub_err_t
grub_script_execute_cmdline (struct grub_script_cmd *cmd) grub_script_execute_cmdline (struct grub_script_cmd *cmd)
{ {
struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd; struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd;
struct grub_script_arglist *arglist;
char **args = 0; char **args = 0;
int i = 0; int i = 0;
grub_command_t grubcmd; grub_command_t grubcmd;
@ -96,7 +192,11 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd)
char *cmdname; char *cmdname;
/* Lookup the command. */ /* Lookup the command. */
cmdname = grub_script_execute_argument_to_string (cmdline->arglist->arg); args = grub_script_execute_arglist_to_argv (cmdline->arglist);
if (!args)
return grub_errno;
cmdname = args[0];
grubcmd = grub_command_find (cmdname); grubcmd = grub_command_find (cmdname);
if (! grubcmd) if (! grubcmd)
{ {
@ -129,27 +229,15 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd)
return 0; return 0;
} }
} }
grub_free (cmdname);
if (cmdline->arglist->next) /* Count argv size. */
{ for (argcount = 0; args[argcount]; argcount++);
argcount = cmdline->arglist->argcount - 1;
/* Create argv from the arguments. */
args = grub_malloc (sizeof (char *) * argcount);
for (arglist = cmdline->arglist->next; arglist; arglist = arglist->next)
{
char *str;
str = grub_script_execute_argument_to_string (arglist->arg);
args[i++] = str;
}
}
/* Execute the GRUB command or function. */ /* Execute the GRUB command or function. */
if (grubcmd) if (grubcmd)
ret = (grubcmd->func) (grubcmd, argcount, args); ret = (grubcmd->func) (grubcmd, argcount - 1, args + 1);
else else
ret = grub_script_function_call (func, argcount, args); ret = grub_script_function_call (func, argcount - 1, args + 1);
/* Free arguments. */ /* Free arguments. */
for (i = 0; i < argcount; i++) for (i = 0; i < argcount; i++)
@ -202,7 +290,6 @@ grub_err_t
grub_script_execute_menuentry (struct grub_script_cmd *cmd) grub_script_execute_menuentry (struct grub_script_cmd *cmd)
{ {
struct grub_script_cmd_menuentry *cmd_menuentry; struct grub_script_cmd_menuentry *cmd_menuentry;
struct grub_script_arglist *arglist;
char **args = 0; char **args = 0;
int argcount = 0; int argcount = 0;
int i = 0; int i = 0;
@ -211,22 +298,11 @@ grub_script_execute_menuentry (struct grub_script_cmd *cmd)
if (cmd_menuentry->arglist) if (cmd_menuentry->arglist)
{ {
argcount = cmd_menuentry->arglist->argcount; args = grub_script_execute_arglist_to_argv (cmd_menuentry->arglist);
/* Create argv from the arguments. */
args = grub_malloc (sizeof (char *) * argcount);
if (!args) if (!args)
{
return grub_errno; return grub_errno;
}
for (arglist = cmd_menuentry->arglist; arglist; arglist = arglist->next) for (argcount = 0; args[argcount]; argcount++);
{
char *str;
str = grub_script_execute_argument_to_string (arglist->arg);
args[i++] = str;
}
} }
grub_normal_add_menu_entry (argcount, (const char **) args, grub_normal_add_menu_entry (argcount, (const char **) args,

View file

@ -1,6 +1,6 @@
/* /*
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. * Copyright (C) 2005,2007,2009,2010 Free Software Foundation, Inc.
* *
* GRUB is free software: you can redistribute it and/or modify * GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -34,7 +34,7 @@ grub_script_function_create (struct grub_script_arg *functionname_arg,
if (! func) if (! func)
return 0; return 0;
func->name = grub_script_execute_argument_to_string (functionname_arg); func->name = grub_strdup (functionname_arg->str);
if (! func->name) if (! func->name)
{ {
grub_free (func); grub_free (func);

View file

@ -1,7 +1,7 @@
/* lexer.c - The scripting lexer. */ /* lexer.c - The scripting lexer. */
/* /*
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
* *
* GRUB is free software: you can redistribute it and/or modify * GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -23,42 +23,7 @@
#include <grub/script_sh.h> #include <grub/script_sh.h>
#include "grub_script.tab.h" #include "grub_script.tab.h"
#include "grub_script.yy.h"
static int
check_varstate (grub_parser_state_t state)
{
return (state == GRUB_PARSER_STATE_VARNAME
|| state == GRUB_PARSER_STATE_VAR
|| state == GRUB_PARSER_STATE_QVAR
|| state == GRUB_PARSER_STATE_VARNAME2
|| state == GRUB_PARSER_STATE_QVARNAME
|| state == GRUB_PARSER_STATE_QVARNAME2);
}
static int
check_textstate (grub_parser_state_t state)
{
return (state == GRUB_PARSER_STATE_TEXT
|| state == GRUB_PARSER_STATE_ESC
|| state == GRUB_PARSER_STATE_QUOTE
|| state == GRUB_PARSER_STATE_DQUOTE);
}
struct grub_lexer_param *
grub_script_lexer_init (char *script, grub_reader_getline_t getline)
{
struct grub_lexer_param *param;
param = grub_zalloc (sizeof (*param));
if (! param)
return 0;
param->state = GRUB_PARSER_STATE_TEXT;
param->getline = getline;
param->script = script;
return param;
}
void void
grub_script_lexer_ref (struct grub_lexer_param *state) grub_script_lexer_ref (struct grub_lexer_param *state)
@ -74,360 +39,305 @@ grub_script_lexer_deref (struct grub_lexer_param *state)
/* Start recording all characters passing through the lexer. */ /* Start recording all characters passing through the lexer. */
void void
grub_script_lexer_record_start (struct grub_lexer_param *state) grub_script_lexer_record_start (struct grub_parser_param *parser)
{ {
state->record = 1; struct grub_lexer_param *lexer = parser->lexerstate;
state->recordlen = 100;
state->recording = grub_malloc (state->recordlen); lexer->record = 1;
state->recordpos = 0; lexer->recordpos = 0;
if (lexer->recording) /* reuse last record */
return;
lexer->recordlen = GRUB_LEXER_RECORD_INCREMENT;
lexer->recording = grub_malloc (lexer->recordlen);
if (!lexer->recording)
{
grub_script_yyerror (parser, 0);
lexer->record = 0;
lexer->recordlen = 0;
}
} }
char * char *
grub_script_lexer_record_stop (struct grub_lexer_param *state) grub_script_lexer_record_stop (struct grub_parser_param *parser)
{ {
state->record = 0; char *ptr;
char *result;
struct grub_lexer_param *lexer = parser->lexerstate;
/* Delete the last character, it is a `}'. */ auto char *compact (char *start, char *end);
if (state->recordpos > 0) char *compact (char *start, char *end)
{ {
if (state->recording[--state->recordpos] != '}') /* Delete '{' and '}' characters and whitespaces. */
{ while (*start && grub_isspace (*start)) start++;
grub_printf ("Internal error while parsing menu entry"); if (*start == '{') start++;
for (;;); /* XXX */ while (*start && grub_isspace (*start)) start++;
}
state->recording[state->recordpos] = '\0'; while (*end && grub_isspace (*end)) end--;
if (*end == '}') end--;
while (*end && grub_isspace (*end)) end--;
*end = '\0';
return start;
} }
return state->recording; if (!lexer->record || !lexer->recording)
return 0;
/* XXX This is not necessary in BASH. */
ptr = compact (lexer->recording, lexer->recording + lexer->recordpos - 1);
lexer->record = 0;
lexer->recordpos = 0;
/* This memory would be freed by, grub_script_free. */
result = grub_script_malloc (parser, grub_strlen (ptr) + 1);
if (result)
grub_strcpy (result, ptr);
return result;
} }
/* When recording is enabled, record the character C as the next item
in the character stream. */ /* Record STR if input recording is enabled. */
static void void
recordchar (struct grub_lexer_param *state, char c) grub_script_lexer_record (struct grub_parser_param *parser, char *str)
{ {
if (state->recordpos == state->recordlen) int len;
struct grub_lexer_param *lexer = parser->lexerstate;
if (!lexer->record)
return;
len = grub_strlen (str);
if (lexer->recordpos + len >= lexer->recordlen - 1)
{ {
char *old = state->recording; char *old = lexer->recording;
state->recordlen += 100; lexer->recordlen += GRUB_LEXER_RECORD_INCREMENT;
state->recording = grub_realloc (state->recording, state->recordlen); lexer->recording = grub_realloc (lexer->recording, lexer->recordlen);
if (! state->recording) if (!lexer->recording)
{ {
grub_free (old); grub_free (old);
state->record = 0; lexer->record = 0;
grub_script_yyerror (parser, 0);
return;
} }
} }
state->recording[state->recordpos++] = c; grub_strcpy (lexer->recording + lexer->recordpos, str);
lexer->recordpos += len;
} }
/* Fetch the next character for the lexer. */ /* Append '\n' to SRC, before '\0' */
static void static char *
nextchar (struct grub_lexer_param *state) append_newline (const char *src)
{ {
if (state->record) char *line;
recordchar (state, *state->script); grub_size_t len;
state->script++;
len = grub_strlen (src);
line = grub_malloc (len + 1);
if (!line)
return 0;
grub_strcpy (line, src);
line[len] = '\n';
line[len + 1] = '\0';
return line;
} }
/* Read next line of input if necessary, and set yyscanner buffers. */
int int
grub_script_yylex (union YYSTYPE *yylval, struct grub_parser_param *parsestate) grub_script_lexer_yywrap (struct grub_parser_param *parserstate)
{ {
grub_parser_state_t newstate; int len;
char use; char *line;
struct grub_lexer_param *state = parsestate->lexerstate; char *line2;
int firstrun = 1; YY_BUFFER_STATE buffer;
struct grub_lexer_param *lexerstate = parserstate->lexerstate;
yylval->arg = 0; if (!lexerstate->refs)
return 0;
if (state->tokenonhold) if (!lexerstate->getline)
{ {
int token = state->tokenonhold; grub_script_yyerror (parserstate, "unexpected end of file");
state->tokenonhold = 0;
return token;
}
for (;! state->done; firstrun = 0)
{
if (! state->script || ! *state->script)
{
/* Check if more tokens are requested by the parser. */
if (((state->refs && ! parsestate->err)
|| state->state == GRUB_PARSER_STATE_ESC
|| state->state == GRUB_PARSER_STATE_QUOTE
|| state->state == GRUB_PARSER_STATE_DQUOTE)
&& state->getline)
{
int doexit = 0;
if (state->state != GRUB_PARSER_STATE_ESC
&& state->state != GRUB_PARSER_STATE_QUOTE
&& state->state != GRUB_PARSER_STATE_DQUOTE
&& ! state->was_newline)
{
state->was_newline = 1;
state->tokenonhold = '\n';
break;
}
while (! state->script || ! *state->script)
{
grub_free (state->newscript);
state->newscript = 0;
state->getline (&state->newscript, 1);
state->script = state->newscript;
if (! state->script)
{
doexit = 1;
break;
}
}
if (doexit)
break;
grub_dprintf ("scripting", "token=`\\n'\n");
recordchar (state, '\n');
if (state->state == GRUB_PARSER_STATE_VARNAME)
state->state = GRUB_PARSER_STATE_TEXT;
if (state->state == GRUB_PARSER_STATE_QVARNAME)
state->state = GRUB_PARSER_STATE_DQUOTE;
if (state->state == GRUB_PARSER_STATE_DQUOTE
|| state->state == GRUB_PARSER_STATE_QUOTE)
yylval->arg = grub_script_arg_add (parsestate, yylval->arg,
GRUB_SCRIPT_ARG_TYPE_STR,
"\n");
}
else
{
grub_free (state->newscript);
state->newscript = 0;
state->done = 1;
grub_dprintf ("scripting", "token=`\\n'\n");
state->tokenonhold = '\n';
break;
}
}
state->was_newline = 0;
newstate = grub_parser_cmdline_state (state->state, *state->script, &use);
/* Check if it is a text. */
if (check_textstate (newstate))
{
char *buffer = NULL;
int bufpos = 0;
/* Buffer is initially large enough to hold most commands
but extends automatically when needed. */
int bufsize = 128;
buffer = grub_malloc (bufsize);
/* In case the string is not quoted, this can be a one char
length symbol. */
if (newstate == GRUB_PARSER_STATE_TEXT)
{
int doexit = 0;
switch (*state->script)
{
case ' ':
while (*state->script)
{
newstate = grub_parser_cmdline_state (state->state,
*state->script, &use);
if (! (state->state == GRUB_PARSER_STATE_TEXT
&& *state->script == ' '))
{
grub_dprintf ("scripting", "token=` '\n");
if (! firstrun)
doexit = 1;
break;
}
state->state = newstate;
nextchar (state);
}
grub_dprintf ("scripting", "token=` '\n");
if (! firstrun)
doexit = 1;
break;
case '{':
case '}':
case ';':
case '\n':
{
char c;
grub_dprintf ("scripting", "token=`%c'\n", *state->script);
c = *state->script;
nextchar (state);
state->tokenonhold = c;
doexit = 1;
break;
}
}
if (doexit)
{
grub_free (buffer);
break;
}
}
/* Read one token, possible quoted. */
while (*state->script)
{
newstate = grub_parser_cmdline_state (state->state,
*state->script, &use);
/* Check if a variable name starts. */
if (check_varstate (newstate))
break;
/* If the string is not quoted or escaped, stop processing
when a special token was found. It will be recognized
next time when this function is called. */
if (newstate == GRUB_PARSER_STATE_TEXT
&& state->state != GRUB_PARSER_STATE_ESC
&& state->state != GRUB_PARSER_STATE_QUOTE
&& state->state != GRUB_PARSER_STATE_DQUOTE)
{
int breakout = 0;
switch (use)
{
case ' ':
case '{':
case '}':
case ';':
case '\n':
breakout = 1;
}
if (breakout)
break;
}
if (use)
{
if (bufsize <= bufpos + 1)
{
bufsize <<= 1;
buffer = grub_realloc (buffer, bufsize);
}
buffer[bufpos++] = use;
}
state->state = newstate;
nextchar (state);
}
/* A string of text was read in. */
if (bufsize <= bufpos + 1)
{
bufsize <<= 1;
buffer = grub_realloc (buffer, bufsize);
}
buffer[bufpos++] = 0;
grub_dprintf ("scripting", "token=`%s'\n", buffer);
yylval->arg = grub_script_arg_add (parsestate, yylval->arg,
GRUB_SCRIPT_ARG_TYPE_STR, buffer);
grub_free (buffer);
}
else if (newstate == GRUB_PARSER_STATE_VAR
|| newstate == GRUB_PARSER_STATE_QVAR)
{
char *buffer = NULL;
int bufpos = 0;
/* Buffer is initially large enough to hold most commands
but extends automatically when needed. */
int bufsize = 128;
buffer = grub_malloc (bufsize);
/* This is a variable, read the variable name. */
while (*state->script)
{
newstate = grub_parser_cmdline_state (state->state,
*state->script, &use);
/* Check if this character is not part of the variable name
anymore. */
if (! (check_varstate (newstate)))
{
if (state->state == GRUB_PARSER_STATE_VARNAME2
|| state->state == GRUB_PARSER_STATE_QVARNAME2)
nextchar (state);
state->state = newstate;
break;
}
if (use)
{
if (bufsize <= bufpos + 1)
{
bufsize <<= 1;
buffer = grub_realloc (buffer, bufsize);
}
buffer[bufpos++] = use;
}
nextchar (state);
state->state = newstate;
}
if (bufsize <= bufpos + 1)
{
bufsize <<= 1;
buffer = grub_realloc (buffer, bufsize);
}
buffer[bufpos++] = 0;
state->state = newstate;
yylval->arg = grub_script_arg_add (parsestate, yylval->arg,
GRUB_SCRIPT_ARG_TYPE_VAR, buffer);
grub_dprintf ("scripting", "vartoken=`%s'\n", buffer);
grub_free (buffer);
}
else
{
/* There is either text or a variable name. In the case you
arrive here there is a serious problem with the lexer. */
grub_error (GRUB_ERR_BAD_ARGUMENT, "internal error");
return 0; return 0;
} }
}
if (yylval->arg == 0) line = 0;
buffer = 0;
lexerstate->getline (&line, 1);
if (!line)
{ {
int token = state->tokenonhold; grub_script_yyerror (parserstate, 0); /* XXX this could be for ^C case? */
state->tokenonhold = 0; return 0;
return token;
} }
if (yylval->arg->next == 0 && yylval->arg->type == GRUB_SCRIPT_ARG_TYPE_STR) len = grub_strlen (line);
if (line[len - 1] == '\n')
{ {
/* Detect some special tokens. */ buffer = yy_scan_string (line, lexerstate->yyscanner);
if (! grub_strcmp (yylval->arg->str, "while")) }
return GRUB_PARSER_TOKEN_WHILE; else
else if (! grub_strcmp (yylval->arg->str, "if")) {
return GRUB_PARSER_TOKEN_IF; line2 = append_newline (line);
else if (! grub_strcmp (yylval->arg->str, "function")) if (line2)
return GRUB_PARSER_TOKEN_FUNCTION; {
else if (! grub_strcmp (yylval->arg->str, "menuentry")) buffer = yy_scan_string (line2, lexerstate->yyscanner);
return GRUB_PARSER_TOKEN_MENUENTRY; grub_free (line2);
else if (! grub_strcmp (yylval->arg->str, "@")) }
return GRUB_PARSER_TOKEN_MENUENTRY;
else if (! grub_strcmp (yylval->arg->str, "else"))
return GRUB_PARSER_TOKEN_ELSE;
else if (! grub_strcmp (yylval->arg->str, "then"))
return GRUB_PARSER_TOKEN_THEN;
else if (! grub_strcmp (yylval->arg->str, "fi"))
return GRUB_PARSER_TOKEN_FI;
} }
return GRUB_PARSER_TOKEN_ARG; grub_free (line);
if (!buffer)
{
grub_script_yyerror (parserstate, 0);
return 0;
}
return 1;
}
struct grub_lexer_param *
grub_script_lexer_init (struct grub_parser_param *parser, char *script,
grub_reader_getline_t getline)
{
int len;
char *script2;
YY_BUFFER_STATE buffer;
struct grub_lexer_param *lexerstate;
lexerstate = grub_zalloc (sizeof (*lexerstate));
if (!lexerstate)
return 0;
lexerstate->text = grub_malloc (GRUB_LEXER_TOKEN_MAX);
if (!lexerstate->text)
{
grub_free (lexerstate);
return 0;
}
lexerstate->getline = getline; /* rest are all zeros already */
if (yylex_init (&lexerstate->yyscanner))
{
grub_free (lexerstate->text);
grub_free (lexerstate);
return 0;
}
buffer = 0;
script = script ? : "\n";
len = grub_strlen (script);
if (script[len - 1] == '\n')
{
buffer = yy_scan_string (script, lexerstate->yyscanner);
}
else
{
script2 = append_newline (script);
if (script2)
{
buffer = yy_scan_string (script2, lexerstate->yyscanner);
grub_free (script2);
}
}
if (!buffer)
{
yylex_destroy (lexerstate->yyscanner);
grub_free (lexerstate->yyscanner);
grub_free (lexerstate->text);
grub_free (lexerstate);
return 0;
}
yyset_extra (parser, lexerstate->yyscanner);
return lexerstate;
} }
void void
grub_script_yyerror (struct grub_parser_param *lex __attribute__ ((unused)), grub_script_lexer_fini (struct grub_lexer_param *lexerstate)
char const *err)
{ {
grub_printf ("%s\n", err); if (!lexerstate)
return;
yylex_destroy (lexerstate->yyscanner);
grub_free (lexerstate->recording);
grub_free (lexerstate->text);
grub_free (lexerstate);
}
int
grub_script_yylex (union YYSTYPE *value,
struct grub_parser_param *parserstate)
{
char *str;
int token;
grub_script_arg_type_t type;
struct grub_lexer_param *lexerstate = parserstate->lexerstate;
value->arg = 0;
if (parserstate->err)
return GRUB_PARSER_TOKEN_BAD;
if (lexerstate->eof)
return GRUB_PARSER_TOKEN_EOF;
/*
* Words with environment variables, like foo${bar}baz needs
* multiple tokens to be merged into a single grub_script_arg. We
* use two variables to achieve this: lexerstate->merge_start and
* lexerstate->merge_end
*/
lexerstate->merge_start = 0;
lexerstate->merge_end = 0;
do
{
/* Empty lexerstate->text. */
lexerstate->size = 0;
lexerstate->text[0] = '\0';
token = yylex (value, lexerstate->yyscanner);
if (token == GRUB_PARSER_TOKEN_BAD)
break;
/* Merging feature uses lexerstate->text instead of yytext. */
if (lexerstate->merge_start)
{
lexerstate->text[lexerstate->size] = '\0';
str = lexerstate->text;
type = lexerstate->type;
}
else
{
str = yyget_text (lexerstate->yyscanner);
type = GRUB_SCRIPT_ARG_TYPE_TEXT;
}
/* grub_printf ("tok %u, txt [%s] size %u\n", token, str, lexerstate->size); */
value->arg = grub_script_arg_add (parserstate, value->arg, type, str);
}
while (lexerstate->merge_start && !lexerstate->merge_end);
if (!value->arg || parserstate->err)
return GRUB_PARSER_TOKEN_BAD;
return token;
}
void
grub_script_yyerror (struct grub_parser_param *state, char const *err)
{
if (err)
grub_error (GRUB_ERR_INVALID_COMMAND, err);
grub_print_error ();
state->err++;
} }

View file

@ -1,7 +1,7 @@
/* parser.y - The scripting parser. */ /* parser.y - The scripting parser. */
/* /*
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
* *
* GRUB is free software: you can redistribute it and/or modify * GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -35,161 +35,206 @@
char *string; char *string;
} }
%token GRUB_PARSER_TOKEN_IF "if" %token GRUB_PARSER_TOKEN_BAD
%token GRUB_PARSER_TOKEN_WHILE "while" %token GRUB_PARSER_TOKEN_EOF 0 "end-of-input"
%token GRUB_PARSER_TOKEN_FUNCTION "function"
%token GRUB_PARSER_TOKEN_MENUENTRY "menuentry" %token GRUB_PARSER_TOKEN_NEWLINE "\n"
%token GRUB_PARSER_TOKEN_ELSE "else" %token GRUB_PARSER_TOKEN_AND "&&"
%token GRUB_PARSER_TOKEN_THEN "then" %token GRUB_PARSER_TOKEN_OR "||"
%token GRUB_PARSER_TOKEN_FI "fi" %token GRUB_PARSER_TOKEN_SEMI2 ";;"
%token GRUB_PARSER_TOKEN_ARG %token GRUB_PARSER_TOKEN_PIPE "|"
%type <cmd> script_init script grubcmd command commands commandblock menuentry if %token GRUB_PARSER_TOKEN_AMP "&"
%type <arglist> arguments; %token GRUB_PARSER_TOKEN_SEMI ";"
%type <arg> GRUB_PARSER_TOKEN_ARG; %token GRUB_PARSER_TOKEN_LPAR "("
%token GRUB_PARSER_TOKEN_RPAR ")"
%token GRUB_PARSER_TOKEN_LBR "{"
%token GRUB_PARSER_TOKEN_RBR "}"
%token GRUB_PARSER_TOKEN_NOT "!"
%token GRUB_PARSER_TOKEN_LSQBR2 "["
%token GRUB_PARSER_TOKEN_RSQBR2 "]"
%token GRUB_PARSER_TOKEN_LT "<"
%token GRUB_PARSER_TOKEN_GT ">"
%token <arg> GRUB_PARSER_TOKEN_CASE "case"
%token <arg> GRUB_PARSER_TOKEN_DO "do"
%token <arg> GRUB_PARSER_TOKEN_DONE "done"
%token <arg> GRUB_PARSER_TOKEN_ELIF "elif"
%token <arg> GRUB_PARSER_TOKEN_ELSE "else"
%token <arg> GRUB_PARSER_TOKEN_ESAC "esac"
%token <arg> GRUB_PARSER_TOKEN_FI "fi"
%token <arg> GRUB_PARSER_TOKEN_FOR "for"
%token <arg> GRUB_PARSER_TOKEN_IF "if"
%token <arg> GRUB_PARSER_TOKEN_IN "in"
%token <arg> GRUB_PARSER_TOKEN_SELECT "select"
%token <arg> GRUB_PARSER_TOKEN_THEN "then"
%token <arg> GRUB_PARSER_TOKEN_UNTIL "until"
%token <arg> GRUB_PARSER_TOKEN_WHILE "while"
%token <arg> GRUB_PARSER_TOKEN_TIME "time"
%token <arg> GRUB_PARSER_TOKEN_FUNCTION "function"
%token <arg> GRUB_PARSER_TOKEN_MENUENTRY "menuentry"
%token <arg> GRUB_PARSER_TOKEN_NAME "name"
%token <arg> GRUB_PARSER_TOKEN_WORD "word"
%type <arglist> word argument arguments0 arguments1
%type <cmd> script_init script grubcmd ifcmd command
%type <cmd> commands1 menuentry statement
%pure-parser %pure-parser
%error-verbose
%lex-param { struct grub_parser_param *state }; %lex-param { struct grub_parser_param *state };
%parse-param { struct grub_parser_param *state }; %parse-param { struct grub_parser_param *state };
%start script_init
%% %%
/* It should be possible to do this in a clean way... */ /* It should be possible to do this in a clean way... */
script_init: { state->err = 0; } script script_init: { state->err = 0; } script { state->parsed = $2; state->err = 0; }
{
state->parsed = $2;
}
; ;
script: { $$ = 0; } script: newlines0
| '\n' { $$ = 0; } {
| commands { $$ = $1; } $$ = 0;
| function '\n' { $$ = 0; } }
| menuentry '\n' { $$ = $1; } | script statement delimiter
{
struct grub_script_cmdblock *cmdblock;
cmdblock = (struct grub_script_cmdblock *) $1;
$$ = grub_script_add_cmd (state, cmdblock, $2);
}
| error | error
{ {
$$ = 0; $$ = 0;
yyerror (state, "Incorrect command"); yyerror (state, "Incorrect command");
state->err = 1;
yyerrok; yyerrok;
} }
; ;
delimiter: '\n' newlines0: /* Empty */ | newlines1 ;
| ';' newlines1: newlines0 "\n" ;
| delimiter '\n'
delimiter: ";"
| "\n"
;
delimiters0: /* Empty */ | delimiters1 ;
delimiters1: delimiter
| delimiters1 "\n"
; ;
newlines: /* Empty */ word: GRUB_PARSER_TOKEN_NAME { $$ = grub_script_add_arglist (state, 0, $1); }
| newlines '\n' | GRUB_PARSER_TOKEN_WORD { $$ = grub_script_add_arglist (state, 0, $1); }
; ;
statement: command { $$ = $1; }
| function { $$ = 0; }
| menuentry { $$ = $1; }
argument : "case" { $$ = grub_script_add_arglist (state, 0, $1); }
| "do" { $$ = grub_script_add_arglist (state, 0, $1); }
| "done" { $$ = grub_script_add_arglist (state, 0, $1); }
| "elif" { $$ = grub_script_add_arglist (state, 0, $1); }
| "else" { $$ = grub_script_add_arglist (state, 0, $1); }
| "esac" { $$ = grub_script_add_arglist (state, 0, $1); }
| "fi" { $$ = grub_script_add_arglist (state, 0, $1); }
| "for" { $$ = grub_script_add_arglist (state, 0, $1); }
| "if" { $$ = grub_script_add_arglist (state, 0, $1); }
| "in" { $$ = grub_script_add_arglist (state, 0, $1); }
| "select" { $$ = grub_script_add_arglist (state, 0, $1); }
| "then" { $$ = grub_script_add_arglist (state, 0, $1); }
| "until" { $$ = grub_script_add_arglist (state, 0, $1); }
| "while" { $$ = grub_script_add_arglist (state, 0, $1); }
| "function" { $$ = grub_script_add_arglist (state, 0, $1); }
| "menuentry" { $$ = grub_script_add_arglist (state, 0, $1); }
| word { $$ = $1; }
;
arguments: GRUB_PARSER_TOKEN_ARG arguments0: /* Empty */ { $$ = 0; }
| arguments1 { $$ = $1; }
;
arguments1: argument arguments0
{ {
$$ = grub_script_add_arglist (state, 0, $1); if ($1 && $2)
{
$1->next = $2;
$1->argcount += $2->argcount;
$2->argcount = 0;
} }
| arguments GRUB_PARSER_TOKEN_ARG $$ = $1;
{
$$ = grub_script_add_arglist (state, $1, $2);
} }
; ;
grubcmd: arguments grubcmd: word arguments0
{ {
if ($1 && $2) {
$1->next = $2;
$1->argcount += $2->argcount;
$2->argcount = 0;
}
$$ = grub_script_create_cmdline (state, $1); $$ = grub_script_create_cmdline (state, $1);
} }
; ;
/* A single command. */ /* A single command. */
command: grubcmd delimiter { $$ = $1; } command: grubcmd { $$ = $1; }
| if delimiter { $$ = $1; } | ifcmd { $$ = $1; }
| commandblock delimiter { $$ = $1; }
; ;
/* A block of commands. */ /* A list of commands. */
commands: command commands1: newlines0 command
{ {
$$ = grub_script_add_cmd (state, 0, $1); $$ = grub_script_add_cmd (state, 0, $2);
} }
| command commands | commands1 delimiters1 command
{ {
struct grub_script_cmdblock *cmd; struct grub_script_cmdblock *cmdblock;
cmd = (struct grub_script_cmdblock *) $2; cmdblock = (struct grub_script_cmdblock *) $1;
$$ = grub_script_add_cmd (state, cmd, $1); $$ = grub_script_add_cmd (state, cmdblock, $3);
} }
; ;
/* A function. Carefully save the memory that is allocated. Don't function: "function" "name"
change any stuff because it might seem like a fun thing to do!
Special care was take to make sure the mid-rule actions are
executed on the right moment. So the `commands' rule should be
recognized after executing the `grub_script_mem_record; and before
`grub_script_mem_record_stop'. */
function: "function" GRUB_PARSER_TOKEN_ARG
{ {
grub_script_lexer_ref (state->lexerstate); grub_script_lexer_ref (state->lexerstate);
} newlines '{'
{
/* The first part of the function was recognized.
Now start recording the memory usage to store
this function. */
state->func_mem = grub_script_mem_record (state); state->func_mem = grub_script_mem_record (state);
} newlines commands '}' }
delimiters0 "{" commands1 delimiters1 "}"
{ {
struct grub_script *script; struct grub_script *script;
/* All the memory usage for parsing this function
was recorded. */
state->func_mem = grub_script_mem_record_stop (state, state->func_mem = grub_script_mem_record_stop (state,
state->func_mem); state->func_mem);
script = grub_script_create ($8, state->func_mem); script = grub_script_create ($6, state->func_mem);
if (script) if (script)
grub_script_function_create ($2, script); grub_script_function_create ($2, script);
grub_script_lexer_deref (state->lexerstate); grub_script_lexer_deref (state->lexerstate);
} }
; ;
/* Carefully designed, together with `menuentry' so everything happens
just in the expected order. */
commandblock: '{'
{
grub_script_lexer_ref (state->lexerstate);
}
newlines commands '}'
{
grub_script_lexer_deref (state->lexerstate);
$$ = $4;
}
;
/* A menu entry. Carefully save the memory that is allocated. */
menuentry: "menuentry" menuentry: "menuentry"
{ {
grub_script_lexer_ref (state->lexerstate); grub_script_lexer_ref (state->lexerstate);
} arguments newlines '{' }
arguments1
{ {
grub_script_lexer_record_start (state->lexerstate); grub_script_lexer_record_start (state);
} newlines commands '}' }
delimiters0 "{" commands1 delimiters1 "}"
{ {
char *menu_entry; char *menu_entry;
menu_entry = grub_script_lexer_record_stop (state->lexerstate); menu_entry = grub_script_lexer_record_stop (state);
grub_script_lexer_deref (state->lexerstate); grub_script_lexer_deref (state->lexerstate);
$$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0); $$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0);
} }
; ;
/* The first part of the if statement. It's used to switch the lexer if: "if" { grub_script_lexer_ref (state->lexerstate); }
to a state in which it demands more tokens. */
if_statement: "if" { grub_script_lexer_ref (state->lexerstate); }
; ;
ifcmd: if commands1 delimiters1 "then" commands1 delimiters1 "fi"
/* The if statement. */
if: if_statement commands "then" newlines commands "fi"
{ {
$$ = grub_script_create_cmdif (state, $2, $5, 0); $$ = grub_script_create_cmdif (state, $2, $5, 0);
grub_script_lexer_deref (state->lexerstate); grub_script_lexer_deref (state->lexerstate);
} }
| if_statement commands "then" newlines commands "else" newlines commands "fi" | if commands1 delimiters1 "then" commands1 delimiters1 "else" commands1 delimiters1 "fi"
{ {
$$ = grub_script_create_cmdif (state, $2, $5, $8); $$ = grub_script_create_cmdif (state, $2, $5, $8);
grub_script_lexer_deref (state->lexerstate); grub_script_lexer_deref (state->lexerstate);

View file

@ -1,7 +1,7 @@
/* script.c -- Functions to create an in memory description of the script. */ /* script.c -- Functions to create an in memory description of the script. */
/* /*
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 2005,2006,2007,2009 Free Software Foundation, Inc. * Copyright (C) 2005,2006,2007,2009,2010 Free Software Foundation, Inc.
* *
* GRUB is free software: you can redistribute it and/or modify * GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -24,12 +24,10 @@
/* It is not possible to deallocate the memory when a syntax error was /* It is not possible to deallocate the memory when a syntax error was
found. Because of that it is required to keep track of all memory found. Because of that it is required to keep track of all memory
allocations. The memory is freed in case of an error, or allocations. The memory is freed in case of an error, or assigned
assigned to the parsed script when parsing was successful. */ to the parsed script when parsing was successful.
/* XXX */ In case of the normal malloc, some additional bytes are allocated
/* In case of the normal malloc, some additional bytes are allocated
for this datastructure. All reserved memory is stored in a linked for this datastructure. All reserved memory is stored in a linked
list so it can be easily freed. The original memory can be found list so it can be easily freed. The original memory can be found
from &mem. */ from &mem. */
@ -46,6 +44,8 @@ grub_script_malloc (struct grub_parser_param *state, grub_size_t size)
struct grub_script_mem *mem; struct grub_script_mem *mem;
mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem) mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem)
- sizeof (char)); - sizeof (char));
if (!mem)
return 0;
grub_dprintf ("scripting", "malloc %p\n", mem); grub_dprintf ("scripting", "malloc %p\n", mem);
mem->next = state->memused; mem->next = state->memused;
@ -99,23 +99,31 @@ grub_script_free (struct grub_script *script)
grub_script_mem_free (script->mem); grub_script_mem_free (script->mem);
grub_free (script); grub_free (script);
} }
/* Extend the argument arg with a variable or string of text. If ARG /* Extend the argument arg with a variable or string of text. If ARG
is zero a new list is created. */ is zero a new list is created. */
struct grub_script_arg * struct grub_script_arg *
grub_script_arg_add (struct grub_parser_param *state, struct grub_script_arg *arg, grub_script_arg_add (struct grub_parser_param *state,
grub_script_arg_type_t type, char *str) struct grub_script_arg *arg, grub_script_arg_type_t type,
char *str)
{ {
struct grub_script_arg *argpart; struct grub_script_arg *argpart;
struct grub_script_arg *ll; struct grub_script_arg *ll;
int len; int len;
argpart = (struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg)); argpart =
(struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg));
if (!argpart)
return arg;
argpart->type = type; argpart->type = type;
len = grub_strlen (str) + 1; len = grub_strlen (str) + 1;
argpart->str = grub_script_malloc (state, len); argpart->str = grub_script_malloc (state, len);
if (!argpart->str)
return arg; /* argpart is freed later, during grub_script_free. */
grub_memcpy (argpart->str, str, len); grub_memcpy (argpart->str, str, len);
argpart->next = 0; argpart->next = 0;
@ -132,14 +140,19 @@ grub_script_arg_add (struct grub_parser_param *state, struct grub_script_arg *ar
is zero, a new list will be created. */ is zero, a new list will be created. */
struct grub_script_arglist * struct grub_script_arglist *
grub_script_add_arglist (struct grub_parser_param *state, grub_script_add_arglist (struct grub_parser_param *state,
struct grub_script_arglist *list, struct grub_script_arg *arg) struct grub_script_arglist *list,
struct grub_script_arg *arg)
{ {
struct grub_script_arglist *link; struct grub_script_arglist *link;
struct grub_script_arglist *ll; struct grub_script_arglist *ll;
grub_dprintf ("scripting", "arglist\n"); grub_dprintf ("scripting", "arglist\n");
link = (struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link)); link =
(struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link));
if (!link)
return list;
link->next = 0; link->next = 0;
link->arg = arg; link->arg = arg;
link->argcount = 0; link->argcount = 0;
@ -171,6 +184,9 @@ grub_script_create_cmdline (struct grub_parser_param *state,
grub_dprintf ("scripting", "cmdline\n"); grub_dprintf ("scripting", "cmdline\n");
cmd = grub_script_malloc (state, sizeof (*cmd)); cmd = grub_script_malloc (state, sizeof (*cmd));
if (!cmd)
return 0;
cmd->cmd.exec = grub_script_execute_cmdline; cmd->cmd.exec = grub_script_execute_cmdline;
cmd->cmd.next = 0; cmd->cmd.next = 0;
cmd->arglist = arglist; cmd->arglist = arglist;
@ -193,6 +209,9 @@ grub_script_create_cmdif (struct grub_parser_param *state,
grub_dprintf ("scripting", "cmdif\n"); grub_dprintf ("scripting", "cmdif\n");
cmd = grub_script_malloc (state, sizeof (*cmd)); cmd = grub_script_malloc (state, sizeof (*cmd));
if (!cmd)
return 0;
cmd->cmd.exec = grub_script_execute_cmdif; cmd->cmd.exec = grub_script_execute_cmdif;
cmd->cmd.next = 0; cmd->cmd.next = 0;
cmd->exec_to_evaluate = exec_to_evaluate; cmd->exec_to_evaluate = exec_to_evaluate;
@ -209,30 +228,16 @@ grub_script_create_cmdif (struct grub_parser_param *state,
struct grub_script_cmd * struct grub_script_cmd *
grub_script_create_cmdmenu (struct grub_parser_param *state, grub_script_create_cmdmenu (struct grub_parser_param *state,
struct grub_script_arglist *arglist, struct grub_script_arglist *arglist,
char *sourcecode, char *sourcecode, int options)
int options)
{ {
struct grub_script_cmd_menuentry *cmd; struct grub_script_cmd_menuentry *cmd;
int i;
/* Skip leading newlines to make the sourcecode better readable when
using the editor. */
while (*sourcecode == '\n')
sourcecode++;
/* Having trailing returns can some some annoying conflicts, remove
them. XXX: Can the parser be improved to handle this? */
for (i = grub_strlen (sourcecode) - 1; i > 0; i--)
{
if (sourcecode[i] != '\n')
break;
sourcecode[i] = '\0';
}
cmd = grub_script_malloc (state, sizeof (*cmd)); cmd = grub_script_malloc (state, sizeof (*cmd));
if (!cmd)
return 0;
cmd->cmd.exec = grub_script_execute_menuentry; cmd->cmd.exec = grub_script_execute_menuentry;
cmd->cmd.next = 0; cmd->cmd.next = 0;
/* XXX: Check if this memory is properly freed. */
cmd->sourcecode = sourcecode; cmd->sourcecode = sourcecode;
cmd->arglist = arglist; cmd->arglist = arglist;
cmd->options = options; cmd->options = options;
@ -248,6 +253,8 @@ grub_script_add_cmd (struct grub_parser_param *state,
struct grub_script_cmdblock *cmdblock, struct grub_script_cmdblock *cmdblock,
struct grub_script_cmd *cmd) struct grub_script_cmd *cmd)
{ {
struct grub_script_cmd *ptr;
grub_dprintf ("scripting", "cmdblock\n"); grub_dprintf ("scripting", "cmdblock\n");
if (!cmd) if (!cmd)
@ -255,8 +262,10 @@ grub_script_add_cmd (struct grub_parser_param *state,
if (!cmdblock) if (!cmdblock)
{ {
cmdblock = (struct grub_script_cmdblock *) grub_script_malloc (state, cmdblock = grub_script_malloc (state, sizeof (*cmdblock));
sizeof (*cmdblock)); if (!cmdblock)
return 0;
cmdblock->cmd.exec = grub_script_execute_cmdblock; cmdblock->cmd.exec = grub_script_execute_cmdblock;
cmdblock->cmd.next = 0; cmdblock->cmd.next = 0;
cmdblock->cmdlist = cmd; cmdblock->cmdlist = cmd;
@ -264,15 +273,22 @@ grub_script_add_cmd (struct grub_parser_param *state,
} }
else else
{ {
cmd->next = cmdblock->cmdlist; if (!cmdblock->cmdlist)
cmdblock->cmdlist = cmd; cmdblock->cmdlist = cmd;
else
{
ptr = cmdblock->cmdlist;
while (ptr->next)
ptr = ptr->next;
ptr->next = cmd;
}
} }
return (struct grub_script_cmd *) cmdblock; return (struct grub_script_cmd *) cmdblock;
} }
struct grub_script * struct grub_script *
grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem) grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem)
{ {
@ -312,7 +328,7 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
return 0; return 0;
/* Initialize the lexer. */ /* Initialize the lexer. */
lexstate = grub_script_lexer_init (script, getline); lexstate = grub_script_lexer_init (parsestate, script, getline);
if (!lexstate) if (!lexstate)
{ {
grub_free (parsed); grub_free (parsed);
@ -330,7 +346,7 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
struct grub_script_mem *memfree; struct grub_script_mem *memfree;
memfree = grub_script_mem_record_stop (parsestate, membackup); memfree = grub_script_mem_record_stop (parsestate, membackup);
grub_script_mem_free (memfree); grub_script_mem_free (memfree);
grub_free (lexstate); grub_script_lexer_fini (lexstate);
grub_free (parsestate); grub_free (parsestate);
return 0; return 0;
} }
@ -338,7 +354,7 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
parsed->mem = grub_script_mem_record_stop (parsestate, membackup); parsed->mem = grub_script_mem_record_stop (parsestate, membackup);
parsed->cmd = parsestate->parsed; parsed->cmd = parsestate->parsed;
grub_free (lexstate); grub_script_lexer_fini (lexstate);
grub_free (parsestate); grub_free (parsestate);
return parsed; return parsed;

331
script/yylex.l Normal file
View file

@ -0,0 +1,331 @@
%{
/* yylex.l The scripting lexer. */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/parser.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/script_sh.h>
#include "grub_script.tab.h"
#define yyfree grub_lexer_yyfree
#define yyalloc grub_lexer_yyalloc
#define yyrealloc grub_lexer_yyrealloc
/*
* As we don't have access to yyscanner, we cannot do much except to
* print the fatal error.
*/
#define YY_FATAL_ERROR(msg) \
do { \
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++; \
} \
} while (0)
#define RECORD \
do { \
grub_script_lexer_record (yyextra, yytext); \
} while (0)
#define ARG(t) \
do { \
yyextra->lexerstate->type = t; \
return GRUB_PARSER_TOKEN_WORD; \
} while (0)
/* We don't need YY_INPUT, as we rely on yy_scan_strings */
#define YY_INPUT(buf,res,max) do { res = 0; } while (0)
/* forward declarations */
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);
%}
%top{
/*
* Some flex hacks for -nostdinc; XXX We need to fix these when libc
* support becomes availble in GRUB.
*/
#include <grub/types.h>
typedef grub_size_t size_t;
typedef grub_size_t yy_size_t;
#define YY_TYPEDEF_YY_SIZE_T 1
#define FILE void
#define stdin 0
#define stdout 0
#define EOF 0
#define errno grub_errno
#define EINVAL GRUB_ERR_BAD_NUMBER
#define ENOMEM GRUB_ERR_OUT_OF_MEMORY
#define strlen grub_strlen
#define memset grub_memset
#define fprintf(...) 0
#define exit(...)
#pragma GCC diagnostic warning "-Wunused-variable"
#pragma GCC diagnostic warning "-Wunused-function"
#pragma GCC diagnostic warning "-Wunused-parameter"
#pragma GCC diagnostic warning "-Wstrict-prototypes"
#pragma GCC diagnostic warning "-Wmissing-prototypes"
}
%option ecs
%option meta-ecs
%option warn
%option array
%option stack
%option reentrant
%option bison-bridge
%option never-interactive
%option noyyfree noyyalloc noyyrealloc
%option nounistd nostdinit nodefault noyylineno noyywrap
/* Reduce lexer size, by not defining these. */
%option noyy_top_state
%option noinput nounput
%option noyyget_in noyyset_in
%option noyyget_out noyyset_out
%option noyyget_debug noyyset_debug
%option noyyget_lineno noyyset_lineno
%option extra-type="struct grub_parser_param*"
BLANK [ \t]
COMMENT ^[ \t]*#.*$
CHAR [^|&;()<> \t\n\'\"]
DIGITS [[:digit:]]+
NAME [[:alpha:]_][[:alnum:][:digit:]_]*
ESC \\.
VARIABLE ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|$\?|$\{\?\}
DQSTR \"([^\"]|\\\")*\"
SQSTR \'[^\']*\'
WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+
%x SPLIT
%x DQUOTE
%x SQUOTE
%x VAR
%%
/* White spaces */
{BLANK}+ { RECORD; }
{COMMENT} { RECORD; }
/* Special symbols */
"\n" { RECORD; return GRUB_PARSER_TOKEN_NEWLINE; }
"||" { RECORD; return GRUB_PARSER_TOKEN_OR; }
"&&" { RECORD; return GRUB_PARSER_TOKEN_AND; }
";;" { RECORD; return GRUB_PARSER_TOKEN_SEMI2; }
"|" { RECORD; return GRUB_PARSER_TOKEN_PIPE; }
"&" { RECORD; return GRUB_PARSER_TOKEN_AMP; }
";" { RECORD; return GRUB_PARSER_TOKEN_SEMI; }
"(" { RECORD; return GRUB_PARSER_TOKEN_LPAR; }
")" { RECORD; return GRUB_PARSER_TOKEN_RPAR; }
"<" { RECORD; return GRUB_PARSER_TOKEN_LT; }
">" { RECORD; return GRUB_PARSER_TOKEN_GT; }
/* Reserved words */
"!" { RECORD; return GRUB_PARSER_TOKEN_NOT; }
"{" { RECORD; return GRUB_PARSER_TOKEN_LBR; }
"}" { RECORD; return GRUB_PARSER_TOKEN_RBR; }
"[[" { RECORD; return GRUB_PARSER_TOKEN_RSQBR2; }
"]]" { RECORD; return GRUB_PARSER_TOKEN_LSQBR2; }
"time" { RECORD; return GRUB_PARSER_TOKEN_TIME; }
"case" { RECORD; return GRUB_PARSER_TOKEN_CASE; }
"do" { RECORD; return GRUB_PARSER_TOKEN_DO; }
"done" { RECORD; return GRUB_PARSER_TOKEN_DONE; }
"elif" { RECORD; return GRUB_PARSER_TOKEN_ELIF; }
"else" { RECORD; return GRUB_PARSER_TOKEN_ELSE; }
"esac" { RECORD; return GRUB_PARSER_TOKEN_ESAC; }
"fi" { RECORD; return GRUB_PARSER_TOKEN_FI; }
"for" { RECORD; return GRUB_PARSER_TOKEN_FOR; }
"if" { RECORD; return GRUB_PARSER_TOKEN_IF; }
"in" { RECORD; return GRUB_PARSER_TOKEN_IN; }
"select" { RECORD; return GRUB_PARSER_TOKEN_SELECT; }
"then" { RECORD; return GRUB_PARSER_TOKEN_THEN; }
"until" { RECORD; return GRUB_PARSER_TOKEN_UNTIL; }
"while" { RECORD; return GRUB_PARSER_TOKEN_WHILE; }
"function" { RECORD; return GRUB_PARSER_TOKEN_FUNCTION; }
"menuentry" { RECORD; return GRUB_PARSER_TOKEN_MENUENTRY; }
{NAME} { RECORD; return GRUB_PARSER_TOKEN_NAME; }
{WORD} {
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;
}
}
.|\n {
grub_script_yyerror (yyextra, "unrecognized token");
return GRUB_PARSER_TOKEN_BAD;
}
/* Split word into multiple args */
<SPLIT>{
\\. { PUSH (yytext[1]); }
\" {
yy_push_state (DQUOTE, yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
}
\' {
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;
}
<<EOF>> {
yy_pop_state (yyscanner);
yypop_buffer_state (yyscanner);
yyextra->lexerstate->merge_end = 1;
ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
}
}
<VAR>{
\? |
{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);
}
\{\?\} |
\{{DIGITS}\} |
\{{NAME}\} {
yytext[yyleng - 1] = '\0';
COPY (yytext + 1);
yy_pop_state (yyscanner);
if (YY_START == SPLIT)
ARG (GRUB_SCRIPT_ARG_TYPE_VAR);
else
ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR);
}
.|\n { return GRUB_PARSER_TOKEN_BAD; }
}
<SQUOTE>{
\' {
yy_pop_state (yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR);
}
(.|\n) { PUSH (yytext[0]); }
}
<DQUOTE>{
\" {
yy_pop_state (yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
}
\$ {
yy_push_state (VAR, yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
}
\\\\ { PUSH ('\\'); }
\\\" { PUSH ('\"'); }
\\\n { /* ignore */ }
(.|\n) { PUSH (yytext[0]); }
}
<<EOF>> {
yypop_buffer_state (yyscanner);
if (! grub_script_lexer_yywrap (yyextra))
{
yyextra->lexerstate->eof = 1;
return GRUB_PARSER_TOKEN_EOF;
}
}
%%
static void
grub_lexer_yyfree (void *ptr, yyscan_t yyscanner __attribute__ ((unused)))
{
grub_free(ptr);
}
static void*
grub_lexer_yyalloc (yy_size_t size, yyscan_t yyscanner __attribute__ ((unused)))
{
return grub_malloc (size);
}
static void*
grub_lexer_yyrealloc (void *ptr, yy_size_t size,
yyscan_t yyscanner __attribute__ ((unused)))
{
return grub_realloc (ptr, size);
}

View file

@ -0,0 +1,32 @@
#! @builddir@/grub-shell-tester
# Run GRUB script in a Qemu instance
# Copyright (C) 2010 Free Software Foundation, Inc.
#
# GRUB is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GRUB 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see <http://www.gnu.org/licenses/>.
foo=bar
echo $foo ${foo}
echo "$foo" "${foo}"
echo '$foo' '${foo}'
echo a$foob a${foo}b
echo ab"cd"ef$foo'gh'ij${foo}kl\ mn\"op\'qr\$st\(uv\<wx\>yz\)
foo=c
bar=h
echo e"$foo"${bar}o
e"$foo"${bar}o hello world
foo=echo
$foo 1234

View file

@ -0,0 +1,3 @@
#! @builddir@/grub-shell-tester
echo if then else fi for do done

View file

@ -0,0 +1,34 @@
#! @builddir@/grub-shell-tester
# Run GRUB script in a Qemu instance
# Copyright (C) 2010 Free Software Foundation, Inc.
#
# GRUB is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GRUB 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GRUB. If not, see <http://www.gnu.org/licenses/>.
var=foo
echo $var
echo "$var"
echo ${var}
echo "${var}"
echo $1 $2 $?
foo=foo
echo "" $foo
echo $bar $foo
bar=""
echo $bar $foo

View file

@ -1,7 +1,7 @@
#! /bin/bash -e #! /bin/bash -e
# Compares GRUB script output with BASH output. # Compares GRUB script output with BASH output.
# Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. # Copyright (C) 2009,2010 Free Software Foundation, Inc.
# #
# GRUB is free software: you can redistribute it and/or modify # GRUB is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View file

@ -1,7 +1,7 @@
#! /bin/bash -e #! /bin/bash -e
# Run GRUB script in a Qemu instance # Run GRUB script in a Qemu instance
# Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. # Copyright (C) 2009,2010 Free Software Foundation, Inc.
# #
# GRUB is free software: you can redistribute it and/or modify # GRUB is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View file

@ -1,7 +1,7 @@
/* grub-script-check.c - check grub script file for syntax errors */ /* grub-script-check.c - check grub script file for syntax errors */
/* /*
* GRUB -- GRand Unified Bootloader * GRUB -- GRand Unified Bootloader
* Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. * Copyright (C) 2009,2010 Free Software Foundation, Inc.
* *
* GRUB is free software: you can redistribute it and/or modify * GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -82,16 +82,8 @@ grub_script_execute_cmdif (struct grub_script_cmd *cmd __attribute__ ((unused)))
} }
grub_err_t grub_err_t
grub_script_execute_menuentry (struct grub_script_cmd *cmd) grub_script_execute_menuentry (struct grub_script_cmd *cmd __attribute__ ((unused)))
{ {
struct grub_script_cmd_menuentry *menu;
menu = (struct grub_script_cmd_menuentry *)cmd;
if (menu->sourcecode)
{
grub_free (menu->sourcecode);
menu->sourcecode = 0;
}
return 0; return 0;
} }