diff options
-rw-r--r-- | parser.cpp | 719 | ||||
-rw-r--r-- | parser.h | 1 |
2 files changed, 0 insertions, 720 deletions
@@ -2935,725 +2935,6 @@ void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &erro } } -parser_test_error_bits_t parser_t::detect_errors2(const wchar_t *buff, wcstring *out, const wchar_t *prefix) -{ - ASSERT_IS_MAIN_THREAD(); - - /* - Set to one if a command name has been given for the currently - parsed process specification - */ - int had_cmd=0; - int err=0; - int unfinished = 0; - - // This is very nearly a stack, but sometimes we have to inspect non-top elements (e.g. return) - std::vector<struct block_info_t> block_infos; - - /* - Set to 1 if the current command is inside a pipeline - */ - int is_pipeline = 0; - - /* - Set to one if the currently specified process can not be used inside a pipeline - */ - int forbid_pipeline = 0; - - /* - Set to one if an additional process specification is needed - */ - bool needs_cmd = false; - - /* - Counter on the number of arguments this function has encountered - so far. Is set to -1 when the count is unknown, i.e. after - encountering an argument that contains substitutions that can - expand to more/less arguemtns then 1. - */ - int arg_count=0; - - /* - The currently validated command. - */ - wcstring command; - bool has_command = false; - - CHECK(buff, 1); - - tokenizer_t tok(buff, 0); - - scoped_push<tokenizer_t*> tokenizer_push(¤t_tokenizer, &tok); - scoped_push<int> tokenizer_pos_push(¤t_tokenizer_pos); - - for (;; tok_next(&tok)) - { - current_tokenizer_pos = tok_get_pos(&tok); - - int last_type = tok_last_type(&tok); - int end_of_cmd = 0; - - switch (last_type) - { - case TOK_STRING: - { - if (!had_cmd) - { - int mark = tok_get_pos(&tok); - had_cmd = 1; - arg_count=0; - - command = tok_last(&tok); - - // Pass SKIP_HOME_DIRECTORIES for https://github.com/fish-shell/fish-shell/issues/512 - has_command = expand_one(command, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_HOME_DIRECTORIES); - if (! has_command) - { - command = L""; - err=1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - ILLEGAL_CMD_ERR_MSG, - tok_last(&tok)); - - print_errors(*out, prefix); - } - break; - } - - if (needs_cmd) - { - /* - end is not a valid command when a followup - command is needed, such as after 'and' or - 'while' - */ - if (contains(command, - L"end")) - { - err=1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - COND_ERR_MSG); - - print_errors(*out, prefix); - } - } - - needs_cmd = false; - } - - /* - Decrement block count on end command - */ - if (command == L"end") - { - tok_next(&tok); - tok_set_pos(&tok, mark); - - /* Test that end is not used when not inside any block */ - if (block_infos.empty()) - { - err = 1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - INVALID_END_ERR_MSG); - print_errors(*out, prefix); - const wcstring h = builtin_help_get(*this, L"end"); - if (! h.empty()) - append_format(*out, L"%ls", h.c_str()); - } - } - else - { - block_infos.pop_back(); - - } - } - - /* - Handle block commands - */ - if (parser_keywords_is_block(command)) - { - struct block_info_t info = {current_tokenizer_pos, parser_get_block_type(command)}; - block_infos.push_back(info); - tok_next(&tok); - tok_set_pos(&tok, mark); - } - - /* - If parser_keywords_is_subcommand is true, the command - accepts a second command as it's first - argument. If parser_skip_arguments is true, the - second argument is optional. - */ - if (parser_keywords_is_subcommand(command) && !parser_keywords_skip_arguments(command)) - { - needs_cmd = true; - had_cmd = 0; - } - - if (contains(command, - L"or", - L"and")) - { - /* - 'or' and 'and' can not be used inside pipelines - */ - if (is_pipeline) - { - err=1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - EXEC_ERR_MSG); - - print_errors(*out, prefix); - - } - } - } - - /* - There are a lot of situations where pipelines - are forbidden, including when using the exec - builtin. - */ - if (parser_is_pipe_forbidden(command)) - { - if (is_pipeline) - { - err=1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - EXEC_ERR_MSG); - - print_errors(*out, prefix); - - } - } - forbid_pipeline = 1; - } - - /* - Test that the case builtin is only used directly in a switch block - */ - if (command == L"case") - { - if (block_infos.empty() || block_infos.back().type != SWITCH) - { - err=1; - - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - INVALID_CASE_ERR_MSG); - - print_errors(*out, prefix); - const wcstring h = builtin_help_get(*this, L"case"); - if (h.size()) - append_format(*out, L"%ls", h.c_str()); - } - } - } - - /* - Test that the return bultin is only used within function definitions - */ - if (command == L"return") - { - bool found_func = false; - size_t block_idx = block_infos.size(); - while (block_idx--) - { - if (block_infos.at(block_idx).type == FUNCTION_DEF) - { - found_func = true; - break; - } - } - - if (!found_func) - { - /* - Peek to see if the next argument is - --help, in which case we'll allow it to - show the help. - */ - - int old_pos = tok_get_pos(&tok); - int is_help = 0; - - tok_next(&tok); - if (tok_last_type(&tok) == TOK_STRING) - { - wcstring first_arg = tok_last(&tok); - if (expand_one(first_arg, EXPAND_SKIP_CMDSUBST) && parser_t::is_help(first_arg.c_str(), 3)) - { - is_help = 1; - } - } - - tok_set_pos(&tok, old_pos); - - if (!is_help) - { - err=1; - - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - INVALID_RETURN_ERR_MSG); - print_errors(*out, prefix); - } - } - } - } - - - /* - Test that break and continue are only used within loop blocks - */ - if (contains(command, L"break", L"continue")) - { - bool found_loop = false; - size_t block_idx = block_infos.size(); - while (block_idx--) - { - block_type_t type = block_infos.at(block_idx).type; - if (type == WHILE || type == FOR) - { - found_loop = true; - break; - } - } - - if (!found_loop) - { - /* - Peek to see if the next argument is - --help, in which case we'll allow it to - show the help. - */ - - int old_pos = tok_get_pos(&tok); - int is_help = 0; - - tok_next(&tok); - if (tok_last_type(&tok) == TOK_STRING) - { - wcstring first_arg = tok_last(&tok); - if (expand_one(first_arg, EXPAND_SKIP_CMDSUBST) && parser_t::is_help(first_arg.c_str(), 3)) - { - is_help = 1; - } - } - - tok_set_pos(&tok, old_pos); - - if (!is_help) - { - err=1; - - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - INVALID_LOOP_ERR_MSG); - print_errors(*out, prefix); - } - } - } - } - - /* - Test that else and else-if are only used directly in an if-block - */ - if (command == L"else") - { - if (block_infos.empty() || block_infos.back().type != IF) - { - err=1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - INVALID_ELSE_ERR_MSG, - command.c_str()); - - print_errors(*out, prefix); - } - } - } - } - else - { - err |= parser_test_argument(tok_last(&tok), out, prefix, tok_get_pos(&tok)); - - /* If possible, keep track of number of supplied arguments */ - if (arg_count >= 0 && expand_is_clean(tok_last(&tok))) - { - arg_count++; - } - else - { - arg_count = -1; - } - - if (has_command) - { - - /* - Try to make sure the second argument to 'for' is 'in' - */ - if (command == L"for") - { - if (arg_count == 1) - { - - if (wcsvarname(tok_last(&tok))) - { - - err = 1; - - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - BUILTIN_FOR_ERR_NAME, - L"for", - tok_last(&tok)); - - print_errors(*out, prefix); - } - } - - } - else if (arg_count == 2) - { - if (wcscmp(tok_last(&tok), L"in") != 0) - { - err = 1; - - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - BUILTIN_FOR_ERR_IN, - L"for"); - - print_errors(*out, prefix); - } - } - } - } - else if (command == L"else") - { - if (arg_count == 1) - { - /* Any second argument must be "if" */ - if (wcscmp(tok_last(&tok), L"if") != 0) - { - err = 1; - - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - BUILTIN_ELSEIF_ERR_ARGUMENT, - L"else"); - print_errors(*out, prefix); - } - } - else - { - /* Successfully detected "else if". Now we need a new command. */ - needs_cmd = true; - had_cmd = false; - } - } - } - } - - } - - break; - } - - case TOK_REDIRECT_OUT: - case TOK_REDIRECT_IN: - case TOK_REDIRECT_APPEND: - case TOK_REDIRECT_FD: - case TOK_REDIRECT_NOCLOB: - { - if (!had_cmd) - { - err = 1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - INVALID_REDIRECTION_ERR_MSG); - print_errors(*out, prefix); - } - } - break; - } - - case TOK_END: - { - if (needs_cmd && !had_cmd) - { - err = 1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - CMD_ERR_MSG, - tok_get_desc(tok_last_type(&tok))); - print_errors(*out, prefix); - } - } - needs_cmd = false; - had_cmd = 0; - is_pipeline=0; - forbid_pipeline=0; - end_of_cmd = 1; - - break; - } - - case TOK_PIPE: - { - if (!had_cmd) - { - err=1; - if (out) - { - if (tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'|') - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - CMD_OR_ERR_MSG, - tok_get_desc(tok_last_type(&tok))); - - } - else - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - CMD_ERR_MSG, - tok_get_desc(tok_last_type(&tok))); - } - - print_errors(*out, prefix); - } - } - else if (forbid_pipeline) - { - err=1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - EXEC_ERR_MSG); - - print_errors(*out, prefix); - } - } - else - { - needs_cmd = true; - is_pipeline=1; - had_cmd=0; - end_of_cmd = 1; - - } - break; - } - - case TOK_BACKGROUND: - { - if (!had_cmd) - { - err = 1; - if (out) - { - if (tok_get_pos(&tok)>0 && buff[tok_get_pos(&tok)-1] == L'&') - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - CMD_AND_ERR_MSG, - tok_get_desc(tok_last_type(&tok))); - - } - else - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - CMD_ERR_MSG, - tok_get_desc(tok_last_type(&tok))); - } - - print_errors(*out, prefix); - } - } - - had_cmd = 0; - end_of_cmd = 1; - - break; - } - - case TOK_ERROR: - default: - if (tok_get_error(&tok) == TOK_UNTERMINATED_QUOTE) - { - unfinished = 1; - } - else - { - // Only print errors once - if (out && ! err) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - TOK_ERR_MSG, - tok_last(&tok)); - - - print_errors(*out, prefix); - } - err = 1; - } - - break; - } - - if (end_of_cmd) - { - if (has_command && command == L"for") - { - if (arg_count >= 0 && arg_count < 2) - { - /* - Not enough arguments to the for builtin - */ - err = 1; - - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - BUILTIN_FOR_ERR_COUNT, - L"for", - arg_count); - - print_errors(*out, prefix); - } - } - } - else if (has_command && command == L"else") - { - if (arg_count == 1) - { - /* If we have any arguments, we must have at least two...either "else" or "else if foo..." */ - err = true; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - BUILTIN_ELSEIF_ERR_COUNT, - L"else", - arg_count); - - print_errors(*out, prefix); - - } - } - } - - } - - if (!tok_has_next(&tok)) - break; - - } - - if (needs_cmd) - { - err=1; - if (out) - { - error(SYNTAX_ERROR, - tok_get_pos(&tok), - COND_ERR_MSG); - - print_errors(*out, prefix); - } - } - - - if (out != NULL && ! block_infos.empty()) - { - const wchar_t *cmd; - int bad_pos = block_infos.back().position; - block_type_t bad_type = block_infos.back().type; - - error(SYNTAX_ERROR, bad_pos, BLOCK_END_ERR_MSG); - - print_errors(*out, prefix); - - cmd = parser_get_block_command(bad_type); - if (cmd) - { - const wcstring h = builtin_help_get(*this, cmd); - if (h.size()) - { - append_format(*out, L"%ls", h.c_str()); - } - } - - - } - - /* - Calculate exit status - */ - if (! block_infos.empty()) - unfinished = 1; - - parser_test_error_bits_t res = 0; - - if (err) - res |= PARSER_TEST_ERROR; - - if (unfinished) - res |= PARSER_TEST_INCOMPLETE; - - /* - Cleanup - */ - - error_code=0; - - - return res; - -} - block_t::block_t(block_type_t t) : block_type(t), made_fake(false), @@ -482,7 +482,6 @@ public: \param out if non-null, any errors in the command will be filled out into this buffer \param prefix the prefix string to prepend to each error message written to the \c out buffer */ - parser_test_error_bits_t detect_errors2(const wchar_t *buff, wcstring *out_error_desc, const wchar_t *prefix); void get_backtrace(const wcstring &src, const parse_error_list_t &errors, wcstring *output) const; /** |