diff --git a/builtin.cpp b/builtin.cpp index 11036e378..215edc596 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -1753,10 +1753,15 @@ static int builtin_pwd(parser_t &parser, wchar_t **argv) } /* This is nearly identical to builtin_function, and is intended to be the successor (with no block manipulation, no function/end split) */ -int define_function(parser_t &parser, const wcstring_list_t &args, const wcstring &contents, wcstring *out_err) +int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstring &contents, wcstring *out_err) { assert(out_err != NULL); + /* wgetopt expects 'function' as the first argument. Make a new wcstring_list with that property. */ + wcstring_list_t args; + args.push_back(L"function"); + args.insert(args.end(), c_args.begin(), c_args.end()); + /* Hackish const_cast matches the one in builtin_run */ const null_terminated_array_t argv_array(args); wchar_t **argv = const_cast(argv_array.get()); diff --git a/builtin.h b/builtin.h index 9900f856e..7162de235 100644 --- a/builtin.h +++ b/builtin.h @@ -178,7 +178,7 @@ const wchar_t *builtin_complete_get_temporary_buffer(); */ wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd); -/** Defines a function, like builtin_function. Returns 0 on success. */ +/** Defines a function, like builtin_function. Returns 0 on success. args should NOT contain 'function' as the first argument. */ int define_function(parser_t &parser, const wcstring_list_t &args, const wcstring &contents, wcstring *out_err); diff --git a/exec.cpp b/exec.cpp index c1fb54be2..e7277169b 100644 --- a/exec.cpp +++ b/exec.cpp @@ -1556,6 +1556,8 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, boo int prev_subshell = is_subshell; const int prev_status = proc_get_last_status(); char sep=0; + + //fprintf(stderr, "subcmd %ls\n", cmd.c_str()); const env_var_t ifs = env_get_string(L"IFS"); diff --git a/expand.cpp b/expand.cpp index f743f8ab6..a73118b89 100644 --- a/expand.cpp +++ b/expand.cpp @@ -1867,7 +1867,7 @@ bool expand_one(wcstring &string, expand_flags_t flags) return true; } - if (expand_string(string, completions, flags)) + if (expand_string(string, completions, flags | EXPAND_NO_DESCRIPTIONS)) { if (completions.size() == 1) { diff --git a/parse_execution.cpp b/parse_execution.cpp index 1a2b16b1f..662506d50 100644 --- a/parse_execution.cpp +++ b/parse_execution.cpp @@ -5,7 +5,9 @@ */ #include "parse_execution.h" +#include "parse_util.h" #include "complete.h" +#include "wildcard.h" #include "builtin.h" #include "parser.h" #include "expand.h" @@ -65,14 +67,13 @@ int parse_execution_context_t::run_if_statement(const parse_node_t &statement) { assert(if_clause != NULL && else_clause != NULL); const parse_node_t &condition = *get_child(*if_clause, 1, symbol_job); - fprintf(stderr, "run %ls\n", get_source(condition).c_str()); if (run_1_job(condition) == EXIT_SUCCESS) { /* condition succeeded */ job_list_to_execute = get_child(*if_clause, 3, symbol_job_list); break; } - else if (else_clause->child_count > 0) + else if (else_clause->child_count == 0) { /* 'if' condition failed, no else clause, we're done */ job_list_to_execute = NULL; @@ -119,7 +120,6 @@ int parse_execution_context_t::run_begin_statement(const parse_node_t &header, c /* Basic begin/end block. Push a scope block. */ scope_block_t *sb = new scope_block_t(BEGIN); parser->push_block(sb); - parser->current_block()->tok_pos = parser->get_pos(); /* Run the job list */ run_job_list(contents); @@ -138,7 +138,7 @@ int parse_execution_context_t::run_function_statement(const parse_node_t &header /* Get arguments */ const parse_node_t *unmatched_wildcard = NULL; - const wcstring_list_t argument_list = this->determine_arguments(header, &unmatched_wildcard); + wcstring_list_t argument_list = this->determine_arguments(header, &unmatched_wildcard); bool errored = false; if (unmatched_wildcard != NULL) @@ -152,6 +152,11 @@ int parse_execution_context_t::run_function_statement(const parse_node_t &header wcstring error_str; int err = define_function(*parser, argument_list, contents_str, &error_str); proc_set_last_status(err); + + if (! error_str.empty()) + { + this->append_error(header, L"%ls", error_str.c_str()); + } } return proc_get_last_status(); } @@ -197,19 +202,17 @@ int parse_execution_context_t::run_for_statement(const parse_node_t &header, con assert(header.type == symbol_for_header); assert(block_contents.type == symbol_job_list); - /* get the variable name: `for var_name in ...` */ + /* Get the variable name: `for var_name in ...` */ const parse_node_t &var_name_node = *get_child(header, 1, parse_token_type_string); const wcstring for_var_name = get_source(var_name_node); - /* get the contents to iterate over */ + /* Get the contents to iterate over. Here we could do something with unmatched_wildcard. However it seems nicer to not make for loops complain about this, i.e. just iterate over a potentially empty list + */ const parse_node_t *unmatched_wildcard = NULL; - wcstring_list_t argument_list = this->determine_arguments(header, &unmatched_wildcard); - - /* Here we could do something with unmatched_wildcard. However it seems nicer to not make for loops complain about this, i.e. just iterate over a potentially empty list */ + wcstring_list_t argument_list = this->determine_arguments(header, NULL); for_block_t *fb = new for_block_t(for_var_name); parser->push_block(fb); - fb->tok_pos = parser->get_pos(); /* Note that we store the sequence of values in opposite order */ std::reverse(argument_list.begin(), argument_list.end()); @@ -234,6 +237,103 @@ int parse_execution_context_t::run_for_statement(const parse_node_t &header, con int parse_execution_context_t::run_switch_statement(const parse_node_t &statement) { + assert(statement.type == symbol_switch_statement); + bool errored = false; + const parse_node_t *matching_case_item = NULL; + + /* Get the switch variable */ + const parse_node_t &switch_value_node = *get_child(statement, 1, parse_token_type_string); + const wcstring switch_value = get_source(switch_value_node); + + /* Expand it */ + std::vector switch_values_expanded; + int expand_ret = expand_string(switch_value, switch_values_expanded, EXPAND_NO_DESCRIPTIONS); + switch (expand_ret) + { + case EXPAND_ERROR: + { + errored = append_error(switch_value_node, + _(L"Could not expand string '%ls'"), + switch_value.c_str()); + break; + } + + case EXPAND_WILDCARD_NO_MATCH: + { + /* Store the node that failed to expand */ + errored = append_error(switch_value_node, WILDCARD_ERR_MSG, switch_value.c_str()); + break; + } + + case EXPAND_WILDCARD_MATCH: + case EXPAND_OK: + { + break; + } + } + + if (! errored && switch_values_expanded.size() != 1) + { + errored = append_error(switch_value_node, + _(L"switch: Expected exactly one argument, got %lu\n"), + switch_values_expanded.size()); + } + const wcstring &switch_value_expanded = switch_values_expanded.at(0).completion; + + if (! errored) + { + /* Expand case statements */ + const parse_node_t *case_item_list = get_child(statement, 3, symbol_case_item_list); + while (matching_case_item == NULL && case_item_list->child_count > 0) + { + if (case_item_list->production_idx == 2) + { + /* Hackish: blank line */ + case_item_list = get_child(*case_item_list, 1, symbol_case_item_list); + continue; + } + + /* Pull out this case item and the rest of the list */ + const parse_node_t &case_item = *get_child(*case_item_list, 0, symbol_case_item); + + /* Pull out the argument list */ + const parse_node_t &arg_list = *get_child(case_item, 1, symbol_argument_list); + + /* Expand arguments. We explicitly ignore unmatched_wildcard. That is, a case item list may have a wildcard that fails to expand to anything. */ + const wcstring_list_t case_args = this->determine_arguments(arg_list, NULL); + + for (size_t i=0; i < case_args.size(); i++) + { + const wcstring &arg = case_args.at(i); + + /* Unescape wildcards so they can be expanded again */ + wchar_t *unescaped_arg = parse_util_unescape_wildcards(arg.c_str()); + bool match = wildcard_match(switch_value_expanded, unescaped_arg); + free(unescaped_arg); + + /* If this matched, we're done */ + if (match) + { + matching_case_item = &case_item; + break; + } + } + + /* Remainder of the list */ + case_item_list = get_child(*case_item_list, 1, symbol_case_item_list); + } + } + + if (! errored && matching_case_item) + { + /* Success, evaluate the job list */ + const parse_node_t *job_list = get_child(*matching_case_item, 3, symbol_job_list); + this->run_job_list(*job_list); + } + + // Oops, this is stomping STATUS_WILDCARD_ERROR. TODO: Don't! + if (errored) + proc_set_last_status(STATUS_BUILTIN_ERROR); return proc_get_last_status(); } @@ -415,7 +515,7 @@ wcstring_list_t parse_execution_context_t::determine_arguments(const parse_node_ /* Expand this string */ std::vector arg_expanded; - int expand_ret = expand_string(arg_str, arg_expanded, 0); + int expand_ret = expand_string(arg_str, arg_expanded, EXPAND_NO_DESCRIPTIONS); switch (expand_ret) { case EXPAND_ERROR: @@ -709,7 +809,7 @@ int parse_execution_context_t::run_1_job(const parse_node_t &job_node) } } - /* Increment the eval_level for the duration of this command */ + /* Increment the eval_level for the duration of this command */ scoped_push saved_eval_level(&eval_level, eval_level + 1); /* TODO: blocks-without-redirections optimization */ diff --git a/parse_productions.cpp b/parse_productions.cpp index a4b1de957..24962e534 100644 --- a/parse_productions.cpp +++ b/parse_productions.cpp @@ -231,7 +231,7 @@ RESOLVE(else_continuation) PRODUCTIONS(switch_statement) = { - { KEYWORD(parse_keyword_switch), parse_token_type_string, parse_token_type_end, symbol_case_item_list, symbol_end_command} + { KEYWORD(parse_keyword_switch), parse_token_type_string, parse_token_type_end, symbol_case_item_list, symbol_end_command, symbol_arguments_or_redirections_list} }; RESOLVE_ONLY(switch_statement) diff --git a/parse_productions.h b/parse_productions.h index e6c003ab2..df3ae9c96 100644 --- a/parse_productions.h +++ b/parse_productions.h @@ -13,7 +13,7 @@ namespace parse_productions { #define MAX_PRODUCTIONS 5 -#define MAX_SYMBOLS_PER_PRODUCTION 5 +#define MAX_SYMBOLS_PER_PRODUCTION 6 typedef uint32_t production_tag_t; diff --git a/parse_tree.h b/parse_tree.h index 91cab4262..266372e08 100644 --- a/parse_tree.h +++ b/parse_tree.h @@ -238,9 +238,11 @@ class parse_node_tree_t : public std::vector else_continuation = if_clause else_clause | STATEMENT_TERMINATOR job_list - switch_statement = SWITCH STATEMENT_TERMINATOR case_item_list end_command + switch_statement = SWITCH STATEMENT_TERMINATOR case_item_list end_command arguments_or_redirections_list case_item_list = | - case_item case_item_list + case_item case_item_list | + case_item_list + case_item = CASE argument_list STATEMENT_TERMINATOR job_list block_statement = block_header job_list end_command arguments_or_redirections_list diff --git a/reader.cpp b/reader.cpp index 5c5b094d9..0905b3792 100644 --- a/reader.cpp +++ b/reader.cpp @@ -2990,6 +2990,7 @@ const wchar_t *reader_readline(void) is_interactive_read = 1; c=input_readch(); is_interactive_read = was_interactive_read; + //fprintf(stderr, "C: %lx\n", (long)c); if (((!wchar_private(c))) && (c>31) && (c != 127)) {