All of lore.kernel.org
 help / color / mirror / Atom feed
* Scripting support (PATCH)
@ 2005-10-29 21:41 Marco Gerards
  2005-10-30  5:25 ` Yoshinori K. Okuji
                   ` (3 more replies)
  0 siblings, 4 replies; 26+ messages in thread
From: Marco Gerards @ 2005-10-29 21:41 UTC (permalink / raw)
  To: grub-devel

Hi,

Here is the patch I promised last week.  It is far from perfect and
will most likely break one or two things, but I think it is worth the
trouble to add it for the following reasons:

1) I don't want to have a big diff between my work on HEAD.
2) It needs to be tested.
3) People can start hacking on it.

First I will start with some design issues and a feature description.
Later in this email I will describe the important internals.  I hope
every regular contributor is willing to read the first part and give
some comments, this patch changes a lot of stuff, too much to just
check it in I think (So I will at least wait for Okuji's opinion).  I
hope someone is capable of extending genmk.rb so .y files are
accepted, otherwise I can not cleanly add it to the rmk.  Perhaps we
can talk things over on IRC, if it is too hard to do this on the list.

Basic description:

There are not many features supported.  Until now the following things
are supported:

- A single command line.

Example: ls -l

- Multiple commands, separated with a `;'.

Example: ls -l ; help

- Functions.

Example: function lsls { ls ; ls }
Run it with: grub> lsls

Argument are not yet supported, but I took care of passing things
around in the code.

- An if statement.

set a=foo
if [ ${a}=foo ]; then ls else help fi
(This executes ls, because a is set to foo)

- The [ commands

It's just a dummy, only capable of checking if two strings are equal.
It should be extended so it can do more tests.

Things that don't work, are broken, etc:

- The parser does not do good error checking of the script yet.

- Only single lines of code are supported.

- No real scripts are parsed, only the command line.

- Not integrated with the menu yet.

- `foo=bar' was removed for now.  I will restore it, but for now you
  have to use `set foo=bar'.

- The syntax it supports is not loose enough yet.  For example, this
  does not work:

if [ $a=foo ]; then ls else help fi

Of course, when it is in CVS I can work on some things, if no one else
picks them up.  Some issues like menu integration have to be discussed
in detail to do it right and clean.


Now the technical issues:

The scanner:

It's in normal/lexer.c.  It's quite a simple scanner.  I haven't used
flex because it was not possible to use it outside of a POSIX
environment.  I yet have to make sure all memory is freed when it
should...

The parser:

For the parser I have used bison.  You will see I had to support
spaces instead of removing them.  This is because spaces have a
functional meaning in the syntax. :(

I think the parser is quite clear.  parser.y parses the tokens and the
functions in script.c generate a binary representation of the script.
The form I am using is some kind of OO approach.  I have described
this approach in an earlier email, it is quite similar to how bash
works and easy to maintain.  I stopped using a type field, but I am
using function pointers to execute and free the code now.  The
advantage of this approach is that someone can extend GRUB with a new
syntax without any cost.  It also makes it easier to extend GRUB with
new language constructs.

The result of this execution is a `struct grub_script_cmd *'.  This
can be stored in a function, later in a menu entry and it can be
executed immediately.

Functions:

Functions are just names linked to a `struct grub_script_cmd *'.  You
can easily execute them, overwrite them, remove them, etc.  They do
not show up in the command list yet.  I think I want to keep it that
way and add another command to add the command to the list of
commands.  By doing that helper functions won't be shown to the user,
only real functions.

Conditions:

This is only used by the if command in combination to `[' so far.  It
works as follows:

`if' accepts any command.  I showed the example of using the `['
command.  It can be made similar to the `[' command of coreutils.
This command sets the environment variable `RESULT' to either 0 or 1.
After execution `if' checks `RESULT' and either executes the `if' or
the `else' branch.  It would also be possible to use a return value
instead of `RESULT', but that was too much trouble for me at first.
But of course this can be changed if someone can convince me of that.

I hope this is clear to everyone, otherwise please ask me.  After this
patch is committed I work on the problems I described in this email.
I don't think I will work on lsb.c or on the language constructs
itself, unless no comes up with something I like. :-)

Thanks,
Marco

2005-10-29  Marco Gerards  <mgerards@xs4all.nl>

	Add initial scripting support.

	* commands/lsb.c: New file.

	* conf/i386-pc.rmk (grub_emu_SOURCES): Add `commands/lsb.c',
	`normal/execute.c', `normal/lexer.c', `normal/parser.tab.c',
	`normal/function.c' and `normal/script.c'.
	(normal_mod_SOURCES): `normal/execute.c', `normal/lexer.c',
	`normal/parser.tab.c', `normal/function.c' and `normal/script.c'.
	(lsb_mod_SOURCES, lsb_mod_CFLAGS, lsb_mod_LDFLAGS): New variables.
	(pkgdata_MODULES): Add `lsb.mod'.

	* include/grub/parser.h: New file.
	* include/grub/script.h: Likewise.
	* kern/parser.c: Likewise.
	* normal/execute.c: Likewise.
	* normal/function.c: Likewise.
	* normal/lexer.c: Likewise.
	* normal/parser.y: Likewise.
	* normal/script.c: Likewise.

	* normal/command.c: Include <grub/script.h>.
	(grub_command_execute): Rewritten.
	
	* util/grub-emu.c (main): Call `grub_lsb_init' and
	`grub_lsb_fini'.


Index: commands/lsb.c
===================================================================
RCS file: commands/lsb.c
diff -N commands/lsb.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands/lsb.c	29 Oct 2005 20:57:29 -0000
@@ -0,0 +1,83 @@
+/* lsb.c - The Left Square Bracket (`[') command.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/normal.h>
+#include <grub/dl.h>
+#include <grub/arg.h>
+#include <grub/misc.h>
+
+
+
+static grub_err_t
+grub_cmd_lsb (struct grub_arg_list *state __attribute__ ((unused)), int argc,
+	       char **args)
+
+{
+  char *eq;
+  char *eqis;
+  int true;
+
+  /* XXX: No fancy expression evaluation yet.  */
+  
+  if (argc == 0)
+    return 0;
+  
+  eq = grub_strdup (args[0]);
+  eqis = grub_strchr (eq, '=');
+  if (! eqis)
+    return 0;
+
+  *eqis = '\0';
+  eqis++;
+  /* Check an expression in the form `A=B'.  */
+  grub_env_set ("RESULT", grub_strcmp (eq, eqis) ? "0" : "1");
+  grub_free (eq);
+
+  return 0;
+}
+
+
+\f
+#ifdef GRUB_UTIL
+void
+grub_lsb_init (void)
+{
+  grub_register_command ("[", grub_cmd_lsb, GRUB_COMMAND_FLAG_CMDLINE,
+			 "[ EXPRESSION ]", "Evaluate an expression", 0);
+}
+
+void
+grub_lsb_fini (void)
+{
+  grub_unregister_command ("[");
+}
+#else /* ! GRUB_UTIL */
+GRUB_MOD_INIT
+{
+  (void)mod;			/* To stop warning. */
+  grub_register_command ("[", grub_cmd_lsb, GRUB_COMMAND_FLAG_CMDLINE,
+			 "[ EXPRESSION ]", "Evaluate an expression", 0);
+}
+
+GRUB_MOD_FINI
+{
+  grub_unregister_command ("[");
+}
+#endif /* ! GRUB_UTIL */
Index: conf/i386-pc.rmk
===================================================================
RCS file: /cvsroot/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.50
diff -u -p -u -p -r1.50 i386-pc.rmk
--- conf/i386-pc.rmk	24 Oct 2005 10:23:46 -0000	1.50
+++ conf/i386-pc.rmk	29 Oct 2005 20:57:29 -0000
@@ -80,19 +80,20 @@ grub_probefs_SOURCES = util/i386/pc/grub
 # For grub-emu.
 grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c 	\
 	commands/configfile.c commands/default.c commands/help.c	\
-	commands/terminal.c commands/ls.c commands/search.c		\
-	commands/timeout.c						\
+	commands/terminal.c commands/ls.c commands/lsb.c 		\
+	commands/search.c commands/timeout.c				\
 	commands/i386/pc/halt.c commands/i386/pc/reboot.c		\
 	disk/loopback.c							\
 	fs/affs.c fs/ext2.c fs/fat.c fs/fshelp.c fs/hfs.c fs/iso9660.c	\
 	fs/jfs.c fs/minix.c fs/sfs.c fs/ufs.c fs/xfs.c			\
 	io/gzio.c							\
 	kern/device.c kern/disk.c kern/dl.c kern/env.c kern/err.c 	\
-	kern/file.c kern/fs.c kern/loader.c kern/main.c kern/misc.c	\
-	kern/parser.c kern/partition.c kern/rescue.c kern/term.c	\
-	normal/arg.c normal/cmdline.c normal/command.c			\
+	normal/execute.c kern/file.c kern/fs.c normal/lexer.c 		\
+	kern/loader.c kern/main.c kern/misc.c kern/parser.c		\
+	normal/parser.tab.c kern/partition.c kern/rescue.c kern/term.c	\
+	normal/arg.c normal/cmdline.c normal/command.c normal/function.c\
 	normal/completion.c normal/context.c normal/main.c		\
-	normal/menu.c normal/menu_entry.c normal/misc.c			\
+	normal/menu.c normal/menu_entry.c normal/misc.c normal/script.c	\
 	partmap/amiga.c	partmap/apple.c partmap/pc.c partmap/sun.c	\
 	util/console.c util/grub-emu.c util/misc.c			\
 	util/i386/pc/biosdisk.c util/i386/pc/getroot.c			\
@@ -117,7 +118,7 @@ pkgdata_MODULES = _chain.mod _linux.mod 
 	apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod	\
 	help.mod default.mod timeout.mod configfile.mod vbe.mod		\
 	vesafb.mod vbetest.mod vbeinfo.mod search.mod gzio.mod		\
-	terminfo.mod serial.mod xfs.mod affs.mod sfs.mod
+	terminfo.mod serial.mod xfs.mod affs.mod sfs.mod lsb.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
@@ -196,9 +197,10 @@ linux_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For normal.mod.
 normal_mod_SOURCES = normal/arg.c normal/cmdline.c normal/command.c	\
-	normal/completion.c normal/context.c normal/main.c		\
-	normal/menu.c normal/menu_entry.c normal/misc.c			\
-	normal/i386/setjmp.S
+	normal/completion.c normal/context.c normal/execute.c 		\
+	normal/function.c normal/lexer.c normal/main.c normal/menu.c	\
+	normal/menu_entry.c normal/misc.c normal/parser.tab.c 		\
+	normal/script.c normal/i386/setjmp.S
 normal_mod_CFLAGS = $(COMMON_CFLAGS)
 normal_mod_ASFLAGS = $(COMMON_ASFLAGS) -m32
 normal_mod_LDFLAGS = $(COMMON_LDFLAGS)
@@ -347,3 +349,8 @@ search_mod_LDFLAGS = $(COMMON_LDFLAGS)
 gzio_mod_SOURCES = io/gzio.c
 gzio_mod_CFLAGS = $(COMMON_CFLAGS)
 gzio_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For lsb.mod.
+lsb_mod_SOURCES = commands/lsb.c
+lsb_mod_CFLAGS = $(COMMON_CFLAGS)
+lsb_mod_LDFLAGS = $(COMMON_LDFLAGS)
Index: include/grub/parser.h
===================================================================
RCS file: include/grub/parser.h
diff -N include/grub/parser.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ include/grub/parser.h	29 Oct 2005 20:57:29 -0000
@@ -0,0 +1,68 @@
+/* parser.h - prototypes for the command line parser.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef GRUB_PARSER_HEADER
+#define GRUB_PARSER_HEADER	1
+
+#include <grub/types.h>
+#include <grub/err.h>
+
+/* All the states for the command line.  */
+typedef enum
+  {
+    GRUB_PARSER_STATE_TEXT = 1,
+    GRUB_PARSER_STATE_ESC,
+    GRUB_PARSER_STATE_QUOTE,
+    GRUB_PARSER_STATE_DQUOTE,
+    GRUB_PARSER_STATE_VAR,
+    GRUB_PARSER_STATE_VARNAME,
+    GRUB_PARSER_STATE_VARNAME2,
+    GRUB_PARSER_STATE_QVAR,
+    GRUB_PARSER_STATE_QVARNAME,
+    GRUB_PARSER_STATE_QVARNAME2
+  } grub_parser_state_t;
+
+/* A single state transition.  */
+struct grub_parser_state_transition
+{
+  /* The state that is looked up.  */
+  grub_parser_state_t from_state;
+
+  /* The next state, determined by FROM_STATE and INPUT.  */
+  grub_parser_state_t to_state;
+
+  /* The input that will determine the next state from FROM_STATE.  */
+  char input;
+
+  /* If set to 1, the input is valid and should be used.  */
+  int keep_value;
+};
+
+/* Determines the state following STATE, determined by C.  */
+grub_parser_state_t
+EXPORT_FUNC (grub_parser_cmdline_state) (grub_parser_state_t state,
+					 char c, char *result);
+
+grub_err_t
+EXPORT_FUNC (grub_parser_split_cmdline) (const char *cmdline,
+					 grub_err_t (*getline) (char **),
+					 int *argc, char ***argv);
+
+#endif /* ! GRUB_PARSER_HEADER */
Index: include/grub/script.h
===================================================================
RCS file: include/grub/script.h
diff -N include/grub/script.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ include/grub/script.h	29 Oct 2005 20:57:29 -0000
@@ -0,0 +1,164 @@
+/* script.h  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* A part of an argument.  */
+struct grub_script_arg
+{
+  /* If this is 0, STR is a string.  If it is one, STR is a variable
+     name.  */
+  int type;
+
+  char *str;
+
+  /* Next argument part.  */
+  struct grub_script_arg *next;
+};
+
+/* A complete argument.  It consists of a list of one or more `struct
+   grub_script_arg's.  */
+struct grub_script_arglist
+{
+  struct grub_script_arglist *next;
+  struct grub_script_arg *arg;
+  /* Only stored in the first link.  */
+  int argcount;
+};
+
+/* The generic header for each scripting command or structure.  */
+struct grub_script_cmd
+{
+  /* This function is called to execute the command.  */
+  int (*exec) (struct grub_script_cmd *cmd);
+
+  /* This function is called to free this command and all of its
+     children.  */
+  void (*free) (struct grub_script_cmd *cmd);
+
+  /* The next command.  This can be used by the parent to form a chain
+     of commands.  */
+  struct grub_script_cmd *next;
+};
+
+
+/* A single command line.  */
+struct grub_script_cmdline
+{
+  struct grub_script_cmd cmd;
+
+  /* The arguments for this command.  */
+  struct grub_script_arglist *arglist;
+
+  /* The command name of this command.  XXX: Perhaps an argument
+     should be used for this so we can use variables as command
+     name.  */
+  char *cmdname;
+};
+
+/* A block of commands, this can be used to group commands.  */
+struct grub_script_cmdblock
+{
+  struct grub_script_cmd cmd;
+
+  /* A chain of commands.  */
+  struct grub_script_cmd *cmdlist;
+};
+
+/* An if statement.  */
+struct grub_script_cmdif
+{
+  struct grub_script_cmd cmd;
+
+  /* The command used to check if the if is true or false.  */
+  struct grub_script_cmd *bool;
+
+  /* The code executed in case the result if bool was true.  */
+  struct grub_script_cmd *true;
+
+  /* The code executed in case the result if bool was false.  */
+  struct grub_script_cmd *false;
+};
+
+struct grub_script_arglist *grub_script_create_arglist (void);
+struct grub_script_arglist *grub_script_add_arglist (struct grub_script_arglist *list,
+						     struct grub_script_arg *arg);
+struct grub_script_cmd *grub_script_create_cmdline (char *cmdname,
+						    struct grub_script_arglist *arglist);
+struct grub_script_cmd *grub_script_create_cmdblock (void);
+struct grub_script_cmd *grub_script_create_cmdif (struct grub_script_cmd *bool,
+						  struct grub_script_cmd *true,
+						  struct grub_script_cmd *false);
+struct grub_script_cmd *grub_script_add_cmd (struct grub_script_cmdblock *cmdblock,
+					     struct grub_script_cmd *cmd);
+struct grub_script_arg *grub_script_arg_add (struct grub_script_arg *arg,
+					     int type, char *str);
+struct grub_script_cmd *grub_script_parse (char *script);
+
+void grub_script_free (struct grub_script_cmd *cmd);
+void grub_script_free_cmdline (struct grub_script_cmd *cmd);
+void grub_script_free_cmdblock (struct grub_script_cmd *cmd);
+void grub_script_free_cmdif (struct grub_script_cmd *cmd);
+void grub_script_free (struct grub_script_cmd *cmd);
+
+void grub_script_lexer_init (char *s);
+
+/* Functions used by bison.  */
+int yylex (void);
+int yyparse (void);
+void yyerror (char const *err);
+
+
+/* Commands to execute, don't use these directly.  */
+int grub_script_execute_cmdline (struct grub_script_cmd *cmd);
+int grub_script_execute_cmdblock (struct grub_script_cmd *cmd);
+int grub_script_execute_cmdif (struct grub_script_cmd *cmd);
+
+/* This variable points to the parsed command.  This is used to
+   communicate with the bison code.  */
+extern struct grub_script_cmd *grub_script_parsed;
+
+/* Execute any GRUB pre-parsed command or script.  */
+int grub_script_execute_cmd (struct grub_script_cmd *cmd);
+
+\f
+
+/* The function description.  */
+struct grub_script_function
+{
+  /* The name.  */
+  char *name;
+
+  /* The callback function.  */
+  struct grub_script_cmd *func;
+
+  /* The flags.  */
+  unsigned flags;
+
+  /* The next element.  */
+  struct grub_script_function *next;
+
+  int references;
+};
+typedef struct grub_script_function *grub_script_function_t;
+
+grub_script_function_t grub_script_function_create (char *functionname, struct grub_script_cmd *cmd);
+void grub_script_function_remove (const char *name);
+grub_script_function_t grub_script_function_find (char *functionname);
+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);
Index: kern/parser.c
===================================================================
RCS file: kern/parser.c
diff -N kern/parser.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ kern/parser.c	29 Oct 2005 20:57:30 -0000
@@ -0,0 +1,230 @@
+/* parser.c - the part of the parser that can return partial tokens */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/parser.h>
+#include <grub/env.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+
+/* All the possible state transitions on the command line.  If a
+   transition can not be found, it is assumed that there is no
+   transition and keep_value is assumed to be 1.  */
+static struct grub_parser_state_transition state_transitions[] =
+{
+  { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_QUOTE, '\'', 0},
+  { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_DQUOTE, '\"', 0},
+  { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_VAR, '$', 0},
+  { GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_ESC, '\\', 0},
+
+  { GRUB_PARSER_STATE_ESC, GRUB_PARSER_STATE_TEXT, 0, 1},
+
+  { GRUB_PARSER_STATE_QUOTE, GRUB_PARSER_STATE_TEXT, '\'', 0},
+
+  { GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_TEXT, '\"', 0},
+  { GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_QVAR, '$', 0},
+
+  { GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME2, '{', 0},
+  { GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME, 0, 1},
+  { GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, ' ', 1},
+  { GRUB_PARSER_STATE_VARNAME2, GRUB_PARSER_STATE_TEXT, '}', 0},
+
+  { GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0},
+  { GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME, 0, 1},
+  { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1},
+  { GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_TEXT, '\"', 0},
+  { GRUB_PARSER_STATE_QVARNAME2, GRUB_PARSER_STATE_DQUOTE, '}', 0},
+
+  { 0, 0, 0, 0}
+};
+
+
+/* Determines the state following STATE, determined by C.  */
+grub_parser_state_t
+grub_parser_cmdline_state (grub_parser_state_t state, char c, char *result)
+{
+  struct grub_parser_state_transition *transition;
+  struct grub_parser_state_transition *next_match = 0;
+  struct grub_parser_state_transition default_transition;
+  int found = 0;
+
+  default_transition.to_state = state;
+  default_transition.keep_value = 1;
+
+  /* Look for a good translation.  */
+  for (transition = state_transitions; transition->from_state; transition++)
+    {
+      /* An exact match was found, use it.  */
+      if (transition->from_state == state && transition->input == c)
+	{
+	  found = 1;
+	  break;
+	}
+
+      /* A less perfect match was found, use this one if no exact
+	 match can be found.  */
+      if (transition->from_state == state && transition->input == 0)
+	next_match = transition;
+    }
+
+  if (! found)
+    {
+      if (next_match)
+	transition = next_match;
+      else
+	transition = &default_transition;
+    }
+
+  if (transition->keep_value)
+    *result = c;
+  else
+    *result = 0;
+  return transition->to_state;
+}
+
+
+grub_err_t
+grub_parser_split_cmdline (const char *cmdline, grub_err_t (*getline) (char **),
+			   int *argc, char ***argv)
+{
+  grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
+  /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
+     allocated.  */
+  char buffer[1024];
+  char *bp = buffer;
+  char *rd = (char *) cmdline;
+  char varname[200];
+  char *vp = varname;
+  char *args;
+  int i;
+
+  auto int check_varstate (grub_parser_state_t state);
+
+  int check_varstate (grub_parser_state_t state)
+    {
+      return (state == GRUB_PARSER_STATE_VARNAME
+	      || state == GRUB_PARSER_STATE_VARNAME2
+	      || state == GRUB_PARSER_STATE_QVARNAME
+	      || state == GRUB_PARSER_STATE_QVARNAME2);
+    }
+
+  auto void add_var (grub_parser_state_t newstate);
+
+  void add_var (grub_parser_state_t newstate)
+    {
+      char *val;
+
+      /* Check if a variable was being read in and the end of the name
+	 was reached.  */
+      if (! (check_varstate (state) && !check_varstate (newstate)))
+	return;
+
+      *(vp++) = '\0';
+      val = grub_env_get (varname);
+      vp = varname;
+      if (! val)
+	return;
+      
+      /* Insert the contents of the variable in the buffer.  */
+      for (; *val; val++)
+	*(bp++) = *val;
+    }
+
+  *argc = 1;
+  do
+    {
+      if (! *rd)
+	{
+	  if (getline)
+	    getline (&rd);
+	  else break;
+	}
+
+      for (; *rd; rd++)
+	{
+	  grub_parser_state_t newstate;
+	  char use;
+	  
+	  newstate = grub_parser_cmdline_state (state, *rd, &use);
+
+	  /* If a variable was being processed and this character does
+	     not describe the variable anymore, write the variable to
+	     the buffer.  */
+	  add_var (newstate);
+
+	  if (check_varstate (newstate))
+	    {
+	      if (use)
+		*(vp++) = use;
+	    }
+	  else
+	    {
+	      if (newstate == GRUB_PARSER_STATE_TEXT
+		  && state != GRUB_PARSER_STATE_ESC && use == ' ')
+		{
+		  /* Don't add more than one argument if multiple
+		     spaces are used.  */
+		  if (bp != buffer && *(bp - 1))
+		    {
+		      *(bp++) = '\0';
+		      (*argc)++;
+		    }
+		}
+	      else if (use)
+		*(bp++) = use;
+	    }
+	  state = newstate;
+	}
+    } while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state));
+  *(bp++) = '\0';
+
+  /* A special case for when the last character was part of a
+     variable.  */
+  add_var (GRUB_PARSER_STATE_TEXT);
+  
+
+  /* Reserve memory for the return values.  */
+  args = grub_malloc (bp - buffer);
+  if (! args)
+    return grub_errno;
+  grub_memcpy (args, buffer, bp - buffer);
+  
+  *argv = grub_malloc (sizeof (char *) * (*argc + 1));
+  if (! *argv)
+    {
+      grub_free (args);
+      return grub_errno;
+    }
+
+  /* The arguments are separated with 0's, setup argv so it points to
+     the right values.  */
+  bp = args;
+  for (i = 0; i < *argc; i++)
+    {
+      (*argv)[i] = bp;
+      while (*bp)
+	bp++;
+      bp++;
+    }
+
+  (*argc)--;
+
+  return 0;
+}
Index: normal/command.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/command.c,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 command.c
--- normal/command.c	24 Oct 2005 10:23:46 -0000	1.13
+++ normal/command.c	29 Oct 2005 20:57:30 -0000
@@ -25,6 +25,7 @@
 #include <grub/env.h>
 #include <grub/dl.h>
 #include <grub/parser.h>
