]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
lexer-rewrite rebase commit
authorBVK Chaitanya <bvk.groups@gmail.com>
Fri, 22 Jan 2010 13:37:27 +0000 (19:07 +0530)
committerBVK Chaitanya <bvk.groups@gmail.com>
Fri, 22 Jan 2010 13:37:27 +0000 (19:07 +0530)
25 files changed:
ChangeLog.lexer-rewrite [new file with mode: 0644]
conf/any-emu.rmk
conf/common.rmk
conf/i386-coreboot.rmk
conf/i386-efi.rmk
conf/i386-ieee1275.rmk
conf/i386-pc.rmk
conf/powerpc-ieee1275.rmk
conf/sparc64-ieee1275.rmk
conf/tests.rmk
conf/x86_64-efi.rmk
configure.ac
include/grub/script_sh.h
script/execute.c
script/function.c
script/lexer.c
script/parser.y
script/script.c
script/yylex.l [new file with mode: 0644]
tests/grub_script_echo1.in [new file with mode: 0644]
tests/grub_script_echo_keywords.in [new file with mode: 0644]
tests/grub_script_vars1.in [new file with mode: 0644]
tests/util/grub-shell-tester.in
tests/util/grub-shell.in
util/grub-script-check.c

diff --git a/ChangeLog.lexer-rewrite b/ChangeLog.lexer-rewrite
new file mode 100644 (file)
index 0000000..56c6379
--- /dev/null
@@ -0,0 +1,70 @@
+2010-01-10  BVK Chaitanya  <bvk.groups@gmail.com>
+
+       * conf/any-emu.rmk: Build rule updates.
+       * conf/common.rmk: Likewise.
+       * conf/i386-coreboot.rmk: Likewise.
+       * conf/i386-efi.rmk: Likewise.
+       * conf/i386-ieee1275.rmk: Likewise.
+       * conf/i386-pc.rmk: Likewise.
+       * conf/powerpc-ieee1275.rmk: Likewise.
+       * conf/x86_64-efi.rmk: Likewise.
+
+       * configure.ac: Configure check for flex.
+
+       * include/grub/script_sh.h (grub_script_arg_type_t): More argument
+       types.
+       (grub_lexer_param): Struct member updates.
+       (grub_parser_param): Likewise.
+       (GRUB_LEXER_TOKEN_MAX): Maximum token size.
+       (GRUB_LEXER_RECORD_INCREMENT): Memory increments' size.
+       (grub_script_lexer_init): Prototype update.
+       (grub_script_lexer_record_start): Likewise.
+       (grub_script_lexer_record_stop): Likewise.
+       (grub_script_lexer_yywrap): New function prototype.
+       (grub_script_lexer_fini): Likewise.
+       (grub_script_execute_argument_to_string): Removed by...
+       (grub_script_execute_argument_to_argv): ...better version.
+
+       * script/execute.c (ROUND_UPTO): New macro.
+       (grub_script_execute_cmdline): Out of memory fixes.
+       (grub_script_execute_menuentry): Likewise.
+       (grub_script_execute_argument_to_string): Removed. Update all
+       users by...
+       (grub_script_execute_argument_to_argv): ...better version.
+       * script/function.c (grub_script_function_create): Use
+       grub_script_execute_argument_to_argv instead of
+       grub_script_execute_argument_to_string.
+
+       * script/lexer.c (check_varstate): Removed.
+       (check_textstate): Removed.
+       (grub_script_lexer_record_start): Likewise.
+       (grub_script_lexer_record_stop): Likewise.
+       (recordchar): Replaced with...
+       (grub_script_lexer_record): ...new function.
+       (nextchar): Removed.
+       (grub_script_lexer_init): Rewritten.
+       (grub_script_yylex): Rewritten.
+       (append_newline): New function.
+       (grub_script_lexer_yywrap): New function.
+       (grub_script_lexer_fini): New function.
+       (grub_script_yyerror): Sets error flag.
+
+       * script/yylex.l: New file.
+       (grub_lexer_yyfree): Wrapper for flex yyffre.
+       (grub_lexer_yyalloc): Likewise.
+       (grub_lexer_yyrealloc): Likewise.
+       * script/parser.y: Refactored.
+
+       * script/script.c (grub_script_arg_add): Out of memory fixes.
+       (grub_script_add_arglist): Likewise.
+       (grub_script_create_cmdline): Likewise.
+       (grub_script_create_cmdmenu): Likewise.
+       (grub_script_add_cmd): Likewise.
+       (grub_script_parse): Use grub_script_lexer_fini to deallocated.
+       * util/grub-script-check.c (grub_script_execute_menuentry): Remove
+       unnecessary code.
+
+       * tests/grub_script_echo1.in: New testcase.
+       * tests/grub_script_vars1.in: New testcase.
+       * tests/grub_script_echo_keywords.in: New testcase.
+
index 1277af7911c70a14d2e7928d893485ee8e024cd9..cf343e61f25c912136c1c78bbf0eaec0ecb04d13 100644 (file)
@@ -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 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c   \
        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 @@ 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.*\(<stdio\.h>\|<string\.h>\|<errno\.h>\|<stdlib\.h>\|<unistd\.h>\)//g' grub_script.yy.h
