]>
Commit | Line | Data |
---|---|---|
ce5bf700 | 1 | /* |
4b13b216 | 2 | * GRUB -- GRand Unified Bootloader |
7f39d92f | 3 | * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2009 Free Software Foundation, Inc. |
ce5bf700 | 4 | * |
5a79f472 | 5 | * GRUB is free software: you can redistribute it and/or modify |
ce5bf700 | 6 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 7 | * the Free Software Foundation, either version 3 of the License, or |
ce5bf700 | 8 | * (at your option) any later version. |
9 | * | |
5a79f472 | 10 | * GRUB is distributed in the hope that it will be useful, |
ce5bf700 | 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 | |
5a79f472 | 16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
ce5bf700 | 17 | */ |
18 | ||
4b13b216 | 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> | |
3f1578fe | 25 | #include <grub/partition.h> |
4b13b216 | 26 | #include <grub/disk.h> |
27 | #include <grub/file.h> | |
28 | #include <grub/env.h> | |
7f39d92f | 29 | #include <grub/i18n.h> |
ce5bf700 | 30 | |
31 | static char *kill_buf; | |
32 | ||
5aded270 | 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 | ||
4b13b216 | 39 | grub_err_t |
40 | grub_set_history (int newsize) | |
5aded270 | 41 | { |
42 | char **old_hist_lines = hist_lines; | |
4b13b216 | 43 | hist_lines = grub_malloc (sizeof (char *) * newsize); |
5aded270 | 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 | ||
cfb12aff | 55 | for (i = 1; i <= delsize; i++) |
5aded270 | 56 | { |
57 | int pos = hist_end - i; | |
cfb12aff | 58 | if (pos < 0) |
59 | pos += hist_size; | |
4b13b216 | 60 | grub_free (old_hist_lines[pos]); |
5aded270 | 61 | } |
62 | ||
63 | hist_end -= delsize; | |
64 | if (hist_end < 0) | |
cfb12aff | 65 | hist_end += hist_size; |
5aded270 | 66 | } |
67 | ||
68 | if (hist_pos < hist_end) | |
4b13b216 | 69 | grub_memmove (hist_lines, old_hist_lines + hist_pos, |
5aded270 | 70 | (hist_end - hist_pos) * sizeof (char *)); |
cfb12aff | 71 | else if (hist_used) |
5aded270 | 72 | { |
cfb12aff | 73 | /* Copy the older part. */ |
74 | grub_memmove (hist_lines, old_hist_lines + hist_pos, | |
75 | (hist_size - hist_pos) * sizeof (char *)); | |
b39f9d20 | 76 | |
cfb12aff | 77 | /* Copy the newer part. */ |
78 | grub_memmove (hist_lines + hist_size - hist_pos, old_hist_lines, | |
79 | hist_end * sizeof (char *)); | |
5aded270 | 80 | } |
81 | } | |
82 | ||
4b13b216 | 83 | grub_free (old_hist_lines); |
5aded270 | 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 * | |
4b13b216 | 94 | grub_history_get (int pos) |
5aded270 | 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 | |
4b13b216 | 103 | grub_history_add (char *s) |
5aded270 | 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 | ||
4b13b216 | 113 | grub_free (hist_lines[hist_end]); |
5aded270 | 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. */ | |
4b13b216 | 124 | hist_lines[hist_pos] = grub_strdup (s); |
5aded270 | 125 | } |
126 | ||
127 | /* Replace the history entry on position POS with the string S. */ | |
128 | static void | |
4b13b216 | 129 | grub_history_replace (int pos, char *s) |
5aded270 | 130 | { |
131 | pos = (hist_pos + pos) % hist_size; | |
4b13b216 | 132 | grub_free (hist_lines[pos]); |
133 | hist_lines[pos] = grub_strdup (s); | |
5aded270 | 134 | } |
135 | ||
992ffbbe | 136 | /* A completion hook to print items. */ |
137 | static void | |
138 | print_completion (const char *item, grub_completion_type_t type, int count) | |
139 | { | |
140 | if (count == 0) | |
141 | { | |
142 | /* If this is the first time, print a label. */ | |
143 | const char *what; | |
144 | ||
145 | switch (type) | |
146 | { | |
147 | case GRUB_COMPLETION_TYPE_COMMAND: | |
148 | what = "commands"; | |
149 | break; | |
150 | case GRUB_COMPLETION_TYPE_DEVICE: | |
151 | what = "devices"; | |
152 | break; | |
153 | case GRUB_COMPLETION_TYPE_FILE: | |
154 | what = "files"; | |
155 | break; | |
156 | case GRUB_COMPLETION_TYPE_PARTITION: | |
157 | what = "partitions"; | |
158 | break; | |
67f44c86 | 159 | case GRUB_COMPLETION_TYPE_ARGUMENT: |
160 | what = "arguments"; | |
161 | break; | |
992ffbbe | 162 | default: |
163 | what = "things"; | |
164 | break; | |
165 | } | |
b39f9d20 | 166 | |
992ffbbe | 167 | grub_printf ("\nPossible %s are:\n", what); |
168 | } | |
169 | ||
170 | if (type == GRUB_COMPLETION_TYPE_PARTITION) | |
171 | { | |
172 | grub_normal_print_device_info (item); | |
173 | grub_errno = GRUB_ERR_NONE; | |
174 | } | |
175 | else | |
176 | grub_printf (" %s", item); | |
177 | } | |
178 | ||
ce5bf700 | 179 | /* Get a command-line. If ECHO_CHAR is not zero, echo it instead of input |
180 | characters. If READLINE is non-zero, readline-like key bindings are | |
990cf3aa | 181 | available. If ESC is pushed, return zero, otherwise return non-zero. */ |
ce5bf700 | 182 | /* FIXME: The dumb interface is not supported yet. */ |
183 | int | |
4b13b216 | 184 | grub_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, |
e7e1f93f | 185 | int echo_char, int readline, int history) |
ce5bf700 | 186 | { |
187 | unsigned xpos, ypos, ystart; | |
4b13b216 | 188 | grub_size_t lpos, llen; |
189 | grub_size_t plen; | |
ce5bf700 | 190 | char buf[max_len]; |
191 | int key; | |
5aded270 | 192 | int histpos = 0; |
ce5bf700 | 193 | auto void cl_insert (const char *str); |
194 | auto void cl_delete (unsigned len); | |
195 | auto void cl_print (int pos, int c); | |
196 | auto void cl_set_pos (void); | |
7f39d92f | 197 | const char *prompt_translated = _(prompt); |
ce5bf700 | 198 | |
199 | void cl_set_pos (void) | |
200 | { | |
201 | xpos = (plen + lpos) % 79; | |
202 | ypos = ystart + (plen + lpos) / 79; | |
4b13b216 | 203 | grub_gotoxy (xpos, ypos); |
b39f9d20 | 204 | |
bd0d7896 | 205 | grub_refresh (); |
ce5bf700 | 206 | } |
b39f9d20 | 207 | |
ce5bf700 | 208 | void cl_print (int pos, int c) |
209 | { | |
210 | char *p; | |
211 | ||
212 | for (p = buf + pos; *p; p++) | |
213 | { | |
214 | if (xpos++ > 78) | |
215 | { | |
4b13b216 | 216 | grub_putchar ('\n'); |
b39f9d20 | 217 | |
ce5bf700 | 218 | xpos = 1; |
4b13b216 | 219 | if (ypos == (unsigned) (grub_getxy () & 0xFF)) |
ce5bf700 | 220 | ystart--; |
221 | else | |
222 | ypos++; | |
223 | } | |
224 | ||
225 | if (c) | |
4b13b216 | 226 | grub_putchar (c); |
ce5bf700 | 227 | else |
4b13b216 | 228 | grub_putchar (*p); |
ce5bf700 | 229 | } |
230 | } | |
b39f9d20 | 231 | |
ce5bf700 | 232 | void cl_insert (const char *str) |
233 | { | |
4b13b216 | 234 | grub_size_t len = grub_strlen (str); |
ce5bf700 | 235 | |
236 | if (len + llen < max_len) | |
237 | { | |
4b13b216 | 238 | grub_memmove (buf + lpos + len, buf + lpos, llen - lpos + 1); |
239 | grub_memmove (buf + lpos, str, len); | |
ce5bf700 | 240 | |
241 | llen += len; | |
242 | lpos += len; | |
243 | cl_print (lpos - len, echo_char); | |
244 | cl_set_pos (); | |
245 | } | |
b39f9d20 | 246 | |
bd0d7896 | 247 | grub_refresh (); |
ce5bf700 | 248 | } |
249 | ||
250 | void cl_delete (unsigned len) | |
251 | { | |
252 | if (lpos + len <= llen) | |
253 | { | |
4b13b216 | 254 | grub_size_t saved_lpos = lpos; |
ce5bf700 | 255 | |
256 | lpos = llen - len; | |
257 | cl_set_pos (); | |
258 | cl_print (lpos, ' '); | |
259 | lpos = saved_lpos; | |
260 | cl_set_pos (); | |
b39f9d20 | 261 | |
4b13b216 | 262 | grub_memmove (buf + lpos, buf + lpos + len, llen - lpos + 1); |
ce5bf700 | 263 | llen -= len; |
264 | cl_print (lpos, echo_char); | |
265 | cl_set_pos (); | |
266 | } | |
b39f9d20 | 267 | |
bd0d7896 | 268 | grub_refresh (); |
ce5bf700 | 269 | } |
b39f9d20 | 270 | |
7f39d92f | 271 | plen = grub_strlen (prompt_translated); |
ce5bf700 | 272 | lpos = llen = 0; |
273 | buf[0] = '\0'; | |
274 | ||
4b13b216 | 275 | if ((grub_getxy () >> 8) != 0) |
276 | grub_putchar ('\n'); | |
b39f9d20 | 277 | |
7f39d92f | 278 | grub_printf ("%s", prompt_translated); |
b39f9d20 | 279 | |
ce5bf700 | 280 | xpos = plen; |
4b13b216 | 281 | ystart = ypos = (grub_getxy () & 0xFF); |
b39f9d20 | 282 | |
ce5bf700 | 283 | cl_insert (cmdline); |
284 | ||
e7e1f93f | 285 | if (history && hist_used == 0) |
6b8fd1c4 | 286 | grub_history_add (buf); |
5aded270 | 287 | |
4b13b216 | 288 | while ((key = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != '\n' && key != '\r') |
ce5bf700 | 289 | { |
290 | if (readline) | |
291 | { | |
292 | switch (key) | |
293 | { | |
294 | case 1: /* Ctrl-a */ | |
295 | lpos = 0; | |
296 | cl_set_pos (); | |
297 | break; | |
298 | ||
299 | case 2: /* Ctrl-b */ | |
300 | if (lpos > 0) | |
301 | { | |
302 | lpos--; | |
303 | cl_set_pos (); | |
304 | } | |
305 | break; | |
306 | ||
307 | case 5: /* Ctrl-e */ | |
308 | lpos = llen; | |
309 | cl_set_pos (); | |
310 | break; | |
311 | ||
312 | case 6: /* Ctrl-f */ | |
313 | if (lpos < llen) | |
314 | { | |
315 | lpos++; | |
316 | cl_set_pos (); | |
317 | } | |
318 | break; | |
319 | ||
320 | case 9: /* Ctrl-i or TAB */ | |
5aded270 | 321 | { |
322 | char *insert; | |
323 | int restore; | |
b39f9d20 | 324 | |
5aded270 | 325 | /* Backup the next character and make it 0 so it will |
326 | be easy to use string functions. */ | |
327 | char backup = buf[lpos]; | |
328 | buf[lpos] = '\0'; | |
b39f9d20 | 329 | |
5aded270 | 330 | |
992ffbbe | 331 | insert = grub_normal_do_completion (buf, &restore, |
332 | print_completion); | |
5aded270 | 333 | /* Restore the original string. */ |
334 | buf[lpos] = backup; | |
b39f9d20 | 335 | |
5aded270 | 336 | if (restore) |
337 | { | |
338 | /* Restore the prompt. */ | |
7f39d92f | 339 | grub_printf ("\n%s %s", prompt_translated, buf); |
5aded270 | 340 | xpos = plen; |
4b13b216 | 341 | ystart = ypos = (grub_getxy () & 0xFF); |
5aded270 | 342 | } |
343 | ||
344 | if (insert) | |
345 | { | |
346 | cl_insert (insert); | |
4b13b216 | 347 | grub_free (insert); |
5aded270 | 348 | } |
349 | } | |
ce5bf700 | 350 | break; |
351 | ||
352 | case 11: /* Ctrl-k */ | |
353 | if (lpos < llen) | |
354 | { | |
355 | if (kill_buf) | |
4b13b216 | 356 | grub_free (kill_buf); |
ce5bf700 | 357 | |
4b13b216 | 358 | kill_buf = grub_strdup (buf + lpos); |
359 | grub_errno = GRUB_ERR_NONE; | |
ce5bf700 | 360 | |
361 | cl_delete (llen - lpos); | |
362 | } | |
363 | break; | |
364 | ||
365 | case 14: /* Ctrl-n */ | |
5aded270 | 366 | { |
367 | char *hist; | |
ce5bf700 | 368 | |
5aded270 | 369 | lpos = 0; |
370 | ||
371 | if (histpos > 0) | |
6b8fd1c4 | 372 | { |
373 | grub_history_replace (histpos, buf); | |
374 | histpos--; | |
375 | } | |
5aded270 | 376 | |
377 | cl_delete (llen); | |
4b13b216 | 378 | hist = grub_history_get (histpos); |
5aded270 | 379 | cl_insert (hist); |
380 | ||
381 | break; | |
382 | } | |
ce5bf700 | 383 | case 16: /* Ctrl-p */ |
5aded270 | 384 | { |
385 | char *hist; | |
386 | ||
387 | lpos = 0; | |
388 | ||
389 | if (histpos < hist_used - 1) | |
6b8fd1c4 | 390 | { |
391 | grub_history_replace (histpos, buf); | |
392 | histpos++; | |
393 | } | |
5aded270 | 394 | |
395 | cl_delete (llen); | |
4b13b216 | 396 | hist = grub_history_get (histpos); |
5aded270 | 397 | |
398 | cl_insert (hist); | |
399 | } | |
ce5bf700 | 400 | break; |
401 | ||
402 | case 21: /* Ctrl-u */ | |
403 | if (lpos > 0) | |
404 | { | |
4b13b216 | 405 | grub_size_t n = lpos; |
b39f9d20 | 406 | |
ce5bf700 | 407 | if (kill_buf) |
4b13b216 | 408 | grub_free (kill_buf); |
ce5bf700 | 409 | |
4b13b216 | 410 | kill_buf = grub_malloc (n + 1); |
411 | grub_errno = GRUB_ERR_NONE; | |
ce5bf700 | 412 | if (kill_buf) |
413 | { | |
4b13b216 | 414 | grub_memcpy (kill_buf, buf, n); |
ce5bf700 | 415 | kill_buf[n] = '\0'; |
416 | } | |
417 | ||
418 | lpos = 0; | |
419 | cl_set_pos (); | |
420 | cl_delete (n); | |
421 | } | |
422 | break; | |
423 | ||
424 | case 25: /* Ctrl-y */ | |
425 | if (kill_buf) | |
426 | cl_insert (kill_buf); | |
427 | break; | |
428 | } | |
429 | } | |
430 | ||
431 | switch (key) | |
432 | { | |
433 | case '\e': | |
434 | return 0; | |
435 | ||
436 | case '\b': | |
437 | if (lpos > 0) | |
438 | { | |
439 | lpos--; | |
440 | cl_set_pos (); | |
441 | } | |
f8f1559a | 442 | else |
443 | break; | |
ce5bf700 | 444 | /* fall through */ |
b39f9d20 | 445 | |
ce5bf700 | 446 | case 4: /* Ctrl-d */ |
447 | if (lpos < llen) | |
448 | cl_delete (1); | |
449 | break; | |
450 | ||
451 | default: | |
4b13b216 | 452 | if (grub_isprint (key)) |
ce5bf700 | 453 | { |
454 | char str[2]; | |
455 | ||
456 | str[0] = key; | |
457 | str[1] = '\0'; | |
458 | cl_insert (str); | |
459 | } | |
460 | break; | |
461 | } | |
462 | } | |
463 | ||
4b13b216 | 464 | grub_putchar ('\n'); |
465 | grub_refresh (); | |
ce5bf700 | 466 | |
467 | /* If ECHO_CHAR is NUL, remove leading spaces. */ | |
468 | lpos = 0; | |
469 | if (! echo_char) | |
470 | while (buf[lpos] == ' ') | |
471 | lpos++; | |
472 | ||
e7e1f93f | 473 | if (history) |
6b8fd1c4 | 474 | { |
e7e1f93f | 475 | histpos = 0; |
476 | if (grub_strlen (buf) > 0) | |
477 | { | |
478 | grub_history_replace (histpos, buf); | |
479 | grub_history_add (""); | |
480 | } | |
6b8fd1c4 | 481 | } |
b39f9d20 | 482 | |
4b13b216 | 483 | grub_memcpy (cmdline, buf + lpos, llen - lpos + 1); |
ce5bf700 | 484 | |
485 | return 1; | |
486 | } |