mirror of
https://github.com/systemd/systemd.git
synced 2025-03-19 22:50:17 +03:00
boot: split out line editor
let's make this beast of boot.c a bit more digestable
This commit is contained in:
parent
33ab1b5b7e
commit
95086da39f
249
src/boot/boot.c
249
src/boot/boot.c
@ -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);
|
||||
|
254
src/boot/line-edit.c
Normal file
254
src/boot/line-edit.c
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
6
src/boot/line-edit.h
Normal file
6
src/boot/line-edit.h
Normal file
@ -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);
|
@ -307,6 +307,7 @@ libefi_sources = files(
|
||||
|
||||
systemd_boot_sources = files(
|
||||
'boot.c',
|
||||
'line-edit.c',
|
||||
)
|
||||
|
||||
stub_sources = files(
|
||||
|
Loading…
x
Reference in New Issue
Block a user