]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: split out line editor
authorLennart Poettering <lennart@poettering.net>
Tue, 25 Feb 2025 10:55:19 +0000 (11:55 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 25 Feb 2025 20:08:38 +0000 (21:08 +0100)
let's make this beast of boot.c a bit more digestable

src/boot/boot.c
src/boot/line-edit.c [new file with mode: 0644]
src/boot/line-edit.h [new file with mode: 0644]
src/boot/meson.build

index 6e55a659dd1ebfcb60ddb7340633955ec712e41b..7431f6d1d1627a487e16b6ba6994d4fe608ba84a 100644 (file)
@@ -11,6 +11,7 @@
 #include "export-vars.h"
 #include "graphics.h"
 #include "initrd.h"
+#include "line-edit.h"
 #include "linux.h"
 #include "measure.h"
 #include "memory-util-fundamental.h"
@@ -150,254 +151,6 @@ enum {
         IDX_INVALID,
 };
 
-static void cursor_left(size_t *cursor, size_t *first) {
-        assert(cursor);
-        assert(first);
-
-        if ((*cursor) > 0)
-                (*cursor)--;
-        else if ((*first) > 0)
-                (*first)--;
-}
-
-static void cursor_right(size_t *cursor, size_t *first, size_t x_max, size_t len) {
-        assert(cursor);
-        assert(first);
-
-        if ((*cursor)+1 < x_max)
-                (*cursor)++;
-        else if ((*first) + (*cursor) < len)
-                (*first)++;
-}
-
-static bool line_edit(char16_t **line_in, size_t x_max, size_t y_pos) {
-        _cleanup_free_ char16_t *line = NULL, *print = NULL;
-        size_t size, len, first = 0, cursor = 0, clear = 0;
-
-        /* Edit the line and return true if it should be executed, false if not. */
-
-        assert(line_in);
-
-        len = strlen16(*line_in);
-        size = len + 1024;
-        line = xnew(char16_t, size);
-        print = xnew(char16_t, x_max + 1);
-        strcpy16(line, strempty(*line_in));
-
-        for (;;) {
-                EFI_STATUS err;
-                uint64_t key;
-                size_t j, cursor_color = EFI_TEXT_ATTR_SWAP(COLOR_EDIT);
-
-                j = MIN(len - first, x_max);
-                memcpy(print, line + first, j * sizeof(char16_t));
-                while (clear > 0 && j < x_max) {
-                        clear--;
-                        print[j++] = ' ';
-                }
-                print[j] = '\0';
-
-                /* See comment at edit_line() call site for why we start at 1. */
-                print_at(1, y_pos, COLOR_EDIT, print);
-
-                if (!print[cursor])
-                        print[cursor] = ' ';
-                print[cursor+1] = '\0';
-                do {
-                        print_at(cursor + 1, y_pos, cursor_color, print + cursor);
-                        cursor_color = EFI_TEXT_ATTR_SWAP(cursor_color);
-
-                        err = console_key_read(&key, 750 * 1000);
-                        if (!IN_SET(err, EFI_SUCCESS, EFI_TIMEOUT, EFI_NOT_READY))
-                                return false;
-
-                        print_at(cursor + 1, y_pos, COLOR_EDIT, print + cursor);
-                } while (err != EFI_SUCCESS);
-
-                switch (key) {
-                case KEYPRESS(0, SCAN_ESC, 0):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
-                        return false;
-
-                case KEYPRESS(0, SCAN_HOME, 0):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
-                        /* beginning-of-line */
-                        cursor = 0;
-                        first = 0;
-                        continue;
-
-                case KEYPRESS(0, SCAN_END, 0):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
-                        /* end-of-line */
-                        cursor = len - first;
-                        if (cursor+1 >= x_max) {
-                                cursor = x_max-1;
-                                first = len - (x_max-1);
-                        }
-                        continue;
-
-                case KEYPRESS(0, SCAN_DOWN, 0):
-                case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
-                        /* forward-word */
-                        while (line[first + cursor] == ' ')
-                                cursor_right(&cursor, &first, x_max, len);
-                        while (line[first + cursor] && line[first + cursor] != ' ')
-                                cursor_right(&cursor, &first, x_max, len);
-                        continue;
-
-                case KEYPRESS(0, SCAN_UP, 0):
-                case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
-                        /* backward-word */
-                        if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
-                                cursor_left(&cursor, &first);
-                                while ((first + cursor) > 0 && line[first + cursor] == ' ')
-                                        cursor_left(&cursor, &first);
-                        }
-                        while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
-                                cursor_left(&cursor, &first);
-                        continue;
-
-                case KEYPRESS(0, SCAN_RIGHT, 0):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
-                        /* forward-char */
-                        if (first + cursor == len)
-                                continue;
-                        cursor_right(&cursor, &first, x_max, len);
-                        continue;
-
-                case KEYPRESS(0, SCAN_LEFT, 0):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
-                        /* backward-char */
-                        cursor_left(&cursor, &first);
-                        continue;
-
-                case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_DELETE, 0):
-                case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
-                        /* kill-word */
-                        clear = 0;
-
-                        size_t k;
-                        for (k = first + cursor; k < len && line[k] == ' '; k++)
-                                clear++;
-                        for (; k < len && line[k] != ' '; k++)
-                                clear++;
-
-                        for (size_t i = first + cursor; i + clear < len; i++)
-                                line[i] = line[i + clear];
-                        len -= clear;
-                        line[len] = '\0';
-                        continue;
-
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
-                case KEYPRESS(EFI_ALT_PRESSED, 0, '\b'):
-                        /* backward-kill-word */
-                        clear = 0;
-                        if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
-                                cursor_left(&cursor, &first);
-                                clear++;
-                                while ((first + cursor) > 0 && line[first + cursor] == ' ') {
-                                        cursor_left(&cursor, &first);
-                                        clear++;
-                                }
-                        }
-                        while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
-                                cursor_left(&cursor, &first);
-                                clear++;
-                        }
-
-                        for (size_t i = first + cursor; i + clear < len; i++)
-                                line[i] = line[i + clear];
-                        len -= clear;
-                        line[len] = '\0';
-                        continue;
-
-                case KEYPRESS(0, SCAN_DELETE, 0):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
-                        if (len == 0)
-                                continue;
-                        if (first + cursor == len)
-                                continue;
-                        for (size_t i = first + cursor; i < len; i++)
-                                line[i] = line[i+1];
-                        clear = 1;
-                        len--;
-                        continue;
-
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
-                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
-                        /* kill-line */
-                        line[first + cursor] = '\0';
-                        clear = len - (first + cursor);
-                        len = first + cursor;
-                        continue;
-
-                case KEYPRESS(0, 0, '\n'):
-                case KEYPRESS(0, 0, '\r'):
-                case KEYPRESS(0, SCAN_F3, 0): /* EZpad Mini 4s firmware sends malformed events */
-                case KEYPRESS(0, SCAN_F3, '\r'): /* Teclast X98+ II firmware sends malformed events */
-                        if (!streq16(line, *line_in)) {
-                                free(*line_in);
-                                *line_in = TAKE_PTR(line);
-                        }
-                        return true;
-
-                case KEYPRESS(0, 0, '\b'):
-                        if (len == 0)
-                                continue;
-                        if (first == 0 && cursor == 0)
-                                continue;
-                        for (size_t i = first + cursor-1; i < len; i++)
-                                line[i] = line[i+1];
-                        clear = 1;
-                        len--;
-                        if (cursor > 0)
-                                cursor--;
-                        if (cursor > 0 || first == 0)
-                                continue;
-                        /* show full line if it fits */
-                        if (len < x_max) {
-                                cursor = first;
-                                first = 0;
-                                continue;
-                        }
-                        /* jump left to see what we delete */
-                        if (first > 10) {
-                                first -= 10;
-                                cursor = 10;
-                        } else {
-                                cursor = first;
-                                first = 0;
-                        }
-                        continue;
-
-                case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
-                case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
-                        if (len+1 == size)
-                                continue;
-                        for (size_t i = len; i > first + cursor; i--)
-                                line[i] = line[i-1];
-                        line[first + cursor] = KEYCHAR(key);
-                        len++;
-                        line[len] = '\0';
-                        if (cursor+1 < x_max)
-                                cursor++;
-                        else if (first + cursor < len)
-                                first++;
-                        continue;
-                }
-        }
-}
 
 static size_t entry_lookup_key(Config *config, size_t start, char16_t key) {
         assert(config);
diff --git a/src/boot/line-edit.c b/src/boot/line-edit.c
new file mode 100644 (file)
index 0000000..ca9dc06
--- /dev/null
@@ -0,0 +1,254 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "console.h"
+#include "line-edit.h"
+#include "util.h"
+
+static void cursor_left(size_t *cursor, size_t *first) {
+        assert(cursor);
+        assert(first);
+
+        if ((*cursor) > 0)
+                (*cursor)--;
+        else if ((*first) > 0)
+                (*first)--;
+}
+
+static void cursor_right(size_t *cursor, size_t *first, size_t x_max, size_t len) {
+        assert(cursor);
+        assert(first);
+
+        if ((*cursor)+1 < x_max)
+                (*cursor)++;
+        else if ((*first) + (*cursor) < len)
+                (*first)++;
+}
+
+bool line_edit(char16_t **line_in, size_t x_max, size_t y_pos) {
+        _cleanup_free_ char16_t *line = NULL, *print = NULL;
+        size_t size, len, first = 0, cursor = 0, clear = 0;
+
+        /* Edit the line and return true if it should be executed, false if not. */
+
+        assert(line_in);
+
+        len = strlen16(*line_in);
+        size = len + 1024;
+        line = xnew(char16_t, size);
+        print = xnew(char16_t, x_max + 1);
+        strcpy16(line, strempty(*line_in));
+
+        for (;;) {
+                EFI_STATUS err;
+                uint64_t key;
+                size_t j, cursor_color = EFI_TEXT_ATTR_SWAP(COLOR_EDIT);
+
+                j = MIN(len - first, x_max);
+                memcpy(print, line + first, j * sizeof(char16_t));
+                while (clear > 0 && j < x_max) {
+                        clear--;
+                        print[j++] = ' ';
+                }
+                print[j] = '\0';
+
+                /* See comment at edit_line() call site for why we start at 1. */
+                print_at(1, y_pos, COLOR_EDIT, print);
+
+                if (!print[cursor])
+                        print[cursor] = ' ';
+                print[cursor+1] = '\0';
+                do {
+                        print_at(cursor + 1, y_pos, cursor_color, print + cursor);
+                        cursor_color = EFI_TEXT_ATTR_SWAP(cursor_color);
+
+                        err = console_key_read(&key, 750 * 1000);
+                        if (!IN_SET(err, EFI_SUCCESS, EFI_TIMEOUT, EFI_NOT_READY))
+                                return false;
+
+                        print_at(cursor + 1, y_pos, COLOR_EDIT, print + cursor);
+                } while (err != EFI_SUCCESS);
+
+                switch (key) {
+                case KEYPRESS(0, SCAN_ESC, 0):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'c'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')):
+                        return false;
+
+                case KEYPRESS(0, SCAN_HOME, 0):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('a')):
+                        /* beginning-of-line */
+                        cursor = 0;
+                        first = 0;
+                        continue;
+
+                case KEYPRESS(0, SCAN_END, 0):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'e'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('e')):
+                        /* end-of-line */
+                        cursor = len - first;
+                        if (cursor+1 >= x_max) {
+                                cursor = x_max-1;
+                                first = len - (x_max-1);
+                        }
+                        continue;
+
+                case KEYPRESS(0, SCAN_DOWN, 0):
+                case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0):
+                        /* forward-word */
+                        while (line[first + cursor] == ' ')
+                                cursor_right(&cursor, &first, x_max, len);
+                        while (line[first + cursor] && line[first + cursor] != ' ')
+                                cursor_right(&cursor, &first, x_max, len);
+                        continue;
+
+                case KEYPRESS(0, SCAN_UP, 0):
+                case KEYPRESS(EFI_ALT_PRESSED, 0, 'b'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_LEFT, 0):
+                        /* backward-word */
+                        if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
+                                cursor_left(&cursor, &first);
+                                while ((first + cursor) > 0 && line[first + cursor] == ' ')
+                                        cursor_left(&cursor, &first);
+                        }
+                        while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
+                                cursor_left(&cursor, &first);
+                        continue;
+
+                case KEYPRESS(0, SCAN_RIGHT, 0):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'f'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('f')):
+                        /* forward-char */
+                        if (first + cursor == len)
+                                continue;
+                        cursor_right(&cursor, &first, x_max, len);
+                        continue;
+
+                case KEYPRESS(0, SCAN_LEFT, 0):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'b'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
+                        /* backward-char */
+                        cursor_left(&cursor, &first);
+                        continue;
+
+                case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_DELETE, 0):
+                case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
+                        /* kill-word */
+                        clear = 0;
+
+                        size_t k;
+                        for (k = first + cursor; k < len && line[k] == ' '; k++)
+                                clear++;
+                        for (; k < len && line[k] != ' '; k++)
+                                clear++;
+
+                        for (size_t i = first + cursor; i + clear < len; i++)
+                                line[i] = line[i + clear];
+                        len -= clear;
+                        line[len] = '\0';
+                        continue;
+
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'w'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('w')):
+                case KEYPRESS(EFI_ALT_PRESSED, 0, '\b'):
+                        /* backward-kill-word */
+                        clear = 0;
+                        if ((first + cursor) > 0 && line[first + cursor-1] == ' ') {
+                                cursor_left(&cursor, &first);
+                                clear++;
+                                while ((first + cursor) > 0 && line[first + cursor] == ' ') {
+                                        cursor_left(&cursor, &first);
+                                        clear++;
+                                }
+                        }
+                        while ((first + cursor) > 0 && line[first + cursor-1] != ' ') {
+                                cursor_left(&cursor, &first);
+                                clear++;
+                        }
+
+                        for (size_t i = first + cursor; i + clear < len; i++)
+                                line[i] = line[i + clear];
+                        len -= clear;
+                        line[len] = '\0';
+                        continue;
+
+                case KEYPRESS(0, SCAN_DELETE, 0):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'd'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('d')):
+                        if (len == 0)
+                                continue;
+                        if (first + cursor == len)
+                                continue;
+                        for (size_t i = first + cursor; i < len; i++)
+                                line[i] = line[i+1];
+                        clear = 1;
+                        len--;
+                        continue;
+
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'k'):
+                case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('k')):
+                        /* kill-line */
+                        line[first + cursor] = '\0';
+                        clear = len - (first + cursor);
+                        len = first + cursor;
+                        continue;
+
+                case KEYPRESS(0, 0, '\n'):
+                case KEYPRESS(0, 0, '\r'):
+                case KEYPRESS(0, SCAN_F3, 0): /* EZpad Mini 4s firmware sends malformed events */
+                case KEYPRESS(0, SCAN_F3, '\r'): /* Teclast X98+ II firmware sends malformed events */
+                        if (!streq16(line, *line_in)) {
+                                free(*line_in);
+                                *line_in = TAKE_PTR(line);
+                        }
+                        return true;
+
+                case KEYPRESS(0, 0, '\b'):
+                        if (len == 0)
+                                continue;
+                        if (first == 0 && cursor == 0)
+                                continue;
+                        for (size_t i = first + cursor-1; i < len; i++)
+                                line[i] = line[i+1];
+                        clear = 1;
+                        len--;
+                        if (cursor > 0)
+                                cursor--;
+                        if (cursor > 0 || first == 0)
+                                continue;
+                        /* show full line if it fits */
+                        if (len < x_max) {
+                                cursor = first;
+                                first = 0;
+                                continue;
+                        }
+                        /* jump left to see what we delete */
+                        if (first > 10) {
+                                first -= 10;
+                                cursor = 10;
+                        } else {
+                                cursor = first;
+                                first = 0;
+                        }
+                        continue;
+
+                case KEYPRESS(0, 0, ' ') ... KEYPRESS(0, 0, '~'):
+                case KEYPRESS(0, 0, 0x80) ... KEYPRESS(0, 0, 0xffff):
+                        if (len+1 == size)
+                                continue;
+                        for (size_t i = len; i > first + cursor; i--)
+                                line[i] = line[i-1];
+                        line[first + cursor] = KEYCHAR(key);
+                        len++;
+                        line[len] = '\0';
+                        if (cursor+1 < x_max)
+                                cursor++;
+                        else if (first + cursor < len)
+                                first++;
+                        continue;
+                }
+        }
+}
diff --git a/src/boot/line-edit.h b/src/boot/line-edit.h
new file mode 100644 (file)
index 0000000..523108d
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "efi.h"
+
+bool line_edit(char16_t **line_in, size_t x_max, size_t y_pos);
index 69c8e40ad3ada070cee7ce65d33269b7e2b6f394..f88371b6b138b00b40d63fd0cf6d440e027a39a4 100644 (file)
@@ -307,6 +307,7 @@ libefi_sources = files(
 
 systemd_boot_sources = files(
         'boot.c',
+        'line-edit.c',
 )
 
 stub_sources = files(