blob: d3afdbea6530ee6891b27b9e1fbae2aaa045fcbe [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"
Patrick Georgi1215cc72022-10-28 01:00:26 +020057"Linewise up <Up> <k>\n"
58"Linewise down <Down> <j>\n"
Patrick Georgid5208402014-04-11 20:24:06 +020059"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:
Patrick Georgi1215cc72022-10-28 01:00:26 +02001121 case 'j':
Patrick Georgid5208402014-04-11 20:24:06 +02001122 menu_driver(curses_menu, REQ_DOWN_ITEM);
1123 break;
1124 case KEY_UP:
Patrick Georgi1215cc72022-10-28 01:00:26 +02001125 case 'k':
Patrick Georgid5208402014-04-11 20:24:06 +02001126 menu_driver(curses_menu, REQ_UP_ITEM);
1127 break;
1128 case KEY_NPAGE:
1129 menu_driver(curses_menu, REQ_SCR_DPAGE);
1130 break;
1131 case KEY_PPAGE:
1132 menu_driver(curses_menu, REQ_SCR_UPAGE);
1133 break;
1134 case KEY_HOME:
1135 menu_driver(curses_menu, REQ_FIRST_ITEM);
1136 break;
1137 case KEY_END:
1138 menu_driver(curses_menu, REQ_LAST_ITEM);
1139 break;
1140 case 'h':
1141 case '?':
1142 show_help((struct menu *) item_data());
1143 break;
1144 }
1145 if (res == 10 || res == 27 ||
1146 res == 32 || res == 'n' || res == 'y' ||
1147 res == KEY_LEFT || res == KEY_RIGHT ||
1148 res == 'm')
1149 break;
1150 refresh_all_windows(main_window);
1151 }
1152
1153 refresh_all_windows(main_window);
1154 /* if ESC or left*/
1155 if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1156 break;
1157
1158 /* remember location in the menu */
1159 last_top_row = top_row(curses_menu);
1160 current_index = curses_item_index();
1161
1162 if (!item_tag())
1163 continue;
1164
1165 submenu = (struct menu *) item_data();
1166 if (!submenu || !menu_is_visible(submenu))
1167 continue;
1168 sym = submenu->sym;
1169
1170 switch (res) {
1171 case ' ':
1172 if (item_is_tag('t'))
1173 sym_toggle_tristate_value(sym);
1174 else if (item_is_tag('m'))
1175 conf(submenu);
1176 break;
1177 case KEY_RIGHT:
1178 case 10: /* ENTER WAS PRESSED */
1179 switch (item_tag()) {
1180 case 'm':
1181 if (single_menu_mode)
1182 submenu->data =
1183 (void *) (long) !submenu->data;
1184 else
1185 conf(submenu);
1186 break;
1187 case 't':
1188 if (sym_is_choice(sym) &&
1189 sym_get_tristate_value(sym) == yes)
1190 conf_choice(submenu);
1191 else if (submenu->prompt &&
1192 submenu->prompt->type == P_MENU)
1193 conf(submenu);
1194 else if (res == 10)
1195 sym_toggle_tristate_value(sym);
1196 break;
1197 case 's':
1198 conf_string(submenu);
1199 break;
1200 }
1201 break;
1202 case 'y':
1203 if (item_is_tag('t')) {
1204 if (sym_set_tristate_value(sym, yes))
1205 break;
1206 if (sym_set_tristate_value(sym, mod))
1207 btn_dialog(main_window, setmod_text, 0);
1208 }
1209 break;
1210 case 'n':
1211 if (item_is_tag('t'))
1212 sym_set_tristate_value(sym, no);
1213 break;
1214 case 'm':
1215 if (item_is_tag('t'))
1216 sym_set_tristate_value(sym, mod);
1217 break;
1218 }
1219 }
1220}
1221
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001222static void conf_message_callback(const char *s)
Patrick Georgid5208402014-04-11 20:24:06 +02001223{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001224 btn_dialog(main_window, s, 1, "<OK>");
Patrick Georgid5208402014-04-11 20:24:06 +02001225}
1226
1227static void show_help(struct menu *menu)
1228{
1229 struct gstr help;
1230
1231 if (!menu)
1232 return;
1233
1234 help = str_new();
1235 menu_get_ext_help(menu, &help);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001236 show_scroll_win(main_window, menu_get_prompt(menu), str_get(&help));
Patrick Georgid5208402014-04-11 20:24:06 +02001237 str_free(&help);
1238}
1239
1240static void conf_choice(struct menu *menu)
1241{
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001242 const char *prompt = menu_get_prompt(menu);
Elyes HAOUAS1d3fde42018-06-05 08:41:29 +02001243 struct menu *child = NULL;
Patrick Georgid5208402014-04-11 20:24:06 +02001244 struct symbol *active;
1245 int selected_index = 0;
1246 int last_top_row = 0;
1247 int res, i = 0;
1248 struct match_state match_state = {
1249 .in_search = 0,
1250 .match_direction = MATCH_TINKER_PATTERN_DOWN,
1251 .pattern = "",
1252 };
1253
1254 active = sym_get_choice_value(menu->sym);
1255 /* this is mostly duplicated from the conf() function. */
1256 while (!global_exit) {
1257 reset_menu();
1258
1259 for (i = 0, child = menu->list; child; child = child->next) {
1260 if (!show_all_items && !menu_is_visible(child))
1261 continue;
1262
1263 if (child->sym == sym_get_choice_value(menu->sym))
1264 item_make(child, ':', "<X> %s",
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001265 menu_get_prompt(child));
Patrick Georgid5208402014-04-11 20:24:06 +02001266 else if (child->sym)
1267 item_make(child, ':', " %s",
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001268 menu_get_prompt(child));
Patrick Georgid5208402014-04-11 20:24:06 +02001269 else
1270 item_make(child, ':', "*** %s ***",
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001271 menu_get_prompt(child));
Patrick Georgid5208402014-04-11 20:24:06 +02001272
1273 if (child->sym == active){
1274 last_top_row = top_row(curses_menu);
1275 selected_index = i;
1276 }
1277 i++;
1278 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001279 show_menu(prompt ? prompt : "Choice Menu",
1280 radiolist_instructions,
Patrick Georgid5208402014-04-11 20:24:06 +02001281 selected_index,
1282 &last_top_row);
1283 while (!global_exit) {
1284 if (match_state.in_search) {
1285 mvprintw(0, 0, "searching: %s",
1286 match_state.pattern);
1287 clrtoeol();
1288 }
1289 refresh_all_windows(main_window);
1290 res = wgetch(menu_win(curses_menu));
1291 if (!res)
1292 break;
1293 if (do_match(res, &match_state, &selected_index) == 0) {
1294 if (selected_index != -1)
1295 center_item(selected_index,
1296 &last_top_row);
1297 continue;
1298 }
1299 if (process_special_keys(
1300 &res,
1301 (struct menu *) item_data()))
1302 break;
1303 switch (res) {
1304 case KEY_DOWN:
Patrick Georgi1215cc72022-10-28 01:00:26 +02001305 case 'j':
Patrick Georgid5208402014-04-11 20:24:06 +02001306 menu_driver(curses_menu, REQ_DOWN_ITEM);
1307 break;
1308 case KEY_UP:
Patrick Georgi1215cc72022-10-28 01:00:26 +02001309 case 'k':
Patrick Georgid5208402014-04-11 20:24:06 +02001310 menu_driver(curses_menu, REQ_UP_ITEM);
1311 break;
1312 case KEY_NPAGE:
1313 menu_driver(curses_menu, REQ_SCR_DPAGE);
1314 break;
1315 case KEY_PPAGE:
1316 menu_driver(curses_menu, REQ_SCR_UPAGE);
1317 break;
1318 case KEY_HOME:
1319 menu_driver(curses_menu, REQ_FIRST_ITEM);
1320 break;
1321 case KEY_END:
1322 menu_driver(curses_menu, REQ_LAST_ITEM);
1323 break;
1324 case 'h':
1325 case '?':
1326 show_help((struct menu *) item_data());
1327 break;
1328 }
1329 if (res == 10 || res == 27 || res == ' ' ||
1330 res == KEY_LEFT){
1331 break;
1332 }
1333 refresh_all_windows(main_window);
1334 }
1335 /* if ESC or left */
1336 if (res == 27 || res == KEY_LEFT)
1337 break;
1338
1339 child = item_data();
1340 if (!child || !menu_is_visible(child) || !child->sym)
1341 continue;
1342 switch (res) {
1343 case ' ':
1344 case 10:
1345 case KEY_RIGHT:
1346 sym_set_tristate_value(child->sym, yes);
1347 return;
1348 case 'h':
1349 case '?':
1350 show_help(child);
1351 active = child->sym;
1352 break;
1353 case KEY_EXIT:
1354 return;
1355 }
1356 }
1357}
1358
1359static void conf_string(struct menu *menu)
1360{
1361 const char *prompt = menu_get_prompt(menu);
1362
1363 while (1) {
1364 int res;
1365 const char *heading;
1366
1367 switch (sym_get_type(menu->sym)) {
1368 case S_INT:
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001369 heading = inputbox_instructions_int;
Patrick Georgid5208402014-04-11 20:24:06 +02001370 break;
1371 case S_HEX:
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001372 heading = inputbox_instructions_hex;
Patrick Georgid5208402014-04-11 20:24:06 +02001373 break;
1374 case S_STRING:
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001375 heading = inputbox_instructions_string;
Patrick Georgid5208402014-04-11 20:24:06 +02001376 break;
1377 default:
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001378 heading = "Internal nconf error!";
Patrick Georgid5208402014-04-11 20:24:06 +02001379 }
1380 res = dialog_inputbox(main_window,
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001381 prompt ? prompt : "Main Menu",
Patrick Georgid5208402014-04-11 20:24:06 +02001382 heading,
1383 sym_get_string_value(menu->sym),
1384 &dialog_input_result,
1385 &dialog_input_result_len);
1386 switch (res) {
1387 case 0:
1388 if (sym_set_string_value(menu->sym,
1389 dialog_input_result))
1390 return;
1391 btn_dialog(main_window,
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001392 "You have made an invalid entry.", 0);
Patrick Georgid5208402014-04-11 20:24:06 +02001393 break;
1394 case 1:
1395 show_help(menu);
1396 break;
1397 case KEY_EXIT:
1398 return;
1399 }
1400 }
1401}
1402
1403static void conf_load(void)
1404{
1405 while (1) {
1406 int res;
1407 res = dialog_inputbox(main_window,
1408 NULL, load_config_text,
1409 filename,
1410 &dialog_input_result,
1411 &dialog_input_result_len);
1412 switch (res) {
1413 case 0:
1414 if (!dialog_input_result[0])
1415 return;
1416 if (!conf_read(dialog_input_result)) {
1417 set_config_filename(dialog_input_result);
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001418 conf_set_changed(true);
Patrick Georgid5208402014-04-11 20:24:06 +02001419 return;
1420 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001421 btn_dialog(main_window, "File does not exist!", 0);
Patrick Georgid5208402014-04-11 20:24:06 +02001422 break;
1423 case 1:
1424 show_scroll_win(main_window,
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001425 "Load Alternate Configuration",
Patrick Georgid5208402014-04-11 20:24:06 +02001426 load_config_help);
1427 break;
1428 case KEY_EXIT:
1429 return;
1430 }
1431 }
1432}
1433
1434static void conf_save(void)
1435{
1436 while (1) {
1437 int res;
1438 res = dialog_inputbox(main_window,
1439 NULL, save_config_text,
1440 filename,
1441 &dialog_input_result,
1442 &dialog_input_result_len);
1443 switch (res) {
1444 case 0:
1445 if (!dialog_input_result[0])
1446 return;
1447 res = conf_write(dialog_input_result);
1448 if (!res) {
1449 set_config_filename(dialog_input_result);
1450 return;
1451 }
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001452 btn_dialog(main_window, "Can't create file!",
Patrick Georgid5208402014-04-11 20:24:06 +02001453 1, "<OK>");
1454 break;
1455 case 1:
1456 show_scroll_win(main_window,
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001457 "Save Alternate Configuration",
Patrick Georgid5208402014-04-11 20:24:06 +02001458 save_config_help);
1459 break;
1460 case KEY_EXIT:
1461 return;
1462 }
1463 }
1464}
1465
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001466static void setup_windows(void)
Patrick Georgid5208402014-04-11 20:24:06 +02001467{
1468 int lines, columns;
1469
1470 getmaxyx(stdscr, lines, columns);
1471
1472 if (main_window != NULL)
1473 delwin(main_window);
1474
1475 /* set up the menu and menu window */
1476 main_window = newwin(lines-2, columns-2, 2, 1);
1477 keypad(main_window, TRUE);
1478 mwin_max_lines = lines-7;
1479 mwin_max_cols = columns-6;
1480
1481 /* panels order is from bottom to top */
1482 new_panel(main_window);
1483}
1484
1485int main(int ac, char **av)
1486{
1487 int lines, columns;
1488 char *mode;
1489
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001490 if (ac > 1 && strcmp(av[1], "-s") == 0) {
1491 /* Silence conf_read() until the real callback is set up */
1492 conf_set_message_callback(NULL);
1493 av++;
1494 }
Patrick Georgid5208402014-04-11 20:24:06 +02001495 conf_parse(av[1]);
1496 conf_read(NULL);
1497
1498 mode = getenv("NCONFIG_MODE");
1499 if (mode) {
1500 if (!strcasecmp(mode, "single_menu"))
1501 single_menu_mode = 1;
1502 }
1503
1504 /* Initialize curses */
1505 initscr();
1506 /* set color theme */
1507 set_colors();
1508
1509 cbreak();
1510 noecho();
1511 keypad(stdscr, TRUE);
1512 curs_set(0);
1513
1514 getmaxyx(stdscr, lines, columns);
1515 if (columns < 75 || lines < 20) {
1516 endwin();
1517 printf("Your terminal should have at "
1518 "least 20 lines and 75 columns\n");
1519 return 1;
1520 }
1521
1522 notimeout(stdscr, FALSE);
1523#if NCURSES_REENTRANT
1524 set_escdelay(1);
1525#else
1526 ESCDELAY = 1;
1527#endif
1528
1529 /* set btns menu */
1530 curses_menu = new_menu(curses_menu_items);
1531 menu_opts_off(curses_menu, O_SHOWDESC);
1532 menu_opts_on(curses_menu, O_SHOWMATCH);
1533 menu_opts_on(curses_menu, O_ONEVALUE);
1534 menu_opts_on(curses_menu, O_NONCYCLIC);
1535 menu_opts_on(curses_menu, O_IGNORECASE);
1536 set_menu_mark(curses_menu, " ");
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001537 set_menu_fore(curses_menu, attr_main_menu_fore);
1538 set_menu_back(curses_menu, attr_main_menu_back);
1539 set_menu_grey(curses_menu, attr_main_menu_grey);
Patrick Georgid5208402014-04-11 20:24:06 +02001540
1541 set_config_filename(conf_get_configname());
1542 setup_windows();
1543
1544 /* check for KEY_FUNC(1) */
1545 if (has_key(KEY_F(1)) == FALSE) {
1546 show_scroll_win(main_window,
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001547 "Instructions",
1548 menu_no_f_instructions);
Patrick Georgid5208402014-04-11 20:24:06 +02001549 }
1550
1551 conf_set_message_callback(conf_message_callback);
1552 /* do the work */
1553 while (!global_exit) {
1554 conf(&rootmenu);
1555 if (!global_exit && do_exit() == 0)
1556 break;
1557 }
1558 /* ok, we are done */
1559 unpost_menu(curses_menu);
1560 free_menu(curses_menu);
1561 delwin(main_window);
1562 clear();
1563 refresh();
1564 endwin();
1565 return 0;
1566}