term.c 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2002,2003,2005,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/term.h>
  19. #include <grub/misc.h>
  20. #include <grub/mm.h>
  21. #include <grub/file.h>
  22. #include <grub/dl.h>
  23. #include <grub/env.h>
  24. #include <grub/normal.h>
  25. #include <grub/charset.h>
  26. #include <grub/i18n.h>
  27. struct term_state
  28. {
  29. struct term_state *next;
  30. struct grub_unicode_glyph *backlog_glyphs;
  31. const grub_uint32_t *backlog_ucs4;
  32. int backlog_fixed_tab;
  33. grub_size_t backlog_len;
  34. int bidi_stack_depth;
  35. grub_uint8_t bidi_stack[GRUB_BIDI_MAX_EXPLICIT_LEVEL];
  36. int invalid_pushes;
  37. void *free;
  38. int num_lines;
  39. char *term_name;
  40. };
  41. static int
  42. print_ucs4_real (const grub_uint32_t * str,
  43. const grub_uint32_t * last_position,
  44. int margin_left, int margin_right,
  45. struct grub_term_output *term, int backlog,
  46. int dry_run, int fixed_tab, unsigned skip_lines,
  47. unsigned max_lines,
  48. grub_uint32_t contchar, int fill_right,
  49. struct grub_term_pos *pos);
  50. static struct term_state *term_states = NULL;
  51. /* If the more pager is active. */
  52. static int grub_more;
  53. static void
  54. putcode_real (grub_uint32_t code, struct grub_term_output *term, int fixed_tab);
  55. void
  56. grub_normal_reset_more (void)
  57. {
  58. static struct term_state *state;
  59. for (state = term_states; state; state = state->next)
  60. state->num_lines = 0;
  61. }
  62. static void
  63. print_more (void)
  64. {
  65. char key;
  66. struct grub_term_coordinate *pos;
  67. grub_term_output_t term;
  68. grub_uint32_t *unicode_str, *unicode_last_position;
  69. /* TRANSLATORS: This has to fit on one line. It's ok to include few
  70. words but don't write poems. */
  71. grub_utf8_to_ucs4_alloc (_("--MORE--"), &unicode_str,
  72. &unicode_last_position);
  73. if (!unicode_str)
  74. {
  75. grub_errno = GRUB_ERR_NONE;
  76. return;
  77. }
  78. pos = grub_term_save_pos ();
  79. grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
  80. FOR_ACTIVE_TERM_OUTPUTS(term)
  81. {
  82. grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term);
  83. }
  84. grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
  85. grub_free (unicode_str);
  86. key = grub_getkey ();
  87. /* Remove the message. */
  88. grub_term_restore_pos (pos);
  89. FOR_ACTIVE_TERM_OUTPUTS(term)
  90. grub_print_spaces (term, 8);
  91. grub_term_restore_pos (pos);
  92. grub_free (pos);
  93. /* Scroll one line or an entire page, depending on the key. */
  94. if (key == '\r' || key =='\n')
  95. {
  96. static struct term_state *state;
  97. for (state = term_states; state; state = state->next)
  98. state->num_lines--;
  99. }
  100. else
  101. grub_normal_reset_more ();
  102. }
  103. void
  104. grub_set_more (int onoff)
  105. {
  106. grub_more = !!onoff;
  107. grub_normal_reset_more ();
  108. }
  109. enum
  110. {
  111. GRUB_CP437_UPDOWNARROW = 0x12,
  112. GRUB_CP437_UPARROW = 0x18,
  113. GRUB_CP437_DOWNARROW = 0x19,
  114. GRUB_CP437_RIGHTARROW = 0x1a,
  115. GRUB_CP437_LEFTARROW = 0x1b,
  116. GRUB_CP437_VLINE = 0xb3,
  117. GRUB_CP437_CORNER_UR = 0xbf,
  118. GRUB_CP437_CORNER_LL = 0xc0,
  119. GRUB_CP437_HLINE = 0xc4,
  120. GRUB_CP437_CORNER_LR = 0xd9,
  121. GRUB_CP437_CORNER_UL = 0xda,
  122. };
  123. static grub_uint32_t
  124. map_code (grub_uint32_t in, struct grub_term_output *term)
  125. {
  126. if (in <= 0x7f)
  127. return in;
  128. switch (term->flags & GRUB_TERM_CODE_TYPE_MASK)
  129. {
  130. case GRUB_TERM_CODE_TYPE_CP437:
  131. switch (in)
  132. {
  133. case GRUB_UNICODE_LEFTARROW:
  134. return GRUB_CP437_LEFTARROW;
  135. case GRUB_UNICODE_UPARROW:
  136. return GRUB_CP437_UPARROW;
  137. case GRUB_UNICODE_UPDOWNARROW:
  138. return GRUB_CP437_UPDOWNARROW;
  139. case GRUB_UNICODE_RIGHTARROW:
  140. return GRUB_CP437_RIGHTARROW;
  141. case GRUB_UNICODE_DOWNARROW:
  142. return GRUB_CP437_DOWNARROW;
  143. case GRUB_UNICODE_HLINE:
  144. return GRUB_CP437_HLINE;
  145. case GRUB_UNICODE_VLINE:
  146. return GRUB_CP437_VLINE;
  147. case GRUB_UNICODE_CORNER_UL:
  148. return GRUB_CP437_CORNER_UL;
  149. case GRUB_UNICODE_CORNER_UR:
  150. return GRUB_CP437_CORNER_UR;
  151. case GRUB_UNICODE_CORNER_LL:
  152. return GRUB_CP437_CORNER_LL;
  153. case GRUB_UNICODE_CORNER_LR:
  154. return GRUB_CP437_CORNER_LR;
  155. }
  156. return '?';
  157. case GRUB_TERM_CODE_TYPE_ASCII:
  158. /* Better than nothing. */
  159. switch (in)
  160. {
  161. case GRUB_UNICODE_LEFTARROW:
  162. return '<';
  163. case GRUB_UNICODE_UPARROW:
  164. return '^';
  165. case GRUB_UNICODE_RIGHTARROW:
  166. return '>';
  167. case GRUB_UNICODE_DOWNARROW:
  168. return 'v';
  169. case GRUB_UNICODE_HLINE:
  170. return '-';
  171. case GRUB_UNICODE_VLINE:
  172. return '|';
  173. case GRUB_UNICODE_CORNER_UL:
  174. case GRUB_UNICODE_CORNER_UR:
  175. case GRUB_UNICODE_CORNER_LL:
  176. case GRUB_UNICODE_CORNER_LR:
  177. return '+';
  178. }
  179. return '?';
  180. }
  181. return in;
  182. }
  183. void
  184. grub_puts_terminal (const char *str, struct grub_term_output *term)
  185. {
  186. grub_uint32_t *unicode_str, *unicode_last_position;
  187. grub_error_push ();
  188. grub_utf8_to_ucs4_alloc (str, &unicode_str,
  189. &unicode_last_position);
  190. grub_error_pop ();
  191. if (!unicode_str)
  192. {
  193. for (; *str; str++)
  194. {
  195. struct grub_unicode_glyph c =
  196. {
  197. .variant = 0,
  198. .attributes = 0,
  199. .ncomb = 0,
  200. .estimated_width = 1,
  201. .base = *str
  202. };
  203. FOR_ACTIVE_TERM_OUTPUTS(term)
  204. {
  205. (term->putchar) (term, &c);
  206. }
  207. if (*str == '\n')
  208. {
  209. c.base = '\r';
  210. FOR_ACTIVE_TERM_OUTPUTS(term)
  211. {
  212. (term->putchar) (term, &c);
  213. }
  214. }
  215. }
  216. return;
  217. }
  218. print_ucs4_real (unicode_str, unicode_last_position, 0, 0, term,
  219. 0, 0, 0, 0, -1, 0, 0, 0);
  220. grub_free (unicode_str);
  221. }
  222. struct grub_term_coordinate *
  223. grub_term_save_pos (void)
  224. {
  225. struct grub_term_output *cur;
  226. unsigned cnt = 0;
  227. struct grub_term_coordinate *ret, *ptr;
  228. FOR_ACTIVE_TERM_OUTPUTS(cur)
  229. cnt++;
  230. ret = grub_calloc (cnt, sizeof (ret[0]));
  231. if (!ret)
  232. return NULL;
  233. ptr = ret;
  234. FOR_ACTIVE_TERM_OUTPUTS(cur)
  235. *ptr++ = grub_term_getxy (cur);
  236. return ret;
  237. }
  238. void
  239. grub_term_restore_pos (struct grub_term_coordinate *pos)
  240. {
  241. struct grub_term_output *cur;
  242. struct grub_term_coordinate *ptr = pos;
  243. if (!pos)
  244. return;
  245. FOR_ACTIVE_TERM_OUTPUTS(cur)
  246. {
  247. grub_term_gotoxy (cur, *ptr);
  248. ptr++;
  249. }
  250. }
  251. static void
  252. grub_terminal_autoload_free (void)
  253. {
  254. struct grub_term_autoload *cur, *next;
  255. unsigned i;
  256. for (i = 0; i < 2; i++)
  257. for (cur = i ? grub_term_input_autoload : grub_term_output_autoload;
  258. cur; cur = next)
  259. {
  260. next = cur->next;
  261. grub_free (cur->name);
  262. grub_free (cur->modname);
  263. grub_free (cur);
  264. }
  265. grub_term_input_autoload = NULL;
  266. grub_term_output_autoload = NULL;
  267. }
  268. /* Read the file terminal.lst for auto-loading. */
  269. void
  270. read_terminal_list (const char *prefix)
  271. {
  272. char *filename;
  273. grub_file_t file;
  274. char *buf = NULL;
  275. if (!prefix)
  276. {
  277. grub_errno = GRUB_ERR_NONE;
  278. return;
  279. }
  280. filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM
  281. "/terminal.lst", prefix);
  282. if (!filename)
  283. {
  284. grub_errno = GRUB_ERR_NONE;
  285. return;
  286. }
  287. file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE_LIST);
  288. grub_free (filename);
  289. if (!file)
  290. {
  291. grub_errno = GRUB_ERR_NONE;
  292. return;
  293. }
  294. /* Override previous terminal.lst. */
  295. grub_terminal_autoload_free ();
  296. for (;; grub_free (buf))
  297. {
  298. char *p, *name;
  299. struct grub_term_autoload *cur;
  300. struct grub_term_autoload **target = NULL;
  301. buf = grub_file_getline (file);
  302. if (! buf)
  303. break;
  304. p = buf;
  305. while (grub_isspace (p[0]))
  306. p++;
  307. switch (p[0])
  308. {
  309. case 'i':
  310. target = &grub_term_input_autoload;
  311. break;
  312. case 'o':
  313. target = &grub_term_output_autoload;
  314. break;
  315. }
  316. if (!target)
  317. continue;
  318. name = p + 1;
  319. p = grub_strchr (name, ':');
  320. if (! p)
  321. continue;
  322. *p = 0;
  323. p++;
  324. while (*p == ' ' || *p == '\t')
  325. p++;
  326. cur = grub_malloc (sizeof (*cur));
  327. if (!cur)
  328. {
  329. grub_errno = GRUB_ERR_NONE;
  330. continue;
  331. }
  332. cur->name = grub_strdup (name);
  333. if (! cur->name)
  334. {
  335. grub_errno = GRUB_ERR_NONE;
  336. grub_free (cur);
  337. continue;
  338. }
  339. cur->modname = grub_strdup (p);
  340. if (! cur->modname)
  341. {
  342. grub_errno = GRUB_ERR_NONE;
  343. grub_free (cur->name);
  344. grub_free (cur);
  345. continue;
  346. }
  347. cur->next = *target;
  348. *target = cur;
  349. }
  350. grub_file_close (file);
  351. grub_errno = GRUB_ERR_NONE;
  352. }
  353. static void
  354. putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term,
  355. int fixed_tab)
  356. {
  357. struct grub_unicode_glyph c2 =
  358. {
  359. .variant = 0,
  360. .attributes = 0,
  361. .ncomb = 0,
  362. .estimated_width = 1
  363. };
  364. if (c->base == '\t' && fixed_tab)
  365. {
  366. int n;
  367. n = GRUB_TERM_TAB_WIDTH;
  368. c2.base = ' ';
  369. while (n--)
  370. (term->putchar) (term, &c2);
  371. return;
  372. }
  373. if (c->base == '\t' && term->getxy)
  374. {
  375. int n;
  376. n = GRUB_TERM_TAB_WIDTH - ((term->getxy (term).x)
  377. % GRUB_TERM_TAB_WIDTH);
  378. c2.base = ' ';
  379. while (n--)
  380. (term->putchar) (term, &c2);
  381. return;
  382. }
  383. if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
  384. == GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
  385. || (term->flags & GRUB_TERM_CODE_TYPE_MASK)
  386. == GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
  387. {
  388. int i;
  389. c2.estimated_width = grub_term_getcharwidth (term, c);
  390. for (i = -1; i < (int) c->ncomb; i++)
  391. {
  392. grub_uint8_t u8[20], *ptr;
  393. grub_uint32_t code;
  394. if (i == -1)
  395. {
  396. code = c->base;
  397. if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
  398. == GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
  399. {
  400. if ((c->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR))
  401. code = grub_unicode_mirror_code (code);
  402. code = grub_unicode_shape_code (code, c->attributes);
  403. }
  404. }
  405. else
  406. code = grub_unicode_get_comb (c) [i].code;
  407. grub_ucs4_to_utf8 (&code, 1, u8, sizeof (u8));
  408. for (ptr = u8; *ptr; ptr++)
  409. {
  410. c2.base = *ptr;
  411. (term->putchar) (term, &c2);
  412. c2.estimated_width = 0;
  413. }
  414. }
  415. c2.estimated_width = 1;
  416. }
  417. else
  418. (term->putchar) (term, c);
  419. if (c->base == '\n')
  420. {
  421. c2.base = '\r';
  422. (term->putchar) (term, &c2);
  423. }
  424. }
  425. static void
  426. putcode_real (grub_uint32_t code, struct grub_term_output *term, int fixed_tab)
  427. {
  428. struct grub_unicode_glyph c =
  429. {
  430. .variant = 0,
  431. .attributes = 0,
  432. .ncomb = 0,
  433. .estimated_width = 1
  434. };
  435. c.base = map_code (code, term);
  436. putglyph (&c, term, fixed_tab);
  437. }
  438. /* Put a Unicode character. */
  439. void
  440. grub_putcode (grub_uint32_t code, struct grub_term_output *term)
  441. {
  442. /* Combining character by itself? */
  443. if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE)
  444. return;
  445. putcode_real (code, term, 0);
  446. }
  447. static grub_ssize_t
  448. get_maxwidth (struct grub_term_output *term,
  449. int margin_left, int margin_right)
  450. {
  451. struct grub_unicode_glyph space_glyph = {
  452. .base = ' ',
  453. .variant = 0,
  454. .attributes = 0,
  455. .ncomb = 0,
  456. };
  457. return (grub_term_width (term)
  458. - grub_term_getcharwidth (term, &space_glyph)
  459. * (margin_left + margin_right) - 1);
  460. }
  461. static grub_ssize_t
  462. get_startwidth (struct grub_term_output *term,
  463. int margin_left)
  464. {
  465. return (term->getxy (term).x) - margin_left;
  466. }
  467. static void
  468. fill_margin (struct grub_term_output *term, int r)
  469. {
  470. int sp = (term->getwh (term).x)
  471. - (term->getxy (term).x) - r;
  472. if (sp > 0)
  473. grub_print_spaces (term, sp);
  474. }
  475. static int
  476. print_ucs4_terminal (const grub_uint32_t * str,
  477. const grub_uint32_t * last_position,
  478. int margin_left, int margin_right,
  479. struct grub_term_output *term,
  480. struct term_state *state,
  481. int dry_run, int fixed_tab, unsigned skip_lines,
  482. unsigned max_lines,
  483. grub_uint32_t contchar,
  484. int primitive_wrap, int fill_right, struct grub_term_pos *pos)
  485. {
  486. const grub_uint32_t *ptr;
  487. grub_ssize_t startwidth = dry_run ? 0 : get_startwidth (term, margin_left);
  488. grub_ssize_t line_width = startwidth;
  489. grub_ssize_t lastspacewidth = 0;
  490. grub_ssize_t max_width = get_maxwidth (term, margin_left, margin_right);
  491. const grub_uint32_t *line_start = str, *last_space = str - 1;
  492. int lines = 0;
  493. int i;
  494. struct term_state local_state;
  495. if (!state)
  496. {
  497. grub_memset (&local_state, 0, sizeof (local_state));
  498. state = &local_state;
  499. }
  500. for (i = 0; i < state->bidi_stack_depth; i++)
  501. putcode_real (state->bidi_stack[i] | (GRUB_UNICODE_LRE & ~0xff),
  502. term, fixed_tab);
  503. for (ptr = str; ptr < last_position; ptr++)
  504. {
  505. grub_ssize_t last_width = 0;
  506. switch (*ptr)
  507. {
  508. case GRUB_UNICODE_LRE:
  509. case GRUB_UNICODE_RLE:
  510. case GRUB_UNICODE_LRO:
  511. case GRUB_UNICODE_RLO:
  512. if (state->bidi_stack_depth >= (int) ARRAY_SIZE (state->bidi_stack))
  513. state->invalid_pushes++;
  514. else
  515. state->bidi_stack[state->bidi_stack_depth++] = *ptr;
  516. break;
  517. case GRUB_UNICODE_PDF:
  518. if (state->invalid_pushes)
  519. state->invalid_pushes--;
  520. else if (state->bidi_stack_depth)
  521. state->bidi_stack_depth--;
  522. break;
  523. }
  524. if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE)
  525. {
  526. struct grub_unicode_glyph c = {
  527. .variant = 0,
  528. .attributes = 0,
  529. .ncomb = 0,
  530. };
  531. c.base = *ptr;
  532. if (pos)
  533. {
  534. pos[ptr - str].x = line_width;
  535. pos[ptr - str].y = lines;
  536. pos[ptr - str].valid = 1;
  537. }
  538. line_width += last_width = grub_term_getcharwidth (term, &c);
  539. }
  540. if (*ptr == ' ' && !primitive_wrap)
  541. {
  542. lastspacewidth = line_width;
  543. last_space = ptr;
  544. }
  545. if (line_width > max_width || *ptr == '\n')
  546. {
  547. const grub_uint32_t *ptr2;
  548. int wasn = (*ptr == '\n');
  549. if (wasn)
  550. {
  551. state->bidi_stack_depth = 0;
  552. state->invalid_pushes = 0;
  553. }
  554. if (line_width > max_width && last_space > line_start)
  555. ptr = last_space;
  556. else if (line_width > max_width
  557. && line_start == str && line_width - lastspacewidth < max_width - 5)
  558. {
  559. ptr = str;
  560. lastspacewidth = startwidth;
  561. }
  562. else
  563. lastspacewidth = line_width - last_width;
  564. lines++;
  565. if (!skip_lines && !dry_run)
  566. {
  567. for (ptr2 = line_start; ptr2 < ptr; ptr2++)
  568. {
  569. /* Skip combining characters on non-UTF8 terminals. */
  570. if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
  571. != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
  572. && grub_unicode_get_comb_type (*ptr2)
  573. != GRUB_UNICODE_COMB_NONE)
  574. continue;
  575. putcode_real (*ptr2, term, fixed_tab);
  576. }
  577. if (!wasn && contchar)
  578. putcode_real (contchar, term, fixed_tab);
  579. if (fill_right)
  580. fill_margin (term, margin_right);
  581. if (!contchar || max_lines != 1)
  582. grub_putcode ('\n', term);
  583. if (state != &local_state && ++state->num_lines
  584. >= (grub_ssize_t) grub_term_height (term) - 2)
  585. {
  586. state->backlog_ucs4 = (ptr == last_space || *ptr == '\n')
  587. ? ptr + 1 : ptr;
  588. state->backlog_len = last_position - state->backlog_ucs4;
  589. state->backlog_fixed_tab = fixed_tab;
  590. return 1;
  591. }
  592. }
  593. line_width -= lastspacewidth;
  594. if (ptr == last_space || *ptr == '\n')
  595. ptr++;
  596. else if (pos)
  597. {
  598. pos[ptr - str].x = line_width - last_width;
  599. pos[ptr - str].y = lines;
  600. pos[ptr - str].valid = 1;
  601. }
  602. line_start = ptr;
  603. if (skip_lines)
  604. skip_lines--;
  605. else if (max_lines != (unsigned) -1)
  606. {
  607. max_lines--;
  608. if (!max_lines)
  609. break;
  610. }
  611. if (!skip_lines && !dry_run)
  612. {
  613. if (!contchar)
  614. grub_print_spaces (term, margin_left);
  615. else
  616. grub_term_gotoxy (term, (struct grub_term_coordinate)
  617. { margin_left, grub_term_getxy (term).y });
  618. for (i = 0; i < state->bidi_stack_depth; i++)
  619. putcode_real (state->bidi_stack[i] | (GRUB_UNICODE_LRE & ~0xff),
  620. term, fixed_tab);
  621. }
  622. }
  623. }
  624. if (pos)
  625. {
  626. pos[ptr - str].x = line_width;
  627. pos[ptr - str].y = lines;
  628. pos[ptr - str].valid = 1;
  629. }
  630. if (line_start < last_position)
  631. lines++;
  632. if (!dry_run && !skip_lines && max_lines)
  633. {
  634. const grub_uint32_t *ptr2;
  635. for (ptr2 = line_start; ptr2 < last_position; ptr2++)
  636. {
  637. /* Skip combining characters on non-UTF8 terminals. */
  638. if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
  639. != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
  640. && grub_unicode_get_comb_type (*ptr2)
  641. != GRUB_UNICODE_COMB_NONE)
  642. continue;
  643. putcode_real (*ptr2, term, fixed_tab);
  644. }
  645. if (fill_right)
  646. fill_margin (term, margin_right);
  647. }
  648. return dry_run ? lines : 0;
  649. }
  650. static struct term_state *
  651. find_term_state (struct grub_term_output *term)
  652. {
  653. struct term_state *state;
  654. for (state = term_states; state; state = state->next)
  655. if (grub_strcmp (state->term_name, term->name) == 0)
  656. return state;
  657. state = grub_zalloc (sizeof (*state));
  658. if (!state)
  659. {
  660. grub_errno = GRUB_ERR_NONE;
  661. return NULL;
  662. }
  663. state->term_name = grub_strdup (term->name);
  664. state->next = term_states;
  665. term_states = state;
  666. return state;
  667. }
  668. static int
  669. put_glyphs_terminal (struct grub_unicode_glyph *visual,
  670. grub_ssize_t visual_len,
  671. int margin_left, int margin_right,
  672. struct grub_term_output *term,
  673. struct term_state *state, int fixed_tab,
  674. grub_uint32_t contchar,
  675. int fill_right)
  676. {
  677. struct grub_unicode_glyph *visual_ptr;
  678. int since_last_nl = 1;
  679. for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++)
  680. {
  681. if (visual_ptr->base == '\n' && fill_right)
  682. fill_margin (term, margin_right);
  683. putglyph (visual_ptr, term, fixed_tab);
  684. since_last_nl++;
  685. if (visual_ptr->base == '\n')
  686. {
  687. since_last_nl = 0;
  688. if (state && ++state->num_lines
  689. >= (grub_ssize_t) grub_term_height (term) - 2)
  690. {
  691. state->backlog_glyphs = visual_ptr + 1;
  692. state->backlog_len = visual_len - (visual_ptr - visual) - 1;
  693. state->backlog_fixed_tab = fixed_tab;
  694. return 1;
  695. }
  696. if (!contchar)
  697. grub_print_spaces (term, margin_left);
  698. else
  699. grub_term_gotoxy (term,
  700. (struct grub_term_coordinate)
  701. { margin_left, grub_term_getxy (term).y });
  702. }
  703. grub_unicode_destroy_glyph (visual_ptr);
  704. }
  705. if (fill_right && since_last_nl)
  706. fill_margin (term, margin_right);
  707. return 0;
  708. }
  709. static int
  710. print_backlog (struct grub_term_output *term,
  711. int margin_left, int margin_right)
  712. {
  713. struct term_state *state = find_term_state (term);
  714. if (!state)
  715. return 0;
  716. if (state->backlog_ucs4)
  717. {
  718. int ret;
  719. ret = print_ucs4_terminal (state->backlog_ucs4,
  720. state->backlog_ucs4 + state->backlog_len,
  721. margin_left, margin_right, term, state, 0,
  722. state->backlog_fixed_tab, 0, -1, 0, 0,
  723. 0, 0);
  724. if (!ret)
  725. {
  726. grub_free (state->free);
  727. state->free = NULL;
  728. state->backlog_len = 0;
  729. state->backlog_ucs4 = 0;
  730. }
  731. return ret;
  732. }
  733. if (state->backlog_glyphs)
  734. {
  735. int ret;
  736. ret = put_glyphs_terminal (state->backlog_glyphs,
  737. state->backlog_len,
  738. margin_left, margin_right, term, state,
  739. state->backlog_fixed_tab, 0, 0);
  740. if (!ret)
  741. {
  742. grub_free (state->free);
  743. state->free = NULL;
  744. state->backlog_len = 0;
  745. state->backlog_glyphs = 0;
  746. }
  747. return ret;
  748. }
  749. return 0;
  750. }
  751. static grub_size_t
  752. getcharwidth (const struct grub_unicode_glyph *c, void *term)
  753. {
  754. return grub_term_getcharwidth (term, c);
  755. }
  756. static int
  757. print_ucs4_real (const grub_uint32_t * str,
  758. const grub_uint32_t * last_position,
  759. int margin_left, int margin_right,
  760. struct grub_term_output *term, int backlog,
  761. int dry_run, int fixed_tab, unsigned skip_lines,
  762. unsigned max_lines,
  763. grub_uint32_t contchar, int fill_right,
  764. struct grub_term_pos *pos)
  765. {
  766. struct term_state *state = NULL;
  767. if (!dry_run)
  768. {
  769. struct grub_term_coordinate xy;
  770. if (backlog)
  771. state = find_term_state (term);
  772. xy = term->getxy (term);
  773. if (xy.x < margin_left)
  774. {
  775. if (!contchar)
  776. grub_print_spaces (term, margin_left - xy.x);
  777. else
  778. grub_term_gotoxy (term, (struct grub_term_coordinate) {margin_left,
  779. xy.y});
  780. }
  781. }
  782. if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
  783. == GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS
  784. || (term->flags & GRUB_TERM_CODE_TYPE_MASK)
  785. == GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
  786. {
  787. grub_ssize_t visual_len;
  788. struct grub_unicode_glyph *visual;
  789. grub_ssize_t visual_len_show;
  790. struct grub_unicode_glyph *visual_show;
  791. int ret;
  792. struct grub_unicode_glyph *vptr;
  793. visual_len = grub_bidi_logical_to_visual (str, last_position - str,
  794. &visual, getcharwidth, term,
  795. get_maxwidth (term,
  796. margin_left,
  797. margin_right),
  798. dry_run ? 0 : get_startwidth (term,
  799. margin_left),
  800. contchar, pos, !!contchar);
  801. if (visual_len < 0)
  802. {
  803. grub_print_error ();
  804. return 0;
  805. }
  806. visual_show = visual;
  807. for (; skip_lines && visual_show < visual + visual_len; visual_show++)
  808. if (visual_show->base == '\n')
  809. skip_lines--;
  810. if (max_lines != (unsigned) -1)
  811. {
  812. for (vptr = visual_show;
  813. max_lines && vptr < visual + visual_len; vptr++)
  814. if (vptr->base == '\n')
  815. max_lines--;
  816. visual_len_show = vptr - visual_show;
  817. }
  818. else
  819. visual_len_show = visual + visual_len - visual_show;
  820. if (dry_run)
  821. {
  822. ret = 0;
  823. for (vptr = visual_show; vptr < visual_show + visual_len_show; vptr++)
  824. if (vptr->base == '\n')
  825. ret++;
  826. if (visual_len_show && visual[visual_len_show - 1].base != '\n')
  827. ret++;
  828. for (vptr = visual; vptr < visual + visual_len; vptr++)
  829. grub_unicode_destroy_glyph (vptr);
  830. grub_free (visual);
  831. }
  832. else
  833. {
  834. ret = put_glyphs_terminal (visual_show, visual_len_show, margin_left,
  835. margin_right,
  836. term, state, fixed_tab, contchar, fill_right);
  837. if (!ret)
  838. grub_free (visual);
  839. else
  840. state->free = visual;
  841. }
  842. return ret;
  843. }
  844. return print_ucs4_terminal (str, last_position, margin_left, margin_right,
  845. term, state, dry_run, fixed_tab, skip_lines,
  846. max_lines, contchar, !!contchar, fill_right, pos);
  847. }
  848. void
  849. grub_print_ucs4_menu (const grub_uint32_t * str,
  850. const grub_uint32_t * last_position,
  851. int margin_left, int margin_right,
  852. struct grub_term_output *term,
  853. int skip_lines, int max_lines,
  854. grub_uint32_t contchar,
  855. struct grub_term_pos *pos)
  856. {
  857. print_ucs4_real (str, last_position, margin_left, margin_right,
  858. term, 0, 0, 1, skip_lines, max_lines,
  859. contchar, 1, pos);
  860. }
  861. void
  862. grub_print_ucs4 (const grub_uint32_t * str,
  863. const grub_uint32_t * last_position,
  864. int margin_left, int margin_right,
  865. struct grub_term_output *term)
  866. {
  867. print_ucs4_real (str, last_position, margin_left, margin_right,
  868. term, 0, 0, 1, 0, -1, 0, 0, 0);
  869. }
  870. int
  871. grub_ucs4_count_lines (const grub_uint32_t * str,
  872. const grub_uint32_t * last_position,
  873. int margin_left, int margin_right,
  874. struct grub_term_output *term)
  875. {
  876. return print_ucs4_real (str, last_position, margin_left, margin_right,
  877. term, 0, 1, 1, 0, -1, 0, 0, 0);
  878. }
  879. void
  880. grub_xnputs (const char *str, grub_size_t msg_len)
  881. {
  882. grub_uint32_t *unicode_str = NULL, *unicode_last_position;
  883. int backlog = 0;
  884. grub_term_output_t term;
  885. grub_error_push ();
  886. unicode_str = grub_calloc (msg_len, sizeof (grub_uint32_t));
  887. grub_error_pop ();
  888. if (!unicode_str)
  889. {
  890. for (; msg_len--; str++, msg_len++)
  891. {
  892. struct grub_unicode_glyph c =
  893. {
  894. .variant = 0,
  895. .attributes = 0,
  896. .ncomb = 0,
  897. .estimated_width = 1,
  898. .base = *str
  899. };
  900. FOR_ACTIVE_TERM_OUTPUTS(term)
  901. {
  902. (term->putchar) (term, &c);
  903. }
  904. if (*str == '\n')
  905. {
  906. c.base = '\r';
  907. FOR_ACTIVE_TERM_OUTPUTS(term)
  908. {
  909. (term->putchar) (term, &c);
  910. }
  911. }
  912. }
  913. return;
  914. }
  915. msg_len = grub_utf8_to_ucs4 (unicode_str, msg_len,
  916. (grub_uint8_t *) str, -1, 0);
  917. unicode_last_position = unicode_str + msg_len;
  918. FOR_ACTIVE_TERM_OUTPUTS(term)
  919. {
  920. int cur;
  921. cur = print_ucs4_real (unicode_str, unicode_last_position, 0, 0,
  922. term, grub_more, 0, 0, 0, -1, 0, 0, 0);
  923. if (cur)
  924. backlog = 1;
  925. }
  926. while (backlog)
  927. {
  928. print_more ();
  929. backlog = 0;
  930. FOR_ACTIVE_TERM_OUTPUTS(term)
  931. {
  932. int cur;
  933. cur = print_backlog (term, 0, 0);
  934. if (cur)
  935. backlog = 1;
  936. }
  937. }
  938. grub_free (unicode_str);
  939. }
  940. void
  941. grub_xputs_normal (const char *str)
  942. {
  943. grub_xnputs (str, grub_strlen (str));
  944. }
  945. void
  946. grub_cls (void)
  947. {
  948. struct grub_term_output *term;
  949. FOR_ACTIVE_TERM_OUTPUTS(term)
  950. {
  951. if ((term->flags & GRUB_TERM_DUMB) || (grub_env_get ("debug")))
  952. {
  953. grub_putcode ('\n', term);
  954. grub_term_refresh (term);
  955. }
  956. else
  957. (term->cls) (term);
  958. }
  959. }