[U-Boot-Users] [PATCH 10/11] Add command history and editing

Keith J Outwater kjoutwater at raytheon.com
Fri May 5 20:20:53 CEST 2006


The patch below adds command history and editing.  This patch was 
originally posted to the list by James MacAulay 
<james.macaulay at amirix.com>.  I recall a poll on whether others found this 
patch useful or not but I do not remember any resolution to the question. 
I have found this feature extremely useful and I use this feature 
constantly, so I 'gitified' the patch and posted it here.

diff --git a/CHANGELOG b/CHANGELOG
index c774dd0..a560990 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,10 @@
 Changes since U-Boot 1.1.4:
 ======================================================================
 
+* Incorporate patch to support command history and command editing
+  posted by uboot-users by James MacAulay <james.macaulay at amirix.com>
+  Patch by James MacAulay
+
 * Coding Style cleanup
 
 * Write RTC seconds first to maintain settings integrity per
diff --git a/CREDITS b/CREDITS
index f91fa3e..7f65ed7 100644
--- a/CREDITS
+++ b/CREDITS
@@ -279,6 +279,12 @@ N: Raymond Lo
 E: lo at routefree.com
 D: Support for DOS partitions
 
+N: James MacAulay
+E: james.macaulay at amirix.com
+D: Support for command history.
+D: Support for command buffer editing.
+W: http://www.amirix.com
+
 N: Dan Malek
 E: dan at embeddedalley.com
 D: FADSROM, the grandfather of all of this
@@ -455,4 +461,5 @@ W: www.elinos.com
 N: James MacAulay
 E: james.macaulay at amirix.com
 D: Suppport for Amirix AP1000
+D: Support for command history and editing
 W: www.amirix.com
diff --git a/README b/README
index 3ffef62..3a2a5c7 100644
--- a/README
+++ b/README
@@ -2238,7 +2238,48 @@ Low Level (hardware related) configurati
                some other boot loader or by a debugger which
                performs these intializations itself.
 
-
+- CONFIG_COMMAND_HISTORY
+               Adds basic command history to the shell.  The command 
history
+               is accessed by using the up and down arrows.  The size of 
the
+               command history defaults to 10, but can be changed by 
defining
+               CONFIG_COMMAND_HISTORY_SIZE to be another value, for 
example:
+
+               #define CONFIG_COMMAND_HISTORY_SIZE 20
+
+               Because the current command in the buffer is considered 
part
+               of the history, (CONFIG_COMMAND_HISTORY_SIZE - 1) commands 
are
+               saved for retrieval.
+
+               The command history code supports the 4 common escape 
sequences
+               implicitly (0x1b + 0x5b), (0x1b + 0x9b), (0x1b + 0x4f), 
and
+               (0x1b + 0x8f).
+
+               Following the escape sequence, the assumed characters for 
the
+               up arrow is 0x41 and for the down arrow 0x42. If these 
values
+               differ for your terminal, the character value for each of 
the
+               arrows can be redefined like so:
+
+               #define CONFIG_UP_ARROW PROPER_UP_ARROW_VALUE
+               #define CONFIG_DOWN_ARROW PROPER_DOWN_ARROW_VALUE
+
+- CONFIG_COMMAND_EDIT
+               Adds basic command buffer editing to the shell.  The left 
and
+               right arrows are used to move the cursor through the 
current
+               command buffer.  Ctrl-A moves the prompt to the start of 
the
+               command while Ctrl-E moves the prompt to the end of the 
command.
+
+               The command history code supports the 4 common escape 
sequences
+               implicitly (0x1b + 0x5b), (0x1b + 0x9b), (0x1b + 0x4f), 
and
+               (0x1b + 0x8f).
+
+               Following the escape sequence, the assumed characters for 
the
+               right arrow is 0x43 and for the left arrow 0x44. If these 
values
+               differ for your terminal, the character value for each of 
the
+               arrows can be redefined like so:
+
+               #define CONFIG_RIGHT_ARROW PROPER_RIGHT_ARROW_VALUE
+               #define CONFIG_LEFT_ARROW PROPER_LEFT_ARROW_VALUE
+ 
 Building the Software:
 ======================
 
diff --git a/common/main.c b/common/main.c
index 758ef8d..01a370e 100644
--- a/common/main.c
+++ b/common/main.c
@@ -26,8 +26,8 @@
 #include <common.h>
 #include <watchdog.h>
 #include <command.h>
-#ifdef CONFIG_MODEM_SUPPORT
-#include <malloc.h>            /* for free() prototype */
+#if defined(CONFIG_MODEM_SUPPORT) || defined(CONFIG_COMMAND_HISTORY)
+#include <malloc.h>    /* for free() prototype (CONFIG_MODEM_SUPPORT) */
 #endif
 
 #ifdef CFG_HUSH_PARSER
