From 77c4a3939d539c1d674861ab9e4904d10734a4a2 Mon Sep 17 00:00:00 2001 From: marco_g Date: Tue, 17 Jan 2006 09:50:47 +0000 Subject: [PATCH] 2006-01-17 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. --- ChangeLog | 37 +++++++ include/grub/normal.h | 23 ++-- include/grub/script.h | 30 ++++++ normal/execute.c | 30 ++++++ normal/lexer.c | 91 ++++++++++++++-- normal/main.c | 241 +++++++++++++++--------------------------- normal/menu.c | 12 +-- normal/menu_entry.c | 76 +++++++------ normal/parser.y | 28 ++++- normal/script.c | 31 ++++++ 10 files changed, 378 insertions(+), 221 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1cd51f5e7..a27b70ec0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,40 @@ +2006-01-17 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. + 2006-01-03 Marco Gerards * INSTALL: GNU Bison is required. diff --git a/include/grub/normal.h b/include/grub/normal.h index 9dd7debb2..1862e272d 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -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); diff --git a/include/grub/script.h b/include/grub/script.h index 6ba0a4cd5..5108eee92 100644 --- a/include/grub/script.h +++ b/include/grub/script.h @@ -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 (struct grub_script_cmd *cmd, 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 *err); 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_function_find (char *functionname); 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 */ diff --git a/normal/execute.c b/normal/execute.c index 98d828e40..fe28876ae 100644 --- a/normal/execute.c +++ b/normal/execute.c @@ -191,6 +191,36 @@ grub_script_execute_cmdif (struct grub_script_cmd *cmd) 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. */ diff --git a/normal/lexer.c b/normal/lexer.c index 968fffa32..20e16dc9d 100644 --- a/normal/lexer.c +++ b/normal/lexer.c @@ -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; } diff --git a/normal/main.c b/normal/main.c index a1b1a684b..3394a33dc 100644 --- a/normal/main.c +++ b/normal/main.c @@ -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) + currline++; + + /* Execute the script, line for line. */ + parsed_script = grub_script_parse (cmdline, getline); + + if (! parsed_script) { - 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; + /* 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 (); + + grub_file_close (file); + return 0; } - 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; + /* Execute the command(s). */ + grub_script_execute (parsed_script); - 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); - - next_cmd = &(cur_entry->command_list); - - menu->size++; - } - else - { - /* 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; - } - } + /* The parsed script was executed, throw it away. */ + grub_script_free (parsed_script); } - fail: + return newmenu; grub_file_close (file); - - /* 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; - } - - /* Check values of the default entry and the fallback one. */ - if (menu->fallback_entry >= menu->size) - menu->fallback_entry = -1; - - 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; + return 0; } /* This starts the normal mode. */ diff --git a/normal/menu.c b/normal/menu.c index dd7fd1bd9..c32a7d987 100644 --- a/normal/menu.c +++ b/normal/menu.c @@ -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. */ diff --git a/normal/menu_entry.c b/normal/menu_entry.c index 987756c58..1690c77a9 100644 --- a/normal/menu_entry.c +++ b/normal/menu_entry.c @@ -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; - - if (grub_command_execute (p, 0) != 0) - break; + *line = p; + currline++; + return 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); + 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_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) { @@ -1021,7 +1035,7 @@ run (struct screen *screen) grub_printf ("\nPress any key to continue..."); (void) grub_getkey (); } - + return 1; } diff --git a/normal/parser.y b/normal/parser.y index 50fc7c21c..00491c4a2 100644 --- a/normal/parser.y +++ b/normal/parser.y @@ -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 = 0; %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 = 0; %% /* 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 ' ' arguments ws command: grubcmd { $$ = $1; } | if { $$ = $1; } | function { $$ = 0; } + | menuentry { $$ = $1; } ; /* A block of commands. */ @@ -172,6 +176,24 @@ function: "function" ' ' GRUB_PARSER_TOKEN_NAME } ; +/* 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" returns commands returns "fi" $$ = 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 (); diff --git a/normal/script.c b/normal/script.c index f14254eeb..bbab94fe2 100644 --- a/normal/script.c +++ b/normal/script.c @@ -201,6 +201,37 @@ grub_script_create_cmdif (struct grub_script_cmd *bool, 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. */