1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com> 4 * 5 * Derived from menuconfig. 6 */ 7 #ifndef _GNU_SOURCE 8 #define _GNU_SOURCE 9 #endif 10 #include <locale.h> 11 #include <string.h> 12 #include <strings.h> 13 #include <stdlib.h> 14 15 #include <list.h> 16 #include <xalloc.h> 17 #include "lkc.h" 18 #include "mnconf-common.h" 19 #include "nconf.h" 20 #include <ctype.h> 21 22 static const char nconf_global_help[] = 23 "Help windows\n" 24 "------------\n" 25 "o Global help: Unless in a data entry window, pressing <F1> will give \n" 26 " you the global help window, which you are just reading.\n" 27 "\n" 28 "o A short version of the global help is available by pressing <F3>.\n" 29 "\n" 30 "o Local help: To get help related to the current menu entry, use any\n" 31 " of <?> <h>, or if in a data entry window then press <F1>.\n" 32 "\n" 33 "\n" 34 "Menu entries\n" 35 "------------\n" 36 "This interface lets you select features and parameters for the kernel\n" 37 "build. Kernel features can either be built-in, modularized, or removed.\n" 38 "Parameters must be entered as text or decimal or hexadecimal numbers.\n" 39 "\n" 40 "Menu entries beginning with following braces represent features that\n" 41 " [ ] can be built in or removed\n" 42 " < > can be built in, modularized or removed\n" 43 " { } can be built in or modularized, are selected by another feature\n" 44 " - - are selected by another feature\n" 45 " XXX cannot be selected. Symbol Info <F2> tells you why.\n" 46 "*, M or whitespace inside braces means to build in, build as a module\n" 47 "or to exclude the feature respectively.\n" 48 "\n" 49 "To change any of these features, highlight it with the movement keys\n" 50 "listed below and press <y> to build it in, <m> to make it a module or\n" 51 "<n> to remove it. You may press the <Space> key to cycle through the\n" 52 "available options.\n" 53 "\n" 54 "A trailing \"--->\" designates a submenu, a trailing \"----\" an\n" 55 "empty submenu.\n" 56 "\n" 57 "Menu navigation keys\n" 58 "----------------------------------------------------------------------\n" 59 "Linewise up <Up> <k>\n" 60 "Linewise down <Down> <j>\n" 61 "Pagewise up <Page Up>\n" 62 "Pagewise down <Page Down>\n" 63 "First entry <Home>\n" 64 "Last entry <End>\n" 65 "Enter a submenu <Right> <Enter>\n" 66 "Go back to parent menu <Left> <Esc> <F5>\n" 67 "Close a help window <Enter> <Esc> <F5>\n" 68 "Close entry window, apply <Enter>\n" 69 "Close entry window, forget <Esc> <F5>\n" 70 "Start incremental, case-insensitive search for STRING in menu entries,\n" 71 " no regex support, STRING is displayed in upper left corner\n" 72 " </>STRING\n" 73 " Remove last character <Backspace>\n" 74 " Jump to next hit <Down>\n" 75 " Jump to previous hit <Up>\n" 76 "Exit menu search mode </> <Esc>\n" 77 "Search for configuration variables with or without leading CONFIG_\n" 78 " <F8>RegExpr<Enter>\n" 79 "Verbose search help <F8><F1>\n" 80 "----------------------------------------------------------------------\n" 81 "\n" 82 "Unless in a data entry window, key <1> may be used instead of <F1>,\n" 83 "<2> instead of <F2>, etc.\n" 84 "\n" 85 "\n" 86 "Radiolist (Choice list)\n" 87 "-----------------------\n" 88 "Use the movement keys listed above to select the option you wish to set\n" 89 "and press <Space>.\n" 90 "\n" 91 "\n" 92 "Data entry\n" 93 "----------\n" 94 "Enter the requested information and press <Enter>. Hexadecimal values\n" 95 "may be entered without the \"0x\" prefix.\n" 96 "\n" 97 "\n" 98 "Text Box (Help Window)\n" 99 "----------------------\n" 100 "Use movement keys as listed in table above.\n" 101 "\n" 102 "Press any of <Enter> <Esc> <q> <F5> <F9> to exit.\n" 103 "\n" 104 "\n" 105 "Alternate configuration files\n" 106 "-----------------------------\n" 107 "nconfig supports switching between different configurations.\n" 108 "Press <F6> to save your current configuration. Press <F7> and enter\n" 109 "a file name to load a previously saved configuration.\n" 110 "\n" 111 "\n" 112 "Terminal configuration\n" 113 "----------------------\n" 114 "If you use nconfig in a xterm window, make sure your TERM environment\n" 115 "variable specifies a terminal configuration which supports at least\n" 116 "16 colors. Otherwise nconfig will look rather bad.\n" 117 "\n" 118 "If the \"stty size\" command reports the current terminalsize correctly,\n" 119 "nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n" 120 "and display longer menus properly.\n" 121 "\n" 122 "\n" 123 "Single menu mode\n" 124 "----------------\n" 125 "If you prefer to have all of the menu entries listed in a single menu,\n" 126 "rather than the default multimenu hierarchy, run nconfig with\n" 127 "NCONFIG_MODE environment variable set to single_menu. Example:\n" 128 "\n" 129 "make NCONFIG_MODE=single_menu nconfig\n" 130 "\n" 131 "<Enter> will then unfold the appropriate category, or fold it if it\n" 132 "is already unfolded. Folded menu entries will be designated by a\n" 133 "leading \"++>\" and unfolded entries by a leading \"-->\".\n" 134 "\n" 135 "Note that this mode can eventually be a little more CPU expensive than\n" 136 "the default mode, especially with a larger number of unfolded submenus.\n" 137 "\n", 138 menu_no_f_instructions[] = 139 "Legend: [*] built-in [ ] excluded <M> module < > module capable.\n" 140 "Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n" 141 "\n" 142 "Use the following keys to navigate the menus:\n" 143 "Move up or down with <Up> and <Down>.\n" 144 "Enter a submenu with <Enter> or <Right>.\n" 145 "Exit a submenu to its parent menu with <Esc> or <Left>.\n" 146 "Pressing <y> includes, <n> excludes, <m> modularizes features.\n" 147 "Pressing <Space> cycles through the available options.\n" 148 "To search for menu entries press </>.\n" 149 "<Esc> always leaves the current window.\n" 150 "\n" 151 "You do not have function keys support.\n" 152 "Press <1> instead of <F1>, <2> instead of <F2>, etc.\n" 153 "For verbose global help use key <1>.\n" 154 "For help related to the current menu entry press <?> or <h>.\n", 155 menu_instructions[] = 156 "Legend: [*] built-in [ ] excluded <M> module < > module capable.\n" 157 "Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n" 158 "\n" 159 "Use the following keys to navigate the menus:\n" 160 "Move up or down with <Up> or <Down>.\n" 161 "Enter a submenu with <Enter> or <Right>.\n" 162 "Exit a submenu to its parent menu with <Esc> or <Left>.\n" 163 "Pressing <y> includes, <n> excludes, <m> modularizes features.\n" 164 "Pressing <Space> cycles through the available options.\n" 165 "To search for menu entries press </>.\n" 166 "<Esc> always leaves the current window.\n" 167 "\n" 168 "Pressing <1> may be used instead of <F1>, <2> instead of <F2>, etc.\n" 169 "For verbose global help press <F1>.\n" 170 "For help related to the current menu entry press <?> or <h>.\n", 171 radiolist_instructions[] = 172 "Press <Up>, <Down>, <Home> or <End> to navigate a radiolist, select\n" 173 "with <Space>.\n" 174 "For help related to the current entry press <?> or <h>.\n" 175 "For global help press <F1>.\n", 176 inputbox_instructions_int[] = 177 "Please enter a decimal value.\n" 178 "Fractions will not be accepted.\n" 179 "Press <Enter> to apply, <Esc> to cancel.", 180 inputbox_instructions_hex[] = 181 "Please enter a hexadecimal value.\n" 182 "Press <Enter> to apply, <Esc> to cancel.", 183 inputbox_instructions_string[] = 184 "Please enter a string value.\n" 185 "Press <Enter> to apply, <Esc> to cancel.", 186 setmod_text[] = 187 "This feature depends on another feature which has been configured as a\n" 188 "module. As a result, the current feature will be built as a module too.", 189 load_config_text[] = 190 "Enter the name of the configuration file you wish to load.\n" 191 "Accept the name shown to restore the configuration you last\n" 192 "retrieved. Leave empty to abort.", 193 load_config_help[] = 194 "For various reasons, one may wish to keep several different\n" 195 "configurations available on a single machine.\n" 196 "\n" 197 "If you have saved a previous configuration in a file other than the\n" 198 "default one, entering its name here will allow you to load and modify\n" 199 "that configuration.\n" 200 "\n" 201 "Leave empty to abort.\n", 202 save_config_text[] = 203 "Enter a filename to which this configuration should be saved\n" 204 "as an alternate. Leave empty to abort.", 205 save_config_help[] = 206 "For various reasons, one may wish to keep several different\n" 207 "configurations available on a single machine.\n" 208 "\n" 209 "Entering a file name here will allow you to later retrieve, modify\n" 210 "and use the current configuration as an alternate to whatever\n" 211 "configuration options you have selected at that time.\n" 212 "\n" 213 "Leave empty to abort.\n", 214 search_help[] = 215 "Search for symbols (configuration variable names CONFIG_*) and display\n" 216 "their relations. Regular expressions are supported.\n" 217 "Example: Search for \"^FOO\".\n" 218 "Result:\n" 219 "-----------------------------------------------------------------\n" 220 "Symbol: FOO [ = m]\n" 221 "Prompt: Foo bus is used to drive the bar HW\n" 222 "Defined at drivers/pci/Kconfig:47\n" 223 "Depends on: X86_LOCAL_APIC && X86_IO_APIC\n" 224 "Location:\n" 225 " -> Bus options (PCI, PCMCIA, EISA, ISA)\n" 226 " -> PCI support (PCI [ = y])\n" 227 "(1) -> PCI access mode (<choice> [ = y])\n" 228 "Selects: LIBCRC32\n" 229 "Selected by: BAR\n" 230 "-----------------------------------------------------------------\n" 231 "o The line 'Prompt:' shows the text displayed for this symbol in\n" 232 " the menu hierarchy.\n" 233 "o The 'Defined at' line tells at what file / line number the symbol is\n" 234 " defined.\n" 235 "o The 'Depends on:' line lists symbols that need to be defined for\n" 236 " this symbol to be visible and selectable in the menu.\n" 237 "o The 'Location:' lines tell, where in the menu structure this symbol\n" 238 " is located.\n" 239 " A location followed by a [ = y] indicates that this is\n" 240 " a selectable menu item, and the current value is displayed inside\n" 241 " brackets.\n" 242 " Press the key in the (#) prefix to jump directly to that\n" 243 " location. You will be returned to the current search results\n" 244 " after exiting this new menu.\n" 245 "o The 'Selects:' line tells, what symbol will be automatically selected\n" 246 " if this symbol is selected (y or m).\n" 247 "o The 'Selected by' line tells what symbol has selected this symbol.\n" 248 "\n" 249 "Only relevant lines are shown.\n" 250 "\n\n" 251 "Search examples:\n" 252 "USB => find all symbols containing USB\n" 253 "^USB => find all symbols starting with USB\n" 254 "USB$ => find all symbols ending with USB\n" 255 "\n"; 256 257 struct mitem { 258 char str[256]; 259 char tag; 260 void *usrptr; 261 int is_visible; 262 }; 263 264 #define MAX_MENU_ITEMS 4096 265 static int show_all_items; 266 static int indent; 267 static struct menu *current_menu; 268 static int child_count; 269 static int single_menu_mode; 270 /* the window in which all information appears */ 271 static WINDOW *main_window; 272 /* the largest size of the menu window */ 273 static int mwin_max_lines; 274 static int mwin_max_cols; 275 /* the window in which we show option buttons */ 276 static MENU *curses_menu; 277 static ITEM *curses_menu_items[MAX_MENU_ITEMS]; 278 static struct mitem k_menu_items[MAX_MENU_ITEMS]; 279 static unsigned int items_num; 280 static int global_exit; 281 /* the currently selected button */ 282 static const char *current_instructions = menu_instructions; 283 284 static char *dialog_input_result; 285 static int dialog_input_result_len; 286 287 static void selected_conf(struct menu *menu, struct menu *active_menu); 288 static void conf(struct menu *menu); 289 static void conf_choice(struct menu *menu); 290 static void conf_string(struct menu *menu); 291 static void conf_load(void); 292 static void conf_save(void); 293 static void show_help(struct menu *menu); 294 static int do_exit(void); 295 static void setup_windows(void); 296 static void search_conf(void); 297 298 typedef void (*function_key_handler_t)(int *key, struct menu *menu); 299 static void handle_f1(int *key, struct menu *current_item); 300 static void handle_f2(int *key, struct menu *current_item); 301 static void handle_f3(int *key, struct menu *current_item); 302 static void handle_f4(int *key, struct menu *current_item); 303 static void handle_f5(int *key, struct menu *current_item); 304 static void handle_f6(int *key, struct menu *current_item); 305 static void handle_f7(int *key, struct menu *current_item); 306 static void handle_f8(int *key, struct menu *current_item); 307 static void handle_f9(int *key, struct menu *current_item); 308 309 struct function_keys { 310 const char *key_str; 311 const char *func; 312 function_key key; 313 function_key_handler_t handler; 314 }; 315 316 static const int function_keys_num = 9; 317 static struct function_keys function_keys[] = { 318 { 319 .key_str = "F1", 320 .func = "Help", 321 .key = F_HELP, 322 .handler = handle_f1, 323 }, 324 { 325 .key_str = "F2", 326 .func = "SymInfo", 327 .key = F_SYMBOL, 328 .handler = handle_f2, 329 }, 330 { 331 .key_str = "F3", 332 .func = "Help 2", 333 .key = F_INSTS, 334 .handler = handle_f3, 335 }, 336 { 337 .key_str = "F4", 338 .func = "ShowAll", 339 .key = F_CONF, 340 .handler = handle_f4, 341 }, 342 { 343 .key_str = "F5", 344 .func = "Back", 345 .key = F_BACK, 346 .handler = handle_f5, 347 }, 348 { 349 .key_str = "F6", 350 .func = "Save", 351 .key = F_SAVE, 352 .handler = handle_f6, 353 }, 354 { 355 .key_str = "F7", 356 .func = "Load", 357 .key = F_LOAD, 358 .handler = handle_f7, 359 }, 360 { 361 .key_str = "F8", 362 .func = "SymSearch", 363 .key = F_SEARCH, 364 .handler = handle_f8, 365 }, 366 { 367 .key_str = "F9", 368 .func = "Exit", 369 .key = F_EXIT, 370 .handler = handle_f9, 371 }, 372 }; 373 374 static void print_function_line(void) 375 { 376 int i; 377 int offset = 1; 378 const int skip = 1; 379 int lines = getmaxy(stdscr); 380 381 for (i = 0; i < function_keys_num; i++) { 382 wattrset(main_window, attr_function_highlight); 383 mvwprintw(main_window, lines-3, offset, 384 "%s", 385 function_keys[i].key_str); 386 wattrset(main_window, attr_function_text); 387 offset += strlen(function_keys[i].key_str); 388 mvwprintw(main_window, lines-3, 389 offset, "%s", 390 function_keys[i].func); 391 offset += strlen(function_keys[i].func) + skip; 392 } 393 wattrset(main_window, attr_normal); 394 } 395 396 /* help */ 397 static void handle_f1(int *key, struct menu *current_item) 398 { 399 show_scroll_win(main_window, 400 "Global help", nconf_global_help); 401 return; 402 } 403 404 /* symbole help */ 405 static void handle_f2(int *key, struct menu *current_item) 406 { 407 show_help(current_item); 408 return; 409 } 410 411 /* instructions */ 412 static void handle_f3(int *key, struct menu *current_item) 413 { 414 show_scroll_win(main_window, 415 "Short help", 416 current_instructions); 417 return; 418 } 419 420 /* config */ 421 static void handle_f4(int *key, struct menu *current_item) 422 { 423 int res = btn_dialog(main_window, 424 "Show all symbols?", 425 2, 426 " <Show All> ", 427 "<Don't show all>"); 428 if (res == 0) 429 show_all_items = 1; 430 else if (res == 1) 431 show_all_items = 0; 432 433 return; 434 } 435 436 /* back */ 437 static void handle_f5(int *key, struct menu *current_item) 438 { 439 *key = KEY_LEFT; 440 return; 441 } 442 443 /* save */ 444 static void handle_f6(int *key, struct menu *current_item) 445 { 446 conf_save(); 447 return; 448 } 449 450 /* load */ 451 static void handle_f7(int *key, struct menu *current_item) 452 { 453 conf_load(); 454 return; 455 } 456 457 /* search */ 458 static void handle_f8(int *key, struct menu *current_item) 459 { 460 search_conf(); 461 return; 462 } 463 464 /* exit */ 465 static void handle_f9(int *key, struct menu *current_item) 466 { 467 do_exit(); 468 return; 469 } 470 471 /* return != 0 to indicate the key was handled */ 472 static int process_special_keys(int *key, struct menu *menu) 473 { 474 int i; 475 476 if (*key == KEY_RESIZE) { 477 setup_windows(); 478 return 1; 479 } 480 481 for (i = 0; i < function_keys_num; i++) { 482 if (*key == KEY_F(function_keys[i].key) || 483 *key == '0' + function_keys[i].key){ 484 function_keys[i].handler(key, menu); 485 return 1; 486 } 487 } 488 489 return 0; 490 } 491 492 static void clean_items(void) 493 { 494 int i; 495 for (i = 0; curses_menu_items[i]; i++) 496 free_item(curses_menu_items[i]); 497 bzero(curses_menu_items, sizeof(curses_menu_items)); 498 bzero(k_menu_items, sizeof(k_menu_items)); 499 items_num = 0; 500 } 501 502 typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN, 503 FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f; 504 505 /* return the index of the matched item, or -1 if no such item exists */ 506 static int get_mext_match(const char *match_str, match_f flag) 507 { 508 int match_start, index; 509 510 /* Do not search if the menu is empty (i.e. items_num == 0) */ 511 match_start = item_index(current_item(curses_menu)); 512 if (match_start == ERR) 513 return -1; 514 515 if (flag == FIND_NEXT_MATCH_DOWN) 516 ++match_start; 517 else if (flag == FIND_NEXT_MATCH_UP) 518 --match_start; 519 520 match_start = (match_start + items_num) % items_num; 521 index = match_start; 522 while (true) { 523 char *str = k_menu_items[index].str; 524 if (strcasestr(str, match_str) != NULL) 525 return index; 526 if (flag == FIND_NEXT_MATCH_UP || 527 flag == MATCH_TINKER_PATTERN_UP) 528 --index; 529 else 530 ++index; 531 index = (index + items_num) % items_num; 532 if (index == match_start) 533 return -1; 534 } 535 } 536 537 /* Make a new item. */ 538 static void item_make(struct menu *menu, char tag, const char *fmt, ...) 539 { 540 va_list ap; 541 542 if (items_num > MAX_MENU_ITEMS-1) 543 return; 544 545 bzero(&k_menu_items[items_num], sizeof(k_menu_items[0])); 546 k_menu_items[items_num].tag = tag; 547 k_menu_items[items_num].usrptr = menu; 548 if (menu != NULL) 549 k_menu_items[items_num].is_visible = 550 menu_is_visible(menu); 551 else 552 k_menu_items[items_num].is_visible = 1; 553 554 va_start(ap, fmt); 555 vsnprintf(k_menu_items[items_num].str, 556 sizeof(k_menu_items[items_num].str), 557 fmt, ap); 558 va_end(ap); 559 560 if (!k_menu_items[items_num].is_visible) 561 memcpy(k_menu_items[items_num].str, "XXX", 3); 562 563 curses_menu_items[items_num] = new_item( 564 k_menu_items[items_num].str, 565 k_menu_items[items_num].str); 566 set_item_userptr(curses_menu_items[items_num], 567 &k_menu_items[items_num]); 568 /* 569 if (!k_menu_items[items_num].is_visible) 570 item_opts_off(curses_menu_items[items_num], O_SELECTABLE); 571 */ 572 573 items_num++; 574 curses_menu_items[items_num] = NULL; 575 } 576 577 /* very hackish. adds a string to the last item added */ 578 static void item_add_str(const char *fmt, ...) 579 { 580 va_list ap; 581 int index = items_num-1; 582 char new_str[256]; 583 char tmp_str[256]; 584 585 if (index < 0) 586 return; 587 588 va_start(ap, fmt); 589 vsnprintf(new_str, sizeof(new_str), fmt, ap); 590 va_end(ap); 591 snprintf(tmp_str, sizeof(tmp_str), "%s%s", 592 k_menu_items[index].str, new_str); 593 strncpy(k_menu_items[index].str, 594 tmp_str, 595 sizeof(k_menu_items[index].str)); 596 597 k_menu_items[index].str[sizeof(k_menu_items[index].str) - 1] = '\0'; 598 599 free_item(curses_menu_items[index]); 600 curses_menu_items[index] = new_item( 601 k_menu_items[index].str, 602 k_menu_items[index].str); 603 set_item_userptr(curses_menu_items[index], 604 &k_menu_items[index]); 605 } 606 607 /* get the tag of the currently selected item */ 608 static char item_tag(void) 609 { 610 ITEM *cur; 611 struct mitem *mcur; 612 613 cur = current_item(curses_menu); 614 if (cur == NULL) 615 return 0; 616 mcur = (struct mitem *) item_userptr(cur); 617 return mcur->tag; 618 } 619 620 static int curses_item_index(void) 621 { 622 return item_index(current_item(curses_menu)); 623 } 624 625 static void *item_data(void) 626 { 627 ITEM *cur; 628 struct mitem *mcur; 629 630 cur = current_item(curses_menu); 631 if (!cur) 632 return NULL; 633 mcur = (struct mitem *) item_userptr(cur); 634 return mcur->usrptr; 635 636 } 637 638 static int item_is_tag(char tag) 639 { 640 return item_tag() == tag; 641 } 642 643 static char filename[PATH_MAX+1]; 644 static char menu_backtitle[PATH_MAX+128]; 645 static void set_config_filename(const char *config_filename) 646 { 647 snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s", 648 config_filename, rootmenu.prompt->text); 649 650 snprintf(filename, sizeof(filename), "%s", config_filename); 651 } 652 653 /* return = 0 means we are successful. 654 * -1 means go on doing what you were doing 655 */ 656 static int do_exit(void) 657 { 658 int res; 659 if (!conf_get_changed()) { 660 global_exit = 1; 661 return 0; 662 } 663 res = btn_dialog(main_window, 664 "Do you wish to save your new configuration?\n" 665 "<ESC> to cancel and resume nconfig.", 666 2, 667 " <save> ", 668 "<don't save>"); 669 if (res == KEY_EXIT) { 670 global_exit = 0; 671 return -1; 672 } 673 674 /* if we got here, the user really wants to exit */ 675 switch (res) { 676 case 0: 677 res = conf_write(filename); 678 if (res) 679 btn_dialog( 680 main_window, 681 "Error during writing of configuration.\n" 682 "Your configuration changes were NOT saved.", 683 1, 684 "<OK>"); 685 conf_write_autoconf(0); 686 break; 687 default: 688 btn_dialog( 689 main_window, 690 "Your configuration changes were NOT saved.", 691 1, 692 "<OK>"); 693 break; 694 } 695 global_exit = 1; 696 return 0; 697 } 698 699 700 static void search_conf(void) 701 { 702 struct symbol **sym_arr; 703 struct gstr res; 704 struct gstr title; 705 char *dialog_input; 706 int dres, vscroll = 0, hscroll = 0; 707 bool again; 708 709 title = str_new(); 710 str_printf( &title, "Enter (sub)string or regexp to search for " 711 "(with or without \"%s\")", CONFIG_); 712 713 again: 714 dres = dialog_inputbox(main_window, 715 "Search Configuration Parameter", 716 str_get(&title), 717 "", &dialog_input_result, &dialog_input_result_len); 718 switch (dres) { 719 case 0: 720 break; 721 case 1: 722 show_scroll_win(main_window, 723 "Search Configuration", search_help); 724 goto again; 725 default: 726 str_free(&title); 727 return; 728 } 729 730 /* strip the prefix if necessary */ 731 dialog_input = dialog_input_result; 732 if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0) 733 dialog_input += strlen(CONFIG_); 734 735 sym_arr = sym_re_search(dialog_input); 736 737 do { 738 LIST_HEAD(head); 739 struct search_data data = { 740 .head = &head, 741 .target = NULL, 742 }; 743 jump_key_char = 0; 744 res = get_relations_str(sym_arr, &head); 745 dres = show_scroll_win_ext(main_window, 746 "Search Results", str_get(&res), 747 &vscroll, &hscroll, 748 handle_search_keys, &data); 749 again = false; 750 if (dres >= '1' && dres <= '9') { 751 assert(data.target != NULL); 752 selected_conf(data.target->parent, data.target); 753 again = true; 754 } 755 str_free(&res); 756 } while (again); 757 free(sym_arr); 758 str_free(&title); 759 } 760 761 762 static void build_conf(struct menu *menu) 763 { 764 struct symbol *sym; 765 struct property *prop; 766 struct menu *child; 767 int type, tmp, doint = 2; 768 tristate val; 769 char ch; 770 771 if (!menu || (!show_all_items && !menu_is_visible(menu))) 772 return; 773 774 sym = menu->sym; 775 prop = menu->prompt; 776 if (!sym) { 777 if (prop && menu != current_menu) { 778 const char *prompt = menu_get_prompt(menu); 779 enum prop_type ptype; 780 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; 781 switch (ptype) { 782 case P_MENU: 783 child_count++; 784 if (single_menu_mode) { 785 item_make(menu, 'm', 786 "%s%*c%s", 787 menu->data ? "-->" : "++>", 788 indent + 1, ' ', prompt); 789 } else 790 item_make(menu, 'm', 791 " %*c%s %s", 792 indent + 1, ' ', prompt, 793 menu_is_empty(menu) ? "----" : "--->"); 794 795 if (single_menu_mode && menu->data) 796 goto conf_childs; 797 return; 798 case P_COMMENT: 799 if (prompt) { 800 child_count++; 801 item_make(menu, ':', 802 " %*c*** %s ***", 803 indent + 1, ' ', 804 prompt); 805 } 806 break; 807 default: 808 if (prompt) { 809 child_count++; 810 item_make(menu, ':', "---%*c%s", 811 indent + 1, ' ', 812 prompt); 813 } 814 } 815 } else 816 doint = 0; 817 goto conf_childs; 818 } 819 820 type = sym_get_type(sym); 821 if (sym_is_choice(sym)) { 822 struct symbol *def_sym = sym_calc_choice(menu); 823 struct menu *def_menu = NULL; 824 825 child_count++; 826 for (child = menu->list; child; child = child->next) { 827 if (menu_is_visible(child) && child->sym == def_sym) 828 def_menu = child; 829 } 830 831 val = sym_get_tristate_value(sym); 832 item_make(menu, def_menu ? 't' : ':', " "); 833 834 item_add_str("%*c%s", indent + 1, 835 ' ', menu_get_prompt(menu)); 836 if (def_menu) 837 item_add_str(" (%s) --->", menu_get_prompt(def_menu)); 838 return; 839 } else { 840 if (menu == current_menu) { 841 item_make(menu, ':', 842 "---%*c%s", indent + 1, 843 ' ', menu_get_prompt(menu)); 844 goto conf_childs; 845 } 846 child_count++; 847 val = sym_get_tristate_value(sym); 848 switch (type) { 849 case S_BOOLEAN: 850 if (sym_is_changeable(sym)) 851 item_make(menu, 't', "[%c]", 852 val == no ? ' ' : '*'); 853 else 854 item_make(menu, 't', "-%c-", 855 val == no ? ' ' : '*'); 856 break; 857 case S_TRISTATE: 858 switch (val) { 859 case yes: 860 ch = '*'; 861 break; 862 case mod: 863 ch = 'M'; 864 break; 865 default: 866 ch = ' '; 867 break; 868 } 869 if (sym_is_changeable(sym)) { 870 if (sym->rev_dep.tri == mod) 871 item_make(menu, 't', "{%c}", ch); 872 else 873 item_make(menu, 't', "<%c>", ch); 874 } else 875 item_make(menu, 't', "-%c-", ch); 876 break; 877 default: 878 tmp = 2 + strlen(sym_get_string_value(sym)); 879 item_make(menu, 's', " (%s)", 880 sym_get_string_value(sym)); 881 tmp = indent - tmp + 4; 882 if (tmp < 0) 883 tmp = 0; 884 item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu), 885 (sym_has_value(sym) || 886 !sym_is_changeable(sym)) ? "" : " (NEW)"); 887 goto conf_childs; 888 } 889 item_add_str("%*c%s%s", indent + 1, ' ', 890 menu_get_prompt(menu), 891 (sym_has_value(sym) || !sym_is_changeable(sym)) ? 892 "" : " (NEW)"); 893 if (menu->prompt && menu->prompt->type == P_MENU) { 894 item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->"); 895 return; 896 } 897 } 898 899 conf_childs: 900 indent += doint; 901 for (child = menu->list; child; child = child->next) 902 build_conf(child); 903 indent -= doint; 904 } 905 906 static void reset_menu(void) 907 { 908 unpost_menu(curses_menu); 909 clean_items(); 910 } 911 912 /* adjust the menu to show this item. 913 * prefer not to scroll the menu if possible*/ 914 static void center_item(int selected_index, int *last_top_row) 915 { 916 int toprow; 917 918 set_top_row(curses_menu, *last_top_row); 919 toprow = top_row(curses_menu); 920 if (selected_index < toprow || 921 selected_index >= toprow+mwin_max_lines) { 922 toprow = max(selected_index-mwin_max_lines/2, 0); 923 if (toprow >= item_count(curses_menu)-mwin_max_lines) 924 toprow = item_count(curses_menu)-mwin_max_lines; 925 set_top_row(curses_menu, toprow); 926 } 927 set_current_item(curses_menu, 928 curses_menu_items[selected_index]); 929 *last_top_row = toprow; 930 post_menu(curses_menu); 931 refresh_all_windows(main_window); 932 } 933 934 /* this function assumes reset_menu has been called before */ 935 static void show_menu(const char *prompt, const char *instructions, 936 int selected_index, int *last_top_row) 937 { 938 int maxx, maxy; 939 WINDOW *menu_window; 940 941 current_instructions = instructions; 942 943 clear(); 944 print_in_middle(stdscr, 1, getmaxx(stdscr), 945 menu_backtitle, 946 attr_main_heading); 947 948 wattrset(main_window, attr_main_menu_box); 949 box(main_window, 0, 0); 950 wattrset(main_window, attr_main_menu_heading); 951 mvwprintw(main_window, 0, 3, " %s ", prompt); 952 wattrset(main_window, attr_normal); 953 954 set_menu_items(curses_menu, curses_menu_items); 955 956 /* position the menu at the middle of the screen */ 957 scale_menu(curses_menu, &maxy, &maxx); 958 maxx = min(maxx, mwin_max_cols-2); 959 maxy = mwin_max_lines; 960 menu_window = derwin(main_window, 961 maxy, 962 maxx, 963 2, 964 (mwin_max_cols-maxx)/2); 965 keypad(menu_window, TRUE); 966 set_menu_win(curses_menu, menu_window); 967 set_menu_sub(curses_menu, menu_window); 968 969 /* must reassert this after changing items, otherwise returns to a 970 * default of 16 971 */ 972 set_menu_format(curses_menu, maxy, 1); 973 center_item(selected_index, last_top_row); 974 set_menu_format(curses_menu, maxy, 1); 975 976 print_function_line(); 977 978 /* Post the menu */ 979 post_menu(curses_menu); 980 refresh_all_windows(main_window); 981 } 982 983 static void adj_match_dir(match_f *match_direction) 984 { 985 if (*match_direction == FIND_NEXT_MATCH_DOWN) 986 *match_direction = 987 MATCH_TINKER_PATTERN_DOWN; 988 else if (*match_direction == FIND_NEXT_MATCH_UP) 989 *match_direction = 990 MATCH_TINKER_PATTERN_UP; 991 /* else, do no change.. */ 992 } 993 994 struct match_state 995 { 996 int in_search; 997 match_f match_direction; 998 char pattern[256]; 999 }; 1000 1001 /* Return 0 means I have handled the key. In such a case, ans should hold the 1002 * item to center, or -1 otherwise. 1003 * Else return -1 . 1004 */ 1005 static int do_match(int key, struct match_state *state, int *ans) 1006 { 1007 char c = (char) key; 1008 int terminate_search = 0; 1009 *ans = -1; 1010 if (key == '/' || (state->in_search && key == 27)) { 1011 move(0, 0); 1012 refresh(); 1013 clrtoeol(); 1014 state->in_search = 1-state->in_search; 1015 bzero(state->pattern, sizeof(state->pattern)); 1016 state->match_direction = MATCH_TINKER_PATTERN_DOWN; 1017 return 0; 1018 } else if (!state->in_search) 1019 return 1; 1020 1021 if (isalnum(c) || isgraph(c) || c == ' ') { 1022 state->pattern[strlen(state->pattern)] = c; 1023 state->pattern[strlen(state->pattern)] = '\0'; 1024 adj_match_dir(&state->match_direction); 1025 *ans = get_mext_match(state->pattern, 1026 state->match_direction); 1027 } else if (key == KEY_DOWN) { 1028 state->match_direction = FIND_NEXT_MATCH_DOWN; 1029 *ans = get_mext_match(state->pattern, 1030 state->match_direction); 1031 } else if (key == KEY_UP) { 1032 state->match_direction = FIND_NEXT_MATCH_UP; 1033 *ans = get_mext_match(state->pattern, 1034 state->match_direction); 1035 } else if (key == KEY_BACKSPACE || key == 8 || key == 127) { 1036 state->pattern[strlen(state->pattern)-1] = '\0'; 1037 adj_match_dir(&state->match_direction); 1038 } else 1039 terminate_search = 1; 1040 1041 if (terminate_search) { 1042 state->in_search = 0; 1043 bzero(state->pattern, sizeof(state->pattern)); 1044 move(0, 0); 1045 refresh(); 1046 clrtoeol(); 1047 return -1; 1048 } 1049 return 0; 1050 } 1051 1052 static void conf(struct menu *menu) 1053 { 1054 selected_conf(menu, NULL); 1055 } 1056 1057 static void selected_conf(struct menu *menu, struct menu *active_menu) 1058 { 1059 struct menu *submenu = NULL; 1060 struct symbol *sym; 1061 int i, res; 1062 int current_index = 0; 1063 int last_top_row = 0; 1064 struct match_state match_state = { 1065 .in_search = 0, 1066 .match_direction = MATCH_TINKER_PATTERN_DOWN, 1067 .pattern = "", 1068 }; 1069 1070 while (!global_exit) { 1071 reset_menu(); 1072 current_menu = menu; 1073 build_conf(menu); 1074 if (!child_count) 1075 break; 1076 1077 if (active_menu != NULL) { 1078 for (i = 0; i < items_num; i++) { 1079 struct mitem *mcur; 1080 1081 mcur = (struct mitem *) item_userptr(curses_menu_items[i]); 1082 if ((struct menu *) mcur->usrptr == active_menu) { 1083 current_index = i; 1084 break; 1085 } 1086 } 1087 active_menu = NULL; 1088 } 1089 1090 show_menu(menu_get_prompt(menu), menu_instructions, 1091 current_index, &last_top_row); 1092 keypad((menu_win(curses_menu)), TRUE); 1093 while (!global_exit) { 1094 if (match_state.in_search) { 1095 mvprintw(0, 0, 1096 "searching: %s", match_state.pattern); 1097 clrtoeol(); 1098 } 1099 refresh_all_windows(main_window); 1100 res = wgetch(menu_win(curses_menu)); 1101 if (!res) 1102 break; 1103 if (do_match(res, &match_state, ¤t_index) == 0) { 1104 if (current_index != -1) 1105 center_item(current_index, 1106 &last_top_row); 1107 continue; 1108 } 1109 if (process_special_keys(&res, 1110 (struct menu *) item_data())) 1111 break; 1112 switch (res) { 1113 case KEY_DOWN: 1114 case 'j': 1115 menu_driver(curses_menu, REQ_DOWN_ITEM); 1116 break; 1117 case KEY_UP: 1118 case 'k': 1119 menu_driver(curses_menu, REQ_UP_ITEM); 1120 break; 1121 case KEY_NPAGE: 1122 menu_driver(curses_menu, REQ_SCR_DPAGE); 1123 break; 1124 case KEY_PPAGE: 1125 menu_driver(curses_menu, REQ_SCR_UPAGE); 1126 break; 1127 case KEY_HOME: 1128 menu_driver(curses_menu, REQ_FIRST_ITEM); 1129 break; 1130 case KEY_END: 1131 menu_driver(curses_menu, REQ_LAST_ITEM); 1132 break; 1133 case 'h': 1134 case '?': 1135 show_help((struct menu *) item_data()); 1136 break; 1137 } 1138 if (res == 10 || res == 27 || 1139 res == 32 || res == 'n' || res == 'y' || 1140 res == KEY_LEFT || res == KEY_RIGHT || 1141 res == 'm') 1142 break; 1143 refresh_all_windows(main_window); 1144 } 1145 1146 refresh_all_windows(main_window); 1147 /* if ESC or left*/ 1148 if (res == 27 || (menu != &rootmenu && res == KEY_LEFT)) 1149 break; 1150 1151 /* remember location in the menu */ 1152 last_top_row = top_row(curses_menu); 1153 current_index = curses_item_index(); 1154 1155 if (!item_tag()) 1156 continue; 1157 1158 submenu = (struct menu *) item_data(); 1159 if (!submenu || !menu_is_visible(submenu)) 1160 continue; 1161 sym = submenu->sym; 1162 1163 switch (res) { 1164 case ' ': 1165 if (item_is_tag('t')) 1166 sym_toggle_tristate_value(sym); 1167 else if (item_is_tag('m')) 1168 conf(submenu); 1169 break; 1170 case KEY_RIGHT: 1171 case 10: /* ENTER WAS PRESSED */ 1172 switch (item_tag()) { 1173 case 'm': 1174 if (single_menu_mode) 1175 submenu->data = 1176 (void *) (long) !submenu->data; 1177 else 1178 conf(submenu); 1179 break; 1180 case 't': 1181 if (sym_is_choice(sym)) 1182 conf_choice(submenu); 1183 else if (submenu->prompt && 1184 submenu->prompt->type == P_MENU) 1185 conf(submenu); 1186 else if (res == 10) 1187 sym_toggle_tristate_value(sym); 1188 break; 1189 case 's': 1190 conf_string(submenu); 1191 break; 1192 } 1193 break; 1194 case 'y': 1195 if (item_is_tag('t')) { 1196 if (sym_set_tristate_value(sym, yes)) 1197 break; 1198 if (sym_set_tristate_value(sym, mod)) 1199 btn_dialog(main_window, setmod_text, 0); 1200 } 1201 break; 1202 case 'n': 1203 if (item_is_tag('t')) 1204 sym_set_tristate_value(sym, no); 1205 break; 1206 case 'm': 1207 if (item_is_tag('t')) 1208 sym_set_tristate_value(sym, mod); 1209 break; 1210 } 1211 } 1212 } 1213 1214 static void conf_message_callback(const char *s) 1215 { 1216 btn_dialog(main_window, s, 1, "<OK>"); 1217 } 1218 1219 static void show_help(struct menu *menu) 1220 { 1221 struct gstr help; 1222 1223 if (!menu) 1224 return; 1225 1226 help = str_new(); 1227 menu_get_ext_help(menu, &help); 1228 show_scroll_win(main_window, menu_get_prompt(menu), str_get(&help)); 1229 str_free(&help); 1230 } 1231 1232 static void conf_choice(struct menu *menu) 1233 { 1234 const char *prompt = menu_get_prompt(menu); 1235 struct menu *child = NULL; 1236 struct symbol *active; 1237 int selected_index = 0; 1238 int last_top_row = 0; 1239 int res, i = 0; 1240 struct match_state match_state = { 1241 .in_search = 0, 1242 .match_direction = MATCH_TINKER_PATTERN_DOWN, 1243 .pattern = "", 1244 }; 1245 1246 active = sym_calc_choice(menu); 1247 /* this is mostly duplicated from the conf() function. */ 1248 while (!global_exit) { 1249 reset_menu(); 1250 1251 for (i = 0, child = menu->list; child; child = child->next) { 1252 if (!show_all_items && !menu_is_visible(child)) 1253 continue; 1254 1255 if (child->sym == sym_calc_choice(menu)) 1256 item_make(child, ':', "<X> %s", 1257 menu_get_prompt(child)); 1258 else if (child->sym) 1259 item_make(child, ':', " %s", 1260 menu_get_prompt(child)); 1261 else 1262 item_make(child, ':', "*** %s ***", 1263 menu_get_prompt(child)); 1264 1265 if (child->sym == active){ 1266 last_top_row = top_row(curses_menu); 1267 selected_index = i; 1268 } 1269 i++; 1270 } 1271 show_menu(prompt ? prompt : "Choice Menu", 1272 radiolist_instructions, 1273 selected_index, 1274 &last_top_row); 1275 while (!global_exit) { 1276 if (match_state.in_search) { 1277 mvprintw(0, 0, "searching: %s", 1278 match_state.pattern); 1279 clrtoeol(); 1280 } 1281 refresh_all_windows(main_window); 1282 res = wgetch(menu_win(curses_menu)); 1283 if (!res) 1284 break; 1285 if (do_match(res, &match_state, &selected_index) == 0) { 1286 if (selected_index != -1) 1287 center_item(selected_index, 1288 &last_top_row); 1289 continue; 1290 } 1291 if (process_special_keys( 1292 &res, 1293 (struct menu *) item_data())) 1294 break; 1295 switch (res) { 1296 case KEY_DOWN: 1297 case 'j': 1298 menu_driver(curses_menu, REQ_DOWN_ITEM); 1299 break; 1300 case KEY_UP: 1301 case 'k': 1302 menu_driver(curses_menu, REQ_UP_ITEM); 1303 break; 1304 case KEY_NPAGE: 1305 menu_driver(curses_menu, REQ_SCR_DPAGE); 1306 break; 1307 case KEY_PPAGE: 1308 menu_driver(curses_menu, REQ_SCR_UPAGE); 1309 break; 1310 case KEY_HOME: 1311 menu_driver(curses_menu, REQ_FIRST_ITEM); 1312 break; 1313 case KEY_END: 1314 menu_driver(curses_menu, REQ_LAST_ITEM); 1315 break; 1316 case 'h': 1317 case '?': 1318 show_help((struct menu *) item_data()); 1319 break; 1320 } 1321 if (res == 10 || res == 27 || res == ' ' || 1322 res == KEY_LEFT){ 1323 break; 1324 } 1325 refresh_all_windows(main_window); 1326 } 1327 /* if ESC or left */ 1328 if (res == 27 || res == KEY_LEFT) 1329 break; 1330 1331 child = item_data(); 1332 if (!child || !menu_is_visible(child) || !child->sym) 1333 continue; 1334 switch (res) { 1335 case ' ': 1336 case 10: 1337 case KEY_RIGHT: 1338 choice_set_value(menu, child->sym); 1339 return; 1340 case 'h': 1341 case '?': 1342 show_help(child); 1343 active = child->sym; 1344 break; 1345 case KEY_EXIT: 1346 return; 1347 } 1348 } 1349 } 1350 1351 static void conf_string(struct menu *menu) 1352 { 1353 const char *prompt = menu_get_prompt(menu); 1354 1355 while (1) { 1356 int res; 1357 const char *heading; 1358 1359 switch (sym_get_type(menu->sym)) { 1360 case S_INT: 1361 heading = inputbox_instructions_int; 1362 break; 1363 case S_HEX: 1364 heading = inputbox_instructions_hex; 1365 break; 1366 case S_STRING: 1367 heading = inputbox_instructions_string; 1368 break; 1369 default: 1370 heading = "Internal nconf error!"; 1371 } 1372 res = dialog_inputbox(main_window, 1373 prompt ? prompt : "Main Menu", 1374 heading, 1375 sym_get_string_value(menu->sym), 1376 &dialog_input_result, 1377 &dialog_input_result_len); 1378 switch (res) { 1379 case 0: 1380 if (sym_set_string_value(menu->sym, 1381 dialog_input_result)) 1382 return; 1383 btn_dialog(main_window, 1384 "You have made an invalid entry.", 0); 1385 break; 1386 case 1: 1387 show_help(menu); 1388 break; 1389 case KEY_EXIT: 1390 return; 1391 } 1392 } 1393 } 1394 1395 static void conf_load(void) 1396 { 1397 while (1) { 1398 int res; 1399 res = dialog_inputbox(main_window, 1400 NULL, load_config_text, 1401 filename, 1402 &dialog_input_result, 1403 &dialog_input_result_len); 1404 switch (res) { 1405 case 0: 1406 if (!dialog_input_result[0]) 1407 return; 1408 if (!conf_read(dialog_input_result)) { 1409 set_config_filename(dialog_input_result); 1410 conf_set_changed(true); 1411 return; 1412 } 1413 btn_dialog(main_window, "File does not exist!", 0); 1414 break; 1415 case 1: 1416 show_scroll_win(main_window, 1417 "Load Alternate Configuration", 1418 load_config_help); 1419 break; 1420 case KEY_EXIT: 1421 return; 1422 } 1423 } 1424 } 1425 1426 static void conf_save(void) 1427 { 1428 while (1) { 1429 int res; 1430 res = dialog_inputbox(main_window, 1431 NULL, save_config_text, 1432 filename, 1433 &dialog_input_result, 1434 &dialog_input_result_len); 1435 switch (res) { 1436 case 0: 1437 if (!dialog_input_result[0]) 1438 return; 1439 res = conf_write(dialog_input_result); 1440 if (!res) { 1441 set_config_filename(dialog_input_result); 1442 return; 1443 } 1444 btn_dialog(main_window, "Can't create file!", 1445 1, "<OK>"); 1446 break; 1447 case 1: 1448 show_scroll_win(main_window, 1449 "Save Alternate Configuration", 1450 save_config_help); 1451 break; 1452 case KEY_EXIT: 1453 return; 1454 } 1455 } 1456 } 1457 1458 static void setup_windows(void) 1459 { 1460 int lines, columns; 1461 1462 getmaxyx(stdscr, lines, columns); 1463 1464 if (main_window != NULL) 1465 delwin(main_window); 1466 1467 /* set up the menu and menu window */ 1468 main_window = newwin(lines-2, columns-2, 2, 1); 1469 keypad(main_window, TRUE); 1470 mwin_max_lines = lines-7; 1471 mwin_max_cols = columns-6; 1472 1473 /* panels order is from bottom to top */ 1474 new_panel(main_window); 1475 } 1476 1477 int main(int ac, char **av) 1478 { 1479 int lines, columns; 1480 char *mode; 1481 1482 setlocale(LC_ALL, ""); 1483 1484 if (ac > 1 && strcmp(av[1], "-s") == 0) { 1485 /* Silence conf_read() until the real callback is set up */ 1486 conf_set_message_callback(NULL); 1487 av++; 1488 } 1489 conf_parse(av[1]); 1490 conf_read(NULL); 1491 1492 mode = getenv("NCONFIG_MODE"); 1493 if (mode) { 1494 if (!strcasecmp(mode, "single_menu")) 1495 single_menu_mode = 1; 1496 } 1497 1498 /* Initialize curses */ 1499 initscr(); 1500 /* set color theme */ 1501 set_colors(); 1502 1503 cbreak(); 1504 noecho(); 1505 keypad(stdscr, TRUE); 1506 curs_set(0); 1507 1508 getmaxyx(stdscr, lines, columns); 1509 if (columns < 75 || lines < 20) { 1510 endwin(); 1511 printf("Your terminal should have at " 1512 "least 20 lines and 75 columns\n"); 1513 return 1; 1514 } 1515 1516 notimeout(stdscr, FALSE); 1517 #if NCURSES_REENTRANT 1518 set_escdelay(1); 1519 #else 1520 ESCDELAY = 1; 1521 #endif 1522 1523 /* set btns menu */ 1524 curses_menu = new_menu(curses_menu_items); 1525 menu_opts_off(curses_menu, O_SHOWDESC); 1526 menu_opts_on(curses_menu, O_SHOWMATCH); 1527 menu_opts_on(curses_menu, O_ONEVALUE); 1528 menu_opts_on(curses_menu, O_NONCYCLIC); 1529 menu_opts_on(curses_menu, O_IGNORECASE); 1530 set_menu_mark(curses_menu, " "); 1531 set_menu_fore(curses_menu, attr_main_menu_fore); 1532 set_menu_back(curses_menu, attr_main_menu_back); 1533 set_menu_grey(curses_menu, attr_main_menu_grey); 1534 1535 set_config_filename(conf_get_configname()); 1536 setup_windows(); 1537 1538 /* check for KEY_FUNC(1) */ 1539 if (has_key(KEY_F(1)) == FALSE) { 1540 show_scroll_win(main_window, 1541 "Instructions", 1542 menu_no_f_instructions); 1543 } 1544 1545 conf_set_message_callback(conf_message_callback); 1546 /* do the work */ 1547 while (!global_exit) { 1548 conf(&rootmenu); 1549 if (!global_exit && do_exit() == 0) 1550 break; 1551 } 1552 /* ok, we are done */ 1553 unpost_menu(curses_menu); 1554 free_menu(curses_menu); 1555 delwin(main_window); 1556 clear(); 1557 refresh(); 1558 endwin(); 1559 return 0; 1560 } 1561