+       sed -i 's/^#include.*\(<stdio\.h>\|<string\.h>\|<errno\.h>\|<stdlib\.h>\|<unistd\.h>\)//g' grub_script.yy.c
+DISTCLEANFILES += grub_script.yy.c grub_script.yy.h
+
 bin_UTILITIES += grub-bin2h
 grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c
index 2ea8ebd5a09a3b6768588701852befa68604c586..52491d5305742a70e88045fbac1cce4aef413008 100644 (file)
@@ -91,13 +91,21 @@ grub_mkrelpath_SOURCES = gnulib/progname.c util/grub-mkrelpath.c util/misc.c
 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.*\(<stdio\.h>\|<string\.h>\|<errno\.h>\|<stdlib\.h>\|<unistd\.h>\)//g' grub_script.yy.h
+       sed -i 's/^#include.*\(<stdio\.h>\|<string\.h>\|<errno\.h>\|<stdlib\.h>\|<unistd\.h>\)//g' grub_script.yy.c
+DISTCLEANFILES += grub_script.yy.c grub_script.yy.h
+
 # For grub-script-check.
 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 @@ normal_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # 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)
 
index 9563c0b2b3f0efc3a4f951587be4a64a51246052..c9703d0828d42e729468e9f0a82380a7081164ef 100644 (file)
@@ -5,7 +5,7 @@ COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32
 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.
 
index c03abb429178da4fd17dc31f64ac49ff7ef36ce7..4314c15eb25fe5249f0b1f8003a8df6913683526 100644 (file)
@@ -5,7 +5,7 @@ COMMON_CFLAGS = -fno-builtin -m32
 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
index e19f6e9a17f886fa3f730f760b4693a3a050c626..7c6520323a7509eef686f0721f8786f8d06ede85 100644 (file)
@@ -5,7 +5,7 @@ COMMON_CFLAGS   = -ffreestanding -mrtd -mregparm=3
 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
index 580bfea0a0ac2a478da282e4472ba45afbc1e410..9cae817ff637666f3eca8fd54bc66cbf31673b98 100644 (file)
@@ -7,7 +7,7 @@ COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32
 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 \
index 23bd2d620f4d7df35291a45743c6cb0f24f2a80f..90afb0b8b501a44070a069bfdaa23388e4dad368 100644 (file)
@@ -6,7 +6,7 @@ COMMON_CFLAGS = -ffreestanding
 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.
 
index befc7dce57738f7e112b48b525e7f578e4b46a6d..946c1f06b94a27430e7b62fc7d592a72ab422911 100644 (file)
@@ -6,7 +6,7 @@ COMMON_CFLAGS = -ffreestanding -m64 -mno-app-regs
 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
index 18b23ff5a92acf130e33af4aaef42dd8d9eda1b8..c5080f58c7146d18930e0e7de06f6cb5b728b87b 100644 (file)
@@ -37,12 +37,28 @@ example_scripted_test_SOURCES = tests/example_scripted_test.in
 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
index d5c3c24cb700527af96579bca6f7b91eb507af69..b46236ade36e10f73d9b899075ba1073abe94afc 100644 (file)
@@ -5,7 +5,7 @@ COMMON_CFLAGS = -fno-builtin -m64
 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
index b1435de44f64b8954920c436fe299426ab4bdb06..ea2218d9e9f74739c837977d363209e8fb70d4bc 100644 (file)
@@ -163,6 +163,11 @@ if test "x$CMP" = x; then
   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])
index 0bd14abcdebb7cce3d202aeb090c2f9f6fc7cef8..889017d2a97d423b8329b948cd173d265652af69 100644 (file)
@@ -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 @@ struct grub_script
 \f
 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 @@ struct grub_script_cmd_menuentry
 /* 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 @@ struct grub_lexer_param
      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,32 @@ struct grub_lexer_param
   /* Size of RECORDING.  */
   int recordlen;
 
-  /* The token that is already parsed but not yet returned. */
-  int tokenonhold;
+  /* End of file reached.  */
+  int eof;
+
+  /* Merge multiple word tokens.  */
+  int merge_start;
+  int merge_end;
+
+  /* Text of current token.  */
+  char *text;
 
-  /* Was the last token a newline? */
-  int was_newline;
+  /* Type of text.  */
+  grub_script_arg_type_t type;
+
+  /* Flex scanner.  */
+  void *yyscanner;
+
+  /* Flex scanner buffer.  */
+  void *buffer;
+
+  /* Length of current token text.  */
+  unsigned size;
 };
 