@@ -518,6 +518,271 @@ #endif
 
 
/****************************************************************************/
 
+#ifdef CONFIG_COMMAND_HISTORY
+
+/* Default to a history size of 10 if no other value specified */
+#ifndef CONFIG_COMMAND_HISTORY_SIZE
+#define CONFIG_COMMAND_HISTORY_SIZE 10
+#endif
+
+#define NEXT_INDEX(index) ((index + 1) % CONFIG_COMMAND_HISTORY_SIZE)
+#define PREV_INDEX(index) ((index + CONFIG_COMMAND_HISTORY_SIZE - 1) % 
CONFIG_COMMAND_HISTORY_SIZE)
+
+/* The index of the current command in the history ring buffer */
+static int cmd_buffer_index = 0;
+
+/*
+ * The original index of the current command line in the history ring 
buffer
+ * before the user begins scrolling through the history.
+ */
+static int cmd_buffer_start_index;
+
+/* How far through the history buffer the user has scrolled. */
+static int cmd_buffer_peek_offset;
+
+/* The command history ring buffer */
+static char* cmd_buffer[CONFIG_COMMAND_HISTORY_SIZE] = { NULL };
+#endif
+
+#if defined(CONFIG_COMMAND_HISTORY) || defined(CONFIG_COMMAND_EDIT)
+#define BEEP 0x7
+
+/* Redefine any necessary arrow keys */
+#ifdef CONFIG_UP_ARROW
+#define UP_ARROW_CHAR CONFIG_UP_ARROW
+#else
+#define UP_ARROW_CHAR 0x41
+#endif
+
+#ifdef CONFIG_DOWN_ARROW
+#define DOWN_ARROW_CHAR CONFIG_DOWN_ARROW
+#else
+#define DOWN_ARROW_CHAR 0x42
+#endif
+
+#ifdef CONFIG_RIGHT_ARROW
+#define RIGHT_ARROW_CHAR CONFIG_RIGHT_ARROW
+#else
+#define RIGHT_ARROW_CHAR 0x43
+#endif
+
+#ifdef CONFIG_LEFT_ARROW
+#define LEFT_ARROW_CHAR CONFIG_LEFT_ARROW
+#else
+#define LEFT_ARROW_CHAR 0x44
+#endif
+
+/* When a new prompt is displayed, clear out edit and/or history data. */
+static void cmd_start_new_line(void){
+       console_buffer[0] = '\0';
+#ifdef CONFIG_COMMAND_HISTORY
+       cmd_buffer_start_index = cmd_buffer_index;
+       cmd_buffer_peek_offset = 0;
+#endif
+}
+#endif
+
+#ifdef CONFIG_COMMAND_HISTORY
+/* Re-print out the characters to get the cursor to the end of the line 
*/
+static void cmd_cursor_to_EOL(char** pp, int n){
+       while(*pp != (console_buffer + n)){
+               putc(**pp);
+               (*pp)++;
+       }
+}
+
+/* Save a command by freeing and allocating a new string in the ring 
buffer */
+static void save_cmd(char* cmd, int history_index){
+       if(cmd_buffer[history_index] != NULL){
+               free(cmd_buffer[history_index]);
+       }
+
+       if((cmd_buffer[history_index] = malloc(strlen(cmd) + 1)) != NULL){
+               strcpy(cmd_buffer[history_index], cmd);
+       }
+}
+
+/*
+ * When a command is executed, save the current command buffer and 
advance the
+ * ring buffer.
+ */
+static void cmd_executed(char** p, int n){
+       cmd_cursor_to_EOL(p, n);
+       console_buffer[n] = '\0';
+       save_cmd(console_buffer, cmd_buffer_start_index);
+       cmd_buffer_index = NEXT_INDEX(cmd_buffer_start_index);
+}
+
+/*
+ * Display the proper number of erase sequences.
+ * Assumes that the cursor is at the EOL.
+ */
+static void erase_line(char** pp, int* n){
+       int ii;
+
+       for(ii = 0; ii < *n;ii++){
+               puts (erase_seq);
+       }
+
+       *pp = console_buffer;
+       console_buffer[0] = '\0';
+       *n = 0;
+}
+
+/*
+ * Display the command history buffer that is currently selected.
+ * Erase the line, then reset pp, col, and n, cursor moves to EOL.
+ */
+static void cmd_history_display(int* col, int plen, char** pp, int* n){
+       int cmd_length;
+
+       erase_line(pp, n);
+       cmd_length = strlen(cmd_buffer[cmd_buffer_index]);
+
+       puts(cmd_buffer[cmd_buffer_index]);
+       strcpy(console_buffer, cmd_buffer[cmd_buffer_index]);
+
+       *pp = console_buffer + cmd_length;
+       *col = cmd_length + plen;
+       *n = cmd_length;
+}
+
+/* Move up on in the history if possible. */
+static void cmd_history_up(int* col, int plen, char** pp, int* n){
+       if((cmd_buffer_peek_offset >= (CONFIG_COMMAND_HISTORY_SIZE - 1)) 
||
+          (cmd_buffer[PREV_INDEX(cmd_buffer_index)] == NULL))
+       {
+               putc(BEEP);
+       }
+       else{
+               /* save users command line only */
+               if(cmd_buffer_peek_offset == 0){
+                       save_cmd(console_buffer, cmd_buffer_index);
+               }
+
+               cmd_buffer_peek_offset++;
+               cmd_cursor_to_EOL(pp, *n);
+
+               cmd_buffer_index = PREV_INDEX(cmd_buffer_index);
+               cmd_history_display(col, plen, pp, n);
+       }
+}
+
+/* Move down on in the history if possible. */
+static void cmd_history_down(int* col, int plen, char** pp, int* n){
+       if(cmd_buffer_peek_offset == 0){
+               putc(BEEP);
+       }
+       else{
+               cmd_buffer_peek_offset--;
+               cmd_cursor_to_EOL(pp, *n);
+               cmd_buffer_index = NEXT_INDEX(cmd_buffer_index);
+
+               cmd_history_display(col, plen, pp, n);
+       }
+}
+#endif
+
+#ifdef CONFIG_COMMAND_EDIT
+/* Insert a character at the current cursor location and increment 
cursor. */
+static void cmd_edit_insert_char(char** pp, int n, char c){
+       int chars_to_move;
+       int ii;
+
+       /* move chars along +1 will include the '/0' */
+       chars_to_move = n - (*pp - console_buffer) + 1;
+
+       for(ii = 0;ii < chars_to_move;ii++){
+               console_buffer[n - ii + 1] = console_buffer[n - ii];
+       }
+
+       /* insert new char */
+       **pp = c;
+       (*pp)++;
+
+       /* redisplay any moved chars, exclude '/0' */
+       for(ii = 0;ii < (chars_to_move - 1);ii++){
+               putc((*pp)[ii]);
+       }
+
+       /* put cursor back where user left it */
+       for(ii = 0;ii < (chars_to_move - 1);ii++){
+               putc('\b');
+       }
+}
+
+/* Remove a character at the current cursor location. */
+static void cmd_edit_remove_char(char** pp, int n){
+       int chars_to_move;
+       int cursor_pos;
+       int ii;
+
+       cursor_pos = *pp - console_buffer;
+
+       /* move chars, include the '/0' */
+       chars_to_move = n - cursor_pos;
+
+       for(ii = 0;ii < chars_to_move;ii++){
+               (*pp)[ii] = (*pp)[ii + 1];
+       }
+
+       /* move cursor */
+       putc('\b');
+
+       /* redisplay any moved chars, exclude '/0' */
+       for(ii = 0;ii < (chars_to_move - 1);ii++){
+               putc((*pp)[ii]);
+       }
+
+       /* clear spot where char used to be */
+       putc(' ');
+
+       /* put cursor back where it user left it */
+       for(ii = 0;ii < chars_to_move;ii++){
+               putc('\b');
+       }
+}
+
+/* Move cursor left one character if possible. */
+static void cmd_edit_left_arrow(char** pp){
+       if(*pp > console_buffer){
+               putc('\b');
+               (*pp)--;
+       }
+       else{
+               putc(BEEP);
+       }
+}
+
+/* Move cursor right one character if possible. */
+static void cmd_edit_right_arrow(char** pp, int n){
+       if((*pp - console_buffer) < n){
+               putc(**pp);
+               (*pp)++;
+       }
+       else{
+               putc(BEEP);
+       }
+}
+
+/* Move cursor to start of the command buffer. */
+static void cmd_edit_home(char** pp){
+       while(*pp > console_buffer){
+               putc('\b');
+               (*pp)--;
+       }
+}
+
+/* Move cursor to end of the command buffer. */
+static void cmd_edit_end(char** pp, int n){
+       while((*pp - console_buffer) < n){
+               putc(**pp);
+               (*pp)++;
+       }
+}
+
+#endif
+
 /*
  * Prompt for input and read a line.
  * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
@@ -534,6 +799,10 @@ int readline (const char *const prompt)
        int     col;                            /* output column cnt    */
        char    c;
 