+#include <grub/script.h>
 
 static grub_command_t grub_command_list;
 
@@ -193,42 +194,11 @@ grub_command_execute (char *cmdline, int
       return grub_cmdline_get (">", *s, GRUB_MAX_CMDLINE, 0, 1);
     }
 
-  grub_command_t cmd;
   grub_err_t ret = 0;
   char *pager;
   int num;
   char **args;
-  struct grub_arg_list *state;
-  struct grub_arg_option *parser;
-  int maxargs = 0;
-  char **arglist;
-  int numargs;
-
-  if (grub_parser_split_cmdline (cmdline, cmdline_get, &num, &args))
-    return 0;
-  
-  /* In case of an assignment set the environment accordingly instead
-     of calling a function.  */
-  if (num == 0 && grub_strchr (args[0], '='))
-    {
-      char *val;
-
-      if (! interactive)
-	grub_printf ("%s\n", cmdline);
-      
-      val = grub_strchr (args[0], '=');
-      val[0] = 0;
-      grub_env_set (args[0], val + 1);
-      val[0] = '=';
-      return 0;
-    }
-  
-  cmd = grub_command_find (args[0]);
-  if (! cmd)
-    return -1;
-
-  if (! (cmd->flags & GRUB_COMMAND_FLAG_NO_ECHO) && ! interactive)
-    grub_printf ("%s\n", cmdline);
+  struct grub_script_cmd *parsed_script;
   
   /* Enable the pager if the environment pager is set to 1.  */
   if (interactive)
@@ -237,27 +207,22 @@ grub_command_execute (char *cmdline, int
     pager = 0;
   if (pager && (! grub_strcmp (pager, "1")))
     grub_set_more (1);
-  
-  parser = (struct grub_arg_option *) cmd->options;
-  while (parser && (parser++)->doc)
-    maxargs++;
-
-  state = grub_malloc (sizeof (struct grub_arg_list) * maxargs);
-  grub_memset (state, 0, sizeof (struct grub_arg_list) * maxargs);
-  if (! (cmd->flags & GRUB_COMMAND_FLAG_NO_ARG_PARSE))
+
+  /* Parse the script.  */
+  parsed_script = grub_script_parse (cmdline);
+
+  if (parsed_script)
     {
-      if (grub_arg_parse (cmd, num, &args[1], state, &arglist, &numargs))
-	ret = (cmd->func) (state, numargs, arglist);
+      /* Execute the command(s).  */
+      grub_script_execute_cmd (parsed_script);
+
+      /* The parsed script was executed, throw it away.  */
+      grub_script_free (parsed_script);
     }
-  else
-    ret = (cmd->func) (state, num, &args[1]);
-  
-  grub_free (state);
 
   if (pager && (! grub_strcmp (pager, "1")))
     grub_set_more (0);
-  
-  grub_free (args);
+
   return ret;
 }
 
