[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