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