Index: normal/execute.c
===================================================================
RCS file: normal/execute.c
diff -N normal/execute.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/execute.c	29 Oct 2005 20:57:30 -0000
@@ -0,0 +1,200 @@
+/* execute.c -- Execute a GRUB script.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/normal.h>
+#include <grub/arg.h>
+#include <grub/env.h>
+#include <grub/script.h>
+
+/* Execute GRUB scripts.  */
+
+/* The result of the parser.  */
+struct grub_script_cmd *grub_script_parsed = 0;
+
+/* Parse ARG and return the textual representation.  Add strings are
+   concatenated and all values of the variables are filled in.  */
+static char *
+grub_script_execute_argument_to_string (struct grub_script_arg *arg)
+{
+  int size = 0;
+  char *val;
+  char *chararg;
+  struct grub_script_arg *argi;
+
+  /* First determine the size of the argument.  */
+  for (argi = arg; argi; argi = argi->next)
+    {
+      if (argi->type == 1)
+	{
+	  grub_printf ("GET: `%s'\n", argi->str);
+	  val = grub_env_get (argi->str);
+	  
+	  size += grub_strlen (val);
+	}
+      else
+	size += grub_strlen (argi->str);
+    }
+
+  /* Create the argument.  */
+  chararg = grub_malloc (size + 1);
+  if (! chararg)
+    return 0;
+
+  *chararg = '\0';
+  /* First determine the size of the argument.  */
+  for (argi = arg; argi; argi = argi->next)
+    {
+      if (argi->type == 1)
+	{
+	  val = grub_env_get (argi->str);
+	  grub_strcat (chararg, val);
+	}
+      else
+	grub_strcat (chararg, argi->str);
+    }
+
+  return chararg;
+}
+
+/* Execute a single command line.  */
+int
+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;
+  struct grub_arg_list *state;
+  struct grub_arg_option *parser;
+  int maxargs = 0;
+  char **parsed_arglist;
+  int numargs;
+  grub_err_t ret = 0;
+  int argcount = 0;
+  grub_script_function_t func = 0;
+
+  /* Lookup the command.  */
+  grubcmd = grub_command_find (cmdline->cmdname);
+  if (! grubcmd)
+    {
+      /* It's not a GRUB command, try all functions.  */
+      func = grub_script_function_find (cmdline->cmdname);
+      if (! func)
+	return 0;
+    }
+
+  if (cmdline->arglist)
+    {
+      argcount = cmdline->arglist->argcount;
+
+      /* Create argv from the arguments.  */
+      args = grub_malloc (sizeof (char *) * argcount);
+      for (arglist = cmdline->arglist; arglist; arglist = arglist->next)
+	{
+	  char *str;
+	  str = grub_script_execute_argument_to_string (arglist->arg);
+	  args[i++] = str;
+	}
+    }
+
+  /* Execute the GRUB command or function.  */
+  if (grubcmd)
+    {
+      /* Count the amount of options the command has.  */
+      parser = (struct grub_arg_option *) grubcmd->options;
+      while (parser && (parser++)->doc)
+	maxargs++;
+      
+      /* Set up the option state.  */
+      state = grub_malloc (sizeof (struct grub_arg_list) * maxargs);
+      grub_memset (state, 0, sizeof (struct grub_arg_list) * maxargs);
+  
+      /* Start the command.  */
+      if (! (grubcmd->flags & GRUB_COMMAND_FLAG_NO_ARG_PARSE))
+	{
+	  if (grub_arg_parse (grubcmd, argcount, args, state, &parsed_arglist, &numargs))
+	    ret = (grubcmd->func) (state, numargs, parsed_arglist);
+	}
+      else
+	ret = (grubcmd->func) (state, argcount, args);
+  
+      grub_free (state);
+    }
+  else
+    ret = grub_script_function_call (func, argcount, args);
+
+  /* Free arguments.  */
+  for (i = 0; i < argcount; i++)
+    grub_free (args[i]);
+  grub_free (args);
+
+  return ret;
+}
+
+/* Execute a block of one or more commands.  */  
+int
+grub_script_execute_cmdblock (struct grub_script_cmd *cmd)
+{
+  struct grub_script_cmdblock *cmdblock = (struct grub_script_cmdblock *) cmd;
+
+  /* Loop over every command and execute it.  */
+  for (cmd = cmdblock->cmdlist; cmd; cmd = cmd->next)
+    grub_script_execute_cmd (cmd);
+
+  return 0;
+}
+
+/* Execute an if statement.  */
+int
+grub_script_execute_cmdif (struct grub_script_cmd *cmd)
+{
+  struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd;
+  char *bool;
+
+  /* Check if the commands results in a true or a false.  The value is
+     read from the env variable `RESULT'.  */
+  grub_script_execute_cmd (cmdif->bool);
+  bool = grub_env_get ("RESULT");
+
+  /* Execute the `if' or the `else' part depending on the value of
+     `RESULT'.  */
+  if (bool && grub_strlen (bool) == 1 && bool[0] == '1')
+    grub_script_execute_cmd (cmdif->true);
+  else
+    grub_script_execute_cmd (cmdif->false);
+
+  return 0;
+}
+
+\f
+
+/* Execute any GRUB pre-parsed command or script.  */
+int
+grub_script_execute_cmd (struct grub_script_cmd *cmd)
+{
+  if (cmd == 0)
+    return 0;
+  cmd->exec (cmd);
+
+  return 0;
+}
Index: normal/function.c
===================================================================
RCS file: normal/function.c
diff -N normal/function.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/function.c	29 Oct 2005 20:57:30 -0000
@@ -0,0 +1,126 @@
+/* script.c */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/misc.h>
+#include <grub/script.h>
+#include <grub/parser.h>
+#include <grub/mm.h>
+
+static grub_script_function_t grub_script_function_list;
+
+grub_script_function_t
+grub_script_function_create (char *functionname, struct grub_script_cmd *cmd)
+{
+  grub_script_function_t func;
+  grub_script_function_t *p;
+  
+  func = (grub_script_function_t) grub_malloc (sizeof (*func));
+  if (! func)
+    return 0;
+
+  func->name = grub_strdup (functionname);
+  if (! func->name)
+    {
+      grub_free (func);
+      return 0;
+    }
+  
+  func->func = cmd;
+
+  /* Keep the list sorted for simplicity.  */
+  p = &grub_script_function_list;
+  while (*p)
+    {
+      if (grub_strcmp ((*p)->name, functionname) >= 0)
+	break;
+
+      p = &((*p)->next);
+    }
+
+  /* If the function already exists, overwrite the old function.  */
+  if (*p && grub_strcmp ((*p)->name, functionname) == 0)
+    {
+      grub_script_function_t q;
+
+      q = *p;
+      grub_script_free (q->func);
+      q->func = cmd;
+      grub_free (functionname);
+      grub_free (func);
+      func = q;
+    }
+  else
+    {
+      func->next = *p;
+      *p = func;
+    }
+
+  return func;
+}
+
+void
+grub_script_function_remove (const char *name)
+{
+  grub_script_function_t *p, q;
+
+  for (p = &grub_script_function_list, q = *p; q; p = &(q->next), q = q->next)
+    if (grub_strcmp (name, q->name) == 0)
+      {
+        *p = q->next;
+	grub_free (q->name);
+	grub_script_free (q->func);
+        grub_free (q);
+        break;
+      }
+}
+
+grub_script_function_t
+grub_script_function_find (char *functionname)
+{
+  grub_script_function_t func;
+
+  for (func = grub_script_function_list; func; func = func->next)
+    if (grub_strcmp (functionname, func->name) == 0)
+      break;
+
+  if (! func)
+    grub_error (GRUB_ERR_UNKNOWN_COMMAND, "unknown command `%s'", functionname);
+
+  return func;
+}
+
+int
+grub_script_function_iterate (int (*iterate) (grub_script_function_t))
+{
+  grub_script_function_t func;
+  
+  for (func = grub_script_function_list; func; func = func->next)
+    if (iterate (func))
+      return 1;
+  
+  return 0;
+}
+
+int
+grub_script_function_call (grub_script_function_t func, int argc, char **args)
+{
+  /* XXX: Arguments are not supported yet.  */
+  return grub_script_execute_cmd (func->func);
+}
Index: normal/lexer.c
===================================================================
RCS file: normal/lexer.c
diff -N normal/lexer.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/lexer.c	29 Oct 2005 20:57:30 -0000
@@ -0,0 +1,204 @@
+/* lexer.c - The scripting lexer.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/parser.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/script.h>
+
+#include "parser.tab.h"
+
+static grub_parser_state_t lexer_state;
+static char *script;
+
+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_QUOTE
+	  || state == GRUB_PARSER_STATE_DQUOTE);
+}
+
+void
+grub_script_lexer_init (char *s)
+{
+  lexer_state = GRUB_PARSER_STATE_TEXT;
+  script = s;
+/*   script = "ls\nls"; */
+}
+
+int
+yylex (void)
+{
+  grub_parser_state_t newstate;
+  char use;
+  char *buffer;
+  char *bp;
+
+  if (! *script)
+    return 0;
+
+  newstate = grub_parser_cmdline_state (lexer_state, *script, &use);
+
+  /* Check if it is a text.  */
+  if (check_textstate (newstate))
+    {
+      /* In case the string is not quoted, this can be a one char
+	 length symbol.  */
+      if (newstate == GRUB_PARSER_STATE_TEXT)
+	{
+	  switch (*script)
+	    {
+	    case ' ':
+	      while (*script)
+		{
+		  newstate = grub_parser_cmdline_state (lexer_state, *script, &use);
+		  if (! (lexer_state == GRUB_PARSER_STATE_TEXT && *script == ' '))
+		    return ' ';
+		  lexer_state = newstate;
+		  script++;
+		}
+
+	    case '{':
+	    case '}':
+	    case ';':
+	      return *(script++);
+	    }
+	}
+
+      buffer = grub_malloc (2096);
+      /* XXX */
+      bp = buffer;
+
+      /* Read one token, possible quoted.  */
+      while (*script)
+	{
+	  newstate = grub_parser_cmdline_state (lexer_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 recognised
+	     next time when this function is called.  */
+	  if (newstate == GRUB_PARSER_STATE_TEXT
+	      && lexer_state != GRUB_PARSER_STATE_ESC)
+	    {
+	      int breakout = 0;
+
+	      switch (use)
+		{
+		case ' ':
+		case '{':
+		case '}':
+		case ';':
+		  breakout = 1;
+		}
+	      if (breakout)
+		break;
+	      *(bp++) = use;
+	    }
+	  else if (use)
+	    *(bp++) = use;
+
+	  lexer_state = newstate;
+	  script++;
+	}
+
+      /* A string of text was read in.  */
+      *bp = '\0';
+      yylval.string = buffer;
+
+      /* Detect some special tokens.  */
+      if (! grub_strcmp (buffer, "while"))
+	return GRUB_PARSER_TOKEN_WHILE;
+      else if (! grub_strcmp (buffer, "if"))
+	return GRUB_PARSER_TOKEN_IF;
+      else if (! grub_strcmp (buffer, "function"))
+	return GRUB_PARSER_TOKEN_FUNCTION;
+      else if (! grub_strcmp (buffer, "else"))
+	return GRUB_PARSER_TOKEN_ELSE;
+      else if (! grub_strcmp (buffer, "then"))
+	return GRUB_PARSER_TOKEN_THEN;
+      else if (! grub_strcmp (buffer, "fi"))
+	return GRUB_PARSER_TOKEN_FI;
+      else
+	return GRUB_PARSER_TOKEN_NAME;
+    }
+  else if (newstate == GRUB_PARSER_STATE_VAR || newstate == GRUB_PARSER_STATE_QVAR)
+    {
+      buffer = grub_malloc (2096);
+      /* XXX */
+      bp = buffer;
+
+      /* This is a variable, read the variable name.  */
+      while (*script)
+	{
+	  newstate = grub_parser_cmdline_state (lexer_state, *script, &use);
+
+	  /* Check if this character is not part of the variable name
+	     anymore.  */
+	  if (! (check_varstate (newstate)))
+	    {
+	      if (lexer_state == GRUB_PARSER_STATE_VARNAME2
+		  || lexer_state == GRUB_PARSER_STATE_QVARNAME2)
+		script++;
+	      lexer_state = newstate;
+	      break;
+	    }
+
+	  if (use)
+	    *(bp++) = use;
+	  script++;
+	  lexer_state = newstate;
+	}
+
+      *bp = '\0';
+      lexer_state = newstate;
+      yylval.string = buffer;
+
+      return GRUB_PARSER_TOKEN_VAR;
+    }
+  else
+    {
+      /* There is either text or a variable name.  In the case you
+	 arrive here there is a serious problem with the parser.  */
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "Internal error\n");
+      return 0;
+    }
+}
+
+void
+yyerror (char const *err)
+{
+  grub_printf (err);
+}
Index: normal/parser.y
===================================================================
RCS file: normal/parser.y
diff -N normal/parser.y
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/parser.y	29 Oct 2005 20:57:30 -0000
@@ -0,0 +1,157 @@
+/* parser.y - The scripting parser.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+%{
+#include <grub/script.h>
+#include <grub/mm.h>
+
+#define YYFREE		grub_free
+#define YYMALLOC	grub_malloc
+
+%}
+
+%union {
+  struct grub_script_cmd *cmd;
+  struct grub_script_arglist *arglist;
+  struct grub_script_arg *arg;
+  char *string;
+}
+
+
+
+%token GRUB_PARSER_TOKEN_IF		"if"
+%token GRUB_PARSER_TOKEN_WHILE		"while"
+%token GRUB_PARSER_TOKEN_FUNCTION	"function"
+%token GRUB_PARSER_TOKEN_ELSE		"else"
+%token GRUB_PARSER_TOKEN_THEN		"then"
+%token GRUB_PARSER_TOKEN_FI		"fi"
+%token GRUB_PARSER_TOKEN_NAME
+%token GRUB_PARSER_TOKEN_VAR
+%type <cmd> script grubcmd commands if
+%type <arglist> arguments;
+%type <arg> argument;
+%type <string> text "if" "while" GRUB_PARSER_TOKEN_NAME GRUB_PARSER_TOKEN_VAR
+
+%%
+/* It should be possible to do this in a clean way...  */
+script:		commands
+		  {
+		    grub_script_parsed = $1;
+		  }
+		| function
+		  {
+		  }
+;
+
+text:		GRUB_PARSER_TOKEN_NAME
+		  {
+		    $$ = $1;
+		  }
+		| "if"
+		  {
+		    $$ = $1;
+		  }
+		| "while"
+		  {
+		    $$ = $1;
+		  }
+;
+
+ws:		/* Empty */
+		| ' '
+;
+
+/* An argument can consist of some static text mixed with variables,
+   for example: `foo${bar}baz'.  */
+argument:	GRUB_PARSER_TOKEN_VAR
+		  {
+		    $$ = grub_script_arg_add (0, 1, $1);
+		  }
+		| text
+		  {
+		    $$ = grub_script_arg_add (0, 0, $1);
+		  }
+		| argument GRUB_PARSER_TOKEN_VAR
+		  {
+		    $$ = grub_script_arg_add ($1, 1, $2);
+		  }
+		| argument text
+		  {
+		    $$ = grub_script_arg_add ($1, 0, $2);
+		  }
+;
+
+arguments:	argument
+		  {
+		    $$ = grub_script_add_arglist (0, $1);
+		  }
+		| arguments ' ' argument
+		  {
+		    $$ = grub_script_add_arglist ($1, $3);
+		  }
+;
+
+grubcmd:	ws GRUB_PARSER_TOKEN_NAME ' ' arguments ws
+		  {
+		    $$ = grub_script_create_cmdline ($2, $4);
+		  }
+		| ws GRUB_PARSER_TOKEN_NAME ws
+		  {
+		    $$ = grub_script_create_cmdline ($2, 0);
+		  }
+;
+
+commands:	grubcmd
+		  { 
+		    $$ = grub_script_add_cmd (0, $1);
+		  }
+		| commands ';' grubcmd
+		  { 
+		    struct grub_script_cmdblock *cmd;
+		    cmd = (struct grub_script_cmdblock *) $1;
+		    $$ = grub_script_add_cmd (cmd, $3);
+		  }
+		| commands '\n' grubcmd
+		  { 
+		    struct grub_script_cmdblock *cmd;
+		    cmd = (struct grub_script_cmdblock *) $1;
+		    $$ = grub_script_add_cmd (cmd, $3);
+		  }
+		| if { $$ = $1 }
+;
+
+
+function:	"function" ' ' GRUB_PARSER_TOKEN_NAME ws  '{' commands '}'
+		  {
+		    grub_script_function_create ($3, $6);
+		  }
+;
+
+if:		 "if" grubcmd ';' ws "then" commands "fi"
+		  {
+		    $$ = grub_script_create_cmdif ($2, $6, 0);
+		  }
+		 | "if" grubcmd ';' ws "then" commands "else" commands "fi"
+		  {
+		    $$ = grub_script_create_cmdif ($2, $6, $8);
+		  }
+;
+
+%%
Index: normal/script.c
===================================================================
RCS file: normal/script.c
diff -N normal/script.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/script.c	29 Oct 2005 20:57:30 -0000
@@ -0,0 +1,221 @@
+/* script.c -- Functions to create an in memory description of the script. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/misc.h>
+#include <grub/script.h>
+#include <grub/parser.h>
+#include <grub/mm.h>
+
+void
+grub_script_free_cmdline (struct grub_script_cmd *cmd)
+{
+  struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd;
+  struct grub_script_arglist *arglist;
+  struct grub_script_arglist *prevarglist = 0;
+
+  /* Free the memory for each argument.  */
+  for (arglist = cmdline->arglist; arglist; arglist = arglist->next)
+    {
+      struct grub_script_arg *argi;
+      struct grub_script_arg *prevarg = 0;
+
+      /* Free the memory for every part of the argument.  */
+      for (argi = arglist->arg; argi; argi = argi->next)
+	{
+	  grub_free (argi->str);
+	  grub_free (prevarg);
+	  prevarg = argi;
+	}
+      grub_free (prevarg);
+      grub_free (prevarglist);
+      prevarglist = arglist;
+    }  
+  grub_free (prevarglist);
+}
+
+void
+grub_script_free_cmdblock (struct grub_script_cmd *cmd)
+{
+  struct grub_script_cmdblock *cmdblock = (struct grub_script_cmdblock *) cmd;
+  struct grub_script_cmd *next;
+
+  /* Loop over every command and free it.  */
+  for (cmd = cmdblock->cmdlist; cmd; cmd = next)
+    {
+      next = cmd->next;
+      grub_script_free (cmd);
+    }
+}
+
+void
+grub_script_free_cmdif (struct grub_script_cmd *cmd)
+{
+  struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd;
+
+  grub_script_free (cmdif->bool);
+  grub_script_free (cmdif->true);
+  grub_script_free (cmdif->false);
+}
+
+/* Free the memory reserved for CMD and all of it's children.  */
+void
+grub_script_free (struct grub_script_cmd *cmd)
+{
+  cmd->free (cmd);
+  grub_free (cmd);
+}
+
+\f
+
+/* Extend the argument arg with a variable or string of text.  If TYPE
+   is 0, STR contains a string of text; if TYPE is 1 STR contains the
+   name of the variable.  If ARG is zero a new list is created.  */
+struct grub_script_arg *
+grub_script_arg_add (struct grub_script_arg *arg, int type, char *str)
+{
+  struct grub_script_arg *argpart;
+  struct grub_script_arg *ll;
+  
+  argpart = (struct grub_script_arg *) grub_malloc (sizeof (*arg));
+  argpart->type = type;
+  argpart->str = str;
+  argpart->next = 0;
+
+  if (! arg)
+    return argpart;
+
+  for (ll = arg; ll->next; ll = ll->next);
+  ll->next = argpart;
+      
+  return arg;
+}
+
+/* Add the argument ARG to the end of the argument list LIST.  If LIST
+   is zero, a new list will be created.  */
+struct grub_script_arglist *
+grub_script_add_arglist (struct grub_script_arglist *list, struct grub_script_arg *arg)
+{
+  struct grub_script_arglist *link;
+  struct grub_script_arglist *ll;
+
+  /* XXX: Perhaps we can just create a character array with '\0'
+     separators right away.  */
+  link = (struct grub_script_arglist *) grub_malloc (sizeof (*link));
+  link->next = 0;
+  link->arg = arg;
+  link->argcount = 0;
+
+  if (! list)
+    {
+      link->argcount++;
+      return link;
+    }
+
+  list->argcount++;
+
+  /* Look up the last link in the chain.  */
+  for (ll = list; ll->next; ll = ll->next);
+  ll->next = link;
+
+  return list;
+}
+
+/* Create a command that describes a single command line.  CMDLINE
+   contains the name of the command that should be executed.  ARGLIST
+   holds all arguments for this command.  */
+struct grub_script_cmd *
+grub_script_create_cmdline (char *cmdname, struct grub_script_arglist *arglist)
+{
+  struct grub_script_cmdline *cmd;
+
+  cmd = grub_malloc (sizeof (*cmd));
+  cmd->cmd.exec = grub_script_execute_cmdline;
+  cmd->cmd.free = grub_script_free_cmdline;
+  cmd->cmd.next = 0;
+  cmd->arglist = arglist;
+  cmd->cmdname = cmdname;
+
+  return (struct grub_script_cmd *) cmd;
+}
+
+/* Create a command that functions as an if statement.  If BOOL is
+   evaluated to true (the value is returned in envvar RESULT), the
+   interpreter will run the command TRUE, otherwise the interpreter
+   runs the command FALSE.  */
+struct grub_script_cmd *
+grub_script_create_cmdif (struct grub_script_cmd *bool,
+			  struct grub_script_cmd *true,
+			  struct grub_script_cmd *false)
+{
+  struct grub_script_cmdif *cmd;
+
+  cmd = grub_malloc (sizeof (*cmd));
+  cmd->cmd.exec = grub_script_execute_cmdif;
+  cmd->cmd.free = grub_script_free_cmdif;
+  cmd->cmd.next = 0;
+  cmd->bool = bool;
+  cmd->true = true;
+  cmd->false = false;
+
+  return (struct grub_script_cmd *) cmd;
+}
+
+/* Create a block of commands.  CMD contains the command that should
+   be added at the end of CMDBLOCK's list.  If CMDBLOCK is zero, a new
+   cmdblock will be created.  */
+struct grub_script_cmd *
+grub_script_add_cmd (struct grub_script_cmdblock *cmdblock, struct grub_script_cmd *cmd)
+{
+  if (! cmdblock)
+    {
+      cmdblock = (struct grub_script_cmdblock *) grub_malloc (sizeof (*cmdblock));
+      cmdblock->cmd.exec = grub_script_execute_cmdblock;
+      cmdblock->cmd.free = grub_script_free_cmdblock;
+      cmdblock->cmd.next = 0;
+      cmdblock->cmdlist = cmd;
+    }
+  else
+    {
+      struct grub_script_cmd **last;
+      for (last = &cmdblock->cmdlist; *last; last = &(*last)->next);
+      *last = cmd;
+    }
+
+  cmd->next = 0;
+
+  return (struct grub_script_cmd *) cmdblock;
+}
+
+\f
+
+/* Parse the script passed in SCRIPT and return the parsed
+   datastructure that is ready to be interpreted.  */
+struct grub_script_cmd *
+grub_script_parse (char *script)
+{
+  /* Initialize the lexer.  */
+  grub_script_lexer_init (script);
+
+  /* Parse the script, the result is stored in
+     `grub_script_parsed'.  */
+  yyparse ();
+
+  return grub_script_parsed;
+}
Index: util/grub-emu.c
===================================================================
RCS file: /cvsroot/grub/grub2/util/grub-emu.c,v
retrieving revision 1.26
diff -u -p -u -p -r1.26 grub-emu.c
--- util/grub-emu.c	9 Oct 2005 13:03:53 -0000	1.26
+++ util/grub-emu.c	29 Oct 2005 20:57:30 -0000
@@ -219,6 +219,7 @@ main (int argc, char *argv[])
   grub_timeout_init ();
   grub_configfile_init ();
   grub_search_init ();
