All of lore.kernel.org
 help / color / mirror / Atom feed
* The menu and scripting
@ 2006-01-05 17:25 Marco Gerards
  2006-01-05 23:04 ` Feature request: MBR backup wobble
  0 siblings, 1 reply; 3+ messages in thread
From: Marco Gerards @ 2006-01-05 17:25 UTC (permalink / raw)
  To: grub-devel

Hi,

Here is a patch that makes it possible to script in the configfile and
make it possible to add menu entries from scripts.  The keyword
`menuentry' (or short `@') was added for this.  Currently the
implementation only adds basic functionality.  You can do things like:

set a=1

if [ ${a}= 1 ]; then
menuitem foo {
foo1
foo2
}
else
menuitem bar {
bar1
bar2
}
fi

In that case only foo will be shown.

There are a lot of things that can be improved.  Here is a list of
things I will work on:

- Making the parser reentrant.
- Relaxing the syntax a bit.
- Adding support for labels.
- Making it possible to generate menu entries using arguments or so.
- Better error handing.

Please tell me if there are any issues with this patch.  I will apply
it next Sunday if I don't hear anything.

Thanks,
Marco


2006-01-05  Marco Gerards  <marco@gnu.org>

	* include/grub/normal.h: Include <grub/script.h>.
	(grub_command_list): Removed struct.
	(grub_command_list_t): Removed type.
	(grub_menu_entry): Remove members `num' and `command_list'.  Add
	members `commands' and `sourcecode'.
	* include/grub/script.h: Add inclusion guards.
	(grub_script_cmd_menuentry): New struct.
	(grub_script_execute_menuentry): New prototype.
	(grub_script_lexer_record_start): Likewise.
	(grub_script_lexer_record_stop): Likewise.
	* normal/execute.c (grub_script_execute_menuentry): New function.
	* normal/lexer.c (record, recording, recordpos, recordlen): New
	variables.
	(grub_script_lexer_record_start): New function.
	(grub_script_lexer_record_stop): Likewise.
	(recordchar): Likewise.
	(nextchar): Likewise.
	(grub_script_yylex): Use `nextchar' to fetch new characters.  Use
	2048 as the buffer size.  Add the tokens `menuentry' and `@'.
	* normal/main.c: Include <grub/parser.h> and <grub/script.h>
	(current_menu): New variable.
	(free_menu): Mainly rewritten.
	(grub_normal_menu_addentry): New function.
	(read_config_file): Rewritten.
	* normal/menu.c (run_menu_entry): Mainly rewritten.
	* normal/menu_entry.c (make_screen): Rewritten te code to insert
	the menu entry.
	(run): Mainly rewritten.
	* normal/parser.y (menu_entry): New variable.
	(GRUB_PARSER_TOKEN_MENUENTRY): New token.
	(menuentry): New rule.
	(command): Add `menuentry'.
	(if_statement): Allow additional returns before `fi'.
	* normal/script.c (grub_script_create_cmdmenu): New function.



Index: include/grub/normal.h
===================================================================
RCS file: /cvsroot/grub/grub2/include/grub/normal.h,v
retrieving revision 1.23
diff -u -p -u -p -r1.23 normal.h
--- include/grub/normal.h	6 Nov 2005 22:19:59 -0000	1.23
+++ include/grub/normal.h	5 Jan 2006 17:14:06 -0000
@@ -25,6 +25,7 @@
 #include <grub/symbol.h>
 #include <grub/err.h>
 #include <grub/arg.h>
+#include <grub/script.h>
 
 /* The maximum size of a command-line.  */
 #define GRUB_MAX_CMDLINE	1600
@@ -84,28 +85,17 @@ struct grub_command
 };
 typedef struct grub_command *grub_command_t;
 
