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
23grub_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)
34static const char *preset_menu = PRESET_MENU_STRING;
35# elif defined(PRESET_MENU_EXTERNAL)
36extern const char *preset_menu;
37# elif defined(SUPPORT_DISKLESS)
38/* Execute the command "bootp" automatically.  */
39static const char *preset_menu = "bootp\n";
40# endif /* SUPPORT_DISKLESS */
41
42static int preset_menu_offset;
43
44static int
45open_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
58static int
59read_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
72static void
73close_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
87static char *
88get_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.  */
105static void
106print_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.  */
137static void
138print_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
172static void
173print_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
199static void
200print_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
238static void
239run_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
249restart:
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
764static int
765get_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.  */
840void
841cmain (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