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