diff options
Diffstat (limited to 'grub-core/normal/term.c')
-rw-r--r-- | grub-core/normal/term.c | 1098 |
1 files changed, 1098 insertions, 0 deletions
diff --git a/grub-core/normal/term.c b/grub-core/normal/term.c new file mode 100644 index 0000000..cc8c173 --- /dev/null +++ b/grub-core/normal/term.c @@ -0,0 +1,1098 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/term.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/file.h> +#include <grub/dl.h> +#include <grub/env.h> +#include <grub/normal.h> +#include <grub/charset.h> +#include <grub/i18n.h> + +struct term_state +{ + struct term_state *next; + struct grub_unicode_glyph *backlog_glyphs; + const grub_uint32_t *backlog_ucs4; + int backlog_fixed_tab; + grub_size_t backlog_len; + + int bidi_stack_depth; + grub_uint8_t bidi_stack[GRUB_BIDI_MAX_EXPLICIT_LEVEL]; + int invalid_pushes; + + void *free; + int num_lines; + char *term_name; +}; + +static int +print_ucs4_real (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term, int backlog, + int dry_run, int fixed_tab, unsigned skip_lines, + unsigned max_lines, + grub_uint32_t contchar, int fill_right, + struct grub_term_pos *pos); + +static struct term_state *term_states = NULL; + +/* If the more pager is active. */ +static int grub_more; + +static void +putcode_real (grub_uint32_t code, struct grub_term_output *term, int fixed_tab); + +void +grub_normal_reset_more (void) +{ + static struct term_state *state; + for (state = term_states; state; state = state->next) + state->num_lines = 0; +} + +static void +print_more (void) +{ + char key; + struct grub_term_coordinate *pos; + grub_term_output_t term; + grub_uint32_t *unicode_str, *unicode_last_position; + + /* TRANSLATORS: This has to fit on one line. It's ok to include few + words but don't write poems. */ + grub_utf8_to_ucs4_alloc (_("--MORE--"), &unicode_str, + &unicode_last_position); + + if (!unicode_str) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + pos = grub_term_save_pos (); + + grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); + + FOR_ACTIVE_TERM_OUTPUTS(term) + { + grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term); + } + grub_setcolorstate (GRUB_TERM_COLOR_NORMAL); + + grub_free (unicode_str); + + key = grub_getkey (); + + /* Remove the message. */ + grub_term_restore_pos (pos); + FOR_ACTIVE_TERM_OUTPUTS(term) + grub_print_spaces (term, 8); + grub_term_restore_pos (pos); + grub_free (pos); + + /* Scroll one line or an entire page, depending on the key. */ + + if (key == '\r' || key =='\n') + { + static struct term_state *state; + for (state = term_states; state; state = state->next) + state->num_lines--; + } + else + grub_normal_reset_more (); +} + +void +grub_set_more (int onoff) +{ + grub_more = !!onoff; + grub_normal_reset_more (); +} + +enum + { + GRUB_CP437_UPDOWNARROW = 0x12, + GRUB_CP437_UPARROW = 0x18, + GRUB_CP437_DOWNARROW = 0x19, + GRUB_CP437_RIGHTARROW = 0x1a, + GRUB_CP437_LEFTARROW = 0x1b, + GRUB_CP437_VLINE = 0xb3, + GRUB_CP437_CORNER_UR = 0xbf, + GRUB_CP437_CORNER_LL = 0xc0, + GRUB_CP437_HLINE = 0xc4, + GRUB_CP437_CORNER_LR = 0xd9, + GRUB_CP437_CORNER_UL = 0xda, + }; + +static grub_uint32_t +map_code (grub_uint32_t in, struct grub_term_output *term) +{ + if (in <= 0x7f) + return in; + + switch (term->flags & GRUB_TERM_CODE_TYPE_MASK) + { + case GRUB_TERM_CODE_TYPE_CP437: + switch (in) + { + case GRUB_UNICODE_LEFTARROW: + return GRUB_CP437_LEFTARROW; + case GRUB_UNICODE_UPARROW: + return GRUB_CP437_UPARROW; + case GRUB_UNICODE_UPDOWNARROW: + return GRUB_CP437_UPDOWNARROW; + case GRUB_UNICODE_RIGHTARROW: + return GRUB_CP437_RIGHTARROW; + case GRUB_UNICODE_DOWNARROW: + return GRUB_CP437_DOWNARROW; + case GRUB_UNICODE_HLINE: + return GRUB_CP437_HLINE; + case GRUB_UNICODE_VLINE: + return GRUB_CP437_VLINE; + case GRUB_UNICODE_CORNER_UL: + return GRUB_CP437_CORNER_UL; + case GRUB_UNICODE_CORNER_UR: + return GRUB_CP437_CORNER_UR; + case GRUB_UNICODE_CORNER_LL: + return GRUB_CP437_CORNER_LL; + case GRUB_UNICODE_CORNER_LR: + return GRUB_CP437_CORNER_LR; + } + return '?'; + case GRUB_TERM_CODE_TYPE_ASCII: + /* Better than nothing. */ + switch (in) + { + case GRUB_UNICODE_LEFTARROW: + return '<'; + + case GRUB_UNICODE_UPARROW: + return '^'; + + case GRUB_UNICODE_RIGHTARROW: + return '>'; + + case GRUB_UNICODE_DOWNARROW: + return 'v'; + + case GRUB_UNICODE_HLINE: + return '-'; + + case GRUB_UNICODE_VLINE: + return '|'; + + case GRUB_UNICODE_CORNER_UL: + case GRUB_UNICODE_CORNER_UR: + case GRUB_UNICODE_CORNER_LL: + case GRUB_UNICODE_CORNER_LR: + return '+'; + + } + return '?'; + } + return in; +} + +void +grub_puts_terminal (const char *str, struct grub_term_output *term) +{ + grub_uint32_t *unicode_str, *unicode_last_position; + grub_error_push (); + grub_utf8_to_ucs4_alloc (str, &unicode_str, + &unicode_last_position); + grub_error_pop (); + if (!unicode_str) + { + for (; *str; str++) + { + struct grub_unicode_glyph c = + { + .variant = 0, + .attributes = 0, + .ncomb = 0, + .estimated_width = 1, + .base = *str + }; + + FOR_ACTIVE_TERM_OUTPUTS(term) + { + (term->putchar) (term, &c); + } + if (*str == '\n') + { + c.base = '\r'; + FOR_ACTIVE_TERM_OUTPUTS(term) + { + (term->putchar) (term, &c); + } + } + } + return; + } + + print_ucs4_real (unicode_str, unicode_last_position, 0, 0, term, + 0, 0, 0, 0, -1, 0, 0, 0); + grub_free (unicode_str); +} + +struct grub_term_coordinate * +grub_term_save_pos (void) +{ + struct grub_term_output *cur; + unsigned cnt = 0; + struct grub_term_coordinate *ret, *ptr; + + FOR_ACTIVE_TERM_OUTPUTS(cur) + cnt++; + + ret = grub_calloc (cnt, sizeof (ret[0])); + if (!ret) + return NULL; + + ptr = ret; + FOR_ACTIVE_TERM_OUTPUTS(cur) + *ptr++ = grub_term_getxy (cur); + + return ret; +} + +void +grub_term_restore_pos (struct grub_term_coordinate *pos) +{ + struct grub_term_output *cur; + struct grub_term_coordinate *ptr = pos; + + if (!pos) + return; + + FOR_ACTIVE_TERM_OUTPUTS(cur) + { + grub_term_gotoxy (cur, *ptr); + ptr++; + } +} + +static void +grub_terminal_autoload_free (void) +{ + struct grub_term_autoload *cur, *next; + unsigned i; + for (i = 0; i < 2; i++) + for (cur = i ? grub_term_input_autoload : grub_term_output_autoload; + cur; cur = next) + { + next = cur->next; + grub_free (cur->name); + grub_free (cur->modname); + grub_free (cur); + } + grub_term_input_autoload = NULL; + grub_term_output_autoload = NULL; +} + +/* Read the file terminal.lst for auto-loading. */ +void +read_terminal_list (const char *prefix) +{ + char *filename; + grub_file_t file; + char *buf = NULL; + + if (!prefix) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM + "/terminal.lst", prefix); + if (!filename) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE_LIST); + grub_free (filename); + if (!file) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + /* Override previous terminal.lst. */ + grub_terminal_autoload_free (); + + for (;; grub_free (buf)) + { + char *p, *name; + struct grub_term_autoload *cur; + struct grub_term_autoload **target = NULL; + + buf = grub_file_getline (file); + + if (! buf) + break; + + p = buf; + while (grub_isspace (p[0])) + p++; + + switch (p[0]) + { + case 'i': + target = &grub_term_input_autoload; + break; + + case 'o': + target = &grub_term_output_autoload; + break; + } + if (!target) + continue; + + name = p + 1; + + p = grub_strchr (name, ':'); + if (! p) + continue; + *p = 0; + + p++; + while (*p == ' ' || *p == '\t') + p++; + + cur = grub_malloc (sizeof (*cur)); + if (!cur) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + + cur->name = grub_strdup (name); + if (! cur->name) + { + grub_errno = GRUB_ERR_NONE; + grub_free (cur); + continue; + } + + cur->modname = grub_strdup (p); + if (! cur->modname) + { + grub_errno = GRUB_ERR_NONE; + grub_free (cur->name); + grub_free (cur); + continue; + } + cur->next = *target; + *target = cur; + } + + grub_file_close (file); + + grub_errno = GRUB_ERR_NONE; +} + +static void +putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term, + int fixed_tab) +{ + struct grub_unicode_glyph c2 = + { + .variant = 0, + .attributes = 0, + .ncomb = 0, + .estimated_width = 1 + }; + + if (c->base == '\t' && fixed_tab) + { + int n; + + n = GRUB_TERM_TAB_WIDTH; + c2.base = ' '; + while (n--) + (term->putchar) (term, &c2); + + return; + } + + if (c->base == '\t' && term->getxy) + { + int n; + + n = GRUB_TERM_TAB_WIDTH - ((term->getxy (term).x) + % GRUB_TERM_TAB_WIDTH); + c2.base = ' '; + while (n--) + (term->putchar) (term, &c2); + + return; + } + + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_LOGICAL + || (term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) + { + int i; + c2.estimated_width = grub_term_getcharwidth (term, c); + for (i = -1; i < (int) c->ncomb; i++) + { + grub_uint8_t u8[20], *ptr; + grub_uint32_t code; + + if (i == -1) + { + code = c->base; + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) + { + if ((c->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)) + code = grub_unicode_mirror_code (code); + code = grub_unicode_shape_code (code, c->attributes); + } + } + else + code = grub_unicode_get_comb (c) [i].code; + + grub_ucs4_to_utf8 (&code, 1, u8, sizeof (u8)); + + for (ptr = u8; *ptr; ptr++) + { + c2.base = *ptr; + (term->putchar) (term, &c2); + c2.estimated_width = 0; + } + } + c2.estimated_width = 1; + } + else + (term->putchar) (term, c); + + if (c->base == '\n') + { + c2.base = '\r'; + (term->putchar) (term, &c2); + } +} + +static void +putcode_real (grub_uint32_t code, struct grub_term_output *term, int fixed_tab) +{ + struct grub_unicode_glyph c = + { + .variant = 0, + .attributes = 0, + .ncomb = 0, + .estimated_width = 1 + }; + + c.base = map_code (code, term); + putglyph (&c, term, fixed_tab); +} + +/* Put a Unicode character. */ +void +grub_putcode (grub_uint32_t code, struct grub_term_output *term) +{ + /* Combining character by itself? */ + if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE) + return; + + putcode_real (code, term, 0); +} + +static grub_ssize_t +get_maxwidth (struct grub_term_output *term, + int margin_left, int margin_right) +{ + struct grub_unicode_glyph space_glyph = { + .base = ' ', + .variant = 0, + .attributes = 0, + .ncomb = 0, + }; + return (grub_term_width (term) + - grub_term_getcharwidth (term, &space_glyph) + * (margin_left + margin_right) - 1); +} + +static grub_ssize_t +get_startwidth (struct grub_term_output *term, + int margin_left) +{ + return (term->getxy (term).x) - margin_left; +} + +static void +fill_margin (struct grub_term_output *term, int r) +{ + int sp = (term->getwh (term).x) + - (term->getxy (term).x) - r; + if (sp > 0) + grub_print_spaces (term, sp); +} + +static int +print_ucs4_terminal (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term, + struct term_state *state, + int dry_run, int fixed_tab, unsigned skip_lines, + unsigned max_lines, + grub_uint32_t contchar, + int primitive_wrap, int fill_right, struct grub_term_pos *pos) +{ + const grub_uint32_t *ptr; + grub_ssize_t startwidth = dry_run ? 0 : get_startwidth (term, margin_left); + grub_ssize_t line_width = startwidth; + grub_ssize_t lastspacewidth = 0; + grub_ssize_t max_width = get_maxwidth (term, margin_left, margin_right); + const grub_uint32_t *line_start = str, *last_space = str - 1; + int lines = 0; + int i; + struct term_state local_state; + + if (!state) + { + grub_memset (&local_state, 0, sizeof (local_state)); + state = &local_state; + } + + for (i = 0; i < state->bidi_stack_depth; i++) + putcode_real (state->bidi_stack[i] | (GRUB_UNICODE_LRE & ~0xff), + term, fixed_tab); + + for (ptr = str; ptr < last_position; ptr++) + { + grub_ssize_t last_width = 0; + + switch (*ptr) + { + case GRUB_UNICODE_LRE: + case GRUB_UNICODE_RLE: + case GRUB_UNICODE_LRO: + case GRUB_UNICODE_RLO: + if (state->bidi_stack_depth >= (int) ARRAY_SIZE (state->bidi_stack)) + state->invalid_pushes++; + else + state->bidi_stack[state->bidi_stack_depth++] = *ptr; + break; + case GRUB_UNICODE_PDF: + if (state->invalid_pushes) + state->invalid_pushes--; + else if (state->bidi_stack_depth) + state->bidi_stack_depth--; + break; + } + if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE) + { + struct grub_unicode_glyph c = { + .variant = 0, + .attributes = 0, + .ncomb = 0, + }; + c.base = *ptr; + if (pos) + { + pos[ptr - str].x = line_width; + pos[ptr - str].y = lines; + pos[ptr - str].valid = 1; + } + line_width += last_width = grub_term_getcharwidth (term, &c); + } + + if (*ptr == ' ' && !primitive_wrap) + { + lastspacewidth = line_width; + last_space = ptr; + } + + if (line_width > max_width || *ptr == '\n') + { + const grub_uint32_t *ptr2; + int wasn = (*ptr == '\n'); + + if (wasn) + { + state->bidi_stack_depth = 0; + state->invalid_pushes = 0; + } + + if (line_width > max_width && last_space > line_start) + ptr = last_space; + else if (line_width > max_width + && line_start == str && line_width - lastspacewidth < max_width - 5) + { + ptr = str; + lastspacewidth = startwidth; + } + else + lastspacewidth = line_width - last_width; + + lines++; + + if (!skip_lines && !dry_run) + { + for (ptr2 = line_start; ptr2 < ptr; ptr2++) + { + /* Skip combining characters on non-UTF8 terminals. */ + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL + && grub_unicode_get_comb_type (*ptr2) + != GRUB_UNICODE_COMB_NONE) + continue; + putcode_real (*ptr2, term, fixed_tab); + } + + if (!wasn && contchar) + putcode_real (contchar, term, fixed_tab); + if (fill_right) + fill_margin (term, margin_right); + + if (!contchar || max_lines != 1) + grub_putcode ('\n', term); + if (state != &local_state && ++state->num_lines + >= (grub_ssize_t) grub_term_height (term) - 2) + { + state->backlog_ucs4 = (ptr == last_space || *ptr == '\n') + ? ptr + 1 : ptr; + state->backlog_len = last_position - state->backlog_ucs4; + state->backlog_fixed_tab = fixed_tab; + return 1; + } + } + + line_width -= lastspacewidth; + if (ptr == last_space || *ptr == '\n') + ptr++; + else if (pos) + { + pos[ptr - str].x = line_width - last_width; + pos[ptr - str].y = lines; + pos[ptr - str].valid = 1; + } + + line_start = ptr; + + if (skip_lines) + skip_lines--; + else if (max_lines != (unsigned) -1) + { + max_lines--; + if (!max_lines) + break; + } + if (!skip_lines && !dry_run) + { + if (!contchar) + grub_print_spaces (term, margin_left); + else + grub_term_gotoxy (term, (struct grub_term_coordinate) + { margin_left, grub_term_getxy (term).y }); + for (i = 0; i < state->bidi_stack_depth; i++) + putcode_real (state->bidi_stack[i] | (GRUB_UNICODE_LRE & ~0xff), + term, fixed_tab); + } + } + } + + if (pos) + { + pos[ptr - str].x = line_width; + pos[ptr - str].y = lines; + pos[ptr - str].valid = 1; + } + + if (line_start < last_position) + lines++; + if (!dry_run && !skip_lines && max_lines) + { + const grub_uint32_t *ptr2; + + for (ptr2 = line_start; ptr2 < last_position; ptr2++) + { + /* Skip combining characters on non-UTF8 terminals. */ + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL + && grub_unicode_get_comb_type (*ptr2) + != GRUB_UNICODE_COMB_NONE) + continue; + putcode_real (*ptr2, term, fixed_tab); + } + + if (fill_right) + fill_margin (term, margin_right); + } + return dry_run ? lines : 0; +} + +static struct term_state * +find_term_state (struct grub_term_output *term) +{ + struct term_state *state; + for (state = term_states; state; state = state->next) + if (grub_strcmp (state->term_name, term->name) == 0) + return state; + + state = grub_zalloc (sizeof (*state)); + if (!state) + { + grub_errno = GRUB_ERR_NONE; + return NULL; + } + + state->term_name = grub_strdup (term->name); + state->next = term_states; + term_states = state; + + return state; +} + +static int +put_glyphs_terminal (struct grub_unicode_glyph *visual, + grub_ssize_t visual_len, + int margin_left, int margin_right, + struct grub_term_output *term, + struct term_state *state, int fixed_tab, + grub_uint32_t contchar, + int fill_right) +{ + struct grub_unicode_glyph *visual_ptr; + int since_last_nl = 1; + for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++) + { + if (visual_ptr->base == '\n' && fill_right) + fill_margin (term, margin_right); + + putglyph (visual_ptr, term, fixed_tab); + since_last_nl++; + if (visual_ptr->base == '\n') + { + since_last_nl = 0; + if (state && ++state->num_lines + >= (grub_ssize_t) grub_term_height (term) - 2) + { + state->backlog_glyphs = visual_ptr + 1; + state->backlog_len = visual_len - (visual_ptr - visual) - 1; + state->backlog_fixed_tab = fixed_tab; + return 1; + } + + if (!contchar) + grub_print_spaces (term, margin_left); + else + grub_term_gotoxy (term, + (struct grub_term_coordinate) + { margin_left, grub_term_getxy (term).y }); + } + grub_unicode_destroy_glyph (visual_ptr); + } + if (fill_right && since_last_nl) + fill_margin (term, margin_right); + + return 0; +} + +static int +print_backlog (struct grub_term_output *term, + int margin_left, int margin_right) +{ + struct term_state *state = find_term_state (term); + + if (!state) + return 0; + + if (state->backlog_ucs4) + { + int ret; + ret = print_ucs4_terminal (state->backlog_ucs4, + state->backlog_ucs4 + state->backlog_len, + margin_left, margin_right, term, state, 0, + state->backlog_fixed_tab, 0, -1, 0, 0, + 0, 0); + if (!ret) + { + grub_free (state->free); + state->free = NULL; + state->backlog_len = 0; + state->backlog_ucs4 = 0; + } + return ret; + } + + if (state->backlog_glyphs) + { + int ret; + ret = put_glyphs_terminal (state->backlog_glyphs, + state->backlog_len, + margin_left, margin_right, term, state, + state->backlog_fixed_tab, 0, 0); + if (!ret) + { + grub_free (state->free); + state->free = NULL; + state->backlog_len = 0; + state->backlog_glyphs = 0; + } + return ret; + } + + return 0; +} + +static grub_size_t +getcharwidth (const struct grub_unicode_glyph *c, void *term) +{ + return grub_term_getcharwidth (term, c); +} + +static int +print_ucs4_real (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term, int backlog, + int dry_run, int fixed_tab, unsigned skip_lines, + unsigned max_lines, + grub_uint32_t contchar, int fill_right, + struct grub_term_pos *pos) +{ + struct term_state *state = NULL; + + if (!dry_run) + { + struct grub_term_coordinate xy; + if (backlog) + state = find_term_state (term); + + xy = term->getxy (term); + + if (xy.x < margin_left) + { + if (!contchar) + grub_print_spaces (term, margin_left - xy.x); + else + grub_term_gotoxy (term, (struct grub_term_coordinate) {margin_left, + xy.y}); + } + } + + if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS + || (term->flags & GRUB_TERM_CODE_TYPE_MASK) + == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) + { + grub_ssize_t visual_len; + struct grub_unicode_glyph *visual; + grub_ssize_t visual_len_show; + struct grub_unicode_glyph *visual_show; + int ret; + struct grub_unicode_glyph *vptr; + + visual_len = grub_bidi_logical_to_visual (str, last_position - str, + &visual, getcharwidth, term, + get_maxwidth (term, + margin_left, + margin_right), + dry_run ? 0 : get_startwidth (term, + margin_left), + contchar, pos, !!contchar); + if (visual_len < 0) + { + grub_print_error (); + return 0; + } + visual_show = visual; + for (; skip_lines && visual_show < visual + visual_len; visual_show++) + if (visual_show->base == '\n') + skip_lines--; + if (max_lines != (unsigned) -1) + { + for (vptr = visual_show; + max_lines && vptr < visual + visual_len; vptr++) + if (vptr->base == '\n') + max_lines--; + + visual_len_show = vptr - visual_show; + } + else + visual_len_show = visual + visual_len - visual_show; + + if (dry_run) + { + ret = 0; + for (vptr = visual_show; vptr < visual_show + visual_len_show; vptr++) + if (vptr->base == '\n') + ret++; + if (visual_len_show && visual[visual_len_show - 1].base != '\n') + ret++; + for (vptr = visual; vptr < visual + visual_len; vptr++) + grub_unicode_destroy_glyph (vptr); + grub_free (visual); + } + else + { + ret = put_glyphs_terminal (visual_show, visual_len_show, margin_left, + margin_right, + term, state, fixed_tab, contchar, fill_right); + + if (!ret) + grub_free (visual); + else + state->free = visual; + } + return ret; + } + return print_ucs4_terminal (str, last_position, margin_left, margin_right, + term, state, dry_run, fixed_tab, skip_lines, + max_lines, contchar, !!contchar, fill_right, pos); +} + +void +grub_print_ucs4_menu (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term, + int skip_lines, int max_lines, + grub_uint32_t contchar, + struct grub_term_pos *pos) +{ + print_ucs4_real (str, last_position, margin_left, margin_right, + term, 0, 0, 1, skip_lines, max_lines, + contchar, 1, pos); +} + +void +grub_print_ucs4 (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term) +{ + print_ucs4_real (str, last_position, margin_left, margin_right, + term, 0, 0, 1, 0, -1, 0, 0, 0); +} + +int +grub_ucs4_count_lines (const grub_uint32_t * str, + const grub_uint32_t * last_position, + int margin_left, int margin_right, + struct grub_term_output *term) +{ + return print_ucs4_real (str, last_position, margin_left, margin_right, + term, 0, 1, 1, 0, -1, 0, 0, 0); +} + +void +grub_xnputs (const char *str, grub_size_t msg_len) +{ + grub_uint32_t *unicode_str = NULL, *unicode_last_position; + int backlog = 0; + grub_term_output_t term; + + grub_error_push (); + + unicode_str = grub_calloc (msg_len, sizeof (grub_uint32_t)); + + grub_error_pop (); + + if (!unicode_str) + { + for (; msg_len--; str++, msg_len++) + { + struct grub_unicode_glyph c = + { + .variant = 0, + .attributes = 0, + .ncomb = 0, + .estimated_width = 1, + .base = *str + }; + + FOR_ACTIVE_TERM_OUTPUTS(term) + { + (term->putchar) (term, &c); + } + if (*str == '\n') + { + c.base = '\r'; + FOR_ACTIVE_TERM_OUTPUTS(term) + { + (term->putchar) (term, &c); + } + } + } + + return; + } + + msg_len = grub_utf8_to_ucs4 (unicode_str, msg_len, + (grub_uint8_t *) str, -1, 0); + unicode_last_position = unicode_str + msg_len; + + FOR_ACTIVE_TERM_OUTPUTS(term) + { + int cur; + cur = print_ucs4_real (unicode_str, unicode_last_position, 0, 0, + term, grub_more, 0, 0, 0, -1, 0, 0, 0); + if (cur) + backlog = 1; + } + while (backlog) + { + print_more (); + backlog = 0; + FOR_ACTIVE_TERM_OUTPUTS(term) + { + int cur; + cur = print_backlog (term, 0, 0); + if (cur) + backlog = 1; + } + } + grub_free (unicode_str); +} + +void +grub_xputs_normal (const char *str) +{ + grub_xnputs (str, grub_strlen (str)); +} + +void +grub_cls (void) +{ + struct grub_term_output *term; + + FOR_ACTIVE_TERM_OUTPUTS(term) + { + if ((term->flags & GRUB_TERM_DUMB) || (grub_env_get ("debug"))) + { + grub_putcode ('\n', term); + grub_term_refresh (term); + } + else + (term->cls) (term); + } +} |