-/* The command list.  */
-struct grub_command_list
-{
-  /* The string of a command.  */
-  char *command;
-
-  /* The next element.  */
-  struct grub_command_list *next;
-};
-typedef struct grub_command_list *grub_command_list_t;
-
 /* The menu entry.  */
 struct grub_menu_entry
 {
   /* The title name.  */
   const char *title;
 
-  /* The number of commands.  */
-  int num;
+    /* The commands associated with this menu entry.  */
+  struct grub_script *commands;
 
-  /* The list of commands.  */
-  grub_command_list_t command_list;
+  /* The sourcecode of the menu entry, used by the editor.  */
+  const char *sourcecode;
 
   /* The next element.  */
   struct grub_menu_entry *next;
@@ -192,6 +182,9 @@ void grub_context_pop_menu (void);
 char *grub_normal_do_completion (char *buf, int *restore,
 				 void (*hook) (const char *item, grub_completion_type_t type, int count));
 grub_err_t grub_normal_print_device_info (const char *name);
+grub_err_t grub_normal_menu_addentry (const char *title,
+				      struct grub_script *script,
+				      const char *sourcecode);
 
 #ifdef GRUB_UTIL
 void grub_normal_init (void);
Index: include/grub/script.h
===================================================================
RCS file: /cvsroot/grub/grub2/include/grub/script.h,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 script.h
--- include/grub/script.h	6 Nov 2005 22:19:59 -0000	1.1
+++ include/grub/script.h	5 Jan 2006 17:14:06 -0000
@@ -17,6 +17,10 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+
+#ifndef GRUB_SCRIPT_HEADER
+#define GRUB_SCRIPT_HEADER	1
+
 #include <grub/types.h>
 #include <grub/err.h>
 
@@ -104,6 +108,21 @@ struct grub_script_cmdif
   struct grub_script_cmd *false;
 };
 
+/* A menu entry generate statement.  */
+struct grub_script_cmd_menuentry
+{
+  struct grub_script_cmd cmd;
+
+  /* The title of the menu entry.  */
+  struct grub_script_arg *title;
+
+  /* The sourcecode the entry will be generated from.  */
+  const char *sourcecode;
+
+  /* Options.  XXX: Not used yet.  */
+  int options;
+};
+
 struct grub_script_arglist *
 grub_script_create_arglist (void);
 
@@ -120,6 +139,12 @@ 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_create_cmdmenu (struct grub_script_arg *title,
+			    char *sourcecode,
+			    int options);
+
 struct grub_script_cmd *
 grub_script_add_cmd (struct grub_script_cmdblock *cmdblock,
 		     struct grub_script_cmd *cmd);
@@ -136,6 +161,8 @@ struct grub_script *grub_script_create (
 void grub_script_lexer_init (char *s, grub_err_t (*getline) (char **));
 void grub_script_lexer_ref (void);
 void grub_script_lexer_deref (void);
+void grub_script_lexer_record_start (void);
+char *grub_script_lexer_record_stop (void);
 
 /* Functions to track allocated memory.  */
 void *grub_script_malloc (grub_size_t size);
@@ -151,6 +178,7 @@ void grub_script_yyerror (char const *er
 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);
+grub_err_t grub_script_execute_menuentry (struct grub_script_cmd *cmd);
 
 /* Execute any GRUB pre-parsed command or script.  */
 grub_err_t grub_script_execute (struct grub_script *script);
@@ -187,3 +215,5 @@ grub_script_function_t grub_script_funct
 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);
+
+#endif /* ! GRUB_SCRIPT_HEADER */
Index: normal/execute.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/execute.c,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 execute.c
--- normal/execute.c	6 Nov 2005 22:19:59 -0000	1.1
+++ normal/execute.c	5 Jan 2006 17:14:06 -0000
@@ -191,6 +191,36 @@ grub_script_execute_cmdif (struct grub_s
     return grub_script_execute_cmd (cmdif->false);
 }
 
+/* Execute the menu entry generate statement.  */
+grub_err_t
+grub_script_execute_menuentry (struct grub_script_cmd *cmd)
+{
+  struct grub_script_cmd_menuentry *cmd_menuentry;
+  char *title;
+  struct grub_script *script;
+
+  cmd_menuentry = (struct grub_script_cmd_menuentry *) cmd;
+
+  /* The title can contain variables, parse them and generate a string
+     from it.  */
+  title = grub_script_execute_argument_to_string (cmd_menuentry->title);
+  if (! title)
+    return grub_errno;
+
+  /* Parse the menu entry *again*.  */
+  script = grub_script_parse ((char *) cmd_menuentry->sourcecode, 0);
+
+  if (! script)
+    {
+      grub_free (title);
+      return grub_errno;
+    }
+
+  /* XXX: When this fails, the memory should be free'ed?  */
+  return grub_normal_menu_addentry (title, script,
+				    cmd_menuentry->sourcecode);
+}
+
 \f
 
 /* Execute any GRUB pre-parsed command or script.  */
