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
;
99 /* Helper for grub_parser_split_cmdline. */
101 check_varstate (grub_parser_state_t s
)
103 return (s
== GRUB_PARSER_STATE_VARNAME
104 || s
== GRUB_PARSER_STATE_VARNAME2
105 || s
== GRUB_PARSER_STATE_QVARNAME
106 || s
== GRUB_PARSER_STATE_QVARNAME2
);
111 add_var (char *varname
, char **bp
, char **vp
,
112 grub_parser_state_t state
, grub_parser_state_t newstate
)
116 /* Check if a variable was being read in and the end of the name
118 if (!(check_varstate (state
) && !check_varstate (newstate
)))
122 val
= grub_env_get (varname
);
127 /* Insert the contents of the variable in the buffer. */
133 grub_parser_split_cmdline (const char *cmdline
,
134 grub_reader_getline_t getline
, void *getline_data
,
135 int *argc
, char ***argv
)
137 grub_parser_state_t state
= GRUB_PARSER_STATE_TEXT
;
138 /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
142 char *rd
= (char *) cmdline
;
154 getline (&rd
, 1, getline_data
);
164 grub_parser_state_t newstate
;
167 newstate
= grub_parser_cmdline_state (state
, *rd
, &use
);
169 /* If a variable was being processed and this character does
170 not describe the variable anymore, write the variable to
172 add_var (varname
, &bp
, &vp
, state
, newstate
);
174 if (check_varstate (newstate
))
181 if (newstate
== GRUB_PARSER_STATE_TEXT
182 && state
!= GRUB_PARSER_STATE_ESC
&& grub_isspace (use
))
184 /* Don't add more than one argument if multiple
186 if (bp
!= buffer
&& *(bp
- 1))
198 while (state
!= GRUB_PARSER_STATE_TEXT
&& !check_varstate (state
));
200 /* A special case for when the last character was part of a
202 add_var (varname
, &bp
, &vp
, state
, GRUB_PARSER_STATE_TEXT
);
204 if (bp
!= buffer
&& *(bp
- 1))
210 /* Reserve memory for the return values. */
211 args
= grub_malloc (bp
- buffer
);
214 grub_memcpy (args
, buffer
, bp
- buffer
);
216 *argv
= grub_malloc (sizeof (char *) * (*argc
+ 1));
223 /* The arguments are separated with 0's, setup argv so it points to
226 for (i
= 0; i
< *argc
; i
++)
237 /* Helper for grub_parser_execute. */
239 grub_parser_execute_getline (char **line
, int cont
__attribute__ ((unused
)),
242 char **source
= data
;
251 p
= grub_strchr (*source
, '\n');
254 *line
= grub_strndup (*source
, p
- *source
);
256 *line
= grub_strdup (*source
);
257 *source
= p
? p
+ 1 : 0;
262 grub_parser_execute (char *source
)
268 grub_parser_execute_getline (&line
, 0, &source
);
269 grub_rescue_parse_line (line
, grub_parser_execute_getline
, &source
);