pull-in block-arg again

This commit is contained in:
BVK Chaitanya 2010-07-22 05:34:38 +05:30
commit 8527be0c5d
6 changed files with 79 additions and 5 deletions

View file

@ -42,6 +42,10 @@ struct grub_script
unsigned refcnt; unsigned refcnt;
struct grub_script_mem *mem; struct grub_script_mem *mem;
struct grub_script_cmd *cmd; struct grub_script_cmd *cmd;
/* Other grub_script's from block arguments. */
struct grub_script *siblings;
struct grub_script *children;
}; };
typedef enum typedef enum
@ -207,6 +211,9 @@ struct grub_parser_param
/* The memory that was used while parsing and scanning. */ /* The memory that was used while parsing and scanning. */
struct grub_script_mem *memused; struct grub_script_mem *memused;
/* The block argument scripts. */
struct grub_script *scripts;
/* The result of the parser. */ /* The result of the parser. */
struct grub_script_cmd *parsed; struct grub_script_cmd *parsed;
@ -331,6 +338,7 @@ grub_script_function_t grub_script_function_create (struct grub_script_arg *func
struct grub_script *cmd); struct grub_script *cmd);
void grub_script_function_remove (const char *name); void grub_script_function_remove (const char *name);
grub_script_function_t grub_script_function_find (char *functionname); grub_script_function_t grub_script_function_find (char *functionname);
grub_err_t grub_script_function_call (grub_script_function_t func, grub_err_t grub_script_function_call (grub_script_function_t func,
int argc, char **args); int argc, char **args);
@ -340,6 +348,7 @@ grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *c
static inline struct grub_script * static inline struct grub_script *
grub_script_get (struct grub_script *script) grub_script_get (struct grub_script *script)
{ {
if (script)
script->refcnt++; script->refcnt++;
return script; return script;
} }
@ -347,6 +356,9 @@ grub_script_get (struct grub_script *script)
static inline void static inline void
grub_script_put (struct grub_script *script) grub_script_put (struct grub_script *script)
{ {
if (! script)
return;
if (script->refcnt == 0) if (script->refcnt == 0)
grub_script_free (script); grub_script_free (script);
else else

View file

@ -52,9 +52,12 @@ grub_script_argv_free (struct grub_script_argv *argv)
grub_free (argv->args); grub_free (argv->args);
} }
if (argv->script)
grub_script_put (argv->script);
argv->argc = 0; argv->argc = 0;
argv->args = 0; argv->args = 0;
argv->script = 0;
} }
/* Prepare for next argc. */ /* Prepare for next argc. */

View file

@ -201,7 +201,7 @@ grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
grub_script_argv_append (&result, arg->str) || grub_script_argv_append (&result, arg->str) ||
grub_script_argv_append (&result, "}")) grub_script_argv_append (&result, "}"))
goto fail; goto fail;
result.script = arg->script; result.script = grub_script_get (arg->script);
break; break;
case GRUB_SCRIPT_ARG_TYPE_TEXT: case GRUB_SCRIPT_ARG_TYPE_TEXT:

View file

@ -34,7 +34,7 @@ grub_normal_parse_line (char *line, grub_reader_getline_t getline)
grub_script_execute (parsed_script); grub_script_execute (parsed_script);
/* The parsed script was executed, throw it away. */ /* The parsed script was executed, throw it away. */
grub_script_free (parsed_script); grub_script_put (parsed_script);
} }
return grub_errno; return grub_errno;

View file