Index: normal/lexer.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/lexer.c,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 lexer.c
--- normal/lexer.c	6 Nov 2005 22:19:59 -0000	1.1
+++ normal/lexer.c	5 Jan 2006 17:14:06 -0000
@@ -54,6 +54,11 @@ static int grub_script_lexer_refs = 0;
 static char *script;
 static char *newscript;
 
+static int record = 0;
+static char *recording = 0;
+static int recordpos = 0;
+static int recordlen = 0;
+
 /* XXX: The lexer is not reentrant.  */
 void
 grub_script_lexer_init (char *s, grub_err_t (*getline) (char **))
@@ -78,6 +83,63 @@ grub_script_lexer_deref (void)
   grub_script_lexer_refs--;
 }
 
+/* Start recording all characters passing through the lexer.  */
+void
+grub_script_lexer_record_start (void)
+{
+  record = 1;
+  recordlen = 100;
+  recording = grub_malloc (recordlen);
+  recordpos = 0;
+}
+
+char *
+grub_script_lexer_record_stop (void)
+{
+  record = 0;
+
+  /* Delete the last character, it is a `}'.  */
+  if (recordpos > 0)
+    {
+      if (recording[--recordpos] != '}')
+	{
+	  grub_printf ("Internal error while parsing menu entry");
+	  for (;;); /* XXX */
+	}
+      recording[recordpos] = '\0';
+    }
+
+  return recording;
+}
+
+/* When recording is enabled, record the character C as the next item
+   in the character stream.  */
+static void
+recordchar (char c)
+{
+  if (recordpos == recordlen)
+    {
+      char *old = recording;
+      recordlen += 100;
+      recording = grub_realloc (recording, recordlen);
+      if (! recording)
+	{
+	  grub_free (old);
+	  record = 0;
+	}
+    }
+  recording[recordpos++] = c;
+}
+
+/* Fetch the next character for the lexer.  */
+static void
+nextchar (void)
+{
+  if (record)
+    recordchar (*script);
+  script++;
+}
+
 int
 grub_script_yylex (void)
 {
@@ -96,13 +158,17 @@ grub_script_yylex (void)
 	   || grub_script_lexer_state == GRUB_PARSER_STATE_ESC)
 	  && grub_script_lexer_getline)
 	{
-	  while (! grub_strlen (script))
+	  while (!script || ! grub_strlen (script))
 	    {
 	      grub_free (newscript);
+	      newscript = 0;
 	      grub_script_lexer_getline (&newscript);
 	      script = newscript;
+	      if (! script)
+		return 0;
 	    }
 	  grub_dprintf ("scripting", "token=`\\n'\n");
+	  recordchar ('\n');
 	  if (grub_script_lexer_state != GRUB_PARSER_STATE_ESC)
 	    return '\n';
 	}
@@ -139,7 +205,7 @@ grub_script_yylex (void)
 		      return ' ';
 		    }
 		  grub_script_lexer_state = newstate;
-		  script++;
+		  nextchar ();
 		}
 	      grub_dprintf ("scripting", "token=` '\n");
 	      return ' ';
@@ -147,13 +213,18 @@ grub_script_yylex (void)
 	    case '}':
 	    case ';':
 	    case '\n':
-	      grub_dprintf ("scripting", "token=`%c'\n", *script);
-	      return *(script++);
+	      {
+		char c;
+		grub_dprintf ("scripting", "token=`%c'\n", *script);
+		c = *script;;
+		nextchar ();
+		return c;
+	      }
 	    }
 	}
 
       /* XXX: Use a better size.  */
-      buffer = grub_script_malloc (2096);
+      buffer = grub_script_malloc (2048);
       if (! buffer)
 	return 0;
 
@@ -194,7 +265,7 @@ grub_script_yylex (void)
 	    *(bp++) = use;
 
 	  grub_script_lexer_state = newstate;
-	  script++;
+	  nextchar ();
 	}
 
       /* A string of text was read in.  */
