# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: address@hidden
# target_branch: ../mainline/
# testament_sha1: c3ba37e16d26006af32fc9ee46b2de8e3319125c
# timestamp: 2010-01-23 10:34:33 +0530
# base_revision_id: address@hidden
#
# Begin patch
=== added file 'ChangeLog.lexer-rewrite'
--- ChangeLog.lexer-rewrite 1970-01-01 00:00:00 +0000
+++ ChangeLog.lexer-rewrite 2010-01-22 13:37:27 +0000
@@ -0,0 +1,70 @@
+2010-01-10 BVK Chaitanya
+
+ * 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.
+
=== modified file 'conf/any-emu.rmk'
--- conf/any-emu.rmk 2010-01-20 20:31:39 +0000
+++ conf/any-emu.rmk 2010-01-22 13:37:27 +0000
@@ -1,7 +1,7 @@
# -*- makefile -*-
# 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
util/grub-emu.c_DEPENDENCIES = grub_emu_init.h
@@ -33,6 +33,8 @@
commands/terminal.c normal/context.c lib/charset.c \
script/main.c script/execute.c script/function.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/acorn.c partmap/gpt.c \
\
@@ -99,5 +101,11 @@
$(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/script/parser.y
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.*\(\|\|\|\|\)//g' grub_script.yy.h
+ sed -i 's/^#include.*\(\|\|\|\|\)//g' grub_script.yy.c
+DISTCLEANFILES += grub_script.yy.c grub_script.yy.h
+
bin_UTILITIES += grub-bin2h
grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c
=== modified file 'conf/common.rmk'
--- conf/common.rmk 2010-01-20 20:31:39 +0000
+++ conf/common.rmk 2010-01-22 13:37:27 +0000
@@ -91,13 +91,21 @@
bin_UTILITIES += grub-bin2h
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.*\(\|\|\|\|\)//g' grub_script.yy.h
+ sed -i 's/^#include.*\(\|\|\|\|\)//g' grub_script.yy.c
+DISTCLEANFILES += grub_script.yy.c grub_script.yy.h
+
# For grub-script-check.
bin_UTILITIES += grub-script-check
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 \
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/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.
grub_script.tab.c grub_script.tab.h: script/parser.y
@@ -594,7 +602,7 @@
# For sh.mod.
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_LDFLAGS = $(COMMON_LDFLAGS)
=== modified file 'conf/i386-coreboot.rmk'
--- conf/i386-coreboot.rmk 2010-01-20 20:31:39 +0000
+++ conf/i386-coreboot.rmk 2010-01-22 13:37:27 +0000
@@ -5,7 +5,7 @@
COMMON_LDFLAGS = -m32 -nostdlib
# 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.
=== modified file 'conf/i386-efi.rmk'
--- conf/i386-efi.rmk 2010-01-20 20:31:39 +0000
+++ conf/i386-efi.rmk 2010-01-22 13:37:27 +0000
@@ -5,7 +5,7 @@
COMMON_LDFLAGS = -melf_i386 -nostdlib
# 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.
bin_UTILITIES = grub-mkimage
=== modified file 'conf/i386-ieee1275.rmk'
--- conf/i386-ieee1275.rmk 2010-01-20 20:31:39 +0000
+++ conf/i386-ieee1275.rmk 2010-01-22 13:37:27 +0000
@@ -5,7 +5,7 @@
COMMON_LDFLAGS = -nostdlib
# 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.
pkglib_PROGRAMS = kernel.img
=== modified file 'conf/i386-pc.rmk'
--- conf/i386-pc.rmk 2010-01-20 20:31:39 +0000
+++ conf/i386-pc.rmk 2010-01-22 13:37:27 +0000
@@ -7,7 +7,7 @@
COMMON_LDFLAGS = -m32 -nostdlib
# 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.
pkglib_IMAGES = boot.img cdboot.img diskboot.img kernel.img lnxboot.img \
=== modified file 'conf/powerpc-ieee1275.rmk'
--- conf/powerpc-ieee1275.rmk 2010-01-20 20:31:39 +0000
+++ conf/powerpc-ieee1275.rmk 2010-01-22 13:37:27 +0000
@@ -6,7 +6,7 @@
COMMON_LDFLAGS += -nostdlib
# 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.
=== modified file 'conf/sparc64-ieee1275.rmk'
--- conf/sparc64-ieee1275.rmk 2010-01-20 20:31:39 +0000
+++ conf/sparc64-ieee1275.rmk 2010-01-22 13:37:27 +0000
@@ -6,7 +6,7 @@
COMMON_LDFLAGS = -melf64_sparc -nostdlib -mno-relax
# 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.
pkglib_IMAGES = boot.img diskboot.img kernel.img
=== modified file 'conf/tests.rmk'
--- conf/tests.rmk 2010-01-12 10:19:40 +0000
+++ conf/tests.rmk 2010-01-22 13:37:27 +0000
@@ -37,12 +37,28 @@
check_SCRIPTS += example_grub_script_test
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"
-SCRIPTED_TESTS = example_scripted_test
-SCRIPTED_TESTS += example_grub_script_test
-UNIT_TESTS = example_unit_test
-FUNCTIONAL_TESTS = example_functional_test.mod
+# SCRIPTED_TESTS = example_scripted_test
+# SCRIPTED_TESTS += example_grub_script_test
+# UNIT_TESTS = example_unit_test
+# 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
$(SCRIPTED_TESTS): grub-shell grub-shell-tester
=== modified file 'conf/x86_64-efi.rmk'
--- conf/x86_64-efi.rmk 2010-01-20 20:31:39 +0000
+++ conf/x86_64-efi.rmk 2010-01-22 13:37:27 +0000
@@ -5,7 +5,7 @@
COMMON_LDFLAGS = -melf_x86_64 -nostdlib
# 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.
bin_UTILITIES = grub-mkimage
=== modified file 'configure.ac'
--- configure.ac 2010-01-20 12:27:23 +0000
+++ configure.ac 2010-01-22 13:37:27 +0000
@@ -163,6 +163,11 @@
AC_MSG_ERROR([cmp is not found])
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])
if test "x$YACC" = x; then
AC_MSG_ERROR([bison is not found])
=== modified file 'include/grub/script_sh.h'
--- include/grub/script_sh.h 2010-01-20 07:18:35 +0000
+++ include/grub/script_sh.h 2010-01-23 03:42:46 +0000
@@ -1,7 +1,7 @@
/* normal_parser.h */
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -45,8 +45,11 @@
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;
/* A part of an argument. */
@@ -121,12 +124,6 @@
/* State of the lexer as passed to the lexer. */
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
expected, but not available. */
grub_reader_getline_t getline;
@@ -137,10 +134,6 @@
depleted. */
int refs;
- /* The character stream that has to be parsed. */
- char *script;
- char *newscript; /* XXX */
-
/* While walking through the databuffer, `record' the characters to
this other buffer. It can be used to edit the menu entry at a
later moment. */
@@ -157,13 +150,31 @@
/* Size of RECORDING. */
int recordlen;
- /* The token that is already parsed but not yet returned. */
- int tokenonhold;
-
- /* Was the last token a newline? */
- int was_newline;
+ /* End of file reached. */
+ int eof;
+
+ /* Merge multiple word tokens. */
+ 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. */
struct grub_parser_param
{
@@ -223,12 +234,16 @@
struct grub_script *grub_script_create (struct grub_script_cmd *cmd,
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);
+void grub_script_lexer_fini (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_record_start (struct grub_lexer_param *);
-char *grub_script_lexer_record_stop (struct grub_lexer_param *);
+void grub_script_lexer_record_start (struct grub_parser_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. */
struct grub_script_mem *grub_script_mem_record (struct grub_parser_param *state);
@@ -284,7 +299,7 @@
int grub_script_function_call (grub_script_function_t func,
int argc, char **args);
-char *
-grub_script_execute_argument_to_string (struct grub_script_arg *arg);
+char **
+grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count);
#endif /* ! GRUB_NORMAL_PARSER_HEADER */
=== modified file 'script/execute.c'
--- script/execute.c 2009-12-29 09:04:06 +0000
+++ script/execute.c 2010-01-23 04:58:52 +0000
@@ -1,7 +1,7 @@
/* execute.c -- Execute a GRUB script. */
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -35,49 +35,146 @@
return cmd->exec (cmd);
}
-/* Parse ARG and return the textual representation. Add strings are
- concatenated and all values of the variables are filled in. */
-char *
-grub_script_execute_argument_to_string (struct grub_script_arg *arg)
+/* Expand arguments in ARGLIST into multiple arguments. */
+char **
+grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count)
{
- int size = 0;
- char *val;
- char *chararg;
- struct grub_script_arg *argi;
-
- /* First determine the size of the argument. */
- for (argi = arg; argi; argi = argi->next)
- {
- if (argi->type == 1)
- {
- val = grub_env_get (argi->str);
- if (val)
- size += grub_strlen (val);
- }
- else
- size += grub_strlen (argi->str);
- }
-
- /* Create the argument. */
- 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)
- {
- val = grub_env_get (argi->str);
- if (val)
- grub_strcat (chararg, val);
- }
- else
- grub_strcat (chararg, argi->str);
- }
-
- return chararg;
+ 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)
+ {
+ 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)
+ {
+ empty = 1;
+ arg = arglist->arg;
+ while (arg)
+ {
+ switch (arg->type)
+ {
+ 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;
+ }
+ if (!empty)
+ push (0);
+ }
+
+ if (oom)
+ {
+ for (i = 0; i < argc; i++)
+ grub_free (argv[i]);
+ grub_free (argv);
+ argv = 0;
+ }
+
+ if (argv)
+ *count = argc - 1;
+
+ return argv;
}
/* Execute a single command line. */
@@ -85,7 +182,6 @@
grub_script_execute_cmdline (struct grub_script_cmd *cmd)
{
struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd;
- struct grub_script_arglist *arglist;
char **args = 0;
int i = 0;
grub_command_t grubcmd;
@@ -96,7 +192,11 @@
char *cmdname;
/* 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);
if (! grubcmd)
{
@@ -129,27 +229,12 @@
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. */
if (grubcmd)
- ret = (grubcmd->func) (grubcmd, argcount, args);
+ ret = (grubcmd->func) (grubcmd, argcount - 1, args + 1);
else
- ret = grub_script_function_call (func, argcount, args);
+ ret = grub_script_function_call (func, argcount - 1, args + 1);
/* Free arguments. */
for (i = 0; i < argcount; i++)
@@ -202,7 +287,6 @@
grub_script_execute_menuentry (struct grub_script_cmd *cmd)
{
struct grub_script_cmd_menuentry *cmd_menuentry;
- struct grub_script_arglist *arglist;
char **args = 0;
int argcount = 0;
int i = 0;
@@ -211,22 +295,9 @@
if (cmd_menuentry->arglist)
{
- argcount = cmd_menuentry->arglist->argcount;
-
- /* Create argv from the arguments. */
- 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;
- }
+ args = grub_script_execute_arglist_to_argv (cmd_menuentry->arglist, &argcount);
+ if (!args)
+ return grub_errno;
}
grub_normal_add_menu_entry (argcount, (const char **) args,
=== modified file 'script/function.c'
--- script/function.c 2009-11-23 15:37:33 +0000
+++ script/function.c 2010-01-22 13:37:27 +0000
@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
if (! func)
return 0;
- func->name = grub_script_execute_argument_to_string (functionname_arg);
+ func->name = grub_strdup (functionname_arg->str);
if (! func->name)
{
grub_free (func);
=== modified file 'script/lexer.c'
--- script/lexer.c 2009-12-24 22:53:05 +0000
+++ script/lexer.c 2010-01-23 04:44:32 +0000
@@ -1,7 +1,7 @@
/* lexer.c - The scripting lexer. */
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -23,42 +23,7 @@
#include
#include "grub_script.tab.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;
-}
+#include "grub_script.yy.h"
void
grub_script_lexer_ref (struct grub_lexer_param *state)
@@ -74,360 +39,308 @@
/* Start recording all characters passing through the lexer. */
void
-grub_script_lexer_record_start (struct grub_lexer_param *state)
+grub_script_lexer_record_start (struct grub_parser_param *parser)
{
- state->record = 1;
- state->recordlen = 100;
- state->recording = grub_malloc (state->recordlen);
- state->recordpos = 0;
+ struct grub_lexer_param *lexer = parser->lexerstate;
+
+ lexer->record = 1;
+ 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 *
-grub_script_lexer_record_stop (struct grub_lexer_param *state)
+grub_script_lexer_record_stop (struct grub_parser_param *parser)
{
- state->record = 0;
-
- /* Delete the last character, it is a `}'. */
- if (state->recordpos > 0)
- {
- if (state->recording[--state->recordpos] != '}')
- {
- grub_printf ("Internal error while parsing menu entry");
- for (;;); /* XXX */
- }
- state->recording[state->recordpos] = '\0';
- }
-
- return state->recording;
+ char *ptr;
+ char *result;
+ struct grub_lexer_param *lexer = parser->lexerstate;
+
+ auto char *compact (char *start, char *end);
+ char *compact (char *start, char *end)
+ {
+ /* Delete '{' and '}' characters and whitespaces. */
+ while (*start && grub_isspace (*start)) start++;
+ if (*start == '{') start++;
+ while (*start && grub_isspace (*start)) start++;
+
+ 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
- in the character stream. */
-static void
-recordchar (struct grub_lexer_param *state, char c)
+#define MAX(a,b) ((a) < (b) ? (b) : (a))
+
+/* Record STR if input recording is enabled. */
+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;
- state->recordlen += 100;
- state->recording = grub_realloc (state->recording, state->recordlen);
- if (! state->recording)
+ old = lexer->recording;
+ lexer->recordlen = MAX (len, lexer->recordlen) * 2;
+ lexer->recording = grub_realloc (lexer->recording, lexer->recordlen);
+ if (!lexer->recording)
{
grub_free (old);
- state->record = 0;
- }
- }
- state->recording[state->recordpos++] = c;
-}
-
-/* Fetch the next character for the lexer. */
-static void
-nextchar (struct grub_lexer_param *state)
-{
- if (state->record)
- recordchar (state, *state->script);
- state->script++;
-}
-
-int
-grub_script_yylex (union YYSTYPE *yylval, struct grub_parser_param *parsestate)
-{
- grub_parser_state_t newstate;
- char use;
- struct grub_lexer_param *state = parsestate->lexerstate;
- int firstrun = 1;
-
- yylval->arg = 0;
-
- if (state->tokenonhold)
- {
- int token = state->tokenonhold;
- 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);
+ lexer->record = 0;
+ lexer->recordpos = 0;
+ lexer->recordlen /= 2;
+ grub_script_yyerror (parser, 0);
+ return;
+ }
+ }
+ grub_strcpy (lexer->recording + lexer->recordpos, str);
+ lexer->recordpos += len;
+}
+
+/* Append '\n' to SRC, before '\0' */
+static char *
+append_newline (const char *src)
+{
+ char *line;
+ grub_size_t len;
+
+ 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
+grub_script_lexer_yywrap (struct grub_parser_param *parserstate)
+{
+ int len;
+ char *line;
+ char *line2;
+ YY_BUFFER_STATE buffer;
+ struct grub_lexer_param *lexerstate = parserstate->lexerstate;
+
+ if (!lexerstate->refs)
+ return 0;
+
+ if (!lexerstate->getline)
+ {
+ grub_script_yyerror (parserstate, "unexpected end of file");
+ return 0;
+ }
+
+ line = 0;
+ buffer = 0;
+ lexerstate->getline (&line, 1);
+ if (!line)
+ {
+ 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)
+ {
+ buffer = yy_scan_string (line2, lexerstate->yyscanner);
+ grub_free (line2);
+ }
+ }
+
+ 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->size = GRUB_LEXER_INITIAL_TEXT_SIZE;
+ lexerstate->text = grub_malloc (lexerstate->size);
+ 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
+grub_script_lexer_fini (struct grub_lexer_param *lexerstate)
+{
+ 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
{
- /* 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;
+ str = yyget_text (lexerstate->yyscanner);
+ type = GRUB_SCRIPT_ARG_TYPE_TEXT;
}
- }
-
- if (yylval->arg == 0)
- {
- int token = state->tokenonhold;
- state->tokenonhold = 0;
- return token;
- }
-
- if (yylval->arg->next == 0 && yylval->arg->type == GRUB_SCRIPT_ARG_TYPE_STR)
- {
- /* Detect some special tokens. */
- if (! grub_strcmp (yylval->arg->str, "while"))
- 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;
+ 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 *lex __attribute__ ((unused)),
- char const *err)
+grub_script_yyerror (struct grub_parser_param *state, char const *err)
{
- grub_printf ("%s\n", err);
+ if (err)
+ grub_error (GRUB_ERR_INVALID_COMMAND, err);
+
+ grub_print_error ();
+ state->err++;
}
=== modified file 'script/parser.y'
--- script/parser.y 2009-11-23 15:37:33 +0000
+++ script/parser.y 2010-01-23 04:46:05 +0000
@@ -1,7 +1,7 @@
/* parser.y - The scripting parser. */
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -21,10 +21,10 @@
#include
#include
-#define YYFREE grub_free
-#define YYMALLOC grub_malloc
+#define YYFREE grub_free
+#define YYMALLOC grub_malloc
#define YYLTYPE_IS_TRIVIAL 0
-#define YYENABLE_NLS 0
+#define YYENABLE_NLS 0
%}
@@ -35,163 +35,206 @@
char *string;
}
-%token GRUB_PARSER_TOKEN_IF "if"
-%token GRUB_PARSER_TOKEN_WHILE "while"
-%token GRUB_PARSER_TOKEN_FUNCTION "function"
-%token GRUB_PARSER_TOKEN_MENUENTRY "menuentry"
-%token GRUB_PARSER_TOKEN_ELSE "else"
-%token GRUB_PARSER_TOKEN_THEN "then"
-%token GRUB_PARSER_TOKEN_FI "fi"
-%token GRUB_PARSER_TOKEN_ARG
-%type script_init script grubcmd command commands commandblock menuentry if
-%type arguments;
-%type GRUB_PARSER_TOKEN_ARG;
+%token GRUB_PARSER_TOKEN_BAD
+%token GRUB_PARSER_TOKEN_EOF 0 "end-of-input"
+
+%token GRUB_PARSER_TOKEN_NEWLINE "\n"
+%token GRUB_PARSER_TOKEN_AND "&&"
+%token GRUB_PARSER_TOKEN_OR "||"
+%token GRUB_PARSER_TOKEN_SEMI2 ";;"
+%token GRUB_PARSER_TOKEN_PIPE "|"
+%token GRUB_PARSER_TOKEN_AMP "&"
+%token GRUB_PARSER_TOKEN_SEMI ";"
+%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 GRUB_PARSER_TOKEN_CASE "case"
+%token GRUB_PARSER_TOKEN_DO "do"
+%token GRUB_PARSER_TOKEN_DONE "done"
+%token GRUB_PARSER_TOKEN_ELIF "elif"
+%token GRUB_PARSER_TOKEN_ELSE "else"
+%token GRUB_PARSER_TOKEN_ESAC "esac"
+%token GRUB_PARSER_TOKEN_FI "fi"
+%token GRUB_PARSER_TOKEN_FOR "for"
+%token GRUB_PARSER_TOKEN_IF "if"
+%token GRUB_PARSER_TOKEN_IN "in"
+%token GRUB_PARSER_TOKEN_SELECT "select"
+%token GRUB_PARSER_TOKEN_THEN "then"
+%token GRUB_PARSER_TOKEN_UNTIL "until"
+%token GRUB_PARSER_TOKEN_WHILE "while"
+%token GRUB_PARSER_TOKEN_TIME "time"
+%token GRUB_PARSER_TOKEN_FUNCTION "function"
+%token GRUB_PARSER_TOKEN_MENUENTRY "menuentry"
+%token GRUB_PARSER_TOKEN_NAME "name"
+%token GRUB_PARSER_TOKEN_WORD "word"
+
+%type word argument arguments0 arguments1
+%type script_init script grubcmd ifcmd command
+%type commands1 menuentry statement
%pure-parser
-%lex-param { struct grub_parser_param *state };
+%lex-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... */
-script_init: { state->err = 0; } script
- {
- state->parsed = $2;
- }
-;
-
-script: { $$ = 0; }
- | '\n' { $$ = 0; }
- | commands { $$ = $1; }
- | function '\n' { $$ = 0; }
- | menuentry '\n' { $$ = $1; }
- | error
- {
- $$ = 0;
- yyerror (state, "Incorrect command");
- state->err = 1;
- yyerrok;
- }
-;
-
-delimiter: '\n'
- | ';'
- | delimiter '\n'
-;
-
-newlines: /* Empty */
- | newlines '\n'
-;
-
-
-
-arguments: GRUB_PARSER_TOKEN_ARG
- {
- $$ = grub_script_add_arglist (state, 0, $1);
- }
- | arguments GRUB_PARSER_TOKEN_ARG
- {
- $$ = grub_script_add_arglist (state, $1, $2);
- }
-;
-
-grubcmd: arguments
- {
- $$ = grub_script_create_cmdline (state, $1);
- }
+script_init: { state->err = 0; } script { state->parsed = $2; state->err = 0; }
+;
+
+script: newlines0
+ {
+ $$ = 0;
+ }
+ | script statement delimiter
+ {
+ struct grub_script_cmdblock *cmdblock;
+ cmdblock = (struct grub_script_cmdblock *) $1;
+ $$ = grub_script_add_cmd (state, cmdblock, $2);
+ }
+ | error
+ {
+ $$ = 0;
+ yyerror (state, "Incorrect command");
+ yyerrok;
+ }
+;
+
+newlines0: /* Empty */ | newlines1 ;
+newlines1: newlines0 "\n" ;
+
+delimiter: ";"
+ | "\n"
+;
+delimiters0: /* Empty */ | delimiters1 ;
+delimiters1: delimiter
+ | delimiters1 "\n"
+;
+
+word: GRUB_PARSER_TOKEN_NAME { $$ = grub_script_add_arglist (state, 0, $1); }
+ | 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; }
+;
+
+arguments0: /* Empty */ { $$ = 0; }
+ | arguments1 { $$ = $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. */
-command: grubcmd delimiter { $$ = $1; }
- | if delimiter { $$ = $1; }
- | commandblock delimiter { $$ = $1; }
-;
-
-/* A block of commands. */
-commands: command
- {
- $$ = grub_script_add_cmd (state, 0, $1);
- }
- | command commands
- {
- struct grub_script_cmdblock *cmd;
- cmd = (struct grub_script_cmdblock *) $2;
- $$ = grub_script_add_cmd (state, cmd, $1);
- }
-;
-
-/* A function. Carefully save the memory that is allocated. Don't
- 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);
- } 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);
- } newlines commands '}'
- {
- 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);
- 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
- 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"
- {
- grub_script_lexer_ref (state->lexerstate);
- } arguments newlines '{'
- {
- grub_script_lexer_record_start (state->lexerstate);
- } newlines commands '}'
- {
- char *menu_entry;
- menu_entry = grub_script_lexer_record_stop (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
- to a state in which it demands more tokens. */
-if_statement: "if" { grub_script_lexer_ref (state->lexerstate); }
-;
-
-/* The if statement. */
-if: if_statement commands "then" newlines commands "fi"
- {
- $$ = 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);
- }
+command: grubcmd { $$ = $1; }
+ | ifcmd { $$ = $1; }
+;
+
+/* A list of commands. */
+commands1: newlines0 command
+ {
+ $$ = grub_script_add_cmd (state, 0, $2);
+ }
+ | commands1 delimiters1 command
+ {
+ struct grub_script_cmdblock *cmdblock;
+ cmdblock = (struct grub_script_cmdblock *) $1;
+ $$ = grub_script_add_cmd (state, cmdblock, $3);
+ }
+;
+
+function: "function" "name"
+ {
+ grub_script_lexer_ref (state->lexerstate);
+ state->func_mem = grub_script_mem_record (state);
+ }
+ delimiters0 "{" commands1 delimiters1 "}"
+ {
+ struct grub_script *script;
+ state->func_mem = grub_script_mem_record_stop (state,
+ state->func_mem);
+ script = grub_script_create ($6, state->func_mem);
+ if (script)
+ grub_script_function_create ($2, script);
+
+ grub_script_lexer_deref (state->lexerstate);
+ }
+;
+
+menuentry: "menuentry"
+ {
+ grub_script_lexer_ref (state->lexerstate);
+ }
+ arguments1
+ {
+ grub_script_lexer_record_start (state);
+ }
+ delimiters0 "{" commands1 delimiters1 "}"
+ {
+ 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);
+ }
+;
+
+if: "if" { grub_script_lexer_ref (state->lexerstate); }
+;
+ifcmd: if commands1 delimiters1 "then" commands1 delimiters1 "fi"
+ {
+ $$ = grub_script_create_cmdif (state, $2, $5, 0);
+ grub_script_lexer_deref (state->lexerstate);
+ }
+ | if commands1 delimiters1 "then" commands1 delimiters1 "else" commands1 delimiters1 "fi"
+ {
+ $$ = grub_script_create_cmdif (state, $2, $5, $8);
+ grub_script_lexer_deref (state->lexerstate);
+ }
;
=== modified file 'script/script.c'
--- script/script.c 2009-11-23 15:37:33 +0000
+++ script/script.c 2010-01-22 13:37:27 +0000
@@ -1,7 +1,7 @@
/* script.c -- Functions to create an in memory description of the script. */
/*
* 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
* 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
found. Because of that it is required to keep track of all memory
- allocations. The memory is freed in case of an error, or
- assigned to the parsed script when parsing was successful. */
-
-/* XXX */
-
-/* In case of the normal malloc, some additional bytes are allocated
+ allocations. The memory is freed in case of an error, or assigned
+ to the parsed script when parsing was successful.
+
+ In case of the normal malloc, some additional bytes are allocated
for this datastructure. All reserved memory is stored in a linked
list so it can be easily freed. The original memory can be found
from &mem. */
@@ -46,6 +44,8 @@
struct grub_script_mem *mem;
mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem)
- sizeof (char));
+ if (!mem)
+ return 0;
grub_dprintf ("scripting", "malloc %p\n", mem);
mem->next = state->memused;
@@ -94,32 +94,40 @@
void
grub_script_free (struct grub_script *script)
{
- if (! script)
+ if (!script)
return;
grub_script_mem_free (script->mem);
grub_free (script);
}
-
+
/* Extend the argument arg with a variable or string of text. If ARG
is zero a new list is created. */
struct grub_script_arg *
-grub_script_arg_add (struct grub_parser_param *state, struct grub_script_arg *arg,
- grub_script_arg_type_t type, char *str)
+grub_script_arg_add (struct grub_parser_param *state,
+ struct grub_script_arg *arg, grub_script_arg_type_t type,
+ char *str)
{
struct grub_script_arg *argpart;
struct grub_script_arg *ll;
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;
len = grub_strlen (str) + 1;
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);
argpart->next = 0;
- if (! arg)
+ if (!arg)
return argpart;
for (ll = arg; ll->next; ll = ll->next);
@@ -132,19 +140,24 @@
is zero, a new list will be created. */
struct grub_script_arglist *
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 *ll;
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->arg = arg;
link->argcount = 0;
- if (! list)
+ if (!list)
{
link->argcount++;
return link;
@@ -171,6 +184,9 @@
grub_dprintf ("scripting", "cmdline\n");
cmd = grub_script_malloc (state, sizeof (*cmd));
+ if (!cmd)
+ return 0;
+
cmd->cmd.exec = grub_script_execute_cmdline;
cmd->cmd.next = 0;
cmd->arglist = arglist;
@@ -193,6 +209,9 @@
grub_dprintf ("scripting", "cmdif\n");
cmd = grub_script_malloc (state, sizeof (*cmd));
+ if (!cmd)
+ return 0;
+
cmd->cmd.exec = grub_script_execute_cmdif;
cmd->cmd.next = 0;
cmd->exec_to_evaluate = exec_to_evaluate;
@@ -209,30 +228,16 @@
struct grub_script_cmd *
grub_script_create_cmdmenu (struct grub_parser_param *state,
struct grub_script_arglist *arglist,
- char *sourcecode,
- int options)
+ char *sourcecode, int options)
{
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));
+ if (!cmd)
+ return 0;
+
cmd->cmd.exec = grub_script_execute_menuentry;
cmd->cmd.next = 0;
- /* XXX: Check if this memory is properly freed. */
cmd->sourcecode = sourcecode;
cmd->arglist = arglist;
cmd->options = options;
@@ -248,15 +253,19 @@
struct grub_script_cmdblock *cmdblock,
struct grub_script_cmd *cmd)
{
+ struct grub_script_cmd *ptr;
+
grub_dprintf ("scripting", "cmdblock\n");
- if (! cmd)
+ if (!cmd)
return (struct grub_script_cmd *) cmdblock;
- if (! cmdblock)
+ if (!cmdblock)
{
- cmdblock = (struct grub_script_cmdblock *) grub_script_malloc (state,
- sizeof (*cmdblock));
+ cmdblock = grub_script_malloc (state, sizeof (*cmdblock));
+ if (!cmdblock)
+ return 0;
+
cmdblock->cmd.exec = grub_script_execute_cmdblock;
cmdblock->cmd.next = 0;
cmdblock->cmdlist = cmd;
@@ -264,22 +273,29 @@
}
else
{
- cmd->next = cmdblock->cmdlist;
- cmdblock->cmdlist = cmd;
+ if (!cmdblock->cmdlist)
+ cmdblock->cmdlist = cmd;
+ else
+ {
+ ptr = cmdblock->cmdlist;
+ while (ptr->next)
+ ptr = ptr->next;
+ ptr->next = cmd;
+ }
}
return (struct grub_script_cmd *) cmdblock;
}
-
+
struct grub_script *
grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem)
{
struct grub_script *parsed;
parsed = grub_malloc (sizeof (*parsed));
- if (! parsed)
+ if (!parsed)
{
grub_script_mem_free (mem);
grub_free (cmd);
@@ -304,16 +320,16 @@
struct grub_parser_param *parsestate;
parsed = grub_malloc (sizeof (*parsed));
- if (! parsed)
+ if (!parsed)
return 0;
parsestate = grub_zalloc (sizeof (*parsestate));
- if (! parsestate)
+ if (!parsestate)
return 0;
/* Initialize the lexer. */
- lexstate = grub_script_lexer_init (script, getline);
- if (! lexstate)
+ lexstate = grub_script_lexer_init (parsestate, script, getline);
+ if (!lexstate)
{
grub_free (parsed);
grub_free (parsestate);
@@ -330,7 +346,7 @@
struct grub_script_mem *memfree;
memfree = grub_script_mem_record_stop (parsestate, membackup);
grub_script_mem_free (memfree);
- grub_free (lexstate);
+ grub_script_lexer_fini (lexstate);
grub_free (parsestate);
return 0;
}
@@ -338,7 +354,7 @@
parsed->mem = grub_script_mem_record_stop (parsestate, membackup);
parsed->cmd = parsestate->parsed;
- grub_free (lexstate);
+ grub_script_lexer_fini (lexstate);
grub_free (parsestate);
return parsed;
=== added file 'script/yylex.l'
--- script/yylex.l 1970-01-01 00:00:00 +0000
+++ script/yylex.l 2010-01-23 04:44:32 +0000
@@ -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 .
+ */
+
+#include
+#include
+#include
+#include
+#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
+
+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 */
+
+{
+ \\. { 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); }
+ <> {
+ yy_pop_state (yyscanner);
+ yypop_buffer_state (yyscanner);
+ yyextra->lexerstate->merge_end = 1;
+ ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
+ }
+}
+
+{
+ \? |
+ {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; }
+}
+
+{
+ \' {
+ yy_pop_state (yyscanner);
+ ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR);
+ }
+ [^\']+ { COPY (yytext, yyleng); }
+}
+
+{
+ \" {
+ 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); }
+}
+
+<> {
+ 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;
+}
=== added file 'tests/grub_script_echo1.in'
--- tests/grub_script_echo1.in 1970-01-01 00:00:00 +0000
+++ tests/grub_script_echo1.in 2010-01-22 13:37:27 +0000
@@ -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 .
+
+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\yz\)
+
+foo=c
+bar=h
+echo e"$foo"${bar}o
+e"$foo"${bar}o hello world
+
+foo=echo
+$foo 1234
=== added file 'tests/grub_script_echo_keywords.in'
--- tests/grub_script_echo_keywords.in 1970-01-01 00:00:00 +0000
+++ tests/grub_script_echo_keywords.in 2010-01-22 13:37:27 +0000
@@ -0,0 +1,3 @@
+#! @builddir@/grub-shell-tester
+
+echo if then else fi for do done
=== added file 'tests/grub_script_vars1.in'
--- tests/grub_script_vars1.in 1970-01-01 00:00:00 +0000
+++ tests/grub_script_vars1.in 2010-01-22 13:37:27 +0000
@@ -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 .
+
+var=foo
+echo $var
+echo "$var"
+echo ${var}
+echo "${var}"
+
+echo $1 $2 $?
+
+foo=foo
+echo "" $foo
+
+echo $bar $foo
+
+bar=""
+echo $bar $foo
+
=== modified file 'tests/util/grub-shell-tester.in'
--- tests/util/grub-shell-tester.in 2010-01-12 03:30:55 +0000
+++ tests/util/grub-shell-tester.in 2010-01-22 13:37:27 +0000
@@ -1,7 +1,7 @@
#! /bin/bash -e
# 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
# it under the terms of the GNU General Public License as published by
=== modified file 'tests/util/grub-shell.in'
--- tests/util/grub-shell.in 2010-01-14 13:09:12 +0000
+++ tests/util/grub-shell.in 2010-01-22 13:37:27 +0000
@@ -1,7 +1,7 @@
#! /bin/bash -e
# 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
# it under the terms of the GNU General Public License as published by
=== modified file 'util/grub-script-check.c'
--- util/grub-script-check.c 2010-01-18 11:28:31 +0000
+++ util/grub-script-check.c 2010-01-22 13:37:27 +0000
@@ -1,7 +1,7 @@
/* grub-script-check.c - check grub script file for syntax errors */
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -82,16 +82,8 @@
}
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;
}
# Begin bundle
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
qVbNzn+c/+ZvaNRSzFUYvYqf6C7kinChIFTnawQ=