@ -38,6 +38,7 @@
struct { struct {
unsigned offset; unsigned offset;
struct grub_script_mem *memory; struct grub_script_mem *memory;
struct grub_script *scripts;
}; };
} }
@ -149,16 +150,45 @@ argument : "case" { $$ = grub_script_add_arglist (state, 0, $1); }
| word { $$ = $1; } | word { $$ = $1; }
; ;
/*
Block parameter is passed to commands in two forms: as unparsed
string and as pre-parsed grub_script object. Passing as grub_script
object makes memory management difficult, because:
(1) Command may want to keep a reference to grub_script objects for
later use, so script framework may not free the grub_script
object after command completes.
(2) Command may get called multiple times with same grub_script
object under loops, so we should not let command implementation
to free the grub_script object.
To solve above problems, we rely on reference counting for
grub_script objects. Commands that want to keep the grub_script
object must take a reference to it.
Other complexity comes with arbitrary nesting of grub_script
objects: a grub_script object may have commands with several block
parameters, and each block parameter may further contain multiple
block parameters nested. We use temporary variable, state->scripts
to collect nested child scripts (that are linked by siblings and
children members), and will build grub_scripts tree from bottom.
*/
block: "{" block: "{"
{ {
grub_script_lexer_ref (state->lexerstate); grub_script_lexer_ref (state->lexerstate);
$<offset>$ = grub_script_lexer_record_start (state); $<offset>$ = grub_script_lexer_record_start (state);
$<memory>$ = grub_script_mem_record (state); $<memory>$ = grub_script_mem_record (state);
/* save currently known scripts. */
$<scripts>$ = state->scripts;
state->scripts = 0;
} }
commands1 delimiters0 "}" commands1 delimiters0 "}"
{ {
char *p; char *p;
struct grub_script_mem *memory; struct grub_script_mem *memory;
struct grub_script *s = $<scripts>2;
memory = grub_script_mem_record_stop (state, $<memory>2); memory = grub_script_mem_record_stop (state, $<memory>2);
if ((p = grub_script_lexer_record_stop (state, $<offset>2))) if ((p = grub_script_lexer_record_stop (state, $<offset>2)))
@ -168,6 +198,19 @@ block: "{"
if (! $$ || ! ($$->script = grub_script_create ($3, memory))) if (! $$ || ! ($$->script = grub_script_create ($3, memory)))
grub_script_mem_free (memory); grub_script_mem_free (memory);
else {
/* attach nested scripts to $$->script as children */
$$->script->children = state->scripts;
/* restore old scripts; append $$->script to siblings. */
state->scripts = $<scripts>2 ?: $$->script;
if (s) {
while (s->siblings)
s = s->siblings;
s->siblings = $$->script;
}
}
grub_script_lexer_deref (state->lexerstate); grub_script_lexer_deref (state->lexerstate);
} }
; ;
@ -244,6 +287,9 @@ function: "function" "name"
{ {
grub_script_lexer_ref (state->lexerstate); grub_script_lexer_ref (state->lexerstate);
state->func_mem = grub_script_mem_record (state); state->func_mem = grub_script_mem_record (state);
$<scripts>$ = state->scripts;
state->scripts = 0;
} }
delimiters0 "{" commands1 delimiters1 "}" delimiters0 "{" commands1 delimiters1 "}"
{ {
@ -253,9 +299,12 @@ function: "function" "name"
script = grub_script_create ($6, state->func_mem); script = grub_script_create ($6, state->func_mem);
if (! script) if (! script)
grub_script_mem_free (state->func_mem); grub_script_mem_free (state->func_mem);
else else {
script->children = state->scripts;
grub_script_function_create ($2, script); grub_script_function_create ($2, script);
}
state->scripts = $<scripts>3;
grub_script_lexer_deref (state->lexerstate); grub_script_lexer_deref (state->lexerstate);
} }
; ;

View file

@ -94,12 +94,19 @@ grub_script_mem_record_stop (struct grub_parser_param *state,
void void
grub_script_free (struct grub_script *script) grub_script_free (struct grub_script *script)
{ {
struct grub_script *s;
if (!script) if (!script)
return; return;
if (script->mem) if (script->mem)
grub_script_mem_free (script->mem); grub_script_mem_free (script->mem);
s = script->children;
while (s) {
grub_script_put (s);
s = s->siblings;
}
grub_free (script); grub_free (script);
} }
@ -322,6 +329,8 @@ grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem)
parsed->mem = mem; parsed->mem = mem;
parsed->cmd = cmd; parsed->cmd = cmd;
parsed->refcnt = 0; parsed->refcnt = 0;
parsed->siblings = 0;
parsed->children = 0;
return parsed; return parsed;
} }
@ -370,6 +379,7 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
parsed->mem = grub_script_mem_record_stop (parsestate, membackup); parsed->mem = grub_script_mem_record_stop (parsestate, membackup);
parsed->cmd = parsestate->parsed; parsed->cmd = parsestate->parsed;
parsed->children = parsestate->scripts;
grub_script_lexer_fini (lexstate); grub_script_lexer_fini (lexstate);
grub_free (parsestate); grub_free (parsestate);