@@ -209,6 +280,10 @@ grub_script_yylex (void)
 	return GRUB_PARSER_TOKEN_IF;
       else if (! grub_strcmp (buffer, "function"))
 	return GRUB_PARSER_TOKEN_FUNCTION;
+      else if (! grub_strcmp (buffer, "menuentry"))
+	return GRUB_PARSER_TOKEN_MENUENTRY;
+      else if (! grub_strcmp (buffer, "@"))
+	return GRUB_PARSER_TOKEN_MENUENTRY;
       else if (! grub_strcmp (buffer, "else"))
 	return GRUB_PARSER_TOKEN_ELSE;
       else if (! grub_strcmp (buffer, "then"))
@@ -240,14 +315,14 @@ grub_script_yylex (void)
 	    {
 	      if (grub_script_lexer_state == GRUB_PARSER_STATE_VARNAME2
 		  || grub_script_lexer_state == GRUB_PARSER_STATE_QVARNAME2)
-		script++;
+		nextchar ();
 	      grub_script_lexer_state = newstate;
 	      break;
 	    }
 
 	  if (use)
 	    *(bp++) = use;
-	  script++;
+	  nextchar ();
 	  grub_script_lexer_state = newstate;
 	}
 
Index: normal/main.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/main.c,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 main.c
--- normal/main.c	13 Nov 2005 15:47:09 -0000	1.13
+++ normal/main.c	5 Jan 2006 17:14:06 -0000
@@ -27,11 +27,16 @@
 #include <grub/mm.h>
 #include <grub/term.h>
 #include <grub/env.h>
+#include <grub/parser.h>
+#include <grub/script.h>
 
 grub_jmp_buf grub_exit_env;
 
 static grub_fs_module_list_t fs_module_list = 0;
 
+/* The menu to which the new entries are added by the parser.  */
+static grub_menu_t current_menu = 0;
+
 #define GRUB_DEFAULT_HISTORY_SIZE	50
 
 /* Read a line from the file FILE.  */
@@ -110,188 +115,118 @@ free_menu (grub_menu_t menu)
   while (entry)
     {
       grub_menu_entry_t next_entry = entry->next;
-      grub_command_list_t cmd = entry->command_list;
-      
-      while (cmd)
-	{
-	  grub_command_list_t next_cmd = cmd->next;
-
-	  grub_free ((void *) cmd->command);
-	  cmd = next_cmd;
-	}
 
+      grub_script_free (entry->commands);
       grub_free ((void *) entry->title);
+      grub_free ((void *) entry->sourcecode);
       entry = next_entry;
     }
 
   grub_free (menu);
 }
 