+#define GRUB_LEXER_TOKEN_MAX        256
+#define GRUB_LEXER_RECORD_INCREMENT 256
+
 /* State of the parser as passes to the parser.  */
 struct grub_parser_param
 {
@@ -223,12 +235,16 @@ void grub_script_free (struct grub_script *script);
 struct grub_script *grub_script_create (struct grub_script_cmd *cmd,
                                        struct grub_script_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 +300,7 @@ int grub_script_function_iterate (int (*iterate) (grub_script_function_t));
 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);
 
 #endif /* ! GRUB_NORMAL_PARSER_HEADER */
index ee7e099bce22afe7bc990b37e5907385c343842d..83b6649ce307cb06c2a3fdc1ca0f93c095dec4e5 100644 (file)
@@ -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 @@ grub_script_execute_cmd (struct grub_script_cmd *cmd)
   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)
-{
-  int size = 0;
-  char *val;
-  char *chararg;
-  struct grub_script_arg *argi;
+#define ROUND_UPTO(sz,up) (((sz) + (up) - 1) / (up) *  (up))
 
-  /* First determine the size of the argument.  */
-  for (argi = arg; argi; argi = argi->next)
+/* Expand arguments in ARGLIST into multiple arguments.  */
+char **
+grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist)
+{
+  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, ROUND_UPTO (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], ROUND_UPTO(old + len + 1, 32));
+
+    if (p)
+      {
+       grub_strncpy (p + old, str, len);
+       p[old + len] = '\0';
+      }
+    else
+      {
+       oom = 1;
+       grub_free (argv[argc - 1]);
+      }
+    argv[argc - 1] = p;
+    return argv[argc - 1];
+  }
+
+  /* Move *STR to the begining of next word, but return current word.  */
+  auto char* move_to_next (char **str);
+  char* move_to_next (char **str)
+  {
+    char *end;
+    char *start;
+
+    if (oom || !str || !*str)
+      return 0;
+
+    start = *str;
+    while (*start && grub_isspace (*start)) start++;
+    if (*start == '\0')
+      return 0;
+
+    end = start + 1;
+    while (*end && !grub_isspace (*end)) end++;
+
+    *str = end;
+    return start;
+  }
+
+  oom = 0;
+  argv = 0;
+  argc = 0;
+  push (0);
+  for (; arglist; arglist = arglist->next)
     {
-      if (argi->type == 1)
+      empty = 1;
+      arg = arglist->arg;
+      while (arg)
        {
-         val = grub_env_get (argi->str);
-         if (val)
-           size += grub_strlen (val);
+         switch (arg->type)
+           {
+           case GRUB_SCRIPT_ARG_TYPE_VAR:
+             value = grub_env_get (arg->str);
+             while (*value && (ptr = move_to_next(&value)))
+               {
+                 empty = 0;
+                 append (ptr, value - ptr);
+                 if (*value) push(0);
+               }
+             break;
+
+           case GRUB_SCRIPT_ARG_TYPE_TEXT:
+             if (grub_strlen (arg->str) > 0)
+               {
+                 empty = 0;
+                 append (arg->str, 0);
+               }
+             break;
+
+           case GRUB_SCRIPT_ARG_TYPE_DQSTR:
+           case GRUB_SCRIPT_ARG_TYPE_SQSTR:
+             empty = 0;
+             append (arg->str, 0);
+             break;
+
+           case GRUB_SCRIPT_ARG_TYPE_DQVAR:
+             empty = 0;
+             append (grub_env_get (arg->str), 0);
+             break;
+           }
+         arg = arg->next;
        }
-      else
-       size += grub_strlen (argi->str);
+      if (!empty)
+       push (0);
     }
+  push (0); /* Ensure argv[argc] == 0.  */
 
-  /* 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 (oom)
     {
-      if (argi->type == 1)
-       {
-         val = grub_env_get (argi->str);
-         if (val)
-           grub_strcat (chararg, val);
-       }
-      else
-       grub_strcat (chararg, argi->str);
+      for (i = 0; i < argc; i++)
+       grub_free (argv[i]);
+      grub_free (argv);
+      argv = 0;
     }
 
-  return chararg;
+  return argv;
 }
 
 /* Execute a single command line.  */
@@ -85,7 +182,6 @@ grub_err_t
 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 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd)
   char *cmdname;
 
   /* Lookup the command.  */
-  cmdname = grub_script_execute_argument_to_string (cmdline->arglist->arg);
+  args = grub_script_execute_arglist_to_argv (cmdline->arglist);
+  if (!args)
+    return grub_errno;
+
+  cmdname = args[0];
   grubcmd = grub_command_find (cmdname);
   if (! grubcmd)
     {
@@ -129,27 +229,15 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd)
          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;
-       }
-    }
+  /* Count argv size.  */
+  for (argcount = 0; args[argcount]; argcount++);
 
   /* 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 +290,6 @@ grub_err_t
 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 +298,11 @@ grub_script_execute_menuentry (struct grub_script_cmd *cmd)
 
   if (cmd_menuentry->arglist)
     {
-      argcount = cmd_menuentry->arglist->argcount;
-
-      /* Create argv from the arguments.  */
-      args = grub_malloc (sizeof (char *) * argcount);
+      args = grub_script_execute_arglist_to_argv (cmd_menuentry->arglist);
+      if (!args)
+       return grub_errno;
 
