Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / normal / cmdline.c
bloba9627ca7663a3d1a125103ec5d0461c20af84468
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2009 Free Software Foundation, Inc.
5 * GRUB 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 3 of the License, or
8 * (at your option) any later version.
10 * GRUB 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.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/normal.h>
20 #include <grub/misc.h>
21 #include <grub/term.h>
22 #include <grub/err.h>
23 #include <grub/types.h>
24 #include <grub/mm.h>
25 #include <grub/partition.h>
26 #include <grub/disk.h>
27 #include <grub/file.h>
28 #include <grub/env.h>
29 #include <grub/i18n.h>
30 #include <grub/charset.h>
32 static grub_uint32_t *kill_buf;
34 static int hist_size;
35 static grub_uint32_t **hist_lines = 0;
36 static int hist_pos = 0;
37 static int hist_end = 0;
38 static int hist_used = 0;
40 grub_err_t
41 grub_set_history (int newsize)
43 grub_uint32_t **old_hist_lines = hist_lines;
44 hist_lines = grub_malloc (sizeof (grub_uint32_t *) * newsize);
46 /* Copy the old lines into the new buffer. */
47 if (old_hist_lines)
49 /* Remove the lines that don't fit in the new buffer. */
50 if (newsize < hist_used)
52 grub_size_t i;
53 grub_size_t delsize = hist_used - newsize;
54 hist_used = newsize;
56 for (i = 1; i < delsize + 1; i++)
58 grub_ssize_t pos = hist_end - i;
59 if (pos < 0)
60 pos += hist_size;
61 grub_free (old_hist_lines[pos]);
64 hist_end -= delsize;
65 if (hist_end < 0)
66 hist_end += hist_size;
69 if (hist_pos < hist_end)
70 grub_memmove (hist_lines, old_hist_lines + hist_pos,
71 (hist_end - hist_pos) * sizeof (grub_uint32_t *));
72 else if (hist_used)
74 /* Copy the older part. */
75 grub_memmove (hist_lines, old_hist_lines + hist_pos,
76 (hist_size - hist_pos) * sizeof (grub_uint32_t *));
78 /* Copy the newer part. */
79 grub_memmove (hist_lines + hist_size - hist_pos, old_hist_lines,
80 hist_end * sizeof (grub_uint32_t *));
84 grub_free (old_hist_lines);
86 hist_size = newsize;
87 hist_pos = 0;
88 hist_end = hist_used;
89 return 0;
92 /* Get the entry POS from the history where `0' is the newest
93 entry. */
94 static grub_uint32_t *
95 grub_history_get (int pos)
97 pos = (hist_pos + pos) % hist_size;
98 return hist_lines[pos];
101 static grub_size_t
102 strlen_ucs4 (const grub_uint32_t *s)
104 const grub_uint32_t *p = s;
106 while (*p)
107 p++;
109 return p - s;
112 /* Replace the history entry on position POS with the string S. */
113 static void
114 grub_history_set (int pos, grub_uint32_t *s, grub_size_t len)
116 grub_free (hist_lines[pos]);
117 hist_lines[pos] = grub_malloc ((len + 1) * sizeof (grub_uint32_t));
118 if (!hist_lines[pos])
120 grub_print_error ();
121 grub_errno = GRUB_ERR_NONE;
122 return ;
124 grub_memcpy (hist_lines[pos], s, len * sizeof (grub_uint32_t));
125 hist_lines[pos][len] = 0;
128 /* Insert a new history line S on the top of the history. */
129 static void
130 grub_history_add (grub_uint32_t *s, grub_size_t len)
132 /* Remove the oldest entry in the history to make room for a new
133 entry. */
134 if (hist_used + 1 > hist_size)
136 hist_end--;
137 if (hist_end < 0)
138 hist_end = hist_size + hist_end;
140 grub_free (hist_lines[hist_end]);
142 else
143 hist_used++;
145 /* Move to the next position. */
146 hist_pos--;
147 if (hist_pos < 0)
148 hist_pos = hist_size + hist_pos;
150 /* Insert into history. */
151 hist_lines[hist_pos] = NULL;
152 grub_history_set (hist_pos, s, len);
155 /* Replace the history entry on position POS with the string S. */
156 static void
157 grub_history_replace (int pos, grub_uint32_t *s, grub_size_t len)
159 grub_history_set ((hist_pos + pos) % hist_size, s, len);
162 /* A completion hook to print items. */
163 static void
164 print_completion (const char *item, grub_completion_type_t type, int count)
166 if (count == 0)
168 /* If this is the first time, print a label. */
170 grub_puts ("");
171 switch (type)
173 case GRUB_COMPLETION_TYPE_COMMAND:
174 grub_puts_ (N_("Possible commands are:"));
175 break;
176 case GRUB_COMPLETION_TYPE_DEVICE:
177 grub_puts_ (N_("Possible devices are:"));
178 break;
179 case GRUB_COMPLETION_TYPE_FILE:
180 grub_puts_ (N_("Possible files are:"));
181 break;
182 case GRUB_COMPLETION_TYPE_PARTITION:
183 grub_puts_ (N_("Possible partitions are:"));
184 break;
185 case GRUB_COMPLETION_TYPE_ARGUMENT:
186 grub_puts_ (N_("Possible arguments are:"));
187 break;
188 default:
189 /* TRANSLATORS: this message is used if none of above matches.
190 This shouldn't happen but please use the general term for
191 "thing" or "object". */
192 grub_puts_ (N_("Possible things are:"));
193 break;
195 grub_puts ("");
198 if (type == GRUB_COMPLETION_TYPE_PARTITION)
200 grub_normal_print_device_info (item);
201 grub_errno = GRUB_ERR_NONE;
203 else
204 grub_printf (" %s", item);
207 struct cmdline_term
209 unsigned xpos, ypos, ystart, width, height;
210 unsigned prompt_len;
211 struct grub_term_output *term;
214 /* Get a command-line. If ESC is pushed, return zero,
215 otherwise return command line. */
216 /* FIXME: The dumb interface is not supported yet. */
217 char *
218 grub_cmdline_get (const char *prompt_translated)
220 grub_size_t lpos, llen;
221 grub_uint32_t *buf;
222 grub_size_t max_len = 256;
223 int key;
224 int histpos = 0;
225 auto void cl_insert (const grub_uint32_t *str);
226 auto void cl_delete (unsigned len);
227 auto inline void __attribute__ ((always_inline)) cl_print (struct cmdline_term *cl_term, int pos,
228 grub_uint32_t c);
229 auto void cl_set_pos (struct cmdline_term *cl_term);
230 auto void cl_print_all (int pos, grub_uint32_t c);
231 auto void cl_set_pos_all (void);
232 auto void init_clterm (struct cmdline_term *cl_term_cur);
233 auto void init_clterm_all (void);
234 struct cmdline_term *cl_terms;
235 char *ret;
236 unsigned nterms;
238 void cl_set_pos (struct cmdline_term *cl_term)
240 cl_term->xpos = (cl_term->prompt_len + lpos) % (cl_term->width - 1);
241 cl_term->ypos = cl_term->ystart
242 + (cl_term->prompt_len + lpos) / (cl_term->width - 1);
243 grub_term_gotoxy (cl_term->term, cl_term->xpos, cl_term->ypos);
246 void cl_set_pos_all (void)
248 unsigned i;
249 for (i = 0; i < nterms; i++)
250 cl_set_pos (&cl_terms[i]);
253 inline void __attribute__ ((always_inline)) cl_print (struct cmdline_term *cl_term, int pos, grub_uint32_t c)
255 grub_uint32_t *p;
257 for (p = buf + pos; p < buf + llen; p++)
259 if (c)
260 grub_putcode (c, cl_term->term);
261 else
262 grub_putcode (*p, cl_term->term);
263 cl_term->xpos++;
264 if (cl_term->xpos >= cl_term->width - 1)
266 cl_term->xpos = 0;
267 if (cl_term->ypos >= (unsigned) (cl_term->height - 1))
268 cl_term->ystart--;
269 else
270 cl_term->ypos++;
271 grub_putcode ('\n', cl_term->term);
276 void cl_print_all (int pos, grub_uint32_t c)
278 unsigned i;
279 for (i = 0; i < nterms; i++)
280 cl_print (&cl_terms[i], pos, c);
283 void cl_insert (const grub_uint32_t *str)
285 grub_size_t len = strlen_ucs4 (str);
287 if (len + llen >= max_len)
289 grub_uint32_t *nbuf;
290 max_len *= 2;
291 nbuf = grub_realloc (buf, sizeof (grub_uint32_t) * max_len);
292 if (nbuf)
293 buf = nbuf;
294 else
296 grub_print_error ();
297 grub_errno = GRUB_ERR_NONE;
298 max_len /= 2;
302 if (len + llen < max_len)
304 grub_memmove (buf + lpos + len, buf + lpos,
305 (llen - lpos + 1) * sizeof (grub_uint32_t));
306 grub_memmove (buf + lpos, str, len * sizeof (grub_uint32_t));
308 llen += len;
309 cl_set_pos_all ();
310 cl_print_all (lpos, 0);
311 lpos += len;
312 cl_set_pos_all ();
316 void cl_delete (unsigned len)
318 if (lpos + len <= llen)
320 grub_size_t saved_lpos = lpos;
322 lpos = llen - len;
323 cl_set_pos_all ();
324 cl_print_all (lpos, ' ');
325 lpos = saved_lpos;
326 cl_set_pos_all ();
328 grub_memmove (buf + lpos, buf + lpos + len,
329 sizeof (grub_uint32_t) * (llen - lpos + 1));
330 llen -= len;
331 cl_print_all (lpos, 0);
332 cl_set_pos_all ();
336 void init_clterm (struct cmdline_term *cl_term_cur)
338 cl_term_cur->xpos = cl_term_cur->prompt_len;
339 cl_term_cur->ypos = (grub_term_getxy (cl_term_cur->term) & 0xFF);
340 cl_term_cur->ystart = cl_term_cur->ypos;
341 cl_term_cur->width = grub_term_width (cl_term_cur->term);
342 cl_term_cur->height = grub_term_height (cl_term_cur->term);
345 void init_clterm_all (void)
347 unsigned i;
348 for (i = 0; i < nterms; i++)
349 init_clterm (&cl_terms[i]);
352 buf = grub_malloc (max_len * sizeof (grub_uint32_t));
353 if (!buf)
354 return 0;
356 lpos = llen = 0;
357 buf[0] = '\0';
360 grub_term_output_t term;
362 FOR_ACTIVE_TERM_OUTPUTS(term)
363 if ((grub_term_getxy (term) >> 8) != 0)
364 grub_putcode ('\n', term);
366 grub_xputs (prompt_translated);
367 grub_xputs (" ");
368 grub_normal_reset_more ();
371 struct cmdline_term *cl_term_cur;
372 struct grub_term_output *cur;
373 grub_uint32_t *unicode_msg;
374 grub_size_t msg_len = grub_strlen (prompt_translated) + 3;
376 nterms = 0;
377 FOR_ACTIVE_TERM_OUTPUTS(cur)
378 nterms++;
380 cl_terms = grub_malloc (sizeof (cl_terms[0]) * nterms);
381 if (!cl_terms)
382 return 0;
383 cl_term_cur = cl_terms;
385 unicode_msg = grub_malloc (msg_len * sizeof (grub_uint32_t));
386 if (!unicode_msg)
387 return 0;;
388 msg_len = grub_utf8_to_ucs4 (unicode_msg, msg_len - 1,
389 (grub_uint8_t *) prompt_translated, -1, 0);
390 unicode_msg[msg_len++] = ' ';
392 FOR_ACTIVE_TERM_OUTPUTS(cur)
394 cl_term_cur->term = cur;
395 cl_term_cur->prompt_len = grub_getstringwidth (unicode_msg,
396 unicode_msg + msg_len,
397 cur);
398 init_clterm (cl_term_cur);
399 cl_term_cur++;
401 grub_free (unicode_msg);
404 if (hist_used == 0)
405 grub_history_add (buf, llen);
407 grub_refresh ();
409 while ((key = grub_getkey ()) != '\n' && key != '\r')
411 switch (key)
413 case GRUB_TERM_CTRL | 'a':
414 case GRUB_TERM_KEY_HOME:
415 lpos = 0;
416 cl_set_pos_all ();
417 break;
419 case GRUB_TERM_CTRL | 'b':
420 case GRUB_TERM_KEY_LEFT:
421 if (lpos > 0)
423 lpos--;
424 cl_set_pos_all ();
426 break;
428 case GRUB_TERM_CTRL | 'e':
429 case GRUB_TERM_KEY_END:
430 lpos = llen;
431 cl_set_pos_all ();
432 break;
434 case GRUB_TERM_CTRL | 'f':
435 case GRUB_TERM_KEY_RIGHT:
436 if (lpos < llen)
438 lpos++;
439 cl_set_pos_all ();
441 break;
443 case GRUB_TERM_CTRL | 'i':
444 case '\t':
446 int restore;
447 char *insertu8;
448 char *bufu8;
449 grub_uint32_t c;
451 c = buf[lpos];
452 buf[lpos] = '\0';
454 bufu8 = grub_ucs4_to_utf8_alloc (buf, lpos);
455 buf[lpos] = c;
456 if (!bufu8)
458 grub_print_error ();
459 grub_errno = GRUB_ERR_NONE;
460 break;
463 insertu8 = grub_normal_do_completion (bufu8, &restore,
464 print_completion);
465 grub_free (bufu8);
467 grub_normal_reset_more ();
469 if (restore)
471 /* Restore the prompt. */
472 grub_xputs ("\n");
473 grub_xputs (prompt_translated);
474 grub_xputs (" ");
475 init_clterm_all ();
476 cl_print_all (0, 0);
479 if (insertu8)
481 grub_size_t insertlen;
482 grub_ssize_t t;
483 grub_uint32_t *insert;
485 insertlen = grub_strlen (insertu8);
486 insert = grub_malloc ((insertlen + 1) * sizeof (grub_uint32_t));
487 if (!insert)
489 grub_free (insertu8);
490 grub_print_error ();
491 grub_errno = GRUB_ERR_NONE;
492 break;
494 t = grub_utf8_to_ucs4 (insert, insertlen,
495 (grub_uint8_t *) insertu8,
496 insertlen, 0);
497 if (t > 0)
499 if (insert[t-1] == ' ' && buf[lpos] == ' ')
501 insert[t-1] = 0;
502 if (t != 1)
503 cl_insert (insert);
504 lpos++;
506 else
508 insert[t] = 0;
509 cl_insert (insert);
513 grub_free (insertu8);
514 grub_free (insert);
516 cl_set_pos_all ();
518 break;
520 case GRUB_TERM_CTRL | 'k':
521 if (lpos < llen)
523 grub_free (kill_buf);
525 kill_buf = grub_malloc ((llen - lpos + 1)
526 * sizeof (grub_uint32_t));
527 if (grub_errno)
529 grub_print_error ();
530 grub_errno = GRUB_ERR_NONE;
532 else
534 grub_memcpy (kill_buf, buf + lpos,
535 (llen - lpos + 1) * sizeof (grub_uint32_t));
536 kill_buf[llen - lpos] = 0;
539 cl_delete (llen - lpos);
541 break;
543 case GRUB_TERM_CTRL | 'n':
544 case GRUB_TERM_KEY_DOWN:
546 grub_uint32_t *hist;
548 lpos = 0;
550 if (histpos > 0)
552 grub_history_replace (histpos, buf, llen);
553 histpos--;
556 cl_delete (llen);
557 hist = grub_history_get (histpos);
558 cl_insert (hist);
560 break;
563 case GRUB_TERM_KEY_UP:
564 case GRUB_TERM_CTRL | 'p':
566 grub_uint32_t *hist;
568 lpos = 0;
570 if (histpos < hist_used - 1)
572 grub_history_replace (histpos, buf, llen);
573 histpos++;
576 cl_delete (llen);
577 hist = grub_history_get (histpos);
579 cl_insert (hist);
581 break;
583 case GRUB_TERM_CTRL | 'u':
584 if (lpos > 0)
586 grub_size_t n = lpos;
588 grub_free (kill_buf);
590 kill_buf = grub_malloc (n + 1);
591 if (grub_errno)
593 grub_print_error ();
594 grub_errno = GRUB_ERR_NONE;
596 if (kill_buf)
598 grub_memcpy (kill_buf, buf, n);
599 kill_buf[n] = '\0';
602 lpos = 0;
603 cl_set_pos_all ();
604 cl_delete (n);
606 break;
608 case GRUB_TERM_CTRL | 'y':
609 if (kill_buf)
610 cl_insert (kill_buf);
611 break;
613 case '\e':
614 grub_free (cl_terms);
615 return 0;
617 case '\b':
618 if (lpos > 0)
620 lpos--;
621 cl_set_pos_all ();
623 else
624 break;
625 /* fall through */
627 case GRUB_TERM_CTRL | 'd':
628 case GRUB_TERM_KEY_DC:
629 if (lpos < llen)
630 cl_delete (1);
631 break;
633 default:
634 if (grub_isprint (key))
636 grub_uint32_t str[2];
638 str[0] = key;
639 str[1] = '\0';
640 cl_insert (str);
642 break;
645 grub_refresh ();
648 grub_xputs ("\n");
649 grub_refresh ();
651 histpos = 0;
652 if (strlen_ucs4 (buf) > 0)
654 grub_uint32_t empty[] = { 0 };
655 grub_history_replace (histpos, buf, llen);
656 grub_history_add (empty, 0);
659 ret = grub_ucs4_to_utf8_alloc (buf, llen + 1);
660 grub_free (buf);
661 grub_free (cl_terms);
662 return ret;