1 /* parser.c - the part of the parser that can return partial tokens */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/parser.h>
22 #include <grub/misc.h>
26 /* All the possible state transitions on the command line. If a
27 transition can not be found, it is assumed that there is no
28 transition and keep_value is assumed to be 1. */
29 static struct grub_parser_state_transition state_transitions
[] = {
30 {GRUB_PARSER_STATE_TEXT
, GRUB_PARSER_STATE_QUOTE
, '\'', 0},
31 {GRUB_PARSER_STATE_TEXT
, GRUB_PARSER_STATE_DQUOTE
, '\"', 0},
32 {GRUB_PARSER_STATE_TEXT
, GRUB_PARSER_STATE_VAR
, '$', 0},
33 {GRUB_PARSER_STATE_TEXT
, GRUB_PARSER_STATE_ESC
, '\\', 0},
35 {GRUB_PARSER_STATE_ESC
, GRUB_PARSER_STATE_TEXT
, 0, 1},
37 {GRUB_PARSER_STATE_QUOTE
, GRUB_PARSER_STATE_TEXT
, '\'', 0},
39 {GRUB_PARSER_STATE_DQUOTE
, GRUB_PARSER_STATE_TEXT
, '\"', 0},
40 {GRUB_PARSER_STATE_DQUOTE
, GRUB_PARSER_STATE_QVAR
, '$', 0},
42 {GRUB_PARSER_STATE_VAR
, GRUB_PARSER_STATE_VARNAME2
, '{', 0},
43 {GRUB_PARSER_STATE_VAR
, GRUB_PARSER_STATE_VARNAME
, 0, 1},
44 {GRUB_PARSER_STATE_VARNAME
, GRUB_PARSER_STATE_TEXT
, ' ', 1},
45 {GRUB_PARSER_STATE_VARNAME
, GRUB_PARSER_STATE_TEXT
, '\t', 1},
46 {GRUB_PARSER_STATE_VARNAME2
, GRUB_PARSER_STATE_TEXT
, '}', 0},
48 {GRUB_PARSER_STATE_QVAR
, GRUB_PARSER_STATE_QVARNAME2
, '{', 0},
49 {GRUB_PARSER_STATE_QVAR
, GRUB_PARSER_STATE_QVARNAME
, 0, 1},
50 {GRUB_PARSER_STATE_QVARNAME
, GRUB_PARSER_STATE_TEXT
, '\"', 0},
51 {GRUB_PARSER_STATE_QVARNAME
, GRUB_PARSER_STATE_DQUOTE
, ' ', 1},
52 {GRUB_PARSER_STATE_QVARNAME
, GRUB_PARSER_STATE_DQUOTE
, '\t', 1},
53 {GRUB_PARSER_STATE_QVARNAME2
, GRUB_PARSER_STATE_DQUOTE
, '}', 0},
59 /* Determines the state following STATE, determined by C. */
61 grub_parser_cmdline_state (grub_parser_state_t state
, char c
, char *result
)
63 struct grub_parser_state_transition
*transition
;
64 struct grub_parser_state_transition default_transition
;
66 default_transition
.to_state
= state
;
67 default_transition
.keep_value
= 1;
69 /* Look for a good translation. */
70 for (transition
= state_transitions
; transition
->from_state
; transition
++)
72 if (transition
->from_state
!= state
)
74 /* An exact match was found, use it. */
75 if (transition
->input
== c
)
78 if (grub_isspace (transition
->input
) && !grub_isalpha (c
)
79 && !grub_isdigit (c
) && c
!= '_')
82 /* A less perfect match was found, use this one if no exact
83 match can be found. */
84 if (transition
->input
== 0)
88 if (!transition
->from_state
)
89 transition
= &default_transition
;
91 if (transition
->keep_value
)
95 return transition
->to_state
;
100 grub_parser_split_cmdline (const char *cmdline
, grub_reader_getline_t getline
,
101 int *argc
, char ***argv
)
103 grub_parser_state_t state
= GRUB_PARSER_STATE_TEXT
;
104 /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
108 char *rd
= (char *) cmdline
;
114 auto int check_varstate (grub_parser_state_t s
);
116 int check_varstate (grub_parser_state_t s
)
118 return (s
== GRUB_PARSER_STATE_VARNAME
119 || s
== GRUB_PARSER_STATE_VARNAME2
120 || s
== GRUB_PARSER_STATE_QVARNAME
121 || s
== GRUB_PARSER_STATE_QVARNAME2
);
124 auto void add_var (grub_parser_state_t newstate
);
126 void add_var (grub_parser_state_t newstate
)
130 /* Check if a variable was being read in and the end of the name
132 if (!(check_varstate (state
) && !check_varstate (newstate
)))
136 val
= grub_env_get (varname
);
141 /* Insert the contents of the variable in the buffer. */
162 grub_parser_state_t newstate
;
165 newstate
= grub_parser_cmdline_state (state
, *rd
, &use
);
167 /* If a variable was being processed and this character does
168 not describe the variable anymore, write the variable to
172 if (check_varstate (newstate
))
179 if (newstate
== GRUB_PARSER_STATE_TEXT
180 && state
!= GRUB_PARSER_STATE_ESC
&& grub_isspace (use
))
182 /* Don't add more than one argument if multiple
184 if (bp
!= buffer
&& *(bp
- 1))
196 while (state
!= GRUB_PARSER_STATE_TEXT
&& !check_varstate (state
));
198 /* A special case for when the last character was part of a
200 add_var (GRUB_PARSER_STATE_TEXT
);
202 if (bp
!= buffer
&& *(bp
- 1))
208 /* Reserve memory for the return values. */
209 args
= grub_malloc (bp
- buffer
);
212 grub_memcpy (args
, buffer
, bp
- buffer
);
214 *argv
= grub_malloc (sizeof (char *) * (*argc
+ 1));
221 /* The arguments are separated with 0's, setup argv so it points to
224 for (i
= 0; i
< *argc
; i
++)
236 grub_parser_execute (char *source
)
238 auto grub_err_t
getline (char **line
, int cont
);
239 grub_err_t
getline (char **line
, int cont
__attribute__ ((unused
)))
249 p
= grub_strchr (source
, '\n');
252 *line
= grub_strndup (source
, p
- source
);
254 *line
= grub_strdup (source
);
255 source
= p
? p
+ 1 : 0;
264 grub_rescue_parse_line (line
, getline
);