#include #include #include #include #include typedef struct { const char* name; int enabled; } option_t; typedef struct { const char* title; option_t* options; size_t num_options; } menu_t; void show_menu(menu_t* menu); void load_config(menu_t* menus, size_t menu_count); void save_config(menu_t* menus, size_t menu_count); #define SIZE(x) (sizeof(x) / sizeof(option_t)) #define NUM_MENUS (sizeof(all_menus) / sizeof(menu_t)) #define KEY_ESCAPE 27 option_t general_opts[] = { { "Enable Debugging Messages", 1 }, { "Enable Filesystems", 0 }, }; option_t drivers_opts[] = { { "Enable IDE", 1 }, { "Enable USB", 0 }, { "Enable Network", 0 }, }; menu_t all_menus[] = { { "General Settings", general_opts, SIZE(general_opts) }, { "Device Drivers", drivers_opts, SIZE(drivers_opts) }, }; bool current_config_saved = false; void strfix(char *str) { for (size_t i = 0; str[i] != '\0'; ++i) { if (isspace((unsigned char)str[i])) { str[i] = '_'; } else { str[i] = toupper((unsigned char)str[i]); } } } void load_config(menu_t* menus, size_t menu_count) { FILE* f = fopen(".config", "r"); if (!f) { return; } char line[128]; while (fgets(line, sizeof(line), f)) { char* newline = strchr(line, '\n'); if (newline) { *newline = 0; } for (size_t m = 0; m < menu_count; m++) { for (size_t i = 0; i < menus[m].num_options; i++) { if (strncmp(line, menus[m].options[i].name, strlen(menus[m].options[i].name)) == 0) { if (strstr(line, "=y")) { menus[m].options[i].enabled = 1; } else { menus[m].options[i].enabled = 0; } } } } } fclose(f); } const char* cc = "\n\nstatic inline kconf_t get_kconfig(void)\n" "{\n" "\tkconf_t kc = { false, false, false, false, false };\n\n" "#ifdef ENABLE_DEBUGGING_MESSAGES\n" "\tkc.enable_debug = true;\n" "#endif\n" "#ifdef ENABLE_FILESYSTEMS\n" "\tkc.enable_fs = true;\n" "#endif\n" "#ifdef ENABLE_IDE\n" "\tkc.enable_ide = true;\n" "#endif\n" "#ifdef ENABLE_USB\n" "\tkc.enable_usb = true;\n" "#endif\n" "#ifdef ENABLE_NETWORK\n" "\tkc.enable_networking = true;\n" "#endif\n\n" "\treturn kc;\n" "}\n\n" "#ifdef __cplusplus\n" "}\n" "#endif\n"; /* Save current configuration options to Makefile and/or kconfig.h */ void save_config(menu_t* menus, size_t menu_count) { FILE* kconff = fopen("../include/kernel/kconfig.h", "w"); if (!kconff) { return; } const char* hg = "#ifndef _KCONFIG_H\n#define _KCONFIG_H\n\n#include \n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"; fprintf(kconff, hg); for (size_t m = 0; m < menu_count; m++) { for (size_t i = 0; i < menus[m].num_options; i++) { char buffer[128]; strncpy(buffer, menus[m].options[i].name, sizeof(buffer)); buffer[sizeof(buffer)-1] = '\0'; strfix(buffer); if (menus[m].options[i].enabled == 0) { fprintf(kconff, "//"); } fprintf(kconff, "#define %s\n", buffer, menus[m].options[i].enabled); } } fprintf(kconff, cc); fprintf(kconff, "\n#endif\n"); fclose(kconff); current_config_saved = true; } menu_t* create_menu(const char* title) { menu_t* menu = malloc(sizeof(menu_t)); menu->title = strdup(title); menu->options = NULL; menu->num_options = 0; return menu; } void add_option(menu_t* menu, const char* name, int default_value) { menu->options = realloc(menu->options, sizeof(option_t) * (menu->num_options + 1)); menu->options[menu->num_options].name = strdup(name); menu->options[menu->num_options].enabled = default_value; menu->num_options++; } /* Display a single submenu */ void show_menu(menu_t* menu) { int selected = 0; while (1) { clear(); mvprintw(0, 0, "%s (SPACE=toggle, (left arrow key or esc)=back, S=save)", menu->title); for (size_t i = 0; i < menu->num_options; i++) { if ((int)i == selected) { attron(A_REVERSE); } mvprintw(i + 2, 2, "[%c] %s", menu->options[i].enabled ? 'X' : ' ', menu->options[i].name); if ((int)i == selected) { attroff(A_REVERSE); } } int ch = getch(); if (ch == KEY_UP && selected > 0) { selected--; } else if (ch == KEY_DOWN && selected < (int)menu->num_options - 1) { selected++; } else if (ch == ' ') { menu->options[selected].enabled = !menu->options[selected].enabled; current_config_saved = false; } else if (ch == 's' || ch == 'S') { save_config(all_menus, NUM_MENUS); mvprintw(menu->num_options + 3, 2, "Saved."); refresh(); getch(); } else if (ch == KEY_LEFT || ch == KEY_ESCAPE || ch == 'q' || ch == 'Q') { break; } } } /* Main menu with submenu navigation */ int main(void) { initscr(); noecho(); cbreak(); keypad(stdscr, TRUE); set_escdelay(25); /* wait only 25ms for escape sequences */ /*load_config(all_menus, NUM_MENUS);*/ int selected = 0; while (1) { clear(); mvprintw(0, 0, "Kernel Configuration (ENTER=submenu, S=save, Q=quit)"); for (size_t i = 0; i < NUM_MENUS; i++) { if ((int)i == selected) { attron(A_REVERSE); } mvprintw(i + 2, 2, "> %s", all_menus[i].title); if ((int)i == selected) { attroff(A_REVERSE); } } int ch = getch(); if (ch == KEY_UP && selected > 0) { selected--; } else if (ch == KEY_DOWN && selected < (int)NUM_MENUS - 1) { selected++; } else if (ch == '\n') { show_menu(&all_menus[selected]); } else if (ch == 's' || ch == 'S') { save_config(all_menus, NUM_MENUS); mvprintw(NUM_MENUS + 3, 2, "Saved."); refresh(); getch(); } else if (ch == 'q' || ch == 'Q') { break; } } endwin(); return 0; }