:: commit 34825df41b40d41b4df831da7a9e9ca1b6fb12e1

Carver <ch@dorper.me> — 2021-07-06 23:27

parents: ae2d924c14

New menu system and editor

diff --git a/CONFIG.md b/CONFIG.md
index ea715ba4..059afa83 100644
--- a/CONFIG.md
+++ b/CONFIG.md
@@ -67,6 +67,9 @@ Some keys take *URIs* as values; these are described in the next section.
 * `BACKGROUND_STYLE` - The style which will be used to display the background image. Either `tiled` or `centered`. Default is `tiled`.
 * `BACKDROP_COLOUR` - When the background style is `centered`, this specifies the colour of the backdrop for parts of the screen not covered by the background image, in RRGGBB format.
 * `BACKDROP_COLOR` - Alias of `BACKDROP_COLOUR`.
+* `EDITOR_ENABLED` - If set to `no`, the editor will not be accessible. Defaults to `yes`.
+* `EDITOR_HIGHLIGHTING` - If set to `no`, syntax highlighting in the editor will be disabled. Defaults to `yes`.
+* `EDITOR_VALIDATION` - If set to `no`, the editor will not alert you about invalid keys / syntax errors. Defaults to `yes`.
 * `VERBOSE` - If set to `yes`, print additional information during boot. Defaults to not verbose.
 
 *Locally assignable (non protocol specific)* keys are:
diff --git a/stage23/menu.c b/stage23/menu.c
index 0c74c8a3..84d14720 100644
--- a/stage23/menu.c
+++ b/stage23/menu.c
@@ -16,6 +16,10 @@
 static char *menu_branding = NULL;
 
 #define EDITOR_MAX_BUFFER_SIZE 4096
