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