All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marco Gerards <mgerards@xs4all.nl>
To: grub-devel@gnu.org
Subject: The menu and scripting
Date: Thu, 05 Jan 2006 18:25:54 +0100	[thread overview]
Message-ID: <87oe2qegd9.fsf@xs4all.nl> (raw)

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.  */




             reply	other threads:[~2006-01-05 17:27 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-01-05 17:25 Marco Gerards [this message]
2006-01-05 23:04 ` Feature request: MBR backup wobble
2006-01-05 22:30   ` Marco Gerards

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87oe2qegd9.fsf@xs4all.nl \
    --to=mgerards@xs4all.nl \
    --cc=grub-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.