X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=blobdiff_plain;f=discover%2Fgrub2%2Fscript.c;h=0cf21967450efb3fbdf22c76f901c1c312b4d9d1;hp=8b008c4b91e919172858ec9864b3b6f23dff763c;hb=1972d9cfcc5300cd8cb2f587d9bc5f856322ae8f;hpb=5e1b3c27ab320eae17b9c9d75b4111c924860268 diff --git a/discover/grub2/script.c b/discover/grub2/script.c index 8b008c4..0cf2196 100644 --- a/discover/grub2/script.c +++ b/discover/grub2/script.c @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -9,10 +10,16 @@ #define to_stmt_simple(stmt) \ container_of(stmt, struct grub2_statement_simple, st) +#define to_stmt_block(stmt) \ + container_of(stmt, struct grub2_statement_block, st) #define to_stmt_if(stmt) \ container_of(stmt, struct grub2_statement_if, st) #define to_stmt_menuentry(stmt) \ container_of(stmt, struct grub2_statement_menuentry, st) +#define to_stmt_function(stmt) \ + container_of(stmt, struct grub2_statement_function, st) +#define to_stmt_conditional(stmt) \ + container_of(stmt, struct grub2_statement_conditional, st) struct env_entry { const char *name; @@ -20,6 +27,26 @@ struct env_entry { struct list_item list; }; +struct grub2_symtab_entry { + const char *name; + grub2_function fn; + void *data; + struct list_item list; +}; + +static struct grub2_symtab_entry *script_lookup_function( + struct grub2_script *script, const char *name) +{ + struct grub2_symtab_entry *entry; + + list_for_each_entry(&script->symtab, entry, list) { + if (!strcmp(entry->name, name)) + return entry; + } + + return NULL; +} + const char *script_env_get(struct grub2_script *script, const char *name) { struct env_entry *entry; @@ -70,6 +97,24 @@ static bool is_delim(char c) return c == ' ' || c == '\t'; } +static bool option_is_default(struct grub2_script *script, + struct discover_boot_option *opt) +{ + unsigned int default_idx; + const char *var; + char *end; + + var = script_env_get(script, "default"); + if (!var) + return false; + + default_idx = strtoul(var, &end, 10); + if (end != var && *end == '\0') + return default_idx == script->n_options; + + return !strcmp(opt->option->name, var); +} + /* For non-double-quoted variable expansions, we may need to split the * variable's value into multiple argv items. * @@ -208,7 +253,8 @@ int statement_simple_execute(struct grub2_script *script, struct grub2_statement *statement) { struct grub2_statement_simple *st = to_stmt_simple(statement); - struct grub2_command *cmd; + struct grub2_symtab_entry *entry; + char *pos; int rc; if (!st->argv) @@ -219,35 +265,67 @@ int statement_simple_execute(struct grub2_script *script, if (!st->argv->argc) return 0; - cmd = script_lookup_command(script, st->argv->argv[0]); - if (!cmd) { - fprintf(stderr, "undefined command '%s'\n", st->argv->argv[0]); + /* is this a var=value assignment? */ + pos = strchr(st->argv->argv[0], '='); + if (pos) { + char *name, *value; + name = st->argv->argv[0]; + name = talloc_strndup(st, name, pos - name); + value = pos + 1; + script_env_set(script, name, value); return 0; } - rc = cmd->exec(script, st->argv->argc, st->argv->argv); + entry = script_lookup_function(script, st->argv->argv[0]); + if (!entry) { + fprintf(stderr, "undefined function '%s'\n", st->argv->argv[0]); + return 1; + } + + rc = entry->fn(script, entry->data, st->argv->argc, st->argv->argv); return rc; } -int statement_if_execute(struct grub2_script *script, +int statement_block_execute(struct grub2_script *script, struct grub2_statement *statement) { - struct grub2_statement_if *st = to_stmt_if(statement); - struct grub2_statements *case_stmts; + struct grub2_statement_block *st = to_stmt_block(statement); + return statements_execute(script, st->statements); +} + +/* returns 0 if the statement was executed, 1 otherwise */ +static int statement_conditional_execute(struct grub2_script *script, + struct grub2_statement *statement, bool *executed) +{ + struct grub2_statement_conditional *st = to_stmt_conditional(statement); int rc; rc = st->condition->exec(script, st->condition); + *executed = (!rc); + if (*executed) + rc = statements_execute(script, st->statements); + + return rc; +} + +int statement_if_execute(struct grub2_script *script, + struct grub2_statement *statement) +{ + struct grub2_statement_if *st = to_stmt_if(statement); + struct grub2_statement *conditional; + bool executed; + int rc; - if (rc == 0) - case_stmts = st->true_case; - else - case_stmts = st->false_case; + list_for_each_entry(&st->conditionals->list, conditional, list) { + rc = statement_conditional_execute(script, + conditional, &executed); + if (executed) + break; + } - if (case_stmts) - statements_execute(script, case_stmts); - else - rc = 0; + if (!executed && st->else_case) + rc = statements_execute(script, st->else_case); return rc; } @@ -266,17 +344,54 @@ int statement_menuentry_execute(struct grub2_script *script, } else { opt->option->name = talloc_strdup(opt, "(unknown)"); } + opt->option->id = talloc_asprintf(opt->option, "%s#%s", + script->ctx->device->device->id, + opt->option->name); script->opt = opt; statements_execute(script, st->statements); + opt->option->is_default = option_is_default(script, opt); + discover_context_add_boot_option(script->ctx, opt); + script->n_options++; script->opt = NULL; return 0; } +static int function_invoke(struct grub2_script *script, + void *data, int argc, char **argv) +{ + struct grub2_statement_function *fn = data; + char *name; + int i; + + /* set positional parameters */ + for (i = 0; i < argc; i++) { + name = talloc_asprintf(script, "$%d", i); + script_env_set(script, name, argv[i]); + } + + return statements_execute(script, fn->body); +} + +int statement_function_execute(struct grub2_script *script, + struct grub2_statement *statement) +{ + struct grub2_statement_function *st = to_stmt_function(statement); + const char *name; + + if (st->name->type == GRUB2_WORD_VAR) + expand_var(script, st->name); + + name = st->name->text; + script_register_function(script, name, function_invoke, st); + + return 0; +} + static void init_env(struct grub2_script *script) { struct env_entry *env; @@ -290,23 +405,17 @@ static void init_env(struct grub2_script *script) list_add(&script->environment, &env->list); } -struct grub2_command *script_lookup_command(struct grub2_script *script, - const char *name) +void script_register_function(struct grub2_script *script, + const char *name, grub2_function fn, + void *data) { - struct grub2_command *command; + struct grub2_symtab_entry *entry; - list_for_each_entry(&script->commands, command, list) { - if (!strcmp(command->name, name)) - return command; - } - - return NULL; -} - -void script_register_command(struct grub2_script *script, - struct grub2_command *command) -{ - list_add(&script->commands, &command->list); + entry = talloc(script, struct grub2_symtab_entry); + entry->fn = fn; + entry->name = name; + entry->data = data; + list_add(&script->symtab, &entry->list); } @@ -320,13 +429,12 @@ struct grub2_script *create_script(struct grub2_parser *parser, { struct grub2_script *script; - script = talloc(parser, struct grub2_script); + script = talloc_zero(parser, struct grub2_script); init_env(script); script->ctx = ctx; - script->opt = NULL; - list_init(&script->commands); + list_init(&script->symtab); register_builtins(script); return script;