]>
Commit | Line | Data |
---|---|---|
ce5bf700 | 1 | /* main.c - the normal mode main routine */ |
2 | /* | |
4b13b216 | 3 | * GRUB -- GRand Unified Bootloader |
8367695c | 4 | * Copyright (C) 2000,2001,2002,2003 Free Software Foundation, Inc. |
ce5bf700 | 5 | * |
6 | * This program 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 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program 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 this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
20 | ||
4b13b216 | 21 | #include <grub/kernel.h> |
22 | #include <grub/normal.h> | |
23 | #include <grub/dl.h> | |
24 | #include <grub/rescue.h> | |
25 | #include <grub/misc.h> | |
26 | #include <grub/file.h> | |
27 | #include <grub/mm.h> | |
28 | #include <grub/term.h> | |
29 | #include <grub/env.h> | |
ce5bf700 | 30 | |
4b13b216 | 31 | grub_jmp_buf grub_exit_env; |
ce5bf700 | 32 | |
4b13b216 | 33 | #define GRUB_DEFAULT_HISTORY_SIZE 50 |
5aded270 | 34 | |
ce5bf700 | 35 | /* Read a line from the file FILE. */ |
36 | static int | |
4b13b216 | 37 | get_line (grub_file_t file, char cmdline[], int max_len) |
ce5bf700 | 38 | { |
39 | char c; | |
40 | int pos = 0; | |
41 | int literal = 0; | |
42 | int comment = 0; | |
43 | ||
44 | while (1) | |
45 | { | |
4b13b216 | 46 | if (grub_file_read (file, &c, 1) != 1) |
ce5bf700 | 47 | break; |
48 | ||
49 | /* Skip all carriage returns. */ | |
50 | if (c == '\r') | |
51 | continue; | |
52 | ||
53 | /* Replace tabs with spaces. */ | |
54 | if (c == '\t') | |
55 | c = ' '; | |
56 | ||
57 | /* The previous is a backslash, then... */ | |
58 | if (literal) | |
59 | { | |
60 | /* If it is a newline, replace it with a space and continue. */ | |
61 | if (c == '\n') | |
62 | { | |
63 | c = ' '; | |
64 | ||
65 | /* Go back to overwrite the backslash. */ | |
66 | if (pos > 0) | |
67 | pos--; | |
68 | } | |
69 | ||
70 | literal = 0; | |
71 | } | |
72 | ||
73 | if (c == '\\') | |
74 | literal = 1; | |
75 | ||
76 | if (comment) | |
77 | { | |
78 | if (c == '\n') | |
79 | comment = 0; | |
80 | } | |
81 | else if (pos == 0) | |
82 | { | |
83 | if (c == '#') | |
84 | comment = 1; | |
4b13b216 | 85 | else if (! grub_isspace (c)) |
ce5bf700 | 86 | cmdline[pos++] = c; |
87 | } | |
88 | else | |
89 | { | |
90 | if (c == '\n') | |
91 | break; | |
92 | ||
93 | if (pos < max_len) | |
94 | cmdline[pos++] = c; | |
95 | } | |
96 | } | |
97 | ||
98 | cmdline[pos] = '\0'; | |
99 | ||
100 | return pos; | |
101 | } | |
102 | ||
103 | static void | |
4b13b216 | 104 | free_menu (grub_menu_t menu) |
ce5bf700 | 105 | { |
4b13b216 | 106 | grub_menu_entry_t entry = menu->entry_list; |
ce5bf700 | 107 | |
108 | while (entry) | |
109 | { | |
4b13b216 | 110 | grub_menu_entry_t next_entry = entry->next; |
111 | grub_command_list_t cmd = entry->command_list; | |
ce5bf700 | 112 | |
113 | while (cmd) | |
114 | { | |
4b13b216 | 115 | grub_command_list_t next_cmd = cmd->next; |
ce5bf700 | 116 | |
4b13b216 | 117 | grub_free ((void *) cmd->command); |
ce5bf700 | 118 | cmd = next_cmd; |
119 | } | |
120 | ||
4b13b216 | 121 | grub_free ((void *) entry->title); |
ce5bf700 | 122 | entry = next_entry; |
123 | } | |
124 | ||
4b13b216 | 125 | grub_free (menu); |
ce5bf700 | 126 | } |
127 | ||
128 | /* Read the config file CONFIG and return a menu. If no entry is present, | |
129 | return NULL. */ | |
4b13b216 | 130 | static grub_menu_t |
ce5bf700 | 131 | read_config_file (const char *config) |
132 | { | |
4b13b216 | 133 | grub_file_t file; |
134 | static char cmdline[GRUB_MAX_CMDLINE]; | |
135 | grub_menu_t menu; | |
136 | grub_menu_entry_t *next_entry, cur_entry = 0; | |
137 | grub_command_list_t *next_cmd, cur_cmd; | |
ce5bf700 | 138 | |
139 | /* Try to open the config file. */ | |
4b13b216 | 140 | file = grub_file_open (config); |
ce5bf700 | 141 | if (! file) |
142 | return 0; | |
143 | ||
144 | /* Initialize the menu. */ | |
4b13b216 | 145 | menu = (grub_menu_t) grub_malloc (sizeof (*menu)); |
ce5bf700 | 146 | if (! menu) |
147 | { | |
4b13b216 | 148 | grub_file_close (file); |
ce5bf700 | 149 | return 0; |
150 | } | |
151 | menu->default_entry = 0; | |
152 | menu->fallback_entry = -1; | |
153 | menu->timeout = -1; | |
154 | menu->size = 0; | |
155 | menu->entry_list = 0; | |
156 | ||
157 | next_entry = &(menu->entry_list); | |
158 | next_cmd = 0; | |
159 | ||
160 | /* Read each line. */ | |
161 | while (get_line (file, cmdline, sizeof (cmdline))) | |
162 | { | |
4b13b216 | 163 | grub_command_t cmd; |
ce5bf700 | 164 | |
4b13b216 | 165 | cmd = grub_command_find (cmdline); |
166 | grub_errno = GRUB_ERR_NONE; | |
ce5bf700 | 167 | if (! cmd) |
168 | { | |
4b13b216 | 169 | grub_printf ("Unknown command `%s' is ignored.\n", cmdline); |
ce5bf700 | 170 | continue; |
171 | } | |
172 | ||
4b13b216 | 173 | if (cmd->flags & GRUB_COMMAND_FLAG_TITLE) |
ce5bf700 | 174 | { |
175 | char *p; | |
176 | ||
4b13b216 | 177 | cur_entry = (grub_menu_entry_t) grub_malloc (sizeof (*cur_entry)); |
ce5bf700 | 178 | if (! cur_entry) |
179 | goto fail; | |
180 | ||
4b13b216 | 181 | p = grub_strchr (cmdline, ' '); |
ce5bf700 | 182 | if (p) |
4b13b216 | 183 | cur_entry->title = grub_strdup (p); |
ce5bf700 | 184 | else |
4b13b216 | 185 | cur_entry->title = grub_strdup (""); |
ce5bf700 | 186 | |
187 | if (! cur_entry->title) | |
188 | { | |
4b13b216 | 189 | grub_free (cur_entry); |
ce5bf700 | 190 | goto fail; |
191 | } | |
192 | ||
193 | cur_entry->num = 0; | |
194 | cur_entry->command_list = 0; | |
195 | cur_entry->next = 0; | |
196 | ||
197 | *next_entry = cur_entry; | |
198 | next_entry = &(cur_entry->next); | |
199 | ||
200 | next_cmd = &(cur_entry->command_list); | |
201 | ||
202 | menu->size++; | |
203 | } | |
204 | else if (! cur_entry) | |
205 | { | |
206 | /* Run the command if possible. */ | |
4b13b216 | 207 | if (cmd->flags & GRUB_COMMAND_FLAG_MENU) |
ce5bf700 | 208 | { |
4b13b216 | 209 | grub_command_execute (cmdline); |
210 | grub_print_error (); | |
211 | grub_errno = GRUB_ERR_NONE; | |
ce5bf700 | 212 | } |
213 | else | |
214 | { | |
4b13b216 | 215 | grub_printf ("Invalid command `%s' is ignored.\n", cmdline); |
ce5bf700 | 216 | continue; |
217 | } | |
218 | } | |
219 | else | |
220 | { | |
4b13b216 | 221 | cur_cmd = (grub_command_list_t) grub_malloc (sizeof (*cur_cmd)); |
ce5bf700 | 222 | if (! cur_cmd) |
223 | goto fail; | |
224 | ||
4b13b216 | 225 | cur_cmd->command = grub_strdup (cmdline); |
ce5bf700 | 226 | if (! cur_cmd->command) |
227 | { | |
4b13b216 | 228 | grub_free (cur_cmd); |
ce5bf700 | 229 | goto fail; |
230 | } | |
231 | ||
232 | cur_cmd->next = 0; | |
233 | ||
234 | *next_cmd = cur_cmd; | |
235 | next_cmd = &(cur_cmd->next); | |
236 | ||
237 | cur_entry->num++; | |
238 | } | |
239 | } | |
240 | ||
241 | fail: | |
242 | ||
4b13b216 | 243 | grub_file_close (file); |
ce5bf700 | 244 | |
245 | /* If no entry was found or any error occurred, return NULL. */ | |
4b13b216 | 246 | if (menu->size == 0 || grub_errno != GRUB_ERR_NONE) |
ce5bf700 | 247 | { |
248 | free_menu (menu); | |
249 | return 0; | |
250 | } | |
251 | ||
252 | /* Check values of the default entry and the fallback one. */ | |
253 | if (menu->fallback_entry >= menu->size) | |
254 | menu->fallback_entry = -1; | |
255 | ||
256 | if (menu->default_entry < 0 || menu->default_entry >= menu->size) | |
257 | { | |
258 | if (menu->fallback_entry < 0) | |
259 | menu->default_entry = 0; | |
260 | else | |
261 | { | |
262 | menu->default_entry = menu->fallback_entry; | |
263 | menu->fallback_entry = -1; | |
264 | } | |
265 | } | |
266 | ||
267 | return menu; | |
268 | } | |
269 | ||
270 | /* This starts the normal mode. */ | |
271 | void | |
4b13b216 | 272 | grub_enter_normal_mode (const char *config) |
ce5bf700 | 273 | { |
4b13b216 | 274 | if (grub_setjmp (grub_exit_env) == 0) |
275 | grub_normal_execute (config, 0); | |
ce5bf700 | 276 | } |
277 | ||
278 | /* Initialize the screen. */ | |
279 | void | |
4b13b216 | 280 | grub_normal_init_page (void) |
ce5bf700 | 281 | { |
4b13b216 | 282 | grub_cls (); |
283 | grub_printf ("\n\ | |
284 | GRUB version %s\n\n", | |
ce5bf700 | 285 | PACKAGE_VERSION); |
286 | } | |
287 | ||
288 | /* Read the config file CONFIG and execute the menu interface or | |
289 | the command-line interface. */ | |
290 | void | |
4b13b216 | 291 | grub_normal_execute (const char *config, int nested) |
ce5bf700 | 292 | { |
4b13b216 | 293 | grub_menu_t menu = 0; |
ce5bf700 | 294 | |
295 | if (config) | |
296 | { | |
297 | menu = read_config_file (config); | |
298 | ||
299 | /* Ignore any error. */ | |
4b13b216 | 300 | grub_errno = GRUB_ERR_NONE; |
ce5bf700 | 301 | } |
302 | ||
303 | if (menu) | |
4b13b216 | 304 | grub_menu_run (menu, nested); |
ce5bf700 | 305 | else |
4b13b216 | 306 | grub_cmdline_run (nested); |
ce5bf700 | 307 | } |
308 | ||
309 | /* Enter normal mode from rescue mode. */ | |
310 | static void | |
4b13b216 | 311 | grub_rescue_cmd_normal (int argc, char *argv[]) |
ce5bf700 | 312 | { |
313 | if (argc == 0) | |
314 | { | |
315 | /* Guess the config filename. */ | |
316 | char *config; | |
317 | const char *prefix; | |
318 | ||
4b13b216 | 319 | prefix = grub_env_get ("prefix"); |
ce5bf700 | 320 | if (prefix) |
321 | { | |
4b13b216 | 322 | config = grub_malloc (grub_strlen (prefix) + sizeof ("/grub.cfg")); |
ce5bf700 | 323 | if (! config) |
324 | return; | |
325 | ||
4b13b216 | 326 | grub_sprintf (config, "%s/grub.cfg", prefix); |
327 | grub_enter_normal_mode (config); | |
328 | grub_free (config); | |
ce5bf700 | 329 | } |
330 | else | |
4b13b216 | 331 | grub_enter_normal_mode (0); |
ce5bf700 | 332 | } |
333 | else | |
4b13b216 | 334 | grub_enter_normal_mode (argv[0]); |
ce5bf700 | 335 | } |
336 | ||
4b13b216 | 337 | #ifdef GRUB_UTIL |
1f7315a3 | 338 | void |
4b13b216 | 339 | grub_normal_init (void) |
1f7315a3 | 340 | { |
4b13b216 | 341 | grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); |
5aded270 | 342 | |
1f7315a3 | 343 | /* Register a command "normal" for the rescue mode. */ |
4b13b216 | 344 | grub_rescue_register_command ("normal", grub_rescue_cmd_normal, |
1f7315a3 | 345 | "enter normal mode"); |
346 | ||
347 | /* This registers some built-in commands. */ | |
4b13b216 | 348 | grub_command_init (); |
1f7315a3 | 349 | |
350 | } | |
351 | ||
352 | void | |
4b13b216 | 353 | grub_normal_fini (void) |
1f7315a3 | 354 | { |
4b13b216 | 355 | grub_set_history (0); |
356 | grub_rescue_unregister_command ("normal"); | |
1f7315a3 | 357 | |
358 | } | |
4b13b216 | 359 | #else /* ! GRUB_UTIL */ |
360 | GRUB_MOD_INIT | |
ce5bf700 | 361 | { |
362 | /* Normal mode shouldn't be unloaded. */ | |
4b13b216 | 363 | grub_dl_ref (mod); |
ce5bf700 | 364 | |
4b13b216 | 365 | grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); |
5aded270 | 366 | |
ce5bf700 | 367 | /* Register a command "normal" for the rescue mode. */ |
4b13b216 | 368 | grub_rescue_register_command ("normal", grub_rescue_cmd_normal, |
ce5bf700 | 369 | "enter normal mode"); |
370 | ||
371 | /* This registers some built-in commands. */ | |
4b13b216 | 372 | grub_command_init (); |
ce5bf700 | 373 | } |
374 | ||
4b13b216 | 375 | GRUB_MOD_FINI |
ce5bf700 | 376 | { |
4b13b216 | 377 | grub_set_history (0); |
378 | grub_rescue_unregister_command ("normal"); | |
ce5bf700 | 379 | } |
4b13b216 | 380 | #endif /* ! GRUB_UTIL */ |