+  grub_lsb_init ();
   
   /* XXX: Should normal mode be started by default?  */
   grub_normal_init ();
@@ -227,6 +228,7 @@ main (int argc, char *argv[])
   if (setjmp (main_env) == 0)
     grub_main ();
 
+  grub_lsb_fini ();
   grub_search_fini ();
   grub_configfile_fini ();
   grub_timeout_fini ();




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-29 21:41 Marco Gerards
@ 2005-10-30  5:25 ` Yoshinori K. Okuji
  2005-10-30  9:45   ` Marco Gerards
  2005-10-30 10:14   ` Marco Gerards
  2005-10-30 14:41 ` Vladimir Serbinenko
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 26+ messages in thread
From: Yoshinori K. Okuji @ 2005-10-30  5:25 UTC (permalink / raw)
  To: The development of GRUB 2

Now, both Marco and Vladimir work on the scripting support separately, and I'm 
glad to see the development going well. However, we must choose either of 
them (or even none of them, if both are not good ;) to integrate a result 
into the official code.

I understand that there are two implementations because they do not agree on 
one or more technical issues. As the maintainer, I need to listen to opinions 
(not mental ones, but technical ones), and determine a direction. So I ask 
you to speak pros and cons in both implementations (especially, Vladimir and 
Marco). Even if you are not Vladimir or Marco, if you have any opinion, feel 
free to describe it.

- What are advantages in Vladimir's implementation?

- What are disadvantages in Vladimir's implementation?

- What are advantages in Marco's implementation?

- What are disadvantages in Marco's implementation?


Here are my own:

- What are advantages in Vladimir's implementation?

It has already implemented most features we need.

- What are disadvantages in Vladimir's implementation?

Poorly documented. The execution model is a bit complicated.

- What are advantages in Marco's implementation?

Well documented. A bit simpler.

- What are disadvantages in Marco's implementation?

Fewer features are implemented. The kernel is bloated.

Okuji



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30  5:25 ` Yoshinori K. Okuji
@ 2005-10-30  9:45   ` Marco Gerards
  2005-10-30 11:55     ` Yoshinori K. Okuji
  2005-10-30 10:14   ` Marco Gerards
  1 sibling, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-10-30  9:45 UTC (permalink / raw)
  To: The development of GRUB 2

"Yoshinori K. Okuji" <okuji@enbug.org> writes:

> Now, both Marco and Vladimir work on the scripting support separately, and I'm 
> glad to see the development going well. However, we must choose either of 
> them (or even none of them, if both are not good ;) to integrate a result 
> into the official code.
>
> I understand that there are two implementations because they do not agree on 
> one or more technical issues. As the maintainer, I need to listen to opinions 
> (not mental ones, but technical ones), and determine a direction. So I ask 
> you to speak pros and cons in both implementations (especially, Vladimir and 
> Marco). Even if you are not Vladimir or Marco, if you have any opinion, feel 
> free to describe it.

Right, I felt it hard to explain the direction I'd like to take on
this list.  So I have implemented the core functionality because that
definitely has to be right.

> - What are advantages in Vladimir's implementation?

More features.

> - What are disadvantages in Vladimir's implementation?

In my opinion it is not clean enough and does not integrate with GRUB
like I would like it to.  And Vladimir's patch was way too hard to
maintain when I started working on this a week ago.

> - What are advantages in Marco's implementation?

It's more flexible, cleaner and easier to maintain.

> - What are disadvantages in Marco's implementation?

It's work in progress.

> Here are my own:
>
> - What are advantages in Vladimir's implementation?
>
> It has already implemented most features we need.

Right, but I have asked Vladimir several times for a discussion on
this mailinglist about what the features are.  That discussion did not
properly take place yet.

> - What are disadvantages in Marco's implementation?
>
> Fewer features are implemented. The kernel is bloated.

What do you mean with the kernel is bloated?  My patch moves a lot of
responsibility to commands.  I would say it is less bloated.

Thanks,
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30  5:25 ` Yoshinori K. Okuji
  2005-10-30  9:45   ` Marco Gerards
@ 2005-10-30 10:14   ` Marco Gerards
  1 sibling, 0 replies; 26+ messages in thread
From: Marco Gerards @ 2005-10-30 10:14 UTC (permalink / raw)
  To: The development of GRUB 2

"Yoshinori K. Okuji" <okuji@enbug.org> writes:

> Now, both Marco and Vladimir work on the scripting support separately, and I'm 
> glad to see the development going well. However, we must choose either of 
> them (or even none of them, if both are not good ;) to integrate a result 
> into the official code.

There is one important thing I forgot to mention in my last email.
Vladimir's newest patch is heavily based on my last week's patch.  My
new patch does only clean up some things I did wrong last week and it
adds an if statement and is better integrated with GRUB.

--
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30  9:45   ` Marco Gerards
@ 2005-10-30 11:55     ` Yoshinori K. Okuji
  2005-10-30 12:17       ` Marco Gerards
  0 siblings, 1 reply; 26+ messages in thread
From: Yoshinori K. Okuji @ 2005-10-30 11:55 UTC (permalink / raw)
  To: The development of GRUB 2

On Sunday 30 October 2005 10:45 am, Marco Gerards wrote:
> Right, I felt it hard to explain the direction I'd like to take on
> this list.  So I have implemented the core functionality because that
> definitely has to be right.

I don't know what direction you would like yet. It is hard to figure out all 
of your ideas from the implementation. I only know that it is differently 
implemented.

> > - What are disadvantages in Vladimir's implementation?
>
> In my opinion it is not clean enough and does not integrate with GRUB
> like I would like it to.  And Vladimir's patch was way too hard to
> maintain when I started working on this a week ago.

Can you elaborate on those points? I still don't understand.

> > - What are disadvantages in Marco's implementation?
> >
> > Fewer features are implemented. The kernel is bloated.
>
> What do you mean with the kernel is bloated?  My patch moves a lot of
> responsibility to commands.  I would say it is less bloated.

I mentioned this, because your parser is built into the kernel. This is 
unnecessary, as the rescue mode does not need fancy features.

Okuji



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 11:55     ` Yoshinori K. Okuji
@ 2005-10-30 12:17       ` Marco Gerards
  0 siblings, 0 replies; 26+ messages in thread
From: Marco Gerards @ 2005-10-30 12:17 UTC (permalink / raw)
  To: The development of GRUB 2

"Yoshinori K. Okuji" <okuji@enbug.org> writes:

> On Sunday 30 October 2005 10:45 am, Marco Gerards wrote:
>> Right, I felt it hard to explain the direction I'd like to take on
>> this list.  So I have implemented the core functionality because that
>> definitely has to be right.
>
> I don't know what direction you would like yet. It is hard to figure out all 
> of your ideas from the implementation. I only know that it is differently 
> implemented.

Sure, but I have explained it in detail in the emails I sent to the
list a week ago.  My most important point was that I did not want to
use pcode.

>> > - What are disadvantages in Vladimir's implementation?
>>
>> In my opinion it is not clean enough and does not integrate with GRUB
>> like I would like it to.  And Vladimir's patch was way too hard to
>> maintain when I started working on this a week ago.
>
> Can you elaborate on those points? I still don't understand.

That was about the previous patch Vladimir wrote.  His current patch
is based on my code, AFAICS.

I had a review of his previous ( > 1 week ago) code, and had some
problems with it:

- It was very hard to understand.
- It was all handwritten (later bison was used, which is a good
  thing).
- Poorly documented.
- Little reuse or adoption of existing GRUB code.
- No well known design and execution model.  I just based my design on
  the model bash uses.

>> > - What are disadvantages in Marco's implementation?
>> >
>> > Fewer features are implemented. The kernel is bloated.
>>
>> What do you mean with the kernel is bloated?  My patch moves a lot of
>> responsibility to commands.  I would say it is less bloated.
>
> I mentioned this, because your parser is built into the kernel. This is 
> unnecessary, as the rescue mode does not need fancy features.

It is build into normal mode.  And even much is not put in normal
mode, but in commands like `['.

The thing you see in the kernel is a replacement for the command to
split lines into arguments.  The old code for this was removed.  So
some code was removed and some was added, the end result is about the
same, but more reusable code.

--
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-29 21:41 Marco Gerards
  2005-10-30  5:25 ` Yoshinori K. Okuji
@ 2005-10-30 14:41 ` Vladimir Serbinenko
  2005-10-30 16:05   ` Marco Gerards
  2005-10-31  6:39   ` Yoshinori K. Okuji
  2005-10-30 20:49 ` Hollis Blanchard
  2005-11-03 21:58 ` Marco Gerards
  3 siblings, 2 replies; 26+ messages in thread
From: Vladimir Serbinenko @ 2005-10-30 14:41 UTC (permalink / raw)
  To: The development of GRUB 2

After discussion with Marco on IRC we decided that his code is better 
for the core features and other things are fast to implement.
But the next questions are menu entries. I propose the following syntax:
menu [arguments] name
{
    Commands
}

arguments can be --default, --fallback perhaps some more in the future.

But the main question is about realization. We need to store the source 
code to be able to edit it. Marco and me propose the following solution:
lexer keeps the buffer of current parsed script/block and it stores 
begin and end of tokens in this buffer in yylloc's new fields: buf_beg 
and buf_end. Then when parser founds a menu entry it copies a part of 
buffer using position of command. E.g (schematically):

"menu" string '{' commands '}'
{
    char was = buf[@4.buf_end];
    buf[@4.buf_end] = 0;
    create_menu_command ($2, grub_strdup (&buf[@4.buf_beg]));
}

And menu is always stored unparsed and parsed only just before 
execution. Than editing is no problem.

Other solution proposed by Marco was to rerun the script file or part of 
it.

We would like to know what other people think about it

                                                                         
         Vladimir 'phcoder' Serbinenko





^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 14:41 ` Vladimir Serbinenko
@ 2005-10-30 16:05   ` Marco Gerards
  2005-10-30 16:46     ` Vladimir Serbinenko
  2005-10-31  6:39   ` Yoshinori K. Okuji
  1 sibling, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-10-30 16:05 UTC (permalink / raw)
  To: The development of GRUB 2

Vladimir Serbinenko <phcoder@gmail.com> writes:

> But the next questions are menu entries. I propose the following syntax:
> menu [arguments] name
> {
>     Commands
> }
>
> arguments can be --default, --fallback perhaps some more in the future.
>
> But the main question is about realization. We need to store the
> source code to be able to edit it. Marco and me propose the following
> solution:
> lexer keeps the buffer of current parsed script/block and it stores
> begin and end of tokens in this buffer in yylloc's new fields: buf_beg
> and buf_end. Then when parser founds a menu entry it copies a part of
> buffer using position of command. E.g (schematically):
>
> "menu" string '{' commands '}'

I would even propose something like:

menuargs:	/* Empty */
		menuargs ARG
...
;

menuentry:	"menu" menuargs '{' commands '}' 
;


Or something like that.  Arg is something the scanner can recognize or
so.  I am not sure how that should work :)

> {
>     char was = buf[@4.buf_end];
>     buf[@4.buf_end] = 0;
>     create_menu_command ($2, grub_strdup (&buf[@4.buf_beg]));
> }

I didn't know that bison had such buffer.  I think it is cleaner to
use grub_strndup so you don't have to modify the buffer.  You also
have to pass the result of `commands' ($4 in your example) to
create_menu_command.

> And menu is always stored unparsed and parsed only just before
> execution. Than editing is no problem.

I think you have to parse it immediately and both store parsed and the
unparsed code.  After editing you do the same again.

> Other solution proposed by Marco was to rerun the script file or part
> of it.

This solution is required if you want to be able to edit the complete
script instead of just the separate menu entries.  I think this is not
interesting, but if someone wants to know I can say more about this.

Thanks,
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 16:05   ` Marco Gerards
@ 2005-10-30 16:46     ` Vladimir Serbinenko
  2005-10-30 16:59       ` Marco Gerards
  0 siblings, 1 reply; 26+ messages in thread
From: Vladimir Serbinenko @ 2005-10-30 16:46 UTC (permalink / raw)
  To: The development of GRUB 2, metgerards

Marco Gerards wrote:

>
>
>I didn't know that bison had such buffer.  
>
It doesn't. This buffer is created by lexer in my example.
And menu is always stored unparsed and parsed only just before

>>execution. Than editing is no problem.
>>    
>>
>
>I think you have to parse it immediately and both store parsed and the
>unparsed code.  After editing you do the same again.
>
>  
>
I see 2 approaches:
1) Parse menu commands right before executing
2) Parse them directly and reparse after editing.
I think 1st one is cleaner because the parser will be at the same place 
after editing or before

>This solution is required if you want to be able to edit the complete
>script instead of just the separate menu entries.  I think this is not
>interesting, but if someone wants to know I can say more about this.
>  
>
I think also. Because theese modifications are lost after boot they are 
generally modified only to boot one time an OS with specifical 
parameters and you don't need scripts for this.
                                                                         
      Vladimir 'phcoder' Serbinenko



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 16:46     ` Vladimir Serbinenko
@ 2005-10-30 16:59       ` Marco Gerards
  2005-10-30 17:15         ` Vladimir Serbinenko
  0 siblings, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-10-30 16:59 UTC (permalink / raw)
  To: Vladimir Serbinenko; +Cc: The development of GRUB 2

Vladimir Serbinenko <phcoder@gmail.com> writes:

>>I think you have to parse it immediately and both store parsed and the
>>unparsed code.  After editing you do the same again.
>>
> I see 2 approaches:
> 1) Parse menu commands right before executing
> 2) Parse them directly and reparse after editing.
> I think 1st one is cleaner because the parser will be at the same
> place after editing or before

Same for #2.  And #2 matches the rest of the code.  Otherwise you are
parsing the code twice.  One when reading it and once when executing
it.  The code is already parsed by bison.  If you ignore the result of
this parsing you are causing a memory leak and wasting processor
time.  It is done already so why not use it? :)

So the problem with approach #1 is doing double work, less obvious
integration with the rest of the parser and memory leakage.

>>This solution is required if you want to be able to edit the complete
>>script instead of just the separate menu entries.  I think this is not
>>interesting, but if someone wants to know I can say more about this.
>>
>>
> I think also. Because theese modifications are lost after boot they
> are generally modified only to boot one time an OS with specifical
> parameters and you don't need scripts for this.

Right.  The only reason I see for editing the script in general is to
edit some menu generation routine.  But that is a very rare case I
think.  If it turns out not to be a rare case, we can provide this
feature later on.

--
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 16:59       ` Marco Gerards
@ 2005-10-30 17:15         ` Vladimir Serbinenko
  2005-10-30 18:17           ` Marco Gerards
  0 siblings, 1 reply; 26+ messages in thread
From: Vladimir Serbinenko @ 2005-10-30 17:15 UTC (permalink / raw)
  To: Marco Gerards, The development of GRUB 2

Marco Gerards wrote:

>Vladimir Serbinenko <phcoder@gmail.com> writes:
>
>So the problem with approach #1 is doing double work, less obvious
>integration with the rest of the parser and memory leakage.
>  
>
Of course in real code it would be freed. It was just schemathical

>
>Right.  The only reason I see for editing the script in general is to
>edit some menu generation routine.  But that is a very rare case I
>think.  If it turns out not to be a rare case, we can provide this
>feature later on.
>  
>
Even if it's not generating is used to create many menus or to create 
menus from templates and anyway you boot only 1 menu entry and only 
interesting thing is generated code, not the template or generating method.

Vladimir 'phcoder' Serbinenko




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 17:15         ` Vladimir Serbinenko
@ 2005-10-30 18:17           ` Marco Gerards
  0 siblings, 0 replies; 26+ messages in thread
From: Marco Gerards @ 2005-10-30 18:17 UTC (permalink / raw)
  To: Vladimir Serbinenko; +Cc: The development of GRUB 2

Vladimir Serbinenko <phcoder@gmail.com> writes:

> Marco Gerards wrote:
>
>>Vladimir Serbinenko <phcoder@gmail.com> writes:
>>
>>So the problem with approach #1 is doing double work, less obvious
>>integration with the rest of the parser and memory leakage.
>>
>>
> Of course in real code it would be freed. It was just schemathical

Right, but the other disadvantages remain.  So I really want approach
#2 to be used unless it has a clear disadvantage.

--
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-29 21:41 Marco Gerards
  2005-10-30  5:25 ` Yoshinori K. Okuji
  2005-10-30 14:41 ` Vladimir Serbinenko
@ 2005-10-30 20:49 ` Hollis Blanchard
  2005-10-30 21:04   ` Marco Gerards
  2005-11-03 21:58 ` Marco Gerards
  3 siblings, 1 reply; 26+ messages in thread
