1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2000,2001,2002,2004,2005 Free Software Foundation, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <shared.h>
21 #include <term.h>
22
23 grub_jmp_buf restart_env;
24
25 #if defined(PRESET_MENU_STRING) && defined(PRESET_MENU_EXTERNAL)
26 #error Defining both PRESET_MENU_STRING and PRESET_MENU_EXTERNAL does not \
27 make sense. Please only define one.
28 #endif
29
30 #if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS) || \
31 defined(PRESET_MENU_EXTERNAL)
32
33 # if defined(PRESET_MENU_STRING)
34 static const char *preset_menu = PRESET_MENU_STRING;
35 # elif defined(PRESET_MENU_EXTERNAL)
36 extern const char *preset_menu;
37 # elif defined(SUPPORT_DISKLESS)
38 /* Execute the command "bootp" automatically. */
39 static const char *preset_menu = "bootp\n";
40 # endif /* SUPPORT_DISKLESS */
41
42 static int preset_menu_offset;
43
44 static int
open_preset_menu(void)45 open_preset_menu (void)
46 {
47 #ifdef GRUB_UTIL
48 /* Unless the user explicitly requests to use the preset menu,
49 always opening the preset menu fails in the grub shell. */
50 if (! use_preset_menu)
51 return 0;
52 #endif /* GRUB_UTIL */
53
54 preset_menu_offset = 0;
55 return preset_menu != 0;
56 }
57
58 static int
read_from_preset_menu(char * buf,int maxlen)59 read_from_preset_menu (char *buf, int maxlen)
60 {
61 int len = grub_strlen (preset_menu + preset_menu_offset);
62
63 if (len > maxlen)
64 len = maxlen;
65
66 grub_memmove (buf, preset_menu + preset_menu_offset, len);
67 preset_menu_offset += len;
68
69 return len;
70 }
71
72 static void
close_preset_menu(void)73 close_preset_menu (void)
74 {
75 /* Disable the preset menu. */
76 preset_menu = 0;
77 }
78
79 #else /* ! PRESET_MENU_STRING && ! SUPPORT_DISKLESS */
80
81 #define open_preset_menu() 0
82 #define read_from_preset_menu(buf, maxlen) 0
83 #define close_preset_menu()
84
85 #endif /* ! PRESET_MENU_STRING && ! SUPPORT_DISKLESS */
86
87 static char *
get_entry(char * list,int num,int nested)88 get_entry (char *list, int num, int nested)
89 {
90 int i;
91
92 for (i = 0; i < num; i++)
93 {
94 do
95 {
96 while (*(list++));
97 }
98 while (nested && *(list++));
99 }
100
101 return list;
102 }
103
104 /* Print an entry in a line of the menu box. */
105 static void
print_entry(int y,int highlight,char * entry)106 print_entry (int y, int highlight, char *entry)
107 {
108 int x;
109
110 if (current_term->setcolorstate)
111 current_term->setcolorstate (COLOR_STATE_NORMAL);
112
113 if (highlight && current_term->setcolorstate)
114 current_term->setcolorstate (COLOR_STATE_HIGHLIGHT);
115
116 gotoxy (2, y);
117 grub_putchar (' ');
118 for (x = 3; x < 75; x++)
119 {
120 if (*entry && x <= 72)
121 {
122 if (x == 72)
123 grub_putchar (DISP_RIGHT);
124 else
125 grub_putchar (*entry++);
126 }
127 else
128 grub_putchar (' ');
129 }
130 gotoxy (74, y);
131
132 if (current_term->setcolorstate)
133 current_term->setcolorstate (COLOR_STATE_STANDARD);
134 }
135
136 /* Print entries in the menu box. */
137 static void
print_entries(int y,int size,int first,int entryno,char * menu_entries)138 print_entries (int y, int size, int first, int entryno, char *menu_entries)
139 {
140 int i;
141
142 gotoxy (77, y + 1);
143
144 if (first)
145 grub_putchar (DISP_UP);
146 else
147 grub_putchar (' ');
148
149 menu_entries = get_entry (menu_entries, first, 0);
150
151 for (i = 0; i < size; i++)
152 {
153 print_entry (y + i + 1, entryno == i, menu_entries);
154
155 while (*menu_entries)
156 menu_entries++;
157
158 if (*(menu_entries - 1))
159 menu_entries++;
160 }
161
162 gotoxy (77, y + size);
163
164 if (*menu_entries)
165 grub_putchar (DISP_DOWN);
166 else
167 grub_putchar (' ');
168
169 gotoxy (74, y + entryno + 1);
170 }
171
172 static void
print_entries_raw(int size,int first,char * menu_entries)173 print_entries_raw (int size, int first, char *menu_entries)
174 {
175 int i;
176
177 #define LINE_LENGTH 67
178
179 for (i = 0; i < LINE_LENGTH; i++)
180 grub_putchar ('-');
181 grub_putchar ('\n');
182
183 for (i = first; i < size; i++)
184 {
185 /* grub's printf can't %02d so ... */
186 if (i < 10)
187 grub_putchar (' ');
188 grub_printf ("%d: %s\n", i, get_entry (menu_entries, i, 0));
189 }
190
191 for (i = 0; i < LINE_LENGTH; i++)
192 grub_putchar ('-');
193 grub_putchar ('\n');
194
195 #undef LINE_LENGTH
196 }
197
198
199 static void
print_border(int y,int size)200 print_border (int y, int size)
201 {
202 int i;
203
204 if (current_term->setcolorstate)
205 current_term->setcolorstate (COLOR_STATE_NORMAL);
206
207 gotoxy (1, y);
208
209 grub_putchar (DISP_UL);
210 for (i = 0; i < 73; i++)
211 grub_putchar (DISP_HORIZ);
212 grub_putchar (DISP_UR);
213
214 i = 1;
215 while (1)
216 {
217 gotoxy (1, y + i);
218
219 if (i > size)
220 break;
221
222 grub_putchar (DISP_VERT);
223 gotoxy (75, y + i);
224 grub_putchar (DISP_VERT);
225
226 i++;
227 }
228
229 grub_putchar (DISP_LL);
230 for (i = 0; i < 73; i++)
231 grub_putchar (DISP_HORIZ);
232 grub_putchar (DISP_LR);
233
234 if (current_term->setcolorstate)
235 current_term->setcolorstate (COLOR_STATE_STANDARD);
236 }
237
238 static void
run_menu(char * menu_entries,char * config_entries,int num_entries,char * heap,int entryno)239 run_menu (char *menu_entries, char *config_entries, int num_entries,
240 char *heap, int entryno)
241 {
242 int c, time1, time2 = -1, first_entry = 0;
243 char *cur_entry = 0;
244
245 /*
246 * Main loop for menu UI.
247 */
248
249 restart:
250 /* Dumb terminal always use all entries for display
251 invariant for TERM_DUMB: first_entry == 0 */
252 if (! (current_term->flags & TERM_DUMB))
253 {
254 while (entryno > 11)
255 {
256 first_entry++;
257 entryno--;
258 }
259 }
260
261 /* If the timeout was expired or wasn't set, force to show the menu
262 interface. */
263 if (grub_timeout < 0)
264 show_menu = 1;
265
266 /* If SHOW_MENU is false, don't display the menu until ESC is pressed. */
267 if (! show_menu)
268 {
269 /* Get current time. */
270 while ((time1 = getrtsecs ()) == 0xFF)
271 ;
272
273 while (1)
274 {
275 /* Check if ESC is pressed. */
276 if (checkkey () != -1 && ASCII_CHAR (getkey ()) == '\e')
277 {
278 grub_timeout = -1;
279 show_menu = 1;
280 break;
281 }
282
283 /* If GRUB_TIMEOUT is expired, boot the default entry. */
284 if (grub_timeout >=0
285 && (time1 = getrtsecs ()) != time2
286 && time1 != 0xFF)
287 {
288 if (grub_timeout <= 0)
289 {
290 grub_timeout = -1;
291 goto boot_entry;
292 }
293
294 time2 = time1;
295 grub_timeout--;
296
297 /* Print a message. */
298 grub_printf ("\rPress `ESC' to enter the menu... %d ",
299 grub_timeout);
300 }
301 }
302 }
303
304 /* Only display the menu if the user wants to see it. */
305 if (show_menu)
306 {
307 init_page ();
308 setcursor (0);
309
310 if (current_term->flags & TERM_DUMB)
311 print_entries_raw (num_entries, first_entry, menu_entries);
312 else
313 print_border (3, 12);
314
315 grub_printf ("\n\
316 Use the %c and %c keys to select which entry is highlighted.\n",
317 DISP_UP, DISP_DOWN);
318
319 if (! auth && password)
320 {
321 printf ("\
322 Press enter to boot the selected OS or \'p\' to enter a\n\
323 password to unlock the next set of features.");
324 }
325 else
326 {
327 if (config_entries)
328 printf ("\
329 Press enter to boot the selected OS, \'e\' to edit the\n\
330 commands before booting, or \'c\' for a command-line.");
331 else
332 printf ("\
333 Press \'b\' to boot, \'e\' to edit the selected command in the\n\
334 boot sequence, \'c\' for a command-line, \'o\' to open a new line\n\
335 after (\'O\' for before) the selected line, \'d\' to remove the\n\
336 selected line, or escape to go back to the main menu.");
337 }
338
339 if (current_term->flags & TERM_DUMB)
340 grub_printf ("\n\nThe selected entry is %d ", entryno);
341 else
342 print_entries (3, 12, first_entry, entryno, menu_entries);
343 }
344
345 /* XX using RT clock now, need to initialize value */
346 while ((time1 = getrtsecs()) == 0xFF);
347
348 while (1)
349 {
350 /* Initialize to NULL just in case... */
351 cur_entry = NULL;
352
353 if (grub_timeout >= 0 && (time1 = getrtsecs()) != time2 && time1 != 0xFF)
354 {
355 if (grub_timeout <= 0)
356 {
357 grub_timeout = -1;
358 break;
359 }
360
361 /* else not booting yet! */
362 time2 = time1;
363
364 if (current_term->flags & TERM_DUMB)
365 grub_printf ("\r Entry %d will be booted automatically in %d seconds. ",
366 entryno, grub_timeout);
367 else
368 {
369 gotoxy (3, 22);
370 grub_printf ("The highlighted entry will be booted automatically in %d seconds. ",
371 grub_timeout);
372 gotoxy (74, 4 + entryno);
373 }
374
375 grub_timeout--;
376 }
377
378 /* Check for a keypress, however if TIMEOUT has been expired
379 (GRUB_TIMEOUT == -1) relax in GETKEY even if no key has been
380 pressed.
381 This avoids polling (relevant in the grub-shell and later on
382 in grub if interrupt driven I/O is done). */
383 if (checkkey () >= 0 || grub_timeout < 0)
384 {
385 /* Key was pressed, show which entry is selected before GETKEY,
386 since we're comming in here also on GRUB_TIMEOUT == -1 and
387 hang in GETKEY */
388 if (current_term->flags & TERM_DUMB)
389 grub_printf ("\r Highlighted entry is %d: ", entryno);
390
391 c = ASCII_CHAR (getkey ());
392
393 if (grub_timeout >= 0)
394 {
395 if (current_term->flags & TERM_DUMB)
396 grub_putchar ('\r');
397 else
398 gotoxy (3, 22);
399 printf (" ");
400 grub_timeout = -1;
401 fallback_entryno = -1;
402 if (! (current_term->flags & TERM_DUMB))
403 gotoxy (74, 4 + entryno);
404 }
405
406 /* We told them above (at least in SUPPORT_SERIAL) to use
407 '^' or 'v' so accept these keys. */
408 if (c == 16 || c == '^')
409 {
410 if (current_term->flags & TERM_DUMB)
411 {
412 if (entryno > 0)
413 entryno--;
414 }
415 else
416 {
417 if (entryno > 0)
418 {
419 print_entry (4 + entryno, 0,
420 get_entry (menu_entries,
421 first_entry + entryno,
422 0));
423 entryno--;
424 print_entry (4 + entryno, 1,
425 get_entry (menu_entries,
426 first_entry + entryno,
427 0));
428 }
429 else if (first_entry > 0)
430 {
431 first_entry--;
432 print_entries (3, 12, first_entry, entryno,
433 menu_entries);
434 }
435 }
436 }
437 else if ((c == 14 || c == 'v')
438 && first_entry + entryno + 1 < num_entries)
439 {
440 if (current_term->flags & TERM_DUMB)
441 entryno++;
442 else
443 {
444 if (entryno < 11)
445 {
446 print_entry (4 + entryno, 0,
447 get_entry (menu_entries,
448 first_entry + entryno,
449 0));
450 entryno++;
451 print_entry (4 + entryno, 1,
452 get_entry (menu_entries,
453 first_entry + entryno,
454 0));
455 }
456 else if (num_entries > 12 + first_entry)
457 {
458 first_entry++;
459 print_entries (3, 12, first_entry, entryno, menu_entries);
460 }
461 }
462 }
463 else if (c == 7)
464 {
465 /* Page Up */
466 first_entry -= 12;
467 if (first_entry < 0)
468 {
469 entryno += first_entry;
470 first_entry = 0;
471 if (entryno < 0)
472 entryno = 0;
473 }
474 print_entries (3, 12, first_entry, entryno, menu_entries);
475 }
476 else if (c == 3)
477 {
478 /* Page Down */
479 first_entry += 12;
480 if (first_entry + entryno + 1 >= num_entries)
481 {
482 first_entry = num_entries - 12;
483 if (first_entry < 0)
484 first_entry = 0;
485 entryno = num_entries - first_entry - 1;
486 }
487 print_entries (3, 12, first_entry, entryno, menu_entries);
488 }
489
490 if (config_entries)
491 {
492 if ((c == '\n') || (c == '\r') || (c == 6))
493 break;
494 }
495 else
496 {
497 if ((c == 'd') || (c == 'o') || (c == 'O'))
498 {
499 if (! (current_term->flags & TERM_DUMB))
500 print_entry (4 + entryno, 0,
501 get_entry (menu_entries,
502 first_entry + entryno,
503 0));
504
505 /* insert after is almost exactly like insert before */
506 if (c == 'o')
507 {
508 /* But `o' differs from `O', since it may causes
509 the menu screen to scroll up. */
510 if (entryno < 11 || (current_term->flags & TERM_DUMB))
511 entryno++;
512 else
513 first_entry++;
514
515 c = 'O';
516 }
517
518 cur_entry = get_entry (menu_entries,
519 first_entry + entryno,
520 0);
521
522 if (c == 'O')
523 {
524 grub_memmove (cur_entry + 2, cur_entry,
525 ((int) heap) - ((int) cur_entry));
526
527 cur_entry[0] = ' ';
528 cur_entry[1] = 0;
529
530 heap += 2;
531
532 num_entries++;
533 }
534 else if (num_entries > 0)
535 {
536 char *ptr = get_entry(menu_entries,
537 first_entry + entryno + 1,
538 0);
539
540 grub_memmove (cur_entry, ptr,
541 ((int) heap) - ((int) ptr));
542 heap -= (((int) ptr) - ((int) cur_entry));
543
544 num_entries--;
545
546 if (entryno >= num_entries)
547 entryno--;
548 if (first_entry && num_entries < 12 + first_entry)
549 first_entry--;
550 }
551
552 if (current_term->flags & TERM_DUMB)
553 {
554 grub_printf ("\n\n");
555 print_entries_raw (num_entries, first_entry,
556 menu_entries);
557 grub_printf ("\n");
558 }
559 else
560 print_entries (3, 12, first_entry, entryno, menu_entries);
561 }
562
563 cur_entry = menu_entries;
564 if (c == 27)
565 return;
566 if (c == 'b')
567 break;
568 }
569
570 if (! auth && password)
571 {
572 if (c == 'p')
573 {
574 /* Do password check here! */
575 char entered[32];
576 char *pptr = password;
577
578 if (current_term->flags & TERM_DUMB)
579 grub_printf ("\r ");
580 else
581 gotoxy (1, 21);
582
583 /* Wipe out the previously entered password */
584 grub_memset (entered, 0, sizeof (entered));
585 get_cmdline (" Password: ", entered, 31, '*', 0);
586
587 while (! isspace (*pptr) && *pptr)
588 pptr++;
589
590 /* Make sure that PASSWORD is NUL-terminated. */
591 *pptr++ = 0;
592
593 if (! check_password (entered, password, password_type))
594 {
595 char *new_file = config_file;
596 while (isspace (*pptr))
597 pptr++;
598
599 /* If *PPTR is NUL, then allow the user to use
600 privileged instructions, otherwise, load
601 another configuration file. */
602 if (*pptr != 0)
603 {
604 while ((*(new_file++) = *(pptr++)) != 0)
605 ;
606
607 /* Make sure that the user will not have
608 authority in the next configuration. */
609 auth = 0;
610 return;
611 }
612 else
613 {
614 /* Now the user is superhuman. */
615 auth = 1;
616 goto restart;
617 }
618 }
619 else
620 {
621 grub_printf ("Failed!\n Press any key to continue...");
622 getkey ();
623 goto restart;
624 }
625 }
626 }
627 else
628 {
629 if (c == 'e')
630 {
631 int new_num_entries = 0, i = 0;
632 char *new_heap;
633
634 if (config_entries)
635 {
636 new_heap = heap;
637 cur_entry = get_entry (config_entries,
638 first_entry + entryno,
639 1);
640 }
641 else
642 {
643 /* safe area! */
644 new_heap = heap + NEW_HEAPSIZE + 1;
645 cur_entry = get_entry (menu_entries,
646 first_entry + entryno,
647 0);
648 }
649
650 do
651 {
652 while ((*(new_heap++) = cur_entry[i++]) != 0);
653 new_num_entries++;
654 }
655 while (config_entries && cur_entry[i]);
656
657 /* this only needs to be done if config_entries is non-NULL,
658 but it doesn't hurt to do it always */
659 *(new_heap++) = 0;
660
661 if (config_entries)
662 run_menu (heap, NULL, new_num_entries, new_heap, 0);
663 else
664 {
665 cls ();
666 print_cmdline_message (0);
667
668 new_heap = heap + NEW_HEAPSIZE + 1;
669
670 saved_drive = boot_drive;
671 saved_partition = install_partition;
672 current_drive = GRUB_INVALID_DRIVE;
673
674 if (! get_cmdline (PACKAGE " edit> ", new_heap,
675 NEW_HEAPSIZE + 1, 0, 1))
676 {
677 int j = 0;
678
679 /* get length of new command */
680 while (new_heap[j++])
681 ;
682
683 if (j < 2)
684 {
685 j = 2;
686 new_heap[0] = ' ';
687 new_heap[1] = 0;
688 }
689
690 /* align rest of commands properly */
691 grub_memmove (cur_entry + j, cur_entry + i,
692 (int) heap - ((int) cur_entry + i));
693
694 /* copy command to correct area */
695 grub_memmove (cur_entry, new_heap, j);
696
697 heap += (j - i);
698 }
699 }
700
701 goto restart;
702 }
703 if (c == 'c')
704 {
705 enter_cmdline (heap, 0);
706 goto restart;
707 }
708 #ifdef GRUB_UTIL
709 if (c == 'q')
710 {
711 /* The same as ``quit''. */
712 stop ();
713 }
714 #endif
715 }
716 }
717 }
718
719 /* Attempt to boot an entry. */
720
721 boot_entry:
722
723 cls ();
724 setcursor (1);
725
726 while (1)
727 {
728 if (config_entries)
729 printf (" Booting \'%s\'\n\n",
730 get_entry (menu_entries, first_entry + entryno, 0));
731 else
732 printf (" Booting command-list\n\n");
733
734 if (! cur_entry)
735 cur_entry = get_entry (config_entries, first_entry + entryno, 1);
736
737 /* Set CURRENT_ENTRYNO for the command "savedefault". */
738 current_entryno = first_entry + entryno;
739
740 if (run_script (cur_entry, heap))
741 {
742 if (fallback_entryno >= 0)
743 {
744 cur_entry = NULL;
745 first_entry = 0;
746 entryno = fallback_entries[fallback_entryno];
747 fallback_entryno++;
748 if (fallback_entryno >= MAX_FALLBACK_ENTRIES
749 || fallback_entries[fallback_entryno] < 0)
750 fallback_entryno = -1;
751 }
752 else
753 break;
754 }
755 else
756 break;
757 }
758
759 show_menu = 1;
760 goto restart;
761 }
762
763
764 static int
get_line_from_config(char * cmdline,int maxlen,int read_from_file)765 get_line_from_config (char *cmdline, int maxlen, int read_from_file)
766 {
767 int pos = 0, literal = 0, comment = 0;
768 char c; /* since we're loading it a byte at a time! */
769
770 while (1)
771 {
772 if (read_from_file)
773 {
774 if (! grub_read (&c, 1))
775 break;
776 }
777 else
778 {
779 if (! read_from_preset_menu (&c, 1))
780 break;
781 }
782
783 /* Skip all carriage returns. */
784 if (c == '\r')
785 continue;
786
787 /* Replace tabs with spaces. */
788 if (c == '\t')
789 c = ' ';
790
791 /* The previous is a backslash, then... */
792 if (literal)
793 {
794 /* If it is a newline, replace it with a space and continue. */
795 if (c == '\n')
796 {
797 c = ' ';
798
799 /* Go back to overwrite a backslash. */
800 if (pos > 0)
801 pos--;
802 }
803
804 literal = 0;
805 }
806
807 /* translate characters first! */
808 if (c == '\\' && ! literal)
809 literal = 1;
810
811 if (comment)
812 {
813 if (c == '\n')
814 comment = 0;
815 }
816 else if (! pos)
817 {
818 if (c == '#')
819 comment = 1;
820 else if ((c != ' ') && (c != '\n'))
821 cmdline[pos++] = c;
822 }
823 else
824 {
825 if (c == '\n')
826 break;
827
828 if (pos < maxlen)
829 cmdline[pos++] = c;
830 }
831 }
832
833 cmdline[pos] = 0;
834
835 return pos;
836 }
837
838
839 /* This is the starting function in C. */
840 void
cmain(void)841 cmain (void)
842 {
843 int config_len, menu_len, num_entries;
844 char *config_entries, *menu_entries;
845 char *kill_buf = (char *) KILL_BUF;
846
847 auto void reset (void);
848 void reset (void)
849 {
850 count_lines = -1;
851 config_len = 0;
852 menu_len = 0;
853 num_entries = 0;
854 config_entries = (char *) mbi.drives_addr + mbi.drives_length;
855 menu_entries = (char *) MENU_BUF;
856 init_config ();
857 }
858
859 /* Initialize the environment for restarting Stage 2. */
860 grub_setjmp (restart_env);
861
862 /* Initialize the kill buffer. */
863 *kill_buf = 0;
864
865 /* Never return. */
866 for (;;)
867 {
868 int is_opened, is_preset;
869
870 reset ();
871
872 /* Here load the configuration file. */
873
874 #ifdef GRUB_UTIL
875 if (use_config_file)
876 #endif /* GRUB_UTIL */
877 {
878 char *default_file = (char *) DEFAULT_FILE_BUF;
879 int i;
880
881 /* Get a saved default entry if possible. */
882 saved_entryno = 0;
883 *default_file = 0;
884 grub_strncat (default_file, config_file, DEFAULT_FILE_BUFLEN);
885 for (i = grub_strlen(default_file); i >= 0; i--)
886 if (default_file[i] == '/')
887 {
888 i++;
889 break;
890 }
891 default_file[i] = 0;
892 grub_strncat (default_file + i, "default", DEFAULT_FILE_BUFLEN - i);
893 if (grub_open (default_file))
894 {
895 char buf[10]; /* This is good enough. */
896 char *p = buf;
897 int len;
898
899 len = grub_read (buf, sizeof (buf));
900 if (len > 0)
901 {
902 buf[sizeof (buf) - 1] = 0;
903 safe_parse_maxint (&p, &saved_entryno);
904 }
905
906 grub_close ();
907 }
908 errnum = ERR_NONE;
909
910 do
911 {
912 /* STATE 0: Before any title command.
913 STATE 1: In a title command.
914 STATE >1: In a entry after a title command. */
915 int state = 0, prev_config_len = 0, prev_menu_len = 0;
916 char *cmdline;
917
918 /* Try the preset menu first. This will succeed at most once,
919 because close_preset_menu disables the preset menu. */
920 is_opened = is_preset = open_preset_menu ();
921 if (! is_opened)
922 {
923 is_opened = grub_open (config_file);
924 errnum = ERR_NONE;
925 }
926
927 if (! is_opened)
928 break;
929
930 /* This is necessary, because the menu must be overrided. */
931 reset ();
932
933 cmdline = (char *) CMDLINE_BUF;
934 while (get_line_from_config (cmdline, NEW_HEAPSIZE,
935 ! is_preset))
936 {
937 struct builtin *builtin;
938
939 /* Get the pointer to the builtin structure. */
940 builtin = find_command (cmdline);
941 errnum = 0;
942 if (! builtin)
943 /* Unknown command. Just skip now. */
944 continue;
945
946 if (builtin->flags & BUILTIN_TITLE)
947 {
948 char *ptr;
949
950 /* the command "title" is specially treated. */
951 if (state > 1)
952 {
953 /* The next title is found. */
954 num_entries++;
955 config_entries[config_len++] = 0;
956 prev_menu_len = menu_len;
957 prev_config_len = config_len;
958 }
959 else
960 {
961 /* The first title is found. */
962 menu_len = prev_menu_len;
963 config_len = prev_config_len;
964 }
965
966 /* Reset the state. */
967 state = 1;
968
969 /* Copy title into menu area. */
970 ptr = skip_to (1, cmdline);
971 while ((menu_entries[menu_len++] = *(ptr++)) != 0)
972 ;
973 }
974 else if (! state)
975 {
976 /* Run a command found is possible. */
977 if (builtin->flags & BUILTIN_MENU)
978 {
979 char *arg = skip_to (1, cmdline);
980 (builtin->func) (arg, BUILTIN_MENU);
981 errnum = 0;
982 }
983 else
984 /* Ignored. */
985 continue;
986 }
987 else
988 {
989 char *ptr = cmdline;
990
991 state++;
992 /* Copy config file data to config area. */
993 while ((config_entries[config_len++] = *ptr++) != 0)
994 ;
995 }
996 }
997
998 if (state > 1)
999 {
1000 /* Finish the last entry. */
1001 num_entries++;
1002 config_entries[config_len++] = 0;
1003 }
1004 else
1005 {
1006 menu_len = prev_menu_len;
1007 config_len = prev_config_len;
1008 }
1009
1010 menu_entries[menu_len++] = 0;
1011 config_entries[config_len++] = 0;
1012 grub_memmove (config_entries + config_len, menu_entries,
1013 menu_len);
1014 menu_entries = config_entries + config_len;
1015
1016 /* Make sure that all fallback entries are valid. */
1017 if (fallback_entryno >= 0)
1018 {
1019 for (i = 0; i < MAX_FALLBACK_ENTRIES; i++)
1020 {
1021 if (fallback_entries[i] < 0)
1022 break;
1023 if (fallback_entries[i] >= num_entries)
1024 {
1025 grub_memmove (fallback_entries + i,
1026 fallback_entries + i + 1,
1027 ((MAX_FALLBACK_ENTRIES - i - 1)
1028 * sizeof (int)));
1029 i--;
1030 }
1031 }
1032
1033 if (fallback_entries[0] < 0)
1034 fallback_entryno = -1;
1035 }
1036 /* Check if the default entry is present. Otherwise reset
1037 it to fallback if fallback is valid, or to DEFAULT_ENTRY
1038 if not. */
1039 if (default_entry >= num_entries)
1040 {
1041 if (fallback_entryno >= 0)
1042 {
1043 default_entry = fallback_entries[0];
1044 fallback_entryno++;
1045 if (fallback_entryno >= MAX_FALLBACK_ENTRIES
1046 || fallback_entries[fallback_entryno] < 0)
1047 fallback_entryno = -1;
1048 }
1049 else
1050 default_entry = 0;
1051 }
1052
1053 if (is_preset)
1054 close_preset_menu ();
1055 else
1056 grub_close ();
1057 }
1058 while (is_preset);
1059 }
1060
1061 if (! num_entries)
1062 {
1063 /* If no acceptable config file, goto command-line, starting
1064 heap from where the config entries would have been stored
1065 if there were any. */
1066 enter_cmdline (config_entries, 1);
1067 }
1068 else
1069 {
1070 /* Run menu interface. */
1071 run_menu (menu_entries, config_entries, num_entries,
1072 menu_entries + menu_len, default_entry);
1073 }
1074 }
1075 }
1076