-      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;
-       }
+      for (argcount = 0; args[argcount]; argcount++);
     }
 
   grub_normal_add_menu_entry (argcount, (const char **) args,
index a3950a8a0eb7e107e8081cae2a6e34c098f8a985..ded470c4ee1650132db920ded1e8768e782afedc 100644 (file)
@@ -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 @@ grub_script_function_create (struct grub_script_arg *functionname_arg,
   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);
index 5bcdf628b4c19d8c009c12ad51e030f1644ae9f4..d43c9f1570192c6328c34dc0964a04d914282074 100644 (file)
@@ -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
 #include <grub/script_sh.h>
 
 #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,305 @@ grub_script_lexer_deref (struct grub_lexer_param *state)
 
 /* 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, 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;
+  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;
 
-  /* 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';
-    }
+  /* XXX This is not necessary in BASH.  */
 
-  return state->recording;
+  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)
+
+/* 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;
+         lexer->record = 0;
+         grub_script_yyerror (parser, 0);
+         return;
        }
     }
-  state->recording[state->recordpos++] = c;
+  grub_strcpy (lexer->recording + lexer->recordpos, str);
+  lexer->recordpos += len;
 }
 
-/* Fetch the next character for the lexer.  */
-static void
-nextchar (struct grub_lexer_param *state)
+/* Append '\n' to SRC, before '\0'  */
+static char *
+append_newline (const char *src)
 {
-  if (state->record)
-    recordchar (state, *state->script);
-  state->script++;
+  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_yylex (union YYSTYPE *yylval, struct grub_parser_param *parsestate)
+grub_script_lexer_yywrap (struct grub_parser_param *parserstate)
 {
-  grub_parser_state_t newstate;
-  char use;
-  struct grub_lexer_param *state = parsestate->lexerstate;
-  int firstrun = 1;
+  int len;
+  char *line;
+  char *line2;
+  YY_BUFFER_STATE buffer;
+  struct grub_lexer_param *lexerstate = parserstate->lexerstate;
 
-  yylval->arg = 0;
+  if (!lexerstate->refs)
+    return 0;
+
+  if (!lexerstate->getline)
+    {
+      grub_script_yyerror (parserstate, "unexpected end of file");
+      return 0;
+    }
 
-  if (state->tokenonhold)
+  line = 0;
+  buffer = 0;
+  lexerstate->getline (&line, 1);
+  if (!line)
     {
-      int token = state->tokenonhold;
-      state->tokenonhold = 0;
-      return token;
+      grub_script_yyerror (parserstate, 0); /* XXX this could be for ^C case? */
+      return 0;
     }
 
-  for (;! state->done; firstrun = 0)
+  len = grub_strlen (line);
+  if (line[len - 1] == '\n')
+    {
+      buffer = yy_scan_string (line, lexerstate->yyscanner);
+    }
+  else
     {
-      if (! state->script || ! *state->script)
+      line2 = append_newline (line);
+      if (line2)
        {
-         /* 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;
-           }
+         buffer = yy_scan_string (line2, lexerstate->yyscanner);
+         grub_free (line2);
        }
-      state->was_newline = 0;
+    }
+
+  grub_free (line);
+  if (!buffer)
+    {
+      grub_script_yyerror (parserstate, 0);
+      return 0;
+    }
+
+  return 1;
+}
 
-      newstate = grub_parser_cmdline_state (state->state, *state->script, &use);
+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;
+    }
 
-      /* Check if it is a text.  */
-      if (check_textstate (newstate))
+  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)
        {
-         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);
+         buffer = yy_scan_string (script2, lexerstate->yyscanner);
+         grub_free (script2);
        }
-      else if (newstate == GRUB_PARSER_STATE_VAR
-              || newstate == GRUB_PARSER_STATE_QVAR)
+    }
+
+  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)
        {
-         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);
+         lexerstate->text[lexerstate->size] = '\0';
+         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;
        }
-    }
+      /* grub_printf ("tok %u, txt [%s] size %u\n", token, str, lexerstate->size); */
 
-  if (yylval->arg == 0)
-    {
-      int token = state->tokenonhold;
-      state->tokenonhold = 0;
-      return token;
+      value->arg = grub_script_arg_add (parserstate, value->arg, type, str);
     }
+  while (lexerstate->merge_start && !lexerstate->merge_end);
 
-  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;
-    }
+  if (!value->arg || parserstate->err)
+    return GRUB_PARSER_TOKEN_BAD;
 
-  return GRUB_PARSER_TOKEN_ARG;
+  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++;
 }
index 094a8856e57b3f14838758fbb929f954804c530c..6d1b5bdbc9d17c48a036c9d4437d04596936b007 100644 (file)
@@ -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
 #include <grub/script_sh.h>
 #include <grub/mm.h>
 
