On Sun, Jun 24, 2007 at 12:22:18PM +0800, Bean wrote:
> Some bugs I found on scripting.
>
> 1. token parser
>
> echo aa"bb"
> aabb
> (correct)
>
> echo aa"bb"cc
> aabb
> (cc is lost)
>
> echo aa$prefix
> aa (hd0,1)/boot/grub
> (should be one token)
>
> echo $prefix/grub.cfg
> (hd0,1)/boot/grub /grub.cfg
> (should be one token)
>
> The problem here is that when a variable is mixed with text, the token breaks.
> I think this is also the reason why set doesn't work with variable.
>
> set AA=1
> set BB=$AA
>
> will expand to
>
> set AA=1
> set BB= 1
>
> Thereforce the value of BB is empty instead of 1.
>
I fix the token parser problem, changes include:
1. Inside grub_script_yylex2, mixed text is detected, and transformed into
arg structure instead of text.
2. Fix a small bug that cause trash output when an undefine variable is
referenced.
3. Close varible definition when / is encountered.
With this patch, the following command works properly:
echo aa"BB"cc
aaBBcc
set AA=1
set BB=$AA
echo $BB
1
echo $prefix/grub.cfg
(hd0,1)/boot/grub/grub.cfg
echo ${prefix}/grub.cfg
(hd0,1)/boot/grub/grub.cfg
set AA=1
echo aa"$AA"bb
aa1bb
echo aa${?}bb
aa0bb
--
Bean <[EMAIL PROTECTED]>
2007-06-24 Bean <[EMAIL PROTECTED]>
* kern/parser.c (state_transitions): Add new case.
* normal/execute.c (grub_script_argument_to_string):
Output empty string when variable does not exist.
* normal/lexer.c (grub_script_yylex2): Handle mixed text
properly.
* normal/parser.y : token GRUB_PARSER_TOKEN_VAR returns arg
instead of text.
Index: kern/parser.c
===================================================================
RCS file: /sources/grub/grub2/kern/parser.c,v
retrieving revision 1.2
diff -u -r1.2 parser.c
--- kern/parser.c 23 Nov 2005 03:36:24 -0000 1.2
+++ kern/parser.c 24 Jun 2007 11:23:48 -0000
@@ -44,6 +44,7 @@
{ GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME2, '{', 0},
{ GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME, 0, 1},
{ GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, ' ', 1},
+ { GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, '/', 1},
{ GRUB_PARSER_STATE_VARNAME2, GRUB_PARSER_STATE_TEXT, '}', 0},
{ GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0},
Index: normal/execute.c
===================================================================
RCS file: /sources/grub/grub2/normal/execute.c,v
retrieving revision 1.4
diff -u -r1.4 execute.c
--- normal/execute.c 28 May 2006 21:58:34 -0000 1.4
+++ normal/execute.c 24 Jun 2007 11:23:48 -0000
@@ -50,7 +50,8 @@
if (argi->type == 1)
{
val = grub_env_get (argi->str);
- size += grub_strlen (val);
+ if (val)
+ size += grub_strlen (val);
}
else
size += grub_strlen (argi->str);
@@ -68,7 +69,8 @@
if (argi->type == 1)
{
val = grub_env_get (argi->str);
- grub_strcat (chararg, val);
+ if (val)
+ grub_strcat (chararg, val);
}
else
grub_strcat (chararg, argi->str);
Index: normal/lexer.c
===================================================================
RCS file: /sources/grub/grub2/normal/lexer.c,v
retrieving revision 1.6
diff -u -r1.6 lexer.c
--- normal/lexer.c 4 Jun 2006 15:56:55 -0000 1.6
+++ normal/lexer.c 24 Jun 2007 11:23:48 -0000
@@ -50,7 +50,7 @@
struct grub_lexer_param *param;
param = grub_malloc (sizeof (*param));
- if (! param)
+ if (!param)
return 0;
param->state = GRUB_PARSER_STATE_TEXT;
@@ -100,7 +100,7 @@
if (state->recording[--state->recordpos] != '}')
{
grub_printf ("Internal error while parsing menu entry");
- for (;;); /* XXX */
+ for (;;); /* XXX */
}
state->recording[state->recordpos] = '\0';
}
@@ -118,7 +118,7 @@
char *old = state->recording;
state->recordlen += 100;
state->recording = grub_realloc (state->recording, state->recordlen);
- if (! state->recording)
+ if (!state->recording)
{
grub_free (old);
state->record = 0;
@@ -137,10 +137,10 @@
}
int
-grub_script_yylex2 (YYSTYPE *yylval, struct grub_parser_param *parsestate);
+grub_script_yylex2 (YYSTYPE * yylval, struct grub_parser_param *parsestate);
int
-grub_script_yylex (YYSTYPE *yylval, struct grub_parser_param *parsestate)
+grub_script_yylex (YYSTYPE * yylval, struct grub_parser_param *parsestate)
{
int r = -1;
@@ -154,31 +154,32 @@
}
int
-grub_script_yylex2 (YYSTYPE *yylval, struct grub_parser_param *parsestate)
+grub_script_yylex2 (YYSTYPE * yylval, struct grub_parser_param *parsestate)
{
grub_parser_state_t newstate;
char use;
char *buffer;
char *bp;
struct grub_lexer_param *state = parsestate->lexerstate;
+ struct grub_script_arg *arg = 0;
+ int done = 0;
if (state->done)
return 0;
- if (! *state->script)
+ if (!*state->script)
{
/* Check if more tokens are requested by the parser. */
if ((state->refs
- || state->state == GRUB_PARSER_STATE_ESC)
- && state->getline)
+ || state->state == GRUB_PARSER_STATE_ESC) && state->getline)
{
- while (!state->script || ! grub_strlen (state->script))
+ while (!state->script || !grub_strlen (state->script))
{
grub_free (state->newscript);
state->newscript = 0;
state->getline (&state->newscript);
state->script = state->newscript;
- if (! state->script)
+ if (!state->script)
return 0;
}
grub_dprintf ("scripting", "token=`\\n'\n");
@@ -196,163 +197,192 @@
}
}
- newstate = grub_parser_cmdline_state (state->state, *state->script, &use);
-
- /* Check if it is a text. */
- if (check_textstate (newstate))
+ while (1)
{
- /* In case the string is not quoted, this can be a one char
- length symbol. */
- if (newstate == GRUB_PARSER_STATE_TEXT)
+ newstate =
+ grub_parser_cmdline_state (state->state, *state->script, &use);
+
+ /* Check if it is a text. */
+ if (check_textstate (newstate))
{
- switch (*state->script)
+ /* In case the string is not quoted, this can be a one char
+ length symbol. */
+ if (newstate == GRUB_PARSER_STATE_TEXT)
{
- case ' ':
- while (*state->script)
+ switch (*state->script)
{
- newstate = grub_parser_cmdline_state (state->state,
- *state->script, &use);
- if (! (state->state == GRUB_PARSER_STATE_TEXT
- && *state->script == ' '))
+ case ' ':
+ while (*state->script)
{
- grub_dprintf ("scripting", "token=` '\n");
- return ' ';
+ newstate = grub_parser_cmdline_state (state->state,
+ *state->script,
+ &use);
+ if (!
+ (state->state == GRUB_PARSER_STATE_TEXT
+ && *state->script == ' '))
+ {
+ grub_dprintf ("scripting", "token=` '\n");
+ return ' ';
+ }
+ state->state = newstate;
+ nextchar (state);
}
- state->state = newstate;
- nextchar (state);
+ grub_dprintf ("scripting", "token=` '\n");
+ return ' ';
+ case '{':
+ case '}':
+ case ';':
+ case '\n':
+ {
+ char c;
+ grub_dprintf ("scripting", "token=`%c'\n",
+ *state->script);
+ c = *state->script;;
+ nextchar (state);
+ return c;
+ }
}
- grub_dprintf ("scripting", "token=` '\n");
- return ' ';
- case '{':
- case '}':
- case ';':
- case '\n':
- {
- char c;
- grub_dprintf ("scripting", "token=`%c'\n", *state->script);
- c = *state->script;;
- nextchar (state);
- return c;
- }
}
- }
-
- /* XXX: Use a better size. */
- buffer = grub_script_malloc (parsestate, 2048);
- if (! buffer)
- return 0;
- bp = buffer;
+ /* XXX: Use a better size. */
+ buffer = grub_script_malloc (parsestate, 2048);
+ if (!buffer)
+ return 0;
- /* Read one token, possible quoted. */
- while (*state->script)
- {
- newstate = grub_parser_cmdline_state (state->state,
- *state->script, &use);
+ bp = buffer;
- /* Check if a variable name starts. */
- if (check_varstate (newstate))
- break;
-
- /* If the string is not quoted or escaped, stop processing
- when a special token was found. It will be recognised
- next time when this function is called. */
- if (newstate == GRUB_PARSER_STATE_TEXT
- && state->state != GRUB_PARSER_STATE_ESC)
+ /* Read one token, possible quoted. */
+ while (*state->script)
{
- int breakout = 0;
+ newstate = grub_parser_cmdline_state (state->state,
+ *state->script, &use);
+
+ /* Check if a variable name starts. */
+ if (check_varstate (newstate))
+ break;
- switch (use)
+ /* If the string is not quoted or escaped, stop processing
+ when a special token was found. It will be recognised
+ next time when this function is called. */
+ if (newstate == GRUB_PARSER_STATE_TEXT
+ && state->state != GRUB_PARSER_STATE_ESC)
{
- case ' ':
- case '{':
- case '}':
- case ';':
- case '\n':
- breakout = 1;
+ switch (use)
+ {
+ case ' ':
+ case '{':
+ case '}':
+ case ';':
+ case '\n':
+ done = 1;
+ }
+ if (done)
+ break;
}
- if (breakout)
- break;
- *(bp++) = use;
+
+ if (use)
+ *(bp++) = use;
+
+ state->state = newstate;
+ nextchar (state);
}
- else if (use)
- *(bp++) = use;
- state->state = newstate;
- nextchar (state);
- }
+ /* A string of text was read in. */
+ *bp = '\0';
+ grub_dprintf ("scripting", "token=`%s'\n", buffer);
- /* A string of text was read in. */
- *bp = '\0';
- grub_dprintf ("scripting", "token=`%s'\n", buffer);
- yylval->string = buffer;
-
- /* Detect some special tokens. */
- if (! grub_strcmp (buffer, "while"))
- return GRUB_PARSER_TOKEN_WHILE;
- else if (! grub_strcmp (buffer, "if"))
- return GRUB_PARSER_TOKEN_IF;
- else if (! grub_strcmp (buffer, "function"))
- return GRUB_PARSER_TOKEN_FUNCTION;
- else if (! grub_strcmp (buffer, "menuentry"))
- return GRUB_PARSER_TOKEN_MENUENTRY;
- else if (! grub_strcmp (buffer, "@"))
- return GRUB_PARSER_TOKEN_MENUENTRY;
- else if (! grub_strcmp (buffer, "else"))
- return GRUB_PARSER_TOKEN_ELSE;
- else if (! grub_strcmp (buffer, "then"))
- return GRUB_PARSER_TOKEN_THEN;
- else if (! grub_strcmp (buffer, "fi"))
- return GRUB_PARSER_TOKEN_FI;
- else
- return GRUB_PARSER_TOKEN_NAME;
- }
- else if (newstate == GRUB_PARSER_STATE_VAR
- || newstate == GRUB_PARSER_STATE_QVAR)
- {
- /* XXX: Use a better size. */
- buffer = grub_script_malloc (parsestate, 2096);
- if (! buffer)
- return 0;
+ if (!*state->script)
+ done = 1;
- bp = buffer;
+ if ((done) && (!arg))
+ {
+ yylval->string = buffer;
+
+ /* Detect some special tokens. */
+ if (!grub_strcmp (buffer, "while"))
+ return GRUB_PARSER_TOKEN_WHILE;
+ else if (!grub_strcmp (buffer, "if"))
+ return GRUB_PARSER_TOKEN_IF;
+ else if (!grub_strcmp (buffer, "function"))
+ return GRUB_PARSER_TOKEN_FUNCTION;
+ else if (!grub_strcmp (buffer, "menuentry"))
+ return GRUB_PARSER_TOKEN_MENUENTRY;
+ else if (!grub_strcmp (buffer, "@"))
+ return GRUB_PARSER_TOKEN_MENUENTRY;
+ else if (!grub_strcmp (buffer, "else"))
+ return GRUB_PARSER_TOKEN_ELSE;
+ else if (!grub_strcmp (buffer, "then"))
+ return GRUB_PARSER_TOKEN_THEN;
+ else if (!grub_strcmp (buffer, "fi"))
+ return GRUB_PARSER_TOKEN_FI;
+ else
+ return GRUB_PARSER_TOKEN_NAME;
+ }
- /* This is a variable, read the variable name. */
- while (*state->script)
+ if (bp != buffer)
+ arg =
+ grub_script_arg_add (parsestate, arg, GRUB_SCRIPT_ARG_TYPE_STR,
+ buffer);
+ }
+ else if (newstate == GRUB_PARSER_STATE_VAR
+ || newstate == GRUB_PARSER_STATE_QVAR)
{
- newstate = grub_parser_cmdline_state (state->state,
- *state->script, &use);
+ /* XXX: Use a better size. */
+ buffer = grub_script_malloc (parsestate, 2096);
+ if (!buffer)
+ return 0;
+
+ bp = buffer;
- /* Check if this character is not part of the variable name
- anymore. */
- if (! (check_varstate (newstate)))
+ /* This is a variable, read the variable name. */
+ while (*state->script)
{
- if (state->state == GRUB_PARSER_STATE_VARNAME2
- || state->state == GRUB_PARSER_STATE_QVARNAME2)
- nextchar (state);
+ newstate = grub_parser_cmdline_state (state->state,
+ *state->script, &use);
+
+ /* Check if this character is not part of the variable name
+ anymore. */
+ if (!(check_varstate (newstate)))
+ {
+ if (state->state == GRUB_PARSER_STATE_VARNAME2
+ || state->state == GRUB_PARSER_STATE_QVARNAME2)
+ nextchar (state);
+ state->state = newstate;
+ break;
+ }
+
+ if (use)
+ *(bp++) = use;
+ nextchar (state);
state->state = newstate;
- break;
}
- if (use)
- *(bp++) = use;
- nextchar (state);
+ *bp = '\0';
state->state = newstate;
- }
- *bp = '\0';
- state->state = newstate;
- yylval->string = buffer;
- grub_dprintf ("scripting", "vartoken=`%s'\n", buffer);
+ if (bp != buffer)
+ arg =
+ grub_script_arg_add (parsestate, arg, GRUB_SCRIPT_ARG_TYPE_VAR,
+ buffer);
+ }
+ else
+ {
+ /* There is either text or a variable name. In the case you
+ arrive here there is a serious problem with the lexer. */
+ grub_error (GRUB_ERR_BAD_ARGUMENT, "Internal error\n");
+ return 0;
+ }
- return GRUB_PARSER_TOKEN_VAR;
- }
- else
- {
- /* There is either text or a variable name. In the case you
- arrive here there is a serious problem with the lexer. */
- grub_error (GRUB_ERR_BAD_ARGUMENT, "Internal error\n");
- return 0;
+ if ((done) || (!*state->script))
+ {
+ if (arg)
+ {
+ yylval->arg = arg;
+ return GRUB_PARSER_TOKEN_VAR;
+ }
+ else
+ return ' ';
+ }
}
}
Index: normal/parser.y
===================================================================
RCS file: /sources/grub/grub2/normal/parser.y,v
retrieving revision 1.6
diff -u -r1.6 parser.y
--- normal/parser.y 4 Jun 2006 15:56:55 -0000 1.6
+++ normal/parser.y 24 Jun 2007 11:23:48 -0000
@@ -46,9 +46,9 @@
%token GRUB_PARSER_TOKEN_VAR
%type <cmd> script grubcmd command commands commandblock menuentry if
%type <arglist> arguments;
-%type <arg> argument;
+%type <arg> argument GRUB_PARSER_TOKEN_VAR;
%type <string> "if" "while" "function" "else" "then" "fi"
-%type <string> text GRUB_PARSER_TOKEN_NAME GRUB_PARSER_TOKEN_VAR
+%type <string> text GRUB_PARSER_TOKEN_NAME
%pure-parser
%lex-param { struct grub_parser_param *state };
@@ -86,7 +86,7 @@
for example: `foo${bar}baz'. */
argument: GRUB_PARSER_TOKEN_VAR
{
- $$ = grub_script_arg_add (state, 0,
GRUB_SCRIPT_ARG_TYPE_VAR, $1);
+ $$ = $1;
}
| text
{
_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel