1 /* cmdline.c - the device-independent GRUB text command line */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 1999,2000,2001,2002,2004  Free Software Foundation, Inc.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include <shared.h>
22 #include <term.h>
23 
24 extern struct term_entry *current_term;
25 
26 #ifdef SUPPORT_DISKLESS
27 # include <grub.h>
28 #endif
29 
30 grub_jmp_buf restart_cmdline_env;
31 
32 /* Find the next word from CMDLINE and return the pointer. If
33    AFTER_EQUAL is non-zero, assume that the character `=' is treated as
34    a space. Caution: this assumption is for backward compatibility.  */
35 char *
skip_to(int after_equal,char * cmdline)36 skip_to (int after_equal, char *cmdline)
37 {
38   /* Skip until we hit whitespace, or maybe an equal sign. */
39   while (*cmdline && *cmdline != ' ' && *cmdline != '\t' &&
40 	 ! (after_equal && *cmdline == '='))
41     cmdline ++;
42 
43   /* Skip whitespace, and maybe equal signs. */
44   while (*cmdline == ' ' || *cmdline == '\t' ||
45 	 (after_equal && *cmdline == '='))
46     cmdline ++;
47 
48   return cmdline;
49 }
50 
51 /* Print a helpful message for the command-line interface.  */
52 void
print_cmdline_message(int forever)53 print_cmdline_message (int forever)
54 {
55   printf (" [ Minimal BASH-like line editing is supported.  For the first word, TAB\n"
56 	  "   lists possible command completions.  Anywhere else TAB lists the possible\n"
57 	  "   completions of a device/filename.%s ]\n",
58 	  (forever ? "" : "  ESC at any time exits."));
59 }
60 
61 /* Find the builtin whose command name is COMMAND and return the
62    pointer. If not found, return 0.  */
63 struct builtin *
find_command(char * command)64 find_command (char *command)
65 {
66   char *ptr;
67   char c;
68   struct builtin **builtin;
69 
70   /* Find the first space and terminate the command name.  */
71   ptr = command;
72   while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '=')
73     ptr ++;
74 
75   c = *ptr;
76   *ptr = 0;
77 
78   /* Seek out the builtin whose command name is COMMAND.  */
79   for (builtin = builtin_table; *builtin != 0; builtin++)
80     {
81       int ret = grub_strcmp (command, (*builtin)->name);
82 
83       if (ret == 0)
84 	{
85 	  /* Find the builtin for COMMAND.  */
86 	  *ptr = c;
87 	  return *builtin;
88 	}
89       else if (ret < 0)
90 	break;
91     }
92 
93   /* Cannot find COMMAND.  */
94   errnum = ERR_UNRECOGNIZED;
95   *ptr = c;
96   return 0;
97 }
98 
99 /* Initialize the data for the command-line.  */
100 static void
init_cmdline(void)101 init_cmdline (void)
102 {
103   /* Initialization.  */
104   saved_drive = boot_drive;
105   saved_partition = install_partition;
106   current_drive = GRUB_INVALID_DRIVE;
107   errnum = 0;
108   count_lines = -1;
109 
110   /* Restore memory probe state.  */
111   mbi.mem_upper = saved_mem_upper;
112   if (mbi.mmap_length)
113     mbi.flags |= MB_INFO_MEM_MAP;
114 
115   /* Initialize the data for the builtin commands.  */
116   init_builtins ();
117 }
118 
119 /* Enter the command-line interface. HEAP is used for the command-line
120    buffer. Return only if FOREVER is nonzero and get_cmdline returns
121    nonzero (ESC is pushed).  */
122 void
enter_cmdline(char * heap,int forever)123 enter_cmdline (char *heap, int forever)
124 {
125   /* Initialize the data and print a message.  */
126   init_cmdline ();
127   grub_setjmp (restart_cmdline_env);
128   init_page ();
129 #ifdef SUPPORT_DISKLESS
130   print_network_configuration ();
131   grub_putchar ('\n');
132 #endif
133   print_cmdline_message (forever);
134 
135   while (1)
136     {
137       struct builtin *builtin;
138       char *arg;
139 
140       *heap = 0;
141       print_error ();
142       errnum = ERR_NONE;
143 
144       /* Get the command-line with the minimal BASH-like interface.  */
145       if (get_cmdline (PACKAGE "> ", heap, 2048, 0, 1))
146 	return;
147 
148       /* If there was no command, grab a new one. */
149       if (! heap[0])
150 	continue;
151 
152       /* Find a builtin.  */
153       builtin = find_command (heap);
154       if (! builtin)
155 	continue;
156 
157       /* If BUILTIN cannot be run in the command-line, skip it.  */
158       if (! (builtin->flags & BUILTIN_CMDLINE))
159 	{
160 	  errnum = ERR_UNRECOGNIZED;
161 	  continue;
162 	}
163 
164       /* Invalidate the cache, because the user may exchange removable
165 	 disks.  */
166       buf_drive = -1;
167 
168       /* Start to count lines, only if the internal pager is in use.  */
169       if (use_pager)
170 	count_lines = 0;
171 
172       /* Run BUILTIN->FUNC.  */
173       arg = skip_to (1, heap);
174       (builtin->func) (arg, BUILTIN_CMDLINE);
175 
176       /* Finish the line count.  */
177       count_lines = -1;
178     }
179 }
180 
181 /* Run an entry from the script SCRIPT. HEAP is used for the
182    command-line buffer. If an error occurs, return non-zero, otherwise
183    return zero.  */
184 int
run_script(char * script,char * heap)185 run_script (char *script, char *heap)
186 {
187   char *old_entry;
188   char *cur_entry = script;
189 
190   /* Initialize the data.  */
191   init_cmdline ();
192 
193   while (1)
194     {
195       struct builtin *builtin;
196       char *arg;
197 
198       print_error ();
199 
200       if (errnum)
201 	{
202 	  errnum = ERR_NONE;
203 
204           /*
205            * At this point something must have gone wrong, so dump the
206            * buffer and flip output back on.
207            */
208           builtin = find_command("verbose");
209           (builtin->func) ("on", BUILTIN_SCRIPT);
210 
211 	  /* If a fallback entry is defined, don't prompt a user's
212 	     intervention.  */
213 	  if (fallback_entryno < 0)
214 	    {
215 	      grub_printf ("\nPress any key to continue...");
216 	      (void) getkey ();
217 	    }
218 
219 	  return 1;
220 	}
221 
222       /* Copy the first string in CUR_ENTRY to HEAP.  */
223       old_entry = cur_entry;
224       while (*cur_entry++)
225 	;
226 
227       grub_memmove (heap, old_entry, (int) cur_entry - (int) old_entry);
228       if (! *heap)
229 	{
230 	  /* If there is no more command in SCRIPT...  */
231 
232 	  /* If any kernel is not loaded, just exit successfully.  */
233 	  if (kernel_type == KERNEL_TYPE_NONE)
234 	    return 0;
235 
236           if (reset_term)
237             if (current_term->shutdown) {
238               (*current_term->shutdown)();
239               current_term = term_table; /* assumption: console is first */
240             }
241 
242 
243 	  /* Otherwise, the command boot is run implicitly.  */
244 	  grub_memmove (heap, "boot", 5);
245 	}
246 
247       /* Find a builtin.  */
248       builtin = find_command (heap);
249       if (! builtin)
250 	{
251 	  grub_printf ("%s\n", old_entry);
252 	  continue;
253 	}
254 
255       if (! (builtin->flags & BUILTIN_NO_ECHO))
256 	grub_printf ("%s\n", old_entry);
257 
258       /* If BUILTIN cannot be run in the command-line, skip it.  */
259       if (! (builtin->flags & BUILTIN_CMDLINE))
260 	{
261 	  errnum = ERR_UNRECOGNIZED;
262 	  continue;
263 	}
264 
265       /* Invalidate the cache, because the user may exchange removable
266 	 disks.  */
267       buf_drive = -1;
268 
269       /* Run BUILTIN->FUNC.  */
270       arg = skip_to (1, heap);
271       (builtin->func) (arg, BUILTIN_SCRIPT);
272     }
273 }
274