grub-devel
[Top][All Lists]
Advanced

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

The menu and scripting


From: Marco Gerards
Subject: The menu and scripting
Date: Thu, 05 Jan 2006 18:25:54 +0100
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

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

        * 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);
+}
+
 
 
 /* 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 via email to

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