diff options
Diffstat (limited to 'grub-core/script/script.c')
-rw-r--r-- | grub-core/script/script.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/grub-core/script/script.c b/grub-core/script/script.c new file mode 100644 index 0000000..ec4d433 --- /dev/null +++ b/grub-core/script/script.c @@ -0,0 +1,396 @@ +/* script.c -- Functions to create an in memory description of the script. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/misc.h> +#include <grub/script_sh.h> +#include <grub/parser.h> +#include <grub/mm.h> + +/* It is not possible to deallocate the memory when a syntax error was + found. Because of that it is required to keep track of all memory + allocations. The memory is freed in case of an error, or assigned + to the parsed script when parsing was successful. + + In case of the normal malloc, some additional bytes are allocated + for this datastructure. All reserved memory is stored in a linked + list so it can be easily freed. The original memory can be found + from &mem. */ +struct grub_script_mem +{ + struct grub_script_mem *next; + char mem; +}; + +/* Return malloc'ed memory and keep track of the allocation. */ +void * +grub_script_malloc (struct grub_parser_param *state, grub_size_t size) +{ + struct grub_script_mem *mem; + mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem) + - sizeof (char)); + if (!mem) + return 0; + + grub_dprintf ("scripting", "malloc %p\n", mem); + mem->next = state->memused; + state->memused = mem; + return (void *) &mem->mem; +} + +/* Free all memory described by MEM. */ +void +grub_script_mem_free (struct grub_script_mem *mem) +{ + struct grub_script_mem *memfree; + + while (mem) + { + memfree = mem->next; + grub_dprintf ("scripting", "free %p\n", mem); + grub_free (mem); + mem = memfree; + } +} + +/* Start recording memory usage. Returns the memory that should be + restored when calling stop. */ +struct grub_script_mem * +grub_script_mem_record (struct grub_parser_param *state) +{ + struct grub_script_mem *mem = state->memused; + state->memused = 0; + + return mem; +} + +/* Stop recording memory usage. Restore previous recordings using + RESTORE. Return the recorded memory. */ +struct grub_script_mem * +grub_script_mem_record_stop (struct grub_parser_param *state, + struct grub_script_mem *restore) +{ + struct grub_script_mem *mem = state->memused; + state->memused = restore; + return mem; +} + +/* Free the memory reserved for CMD and all of it's children. */ +void +grub_script_free (struct grub_script *script) +{ + struct grub_script *s; + struct grub_script *t; + + if (! script) + return; + + if (script->mem) + grub_script_mem_free (script->mem); + + s = script->children; + while (s) { + t = s->next_siblings; + grub_script_unref (s); + s = t; + } + grub_free (script); +} + + + +/* Extend the argument arg with a variable or string of text. If ARG + is zero a new list is created. */ +struct grub_script_arg * +grub_script_arg_add (struct grub_parser_param *state, + struct grub_script_arg *arg, grub_script_arg_type_t type, + char *str) +{ + struct grub_script_arg *argpart; + struct grub_script_arg *ll; + int len; + + argpart = + (struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg)); + if (!argpart) + return arg; + + argpart->type = type; + argpart->script = 0; + + len = grub_strlen (str) + 1; + argpart->str = grub_script_malloc (state, len); + if (!argpart->str) + return arg; /* argpart is freed later, during grub_script_free. */ + + grub_memcpy (argpart->str, str, len); + argpart->next = 0; + + if (!arg) + return argpart; + + for (ll = arg; ll->next; ll = ll->next); + ll->next = argpart; + + return arg; +} + +/* Add the argument ARG to the end of the argument list LIST. If LIST + is zero, a new list will be created. */ +struct grub_script_arglist * +grub_script_add_arglist (struct grub_parser_param *state, + struct grub_script_arglist *list, + struct grub_script_arg *arg) +{ + struct grub_script_arglist *link; + struct grub_script_arglist *ll; + + grub_dprintf ("scripting", "arglist\n"); + + link = + (struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link)); + if (!link) + return list; + + link->next = 0; + link->arg = arg; + link->argcount = 0; + + if (!list) + { + link->argcount++; + return link; + } + + list->argcount++; + + /* Look up the last link in the chain. */ + for (ll = list; ll->next; ll = ll->next); + ll->next = link; + + return list; +} + +/* Create a command that describes a single command line. CMDLINE + contains the name of the command that should be executed. ARGLIST + holds all arguments for this command. */ +struct grub_script_cmd * +grub_script_create_cmdline (struct grub_parser_param *state, + struct grub_script_arglist *arglist) +{ + struct grub_script_cmdline *cmd; + + grub_dprintf ("scripting", "cmdline\n"); + + cmd = grub_script_malloc (state, sizeof (*cmd)); + if (!cmd) + return 0; + + cmd->cmd.exec = grub_script_execute_cmdline; + cmd->cmd.next = 0; + cmd->arglist = arglist; + + return (struct grub_script_cmd *) cmd; +} + +/* Create a command that functions as an if statement. If BOOL is + evaluated to true (the value is returned in envvar '?'), the + interpreter will run the command TRUE, otherwise the interpreter + runs the command FALSE. */ +struct grub_script_cmd * +grub_script_create_cmdif (struct grub_parser_param *state, + struct grub_script_cmd *exec_to_evaluate, + struct grub_script_cmd *exec_on_true, + struct grub_script_cmd *exec_on_false) +{ + struct grub_script_cmdif *cmd; + + grub_dprintf ("scripting", "cmdif\n"); + + cmd = grub_script_malloc (state, sizeof (*cmd)); + if (!cmd) + return 0; + + cmd->cmd.exec = grub_script_execute_cmdif; + cmd->cmd.next = 0; + cmd->exec_to_evaluate = exec_to_evaluate; + cmd->exec_on_true = exec_on_true; + cmd->exec_on_false = exec_on_false; + + return (struct grub_script_cmd *) cmd; +} + +/* Create a command that functions as a for statement. */ +struct grub_script_cmd * +grub_script_create_cmdfor (struct grub_parser_param *state, + struct grub_script_arg *name, + struct grub_script_arglist *words, + struct grub_script_cmd *list) +{ + struct grub_script_cmdfor *cmd; + + grub_dprintf ("scripting", "cmdfor\n"); + + cmd = grub_script_malloc (state, sizeof (*cmd)); + if (! cmd) + return 0; + + cmd->cmd.exec = grub_script_execute_cmdfor; + cmd->cmd.next = 0; + cmd->name = name; + cmd->words = words; + cmd->list = list; + + return (struct grub_script_cmd *) cmd; +} + +/* Create a "while" or "until" command. */ +struct grub_script_cmd * +grub_script_create_cmdwhile (struct grub_parser_param *state, + struct grub_script_cmd *cond, + struct grub_script_cmd *list, + int is_an_until_loop) +{ + struct grub_script_cmdwhile *cmd; + + cmd = grub_script_malloc (state, sizeof (*cmd)); + if (! cmd) + return 0; + + cmd->cmd.exec = grub_script_execute_cmdwhile; + cmd->cmd.next = 0; + cmd->cond = cond; + cmd->list = list; + cmd->until = is_an_until_loop; + + return (struct grub_script_cmd *) cmd; +} + +/* Create a chain of commands. LAST contains the command that should + be added at the end of LIST's list. If LIST is zero, a new list + will be created. */ +struct grub_script_cmd * +grub_script_append_cmd (struct grub_parser_param *state, + struct grub_script_cmd *list, + struct grub_script_cmd *last) +{ + struct grub_script_cmd *ptr; + + grub_dprintf ("scripting", "append command\n"); + + if (! last) + return list; + + if (! list) + { + list = grub_script_malloc (state, sizeof (*list)); + if (! list) + return 0; + + list->exec = grub_script_execute_cmdlist; + list->next = last; + } + else + { + ptr = list; + while (ptr->next) + ptr = ptr->next; + + ptr->next = last; + } + + return list; +} + + + +struct grub_script * +grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem) +{ + struct grub_script *parsed; + + parsed = grub_malloc (sizeof (*parsed)); + if (! parsed) + return 0; + + parsed->mem = mem; + parsed->cmd = cmd; + parsed->refcnt = 0; + parsed->children = 0; + parsed->next_siblings = 0; + + return parsed; +} + +/* Parse the script passed in SCRIPT and return the parsed + datastructure that is ready to be interpreted. */ +struct grub_script * +grub_script_parse (char *script, + grub_reader_getline_t getline, void *getline_data) +{ + struct grub_script *parsed; + struct grub_script_mem *membackup; + struct grub_lexer_param *lexstate; + struct grub_parser_param *parsestate; + + parsed = grub_script_create (0, 0); + if (!parsed) + return 0; + + parsestate = grub_zalloc (sizeof (*parsestate)); + if (!parsestate) + { + grub_free (parsed); + return 0; + } + + /* Initialize the lexer. */ + lexstate = grub_script_lexer_init (parsestate, script, + getline, getline_data); + if (!lexstate) + { + grub_free (parsed); + grub_free (parsestate); + return 0; + } + + parsestate->lexerstate = lexstate; + + membackup = grub_script_mem_record (parsestate); + + /* Parse the script. */ + if (grub_script_yyparse (parsestate) || parsestate->err) + { + struct grub_script_mem *memfree; + memfree = grub_script_mem_record_stop (parsestate, membackup); + grub_script_mem_free (memfree); + grub_script_lexer_fini (lexstate); + grub_free (parsestate); + grub_free (parsed); + return 0; + } + + parsed->mem = grub_script_mem_record_stop (parsestate, membackup); + parsed->cmd = parsestate->parsed; + parsed->children = parsestate->scripts; + + grub_script_lexer_fini (lexstate); + grub_free (parsestate); + + return parsed; +} |