diff mbox

[U-Boot] common: cli_readline: Improve command line editing

Message ID 1465390186-26157-1-git-send-email-james.byrne@origamienergy.com
State Accepted
Delegated to: Tom Rini
Headers show

Commit Message

James Byrne June 8, 2016, 12:49 p.m. UTC
This improves the cread_line() function so that it will correctly
process the 'Home', 'End', 'Delete' and arrow key escape sequences
produced by various terminal emulators. This makes command line editing
a more pleasant experience.

The previous code only supported the cursor keys and the 'Home' key, and
only for certain terminal emulator configurations. This adds support for
the 'End and 'Delete' keys, and recognises a wider range of escape
sequences. For example, the left arrow key can be 'ESC O D' instead of
'ESC [ D', and the 'Home' key can be 'ESC [ H', 'ESC O H', 'ESC 1 ~' or
'ESC 7 ~', depending on what terminal emulator you use and how it is
configured.

Signed-off-by: James Byrne <james.byrne@origamienergy.com>
---

 common/cli_readline.c | 108 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 78 insertions(+), 30 deletions(-)

Comments

Tom Rini July 14, 2016, 10:32 p.m. UTC | #1
On Wed, Jun 08, 2016 at 01:49:46PM +0100, James Byrne wrote:

> This improves the cread_line() function so that it will correctly
> process the 'Home', 'End', 'Delete' and arrow key escape sequences
> produced by various terminal emulators. This makes command line editing
> a more pleasant experience.
> 
> The previous code only supported the cursor keys and the 'Home' key, and
> only for certain terminal emulator configurations. This adds support for
> the 'End and 'Delete' keys, and recognises a wider range of escape
> sequences. For example, the left arrow key can be 'ESC O D' instead of
> 'ESC [ D', and the 'Home' key can be 'ESC [ H', 'ESC O H', 'ESC 1 ~' or
> 'ESC 7 ~', depending on what terminal emulator you use and how it is
> configured.
> 
> Signed-off-by: James Byrne <james.byrne@origamienergy.com>

This introduces a new warning:
common/cli_readline.c: In function ‘cli_readline_into_buffer’:
common/cli_readline.c:361:4: warning: ‘act’ may be used uninitialized in this function [-Wmaybe-uninitialized]
diff mbox

Patch

diff --git a/common/cli_readline.c b/common/cli_readline.c
index c1476e4..0d4ad49 100644
--- a/common/cli_readline.c
+++ b/common/cli_readline.c
@@ -283,46 +283,94 @@  static int cread_line(const char *const prompt, char *buf, unsigned int *len,
 		 * handle standard linux xterm esc sequences for arrow key, etc.
 		 */
 		if (esc_len != 0) {
+			enum { ESC_REJECT, ESC_SAVE, ESC_CONVERTED } act;
+
 			if (esc_len == 1) {
-				if (ichar == '[') {
-					esc_save[esc_len] = ichar;
-					esc_len = 2;
+				if (ichar == '[' || ichar == 'O')
+					act = ESC_SAVE;
+				else
+					act = ESC_REJECT;
+			} else if (esc_len == 2) {
+				switch (ichar) {
+				case 'D':	/* <- key */
+					ichar = CTL_CH('b');
+					act = ESC_CONVERTED;
+					break;	/* pass off to ^B handler */
+				case 'C':	/* -> key */
+					ichar = CTL_CH('f');
+					act = ESC_CONVERTED;
+					break;	/* pass off to ^F handler */
+				case 'H':	/* Home key */
+					ichar = CTL_CH('a');
+					act = ESC_CONVERTED;
+					break;	/* pass off to ^A handler */
+				case 'F':	/* End key */
+					ichar = CTL_CH('e');
+					act = ESC_CONVERTED;
+					break;	/* pass off to ^E handler */
+				case 'A':	/* up arrow */
+					ichar = CTL_CH('p');
+					act = ESC_CONVERTED;
+					break;	/* pass off to ^P handler */
+				case 'B':	/* down arrow */
+					ichar = CTL_CH('n');
+					act = ESC_CONVERTED;
+					break;	/* pass off to ^N handler */
+				case '1':
+				case '3':
+				case '4':
+				case '7':
+				case '8':
+					if (esc_save[1] == '[') {
+						/* see if next character is ~ */
+						act = ESC_SAVE;
+					} else {
+						act = ESC_REJECT;
+					}
+					break;
+				default:
+					act = ESC_REJECT;
+					break;
+				}
+			} else if (esc_len == 3) {
+				if (ichar == '~') {
+					switch (esc_save[2]) {
+					case '3':	/* Delete key */
+						ichar = CTL_CH('d');
+						act = ESC_CONVERTED;
+						break;	/* pass to ^D handler */
+					case '1':	/* Home key */
+					case '7':
+						ichar = CTL_CH('a');
+						act = ESC_CONVERTED;
+						break;	/* pass to ^A handler */
+					case '4':	/* End key */
+					case '8':
+						ichar = CTL_CH('e');
+						act = ESC_CONVERTED;
+						break;	/* pass to ^E handler */
+					default:
+						act = ESC_REJECT;
+						break;
+					}
 				} else {
-					cread_add_str(esc_save, esc_len,
-						      insert, &num, &eol_num,
-						      buf, *len);
-					esc_len = 0;
+					act = ESC_REJECT;
 				}
-				continue;
 			}
 
-			switch (ichar) {
-			case 'D':	/* <- key */
-				ichar = CTL_CH('b');
-				esc_len = 0;
-				break;
-			case 'C':	/* -> key */
-				ichar = CTL_CH('f');
-				esc_len = 0;
-				break;	/* pass off to ^F handler */
-			case 'H':	/* Home key */
-				ichar = CTL_CH('a');
-				esc_len = 0;
-				break;	/* pass off to ^A handler */
-			case 'A':	/* up arrow */
-				ichar = CTL_CH('p');
-				esc_len = 0;
-				break;	/* pass off to ^P handler */
-			case 'B':	/* down arrow */
-				ichar = CTL_CH('n');
-				esc_len = 0;
-				break;	/* pass off to ^N handler */
-			default:
+			switch (act) {
+			case ESC_SAVE:
+				esc_save[esc_len++] = ichar;
+				continue;
+			case ESC_REJECT:
 				esc_save[esc_len++] = ichar;
 				cread_add_str(esc_save, esc_len, insert,
 					      &num, &eol_num, buf, *len);
 				esc_len = 0;
 				continue;
+			case ESC_CONVERTED:
+				esc_len = 0;
+				break;
 			}
 		}