-#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
 
 %}
 
   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 <cmd> script_init script grubcmd command commands commandblock menuentry if
-%type <arglist> arguments;
-%type <arg> 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 <arg> GRUB_PARSER_TOKEN_CASE      "case"
+%token <arg> GRUB_PARSER_TOKEN_DO        "do"
+%token <arg> GRUB_PARSER_TOKEN_DONE      "done"
+%token <arg> GRUB_PARSER_TOKEN_ELIF      "elif"
+%token <arg> GRUB_PARSER_TOKEN_ELSE      "else"
+%token <arg> GRUB_PARSER_TOKEN_ESAC      "esac"
+%token <arg> GRUB_PARSER_TOKEN_FI        "fi"
+%token <arg> GRUB_PARSER_TOKEN_FOR       "for"
+%token <arg> GRUB_PARSER_TOKEN_IF        "if"
+%token <arg> GRUB_PARSER_TOKEN_IN        "in"
+%token <arg> GRUB_PARSER_TOKEN_SELECT    "select"
+%token <arg> GRUB_PARSER_TOKEN_THEN      "then"
+%token <arg> GRUB_PARSER_TOKEN_UNTIL     "until"
+%token <arg> GRUB_PARSER_TOKEN_WHILE     "while"
+%token <arg> GRUB_PARSER_TOKEN_TIME      "time"
+%token <arg> GRUB_PARSER_TOKEN_FUNCTION  "function"
+%token <arg> GRUB_PARSER_TOKEN_MENUENTRY "menuentry"
+%token <arg> GRUB_PARSER_TOKEN_NAME      "name"
+%token <arg> GRUB_PARSER_TOKEN_WORD      "word"
+
+%type <arglist> word argument arguments0 arguments1
+%type <cmd> script_init script grubcmd ifcmd command
+%type <cmd> commands1 menuentry statement
 
 %pure-parser
-%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_init: { state->err = 0; } script { state->parsed = $2; state->err = 0; }
 ;
 
-script:                { $$ = 0; }
-                | '\n' { $$ = 0; }
-                | commands { $$ = $1; }
-               | function '\n' { $$ = 0; }
-               | menuentry '\n' { $$ = $1; }
-               | error
-                 {
-                   $$ = 0;
-                   yyerror (state, "Incorrect command");
-                   state->err = 1;
-                   yyerrok;
-                 }
+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;
+        }
 ;
 
-delimiter:     '\n'
-               | ';'
-               | delimiter '\n'
-;
+newlines0: /* Empty */ | newlines1 ;
+newlines1: newlines0 "\n" ;
 
-newlines:      /* Empty */
-               | newlines '\n'
+delimiter: ";"
+         | "\n"
+;
+delimiters0: /* Empty */ | delimiters1 ;
+delimiters1: delimiter
+          | delimiters1 "\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);
-                 }
+word: GRUB_PARSER_TOKEN_NAME { $$ = grub_script_add_arglist (state, 0, $1); }
+    | GRUB_PARSER_TOKEN_WORD { $$ = grub_script_add_arglist (state, 0, $1); }
 ;
 
-grubcmd:       arguments
-                 {
-                   $$ = grub_script_create_cmdline (state, $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; }
 ;
 
-/* A single command.  */
-command:       grubcmd delimiter { $$ = $1; }
-               | if delimiter  { $$ = $1; }
-               | commandblock delimiter { $$ = $1; }
+arguments0: /* Empty */ { $$ = 0; }
+          | arguments1  { $$ = $1; }
+;
+arguments1: argument arguments0
+            {
+             if ($1 && $2)
+               {
+                 $1->next = $2;
+                 $1->argcount += $2->argcount;
+                 $2->argcount = 0;
+               }
+             $$ = $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);
-                 }
+grubcmd: word arguments0
+         {
+           if ($1 && $2) {
+             $1->next = $2;
+             $1->argcount += $2->argcount;
+             $2->argcount = 0;
+           }
+           $$ = grub_script_create_cmdline (state, $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);
-                 }
+/* A single command.  */
+command: grubcmd { $$ = $1; }
+       | ifcmd   { $$ = $1; }
 ;
 
-/* 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 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);
+           }
 ;
 
-/* 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);
-                 }
+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);
+          }
 ;
 
-/* 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); }
+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);
+           }
 ;
 
-/* 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);
-                 }
+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);
+       }
 ;
index c04a4496690d0b7b65ec560178ac43417c1bc3c8..b2d870745c5dd233ec03da10e9ece733281bb09d 100644 (file)
@@ -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
 
 /* 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.  */
