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