+#if defined(CONFIG_COMMAND_HISTORY) || defined(CONFIG_COMMAND_EDIT)
+       cmd_start_new_line();
+#endif
+
        /* print prompt */
        if (prompt) {
                plen = strlen (prompt);
@@ -564,17 +833,40 @@ #endif
                switch (c) {
                case '\r':                              /* Enter */
                case '\n':
+#if !defined (CONFIG_COMMAND_HISTORY) && !defined(CONFIG_COMMAND_EDIT)
                        *p = '\0';
+#endif
+
+#ifdef CONFIG_COMMAND_HISTORY
+                       /* save command in history */
+                       if(n != 0){
+                               cmd_executed(&p, n);
+                       }
+#endif
                        puts ("\r\n");
                        return (p - console_buffer);
 
                case '\0':                              /* nul */
                        continue;
 
-               case 0x03:                              /* ^C - break */
-                       console_buffer[0] = '\0';       /* discard input 
*/
+#ifdef CONFIG_COMMAND_EDIT
+               case 0x01:                        /* ^A - home     */
+                       cmd_edit_home(&p);
+                       continue;
+#endif
+               case 0x03:                        /* ^C - break    */
+#ifdef CONFIG_COMMAND_HISTORY
+                       cmd_buffer_index = cmd_buffer_start_index;
+#else
+                       console_buffer[0] = '\0';   /* discard input */
+#endif
                        return (-1);
 
+#ifdef CONFIG_COMMAND_EDIT
+               case 0x05:                        /* ^E - end      */
+                       cmd_edit_end(&p, n);
+                       continue;
+#endif
                case 0x15:                              /* ^U - erase line 
*/
                        while (col > plen) {
                                puts (erase_seq);
@@ -593,9 +885,60 @@ #endif
 
                case 0x08:                              /* ^H  - backspace 
*/
                case 0x7F:                              /* DEL - backspace 
*/
+#ifdef CONFIG_COMMAND_EDIT
+                       if(p > console_buffer){
+                               /* remove character before cursor */
+                               p--;
+                               cmd_edit_remove_char(&p, n);
+                               n--;
+                       }
+                       else{
+                               putc(BEEP);
+                       }
+#else
                        p=delete_char(console_buffer, p, &col, &n, plen);
+#endif
                        continue;
 
+#if defined(CONFIG_COMMAND_HISTORY) || defined(CONFIG_COMMAND_EDIT)
+               /* escape char used for special characters */
+               case 0x1b:
+                       c = getc();
+                       /* accept any of the 4 possible escape sequences 
*/
+                       if((c == 0x5b) || (c == 0x9b) || (c == 0x4f) || (c 
== 0x8f)){
+                               c = getc();
+                               switch(c){
+                                       case UP_ARROW_CHAR:{
+#ifdef CONFIG_COMMAND_HISTORY
+                                               cmd_history_up(&col, plen, 
&p, &n);
+#endif
+                                               continue;
+                                       }
+                                       case DOWN_ARROW_CHAR:{
+#ifdef CONFIG_COMMAND_HISTORY
+                                               cmd_history_down(&col, 
plen, &p, &n);
+#endif
+                                               continue;
+                                       }
+                                       case RIGHT_ARROW_CHAR:{
+#ifdef CONFIG_COMMAND_EDIT
+                                               cmd_edit_right_arrow(&p, 
n);
+#endif
+                                               continue;
+                                       }
+                                       case LEFT_ARROW_CHAR:{
+#ifdef CONFIG_COMMAND_EDIT
+                                               cmd_edit_left_arrow(&p);
+#endif
+                                               continue;
+                                       }
+                                       default:{
+                                               continue;
+                                       }
+                               }
+                       }
+                       continue;
+#endif
                default:
                        /*
                         * Must be a normal character then
@@ -610,13 +953,24 @@ #ifdef CONFIG_AUTO_COMPLETE
                                                continue;
                                        }
 #endif
+#ifdef CONFIG_COMMAND_EDIT
+                                       /* treat tabs as space characters 
*/
+                                       c = ' ';
+                                       ++col;
+                                       putc(c);
+#else
                                        puts (tab_seq+(col&07));
                                        col += 8 - (col&07);
+#endif
                                } else {
                                        ++col;          /* echo input */
                                        putc (c);
                                }
+#ifdef CONFIG_COMMAND_EDIT
+                               cmd_edit_insert_char(&p, n, c);
+#else
                                *p++ = c;
+#endif
                                ++n;
                        } else {                        /* Buffer full */
                                putc ('\a');




More information about the U-Boot mailing list