From dfe653fc492345ac03a58383b3bb6b23439d51b1 Mon Sep 17 00:00:00 2001 From: okuji Date: Fri, 17 Sep 1999 03:21:31 +0000 Subject: [PATCH] improve the command-line interface. --- ChangeLog | 36 +++++ NEWS | 2 + stage2/boot.c | 1 - stage2/char_io.c | 354 ++++++++++++++++++++++++++++++----------------- stage2/shared.h | 28 ++-- 5 files changed, 285 insertions(+), 136 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6aab44524..c17388075 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +1999-09-17 OKUJI Yoshinori + + Killing, yanking and manipulating the history are supported. + + * stage2/shared.h (cur_cmdline): Removed. + (MAX_CMDLINE): Moved near the beginning of the file. + (NEW_HEAPSIZE): Likewise. + (CMDLINE_BUFLEN): Set to MAX_CMDLINE. + (KILL_BUF): New macro. + (KILL_BUFLEN): Likewise. + (HISTORY_BUF): Likewise. + (HISTORY_SIZE): Likewise. + (HISTORY_BUFLEN): Likewise. + (MENU_BUF): Set to HISTORY_BUF + HISTORY_BUFLEN. + (MENU_BUFLEN): Set to 0x8000 + PASSWORD_BUF - HISTORY_BUF. + (strcpy): New macro. + (grub_strcpy): Delared. + * stage2/boot.c (cur_cmdline): Removed. + * stage2/char_io.c [!STAGE1_5] (grub_strcpy): New function. + [!STAGE1_5] (get_history): Likewise. + [!STAGE1_5] (add_history): Likewise. + [!STAGE1_5] (get_cmdline): Use BUF instead of CMDLINE for the + working buffer for the command-line. + A new function cl_insert is used to insert a string to the + command-line. + In the case where C-u or C-k is pressed, copy the string being + deleted to KILL. + If C-y is pressed, insert KILL to the command-line. + If C-p is pressed, fetch the previous command from the history + list HISTORY, and if C-n is pressed, fetch the next command from + it. + If LPOS is less than LLEN, add CMDLINE into the history list. + If C is equal to KEY_UP, set C to 16, and if C is equal to + KEY_DOWN, set C to 14. + [!STAGE1_5] (num_history): New variable. + 1999-09-15 OKUJI Yoshinori * stage2/size_test: Do not check for the size of Stage 2. diff --git a/NEWS b/NEWS index b6f9f2a4d..02bbcedf1 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,8 @@ New in 0.5.93: the second floppy drive. * Integrated the netboot support in the Dresden version of GRUB. * FreeBSD support in the grub shell is improved. +* Killing (C-u and C-k), yanking (C-y) and manipulating the history + (C-p and C-n) are supported. New in 0.5.92 - 1999-07-26: * Bug fixes (i.e. Stage 1.5 can work fine again). diff --git a/stage2/boot.c b/stage2/boot.c index 709bcfeb0..7493a54c4 100644 --- a/stage2/boot.c +++ b/stage2/boot.c @@ -26,7 +26,6 @@ #include "imgact_aout.h" #include "i386-elf.h" -char *cur_cmdline; static int cur_addr; entry_func entry_addr; static struct mod_list mll[99]; diff --git a/stage2/char_io.c b/stage2/char_io.c index e076f40e2..c70fc345c 100644 --- a/stage2/char_io.c +++ b/stage2/char_io.c @@ -184,6 +184,31 @@ init_page (void) version_string, mbi.mem_lower, mbi.mem_upper); } +/* The number of the history entries. */ +static int num_history = 0; + +/* Get the NOth history. If NO is less than zero or greater than or + equal to NUM_HISTORY, return NULL. Otherwise return a valid string. */ +static char * +get_history (int no) +{ + if (no < 0 || no >= num_history) + return 0; + + return (char *) HISTORY_BUF + MAX_CMDLINE * no; +} + +/* Add CMDLINE to the history buffer. */ +static void +add_history (const char *cmdline, int no) +{ + grub_memmove ((char *) HISTORY_BUF + MAX_CMDLINE * (no + 1), + (char *) HISTORY_BUF + MAX_CMDLINE * no, + MAX_CMDLINE * (num_history - no)); + grub_strcpy ((char *) HISTORY_BUF + MAX_CMDLINE * no, cmdline); + if (num_history < HISTORY_SIZE) + num_history++; +} /* Don't use this with a MAXLEN greater than 1600 or so! The problem is that GET_CMDLINE depends on the everything fitting on the screen @@ -198,69 +223,103 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, int echo_char, int completion) { int ystart, yend, xend, lpos, c; + /* The length of PROMPT. */ int plen = 0; + /* The length of the command-line. */ int llen = 0; - + /* The index for the history. */ + int history = -1; + /* The working buffer for the command-line. */ + char *buf = (char *) CMDLINE_BUF; + /* The kill buffer. */ + char *kill = (char *) KILL_BUF; + /* nested function definition for code simplicity */ static void cl_print (char *str, int echo_char) - { - while (*str != 0) - { - putchar (echo_char ? echo_char : *str); - str++; - if (++xend > 78) - { - xend = 0; - putchar (' '); - if (yend == (getxy () & 0xff)) - ystart--; - else - yend++; - } - } - } + { + while (*str != 0) + { + putchar (echo_char ? echo_char : *str); + str++; + if (++xend > 78) + { + xend = 0; + putchar (' '); + if (yend == (getxy () & 0xff)) + ystart--; + else + yend++; + } + } + } + /* nested function definition for code simplicity */ static void cl_setcpos (void) - { - yend = ((lpos + plen) / 79) + ystart; - xend = ((lpos + plen) % 79); - gotoxy (xend, yend); - } - + { + yend = ((lpos + plen) / 79) + ystart; + xend = ((lpos + plen) % 79); + gotoxy (xend, yend); + } + /* nested function definition for initial command-line printing */ static void cl_init () - { - /* distinguish us from other lines and error messages! */ - putchar ('\n'); - - /* print full line and set position here */ - ystart = (getxy () & 0xff); - yend = ystart; - xend = 0; - cl_print (prompt, 0); - cl_print (cmdline, echo_char); - cl_setcpos (); - } - + { + /* distinguish us from other lines and error messages! */ + putchar ('\n'); + + /* print full line and set position here */ + ystart = (getxy () & 0xff); + yend = ystart; + xend = 0; + cl_print (prompt, 0); + cl_print (buf, echo_char); + cl_setcpos (); + } + /* nested function definition for erasing to the end of the line */ static void cl_kill_to_end () - { - int i; - cmdline[lpos] = 0; - for (i = lpos; i <= llen; i++) - { - if (i && ((i + plen) % 79) == 0) + { + int i; + buf[lpos] = 0; + for (i = lpos; i <= llen; i++) + { + if (i && ((i + plen) % 79) == 0) + putchar (' '); putchar (' '); - putchar (' '); - } - llen = lpos; - cl_setcpos (); - } + } + llen = lpos; + cl_setcpos (); + } + + static void cl_insert (const char *str) + { + int l = grub_strlen (str); + + if (llen + l < maxlen) + { + if (lpos == llen) + { + grub_memmove (buf + lpos, str, l + 1); + cl_print (buf + lpos, echo_char); + lpos += l; + cl_setcpos (); + } + else + { + grub_memmove (buf + lpos + l, buf + lpos, llen - lpos + 1); + grub_memmove (buf + lpos, str, l); + cl_setcpos (); + cl_print (buf + lpos, echo_char); + lpos += l; + cl_setcpos (); + } + llen += l; + } + } + + plen = grub_strlen (prompt); + llen = grub_strlen (cmdline); - while (prompt[plen]) - plen++; - while (cmdline[llen]) - llen++; if (maxlen > MAX_CMDLINE) { maxlen = MAX_CMDLINE; @@ -271,9 +330,11 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, } } lpos = llen; - + grub_strcpy (buf, cmdline); + *kill = 0; + cl_init (); - + while (ASCII_CHAR (c = getkey ()) != '\n' && ASCII_CHAR (c) != '\r') { switch (c) @@ -284,6 +345,12 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, case KEY_RIGHT: c = 6; break; + case KEY_UP: + c = 16; + break; + case KEY_DOWN: + c = 14; + break; case KEY_HOME: c = 1; break; @@ -296,11 +363,10 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, case KEY_BACKSPACE: c = 8; break; - default: } c = ASCII_CHAR (c); - + switch (c) { case 27: /* ESC immediately return 1 */ @@ -311,14 +377,14 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, int i, j = 0, llen_old = llen; /* Find the first word. */ - while (cmdline[j] == ' ') + while (buf[j] == ' ') j++; - while (cmdline[j] && cmdline[j] != '=' && cmdline[j] != ' ') + while (buf[j] && buf[j] != '=' && buf[j] != ' ') j++; /* Since the command line cannot have a '\n', we're OK to use C. */ - c = cmdline[lpos]; + c = buf[lpos]; cl_kill_to_end (); @@ -330,21 +396,21 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, if (lpos > j) { - for (i = lpos; i > 0 && cmdline[i - 1] != ' '; i--); + for (i = lpos; i > 0 && buf[i - 1] != ' '; i--); if (i <= j) i = j + 1; /* print possible completions */ - print_completions (cmdline + i); + print_completions (buf + i); /* if somebody in print_completions has added something, account for that */ - while (cmdline[lpos]) + while (buf[lpos]) lpos++, llen_old++; } else { /* Print the command list. */ struct builtin **builtin; - + for (builtin = builtin_table; *builtin != 0; builtin++) { /* Do not print the name if it cannot be run in @@ -357,7 +423,7 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, } /* restore command-line */ - cmdline[lpos] = c; + buf[lpos] = c; llen = llen_old; cl_init (); } @@ -373,7 +439,7 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, case 6: /* C-f forward one character */ if (lpos < llen) { - lpos ++; + lpos++; cl_setcpos (); } break; @@ -387,7 +453,7 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, case 4: /* C-d delete character under cursor */ if (lpos == llen) break; - lpos ++; + lpos++; /* fallthrough is on purpose! */ case 8: /* C-h backspace */ #ifdef GRUB_UTIL @@ -396,8 +462,7 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, if (lpos > 0) { int i; - for (i = lpos - 1; i < llen; i++) - cmdline[i] = cmdline[i + 1]; + grub_memmove (buf + lpos - 1, buf + lpos, llen - lpos + 1); i = lpos; lpos = llen - 1; cl_setcpos (); @@ -407,7 +472,7 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, cl_setcpos (); if (lpos != llen) { - cl_print (cmdline + lpos, echo_char); + cl_print (buf + lpos, echo_char); cl_setcpos (); } } @@ -415,78 +480,108 @@ get_cmdline (char *prompt, char *cmdline, int maxlen, case 21: /* C-u kill to beginning of line */ if (lpos == 0) break; - { - int i; - for (i = 0; i < (llen - lpos); i++) - cmdline[i] = cmdline[lpos + i]; - } + /* Copy the string being deleted to KILL. */ + grub_memmove (kill, buf, lpos); + kill[lpos] = 0; + grub_memmove (buf, buf + lpos, llen - lpos + 1); lpos = llen - lpos; cl_setcpos (); - /* fallthrough on purpose! */ + cl_kill_to_end (); + lpos = 0; + cl_setcpos (); + cl_print (buf, echo_char); + cl_setcpos (); + break; case 11: /* C-k kill to end of line */ - if (lpos < llen) - { - cl_kill_to_end (); - if (c == 21) - { - lpos = 0; - cl_setcpos (); - cl_print (cmdline, echo_char); - cl_setcpos (); - } - } + if (lpos == llen) + break; + /* Copy the string being deleted to KILL. */ + grub_memmove (kill, buf + lpos, llen - lpos + 1); + cl_kill_to_end (); + break; + case 25: /* C-y yank the kill buffer */ + cl_insert (kill); + break; + case 16: /* C-p fetch the previous command */ + { + char *p; + + if (history < 0) + /* Save the working buffer. */ + grub_strcpy (cmdline, buf); + else if (grub_strcmp (get_history (history), buf) != 0) + /* If BUF is modified, add it into the history list. */ + add_history (buf, history); + + history++; + p = get_history (history); + if (! p) + { + history--; + break; + } + + lpos = 0; + cl_setcpos (); + cl_kill_to_end (); + grub_strcpy (buf, p); + llen = grub_strlen (buf); + lpos = llen; + cl_print (buf, 0); + cl_setcpos (); + } + break; + case 14: /* C-n fetch the next command */ + { + char *p; + + if (history < 0) + { + break; + } + else if (grub_strcmp (get_history (history), buf) != 0) + /* If BUF is modified, add it into the history list. */ + add_history (buf, history); + + history--; + p = get_history (history); + if (! p) + p = cmdline; + + lpos = 0; + cl_setcpos (); + cl_kill_to_end (); + grub_strcpy (buf, p); + llen = grub_strlen (buf); + lpos = llen; + cl_print (buf, 0); + cl_setcpos (); + } break; default: /* insert printable character into line */ - if (llen < (maxlen - 1) && c >= ' ' && c <= '~') + if (c >= ' ' && c <= '~') { - if (lpos == llen) - { - cmdline[lpos] = c; - cmdline[lpos + 1] = 0; - cl_print (cmdline + lpos, echo_char); - lpos ++; - cl_setcpos (); - } - else - { - int i; - for (i = llen; i >= lpos; i--) - cmdline[i + 1] = cmdline[i]; - cmdline[lpos] = c; - cl_setcpos (); - cl_print (cmdline + lpos, echo_char); - lpos++; - cl_setcpos (); - } - llen++; + char str[2]; + + str[0] = c; + str[1] = 0; + cl_insert (str); } } } - + /* goto part after line here */ yend = ((llen + plen) / 79) + ystart; putchar ('\n'); gotoxy (0, getxy () & 0xff); - + /* remove leading spaces */ - /* use c and lpos as indexes now */ - for (lpos = 0; cmdline[lpos] == ' '; lpos++); - - if (lpos != 0) - { - c = 0; - do - { - cmdline[c] = cmdline[lpos]; - c++; - lpos++; - } - while (cmdline[lpos]); - - /* Zero-terminate the string. */ - cmdline[c] = 0; - } - + for (lpos = 0; buf[lpos] == ' '; lpos++); + + grub_memmove (cmdline, buf + lpos, llen - lpos + 1); + if (lpos < llen) + add_history (cmdline, 0); + return 0; } @@ -753,4 +848,11 @@ grub_memset (void *start, int c, int len) return errnum ? NULL : start; } + +char * +grub_strcpy (char *dest, const char *src) +{ + grub_memmove (dest, src, grub_strlen (src) + 1); + return dest; +} #endif /* ! STAGE1_5 */ diff --git a/stage2/shared.h b/stage2/shared.h index 038b548fd..9aa4f3950 100644 --- a/stage2/shared.h +++ b/stage2/shared.h @@ -48,6 +48,11 @@ extern char *grub_scratch_mem; #define MAXINT 0x7FFFFFFF +/* Maximum command line size. Before you blindly increase this value, + see the comment in char_io.c (get_cmdline). */ +#define MAX_CMDLINE 1600 +#define NEW_HEAPSIZE 1500 + /* 512-byte scratch area */ #define SCRATCHADDR RAW_ADDR (0x77e00) #define SCRATCHSEG RAW_SEG (0x77e0) @@ -89,12 +94,20 @@ extern char *grub_scratch_mem; /* The buffer for the command-line. */ #define CMDLINE_BUF (PASSWORD_BUF + PASSWORD_BUFLEN) -/* Make sure that this is larger than NEW_HEAPSIZE defined below. */ -#define CMDLINE_BUFLEN 0x600 +#define CMDLINE_BUFLEN MAX_CMDLINE + +/* The kill buffer for the command-line. */ +#define KILL_BUF (CMDLINE_BUF + CMDLINE_BUFLEN) +#define KILL_BUFLEN MAX_CMDLINE + +/* The history buffer for the command-line. */ +#define HISTORY_BUF (KILL_BUF + KILL_BUFLEN) +#define HISTORY_SIZE 5 +#define HISTORY_BUFLEN (MAX_CMDLINE * HISTORY_SIZE) /* The buffer for the menu entries. */ -#define MENU_BUF (CMDLINE_BUF + CMDLINE_BUFLEN) -#define MENU_BUFLEN (0x8000 + PASSWORD_BUF - MENU_BUF) +#define MENU_BUF (HISTORY_BUF + HISTORY_BUFLEN) +#define MENU_BUFLEN (0x8000 + PASSWORD_BUF - HISTORY_BUF) /* * Linux setup parameters @@ -272,6 +285,7 @@ extern char *grub_scratch_mem; #define strcmp grub_strcmp #define tolower grub_tolower #define strlen grub_strlen +#define strcpy grub_strcpy #endif /* WITHOUT_LIBC_STUBS */ @@ -448,11 +462,6 @@ extern char *err_list[]; typedef void (*entry_func) (int, int, int, int, int, int) __attribute__ ((noreturn)); -/* Maximum command line size. Before you blindly increase this value, - see the comment in char_io.c (get_cmdline). */ -#define MAX_CMDLINE 1600 -#define NEW_HEAPSIZE 1500 -extern char *cur_cmdline; extern entry_func entry_addr; /* Enter the stage1.5/stage2 C code after the stack is set up. */ @@ -598,6 +607,7 @@ char *grub_strstr (const char *s1, const char *s2); int grub_memcmp (const char *s1, const char *s2, int n); int grub_strcmp (const char *s1, const char *s2); int grub_strlen (const char *str); +char *grub_strcpy (char *dest, const char *src); /* misc */ void init_page (void);