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