-/* Read the config file CONFIG and return a menu. If no entry is present,
-   return NULL.  */
+grub_err_t
+grub_normal_menu_addentry (const char *title, struct grub_script *script,
+			   const char *sourcecode)
+{
+  const char *menutitle;
+  grub_menu_entry_t *last = &current_menu->entry_list;
+
+  menutitle = grub_strdup (title);
+  if (! menutitle)
+    return grub_errno;
+
+  /* Add the menu entry at the end of the list.  */
+  while (*last)
+    last = &(*last)->next;
+
+  *last = grub_malloc (sizeof (**last));
+  if (! *last)
+    {
+      grub_free ((void *) menutitle);
+      grub_free ((void *) sourcecode);
+      return grub_errno;
+    }
+
+  (*last)->commands = script;
+  (*last)->title = menutitle;
+  (*last)->next = 0;
+  (*last)->sourcecode = sourcecode;
+
+  return GRUB_ERR_NONE;
+}
+
 static grub_menu_t
 read_config_file (const char *config)
 {
   grub_file_t file;
-  static char cmdline[GRUB_MAX_CMDLINE];
-  grub_menu_t menu;
-  grub_menu_entry_t *next_entry, cur_entry = 0;
-  grub_command_list_t *next_cmd, cur_cmd;
+  auto grub_err_t getline (char **line);
+  int currline = 0;
   
+  grub_err_t getline (char **line)
+    {
+      char cmdline[100];
+      currline++;
+
+      if (! get_line (file, cmdline, sizeof (cmdline)))
+	return 0;
+      
+      *line = grub_strdup (cmdline);
+      if (! *line)
+	return grub_errno;
+
+      return GRUB_ERR_NONE;
+    }
+
+  char cmdline[100];
+  grub_menu_t newmenu;
+
+  newmenu = grub_malloc (sizeof (*newmenu));
+  if (! newmenu)
+    return 0;
+  newmenu->default_entry = 0;
+  newmenu->fallback_entry = -1;
+  newmenu->timeout = -1;
+  newmenu->size = 0;
+  newmenu->entry_list = 0;
+  current_menu = newmenu;
+
   /* Try to open the config file.  */
   file = grub_file_open (config);
   if (! file)
     return 0;
 
-  /* Initialize the menu.  */
-  menu = (grub_menu_t) grub_malloc (sizeof (*menu));
-  if (! menu)
-    {
-      grub_file_close (file);
-      return 0;
-    }
-  menu->default_entry = 0;
-  menu->fallback_entry = -1;
-  menu->timeout = -1;
-  menu->size = 0;
-  menu->entry_list = 0;
-
-  if (! grub_context_push_menu (menu))
-    {
-      grub_print_error ();
-      grub_errno = GRUB_ERR_NONE;
-
-      free_menu (menu);
-      grub_file_close (file);
-      
-      /* Wait until the user pushes any key so that the user
-	 can see what happened.  */
-      grub_printf ("\nPress any key to continue...");
-      (void) grub_getkey ();
-      return 0;
-    }
-  
-  next_entry = &(menu->entry_list);
-  next_cmd = 0;
-  
-  /* Read each line.  */
   while (get_line (file, cmdline, sizeof (cmdline)))
     {
-      grub_command_t cmd;
-
-      cmd = grub_command_find (cmdline);
-      grub_errno = GRUB_ERR_NONE;
+      struct grub_script *parsed_script;
       
-      if (cur_entry)
-	{
-	  if (! cmd || ! (cmd->flags & GRUB_COMMAND_FLAG_TITLE))
-	    {
-	      cur_cmd = (grub_command_list_t) grub_malloc (sizeof (*cur_cmd));
-	      if (! cur_cmd)
-		goto fail;
-	      
-	      cur_cmd->command = grub_strdup (cmdline);
-	      if (! cur_cmd->command)
-		{
-		  grub_free (cur_cmd);
-		  goto fail;
-		}
-	      
-	      cur_cmd->next = 0;
-	      
-	      *next_cmd = cur_cmd;
-	      next_cmd = &(cur_cmd->next);
-	      
-	      cur_entry->num++;
-	      continue;
-	    }
-	}
-      
-      if (! cmd)
-	{
-	  grub_printf ("Unknown command `%s' is ignored.\n", cmdline);
-	  continue;
-	}
+      currline++;
 
-      if (cmd->flags & GRUB_COMMAND_FLAG_TITLE)
-	{
-	  char *p;
-	  
-	  cur_entry = (grub_menu_entry_t) grub_malloc (sizeof (*cur_entry));
-	  if (! cur_entry)
-	    goto fail;
-
-	  p = grub_strchr (cmdline, ' ');
-	  if (p)
-	    cur_entry->title = grub_strdup (p);
-	  else
-	    cur_entry->title = grub_strdup ("");
-	  
-	  if (! cur_entry->title)
-	    {
-	      grub_free (cur_entry);
-	      goto fail;
-	    }
-	  
-	  cur_entry->num = 0;
-	  cur_entry->command_list = 0;
-	  cur_entry->next = 0;
-	  
-	  *next_entry = cur_entry;
-	  next_entry = &(cur_entry->next);
+      /* Execute the script, line for line.  */
+      parsed_script = grub_script_parse (cmdline, getline);
 
-	  next_cmd = &(cur_entry->command_list);
-	  
-	  menu->size++;
-	}
-      else
+      if (! parsed_script)
 	{
-	  /* Run the command if possible.  */
-	  if (cmd->flags & GRUB_COMMAND_FLAG_MENU)
-	    {
-	      grub_command_execute (cmdline, 0);
-	      if (grub_errno != GRUB_ERR_NONE)
-		{
-		  grub_print_error ();
-		  grub_errno = GRUB_ERR_NONE;
-		}
-	    }
-	  else
-	    {
-	      grub_printf ("Invalid command `%s' is ignored.\n", cmdline);
-	      continue;
-	    }
-	}
-    }
+	  /* Wait until the user pushes any key so that the user can
+	     see what happened.  */
+	  grub_printf ("\nPress any key to continue...");
+	  (void) grub_getkey ();
 
- fail:
+	  grub_file_close (file);
+	  return 0;
+	}
 