+#define TOK_KEY 0
+#define TOK_EQUALS 1
+#define TOK_VALUE 2
+#define TOK_BADKEY 3
 
 static size_t get_line_offset(size_t *displacement, size_t index, const char *buffer) {
     size_t offset = 0;
@@ -67,7 +71,70 @@ static size_t get_prev_line(size_t index, const char *buffer) {
     return offset;
 }
 
-static char *config_entry_editor(const char *orig_entry) {
+static const char *VALID_KEYS[] = {
+    "TIMEOUT",
+    "DEFAULT_ENTRY",
+    "GRAPHICS",
+    "MENU_RESOLUTION",
+    "MENU_BRANDING",
+    "MENU_FONT",
+    "TERMINAL_FONT",
+    "THEME_COLOURS",
+    "THEME_COLORS",
+    "THEME_BACKGROUND",
+    "THEME_FOREGROUND",
+    "THEME_MARGIN",
+    "THEME_MARGIN_GRADIENT",
+    "BACKGROUND_PATH",
+    "BACKGROUND_STYLE",
+    "BACKDROP_COLOUR",
+    "BACKDROP_COLOR",
+    "EDITOR_ENABLED",
+    "EDITOR_HIGHLIGHTING",
+    "EDITOR_VALIDATION",
+    "VERBOSE",
+    "PROTOCOL",
+    "CMDLINE",
+    "KERNEL_CMDLINE",
+    "KERNEL_PATH",
+    "MODULE_PATH",
+    "MODULE_STRING",
+    "RESOLUTION",
+    "KASLR",
+    "DRIVE",
+    "PARTITION",
+    "IMAGE_PATH",
+    NULL
+};
+
+static bool validation_enabled = true;
+static bool invalid_syntax = false;
+
+static int validate_line(const char *buffer) {
+    if (!validation_enabled) return TOK_KEY;
+    char keybuf[64];
+    int i;
+    for (i = 0; buffer[i] && i < 64; i++) {
+        if (buffer[i] == '=') goto found_equals;
+        keybuf[i] = buffer[i];
+    }
+fail:
+    if (i < 64) keybuf[i] = 0;
+    if (keybuf[0] == '\n' || (!keybuf[0] && buffer[0] != '=')) return TOK_KEY; // blank line is valid
+    invalid_syntax = 1;
+    return TOK_BADKEY;
+found_equals:
+    if (i < 64) keybuf[i] = 0;
+    for (i = 0; VALID_KEYS[i]; i++) {
+        //print("'%s'", keybuf);
+        if (!strcmp(keybuf, VALID_KEYS[i])) {
+            return TOK_KEY;
+        }
+    }
+    goto fail;
+}
+
+static 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   = term_rows - 11;
@@ -82,9 +149,18 @@ static char *config_entry_editor(const char *orig_entry) {
         entry_size--;
     }
 
-    if (entry_size >= EDITOR_MAX_BUFFER_SIZE)
+    if (entry_size >= EDITOR_MAX_BUFFER_SIZE) {
         panic("Entry is too big to be edited.");
+    }
 
+    bool syntax_highlighting_enabled = true;
+    char *syntax_highlighting_enabled_config = config_get_value(NULL, 0, "EDITOR_HIGHLIGHTING");
+    if (!strcmp(syntax_highlighting_enabled_config, "no")) syntax_highlighting_enabled = false;
+    
+    validation_enabled = true;
+    char *validation_enabled_config = config_get_value(NULL, 0, "EDITOR_VALIDATION");
+    if (!strcmp(validation_enabled_config, "no")) validation_enabled = false;
+    
     static char *buffer = NULL;
     if (buffer == NULL)
         buffer = ext_mem_alloc(EDITOR_MAX_BUFFER_SIZE);
@@ -92,12 +168,15 @@ static char *config_entry_editor(const char *orig_entry) {
     buffer[entry_size] = 0;
 
 refresh:
+    invalid_syntax = 0;
+    
     clear(true);
     disable_cursor();
     print("\n\n  \e[36m %s \e[37m\n\n\n", menu_branding);
 
-    print("Editing entry.\n");
-    print("Press esc to return to main menu and discard changes, press F10 to boot.\n");
+    //print("Editing \"%s\"\n", title);
+    print("   \e[32mESC\e[0m Discard and Exit    \e[32mF10\e[0m Boot\n");
+
 
     print("\n\xda");
     for (int i = 0; i < term_cols - 2; i++) {
@@ -109,7 +188,12 @@ refresh:
                 }
                 // FALLTHRU
             default:
-                print("\xc4");
+                if (i > term_cols / 2 - (int)strlen(title) && i < term_cols / 2) {
+                    print(title);
+                    i += strlen(title) - 1;
+                } else {
+                    print("\xc4");
+                }
         }
     }
     print("\xbf\xb3");
@@ -117,7 +201,9 @@ refresh:
     int cursor_x, cursor_y;
     size_t current_line = 0, line_offset = 0, window_size = _window_size;
     bool printed_cursor = false;
+    int token_type = validate_line(buffer);
     for (size_t i = 0; ; i++) {
+        // newline
         if (buffer[i] == '\n'
          && current_line <  window_offset + window_size
          && current_line >= window_offset) {
@@ -134,6 +220,7 @@ refresh:
             else
                 print("\xb3\xb3");
             line_offset = 0;
+            token_type = validate_line(buffer+i+1);
             current_line++;
             continue;
         }
@@ -175,9 +262,47 @@ refresh:
 
         if (current_line >= window_offset) {
             line_offset++;
-            print("%c", buffer[i]);
+            
+            // switch to token type 1 if equals sign
+            if (token_type == TOK_KEY && buffer[i] == '=') token_type = TOK_EQUALS;
+            
+            // syntax highlighting
+            if (syntax_highlighting_enabled) {
+                switch (token_type) {
+                case TOK_KEY:
+                    print("\e[36m%c\e[0m", buffer[i]);
+                    break;
+                case TOK_EQUALS:
+                    print("\e[32m%c\e[0m", buffer[i]);
+                    break;
+                   case TOK_VALUE:
+                       print("\e[39m%c\e[0m", buffer[i]);
+                       break;
+                   case TOK_BADKEY:
+                       print("\e[31m%c\e[0m", buffer[i]);
+                       break;
+                   }
+               } else {
+                   print("%c", buffer[i]);
+               }
+               
+               // switch to token type 2 after equals sign
+               if (token_type == TOK_EQUALS) token_type = TOK_VALUE;
         }
     }
+    
+    // syntax error alert
+    if (validation_enabled) {
+        int x, y;
+        get_cursor_pos(&x, &y);
+        set_cursor_pos(0, term_rows-2);
+        if (invalid_syntax) {
+            print("\e[31mConfiguration is INVALID.\e[0m");
+        } else {
+            print("\e[32mConfiguration is valid.\e[0m");
+        }
+        set_cursor_pos(x, y);
+    }
 
     if (current_line - window_offset < window_size) {
         int x, y;
@@ -348,6 +473,10 @@ char *menu(char **cmdline) {
         else
             timeout = strtoui(timeout_config, NULL, 10);
     }
+    
+    bool editor_enabled = true;
+    char *editor_enabled_config = config_get_value(NULL, 0, "EDITOR_ENABLED");
+    if (!strcmp(editor_enabled_config, "no")) editor_enabled = false;
 
     if (!timeout) {
         // Use print tree to load up selected_menu_entry and determine if the
@@ -394,7 +523,7 @@ refresh:
         getchar();
         char *new_body = NULL;
         while (new_body == NULL)
-            new_body = config_entry_editor("");
+            new_body = config_entry_editor("New Entry", "");
         selected_menu_entry = ext_mem_alloc(sizeof(struct menu_entry));
         selected_menu_entry->body = new_body;
         config_ready = true;
@@ -406,7 +535,17 @@ refresh:
     int max_entries = print_tree(0, 0, selected_entry, menu_tree,
                                  &selected_menu_entry);
 
-    print("\nArrows to select, enter to boot, 'e' to edit selected entry.");
+    {
+        int x, y;
+        get_cursor_pos(&x, &y);
+        set_cursor_pos(0, 4);
+        if (editor_enabled && selected_menu_entry->sub == NULL) {
+            print("\n   \e[32mARROWS\e[0m Select    \e[32mENTER\e[0m Boot    \e[32mE\e[0m Edit");
+        } else {
+            print("\n   \e[32mARROWS\e[0m Select    \e[32mENTER\e[0m Boot");
+        }
+        set_cursor_pos(x, y);
+    }
 
     if (selected_menu_entry->sub != NULL)
         skip_timeout = true;
@@ -461,14 +600,16 @@ timeout_aborted:
                 term_double_buffer(false);
                 return selected_menu_entry->body;
             case 'e': {
-                if (selected_menu_entry->sub != NULL)
-                    goto refresh;
-                enable_cursor();
-                char *new_body = config_entry_editor(selected_menu_entry->body);
-                if (new_body == NULL)
-                    goto refresh;
-                selected_menu_entry->body = new_body;
-                goto autoboot;
+                if (editor_enabled) {
+                    if (selected_menu_entry->sub != NULL)
+                        goto refresh;
+                    enable_cursor();
+                    char *new_body = config_entry_editor(selected_menu_entry->name, selected_menu_entry->body);
+                    if (new_body == NULL)
+                        goto refresh;
+                    selected_menu_entry->body = new_body;
+                    goto autoboot;
+                }
             }
         }
     }
tab: 248 wrap: offon