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