Home | History | Annotate | Download | only in stage2
      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
     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
     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
     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 *
     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
    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
    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
    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
    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
    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
    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
    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