Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
4 */
5%{
6
7#include <ctype.h>
8#include <stdarg.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <stdbool.h>
13
14#include "lkc.h"
15#include "internal.h"
16#include "preprocess.h"
17
18#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
19
20#define PRINTD 0x0001
21#define DEBUG_PARSE 0x0002
22
23int cdebug = PRINTD;
24
25static void yyerror(const char *err);
26static void zconfprint(const char *err, ...);
27static void zconf_error(const char *err, ...);
28static bool zconf_endtoken(const char *tokenname,
29 const char *expected_tokenname);
30
31struct menu *current_menu, *current_entry;
32
33static bool inside_choice = false;
34
35%}
36
37%union
38{
39 char *string;
40 struct symbol *symbol;
41 struct expr *expr;
42 struct menu *menu;
43 enum symbol_type type;
44 enum variable_flavor flavor;
45}
46
47%token <string> T_HELPTEXT
48%token <string> T_WORD
49%token <string> T_WORD_QUOTE
50%token T_BOOL
51%token T_CHOICE
52%token T_CLOSE_PAREN
53%token T_COLON_EQUAL
54%token T_COMMENT
55%token T_CONFIG
56%token T_DEFAULT
57%token T_DEF_BOOL
58%token T_DEF_TRISTATE
59%token T_DEPENDS
60%token T_ENDCHOICE
61%token T_ENDIF
62%token T_ENDMENU
63%token T_HELP
64%token T_HEX
65%token T_IF
66%token T_IMPLY
67%token T_INT
68%token T_MAINMENU
69%token T_MENU
70%token T_MENUCONFIG
71%token T_MODULES
72%token T_ON
73%token T_OPEN_PAREN
74%token T_PLUS_EQUAL
75%token T_PROMPT
76%token T_RANGE
77%token T_SELECT
78%token T_SOURCE
79%token T_STRING
80%token T_TRISTATE
81%token T_VISIBLE
82%token T_EOL
83%token <string> T_ASSIGN_VAL
84
85%left T_OR
86%left T_AND
87%left T_EQUAL T_UNEQUAL
88%left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL
89%nonassoc T_NOT
90
91%type <symbol> nonconst_symbol
92%type <symbol> symbol
93%type <type> type logic_type default
94%type <expr> expr
95%type <expr> if_expr
96%type <string> end
97%type <menu> if_entry menu_entry choice_entry
98%type <string> assign_val
99%type <flavor> assign_op
100
101%destructor {
102 fprintf(stderr, "%s:%d: missing end statement for this entry\n",
103 $$->filename, $$->lineno);
104 if (current_menu == $$)
105 menu_end_menu();
106} if_entry menu_entry choice_entry
107
108%%
109input: mainmenu_stmt stmt_list | stmt_list;
110
111/* mainmenu entry */
112
113mainmenu_stmt: T_MAINMENU T_WORD_QUOTE T_EOL
114{
115 menu_add_prompt(P_MENU, $2, NULL);
116};
117
118stmt_list:
119 /* empty */
120 | stmt_list assignment_stmt
121 | stmt_list choice_stmt
122 | stmt_list comment_stmt
123 | stmt_list config_stmt
124 | stmt_list if_stmt
125 | stmt_list menu_stmt
126 | stmt_list menuconfig_stmt
127 | stmt_list source_stmt
128 | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); }
129 | stmt_list error T_EOL { zconf_error("invalid statement"); }
130;
131
132stmt_list_in_choice:
133 /* empty */
134 | stmt_list_in_choice comment_stmt
135 | stmt_list_in_choice config_stmt
136 | stmt_list_in_choice if_stmt_in_choice
137 | stmt_list_in_choice error T_EOL { zconf_error("invalid statement"); }
138;
139
140/* config/menuconfig entry */
141
142config_entry_start: T_CONFIG nonconst_symbol T_EOL
143{
144 menu_add_entry($2);
145 printd(DEBUG_PARSE, "%s:%d:config %s\n", cur_filename, cur_lineno, $2->name);
146};
147
148config_stmt: config_entry_start config_option_list
149{
150 if (inside_choice) {
151 if (!current_entry->prompt) {
152 fprintf(stderr, "%s:%d: error: choice member must have a prompt\n",
153 current_entry->filename, current_entry->lineno);
154 yynerrs++;
155 }
156 }
157
158 printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
159};
160
161menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL
162{
163 menu_add_entry($2);
164 printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", cur_filename, cur_lineno, $2->name);
165};
166
167menuconfig_stmt: menuconfig_entry_start config_option_list
168{
169 if (current_entry->prompt)
170 current_entry->prompt->type = P_MENU;
171 else
172 zconfprint("warning: menuconfig statement without prompt");
173 printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
174};
175
176config_option_list:
177 /* empty */
178 | config_option_list config_option
179 | config_option_list depends
180 | config_option_list help
181;
182
183config_option: type prompt_stmt_opt T_EOL
184{
185 menu_set_type($1);
186 printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1);
187};
188
189config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
190{
191 menu_add_prompt(P_PROMPT, $2, $3);
192 printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
193};
194
195config_option: default expr if_expr T_EOL
196{
197 menu_add_expr(P_DEFAULT, $2, $3);
198 if ($1 != S_UNKNOWN)
199 menu_set_type($1);
200 printd(DEBUG_PARSE, "%s:%d:default(%u)\n", cur_filename, cur_lineno,
201 $1);
202};
203
204config_option: T_SELECT nonconst_symbol if_expr T_EOL
205{
206 menu_add_symbol(P_SELECT, $2, $3);
207 printd(DEBUG_PARSE, "%s:%d:select\n", cur_filename, cur_lineno);
208};
209
210config_option: T_IMPLY nonconst_symbol if_expr T_EOL
211{
212 menu_add_symbol(P_IMPLY, $2, $3);
213 printd(DEBUG_PARSE, "%s:%d:imply\n", cur_filename, cur_lineno);
214};
215
216config_option: T_RANGE symbol symbol if_expr T_EOL
217{
218 menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
219 printd(DEBUG_PARSE, "%s:%d:range\n", cur_filename, cur_lineno);
220};
221
222config_option: T_MODULES T_EOL
223{
224 if (modules_sym)
225 zconf_error("symbol '%s' redefines option 'modules' already defined by symbol '%s'",
226 current_entry->sym->name, modules_sym->name);
227 modules_sym = current_entry->sym;
228};
229
230/* choice entry */
231
232choice: T_CHOICE T_EOL
233{
234 struct symbol *sym = sym_lookup(NULL, 0);
235
236 menu_add_entry(sym);
237 menu_add_expr(P_CHOICE, NULL, NULL);
238 printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno);
239};
240
241choice_entry: choice choice_option_list
242{
243 if (!current_entry->prompt) {
244 fprintf(stderr, "%s:%d: error: choice must have a prompt\n",
245 current_entry->filename, current_entry->lineno);
246 yynerrs++;
247 }
248
249 $$ = menu_add_menu();
250
251 inside_choice = true;
252};
253
254choice_end: end
255{
256 inside_choice = false;
257
258 if (zconf_endtoken($1, "choice")) {
259 menu_end_menu();
260 printd(DEBUG_PARSE, "%s:%d:endchoice\n", cur_filename, cur_lineno);
261 }
262};
263
264choice_stmt: choice_entry stmt_list_in_choice choice_end
265;
266
267choice_option_list:
268 /* empty */
269 | choice_option_list choice_option
270 | choice_option_list depends
271 | choice_option_list help
272;
273
274choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
275{
276 menu_add_prompt(P_PROMPT, $2, $3);
277 printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
278};
279
280choice_option: logic_type prompt_stmt_opt T_EOL
281{
282 menu_set_type($1);
283 printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1);
284};
285
286choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
287{
288 menu_add_symbol(P_DEFAULT, $2, $3);
289 printd(DEBUG_PARSE, "%s:%d:default\n", cur_filename, cur_lineno);
290};
291
292type:
293 logic_type
294 | T_INT { $$ = S_INT; }
295 | T_HEX { $$ = S_HEX; }
296 | T_STRING { $$ = S_STRING; }
297
298logic_type:
299 T_BOOL { $$ = S_BOOLEAN; }
300 | T_TRISTATE { $$ = S_TRISTATE; }
301
302default:
303 T_DEFAULT { $$ = S_UNKNOWN; }
304 | T_DEF_BOOL { $$ = S_BOOLEAN; }
305 | T_DEF_TRISTATE { $$ = S_TRISTATE; }
306
307/* if entry */
308
309if_entry: T_IF expr T_EOL
310{
311 printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno);
312 menu_add_entry(NULL);
313 menu_add_dep($2);
314 $$ = menu_add_menu();
315};
316
317if_end: end
318{
319 if (zconf_endtoken($1, "if")) {
320 menu_end_menu();
321 printd(DEBUG_PARSE, "%s:%d:endif\n", cur_filename, cur_lineno);
322 }
323};
324
325if_stmt: if_entry stmt_list if_end
326;
327
328if_stmt_in_choice: if_entry stmt_list_in_choice if_end
329;
330
331/* menu entry */
332
333menu: T_MENU T_WORD_QUOTE T_EOL
334{
335 menu_add_entry(NULL);
336 menu_add_prompt(P_MENU, $2, NULL);
337 printd(DEBUG_PARSE, "%s:%d:menu\n", cur_filename, cur_lineno);
338};
339
340menu_entry: menu menu_option_list
341{
342 $$ = menu_add_menu();
343};
344
345menu_end: end
346{
347 if (zconf_endtoken($1, "menu")) {
348 menu_end_menu();
349 printd(DEBUG_PARSE, "%s:%d:endmenu\n", cur_filename, cur_lineno);
350 }
351};
352
353menu_stmt: menu_entry stmt_list menu_end
354;
355
356menu_option_list:
357 /* empty */
358 | menu_option_list visible
359 | menu_option_list depends
360;
361
362source_stmt: T_SOURCE T_WORD_QUOTE T_EOL
363{
364 printd(DEBUG_PARSE, "%s:%d:source %s\n", cur_filename, cur_lineno, $2);
365 zconf_nextfile($2);
366 free($2);
367};
368
369/* comment entry */
370
371comment: T_COMMENT T_WORD_QUOTE T_EOL
372{
373 menu_add_entry(NULL);
374 menu_add_prompt(P_COMMENT, $2, NULL);
375 printd(DEBUG_PARSE, "%s:%d:comment\n", cur_filename, cur_lineno);
376};
377
378comment_stmt: comment comment_option_list
379;
380
381comment_option_list:
382 /* empty */
383 | comment_option_list depends
384;
385
386/* help option */
387
388help_start: T_HELP T_EOL
389{
390 printd(DEBUG_PARSE, "%s:%d:help\n", cur_filename, cur_lineno);
391 zconf_starthelp();
392};
393
394help: help_start T_HELPTEXT
395{
396 if (current_entry->help) {
397 free(current_entry->help);
398 zconfprint("warning: '%s' defined with more than one help text -- only the last one will be used",
399 current_entry->sym->name ?: "<choice>");
400 }
401
402 /* Is the help text empty or all whitespace? */
403 if ($2[strspn($2, " \f\n\r\t\v")] == '\0')
404 zconfprint("warning: '%s' defined with blank help text",
405 current_entry->sym->name ?: "<choice>");
406
407 current_entry->help = $2;
408};
409
410/* depends option */
411
412depends: T_DEPENDS T_ON expr T_EOL
413{
414 menu_add_dep($3);
415 printd(DEBUG_PARSE, "%s:%d:depends on\n", cur_filename, cur_lineno);
416};
417
418/* visibility option */
419visible: T_VISIBLE if_expr T_EOL
420{
421 menu_add_visibility($2);
422};
423
424/* prompt statement */
425
426prompt_stmt_opt:
427 /* empty */
428 | T_WORD_QUOTE if_expr
429{
430 menu_add_prompt(P_PROMPT, $1, $2);
431};
432
433end: T_ENDMENU T_EOL { $$ = "menu"; }
434 | T_ENDCHOICE T_EOL { $$ = "choice"; }
435 | T_ENDIF T_EOL { $$ = "if"; }
436;
437
438if_expr: /* empty */ { $$ = NULL; }
439 | T_IF expr { $$ = $2; }
440;
441
442expr: symbol { $$ = expr_alloc_symbol($1); }
443 | symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); }
444 | symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); }
445 | symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); }
446 | symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); }
447 | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
448 | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
449 | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; }
450 | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); }
451 | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); }
452 | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); }
453;
454
455/* For symbol definitions, selects, etc., where quotes are not accepted */
456nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); };
457
458symbol: nonconst_symbol
459 | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); }
460;
461
462/* assignment statement */
463
464assignment_stmt: T_WORD assign_op assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); }
465
466assign_op:
467 T_EQUAL { $$ = VAR_RECURSIVE; }
468 | T_COLON_EQUAL { $$ = VAR_SIMPLE; }
469 | T_PLUS_EQUAL { $$ = VAR_APPEND; }
470;
471
472assign_val:
473 /* empty */ { $$ = xstrdup(""); };
474 | T_ASSIGN_VAL
475;
476
477%%
478
479/**
480 * choice_check_sanity - check sanity of a choice member
481 *
482 * @menu: menu of the choice member
483 *
484 * Return: -1 if an error is found, 0 otherwise.
485 */
486static int choice_check_sanity(struct menu *menu)
487{
488 struct property *prop;
489 int ret = 0;
490
491 for (prop = menu->sym->prop; prop; prop = prop->next) {
492 if (prop->type == P_DEFAULT) {
493 fprintf(stderr, "%s:%d: error: %s",
494 prop->filename, prop->lineno,
495 "defaults for choice values not supported\n");
496 ret = -1;
497 }
498
499 if (prop->menu != menu && prop->type == P_PROMPT &&
500 prop->menu->parent != menu->parent) {
501 fprintf(stderr, "%s:%d: error: %s",
502 prop->filename, prop->lineno,
503 "choice value has a prompt outside its choice group\n");
504 ret = -1;
505 }
506 }
507
508 return ret;
509}
510
511void conf_parse(const char *name)
512{
513 struct menu *menu;
514
515 autoconf_cmd = str_new();
516
517 str_printf(&autoconf_cmd, "\ndeps_config := \\\n");
518
519 zconf_initscan(name);
520
521 _menu_init();
522
523 if (getenv("ZCONF_DEBUG"))
524 yydebug = 1;
525 yyparse();
526
527 /*
528 * FIXME:
529 * cur_filename and cur_lineno are used even after yyparse();
530 * menu_finalize() calls menu_add_symbol(). This should be fixed.
531 */
532 cur_filename = "<none>";
533 cur_lineno = 0;
534
535 str_printf(&autoconf_cmd,
536 "\n"
537 "$(autoconfig): $(deps_config)\n"
538 "$(deps_config): ;\n");
539
540 env_write_dep(&autoconf_cmd);
541
542 /* Variables are expanded in the parse phase. We can free them here. */
543 variable_all_del();
544
545 if (yynerrs)
546 exit(1);
547 if (!modules_sym)
548 modules_sym = &symbol_no;
549
550 if (!menu_has_prompt(&rootmenu)) {
551 current_entry = &rootmenu;
552 menu_add_prompt(P_MENU, "Main menu", NULL);
553 }
554
555 menu_finalize();
556
557 menu_for_each_entry(menu) {
558 struct menu *child;
559
560 if (menu->sym && sym_check_deps(menu->sym))
561 yynerrs++;
562
563 if (menu->sym && sym_is_choice(menu->sym)) {
564 menu_for_each_sub_entry(child, menu)
565 if (child->sym && choice_check_sanity(child))
566 yynerrs++;
567 }
568 }
569
570 if (yynerrs)
571 exit(1);
572 conf_set_changed(true);
573}
574
575static bool zconf_endtoken(const char *tokenname,
576 const char *expected_tokenname)
577{
578 if (strcmp(tokenname, expected_tokenname)) {
579 zconf_error("unexpected '%s' within %s block",
580 tokenname, expected_tokenname);
581 yynerrs++;
582 return false;
583 }
584 if (strcmp(current_menu->filename, cur_filename)) {
585 zconf_error("'%s' in different file than '%s'",
586 tokenname, expected_tokenname);
587 fprintf(stderr, "%s:%d: location of the '%s'\n",
588 current_menu->filename, current_menu->lineno,
589 expected_tokenname);
590 yynerrs++;
591 return false;
592 }
593 return true;
594}
595
596static void zconfprint(const char *err, ...)
597{
598 va_list ap;
599
600 fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno);
601 va_start(ap, err);
602 vfprintf(stderr, err, ap);
603 va_end(ap);
604 fprintf(stderr, "\n");
605}
606
607static void zconf_error(const char *err, ...)
608{
609 va_list ap;
610
611 yynerrs++;
612 fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno);
613 va_start(ap, err);
614 vfprintf(stderr, err, ap);
615 va_end(ap);
616 fprintf(stderr, "\n");
617}
618
619static void yyerror(const char *err)
620{
621 fprintf(stderr, "%s:%d: %s\n", cur_filename, cur_lineno, err);
622}
623
624static void print_quoted_string(FILE *out, const char *str)
625{
626 const char *p;
627 int len;
628
629 putc('"', out);
630 while ((p = strchr(str, '"'))) {
631 len = p - str;
632 if (len)
633 fprintf(out, "%.*s", len, str);
634 fputs("\\\"", out);
635 str = p + 1;
636 }
637 fputs(str, out);
638 putc('"', out);
639}
640
641static void print_symbol(FILE *out, struct menu *menu)
642{
643 struct symbol *sym = menu->sym;
644 struct property *prop;
645
646 if (sym_is_choice(sym))
647 fprintf(out, "\nchoice\n");
648 else
649 fprintf(out, "\nconfig %s\n", sym->name);
650 switch (sym->type) {
651 case S_BOOLEAN:
652 fputs(" bool\n", out);
653 break;
654 case S_TRISTATE:
655 fputs(" tristate\n", out);
656 break;
657 case S_STRING:
658 fputs(" string\n", out);
659 break;
660 case S_INT:
661 fputs(" integer\n", out);
662 break;
663 case S_HEX:
664 fputs(" hex\n", out);
665 break;
666 default:
667 fputs(" ???\n", out);
668 break;
669 }
670 for (prop = sym->prop; prop; prop = prop->next) {
671 if (prop->menu != menu)
672 continue;
673 switch (prop->type) {
674 case P_PROMPT:
675 fputs(" prompt ", out);
676 print_quoted_string(out, prop->text);
677 if (!expr_is_yes(prop->visible.expr)) {
678 fputs(" if ", out);
679 expr_fprint(prop->visible.expr, out);
680 }
681 fputc('\n', out);
682 break;
683 case P_DEFAULT:
684 fputs( " default ", out);
685 expr_fprint(prop->expr, out);
686 if (!expr_is_yes(prop->visible.expr)) {
687 fputs(" if ", out);
688 expr_fprint(prop->visible.expr, out);
689 }
690 fputc('\n', out);
691 break;
692 case P_CHOICE:
693 fputs(" #choice value\n", out);
694 break;
695 case P_SELECT:
696 fputs( " select ", out);
697 expr_fprint(prop->expr, out);
698 fputc('\n', out);
699 break;
700 case P_IMPLY:
701 fputs( " imply ", out);
702 expr_fprint(prop->expr, out);
703 fputc('\n', out);
704 break;
705 case P_RANGE:
706 fputs( " range ", out);
707 expr_fprint(prop->expr, out);
708 fputc('\n', out);
709 break;
710 case P_MENU:
711 fputs( " menu ", out);
712 print_quoted_string(out, prop->text);
713 fputc('\n', out);
714 break;
715 case P_SYMBOL:
716 fputs( " symbol ", out);
717 fprintf(out, "%s\n", prop->menu->sym->name);
718 break;
719 default:
720 fprintf(out, " unknown prop %d!\n", prop->type);
721 break;
722 }
723 }
724 if (menu->help) {
725 int len = strlen(menu->help);
726 while (menu->help[--len] == '\n')
727 menu->help[len] = 0;
728 fprintf(out, " help\n%s\n", menu->help);
729 }
730}
731
732void zconfdump(FILE *out)
733{
734 struct property *prop;
735 struct symbol *sym;
736 struct menu *menu;
737
738 menu = rootmenu.list;
739 while (menu) {
740 if ((sym = menu->sym))
741 print_symbol(out, menu);
742 else if ((prop = menu->prompt)) {
743 switch (prop->type) {
744 case P_COMMENT:
745 fputs("\ncomment ", out);
746 print_quoted_string(out, prop->text);
747 fputs("\n", out);
748 break;
749 case P_MENU:
750 fputs("\nmenu ", out);
751 print_quoted_string(out, prop->text);
752 fputs("\n", out);
753 break;
754 default:
755 ;
756 }
757 if (!expr_is_yes(prop->visible.expr)) {
758 fputs(" depends ", out);
759 expr_fprint(prop->visible.expr, out);
760 fputc('\n', out);
761 }
762 }
763
764 if (menu->list)
765 menu = menu->list;
766 else if (menu->next)
767 menu = menu->next;
768 else while ((menu = menu->parent)) {
769 if (menu->prompt && menu->prompt->type == P_MENU)
770 fputs("\nendmenu\n", out);
771 if (menu->next) {
772 menu = menu->next;
773 break;
774 }
775 }
776 }
777}