=== added file 'ChangeLog.scripting'
--- old/ChangeLog.scripting 1970-01-01 00:00:00 +0000
+++ new/ChangeLog.scripting 2009-12-20 08:28:15 +0000
@@ -0,0 +1,47 @@
+2009-12-20 BVK Chaitanya
+
+ * conf/any-emu.rmk: Makerules for flex based lexer.
+ * 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/mips.rmk: Likewise.
+ * conf/powerpc-ieee1275.rmk: Likewise.
+ * conf/x86_64-efi.rmk: Likewise.
+ * configure.ac: Configure rule for flex.
+ * include/grub/script_sh.h (grub_lexer_param): Simplified.
+ (grub_script_lexer_fini): New function to deinit grub_lexer_param.
+ (grub_script_lexer_init): Takes grub_parser_param parameter.
+ (grub_script_lexer_record_start): Likewise.
+ (grub_script_lexer_record_stop): Likewise.
+ (grub_script_lexer_yywrap): New function to feed more data to
+ lexer.
+ (grub_script_lexer_record): New function to save source code
+ tokens.
+ (grub_script_yylex): Rewrote to use yylex for token parsing.
+ * script/lexer.c: New defintions for above functions.
+ * script/parser.y: Better and more complete grammar.
+ * script/script.c: Cleanup.
+ (grub_script_parse): grub_lexer_param deinitialized using new
+ grub_script_lexer_fini function.
+ * script/yylex.l: Newfile. Flex script for GRUB Script.
+
+2009-12-20 BVK Chaitanya
+
+ Added --verbose (-v) option to grub-script-check.
+
+ * util/grub-script-check.c (main): Added --verbose option.
+
+2009-12-19 BVK Chaitanya
+
+ Added grub-script-check utility.
+
+ * util/grub-script-check.c: Newfile.
+ * conf/common.rmk: Makerules for building grub-script-check.
+
+2009-12-17 BVK Chaitanya
+
+ Bug fixes in GRUB script parsing.
+
+ * script/parser.y: Better grammar.
=== added file 'ChangeLog.unit-testing'
--- old/ChangeLog.unit-testing 1970-01-01 00:00:00 +0000
+++ new/ChangeLog.unit-testing 2009-12-20 12:14:10 +0000
@@ -0,0 +1,35 @@
+2009-12-20 BVK Chaitanya
+
+ * Makefile.in: Unit tests are skipped for platform=emu.
+
+2009-12-19 BVK Chaitanya
+
+ New unit tests for GRUB script parser.
+
+ * conf/tests.rmk: Make rules for new unit tests.
+ * tests/grub_script_echo1.in: New unit test.
+ * tests/grub_script_functions1.in: New unit test.
+
+
+2009-12-17 BVK Chaitanya
+
+ Unit testing framework for GRUB
+
+ * Makefile.in: Added make check rules.
+ * genmk.rb: Added TESTS type target for unit tests.
+ * ChangeLog.unit-testing: Newfile. ChangeLog for unit tests
+ branch.
+ * conf/tests.rmk: Newfile. Makefile for tests and test framework.
+ * conf/util/grub-shell-tester.in: Newfile. Tool for runing grub
+ script tests.
+ * include/grub/test.h: Newfile. Testing framework header file.
+ * tests/test.c: Newfile. Common definitions for testing framework.
+ * test/unit_test.c: Newfile. Unit tests framework.
+ * tests/functional_test.c: Newfile. Functional tests framework.
+ * tests/example_functional_test.c: Newfile. Functional test
+ example.
+ * tests/example_scripted_test.in: Newfile. Scripted test example.
+ * tests/example_unit_test.c: Newfile. Unit test example.
+ * tests/grub_sprintf.c: Newfile. Unittest for grub_printf logic.
+ * tests/gettext_1.in: Newfile. Scripted test for gettext.
+ * tests/echo_keywords.in: Newfile. Scripted test for GRUB parser.
=== modified file 'Makefile.in'
--- old/Makefile.in 2009-12-13 20:25:49 +0000
+++ new/Makefile.in 2009-12-20 12:14:10 +0000
@@ -138,6 +138,7 @@
SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS) $(grub-mkconfig_SCRIPTS) \
$(lib_SCRIPTS)
INFOS = $(info_INFOS)
+TESTS = $(check_TESTS) $(check_SCRIPTS) $(check_MODULES)
CLEANFILES =
MOSTLYCLEANFILES =
@@ -164,6 +165,8 @@
include $(srcdir)/conf/any-emu.mk
else
include $(srcdir)/conf/$(target_cpu)-$(platform).mk
+# For tests.
+include $(srcdir)/conf/tests.mk
# For external modules.
-include $(wildcard $(GRUB_CONTRIB)/*/conf/common.mk)
endif
@@ -452,7 +455,15 @@
@echo "$(distdir).tar.gz is ready for distribution" | \
sed 'h;s/./=/g;p;x;p;x'
-check:
+check: all $(TESTS)
+ @list="$(check_TESTS) $(check_SCRIPTS)"; \
+ for file in $$list; do \
+ if $(builddir)/$$file; then \
+ echo "$$file: PASS"; \
+ else \
+ echo "$$file: FAIL"; \
+ fi; \
+ done
.SUFFIX:
.SUFFIX: .c .o .S .d
=== modified file 'conf/any-emu.rmk'
--- old/conf/any-emu.rmk 2009-12-05 19:47:36 +0000
+++ new/conf/any-emu.rmk 2009-12-20 08:28:15 +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
@@ -32,6 +32,7 @@
normal/menu_text.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 \
\
@@ -123,3 +124,9 @@
grub_script.tab.c grub_script.tab.h: 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
+
+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
=== modified file 'conf/common.rmk'
--- old/conf/common.rmk 2009-12-09 21:03:26 +0000
+++ new/conf/common.rmk 2009-12-20 08:28:15 +0000
@@ -88,11 +88,40 @@
bin_UTILITIES += grub-mkrelpath
grub_mkrelpath_SOURCES = gnulib/progname.c util/grub-mkrelpath.c util/misc.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 \
+ grub_script.yy.c
+
# For the parser.
grub_script.tab.c grub_script.tab.h: 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
+# For grub-check.
+grub_script_check_init.lst: geninit.sh $(filter-out grub_script_check_init.c,$(grub_script_check_SOURCES))
+ rm -f $@; grep GRUB_MOD_INIT $(filter %.c,$^) /dev/null > $@
+DISTCLEANFILES += grub_script_check_init.lst
+
+grub_script_check_init.h: grub_script_check_init.lst $(filter-out grub_script_check_init.c,$(grub_script_check_SOURCES)) geninitheader.sh
+ rm -f $@; sh $(srcdir)/geninitheader.sh $< > $@
+DISTCLEANFILES += grub_script_check_init.h
+
+grub_script_check_init.c: grub_script_check_init.lst $(filter-out grub_script_check_init.c,$(grub_script_check_SOURCES)) geninit.sh
+ rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@
+DISTCLEANFILES += grub_script_check_init.c
+
# For grub-probe.
grub_probe_init.lst: geninit.sh $(filter-out grub_probe_init.c,$(grub_probe_SOURCES))
rm -f $@; grep GRUB_MOD_INIT $(filter %.c,$^) /dev/null > $@
@@ -592,7 +621,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'
--- old/conf/i386-coreboot.rmk 2009-12-12 13:39:59 +0000
+++ new/conf/i386-coreboot.rmk 2009-12-20 08:28:15 +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'
--- old/conf/i386-efi.rmk 2009-12-13 20:25:49 +0000
+++ new/conf/i386-efi.rmk 2009-12-20 08:28:15 +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'
--- old/conf/i386-ieee1275.rmk 2009-12-12 13:39:59 +0000
+++ new/conf/i386-ieee1275.rmk 2009-12-20 08:28:15 +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'
--- old/conf/i386-pc.rmk 2009-12-13 20:25:49 +0000
+++ new/conf/i386-pc.rmk 2009-12-20 08:28:15 +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/mips.rmk'
--- old/conf/mips.rmk 2009-12-05 17:46:28 +0000
+++ new/conf/mips.rmk 2009-12-20 08:28:15 +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/powerpc-ieee1275.rmk'
--- old/conf/powerpc-ieee1275.rmk 2009-12-05 15:42:20 +0000
+++ new/conf/powerpc-ieee1275.rmk 2009-12-20 08:28:15 +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.
=== added file 'conf/tests.rmk'
--- old/conf/tests.rmk 1970-01-01 00:00:00 +0000
+++ new/conf/tests.rmk 2009-12-20 08:28:15 +0000
@@ -0,0 +1,47 @@
+# -*- makefile -*-
+
+# For grub-mkconfig
+grub-shell-tester: tests/util/grub-shell-tester.in config.status
+ ./config.status --file=$@:$<
+ chmod +x $@
+bin_SCRIPTS += grub-shell-tester
+CLEANFILES += grub-shell-tester
+
+check_MODULES += functional_test.mod
+functional_test_mod_SOURCES = tests/functional_test.c tests/test.c
+functional_test_mod_CFLAGS = $(COMMON_CFLAGS)
+functional_test_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# Unit tests
+
+check_TESTS += example_unit_test
+example_unit_test_SOURCES = tests/example_unit_test.c kern/list.c kern/misc.c tests/test.c tests/unit_test.c
+example_unit_test_CFLAGS = -Wno-format
+
+check_TESTS += grub_sprintf
+grub_sprintf_SOURCES = tests/grub_sprintf.c kern/misc.c kern/list.c tests/test.c tests/unit_test.c
+grub_sprintf_CFLAGS = -Wno-error -Wno-format
+
+# Functional tests
+
+check_MODULES += example_functional_test.mod
+example_functional_test_mod_SOURCES = tests/example_functional_test.c
+example_functional_test_mod_CFLAGS = -Wno-format $(COMMON_CFLAGS)
+example_functional_test_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# Scripted tests
+
+check_SCRIPTS += example_scripted_test
+example_scripted_test_SOURCES = tests/example_scripted_test.in
+
+check_SCRIPTS += echo_keywords
+echo_keywords_SOURCES = tests/echo_keywords.in
+
+check_SCRIPTS += gettext_1
+gettext_1_SOURCES = tests/gettext_1.in
+
+check_SCRIPTS += grub_script_echo1
+grub_script_echo1_SOURCES = tests/grub_script_echo1.in
+
+check_SCRIPTS += grub_script_functions1
+grub_script_functions1_SOURCES = tests/grub_script_functions1.in
=== modified file 'conf/x86_64-efi.rmk'
--- old/conf/x86_64-efi.rmk 2009-12-13 20:25:49 +0000
+++ new/conf/x86_64-efi.rmk 2009-12-20 08:28:15 +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'
--- old/configure.ac 2009-12-12 13:39:59 +0000
+++ new/configure.ac 2009-12-20 08:28:15 +0000
@@ -158,6 +158,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 'genmk.rb'
--- old/genmk.rb 2009-12-13 20:25:49 +0000
+++ new/genmk.rb 2009-12-20 08:28:15 +0000
@@ -413,7 +413,7 @@
PModule.new(prefix, pmod)
end
- when 'UTILITIES'
+ when 'UTILITIES', 'TESTS'
utils += args.split(/\s+/).collect do |util|
Utility.new(prefix, util)
end
=== modified file 'include/grub/script_sh.h'
--- old/include/grub/script_sh.h 2009-06-17 13:47:37 +0000
+++ new/include/grub/script_sh.h 2009-12-20 08:28:15 +0000
@@ -112,7 +112,7 @@
struct grub_script_arglist *arglist;
/* The sourcecode the entry will be generated from. */
- const char *sourcecode;
+ char *sourcecode;
/* Options. XXX: Not used yet. */
int options;
@@ -121,12 +121,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 +131,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 +147,29 @@
/* 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;
+
+ /* Text of current token. */
+ char *text;
+
+ /* Flex scanner. */
+ void *yyscanner;
+
+ /* Flex scanner buffer. */
+ void *buffer;
+
+ /* Length of current token text. */
+ unsigned size;
};
+#define GRUB_LEXER_TOKEN_MAX 256
+#define GRUB_LEXER_RECORD_INCREMENT 256
+
/* State of the parser as passes to the parser. */
struct grub_parser_param
{
@@ -223,12 +229,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);
=== added file 'include/grub/test.h'
--- old/include/grub/test.h 1970-01-01 00:00:00 +0000
+++ new/include/grub/test.h 2009-12-20 08:28:15 +0000
@@ -0,0 +1,84 @@
+#ifndef GRUB_TEST_HEADER
+#define GRUB_TEST_HEADER
+
+#include
+#include
+#include
+#include
+#include
+
+struct grub_test
+{
+ /* The next test. */
+ struct grub_test *next;
+
+ /* The test name. */
+ char *name;
+
+ /* The test main function. */
+ void (*main) (void);
+};
+typedef struct grub_test *grub_test_t;
+
+extern grub_test_t EXPORT_VAR (grub_test_list);
+
+void EXPORT_FUNC (grub_test_register) (const char *name, void (*test) (void));
+
+void EXPORT_FUNC (grub_test_unregister) (const char *name);
+
+/* Execute a test and print results. */
+int grub_test_run (const char *name);
+
+/* Test `cond' for nonzero; log failure otherwise. */
+void grub_test_nonzero (int cond, const char *file,
+ const char *func, grub_uint32_t line,
+ const char *fmt, ...)
+ __attribute__ ((format (printf, 5, 6)));
+
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ ""
+# endif
+#endif
+
+/* Macro to fill in location details and an optional error message. */
+#define grub_test_assert(cond, ...) \
+ grub_test_nonzero(cond, __FILE__, __func__, __LINE__, \
+ ## __VA_ARGS__, \
+ "assert failed: %s", #cond)
+
+/* Macro to define a unit test. */
+#define GRUB_UNIT_TEST(name, funp) \
+ void grub_unit_test_init (void) \
+ { \
+ grub_test_register (name, funp); \
+ } \
+ \
+ void grub_unit_test_fini (void) \
+ { \
+ grub_test_unregister (name); \
+ }
+
+/* Macro to define a functional test. */
+#define GRUB_FUNCTIONAL_TEST(name, funp) \
+ GRUB_MOD_INIT(functional_test_##funp) \
+ { \
+ grub_test_register (name, funp); \
+ } \
+ \
+ GRUB_MOD_FINI(functional_test_##funp) \
+ { \
+ grub_test_unregister (name); \
+ }
+
+/* Functions that are defined differently for unit and functional tests. */
+void *grub_test_malloc (grub_size_t size);
+void grub_test_free (void *ptr);
+char *grub_test_strdup (const char *str);
+int grub_test_vsprintf (char *str, const char *fmt, va_list args);
+int grub_test_printf (const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+#endif /* ! GRUB_TEST_HEADER */
=== modified file 'script/lexer.c'
--- old/script/lexer.c 2009-11-23 15:37:33 +0000
+++ new/script/lexer.c 2009-12-20 08:28:15 +0000
@@ -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,292 @@
/* 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_RECORD_INCREMENT;
+ lexer->recording = grub_malloc (lexer->recordlen);
+
+ if (!lexer->recording)
+ {
+ grub_script_yyerror (parser, "out of memory");
+ 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;
+ struct grub_lexer_param *lexer = parser->lexerstate;
+
+ if (!lexer->record || !lexer->recording)
+ return 0;
+
+ /* Delete '{' and '}' characters. */
+ /* XXX This is not necessary in BASH. */
+
+ ptr = lexer->recording + lexer->recordpos - 1;
+ while (*ptr && grub_isspace (*ptr))
+ ptr--;
+ if (*ptr == '}')
+ *ptr = '\0';
+
+ ptr = lexer->recording;
+ while (*ptr && grub_isspace (*ptr))
+ ptr++;
+ if (*ptr == '{')
+ ptr++;
+
+ lexer->record = 0;
+ lexer->recordpos = 0;
+ return (*ptr ? grub_strdup (ptr) : 0);
}
-/* 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)
+
+/* 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;
+ struct grub_lexer_param *lexer = parser->lexerstate;
+
+ if (!lexer->record)
+ return;
+
+ len = grub_strlen (str);
+ if (lexer->recordpos + len >= lexer->recordlen - 1)
{
- char *old = state->recording;
- state->recordlen += 100;
- state->recording = grub_realloc (state->recording, state->recordlen);
- if (! state->recording)
+ char *old = lexer->recording;
+ lexer->recordlen += GRUB_LEXER_RECORD_INCREMENT;
+ 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;
+ grub_script_yyerror (parser, "out of memory");
+ 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 + 1);
+ if (!line)
+ return 0;
+
+ grub_strcpy (line, src);
+
+ line[len] = '\n';
+ line[len + 1] = '\0';
+ return line;
+}
+
+/* Read next line of input if necessary, and set yyscanner buffers. */
+int
+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, "input error"); /* 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, "out of memory");
+ return 0;
+ }
+
+ return 1;
+}
+
+struct grub_lexer_param *
+grub_script_lexer_init (struct grub_parser_param *parser, char *script,
+ grub_reader_getline_t getline)
+{
+ int len;
+ char *script2;
+ YY_BUFFER_STATE buffer;
+ struct grub_lexer_param *lexerstate;
+
+ lexerstate = grub_zalloc (sizeof (*lexerstate));
+ if (!lexerstate)
+ return 0;
+
+ lexerstate->text = grub_malloc (GRUB_LEXER_TOKEN_MAX);
+ if (!lexerstate->text)
+ {
+ grub_free (lexerstate);
+ return 0;
+ }
+
+ lexerstate->getline = getline; /* rest are all zeros already */
+ if (yylex_init (&lexerstate->yyscanner))
+ {
+ grub_free (lexerstate->text);
+ grub_free (lexerstate);
+ return 0;
+ }
+
+ buffer = 0;
+ script = script ? : "\n";
+ len = grub_strlen (script);
+
+ if (script[len - 1] == '\n')
+ {
+ buffer = yy_scan_string (script, lexerstate->yyscanner);
+ }
+ else
+ {
+ script2 = append_newline (script);
+ if (script2)
+ {
+ buffer = yy_scan_string (script2, lexerstate->yyscanner);
+ grub_free (script2);
+ }
+ }
+
+ if (!buffer)
+ {
+ yylex_destroy (lexerstate->yyscanner);
+ grub_free (lexerstate->yyscanner);
+
+ grub_free (lexerstate->text);
+ grub_free (lexerstate);
+ return 0;
+ }
+ yyset_extra (parser, lexerstate->yyscanner);
+
+ return lexerstate;
+}
+
+void
+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->size = 0;
+ lexerstate->text[0] = '\0';
+
+ token = yylex (value, lexerstate->yyscanner);
+ if (token == GRUB_PARSER_TOKEN_BAD)
+ break;
+
+ /* Merging feature uses lexerstate->text instead of yytext. */
+ if (lexerstate->merge_start)
+ {
+ lexerstate->text[lexerstate->size] = '\0';
+ str = lexerstate->text;
+ type = (token == GRUB_PARSER_TOKEN_NAME ?
+ GRUB_SCRIPT_ARG_TYPE_VAR : GRUB_SCRIPT_ARG_TYPE_STR);
}
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\n");
- return 0;
+ str = yyget_text (lexerstate->yyscanner);
+ type = GRUB_SCRIPT_ARG_TYPE_STR;
}
- }
-
- 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_printf ("tok %u, txt [%s] size %u\n", token, str, lexerstate->size); */
+
+ value->arg = grub_script_arg_add (parserstate, value->arg, type, str);
+ }
+ while (lexerstate->merge_start && !lexerstate->merge_end);
+
+ if (!value->arg || parserstate->err)
+ return GRUB_PARSER_TOKEN_BAD;
+
+ return token;
}
void
-grub_script_yyerror (struct grub_parser_param *lex __attribute__ ((unused)),
- char const *err)
+grub_script_yyerror (struct grub_parser_param *state, char const *err)
{
grub_printf ("%s\n", err);
+ state->err++;
}
=== modified file 'script/parser.y'
--- old/script/parser.y 2009-11-23 15:37:33 +0000
+++ new/script/parser.y 2009-12-20 08:28:15 +0000
@@ -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,217 @@
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 };
+%error-verbose
+
+%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: /* Empty */
+ {
+ $$ = 0;
+ }
+ | "\n" script
+ {
+ $$ = 0;
+ }
+ | statement delimiter script
+ {
+ struct grub_script_cmdblock *cmd;
+ cmd = (struct grub_script_cmdblock *) $3;
+ $$ = grub_script_add_cmd (state, cmd, $1);
+ }
+ | error
+ {
+ $$ = 0;
+ yyerror (state, "Incorrect command");
+ yyerrok;
+ }
+;
+
+newlines0: /* Empty */ | newlines1 ;
+newlines1: newlines0 "\n" ;
+
+delimiter: ";"
+ | "\n"
+;
+delimiters0: /* Empty */ | delimiters1 ;
+delimiters1: delimiter
+ | delimiters1 "\n"
+;
+
+word: "name" { $$ = grub_script_add_arglist (state, 0, $1); }
+ | "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 ($2)
+ {
+ $1->next = $2;
+ $1->argcount += $2->argcount;
+ $2->argcount = 0;
+ }
+ $$ = $1;
+ }
+;
+
+grubcmd: word arguments0
+ {
+ if ($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_cmd *cmd;
+ struct grub_script_cmdblock *cmdblock;
+
+ cmdblock = (struct grub_script_cmdblock *) $1;
+ cmd = cmdblock->cmdlist;
+ while (cmd->next)
+ cmd = cmd->next;
+ cmd->next = $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'
--- old/script/script.c 2009-11-23 15:37:33 +0000
+++ new/script/script.c 2009-12-20 08:28:15 +0000
@@ -94,32 +94,34 @@
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));
argpart->type = type;
len = grub_strlen (str) + 1;
argpart->str = grub_script_malloc (state, len);
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 +134,21 @@
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));
link->next = 0;
link->arg = arg;
link->argcount = 0;
- if (! list)
+ if (!list)
{
link->argcount++;
return link;
@@ -209,8 +213,7 @@
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;
@@ -250,13 +253,14 @@
{
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));
+ sizeof
+ (*cmdblock));
cmdblock->cmd.exec = grub_script_execute_cmdblock;
cmdblock->cmd.next = 0;
cmdblock->cmdlist = cmd;
@@ -270,16 +274,16 @@
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 +308,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 +334,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 +342,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'
--- old/script/yylex.l 1970-01-01 00:00:00 +0000
+++ new/script/yylex.l 2009-12-20 08:28:15 +0000
@@ -0,0 +1,281 @@
+%{
+
+#include
+#include
+#include
+#include
+#include "grub_script.tab.h"
+
+#define yyfree grub_lexer_yyfree
+#define yyalloc grub_lexer_yyalloc
+#define yyrealloc grub_lexer_yyrealloc
+
+/*
+ * XXX As we don't have access to yyscanner, we cannot do much except
+ * to print the fatal error.
+ */
+#define YY_FATAL_ERROR(msg) \
+ do { \
+ grub_printf ("fatal error: %s\n", msg); \
+ } while (0)
+
+#define PUSH(c) \
+ do { \
+ if (yyextra->lexerstate->size >= GRUB_LEXER_TOKEN_MAX - 1) \
+ grub_script_yyerror (yyextra, "token too long"); \
+ else \
+ yyextra->lexerstate->text[yyextra->lexerstate->size++] = c; \
+ } while (0)
+
+#define COPY(str) \
+ do { \
+ char *ptr = str; \
+ while (*ptr && ! yyextra->err) \
+ { \
+ PUSH (*ptr); \
+ ptr++; \
+ } \
+ } while (0)
+
+#define RECORD \
+ do { \
+ grub_script_lexer_record (yyextra, yytext); \
+ } while (0)
+
+/* We don't need YY_INPUT, as we rely on yy_scan_strings */
+#define YY_INPUT(buf,res,max) do { res = 0; } while (0)
+
+/* forward declarations */
+static void grub_lexer_yyfree (void *, yyscan_t yyscanner);
+static void* grub_lexer_yyalloc (yy_size_t, yyscan_t yyscanner);
+static void* grub_lexer_yyrealloc (void*, yy_size_t, yyscan_t yyscanner);
+
+%}
+
+%top{
+
+/*
+ * Some flex hacks for -nostdinc; XXX We need to fix these when libc
+ * support becomes availble in GRUB.
+ */
+
+#include
+
+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"
+
+}
+
+%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\'\"]
+NAME [[:alpha:]_][[:alnum:][:digit:]_]*
+
+ESC \\.
+VARIABLE ${NAME}|$\{{NAME}\}
+DQSTR \"([^\"]|\\\")*\"
+SQSTR \'[^\']*\'
+WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+
+
+%x SPLIT
+%x DQUOTE
+%x SQUOTE
+%x VAR
+
+%%
+
+ /* White spaces */
+{BLANK}+ { RECORD; }
+{COMMENT} { RECORD; }
+
+ /* Special symbols */
+"\n" { RECORD; return GRUB_PARSER_TOKEN_NEWLINE; }
+"||" { RECORD; return GRUB_PARSER_TOKEN_OR; }
+"&&" { RECORD; return GRUB_PARSER_TOKEN_AND; }
+";;" { RECORD; return GRUB_PARSER_TOKEN_SEMI2; }
+"|" { RECORD; return GRUB_PARSER_TOKEN_PIPE; }
+"&" { RECORD; return GRUB_PARSER_TOKEN_AMP; }
+";" { RECORD; return GRUB_PARSER_TOKEN_SEMI; }
+"(" { RECORD; return GRUB_PARSER_TOKEN_LPAR; }
+")" { RECORD; return GRUB_PARSER_TOKEN_RPAR; }
+"<" { RECORD; return GRUB_PARSER_TOKEN_LT; }
+">" { RECORD; return GRUB_PARSER_TOKEN_GT; }
+
+ /* Reserved words */
+"!" { RECORD; return GRUB_PARSER_TOKEN_NOT; }
+"{" { RECORD; return GRUB_PARSER_TOKEN_LBR; }
+"}" { RECORD; return GRUB_PARSER_TOKEN_RBR; }
+"[[" { RECORD; return GRUB_PARSER_TOKEN_RSQBR2; }
+"]]" { RECORD; return GRUB_PARSER_TOKEN_LSQBR2; }
+"time" { RECORD; return GRUB_PARSER_TOKEN_TIME; }
+"case" { RECORD; return GRUB_PARSER_TOKEN_CASE; }
+"do" { RECORD; return GRUB_PARSER_TOKEN_DO; }
+"done" { RECORD; return GRUB_PARSER_TOKEN_DONE; }
+"elif" { RECORD; return GRUB_PARSER_TOKEN_ELIF; }
+"else" { RECORD; return GRUB_PARSER_TOKEN_ELSE; }
+"esac" { RECORD; return GRUB_PARSER_TOKEN_ESAC; }
+"fi" { RECORD; return GRUB_PARSER_TOKEN_FI; }
+"for" { RECORD; return GRUB_PARSER_TOKEN_FOR; }
+"if" { RECORD; return GRUB_PARSER_TOKEN_IF; }
+"in" { RECORD; return GRUB_PARSER_TOKEN_IN; }
+"select" { RECORD; return GRUB_PARSER_TOKEN_SELECT; }
+"then" { RECORD; return GRUB_PARSER_TOKEN_THEN; }
+"until" { RECORD; return GRUB_PARSER_TOKEN_UNTIL; }
+"while" { RECORD; return GRUB_PARSER_TOKEN_WHILE; }
+"function" { RECORD; return GRUB_PARSER_TOKEN_FUNCTION; }
+"menuentry" { RECORD; return GRUB_PARSER_TOKEN_MENUENTRY; }
+
+{NAME} { RECORD; return GRUB_PARSER_TOKEN_NAME; }
+{WORD} {
+ RECORD;
+ /* resplit yytext */
+ yypush_buffer_state (YY_CURRENT_BUFFER, yyscanner);
+ if (yy_scan_string (yytext, yyscanner))
+ {
+ yyextra->lexerstate->merge_start = 1;
+ yy_push_state (SPLIT, yyscanner);
+ }
+ else
+ {
+ grub_script_yyerror (yyextra, "out of memory");
+ 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 */
+
+{
+ \\. { PUSH (yytext[1]); }
+ \" { yy_push_state (DQUOTE, yyscanner); }
+ \' { yy_push_state (SQUOTE, yyscanner); }
+ \$ {
+ yy_push_state (VAR, yyscanner);
+ return GRUB_PARSER_TOKEN_WORD;
+ }
+ {CHAR} { PUSH (yytext[0]); }
+ .|\n {
+ grub_script_yyerror (yyextra, "unrecognized token");
+ return GRUB_PARSER_TOKEN_BAD;
+ }
+
+ <> {
+ yy_pop_state (yyscanner);
+ yypop_buffer_state (yyscanner);
+ yyextra->lexerstate->merge_end = 1;
+ return GRUB_PARSER_TOKEN_WORD;
+ }
+}
+
+{
+ {NAME} {
+ COPY (yytext);
+ yy_pop_state (yyscanner);
+ return GRUB_PARSER_TOKEN_NAME;
+ }
+ \{{NAME}\} {
+ yytext[yyleng - 1] = '\0';
+ COPY (yytext + 1);
+ yy_pop_state (yyscanner);
+ return GRUB_PARSER_TOKEN_NAME;
+ }
+ .|\n { return GRUB_PARSER_TOKEN_BAD; }
+}
+
+{
+ \' { yy_pop_state (yyscanner); }
+ (.|\n) { PUSH (yytext[0]); }
+}
+
+{
+ \" { yy_pop_state (yyscanner); }
+ \$ {
+ yy_push_state (VAR, yyscanner);
+ return GRUB_PARSER_TOKEN_WORD;
+ }
+ \\\\ { PUSH ('\\'); }
+ \\\" { PUSH ('\"'); }
+ \\\n { /* ignore */ }
+ (.|\n) { PUSH (yytext[0]); }
+}
+
+<> {
+ 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);
+}
+
=== added directory 'tests'
=== added file 'tests/echo_keywords.in'
--- old/tests/echo_keywords.in 1970-01-01 00:00:00 +0000
+++ new/tests/echo_keywords.in 2009-12-20 08:28:15 +0000
@@ -0,0 +1,3 @@
+#! @builddir@/grub-shell-tester
+
+echo if then else fi for do done
=== added file 'tests/example_functional_test.c'
--- old/tests/example_functional_test.c 1970-01-01 00:00:00 +0000
+++ new/tests/example_functional_test.c 2009-12-20 08:28:15 +0000
@@ -0,0 +1,17 @@
+/* All tests need to include test.h for GRUB testing framework. */
+#include
+
+/* Functional test main method. */
+static void
+example_test (void)
+{
+ /* Check if 1st argument is true and report with default error message. */
+ grub_test_assert (1 == 1);
+
+ /* Check if 1st argument is true and report with custom error message. */
+ grub_test_assert (2 == 2, "2 equal 2 expected");
+ grub_test_assert (2 == 3, "2 is not equal to %d", 3);
+}
+
+/* Register example_test method as a functional test. */
+GRUB_FUNCTIONAL_TEST ("example_functional_test", example_test);
=== added file 'tests/example_scripted_test.in'
--- old/tests/example_scripted_test.in 1970-01-01 00:00:00 +0000
+++ new/tests/example_scripted_test.in 2009-12-20 08:28:15 +0000
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+
+true
=== added file 'tests/example_unit_test.c'
--- old/tests/example_unit_test.c 1970-01-01 00:00:00 +0000
+++ new/tests/example_unit_test.c 2009-12-20 08:28:15 +0000
@@ -0,0 +1,20 @@
+/* Unit tests are normal programs, so they can include C library. */
+#include
+
+/* All tests need to include test.h for GRUB testing framework. */
+#include
+
+/* Unit test main method. */
+static void
+example_test (void)
+{
+ /* Check if 1st argument is true and report with default error message. */
+ grub_test_assert (1 == 1);
+
+ /* Check if 1st argument is true and report with custom error message. */
+ grub_test_assert (2 == 2, "2 equal 2 expected");
+ grub_test_assert (2 == 3, "2 is not equal to %d", 3);
+}
+
+/* Register example_test method as a unit test. */
+GRUB_UNIT_TEST ("example_unit_test", example_test);
=== added file 'tests/functional_test.c'
--- old/tests/functional_test.c 1970-01-01 00:00:00 +0000
+++ new/tests/functional_test.c 2009-12-20 08:28:15 +0000
@@ -0,0 +1,89 @@
+#include
+#include
+#include
+#include
+
+void *
+grub_test_malloc (grub_size_t size)
+{
+ return grub_malloc (size);
+}
+
+void
+grub_test_free (void *ptr)
+{
+ grub_free (ptr);
+}
+
+int
+grub_test_vsprintf (char *str, const char *fmt, va_list args)
+{
+ return grub_vsprintf (str, fmt, args);
+}
+
+char *
+grub_test_strdup (const char *str)
+{
+ return grub_strdup (str);
+}
+
+int
+grub_test_printf (const char *fmt, ...)
+{
+ int r;
+ va_list ap;
+
+ va_start (ap, fmt);
+ r = grub_vprintf (fmt, ap);
+ va_end (ap);
+
+ return r;
+}
+
+static grub_err_t
+grub_functional_test (struct grub_extcmd *cmd __attribute__ ((unused)),
+ int argc, char **args __attribute__ ((unused)))
+{
+ int i;
+ int status;
+ grub_test_t test;
+
+ auto int print_name (grub_test_t test);
+ int print_name (grub_test_t test)
+ {
+ grub_printf ("%s\n", test->name);
+ return 0;
+ }
+
+ if (!argc)
+ {
+ grub_list_iterate (GRUB_AS_LIST (grub_test_list),
+ (grub_list_hook_t) print_name);
+ return GRUB_ERR_NONE;
+ }
+
+ status = 0;
+ for (i = 0; i < argc; i++)
+ {
+ test = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_test_list),
+ args[i]);
+ status = grub_test_run (test->name) ? : status;
+ }
+
+ return status;
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT (functional_test)
+{
+ cmd = grub_register_extcmd ("functional_test", grub_functional_test,
+ GRUB_COMMAND_FLAG_CMDLINE,
+ "functional_test [name ...]",
+ "Run or list functional tests.", 0);
+}
+
+GRUB_MOD_FINI (functional_test)
+{
+ grub_unregister_extcmd (cmd);
+}
=== added file 'tests/gettext_1.in'
--- old/tests/gettext_1.in 1970-01-01 00:00:00 +0000
+++ new/tests/gettext_1.in 2009-12-20 08:28:15 +0000
@@ -0,0 +1,4 @@
+#! @builddir@/grub-shell-tester --modules=gettext
+
+gettext "hello world"
+
=== added file 'tests/grub_script_echo1.in'
--- old/tests/grub_script_echo1.in 1970-01-01 00:00:00 +0000
+++ new/tests/grub_script_echo1.in 2009-12-20 08:28:15 +0000
@@ -0,0 +1,16 @@
+#! @builddir@/grub-shell-tester
+
+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_functions1.in'
--- old/tests/grub_script_functions1.in 1970-01-01 00:00:00 +0000
+++ new/tests/grub_script_functions1.in 2009-12-20 08:28:15 +0000
@@ -0,0 +1,18 @@
+#! @builddir@/grub-shell-tester
+
+function foo { echo foo; }; foo
+function foo2
+{
+ echo foo2
+}
+foo2
+
+function bar { echo one; if true; then echo yes; else echo no; fi; echo bar; foo; }; bar
+function bar
+{
+ foo;
+ foo2;
+ bar;
+}
+
+
=== added file 'tests/grub_sprintf.c'
--- old/tests/grub_sprintf.c 1970-01-01 00:00:00 +0000
+++ new/tests/grub_sprintf.c 2009-12-20 08:28:15 +0000
@@ -0,0 +1,162 @@
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#define TEST(type,fmt,val) \
+ do { \
+ int r1, r2; \
+ char b1[1024]; \
+ char b2[1024]; \
+ \
+ b1[0] = b2[0] = '\0'; \
+ r1 = sprintf (b1, fmt, val); \
+ r2 = grub_sprintf (b2, fmt, val); \
+ \
+ grub_test_assert (strcmp (b1, b2) == 0, \
+ "for (\"%s\","type") " \
+ "result should be (\"%s\",%d) " \
+ "but got (\"%s\",%d)", \
+ fmt, val, b1, r1, b2, r2); \
+ \
+ grub_test_assert (r1 == r2, \
+ "for (\"%s\","type") " \
+ "result should be (\"%s\",%d) " \
+ "but got (\"%s\",%d)", \
+ fmt, val, b1, r1, b2, r2); \
+ } while (0)
+
+#define D(fmt,v) TEST("%d",fmt,v)
+#define U(fmt,v) TEST("%u",fmt,v)
+#define x(fmt,v) TEST("%x",fmt,v)
+#define X(fmt,v) TEST("%X",fmt,v)
+#define P(fmt,v) TEST("%p",fmt,v)
+#define S(fmt,s) TEST("%s",fmt,s)
+
+static void
+sprintf_checks (void)
+{
+ D ("%d", -1);
+ D ("%d", 0);
+ D ("%d", 1);
+ D ("%5d", -1);
+ D ("%5d", 0);
+ D ("%5d", 1);
+ D ("%-5d", -1);
+ D ("%-5d", 0);
+ D ("%-5d", 1);
+ D ("%.5d", -1);
+ D ("%.5d", 0);
+ D ("%.5d", 1);
+ D ("%5.0d", -1);
+ D ("%5.0d", 0);
+ D ("%5.0d", 1);
+ D ("%-5.0d", -1);
+ D ("%-5.0d", 0);
+ D ("%-5.0d", 1);
+
+ U ("%d", -1);
+ U ("%d", 0);
+ U ("%d", 1);
+ U ("%5d", -1);
+ U ("%5d", 0);
+ U ("%5d", 1);
+ U ("%-5d", -1);
+ U ("%-5d", 0);
+ U ("%-5d", 1);
+ U ("%.5d", -1);
+ U ("%.5d", 0);
+ U ("%.5d", 1);
+ U ("%5.0d", -1);
+ U ("%5.0d", 0);
+ U ("%5.0d", 1);
+ U ("%-5.0d", -1);
+ U ("%-5.0d", 0);
+ U ("%-5.0d", 1);
+
+ x ("%d", -1);
+ x ("%d", 0);
+ x ("%d", 1);
+ x ("%5d", -1);
+ x ("%5d", 0);
+ x ("%5d", 1);
+ x ("%-5d", -1);
+ x ("%-5d", 0);
+ x ("%-5d", 1);
+ x ("%.5d", -1);
+ x ("%.5d", 0);
+ x ("%.5d", 1);
+ x ("%5.0d", -1);
+ x ("%5.0d", 0);
+ x ("%5.0d", 1);
+ x ("%-5.0d", -1);
+ x ("%-5.0d", 0);
+ x ("%-5.0d", 1);
+
+ X ("%d", -1);
+ X ("%d", 0);
+ X ("%d", 1);
+ X ("%5d", -1);
+ X ("%5d", 0);
+ X ("%5d", 1);
+ X ("%-5d", -1);
+ X ("%-5d", 0);
+ X ("%-5d", 1);
+ X ("%.5d", -1);
+ X ("%.5d", 0);
+ X ("%.5d", 1);
+ X ("%5.0d", -1);
+ X ("%5.0d", 0);
+ X ("%5.0d", 1);
+ X ("%-5.0d", -1);
+ X ("%-5.0d", 0);
+ X ("%-5.0d", 1);
+
+ P ("%p", NULL);
+ P ("%p", sprintf_checks);
+
+ S ("%s", (char *) NULL);
+ S ("%s", "abcd");
+ S ("%10s", "abcd");
+ S ("%10.5s", "abcdefgh");
+ S ("%10.5s", "ab");
+ S ("%2.5s", "a");
+ S ("%2.5s", "abcdefgh");
+
+ D ("%4.2d", 1);
+ D ("%4.2d", 12);
+ D ("%4.2d", 123);
+ D ("%4.2d", 1234);
+ D ("%4.2d", 12345);
+ D ("%3.3d", 12);
+ D ("%3.3d", 123);
+ D ("%3.3d", 1234);
+ D ("%2.4d", 12345);
+ D ("%2.4d", 1234);
+ D ("%2.4d", 123);
+ D ("%2.4d", 12);
+ D ("%2.4d", 1);
+ D ("%.0d", 0);
+ D ("%.0d", 1);
+
+ S ("%4.2s", "1");
+ S ("%4.2s", "12");
+ S ("%4.2s", "123");
+ S ("%4.2s", "1234");
+ S ("%4.2s", "12345");
+ S ("%3.3s", "12");
+ S ("%3.3s", "123");
+ S ("%3.3s", "1234");
+ S ("%2.4s", "12345");
+ S ("%2.4s", "1234");
+ S ("%2.4s", "123");
+ S ("%2.4s", "12");
+ S ("%2.4s", "1");
+
+ /* add few more here, if necessary */
+}
+
+GRUB_UNIT_TEST ("grub_sprintf", sprintf_checks);
=== added file 'tests/test.c'
--- old/tests/test.c 1970-01-01 00:00:00 +0000
+++ new/tests/test.c 2009-12-20 08:28:15 +0000
@@ -0,0 +1,150 @@
+#include
+#include
+
+struct grub_test_failure
+{
+ /* The next failure. */
+ struct grub_test_failure *next;
+
+ /* The test source file name. */
+ char *file;
+
+ /* The test function name. */
+ char *funp;
+
+ /* The test call line number. */
+ grub_uint32_t line;
+
+ /* The test failure message. */
+ char *message;
+};
+typedef struct grub_test_failure *grub_test_failure_t;
+
+grub_test_t grub_test_list;
+static grub_test_failure_t failure_list;
+
+static void
+add_failure (const char *file,
+ const char *funp,
+ grub_uint32_t line, const char *fmt, va_list args)
+{
+ char buf[1024];
+ grub_test_failure_t failure;
+
+ failure = (grub_test_failure_t) grub_test_malloc (sizeof (*failure));
+ if (!failure)
+ return;
+
+ grub_test_vsprintf (buf, fmt, args);
+
+ failure->file = grub_test_strdup (file ? : "");
+ failure->funp = grub_test_strdup (funp ? : "");
+ failure->line = line;
+ failure->message = grub_test_strdup (buf);
+
+ grub_list_push (GRUB_AS_LIST_P (&failure_list), GRUB_AS_LIST (failure));
+}
+
+void
+free_failures (void)
+{
+ grub_test_failure_t item;
+
+ while ((item = grub_list_pop (GRUB_AS_LIST_P (&failure_list))) != 0)
+ {
+ if (item->message)
+ grub_test_free (item->message);
+
+ if (item->funp)
+ grub_test_free (item->funp);
+
+ if (item->file)
+ grub_test_free (item->file);
+
+ grub_test_free (item);
+ }
+ failure_list = 0;
+}
+
+void
+grub_test_nonzero (int cond,
+ const char *file,
+ const char *funp, grub_uint32_t line, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (cond)
+ return;
+
+ va_start (ap, fmt);
+ add_failure (file, funp, line, fmt, ap);
+ va_end (ap);
+}
+
+void
+grub_test_register (const char *name, void (*test_main) (void))
+{
+ grub_test_t test;
+
+ test = (grub_test_t) grub_test_malloc (sizeof (*test));
+ if (!test)
+ return;
+
+ test->name = grub_test_strdup (name);
+ test->main = test_main;
+
+ grub_list_push (GRUB_AS_LIST_P (&grub_test_list), GRUB_AS_LIST (test));
+}
+
+void
+grub_test_unregister (const char *name)
+{
+ grub_test_t test;
+
+ test = (grub_test_t) grub_named_list_find
+ (GRUB_AS_NAMED_LIST (grub_test_list), name);
+
+ if (test)
+ {
+ grub_list_remove (GRUB_AS_LIST_P (&grub_test_list),
+ GRUB_AS_LIST (test));
+
+ if (test->name)
+ grub_test_free (test->name);
+
+ grub_test_free (test);
+ }
+}
+
+int
+grub_test_run (const char *name)
+{
+ grub_test_t test;
+
+ auto int print_failure (grub_test_failure_t item);
+ int print_failure (grub_test_failure_t item)
+ {
+ grub_test_failure_t failure = (grub_test_failure_t) item;
+
+ grub_test_printf (" %s:%s:%u: %s\n",
+ (failure->file ? : ""),
+ (failure->funp ? : ""),
+ failure->line, (failure->message ? : ""));
+ return 0;
+ }
+
+ test = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_test_list), name);
+ if (!test)
+ return GRUB_ERR_FILE_NOT_FOUND;
+
+ test->main ();
+
+ if (!failure_list)
+ return GRUB_ERR_NONE;
+
+ grub_test_printf ("%s:\n", test->name);
+ grub_list_iterate (GRUB_AS_LIST (failure_list),
+ (grub_list_hook_t) print_failure);
+ free_failures ();
+ return GRUB_ERR_TEST_FAILURE;
+}
=== added file 'tests/unit_test.c'
--- old/tests/unit_test.c 1970-01-01 00:00:00 +0000
+++ new/tests/unit_test.c 2009-12-20 08:28:15 +0000
@@ -0,0 +1,122 @@
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+void *
+grub_test_malloc (grub_size_t size)
+{
+ return malloc (size);
+}
+
+void
+grub_test_free (void *ptr)
+{
+ free (ptr);
+}
+
+int
+grub_test_vsprintf (char *str, const char *fmt, va_list args)
+{
+ return vsprintf (str, fmt, args);
+}
+
+char *
+grub_test_strdup (const char *str)
+{
+ return strdup (str);
+}
+
+int
+grub_test_printf (const char *fmt, ...)
+{
+ int r;
+ va_list ap;
+
+ va_start (ap, fmt);
+ r = vprintf (fmt, ap);
+ va_end (ap);
+
+ return r;
+}
+
+int
+main (int argc __attribute__ ((unused)),
+ char *argv[] __attribute__ ((unused)))
+{
+ int status = 0;
+
+ extern void grub_unit_test_init (void);
+ extern void grub_unit_test_fini (void);
+
+ auto int run_test (grub_test_t test);
+ int run_test (grub_test_t test)
+ {
+ status = grub_test_run (test->name) ? : status;
+ return 0;
+ }
+
+ grub_unit_test_init ();
+ grub_list_iterate (GRUB_AS_LIST (grub_test_list),
+ (grub_list_hook_t) run_test);
+ grub_unit_test_fini ();
+
+ exit (status);
+}
+
+/* Other misc. functions necessary for successful linking. */
+
+char *
+grub_env_get (const char *name __attribute__ ((unused)))
+{
+ return NULL;
+}
+
+grub_err_t
+grub_error (grub_err_t n, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+
+ return n;
+}
+
+void *
+grub_malloc (grub_size_t size)
+{
+ return malloc (size);
+}
+
+void
+grub_refresh (void)
+{
+ fflush (stdout);
+}
+
+void
+grub_putchar (int c)
+{
+ putchar (c);
+}
+
+int
+grub_getkey (void)
+{
+ return -1;
+}
+
+void
+grub_exit (void)
+{
+ exit (1);
+}
+
+struct grub_handler_class grub_term_input_class;
+struct grub_handler_class grub_term_output_class;
=== added directory 'tests/util'
=== added file 'tests/util/grub-shell-tester.in'
--- old/tests/util/grub-shell-tester.in 1970-01-01 00:00:00 +0000
+++ new/tests/util/grub-shell-tester.in 2009-12-20 08:28:15 +0000
@@ -0,0 +1,128 @@
+#! /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.
+#
+# 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 .
+
+# Initialize some variables.
+transform="@program_transform_name@"
+
address@hidden@
address@hidden@
address@hidden@
address@hidden@
address@hidden@
address@hidden@
address@hidden@
address@hidden@
address@hidden@
+
+# Force build directory components
+PATH=${builddir}:$PATH
+export PATH
+
+# Usage: usage
+# Print the usage.
+usage () {
+ cat <.
+EOF
+}
+
+# Check the arguments.
+for option in "$@"; do
+ case "$option" in
+ -h | --help)
+ usage
+ exit 0 ;;
+ -v | --version)
+ echo "$0 (GNU GRUB ${PACKAGE_VERSION})"
+ exit 0 ;;
+ --modules=*)
+ ms=`echo "$option" | sed -e 's/--modules=//' -e 's/,/ /g'`
+ modules="$modules $ms" ;;
+ -*)
+ echo "Unrecognized option \`$option'" 1>&2
+ usage
+ exit 1
+ ;;
+ *)
+ if [ "x${source}" != x ] ; then
+ echo "too many parameters at the end" 1>&2
+ usage
+ exit 1
+ fi
+ source="${option}" ;;
+ esac
+done
+
+if [ "x${source}" = x ] ; then
+ echo "source file must be given" >&2
+ usage
+ exit 1
+fi
+
+cfgfile=`mktemp`
+cat <${cfgfile}
+insmod serial
+serial
+terminal_input serial
+terminal_output serial
+EOF
+
+for mod in ${modules}
+do
+ echo "insmod ${mod}" >> ${cfgfile}
+done
+
+cat <>${cfgfile}
+source /boot/grub/testcase.cfg
+halt
+EOF
+
+isofile=`mktemp`
+grub-mkrescue --output=${isofile} --override-directory=${builddir} \
+ /boot/grub/grub.cfg=${cfgfile} /boot/grub/testcase.cfg=${source} \
+ >/dev/null 2>&1
+
+outfile1=`mktemp`
+qemu -nographic -serial stdio -cdrom ${isofile} | tr -d "\r" >${outfile1}
+
+outfile2=`mktemp`
+bash ${source} >${outfile2}
+
+if ! diff -q ${outfile1} ${outfile2} >/dev/null
+then
+ echo "GRUB: ${outfile1}"
+ hexdump -C ${outfile1}
+ echo "BASH: ${outfile2}"
+ hexdump -C ${outfile2}
+ status=1
+else
+ rm -f ${cfgfile} ${isofile} ${outfile1} ${outfile2}
+fi
+
+exit $status
+
+
=== added file 'util/grub-script-check.c'
--- old/util/grub-script-check.c 1970-01-01 00:00:00 +0000
+++ new/util/grub-script-check.c 2009-12-20 08:28:15 +0000
@@ -0,0 +1,248 @@
+/* grub-script-check.c - check grub script file for syntax errors */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 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
+#include
+#include
+
+#include
+
+#define _GNU_SOURCE 1
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "progname.h"
+
+void
+grub_putchar (int c)
+{
+ putchar (c);
+}
+
+int
+grub_getkey (void)
+{
+ return -1;
+}
+
+struct grub_handler_class grub_term_input_class;
+struct grub_handler_class grub_term_output_class;
+
+char *
+grub_script_execute_argument_to_string (struct grub_script_arg *arg __attribute__ ((unused)))
+{
+ return 0;
+}
+
+grub_err_t
+grub_script_execute_cmdline (struct grub_script_cmd *cmd __attribute__ ((unused)))
+{
+ return 0;
+}
+
+grub_err_t
+grub_script_execute_cmdblock (struct grub_script_cmd *cmd __attribute__ ((unused)))
+{
+ return 0;
+}
+
+grub_err_t
+grub_script_execute_cmdif (struct grub_script_cmd *cmd __attribute__ ((unused)))
+{
+ return 0;
+}
+
+grub_err_t
+grub_script_execute_menuentry (struct grub_script_cmd *cmd)
+{
+ 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;
+}
+
+grub_err_t
+grub_script_execute (struct grub_script *script)
+{
+ if (script == 0 || script->cmd == 0)
+ return 0;
+
+ return script->cmd->exec (script->cmd);
+}
+
+static struct option options[] =
+ {
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {"verbose", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+ };
+
+static void
+usage (int status)
+{
+ if (status)
+ fprintf (stderr,
+ "Try ``%s --help'' for more information.\n", program_name);
+ else
+ printf ("\
+Usage: %s [PATH]\n\
+\n\
+Checks GRUB script configuration file for syntax errors.\n\
+\n\
+ -h, --help display this message and exit\n\
+ -V, --version print version information and exit\n\
+ -v, --verbose print script being processed\n\
+\n\
+Report bugs to <%s>.\n\
+", program_name,
+ PACKAGE_BUGREPORT);
+ exit (status);
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *argument;
+ char *input;
+ FILE *file = 0;
+ int verbose = 0;
+ struct grub_script *script;
+
+ auto grub_err_t get_config_line (char **line, int cont);
+ grub_err_t get_config_line (char **line, int cont __attribute__ ((unused)))
+ {
+ char *cmdline = 0;
+ size_t len = 0;
+ ssize_t read;
+
+ read = getline(&cmdline, &len, (file ?: stdin));
+ if (read == -1)
+ {
+ *line = 0;
+ grub_errno = GRUB_ERR_READ_ERROR;
+
+ if (cmdline)
+ free (cmdline);
+ return grub_errno;
+ }
+
+ if (verbose)
+ grub_printf("%s", cmdline);
+
+ *line = grub_strdup (cmdline);
+
+ free (cmdline);
+ return 0;
+ }
+
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ /* Check for options. */
+ while (1)
+ {
+ int c = getopt_long (argc, argv, "hvV", options, 0);
+
+ if (c == -1)
+ break;
+ else
+ switch (c)
+ {
+ case 'h':
+ usage (0);
+ break;
+
+ case 'V':
+ printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
+ return 0;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ usage (1);
+ break;
+ }
+ }
+
+ /* Obtain ARGUMENT. */
+ if (optind >= argc)
+ {
+ file = 0; // read from stdin
+ }
+ else if (optind + 1 != argc)
+ {
+ fprintf (stderr, "Unknown extra argument `%s'.\n", argv[optind + 1]);
+ usage (1);
+ }
+ else
+ {
+ argument = argv[optind];
+ file = fopen (argument, "r");
+ if (! file)
+ {
+ fprintf (stderr, "%s: %s: %s\n", program_name, argument, strerror(errno));
+ usage (1);
+ }
+ }
+
+ /* Initialize all modules. */
+ grub_init_all ();
+
+ do
+ {
+ input = 0;
+ get_config_line(&input, 0);
+ if (! input)
+ break;
+
+ script = grub_script_parse (input, get_config_line);
+ if (script)
+ {
+ grub_script_execute (script);
+ grub_script_free (script);
+ }
+
+ grub_free (input);
+ } while (script != 0);
+
+ /* Free resources. */
+ grub_fini_all ();
+ if (file) fclose (file);
+
+ return (script == 0);
+}