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