1 /* main.c - the normal mode main routine */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2000,2001,2002,2003,2005,2006,2007,2008,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/kernel.h>
21 #include <grub/normal.h>
23 #include <grub/misc.h>
24 #include <grub/file.h>
26 #include <grub/term.h>
28 #include <grub/parser.h>
29 #include <grub/reader.h>
30 #include <grub/menu_viewer.h>
32 #define GRUB_DEFAULT_HISTORY_SIZE 50
34 /* Read a line from the file FILE. */
36 grub_file_getline (grub_file_t file
)
44 /* Initially locate some space. */
45 cmdline
= grub_malloc (max_len
);
51 if (grub_file_read (file
, &c
, 1) != 1)
54 /* Skip all carriage returns. */
58 /* Replace tabs with spaces. */
62 /* The previous is a backslash, then... */
65 /* If it is a newline, replace it with a space and continue. */
70 /* Go back to overwrite the backslash. */
83 if (! grub_isspace (c
))
90 char *old_cmdline
= cmdline
;
91 max_len
= max_len
* 2;
92 cmdline
= grub_realloc (cmdline
, max_len
);
95 grub_free (old_cmdline
);
109 /* If the buffer is empty, don't return anything at all. */
120 free_menu (grub_menu_t menu
)
122 grub_menu_entry_t entry
= menu
->entry_list
;
126 grub_menu_entry_t next_entry
= entry
->next
;
128 grub_free ((void *) entry
->title
);
129 grub_free ((void *) entry
->sourcecode
);
134 grub_env_unset_data_slot ("menu");
138 free_menu_entry_classes (struct grub_menu_entry_class
*head
)
140 /* Free all the classes. */
143 struct grub_menu_entry_class
*next
;
145 grub_free (head
->name
);
152 /* Add a menu entry to the current menu context (as given by the environment
153 variable data slot `menu'). As the configuration file is read, the script
154 parser calls this when a menu entry is to be created. */
156 grub_normal_add_menu_entry (int argc
, const char **args
,
157 const char *sourcecode
)
159 const char *menutitle
= 0;
160 const char *menusourcecode
;
162 grub_menu_entry_t
*last
;
165 struct grub_menu_entry_class
*classes_head
; /* Dummy head node for list. */
166 struct grub_menu_entry_class
*classes_tail
;
168 /* Allocate dummy head node for class list. */
169 classes_head
= grub_malloc (sizeof (struct grub_menu_entry_class
));
172 classes_head
->name
= 0;
173 classes_head
->next
= 0;
174 classes_tail
= classes_head
;
176 menu
= grub_env_get_data_slot ("menu");
178 return grub_error (GRUB_ERR_MENU
, "no menu context");
180 last
= &menu
->entry_list
;
182 menusourcecode
= grub_strdup (sourcecode
);
183 if (! menusourcecode
)
186 /* Parse menu arguments. */
187 for (i
= 0; i
< argc
; i
++)
189 /* Capture arguments. */
190 if (grub_strncmp ("--", args
[i
], 2) == 0)
192 const char *arg
= &args
[i
][2];
194 /* Handle menu class. */
195 if (grub_strcmp(arg
, "class") == 0)
198 struct grub_menu_entry_class
*new_class
;
201 class_name
= grub_strdup (args
[i
]);
208 /* Create a new class and add it at the tail of the list. */
209 new_class
= grub_malloc (sizeof (struct grub_menu_entry_class
));
212 grub_free (class_name
);
216 /* Fill in the new class node. */
217 new_class
->name
= class_name
;
219 /* Link the tail to it, and make it the new tail. */
220 classes_tail
->next
= new_class
;
221 classes_tail
= new_class
;
226 /* Handle invalid argument. */
228 grub_error (GRUB_ERR_MENU
,
229 "invalid argument for menuentry: %s", args
[i
]);
237 menutitle
= grub_strdup (args
[i
]);
242 grub_error (GRUB_ERR_MENU
,
243 "too many titles for menuentry: %s", args
[i
]);
248 /* Validate arguments. */
249 if ((! failed
) && (! menutitle
))
251 grub_error (GRUB_ERR_MENU
, "menuentry is missing title");
255 /* If argument parsing failed, free any allocated resources. */
258 free_menu_entry_classes (classes_head
);
259 grub_free ((void *) menutitle
);
260 grub_free ((void *) menusourcecode
);
262 /* Here we assume that grub_error has been used to specify failure details. */
266 /* Add the menu entry at the end of the list. */
268 last
= &(*last
)->next
;
270 *last
= grub_malloc (sizeof (**last
));
273 free_menu_entry_classes (classes_head
);
274 grub_free ((void *) menutitle
);
275 grub_free ((void *) menusourcecode
);
279 (*last
)->title
= menutitle
;
280 (*last
)->classes
= classes_head
;
282 (*last
)->sourcecode
= menusourcecode
;
286 return GRUB_ERR_NONE
;
290 read_config_file (const char *config
)
293 grub_parser_t old_parser
= 0;
295 auto grub_err_t
getline (char **line
, int cont
);
296 grub_err_t
getline (char **line
, int cont
__attribute__ ((unused
)))
302 *line
= buf
= grub_file_getline (file
);
310 grub_parser_t parser
;
311 grub_named_list_t list
;
314 while (grub_isspace (*buf
))
318 old_parser
= grub_parser_get_current ();
320 list
= GRUB_AS_NAMED_LIST (grub_parser_class
.handler_list
);
321 parser
= grub_named_list_find (list
, buf
);
323 grub_parser_set_current (parser
);
326 char cmd_name
[8 + grub_strlen (buf
)];
328 /* Perhaps it's not loaded yet, try the autoload
330 grub_strcpy (cmd_name
, "parser.");
331 grub_strcat (cmd_name
, buf
);
332 grub_command_execute (cmd_name
, 0, 0);
341 return GRUB_ERR_NONE
;
346 newmenu
= grub_env_get_data_slot ("menu");
349 newmenu
= grub_malloc (sizeof (*newmenu
));
353 newmenu
->entry_list
= 0;
355 grub_env_set_data_slot ("menu", newmenu
);
358 /* Try to open the config file. */
359 file
= grub_file_open (config
);
363 grub_reader_loop (getline
);
364 grub_file_close (file
);
367 grub_parser_set_current (old_parser
);
372 /* Initialize the screen. */
374 grub_normal_init_page (void)
376 grub_uint8_t width
, margin
;
378 #define TITLE ("GNU GRUB version " PACKAGE_VERSION)
380 width
= grub_getwh () >> 8;
381 margin
= (width
- (sizeof(TITLE
) + 7)) / 2;
389 grub_printf ("%s\n\n", TITLE
);
394 static int reader_nested
;
396 /* Read the config file CONFIG and execute the menu interface or
397 the command line interface if BATCH is false. */
399 grub_normal_execute (const char *config
, int nested
, int batch
)
401 grub_menu_t menu
= 0;
403 read_command_list ();
405 read_handler_list ();
406 grub_command_execute ("parser.sh", 0, 0);
408 reader_nested
= nested
;
412 menu
= read_config_file (config
);
414 /* Ignore any error. */
415 grub_errno
= GRUB_ERR_NONE
;
420 if (menu
&& menu
->size
)
422 grub_menu_viewer_show_menu (menu
, nested
);
429 /* This starts the normal mode. */
431 grub_enter_normal_mode (const char *config
)
433 grub_normal_execute (config
, 0, 0);
436 /* Enter normal mode from rescue mode. */
438 grub_cmd_normal (struct grub_command
*cmd
,
439 int argc
, char *argv
[])
441 grub_unregister_command (cmd
);
445 /* Guess the config filename. It is necessary to make CONFIG static,
446 so that it won't get broken by longjmp. */
450 prefix
= grub_env_get ("prefix");
453 config
= grub_malloc (grub_strlen (prefix
) + sizeof ("/grub.cfg"));
457 grub_sprintf (config
, "%s/grub.cfg", prefix
);
458 grub_enter_normal_mode (config
);
462 grub_enter_normal_mode (0);
465 grub_enter_normal_mode (argv
[0]);
472 grub_cmdline_run (int nested
)
474 grub_reader_t reader
= grub_reader_get_current ();
476 reader_nested
= nested
;
479 grub_reader_loop (0);
483 grub_normal_reader_init (void)
485 grub_normal_init_page ();
489 [ Minimal BASH-like line editing is supported. For the first word, TAB\n\
490 lists possible command completions. Anywhere else TAB lists possible\n\
491 device/file completions.%s ]\n\n",
492 reader_nested
? " ESC at any time exits." : "");
497 static char cmdline
[GRUB_MAX_CMDLINE
];
500 grub_normal_read_line (char **line
, int cont
)
502 grub_parser_t parser
= grub_parser_get_current ();
503 char prompt
[8 + grub_strlen (parser
->name
)];
505 grub_sprintf (prompt
, "%s:%s> ", parser
->name
, (cont
) ? "" : "grub");
510 if (grub_cmdline_get (prompt
, cmdline
, sizeof (cmdline
), 0, 1))
513 if ((reader_nested
) || (cont
))
520 *line
= grub_strdup (cmdline
);
524 static struct grub_reader grub_normal_reader
=
527 .init
= grub_normal_reader_init
,
528 .read_line
= grub_normal_read_line
532 grub_env_write_pager (struct grub_env_var
*var
__attribute__ ((unused
)),
535 grub_set_more ((*val
== '1'));
536 return grub_strdup (val
);
539 GRUB_MOD_INIT(normal
)
541 /* Normal mode shouldn't be unloaded. */
545 grub_menu_viewer_register (&grub_normal_text_menu_viewer
);
547 grub_set_history (GRUB_DEFAULT_HISTORY_SIZE
);
549 grub_reader_register ("normal", &grub_normal_reader
);
550 grub_reader_set_current (&grub_normal_reader
);
551 grub_register_variable_hook ("pager", 0, grub_env_write_pager
);
553 /* Register a command "normal" for the rescue mode. */
554 grub_register_command_prio ("normal", grub_cmd_normal
,
555 0, "Enter normal mode", 0);
557 /* Reload terminal colors when these variables are written to. */
558 grub_register_variable_hook ("color_normal", NULL
, grub_env_write_color_normal
);
559 grub_register_variable_hook ("color_highlight", NULL
, grub_env_write_color_highlight
);
561 /* Preserve hooks after context changes. */
562 grub_env_export ("color_normal");
563 grub_env_export ("color_highlight");
566 GRUB_MOD_FINI(normal
)
568 grub_set_history (0);
569 grub_reader_unregister (&grub_normal_reader
);
570 grub_register_variable_hook ("pager", 0, 0);
571 grub_fs_autoload_hook
= 0;
572 free_handler_list ();