| /* terminfo.c - simple terminfo module */ |
| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2003,2004,2005,2007 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/>. |
| */ |
| |
| /* |
| * This file contains various functions dealing with different |
| * terminal capabilities. For example, vt52 and vt100. |
| */ |
| |
| #include <grub/types.h> |
| #include <grub/misc.h> |
| #include <grub/mm.h> |
| #include <grub/err.h> |
| #include <grub/dl.h> |
| #include <grub/term.h> |
| #include <grub/terminfo.h> |
| #include <grub/tparm.h> |
| #include <grub/extcmd.h> |
| #include <grub/i18n.h> |
| #include <grub/time.h> |
| #if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275) |
| #include <grub/ieee1275/ieee1275.h> |
| #endif |
| |
| GRUB_MOD_LICENSE ("GPLv3+"); |
| |
| #define ANSI_CSI 0x9b |
| #define ANSI_CSI_STR "\x9b" |
| |
| static struct grub_term_output *terminfo_outputs; |
| |
| /* Get current terminfo name. */ |
| char * |
| grub_terminfo_get_current (struct grub_term_output *term) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| return data->name; |
| } |
| |
| /* Free *PTR and set *PTR to NULL, to prevent double-free. */ |
| static void |
| grub_terminfo_free (char **ptr) |
| { |
| grub_free (*ptr); |
| *ptr = 0; |
| } |
| |
| static void |
| grub_terminfo_all_free (struct grub_term_output *term) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| |
| /* Free previously allocated memory. */ |
| grub_terminfo_free (&data->name); |
| grub_terminfo_free (&data->gotoxy); |
| grub_terminfo_free (&data->cls); |
| grub_terminfo_free (&data->reverse_video_on); |
| grub_terminfo_free (&data->reverse_video_off); |
| grub_terminfo_free (&data->cursor_on); |
| grub_terminfo_free (&data->cursor_off); |
| } |
| |
| /* Set current terminfo type. */ |
| grub_err_t |
| grub_terminfo_set_current (struct grub_term_output *term, |
| const char *str) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| /* TODO |
| * Lookup user specified terminfo type. If found, set term variables |
| * as appropriate. Otherwise return an error. |
| * |
| * How should this be done? |
| * a. A static table included in this module. |
| * - I do not like this idea. |
| * b. A table stored in the configuration directory. |
| * - Users must convert their terminfo settings if we have not already. |
| * c. Look for terminfo files in the configuration directory. |
| * - /usr/share/terminfo is 6.3M on my system. |
| * - /usr/share/terminfo is not on most users boot partition. |
| * + Copying the terminfo files you want to use to the grub |
| * configuration directory is easier then (b). |
| * d. Your idea here. |
| */ |
| |
| grub_terminfo_all_free (term); |
| |
| if (grub_strcmp ("vt100", str) == 0) |
| { |
| data->name = grub_strdup ("vt100"); |
| data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH"); |
| data->cls = grub_strdup ("\e[H\e[J"); |
| data->reverse_video_on = grub_strdup ("\e[7m"); |
| data->reverse_video_off = grub_strdup ("\e[m"); |
| data->cursor_on = grub_strdup ("\e[?25h"); |
| data->cursor_off = grub_strdup ("\e[?25l"); |
| data->setcolor = NULL; |
| return grub_errno; |
| } |
| |
| if (grub_strcmp ("vt100-color", str) == 0) |
| { |
| data->name = grub_strdup ("vt100-color"); |
| data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH"); |
| data->cls = grub_strdup ("\e[H\e[J"); |
| data->reverse_video_on = grub_strdup ("\e[7m"); |
| data->reverse_video_off = grub_strdup ("\e[m"); |
| data->cursor_on = grub_strdup ("\e[?25h"); |
| data->cursor_off = grub_strdup ("\e[?25l"); |
| data->setcolor = grub_strdup ("\e[3%p1%dm\e[4%p2%dm"); |
| return grub_errno; |
| } |
| |
| if (grub_strcmp ("arc", str) == 0) |
| { |
| data->name = grub_strdup ("arc"); |
| data->gotoxy = grub_strdup (ANSI_CSI_STR "%i%p1%d;%p2%dH"); |
| data->cls = grub_strdup (ANSI_CSI_STR "2J"); |
| data->reverse_video_on = grub_strdup (ANSI_CSI_STR "7m"); |
| data->reverse_video_off = grub_strdup (ANSI_CSI_STR "0m"); |
| data->cursor_on = 0; |
| data->cursor_off = 0; |
| data->setcolor = grub_strdup (ANSI_CSI_STR "3%p1%dm" |
| ANSI_CSI_STR "4%p2%dm"); |
| return grub_errno; |
| } |
| |
| if (grub_strcmp ("ieee1275", str) == 0 |
| || grub_strcmp ("ieee1275-nocursor", str) == 0) |
| { |
| data->name = grub_strdup ("ieee1275"); |
| data->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH"); |
| /* Clear the screen. Using serial console, screen(1) only recognizes the |
| * ANSI escape sequence. Using video console, Apple Open Firmware |
| * (version 3.1.1) only recognizes the literal ^L. So use both. */ |
| data->cls = grub_strdup ("\e[2J"); |
| data->reverse_video_on = grub_strdup ("\e[7m"); |
| data->reverse_video_off = grub_strdup ("\e[m"); |
| if (grub_strcmp ("ieee1275", str) == 0) |
| { |
| data->cursor_on = grub_strdup ("\e[?25h"); |
| data->cursor_off = grub_strdup ("\e[?25l"); |
| } |
| else |
| { |
| data->cursor_on = 0; |
| data->cursor_off = 0; |
| } |
| data->setcolor = grub_strdup ("\e[3%p1%dm\e[4%p2%dm"); |
| return grub_errno; |
| } |
| |
| if (grub_strcmp ("dumb", str) == 0) |
| { |
| data->name = grub_strdup ("dumb"); |
| data->gotoxy = NULL; |
| data->cls = NULL; |
| data->reverse_video_on = NULL; |
| data->reverse_video_off = NULL; |
| data->cursor_on = NULL; |
| data->cursor_off = NULL; |
| data->setcolor = NULL; |
| return grub_errno; |
| } |
| |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown terminfo type `%s'"), |
| str); |
| } |
| |
| grub_err_t |
| grub_terminfo_output_register (struct grub_term_output *term, |
| const char *type) |
| { |
| grub_err_t err; |
| struct grub_terminfo_output_state *data; |
| |
| err = grub_terminfo_set_current (term, type); |
| |
| if (err) |
| return err; |
| |
| data = (struct grub_terminfo_output_state *) term->data; |
| data->next = terminfo_outputs; |
| terminfo_outputs = term; |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| grub_err_t |
| grub_terminfo_output_unregister (struct grub_term_output *term) |
| { |
| struct grub_term_output **ptr; |
| |
| for (ptr = &terminfo_outputs; *ptr; |
| ptr = &((struct grub_terminfo_output_state *) (*ptr)->data)->next) |
| if (*ptr == term) |
| { |
| grub_terminfo_all_free (term); |
| *ptr = ((struct grub_terminfo_output_state *) (*ptr)->data)->next; |
| return GRUB_ERR_NONE; |
| } |
| return grub_error (GRUB_ERR_BUG, "terminal not found"); |
| } |
| |
| /* Wrapper for grub_putchar to write strings. */ |
| static void |
| putstr (struct grub_term_output *term, const char *str) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| while (*str) |
| data->put (term, *str++); |
| } |
| |
| struct grub_term_coordinate |
| grub_terminfo_getxy (struct grub_term_output *term) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| |
| return data->pos; |
| } |
| |
| void |
| grub_terminfo_gotoxy (struct grub_term_output *term, |
| struct grub_term_coordinate pos) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| |
| if (pos.x > grub_term_width (term) || pos.y > grub_term_height (term)) |
| { |
| grub_error (GRUB_ERR_BUG, "invalid point (%u,%u)", pos.x, pos.y); |
| return; |
| } |
| |
| if (data->gotoxy) |
| putstr (term, grub_terminfo_tparm (data->gotoxy, pos.y, pos.x)); |
| else |
| { |
| if ((pos.y == data->pos.y) && (pos.x == data->pos.x - 1)) |
| data->put (term, '\b'); |
| } |
| |
| data->pos = pos; |
| } |
| |
| /* Clear the screen. */ |
| void |
| grub_terminfo_cls (struct grub_term_output *term) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| |
| putstr (term, grub_terminfo_tparm (data->cls)); |
| grub_terminfo_gotoxy (term, (struct grub_term_coordinate) { 0, 0 }); |
| } |
| |
| void |
| grub_terminfo_setcolorstate (struct grub_term_output *term, |
| const grub_term_color_state state) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| |
| if (data->setcolor) |
| { |
| int fg; |
| int bg; |
| /* Map from VGA to terminal colors. */ |
| const int colormap[8] |
| = { 0, /* Black. */ |
| 4, /* Blue. */ |
| 2, /* Green. */ |
| 6, /* Cyan. */ |
| 1, /* Red. */ |
| 5, /* Magenta. */ |
| 3, /* Yellow. */ |
| 7, /* White. */ |
| }; |
| |
| switch (state) |
| { |
| case GRUB_TERM_COLOR_STANDARD: |
| case GRUB_TERM_COLOR_NORMAL: |
| fg = grub_term_normal_color & 0x0f; |
| bg = grub_term_normal_color >> 4; |
| break; |
| case GRUB_TERM_COLOR_HIGHLIGHT: |
| fg = grub_term_highlight_color & 0x0f; |
| bg = grub_term_highlight_color >> 4; |
| break; |
| default: |
| return; |
| } |
| |
| putstr (term, grub_terminfo_tparm (data->setcolor, colormap[fg & 7], |
| colormap[bg & 7])); |
| return; |
| } |
| |
| switch (state) |
| { |
| case GRUB_TERM_COLOR_STANDARD: |
| case GRUB_TERM_COLOR_NORMAL: |
| putstr (term, grub_terminfo_tparm (data->reverse_video_off)); |
| break; |
| case GRUB_TERM_COLOR_HIGHLIGHT: |
| putstr (term, grub_terminfo_tparm (data->reverse_video_on)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void |
| grub_terminfo_setcursor (struct grub_term_output *term, const int on) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| |
| if (on) |
| putstr (term, grub_terminfo_tparm (data->cursor_on)); |
| else |
| putstr (term, grub_terminfo_tparm (data->cursor_off)); |
| } |
| |
| /* The terminfo version of putchar. */ |
| void |
| grub_terminfo_putchar (struct grub_term_output *term, |
| const struct grub_unicode_glyph *c) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| |
| /* Keep track of the cursor. */ |
| switch (c->base) |
| { |
| case '\a': |
| break; |
| |
| case '\b': |
| case 127: |
| if (data->pos.x > 0) |
| data->pos.x--; |
| break; |
| |
| case '\n': |
| if (data->pos.y < grub_term_height (term) - 1) |
| data->pos.y++; |
| break; |
| |
| case '\r': |
| data->pos.x = 0; |
| break; |
| |
| default: |
| if ((int) data->pos.x + c->estimated_width >= (int) grub_term_width (term) + 1) |
| { |
| data->pos.x = 0; |
| if (data->pos.y < grub_term_height (term) - 1) |
| data->pos.y++; |
| data->put (term, '\r'); |
| data->put (term, '\n'); |
| } |
| data->pos.x += c->estimated_width; |
| break; |
| } |
| |
| data->put (term, c->base); |
| } |
| |
| struct grub_term_coordinate |
| grub_terminfo_getwh (struct grub_term_output *term) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) term->data; |
| |
| return data->size; |
| } |
| |
| static void |
| grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len, |
| int (*readkey) (struct grub_term_input *term)) |
| { |
| int c; |
| |
| #define CONTINUE_READ \ |
| { \ |
| grub_uint64_t start; \ |
| /* On 9600 we have to wait up to 12 milliseconds. */ \ |
| start = grub_get_time_ms (); \ |
| do \ |
| c = readkey (term); \ |
| while (c == -1 && grub_get_time_ms () - start < 100); \ |
| if (c == -1) \ |
| return; \ |
| \ |
| keys[*len] = c; \ |
| (*len)++; \ |
| } |
| |
| c = readkey (term); |
| if (c < 0) |
| { |
| *len = 0; |
| return; |
| } |
| *len = 1; |
| keys[0] = c; |
| if (c != ANSI_CSI && c != '\e') |
| { |
| /* Backspace: Ctrl-h. */ |
| if (c == 0x7f) |
| c = '\b'; |
| if (c < 0x20 && c != '\t' && c!= '\b' && c != '\n' && c != '\r') |
| c = GRUB_TERM_CTRL | (c - 1 + 'a'); |
| *len = 1; |
| keys[0] = c; |
| return; |
| } |
| |
| { |
| static struct |
| { |
| char key; |
| unsigned ascii; |
| } |
| three_code_table[] = |
| { |
| {'4', GRUB_TERM_KEY_DC}, |
| {'A', GRUB_TERM_KEY_UP}, |
| {'B', GRUB_TERM_KEY_DOWN}, |
| {'C', GRUB_TERM_KEY_RIGHT}, |
| {'D', GRUB_TERM_KEY_LEFT}, |
| {'F', GRUB_TERM_KEY_END}, |
| {'H', GRUB_TERM_KEY_HOME}, |
| {'K', GRUB_TERM_KEY_END}, |
| {'P', GRUB_TERM_KEY_DC}, |
| {'?', GRUB_TERM_KEY_PPAGE}, |
| {'/', GRUB_TERM_KEY_NPAGE}, |
| {'@', GRUB_TERM_KEY_INSERT}, |
| }; |
| |
| static unsigned four_code_table[] = |
| { |
| [1] = GRUB_TERM_KEY_HOME, |
| [3] = GRUB_TERM_KEY_DC, |
| [5] = GRUB_TERM_KEY_PPAGE, |
| [6] = GRUB_TERM_KEY_NPAGE, |
| [7] = GRUB_TERM_KEY_HOME, |
| [8] = GRUB_TERM_KEY_END, |
| [17] = GRUB_TERM_KEY_F6, |
| [18] = GRUB_TERM_KEY_F7, |
| [19] = GRUB_TERM_KEY_F8, |
| [20] = GRUB_TERM_KEY_F9, |
| [21] = GRUB_TERM_KEY_F10, |
| [23] = GRUB_TERM_KEY_F11, |
| [24] = GRUB_TERM_KEY_F12, |
| }; |
| char fx_key[] = |
| { 'P', 'Q', 'w', 'x', 't', 'u', |
| 'q', 'r', 'p', 'M', 'A', 'B', 'H', 'F' }; |
| unsigned fx_code[] = |
| { GRUB_TERM_KEY_F1, GRUB_TERM_KEY_F2, GRUB_TERM_KEY_F3, |
| GRUB_TERM_KEY_F4, GRUB_TERM_KEY_F5, GRUB_TERM_KEY_F6, |
| GRUB_TERM_KEY_F7, GRUB_TERM_KEY_F8, GRUB_TERM_KEY_F9, |
| GRUB_TERM_KEY_F10, GRUB_TERM_KEY_F11, GRUB_TERM_KEY_F12, |
| GRUB_TERM_KEY_HOME, GRUB_TERM_KEY_END }; |
| unsigned i; |
| |
| if (c == '\e') |
| { |
| CONTINUE_READ; |
| |
| if (c == 'O') |
| { |
| CONTINUE_READ; |
| |
| for (i = 0; i < ARRAY_SIZE (fx_key); i++) |
| if (fx_key[i] == c) |
| { |
| keys[0] = fx_code[i]; |
| *len = 1; |
| return; |
| } |
| } |
| |
| if (c != '[') |
| return; |
| } |
| |
| CONTINUE_READ; |
| |
| for (i = 0; i < ARRAY_SIZE (three_code_table); i++) |
| if (three_code_table[i].key == c) |
| { |
| keys[0] = three_code_table[i].ascii; |
| *len = 1; |
| return; |
| } |
| |
| switch (c) |
| { |
| case '[': |
| CONTINUE_READ; |
| if (c >= 'A' && c <= 'E') |
| { |
| keys[0] = GRUB_TERM_KEY_F1 + c - 'A'; |
| *len = 1; |
| return; |
| } |
| return; |
| case 'O': |
| CONTINUE_READ; |
| for (i = 0; i < ARRAY_SIZE (fx_key); i++) |
| if (fx_key[i] == c) |
| { |
| keys[0] = fx_code[i]; |
| *len = 1; |
| return; |
| } |
| return; |
| |
| case '0': |
| { |
| int num = 0; |
| CONTINUE_READ; |
| if (c != '0' && c != '1') |
| return; |
| num = (c - '0') * 10; |
| CONTINUE_READ; |
| if (c < '0' || c > '9') |
| return; |
| num += (c - '0'); |
| if (num == 0 || num > 12) |
| return; |
| CONTINUE_READ; |
| if (c != 'q') |
| return; |
| keys[0] = fx_code[num - 1]; |
| *len = 1; |
| return; |
| } |
| |
| case '1' ... '9': |
| { |
| unsigned val = c - '0'; |
| CONTINUE_READ; |
| if (c >= '0' && c <= '9') |
| { |
| val = val * 10 + (c - '0'); |
| CONTINUE_READ; |
| } |
| if (c != '~') |
| return; |
| if (val >= ARRAY_SIZE (four_code_table) |
| || four_code_table[val] == 0) |
| return; |
| keys[0] = four_code_table[val]; |
| *len = 1; |
| return; |
| } |
| default: |
| return; |
| } |
| } |
| #undef CONTINUE_READ |
| } |
| |
| /* The terminfo version of getkey. */ |
| int |
| grub_terminfo_getkey (struct grub_term_input *termi) |
| { |
| struct grub_terminfo_input_state *data |
| = (struct grub_terminfo_input_state *) (termi->data); |
| if (data->npending) |
| { |
| int ret; |
| data->npending--; |
| ret = data->input_buf[0]; |
| grub_memmove (data->input_buf, data->input_buf + 1, data->npending |
| * sizeof (data->input_buf[0])); |
| return ret; |
| } |
| |
| grub_terminfo_readkey (termi, data->input_buf, |
| &data->npending, data->readkey); |
| |
| #if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275) |
| if (data->npending == 1 && data->input_buf[0] == '\e' |
| && grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_REPEAT) |
| && grub_get_time_ms () - data->last_key_time < 1000 |
| && (data->last_key & GRUB_TERM_EXTENDED)) |
| { |
| data->npending = 0; |
| data->last_key_time = grub_get_time_ms (); |
| return data->last_key; |
| } |
| #endif |
| |
| if (data->npending) |
| { |
| int ret; |
| data->npending--; |
| ret = data->input_buf[0]; |
| #if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275) |
| if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_REPEAT)) |
| { |
| data->last_key = ret; |
| data->last_key_time = grub_get_time_ms (); |
| } |
| #endif |
| grub_memmove (data->input_buf, data->input_buf + 1, data->npending |
| * sizeof (data->input_buf[0])); |
| return ret; |
| } |
| |
| return GRUB_TERM_NO_KEY; |
| } |
| |
| grub_err_t |
| grub_terminfo_input_init (struct grub_term_input *termi) |
| { |
| struct grub_terminfo_input_state *data |
| = (struct grub_terminfo_input_state *) (termi->data); |
| data->npending = 0; |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| grub_err_t |
| grub_terminfo_output_init (struct grub_term_output *term) |
| { |
| grub_terminfo_cls (term); |
| return GRUB_ERR_NONE; |
| } |
| |
| /* GRUB Command. */ |
| |
| static grub_err_t |
| print_terminfo (void) |
| { |
| const char *encoding_names[(GRUB_TERM_CODE_TYPE_MASK |
| >> GRUB_TERM_CODE_TYPE_SHIFT) + 1] |
| = { |
| /* VGA and glyph descriptor types are just for completeness, |
| they are not used on terminfo terminals. |
| */ |
| [GRUB_TERM_CODE_TYPE_ASCII >> GRUB_TERM_CODE_TYPE_SHIFT] = _("ASCII"), |
| [GRUB_TERM_CODE_TYPE_CP437 >> GRUB_TERM_CODE_TYPE_SHIFT] = "CP-437", |
| [GRUB_TERM_CODE_TYPE_UTF8_LOGICAL >> GRUB_TERM_CODE_TYPE_SHIFT] |
| = _("UTF-8"), |
| [GRUB_TERM_CODE_TYPE_UTF8_VISUAL >> GRUB_TERM_CODE_TYPE_SHIFT] |
| /* TRANSLATORS: visually ordered UTF-8 is a non-compliant encoding |
| based on UTF-8 with right-to-left languages written in reverse. |
| Used on some terminals. Normal UTF-8 is refered as |
| "logically-ordered UTF-8" by opposition. */ |
| = _("visually-ordered UTF-8"), |
| [GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS >> GRUB_TERM_CODE_TYPE_SHIFT] |
| = "Glyph descriptors", |
| _("Unknown encoding"), _("Unknown encoding"), _("Unknown encoding") |
| }; |
| struct grub_term_output *cur; |
| |
| grub_puts_ (N_("Current terminfo types:")); |
| for (cur = terminfo_outputs; cur; |
| cur = ((struct grub_terminfo_output_state *) cur->data)->next) |
| grub_printf ("%s: %s\t%s\t%dx%d\n", cur->name, |
| grub_terminfo_get_current(cur), |
| encoding_names[(cur->flags & GRUB_TERM_CODE_TYPE_MASK) |
| >> GRUB_TERM_CODE_TYPE_SHIFT], |
| ((struct grub_terminfo_output_state *) cur->data)->pos.x, |
| ((struct grub_terminfo_output_state *) cur->data)->pos.y); |
| |
| return GRUB_ERR_NONE; |
| } |
| |
| static const struct grub_arg_option options[] = |
| { |
| {"ascii", 'a', 0, N_("Terminal is ASCII-only [default]."), 0, ARG_TYPE_NONE}, |
| {"utf8", 'u', 0, N_("Terminal is logical-ordered UTF-8."), 0, ARG_TYPE_NONE}, |
| {"visual-utf8", 'v', 0, N_("Terminal is visually-ordered UTF-8."), 0, |
| ARG_TYPE_NONE}, |
| {"geometry", 'g', 0, N_("Terminal has specified geometry."), |
| /* TRANSLATORS: "x" has to be entered in, like an identifier, so please don't |
| use better Unicode codepoints. */ |
| N_("WIDTHxHEIGHT."), ARG_TYPE_STRING}, |
| {0, 0, 0, 0, 0, 0} |
| }; |
| |
| enum |
| { |
| OPTION_ASCII, |
| OPTION_UTF8, |
| OPTION_VISUAL_UTF8, |
| OPTION_GEOMETRY |
| }; |
| |
| static grub_err_t |
| grub_cmd_terminfo (grub_extcmd_context_t ctxt, int argc, char **args) |
| { |
| struct grub_term_output *cur; |
| int encoding = GRUB_TERM_CODE_TYPE_ASCII; |
| struct grub_arg_list *state = ctxt->state; |
| int w = 0, h = 0; |
| |
| if (argc == 0) |
| return print_terminfo (); |
| |
| if (state[OPTION_ASCII].set) |
| encoding = GRUB_TERM_CODE_TYPE_ASCII; |
| |
| if (state[OPTION_UTF8].set) |
| encoding = GRUB_TERM_CODE_TYPE_UTF8_LOGICAL; |
| |
| if (state[OPTION_VISUAL_UTF8].set) |
| encoding = GRUB_TERM_CODE_TYPE_UTF8_VISUAL; |
| |
| if (state[OPTION_GEOMETRY].set) |
| { |
| char *ptr = state[OPTION_GEOMETRY].arg; |
| w = grub_strtoul (ptr, &ptr, 0); |
| if (grub_errno) |
| return grub_errno; |
| if (*ptr != 'x') |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, |
| N_("incorrect terminal dimensions specification")); |
| ptr++; |
| h = grub_strtoul (ptr, &ptr, 0); |
| if (grub_errno) |
| return grub_errno; |
| } |
| |
| for (cur = terminfo_outputs; cur; |
| cur = ((struct grub_terminfo_output_state *) cur->data)->next) |
| if (grub_strcmp (args[0], cur->name) == 0 |
| || (grub_strcmp (args[0], "ofconsole") == 0 |
| && grub_strcmp ("console", cur->name) == 0)) |
| { |
| cur->flags = (cur->flags & ~GRUB_TERM_CODE_TYPE_MASK) | encoding; |
| |
| if (w && h) |
| { |
| struct grub_terminfo_output_state *data |
| = (struct grub_terminfo_output_state *) cur->data; |
| data->size.x = w; |
| data->size.y = h; |
| } |
| |
| if (argc == 1) |
| return GRUB_ERR_NONE; |
| |
| return grub_terminfo_set_current (cur, args[1]); |
| } |
| |
| return grub_error (GRUB_ERR_BAD_ARGUMENT, |
| N_("terminal %s isn't found or it's not handled by terminfo"), |
| args[0]); |
| } |
| |
| static grub_extcmd_t cmd; |
| |
| GRUB_MOD_INIT(terminfo) |
| { |
| cmd = grub_register_extcmd ("terminfo", grub_cmd_terminfo, 0, |
| N_("[[-a|-u|-v] [-g WxH] TERM [TYPE]]"), |
| N_("Set terminfo type of TERM to TYPE.\n"), |
| options); |
| } |
| |
| GRUB_MOD_FINI(terminfo) |
| { |
| grub_unregister_extcmd (cmd); |
| } |