From: Hollis Blanchard @ 2005-10-30 20:49 UTC (permalink / raw)
  To: The development of GRUB 2

I don't have many comments, since I'm not familiar with lexing and 
parsing. Just a couple small comments:

On Oct 29, 2005, at 4:41 PM, Marco Gerards wrote:
>
> `if' accepts any command.  I showed the example of using the `['
> command.  It can be made similar to the `[' command of coreutils.
> This command sets the environment variable `RESULT' to either 0 or 1.
> After execution `if' checks `RESULT' and either executes the `if' or
> the `else' branch.  It would also be possible to use a return value
> instead of `RESULT', but that was too much trouble for me at first.
> But of course this can be changed if someone can convince me of that.

You want it to be "$RESULT" instead of "$?" ?

I really don't like that each command has to explicitly set RESULT. As 
you note, it would be better if the return code from the command were 
automatically placed into the status environment variable.

> +#ifdef GRUB_UTIL
> +void
> +grub_lsb_init (void)
> +{
> +  grub_register_command ("[", grub_cmd_lsb, GRUB_COMMAND_FLAG_CMDLINE,
> +			 "[ EXPRESSION ]", "Evaluate an expression", 0);
> +}
> +
> +void
> +grub_lsb_fini (void)
> +{
> +  grub_unregister_command ("[");
> +}
> +#else /* ! GRUB_UTIL */
> +GRUB_MOD_INIT
> +{
> +  (void)mod;			/* To stop warning. */
> +  grub_register_command ("[", grub_cmd_lsb, GRUB_COMMAND_FLAG_CMDLINE,
> +			 "[ EXPRESSION ]", "Evaluate an expression", 0);
> +}
> +
> +GRUB_MOD_FINI
> +{
> +  grub_unregister_command ("[");
> +}
> +#endif /* ! GRUB_UTIL */

We *really* need to redefine GRUB_MOD_INIT/FINI to remove all this 
duplicated code. I guess I will add that to my list.

> +/* A part of an argument.  */
> +struct grub_script_arg
> +{
> +  /* If this is 0, STR is a string.  If it is one, STR is a variable
> +     name.  */
> +  int type;

This should probably be an enum.

Since this code won't break any existing behavior (simple "ls (hd,0)/" 
will still work, right?), I guess it can be committed as soon as 
serious issues like the memory leak have been fixed.

-Hollis




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 20:49 ` Hollis Blanchard
@ 2005-10-30 21:04   ` Marco Gerards
  2005-10-31  6:45     ` Yoshinori K. Okuji
  0 siblings, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-10-30 21:04 UTC (permalink / raw)
  To: The development of GRUB 2

Hollis Blanchard <hollis@penguinppc.org> writes:

> I don't have many comments, since I'm not familiar with lexing and
> parsing. Just a couple small comments:
>
> On Oct 29, 2005, at 4:41 PM, Marco Gerards wrote:
>>
>> `if' accepts any command.  I showed the example of using the `['
>> command.  It can be made similar to the `[' command of coreutils.
>> This command sets the environment variable `RESULT' to either 0 or 1.
>> After execution `if' checks `RESULT' and either executes the `if' or
>> the `else' branch.  It would also be possible to use a return value
>> instead of `RESULT', but that was too much trouble for me at first.
>> But of course this can be changed if someone can convince me of that.
>
> You want it to be "$RESULT" instead of "$?" ?

You are right.

> I really don't like that each command has to explicitly set RESULT. As
> you note, it would be better if the return code from the command were
> automatically placed into the status environment variable.

Most command return grub_err_t.  The only commands that matter for us
are commands like `['.  Would you propose every commands returns an
int and that on function return grub_errno is checked?

>> +#ifdef GRUB_UTIL
>> +void
>> +grub_lsb_init (void)
>> +{
>> +  grub_register_command ("[", grub_cmd_lsb, GRUB_COMMAND_FLAG_CMDLINE,
>> +			 "[ EXPRESSION ]", "Evaluate an expression", 0);
>> +}
>> +
>> +void
>> +grub_lsb_fini (void)
>> +{
>> +  grub_unregister_command ("[");
>> +}
>> +#else /* ! GRUB_UTIL */
>> +GRUB_MOD_INIT
>> +{
>> +  (void)mod;			/* To stop warning. */
>> +  grub_register_command ("[", grub_cmd_lsb, GRUB_COMMAND_FLAG_CMDLINE,
>> +			 "[ EXPRESSION ]", "Evaluate an expression", 0);
>> +}
>> +
>> +GRUB_MOD_FINI
>> +{
>> +  grub_unregister_command ("[");
>> +}
>> +#endif /* ! GRUB_UTIL */
>
> We *really* need to redefine GRUB_MOD_INIT/FINI to remove all this
> duplicated code. I guess I will add that to my list.

Cool :-)

>> +/* A part of an argument.  */
>> +struct grub_script_arg
>> +{
>> +  /* If this is 0, STR is a string.  If it is one, STR is a variable
>> +     name.  */
>> +  int type;
>
> This should probably be an enum.

Yes, good catch.

> Since this code won't break any existing behavior (simple "ls (hd,0)/"
> will still work, right?), I guess it can be committed as soon as
> serious issues like the memory leak have been fixed.

That is right.  All normal code will continue to function, otherwise
it would be a bug or have been discussed on the list.  So far there is
nothing that would break the current behavior except multiple lines
(which I should fix before committing).

--
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 14:41 ` Vladimir Serbinenko
  2005-10-30 16:05   ` Marco Gerards
@ 2005-10-31  6:39   ` Yoshinori K. Okuji
  1 sibling, 0 replies; 26+ messages in thread
From: Yoshinori K. Okuji @ 2005-10-31  6:39 UTC (permalink / raw)
  To: The development of GRUB 2

On Sunday 30 October 2005 03:41 pm, Vladimir Serbinenko wrote:
> After discussion with Marco on IRC we decided that his code is better
> for the core features and other things are fast to implement.

I feel very happy with that both of you have agreed.

Okuji



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-30 21:04   ` Marco Gerards
@ 2005-10-31  6:45     ` Yoshinori K. Okuji
  2005-10-31 18:58       ` Marco Gerards
  0 siblings, 1 reply; 26+ messages in thread
From: Yoshinori K. Okuji @ 2005-10-31  6:45 UTC (permalink / raw)
  To: The development of GRUB 2

On Sunday 30 October 2005 10:04 pm, Marco Gerards wrote:
> > I really don't like that each command has to explicitly set RESULT. As
> > you note, it would be better if the return code from the command were
> > automatically placed into the status environment variable.
>
> Most command return grub_err_t.  The only commands that matter for us
> are commands like `['.  Would you propose every commands returns an
> int and that on function return grub_errno is checked?

I agree with Hollis. It should be automatic. What is wrong with setting $? to 
grub_errno? Isn't it enough to see if it is GRUB_ERR_NONE or not?

BTW, you added lsb.c, but I don't like this very much. On Unix, the testing 
command is "test", and "[" is just an aliases, isn't it? I prefer to use 
commands/test.c, and register "[" as another name of test.

Okuji



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-31  6:45     ` Yoshinori K. Okuji
@ 2005-10-31 18:58       ` Marco Gerards
  2005-11-01 19:06         ` Marco Gerards
  0 siblings, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-10-31 18:58 UTC (permalink / raw)
  To: The development of GRUB 2

"Yoshinori K. Okuji" <okuji@enbug.org> writes:

> On Sunday 30 October 2005 10:04 pm, Marco Gerards wrote:
>> > I really don't like that each command has to explicitly set RESULT. As
>> > you note, it would be better if the return code from the command were
>> > automatically placed into the status environment variable.
>>
>> Most command return grub_err_t.  The only commands that matter for us
>> are commands like `['.  Would you propose every commands returns an
>> int and that on function return grub_errno is checked?
>
> I agree with Hollis. It should be automatic. What is wrong with setting $? to 
> grub_errno? Isn't it enough to see if it is GRUB_ERR_NONE or not?

Perhaps there is no problem with that.  I just assumed it should be an
int.  I will make this change.

> BTW, you added lsb.c, but I don't like this very much. On Unix, the testing 
> command is "test", and "[" is just an aliases, isn't it? I prefer to use 
> commands/test.c, and register "[" as another name of test.

You are right.  I am not that familiar with shell scripting, so there
might be more stupid mistakes you could all catch.  Please tell me
about them.

Thanks,
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-31 18:58       ` Marco Gerards
@ 2005-11-01 19:06         ` Marco Gerards
  2005-11-01 19:55           ` Yoshinori K. Okuji
  0 siblings, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-11-01 19:06 UTC (permalink / raw)
  To: The development of GRUB 2

Marco Gerards <metgerards@student.han.nl> writes:

> "Yoshinori K. Okuji" <okuji@enbug.org> writes:
>
>> On Sunday 30 October 2005 10:04 pm, Marco Gerards wrote:
>>> > I really don't like that each command has to explicitly set RESULT. As
>>> > you note, it would be better if the return code from the command were
>>> > automatically placed into the status environment variable.
>>>
>>> Most command return grub_err_t.  The only commands that matter for us
>>> are commands like `['.  Would you propose every commands returns an
>>> int and that on function return grub_errno is checked?
>>
>> I agree with Hollis. It should be automatic. What is wrong with setting $? to 
>> grub_errno? Isn't it enough to see if it is GRUB_ERR_NONE or not?
>
> Perhaps there is no problem with that.  I just assumed it should be an
> int.  I will make this change.

To which value should grub_err_t be set on function return in the case
of the test command?  I could add a new error `GRUB_ERR_TEST_RESULT'
and make it 1.  I would prefer some better suggestion though :).

--
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-11-01 19:06         ` Marco Gerards
@ 2005-11-01 19:55           ` Yoshinori K. Okuji
  0 siblings, 0 replies; 26+ messages in thread
From: Yoshinori K. Okuji @ 2005-11-01 19:55 UTC (permalink / raw)
  To: The development of GRUB 2

On Tuesday 01 November 2005 08:06 pm, Marco Gerards wrote:
> To which value should grub_err_t be set on function return in the case
> of the test command?  I could add a new error `GRUB_ERR_TEST_RESULT'
> and make it 1.  I would prefer some better suggestion though :).

Vladimir used GRUB_ERR_TEST_FAILURE. Not bad.

Okuji



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
       [not found] <200511010611.jA16B6Bq030884@dell01.dinaserver.com>
@ 2005-11-02  9:37 ` adrian15
  2005-11-06 22:13   ` Marco Gerards
  0 siblings, 1 reply; 26+ messages in thread
From: adrian15 @ 2005-11-02  9:37 UTC (permalink / raw)
  To: grub-devel

> > I think also. Because theese modifications are lost after boot they >
> are generally modified only to boot one time an OS with specifical >
> parameters and you don't need scripts for this.
> 
> Right.  The only reason I see for editing the script in general is to
> edit some menu generation routine.  But that is a very rare case I
> think.  If it turns out not to be a rare case, we can provide this
> feature later on.

I'm not quite sure if this discussion will affect my next development of 
GSD based on grub2 but I'll tell you my opinnion:

Grub2 config files should be interpreted as if you were running a bash 
script. (I actually don't know if it corresponds to approach #1 or #2 or if 
it hasn't any relation with it)

The thing is that for developing GSD easily I will need some menu 
generation routines... althought... now that I think it better...
I can made these routines from normal bash scripts.

In conclusion, I'm quite confused but try to help further GSD development.

adrian15

Grub Super Disk (GSD) : http://adrian15.raulete.net/grub/




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-10-29 21:41 Marco Gerards
                   ` (2 preceding siblings ...)
  2005-10-30 20:49 ` Hollis Blanchard
@ 2005-11-03 21:58 ` Marco Gerards
  2005-11-06 22:26   ` Marco Gerards
  3 siblings, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-11-03 21:58 UTC (permalink / raw)
  To: The development of GRUB 2

Marco Gerards <metgerards@student.han.nl> writes:

Hi,

> Here is the patch I promised last week.  It is far from perfect and
> will most likely break one or two things, but I think it is worth the
> trouble to add it for the following reasons:

Here is a new version.  If you have some time, please have a look at
it.  If I don't hear about any important problems I will commit this
patch at the end of the weekend.

> Things that don't work, are broken, etc:
>
> - The parser does not do good error checking of the script yet.

The error message is still cryptic, but at least things don't go
completely wrong is some situations.  No memory leaks! :-)

> - Only single lines of code are supported.

This is fixed.

> - No real scripts are parsed, only the command line.

It should be easy to add.

> - Not integrated with the menu yet.
>
> - `foo=bar' was removed for now.  I will restore it, but for now you
>   have to use `set foo=bar'.
>
> - The syntax it supports is not loose enough yet.  For example, this
>   does not work:
>
> if [ $a=foo ]; then ls else help fi

This is somewhat improved.

Thanks,
Marco


2005-11-03  Marco Gerards  <mgerards@xs4all.nl>

	Add initial scripting support.

	* commands/test.c: New file.
	* include/grub/script.h: Likewise.
	* normal/execute.c: Likewise.
	* normal/function.c: Likewise.
	* normal/lexer.c: Likewise.
	* normal/parser.y: Likewise.
	* normal/script.c: Likewise.

	* configure.ac: Add `AC_PROG_YACC' test.
	
	* conf/i386-pc.rmk (grub_emu_SOURCES): Add `commands/test.c',
	`normal/execute.c', `normal/lexer.c', `grub_script.tab.c',
	`normal/function.c' and `normal/script.c'.
	(normal_mod_SOURCES): `normal/execute.c', `normal/lexer.c',
	`grub_script.tab.c', `normal/function.c' and `normal/script.c'.
	(test_mod_SOURCES, test_mod_CFLAGS, test_mod_LDFLAGS): New variables.
	(pkgdata_MODULES): Add `test.mod'.
	(grub_script.tab.c): New rule.
	(grub_script.tab.h): Likewise.

	* include/grub/err.h (grub_err_t): Add `GRUB_ERR_TEST_FAILURE'.

	* include/grub/normal.h (grub_test_init): New prototype.
	(grub_test_fini): Likewise.
	
	* normal/command.c: Include <grub/script.h>.
	(grub_command_execute): Rewritten.
	
	* util/grub-emu.c (main): Call `grub_test_init' and
	`grub_test_fini'.



Index: configure.ac
===================================================================
RCS file: /cvsroot/grub/grub2/configure.ac,v
retrieving revision 1.15
diff -u -p -u -p -r1.15 configure.ac
--- configure.ac	15 Oct 2005 18:10:37 -0000	1.15
+++ configure.ac	3 Nov 2005 21:53:12 -0000
@@ -44,6 +44,7 @@ if test "x$CFLAGS" = x; then
 fi
 
 AC_PROG_CC
+AC_PROG_YACC
 AC_SYS_LARGEFILE
 
 # Must be GCC.
Index: commands/test.c
===================================================================
RCS file: commands/test.c
diff -N commands/test.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ commands/test.c	3 Nov 2005 21:53:12 -0000
@@ -0,0 +1,89 @@
+/* test.c -- The test command..  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/normal.h>
+#include <grub/dl.h>
+#include <grub/arg.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/env.h>
+
+static grub_err_t
+grub_cmd_test (struct grub_arg_list *state __attribute__ ((unused)), int argc,
+	       char **args)
+
+{
+  char *eq;
+  char *eqis;
+
+  /* XXX: No fancy expression evaluation yet.  */
+  
+  if (argc == 0)
+    return 0;
+  
+  eq = grub_strdup (args[0]);
+  eqis = grub_strchr (eq, '=');
+  if (! eqis)
+    return 0;
+
+  *eqis = '\0';
+  eqis++;
+  /* Check an expression in the form `A=B'.  */
+  if (grub_strcmp (eq, eqis))
+    grub_error (GRUB_ERR_TEST_FAILURE, "false");
+  grub_free (eq);
+
+  return grub_errno;
+}
+
+
+\f
+#ifdef GRUB_UTIL
+void
+grub_test_init (void)
+{
+  grub_register_command ("[", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE,
+			 "[ EXPRESSION ]", "Evaluate an expression", 0);
+  grub_register_command ("test", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE,
+			 "test EXPRESSION", "Evaluate an expression", 0);
+}
+
+void
+grub_test_fini (void)
+{
+  grub_unregister_command ("[");
+  grub_unregister_command ("test");
+}
+#else /* ! GRUB_UTIL */
+GRUB_MOD_INIT
+{
+  (void)mod;			/* To stop warning. */
+  grub_register_command ("[", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE,
+			 "[ EXPRESSION ]", "Evaluate an expression", 0);
+  grub_register_command ("test", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE,
+			 "test EXPRESSION", "Evaluate an expression", 0);
+}
+
+GRUB_MOD_FINI
+{
+  grub_unregister_command ("[");
+  grub_unregister_command ("test");
+}
+#endif /* ! GRUB_UTIL */
Index: conf/i386-pc.rmk
===================================================================
RCS file: /cvsroot/grub/grub2/conf/i386-pc.rmk,v
retrieving revision 1.50
diff -u -p -u -p -r1.50 i386-pc.rmk
--- conf/i386-pc.rmk	24 Oct 2005 10:23:46 -0000	1.50
+++ conf/i386-pc.rmk	3 Nov 2005 21:53:12 -0000
@@ -46,6 +46,13 @@ DEFSYMFILES += kernel_syms.lst
 symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) gensymlist.sh
 	sh $(srcdir)/gensymlist.sh $(filter %.h,$^) > $@
 
+# For the parser.
+grub_script.tab.c: normal/parser.y
+	$(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/normal/parser.y
+grub_script.tab.h: normal/parser.y
+	$(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/normal/parser.y
+
+
 kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) genkernsyms.sh
 	sh $(srcdir)/genkernsyms.sh $(filter %h,$^) > $@
 
@@ -80,19 +87,20 @@ grub_probefs_SOURCES = util/i386/pc/grub
 # For grub-emu.
 grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c 	\
 	commands/configfile.c commands/default.c commands/help.c	\
-	commands/terminal.c commands/ls.c commands/search.c		\
-	commands/timeout.c						\
+	commands/terminal.c commands/ls.c commands/test.c 		\
+	commands/search.c commands/timeout.c				\
 	commands/i386/pc/halt.c commands/i386/pc/reboot.c		\
 	disk/loopback.c							\
 	fs/affs.c fs/ext2.c fs/fat.c fs/fshelp.c fs/hfs.c fs/iso9660.c	\
 	fs/jfs.c fs/minix.c fs/sfs.c fs/ufs.c fs/xfs.c			\
 	io/gzio.c							\
 	kern/device.c kern/disk.c kern/dl.c kern/env.c kern/err.c 	\
-	kern/file.c kern/fs.c kern/loader.c kern/main.c kern/misc.c	\
-	kern/parser.c kern/partition.c kern/rescue.c kern/term.c	\
-	normal/arg.c normal/cmdline.c normal/command.c			\
+	normal/execute.c kern/file.c kern/fs.c normal/lexer.c 		\
+	kern/loader.c kern/main.c kern/misc.c kern/parser.c		\
+	grub_script.tab.c kern/partition.c kern/rescue.c kern/term.c	\
+	normal/arg.c normal/cmdline.c normal/command.c normal/function.c\
 	normal/completion.c normal/context.c normal/main.c		\
-	normal/menu.c normal/menu_entry.c normal/misc.c			\
+	normal/menu.c normal/menu_entry.c normal/misc.c normal/script.c	\
 	partmap/amiga.c	partmap/apple.c partmap/pc.c partmap/sun.c	\
 	util/console.c util/grub-emu.c util/misc.c			\
 	util/i386/pc/biosdisk.c util/i386/pc/getroot.c			\
@@ -117,7 +125,7 @@ pkgdata_MODULES = _chain.mod _linux.mod 
 	apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod	\
 	help.mod default.mod timeout.mod configfile.mod vbe.mod		\
 	vesafb.mod vbetest.mod vbeinfo.mod search.mod gzio.mod		\
-	terminfo.mod serial.mod xfs.mod affs.mod sfs.mod
+	terminfo.mod serial.mod xfs.mod affs.mod sfs.mod test.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
@@ -196,9 +204,10 @@ linux_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For normal.mod.
 normal_mod_SOURCES = normal/arg.c normal/cmdline.c normal/command.c	\
-	normal/completion.c normal/context.c normal/main.c		\
-	normal/menu.c normal/menu_entry.c normal/misc.c			\
-	normal/i386/setjmp.S
+	normal/completion.c normal/context.c normal/execute.c 		\
+	normal/function.c normal/lexer.c normal/main.c normal/menu.c	\
+	normal/menu_entry.c normal/misc.c grub_script.tab.c 		\
+	normal/script.c normal/i386/setjmp.S
 normal_mod_CFLAGS = $(COMMON_CFLAGS)
 normal_mod_ASFLAGS = $(COMMON_ASFLAGS) -m32
 normal_mod_LDFLAGS = $(COMMON_LDFLAGS)
@@ -347,3 +356,8 @@ search_mod_LDFLAGS = $(COMMON_LDFLAGS)
 gzio_mod_SOURCES = io/gzio.c
 gzio_mod_CFLAGS = $(COMMON_CFLAGS)
 gzio_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For test.mod.
+test_mod_SOURCES = commands/test.c
+test_mod_CFLAGS = $(COMMON_CFLAGS)
+test_mod_LDFLAGS = $(COMMON_LDFLAGS)
Index: include/grub/err.h
===================================================================
RCS file: /cvsroot/grub/grub2/include/grub/err.h,v
retrieving revision 1.9
diff -u -p -u -p -r1.9 err.h
--- include/grub/err.h	22 Aug 2005 17:28:59 -0000	1.9
+++ include/grub/err.h	3 Nov 2005 21:53:12 -0000
@@ -26,6 +26,7 @@
 typedef enum
   {
     GRUB_ERR_NONE = 0,
+    GRUB_ERR_TEST_FAILURE,
     GRUB_ERR_BAD_MODULE,
     GRUB_ERR_OUT_OF_MEMORY,
     GRUB_ERR_BAD_FILE_TYPE,
Index: include/grub/normal.h
===================================================================
RCS file: /cvsroot/grub/grub2/include/grub/normal.h,v
retrieving revision 1.22
diff -u -p -u -p -r1.22 normal.h
--- include/grub/normal.h	28 Aug 2005 17:01:16 -0000	1.22
+++ include/grub/normal.h	3 Nov 2005 21:53:12 -0000
@@ -224,6 +224,8 @@ void grub_configfile_init (void);
 void grub_configfile_fini (void);
 void grub_search_init (void);
 void grub_search_fini (void);
+void grub_test_init (void);
+void grub_test_fini (void);
 #endif
 
 #endif /* ! GRUB_NORMAL_HEADER */
Index: include/grub/script.h
===================================================================
RCS file: include/grub/script.h
diff -N include/grub/script.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ include/grub/script.h	3 Nov 2005 21:53:12 -0000
@@ -0,0 +1,189 @@
+/* script.h  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <grub/types.h>
+#include <grub/err.h>
+
+struct grub_script_mem;
+
+/* The generic header for each scripting command or structure.  */
+struct grub_script_cmd
+{
+  /* This function is called to execute the command.  */
+  grub_err_t (*exec) (struct grub_script_cmd *cmd);
+
+  /* The next command.  This can be used by the parent to form a chain
+     of commands.  */
+  struct grub_script_cmd *next;
+};
+
+struct grub_script
+{
+  struct grub_script_mem *mem;
+  struct grub_script_cmd *cmd;
+};
+\f
+typedef enum
+{
+  GRUB_SCRIPT_ARG_TYPE_STR,
+  GRUB_SCRIPT_ARG_TYPE_VAR
+} grub_script_arg_type_t;
+
+/* A part of an argument.  */
+struct grub_script_arg
+{
+  grub_script_arg_type_t type;
+
+  char *str;
+
+  /* Next argument part.  */
+  struct grub_script_arg *next;
+};
+
+/* A complete argument.  It consists of a list of one or more `struct
+   grub_script_arg's.  */
+struct grub_script_arglist
+{
+  struct grub_script_arglist *next;
+  struct grub_script_arg *arg;
+  /* Only stored in the first link.  */
+  int argcount;
+};
+
+/* A single command line.  */
+struct grub_script_cmdline
+{
+  struct grub_script_cmd cmd;
+
+  /* The arguments for this command.  */
+  struct grub_script_arglist *arglist;
+
+  /* The command name of this command.  XXX: Perhaps an argument
+     should be used for this so we can use variables as command
+     name.  */
+  char *cmdname;
+};
+
+/* A block of commands, this can be used to group commands.  */
+struct grub_script_cmdblock
+{
+  struct grub_script_cmd cmd;
+
+  /* A chain of commands.  */
+  struct grub_script_cmd *cmdlist;
+};
+
+/* An if statement.  */
+struct grub_script_cmdif
+{
+  struct grub_script_cmd cmd;
+
+  /* The command used to check if the if is true or false.  */
+  struct grub_script_cmd *bool;
+
+  /* The code executed in case the result if bool was true.  */
+  struct grub_script_cmd *true;
+
+  /* The code executed in case the result if bool was false.  */
+  struct grub_script_cmd *false;
+};
+
+struct grub_script_arglist *
+grub_script_create_arglist (void);
+
+struct grub_script_arglist *
+grub_script_add_arglist (struct grub_script_arglist *list,
+			 struct grub_script_arg *arg);
+struct grub_script_cmd *
+grub_script_create_cmdline (char *cmdname,
+			    struct grub_script_arglist *arglist);
+struct grub_script_cmd *
+grub_script_create_cmdblock (void);
+
+struct grub_script_cmd *
+grub_script_create_cmdif (struct grub_script_cmd *bool,
+			  struct grub_script_cmd *true,
+			  struct grub_script_cmd *false);
+struct grub_script_cmd *
+grub_script_add_cmd (struct grub_script_cmdblock *cmdblock,
+		     struct grub_script_cmd *cmd);
+struct grub_script_arg *
+grub_script_arg_add (struct grub_script_arg *arg,
+		     grub_script_arg_type_t type, char *str);
+
+struct grub_script *grub_script_parse (char *script,
+				       grub_err_t (*getline) (char **));
+void grub_script_free (struct grub_script *script);
+struct grub_script *grub_script_create (struct grub_script_cmd *cmd,
+					struct grub_script_mem *mem);
+
+void grub_script_lexer_init (char *s, grub_err_t (*getline) (char **));
+void grub_script_lexer_ref (void);
+void grub_script_lexer_deref (void);
+
+/* Functions to track allocated memory.  */
+void *grub_script_malloc (grub_size_t size);
+struct grub_script_mem *grub_script_mem_record (void);
+struct grub_script_mem *grub_script_mem_record_stop (struct grub_script_mem *restore);
+
+/* Functions used by bison.  */
+int grub_script_yylex (void);
+int grub_script_yyparse (void);
+void grub_script_yyerror (char const *err);
+
+/* Commands to execute, don't use these directly.  */
+grub_err_t grub_script_execute_cmdline (struct grub_script_cmd *cmd);
+grub_err_t grub_script_execute_cmdblock (struct grub_script_cmd *cmd);
+grub_err_t grub_script_execute_cmdif (struct grub_script_cmd *cmd);
+
+/* Execute any GRUB pre-parsed command or script.  */
+grub_err_t grub_script_execute (struct grub_script *script);
+
+/* This variable points to the parsed command.  This is used to
+   communicate with the bison code.  */
+extern struct grub_script_cmd *grub_script_parsed;
+
+\f
+
+/* The function description.  */
+struct grub_script_function
+{
+  /* The name.  */
+  char *name;
+
+  /* The script function.  */
+  struct grub_script *func;
+
+  /* The flags.  */
+  unsigned flags;
+
+  /* The next element.  */
+  struct grub_script_function *next;
+
+  int references;
+};
+typedef struct grub_script_function *grub_script_function_t;
+
+grub_script_function_t grub_script_function_create (char *functionname,
+						    struct grub_script *cmd);
+void grub_script_function_remove (const char *name);
+grub_script_function_t grub_script_function_find (char *functionname);
+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);
Index: normal/command.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/command.c,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 command.c
--- normal/command.c	24 Oct 2005 10:23:46 -0000	1.13
+++ normal/command.c	3 Nov 2005 21:53:12 -0000
@@ -25,6 +25,7 @@
 #include <grub/env.h>
 #include <grub/dl.h>
 #include <grub/parser.h>
+#include <grub/script.h>
 
 static grub_command_t grub_command_list;
 
@@ -193,42 +194,9 @@ grub_command_execute (char *cmdline, int
       return grub_cmdline_get (">", *s, GRUB_MAX_CMDLINE, 0, 1);
     }
 
-  grub_command_t cmd;
   grub_err_t ret = 0;
   char *pager;
-  int num;
-  char **args;
-  struct grub_arg_list *state;
-  struct grub_arg_option *parser;
-  int maxargs = 0;
-  char **arglist;
-  int numargs;
-
-  if (grub_parser_split_cmdline (cmdline, cmdline_get, &num, &args))
-    return 0;
-  
-  /* In case of an assignment set the environment accordingly instead
-     of calling a function.  */
-  if (num == 0 && grub_strchr (args[0], '='))
-    {
-      char *val;
-
-      if (! interactive)
-	grub_printf ("%s\n", cmdline);
-      
-      val = grub_strchr (args[0], '=');
-      val[0] = 0;
-      grub_env_set (args[0], val + 1);
-      val[0] = '=';
-      return 0;
-    }
-  
-  cmd = grub_command_find (args[0]);
-  if (! cmd)
-    return -1;
-
-  if (! (cmd->flags & GRUB_COMMAND_FLAG_NO_ECHO) && ! interactive)
-    grub_printf ("%s\n", cmdline);
+  struct grub_script *parsed_script;
   
   /* Enable the pager if the environment pager is set to 1.  */
   if (interactive)
@@ -237,27 +205,22 @@ grub_command_execute (char *cmdline, int
     pager = 0;
   if (pager && (! grub_strcmp (pager, "1")))
     grub_set_more (1);
-  
-  parser = (struct grub_arg_option *) cmd->options;
-  while (parser && (parser++)->doc)
-    maxargs++;
-
-  state = grub_malloc (sizeof (struct grub_arg_list) * maxargs);
-  grub_memset (state, 0, sizeof (struct grub_arg_list) * maxargs);
-  if (! (cmd->flags & GRUB_COMMAND_FLAG_NO_ARG_PARSE))
+
+  /* Parse the script.  */
+  parsed_script = grub_script_parse (cmdline, cmdline_get);
+
+  if (parsed_script)
     {
-      if (grub_arg_parse (cmd, num, &args[1], state, &arglist, &numargs))
-	ret = (cmd->func) (state, numargs, arglist);
+      /* Execute the command(s).  */
+      grub_script_execute (parsed_script);
+
+      /* The parsed script was executed, throw it away.  */
+      grub_script_free (parsed_script);
     }
-  else
-    ret = (cmd->func) (state, num, &args[1]);
-  
-  grub_free (state);
 
   if (pager && (! grub_strcmp (pager, "1")))
     grub_set_more (0);
-  
-  grub_free (args);
+
   return ret;
 }
 
Index: normal/execute.c
===================================================================
RCS file: normal/execute.c
diff -N normal/execute.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/execute.c	3 Nov 2005 21:53:12 -0000
@@ -0,0 +1,204 @@
+/* execute.c -- Execute a GRUB script.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/normal.h>
+#include <grub/arg.h>
+#include <grub/env.h>
+#include <grub/script.h>
+
+static int
+grub_script_execute_cmd (struct grub_script_cmd *cmd)
+{
+  if (cmd == 0)
+    return 0;
+  cmd->exec (cmd);
+
+  return 0;
+}
+
+/* Parse ARG and return the textual representation.  Add strings are
+   concatenated and all values of the variables are filled in.  */
+static char *
+grub_script_execute_argument_to_string (struct grub_script_arg *arg)
+{
+  int size = 0;
+  char *val;
+  char *chararg;
+  struct grub_script_arg *argi;
+
+  /* First determine the size of the argument.  */
+  for (argi = arg; argi; argi = argi->next)
+    {
+      if (argi->type == 1)
+	{
+	  val = grub_env_get (argi->str);
+	  size += grub_strlen (val);
+	}
+      else
+	size += grub_strlen (argi->str);
+    }
+
+  /* Create the argument.  */
+  chararg = grub_malloc (size + 1);
+  if (! chararg)
+    return 0;
+
+  *chararg = '\0';
+  /* First determine the size of the argument.  */
+  for (argi = arg; argi; argi = argi->next)
+    {
+      if (argi->type == 1)
+	{
+	  val = grub_env_get (argi->str);
+	  grub_strcat (chararg, val);
+	}
+      else
+	grub_strcat (chararg, argi->str);
+    }
+
+  return chararg;
+}
+
+/* Execute a single command line.  */
+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;
+  struct grub_arg_list *state;
+  struct grub_arg_option *parser;
+  int maxargs = 0;
+  char **parsed_arglist;
+  int numargs;
+  grub_err_t ret = 0;
+  int argcount = 0;
+  grub_script_function_t func = 0;
+  char errnobuf[6];
+
+  /* Lookup the command.  */
+  grubcmd = grub_command_find (cmdline->cmdname);
+  if (! grubcmd)
+    {
+      /* It's not a GRUB command, try all functions.  */
+      func = grub_script_function_find (cmdline->cmdname);
+      if (! func)
+	return 0;
+    }
+
+  if (cmdline->arglist)
+    {
+      argcount = cmdline->arglist->argcount;
+
+      /* Create argv from the arguments.  */
+      args = grub_malloc (sizeof (char *) * argcount);
+      for (arglist = cmdline->arglist; arglist; arglist = arglist->next)
+	{
+	  char *str;
+	  str = grub_script_execute_argument_to_string (arglist->arg);
+	  args[i++] = str;
+	}
+    }
+
+  /* Execute the GRUB command or function.  */
+  if (grubcmd)
+    {
+      /* Count the amount of options the command has.  */
+      parser = (struct grub_arg_option *) grubcmd->options;
+      while (parser && (parser++)->doc)
+	maxargs++;
+      
+      /* Set up the option state.  */
+      state = grub_malloc (sizeof (struct grub_arg_list) * maxargs);
+      grub_memset (state, 0, sizeof (struct grub_arg_list) * maxargs);
+  
+      /* Start the command.  */
+      if (! (grubcmd->flags & GRUB_COMMAND_FLAG_NO_ARG_PARSE))
+	{
+	  if (grub_arg_parse (grubcmd, argcount, args, state, &parsed_arglist, &numargs))
+	    ret = (grubcmd->func) (state, numargs, parsed_arglist);
+	}
+      else
+	ret = (grubcmd->func) (state, argcount, args);
+  
+      grub_free (state);
+    }
+  else
+    ret = grub_script_function_call (func, argcount, args);
+
+  /* Free arguments.  */
+  for (i = 0; i < argcount; i++)
+    grub_free (args[i]);
+  grub_free (args);
+
+  grub_sprintf (errnobuf, "%d", ret);
+  grub_env_set ("?", errnobuf);
+
+  return ret;
+}
+
+/* Execute a block of one or more commands.  */  
+grub_err_t
+grub_script_execute_cmdblock (struct grub_script_cmd *cmd)
+{
+  struct grub_script_cmdblock *cmdblock = (struct grub_script_cmdblock *) cmd;
+
+  /* Loop over every command and execute it.  */
+  for (cmd = cmdblock->cmdlist; cmd; cmd = cmd->next)
+    grub_script_execute_cmd (cmd);
+
+  return 0;
+}
+
+/* Execute an if statement.  */
+grub_err_t
+grub_script_execute_cmdif (struct grub_script_cmd *cmd)
+{
+  struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd;
+  char *bool;
+
+  /* Check if the commands results in a true or a false.  The value is
+     read from the env variable `RESULT'.  */
+  grub_script_execute_cmd (cmdif->bool);
+  bool = grub_env_get ("?");
+
+  /* Execute the `if' or the `else' part depending on the value of
+     `RESULT'.  */
+  if (bool && ! grub_strcmp (bool, "0"))
+    return grub_script_execute_cmd (cmdif->true);
+  else
+    return grub_script_execute_cmd (cmdif->false);
+}
+
+\f
+
+/* Execute any GRUB pre-parsed command or script.  */
+grub_err_t
+grub_script_execute (struct grub_script *script)
+{
+  if (script == 0)
+    return 0;
+
+  return grub_script_execute_cmd (script->cmd);
+}
Index: normal/function.c
===================================================================
RCS file: normal/function.c
diff -N normal/function.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/function.c	3 Nov 2005 21:53:12 -0000
@@ -0,0 +1,127 @@
+/* script.c */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/misc.h>
+#include <grub/script.h>
+#include <grub/parser.h>
+#include <grub/mm.h>
+
+static grub_script_function_t grub_script_function_list;
+
+grub_script_function_t
+grub_script_function_create (char *functionname, struct grub_script *cmd)
+{
+  grub_script_function_t func;
+  grub_script_function_t *p;
+  
+  func = (grub_script_function_t) grub_malloc (sizeof (*func));
+  if (! func)
+    return 0;
+
+  func->name = grub_strdup (functionname);
+  if (! func->name)
+    {
+      grub_free (func);
+      return 0;
+    }
+  
+  func->func = cmd;
+
+  /* Keep the list sorted for simplicity.  */
+  p = &grub_script_function_list;
+  while (*p)
+    {
+      if (grub_strcmp ((*p)->name, functionname) >= 0)
+	break;
+
+      p = &((*p)->next);
+    }
+
+  /* If the function already exists, overwrite the old function.  */
+  if (*p && grub_strcmp ((*p)->name, functionname) == 0)
+    {
+      grub_script_function_t q;
+
+      q = *p;
+      grub_script_free (q->func);
+      q->func = cmd;
+      grub_free (func);
+      func = q;
+    }
+  else
+    {
+      func->next = *p;
+      *p = func;
+    }
+
+  return func;
+}
+
+void
+grub_script_function_remove (const char *name)
+{
+  grub_script_function_t *p, q;
+
+  for (p = &grub_script_function_list, q = *p; q; p = &(q->next), q = q->next)
+    if (grub_strcmp (name, q->name) == 0)
+      {
+        *p = q->next;
+	grub_free (q->name);
+	grub_script_free (q->func);
+        grub_free (q);
+        break;
+      }
+}
+
+grub_script_function_t
+grub_script_function_find (char *functionname)
+{
+  grub_script_function_t func;
+
+  for (func = grub_script_function_list; func; func = func->next)
+    if (grub_strcmp (functionname, func->name) == 0)
+      break;
+
+  if (! func)
+    grub_error (GRUB_ERR_UNKNOWN_COMMAND, "unknown command `%s'", functionname);
+
+  return func;
+}
+
+int
+grub_script_function_iterate (int (*iterate) (grub_script_function_t))
+{
+  grub_script_function_t func;
+  
+  for (func = grub_script_function_list; func; func = func->next)
+    if (iterate (func))
+      return 1;
+  
+  return 0;
+}
+
+int
+grub_script_function_call (grub_script_function_t func,
+			   int argc __attribute__((unused)),
+			   char **args __attribute__((unused)))
+{
+  /* XXX: Arguments are not supported yet.  */
+  return grub_script_execute (func->func);
+}
Index: normal/lexer.c
===================================================================
RCS file: normal/lexer.c
diff -N normal/lexer.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/lexer.c	3 Nov 2005 21:53:12 -0000
@@ -0,0 +1,274 @@
+/* lexer.c - The scripting lexer.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/parser.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/script.h>
+
+#include "grub_script.tab.h"
+
+static grub_parser_state_t grub_script_lexer_state;
+static int grub_script_lexer_done = 0;
+static grub_err_t (*grub_script_lexer_getline) (char **);
+
+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_QUOTE
+	  || state == GRUB_PARSER_STATE_DQUOTE);
+}
+
+/* The amount of references to the lexer by the parser.  If the parser
+   expects tokens the lexer is referenced.  */
+static int grub_script_lexer_refs = 0;
+static char *script;
+static char *newscript;
+
+/* XXX: The lexer is not reentrant.  */
+void
+grub_script_lexer_init (char *s, grub_err_t (*getline) (char **))
+{
+  grub_script_lexer_state = GRUB_PARSER_STATE_TEXT;
+  grub_script_lexer_getline = getline;
+  grub_script_lexer_refs = 0;
+  grub_script_lexer_done = 0;
+  newscript = 0;
+  script = s;
+}
+
+void
+grub_script_lexer_ref (void)
+{
+  grub_script_lexer_refs++;
+}
+
+void
+grub_script_lexer_deref (void)
+{
+  grub_script_lexer_refs--;
+}
+
+int
+grub_script_yylex (void)
+{
+  grub_parser_state_t newstate;
+  char use;
+  char *buffer;
+  char *bp;
+
+  if (grub_script_lexer_done)
+    return 0;
+
+  if (! *script)
+    {
+      /* Check if more tokens are requested by the parser.  */
+      if ((grub_script_lexer_refs
+	   || grub_script_lexer_state == GRUB_PARSER_STATE_ESC)
+	  && grub_script_lexer_getline)
+	{
+	  while (! grub_strlen (script))
+	    {
+	      grub_free (newscript);
+	      grub_script_lexer_getline (&newscript);
+	      script = newscript;
+	    }
+	  grub_dprintf ("scripting", "token=`\\n'\n");
+	  if (grub_script_lexer_state != GRUB_PARSER_STATE_ESC)
+	    return '\n';
+	}
+      else
+	{
+	  grub_free (newscript);
+	  newscript = 0;
+	  grub_script_lexer_done = 1;
+	  grub_dprintf ("scripting", "token=`\\n'\n");
+	  return '\n';
+	}
+    }
+
+  newstate = grub_parser_cmdline_state (grub_script_lexer_state, *script, &use);
+
+  /* Check if it is a text.  */
+  if (check_textstate (newstate))
+    {
+      /* In case the string is not quoted, this can be a one char
+	 length symbol.  */
+      if (newstate == GRUB_PARSER_STATE_TEXT)
+	{
+	  switch (*script)
+	    {
+	    case ' ':
+	      while (*script)
+		{
+		  newstate = grub_parser_cmdline_state (grub_script_lexer_state,
+							*script, &use);
+		  if (! (grub_script_lexer_state == GRUB_PARSER_STATE_TEXT
+			 && *script == ' '))
+		    {
+		      grub_dprintf ("scripting", "token=` '\n");
+		      return ' ';
+		    }
+		  grub_script_lexer_state = newstate;
+		  script++;
+		}
+	      grub_dprintf ("scripting", "token=` '\n");
+	      return ' ';
+	    case '{':
+	    case '}':
+	    case ';':
+	    case '\n':
+	      grub_dprintf ("scripting", "token=`%c'\n", *script);
+	      return *(script++);
+	    }
+	}
+
+      /* XXX: Use a better size.  */
+      buffer = grub_script_malloc (2096);
+      if (! buffer)
+	return 0;
+
+      bp = buffer;
+
+      /* Read one token, possible quoted.  */
+      while (*script)
+	{
+	  newstate = grub_parser_cmdline_state (grub_script_lexer_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 recognised
+	     next time when this function is called.  */
+	  if (newstate == GRUB_PARSER_STATE_TEXT
+	      && grub_script_lexer_state != GRUB_PARSER_STATE_ESC)
+	    {
+	      int breakout = 0;
+
+	      switch (use)
+		{
+		case ' ':
+		case '{':
+		case '}':
+		case ';':
+		case '\n':
+		  breakout = 1;
+		}
+	      if (breakout)
+		break;
+	      *(bp++) = use;
+	    }
+	  else if (use)
+	    *(bp++) = use;
+
+	  grub_script_lexer_state = newstate;
+	  script++;
+	}
+
+      /* A string of text was read in.  */
+      *bp = '\0';
+      grub_dprintf ("scripting", "token=`%s'\n", buffer);
+      grub_script_yylval.string = buffer;
+
+      /* Detect some special tokens.  */
+      if (! grub_strcmp (buffer, "while"))
+	return GRUB_PARSER_TOKEN_WHILE;
+      else if (! grub_strcmp (buffer, "if"))
+	return GRUB_PARSER_TOKEN_IF;
+      else if (! grub_strcmp (buffer, "function"))
+	return GRUB_PARSER_TOKEN_FUNCTION;
+      else if (! grub_strcmp (buffer, "else"))
+	return GRUB_PARSER_TOKEN_ELSE;
+      else if (! grub_strcmp (buffer, "then"))
+	return GRUB_PARSER_TOKEN_THEN;
+      else if (! grub_strcmp (buffer, "fi"))
+	return GRUB_PARSER_TOKEN_FI;
+      else
+	return GRUB_PARSER_TOKEN_NAME;
+    }
+  else if (newstate == GRUB_PARSER_STATE_VAR
+	   || newstate == GRUB_PARSER_STATE_QVAR)
+    {
+      /* XXX: Use a better size.  */
+      buffer = grub_script_malloc (2096);
+      if (! buffer)
+	return 0;
+
+      bp = buffer;
+
+      /* This is a variable, read the variable name.  */
+      while (*script)
+	{
+	  newstate = grub_parser_cmdline_state (grub_script_lexer_state,
+						*script, &use);
+
+	  /* Check if this character is not part of the variable name
+	     anymore.  */
+	  if (! (check_varstate (newstate)))
+	    {
+	      if (grub_script_lexer_state == GRUB_PARSER_STATE_VARNAME2
+		  || grub_script_lexer_state == GRUB_PARSER_STATE_QVARNAME2)
+		script++;
+	      grub_script_lexer_state = newstate;
+	      break;
+	    }
+
+	  if (use)
+	    *(bp++) = use;
+	  script++;
+	  grub_script_lexer_state = newstate;
+	}
+
+      *bp = '\0';
+      grub_script_lexer_state = newstate;
+      grub_script_yylval.string = buffer;
+      grub_dprintf ("scripting", "vartoken=`%s'\n", buffer);
+
+      return GRUB_PARSER_TOKEN_VAR;
+    }
+  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;
+    }
+}
+
+void
+grub_script_yyerror (char const *err)
+{
+  grub_printf (err);
+}
Index: normal/parser.y
===================================================================
RCS file: normal/parser.y
diff -N normal/parser.y
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/parser.y	3 Nov 2005 21:53:12 -0000
@@ -0,0 +1,191 @@
+/* parser.y - The scripting parser.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+%{
+#include <grub/script.h>
+#include <grub/mm.h>
+
+#define YYFREE		grub_free
+#define YYMALLOC	grub_malloc
+
+/* Keep track of the memory allocated for this specific function.  */
+static struct grub_script_mem *func_mem = 0;
+
+%}
+
+%union {
+  struct grub_script_cmd *cmd;
+  struct grub_script_arglist *arglist;
+  struct grub_script_arg *arg;
+  char *string;
+}
+
+%token GRUB_PARSER_TOKEN_IF		"if"
+%token GRUB_PARSER_TOKEN_WHILE		"while"
+%token GRUB_PARSER_TOKEN_FUNCTION	"function"
+%token GRUB_PARSER_TOKEN_ELSE		"else"
+%token GRUB_PARSER_TOKEN_THEN		"then"
+%token GRUB_PARSER_TOKEN_FI		"fi"
+%token GRUB_PARSER_TOKEN_NAME
+%token GRUB_PARSER_TOKEN_VAR
+%type <cmd> script grubcmd command commands if
+%type <arglist> arguments;
+%type <arg> argument;
+%type <string> "if" "while" "function" "else" "then" "fi"
+%type <string> text GRUB_PARSER_TOKEN_NAME GRUB_PARSER_TOKEN_VAR
+
+%%
+/* It should be possible to do this in a clean way...  */
+script:		commands '\n'
+		  {
+		    grub_script_parsed = $1;
+		  }
+;
+
+/* Some tokens are both used as token or as plain text.  XXX: Add all
+   tokens without causing conflicts.  */
+text:		GRUB_PARSER_TOKEN_NAME
+		  {
+		    $$ = $1;
+		  }
+		| "if"
+		  {
+		    $$ = $1;
+		  }
+		| "while"
+		  {
+		    $$ = $1;
+		  }
+;
+
+ws:		/* Empty */
+		| ' '
+;
+
+returns:	/* Empty */
+		| '\n'
+;
+
+/* An argument can consist of some static text mixed with variables,
+   for example: `foo${bar}baz'.  */
+argument:	GRUB_PARSER_TOKEN_VAR
+		  {
+		    $$ = grub_script_arg_add (0, GRUB_SCRIPT_ARG_TYPE_VAR, $1);
+		  }
+		| text
+		  {
+		    $$ = grub_script_arg_add (0, GRUB_SCRIPT_ARG_TYPE_STR, $1);
+		  }
+		| argument GRUB_PARSER_TOKEN_VAR
+		  {
+		    $$ = grub_script_arg_add ($1, GRUB_SCRIPT_ARG_TYPE_VAR, $2);
+		  }
+		| argument text
+		  {
+		    $$ = grub_script_arg_add ($1, GRUB_SCRIPT_ARG_TYPE_STR, $2);
+		  }
+;
+
+arguments:	argument
+		  {
+		    $$ = grub_script_add_arglist (0, $1);
+		  }
+		| arguments ' ' argument
+		  {
+		    $$ = grub_script_add_arglist ($1, $3);
+		  }
+;
+
+grubcmd:	ws GRUB_PARSER_TOKEN_NAME ' ' arguments ws
+		  {
+		    $$ = grub_script_create_cmdline ($2, $4);
+		  }
+		| ws GRUB_PARSER_TOKEN_NAME ws
+		  {
+		    $$ = grub_script_create_cmdline ($2, 0);
+		  }
+;
+
+/* A single command.  */
+command:	grubcmd 	{ $$ = $1; }
+		| if 		{ $$ = $1; }
+		| function	{ $$ = 0;  }
+;
+
+/* A block of commands.  */
+commands:	command
+		  { 
+		    $$ = grub_script_add_cmd (0, $1);
+		  }
+		| commands ';' command
+		  { 
+		    struct grub_script_cmdblock *cmd;
+		    cmd = (struct grub_script_cmdblock *) $1;
+		    $$ = grub_script_add_cmd (cmd, $3);
+		  }
+		| commands '\n' command
+		  { 
+		    struct grub_script_cmdblock *cmd;
+		    cmd = (struct grub_script_cmdblock *) $1;
+		    $$ = grub_script_add_cmd (cmd, $3);
+		  }
+;
+
+/* A function.  Carefully save the memory that is allocated.  */
+function:	"function" ' ' GRUB_PARSER_TOKEN_NAME
+		  { 
+		    grub_script_lexer_ref ();
+		  } ws '{' returns
+		  { 
+		    /* The first part of the function was recognised.
+		       Now start recording the memory usage to store
+		       this function.  */
+		    func_mem = grub_script_mem_record ();
+		  } commands returns '}'
+		  {
+		    struct grub_script *script;
+
+		    /* All the memory usage for parsing this function
+		       was recorded.  */
+		    func_mem = grub_script_mem_record_stop (func_mem);
+		    script = grub_script_create ($9, func_mem);
+		    if (script)
+		      grub_script_function_create ($3, script);
+		    grub_script_lexer_deref ();
+		  }
+;
+
+/* 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 (); }
+;
+
+/* The if statement.  */
+if:		 if_statement grubcmd ';' ws "then" returns commands returns "fi"
+		  {
+		    $$ = grub_script_create_cmdif ($2, $7, 0);
+		    grub_script_lexer_deref ();
+		  }
+		 | if_statement grubcmd ';' ws "then" returns commands returns "else" returns commands "fi"
+		  {
+		    $$ = grub_script_create_cmdif ($2, $7, $11);
+		    grub_script_lexer_deref ();
+		  }
+;
Index: normal/script.c
===================================================================
RCS file: normal/script.c
diff -N normal/script.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ normal/script.c	3 Nov 2005 21:53:12 -0000
@@ -0,0 +1,289 @@
+/* script.c -- Functions to create an in memory description of the script. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2005  Free Software Foundation, Inc.
+ *
+ *  This program 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 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <grub/misc.h>
+#include <grub/script.h>
+#include <grub/parser.h>
+#include <grub/mm.h>
+
+/* 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 free'ed in case of an error, or
+   assigned to the parsed script when parsing was successful.  */
+
+/* The memory that was used while parsing and scanning.  */
+static struct grub_script_mem *grub_script_memused;
+
+/* The result of the parser.  */
+struct grub_script_cmd *grub_script_parsed = 0;
+
+/* 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 free'ed.  The original memory can be found
+   from &mem.  */
+struct grub_script_mem
+{
+  struct grub_script_mem *next;
+  char mem;
+};
+
+/* Return malloc'ed memory and keep track of the allocation.  */
+void *
+grub_script_malloc (grub_size_t size)
+{
+  struct grub_script_mem *mem;
+  mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem)
+						- sizeof (char));
+
+  grub_dprintf ("scripting", "malloc %p\n", mem);
+  mem->next = grub_script_memused;
+  grub_script_memused = mem;
+  return (void *) &mem->mem;
+}
+
+/* Free all memory described by MEM.  */
+static void
+grub_script_mem_free (struct grub_script_mem *mem)
+{
+  struct grub_script_mem *memfree;
+
+  while (mem)
+    {
+      memfree = mem->next;
+      grub_dprintf ("scripting", "free %p\n", mem);
+      grub_free (mem);
+      mem = memfree;
+    }
+}
+
+/* Start recording memory usage.  Returns the memory that should be
+   restored when calling stop.  */
+struct grub_script_mem *
+grub_script_mem_record (void)
+{
+  struct grub_script_mem *mem = grub_script_memused;
+  grub_script_memused = 0;
+  return mem;
+}
+
+/* Stop recording memory usage.  Restore previous recordings using
+   RESTORE.  Return the recorded memory.  */
+struct grub_script_mem *
+grub_script_mem_record_stop (struct grub_script_mem *restore)
+{
+  struct grub_script_mem *mem = grub_script_memused;
+  grub_script_memused = restore;
+  return mem;
+}
+
+/* Free the memory reserved for CMD and all of it's children.  */
+void
+grub_script_free (struct grub_script *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_script_arg *arg,
+		     grub_script_arg_type_t type, char *str)
+{
+  struct grub_script_arg *argpart;
+  struct grub_script_arg *ll;
+  
+  argpart = (struct grub_script_arg *) grub_script_malloc (sizeof (*arg));
+  argpart->type = type;
+  argpart->str = str;
+  argpart->next = 0;
+
+  if (! arg)
+    return argpart;
+
+  for (ll = arg; ll->next; ll = ll->next);
+  ll->next = argpart;
+      
+  return arg;
+}
+
+/* Add the argument ARG to the end of the argument list LIST.  If LIST
+   is zero, a new list will be created.  */
+struct grub_script_arglist *
+grub_script_add_arglist (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 (sizeof (*link));
+  link->next = 0;
+  link->arg = arg;
+  link->argcount = 0;
+
+  if (! list)
+    {
+      link->argcount++;
+      return link;
+    }
+
+  list->argcount++;
+
+  /* Look up the last link in the chain.  */
+  for (ll = list; ll->next; ll = ll->next);
+  ll->next = link;
+
+  return list;
+}
+
+/* Create a command that describes a single command line.  CMDLINE
+   contains the name of the command that should be executed.  ARGLIST
+   holds all arguments for this command.  */
+struct grub_script_cmd *
+grub_script_create_cmdline (char *cmdname, struct grub_script_arglist *arglist)
+{
+  struct grub_script_cmdline *cmd;
+
+  grub_dprintf ("scripting", "cmdline\n");
+
+  cmd = grub_script_malloc (sizeof (*cmd));
+  cmd->cmd.exec = grub_script_execute_cmdline;
+/*   cmd->cmd.free = grub_script_free_cmdline; */
+  cmd->cmd.next = 0;
+  cmd->arglist = arglist;
+  cmd->cmdname = cmdname;
+
+  return (struct grub_script_cmd *) cmd;
+}
+
+/* Create a command that functions as an if statement.  If BOOL is
+   evaluated to true (the value is returned in envvar RESULT), the
+   interpreter will run the command TRUE, otherwise the interpreter
+   runs the command FALSE.  */
+struct grub_script_cmd *
+grub_script_create_cmdif (struct grub_script_cmd *bool,
+			  struct grub_script_cmd *true,
+			  struct grub_script_cmd *false)
+{
+  struct grub_script_cmdif *cmd;
+
+  grub_dprintf ("scripting", "cmdif\n");
+
+  cmd = grub_script_malloc (sizeof (*cmd));
+  cmd->cmd.exec = grub_script_execute_cmdif;
+  cmd->cmd.next = 0;
+  cmd->bool = bool;
+  cmd->true = true;
+  cmd->false = false;
+
+  return (struct grub_script_cmd *) cmd;
+}
+
+/* Create a block of commands.  CMD contains the command that should
+   be added at the end of CMDBLOCK's list.  If CMDBLOCK is zero, a new
+   cmdblock will be created.  */
+struct grub_script_cmd *
+grub_script_add_cmd (struct grub_script_cmdblock *cmdblock, struct grub_script_cmd *cmd)
+{
+  grub_dprintf ("scripting", "cmdblock\n");
+
+  if (! cmd)
+    return (struct grub_script_cmd *) cmdblock;
+
+  if (! cmdblock)
+    {
+      cmdblock = (struct grub_script_cmdblock *) grub_script_malloc (sizeof (*cmdblock));
+      cmdblock->cmd.exec = grub_script_execute_cmdblock;
+      cmdblock->cmd.next = 0;
+      cmdblock->cmdlist = cmd;
+    }
+  else
+    {
+      struct grub_script_cmd **last;
+      for (last = &cmdblock->cmdlist; *last; last = &(*last)->next);
+      *last = cmd;
+    }
+
+  cmd->next = 0;
+
+  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)
+    {
+      grub_script_mem_free (mem);
+      grub_free (cmd);
+
+      return 0;
+    }
+
+  parsed->mem = mem;
+  parsed->cmd = cmd;
+
+  return parsed;
+}
+
+/* Parse the script passed in SCRIPT and return the parsed
+   datastructure that is ready to be interpreted.  */
+struct grub_script *
+grub_script_parse (char *script, grub_err_t (*getline) (char **))
+{
+  struct grub_script *parsed;
+  struct grub_script_mem *membackup;
+
+  parsed = grub_malloc (sizeof (*parsed));
+  if (! parsed)
+    return 0;
+
+  /* Initialize the lexer.  */
+  grub_script_lexer_init (script, getline);
+
+  grub_script_parsed = 0;
+
+  membackup = grub_script_mem_record ();
+
+  /* Parse the script, the result is stored in
+     `grub_script_parsed'.  */
+  if (grub_script_yyparse ())
+    {
+      struct grub_script_mem *memfree;
+      memfree = grub_script_mem_record_stop (membackup);
+      grub_script_mem_free (memfree);
+      return 0;
+    }
+
+  parsed->mem = grub_script_mem_record_stop (membackup);
+  parsed->cmd = grub_script_parsed;
+
+  return parsed;
+}
Index: util/grub-emu.c
===================================================================
RCS file: /cvsroot/grub/grub2/util/grub-emu.c,v
retrieving revision 1.26
diff -u -p -u -p -r1.26 grub-emu.c
--- util/grub-emu.c	9 Oct 2005 13:03:53 -0000	1.26
+++ util/grub-emu.c	3 Nov 2005 21:53:12 -0000
@@ -219,6 +219,7 @@ main (int argc, char *argv[])
   grub_timeout_init ();
   grub_configfile_init ();
   grub_search_init ();
