cmdline.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2009 Free Software Foundation, Inc.
  4. *
  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.
  9. *
  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.
  14. *
  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/>.
  17. */
  18. #include <grub/normal.h>
  19. #include <grub/misc.h>
  20. #include <grub/term.h>
  21. #include <grub/err.h>
  22. #include <grub/types.h>
  23. #include <grub/mm.h>
  24. #include <grub/partition.h>
  25. #include <grub/disk.h>
  26. #include <grub/file.h>
  27. #include <grub/env.h>
  28. #include <grub/i18n.h>
  29. #include <grub/charset.h>
  30. static grub_uint32_t *kill_buf;
  31. static int hist_size;
  32. static grub_uint32_t **hist_lines = 0;
  33. static int hist_pos = 0;
  34. static int hist_end = 0;
  35. static int hist_used = 0;
  36. grub_err_t
  37. grub_set_history (int newsize)
  38. {
  39. grub_uint32_t **old_hist_lines = hist_lines;
  40. hist_lines = grub_malloc (sizeof (grub_uint32_t *) * newsize);
  41. /* Copy the old lines into the new buffer. */
  42. if (old_hist_lines)
  43. {
  44. /* Remove the lines that don't fit in the new buffer. */
  45. if (newsize < hist_used)
  46. {
  47. grub_size_t i;
  48. grub_size_t delsize = hist_used - newsize;
  49. hist_used = newsize;
  50. for (i = 1; i < delsize + 1; i++)
  51. {
  52. grub_ssize_t pos = hist_end - i;
  53. if (pos < 0)
  54. pos += hist_size;
  55. grub_free (old_hist_lines[pos]);
  56. }
  57. hist_end -= delsize;
  58. if (hist_end < 0)
  59. hist_end += hist_size;
  60. }
  61. if (hist_pos < hist_end)
  62. grub_memmove (hist_lines, old_hist_lines + hist_pos,
  63. (hist_end - hist_pos) * sizeof (grub_uint32_t *));
  64. else if (hist_used)
  65. {
  66. /* Copy the older part. */
  67. grub_memmove (hist_lines, old_hist_lines + hist_pos,
  68. (hist_size - hist_pos) * sizeof (grub_uint32_t *));
  69. /* Copy the newer part. */
  70. grub_memmove (hist_lines + hist_size - hist_pos, old_hist_lines,
  71. hist_end * sizeof (grub_uint32_t *));
  72. }
  73. }
  74. grub_free (old_hist_lines);
  75. hist_size = newsize;
  76. hist_pos = 0;
  77. hist_end = hist_used;
  78. return 0;
  79. }
  80. /* Get the entry POS from the history where `0' is the newest
  81. entry. */
  82. static grub_uint32_t *
  83. grub_history_get (unsigned pos)
  84. {
  85. pos = (hist_pos + pos) % hist_size;
  86. return hist_lines[pos];
  87. }
  88. static grub_size_t
  89. strlen_ucs4 (const grub_uint32_t *s)
  90. {
  91. const grub_uint32_t *p = s;
  92. while (*p)
  93. p++;
  94. return p - s;
  95. }
  96. /* Replace the history entry on position POS with the string S. */
  97. static void
  98. grub_history_set (int pos, grub_uint32_t *s, grub_size_t len)
  99. {
  100. grub_free (hist_lines[pos]);
  101. hist_lines[pos] = grub_malloc ((len + 1) * sizeof (grub_uint32_t));
  102. if (!hist_lines[pos])
  103. {
  104. grub_print_error ();
  105. grub_errno = GRUB_ERR_NONE;
  106. return ;
  107. }
  108. grub_memcpy (hist_lines[pos], s, len * sizeof (grub_uint32_t));
  109. hist_lines[pos][len] = 0;
  110. }
  111. /* Insert a new history line S on the top of the history. */
  112. static void
  113. grub_history_add (grub_uint32_t *s, grub_size_t len)
  114. {
  115. /* Remove the oldest entry in the history to make room for a new
  116. entry. */
  117. if (hist_used + 1 > hist_size)
  118. {
  119. hist_end--;
  120. if (hist_end < 0)
  121. hist_end = hist_size + hist_end;
  122. grub_free (hist_lines[hist_end]);
  123. }
  124. else
  125. hist_used++;
  126. /* Move to the next position. */
  127. hist_pos--;
  128. if (hist_pos < 0)
  129. hist_pos = hist_size + hist_pos;
  130. /* Insert into history. */
  131. hist_lines[hist_pos] = NULL;
  132. grub_history_set (hist_pos, s, len);
  133. }
  134. /* Replace the history entry on position POS with the string S. */
  135. static void
  136. grub_history_replace (unsigned pos, grub_uint32_t *s, grub_size_t len)
  137. {
  138. grub_history_set ((hist_pos + pos) % hist_size, s, len);
  139. }
  140. /* A completion hook to print items. */
  141. static void
  142. print_completion (const char *item, grub_completion_type_t type, int count)
  143. {
  144. if (count == 0)
  145. {
  146. /* If this is the first time, print a label. */
  147. grub_puts ("");
  148. switch (type)
  149. {
  150. case GRUB_COMPLETION_TYPE_COMMAND:
  151. grub_puts_ (N_("Possible commands are:"));
  152. break;
  153. case GRUB_COMPLETION_TYPE_DEVICE:
  154. grub_puts_ (N_("Possible devices are:"));
  155. break;
  156. case GRUB_COMPLETION_TYPE_FILE:
  157. grub_puts_ (N_("Possible files are:"));
  158. break;
  159. case GRUB_COMPLETION_TYPE_PARTITION:
  160. grub_puts_ (N_("Possible partitions are:"));
  161. break;
  162. case GRUB_COMPLETION_TYPE_ARGUMENT:
  163. grub_puts_ (N_("Possible arguments are:"));
  164. break;
  165. default:
  166. /* TRANSLATORS: this message is used if none of above matches.
  167. This shouldn't happen but please use the general term for
  168. "thing" or "object". */
  169. grub_puts_ (N_("Possible things are:"));
  170. break;
  171. }
  172. grub_puts ("");
  173. }
  174. if (type == GRUB_COMPLETION_TYPE_PARTITION)
  175. {
  176. grub_normal_print_device_info (item);
  177. grub_errno = GRUB_ERR_NONE;
  178. }
  179. else
  180. grub_printf (" %s", item);
  181. }
  182. struct cmdline_term
  183. {
  184. struct grub_term_coordinate pos;
  185. unsigned ystart, width, height;
  186. unsigned prompt_len;
  187. struct grub_term_output *term;
  188. };
  189. static inline void
  190. cl_set_pos (struct cmdline_term *cl_term, grub_size_t lpos)
  191. {
  192. cl_term->pos.x = (cl_term->prompt_len + lpos) % cl_term->width;
  193. cl_term->pos.y = cl_term->ystart
  194. + (cl_term->prompt_len + lpos) / cl_term->width;
  195. grub_term_gotoxy (cl_term->term, cl_term->pos);
  196. }
  197. static void
  198. cl_set_pos_all (struct cmdline_term *cl_terms, unsigned nterms,
  199. grub_size_t lpos)
  200. {
  201. unsigned i;
  202. for (i = 0; i < nterms; i++)
  203. cl_set_pos (&cl_terms[i], lpos);
  204. }
  205. static inline void __attribute__ ((always_inline))
  206. cl_print (struct cmdline_term *cl_term, grub_uint32_t c,
  207. grub_uint32_t *start, grub_uint32_t *end)
  208. {
  209. grub_uint32_t *p;
  210. for (p = start; p < end; p++)
  211. {
  212. if (c)
  213. grub_putcode (c, cl_term->term);
  214. else
  215. grub_putcode (*p, cl_term->term);
  216. cl_term->pos.x++;
  217. if (cl_term->pos.x >= cl_term->width - 1)
  218. {
  219. cl_term->pos.x = 0;
  220. if (cl_term->pos.y >= (unsigned) (cl_term->height - 1))
  221. cl_term->ystart--;
  222. else
  223. cl_term->pos.y++;
  224. grub_putcode ('\n', cl_term->term);
  225. }
  226. }
  227. }
  228. static void
  229. cl_print_all (struct cmdline_term *cl_terms, unsigned nterms,
  230. grub_uint32_t c, grub_uint32_t *start, grub_uint32_t *end)
  231. {
  232. unsigned i;
  233. for (i = 0; i < nterms; i++)
  234. cl_print (&cl_terms[i], c, start, end);
  235. }
  236. static void
  237. init_clterm (struct cmdline_term *cl_term_cur)
  238. {
  239. cl_term_cur->pos.x = cl_term_cur->prompt_len;
  240. cl_term_cur->pos.y = grub_term_getxy (cl_term_cur->term).y;
  241. cl_term_cur->ystart = cl_term_cur->pos.y;
  242. cl_term_cur->width = grub_term_width (cl_term_cur->term);
  243. cl_term_cur->height = grub_term_height (cl_term_cur->term);
  244. }
  245. static void
  246. cl_delete (struct cmdline_term *cl_terms, unsigned nterms,
  247. grub_uint32_t *buf,
  248. grub_size_t lpos, grub_size_t *llen, unsigned len)
  249. {
  250. if (lpos + len <= (*llen))
  251. {
  252. cl_set_pos_all (cl_terms, nterms, (*llen) - len);
  253. cl_print_all (cl_terms, nterms, ' ', buf + (*llen) - len, buf + (*llen));
  254. cl_set_pos_all (cl_terms, nterms, lpos);
  255. grub_memmove (buf + lpos, buf + lpos + len,
  256. sizeof (grub_uint32_t) * ((*llen) - lpos + 1));
  257. (*llen) -= len;
  258. cl_print_all (cl_terms, nterms, 0, buf + lpos, buf + (*llen));
  259. cl_set_pos_all (cl_terms, nterms, lpos);
  260. }
  261. }
  262. static void
  263. cl_insert (struct cmdline_term *cl_terms, unsigned nterms,
  264. grub_size_t *lpos, grub_size_t *llen,
  265. grub_size_t *max_len, grub_uint32_t **buf,
  266. const grub_uint32_t *str)
  267. {
  268. grub_size_t len = strlen_ucs4 (str);
  269. if (len + (*llen) >= (*max_len))
  270. {
  271. grub_uint32_t *nbuf;
  272. (*max_len) *= 2;
  273. nbuf = grub_realloc ((*buf), sizeof (grub_uint32_t) * (*max_len));
  274. if (nbuf)
  275. (*buf) = nbuf;
  276. else
  277. {
  278. grub_print_error ();
  279. grub_errno = GRUB_ERR_NONE;
  280. (*max_len) /= 2;
  281. }
  282. }
  283. if (len + (*llen) < (*max_len))
  284. {
  285. grub_memmove ((*buf) + (*lpos) + len, (*buf) + (*lpos),
  286. ((*llen) - (*lpos) + 1) * sizeof (grub_uint32_t));
  287. grub_memmove ((*buf) + (*lpos), str, len * sizeof (grub_uint32_t));
  288. (*llen) += len;
  289. cl_set_pos_all (cl_terms, nterms, (*lpos));
  290. cl_print_all (cl_terms, nterms, 0, *buf + (*lpos), *buf + (*llen));
  291. (*lpos) += len;
  292. cl_set_pos_all (cl_terms, nterms, (*lpos));
  293. }
  294. }
  295. /* Get a command-line. If ESC is pushed, return zero,
  296. otherwise return command line. */
  297. /* FIXME: The dumb interface is not supported yet. */
  298. char *
  299. grub_cmdline_get (const char *prompt_translated)
  300. {
  301. grub_size_t lpos, llen;
  302. grub_uint32_t *buf;
  303. grub_size_t max_len = 256;
  304. int key;
  305. int histpos = 0;
  306. struct cmdline_term *cl_terms;
  307. char *ret;
  308. unsigned nterms;
  309. buf = grub_malloc (max_len * sizeof (grub_uint32_t));
  310. if (!buf)
  311. return 0;
  312. lpos = llen = 0;
  313. buf[0] = '\0';
  314. {
  315. grub_term_output_t term;
  316. FOR_ACTIVE_TERM_OUTPUTS(term)
  317. if ((grub_term_getxy (term).x) != 0)
  318. grub_putcode ('\n', term);
  319. }
  320. grub_xputs (prompt_translated);
  321. grub_xputs (" ");
  322. grub_normal_reset_more ();
  323. {
  324. struct cmdline_term *cl_term_cur;
  325. struct grub_term_output *cur;
  326. grub_uint32_t *unicode_msg;
  327. grub_size_t msg_len = grub_strlen (prompt_translated) + 3;
  328. nterms = 0;
  329. FOR_ACTIVE_TERM_OUTPUTS(cur)
  330. nterms++;
  331. cl_terms = grub_malloc (sizeof (cl_terms[0]) * nterms);
  332. if (!cl_terms)
  333. {
  334. grub_free (buf);
  335. return 0;
  336. }
  337. cl_term_cur = cl_terms;
  338. unicode_msg = grub_malloc (msg_len * sizeof (grub_uint32_t));
  339. if (!unicode_msg)
  340. {
  341. grub_free (buf);
  342. grub_free (cl_terms);
  343. return 0;
  344. }
  345. msg_len = grub_utf8_to_ucs4 (unicode_msg, msg_len - 1,
  346. (grub_uint8_t *) prompt_translated, -1, 0);
  347. unicode_msg[msg_len++] = ' ';
  348. FOR_ACTIVE_TERM_OUTPUTS(cur)
  349. {
  350. cl_term_cur->term = cur;
  351. cl_term_cur->prompt_len = grub_getstringwidth (unicode_msg,
  352. unicode_msg + msg_len,
  353. cur);
  354. init_clterm (cl_term_cur);
  355. cl_term_cur++;
  356. }
  357. grub_free (unicode_msg);
  358. }
  359. if (hist_used == 0)
  360. grub_history_add (buf, llen);
  361. grub_refresh ();
  362. while ((key = grub_getkey ()) != '\n' && key != '\r')
  363. {
  364. switch (key)
  365. {
  366. case GRUB_TERM_CTRL | 'a':
  367. case GRUB_TERM_KEY_HOME:
  368. lpos = 0;
  369. cl_set_pos_all (cl_terms, nterms, lpos);
  370. break;
  371. case GRUB_TERM_CTRL | 'b':
  372. case GRUB_TERM_KEY_LEFT:
  373. if (lpos > 0)
  374. {
  375. lpos--;
  376. cl_set_pos_all (cl_terms, nterms, lpos);
  377. }
  378. break;
  379. case GRUB_TERM_CTRL | 'e':
  380. case GRUB_TERM_KEY_END:
  381. lpos = llen;
  382. cl_set_pos_all (cl_terms, nterms, lpos);
  383. break;
  384. case GRUB_TERM_CTRL | 'f':
  385. case GRUB_TERM_KEY_RIGHT:
  386. if (lpos < llen)
  387. {
  388. lpos++;
  389. cl_set_pos_all (cl_terms, nterms, lpos);
  390. }
  391. break;
  392. case GRUB_TERM_CTRL | 'i':
  393. case '\t':
  394. {
  395. int restore;
  396. char *insertu8;
  397. char *bufu8;
  398. grub_uint32_t c;
  399. c = buf[lpos];
  400. buf[lpos] = '\0';
  401. bufu8 = grub_ucs4_to_utf8_alloc (buf, lpos);
  402. buf[lpos] = c;
  403. if (!bufu8)
  404. {
  405. grub_print_error ();
  406. grub_errno = GRUB_ERR_NONE;
  407. break;
  408. }
  409. insertu8 = grub_normal_do_completion (bufu8, &restore,
  410. print_completion);
  411. grub_free (bufu8);
  412. grub_normal_reset_more ();
  413. if (restore)
  414. {
  415. unsigned i;
  416. /* Restore the prompt. */
  417. grub_xputs ("\n");
  418. grub_xputs (prompt_translated);
  419. grub_xputs (" ");
  420. for (i = 0; i < nterms; i++)
  421. init_clterm (&cl_terms[i]);
  422. cl_print_all (cl_terms, nterms, 0, buf, buf + llen);
  423. }
  424. if (insertu8)
  425. {
  426. grub_size_t insertlen;
  427. grub_ssize_t t;
  428. grub_uint32_t *insert;
  429. insertlen = grub_strlen (insertu8);
  430. insert = grub_malloc ((insertlen + 1) * sizeof (grub_uint32_t));
  431. if (!insert)
  432. {
  433. grub_free (insertu8);
  434. grub_print_error ();
  435. grub_errno = GRUB_ERR_NONE;
  436. break;
  437. }
  438. t = grub_utf8_to_ucs4 (insert, insertlen,
  439. (grub_uint8_t *) insertu8,
  440. insertlen, 0);
  441. if (t > 0)
  442. {
  443. if (insert[t-1] == ' ' && buf[lpos] == ' ')
  444. {
  445. insert[t-1] = 0;
  446. if (t != 1)
  447. cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, insert);
  448. lpos++;
  449. }
  450. else
  451. {
  452. insert[t] = 0;
  453. cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, insert);
  454. }
  455. }
  456. grub_free (insertu8);
  457. grub_free (insert);
  458. }
  459. cl_set_pos_all (cl_terms, nterms, lpos);
  460. }
  461. break;
  462. case GRUB_TERM_CTRL | 'k':
  463. if (lpos < llen)
  464. {
  465. grub_free (kill_buf);
  466. kill_buf = grub_malloc ((llen - lpos + 1)
  467. * sizeof (grub_uint32_t));
  468. if (grub_errno)
  469. {
  470. grub_print_error ();
  471. grub_errno = GRUB_ERR_NONE;
  472. }
  473. else
  474. {
  475. grub_memcpy (kill_buf, buf + lpos,
  476. (llen - lpos + 1) * sizeof (grub_uint32_t));
  477. kill_buf[llen - lpos] = 0;
  478. }
  479. cl_delete (cl_terms, nterms,
  480. buf, lpos, &llen, llen - lpos);
  481. }
  482. break;
  483. case GRUB_TERM_CTRL | 'n':
  484. case GRUB_TERM_KEY_DOWN:
  485. {
  486. grub_uint32_t *hist;
  487. lpos = 0;
  488. if (histpos > 0)
  489. {
  490. grub_history_replace (histpos, buf, llen);
  491. histpos--;
  492. }
  493. cl_delete (cl_terms, nterms,
  494. buf, lpos, &llen, llen);
  495. hist = grub_history_get (histpos);
  496. cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, hist);
  497. break;
  498. }
  499. case GRUB_TERM_KEY_UP:
  500. case GRUB_TERM_CTRL | 'p':
  501. {
  502. grub_uint32_t *hist;
  503. lpos = 0;
  504. if (histpos < hist_used - 1)
  505. {
  506. grub_history_replace (histpos, buf, llen);
  507. histpos++;
  508. }
  509. cl_delete (cl_terms, nterms,
  510. buf, lpos, &llen, llen);
  511. hist = grub_history_get (histpos);
  512. cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, hist);
  513. }
  514. break;
  515. case GRUB_TERM_CTRL | 'u':
  516. if (lpos > 0)
  517. {
  518. grub_size_t n = lpos;
  519. grub_free (kill_buf);
  520. kill_buf = grub_malloc ((n + 1) * sizeof(grub_uint32_t));
  521. if (grub_errno)
  522. {
  523. grub_print_error ();
  524. grub_errno = GRUB_ERR_NONE;
  525. }
  526. if (kill_buf)
  527. {
  528. grub_memcpy (kill_buf, buf, n * sizeof(grub_uint32_t));
  529. kill_buf[n] = 0;
  530. }
  531. lpos = 0;
  532. cl_set_pos_all (cl_terms, nterms, lpos);
  533. cl_delete (cl_terms, nterms,
  534. buf, lpos, &llen, n);
  535. }
  536. break;
  537. case GRUB_TERM_CTRL | 'y':
  538. if (kill_buf)
  539. cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, kill_buf);
  540. break;
  541. case GRUB_TERM_ESC:
  542. grub_free (cl_terms);
  543. grub_free (buf);
  544. return 0;
  545. case GRUB_TERM_BACKSPACE:
  546. if (lpos > 0)
  547. {
  548. lpos--;
  549. cl_set_pos_all (cl_terms, nterms, lpos);
  550. }
  551. else
  552. break;
  553. /* fall through */
  554. case GRUB_TERM_CTRL | 'd':
  555. case GRUB_TERM_KEY_DC:
  556. if (lpos < llen)
  557. cl_delete (cl_terms, nterms,
  558. buf, lpos, &llen, 1);
  559. break;
  560. default:
  561. if (grub_isprint (key))
  562. {
  563. grub_uint32_t str[2];
  564. str[0] = key;
  565. str[1] = '\0';
  566. cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, str);
  567. }
  568. break;
  569. }
  570. grub_refresh ();
  571. }
  572. grub_xputs ("\n");
  573. grub_refresh ();
  574. histpos = 0;
  575. if (strlen_ucs4 (buf) > 0)
  576. {
  577. grub_uint32_t empty[] = { 0 };
  578. grub_history_replace (histpos, buf, llen);
  579. grub_history_add (empty, 0);
  580. }
  581. ret = grub_ucs4_to_utf8_alloc (buf, llen + 1);
  582. grub_free (buf);
  583. grub_free (cl_terms);
  584. return ret;
  585. }