pulled in for-loop branch

This commit is contained in:
BVK Chaitanya 2010-01-23 11:08:25 +05:30
commit 3babea95bb
27 changed files with 1351 additions and 649 deletions

13
ChangeLog.for-loop Normal file
View file

@ -0,0 +1,13 @@
2010-01-12 BVK Chaitanya <bvk.groups@gmail.com>
For loop support to GRUB script.
* include/grub/script_sh.h (grub_script_cmdfor): New struct.
(grub_script_create_cmdfor): New function prototype.
(grub_script_execute_cmdfor): New function prototype.
* script/execute.c (grub_script_execute_cmdfor): New function.
* script/parser.y (command): New for command.
(forcmd): New grammar rule.
* script/script.c (grub_script_create_cmdfor): New function.
* util/grub-script-check.c (grub_script_execute_cmdfor): New
function.

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,32 @@ 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
check_SCRIPTS += grub_script_for1
grub_script_for1_SOURCES = tests/grub_script_for1.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
SCRIPTED_TESTS += grub_script_for1
# 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. */
@ -103,6 +106,21 @@ struct grub_script_cmdif
struct grub_script_cmd *exec_on_false; struct grub_script_cmd *exec_on_false;
}; };
/* A for statement. */
struct grub_script_cmdfor
{
struct grub_script_cmd cmd;
/* The name used as looping variable. */
struct grub_script_arg *name;
/* The words loop iterates over. */
struct grub_script_arglist *words;
/* The command list executed in each loop. */
struct grub_script_cmd *list;
};
/* A menu entry generate statement. */ /* A menu entry generate statement. */
struct grub_script_cmd_menuentry struct grub_script_cmd_menuentry
{ {
@ -121,12 +139,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 +149,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 +165,31 @@ 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;
/* Part of a multi-part token. */
char *text;
unsigned used;
unsigned size;
/* Type of text. */
grub_script_arg_type_t type;
/* Flex scanner. */
void *yyscanner;
/* Flex scanner buffer. */
void *buffer;
}; };
#define GRUB_LEXER_INITIAL_TEXT_SIZE 32
#define GRUB_LEXER_INITIAL_RECORD_SIZE 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
{ {
@ -202,6 +228,12 @@ grub_script_create_cmdif (struct grub_parser_param *state,
struct grub_script_cmd *exec_on_true, struct grub_script_cmd *exec_on_true,
struct grub_script_cmd *exec_on_false); struct grub_script_cmd *exec_on_false);
struct grub_script_cmd *
grub_script_create_cmdfor (struct grub_parser_param *state,
struct grub_script_arg *name,
struct grub_script_arglist *words,
struct grub_script_cmd *list);
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,
@ -223,12 +255,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);
@ -246,6 +282,7 @@ void grub_script_yyerror (struct grub_parser_param *, char const *);
grub_err_t grub_script_execute_cmdline (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_cmdline (struct grub_script_cmd *cmd);
grub_err_t grub_script_execute_cmdblock (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_cmdblock (struct grub_script_cmd *cmd);
grub_err_t grub_script_execute_cmdif (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_cmdif (struct grub_script_cmd *cmd);
grub_err_t grub_script_execute_cmdfor (struct grub_script_cmd *cmd);
grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd); grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd);
/* Execute any GRUB pre-parsed command or script. */ /* Execute any GRUB pre-parsed command or script. */
@ -284,7 +321,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, int *count);
#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 /* Expand arguments in ARGLIST into multiple arguments. */
concatenated and all values of the variables are filled in. */ char **
char * grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count)
grub_script_execute_argument_to_string (struct grub_script_arg *arg)
{ {
int size = 0; int i;
char *val; int oom;
char *chararg; int argc;
struct grub_script_arg *argi; int empty;
char *ptr;
char **argv;
char *value;
struct grub_script_arg *arg;
/* First determine the size of the argument. */ auto void push (char *str);
for (argi = arg; argi; argi = argi->next) void push (char *str)
{
char **p;
if (oom)
return;
p = grub_realloc (argv, ALIGN_UP (sizeof(char*) * (argc + 1), 32));
if (!p)
oom = 1;
else
{
p[argc++] = str;
argv = p;
}
}
auto char* append (const char *str, grub_size_t nchar);
char* append (const char *str, grub_size_t nchar)
{
int len;
int old;
char *p;
if (oom || !str)
return 0;
len = nchar ?: grub_strlen (str);
old = argv[argc - 1] ? grub_strlen (argv[argc - 1]) : 0;
p = grub_realloc (argv[argc - 1], ALIGN_UP(old + len + 1, 32));
if (p)
{
grub_strncpy (p + old, str, len);
p[old + len] = '\0';
}
else
{
oom = 1;
grub_free (argv[argc - 1]);
}
argv[argc - 1] = p;
return argv[argc - 1];
}
/* 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)
{ {
if (argi->type == 1) empty = 1;
arg = arglist->arg;
while (arg)
{ {
val = grub_env_get (argi->str); switch (arg->type)
if (val) {
size += grub_strlen (val); case GRUB_SCRIPT_ARG_TYPE_VAR:
value = grub_env_get (arg->str);
while (value && *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;
} }
else if (!empty)
size += grub_strlen (argi->str); push (0);
} }
/* Create the argument. */ if (oom)
chararg = grub_malloc (size + 1);
if (! chararg)
return 0;
*chararg = '\0';
/* First determine the size of the argument. */
for (argi = arg; argi; argi = argi->next)
{ {
if (argi->type == 1) for (i = 0; i < argc; i++)
{ grub_free (argv[i]);
val = grub_env_get (argi->str); grub_free (argv);
if (val) argv = 0;
grub_strcat (chararg, val);
}
else
grub_strcat (chararg, argi->str);
} }
return chararg; if (argv)
*count = argc - 1;
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, &argcount);
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,12 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd)
return 0; return 0;
} }
} }
grub_free (cmdname);
if (cmdline->arglist->next)
{
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++)
@ -197,12 +282,37 @@ grub_script_execute_cmdif (struct grub_script_cmd *cmd)
return grub_script_execute_cmd (cmdif->exec_on_false); return grub_script_execute_cmd (cmdif->exec_on_false);
} }
/* Execute a for statement. */
grub_err_t
grub_script_execute_cmdfor (struct grub_script_cmd *cmd)
{
int i;
int result;
char **args;
int argcount;
struct grub_script_cmdfor *cmdfor = (struct grub_script_cmdfor *) cmd;
args = grub_script_execute_arglist_to_argv (cmdfor->words, &argcount);
if (!args)
return grub_errno;
result = 0;
for (i = 0; i < argcount; i++)
{
grub_env_set (cmdfor->name->str, args[i]);
result = grub_script_execute_cmd (cmdfor->list);
grub_free (args[i]);
}
grub_free (args);
return result;
}
/* Execute the menu entry generate statement. */ /* Execute the menu entry generate statement. */
grub_err_t 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 +321,9 @@ 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, &argcount);
if (!args)
/* Create argv from the arguments. */ return grub_errno;
args = grub_malloc (sizeof (char *) * argcount);
if (! args)
{
return grub_errno;
}
for (arglist = cmd_menuentry->arglist; arglist; arglist = arglist->next)
{
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,308 @@ 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_INITIAL_RECORD_SIZE;
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';
}
return state->recording; while (*end && grub_isspace (*end)) end--;
if (*end == '}') end--;
while (*end && grub_isspace (*end)) end--;
*end = '\0';
return start;
}
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 #define MAX(a,b) ((a) < (b) ? (b) : (a))
in the character stream. */
static void /* Record STR if input recording is enabled. */
recordchar (struct grub_lexer_param *state, char c) void
grub_script_lexer_record (struct grub_parser_param *parser, char *str)
{ {
if (state->recordpos == state->recordlen) int len;
char *old;
struct grub_lexer_param *lexer = parser->lexerstate;
if (!lexer->record)
return;
len = grub_strlen (str);
if (lexer->recordpos + len + 1 > lexer->recordlen)
{ {
char *old = state->recording; old = lexer->recording;
state->recordlen += 100; lexer->recordlen = MAX (len, lexer->recordlen) * 2;
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;
lexer->recordpos = 0;
lexer->recordlen /= 2;
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 + 2);
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 0;
return token;
} }
for (;! state->done; firstrun = 0) line = 0;
buffer = 0;
lexerstate->getline (&line, 1);
if (!line)
{ {
if (! state->script || ! *state->script) grub_script_yyerror (parserstate, 0); /* XXX this could be for ^C case? */
return 0;
}
len = grub_strlen (line);
if (line[len - 1] == '\n')
{
buffer = yy_scan_string (line, lexerstate->yyscanner);
}
else
{
line2 = append_newline (line);
if (line2)
{ {
/* Check if more tokens are requested by the parser. */ buffer = yy_scan_string (line2, lexerstate->yyscanner);
if (((state->refs && ! parsestate->err) grub_free (line2);
|| 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;
} }
} }
if (yylval->arg == 0) grub_free (line);
if (!buffer)
{ {
int token = state->tokenonhold; grub_script_yyerror (parserstate, 0);
state->tokenonhold = 0; return 0;
return token;
} }
if (yylval->arg->next == 0 && yylval->arg->type == GRUB_SCRIPT_ARG_TYPE_STR) 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->size = GRUB_LEXER_INITIAL_TEXT_SIZE;
lexerstate->text = grub_malloc (lexerstate->size);
if (!lexerstate->text)
{ {
/* Detect some special tokens. */ grub_free (lexerstate);
if (! grub_strcmp (yylval->arg->str, "while")) return 0;
return GRUB_PARSER_TOKEN_WHILE;
else if (! grub_strcmp (yylval->arg->str, "if"))
return GRUB_PARSER_TOKEN_IF;
else if (! grub_strcmp (yylval->arg->str, "function"))
return GRUB_PARSER_TOKEN_FUNCTION;
else if (! grub_strcmp (yylval->arg->str, "menuentry"))
return GRUB_PARSER_TOKEN_MENUENTRY;
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; 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->used = 1;
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)
{
str = lexerstate->text;
type = lexerstate->type;
}
else
{
str = yyget_text (lexerstate->yyscanner);
type = GRUB_SCRIPT_ARG_TYPE_TEXT;
}
grub_dprintf("lexer", "token %u text [%s]\n", token, str);
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
@ -21,10 +21,10 @@
#include <grub/script_sh.h> #include <grub/script_sh.h>
#include <grub/mm.h> #include <grub/mm.h>
#define YYFREE grub_free #define YYFREE grub_free
#define YYMALLOC grub_malloc #define YYMALLOC grub_malloc
#define YYLTYPE_IS_TRIVIAL 0 #define YYLTYPE_IS_TRIVIAL 0
#define YYENABLE_NLS 0 #define YYENABLE_NLS 0
%} %}
@ -35,163 +35,218 @@
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 forcmd command
%type <cmd> commands1 menuentry statement
%pure-parser %pure-parser
%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
| error {
{ struct grub_script_cmdblock *cmdblock;
$$ = 0; cmdblock = (struct grub_script_cmdblock *) $1;
yyerror (state, "Incorrect command"); $$ = grub_script_add_cmd (state, cmdblock, $2);
state->err = 1; }
yyerrok; | error
} {
$$ = 0;
yyerror (state, "Incorrect command");
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); }
arguments: GRUB_PARSER_TOKEN_ARG | "do" { $$ = grub_script_add_arglist (state, 0, $1); }
{ | "done" { $$ = grub_script_add_arglist (state, 0, $1); }
$$ = grub_script_add_arglist (state, 0, $1); | "elif" { $$ = grub_script_add_arglist (state, 0, $1); }
} | "else" { $$ = grub_script_add_arglist (state, 0, $1); }
| arguments GRUB_PARSER_TOKEN_ARG | "esac" { $$ = grub_script_add_arglist (state, 0, $1); }
{ | "fi" { $$ = grub_script_add_arglist (state, 0, $1); }
$$ = grub_script_add_arglist (state, $1, $2); | "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; }
; ;
grubcmd: arguments arguments0: /* Empty */ { $$ = 0; }
{ | arguments1 { $$ = $1; }
$$ = grub_script_create_cmdline (state, $1); ;
} arguments1: argument arguments0
{
if ($1 && $2)
{
$1->next = $2;
$1->argcount += $2->argcount;
$2->argcount = 0;
}
$$ = $1;
}
;
grubcmd: word arguments0
{
if ($1 && $2) {
$1->next = $2;
$1->argcount += $2->argcount;
$2->argcount = 0;
}
$$ = 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; } | forcmd { $$ = $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 grub_script_lexer_ref (state->lexerstate);
executed on the right moment. So the `commands' rule should be state->func_mem = grub_script_mem_record (state);
recognized after executing the `grub_script_mem_record; and before }
`grub_script_mem_record_stop'. */ delimiters0 "{" commands1 delimiters1 "}"
function: "function" GRUB_PARSER_TOKEN_ARG {
{ struct grub_script *script;
grub_script_lexer_ref (state->lexerstate); state->func_mem = grub_script_mem_record_stop (state,
} newlines '{' state->func_mem);
{ script = grub_script_create ($6, state->func_mem);
/* The first part of the function was recognized. if (script)
Now start recording the memory usage to store grub_script_function_create ($2, script);
this function. */
state->func_mem = grub_script_mem_record (state);
} newlines commands '}'
{
struct grub_script *script;
/* All the memory usage for parsing this function grub_script_lexer_deref (state->lexerstate);
was recorded. */ }
state->func_mem = grub_script_mem_record_stop (state,
state->func_mem);
script = grub_script_create ($8, state->func_mem);
if (script)
grub_script_function_create ($2, script);
grub_script_lexer_deref (state->lexerstate);
}
; ;
/* Carefully designed, together with `menuentry' so everything happens menuentry: "menuentry"
just in the expected order. */ {
commandblock: '{' grub_script_lexer_ref (state->lexerstate);
{ }
grub_script_lexer_ref (state->lexerstate); arguments1
} {
newlines commands '}' grub_script_lexer_record_start (state);
{ }
grub_script_lexer_deref (state->lexerstate); delimiters0 "{" commands1 delimiters1 "}"
$$ = $4; {
} char *menu_entry;
menu_entry = grub_script_lexer_record_stop (state);
grub_script_lexer_deref (state->lexerstate);
$$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0);
}
; ;
/* A menu entry. Carefully save the memory that is allocated. */ if: "if" { grub_script_lexer_ref (state->lexerstate); }
menuentry: "menuentry" ;
{ ifcmd: if commands1 delimiters1 "then" commands1 delimiters1 "fi"
grub_script_lexer_ref (state->lexerstate); {
} arguments newlines '{' $$ = grub_script_create_cmdif (state, $2, $5, 0);
{ grub_script_lexer_deref (state->lexerstate);
grub_script_lexer_record_start (state->lexerstate); }
} newlines commands '}' | if commands1 delimiters1 "then" commands1 delimiters1 "else" commands1 delimiters1 "fi"
{ {
char *menu_entry; $$ = grub_script_create_cmdif (state, $2, $5, $8);
menu_entry = grub_script_lexer_record_stop (state->lexerstate); grub_script_lexer_deref (state->lexerstate);
grub_script_lexer_deref (state->lexerstate); }
$$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0);
}
; ;
/* The first part of the if statement. It's used to switch the lexer forcmd: "for" "name"
to a state in which it demands more tokens. */ {
if_statement: "if" { grub_script_lexer_ref (state->lexerstate); } grub_script_lexer_ref (state->lexerstate);
; }
"in" arguments0 delimiters1 "do" commands1 delimiters1 "done"
/* The if statement. */ {
if: if_statement commands "then" newlines commands "fi" $$ = grub_script_create_cmdfor (state, $2, $5, $8);
{ grub_script_lexer_deref (state->lexerstate);
$$ = grub_script_create_cmdif (state, $2, $5, 0); }
grub_script_lexer_deref (state->lexerstate);
}
| if_statement commands "then" newlines commands "else" newlines commands "fi"
{
$$ = grub_script_create_cmdif (state, $2, $5, $8);
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;
@ -94,32 +94,40 @@ grub_script_mem_record_stop (struct grub_parser_param *state,
void void
grub_script_free (struct grub_script *script) grub_script_free (struct grub_script *script)
{ {
if (! script) if (!script)
return; return;
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;
if (! arg) if (!arg)
return argpart; return argpart;
for (ll = arg; ll->next; ll = ll->next); for (ll = arg; ll->next; ll = ll->next);
@ -132,19 +140,24 @@ 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;
if (! list) if (!list)
{ {
link->argcount++; link->argcount++;
return link; return link;
@ -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;
@ -202,6 +221,30 @@ grub_script_create_cmdif (struct grub_parser_param *state,
return (struct grub_script_cmd *) cmd; return (struct grub_script_cmd *) cmd;
} }
/* Create a command that functions as a for statement. */
struct grub_script_cmd *
grub_script_create_cmdfor (struct grub_parser_param *state,
struct grub_script_arg *name,
struct grub_script_arglist *words,
struct grub_script_cmd *list)
{
struct grub_script_cmdfor *cmd;
grub_dprintf ("scripting", "cmdfor\n");
cmd = grub_script_malloc (state, sizeof (*cmd));
if (! cmd)
return 0;
cmd->cmd.exec = grub_script_execute_cmdfor;
cmd->cmd.next = 0;
cmd->name = name;
cmd->words = words;
cmd->list = list;
return (struct grub_script_cmd *) cmd;
}
/* Create a command that adds a menu entry to the menu. Title is an /* Create a command that adds a menu entry to the menu. Title is an
argument that is parsed to generate a string that can be used as argument that is parsed to generate a string that can be used as
the title. The sourcecode for this entry is passed in SOURCECODE. the title. The sourcecode for this entry is passed in SOURCECODE.
@ -209,30 +252,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,15 +277,19 @@ 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)
return (struct grub_script_cmd *) cmdblock; return (struct grub_script_cmd *) cmdblock;
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,22 +297,29 @@ 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)
{ {
struct grub_script *parsed; struct grub_script *parsed;
parsed = grub_malloc (sizeof (*parsed)); parsed = grub_malloc (sizeof (*parsed));
if (! parsed) if (!parsed)
{ {
grub_script_mem_free (mem); grub_script_mem_free (mem);
grub_free (cmd); grub_free (cmd);
@ -304,16 +344,16 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
struct grub_parser_param *parsestate; struct grub_parser_param *parsestate;
parsed = grub_malloc (sizeof (*parsed)); parsed = grub_malloc (sizeof (*parsed));
if (! parsed) if (!parsed)
return 0; return 0;
parsestate = grub_zalloc (sizeof (*parsestate)); parsestate = grub_zalloc (sizeof (*parsestate));
if (! parsestate) if (!parsestate)
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);
grub_free (parsestate); grub_free (parsestate);
@ -330,7 +370,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 +378,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;

342
script/yylex.l Normal file
View file

@ -0,0 +1,342 @@
%{
/* 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 COPY(str, hint) \
do { \
copy_string (yyextra, str, hint); \
} 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);
static void copy_string (struct grub_parser_param *, const char *,
unsigned hint);
%}
%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 \"([^\\\"]|{ESC})*\"
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 */
grub_dprintf ("lexer", "word: [%s]\n", 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>{
\\. { COPY (yytext + 1, yyleng - 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);
}
\\ |
[^\"\'$\\]+ { COPY (yytext, yyleng); }
<<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, 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, yyleng - 2);
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);
}
[^\']+ { COPY (yytext, yyleng); }
}
<DQUOTE>{
\" {
yy_pop_state (yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
}
\$ {
yy_push_state (VAR, yyscanner);
ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
}
\\\\ { COPY ("\\", 1); }
\\\" { COPY ("\"", 1); }
\\\n { /* ignore */ }
[^\"$\\\n]+ { COPY (yytext, yyleng); }
(.|\n) { COPY (yytext, yyleng); }
}
<<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);
}
#define MAX(a,b) ((a) < (b) ? (b) : (a))
static void copy_string (struct grub_parser_param *parser, const char *str, unsigned hint)
{
int size;
char *ptr;
unsigned len;
len = hint ? hint : grub_strlen (str);
if (parser->lexerstate->used + len >= parser->lexerstate->size)
{
size = MAX (len, 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;
}

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

27
tests/grub_script_for1.in Normal file
View file

@ -0,0 +1,27 @@
#! @builddir@/grub-shell-tester
for x in one two 'three 3' "four 4" five six-6; do echo $x; done
for x in one two 'three 3' "four 4" five six-6
do
echo $x
done
foo="1 2"
for x in ab${foo}cd; do echo $x; done
for x in "ab${foo}cd"; do echo $x; done
a="one two three"
y=foo
echo $y
for y in $a; do
echo $y
done
echo $y
b="one two three"
for z in $b; do
echo $z
done
echo $z

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,14 @@ 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_cmdfor (struct grub_script_cmd *cmd __attribute__ ((unused)))
{ {
struct grub_script_cmd_menuentry *menu; return 0;
menu = (struct grub_script_cmd_menuentry *)cmd; }
if (menu->sourcecode) grub_err_t
{ grub_script_execute_menuentry (struct grub_script_cmd *cmd __attribute__ ((unused)))
grub_free (menu->sourcecode); {
menu->sourcecode = 0;
}
return 0; return 0;
} }