+  grub_test_init ();
   
   /* XXX: Should normal mode be started by default?  */
   grub_normal_init ();
@@ -227,6 +228,7 @@ main (int argc, char *argv[])
   if (setjmp (main_env) == 0)
     grub_main ();
 
+  grub_test_fini ();
   grub_search_fini ();
   grub_configfile_fini ();
   grub_timeout_fini ();




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-11-02  9:37 ` Scripting support (PATCH) adrian15
@ 2005-11-06 22:13   ` Marco Gerards
  0 siblings, 0 replies; 26+ messages in thread
From: Marco Gerards @ 2005-11-06 22:13 UTC (permalink / raw)
  To: The development of GRUB 2

"adrian15" <adrian15@raulete.net> writes:

>> > I think also. Because theese modifications are lost after boot they >
>> are generally modified only to boot one time an OS with specifical >
>> parameters and you don't need scripts for this.
>> 
>> Right.  The only reason I see for editing the script in general is to
>> edit some menu generation routine.  But that is a very rare case I
>> think.  If it turns out not to be a rare case, we can provide this
>> feature later on.
>
> I'm not quite sure if this discussion will affect my next development of 
> GSD based on grub2 but I'll tell you my opinnion:
>
> Grub2 config files should be interpreted as if you were running a bash 
> script. (I actually don't know if it corresponds to approach #1 or #2 or if 
> it hasn't any relation with it)

What do you mean by that?

I think it will work like you expect it will work. :-)

