1/* 2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> 3 * Released under the terms of the GNU GPL v2.0. 4 * 5 * Introduced single menu mode (show all sub-menus in one large tree). 6 * 2002-11-06 Petr Baudis <pasky@ucw.cz> 7 * 8 * Directly use liblxdialog library routines. 9 * 2002-11-14 Petr Baudis <pasky@ucw.cz> 10 */ 11 12#include <sys/ioctl.h> 13#include <sys/wait.h> 14#include <sys/termios.h> 15#include <ctype.h> 16#include <errno.h> 17#include <fcntl.h> 18#include <limits.h> 19#include <signal.h> 20#include <stdarg.h> 21#include <stdlib.h> 22#include <string.h> 23#include <termios.h> 24#include <unistd.h> 25 26#include "dialog.h" 27 28#define LKC_DIRECT_LINK 29#include "lkc.h" 30 31static const char menu_instructions[] = 32 "Arrow keys navigate the menu. " 33 "<Enter> selects submenus --->. " 34 "Highlighted letters are hotkeys. " 35 "Pressing <Y> selectes a feature, while <N> will exclude a feature. " 36 "Press <Esc><Esc> to exit, <?> for Help. " 37 "Legend: [*] feature is selected [ ] feature is excluded", 38radiolist_instructions[] = 39 "Use the arrow keys to navigate this window or " 40 "press the hotkey of the item you wish to select " 41 "followed by the <SPACE BAR>. " 42 "Press <?> for additional information about this option.", 43inputbox_instructions_int[] = 44 "Please enter a decimal value. " 45 "Fractions will not be accepted. " 46 "Use the <TAB> key to move from the input field to the buttons below it.", 47inputbox_instructions_hex[] = 48 "Please enter a hexadecimal value. " 49 "Use the <TAB> key to move from the input field to the buttons below it.", 50inputbox_instructions_string[] = 51 "Please enter a string value. " 52 "Use the <TAB> key to move from the input field to the buttons below it.", 53setmod_text[] = 54 "This feature depends on another which has been configured as a module.\n" 55 "As a result, this feature will be built as a module.", 56nohelp_text[] = 57 "There is no help available for this option.\n", 58load_config_text[] = 59 "Enter the name of the configuration file you wish to load. " 60 "Accept the name shown to restore the configuration you " 61 "last retrieved. Leave blank to abort.", 62load_config_help[] = 63 "\n" 64 "For various reasons, one may wish to keep several different\n" 65 "configurations available on a single machine.\n" 66 "\n" 67 "If you have saved a previous configuration in a file other than the\n" 68 "default, entering the name of the file here will allow you\n" 69 "to modify that configuration.\n" 70 "\n" 71 "If you are uncertain, then you have probably never used alternate\n" 72 "configuration files. You should therefor leave this blank to abort.\n", 73save_config_text[] = 74 "Enter a filename to which this configuration should be saved " 75 "as an alternate. Leave blank to abort.", 76save_config_help[] = 77 "\n" 78 "For various reasons, one may wish to keep different\n" 79 "configurations available on a single machine.\n" 80 "\n" 81 "Entering a file name here will allow you to later retrieve, modify\n" 82 "and use the current configuration as an alternate to whatever\n" 83 "configuration options you have selected at that time.\n" 84 "\n" 85 "If you are uncertain what all this means then you should probably\n" 86 "leave this blank.\n" 87; 88 89static char filename[PATH_MAX+1] = ".config"; 90static int indent = 0; 91static struct termios ios_org; 92static int rows, cols; 93struct menu *current_menu; 94static int child_count; 95static int single_menu_mode; 96 97static struct dialog_list_item *items[16384]; 98static int item_no; 99 100static void conf(struct menu *menu); 101static void conf_choice(struct menu *menu); 102static void conf_string(struct menu *menu); 103static void conf_load(void); 104static void conf_save(void); 105static void show_textbox(const char *title, const char *text, int r, int c); 106static void show_helptext(const char *title, const char *text); 107static void show_help(struct menu *menu); 108static void show_readme(void); 109 110static void init_wsize(void) 111{ 112 struct winsize ws; 113 114 if (ioctl(1, TIOCGWINSZ, &ws) == -1) { 115 rows = 24; 116 cols = 80; 117 } else { 118 rows = ws.ws_row; 119 cols = ws.ws_col; 120 } 121 122 if (rows < 19 || cols < 80) { 123 fprintf(stderr, "Your display is too small to run Menuconfig!\n"); 124 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n"); 125 exit(1); 126 } 127 128 rows -= 4; 129 cols -= 5; 130} 131 132static void cinit(void) 133{ 134 item_no = 0; 135} 136 137static void cmake(void) 138{ 139 items[item_no] = malloc(sizeof(struct dialog_list_item)); 140 memset(items[item_no], 0, sizeof(struct dialog_list_item)); 141 items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0; 142 items[item_no]->name = malloc(512); items[item_no]->name[0] = 0; 143 items[item_no]->namelen = 0; 144 item_no++; 145} 146 147static int cprint_name(const char *fmt, ...) 148{ 149 va_list ap; 150 int res; 151 152 if (!item_no) 153 cmake(); 154 va_start(ap, fmt); 155 res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen, 156 512 - items[item_no - 1]->namelen, fmt, ap); 157 if (res > 0) 158 items[item_no - 1]->namelen += res; 159 va_end(ap); 160 161 return res; 162} 163 164static int cprint_tag(const char *fmt, ...) 165{ 166 va_list ap; 167 int res; 168 169 if (!item_no) 170 cmake(); 171 va_start(ap, fmt); 172 res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap); 173 va_end(ap); 174 175 return res; 176} 177 178static void cdone(void) 179{ 180 int i; 181 182 for (i = 0; i < item_no; i++) { 183 free(items[i]->tag); 184 free(items[i]->name); 185 free(items[i]); 186 } 187 188 item_no = 0; 189} 190 191static void build_conf(struct menu *menu) 192{ 193 struct symbol *sym; 194 struct property *prop; 195 struct menu *child; 196 int type, tmp, doint = 2; 197 tristate val; 198 char ch; 199 200 if (!menu_is_visible(menu)) 201 return; 202 203 sym = menu->sym; 204 prop = menu->prompt; 205 if (!sym) { 206 if (prop && menu != current_menu) { 207 const char *prompt = menu_get_prompt(menu); 208 switch (prop->type) { 209 case P_MENU: 210 child_count++; 211 cmake(); 212 cprint_tag("m%p", menu); 213 214 if (single_menu_mode) { 215 cprint_name("%s%*c%s", 216 menu->data ? "-->" : "++>", 217 indent + 1, ' ', prompt); 218 } else { 219 if (menu->parent != &rootmenu) 220 cprint_name(" %*c", indent + 1, ' '); 221 cprint_name("%s --->", prompt); 222 } 223 224 if (single_menu_mode && menu->data) 225 goto conf_childs; 226 return; 227 default: 228 if (prompt) { 229 child_count++; 230 cmake(); 231 cprint_tag(":%p", menu); 232 cprint_name("---%*c%s", indent + 1, ' ', prompt); 233 } 234 } 235 } else 236 doint = 0; 237 goto conf_childs; 238 } 239 240 cmake(); 241 type = sym_get_type(sym); 242 if (sym_is_choice(sym)) { 243 struct symbol *def_sym = sym_get_choice_value(sym); 244 struct menu *def_menu = NULL; 245 246 child_count++; 247 for (child = menu->list; child; child = child->next) { 248 if (menu_is_visible(child) && child->sym == def_sym) 249 def_menu = child; 250 } 251 252 val = sym_get_tristate_value(sym); 253 if (sym_is_changable(sym)) { 254 cprint_tag("t%p", menu); 255 switch (type) { 256 case S_BOOLEAN: 257 cprint_name("[%c]", val == no ? ' ' : '*'); 258 break; 259 case S_TRISTATE: 260 switch (val) { 261 case yes: ch = '*'; break; 262 case mod: ch = 'M'; break; 263 default: ch = ' '; break; 264 } 265 cprint_name("<%c>", ch); 266 break; 267 } 268 } else { 269 cprint_tag("%c%p", def_menu ? 't' : ':', menu); 270 cprint_name(" "); 271 } 272 273 cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); 274 if (val == yes) { 275 if (def_menu) { 276 cprint_name(" (%s)", menu_get_prompt(def_menu)); 277 cprint_name(" --->"); 278 if (def_menu->list) { 279 indent += 2; 280 build_conf(def_menu); 281 indent -= 2; 282 } 283 } 284 return; 285 } 286 } else { 287 child_count++; 288 val = sym_get_tristate_value(sym); 289 if (sym_is_choice_value(sym) && val == yes) { 290 cprint_tag(":%p", menu); 291 cprint_name(" "); 292 } else { 293 switch (type) { 294 case S_BOOLEAN: 295 cprint_tag("t%p", menu); 296 cprint_name("[%c]", val == no ? ' ' : '*'); 297 break; 298 case S_TRISTATE: 299 cprint_tag("t%p", menu); 300 switch (val) { 301 case yes: ch = '*'; break; 302 case mod: ch = 'M'; break; 303 default: ch = ' '; break; 304 } 305 cprint_name("<%c>", ch); 306 break; 307 default: 308 cprint_tag("s%p", menu); 309 tmp = cprint_name("(%s)", sym_get_string_value(sym)); 310 tmp = indent - tmp + 4; 311 if (tmp < 0) 312 tmp = 0; 313 cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu), 314 sym_has_value(sym) ? "" : " (NEW)"); 315 goto conf_childs; 316 } 317 } 318 cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu), 319 sym_has_value(sym) ? "" : " (NEW)"); 320 } 321 322conf_childs: 323 indent += doint; 324 for (child = menu->list; child; child = child->next) 325 build_conf(child); 326 indent -= doint; 327} 328 329static void conf(struct menu *menu) 330{ 331 struct dialog_list_item *active_item = NULL; 332 struct menu *submenu; 333 const char *prompt = menu_get_prompt(menu); 334 struct symbol *sym; 335 char active_entry[40]; 336 int stat, type; 337 338 unlink("lxdialog.scrltmp"); 339 active_entry[0] = 0; 340 while (1) { 341 indent = 0; 342 child_count = 0; 343 current_menu = menu; 344 cdone(); cinit(); 345 build_conf(menu); 346 if (!child_count) 347 break; 348 if (menu == &rootmenu) { 349 cmake(); cprint_tag(":"); cprint_name("--- "); 350 cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File"); 351 cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File"); 352 } 353 dialog_clear(); 354 stat = dialog_menu(prompt ? prompt : "Main Menu", 355 menu_instructions, rows, cols, rows - 10, 356 active_entry, item_no, items); 357 if (stat < 0) 358 return; 359 360 if (stat == 1 || stat == 255) 361 break; 362 363 active_item = first_sel_item(item_no, items); 364 if (!active_item) 365 continue; 366 active_item->selected = 0; 367 strncpy(active_entry, active_item->tag, sizeof(active_entry)); 368 active_entry[sizeof(active_entry)-1] = 0; 369 type = active_entry[0]; 370 if (!type) 371 continue; 372 373 sym = NULL; 374 submenu = NULL; 375 if (sscanf(active_entry + 1, "%p", &submenu) == 1) 376 sym = submenu->sym; 377 378 switch (stat) { 379 case 0: 380 switch (type) { 381 case 'm': 382 if (single_menu_mode) 383 submenu->data = (submenu->data)? NULL : (void *)1; 384 else 385 conf(submenu); 386 break; 387 case 't': 388 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) 389 conf_choice(submenu); 390 break; 391 case 's': 392 conf_string(submenu); 393 break; 394 case 'L': 395 conf_load(); 396 break; 397 case 'S': 398 conf_save(); 399 break; 400 } 401 break; 402 case 2: 403 if (sym) 404 show_help(submenu); 405 else 406 show_readme(); 407 break; 408 case 3: 409 if (type == 't') { 410 if (sym_set_tristate_value(sym, yes)) 411 break; 412 if (sym_set_tristate_value(sym, mod)) 413 show_textbox(NULL, setmod_text, 6, 74); 414 } 415 break; 416 case 4: 417 if (type == 't') 418 sym_set_tristate_value(sym, no); 419 break; 420 case 5: 421 if (type == 't') 422 sym_set_tristate_value(sym, mod); 423 break; 424 case 6: 425 if (type == 't') 426 sym_toggle_tristate_value(sym); 427 else if (type == 'm') 428 conf(submenu); 429 break; 430 } 431 } 432} 433 434static void show_textbox(const char *title, const char *text, int r, int c) 435{ 436 int fd; 437 438 fd = creat(".help.tmp", 0777); 439 write(fd, text, strlen(text)); 440 close(fd); 441 while (dialog_textbox(title, ".help.tmp", r, c) < 0) 442 ; 443 unlink(".help.tmp"); 444} 445 446static void show_helptext(const char *title, const char *text) 447{ 448 show_textbox(title, text, rows, cols); 449} 450 451static void show_help(struct menu *menu) 452{ 453 const char *help; 454 char *helptext; 455 struct symbol *sym = menu->sym; 456 457 help = sym->help; 458 if (!help) 459 help = nohelp_text; 460 if (sym->name) { 461 helptext = malloc(strlen(sym->name) + strlen(help) + 16); 462 sprintf(helptext, "%s:\n\n%s", sym->name, help); 463 show_helptext(menu_get_prompt(menu), helptext); 464 free(helptext); 465 } else 466 show_helptext(menu_get_prompt(menu), help); 467} 468 469static void show_readme(void) 470{ 471 while (dialog_textbox(NULL, "config/README.Menuconfig", rows, cols) < 0) 472 ; 473} 474 475static void conf_choice(struct menu *menu) 476{ 477 const char *prompt = menu_get_prompt(menu); 478 struct menu *child; 479 struct symbol *active; 480 481 while (1) { 482 current_menu = menu; 483 active = sym_get_choice_value(menu->sym); 484 cdone(); cinit(); 485 for (child = menu->list; child; child = child->next) { 486 if (!menu_is_visible(child)) 487 continue; 488 cmake(); 489 cprint_tag("%p", child); 490 cprint_name("%s", menu_get_prompt(child)); 491 items[item_no - 1]->selected = (child->sym == active); 492 } 493 494 switch (dialog_checklist(prompt ? prompt : "Main Menu", 495 radiolist_instructions, 15, 70, 6, 496 item_no, items, FLAG_RADIO)) { 497 case 0: 498 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &menu) != 1) 499 break; 500 sym_set_tristate_value(menu->sym, yes); 501 return; 502 case 1: 503 show_help(menu); 504 break; 505 case 255: 506 return; 507 } 508 } 509} 510 511static void conf_string(struct menu *menu) 512{ 513 const char *prompt = menu_get_prompt(menu); 514 515 while (1) { 516 char *heading; 517 518 switch (sym_get_type(menu->sym)) { 519 case S_INT: 520 heading = (char *) inputbox_instructions_int; 521 break; 522 case S_HEX: 523 heading = (char *) inputbox_instructions_hex; 524 break; 525 case S_STRING: 526 heading = (char *) inputbox_instructions_string; 527 break; 528 default: 529 heading = "Internal mconf error!"; 530 /* panic? */; 531 } 532 533 switch (dialog_inputbox(prompt ? prompt : "Main Menu", 534 heading, 10, 75, 535 sym_get_string_value(menu->sym))) { 536 case 0: 537 if (sym_set_string_value(menu->sym, dialog_input_result)) 538 return; 539 show_textbox(NULL, "You have made an invalid entry.", 5, 43); 540 break; 541 case 1: 542 show_help(menu); 543 break; 544 case 255: 545 return; 546 } 547 } 548} 549 550static void conf_load(void) 551{ 552 while (1) { 553 switch (dialog_inputbox(NULL, load_config_text, 11, 55, 554 filename)) { 555 case 0: 556 if (!dialog_input_result[0]) 557 return; 558 if (!conf_read(dialog_input_result)) 559 return; 560 show_textbox(NULL, "File does not exist!", 5, 38); 561 break; 562 case 1: 563 show_helptext("Load Alternate Configuration", load_config_help); 564 break; 565 case 255: 566 return; 567 } 568 } 569} 570 571static void conf_save(void) 572{ 573 while (1) { 574 switch (dialog_inputbox(NULL, save_config_text, 11, 55, 575 filename)) { 576 case 0: 577 if (!dialog_input_result[0]) 578 return; 579 if (!conf_write(dialog_input_result)) 580 return; 581 show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60); 582 break; 583 case 1: 584 show_helptext("Save Alternate Configuration", save_config_help); 585 break; 586 case 255: 587 return; 588 } 589 } 590} 591 592static void conf_cleanup(void) 593{ 594 tcsetattr(1, TCSAFLUSH, &ios_org); 595 unlink(".help.tmp"); 596 unlink("lxdialog.scrltmp"); 597} 598 599static void winch_handler(int sig) 600{ 601 struct winsize ws; 602 603 if (ioctl(1, TIOCGWINSZ, &ws) == -1) { 604 rows = 24; 605 cols = 80; 606 } else { 607 rows = ws.ws_row; 608 cols = ws.ws_col; 609 } 610 611 if (rows < 19 || cols < 80) { 612 end_dialog(); 613 fprintf(stderr, "Your display is too small to run Menuconfig!\n"); 614 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n"); 615 exit(1); 616 } 617 618 rows -= 4; 619 cols -= 5; 620 621} 622 623int main(int ac, char **av) 624{ 625 int stat; 626 char *mode; 627 struct symbol *sym; 628 629 conf_parse(av[1]); 630 conf_read(NULL); 631 632 backtitle = malloc(128); 633 sym = sym_lookup("VERSION", 0); 634 sym_calc_value(sym); 635 snprintf(backtitle, 128, "Tomato Configuration"); 636 637 mode = getenv("MENUCONFIG_MODE"); 638 if (mode) { 639 if (!strcasecmp(mode, "single_menu")) 640 single_menu_mode = 1; 641 } 642 643 tcgetattr(1, &ios_org); 644 atexit(conf_cleanup); 645 init_wsize(); 646 init_dialog(); 647 signal(SIGWINCH, winch_handler); 648 conf(&rootmenu); 649 end_dialog(); 650 651 /* Restart dialog to act more like when lxdialog was still separate */ 652 init_dialog(); 653 do { 654 stat = dialog_yesno(NULL, 655 "Do you wish to save your new configuration?", 5, 60); 656 } while (stat < 0); 657 end_dialog(); 658 659 if (stat == 0) { 660 conf_write(NULL); 661 printf("\n\n" 662 "*** End of configuration.\n" 663 "*** Check the top-level Makefile for additional configuration options.\n\n"); 664 } else 665 printf("\n\nYour configuration changes were NOT saved.\n\n"); 666 667 return 0; 668} 669