+   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
+   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 @@ grub_script_malloc (struct grub_parser_param *state, grub_size_t size)
   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 @@ grub_script_mem_record_stop (struct grub_parser_param *state,
 void
 grub_script_free (struct grub_script *script)
 {
-  if (! script)
+  if (!script)
     return;
   grub_script_mem_free (script->mem);
   grub_free (script);
 }
-
 \f
 
+
 /* 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 @@ grub_script_arg_add (struct grub_parser_param *state, struct grub_script_arg *ar
    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_script_create_cmdline (struct grub_parser_param *state,
   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_script_create_cmdif (struct grub_parser_param *state,
   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 @@ grub_script_create_cmdif (struct grub_parser_param *state,
 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 @@ grub_script_add_cmd (struct grub_parser_param *state,
                     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 @@ grub_script_add_cmd (struct grub_parser_param *state,
     }
   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;
 }
-
 \f
 
+
 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 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
   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 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
       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 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
   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;
diff --git a/script/yylex.l b/script/yylex.l
new file mode 100644 (file)
index 0000000..4f6c00e
--- /dev/null
@@ -0,0 +1,331 @@
+%{
+/* yylex.l  The scripting lexer.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009,2010  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/parser.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/script_sh.h>
+#include "grub_script.tab.h"
+
+#define yyfree    grub_lexer_yyfree
+#define yyalloc   grub_lexer_yyalloc
+#define yyrealloc grub_lexer_yyrealloc
+
+/* 
+ * As we don't have access to yyscanner, we cannot do much except to
+ * print the fatal error.
+ */
+#define YY_FATAL_ERROR(msg)                     \
+  do {                                          \
+    grub_printf ("fatal error: %s\n", msg);     \
+  } while (0)
+
+#define PUSH(c)                                                                \
+  do {                                                                 \
+    if (yyextra->lexerstate->size >= GRUB_LEXER_TOKEN_MAX - 1)         \
+      grub_script_yyerror (yyextra, "token too long");                 \
+    else                                                               \
+      yyextra->lexerstate->text[yyextra->lexerstate->size++] = c;      \
+  } while (0)
+      
+#define COPY(str)                              \
+  do {                                         \
+    char *ptr = str;                           \
+    while (*ptr && ! yyextra->err)             \
+      {                                                \
+        PUSH (*ptr);                           \
+       ptr++;                                  \
+      }                                                \
+  } while (0)
+
+#define RECORD                                 \
+  do {                                         \
+    grub_script_lexer_record (yyextra, yytext);        \
+  } while (0)
+
+#define ARG(t)                       \
+  do {                               \
+    yyextra->lexerstate->type = t;    \
+    return GRUB_PARSER_TOKEN_WORD;    \
+  } while (0)
+
+/* We don't need YY_INPUT, as we rely on yy_scan_strings */
+#define YY_INPUT(buf,res,max) do { res = 0; } while (0)
+
+/* forward declarations */
+static void  grub_lexer_yyfree (void *, yyscan_t yyscanner);
+static void* grub_lexer_yyalloc (yy_size_t, yyscan_t yyscanner);
+static void* grub_lexer_yyrealloc (void*, yy_size_t, yyscan_t yyscanner);
+
+%}
+
+%top{
+
+/* 
+ * Some flex hacks for -nostdinc; XXX We need to fix these when libc
+ * support becomes availble in GRUB.
+ */
+
+#include <grub/types.h>
+
+typedef grub_size_t size_t;
+typedef grub_size_t yy_size_t;
+#define YY_TYPEDEF_YY_SIZE_T 1
+
+#define FILE   void
+#define stdin  0
+#define stdout 0
+#define EOF    0
+
+#define errno  grub_errno
+#define EINVAL GRUB_ERR_BAD_NUMBER
+#define ENOMEM GRUB_ERR_OUT_OF_MEMORY
+
+#define strlen grub_strlen
+#define memset grub_memset
+
+#define fprintf(...) 0
+#define exit(...)
+
+#pragma GCC diagnostic warning "-Wunused-variable"
+#pragma GCC diagnostic warning "-Wunused-function"
+#pragma GCC diagnostic warning "-Wunused-parameter"
+#pragma GCC diagnostic warning "-Wstrict-prototypes"
+#pragma GCC diagnostic warning "-Wmissing-prototypes"
+
+}
+
+%option ecs
+%option meta-ecs
+
+%option warn
+%option array
+%option stack
+%option reentrant
+%option bison-bridge
+%option never-interactive
+
+%option noyyfree noyyalloc noyyrealloc
+%option nounistd nostdinit nodefault noyylineno noyywrap
+
+/* Reduce lexer size, by not defining these.  */
+%option noyy_top_state
+%option noinput nounput
+%option noyyget_in noyyset_in
+%option noyyget_out noyyset_out
+%option noyyget_debug noyyset_debug
+%option noyyget_lineno noyyset_lineno
+
+%option extra-type="struct grub_parser_param*"
+
+BLANK           [ \t]
+COMMENT         ^[ \t]*#.*$
+
+CHAR            [^|&;()<> \t\n\'\"]
+DIGITS          [[:digit:]]+
+NAME            [[:alpha:]_][[:alnum:][:digit:]_]*
+
+ESC             \\.
+VARIABLE        ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|$\?|$\{\?\}
+DQSTR           \"([^\"]|\\\")*\"
+SQSTR           \'[^\']*\'
+WORD            ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+
+
+%x              SPLIT
+%x              DQUOTE
+%x              SQUOTE
+%x              VAR
+
+%%
+
+ /* White spaces */
+{BLANK}+        { RECORD; }
+{COMMENT}       { RECORD; }
+
+ /* Special symbols */
+"\n"            { RECORD; return GRUB_PARSER_TOKEN_NEWLINE; }
+"||"            { RECORD; return GRUB_PARSER_TOKEN_OR;      }
+"&&"            { RECORD; return GRUB_PARSER_TOKEN_AND;     }
+";;"            { RECORD; return GRUB_PARSER_TOKEN_SEMI2;   }
+"|"             { RECORD; return GRUB_PARSER_TOKEN_PIPE;    }
+"&"             { RECORD; return GRUB_PARSER_TOKEN_AMP;     }
+";"             { RECORD; return GRUB_PARSER_TOKEN_SEMI;    }
+"("             { RECORD; return GRUB_PARSER_TOKEN_LPAR;    }
+")"             { RECORD; return GRUB_PARSER_TOKEN_RPAR;    }
+"<"             { RECORD; return GRUB_PARSER_TOKEN_LT;      }
+">"             { RECORD; return GRUB_PARSER_TOKEN_GT;      }
+
+ /* Reserved words */
+"!"             { RECORD; return GRUB_PARSER_TOKEN_NOT;       }
+"{"             { RECORD; return GRUB_PARSER_TOKEN_LBR;       }
+"}"             { RECORD; return GRUB_PARSER_TOKEN_RBR;       }
+"[["            { RECORD; return GRUB_PARSER_TOKEN_RSQBR2;    }
+"]]"            { RECORD; return GRUB_PARSER_TOKEN_LSQBR2;    }
+"time"          { RECORD; return GRUB_PARSER_TOKEN_TIME;      }
+"case"          { RECORD; return GRUB_PARSER_TOKEN_CASE;      }
+"do"            { RECORD; return GRUB_PARSER_TOKEN_DO;        }
+"done"          { RECORD; return GRUB_PARSER_TOKEN_DONE;      }
+"elif"          { RECORD; return GRUB_PARSER_TOKEN_ELIF;      }
+"else"          { RECORD; return GRUB_PARSER_TOKEN_ELSE;      }
+"esac"          { RECORD; return GRUB_PARSER_TOKEN_ESAC;      }
+"fi"            { RECORD; return GRUB_PARSER_TOKEN_FI;        }
+"for"           { RECORD; return GRUB_PARSER_TOKEN_FOR;       }
+"if"            { RECORD; return GRUB_PARSER_TOKEN_IF;        }
+"in"            { RECORD; return GRUB_PARSER_TOKEN_IN;        }
+"select"        { RECORD; return GRUB_PARSER_TOKEN_SELECT;    }
+"then"          { RECORD; return GRUB_PARSER_TOKEN_THEN;      }
+"until"         { RECORD; return GRUB_PARSER_TOKEN_UNTIL;     }
+"while"         { RECORD; return GRUB_PARSER_TOKEN_WHILE;     }
+"function"      { RECORD; return GRUB_PARSER_TOKEN_FUNCTION;  }
+"menuentry"     { RECORD; return GRUB_PARSER_TOKEN_MENUENTRY; }
+
+{NAME}          { RECORD; return GRUB_PARSER_TOKEN_NAME; }
+{WORD}          {
+                  RECORD;
+                  /* resplit yytext */
+                  yypush_buffer_state (YY_CURRENT_BUFFER, yyscanner);
+                 if (yy_scan_string (yytext, yyscanner))
+                   {
+                     yyextra->lexerstate->merge_start = 1;
+                     yy_push_state (SPLIT, yyscanner);
+                   }
+                 else
+                   {
+                     grub_script_yyerror (yyextra, 0);
+                     yypop_buffer_state (yyscanner);
+                     return GRUB_PARSER_TOKEN_WORD;
+                   }
+                }
+
+.|\n            {
+                  grub_script_yyerror (yyextra, "unrecognized token");
+                 return GRUB_PARSER_TOKEN_BAD;
+                }
+
+
+ /* Split word into multiple args */
+
+<SPLIT>{
+  \\.           { PUSH (yytext[1]); }
+  \"            {
+                 yy_push_state (DQUOTE, yyscanner);
+                  ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
+                }
+  \'            {
+                 yy_push_state (SQUOTE, yyscanner);
+                  ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
+                }
+  \$            {
+                  yy_push_state (VAR, yyscanner);
+                  ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
+               }
+  {CHAR}        { PUSH (yytext[0]); }
+  .|\n          {
+                  /* This cannot happen.  */
+                 grub_script_yyerror (yyextra, "internal error: unexpected characters in a word");
+                  return GRUB_PARSER_TOKEN_BAD;
+               }
+
+  <<EOF>>       {
+                 yy_pop_state (yyscanner);
+                 yypop_buffer_state (yyscanner);
+                 yyextra->lexerstate->merge_end = 1;
+                  ARG (GRUB_SCRIPT_ARG_TYPE_TEXT);
+                }
+}
+
+<VAR>{
+  \?            |
+  {DIGITS}      |
+  {NAME}        {
+                  COPY (yytext);
+                 yy_pop_state (yyscanner);
+                 if (YY_START == SPLIT)
+                   ARG (GRUB_SCRIPT_ARG_TYPE_VAR);
+                 else
+                   ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR);
+               }
+  \{\?\}        |
+  \{{DIGITS}\}  |
+  \{{NAME}\}    {
+                  yytext[yyleng - 1] = '\0';
+                 COPY (yytext + 1);
+                  yy_pop_state (yyscanner);
+                 if (YY_START == SPLIT)
+                   ARG (GRUB_SCRIPT_ARG_TYPE_VAR);
+                 else
+                   ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR);
+                }
+  .|\n          { return GRUB_PARSER_TOKEN_BAD; }
+}
+
+<SQUOTE>{
+  \'            {
+                  yy_pop_state (yyscanner);
+                 ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR);
+                }
+  (.|\n)        { PUSH (yytext[0]); }
+}
+
+<DQUOTE>{
+  \"            {
+                  yy_pop_state (yyscanner);
+                 ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
+                }
+  \$            {
+                  yy_push_state (VAR, yyscanner);
+                 ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR);
+               }
+  \\\\          { PUSH ('\\'); }
+  \\\"          { PUSH ('\"'); }
+  \\\n          { /* ignore */ }
+  (.|\n)        { PUSH (yytext[0]); }
+}
+
+<<EOF>>         {
+                 yypop_buffer_state (yyscanner);
+                 if (! grub_script_lexer_yywrap (yyextra))
+                   {
+                     yyextra->lexerstate->eof = 1;
+                     return GRUB_PARSER_TOKEN_EOF;
+                   }
+                }
+
+%%
+
+static void
+grub_lexer_yyfree (void *ptr, yyscan_t yyscanner __attribute__ ((unused)))
+{
+  grub_free(ptr);
+}
+
+static void*
+grub_lexer_yyalloc (yy_size_t size, yyscan_t yyscanner __attribute__ ((unused)))
+{
+  return grub_malloc (size);
+}
+
+static void*
+grub_lexer_yyrealloc (void *ptr, yy_size_t size,
+                      yyscan_t yyscanner __attribute__ ((unused)))
+{
+  return grub_realloc (ptr, size);
+}
+
diff --git a/tests/grub_script_echo1.in b/tests/grub_script_echo1.in
new file mode 100644 (file)
index 0000000..048907a
--- /dev/null
@@ -0,0 +1,32 @@
+#! @builddir@/grub-shell-tester
+
+# Run GRUB script in a Qemu instance
+# Copyright (C) 2010  Free Software Foundation, Inc.
+#
+# GRUB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# GRUB is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+
+foo=bar
+echo $foo ${foo}
+echo "$foo" "${foo}"
+echo '$foo' '${foo}'
+echo a$foob a${foo}b
+echo ab"cd"ef$foo'gh'ij${foo}kl\ mn\"op\'qr\$st\(uv\<wx\>yz\)
+
+foo=c
+bar=h
+echo e"$foo"${bar}o
+e"$foo"${bar}o hello world
+
+foo=echo
+$foo 1234
diff --git a/tests/grub_script_echo_keywords.in b/tests/grub_script_echo_keywords.in
new file mode 100644 (file)
index 0000000..a6383f0
--- /dev/null
@@ -0,0 +1,3 @@
+#! @builddir@/grub-shell-tester
+
+echo if then else fi for do done
diff --git a/tests/grub_script_vars1.in b/tests/grub_script_vars1.in
new file mode 100644 (file)
index 0000000..9ff8976
--- /dev/null
@@ -0,0 +1,34 @@
+#! @builddir@/grub-shell-tester
+
+# Run GRUB script in a Qemu instance
+# Copyright (C) 2010  Free Software Foundation, Inc.
+#
+# GRUB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# GRUB is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+
+var=foo
+echo $var
+echo "$var"
+echo ${var}
+echo "${var}"
+
+echo $1 $2 $?
+
+foo=foo
+echo "" $foo
+
+echo $bar $foo
+bar=""
+echo $bar $foo
+
index 6ed4ebcac8e9485084daa860f66dff9760da459f..e9507c8f546f3d51a13739df8200619c8304f966 100644 (file)
@@ -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
index e6fef8313b5bb36321657f3d44e3765880aaf882..2b9131547fef0134d577b30e6ce86f96987aa694 100644 (file)
@@ -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
index 3bfd6a425a60a6d4a9d1fbcb19a550768c8c03bb..0ad24186d02266d6ab72beba797ba668b387e9b4 100644 (file)
@@ -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_script_execute_cmdif (struct grub_script_cmd *cmd __attribute__ ((unused)))
 }
 
 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;
 }