Merge/rework config system per the latest from linux-2.6.0-test2.
[people/mcb30/busybox.git] / scripts / config / conf.c
1 /*
2  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3  * Released under the terms of the GNU GPL v2.0.
4  */
5
6 #include <ctype.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <time.h>
11 #include <sys/stat.h>
12
13 #define LKC_DIRECT_LINK
14 #include "lkc.h"
15
16 static void conf(struct menu *menu);
17 static void check_conf(struct menu *menu);
18
19 enum {
20         ask_all,
21         ask_new,
22         ask_silent,
23         set_default,
24         set_yes,
25         set_mod,
26         set_no,
27         set_random
28 } input_mode = ask_all;
29
30 static int indent = 1;
31 static int valid_stdin = 1;
32 static int conf_cnt;
33 static char line[128];
34 static struct menu *rootEntry;
35
36 static char nohelp_text[] = "Sorry, no help available for this option yet.\n";
37
38 static void strip(char *str)
39 {
40         char *p = str;
41         int l;
42
43         while ((isspace(*p)))
44                 p++;
45         l = strlen(p);
46         if (p != str)
47                 memmove(str, p, l + 1);
48         if (!l)
49                 return;
50         p = str + l - 1;
51         while ((isspace(*p)))
52                 *p-- = 0;
53 }
54
55 static void check_stdin(void)
56 {
57         if (!valid_stdin && input_mode == ask_silent) {
58                 printf("aborted!\n\n");
59                 printf("Console input/output is redirected. ");
60                 printf("Run 'make oldconfig' to update configuration.\n\n");
61                 exit(1);
62         }
63 }
64
65 static void conf_askvalue(struct symbol *sym, const char *def)
66 {
67         enum symbol_type type = sym_get_type(sym);
68         tristate val;
69
70         if (!sym_has_value(sym))
71                 printf("(NEW) ");
72
73         line[0] = '\n';
74         line[1] = 0;
75
76         if (!sym_is_changable(sym)) {
77                 printf("%s\n", def);
78                 line[0] = '\n';
79                 line[1] = 0;
80                 return;
81         }
82
83         switch (input_mode) {
84         case ask_new:
85         case ask_silent:
86                 if (sym_has_value(sym)) {
87                         printf("%s\n", def);
88                         return;
89                 }
90                 check_stdin();
91         case ask_all:
92                 fflush(stdout);
93                 fgets(line, 128, stdin);
94                 return;
95         case set_default:
96                 printf("%s\n", def);
97                 return;
98         default:
99                 break;
100         }
101
102         switch (type) {
103         case S_INT:
104         case S_HEX:
105         case S_STRING:
106                 printf("%s\n", def);
107                 return;
108         default:
109                 ;
110         }
111         switch (input_mode) {
112         case set_yes:
113                 if (sym_tristate_within_range(sym, yes)) {
114                         line[0] = 'y';
115                         line[1] = '\n';
116                         line[2] = 0;
117                         break;
118                 }
119         case set_mod:
120                 if (type == S_TRISTATE) {
121                         if (sym_tristate_within_range(sym, mod)) {
122                                 line[0] = 'm';
123                                 line[1] = '\n';
124                                 line[2] = 0;
125                                 break;
126                         }
127                 } else {
128                         if (sym_tristate_within_range(sym, yes)) {
129                                 line[0] = 'y';
130                                 line[1] = '\n';
131                                 line[2] = 0;
132                                 break;
133                         }
134                 }
135         case set_no:
136                 if (sym_tristate_within_range(sym, no)) {
137                         line[0] = 'n';
138                         line[1] = '\n';
139                         line[2] = 0;
140                         break;
141                 }
142         case set_random:
143                 do {
144                         val = (tristate)(random() % 3);
145                 } while (!sym_tristate_within_range(sym, val));
146                 switch (val) {
147                 case no: line[0] = 'n'; break;
148                 case mod: line[0] = 'm'; break;
149                 case yes: line[0] = 'y'; break;
150                 }
151                 line[1] = '\n';
152                 line[2] = 0;
153                 break;
154         default:
155                 break;
156         }
157         printf("%s", line);
158 }
159
160 int conf_string(struct menu *menu)
161 {
162         struct symbol *sym = menu->sym;
163         const char *def, *help;
164
165         while (1) {
166                 printf("%*s%s ", indent - 1, "", menu->prompt->text);
167                 printf("(%s) ", sym->name);
168                 def = sym_get_string_value(sym);
169                 if (sym_get_string_value(sym))
170                         printf("[%s] ", def);
171                 conf_askvalue(sym, def);
172                 switch (line[0]) {
173                 case '\n':
174                         break;
175                 case '?':
176                         /* print help */
177                         if (line[1] == 0) {
178                                 help = nohelp_text;
179                                 if (menu->sym->help)
180                                         help = menu->sym->help;
181                                 printf("\n%s\n", menu->sym->help);
182                                 def = NULL;
183                                 break;
184                         }
185                 default:
186                         line[strlen(line)-1] = 0;
187                         def = line;
188                 }
189                 if (def && sym_set_string_value(sym, def))
190                         return 0;
191         }
192 }
193
194 static int conf_sym(struct menu *menu)
195 {
196         struct symbol *sym = menu->sym;
197         int type;
198         tristate oldval, newval;
199         const char *help;
200
201         while (1) {
202                 printf("%*s%s ", indent - 1, "", menu->prompt->text);
203                 if (sym->name)
204                         printf("(%s) ", sym->name);
205                 type = sym_get_type(sym);
206                 putchar('[');
207                 oldval = sym_get_tristate_value(sym);
208                 switch (oldval) {
209                 case no:
210                         putchar('N');
211                         break;
212                 case mod:
213                         putchar('M');
214                         break;
215                 case yes:
216                         putchar('Y');
217                         break;
218                 }
219                 if (oldval != no && sym_tristate_within_range(sym, no))
220                         printf("/n");
221                 if (oldval != mod && sym_tristate_within_range(sym, mod))
222                         printf("/m");
223                 if (oldval != yes && sym_tristate_within_range(sym, yes))
224                         printf("/y");
225                 if (sym->help)
226                         printf("/?");
227                 printf("] ");
228                 conf_askvalue(sym, sym_get_string_value(sym));
229                 strip(line);
230
231                 switch (line[0]) {
232                 case 'n':
233                 case 'N':
234                         newval = no;
235                         if (!line[1] || !strcmp(&line[1], "o"))
236                                 break;
237                         continue;
238                 case 'm':
239                 case 'M':
240                         newval = mod;
241                         if (!line[1])
242                                 break;
243                         continue;
244                 case 'y':
245                 case 'Y':
246                         newval = yes;
247                         if (!line[1] || !strcmp(&line[1], "es"))
248                                 break;
249                         continue;
250                 case 0:
251                         newval = oldval;
252                         break;
253                 case '?':
254                         goto help;
255                 default:
256                         continue;
257                 }
258                 if (sym_set_tristate_value(sym, newval))
259                         return 0;
260 help:
261                 help = nohelp_text;
262                 if (sym->help)
263                         help = sym->help;
264                 printf("\n%s\n", help);
265         }
266 }
267
268 static int conf_choice(struct menu *menu)
269 {
270         struct symbol *sym, *def_sym;
271         struct menu *child;
272         int type;
273         bool is_new;
274
275         sym = menu->sym;
276         type = sym_get_type(sym);
277         is_new = !sym_has_value(sym);
278         if (sym_is_changable(sym)) {
279                 conf_sym(menu);
280                 sym_calc_value(sym);
281                 switch (sym_get_tristate_value(sym)) {
282                 case no:
283                         return 1;
284                 case mod:
285                         return 0;
286                 case yes:
287                         break;
288                 }
289         } else {
290                 switch (sym_get_tristate_value(sym)) {
291                 case no:
292                         return 1;
293                 case mod:
294                         printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
295                         return 0;
296                 case yes:
297                         break;
298                 }
299         }
300
301         while (1) {
302                 int cnt, def;
303
304                 printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
305                 def_sym = sym_get_choice_value(sym);
306                 cnt = def = 0;
307                 line[0] = '0';
308                 line[1] = 0;
309                 for (child = menu->list; child; child = child->next) {
310                         if (!menu_is_visible(child))
311                                 continue;
312                         if (!child->sym) {
313                                 printf("%*c %s\n", indent, '*', menu_get_prompt(child));
314                                 continue;
315                         }
316                         cnt++;
317                         if (child->sym == def_sym) {
318                                 def = cnt;
319                                 printf("%*c", indent, '>');
320                         } else
321                                 printf("%*c", indent, ' ');
322                         printf(" %d. %s", cnt, menu_get_prompt(child));
323                         if (child->sym->name)
324                                 printf(" (%s)", child->sym->name);
325                         if (!sym_has_value(child->sym))
326                                 printf(" (NEW)");
327                         printf("\n");
328                 }
329                 printf("%*schoice", indent - 1, "");
330                 if (cnt == 1) {
331                         printf("[1]: 1\n");
332                         goto conf_childs;
333                 }
334                 printf("[1-%d", cnt);
335                 if (sym->help)
336                         printf("?");
337                 printf("]: ");
338                 switch (input_mode) {
339                 case ask_new:
340                 case ask_silent:
341                         if (!is_new) {
342                                 cnt = def;
343                                 printf("%d\n", cnt);
344                                 break;
345                         }
346                         check_stdin();
347                 case ask_all:
348                         fflush(stdout);
349                         fgets(line, 128, stdin);
350                         strip(line);
351                         if (line[0] == '?') {
352                                 printf("\n%s\n", menu->sym->help ?
353                                         menu->sym->help : nohelp_text);
354                                 continue;
355                         }
356                         if (!line[0])
357                                 cnt = def;
358                         else if (isdigit(line[0]))
359                                 cnt = atoi(line);
360                         else
361                                 continue;
362                         break;
363                 case set_random:
364                         def = (random() % cnt) + 1;
365                 case set_default:
366                 case set_yes:
367                 case set_mod:
368                 case set_no:
369                         cnt = def;
370                         printf("%d\n", cnt);
371                         break;
372                 }
373
374         conf_childs:
375                 for (child = menu->list; child; child = child->next) {
376                         if (!child->sym || !menu_is_visible(child))
377                                 continue;
378                         if (!--cnt)
379                                 break;
380                 }
381                 if (!child)
382                         continue;
383                 if (line[strlen(line) - 1] == '?') {
384                         printf("\n%s\n", child->sym->help ?
385                                 child->sym->help : nohelp_text);
386                         continue;
387                 }
388                 sym_set_choice_value(sym, child->sym);
389                 if (child->list) {
390                         indent += 2;
391                         conf(child->list);
392                         indent -= 2;
393                 }
394                 return 1;
395         }
396 }
397
398 static void conf(struct menu *menu)
399 {
400         struct symbol *sym;
401         struct property *prop;
402         struct menu *child;
403
404         if (!menu_is_visible(menu))
405                 return;
406
407         sym = menu->sym;
408         prop = menu->prompt;
409         if (prop) {
410                 const char *prompt;
411
412                 switch (prop->type) {
413                 case P_MENU:
414                         if (input_mode == ask_silent && rootEntry != menu) {
415                                 check_conf(menu);
416                                 return;
417                         }
418                 case P_COMMENT:
419                         prompt = menu_get_prompt(menu);
420                         if (prompt)
421                                 printf("%*c\n%*c %s\n%*c\n",
422                                         indent, '*',
423                                         indent, '*', prompt,
424                                         indent, '*');
425                 default:
426                         ;
427                 }
428         }
429
430         if (!sym)
431                 goto conf_childs;
432
433         if (sym_is_choice(sym)) {
434                 conf_choice(menu);
435                 if (sym->curr.tri != mod)
436                         return;
437                 goto conf_childs;
438         }
439
440         switch (sym->type) {
441         case S_INT:
442         case S_HEX:
443         case S_STRING:
444                 conf_string(menu);
445                 break;
446         default:
447                 conf_sym(menu);
448                 break;
449         }
450
451 conf_childs:
452         if (sym)
453                 indent += 2;
454         for (child = menu->list; child; child = child->next)
455                 conf(child);
456         if (sym)
457                 indent -= 2;
458 }
459
460 static void check_conf(struct menu *menu)
461 {
462         struct symbol *sym;
463         struct menu *child;
464
465         if (!menu_is_visible(menu))
466                 return;
467
468         sym = menu->sym;
469         if (sym) {
470                 if (sym_is_changable(sym) && !sym_has_value(sym)) {
471                         if (!conf_cnt++)
472                                 printf("*\n* Restart config...\n*\n");
473                         rootEntry = menu_get_parent_menu(menu);
474                         conf(rootEntry);
475                 }
476                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) != mod)
477                         return;
478         }
479
480         for (child = menu->list; child; child = child->next)
481                 check_conf(child);
482 }
483
484 int main(int ac, char **av)
485 {
486         const char *name;
487         struct stat tmpstat;
488
489         if (ac > 1 && av[1][0] == '-') {
490                 switch (av[1][1]) {
491                 case 'o':
492                         input_mode = ask_new;
493                         break;
494                 case 's':
495                         input_mode = ask_silent;
496                         valid_stdin = isatty(0) && isatty(1) && isatty(2);
497                         break;
498                 case 'd':
499                         input_mode = set_default;
500                         break;
501                 case 'n':
502                         input_mode = set_no;
503                         break;
504                 case 'm':
505                         input_mode = set_mod;
506                         break;
507                 case 'y':
508                         input_mode = set_yes;
509                         break;
510                 case 'r':
511                         input_mode = set_random;
512                         srandom(time(NULL));
513                         break;
514                 case 'h':
515                 case '?':
516                         printf("%s [-o|-s] config\n", av[0]);
517                         exit(0);
518                 }
519                 name = av[2];
520         } else
521                 name = av[1];
522         conf_parse(name);
523         //zconfdump(stdout);
524         switch (input_mode) {
525         case set_default:
526                 name = conf_get_default_confname();
527                 if (conf_read(name)) {
528                         printf("***\n"
529                                 "*** Can't find default configuration \"%s\"!\n"
530                                 "***\n", name);
531                         exit(1);
532                 }
533                 break;
534         case ask_silent:
535                 if (stat(".config", &tmpstat)) {
536                         printf("***\n"
537                                 "*** You have not yet configured BusyBox!\n"
538                                 "***\n"
539                                 "*** Please run some configurator (e.g. \"make config\" or\n"
540                                 "*** \"make oldconfig\" or \"make menuconfig\").\n"
541                                 "***\n");
542                         exit(1);
543                 }
544         case ask_all:
545         case ask_new:
546                 conf_read(NULL);
547                 break;
548         default:
549                 break;
550         }
551
552         if (input_mode != ask_silent) {
553                 rootEntry = &rootmenu;
554                 conf(&rootmenu);
555                 if (input_mode == ask_all) {
556                         input_mode = ask_silent;
557                         valid_stdin = 1;
558                 }
559         }
560         do {
561                 conf_cnt = 0;
562                 check_conf(&rootmenu);
563         } while (conf_cnt);
564         conf_write(NULL);
565         return 0;
566 }