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