{
int new_argc;
char **new_args;
- struct grub_arg_option *parser;
struct grub_arg_list *state;
struct grub_extcmd_context context;
- int maxargs = 0;
grub_err_t ret;
grub_extcmd_t ext = cmd->data;
return ret;
}
- parser = (struct grub_arg_option *) ext->options;
- while (parser && (parser++)->doc)
- maxargs++;
-
- /* Set up the option state. */
- state = grub_zalloc (sizeof (struct grub_arg_list) * maxargs);
-
+ state = grub_arg_list_alloc (ext, argc, args);
if (grub_arg_parse (ext, argc, args, state, &new_args, &new_argc))
{
context.state = state;
ret = (ext->func) (&context, new_argc, new_args);
grub_free (new_args);
+ grub_free (state);
+ return ret;
}
- else
- ret = grub_errno;
- grub_free (state);
- return ret;
+ if (state)
+ grub_free (state);
+
+ return grub_errno;
}
static grub_err_t
#include <grub/i18n.h>
#include <grub/normal.h>
+static const struct grub_arg_option options[] =
+ {
+ {"class", 1, GRUB_ARG_OPTION_REPEATABLE,
+ N_("Menu entry type."), "STRING", ARG_TYPE_STRING},
+ {"users", 2, 0,
+ N_("Users allowed to boot this entry."), "USERNAME", ARG_TYPE_STRING},
+ {"hotkey", 3, 0,
+ N_("Keyboard key for this entry."), "KEY", ARG_TYPE_STRING},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+static struct
+{
+ char *name;
+ int key;
+} hotkey_aliases[] =
+ {
+ {"backspace", '\b'},
+ {"tab", '\t'},
+ {"delete", GRUB_TERM_DC}
+ };
+
+/* Add a menu entry to the current menu context (as given by the environment
+ variable data slot `menu'). As the configuration file is read, the script
+ parser calls this when a menu entry is to be created. */
+static grub_err_t
+add_menu_entry (int argc, const char **args, char **classes,
+ const char *users, const char *hotkey,
+ const char *sourcecode)
+{
+ unsigned i;
+ int menu_hotkey = 0;
+ char *menu_users = NULL;
+ char *menu_title = NULL;
+ char *menu_sourcecode = NULL;
+ struct grub_menu_entry_class *menu_classes = NULL;
+
+ grub_menu_t menu;
+ grub_menu_entry_t *last;
+
+ menu = grub_env_get_menu ();
+ if (! menu)
+ return grub_error (GRUB_ERR_MENU, "no menu context");
+
+ last = &menu->entry_list;
+
+ menu_sourcecode = grub_strdup (sourcecode);
+ if (! menu_sourcecode)
+ return grub_errno;
+
+ if (classes)
+ {
+ for (i = 0; classes[i]; i++); /* count # of menuentry classes */
+ menu_classes = grub_zalloc (sizeof (struct grub_menu_entry_class) * i);
+ if (! menu_classes)
+ goto fail;
+
+ for (i = 0; classes[i]; i++)
+ {
+ menu_classes[i].name = grub_strdup (classes[i]);
+ if (! menu_classes[i].name)
+ goto fail;
+ menu_classes[i].next = classes[i + 1] ? &menu_classes[i + 1] : NULL;
+ }
+ }
+
+ if (users)
+ {
+ menu_users = grub_strdup (users);
+ if (! menu_users)
+ goto fail;
+ }
+
+ if (hotkey)
+ {
+ for (i = 0; i < ARRAY_SIZE (hotkey_aliases); i++)
+ if (grub_strcmp (hotkey, hotkey_aliases[i].name) == 0)
+ {
+ menu_hotkey = hotkey_aliases[i].key;
+ break;
+ }
+ if (i > ARRAY_SIZE (hotkey_aliases))
+ goto fail;
+ }
+
+ if (! argc)
+ {
+ grub_error (GRUB_ERR_MENU, "menuentry is missing title");
+ goto fail;
+ }
+
+ menu_title = grub_strdup (args[0]);
+ if (! menu_title)
+ goto fail;
+
+ /* XXX: pass args[1..end] as parameters to block arg. */
+
+ /* Add the menu entry at the end of the list. */
+ while (*last)
+ last = &(*last)->next;
+
+ *last = grub_zalloc (sizeof (**last));
+ if (! *last)
+ goto fail;
+
+ (*last)->title = menu_title;
+ (*last)->hotkey = menu_hotkey;
+ (*last)->classes = menu_classes;
+ if (menu_users)
+ (*last)->restricted = 1;
+ (*last)->users = menu_users;
+ (*last)->sourcecode = menu_sourcecode;
+
+ menu->size++;
+ return GRUB_ERR_NONE;
+
+ fail:
+
+ grub_free (menu_sourcecode);
+ for (i = 0; menu_classes && menu_classes[i].name; i++)
+ grub_free (menu_classes[i].name);
+ grub_free (menu_classes);
+ grub_free (menu_users);
+ grub_free (menu_title);
+ return grub_errno;
+}
+
static grub_err_t
grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
{
+ char ch;
char *src;
unsigned len;
grub_err_t r;
- /* XXX Rewrite to make use of already parsed menu definition. */
if (! argc || ! ctxt->script)
return GRUB_ERR_BAD_ARGUMENT;
src = args[argc - 1];
- args[argc - 1] = '\0';
+ args[argc - 1] = NULL;
+
len = grub_strlen(src);
+ ch = src[len - 1];
src[len - 1] = '\0';
- r = grub_normal_add_menu_entry (argc - 1, (const char **) args, src + 1);
+ r = add_menu_entry (argc - 1, (const char **) args,
+ ctxt->state[0].args, ctxt->state[1].arg,
+ ctxt->state[2].arg, src + 1);
- src[len - 1] = '}';
+ src[len - 1] = ch;
args[argc - 1] = src;
return r;
}
{
cmd = grub_register_extcmd ("menuentry", grub_cmd_menuentry,
GRUB_COMMAND_FLAG_BOTH | GRUB_COMMAND_FLAG_BLOCKS,
- N_("BLOCK"), N_("Define a menuentry."), 0);
+ N_("BLOCK"), N_("Define a menuentry."), options);
}
GRUB_MOD_FINI(menuentry)
/* Flags for the option field op grub_arg_option. */
#define GRUB_ARG_OPTION_OPTIONAL (1 << 1)
+/* Flags for an option that can appear multiple times. */
+#define GRUB_ARG_OPTION_REPEATABLE (1 << 2)
enum grub_key_type
{
struct grub_arg_list
{
int set;
- char *arg;
+ union {
+ char *arg;
+ char **args;
+ };
};
struct grub_extcmd;
struct grub_arg_list *usr, char ***args, int *argnum);
void grub_arg_show_help (struct grub_extcmd *cmd);
+struct grub_arg_list* grub_arg_list_alloc (struct grub_extcmd *cmd,
+ int argc, char *argv[]);
#endif /* ! GRUB_ARG_HEADER */
void grub_menu_init_page (int nested, int edit,
struct grub_term_output *term);
void grub_normal_init_page (struct grub_term_output *term);
-grub_err_t grub_normal_add_menu_entry (int argc, const char **args,
- const char *sourcecode);
char *grub_file_getline (grub_file_t file);
void grub_cmdline_run (int nested);
if (found == -1)
return -1;
- usr[found].set = 1;
- usr[found].arg = arg;
+ if (opt->flags & GRUB_ARG_OPTION_REPEATABLE)
+ {
+ usr[found].args[usr[found].set++] = arg;
+ usr[found].args[usr[found].set] = NULL;
+ }
+ else
+ {
+ usr[found].set = 1;
+ usr[found].arg = arg;
+ }
}
}
grub_err_t add_arg (char *s)
{
char **p = argl;
- argl = grub_realloc (argl, (++num) * sizeof (char *));
+ argl = grub_realloc (argl, (++num + 1) * sizeof (char *));
if (! argl)
{
grub_free (p);
return grub_errno;
}
argl[num - 1] = s;
+ argl[num] = NULL;
return 0;
}
if (option) {
arglen = option - arg - 2;
option++;
- } else
+ } else {
arglen = grub_strlen (arg) - 2;
+ if (argv[curarg + 1])
+ option = argv[curarg + 1][0] == '-' ? 0 : argv[++curarg];
+ }
opt = find_long (cmd->options, arg + 2, arglen);
if (! opt)
fail:
return complete;
}
+
+struct grub_arg_list*
+grub_arg_list_alloc(grub_extcmd_t extcmd, int argc,
+ char **argv __attribute__((unused)))
+{
+ int i;
+ char **args;
+ unsigned argcnt;
+ struct grub_arg_list *list;
+ const struct grub_arg_option *options;
+
+ options = extcmd->options;
+ if (! options)
+ return 0;
+
+ argcnt = 0;
+ for (i = 0; options[i].doc; i++)
+ {
+ if (options[i].flags & GRUB_ARG_OPTION_REPEATABLE)
+ argcnt += (argc + 1) / 2 + 1; /* max possible for any option */
+ }
+
+ list = grub_zalloc (sizeof (*list) * i + sizeof (char*) * argcnt);
+ if (! list)
+ return 0;
+
+ args = (char**) (list + i);
+ for (i = 0; options[i].doc; i++)
+ {
+ list[i].set = 0;
+ list[i].arg = 0;
+
+ if (options[i].flags & GRUB_ARG_OPTION_REPEATABLE)
+ {
+ list[i].args = args;
+ args += argc / 2 + 1;
+ }
+ }
+ return list;
+}
grub_env_unset_menu ();
}
-static void
-free_menu_entry_classes (struct grub_menu_entry_class *head)
-{
- /* Free all the classes. */
- while (head)
- {
- struct grub_menu_entry_class *next;
-
- grub_free (head->name);
- next = head->next;
- grub_free (head);
- head = next;
- }
-}
-
-static struct
-{
- char *name;
- int key;
-} hotkey_aliases[] =
- {
- {"backspace", '\b'},
- {"tab", '\t'},
- {"delete", GRUB_TERM_DC}
- };
-
-/* Add a menu entry to the current menu context (as given by the environment
- variable data slot `menu'). As the configuration file is read, the script
- parser calls this when a menu entry is to be created. */
-grub_err_t
-grub_normal_add_menu_entry (int argc, const char **args,
- const char *sourcecode)
-{
- const char *menutitle = 0;
- const char *menusourcecode;
- grub_menu_t menu;
- grub_menu_entry_t *last;
- int failed = 0;
- int i;
- struct grub_menu_entry_class *classes_head; /* Dummy head node for list. */
- struct grub_menu_entry_class *classes_tail;
- char *users = NULL;
- int hotkey = 0;
-
- /* Allocate dummy head node for class list. */
- classes_head = grub_zalloc (sizeof (struct grub_menu_entry_class));
- if (! classes_head)
- return grub_errno;
- classes_tail = classes_head;
-
- menu = grub_env_get_menu ();
- if (! menu)
- return grub_error (GRUB_ERR_MENU, "no menu context");
-
- last = &menu->entry_list;
-
- menusourcecode = grub_strdup (sourcecode);
- if (! menusourcecode)
- return grub_errno;
-
- /* Parse menu arguments. */
- for (i = 0; i < argc; i++)
- {
- /* Capture arguments. */
- if (grub_strncmp ("--", args[i], 2) == 0 && i + 1 < argc)
- {
- const char *arg = &args[i][2];
-
- /* Handle menu class. */
- if (grub_strcmp(arg, "class") == 0)
- {
- char *class_name;
- struct grub_menu_entry_class *new_class;
-
- i++;
- class_name = grub_strdup (args[i]);
- if (! class_name)
- {
- failed = 1;
- break;
- }
-
- /* Create a new class and add it at the tail of the list. */
- new_class = grub_zalloc (sizeof (struct grub_menu_entry_class));
- if (! new_class)
- {
- grub_free (class_name);
- failed = 1;
- break;
- }
- /* Fill in the new class node. */
- new_class->name = class_name;
- /* Link the tail to it, and make it the new tail. */
- classes_tail->next = new_class;
- classes_tail = new_class;
- continue;
- }
- else if (grub_strcmp(arg, "users") == 0)
- {
- i++;
- users = grub_strdup (args[i]);
- if (! users)
- {
- failed = 1;
- break;
- }
-
- continue;
- }
- else if (grub_strcmp(arg, "hotkey") == 0)
- {
- unsigned j;
-
- i++;
- if (args[i][1] == 0)
- {
- hotkey = args[i][0];
- continue;
- }
-
- for (j = 0; j < ARRAY_SIZE (hotkey_aliases); j++)
- if (grub_strcmp (args[i], hotkey_aliases[j].name) == 0)
- {
- hotkey = hotkey_aliases[j].key;
- break;
- }
-
- if (j < ARRAY_SIZE (hotkey_aliases))
- continue;
-
- failed = 1;
- grub_error (GRUB_ERR_MENU,
- "Invalid hotkey: '%s'.", args[i]);
- break;
- }
- else
- {
- /* Handle invalid argument. */
- failed = 1;
- grub_error (GRUB_ERR_MENU,
- "invalid argument for menuentry: %s", args[i]);
- break;
- }
- }
-
- /* Capture title. */
- if (! menutitle)
- {
- menutitle = grub_strdup (args[i]);
- }
- else
- {
- failed = 1;
- grub_error (GRUB_ERR_MENU,
- "too many titles for menuentry: %s", args[i]);
- break;
- }
- }
-
- /* Validate arguments. */
- if ((! failed) && (! menutitle))
- {
- grub_error (GRUB_ERR_MENU, "menuentry is missing title");
- failed = 1;
- }
-
- /* If argument parsing failed, free any allocated resources. */
- if (failed)
- {
- free_menu_entry_classes (classes_head);
- grub_free ((void *) menutitle);
- grub_free ((void *) menusourcecode);
-
- /* Here we assume that grub_error has been used to specify failure details. */
- return grub_errno;
- }
-
- /* Add the menu entry at the end of the list. */
- while (*last)
- last = &(*last)->next;
-
- *last = grub_zalloc (sizeof (**last));
- if (! *last)
- {
- free_menu_entry_classes (classes_head);
- grub_free ((void *) menutitle);
- grub_free ((void *) menusourcecode);
- return grub_errno;
- }
-
- (*last)->title = menutitle;
- (*last)->hotkey = hotkey;
- (*last)->classes = classes_head;
- if (users)
- (*last)->restricted = 1;
- (*last)->users = users;
- (*last)->sourcecode = menusourcecode;
-
- menu->size++;
-
- return GRUB_ERR_NONE;
-}
-
static grub_menu_t
read_config_file (const char *config)
{
s = script->children;
while (s) {
- grub_script_put (s);
s = s->siblings;
+ grub_script_put (s);
}
grub_free (script);
}