grub-devel
[Top][All Lists]
Advanced

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

Re: Scripting support (PATCH)


From: Marco Gerards
Subject: Re: Scripting support (PATCH)
Date: Thu, 03 Nov 2005 22:58:45 +0100
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

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

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





reply via email to

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