grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Scripting support (PATCH)


From: Marco Gerards
Subject: Scripting support (PATCH)
Date: Sat, 29 Oct 2005 23:41:15 +0200
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

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  <address@hidden>

        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;
+}
+
+
+
+#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);
+
+
+
+/* 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;
+}
+
+
+
+/* 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);
+}
+
+
+
+/* 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;
+}
+
+
+
+/* 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 ();





reply via email to

[Prev in Thread] Current Thread [Next in Thread]