menu_entry.c 33 KB

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