]>
Commit | Line | Data |
---|---|---|
4d8d554a CW |
1 | /* parser.c - the part of the parser that can return partial tokens */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
4 | * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. | |
5 | * | |
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. | |
10 | * | |
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. | |
15 | * | |
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/>. | |
18 | */ | |
19 | ||
20 | #include <grub/parser.h> | |
21 | #include <grub/env.h> | |
22 | #include <grub/misc.h> | |
23 | #include <grub/mm.h> | |
24 | ||
25 | ||
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}, | |
4d8d554a CW |
33 | |
34 | {GRUB_PARSER_STATE_ESC, GRUB_PARSER_STATE_TEXT, 0, 1}, | |
35 | ||
36 | {GRUB_PARSER_STATE_QUOTE, GRUB_PARSER_STATE_TEXT, '\'', 0}, | |
37 | ||
38 | {GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_TEXT, '\"', 0}, | |
39 | {GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_QVAR, '$', 0}, | |
40 | ||
41 | {GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME2, '{', 0}, | |
42 | {GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME, 0, 1}, | |
43 | {GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, ' ', 1}, | |
44 | {GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, '\t', 1}, | |
45 | {GRUB_PARSER_STATE_VARNAME2, GRUB_PARSER_STATE_TEXT, '}', 0}, | |
46 | ||
47 | {GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0}, | |
48 | {GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME, 0, 1}, | |
49 | {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_TEXT, '\"', 0}, | |
50 | {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1}, | |
51 | {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, '\t', 1}, | |
52 | {GRUB_PARSER_STATE_QVARNAME2, GRUB_PARSER_STATE_DQUOTE, '}', 0}, | |
53 | ||
54 | {0, 0, 0, 0} | |
55 | }; | |
56 | ||
57 | ||
58 | /* Determines the state following STATE, determined by C. */ | |
59 | grub_parser_state_t | |
60 | grub_parser_cmdline_state (grub_parser_state_t state, char c, char *result) | |
61 | { | |
62 | struct grub_parser_state_transition *transition; | |
63 | struct grub_parser_state_transition default_transition; | |
64 | ||
65 | default_transition.to_state = state; | |
66 | default_transition.keep_value = 1; | |
67 | ||
68 | /* Look for a good translation. */ | |
69 | for (transition = state_transitions; transition->from_state; transition++) | |
70 | { | |
71 | if (transition->from_state != state) | |
72 | continue; | |
73 | /* An exact match was found, use it. */ | |
74 | if (transition->input == c) | |
75 | break; | |
76 | ||
77 | if (grub_isspace (transition->input) && !grub_isalpha (c) | |
78 | && !grub_isdigit (c) && c != '_') | |
79 | break; | |
80 | ||
81 | /* A less perfect match was found, use this one if no exact | |
82 | match can be found. */ | |
83 | if (transition->input == 0) | |
84 | break; | |
85 | } | |
86 | ||
87 | if (!transition->from_state) | |
88 | transition = &default_transition; | |
89 | ||
90 | if (transition->keep_value) | |
91 | *result = c; | |
92 | else | |
93 | *result = 0; | |
94 | return transition->to_state; | |
95 | } | |
96 | ||
97 | ||
98 | /* Helper for grub_parser_split_cmdline. */ | |
99 | static inline int | |
100 | check_varstate (grub_parser_state_t s) | |
101 | { | |
102 | return (s == GRUB_PARSER_STATE_VARNAME | |
103 | || s == GRUB_PARSER_STATE_VARNAME2 | |
104 | || s == GRUB_PARSER_STATE_QVARNAME | |
105 | || s == GRUB_PARSER_STATE_QVARNAME2); | |
106 | } | |
107 | ||
108 | ||
109 | static void | |
110 | add_var (char *varname, char **bp, char **vp, | |
111 | grub_parser_state_t state, grub_parser_state_t newstate) | |
112 | { | |
113 | const char *val; | |
114 | ||
115 | /* Check if a variable was being read in and the end of the name | |
116 | was reached. */ | |
117 | if (!(check_varstate (state) && !check_varstate (newstate))) | |
118 | return; | |
119 | ||
120 | *((*vp)++) = '\0'; | |
121 | val = grub_env_get (varname); | |
122 | *vp = varname; | |
123 | if (!val) | |
124 | return; | |
125 | ||
126 | /* Insert the contents of the variable in the buffer. */ | |
127 | for (; *val; val++) | |
128 | *((*bp)++) = *val; | |
129 | } | |
130 | ||
131 | grub_err_t | |
132 | grub_parser_split_cmdline (const char *cmdline, | |
133 | grub_reader_getline_t getline, void *getline_data, | |
134 | int *argc, char ***argv) | |
135 | { | |
136 | grub_parser_state_t state = GRUB_PARSER_STATE_TEXT; | |
137 | /* XXX: Fixed size buffer, perhaps this buffer should be dynamically | |
138 | allocated. */ | |
139 | char buffer[1024]; | |
140 | char *bp = buffer; | |
141 | char *rd = (char *) cmdline; | |
142 | char varname[200]; | |
143 | char *vp = varname; | |
144 | char *args; | |
145 | int i; | |
146 | ||
147 | *argc = 0; | |
148 | do | |
149 | { | |
150 | if (!rd || !*rd) | |
151 | { | |
152 | if (getline) | |
153 | getline (&rd, 1, getline_data); | |
154 | else | |
155 | break; | |
156 | } | |
157 | ||
158 | if (!rd) | |
159 | break; | |
160 | ||
161 | for (; *rd; rd++) | |
162 | { | |
163 | grub_parser_state_t newstate; | |
164 | char use; | |
165 | ||
166 | newstate = grub_parser_cmdline_state (state, *rd, &use); | |
167 | ||
168 | /* If a variable was being processed and this character does | |
169 | not describe the variable anymore, write the variable to | |
170 | the buffer. */ | |
171 | add_var (varname, &bp, &vp, state, newstate); | |
172 | ||
173 | if (check_varstate (newstate)) | |
174 | { | |
175 | if (use) | |
176 | *(vp++) = use; | |
177 | } | |
178 | else | |
179 | { | |
180 | if (newstate == GRUB_PARSER_STATE_TEXT | |
181 | && state != GRUB_PARSER_STATE_ESC && grub_isspace (use)) | |
182 | { | |
183 | /* Don't add more than one argument if multiple | |
184 | spaces are used. */ | |
185 | if (bp != buffer && *(bp - 1)) | |
186 | { | |
187 | *(bp++) = '\0'; | |
188 | (*argc)++; | |
189 | } | |
190 | } | |
191 | else if (use) | |
192 | *(bp++) = use; | |
193 | } | |
194 | state = newstate; | |
195 | } | |
196 | } | |
197 | while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state)); | |
198 | ||
199 | /* A special case for when the last character was part of a | |
200 | variable. */ | |
201 | add_var (varname, &bp, &vp, state, GRUB_PARSER_STATE_TEXT); | |
202 | ||
203 | if (bp != buffer && *(bp - 1)) | |
204 | { | |
205 | *(bp++) = '\0'; | |
206 | (*argc)++; | |
207 | } | |
208 | ||
209 | /* Reserve memory for the return values. */ | |
210 | args = grub_malloc (bp - buffer); | |
211 | if (!args) | |
212 | return grub_errno; | |
213 | grub_memcpy (args, buffer, bp - buffer); | |
214 | ||
215 | *argv = grub_malloc (sizeof (char *) * (*argc + 1)); | |
216 | if (!*argv) | |
217 | { | |
218 | grub_free (args); | |
219 | return grub_errno; | |
220 | } | |
221 | ||
222 | /* The arguments are separated with 0's, setup argv so it points to | |
223 | the right values. */ | |
224 | bp = args; | |
225 | for (i = 0; i < *argc; i++) | |
226 | { | |
227 | (*argv)[i] = bp; | |
228 | while (*bp) | |
229 | bp++; | |
230 | bp++; | |
231 | } | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
236 | /* Helper for grub_parser_execute. */ | |
237 | static grub_err_t | |
238 | grub_parser_execute_getline (char **line, int cont __attribute__ ((unused)), | |
239 | void *data) | |
240 | { | |
241 | char **source = data; | |
242 | char *p; | |
243 | ||
244 | if (!*source) | |
245 | { | |
246 | *line = 0; | |
247 | return 0; | |
248 | } | |
249 | ||
250 | p = grub_strchr (*source, '\n'); | |
251 | ||
252 | if (p) | |
253 | *line = grub_strndup (*source, p - *source); | |
254 | else | |
255 | *line = grub_strdup (*source); | |
256 | *source = p ? p + 1 : 0; | |
257 | return 0; | |
258 | } | |
259 | ||
260 | grub_err_t | |
261 | grub_parser_execute (char *source) | |
262 | { | |
263 | while (source) | |
264 | { | |
265 | char *line; | |
266 | ||
267 | grub_parser_execute_getline (&line, 0, &source); | |
268 | grub_rescue_parse_line (line, grub_parser_execute_getline, &source); | |
269 | grub_free (line); | |
270 | grub_print_error (); | |
271 | } | |
272 | ||
273 | return grub_errno; | |
274 | } |