-  grub_file_close (file);
+      /* Execute the command(s).  */
+      grub_script_execute (parsed_script);
 
-  /* If no entry was found or any error occurred, return NULL.  */
-  if (menu->size == 0 || grub_errno != GRUB_ERR_NONE)
-    {
-      grub_context_pop_menu ();
-      free_menu (menu);
-      return 0;
+      /* The parsed script was executed, throw it away.  */
+      grub_script_free (parsed_script);
     }
 
-  /* Check values of the default entry and the fallback one.  */
-  if (menu->fallback_entry >= menu->size)
-    menu->fallback_entry = -1;
+  return newmenu;
 
-  if (menu->default_entry < 0 || menu->default_entry >= menu->size)
-    {
-      if (menu->fallback_entry < 0)
-	menu->default_entry = 0;
-      else
-	{
-	  menu->default_entry = menu->fallback_entry;
-	  menu->fallback_entry = -1;
-	}
-    }
-  
-  return menu;
+  grub_file_close (file);
+  return 0;
 }
 
 /* This starts the normal mode.  */
Index: normal/menu.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/menu.c,v
retrieving revision 1.14
diff -u -p -u -p -r1.14 menu.c
--- normal/menu.c	21 Aug 2005 07:22:51 -0000	1.14
+++ normal/menu.c	5 Jan 2006 17:14:06 -0000
@@ -347,17 +347,7 @@ run_menu (grub_menu_t menu, int nested)
 static void
 run_menu_entry (grub_menu_entry_t entry)
 {
-  grub_command_list_t cl;
-
-  for (cl = entry->command_list; cl != 0; cl = cl->next)
-    {
-      if (cl->command[0] == '\0')
-	/* Ignore an empty command line.  */
-	continue;
-      
-      if (grub_command_execute (cl->command, 0) != 0)
-	break;
-    }
+  grub_script_execute (entry->commands);
   
   if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
     /* Implicit execution of boot, only if something is loaded.  */
Index: normal/menu_entry.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/menu_entry.c,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 menu_entry.c
--- normal/menu_entry.c	28 Aug 2005 17:01:16 -0000	1.4
+++ normal/menu_entry.c	5 Jan 2006 17:14:06 -0000
@@ -413,7 +413,6 @@ static struct screen *
 make_screen (grub_menu_entry_t entry)
 {
   struct screen *screen;
-  grub_command_list_t cl;
 
   /* Initialize the screen.  */
   screen = grub_malloc (sizeof (*screen));
@@ -435,16 +434,8 @@ make_screen (grub_menu_entry_t entry)
   /* Initialize the first line which must be always present.  */
   if (! init_line (screen->lines))
     goto fail;
-  
-  /* Input the entry.  */
-  for (cl = entry->command_list; cl; cl = cl->next)
-    {
-      if (! insert_string (screen, cl->command, 0))
-	goto fail;
-      
-      if (! insert_string (screen, "\n", 0))
-	goto fail;
-    }
+
+  insert_string (screen, (char *) entry->sourcecode, 0);
 
   /* Reset the cursor position.  */
   screen->column = 0;
@@ -979,38 +970,61 @@ clear_completions (void)
 static int
 run (struct screen *screen)
 {
-  int i;
-  
-  grub_cls ();
-  grub_printf ("  Booting a command list\n\n");
-  
-  for (i = 0; i < screen->num_lines; i++)
+  struct grub_script *parsed_script = 0;
+  int currline = 0;
+  char *nextline;
+
+  auto grub_err_t editor_getline (char **line);
+  grub_err_t editor_getline (char **line)
     {
-      struct line *linep = screen->lines + i;
+      struct line *linep = screen->lines + currline;
       char *p;
-      
+
+      if (currline > screen->num_lines)
+	{
+	  *line = 0;
+	  return 0;
+	}
+
       /* Trim down space characters.  */
       for (p = linep->buf + linep->len - 1;
 	   p >= linep->buf && grub_isspace (*p);
 	   p--)
 	;
       *++p = '\0';
+
       linep->len = p - linep->buf;
-      
       for (p = linep->buf; grub_isspace (*p); p++)
 	;
-      
-      if (*p == '\0')
-	/* Ignore an empty command line.  */
-	continue;
+      *line = p;
+      currline++;
+      return 0;
+    }
+  
+  grub_cls ();
+  grub_printf ("  Booting a command list\n\n");
+
+
+  /* Execute the script, line for line.  */
+  while (currline < screen->num_lines - 1)
+    {
+      editor_getline (&nextline);
+      parsed_script = grub_script_parse (nextline, editor_getline);
+      if (parsed_script)
+	{
+	  /* Execute the command(s).  */
+	  grub_script_execute (parsed_script);
+	  
+	  /* The parsed script was executed, throw it away.  */
+	  grub_script_free (parsed_script);
 
-      if (grub_command_execute (p, 0) != 0)
+	  if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
+	    /* Implicit execution of boot, only if something is loaded.  */
+	    grub_command_execute ("boot", 0);
+	}
+      else
 	break;
     }
-  
-  if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
-    /* Implicit execution of boot, only if something is loaded.  */
-    grub_command_execute ("boot", 0);
 
   if (grub_errno != GRUB_ERR_NONE)
     {
@@ -1021,7 +1035,7 @@ run (struct screen *screen)
       grub_printf ("\nPress any key to continue...");
       (void) grub_getkey ();
     }
-  
+
   return 1;
 }
 
Index: normal/parser.y
===================================================================
RCS file: /cvsroot/grub/grub2/normal/parser.y,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 parser.y
--- normal/parser.y	6 Nov 2005 22:19:59 -0000	1.1
+++ normal/parser.y	5 Jan 2006 17:14:06 -0000
@@ -28,6 +28,8 @@
 /* Keep track of the memory allocated for this specific function.  */
 static struct grub_script_mem *func_mem = 0;
 
+static char *menu_entry = 0;
+
 %}
 
 %union {
@@ -40,12 +42,13 @@ static struct grub_script_mem *func_mem 
 %token GRUB_PARSER_TOKEN_IF		"if"
 %token GRUB_PARSER_TOKEN_WHILE		"while"
 %token GRUB_PARSER_TOKEN_FUNCTION	"function"
+%token GRUB_PARSER_TOKEN_MENUENTRY	"menuentry"
 %token GRUB_PARSER_TOKEN_ELSE		"else"
 %token GRUB_PARSER_TOKEN_THEN		"then"
 %token GRUB_PARSER_TOKEN_FI		"fi"
 %token GRUB_PARSER_TOKEN_NAME
 %token GRUB_PARSER_TOKEN_VAR
-%type <cmd> script grubcmd command commands if
+%type <cmd> script grubcmd command commands menuentry if
 %type <arglist> arguments;
 %type <arg> argument;
 %type <string> "if" "while" "function" "else" "then" "fi"
@@ -53,7 +56,7 @@ static struct grub_script_mem *func_mem 
 
 %%
 /* It should be possible to do this in a clean way...  */
-script:		commands '\n'
+script:		commands returns
 		  {
 		    grub_script_parsed = $1;
 		  }
@@ -127,6 +130,7 @@ grubcmd:	ws GRUB_PARSER_TOKEN_NAME ' ' a
 command:	grubcmd 	{ $$ = $1; }
 		| if 		{ $$ = $1; }
 		| function	{ $$ = 0;  }
+		| menuentry	{ $$ = $1;  }
 ;
 
 /* A block of commands.  */
@@ -172,6 +176,24 @@ function:	"function" ' ' GRUB_PARSER_TOK
 		  }
 ;
 
+/* A menu entry.  Carefully save the memory that is allocated.  */
+menuentry:	"menuentry" ' ' argument
+		  { 
+		    grub_script_lexer_ref ();
+		  } ws '{' returns
+		  { 
+		    /* Record sourcecode of the menu entry.  It can be
+		       parsed multiple times if it is part of a
+		       loop.  */
+		    grub_script_lexer_record_start ();
+		  } commands returns '}'
+		  {
+		    menu_entry = grub_script_lexer_record_stop ();
+		    $$ = grub_script_create_cmdmenu ($3, menu_entry, 0);
+		    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 (); }
@@ -183,7 +205,7 @@ if:		 if_statement grubcmd ';' ws "then"
 		    $$ = grub_script_create_cmdif ($2, $7, 0);
 		    grub_script_lexer_deref ();
 		  }
