When editing eficonfig "optional data" (typically cmdline arguments)
it's useful to be able to edit the string rather than having to re-type
the entire thing. Implement support for editing buffers to make this a
whole lot nicer. Specifically, add support for moving the cursor with
the arrow keys and End key as well as deleting backwards with the delete
key.

Signed-off-by: Casey Connolly <[email protected]>
---
 cmd/eficonfig.c              |  2 ++
 lib/efi_loader/efi_console.c | 52 ++++++++++++++++++++++++++++++++++++--------
 2 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/cmd/eficonfig.c b/cmd/eficonfig.c
index 8ac0fb98e02e..d8d946c87ac8 100644
--- a/cmd/eficonfig.c
+++ b/cmd/eficonfig.c
@@ -973,8 +973,10 @@ static efi_status_t handle_user_input(u16 *buf, int 
buf_size,
        tmp = calloc(1, buf_size * sizeof(u16));
        if (!tmp)
                return EFI_OUT_OF_RESOURCES;
 
+       /* Populate tmp so user can edit existing string */
+       u16_strcpy(tmp, buf);
        ret = efi_console_get_u16_string(cin, tmp, buf_size, NULL, 4, 
cursor_col);
        if (ret == EFI_SUCCESS)
                u16_strcpy(buf, tmp);
 
diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c
index 953f6831466e..7bfd18233feb 100644
--- a/lib/efi_loader/efi_console.c
+++ b/lib/efi_loader/efi_console.c
@@ -1383,36 +1383,61 @@ efi_status_t efi_console_get_u16_string(struct 
efi_simple_text_input_protocol *c
                                        efi_console_filter_func filter_func,
                                        int row, int col)
 {
        efi_status_t ret;
-       efi_uintn_t len = 0;
+       efi_uintn_t len;
+       efi_uintn_t cursor;
+       efi_uintn_t i;
        struct efi_input_key key;
 
        printf(ANSI_CURSOR_POSITION
               ANSI_CLEAR_LINE_TO_END
               ANSI_CURSOR_SHOW, row, col);
 
        efi_cin_empty_buffer();
 
+       len = u16_strlen(buf);
+       cursor = len;
        for (;;) {
+               printf(ANSI_CURSOR_POSITION "%ls"
+                      ANSI_CLEAR_LINE_TO_END ANSI_CURSOR_POSITION,
+                      row, col, buf, row, col + (int)cursor);
                do {
                        ret = EFI_CALL(cin->read_key_stroke(cin, &key));
                        mdelay(10);
                } while (ret == EFI_NOT_READY);
 
                if (key.unicode_char == u'\b') {
-                       if (len > 0)
-                               buf[--len] = u'\0';
-
-                       printf(ANSI_CURSOR_POSITION
-                              "%ls"
-                              ANSI_CLEAR_LINE_TO_END, row, col, buf);
+                       if (cursor > 0) {
+                               if (cursor == len) {
+                                       buf[--cursor] = u'\0';
+                               } else {
+                                       for (i = cursor - 1; i < len; i++)
+                                               buf[i] = buf[i + 1];
+                                       cursor--;
+                               }
+                               len--;
+                       }
+                       continue;
+               } else if (key.scan_code == 8) { /* delete */
+                       for (i = cursor; i <= len; i++)
+                               buf[i] = buf[i + 1];
+                       len--;
                        continue;
                } else if (key.unicode_char == u'\r') {
                        buf[len] = u'\0';
                        return EFI_SUCCESS;
                } else if (key.unicode_char == 0x3 || key.scan_code == 23) {
                        return EFI_ABORTED;
+               } else if (key.scan_code == 3) { /* Right arrow */
+                       cursor += (cursor < len) ? 1 : 0;
+                       continue;
+               } else if (key.scan_code == 4) { /* Left arrow */
+                       cursor -= (cursor > 0) ? 1 : 0;
+                       continue;
+               } else if (key.scan_code == 6) { /* End */
+                       cursor = len;
+                       continue;
                } else if (key.unicode_char < 0x20) {
                        /* ignore control codes other than Ctrl+C, '\r' and 
'\b' */
                        continue;
                } else if (key.scan_code != 0) {
@@ -1427,9 +1452,18 @@ efi_status_t efi_console_get_u16_string(struct 
efi_simple_text_input_protocol *c
 
                if (len >= (count - 1))
                        continue;
 
-               buf[len] = key.unicode_char;
+               /*
+                * Insert the character into the middle of the buffer, shift the
+                * characters after the cursor along. The check above ensures we
+                * will never overflow the buffer.
+                * If the cursor is at the end of the string then this will
+                * do nothing.
+                */
+               for (i = len + 1; i > cursor; i--)
+                       buf[i] = buf[i - 1];
+               buf[cursor] = key.unicode_char;
+               cursor++;
                len++;
-               printf(ANSI_CURSOR_POSITION "%ls", row, col, buf);
        }
 }

-- 
2.51.0

Reply via email to