menu_entry.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2005,2006,2007,2008,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/term.h>
  20. #include <grub/misc.h>
  21. #include <grub/mm.h>
  22. #include <grub/loader.h>
  23. #include <grub/command.h>
  24. #include <grub/parser.h>
  25. #include <grub/script_sh.h>
  26. #include <grub/auth.h>
  27. #include <grub/i18n.h>
  28. #include <grub/charset.h>
  29. enum update_mode
  30. {
  31. NO_LINE,
  32. SINGLE_LINE,
  33. ALL_LINES
  34. };
  35. struct line
  36. {
  37. /* The line buffer. */
  38. grub_uint32_t *buf;
  39. /* The length of the line. */
  40. int len;
  41. /* The maximum length of the line. */
  42. int max_len;
  43. struct grub_term_pos **pos;
  44. };
  45. struct per_term_screen
  46. {
  47. struct grub_term_output *term;
  48. int y_line_start;
  49. struct grub_term_screen_geometry geo;
  50. /* Scratch variables used when updating. Having them here avoids
  51. loads of small mallocs. */
  52. int orig_num;
  53. int down;
  54. enum update_mode mode;
  55. };
  56. struct screen
  57. {
  58. /* The array of lines. */
  59. struct line *lines;
  60. /* The number of lines. */
  61. int num_lines;
  62. /* The current column. */
  63. int column;
  64. /* The real column. */
  65. int real_column;
  66. /* The current line. */
  67. int line;
  68. /* The kill buffer. */
  69. char *killed_text;
  70. /* The flag of a completion window. */
  71. int completion_shown;
  72. int submenu;
  73. struct per_term_screen *terms;
  74. unsigned nterms;
  75. };
  76. /* Used for storing completion items temporarily. */
  77. static struct {
  78. char *buf;
  79. grub_size_t len;
  80. grub_size_t max_len;
  81. } completion_buffer;
  82. static int completion_type;
  83. /* Initialize a line. */
  84. static int
  85. init_line (struct screen *screen, struct line *linep)
  86. {
  87. linep->len = 0;
  88. linep->max_len = 80;
  89. linep->buf = grub_malloc ((linep->max_len + 1) * sizeof (linep->buf[0]));
  90. linep->pos = grub_zalloc (screen->nterms * sizeof (linep->pos[0]));
  91. if (! linep->buf || !linep->pos)
  92. {
  93. grub_free (linep->buf);
  94. grub_free (linep->pos);
  95. return 0;
  96. }
  97. return 1;
  98. }
  99. /* Allocate extra space if necessary. */
  100. static int
  101. ensure_space (struct line *linep, int extra)
  102. {
  103. if (linep->max_len < linep->len + extra)
  104. {
  105. linep->max_len = 2 * (linep->len + extra);
  106. linep->buf = grub_realloc (linep->buf, (linep->max_len + 1) * sizeof (linep->buf[0]));
  107. if (! linep->buf)
  108. return 0;
  109. }
  110. return 1;
  111. }
  112. /* Return the number of lines occupied by this line on the screen. */
  113. static int
  114. get_logical_num_lines (struct line *linep, struct per_term_screen *term_screen)
  115. {
  116. grub_size_t width = grub_getstringwidth (linep->buf, linep->buf + linep->len,
  117. term_screen->term);
  118. /* Empty line still consumes space on screen */
  119. return width ? (width + (unsigned) term_screen->geo.entry_width - 1) /
  120. (unsigned) term_screen->geo.entry_width
  121. : 1;
  122. }
  123. static void
  124. advance_to (struct screen *screen, int c)
  125. {
  126. if (c > screen->lines[screen->line].len)
  127. c = screen->lines[screen->line].len;
  128. screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf
  129. + screen->lines[screen->line].len,
  130. screen->lines[screen->line].buf
  131. + c)
  132. - screen->lines[screen->line].buf;
  133. }
  134. /* Print an empty line. */
  135. static void
  136. print_empty_line (int y, struct per_term_screen *term_screen)
  137. {
  138. int i;
  139. grub_term_gotoxy (term_screen->term,
  140. (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
  141. y + term_screen->geo.first_entry_y });
  142. for (i = 0; i < term_screen->geo.entry_width + 1; i++)
  143. grub_putcode (' ', term_screen->term);
  144. }
  145. static void
  146. print_updown (int upflag, int downflag, struct per_term_screen *term_screen)
  147. {
  148. grub_term_gotoxy (term_screen->term,
  149. (struct grub_term_coordinate) { term_screen->geo.first_entry_x
  150. + term_screen->geo.entry_width + 1
  151. + term_screen->geo.border,
  152. term_screen->geo.first_entry_y });
  153. if (upflag && downflag)
  154. grub_putcode (GRUB_UNICODE_UPDOWNARROW, term_screen->term);
  155. else if (upflag)
  156. grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
  157. else if (downflag)
  158. grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
  159. else
  160. grub_putcode (' ', term_screen->term);
  161. }
  162. /* Print an up arrow. */
  163. static void
  164. print_up (int flag, struct per_term_screen *term_screen)
  165. {
  166. grub_term_gotoxy (term_screen->term,
  167. (struct grub_term_coordinate) { term_screen->geo.first_entry_x
  168. + term_screen->geo.entry_width + 1
  169. + term_screen->geo.border,
  170. term_screen->geo.first_entry_y });
  171. if (flag)
  172. grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
  173. else
  174. grub_putcode (' ', term_screen->term);
  175. }
  176. /* Print a down arrow. */
  177. static void
  178. print_down (int flag, struct per_term_screen *term_screen)
  179. {
  180. grub_term_gotoxy (term_screen->term,
  181. (struct grub_term_coordinate) { term_screen->geo.first_entry_x
  182. + term_screen->geo.entry_width + 1
  183. + term_screen->geo.border,
  184. term_screen->geo.first_entry_y
  185. + term_screen->geo.num_entries - 1 });
  186. if (flag)
  187. grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
  188. else
  189. grub_putcode (' ', term_screen->term);
  190. }
  191. /* Draw the lines of the screen SCREEN. */
  192. static void
  193. update_screen (struct screen *screen, struct per_term_screen *term_screen,
  194. int region_start, int region_column __attribute__ ((unused)),
  195. int up, int down, enum update_mode mode)
  196. {
  197. int up_flag = 0;
  198. int down_flag = 0;
  199. int y;
  200. int i;
  201. struct line *linep;
  202. y = term_screen->y_line_start;
  203. linep = screen->lines;
  204. for (i = 0; i < screen->line; i++, linep++)
  205. y += get_logical_num_lines (linep, term_screen);
  206. linep = screen->lines + screen->line;
  207. grub_size_t t = grub_getstringwidth (linep->buf, linep->buf + screen->column,
  208. term_screen->term);
  209. y += t / (unsigned) term_screen->geo.entry_width;
  210. if (t % (unsigned) term_screen->geo.entry_width == 0
  211. && t != 0 && screen->column == linep->len)
  212. y--;
  213. /* Check if scrolling is necessary. */
  214. if (y < 0 || y >= term_screen->geo.num_entries)
  215. {
  216. int delta;
  217. if (y < 0)
  218. delta = -y;
  219. else
  220. delta = term_screen->geo.num_entries - 1 - y;
  221. term_screen->y_line_start += delta;
  222. region_start = 0;
  223. up = 1;
  224. down = 1;
  225. mode = ALL_LINES;
  226. }
  227. grub_term_setcursor (term_screen->term, 0);
  228. if (mode != NO_LINE)
  229. {
  230. /* Draw lines. This code is tricky, because this must calculate logical
  231. positions. */
  232. y = term_screen->y_line_start;
  233. i = 0;
  234. linep = screen->lines;
  235. while (1)
  236. {
  237. int add;
  238. add = get_logical_num_lines (linep, term_screen);
  239. if (y + add > 0)
  240. break;
  241. i++;
  242. linep++;
  243. y += add;
  244. }
  245. if (y < 0 || i > 0)
  246. up_flag = 1;
  247. do
  248. {
  249. struct grub_term_pos **pos;
  250. if (linep >= screen->lines + screen->num_lines)
  251. break;
  252. pos = linep->pos + (term_screen - screen->terms);
  253. if (!*pos)
  254. *pos = grub_zalloc ((linep->len + 1) * sizeof (**pos));
  255. if (i == region_start || linep == screen->lines + screen->line
  256. || (i > region_start && mode == ALL_LINES))
  257. {
  258. grub_term_gotoxy (term_screen->term,
  259. (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
  260. (y < 0 ? 0 : y)
  261. + term_screen->geo.first_entry_y });
  262. grub_print_ucs4_menu (linep->buf,
  263. linep->buf + linep->len,
  264. term_screen->geo.first_entry_x,
  265. term_screen->geo.right_margin,
  266. term_screen->term,
  267. (y < 0) ? -y : 0,
  268. term_screen->geo.num_entries
  269. - ((y > 0) ? y : 0), '\\',
  270. *pos);
  271. }
  272. y += get_logical_num_lines (linep, term_screen);
  273. if (y >= term_screen->geo.num_entries)
  274. {
  275. if (i + 1 < screen->num_lines)
  276. down_flag = 1;
  277. }
  278. linep++;
  279. i++;
  280. if (mode == ALL_LINES && i == screen->num_lines)
  281. for (; y < term_screen->geo.num_entries; y++)
  282. print_empty_line (y, term_screen);
  283. }
  284. while (y < term_screen->geo.num_entries);
  285. /* Draw up and down arrows. */
  286. if (term_screen->geo.num_entries == 1)
  287. {
  288. if (up || down)
  289. print_updown (up_flag, down_flag, term_screen);
  290. }
  291. else
  292. {
  293. if (up)
  294. print_up (up_flag, term_screen);
  295. if (down)
  296. print_down (down_flag, term_screen);
  297. }
  298. }
  299. /* Place the cursor. */
  300. if (screen->lines[screen->line].pos[term_screen - screen->terms])
  301. {
  302. const struct grub_term_pos *cpos;
  303. for (cpos = &(screen->lines[screen->line].pos[term_screen - screen->terms])[screen->column];
  304. cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0];
  305. cpos--)
  306. if (cpos->valid)
  307. break;
  308. y = term_screen->y_line_start;
  309. for (i = 0; i < screen->line; i++)
  310. y += get_logical_num_lines (screen->lines + i, term_screen);
  311. if (cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0])
  312. grub_term_gotoxy (term_screen->term,
  313. (struct grub_term_coordinate) { cpos->x + term_screen->geo.first_entry_x,
  314. cpos->y + y
  315. + term_screen->geo.first_entry_y });
  316. else
  317. grub_term_gotoxy (term_screen->term,
  318. (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
  319. y + term_screen->geo.first_entry_y });
  320. }
  321. grub_term_setcursor (term_screen->term, 1);
  322. grub_term_refresh (term_screen->term);
  323. }
  324. static void
  325. update_screen_all (struct screen *screen,
  326. int region_start, int region_column,
  327. int up, int down, enum update_mode mode)
  328. {
  329. unsigned i;
  330. for (i = 0; i < screen->nterms; i++)
  331. update_screen (screen, &screen->terms[i], region_start, region_column,
  332. up, down, mode);
  333. }
  334. static int
  335. insert_string (struct screen *screen, const char *s, int update)
  336. {
  337. int region_start = screen->num_lines;
  338. int region_column = 0;
  339. unsigned i;
  340. for (i = 0; i < screen->nterms; i++)
  341. {
  342. screen->terms[i].down = 0;
  343. screen->terms[i].mode = NO_LINE;
  344. }
  345. while (*s)
  346. {
  347. if (*s == '\n')
  348. {
  349. /* LF is special because it creates a new line. */
  350. struct line *current_linep;
  351. struct line *next_linep;
  352. int size;
  353. /* Make a new line. */
  354. screen->num_lines++;
  355. screen->lines = grub_realloc (screen->lines,
  356. screen->num_lines
  357. * sizeof (screen->lines[0]));
  358. if (! screen->lines)
  359. return 0;
  360. /* Shift down if not appending after the last line. */
  361. if (screen->line < screen->num_lines - 2)
  362. grub_memmove (screen->lines + screen->line + 2,
  363. screen->lines + screen->line + 1,
  364. ((screen->num_lines - screen->line - 2)
  365. * sizeof (struct line)));
  366. if (! init_line (screen, screen->lines + screen->line + 1))
  367. return 0;
  368. /* Fold the line. */
  369. current_linep = screen->lines + screen->line;
  370. next_linep = current_linep + 1;
  371. size = current_linep->len - screen->column;
  372. if (! ensure_space (next_linep, size))
  373. return 0;
  374. grub_memmove (next_linep->buf,
  375. current_linep->buf + screen->column,
  376. size * sizeof (next_linep->buf[0]));
  377. current_linep->len = screen->column;
  378. for (i = 0; i < screen->nterms; i++)
  379. {
  380. grub_free (current_linep->pos[i]);
  381. current_linep->pos[i] = 0;
  382. }
  383. next_linep->len = size;
  384. /* Update a dirty region. */
  385. if (region_start > screen->line)
  386. {
  387. region_start = screen->line;
  388. region_column = screen->column;
  389. }
  390. for (i = 0; i < screen->nterms; i++)
  391. {
  392. screen->terms[i].mode = ALL_LINES;
  393. screen->terms[i].down = 1; /* XXX not optimal. */
  394. }
  395. /* Move the cursor. */
  396. screen->column = screen->real_column = 0;
  397. screen->line++;
  398. s++;
  399. }
  400. else
  401. {
  402. /* All but LF. */
  403. const char *p;
  404. struct line *current_linep;
  405. int size;
  406. grub_uint32_t *unicode_msg;
  407. /* Find a string delimited by LF. */
  408. p = grub_strchr (s, '\n');
  409. if (! p)
  410. p = s + grub_strlen (s);
  411. /* Insert the string. */
  412. current_linep = screen->lines + screen->line;
  413. unicode_msg = grub_malloc ((p - s) * sizeof (grub_uint32_t));
  414. if (!unicode_msg)
  415. return 0;
  416. size = grub_utf8_to_ucs4 (unicode_msg, (p - s),
  417. (grub_uint8_t *) s, (p - s), 0);
  418. if (! ensure_space (current_linep, size))
  419. {
  420. grub_free (unicode_msg);
  421. return 0;
  422. }
  423. grub_memmove (current_linep->buf + screen->column + size,
  424. current_linep->buf + screen->column,
  425. (current_linep->len - screen->column)
  426. * sizeof (current_linep->buf[0]));
  427. grub_memmove (current_linep->buf + screen->column,
  428. unicode_msg,
  429. size * sizeof (current_linep->buf[0]));
  430. grub_free (unicode_msg);
  431. for (i = 0; i < screen->nterms; i++)
  432. {
  433. grub_free (current_linep->pos[i]);
  434. current_linep->pos[i] = 0;
  435. }
  436. for (i = 0; i < screen->nterms; i++)
  437. screen->terms[i].orig_num = get_logical_num_lines (current_linep,
  438. &screen->terms[i]);
  439. current_linep->len += size;
  440. /* Update the dirty region. */
  441. if (region_start > screen->line)
  442. {
  443. region_start = screen->line;
  444. region_column = screen->column;
  445. }
  446. for (i = 0; i < screen->nterms; i++)
  447. {
  448. int new_num = get_logical_num_lines (current_linep,
  449. &screen->terms[i]);
  450. if (screen->terms[i].orig_num != new_num)
  451. {
  452. screen->terms[i].mode = ALL_LINES;
  453. screen->terms[i].down = 1; /* XXX not optimal. */
  454. }
  455. else if (screen->terms[i].mode != ALL_LINES)
  456. screen->terms[i].mode = SINGLE_LINE;
  457. }
  458. /* Move the cursor. */
  459. advance_to (screen, screen->column + size);
  460. screen->real_column = screen->column;
  461. s = p;
  462. }
  463. }
  464. if (update)
  465. for (i = 0; i < screen->nterms; i++)
  466. update_screen (screen, &screen->terms[i],
  467. region_start, region_column, 0, screen->terms[i].down,
  468. screen->terms[i].mode);
  469. return 1;
  470. }
  471. /* Release the resource allocated for SCREEN. */
  472. static void
  473. destroy_screen (struct screen *screen)
  474. {
  475. int i;
  476. if (screen->lines)
  477. for (i = 0; i < screen->num_lines; i++)
  478. {
  479. struct line *linep = screen->lines + i;
  480. if (linep)
  481. {
  482. unsigned j;
  483. if (linep->pos)
  484. for (j = 0; j < screen->nterms; j++)
  485. grub_free (linep->pos[j]);
  486. grub_free (linep->buf);
  487. grub_free (linep->pos);
  488. }
  489. }
  490. grub_free (screen->killed_text);
  491. grub_free (screen->lines);
  492. grub_free (screen->terms);
  493. grub_free (screen);
  494. }
  495. /* Make a new screen. */
  496. static struct screen *
  497. make_screen (grub_menu_entry_t entry)
  498. {
  499. struct screen *screen;
  500. unsigned i;
  501. /* Initialize the screen. */
  502. screen = grub_zalloc (sizeof (*screen));
  503. if (! screen)
  504. return 0;
  505. screen->submenu = entry->submenu;
  506. screen->num_lines = 1;
  507. screen->lines = grub_malloc (sizeof (struct line));
  508. if (! screen->lines)
  509. goto fail;
  510. /* Initialize the first line which must be always present. */
  511. if (! init_line (screen, screen->lines))
  512. goto fail;
  513. insert_string (screen, (char *) entry->sourcecode, 0);
  514. /* Reset the cursor position. */
  515. screen->column = 0;
  516. screen->real_column = 0;
  517. screen->line = 0;
  518. for (i = 0; i < screen->nterms; i++)
  519. {
  520. screen->terms[i].y_line_start = 0;
  521. }
  522. return screen;
  523. fail:
  524. destroy_screen (screen);
  525. return 0;
  526. }
  527. static int
  528. forward_char (struct screen *screen, int update)
  529. {
  530. struct line *linep;
  531. linep = screen->lines + screen->line;
  532. if (screen->column < linep->len)
  533. {
  534. screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf
  535. + screen->lines[screen->line].len,
  536. screen->lines[screen->line].buf
  537. + screen->column + 1)
  538. - screen->lines[screen->line].buf;
  539. }
  540. else if (screen->num_lines > screen->line + 1)
  541. {
  542. screen->column = 0;
  543. screen->line++;
  544. }
  545. screen->real_column = screen->column;
  546. if (update)
  547. update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
  548. return 1;
  549. }
  550. static int
  551. backward_char (struct screen *screen, int update)
  552. {
  553. if (screen->column > 0)
  554. {
  555. struct grub_unicode_glyph glyph;
  556. struct line *linep;
  557. linep = screen->lines + screen->line;
  558. screen->column--;
  559. screen->column = grub_unicode_get_comb_start (linep->buf,
  560. linep->buf + screen->column)
  561. - linep->buf;
  562. grub_unicode_aglomerate_comb (screen->lines[screen->line].buf + screen->column,
  563. screen->lines[screen->line].len - screen->column,
  564. &glyph);
  565. screen->column = grub_unicode_get_comb_start (linep->buf,
  566. linep->buf + screen->column)
  567. - linep->buf;
  568. grub_unicode_destroy_glyph (&glyph);
  569. }
  570. else if (screen->line > 0)
  571. {
  572. screen->line--;
  573. screen->column = screen->lines[screen->line].len;
  574. }
  575. screen->real_column = screen->column;
  576. if (update)
  577. update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
  578. return 1;
  579. }
  580. static int
  581. previous_line (struct screen *screen, int update)
  582. {
  583. if (screen->line > 0)
  584. {
  585. struct line *linep;
  586. int col;
  587. screen->line--;
  588. linep = screen->lines + screen->line;
  589. if (linep->len < screen->real_column)
  590. col = linep->len;
  591. else
  592. col = screen->real_column;
  593. screen->column = 0;
  594. advance_to (screen, col);
  595. }
  596. else
  597. {
  598. screen->column = 0;
  599. }
  600. if (update)
  601. update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
  602. return 1;
  603. }
  604. static int
  605. next_line (struct screen *screen, int update)
  606. {
  607. if (screen->line < screen->num_lines - 1)
  608. {
  609. struct line *linep;
  610. int c;
  611. /* How many physical lines from the current position
  612. to the last physical line? */
  613. linep = screen->lines + screen->line;
  614. screen->line++;
  615. if ((linep + 1)->len < screen->real_column)
  616. c = (linep + 1)->len;
  617. else
  618. c = screen->real_column;
  619. screen->column = 0;
  620. advance_to (screen, c);
  621. }
  622. else
  623. advance_to (screen, screen->lines[screen->line].len);
  624. if (update)
  625. update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
  626. return 1;
  627. }
  628. static int
  629. beginning_of_line (struct screen *screen, int update)
  630. {
  631. screen->column = screen->real_column = 0;
  632. if (update)
  633. update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
  634. return 1;
  635. }
  636. static int
  637. end_of_line (struct screen *screen, int update)
  638. {
  639. advance_to (screen, screen->lines[screen->line].len);
  640. if (update)
  641. update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
  642. return 1;
  643. }
  644. static int
  645. delete_char (struct screen *screen, int update)
  646. {
  647. struct line *linep;
  648. int start = screen->num_lines;
  649. int column = 0;
  650. linep = screen->lines + screen->line;
  651. if (linep->len > screen->column)
  652. {
  653. unsigned i;
  654. for (i = 0; i < screen->nterms; i++)
  655. screen->terms[i].orig_num = get_logical_num_lines (linep, &screen->terms[i]);
  656. grub_memmove (linep->buf + screen->column,
  657. linep->buf + screen->column + 1,
  658. (linep->len - screen->column - 1)
  659. * sizeof (linep->buf[0]));
  660. linep->len--;
  661. for (i = 0; i < screen->nterms; i++)
  662. {
  663. grub_free (linep->pos[i]);
  664. linep->pos[i] = 0;
  665. }
  666. start = screen->line;
  667. column = screen->column;
  668. screen->real_column = screen->column;
  669. if (update)
  670. {
  671. for (i = 0; i < screen->nterms; i++)
  672. {
  673. int new_num;
  674. new_num = get_logical_num_lines (linep, &screen->terms[i]);
  675. if (screen->terms[i].orig_num != new_num)
  676. update_screen (screen, &screen->terms[i],
  677. start, column, 0, 0, ALL_LINES);
  678. else
  679. update_screen (screen, &screen->terms[i],
  680. start, column, 0, 0, SINGLE_LINE);
  681. }
  682. }
  683. }
  684. else if (screen->num_lines > screen->line + 1)
  685. {
  686. struct line *next_linep;
  687. unsigned i;
  688. next_linep = linep + 1;
  689. if (! ensure_space (linep, next_linep->len))
  690. return 0;
  691. grub_memmove (linep->buf + linep->len, next_linep->buf,
  692. next_linep->len * sizeof (linep->buf[0]));
  693. linep->len += next_linep->len;
  694. for (i = 0; i < screen->nterms; i++)
  695. {
  696. grub_free (linep->pos[i]);
  697. linep->pos[i] = 0;
  698. }
  699. grub_free (next_linep->buf);
  700. grub_memmove (next_linep,
  701. next_linep + 1,
  702. (screen->num_lines - screen->line - 2)
  703. * sizeof (struct line));
  704. screen->num_lines--;
  705. start = screen->line;
  706. column = screen->column;
  707. screen->real_column = screen->column;
  708. if (update)
  709. update_screen_all (screen, start, column, 0, 1, ALL_LINES);
  710. }
  711. return 1;
  712. }
  713. static int
  714. backward_delete_char (struct screen *screen, int update)
  715. {
  716. int saved_column;
  717. int saved_line;
  718. saved_column = screen->column;
  719. saved_line = screen->line;
  720. if (! backward_char (screen, 0))
  721. return 0;
  722. if (saved_column != screen->column || saved_line != screen->line)
  723. if (! delete_char (screen, update))
  724. return 0;
  725. return 1;
  726. }
  727. static int
  728. kill_line (struct screen *screen, int continuous, int update)
  729. {
  730. struct line *linep;
  731. char *p;
  732. int size;
  733. int offset;
  734. p = screen->killed_text;
  735. if (! continuous && p)
  736. p[0] = '\0';
  737. linep = screen->lines + screen->line;
  738. size = linep->len - screen->column;
  739. if (p)
  740. offset = grub_strlen (p);
  741. else
  742. offset = 0;
  743. if (size > 0)
  744. {
  745. unsigned i;
  746. p = grub_realloc (p, offset + size + 1);
  747. if (! p)
  748. return 0;
  749. grub_memmove (p + offset, linep->buf + screen->column, size);
  750. p[offset + size] = '\0';
  751. screen->killed_text = p;
  752. for (i = 0; i < screen->nterms; i++)
  753. screen->terms[i].orig_num = get_logical_num_lines (linep, &screen->terms[i]);
  754. linep->len = screen->column;
  755. if (update)
  756. {
  757. for (i = 0; i < screen->nterms; i++)
  758. {
  759. int new_num;
  760. new_num = get_logical_num_lines (linep, &screen->terms[i]);
  761. if (screen->terms[i].orig_num != new_num)
  762. update_screen (screen, &screen->terms[i],
  763. screen->line, screen->column, 0, 1, ALL_LINES);
  764. else
  765. update_screen (screen, &screen->terms[i],
  766. screen->line, screen->column, 0, 0, SINGLE_LINE);
  767. }
  768. }
  769. }
  770. else if (screen->line + 1 < screen->num_lines)
  771. {
  772. p = grub_realloc (p, offset + 1 + 1);
  773. if (! p)
  774. return 0;
  775. p[offset] = '\n';
  776. p[offset + 1] = '\0';
  777. screen->killed_text = p;
  778. return delete_char (screen, update);
  779. }
  780. return 1;
  781. }
  782. static int
  783. yank (struct screen *screen, int update)
  784. {
  785. if (screen->killed_text)
  786. return insert_string (screen, screen->killed_text, update);
  787. return 1;
  788. }
  789. static int
  790. open_line (struct screen *screen, int update)
  791. {
  792. if (! insert_string (screen, "\n", 0))
  793. return 0;
  794. if (! backward_char (screen, 0))
  795. return 0;
  796. if (update)
  797. update_screen_all (screen, screen->line, screen->column, 0, 1, ALL_LINES);
  798. return 1;
  799. }
  800. /* A completion hook to print items. */
  801. static void
  802. store_completion (const char *item, grub_completion_type_t type,
  803. int count __attribute__ ((unused)))
  804. {
  805. char *p;
  806. completion_type = type;
  807. /* Make sure that the completion buffer has enough room. */
  808. if (completion_buffer.max_len < (completion_buffer.len
  809. + (int) grub_strlen (item) + 1 + 1))
  810. {
  811. grub_size_t new_len;
  812. new_len = completion_buffer.len + grub_strlen (item) + 80;
  813. p = grub_realloc (completion_buffer.buf, new_len);
  814. if (! p)
  815. {
  816. /* Possibly not fatal. */
  817. grub_errno = GRUB_ERR_NONE;
  818. return;
  819. }
  820. p[completion_buffer.len] = 0;
  821. completion_buffer.buf = p;
  822. completion_buffer.max_len = new_len;
  823. }
  824. p = completion_buffer.buf + completion_buffer.len;
  825. if (completion_buffer.len != 0)
  826. {
  827. *p++ = ' ';
  828. completion_buffer.len++;
  829. }
  830. grub_strcpy (p, item);
  831. completion_buffer.len += grub_strlen (item);
  832. }
  833. static int
  834. complete (struct screen *screen, int continuous, int update)
  835. {
  836. struct line *linep;
  837. int restore;
  838. char *insert;
  839. static int count = -1;
  840. unsigned i;
  841. grub_uint32_t *ucs4;
  842. grub_size_t buflen;
  843. grub_ssize_t ucs4len;
  844. char *u8;
  845. if (continuous)
  846. count++;
  847. else
  848. count = 0;
  849. completion_buffer.buf = 0;
  850. completion_buffer.len = 0;
  851. completion_buffer.max_len = 0;
  852. linep = screen->lines + screen->line;
  853. u8 = grub_ucs4_to_utf8_alloc (linep->buf, screen->column);
  854. if (!u8)
  855. return 1;
  856. insert = grub_normal_do_completion (u8, &restore, store_completion);
  857. if (completion_buffer.buf)
  858. {
  859. buflen = grub_strlen (completion_buffer.buf);
  860. ucs4 = grub_malloc (sizeof (grub_uint32_t) * (buflen + 1));
  861. if (!ucs4)
  862. {
  863. grub_print_error ();
  864. grub_errno = GRUB_ERR_NONE;
  865. return 1;
  866. }
  867. ucs4len = grub_utf8_to_ucs4 (ucs4, buflen,
  868. (grub_uint8_t *) completion_buffer.buf,
  869. buflen, 0);
  870. ucs4[ucs4len] = 0;
  871. if (restore)
  872. for (i = 0; i < screen->nterms; i++)
  873. {
  874. unsigned width = grub_term_width (screen->terms[i].term);
  875. if (width > 2)
  876. width -= 2;
  877. if (width > 15)
  878. width -= 6;
  879. unsigned num_sections = ((completion_buffer.len
  880. + width - 1)
  881. / width);
  882. grub_uint32_t *endp;
  883. struct grub_term_coordinate pos;
  884. grub_uint32_t *p = ucs4;
  885. pos = grub_term_getxy (screen->terms[i].term);
  886. screen->completion_shown = 1;
  887. grub_term_gotoxy (screen->terms[i].term,
  888. (struct grub_term_coordinate) { 0,
  889. screen->terms[i].geo.timeout_y });
  890. if (screen->terms[i].geo.timeout_lines >= 2)
  891. {
  892. grub_puts_terminal (" ", screen->terms[i].term);
  893. switch (completion_type)
  894. {
  895. case GRUB_COMPLETION_TYPE_COMMAND:
  896. grub_puts_terminal (_("Possible commands are:"),
  897. screen->terms[i].term);
  898. break;
  899. case GRUB_COMPLETION_TYPE_DEVICE:
  900. grub_puts_terminal (_("Possible devices are:"),
  901. screen->terms[i].term);
  902. break;
  903. case GRUB_COMPLETION_TYPE_FILE:
  904. grub_puts_terminal (_("Possible files are:"),
  905. screen->terms[i].term);
  906. break;
  907. case GRUB_COMPLETION_TYPE_PARTITION:
  908. grub_puts_terminal (_("Possible partitions are:"),
  909. screen->terms[i].term);
  910. break;
  911. case GRUB_COMPLETION_TYPE_ARGUMENT:
  912. grub_puts_terminal (_("Possible arguments are:"),
  913. screen->terms[i].term);
  914. break;
  915. default:
  916. grub_puts_terminal (_("Possible things are:"),
  917. screen->terms[i].term);
  918. break;
  919. }
  920. grub_puts_terminal ("\n ", screen->terms[i].term);
  921. }
  922. p += ((unsigned) count % num_sections) * width;
  923. endp = p + width;
  924. if (p != ucs4)
  925. grub_putcode (GRUB_UNICODE_LEFTARROW, screen->terms[i].term);
  926. else
  927. grub_putcode (' ', screen->terms[i].term);
  928. grub_print_ucs4 (p, ucs4 + ucs4len < endp ? ucs4 + ucs4len : endp,
  929. 0, 0, screen->terms[i].term);
  930. if (ucs4 + ucs4len > endp)
  931. grub_putcode (GRUB_UNICODE_RIGHTARROW, screen->terms[i].term);
  932. grub_term_gotoxy (screen->terms[i].term, pos);
  933. }
  934. }
  935. if (insert)
  936. {
  937. insert_string (screen, insert, update);
  938. count = -1;
  939. grub_free (insert);
  940. }
  941. else if (update)
  942. grub_refresh ();
  943. grub_free (completion_buffer.buf);
  944. return 1;
  945. }
  946. /* Clear displayed completions. */
  947. static void
  948. clear_completions (struct per_term_screen *term_screen)
  949. {
  950. struct grub_term_coordinate pos;
  951. unsigned j;
  952. int i;
  953. pos = grub_term_getxy (term_screen->term);
  954. grub_term_gotoxy (term_screen->term,
  955. (struct grub_term_coordinate) { 0,
  956. term_screen->geo.timeout_y });
  957. for (i = 0; i < term_screen->geo.timeout_lines; i++)
  958. {
  959. for (j = 0; j < grub_term_width (term_screen->term) - 1; j++)
  960. grub_putcode (' ', term_screen->term);
  961. if (i + 1 < term_screen->geo.timeout_lines)
  962. grub_putcode ('\n', term_screen->term);
  963. }
  964. grub_term_gotoxy (term_screen->term, pos);
  965. grub_term_refresh (term_screen->term);
  966. }
  967. static void
  968. clear_completions_all (struct screen *screen)
  969. {
  970. unsigned i;
  971. for (i = 0; i < screen->nterms; i++)
  972. clear_completions (&screen->terms[i]);
  973. }
  974. /* Execute the command list in the screen SCREEN. */
  975. static int
  976. run (struct screen *screen)
  977. {
  978. char *script;
  979. int errs_before;
  980. grub_menu_t menu = NULL;
  981. char *dummy[1] = { NULL };
  982. grub_cls ();
  983. grub_printf (" ");
  984. grub_printf_ (N_("Booting a command list"));
  985. grub_printf ("\n\n");
  986. errs_before = grub_err_printed_errors;
  987. if (screen->submenu)
  988. {
  989. grub_env_context_open ();
  990. menu = grub_zalloc (sizeof (*menu));
  991. if (! menu)
  992. return 0;
  993. grub_env_set_menu (menu);
  994. }
  995. /* Execute the script, line for line. */
  996. {
  997. int i;
  998. grub_size_t size = 0, tot_size = 0;
  999. for (i = 0; i < screen->num_lines; i++)
  1000. tot_size += grub_get_num_of_utf8_bytes (screen->lines[i].buf,
  1001. screen->lines[i].len) + 1;
  1002. script = grub_malloc (tot_size + 1);
  1003. if (! script)
  1004. return 0;
  1005. for (i = 0; i < screen->num_lines; i++)
  1006. {
  1007. size += grub_ucs4_to_utf8 (screen->lines[i].buf, screen->lines[i].len,
  1008. (grub_uint8_t *) script + size,
  1009. tot_size - size);
  1010. script[size++] = '\n';
  1011. }
  1012. script[size] = '\0';
  1013. }
  1014. grub_script_execute_new_scope (script, 0, dummy);
  1015. grub_free (script);
  1016. if (errs_before != grub_err_printed_errors)
  1017. grub_wait_after_message ();
  1018. if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
  1019. /* Implicit execution of boot, only if something is loaded. */
  1020. grub_command_execute ("boot", 0, 0);
  1021. if (screen->submenu)
  1022. {
  1023. if (menu && menu->size)
  1024. {
  1025. grub_show_menu (menu, 1, 0);
  1026. grub_normal_free_menu (menu);
  1027. }
  1028. grub_env_context_close ();
  1029. }
  1030. if (grub_errno != GRUB_ERR_NONE)
  1031. {
  1032. grub_print_error ();
  1033. grub_errno = GRUB_ERR_NONE;
  1034. grub_wait_after_message ();
  1035. }
  1036. return 1;
  1037. }
  1038. /* Edit a menu entry with an Emacs-like interface. */
  1039. void
  1040. grub_menu_entry_run (grub_menu_entry_t entry)
  1041. {
  1042. struct screen *screen;
  1043. int prev_c;
  1044. grub_err_t err = GRUB_ERR_NONE;
  1045. unsigned i;
  1046. grub_term_output_t term;
  1047. err = grub_auth_check_authentication (NULL);
  1048. if (err)
  1049. {
  1050. grub_print_error ();
  1051. grub_errno = GRUB_ERR_NONE;
  1052. return;
  1053. }
  1054. screen = make_screen (entry);
  1055. if (! screen)
  1056. return;
  1057. screen->terms = NULL;
  1058. refresh:
  1059. grub_free (screen->terms);
  1060. screen->nterms = 0;
  1061. FOR_ACTIVE_TERM_OUTPUTS(term)
  1062. screen->nterms++;
  1063. for (i = 0; i < (unsigned) screen->num_lines; i++)
  1064. {
  1065. grub_free (screen->lines[i].pos);
  1066. screen->lines[i].pos = grub_zalloc (screen->nterms * sizeof (screen->lines[i].pos[0]));
  1067. if (! screen->lines[i].pos)
  1068. {
  1069. grub_print_error ();
  1070. destroy_screen (screen);
  1071. grub_errno = GRUB_ERR_NONE;
  1072. return;
  1073. }
  1074. }
  1075. screen->terms = grub_zalloc (screen->nterms * sizeof (screen->terms[0]));
  1076. if (!screen->terms)
  1077. {
  1078. grub_print_error ();
  1079. destroy_screen (screen);
  1080. grub_errno = GRUB_ERR_NONE;
  1081. return;
  1082. }
  1083. i = 0;
  1084. FOR_ACTIVE_TERM_OUTPUTS(term)
  1085. {
  1086. screen->terms[i].term = term;
  1087. screen->terms[i].y_line_start = 0;
  1088. i++;
  1089. }
  1090. /* Draw the screen. */
  1091. for (i = 0; i < screen->nterms; i++)
  1092. grub_menu_init_page (0, 1, &screen->terms[i].geo,
  1093. screen->terms[i].term);
  1094. update_screen_all (screen, 0, 0, 1, 1, ALL_LINES);
  1095. for (i = 0; i < screen->nterms; i++)
  1096. grub_term_setcursor (screen->terms[i].term, 1);
  1097. prev_c = '\0';
  1098. while (1)
  1099. {
  1100. int c = grub_getkey ();
  1101. if (screen->completion_shown)
  1102. {
  1103. clear_completions_all (screen);
  1104. screen->completion_shown = 0;
  1105. }
  1106. if (grub_normal_exit_level)
  1107. {
  1108. destroy_screen (screen);
  1109. return;
  1110. }
  1111. switch (c)
  1112. {
  1113. case GRUB_TERM_KEY_UP:
  1114. case GRUB_TERM_CTRL | 'p':
  1115. if (! previous_line (screen, 1))
  1116. goto fail;
  1117. break;
  1118. case GRUB_TERM_CTRL | 'n':
  1119. case GRUB_TERM_KEY_DOWN:
  1120. if (! next_line (screen, 1))
  1121. goto fail;
  1122. break;
  1123. case GRUB_TERM_CTRL | 'f':
  1124. case GRUB_TERM_KEY_RIGHT:
  1125. if (! forward_char (screen, 1))
  1126. goto fail;
  1127. break;
  1128. case GRUB_TERM_CTRL | 'b':
  1129. case GRUB_TERM_KEY_LEFT:
  1130. if (! backward_char (screen, 1))
  1131. goto fail;
  1132. break;
  1133. case GRUB_TERM_CTRL | 'a':
  1134. case GRUB_TERM_KEY_HOME:
  1135. if (! beginning_of_line (screen, 1))
  1136. goto fail;
  1137. break;
  1138. case GRUB_TERM_CTRL | 'e':
  1139. case GRUB_TERM_KEY_END:
  1140. if (! end_of_line (screen, 1))
  1141. goto fail;
  1142. break;
  1143. case GRUB_TERM_CTRL | 'i':
  1144. case '\t':
  1145. if (! complete (screen, prev_c == c, 1))
  1146. goto fail;
  1147. break;
  1148. case GRUB_TERM_CTRL | 'd':
  1149. case GRUB_TERM_KEY_DC:
  1150. if (! delete_char (screen, 1))
  1151. goto fail;
  1152. break;
  1153. case GRUB_TERM_CTRL | 'h':
  1154. case '\b':
  1155. if (! backward_delete_char (screen, 1))
  1156. goto fail;
  1157. break;
  1158. case GRUB_TERM_CTRL | 'k':
  1159. if (! kill_line (screen, prev_c == c, 1))
  1160. goto fail;
  1161. break;
  1162. case GRUB_TERM_CTRL | 'u':
  1163. /* FIXME: What behavior is good for this key? */
  1164. break;
  1165. case GRUB_TERM_CTRL | 'y':
  1166. if (! yank (screen, 1))
  1167. goto fail;
  1168. break;
  1169. case GRUB_TERM_CTRL | 'l':
  1170. /* FIXME: centering. */
  1171. goto refresh;
  1172. case GRUB_TERM_CTRL | 'o':
  1173. if (! open_line (screen, 1))
  1174. goto fail;
  1175. break;
  1176. case '\n':
  1177. case '\r':
  1178. if (! insert_string (screen, "\n", 1))
  1179. goto fail;
  1180. break;
  1181. case GRUB_TERM_ESC:
  1182. destroy_screen (screen);
  1183. return;
  1184. case GRUB_TERM_CTRL | 'c':
  1185. case GRUB_TERM_KEY_F2:
  1186. grub_cmdline_run (1, 0);
  1187. goto refresh;
  1188. case GRUB_TERM_CTRL | 'x':
  1189. case GRUB_TERM_KEY_F10:
  1190. run (screen);
  1191. goto refresh;
  1192. case GRUB_TERM_CTRL | 'r':
  1193. case GRUB_TERM_CTRL | 's':
  1194. case GRUB_TERM_CTRL | 't':
  1195. /* FIXME */
  1196. break;
  1197. default:
  1198. if (grub_isprint (c))
  1199. {
  1200. char buf[2];
  1201. buf[0] = c;
  1202. buf[1] = '\0';
  1203. if (! insert_string (screen, buf, 1))
  1204. goto fail;
  1205. }
  1206. break;
  1207. }
  1208. prev_c = c;
  1209. }
  1210. fail:
  1211. destroy_screen (screen);
  1212. grub_cls ();
  1213. grub_print_error ();
  1214. grub_errno = GRUB_ERR_NONE;
  1215. grub_xputs ("\n");
  1216. grub_printf_ (N_("Press any key to continue..."));
  1217. (void) grub_getkey ();
  1218. }