lib/config, menu: Overhaul of the menu and remove entry name limits
diff --git a/common/lib/config.c b/common/lib/config.c
index 26bbf895..d0121d4d 100644
--- a/common/lib/config.c
+++ b/common/lib/config.c
@@ -17,7 +17,7 @@
const char *config_b2sum = CONFIG_B2SUM_SIGNATURE CONFIG_B2SUM_EMPTY;
-static bool config_get_entry_name(char *ret, size_t index, size_t limit);
+static char *config_get_entry_name(size_t index);
static char *config_get_entry(size_t *size, size_t index);
#define SEPARATOR '\n'
@@ -252,23 +252,30 @@ bool init_config_smbios(void) {
#define DIRECT_CHILD 0
#define INDIRECT_CHILD 1
-static int is_child(char *buf, size_t limit,
- size_t current_depth, size_t index) {
- if (!config_get_entry_name(buf, index, limit))
+static int is_child(size_t current_depth, size_t index) {
+ char *buf = config_get_entry_name(index);
+ if (buf == NULL)
return NOT_CHILD;
- if (strlen(buf) < current_depth + 1)
- return NOT_CHILD;
- for (size_t j = 0; j < current_depth; j++)
- if (buf[j] != '/')
- return NOT_CHILD;
- if (buf[current_depth] == '/')
- return INDIRECT_CHILD;
- return DIRECT_CHILD;
+ int ret;
+ if (strlen(buf) < current_depth + 1) {
+ ret = NOT_CHILD;
+ } else {
+ ret = DIRECT_CHILD;
+ for (size_t j = 0; j < current_depth; j++) {
+ if (buf[j] != '/') {
+ ret = NOT_CHILD;
+ break;
+ }
+ }
+ if (ret == DIRECT_CHILD && buf[current_depth] == '/')
+ ret = INDIRECT_CHILD;
+ }
+ pmm_free(buf, strlen(buf) + 1);
+ return ret;
}
-static bool is_directory(char *buf, size_t limit,
- size_t current_depth, size_t index) {
- switch (is_child(buf, limit, current_depth + 1, index + 1)) {
+static bool is_directory(size_t current_depth, size_t index) {
+ switch (is_child(current_depth + 1, index + 1)) {
default:
case NOT_CHILD:
return false;
@@ -285,9 +292,7 @@ static struct menu_entry *create_menu_tree(struct menu_entry *parent,
struct menu_entry *root = NULL, *prev = NULL;
for (size_t i = index; ; i++) {
- static char name[64];
-
- switch (is_child(name, 64, current_depth, i)) {
+ switch (is_child(current_depth, i)) {
case NOT_CHILD:
return root;
case INDIRECT_CHILD:
@@ -301,7 +306,7 @@ static struct menu_entry *create_menu_tree(struct menu_entry *parent,
if (root == NULL)
root = entry;
- config_get_entry_name(name, i, 64);
+ char *name = config_get_entry_name(i);
bool default_expanded = name[current_depth] == '+';
@@ -310,12 +315,8 @@ static struct menu_entry *create_menu_tree(struct menu_entry *parent,
n++;
}
- size_t n_len = strlen(n);
- if (n_len >= sizeof(entry->name)) {
- n_len = sizeof(entry->name) - 1;
- }
- memcpy(entry->name, n, n_len);
- entry->name[n_len] = 0;
+ entry->name = strdup(n);
+ pmm_free(name, strlen(name) + 1);
entry->parent = parent;
size_t entry_size;
@@ -324,7 +325,7 @@ static struct menu_entry *create_menu_tree(struct menu_entry *parent,
memcpy(entry->body, config_entry, entry_size);
entry->body[entry_size] = 0;
- if (is_directory(name, 64, current_depth, i)) {
+ if (is_directory(current_depth, i)) {
entry->sub = create_menu_tree(entry, current_depth + 1, i + 1);
entry->expanded = default_expanded;
}
@@ -604,16 +605,16 @@ overflow:
return 0;
}
-static bool config_get_entry_name(char *ret, size_t index, size_t limit) {
+static char *config_get_entry_name(size_t index) {
if (!config_ready)
- return false;
+ return NULL;
char *p = config_addr;
for (size_t i = 0; i <= index; i++) {
while (*p != '/') {
if (!*p)
- return false;
+ return NULL;
p++;
}
p++;
@@ -623,15 +624,15 @@ static bool config_get_entry_name(char *ret, size_t index, size_t limit) {
p--;
- size_t i;
- for (i = 0; i < (limit - 1); i++) {
- if (p[i] == SEPARATOR)
- break;
- ret[i] = p[i];
+ size_t len = 0;
+ while (p[len] != SEPARATOR) {
+ len++;
}
- ret[i] = 0;
- return true;
+ char *ret = ext_mem_alloc(len + 1);
+ memcpy(ret, p, len);
+ ret[len] = 0;
+ return ret;
}
static char *config_get_entry(size_t *size, size_t index) {
diff --git a/common/lib/config.h b/common/lib/config.h
index 00c8094c..ff2a8fdd 100644
--- a/common/lib/config.h
+++ b/common/lib/config.h
@@ -9,7 +9,7 @@ extern bool config_ready;
extern bool bad_config;
struct menu_entry {
- char name[64];
+ char *name;
char *comment;
struct menu_entry *parent;
struct menu_entry *sub;
diff --git a/common/menu.c b/common/menu.c
index 24036220..48cdbb3d 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -45,7 +45,7 @@ static char *menu_branding_colour = NULL;
no_unwind bool booting_from_editor = false;
static no_unwind bool booting_from_blank = false;
static no_unwind char saved_orig_entry[EDITOR_MAX_BUFFER_SIZE];
-static no_unwind char saved_title[64];
+static no_unwind char saved_title[256];
static size_t get_line_offset(size_t *displacement, size_t index, const char *buffer) {
size_t offset = 0;
@@ -128,7 +128,6 @@ static const char *VALID_KEYS[] = {
};
static bool validation_enabled = true;
-static bool invalid_syntax = false;
static int validate_line(const char *buffer) {
if (!validation_enabled) return TOK_KEY;
@@ -143,7 +142,6 @@ static int validate_line(const char *buffer) {
fail:
if (i < 64) keybuf[i] = 0;
if (keybuf[0] == '\n' || (!keybuf[0] && buffer[0] != ':')) return TOK_KEY; // blank line is valid
- invalid_syntax = true;
return TOK_BADKEY;
found_equals:
if (i < 64) keybuf[i] = 0;
@@ -196,12 +194,10 @@ char *config_entry_editor(const char *title, const char *orig_entry) {
size_t cursor_offset = 0;
size_t entry_size = strlen(orig_entry);
- size_t _window_size = terms[0]->rows - 8;
+ size_t _window_size = terms[0]->rows - 7 + (menu_branding[0] == '\0' ? 2 : 0);
size_t window_offset = 0;
size_t line_size = terms[0]->cols - 2;
- bool display_overflow_error = false;
-
// Skip leading newlines
while (*orig_entry == '\n') {
orig_entry++;
@@ -231,17 +227,17 @@ char *config_entry_editor(const char *title, const char *orig_entry) {
buffer[entry_size] = 0;
refresh:
- invalid_syntax = false;
-
print("\e[2J\e[H");
FOR_TERM(TERM->cursor_enabled = false);
{
size_t x, y;
print("\n");
- terms[0]->get_cursor_pos(terms[0], &x, &y);
- set_cursor_pos_helper(terms[0]->cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2, panic(false, "Alignment overflow")), y);
- print("\e[3%sm%s\e[0m", menu_branding_colour, menu_branding);
- print("\n\n");
+ if (menu_branding[0] != '\0') {
+ terms[0]->get_cursor_pos(terms[0], &x, &y);
+ set_cursor_pos_helper(terms[0]->cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2, panic(false, "Alignment overflow")), y);
+ print("\e[3%sm%s\e[0m", menu_branding_colour, menu_branding);
+ print("\n\n");
+ }
}
print(" %sESC\e[0m Discard and Exit %sF10\e[0m Boot\n\n", interface_help_colour, interface_help_colour);
@@ -257,9 +253,20 @@ refresh:
// FALLTHRU
default: {
size_t title_length = strlen(title);
- if (i == (terms[0]->cols / 2) - DIV_ROUNDUP(title_length, 2, panic(false, "Alignment overflow")) - 1 - 1) {
- print(serial ? "|%s|" : "â¤%sâ", title);
- i += (title_length + 2) - 1;
+ size_t max_title = terms[0]->cols - 4;
+ size_t display_length = title_length;
+ bool truncated = false;
+ if (display_length > max_title && max_title > 3) {
+ display_length = max_title;
+ truncated = true;
+ }
+ if (i == (terms[0]->cols / 2) - DIV_ROUNDUP(display_length, 2, panic(false, "Alignment overflow")) - 1 - 1) {
+ if (truncated) {
+ print(serial ? "|%S...|" : "â¤%S...â", title, (size_t)(display_length - 3));
+ } else {
+ print(serial ? "|%s|" : "â¤%sâ", title);
+ }
+ i += (display_length + 2) - 1;
} else {
print(serial ? "-" : "â");
}
@@ -337,7 +344,7 @@ tab_part:
printed_early = true;
size_t x, y;
terms[0]->get_cursor_pos(terms[0], &x, &y);
- if (y == terms[0]->rows - 3) {
+ if (y >= terms[0]->rows - 2) {
print(serial ? ">" : "â");
set_cursor_pos_helper(0, y + 1);
print(serial ? "\\" : "â");
@@ -409,21 +416,6 @@ tab_part:
}
}
- // syntax error alert
- if (validation_enabled) {
- size_t x, y;
- terms[0]->get_cursor_pos(terms[0], &x, &y);
- set_cursor_pos_helper(0, terms[0]->rows - 1);
- FOR_TERM(TERM->scroll_enabled = false);
- if (invalid_syntax) {
- print("\e[31mConfiguration is INVALID.\e[0m");
- } else {
- print("\e[32mConfiguration is valid.\e[0m");
- }
- FOR_TERM(TERM->scroll_enabled = true);
- set_cursor_pos_helper(x, y);
- }
-
if (current_line - window_offset < window_size) {
size_t x, y;
for (size_t i = 0; i < (window_size - (current_line - window_offset)) - 1; i++) {
@@ -440,28 +432,33 @@ tab_part:
print(serial ? "\\" : "â");
}
- for (size_t i = 0; i < terms[0]->cols - 2; i++) {
- switch (i) {
- case 1: case 2: case 3:
- if (current_line - window_offset >= window_size) {
- print(serial ? "v" : "â");
- break;
- }
- // FALLTHRU
- default:
- print(serial ? "-" : "â");
+ {
+ const char *overflow_msg = (strlen(buffer) >= EDITOR_MAX_BUFFER_SIZE - 1) ? "Buffer full" : NULL;
+ size_t overflow_len = overflow_msg ? strlen(overflow_msg) : 0;
+
+ for (size_t i = 0; i < terms[0]->cols - 2; i++) {
+ switch (i) {
+ case 1: case 2: case 3:
+ if (current_line - window_offset >= window_size) {
+ print(serial ? "v" : "â");
+ break;
+ }
+ // FALLTHRU
+ default:
+ if (overflow_msg != NULL
+ && i == (terms[0]->cols / 2) - DIV_ROUNDUP(overflow_len, 2, panic(false, "Alignment overflow")) - 1 - 1) {
+ print(serial ? "|" : "â¤");
+ print("\e[31m%s\e[0m", overflow_msg);
+ print(serial ? "|" : "â");
+ i += (overflow_len + 2) - 1;
+ } else {
+ print(serial ? "-" : "â");
+ }
+ }
}
}
terms[0]->get_cursor_pos(terms[0], &tmpx, &tmpy);
print(serial ? "/" : "â");
- set_cursor_pos_helper(0, tmpy + 1);
-
- if (display_overflow_error) {
- FOR_TERM(TERM->scroll_enabled = false);
- print("\e[31mText buffer not big enough, delete something instead.");
- FOR_TERM(TERM->scroll_enabled = true);
- display_overflow_error = false;
- }
// Hack to redraw the cursor
set_cursor_pos_helper(cursor_x, cursor_y);
@@ -514,10 +511,13 @@ tab_part:
saved_orig_entry[buffer_len] = 0;
size_t title_len = strlen(title);
if (title_len >= sizeof(saved_title)) {
- title_len = sizeof(saved_title) - 1;
+ title_len = sizeof(saved_title) - 4;
+ memcpy(saved_title, title, title_len);
+ memcpy(saved_title + title_len, "...", 4);
+ } else {
+ memcpy(saved_title, title, title_len);
+ saved_title[title_len] = 0;
}
- memcpy(saved_title, title, title_len);
- saved_title[title_len] = 0;
editor_no_term_reset ? editor_no_term_reset = false : reset_term();
booting_from_editor = true;
return buffer;
@@ -536,8 +536,6 @@ tab_part:
}
buffer[cursor_offset++] = c;
}
- } else {
- display_overflow_error = true;
}
break;
}
@@ -665,19 +663,41 @@ static void get_entry_path(struct menu_entry *entry, char *buf, size_t buf_size,
// Parse one component from an escaped path string.
// Writes the unescaped name into name_buf and the duplicate index into *dup_index.
// Returns a pointer to the remainder of the path (past the separator).
-static const char *parse_path_component(const char *path, char *name_buf, size_t name_buf_size, size_t *dup_index) {
+static const char *parse_path_component(const char *path, char **name_out, size_t *dup_index) {
*dup_index = 0;
- if (name_buf_size == 0) {
- return path;
+ const char *p = path;
+
+ // First pass: measure the component length
+ size_t len = 0;
+ const char *scan = path;
+ while (*scan != '\0' && *scan != '/') {
+ if (*scan == '\\' && scan[1] != '\0') {
+ len++;
+ scan += 2;
+ continue;
+ }
+ if (*scan == '#') {
+ const char *q = scan + 1;
+ if (*q >= '0' && *q <= '9') {
+ while (*q >= '0' && *q <= '9') {
+ q++;
+ }
+ if (*q == '\0' || *q == '/') {
+ break;
+ }
+ }
+ }
+ len++;
+ scan++;
}
+
+ // Allocate and fill
+ char *name_buf = ext_mem_alloc(len + 1);
size_t j = 0;
- const char *p = path;
while (*p != '\0' && *p != '/') {
if (*p == '\\' && p[1] != '\0') {
- if (j < name_buf_size - 1) {
- name_buf[j++] = p[1];
- }
+ name_buf[j++] = p[1];
p += 2;
continue;
}
@@ -695,13 +715,12 @@ static const char *parse_path_component(const char *path, char *name_buf, size_t
}
}
}
- if (j < name_buf_size - 1) {
- name_buf[j++] = *p;
- }
+ name_buf[j++] = *p;
p++;
}
name_buf[j] = '\0';
+ *name_out = name_buf;
if (*p == '/') {
p++;
@@ -715,13 +734,14 @@ static const char *parse_path_component(const char *path, char *name_buf, size_t
static bool find_entry_by_path(const char *path, struct menu_entry *current_entry,
size_t base_index, struct menu_entry **found_entry,
size_t *found_index, bool expand_dirs) {
- char comp_name[64];
+ char *comp_name;
size_t dup_index = 0;
- const char *rest = parse_path_component(path, comp_name, sizeof(comp_name), &dup_index);
+ const char *rest = parse_path_component(path, &comp_name, &dup_index);
bool is_last = (*rest == '\0');
size_t idx = base_index;
size_t same_name_count = 0;
+ bool ret = false;
while (current_entry != NULL) {
if (should_skip_entry(current_entry)) {
@@ -737,13 +757,15 @@ static bool find_entry_by_path(const char *path, struct menu_entry *current_entr
if (found_index != NULL) {
*found_index = idx;
}
- return true;
+ ret = true;
+ break;
} else if (!is_last && current_entry->sub != NULL) {
if (expand_dirs) {
current_entry->expanded = true;
}
- return find_entry_by_path(rest, current_entry->sub,
- idx + 1, found_entry, found_index, expand_dirs);
+ ret = find_entry_by_path(rest, current_entry->sub,
+ idx + 1, found_entry, found_index, expand_dirs);
+ break;
}
}
@@ -759,7 +781,8 @@ static bool find_entry_by_path(const char *path, struct menu_entry *current_entr
current_entry = current_entry->next;
}
- return false;
+ pmm_free(comp_name, strlen(comp_name) + 1);
+ return ret;
}
static size_t print_tree(size_t offset, size_t window, const char *shift, size_t level, size_t base_index, size_t selected_entry,
@@ -831,9 +854,21 @@ static size_t print_tree(size_t offset, size_t window, const char *shift, size_t
*selected_menu_entry = current_entry;
if (!no_print) print("\e[7m");
}
- if (!no_print) print(" %s \e[27m\n", current_entry->name);
- (*max_height)++;
- cur_len += 1 + strlen(current_entry->name) + 1;
+ {
+ size_t name_len = strlen(current_entry->name);
+ if (!no_print) {
+ size_t prefix_len = shift ? strlen(shift) : 0;
+ size_t used = prefix_len + cur_len + 1 + 1; // shift + decorations + space before + space after
+ size_t max_name = (terms[0]->cols > used) ? terms[0]->cols - used : 0;
+ if (name_len > max_name && max_name > 3) {
+ print(" %S...\e[27m\n", current_entry->name, (size_t)(max_name - 3));
+ } else {
+ print(" %s \e[27m\n", current_entry->name);
+ }
+ }
+ (*max_height)++;
+ cur_len += 1 + name_len + 1;
+ }
skip_line:
if (current_entry->sub && current_entry->expanded) {
max_entries += print_tree(offset, window, shift, level + 1, base_index + max_entries + 1,
@@ -1235,10 +1270,11 @@ noreturn void _menu(bool first_run) {
}
size_t tree_offset = 0;
+ size_t branding_offset = (menu_branding[0] != '\0') ? 2 : 0;
refresh:
- if (selected_entry >= tree_offset + terms[0]->rows - 8) {
- tree_offset = selected_entry - (terms[0]->rows - 9);
+ if (selected_entry >= tree_offset + terms[0]->rows - 7 - branding_offset) {
+ tree_offset = selected_entry - (terms[0]->rows - 8 - branding_offset);
}
if (selected_entry < tree_offset) {
tree_offset = selected_entry;
@@ -1252,10 +1288,12 @@ refresh:
{
size_t x, y;
print("\n");
- terms[0]->get_cursor_pos(terms[0], &x, &y);
- set_cursor_pos_helper(terms[0]->cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2, panic(false, "Alignment overflow")), y);
- print("\e[3%sm%s\e[0m", menu_branding_colour, menu_branding);
- print("\n\n\n\n");
+ if (menu_branding[0] != '\0') {
+ terms[0]->get_cursor_pos(terms[0], &x, &y);
+ set_cursor_pos_helper(terms[0]->cols / 2 - DIV_ROUNDUP(strlen(menu_branding), 2, panic(false, "Alignment overflow")), y);
+ print("\e[3%sm%s\e[0m", menu_branding_colour, menu_branding);
+ print("\n\n\n\n");
+ }
}
if (max_entries == 0) {
@@ -1274,23 +1312,23 @@ refresh:
}
size_t max_tree_len, max_tree_height;
- max_entries = print_tree(tree_offset, terms[0]->rows - 8, NULL, 0, 0, selected_entry, menu_tree,
+ max_entries = print_tree(tree_offset, terms[0]->rows - 7 - branding_offset, NULL, 0, 0, selected_entry, menu_tree,
&selected_menu_entry, &max_tree_len, &max_tree_height);
if (max_entries != 0) {
size_t half_cols = terms[0]->cols / 2;
size_t half_tree = DIV_ROUNDUP(max_tree_len, 2, panic(false, "Alignment overflow"));
- size_t tree_prefix_len = (half_cols > half_tree + 2) ? (half_cols - half_tree - 2) : 0;
+ size_t tree_prefix_len = (half_cols > half_tree + 2) ? (half_cols - half_tree - 2) : 1;
char *tree_prefix = ext_mem_alloc(tree_prefix_len + 1);
memset(tree_prefix, ' ', tree_prefix_len);
- if (max_tree_height > terms[0]->rows - 10) {
- max_tree_height = terms[0]->rows - 10;
+ if (max_tree_height > terms[0]->rows - 9 - branding_offset) {
+ max_tree_height = terms[0]->rows - 9 - branding_offset;
}
set_cursor_pos_helper(0, terms[0]->rows / 2 - max_tree_height / 2);
- max_entries = print_tree(tree_offset, terms[0]->rows - 8, tree_prefix, 0, 0, selected_entry, menu_tree,
+ max_entries = print_tree(tree_offset, terms[0]->rows - 7 - branding_offset, tree_prefix, 0, 0, selected_entry, menu_tree,
&selected_menu_entry, NULL, NULL);
pmm_free(tree_prefix, tree_prefix_len + 1);
@@ -1302,18 +1340,18 @@ refresh:
if (max_entries != 0) {
if (tree_offset > 0) {
- set_cursor_pos_helper(terms[0]->cols / 2 - 1, 4);
+ set_cursor_pos_helper(terms[0]->cols / 2 - 1, 2 + branding_offset);
print(serial ? "^^^" : "âââ");
}
- if (tree_offset + (terms[0]->rows - 8) < max_entries) {
+ if (tree_offset + (terms[0]->rows - 7 - branding_offset) < max_entries) {
set_cursor_pos_helper(terms[0]->cols / 2 - 1, terms[0]->rows - 3);
print(serial ? "vvv" : "âââ");
}
}
if (!help_hidden) {
- set_cursor_pos_helper(0, 3);
+ set_cursor_pos_helper(0, 1 + branding_offset);
if (max_entries != 0) {
if (selected_menu_entry->sub == NULL) {
print(" %sARROWS\e[0m Select %sENTER\e[0m Boot %s%s",
@@ -1327,12 +1365,12 @@ refresh:
}
#if defined(UEFI)
if (reboot_to_firmware_supported) {
- set_cursor_pos_helper(terms[0]->cols - (editor_enabled ? 37 : 20), 3);
+ set_cursor_pos_helper(terms[0]->cols - (editor_enabled ? 37 : 20), 1 + branding_offset);
print("%sS\e[0m Firmware Setup", interface_help_colour);
}
#endif
if (editor_enabled) {
- set_cursor_pos_helper(terms[0]->cols - 17, 3);
+ set_cursor_pos_helper(terms[0]->cols - 17, 1 + branding_offset);
print("%sB\e[0m Blank Entry", interface_help_colour);
}
}
@@ -1347,7 +1385,10 @@ refresh:
if (skip_timeout == false) {
print("\n\n");
for (size_t i = timeout; i; i--) {
- set_cursor_pos_helper(0, terms[0]->rows - 1);
+ size_t ndigits = 1;
+ for (size_t tmp = i / 10; tmp > 0; tmp /= 10) ndigits++;
+ size_t msg_len = 65 + ndigits;
+ set_cursor_pos_helper((terms[0]->cols - msg_len) / 2, terms[0]->rows - 2);
FOR_TERM(TERM->scroll_enabled = false);
print("\e[2K%sBooting automatically in %s%U%s, press any key to stop the countdown...\e[0m",
interface_help_colour, interface_help_colour_bright, (uint64_t)i, interface_help_colour);
@@ -1368,10 +1409,17 @@ refresh:
goto autoboot;
}
- set_cursor_pos_helper(0, terms[0]->rows - 1);
if (max_entries != 0 && selected_menu_entry->comment != NULL) {
+ size_t comment_len = strlen(selected_menu_entry->comment);
+ size_t max_len = terms[0]->cols - 2;
FOR_TERM(TERM->scroll_enabled = false);
- print("\e[36m%s\e[0m", selected_menu_entry->comment);
+ if (comment_len <= max_len) {
+ set_cursor_pos_helper((terms[0]->cols - comment_len) / 2, terms[0]->rows - 2);
+ print("\e[36m%s\e[0m", selected_menu_entry->comment);
+ } else {
+ set_cursor_pos_helper(1, terms[0]->rows - 2);
+ print("\e[36m%S...\e[0m", selected_menu_entry->comment, (size_t)(max_len - 3));
+ }
FOR_TERM(TERM->scroll_enabled = true);
}
