menuentry option parsing is done using lib/arg.c

This commit is contained in:
BVK Chaitanya 2010-07-22 19:01:40 +05:30
parent 8527be0c5d
commit 639cc5ab44
7 changed files with 208 additions and 229 deletions

View file

@ -29,10 +29,8 @@ grub_extcmd_dispatcher (struct grub_command *cmd, int argc, char **args,
{
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;
@ -46,24 +44,20 @@ grub_extcmd_dispatcher (struct grub_command *cmd, int argc, char **args,
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

View file

@ -25,25 +25,156 @@
#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;
}
@ -54,7 +185,7 @@ GRUB_MOD_INIT(menuentry)
{
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)

View file

@ -38,6 +38,8 @@ typedef enum grub_arg_type grub_arg_type_t;
/* 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
{
@ -59,7 +61,10 @@ struct grub_arg_option
struct grub_arg_list
{
int set;
char *arg;
union {
char *arg;
char **args;
};
};
struct grub_extcmd;
@ -68,5 +73,7 @@ int grub_arg_parse (struct grub_extcmd *cmd, int argc, char **argv,
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 */

View file

@ -54,8 +54,6 @@ void grub_normal_execute (const char *config, int nested, int batch);
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);

View file

@ -207,8 +207,16 @@ parse_option (grub_extcmd_t cmd, int key, char *arg, struct grub_arg_list *usr)
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;
}
}
}
@ -229,13 +237,14 @@ grub_arg_parse (grub_extcmd_t cmd, int argc, char **argv,
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;
}
@ -312,8 +321,11 @@ grub_arg_parse (grub_extcmd_t cmd, int argc, char **argv,
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)
@ -392,3 +404,43 @@ grub_arg_parse (grub_extcmd_t cmd, int argc, char **argv,
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;
}

View file

@ -141,209 +141,6 @@ free_menu (grub_menu_t menu)
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)
{

View file

@ -104,8 +104,8 @@ grub_script_free (struct grub_script *script)
s = script->children;
while (s) {
grub_script_put (s);
s = s->siblings;
grub_script_put (s);
}
grub_free (script);
}