aboutsummaryrefslogtreecommitdiffhomepage
path: root/parser.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2014-02-28 17:54:05 -0800
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2014-02-28 20:01:04 -0800
commit84ea96f383d3713f750d39a74d7b8693275db25d (patch)
tree6b55f4b61770a46d4c3c2f6e640fa9f28d89f507 /parser.cpp
parent0e991c596f9ec1121689b04f558c4cf9b23fd220 (diff)
Begin removing remnants of old parser
Diffstat (limited to 'parser.cpp')
-rw-r--r--parser.cpp1539
1 files changed, 1 insertions, 1538 deletions
diff --git a/parser.cpp b/parser.cpp
index 7b1abe18..adfa263a 100644
--- a/parser.cpp
+++ b/parser.cpp
@@ -48,105 +48,16 @@ The fish parser. Contains functions for parsing and evaluating code.
#include "parse_execution.h"
/**
- Error message for unknown builtin
-*/
-#define UNKNOWN_BUILTIN_ERR_MSG _(L"Unknown builtin '%ls'")
-
-/**
- Error message for improper use of the exec builtin
-*/
-#define EXEC_ERR_MSG _(L"This command can not be used in a pipeline")
-
-/**
Error message for tokenizer error. The tokenizer message is
appended to this message.
*/
#define TOK_ERR_MSG _( L"Tokenizer error: '%ls'")
/**
- Error message for short circuit command error.
-*/
-#define COND_ERR_MSG _( L"An additional command is required" )
-
-/**
- Error message on a function that calls itself immediately
-*/
-#define INFINITE_RECURSION_ERR_MSG _( L"The function calls itself immediately, which would result in an infinite loop.")
-
-/**
- Error message used when the end of a block can't be located
-*/
-#define BLOCK_END_ERR_MSG _( L"Could not locate end of block. The 'end' command is missing, misspelled or a ';' is missing.")
-
-/** Error message when a non-string token is found when expecting a command name */
-#define CMD_ERR_MSG _( L"Expected a command name, got token of type '%ls'")
-
-/**
- Error message when encountering an illegal command name
-*/
-#define ILLEGAL_CMD_ERR_MSG _( L"Illegal command name '%ls'")
-
-/**
- Error message when encountering an illegal file descriptor
-*/
-#define ILLEGAL_FD_ERR_MSG _( L"Illegal file descriptor in redirection '%ls'")
-
-/**
- Error message for wildcards with no matches
-*/
-#define WILDCARD_ERR_MSG _( L"No matches for wildcard '%ls'.")
-
-/**
- Error when using case builtin outside of switch block
-*/
-#define INVALID_CASE_ERR_MSG _( L"'case' builtin not inside of switch block")
-
-/**
- Error when using loop control builtins (break or continue) outside of loop
-*/
-#define INVALID_LOOP_ERR_MSG _( L"Loop control command while not inside of loop" )
-
-/**
- Error when using return builtin outside of function definition
-*/
-#define INVALID_RETURN_ERR_MSG _( L"'return' builtin command outside of function definition" )
-
-/**
- Error when using else builtin outside of if block
-*/
-#define INVALID_ELSE_ERR_MSG _( L"'%ls' builtin not inside of if block" )
-
-/**
- Error when using 'else if' past a naked 'else'
-*/
-#define INVALID_ELSEIF_PAST_ELSE_ERR_MSG _( L"'%ls' used past terminating 'else'" )
-
-/**
- Error when using end builtin outside of block
-*/
-#define INVALID_END_ERR_MSG _( L"'end' command outside of block")
-
-/**
- Error message for Posix-style assignment: foo=bar
-*/
-#define COMMAND_ASSIGN_ERR_MSG _( L"Unknown command '%ls'. Did you mean 'set %ls %ls'? See the help section on the set command by typing 'help set'.")
-
-/**
- Error for invalid redirection token
-*/
-#define REDIRECT_TOKEN_ERR_MSG _( L"Expected redirection specification, got token of type '%ls'")
-
-/**
- Error when encountering redirection without a command
-*/
-#define INVALID_REDIRECTION_ERR_MSG _( L"Encountered redirection when expecting a command name. Fish does not allow a redirection operation before a command.")
-
-/**
Error for evaluating in illegal scope
*/
#define INVALID_SCOPE_ERR_MSG _( L"Tried to evaluate commands using invalid block type '%ls'" )
-
/**
Error for wrong token type
*/
@@ -167,20 +78,16 @@ The fish parser. Contains functions for parsing and evaluating code.
*/
#define BREAKPOINT_BLOCK N_( L"Block created by breakpoint" )
-
-
/**
If block description
*/
#define IF_BLOCK N_( L"'if' conditional block" )
-
/**
Function definition block description
*/
#define FUNCTION_DEF_BLOCK N_( L"function definition block" )
-
/**
Function invocation block description
*/
@@ -191,37 +98,31 @@ The fish parser. Contains functions for parsing and evaluating code.
*/
#define FUNCTION_CALL_NO_SHADOW_BLOCK N_( L"function invocation block with no variable shadowing" )
-
/**
Switch block description
*/
#define SWITCH_BLOCK N_( L"'switch' block" )
-
/**
Fake block description
*/
#define FAKE_BLOCK N_( L"unexecutable block" )
-
/**
Top block description
*/
#define TOP_BLOCK N_( L"global root block" )
-
/**
Command substitution block description
*/
#define SUBST_BLOCK N_( L"command substitution block" )
-
/**
Begin block description
*/
#define BEGIN_BLOCK N_( L"'begin' unconditional block" )
-
/**
Source block description
*/
@@ -232,7 +133,6 @@ The fish parser. Contains functions for parsing and evaluating code.
*/
#define EVENT_BLOCK N_( L"event handler block" )
-
/**
Unknown block description
*/
@@ -284,8 +184,6 @@ static const struct block_lookup_entry block_lookup[]=
{ (block_type_t)0, 0, 0 }
};
-static bool job_should_skip_elseif(const job_t *job, const block_t *current_block);
-
// Given a file path, return something nicer. Currently we just "unexpand" tildes.
static wcstring user_presentable_path(const wcstring &path)
{
@@ -1237,1332 +1135,6 @@ profile_item_t *parser_t::create_profile_item()
return result;
}
-/**
- Parse options for the specified job
-
- \param p the process to parse options for
- \param j the job to which the process belongs to
- \param tok the tokenizer to read options from
- \param args the argument list to insert options into
- \param args unskip whether we should ignore current_block()->skip. Big hack because of our dumb handling of if statements.
-*/
-void parser_t::parse_job_argument_list(process_t *p,
- job_t *j,
- tokenizer_t *tok,
- std::vector<completion_t> &args,
- bool unskip)
-{
- int is_finished=0;
-
- int proc_is_count=0;
-
- int matched_wildcard = 0, unmatched_wildcard = 0;
-
- wcstring unmatched;
- int unmatched_pos=0;
-
- /* The set of IO redirections that we construct for the process */
- io_chain_t process_io_chain;
-
- /*
- Test if this is the 'count' command. We need to special case
- count in the shell, since it should display a help message on
- 'count -h', but not on 'set foo -h; count $foo'. This is an ugly
- workaround and a huge hack, but as near as I can tell, the
- alternatives are worse.
- */
- proc_is_count = (args.at(0).completion == L"count");
-
- while (1)
- {
-
- switch (tok_last_type(tok))
- {
- case TOK_PIPE:
- {
- wchar_t *end;
-
- if (p->type == INTERNAL_EXEC)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- EXEC_ERR_MSG);
- return;
- }
-
- errno = 0;
- p->pipe_write_fd = fish_wcstoi(tok_last(tok), &end, 10);
- if (p->pipe_write_fd < 0 || errno || *end)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- ILLEGAL_FD_ERR_MSG,
- tok_last(tok));
- return;
- }
-
- p->set_argv(completions_to_wcstring_list(args));
- p->next = new process_t();
-
- tok_next(tok);
-
- /*
- Don't do anything on failure. parse_job will notice
- the error flag and report any errors for us
- */
- parse_job(p->next, j, tok);
-
- is_finished = 1;
- break;
- }
-
- case TOK_BACKGROUND:
- {
- job_set_flag(j, JOB_FOREGROUND, 0);
- // PCA note fall through, this is deliberate. The background modifier & terminates a command
- }
-
- case TOK_END:
- {
- if (!p->get_argv())
- p->set_argv(completions_to_wcstring_list(args));
- if (tok_has_next(tok))
- tok_next(tok);
-
- is_finished = 1;
-
- break;
- }
-
- case TOK_STRING:
- {
- int skip=0;
-
- if (job_get_flag(j, JOB_SKIP))
- {
- skip = 1;
- }
- else if (current_block()->skip && ! unskip)
- {
- /*
- If this command should be skipped, we do not expand the arguments
- */
- skip=1;
-
- /* But if this is in fact a case statement or an elseif statement, then it should be evaluated */
- block_type_t type = current_block()->type();
- if (type == SWITCH && args.at(0).completion == L"case" && p->type == INTERNAL_BUILTIN)
- {
- skip=0;
- }
- else if (job_get_flag(j, JOB_ELSEIF) && ! job_should_skip_elseif(j, current_block()))
- {
- skip=0;
- }
- }
- else
- {
- /* If this is an else if, and we should skip it, then don't expand any arguments */
- if (job_get_flag(j, JOB_ELSEIF) && job_should_skip_elseif(j, current_block()))
- {
- skip = 1;
- }
- }
-
- if (!skip)
- {
- if ((proc_is_count) &&
- (args.size() == 1) &&
- (parser_t::is_help(tok_last(tok), 0)) &&
- (p->type == INTERNAL_BUILTIN))
- {
- /*
- Display help for count
- */
- p->count_help_magic = 1;
- }
-
- switch (expand_string(tok_last(tok), args, 0))
- {
- case EXPAND_ERROR:
- {
- err_pos=tok_get_pos(tok);
- if (error_code == 0)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- _(L"Could not expand string '%ls'"),
- tok_last(tok));
-
- }
- break;
- }
-
- case EXPAND_WILDCARD_NO_MATCH:
- {
- unmatched_wildcard = 1;
- if (unmatched.empty())
- {
- unmatched = tok_last(tok);
- unmatched_pos = tok_get_pos(tok);
- }
-
- break;
- }
-
- case EXPAND_WILDCARD_MATCH:
- {
- matched_wildcard = 1;
- break;
- }
-
- case EXPAND_OK:
- {
- break;
- }
-
- }
-
- }
-
- break;
- }
-
- case TOK_REDIRECT_OUT:
- case TOK_REDIRECT_IN:
- case TOK_REDIRECT_APPEND:
- case TOK_REDIRECT_FD:
- case TOK_REDIRECT_NOCLOB:
- {
- int type = tok_last_type(tok);
- shared_ptr<io_data_t> new_io;
- wcstring target;
- bool has_target = false;
- wchar_t *end;
-
- /*
- Don't check redirections in skipped part
-
- Otherwise, bogus errors may be the result. (Do check
- that token is string, though)
- */
- if (current_block()->skip && ! unskip)
- {
- tok_next(tok);
- if (tok_last_type(tok) != TOK_STRING)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- REDIRECT_TOKEN_ERR_MSG,
- tok_get_desc(tok_last_type(tok)));
- }
-
- break;
- }
-
-
- errno = 0;
- int fd = fish_wcstoi(tok_last(tok),
- &end,
- 10);
- if (fd < 0 || errno || *end)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- ILLEGAL_FD_ERR_MSG,
- tok_last(tok));
- }
- else
- {
-
- tok_next(tok);
-
- switch (tok_last_type(tok))
- {
- case TOK_STRING:
- {
- target = tok_last(tok);
- has_target = expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0);
-
- if (! has_target && error_code == 0)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- REDIRECT_TOKEN_ERR_MSG,
- tok_last(tok));
-
- }
- break;
- }
-
- default:
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- REDIRECT_TOKEN_ERR_MSG,
- tok_get_desc(tok_last_type(tok)));
- }
-
- if (! has_target || target.empty())
- {
- if (error_code == 0)
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- _(L"Invalid IO redirection"));
- tok_next(tok);
- }
- else if (type == TOK_REDIRECT_FD)
- {
- if (target == L"-")
- {
- new_io.reset(new io_close_t(fd));
- }
- else
- {
- wchar_t *end;
-
- errno = 0;
-
- int old_fd = fish_wcstoi(target.c_str(), &end, 10);
-
- if (old_fd < 0 || errno || *end)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- _(L"Requested redirection to something that is not a file descriptor %ls"),
- target.c_str());
-
- tok_next(tok);
- }
- else
- {
- new_io.reset(new io_fd_t(fd, old_fd));
- }
- }
- }
- else
- {
- int flags = 0;
- switch (type)
- {
- case TOK_REDIRECT_APPEND:
- flags = O_CREAT | O_APPEND | O_WRONLY;
- break;
-
- case TOK_REDIRECT_OUT:
- flags = O_CREAT | O_WRONLY | O_TRUNC;
- break;
-
- case TOK_REDIRECT_NOCLOB:
- flags = O_CREAT | O_EXCL | O_WRONLY;
- break;
-
- case TOK_REDIRECT_IN:
- flags = O_RDONLY;
- break;
-
- }
- io_file_t *new_io_file = new io_file_t(fd, target, flags);
- new_io.reset(new_io_file);
- }
- }
-
- if (new_io.get() != NULL)
- {
- process_io_chain.push_back(new_io);
- }
-
- }
- break;
-
- case TOK_ERROR:
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- TOK_ERR_MSG,
- tok_last(tok));
-
- return;
- }
-
- default:
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- UNEXPECTED_TOKEN_ERR_MSG,
- tok_get_desc(tok_last_type(tok)));
-
- tok_next(tok);
- break;
- }
-
- if ((is_finished) || (error_code != 0))
- break;
-
- tok_next(tok);
- }
-
- if (!error_code)
- {
- if (unmatched_wildcard && !matched_wildcard)
- {
- job_set_flag(j, JOB_WILDCARD_ERROR, 1);
- proc_set_last_status(STATUS_UNMATCHED_WILDCARD);
- if (get_is_interactive() && !is_block)
- {
- int tmp;
-
- debug(1, WILDCARD_ERR_MSG, unmatched.c_str());
- tmp = current_tokenizer_pos;
- current_tokenizer_pos = unmatched_pos;
-
- fwprintf(stderr, L"%ls", parser_t::current_line());
-
- current_tokenizer_pos=tmp;
- }
-
- }
- }
-
- /* Store our IO chain. The existing chain should be empty. */
- assert(p->io_chain().empty());
- p->set_io_chain(process_io_chain);
-}
-
-/**
- Fully parse a single job. Does not call exec on it, but any command substitutions in the job will be executed.
-
- \param p The process structure that should be used to represent the first process in the job.
- \param j The job structure to contain the parsed job
- \param tok tokenizer to read from
-f
- \return 1 on success, 0 on error
-*/
-int parser_t::parse_job(process_t *p, job_t *j, tokenizer_t *tok)
-{
- std::vector<completion_t> args; // The list that will become the argv array for the program
- int use_function = 1; // May functions be considered when checking what action this command represents
- int use_builtin = 1; // May builtins be considered when checking what action this command represents
- int use_command = 1; // May commands be considered when checking what action this command represents
- int is_new_block=0; // Does this command create a new block?
- bool unskip = false; // Maybe we are an elseif inside an if block; if so we may want to evaluate this even if the if block is currently set to skip
- bool allow_bogus_command = false; // If we are an elseif that will not be executed, or an AND or OR that will have been short circuited, don't complain about non-existent commands
-
- const block_t *prev_block = current_block();
- scoped_push<int> tokenizer_pos_push(&current_tokenizer_pos, tok_get_pos(tok));
-
- while (args.empty())
- {
- wcstring nxt;
- bool has_nxt = false;
- bool consumed = false; // Set to one if the command requires a second command, like e.g. while does
- int mark; // Use to save the position of the beginning of the token
-
- switch (tok_last_type(tok))
- {
- case TOK_STRING:
- {
- nxt = tok_last(tok);
- has_nxt = expand_one(nxt, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES);
-
- if (! has_nxt)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- ILLEGAL_CMD_ERR_MSG,
- tok_last(tok));
-
- return 0;
- }
- break;
- }
-
- case TOK_ERROR:
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- TOK_ERR_MSG,
- tok_last(tok));
-
- return 0;
- }
-
- case TOK_PIPE:
- {
- const wchar_t *str = tok_string(tok);
- if (tok_get_pos(tok)>0 && str[tok_get_pos(tok)-1] == L'|')
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- CMD_OR_ERR_MSG);
- }
- else
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- CMD_ERR_MSG,
- tok_get_desc(tok_last_type(tok)));
- }
-
- return 0;
- }
-
- default:
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- CMD_ERR_MSG,
- tok_get_desc(tok_last_type(tok)));
-
- return 0;
- }
- }
-
- mark = tok_get_pos(tok);
-
- if (contains(nxt,
- L"command",
- L"builtin",
- L"not",
- L"and",
- L"or",
- L"exec"))
- {
- int sw;
- int is_exec = nxt == L"exec";
-
- if (is_exec && (p != j->first_process))
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- EXEC_ERR_MSG);
- return 0;
- }
-
- tok_next(tok);
- sw = parser_keywords_is_switch(tok_last(tok));
-
- if (sw == ARG_SWITCH)
- {
- tok_set_pos(tok, mark);
- }
- else
- {
- if (sw == ARG_SKIP)
- {
- tok_next(tok);
- }
-
- consumed = true;
-
- if (nxt == L"command" || nxt == L"builtin")
- {
- use_function = 0;
- if (nxt == L"command")
- {
- use_builtin = 0;
- use_command = 1;
- }
- else
- {
- use_builtin = 1;
- use_command = 0;
- }
- }
- else if (nxt == L"not")
- {
- job_set_flag(j, JOB_NEGATE, !job_get_flag(j, JOB_NEGATE));
- }
- else if (nxt == L"and")
- {
- bool skip = (proc_get_last_status() != 0);
- job_set_flag(j, JOB_SKIP, skip);
- allow_bogus_command = skip;
- }
- else if (nxt == L"or")
- {
- bool skip = (proc_get_last_status() == 0);
- job_set_flag(j, JOB_SKIP, skip);
- allow_bogus_command = skip;
- }
- else if (is_exec)
- {
- use_function = 0;
- use_builtin=0;
- p->type=INTERNAL_EXEC;
- tokenizer_pos_push.restore();
- }
- }
- }
- else if (nxt == L"while")
- {
- bool new_block = false;
- tok_next(tok);
- while_block_t *wb = NULL;
-
- if ((current_block()->type() != WHILE))
- {
- new_block = true;
- }
- else if ((wb = static_cast<while_block_t*>(current_block()))->status == WHILE_TEST_AGAIN)
- {
- wb->status = WHILE_TEST_FIRST;
- }
- else
- {
- new_block = true;
- }
-
- if (new_block)
- {
- while_block_t *wb = new while_block_t();
- wb->status = WHILE_TEST_FIRST;
- wb->tok_pos = mark;
- this->push_block(wb);
- }
-
- consumed = true;
- is_new_block=1;
-
- }
- else if (nxt == L"if")
- {
- tok_next(tok);
-
- if_block_t *ib = new if_block_t();
- this->push_block(ib);
- ib->tok_pos = mark;
-
- is_new_block=1;
- consumed = true;
- }
- else if (nxt == L"else")
- {
- /* Record where the else is for error reporting */
- const int else_pos = tok_get_pos(tok);
- /* See if we have any more arguments, that is, whether we're ELSE IF ... or just ELSE. */
- tok_next(tok);
- if (tok_last_type(tok) == TOK_STRING && current_block()->type() == IF)
- {
- const if_block_t *ib = static_cast<const if_block_t *>(current_block());
-
- /* If we've already encountered an else, complain */
- if (ib->else_evaluated)
- {
- error(SYNTAX_ERROR,
- else_pos,
- INVALID_ELSEIF_PAST_ELSE_ERR_MSG,
- L"else if");
-
- }
- else
- {
-
- job_set_flag(j, JOB_ELSEIF, 1);
- consumed = true;
-
- /* We're at the IF. Go past it. */
- tok_next(tok);
-
- /* We want to execute this ELSEIF if the IF expression was evaluated, it failed, and so has every other ELSEIF (if any) */
- unskip = (ib->if_expr_evaluated && ! ib->any_branch_taken);
-
- /* But if we're not executing it, don't complain about its command if it doesn't exist */
- if (! unskip)
- allow_bogus_command = true;
- }
- }
- }
-
- /*
- Test if we need another command
- */
- if (consumed)
- {
- /*
- Yes we do, around in the loop for another lap, then!
- */
- continue;
- }
-
- if (use_function && (unskip || ! current_block()->skip))
- {
- bool nxt_forbidden=false;
- wcstring forbid;
-
- int is_function_call=0;
-
- /*
- This is a bit fragile. It is a test to see if we are
- inside of function call, but not inside a block in that
- function call. If, in the future, the rules for what
- block scopes are pushed on function invocation changes,
- then this check will break.
- */
- const block_t *current = this->block_at_index(0), *parent = this->block_at_index(1);
- if (current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL)
- is_function_call = 1;
-
- /*
- If we are directly in a function, and this is the first
- command of the block, then the function we are executing
- may not be called, since that would mean an infinite
- recursion.
- */
- if (is_function_call && !current->had_command)
- {
- forbid = forbidden_function.empty() ? wcstring(L"") : forbidden_function.back();
- if (forbid == nxt)
- {
- /* Infinite recursive loop */
- nxt_forbidden = true;
- error(SYNTAX_ERROR, tok_get_pos(tok), INFINITE_RECURSION_ERR_MSG);
- }
- }
-
- if (!nxt_forbidden && has_nxt && function_exists(nxt))
- {
- /*
- Check if we have reached the maximum recursion depth
- */
- if (forbidden_function.size() > FISH_MAX_STACK_DEPTH)
- {
- error(SYNTAX_ERROR, tok_get_pos(tok), CALL_STACK_LIMIT_EXCEEDED_ERR_MSG);
- }
- else
- {
- p->type = INTERNAL_FUNCTION;
- }
- }
- }
- append_completion(args, nxt);
- }
-
- if (error_code == 0)
- {
- if (!p->type)
- {
- if (use_builtin &&
- builtin_exists(args.at(0).completion))
- {
- p->type = INTERNAL_BUILTIN;
- is_new_block |= parser_keywords_is_block(args.at(0).completion);
- }
- }
-
- if ((!p->type || (p->type == INTERNAL_EXEC)))
- {
- /*
- If we are not executing the current block, allow
- non-existent commands.
- */
- if (current_block()->skip && ! unskip)
- allow_bogus_command = true; //note this may already be true for other reasons
-
- if (allow_bogus_command)
- {
- p->actual_cmd.clear();
- }
- else
- {
- int err;
- bool has_command = path_get_path(args.at(0).completion, &p->actual_cmd);
- err = errno;
-
- bool use_implicit_cd = false;
- if (! has_command)
- {
- /* If the specified command does not exist, try using an implicit cd. */
- wcstring implicit_cd_path;
- use_implicit_cd = path_can_be_implicit_cd(args.at(0).completion, &implicit_cd_path);
- if (use_implicit_cd)
- {
- args.clear();
- append_completion(args, L"cd");
- append_completion(args, implicit_cd_path);
-
- /* If we have defined a wrapper around cd, use it, otherwise use the cd builtin */
- if (use_function && function_exists(L"cd"))
- p->type = INTERNAL_FUNCTION;
- else
- p->type = INTERNAL_BUILTIN;
- }
- }
-
- // Disabled pending discussion in https://github.com/fish-shell/fish-shell/issues/367
-#if 0
- if (! has_command && ! use_implicit_cd)
- {
- if (fish_openSUSE_dbus_hack_hack_hack_hack(&args))
- {
- has_command = true;
- p->type = INTERNAL_BUILTIN;
- }
- }
-#endif
-
- /* Check if the specified command exists */
- if (! has_command && ! use_implicit_cd)
- {
-
- const wchar_t *cmd = args.at(0).completion.c_str();
-
- /*
- We couldn't find the specified command.
-
- What we want to happen now is that the
- specified job won't get executed, and an
- error message is printed on-screen, but
- otherwise, the parsing/execution of the
- file continues. Because of this, we don't
- want to call error(), since that would stop
- execution of the file. Instead we let
- p->actual_command be 0 (null), which will
- cause the job to silently not execute. We
- also print an error message and set the
- status to 127 (This is the standard number
- for this, used by other shells like bash
- and zsh).
- */
-
- const wchar_t * const equals_ptr = wcschr(cmd, L'=');
- if (equals_ptr != NULL)
- {
- /* Try to figure out if this is a pure variable assignment (foo=bar), or if this appears to be running a command (foo=bar ruby...) */
-
- const wcstring name_str = wcstring(cmd, equals_ptr - cmd); //variable name, up to the =
- const wcstring val_str = wcstring(equals_ptr + 1); //variable value, past the =
-
- wcstring next_str;
- if (tok_peek_next(tok, &next_str) == TOK_STRING && ! next_str.empty())
- {
- wcstring ellipsis_str = wcstring(1, ellipsis_char);
- if (ellipsis_str == L"$")
- ellipsis_str = L"...";
-
- /* Looks like a command */
- debug(0,
- _(L"Unknown command '%ls'. Did you mean to run %ls with a modified environment? Try 'env %ls=%ls %ls%ls'. See the help section on the set command by typing 'help set'."),
- cmd,
- next_str.c_str(),
- name_str.c_str(),
- val_str.c_str(),
- next_str.c_str(),
- ellipsis_str.c_str());
- }
- else
- {
- debug(0,
- COMMAND_ASSIGN_ERR_MSG,
- cmd,
- name_str.c_str(),
- val_str.c_str());
- }
- }
- else if (cmd[0]==L'$' || cmd[0] == VARIABLE_EXPAND || cmd[0] == VARIABLE_EXPAND_SINGLE)
- {
-
- const env_var_t val_wstr = env_get_string(cmd+1);
- const wchar_t *val = val_wstr.missing() ? NULL : val_wstr.c_str();
- if (val)
- {
- debug(0,
- _(L"Variables may not be used as commands. Instead, define a function like 'function %ls; %ls $argv; end' or use the eval builtin instead, like 'eval %ls'. See the help section for the function command by typing 'help function'."),
- cmd+1,
- val,
- cmd,
- cmd);
- }
- else
- {
- debug(0,
- _(L"Variables may not be used as commands. Instead, define a function or use the eval builtin instead, like 'eval %ls'. See the help section for the function command by typing 'help function'."),
- cmd,
- cmd);
- }
- }
- else if (wcschr(cmd, L'$'))
- {
- debug(0,
- _(L"Commands may not contain variables. Use the eval builtin instead, like 'eval %ls'. See the help section for the eval command by typing 'help eval'."),
- cmd,
- cmd);
- }
- else if (err!=ENOENT)
- {
- debug(0,
- _(L"The file '%ls' is not executable by this user"),
- cmd?cmd:L"UNKNOWN");
- }
- else
- {
- /*
- Handle unrecognized commands with standard
- command not found handler that can make better
- error messages
- */
-
- wcstring_list_t event_args;
- event_args.push_back(args.at(0).completion);
- event_fire_generic(L"fish_command_not_found", &event_args);
- }
-
- int tmp = current_tokenizer_pos;
- current_tokenizer_pos = tok_get_pos(tok);
-
- fwprintf(stderr, L"%ls", parser_t::current_line());
-
- current_tokenizer_pos=tmp;
-
- job_set_flag(j, JOB_SKIP, 1);
-
- proc_set_last_status(err==ENOENT?STATUS_UNKNOWN_COMMAND:STATUS_NOT_EXECUTABLE);
- }
- }
- }
-
- if ((p->type == EXTERNAL) && !use_command)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- UNKNOWN_BUILTIN_ERR_MSG,
- args.back().completion.c_str());
- }
- }
-
-
- if (is_new_block)
- {
-
- const wchar_t *end=parser_find_end(tok_string(tok) +
- current_tokenizer_pos);
- int make_sub_block = j->first_process != p;
-
- if (!end)
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- BLOCK_END_ERR_MSG);
-
- }
- else
- {
-
- if (!make_sub_block)
- {
- int done=0;
-
- tokenizer_t subtok(end, 0);
- for (; ! done && tok_has_next(&subtok); tok_next(&subtok))
- {
-
- switch (tok_last_type(&subtok))
- {
- case TOK_END:
- done = 1;
- break;
-
- case TOK_REDIRECT_OUT:
- case TOK_REDIRECT_NOCLOB:
- case TOK_REDIRECT_APPEND:
- case TOK_REDIRECT_IN:
- case TOK_REDIRECT_FD:
- case TOK_PIPE:
- {
- done = 1;
- make_sub_block = 1;
- break;
- }
-
- case TOK_STRING:
- {
- break;
- }
-
- default:
- {
- done = 1;
- error(SYNTAX_ERROR,
- current_tokenizer_pos,
- BLOCK_END_ERR_MSG);
- }
- }
- }
- }
-
- if (make_sub_block)
- {
-
- long end_pos = end-tok_string(tok);
- const wcstring sub_block(tok_string(tok) + current_tokenizer_pos, end_pos - current_tokenizer_pos);
-
- p->type = INTERNAL_BLOCK;
- args.at(0) = completion_t(sub_block);
-
- tok_set_pos(tok, (int)end_pos);
-
- while (prev_block != this->current_block())
- {
- parser_t::pop_block();
- }
-
- }
- else tok_next(tok);
- }
-
- }
- else tok_next(tok);
-
- if (!error_code)
- {
- if (p->type == INTERNAL_BUILTIN && parser_keywords_skip_arguments(args.at(0).completion))
- {
- if (!p->get_argv())
- p->set_argv(completions_to_wcstring_list(args));
- }
- else
- {
- parse_job_argument_list(p, j, tok, args, unskip);
- }
- }
-
- if (!error_code)
- {
- if (!is_new_block)
- {
- current_block()->had_command = true;
- }
- }
-
- if (error_code)
- {
- /*
- Make sure the block stack is consistent
- */
- while (prev_block != current_block())
- {
- parser_t::pop_block();
- }
- }
- return !error_code;
-}
-
-/**
- Do skipped execution of command. This means that only limited
- execution of block level commands such as end and switch should be
- preformed.
-
- \param j the job to execute
-
-*/
-void parser_t::skipped_exec(job_t * j)
-{
- process_t *p;
-
- /* Handle other skipped guys */
- for (p = j->first_process; p; p=p->next)
- {
- if (p->type == INTERNAL_BUILTIN)
- {
- if ((wcscmp(p->argv0(), L"for")==0) ||
- (wcscmp(p->argv0(), L"switch")==0) ||
- (wcscmp(p->argv0(), L"begin")==0) ||
- (wcscmp(p->argv0(), L"function")==0))
- {
- this->push_block(new fake_block_t());
- }
- else if (wcscmp(p->argv0(), L"end")==0)
- {
- const block_t *parent = this->block_at_index(1);
- if (parent && ! parent->skip)
- {
- exec_job(*this, j);
- return;
- }
- parser_t::pop_block();
- }
- else if (wcscmp(p->argv0(), L"else")==0)
- {
- if (current_block()->type() == IF)
- {
- /* Evaluate this ELSE if the IF expression failed, and so has every ELSEIF (if any) expression thus far */
- const if_block_t *ib = static_cast<const if_block_t*>(current_block());
- if (ib->if_expr_evaluated && ! ib->any_branch_taken)
- {
- exec_job(*this, j);
- return;
- }
- }
- }
- else if (wcscmp(p->argv0(), L"case")==0)
- {
- if (current_block()->type() == SWITCH)
- {
- exec_job(*this, j);
- return;
- }
- }
- }
- }
- job_free(j);
-}
-
-/* Return whether we should skip the current block, if it is an elseif. */
-static bool job_should_skip_elseif(const job_t *job, const block_t *current_block)
-{
- if (current_block->type() != IF)
- {
- /* Not an IF block, so just honor the skip property */
- return current_block->skip;
- }
- else
- {
- /* We are an IF block */
- const if_block_t *ib = static_cast<const if_block_t *>(current_block);
-
- /* Execute this ELSEIF if the IF expression has been evaluated, it evaluated to false, and all ELSEIFs so far have evaluated to false. */
- bool execute_elseif = (ib->if_expr_evaluated && ! ib->any_branch_taken);
-
- /* Invert the sense */
- return ! execute_elseif;
- }
-}
-
-/**
- Evaluates a job from the specified tokenizer. First calls
- parse_job to parse the job and then calls exec to execute it.
-
- \param tok The tokenizer to read tokens from
-*/
-
-void parser_t::eval_job(tokenizer_t *tok)
-{
- ASSERT_IS_MAIN_THREAD();
-
- int start_pos = job_start_pos = tok_get_pos(tok);
- long long t1=0, t2=0, t3=0;
-
-
- bool skip = false;
- int job_begin_pos;
-
- profile_item_t *profile_item = create_profile_item();
- const bool do_profile = (profile_item != NULL);
- if (do_profile)
- {
- profile_item->skipped = 1;
- t1 = get_time();
- }
-
- switch (tok_last_type(tok))
- {
- case TOK_STRING:
- {
- job_t *j = this->job_create(this->block_io);
- job_set_flag(j, JOB_FOREGROUND, 1);
- job_set_flag(j, JOB_TERMINAL, job_get_flag(j, JOB_CONTROL));
- job_set_flag(j, JOB_TERMINAL, job_get_flag(j, JOB_CONTROL) \
- && (!is_subshell && !is_event));
- job_set_flag(j, JOB_SKIP_NOTIFICATION, is_subshell \
- || is_block \
- || is_event \
- || (!get_is_interactive()));
-
- current_block()->job = j;
-
- if (get_is_interactive())
- {
- if (tcgetattr(0, &j->tmodes))
- {
- tok_next(tok);
- wperror(L"tcgetattr");
- job_free(j);
- break;
- }
- }
-
- j->first_process = new process_t();
- job_begin_pos = tok_get_pos(tok);
-
- if (parse_job(j->first_process, j, tok) &&
- j->first_process->get_argv())
- {
- if (job_start_pos < tok_get_pos(tok))
- {
- long stop_pos = tok_get_pos(tok);
- const wchar_t *newline = wcschr(tok_string(tok)+start_pos, L'\n');
- if (newline)
- stop_pos = mini<long>(stop_pos, newline - tok_string(tok));
-
- j->set_command(wcstring(tok_string(tok)+start_pos, stop_pos-start_pos));
- }
- else
- j->set_command(L"");
-
- if (do_profile)
- {
- t2 = get_time();
- profile_item->cmd = j->command();
- profile_item->skipped=current_block()->skip;
- }
-
- /* If we're an ELSEIF, then we may want to unskip, if we're skipping because of an IF */
- if (job_get_flag(j, JOB_ELSEIF))
- {
- bool skip_elseif = job_should_skip_elseif(j, current_block());
-
- /* Record that we're entering an elseif */
- if (! skip_elseif)
- {
- /* We must be an IF block here */
- assert(current_block()->type() == IF);
- static_cast<if_block_t *>(current_block())->is_elseif_entry = true;
- }
-
- /* Record that in the block too. This is similar to what builtin_else does. */
- current_block()->skip = skip_elseif;
- }
-
- skip = skip || current_block()->skip;
- skip = skip || job_get_flag(j, JOB_WILDCARD_ERROR);
- skip = skip || job_get_flag(j, JOB_SKIP);
-
- if (!skip)
- {
- int was_builtin = 0;
- if (j->first_process->type==INTERNAL_BUILTIN && !j->first_process->next)
- {
- was_builtin = 1;
- }
- scoped_push<int> tokenizer_pos_push(&current_tokenizer_pos, job_begin_pos);
- exec_job(*this, j);
-
- /* Only external commands require a new fishd barrier */
- if (!was_builtin)
- set_proc_had_barrier(false);
- }
- else
- {
- this->skipped_exec(j);
- }
-
- if (do_profile)
- {
- t3 = get_time();
- profile_item->level=eval_level;
- profile_item->parse = (int)(t2-t1);
- profile_item->exec=(int)(t3-t2);
- }
-
- if (current_block()->type() == WHILE)
- {
- while_block_t *wb = static_cast<while_block_t *>(current_block());
- switch (wb->status)
- {
- case WHILE_TEST_FIRST:
- {
- // PCA I added the 'wb->skip ||' part because we couldn't reliably
- // control-C out of loops like this: while test 1 -eq 1; end
- wb->skip = wb->skip || proc_get_last_status()!= 0;
- wb->status = WHILE_TESTED;
- }
- break;
- }
- }
-
- if (current_block()->type() == IF)
- {
- if_block_t *ib = static_cast<if_block_t *>(current_block());
-
- if (ib->skip)
- {
- /* Nothing */
- }
- else if (! ib->if_expr_evaluated)
- {
- /* Execute the IF */
- bool if_result = (proc_get_last_status() == 0);
- ib->any_branch_taken = if_result;
-
- /* Don't execute if the expression failed */
- current_block()->skip = ! if_result;
- ib->if_expr_evaluated = true;
- }
- else if (ib->is_elseif_entry && ! ib->any_branch_taken)
- {
- /* Maybe mark an ELSEIF branch as taken */
- bool elseif_taken = (proc_get_last_status() == 0);
- ib->any_branch_taken = elseif_taken;
- current_block()->skip = ! elseif_taken;
- ib->is_elseif_entry = false;
- }
- }
-
- }
- else
- {
- /*
- This job could not be properly parsed. We free it
- instead, and set the status to 1. This should be
- rare, since most errors should be detected by the
- ahead of time validator.
- */
- job_free(j);
-
- proc_set_last_status(1);
- }
- current_block()->job = 0;
- break;
- }
-
- case TOK_END:
- {
- if (tok_has_next(tok))
- tok_next(tok);
- break;
- }
-
- case TOK_BACKGROUND:
- {
- const wchar_t *str = tok_string(tok);
- if (tok_get_pos(tok)>0 && str[tok_get_pos(tok)-1] == L'&')
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- CMD_AND_ERR_MSG);
- }
- else
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- CMD_ERR_MSG,
- tok_get_desc(tok_last_type(tok)));
- }
-
- return;
- }
-
- case TOK_ERROR:
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- TOK_ERR_MSG,
- tok_last(tok));
-
- return;
- }
-
- default:
- {
- error(SYNTAX_ERROR,
- tok_get_pos(tok),
- CMD_ERR_MSG,
- tok_get_desc(tok_last_type(tok)));
-
- return;
- }
- }
-
- job_reap(0);
-}
int parser_t::eval_new_parser(const wcstring &cmd, const io_chain_t &io, enum block_type_t block_type)
{
@@ -2664,116 +1236,7 @@ int parser_t::eval_block_node(node_offset_t node_idx, const io_chain_t &io, enum
int parser_t::eval(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type)
{
-
- if (parser_use_ast())
- return this->eval_new_parser(cmd_str, io, block_type);
-
- const wchar_t * const cmd = cmd_str.c_str();
- size_t forbid_count;
- int code;
- const block_t *start_current_block = current_block();
-
- /* Record the current chain so we can put it back later */
- scoped_push<io_chain_t> block_io_push(&block_io, io);
-
- scoped_push<wcstring_list_t> forbidden_function_push(&forbidden_function);
-
- if (block_type == SUBST)
- {
- forbidden_function.clear();
- }
-
- CHECK_BLOCK(1);
-
- forbid_count = forbidden_function.size();
-
- job_reap(0);
-
- debug(4, L"eval: %ls", cmd);
-
-
- if ((block_type != TOP) &&
- (block_type != SUBST))
- {
- debug(1,
- INVALID_SCOPE_ERR_MSG,
- parser_t::get_block_desc(block_type));
- bugreport();
- return 1;
- }
-
- eval_level++;
-
- this->push_block(new scope_block_t(block_type));
-
- tokenizer_t local_tokenizer(cmd, 0);
- scoped_push<tokenizer_t *> tokenizer_push(&current_tokenizer, &local_tokenizer);
- scoped_push<int> tokenizer_pos_push(&current_tokenizer_pos, 0);
-
- error_code = 0;
-
- event_fire(NULL);
-
- while (tok_has_next(current_tokenizer) &&
- !error_code &&
- !sanity_check() &&
- !shell_is_exiting())
- {
- this->eval_job(current_tokenizer);
- event_fire(NULL);
- }
-
- parser_t::pop_block();
-
- while (start_current_block != current_block())
- {
- if (current_block() == NULL)
- {
- debug(0,
- _(L"End of block mismatch. Program terminating."));
- bugreport();
- FATAL_EXIT();
- break;
- }
-
- if ((!error_code) && (!shell_is_exiting()) && (!proc_get_last_status()))
- {
-
- //debug( 2, L"Status %d\n", proc_get_last_status() );
-
- debug(1,
- L"%ls", parser_t::get_block_desc(current_block()->type()));
- debug(1,
- BLOCK_END_ERR_MSG);
- fwprintf(stderr, L"%ls", parser_t::current_line());
-
- const wcstring h = builtin_help_get(*this, L"end");
- if (h.size())
- fwprintf(stderr, L"%ls", h.c_str());
- break;
-
- }
- parser_t::pop_block();
- }
-
- this->print_errors_stderr();
-
- tokenizer_push.restore();
-
- while (forbidden_function.size() > forbid_count)
- parser_t::allow_function();
-
- /*
- Restore previous eval state
- */
- eval_level--;
-
- code=error_code;
- error_code=0;
-
- job_reap(0);
-
- return code;
+ return this->eval_new_parser(cmd_str, io, block_type);
}