From 034dd340b08be1f2f0477ad16131d609f9dbd53c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 30 Aug 2026 09:22:00 -0700 Subject: Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure --- scripts/kconfig/parser.y | 797 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 797 insertions(+) create mode 100644 scripts/kconfig/parser.y (limited to 'scripts/kconfig/parser.y') diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y new file mode 100644 index 000000000..5fb6f07b6 --- /dev/null +++ b/scripts/kconfig/parser.y @@ -0,0 +1,797 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2002 Roman Zippel + */ +%{ + +#include +#include +#include +#include +#include +#include + +#include +#include "lkc.h" +#include "internal.h" +#include "preprocess.h" + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +static void yyerror(const char *err); +static void zconf_error(const char *err, ...); +static bool zconf_endtoken(const char *tokenname, + const char *expected_tokenname); + +struct menu *current_menu, *current_entry, *current_choice; + +%} + +%union +{ + char *string; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; + enum symbol_type type; + enum variable_flavor flavor; +} + +%token T_HELPTEXT +%token T_WORD +%token T_WORD_QUOTE +%token T_BOOL +%token T_CHOICE +%token T_CLOSE_PAREN +%token T_COLON_EQUAL +%token T_COMMENT +%token T_CONFIG +%token T_DEFAULT +%token T_DEF_BOOL +%token T_DEF_TRISTATE +%token T_DEPENDS +%token T_ENDCHOICE +%token T_ENDIF +%token T_ENDMENU +%token T_HELP +%token T_HEX +%token T_IF +%token T_IMPLY +%token T_INT +%token T_MAINMENU +%token T_MENU +%token T_MENUCONFIG +%token T_MODULES +%token T_ON +%token T_OPEN_PAREN +%token T_PLUS_EQUAL +%token T_PROMPT +%token T_RANGE +%token T_SELECT +%token T_SOURCE +%token T_STRING +%token T_TRANSITIONAL +%token T_TRISTATE +%token T_VISIBLE +%token T_EOL +%token T_ASSIGN_VAL + +%left T_OR +%left T_AND +%left T_EQUAL T_UNEQUAL +%left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL +%nonassoc T_NOT + +%type nonconst_symbol +%type symbol +%type type default +%type expr +%type if_expr +%type end +%type if_entry menu_entry choice_entry +%type assign_val +%type assign_op + +%destructor { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + $$->filename, $$->lineno); + if (current_menu == $$) + menu_end_menu(); +} if_entry menu_entry choice_entry + +%% +input: mainmenu_stmt stmt_list | stmt_list; + +/* mainmenu entry */ + +mainmenu_stmt: T_MAINMENU T_WORD_QUOTE T_EOL +{ + menu_add_prompt(P_MENU, $2, NULL); +}; + +stmt_list: + /* empty */ + | stmt_list assignment_stmt + | stmt_list choice_stmt + | stmt_list comment_stmt + | stmt_list config_stmt + | stmt_list if_stmt + | stmt_list menu_stmt + | stmt_list menuconfig_stmt + | stmt_list source_stmt + | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } + | stmt_list error T_EOL { zconf_error("invalid statement"); } +; + +stmt_list_in_choice: + /* empty */ + | stmt_list_in_choice comment_stmt + | stmt_list_in_choice config_stmt + | stmt_list_in_choice if_stmt_in_choice + | stmt_list_in_choice error T_EOL { zconf_error("invalid statement"); } +; + +/* config/menuconfig entry */ + +config_entry_start: T_CONFIG nonconst_symbol T_EOL +{ + menu_add_entry($2, M_NORMAL); + printd(DEBUG_PARSE, "%s:%d:config %s\n", cur_filename, cur_lineno, $2->name); +}; + +config_stmt: config_entry_start config_option_list +{ + if (current_choice) { + if (!current_entry->prompt) { + fprintf(stderr, "%s:%d: error: choice member must have a prompt\n", + current_entry->filename, current_entry->lineno); + yynerrs++; + } + + if (current_entry->sym->type != S_BOOLEAN) { + fprintf(stderr, "%s:%d: error: choice member must be bool\n", + current_entry->filename, current_entry->lineno); + yynerrs++; + } + + list_add_tail(¤t_entry->sym->choice_link, + ¤t_choice->choice_members); + } + + printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); +}; + +menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL +{ + menu_add_entry($2, M_MENU); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", cur_filename, cur_lineno, $2->name); +}; + +menuconfig_stmt: menuconfig_entry_start config_option_list +{ + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconf_error("menuconfig statement without prompt"); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); +}; + +config_option_list: + /* empty */ + | config_option_list config_option + | config_option_list depends + | config_option_list help +; + +config_option: type prompt_stmt_opt T_EOL +{ + menu_set_type($1); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1); +}; + +config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); +}; + +config_option: T_TRANSITIONAL T_EOL +{ + current_entry->sym->flags |= SYMBOL_TRANS; + printd(DEBUG_PARSE, "%s:%d:transitional\n", cur_filename, cur_lineno); +}; + +config_option: default expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + if ($1 != S_UNKNOWN) + menu_set_type($1); + printd(DEBUG_PARSE, "%s:%d:default(%u)\n", cur_filename, cur_lineno, + $1); +}; + +config_option: T_SELECT nonconst_symbol if_expr T_EOL +{ + menu_add_symbol(P_SELECT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:select\n", cur_filename, cur_lineno); +}; + +config_option: T_IMPLY nonconst_symbol if_expr T_EOL +{ + menu_add_symbol(P_IMPLY, $2, $3); + printd(DEBUG_PARSE, "%s:%d:imply\n", cur_filename, cur_lineno); +}; + +config_option: T_RANGE symbol symbol if_expr T_EOL +{ + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); + printd(DEBUG_PARSE, "%s:%d:range\n", cur_filename, cur_lineno); +}; + +config_option: T_MODULES T_EOL +{ + if (modules_sym) + zconf_error("symbol '%s' redefines option 'modules' already defined by symbol '%s'", + current_entry->sym->name, modules_sym->name); + modules_sym = current_entry->sym; +}; + +/* choice entry */ + +choice: T_CHOICE T_EOL +{ + struct symbol *sym = sym_lookup(NULL, 0); + + menu_add_entry(sym, M_CHOICE); + menu_set_type(S_BOOLEAN); + INIT_LIST_HEAD(¤t_entry->choice_members); + + printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno); +}; + +choice_entry: choice choice_option_list +{ + if (!current_entry->prompt) { + fprintf(stderr, "%s:%d: error: choice must have a prompt\n", + current_entry->filename, current_entry->lineno); + yynerrs++; + } + + $$ = menu_add_menu(); + + current_choice = current_entry; +}; + +choice_end: end +{ + current_choice = NULL; + + if (zconf_endtoken($1, "choice")) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", cur_filename, cur_lineno); + } +}; + +choice_stmt: choice_entry stmt_list_in_choice choice_end +; + +choice_option_list: + /* empty */ + | choice_option_list choice_option + | choice_option_list depends + | choice_option_list help +; + +choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); +}; + +choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL +{ + menu_add_symbol(P_DEFAULT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:default\n", cur_filename, cur_lineno); +}; + +type: + T_BOOL { $$ = S_BOOLEAN; } + | T_TRISTATE { $$ = S_TRISTATE; } + | T_INT { $$ = S_INT; } + | T_HEX { $$ = S_HEX; } + | T_STRING { $$ = S_STRING; } + +default: + T_DEFAULT { $$ = S_UNKNOWN; } + | T_DEF_BOOL { $$ = S_BOOLEAN; } + | T_DEF_TRISTATE { $$ = S_TRISTATE; } + +/* if entry */ + +if_entry: T_IF expr T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno); + menu_add_entry(NULL, M_IF); + menu_add_dep($2, NULL); + $$ = menu_add_menu(); +}; + +if_end: end +{ + if (zconf_endtoken($1, "if")) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", cur_filename, cur_lineno); + } +}; + +if_stmt: if_entry stmt_list if_end +; + +if_stmt_in_choice: if_entry stmt_list_in_choice if_end +; + +/* menu entry */ + +menu: T_MENU T_WORD_QUOTE T_EOL +{ + menu_add_entry(NULL, M_MENU); + menu_add_prompt(P_MENU, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", cur_filename, cur_lineno); +}; + +menu_entry: menu menu_option_list +{ + $$ = menu_add_menu(); +}; + +menu_end: end +{ + if (zconf_endtoken($1, "menu")) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", cur_filename, cur_lineno); + } +}; + +menu_stmt: menu_entry stmt_list menu_end +; + +menu_option_list: + /* empty */ + | menu_option_list visible + | menu_option_list depends +; + +source_stmt: T_SOURCE T_WORD_QUOTE T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:source %s\n", cur_filename, cur_lineno, $2); + zconf_nextfile($2); + free($2); +}; + +/* comment entry */ + +comment: T_COMMENT T_WORD_QUOTE T_EOL +{ + menu_add_entry(NULL, M_COMMENT); + menu_add_prompt(P_COMMENT, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", cur_filename, cur_lineno); +}; + +comment_stmt: comment comment_option_list +; + +comment_option_list: + /* empty */ + | comment_option_list depends +; + +/* help option */ + +help_start: T_HELP T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:help\n", cur_filename, cur_lineno); + zconf_starthelp(); +}; + +help: help_start T_HELPTEXT +{ + if (current_entry->help) { + free(current_entry->help); + zconf_error("'%s' defined with more than one help text", + current_entry->sym->name ?: ""); + } + + /* Is the help text empty or all whitespace? */ + if ($2[strspn($2, " \f\n\r\t\v")] == '\0') + zconf_error("'%s' defined with blank help text", + current_entry->sym->name ?: ""); + + current_entry->help = $2; +}; + +/* depends option */ + +depends: T_DEPENDS T_ON expr if_expr T_EOL +{ + menu_add_dep($3, $4); + printd(DEBUG_PARSE, "%s:%d:depends on\n", cur_filename, cur_lineno); +}; + +/* visibility option */ +visible: T_VISIBLE if_expr T_EOL +{ + menu_add_visibility($2); +}; + +/* prompt statement */ + +prompt_stmt_opt: + /* empty */ + | T_WORD_QUOTE if_expr +{ + menu_add_prompt(P_PROMPT, $1, $2); +}; + +end: T_ENDMENU T_EOL { $$ = "menu"; } + | T_ENDCHOICE T_EOL { $$ = "choice"; } + | T_ENDIF T_EOL { $$ = "if"; } +; + +if_expr: /* empty */ { $$ = NULL; } + | T_IF expr { $$ = $2; } +; + +expr: symbol { $$ = expr_alloc_symbol($1); } + | symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); } + | symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); } + | symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); } + | symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); } + | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } + | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } + | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } + | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } + | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } + | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } +; + +/* For symbol definitions, selects, etc., where quotes are not accepted */ +nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }; + +symbol: nonconst_symbol + | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } +; + +/* assignment statement */ + +assignment_stmt: T_WORD assign_op assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); } + +assign_op: + T_EQUAL { $$ = VAR_RECURSIVE; } + | T_COLON_EQUAL { $$ = VAR_SIMPLE; } + | T_PLUS_EQUAL { $$ = VAR_APPEND; } +; + +assign_val: + /* empty */ { $$ = xstrdup(""); }; + | T_ASSIGN_VAL +; + +%% + +/** + * transitional_check_sanity - check transitional symbols have no other + * properties + * + * @menu: menu of the potentially transitional symbol + * + * Return: -1 if an error is found, 0 otherwise. + */ +static int transitional_check_sanity(const struct menu *menu) +{ + struct property *prop; + + if (!menu->sym || !(menu->sym->flags & SYMBOL_TRANS)) + return 0; + + /* Check for depends and visible conditions. */ + if ((menu->dep && !expr_is_yes(menu->dep)) || + (menu->visibility && !expr_is_yes(menu->visibility))) { + fprintf(stderr, "%s:%d: error: %s", + menu->filename, menu->lineno, + "transitional symbols can only have help sections\n"); + return -1; + } + + /* Check for any property other than "help". */ + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type != P_COMMENT) { + fprintf(stderr, "%s:%d: error: %s", + prop->filename, prop->lineno, + "transitional symbols can only have help sections\n"); + return -1; + } + } + + return 0; +} + +/** + * choice_check_sanity - check sanity of a choice member + * + * @menu: menu of the choice member + * + * Return: -1 if an error is found, 0 otherwise. + */ +static int choice_check_sanity(const struct menu *menu) +{ + struct property *prop; + int ret = 0; + + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type == P_DEFAULT) { + fprintf(stderr, "%s:%d: error: %s", + prop->filename, prop->lineno, + "defaults for choice values not supported\n"); + ret = -1; + } + + if (prop->menu != menu && prop->type == P_PROMPT) { + fprintf(stderr, "%s:%d: error: %s", + prop->filename, prop->lineno, + "choice value must not have a prompt in another entry\n"); + ret = -1; + } + } + + return ret; +} + +void conf_parse(const char *name) +{ + struct menu *menu; + + autoconf_cmd = str_new(); + + str_printf(&autoconf_cmd, "\ndeps_config := \\\n"); + + zconf_initscan(name); + + _menu_init(); + + if (getenv("ZCONF_DEBUG")) + yydebug = 1; + yyparse(); + + str_printf(&autoconf_cmd, + "\n" + "$(autoconfig): $(deps_config)\n" + "$(deps_config): ;\n"); + + env_write_dep(&autoconf_cmd); + + /* Variables are expanded in the parse phase. We can free them here. */ + variable_all_del(); + + if (yynerrs) + exit(1); + if (!modules_sym) + modules_sym = &symbol_no; + + if (!menu_has_prompt(&rootmenu)) { + current_entry = &rootmenu; + menu_add_prompt(P_MENU, "Main menu", NULL); + } + + menu_finalize(); + + menu_for_each_entry(menu) { + struct menu *child; + + if (menu->sym && sym_check_deps(menu->sym)) + yynerrs++; + + if (transitional_check_sanity(menu)) + yynerrs++; + + if (menu->sym && sym_is_choice(menu->sym)) { + menu_for_each_sub_entry(child, menu) + if (child->sym && choice_check_sanity(child)) + yynerrs++; + } + } + + if (yynerrs) + exit(1); + conf_set_changed(true); +} + +static bool zconf_endtoken(const char *tokenname, + const char *expected_tokenname) +{ + if (strcmp(tokenname, expected_tokenname)) { + zconf_error("unexpected '%s' within %s block", + tokenname, expected_tokenname); + yynerrs++; + return false; + } + if (strcmp(current_menu->filename, cur_filename)) { + zconf_error("'%s' in different file than '%s'", + tokenname, expected_tokenname); + fprintf(stderr, "%s:%d: location of the '%s'\n", + current_menu->filename, current_menu->lineno, + expected_tokenname); + yynerrs++; + return false; + } + return true; +} + +static void zconf_error(const char *err, ...) +{ + va_list ap; + + yynerrs++; + fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void yyerror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", cur_filename, cur_lineno, err); +} + +static void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +static void print_symbol(FILE *out, const struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "\nchoice\n"); + else + fprintf(out, "\nconfig %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" bool\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_SELECT: + fputs( " select ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_IMPLY: + fputs( " imply ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_RANGE: + fputs( " range ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_MENU: + fputs( " menu ", out); + print_quoted_string(out, prop->text); + fputc('\n', out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (menu->help) { + int len = strlen(menu->help); + while (menu->help[--len] == '\n') + menu->help[len] = 0; + fprintf(out, " help\n%s\n", menu->help); + } +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} -- cgit v1.3.1