--
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-11-03 21:58 ` Marco Gerards
@ 2005-11-06 22:26   ` Marco Gerards
  2005-11-08  3:38     ` Hollis Blanchard
  0 siblings, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-11-06 22:26 UTC (permalink / raw)
  To: The development of GRUB 2

Marco Gerards <metgerards@student.han.nl> writes:

> Marco Gerards <metgerards@student.han.nl> writes:
>
> Hi,
>
>> Here is the patch I promised last week.  It is far from perfect and
>> will most likely break one or two things, but I think it is worth the
>> trouble to add it for the following reasons:
>
> Here is a new version.  If you have some time, please have a look at
> it.  If I don't hear about any important problems I will commit this
> patch at the end of the weekend.

Committed!

It does not work on the PPC yet, but I'll take care of that tomorrow.

Please test if everything still works.  Of course I took great care in
testing and implementing, but a lot changed and I am not perfect.
This week I will have some time, so if there are any problems
reported, they should be fixed in no time.

Thanks,
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-11-06 22:26   ` Marco Gerards
@ 2005-11-08  3:38     ` Hollis Blanchard
  2005-11-08 18:24       ` Marco Gerards
  0 siblings, 1 reply; 26+ messages in thread
From: Hollis Blanchard @ 2005-11-08  3:38 UTC (permalink / raw)
  To: The development of GRUB 2

