]> git.proxmox.com Git - grub2.git/blob - normal/cmdline.c
5403fa6cd31d9bab58e1f46c62fc84a9afa88661
[grub2.git] / normal / cmdline.c
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2003,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 <grub/normal.h>
21 #include <grub/misc.h>
22 #include <grub/term.h>
23 #include <grub/err.h>
24 #include <grub/types.h>
25 #include <grub/mm.h>
26 #include <grub/partition.h>
27 #include <grub/disk.h>
28 #include <grub/file.h>
29 #include <grub/env.h>
30
31 static char *kill_buf;
32
33 static int hist_size;
34 static char **hist_lines = 0;
35 static int hist_pos = 0;
36 static int hist_end = 0;
37 static int hist_used = 0;
38
39 grub_err_t
40 grub_set_history (int newsize)
41 {
42 char **old_hist_lines = hist_lines;
43 hist_lines = grub_malloc (sizeof (char *) * newsize);
44
45 /* Copy the old lines into the new buffer. */
46 if (old_hist_lines)
47 {
48 /* Remove the lines that don't fit in the new buffer. */
49 if (newsize < hist_used)
50 {
51 int i;
52 int delsize = hist_used - newsize;
53 hist_used = newsize;
54
55 for (i = 1; i <= delsize; i++)
56 {
57 int pos = hist_end - i;
58 if (pos < 0)
59 pos += hist_size;
60 grub_free (old_hist_lines[pos]);
61 }
62
63 hist_end -= delsize;
64 if (hist_end < 0)
65 hist_end += hist_size;
66 }
67
68 if (hist_pos < hist_end)
69 grub_memmove (hist_lines, old_hist_lines + hist_pos,
70 (hist_end - hist_pos) * sizeof (char *));
71 else if (hist_used)
72 {
73 /* Copy the older part. */
74 grub_memmove (hist_lines, old_hist_lines + hist_pos,
75 (hist_size - hist_pos) * sizeof (char *));
76
77 /* Copy the newer part. */
78 grub_memmove (hist_lines + hist_size - hist_pos, old_hist_lines,
79 hist_end * sizeof (char *));
80 }
81 }
82
83 grub_free (old_hist_lines);
84
85 hist_size = newsize;
86 hist_pos = 0;
87 hist_end = hist_used;
88 return 0;
89 }
90
91 /* Get the entry POS from the history where `0' is the newest
92 entry. */
93 static char *
94 grub_history_get (int pos)
95 {
96 pos = (hist_pos + pos) % hist_size;
97 return hist_lines[pos];
98 }
99
100
101 /* Insert a new history line S on the top of the history. */
102 static void
103 grub_history_add (char *s)
104 {
105 /* Remove the oldest entry in the history to make room for a new
106 entry. */
107 if (hist_used + 1 > hist_size)
108 {
109 hist_end--;
110 if (hist_end < 0)
111 hist_end = hist_size + hist_end;
112
113 grub_free (hist_lines[hist_end]);
114 }
115 else
116 hist_used++;
117
118 /* Move to the next position. */
119 hist_pos--;
120 if (hist_pos < 0)
121 hist_pos = hist_size + hist_pos;
122
123 /* Insert into history. */
124 hist_lines[hist_pos] = grub_strdup (s);
125 }
126
127 /* Replace the history entry on position POS with the string S. */
128 static void
129 grub_history_replace (int pos, char *s)
130 {
131 pos = (hist_pos + pos) % hist_size;
132 grub_free (hist_lines[pos]);
133 hist_lines[pos] = grub_strdup (s);
134 }
135
136 void
137 grub_cmdline_run (int nested)
138 {
139 grub_normal_init_page ();
140 grub_setcursor (1);
141
142 grub_printf ("\
143 [ Minimal BASH-like line editing is supported. For the first word, TAB\n\
144 lists possible command completions. Anywhere else TAB lists possible\n\
145 device/file completions.%s ]\n\n",
146 nested ? " ESC at any time exits." : "");
147
148 while (1)
149 {
150 static char cmdline[GRUB_MAX_CMDLINE];
151
152 grub_print_error ();
153 grub_errno = GRUB_ERR_NONE;
154 cmdline[0] = '\0';
155
156 if (! grub_cmdline_get ("grub> ", cmdline, sizeof (cmdline), 0, 1)
157 && nested)
158 return;
159
160 if (! *cmdline)
161 continue;
162
163 grub_command_execute (cmdline, 1);
164 }
165 }
166
167 /* A completion hook to print items. */
168 static void
169 print_completion (const char *item, grub_completion_type_t type, int count)
170 {
171 if (count == 0)
172 {
173 /* If this is the first time, print a label. */
174 const char *what;
175
176 switch (type)
177 {
178 case GRUB_COMPLETION_TYPE_COMMAND:
179 what = "commands";
180 break;
181 case GRUB_COMPLETION_TYPE_DEVICE:
182 what = "devices";
183 break;
184 case GRUB_COMPLETION_TYPE_FILE:
185 what = "files";
186 break;
187 case GRUB_COMPLETION_TYPE_PARTITION:
188 what = "partitions";
189 break;
190 case GRUB_COMPLETION_TYPE_ARGUMENT:
191 what = "arguments";
192 break;
193 default:
194 what = "things";
195 break;
196 }
197
198 grub_printf ("\nPossible %s are:\n", what);
199 }
200
201 if (type == GRUB_COMPLETION_TYPE_PARTITION)
202 {
203 grub_normal_print_device_info (item);
204 grub_errno = GRUB_ERR_NONE;
205 }
206 else
207 grub_printf (" %s", item);
208 }
209
210 /* Get a command-line. If ECHO_CHAR is not zero, echo it instead of input
211 characters. If READLINE is non-zero, readline-like key bindings are
212 available. If ESC is pushed, return zero, otherwise return non-zero. */
213 /* FIXME: The dumb interface is not supported yet. */
214 int
215 grub_cmdline_get (const char *prompt, char cmdline[], unsigned max_len,
216 int echo_char, int readline)
217 {
218 unsigned xpos, ypos, ystart;
219 grub_size_t lpos, llen;
220 grub_size_t plen;
221 char buf[max_len];
222 int key;
223 int histpos = 0;
224 auto void cl_insert (const char *str);
225 auto void cl_delete (unsigned len);
226 auto void cl_print (int pos, int c);
227 auto void cl_set_pos (void);
228
229 void cl_set_pos (void)
230 {
231 xpos = (plen + lpos) % 79;
232 ypos = ystart + (plen + lpos) / 79;
233 grub_gotoxy (xpos, ypos);
234 }
235
236 void cl_print (int pos, int c)
237 {
238 char *p;
239
240 for (p = buf + pos; *p; p++)
241 {
242 if (xpos++ > 78)
243 {
244 grub_putchar ('\n');
245
246 xpos = 1;
247 if (ypos == (unsigned) (grub_getxy () & 0xFF))
248 ystart--;
249 else
250 ypos++;
251 }
252
253 if (c)
254 grub_putchar (c);
255 else
256 grub_putchar (*p);
257 }
258 }
259
260 void cl_insert (const char *str)
261 {
262 grub_size_t len = grub_strlen (str);
263
264 if (len + llen < max_len)
265 {
266 grub_memmove (buf + lpos + len, buf + lpos, llen - lpos + 1);
267 grub_memmove (buf + lpos, str, len);
268
269 llen += len;
270 lpos += len;
271 cl_print (lpos - len, echo_char);
272 cl_set_pos ();
273 }
274 }
275
276 void cl_delete (unsigned len)
277 {
278 if (lpos + len <= llen)
279 {
280 grub_size_t saved_lpos = lpos;
281
282 lpos = llen - len;
283 cl_set_pos ();
284 cl_print (lpos, ' ');
285 lpos = saved_lpos;
286 cl_set_pos ();
287
288 grub_memmove (buf + lpos, buf + lpos + len, llen - lpos + 1);
289 llen -= len;
290 cl_print (lpos, echo_char);
291 cl_set_pos ();
292 }
293 }
294
295 plen = grub_strlen (prompt);
296 lpos = llen = 0;
297 buf[0] = '\0';
298
299 if ((grub_getxy () >> 8) != 0)
300 grub_putchar ('\n');
301
302 grub_printf (prompt);
303
304 xpos = plen;
305 ystart = ypos = (grub_getxy () & 0xFF);
306
307 cl_insert (cmdline);
308
309 if (hist_used == 0)
310 grub_history_add (buf);
311
312 while ((key = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != '\n' && key != '\r')
313 {
314 if (readline)
315 {
316 switch (key)
317 {
318 case 1: /* Ctrl-a */
319 lpos = 0;
320 cl_set_pos ();
321 break;
322
323 case 2: /* Ctrl-b */
324 if (lpos > 0)
325 {
326 lpos--;
327 cl_set_pos ();
328 }
329 break;
330
331 case 5: /* Ctrl-e */
332 lpos = llen;
333 cl_set_pos ();
334 break;
335
336 case 6: /* Ctrl-f */
337 if (lpos < llen)
338 {
339 lpos++;
340 cl_set_pos ();
341 }
342 break;
343
344 case 9: /* Ctrl-i or TAB */
345 {
346 char *insert;
347 int restore;
348
349 /* Backup the next character and make it 0 so it will
350 be easy to use string functions. */
351 char backup = buf[lpos];
352 buf[lpos] = '\0';
353
354
355 insert = grub_normal_do_completion (buf, &restore,
356 print_completion);
357 /* Restore the original string. */
358 buf[lpos] = backup;
359
360 if (restore)
361 {
362 /* Restore the prompt. */
363 grub_printf ("\n%s%s", prompt, buf);
364 xpos = plen;
365 ystart = ypos = (grub_getxy () & 0xFF);
366 }
367
368 if (insert)
369 {
370 cl_insert (insert);
371 grub_free (insert);
372 }
373 }
374 break;
375
376 case 11: /* Ctrl-k */
377 if (lpos < llen)
378 {
379 if (kill_buf)
380 grub_free (kill_buf);
381
382 kill_buf = grub_strdup (buf + lpos);
383 grub_errno = GRUB_ERR_NONE;
384
385 cl_delete (llen - lpos);
386 }
387 break;
388
389 case 14: /* Ctrl-n */
390 {
391 char *hist;
392
393 lpos = 0;
394
395 if (histpos > 0)
396 {
397 grub_history_replace (histpos, buf);
398 histpos--;
399 }
400
401 cl_delete (llen);
402 hist = grub_history_get (histpos);
403 cl_insert (hist);
404
405 break;
406 }
407 case 16: /* Ctrl-p */
408 {
409 char *hist;
410
411 lpos = 0;
412
413 if (histpos < hist_used - 1)
414 {
415 grub_history_replace (histpos, buf);
416 histpos++;
417 }
418
419 cl_delete (llen);
420 hist = grub_history_get (histpos);
421
422 cl_insert (hist);
423 }
424 break;
425
426 case 21: /* Ctrl-u */
427 if (lpos > 0)
428 {
429 grub_size_t n = lpos;
430
431 if (kill_buf)
432 grub_free (kill_buf);
433
434 kill_buf = grub_malloc (n + 1);
435 grub_errno = GRUB_ERR_NONE;
436 if (kill_buf)
437 {
438 grub_memcpy (kill_buf, buf, n);
439 kill_buf[n] = '\0';
440 }
441
442 lpos = 0;
443 cl_set_pos ();
444 cl_delete (n);
445 }
446 break;
447
448 case 25: /* Ctrl-y */
449 if (kill_buf)
450 cl_insert (kill_buf);
451 break;
452 }
453 }
454
455 switch (key)
456 {
457 case '\e':
458 return 0;
459
460 case '\b':
461 if (lpos > 0)
462 {
463 lpos--;
464 cl_set_pos ();
465 }
466 else
467 break;
468 /* fall through */
469
470 case 4: /* Ctrl-d */
471 if (lpos < llen)
472 cl_delete (1);
473 break;
474
475 default:
476 if (grub_isprint (key))
477 {
478 char str[2];
479
480 str[0] = key;
481 str[1] = '\0';
482 cl_insert (str);
483 }
484 break;
485 }
486 }
487
488 grub_putchar ('\n');
489 grub_refresh ();
490
491 /* If ECHO_CHAR is NUL, remove leading spaces. */
492 lpos = 0;
493 if (! echo_char)
494 while (buf[lpos] == ' ')
495 lpos++;
496
497 histpos = 0;
498 if (grub_strlen (buf) > 0)
499 {
500 grub_history_replace (histpos, buf);
501 grub_history_add ("");
502 }
503
504 grub_memcpy (cmdline, buf + lpos, llen - lpos + 1);
505
506 return 1;
507 }