-		 | if_statement grubcmd ';' ws "then" returns commands returns "else" returns commands "fi"
+		 | if_statement grubcmd ';' ws "then" returns commands returns "else" returns commands returns  "fi"
 		  {
 		    $$ = grub_script_create_cmdif ($2, $7, $11);
 		    grub_script_lexer_deref ();
Index: normal/script.c
===================================================================
RCS file: /cvsroot/grub/grub2/normal/script.c,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 script.c
--- normal/script.c	6 Nov 2005 22:19:59 -0000	1.1
+++ normal/script.c	5 Jan 2006 17:14:06 -0000
@@ -201,6 +201,37 @@ grub_script_create_cmdif (struct grub_sc
   return (struct grub_script_cmd *) cmd;
 }
 
+/* Create a command that adds a menu entry to the menu.  Title is an
+   argument that is parsed to generate a string that can be used as
+   the title.  The sourcecode for this entry is passed in SOURCECODE.
+   The options for this entry are passed in OPTIONS.  */
+struct grub_script_cmd *
+grub_script_create_cmdmenu (struct grub_script_arg *title,
+			    char *sourcecode,
+			    int options)
+{
+  struct grub_script_cmd_menuentry *cmd;
+  int i;
+
+  /* Having trailing returns can some some annoying conflicts, remove
+     them.  XXX: Can the parser be improved to handle this?  */
+  for (i = grub_strlen (sourcecode) - 1; i > 0; i--)
+    {
+      if (sourcecode[i] != '\n')
+	break;
+      sourcecode[i] = '\0';
+    }
+
+  cmd = grub_script_malloc (sizeof (*cmd));
+  cmd->cmd.exec = grub_script_execute_menuentry;
+  cmd->cmd.next = 0;
+  cmd->sourcecode = sourcecode;
+  cmd->title = title;
+  cmd->options = options;
+ 
+  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.  */




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

* Re: Feature request: MBR backup
  2006-01-05 23:04 ` Feature request: MBR backup wobble
@ 2006-01-05 22:30   ` Marco Gerards
  0 siblings, 0 replies; 3+ messages in thread
From: Marco Gerards @ 2006-01-05 22:30 UTC (permalink / raw)
  To: The development of GRUB 2

wobble@gmx.de writes:

Hi,

> I'd like to propose a backup-mechanism for GRUB2. 
> On installation the old MBR could easily be stored in the partition hosting 
> the stage2. 
> That would allow for a clean self-destruction of GRUB2 as it could simply 
> overwrite its own MBR-data if required. 
>
> I guess many people install grub to play a little with linux/fbsd/etc and 
> afterwards have a messed-up MBR and they want their Windows back. Such a 
> feature would prevent a lot of support-requests I believe.

You are right, it might be a nice feature to add.  I think it would be
quite easy to add an uninstall feature that does just this.  I'll add
it to the todo list on the wiki.

Thanks,
Marco




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

* Feature request: MBR backup
  2006-01-05 17:25 The menu and scripting Marco Gerards
@ 2006-01-05 23:04 ` wobble
  2006-01-05 22:30   ` Marco Gerards
  0 siblings, 1 reply; 3+ messages in thread
From: wobble @ 2006-01-05 23:04 UTC (permalink / raw)
  To: grub-devel

Hello everyone, 

I'd like to propose a backup-mechanism for GRUB2. 
On installation the old MBR could easily be stored in the partition hosting 
the stage2. 
That would allow for a clean self-destruction of GRUB2 as it could simply 
overwrite its own MBR-data if required. 

I guess many people install grub to play a little with linux/fbsd/etc and 
afterwards have a messed-up MBR and they want their Windows back. Such a 
feature would prevent a lot of support-requests I believe.



Cheers

Jan



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

end of thread, other threads:[~2006-01-05 22:32 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-01-05 17:25 The menu and scripting Marco Gerards
2006-01-05 23:04 ` Feature request: MBR backup wobble
2006-01-05 22:30   ` Marco Gerards

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