[Top][All Lists]
[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 = ¤t_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. */
- The menu and scripting,
Marco Gerards <=