diff -uNr grub2.cvs/include/grub/terminfo.h grub2/include/grub/terminfo.h --- grub2.cvs/include/grub/terminfo.h 2005-09-03 10:54:26.000000000 -0600 +++ grub2/include/grub/terminfo.h 2005-12-27 16:57:57.000000000 -0700 @@ -23,9 +23,6 @@ #include #include -char *grub_terminfo_get_current (void); -grub_err_t grub_terminfo_set_current (const char *); - void grub_terminfo_gotoxy (grub_uint8_t x, grub_uint8_t y); void grub_terminfo_cls (void); void grub_terminfo_reverse_video_on (void); diff -uNr grub2.cvs/term/terminfo.c grub2/term/terminfo.c --- grub2.cvs/term/terminfo.c 2005-11-13 08:47:09.000000000 -0700 +++ grub2/term/terminfo.c 2005-12-27 16:57:57.000000000 -0700 @@ -33,78 +33,244 @@ #include #include +/* A terminfo definition. */ struct terminfo { char *name; - char *gotoxy; char *cls; char *reverse_video_on; char *reverse_video_off; char *cursor_on; char *cursor_off; + + struct terminfo *next; }; +typedef struct terminfo terminfo_t; + +/* The list of terminfo definitions. */ +static terminfo_t *terminfo_list; + +/* The current terminfo definition. */ +static terminfo_t *cur_terminfo; + +/* Argument options. */ +static const struct grub_arg_option options[] = +{ + {"add", 'a', 0, "Add a definition", 0, 0}, + {"modify", 'm', 0, "Modify a definition", 0, 0}, + {"delete", 'd', 0, "Delete a definition", 0, 0}, + {"view", 'v', 0, "View a definition", 0, 0}, + {"cursor-address", 'p', 0, "Sequence to position cursor", 0, ARG_TYPE_STRING}, + {"clear-screen", 'c', 0, "Sequence to clear screen", 0, ARG_TYPE_STRING}, + {"reverse-on", 'w', 0, "Sequence to enable reverse video mode", 0, ARG_TYPE_STRING}, + {"reverse-off", 'x', 0, "Sequence to disable reverse video mode", 0, ARG_TYPE_STRING}, + {"cursor-on", 'y', 0, "Sequence to enable cursor", 0, ARG_TYPE_STRING}, + {"cursor-off", 'z', 0, "Sequence to disable cursor", 0, ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} +}; + +/* Add new terminfo definition to list. */ +static grub_err_t +add_definition (terminfo_t *ti) +{ + terminfo_t *p; -static struct terminfo term; + for (p = terminfo_list; p; p = p->next) + if (grub_strcasecmp (p->name, ti->name) == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "terminfo definition name already exists."); -/* Get current terminfo name. */ -char * -grub_terminfo_get_current (void) + ti->next = terminfo_list; + terminfo_list = ti; + + return GRUB_ERR_NONE; +} + +/* Delete terminfo definition from list. */ +static void +delete_definition (terminfo_t *ti) { - return term.name; + terminfo_t *p, *q; + + for (p = 0, q = terminfo_list; q; p = q, q = q->next) + if (q == ti) + { + if (p) + p->next = q->next; + else + terminfo_list = q->next; + + grub_free (q->name); + grub_free (q->gotoxy); + grub_free (q->cls); + grub_free (q->reverse_video_on); + grub_free (q->reverse_video_off); + grub_free (q->cursor_on); + grub_free (q->cursor_off); + grub_free (q); + + break; + } } -/* Free *PTR and set *PTR to NULL, to prevent double-free. */ +/* Iterate through terminfo definitions. */ static void -grub_terminfo_free (char **ptr) +iterate_definitions (int (*hook) (terminfo_t *ti)) { - grub_free (*ptr); - *ptr = 0; + terminfo_t *p; + + for (p = terminfo_list; p; p = p->next) + if (hook (p)) + break; +} + +/* Escape a string. */ +static char * +escape_string (const char *in) +{ + char *q, *new_string; + + new_string = (char *) grub_malloc (grub_strlen (in) * 4 + 1); /* Max escaped string size is 4x. */ + + for (q = new_string; *in; in++, q++) + { + switch (*in) + { + case '\a': /* Common escape sequences. */ + *q++ = '\\'; + *q = 'a'; + break; + case '\b': + *q++ = '\\'; + *q = 'b'; + break; + case '\e': + *q++ = '\\'; + *q = 'e'; + break; + case '\f': + *q++ = '\\'; + *q = 'f'; + break; + case '\n': + *q++ = '\\'; + *q = 'n'; + break; + case '\r': + *q++ = '\\'; + *q = 'r'; + break; + case '\t': + *q++ = '\\'; + *q = 't'; + break; + case '\v': + *q++ = '\\'; + *q = 'v'; + break; + case '\\': + *q++ = '\\'; + *q = '\\'; + break; + case 32 ... 91: /* Typeable character. Leave as is. */ + case 93 ... 126: + *q = *in; + break; + default: /* Convert to octal sequence. */ + *q++ = '\\'; + *q++ = ((*in >> 8) & 7) + '0'; + *q++ = ((*in >> 4) & 7) + '0'; + *q = ((*in >> 0) & 7) + '0'; + } + } + + *q = 0; + return grub_realloc (new_string, grub_strlen (new_string) + 1); /* Shrink alloc. */ } -/* Set current terminfo type. */ -grub_err_t -grub_terminfo_set_current (const char *str) -{ - /* 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. - */ - - /* Free previously allocated memory. */ - grub_terminfo_free (&term.name); - grub_terminfo_free (&term.gotoxy); - grub_terminfo_free (&term.cls); - grub_terminfo_free (&term.reverse_video_on); - grub_terminfo_free (&term.reverse_video_off); - grub_terminfo_free (&term.cursor_on); - grub_terminfo_free (&term.cursor_off); - - if (grub_strcmp ("vt100", str) == 0) - { - term.name = grub_strdup ("vt100"); - term.gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH"); - term.cls = grub_strdup ("\e[H\e[J"); - term.reverse_video_on = grub_strdup ("\e[7m"); - term.reverse_video_off = grub_strdup ("\e[m"); - term.cursor_on = grub_strdup ("\e[?25l"); - term.cursor_off = grub_strdup ("\e[?25h"); - return grub_errno; +/* Unescape a string. */ +static char * +unescape_string (const char *in) +{ + char *q, *new_string; + + new_string = (char *) grub_malloc (grub_strlen (in) + 1); /* New string will be <= current string. */ + + for (q = new_string; *in; in++, q++) + { + switch (*in) + { + case '\\': /* Escape sequence. */ + in++; + if (*in >= '0' && *in <= '3') /* Looks like the begining of an octal. */ + { + *q = *in - '0'; + in++; + if (*in >= '0' && *in <= '7') /* Middle of an octal. */ + { + *q = (*q << 3) | (*in - '0'); + in++; + if (*in >= '0' && *in <= '7') /* End of an octal. */ + { + *q = (*q << 3) | (*in - '0'); + break; + } + in--; /* Was not an octal; rewind. */ + } + in -= 2; /* Was not an octal; rewind. */ + q--; /* Nothing to put here; rewind. */ + break; + } + switch (*in) /* Does not look like an octal. */ + { + case 'a': /* Alert. */ + *q = '\a'; + break; + + case 'b': /* Backspace. */ + *q = '\b'; + break; + + case 'e': /* Escape. */ + case 'E': + *q = '\e'; + break; + + case 'f': /* Form Feed. */ + *q = '\f'; + break; + + case 'n': /* Newline. */ + *q = '\n'; + break; + + case 'r': /* Carriage Return. */ + *q = '\r'; + break; + + case 't': /* Tab. */ + *q = '\t'; + break; + + case 'v': /* Vertical Tab. */ + *q = '\v'; + break; + + case '\\': /* Backslash. */ + *q = '\\'; + break; + + default: /* Anything else. */ + *q = *in; + } + break; + + default: /* Does not need any interpretation. */ + *q = *in; + } } - - return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminfo type."); + *q = 0; + return grub_realloc (new_string, grub_strlen (new_string) + 1); /* Shrink alloc. */ } /* Wrapper for grub_putchar to write strings. */ @@ -119,70 +285,273 @@ void grub_terminfo_gotoxy (grub_uint8_t x, grub_uint8_t y) { - putstr (grub_terminfo_tparm (term.gotoxy, y, x)); + putstr (grub_terminfo_tparm (cur_terminfo->gotoxy, y, x)); } /* Clear the screen. */ void grub_terminfo_cls (void) { - putstr (grub_terminfo_tparm (term.cls)); + putstr (grub_terminfo_tparm (cur_terminfo->cls)); } /* Set reverse video mode on. */ void grub_terminfo_reverse_video_on (void) { - putstr (grub_terminfo_tparm (term.reverse_video_on)); + putstr (grub_terminfo_tparm (cur_terminfo->reverse_video_on)); } /* Set reverse video mode off. */ void grub_terminfo_reverse_video_off (void) { - putstr (grub_terminfo_tparm (term.reverse_video_off)); + putstr (grub_terminfo_tparm (cur_terminfo->reverse_video_off)); } /* Show cursor. */ void grub_terminfo_cursor_on (void) { - putstr (grub_terminfo_tparm (term.cursor_on)); + putstr (grub_terminfo_tparm (cur_terminfo->cursor_on)); } /* Hide cursor. */ void grub_terminfo_cursor_off (void) { - putstr (grub_terminfo_tparm (term.cursor_off)); + putstr (grub_terminfo_tparm (cur_terminfo->cursor_off)); } /* GRUB Command. */ static grub_err_t -grub_cmd_terminfo (struct grub_arg_list *state __attribute__ ((unused)), - int argc, char **args) +grub_cmd_terminfo (struct grub_arg_list *state, int argc, char **args) { + int i; + char *p; + terminfo_t *ti = 0; + + auto int print_terminfo (terminfo_t *); + auto int find_terminfo (terminfo_t *); + + int print_terminfo (terminfo_t *t) + { + grub_printf (" %s", t->name); + return 0; + } + + int find_terminfo (terminfo_t *t) + { + if (grub_strcasecmp (t->name, args[0]) == 0) + { + ti = t; + return 1; + } + + return 0; + } + + i = state[0].set + state[1].set + state[2].set + state[3].set; + + if (i > 1) + return grub_error (GRUB_ERR_INVALID_COMMAND, "add, modify, delete and view are exclusive operations."); + if (argc == 0) - { - grub_printf ("Current terminfo type: %s\n", grub_terminfo_get_current()); - return GRUB_ERR_NONE; - } - else if (argc != 1) + { + if (i == 0) + { + grub_printf ("Available terminfo definition(s):"); + iterate_definitions (print_terminfo); + grub_putchar ('\n'); + grub_printf ("Current terminfo definition: %s\n", cur_terminfo->name); + + return GRUB_ERR_NONE; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, "terminfo definition name must be specified."); + } + + if (argc > 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many parameters."); + + iterate_definitions (find_terminfo); + if ((! ti) && (! state[0].set)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such terminfo definition."); + + if (ti && state[0].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "terminfo definition name already exists"); + + if (i == 0) + cur_terminfo = ti; else - return grub_terminfo_set_current (args[0]); + { + if (state[0].set) /* Add new definition. */ + { + if (! state[4].set) + return grub_error (GRUB_ERR_INVALID_COMMAND, "Missing mandatory option for `cursor-address'."); + + p = unescape_string (state[4].arg); /* Cursor address. */ + if (! *p) + { + grub_free (p); + return grub_error (GRUB_ERR_INVALID_COMMAND, "option `cursor-address' must not be blank."); + } + + ti = (terminfo_t *) grub_malloc (sizeof (terminfo_t)); + if (! ti) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for new definition."); + grub_memset (ti, 0, sizeof (terminfo_t)); + + ti->name = grub_strdup (args[0]); + + ti->gotoxy = p; + + if (state[5].set) /* Clear screen. */ + ti->cls = unescape_string (state[5].arg); + else + ti->cls = grub_strdup (""); + + if (state[6].set) /* Reverse video on. */ + ti->reverse_video_on = unescape_string (state[6].arg); + else + ti->reverse_video_on = grub_strdup (""); + + if (state[7].set) /* Reverse video off. */ + ti->reverse_video_off = unescape_string (state[7].arg); + else + ti->reverse_video_off = grub_strdup (""); + + if (state[8].set) /* Cursor on. */ + ti->cursor_on = unescape_string (state[8].arg); + else + ti->cursor_on = grub_strdup (""); + + if (state[9].set) /* Cursor off. */ + ti->cursor_off = unescape_string (state[9].arg); + else + ti->cursor_off = grub_strdup (""); + + add_definition (ti); + } + else if (state[1].set) /* Modify definition. */ + { + if (state[4].set) + { + p = unescape_string (state[4].arg); + if (! *p) + { + grub_free (p); + return grub_error (GRUB_ERR_INVALID_COMMAND, "option `cursor-address' must not be blank."); + } + + grub_free (ti->gotoxy); + ti->gotoxy = p; + } + + if (state[5].set) + { + grub_free (ti->cls); + ti->cls = unescape_string (state[5].arg); + } + + if (state[6].set) + { + grub_free (ti->reverse_video_on); + ti->reverse_video_on = unescape_string (state[6].arg); + } + + if (state[7].set) + { + grub_free (ti->reverse_video_off); + ti->reverse_video_off = unescape_string (state[7].arg); + } + + if (state[8].set) + { + grub_free (ti->cursor_on); + ti->cursor_on = unescape_string (state[8].arg); + } + + if (state[9].set) + { + grub_free (ti->cursor_off); + ti->cursor_off = unescape_string (state[9].arg); + } + } + else if (state[2].set) /* Delete definition. */ + { + if (cur_terminfo == ti) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "cannot delete in use definition."); + + delete_definition (ti); + } + else if (state[3].set) /* View definition. */ + { + grub_printf ("Terminfo definition: %s\n", ti->name); + + p = escape_string (ti->gotoxy); + grub_printf ("Cursor address: %s\n", p); + grub_free (p); + + p = escape_string (ti->cls); + grub_printf ("Clear screen: %s\n", p); + grub_free (p); + + p = escape_string (ti->reverse_video_on); + grub_printf ("Reverse video on: %s\n", p); + grub_free (p); + + p = escape_string (ti->reverse_video_off); + grub_printf ("Reverse video off: %s\n", p); + grub_free (p); + + p = escape_string (ti->cursor_on); + grub_printf ("Cursor on: %s\n", p); + grub_free (p); + + p = escape_string (ti->cursor_off); + grub_printf ("Cursor off: %s\n", p); + grub_free (p); + } + } + + return GRUB_ERR_NONE; +} + +static void +setup_defaults (void) +{ + terminfo_t *ti; + + ti = (terminfo_t *) grub_malloc (sizeof (terminfo_t)); + /* Do I need to test if malloc succeeded here? What do I do if it did not? */ + + /* Default terminal definition vt100. */ + ti->name = grub_strdup ("vt100"); + ti->gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH"); + ti->cls = grub_strdup ("\e[H\e[J"); + ti->reverse_video_on = grub_strdup ("\e[7m"); + ti->reverse_video_off = grub_strdup ("\e[m"); + ti->cursor_on = grub_strdup ("\e[?25l"); + ti->cursor_off = grub_strdup ("\e[?25h"); + ti->next = 0; + + add_definition (ti); + cur_terminfo = ti; } GRUB_MOD_INIT(terminfo) { (void) mod; /* To stop warning. */ + setup_defaults (); grub_register_command ("terminfo", grub_cmd_terminfo, GRUB_COMMAND_FLAG_BOTH, - "terminfo [TERM]", "Set terminfo type.", 0); - grub_terminfo_set_current ("vt100"); + "terminfo [TERMTYPE [-a|-m|-d|-v [ARGS...]]]", "Select or define terminfo definition(s). Escape sequences must be double quoted.", options); } GRUB_MOD_FINI(terminfo) { grub_unregister_command ("terminfo"); + while (terminfo_list) + delete_definition (terminfo_list); }