1 %{
2 /*
3  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
4  * Released under the terms of the GNU GPL v2.0.
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 #define LKC_DIRECT_LINK
15 #include "lkc.h"
16 
17 #define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
18 
19 #define PRINTD		0x0001
20 #define DEBUG_PARSE	0x0002
21 
22 int cdebug = PRINTD;
23 
24 extern int zconflex(void);
25 static void zconfprint(const char *err, ...);
26 static void zconf_error(const char *err, ...);
27 static void zconferror(const char *err);
28 static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken);
29 
30 struct symbol *symbol_hash[257];
31 
32 static struct menu *current_menu, *current_entry;
33 
34 #define YYDEBUG 0
35 #if YYDEBUG
36 #define YYERROR_VERBOSE
37 #endif
38 %}
39 %expect 26
40 
41 %union
42 {
43 	char *string;
44 	struct file *file;
45 	struct symbol *symbol;
46 	struct expr *expr;
47 	struct menu *menu;
48 	struct kconf_id *id;
49 }
50 
51 %token <id>T_MAINMENU
52 %token <id>T_MENU
53 %token <id>T_ENDMENU
54 %token <id>T_SOURCE
55 %token <id>T_CHOICE
56 %token <id>T_ENDCHOICE
57 %token <id>T_COMMENT
58 %token <id>T_CONFIG
59 %token <id>T_MENUCONFIG
60 %token <id>T_HELP
61 %token <string> T_HELPTEXT
62 %token <id>T_IF
63 %token <id>T_ENDIF
64 %token <id>T_DEPENDS
65 %token <id>T_REQUIRES
66 %token <id>T_OPTIONAL
67 %token <id>T_PROMPT
68 %token <id>T_TYPE
69 %token <id>T_DEFAULT
70 %token <id>T_SELECT
71 %token <id>T_RANGE
72 %token <id>T_ON
73 %token <string> T_WORD
74 %token <string> T_WORD_QUOTE
75 %token T_UNEQUAL
76 %token T_CLOSE_PAREN
77 %token T_OPEN_PAREN
78 %token T_EOL
79 
80 %left T_OR
81 %left T_AND
82 %left T_EQUAL T_UNEQUAL
83 %nonassoc T_NOT
84 
85 %type <string> prompt
86 %type <symbol> symbol
87 %type <expr> expr
88 %type <expr> if_expr
89 %type <id> end
90 %type <id> option_name
91 %type <menu> if_entry menu_entry choice_entry
92 
93 %destructor {
94 	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
95 		$$->file->name, $$->lineno);
96 	if (current_menu == $$)
97 		menu_end_menu();
98 } if_entry menu_entry choice_entry
99 
100 %{
101 #include "zconf.hash.c"
102 %}
103 
104 %%
105 input: stmt_list;
106 
107 stmt_list:
108 	  /* empty */
109 	| stmt_list common_stmt
110 	| stmt_list choice_stmt
111 	| stmt_list menu_stmt
112 	| stmt_list T_MAINMENU prompt nl
113 	| stmt_list end			{ zconf_error("unexpected end statement"); }
114 	| stmt_list T_WORD error T_EOL	{ zconf_error("unknown statement \"%s\"", $2); }
115 	| stmt_list option_name error T_EOL
116 {
117 	zconf_error("unexpected option \"%s\"", kconf_id_strings + $2->name);
118 }
119 	| stmt_list error T_EOL		{ zconf_error("invalid statement"); }
120 ;
121 
122 option_name:
123 	T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_OPTIONAL | T_RANGE | T_DEFAULT
124 ;
125 
126 common_stmt:
127 	  T_EOL
128 	| if_stmt
129 	| comment_stmt
130 	| config_stmt
131 	| menuconfig_stmt
132 	| source_stmt
133 ;
134 
135 option_error:
136 	  T_WORD error T_EOL		{ zconf_error("unknown option \"%s\"", $1); }
137 	| error T_EOL			{ zconf_error("invalid option"); }
138 ;
139 
140 
141 /* config/menuconfig entry */
142 
143 config_entry_start: T_CONFIG T_WORD T_EOL
144 {
145 	struct symbol *sym = sym_lookup($2, 0);
146 	sym->flags |= SYMBOL_OPTIONAL;
147 	menu_add_entry(sym);
148 	printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2);
149 };
150 
151 config_stmt: config_entry_start config_option_list
152 {
153 	menu_end_entry();
154 	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
155 };
156 
157 menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL
158 {
159 	struct symbol *sym = sym_lookup($2, 0);
160 	sym->flags |= SYMBOL_OPTIONAL;
161 	menu_add_entry(sym);
162 	printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2);
163 };
164 
165 menuconfig_stmt: menuconfig_entry_start config_option_list
166 {
167 	if (current_entry->prompt)
168 		current_entry->prompt->type = P_MENU;
169 	else
170 		zconfprint("warning: menuconfig statement without prompt");
171 	menu_end_entry();
172 	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
173 };
174 
175 config_option_list:
176 	  /* empty */
177 	| config_option_list config_option
178 	| config_option_list depends
179 	| config_option_list help
180 	| config_option_list option_error
181 	| config_option_list T_EOL
182 ;
183 
184 config_option: T_TYPE prompt_stmt_opt T_EOL
185 {
186 	menu_set_type($1->stype);
187 	printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
188 		zconf_curname(), zconf_lineno(),
189 		$1->stype);
190 };
191 
192 config_option: T_PROMPT prompt if_expr T_EOL
193 {
194 	menu_add_prompt(P_PROMPT, $2, $3);
195 	printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
196 };
197 
198 config_option: T_DEFAULT expr if_expr T_EOL
199 {
200 	menu_add_expr(P_DEFAULT, $2, $3);
201 	if ($1->stype != S_UNKNOWN)
202 		menu_set_type($1->stype);
203 	printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
204 		zconf_curname(), zconf_lineno(),
205 		$1->stype);
206 };
207 
208 config_option: T_SELECT T_WORD if_expr T_EOL
209 {
210 	menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3);
211 	printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
212 };
213 
214 config_option: T_RANGE symbol symbol if_expr T_EOL
215 {
216 	menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
217 	printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
218 };
219 
220 /* choice entry */
221 
222 choice: T_CHOICE T_EOL
223 {
224 	struct symbol *sym = sym_lookup(NULL, 0);
225 	sym->flags |= SYMBOL_CHOICE;
226 	menu_add_entry(sym);
227 	menu_add_expr(P_CHOICE, NULL, NULL);
228 	printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
229 };
230 
231 choice_entry: choice choice_option_list
232 {
233 	$$ = menu_add_menu();
234 };
235 
236 choice_end: end
237 {
238 	if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) {
239 		menu_end_menu();
240 		printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
241 	}
242 };
243 
244 choice_stmt: choice_entry choice_block choice_end
245 ;
246 
247 choice_option_list:
248 	  /* empty */
249 	| choice_option_list choice_option
250 	| choice_option_list depends
251 	| choice_option_list help
252 	| choice_option_list T_EOL
253 	| choice_option_list option_error
254 ;
255 
256 choice_option: T_PROMPT prompt if_expr T_EOL
257 {
258 	menu_add_prompt(P_PROMPT, $2, $3);
259 	printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
260 };
261 
262 choice_option: T_TYPE prompt_stmt_opt T_EOL
263 {
264 	if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) {
265 		menu_set_type($1->stype);
266 		printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
267 			zconf_curname(), zconf_lineno(),
268 			$1->stype);
269 	} else
270 		YYERROR;
271 };
272 
273 choice_option: T_OPTIONAL T_EOL
274 {
275 	current_entry->sym->flags |= SYMBOL_OPTIONAL;
276 	printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
277 };
278 
279 choice_option: T_DEFAULT T_WORD if_expr T_EOL
280 {
281 	if ($1->stype == S_UNKNOWN) {
282 		menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3);
283 		printd(DEBUG_PARSE, "%s:%d:default\n",
284 			zconf_curname(), zconf_lineno());
285 	} else
286 		YYERROR;
287 };
288 
289 choice_block:
290 	  /* empty */
291 	| choice_block common_stmt
292 ;
293 
294 /* if entry */
295 
296 if_entry: T_IF expr nl
297 {
298 	printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
299 	menu_add_entry(NULL);
300 	menu_add_dep($2);
301 	$$ = menu_add_menu();
302 };
303 
304 if_end: end
305 {
306 	if (zconf_endtoken($1, T_IF, T_ENDIF)) {
307 		menu_end_menu();
308 		printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
309 	}
310 };
311 
312 if_stmt: if_entry if_block if_end
313 ;
314 
315 if_block:
316 	  /* empty */
317 	| if_block common_stmt
318 	| if_block menu_stmt
319 	| if_block choice_stmt
320 ;
321 
322 /* menu entry */
323 
324 menu: T_MENU prompt T_EOL
325 {
326 	menu_add_entry(NULL);
327 	menu_add_prompt(P_MENU, $2, NULL);
328 	printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
329 };
330 
331 menu_entry: menu depends_list
332 {
333 	$$ = menu_add_menu();
334 };
335 
336 menu_end: end
337 {
338 	if (zconf_endtoken($1, T_MENU, T_ENDMENU)) {
339 		menu_end_menu();
340 		printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
341 	}
342 };
343 
344 menu_stmt: menu_entry menu_block menu_end
345 ;
346 
347 menu_block:
348 	  /* empty */
349 	| menu_block common_stmt
350 	| menu_block menu_stmt
351 	| menu_block choice_stmt
352 ;
353 
354 source_stmt: T_SOURCE prompt T_EOL
355 {
356 	printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
357 	zconf_nextfile($2);
358 };
359 
360 /* comment entry */
361 
362 comment: T_COMMENT prompt T_EOL
363 {
364 	menu_add_entry(NULL);
365 	menu_add_prompt(P_COMMENT, $2, NULL);
366 	printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
367 };
368 
369 comment_stmt: comment depends_list
370 {
371 	menu_end_entry();
372 };
373 
374 /* help option */
375 
376 help_start: T_HELP T_EOL
377 {
378 	printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
379 	zconf_starthelp();
380 };
381 
382 help: help_start T_HELPTEXT
383 {
384 	current_entry->sym->help = $2;
385 };
386 
387 /* depends option */
388 
389 depends_list:
390 	  /* empty */
391 	| depends_list depends
392 	| depends_list T_EOL
393 	| depends_list option_error
394 ;
395 
396 depends: T_DEPENDS T_ON expr T_EOL
397 {
398 	menu_add_dep($3);
399 	printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
400 }
401 	| T_DEPENDS expr T_EOL
402 {
403 	menu_add_dep($2);
404 	printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
405 }
406 	| T_REQUIRES expr T_EOL
407 {
408 	menu_add_dep($2);
409 	printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
410 };
411 
412 /* prompt statement */
413 
414 prompt_stmt_opt:
415 	  /* empty */
416 	| prompt if_expr
417 {
418 	menu_add_prompt(P_PROMPT, $1, $2);
419 };
420 
421 prompt:	  T_WORD
422 	| T_WORD_QUOTE
423 ;
424 
425 end:	  T_ENDMENU T_EOL	{ $$ = $1; }
426 	| T_ENDCHOICE T_EOL	{ $$ = $1; }
427 	| T_ENDIF T_EOL		{ $$ = $1; }
428 ;
429 
430 nl:
431 	  T_EOL
432 	| nl T_EOL
433 ;
434 
435 if_expr:  /* empty */			{ $$ = NULL; }
436 	| T_IF expr			{ $$ = $2; }
437 ;
438 
439 expr:	  symbol				{ $$ = expr_alloc_symbol($1); }
440 	| symbol T_EQUAL symbol			{ $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
441 	| symbol T_UNEQUAL symbol		{ $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
442 	| T_OPEN_PAREN expr T_CLOSE_PAREN	{ $$ = $2; }
443 	| T_NOT expr				{ $$ = expr_alloc_one(E_NOT, $2); }
444 	| expr T_OR expr			{ $$ = expr_alloc_two(E_OR, $1, $3); }
445 	| expr T_AND expr			{ $$ = expr_alloc_two(E_AND, $1, $3); }
446 ;
447 
448 symbol:	  T_WORD	{ $$ = sym_lookup($1, 0); free($1); }
449 	| T_WORD_QUOTE	{ $$ = sym_lookup($1, 1); free($1); }
450 ;
451 
452 %%
453 
454 void conf_parse(const char *name)
455 {
456 	struct symbol *sym;
457 	int i;
458 
459 	zconf_initscan(name);
460 
461 	sym_init();
462 	menu_init();
463 	modules_sym = sym_lookup("MODULES", 0);
464 	rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL);
465 
466 #if YYDEBUG
467 	if (getenv("ZCONF_DEBUG"))
468 		zconfdebug = 1;
469 #endif
470 	zconfparse();
471 	if (zconfnerrs)
472 		exit(1);
473 	menu_finalize(&rootmenu);
for_all_symbols(i,sym)474 	for_all_symbols(i, sym) {
475 		sym_check_deps(sym);
476 	}
477 
478 	sym_change_count = 1;
479 }
480 
zconf_tokenname(int token)481 const char *zconf_tokenname(int token)
482 {
483 	switch (token) {
484 	case T_MENU:		return "menu";
485 	case T_ENDMENU:		return "endmenu";
486 	case T_CHOICE:		return "choice";
487 	case T_ENDCHOICE:	return "endchoice";
488 	case T_IF:		return "if";
489 	case T_ENDIF:		return "endif";
490 	case T_DEPENDS:		return "depends";
491 	}
492 	return "<token>";
493 }
494 
zconf_endtoken(struct kconf_id * id,int starttoken,int endtoken)495 static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken)
496 {
497 	if (id->token != endtoken) {
498 		zconf_error("unexpected '%s' within %s block",
499 			kconf_id_strings + id->name, zconf_tokenname(starttoken));
500 		zconfnerrs++;
501 		return false;
502 	}
503 	if (current_menu->file != current_file) {
504 		zconf_error("'%s' in different file than '%s'",
505 			kconf_id_strings + id->name, zconf_tokenname(starttoken));
506 		fprintf(stderr, "%s:%d: location of the '%s'\n",
507 			current_menu->file->name, current_menu->lineno,
508 			zconf_tokenname(starttoken));
509 		zconfnerrs++;
510 		return false;
511 	}
512 	return true;
513 }
514 
zconfprint(const char * err,...)515 static void zconfprint(const char *err, ...)
516 {
517 	va_list ap;
518 
519 	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
520 	va_start(ap, err);
521 	vfprintf(stderr, err, ap);
522 	va_end(ap);
523 	fprintf(stderr, "\n");
524 }
525 
zconf_error(const char * err,...)526 static void zconf_error(const char *err, ...)
527 {
528 	va_list ap;
529 
530 	zconfnerrs++;
531 	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
532 	va_start(ap, err);
533 	vfprintf(stderr, err, ap);
534 	va_end(ap);
535 	fprintf(stderr, "\n");
536 }
537 
zconferror(const char * err)538 static void zconferror(const char *err)
539 {
540 #if YYDEBUG
541 	fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
542 #endif
543 }
544 
print_quoted_string(FILE * out,const char * str)545 void print_quoted_string(FILE *out, const char *str)
546 {
547 	const char *p;
548 	int len;
549 
550 	putc('"', out);
551 	while ((p = strchr(str, '"'))) {
552 		len = p - str;
553 		if (len)
554 			fprintf(out, "%.*s", len, str);
555 		fputs("\\\"", out);
556 		str = p + 1;
557 	}
558 	fputs(str, out);
559 	putc('"', out);
560 }
561 
print_symbol(FILE * out,struct menu * menu)562 void print_symbol(FILE *out, struct menu *menu)
563 {
564 	struct symbol *sym = menu->sym;
565 	struct property *prop;
566 
567 	if (sym_is_choice(sym))
568 		fprintf(out, "choice\n");
569 	else
570 		fprintf(out, "config %s\n", sym->name);
571 	switch (sym->type) {
572 	case S_BOOLEAN:
573 		fputs("  boolean\n", out);
574 		break;
575 	case S_TRISTATE:
576 		fputs("  tristate\n", out);
577 		break;
578 	case S_STRING:
579 		fputs("  string\n", out);
580 		break;
581 	case S_INT:
582 		fputs("  integer\n", out);
583 		break;
584 	case S_HEX:
585 		fputs("  hex\n", out);
586 		break;
587 	default:
588 		fputs("  ???\n", out);
589 		break;
590 	}
591 	for (prop = sym->prop; prop; prop = prop->next) {
592 		if (prop->menu != menu)
593 			continue;
594 		switch (prop->type) {
595 		case P_PROMPT:
596 			fputs("  prompt ", out);
597 			print_quoted_string(out, prop->text);
598 			if (!expr_is_yes(prop->visible.expr)) {
599 				fputs(" if ", out);
600 				expr_fprint(prop->visible.expr, out);
601 			}
602 			fputc('\n', out);
603 			break;
604 		case P_DEFAULT:
605 			fputs( "  default ", out);
606 			expr_fprint(prop->expr, out);
607 			if (!expr_is_yes(prop->visible.expr)) {
608 				fputs(" if ", out);
609 				expr_fprint(prop->visible.expr, out);
610 			}
611 			fputc('\n', out);
612 			break;
613 		case P_CHOICE:
614 			fputs("  #choice value\n", out);
615 			break;
616 		default:
617 			fprintf(out, "  unknown prop %d!\n", prop->type);
618 			break;
619 		}
620 	}
621 	if (sym->help) {
622 		int len = strlen(sym->help);
623 		while (sym->help[--len] == '\n')
624 			sym->help[len] = 0;
625 		fprintf(out, "  help\n%s\n", sym->help);
626 	}
627 	fputc('\n', out);
628 }
629 
zconfdump(FILE * out)630 void zconfdump(FILE *out)
631 {
632 	struct property *prop;
633 	struct symbol *sym;
634 	struct menu *menu;
635 
636 	menu = rootmenu.list;
637 	while (menu) {
638 		if ((sym = menu->sym))
639 			print_symbol(out, menu);
640 		else if ((prop = menu->prompt)) {
641 			switch (prop->type) {
642 			case P_COMMENT:
643 				fputs("\ncomment ", out);
644 				print_quoted_string(out, prop->text);
645 				fputs("\n", out);
646 				break;
647 			case P_MENU:
648 				fputs("\nmenu ", out);
649 				print_quoted_string(out, prop->text);
650 				fputs("\n", out);
651 				break;
652 			default:
653 				;
654 			}
655 			if (!expr_is_yes(prop->visible.expr)) {
656 				fputs("  depends ", out);
657 				expr_fprint(prop->visible.expr, out);
658 				fputc('\n', out);
659 			}
660 			fputs("\n", out);
661 		}
662 
663 		if (menu->list)
664 			menu = menu->list;
665 		else if (menu->next)
666 			menu = menu->next;
667 		else while ((menu = menu->parent)) {
668 			if (menu->prompt && menu->prompt->type == P_MENU)
669 				fputs("\nendmenu\n", out);
670 			if (menu->next) {
671 				menu = menu->next;
672 				break;
673 			}
674 		}
675 	}
676 }
677 
678 #include "lex.zconf.c"
679 #include "util.c"
680 #include "confdata.c"
681 #include "expr.c"
682 #include "symbol.c"
683 #include "menu.c"
684