From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with archive (Exim 4.43) id 1EuYu6-0004Qt-Vm for mharc-grub-devel@gnu.org; Thu, 05 Jan 2006 12:27:59 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1EuYu3-0004OP-KC for grub-devel@gnu.org; Thu, 05 Jan 2006 12:27:55 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1EuYu0-0004Lw-Ro for grub-devel@gnu.org; Thu, 05 Jan 2006 12:27:54 -0500 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1EuYty-0004LW-6x for grub-devel@gnu.org; Thu, 05 Jan 2006 12:27:52 -0500 Received: from [194.109.24.27] (helo=smtp-vbr7.xs4all.nl) by monty-python.gnu.org with esmtp (Exim 4.34) id 1EuYvZ-00031w-8c for grub-devel@gnu.org; Thu, 05 Jan 2006 12:29:29 -0500 Received: from localhost.localdomain (mgerards.xs4all.nl [82.92.27.129]) by smtp-vbr7.xs4all.nl (8.13.3/8.13.3) with ESMTP id k05HPrKE035652 for ; Thu, 5 Jan 2006 18:25:54 +0100 (CET) (envelope-from mgerards@xs4all.nl) Mail-Copies-To: mgerards@xs4all.nl To: grub-devel@gnu.org From: Marco Gerards Date: Thu, 05 Jan 2006 18:25:54 +0100 Message-ID: <87oe2qegd9.fsf@xs4all.nl> User-Agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Virus-Scanned: by XS4ALL Virus Scanner Subject: The menu and scripting X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: The development of GRUB 2 List-Id: The development of GRUB 2 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 Jan 2006 17:27:56 -0000 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 * include/grub/normal.h: Include . (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 and (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 #include #include +#include /* 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 #include @@ -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 #include #include +#include +#include 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 script grubcmd command commands if +%type script grubcmd command commands menuentry if %type arguments; %type argument; %type "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. */