:: commit 41e25c0709f85bb0f8ea69b9e07656b9e12153e6

Mintsuki <mintsuki@protonmail.com> — 2026-04-11 18:03

parents: 43f8d1cb85

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);
     }
 
tab: 248 wrap: offon