On Nov 6, 2005, at 4:26 PM, Marco Gerards wrote:
>
> Please test if everything still works.  Of course I took great care in
> testing and implementing, but a lot changed and I am not perfect.
> This week I will have some time, so if there are any problems
> reported, they should be fixed in no time.

Could you add a configure check for yacc? I didn't have it installed on 
my system, and got a compile error.

-Hollis




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-11-08  3:38     ` Hollis Blanchard
@ 2005-11-08 18:24       ` Marco Gerards
  2005-11-09  6:14         ` Hollis Blanchard
  0 siblings, 1 reply; 26+ messages in thread
From: Marco Gerards @ 2005-11-08 18:24 UTC (permalink / raw)
  To: The development of GRUB 2

Hollis Blanchard <hollis@penguinppc.org> writes:

> On Nov 6, 2005, at 4:26 PM, Marco Gerards wrote:
>>
>> Please test if everything still works.  Of course I took great care in
>> testing and implementing, but a lot changed and I am not perfect.
>> This week I will have some time, so if there are any problems
>> reported, they should be fixed in no time.
>
> Could you add a configure check for yacc? I didn't have it installed
> on my system, and got a compile error.

That is really weird, I have added such test:

	* configure.ac: Add `AC_PROG_YACC' test.

When configuring I see:

checking for bison... bison -y

Did you see that test and what did it output?

--
Marco




^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: Scripting support (PATCH)
  2005-11-08 18:24       ` Marco Gerards
@ 2005-11-09  6:14         ` Hollis Blanchard
  0 siblings, 0 replies; 26+ messages in thread
From: Hollis Blanchard @ 2005-11-09  6:14 UTC (permalink / raw)
  To: The development of GRUB 2

On Nov 8, 2005, at 12:24 PM, Marco Gerards wrote:

> Hollis Blanchard <hollis@penguinppc.org> writes:
>
>> On Nov 6, 2005, at 4:26 PM, Marco Gerards wrote:
>>>
>>> Please test if everything still works.  Of course I took great care 
>>> in
>>> testing and implementing, but a lot changed and I am not perfect.
>>> This week I will have some time, so if there are any problems
>>> reported, they should be fixed in no time.
>>
>> Could you add a configure check for yacc? I didn't have it installed
>> on my system, and got a compile error.
>
> That is really weird, I have added such test:
>
> 	* configure.ac: Add `AC_PROG_YACC' test.
>
> When configuring I see:
>
> checking for bison... bison -y
>
> Did you see that test and what did it output?

	checking for bison... no
	checking for byacc... no

And then it happily continued to configure and make, with the eventual 
output:
	yacc -d -p grub_script_yy -b grub_script ../normal/parser.y
	make: yacc: Command not found

It's a little late for me, otherwise I'd have a look at configure.ac 
myself.

-Hollis




^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2005-11-09  6:14 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <200511010611.jA16B6Bq030884@dell01.dinaserver.com>
2005-11-02  9:37 ` Scripting support (PATCH) adrian15
2005-11-06 22:13   ` Marco Gerards
2005-10-29 21:41 Marco Gerards
2005-10-30  5:25 ` Yoshinori K. Okuji
2005-10-30  9:45   ` Marco Gerards
2005-10-30 11:55     ` Yoshinori K. Okuji
2005-10-30 12:17       ` Marco Gerards
2005-10-30 10:14   ` Marco Gerards
2005-10-30 14:41 ` Vladimir Serbinenko
2005-10-30 16:05   ` Marco Gerards
2005-10-30 16:46     ` Vladimir Serbinenko
2005-10-30 16:59       ` Marco Gerards
2005-10-30 17:15         ` Vladimir Serbinenko
2005-10-30 18:17           ` Marco Gerards
2005-10-31  6:39   ` Yoshinori K. Okuji
2005-10-30 20:49 ` Hollis Blanchard
2005-10-30 21:04   ` Marco Gerards
2005-10-31  6:45     ` Yoshinori K. Okuji
2005-10-31 18:58       ` Marco Gerards
2005-11-01 19:06         ` Marco Gerards
2005-11-01 19:55           ` Yoshinori K. Okuji
2005-11-03 21:58 ` Marco Gerards
2005-11-06 22:26   ` Marco Gerards
2005-11-08  3:38     ` Hollis Blanchard
2005-11-08 18:24       ` Marco Gerards
2005-11-09  6:14         ` Hollis Blanchard

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.