Very early work in adopting new parser for actual execution of commands.

Not turned on yet.
This commit is contained in:
ridiculousfish 2013-12-23 14:53:56 -08:00
parent ebc8bd6ff5
commit b6af3e51ab
7 changed files with 461 additions and 208 deletions

View file

@ -1862,7 +1862,7 @@ void highlighter_t::color_redirection(const parse_node_t &redirection_node)
if (redirection_primitive != NULL)
{
wcstring target;
const enum token_type redirect_type = this->parse_tree.type_for_redirection(redirection_node, this->buff, &target);
const enum token_type redirect_type = this->parse_tree.type_for_redirection(redirection_node, this->buff, NULL, &target);
/* We may get a TOK_NONE redirection type, e.g. if the redirection is invalid */
this->color_node(*redirection_primitive, redirect_type == TOK_NONE ? HIGHLIGHT_ERROR : HIGHLIGHT_REDIRECTION);

View file

@ -1083,6 +1083,20 @@ const parse_node_t *parse_node_tree_t::get_child(const parse_node_t &parent, nod
return result;
}
const parse_node_t &parse_node_tree_t::find_child(const parse_node_t &parent, parse_token_type_t type) const
{
for (size_t i=0; i < parent.child_count; i++)
{
const parse_node_t *child = this->get_child(parent, i);
if (child->type == type)
{
return *child;
}
}
PARSE_ASSERT(0);
return *(parse_node_t *)(NULL); //unreachable
}
const parse_node_t *parse_node_tree_t::get_parent(const parse_node_t &node, parse_token_type_t expected_type) const
{
const parse_node_t *result = NULL;
@ -1277,7 +1291,7 @@ bool parse_node_tree_t::plain_statement_is_in_pipeline(const parse_node_t &node,
return result;
}
enum token_type parse_node_tree_t::type_for_redirection(const parse_node_t &redirection_node, const wcstring &src, wcstring *out_target) const
enum token_type parse_node_tree_t::type_for_redirection(const parse_node_t &redirection_node, const wcstring &src, int *out_fd, wcstring *out_target) const
{
assert(redirection_node.type == symbol_redirection);
enum token_type result = TOK_NONE;
@ -1286,7 +1300,7 @@ enum token_type parse_node_tree_t::type_for_redirection(const parse_node_t &redi
if (redirection_primitive != NULL && redirection_primitive->has_source())
{
result = redirection_type_for_string(redirection_primitive->get_source(src));
result = redirection_type_for_string(redirection_primitive->get_source(src), out_fd);
}
if (out_target != NULL)
{

View file

@ -166,6 +166,10 @@ public:
*/
const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type = token_type_invalid) const;
/* Find the first direct child of the given node of the given type. asserts on failure
*/
const parse_node_t &find_child(const parse_node_t &parent, parse_token_type_t type) const;
/* Get the node corresponding to the parent of the given node, or NULL if there is no such child. If expected_type is provided, only returns the parent if it is of that type. Note the asymmetry: get_child asserts since the children are known, but get_parent does not, since the parent may not be known. */
const parse_node_t *get_parent(const parse_node_t &node, parse_token_type_t expected_type = token_type_invalid) const;
@ -197,7 +201,7 @@ public:
bool plain_statement_is_in_pipeline(const parse_node_t &node, bool include_first) const;
/* Given a redirection, get the redirection type (or TOK_NONE) and target (file path, or fd) */
enum token_type type_for_redirection(const parse_node_t &node, const wcstring &src, wcstring *out_target) const;
enum token_type type_for_redirection(const parse_node_t &node, const wcstring &src, int *out_fd, wcstring *out_target) const;
/* If the given node is a block statement, returns the header node (for_header, while_header, begin_header, or function_header). Otherwise returns NULL */
const parse_node_t *header_node_for_block_statement(const parse_node_t &node);

View file

@ -1648,13 +1648,12 @@ void parser_t::parse_job_argument_list(process_t *p,
}
*/
#if 0
process_t *parser_t::create_boolean_process(job_t *job, const parse_node_t &bool_statement, const parser_context_t &ctx)
{
// Handle a boolean statement
bool skip_job = false;
assert(bool_statement.type == symbol_boolean_statement);
switch (specific_statement.production_idx)
switch (bool_statement.production_idx)
{
// These magic numbers correspond to productions for boolean_statement
case 0:
@ -1689,15 +1688,280 @@ process_t *parser_t::create_boolean_process(job_t *job, const parse_node_t &bool
return result;
}
process_t *parser_t::create_for_process(job_t *job, const parse_node_t &header, const parse_node_t &statement, const parser_context_t &ctx)
{
return NULL;
}
process_t *parser_t::create_while_process(job_t *job, const parse_node_t &header, const parse_node_t &statement, const parser_context_t &ctx)
{
return NULL;
}
process_t *parser_t::create_begin_process(job_t *job, const parse_node_t &header, const parse_node_t &statement, const parser_context_t &ctx)
{
return NULL;
}
process_t *parser_t::create_plain_process(job_t *job, const parse_node_t &statement, const parser_context_t &ctx)
{
/* Get the decoration */
assert(statement.type == symbol_plain_statement);
/* Get the command. We expect to always get it here. */
wcstring cmd;
bool got_cmd = ctx.tree.command_for_plain_statement(statement, ctx.src, &cmd);
assert(got_cmd);
/* Expand it as a command */
bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES);
if (! expanded)
{
error(SYNTAX_ERROR,
statement.source_start,
ILLEGAL_CMD_ERR_MSG,
cmd.c_str());
return 0;
}
/* The list of arguments. The command is the first argument. TODO: count hack */
const parse_node_t *unmatched_wildcard = NULL;
wcstring_list_t argument_list = this->determine_arguments(statement, &unmatched_wildcard, ctx);
argument_list.insert(argument_list.begin(), cmd);
/* We were not able to expand any wildcards. Here is the first one that failed */
if (unmatched_wildcard != NULL)
{
job_set_flag(job, JOB_WILDCARD_ERROR, 1);
proc_set_last_status(STATUS_UNMATCHED_WILDCARD);
error(EVAL_ERROR, unmatched_wildcard->source_start, WILDCARD_ERR_MSG, unmatched_wildcard->get_source(ctx.src).c_str());
}
/* The set of IO redirections that we construct for the process */
const io_chain_t process_io_chain = this->determine_io_chain(statement, ctx);
/* Determine the process type, which depends on the statement decoration (command, builtin, etc) */
enum parse_statement_decoration_t decoration = ctx.tree.decoration_for_plain_statement(statement);
enum process_type_t process_type = EXTERNAL;
/* exec hack */
if (decoration != parse_statement_decoration_command && cmd == L"exec")
{
/* Either 'builtin exec' or just plain 'exec', and definitely not 'command exec'. Note we don't allow overriding exec with a function. */
process_type = INTERNAL_EXEC;
}
else if (decoration == parse_statement_decoration_command)
{
/* Always a command */
process_type = EXTERNAL;
}
else if (decoration == parse_statement_decoration_builtin)
{
/* What happens if this builtin is not valid? */
process_type = INTERNAL_BUILTIN;
}
else if (function_exists(cmd))
{
process_type = INTERNAL_FUNCTION;
}
else if (builtin_exists(cmd))
{
process_type = INTERNAL_BUILTIN;
}
else
{
process_type = EXTERNAL;
}
wcstring actual_cmd;
if (process_type == EXTERNAL)
{
/* Determine the actual command. Need to support implicit cd here */
bool has_command = path_get_path(cmd, &actual_cmd);
if (! has_command)
{
/* TODO: support fish_command_not_found, implicit cd, etc. here */
}
}
/* Return the process */
process_t *result = new process_t();
result->type = process_type;
result->set_argv(argument_list);
result->set_io_chain(process_io_chain);
result->actual_cmd = actual_cmd;
return result;
}
/* Determine the list of arguments, expanding stuff. If we have a wildcard and none could be expanded, return the unexpandable wildcard node by reference. */
wcstring_list_t parser_t::determine_arguments(const parse_node_t &statement, const parse_node_t **out_unmatched_wildcard_node, const parser_context_t &ctx)
{
wcstring_list_t argument_list;
/* Whether we failed to match any wildcards, and succeeded in matching any wildcards */
bool unmatched_wildcard = false, matched_wildcard = false;
/* First node that failed to expand as a wildcard (if any) */
const parse_node_t *unmatched_wildcard_node = NULL;
/* Get all argument nodes underneath the statement */
const parse_node_tree_t::parse_node_list_t argument_nodes = ctx.tree.find_nodes(statement, symbol_argument);
argument_list.reserve(argument_nodes.size());
for (size_t i=0; i < argument_nodes.size(); i++)
{
const parse_node_t &arg_node = *argument_nodes.at(i);
/* Expect all arguments to have source */
assert(arg_node.has_source());
const wcstring arg_str = arg_node.get_source(ctx.src);
/* Expand this string */
std::vector<completion_t> arg_expanded;
int expand_ret = expand_string(arg_str, arg_expanded, 0);
switch (expand_ret)
{
case EXPAND_ERROR:
{
error(SYNTAX_ERROR,
arg_node.source_start,
_(L"Could not expand string '%ls'"),
arg_str.c_str());
break;
}
case EXPAND_WILDCARD_NO_MATCH:
{
/* Store the node that failed to expand */
unmatched_wildcard = true;
if (! unmatched_wildcard_node)
{
unmatched_wildcard_node = &arg_node;
}
break;
}
case EXPAND_WILDCARD_MATCH:
{
matched_wildcard = true;
break;
}
case EXPAND_OK:
{
break;
}
}
/* Now copy over any expanded arguments */
for (size_t i=0; i < arg_expanded.size(); i++)
{
argument_list.push_back(arg_expanded.at(i).completion);
}
}
/* Return if we had a wildcard problem */
if (unmatched_wildcard && ! matched_wildcard)
{
*out_unmatched_wildcard_node = unmatched_wildcard_node;
}
return argument_list;
}
io_chain_t parser_t::determine_io_chain(const parse_node_t &statement,const parser_context_t &ctx)
{
io_chain_t result;
/* Get all redirection nodes underneath the statement */
const parse_node_tree_t::parse_node_list_t redirect_nodes = ctx.tree.find_nodes(statement, symbol_redirection);
for (size_t i=0; i < redirect_nodes.size(); i++)
{
const parse_node_t &redirect_node = *redirect_nodes.at(i);
int source_fd = -1; /* source fd */
wcstring target; /* file path or target fd */
enum token_type redirect_type = ctx.tree.type_for_redirection(redirect_node, ctx.src, &source_fd, &target);
/* PCA: I can't justify this EXPAND_SKIP_VARIABLES flag. It was like this when I got here. */
bool target_expanded = expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0);
if (! target_expanded || target.empty())
{
/* Should improve this error message */
error(SYNTAX_ERROR,
redirect_node.source_start,
_(L"Invalid redirection target: %ls"),
target.c_str());
}
/* Generate the actual IO redirection */
shared_ptr<io_data_t> new_io;
assert(redirect_type != TOK_NONE);
switch (redirect_type)
{
case TOK_REDIRECT_FD:
{
if (target == L"-")
{
new_io.reset(new io_close_t(source_fd));
}
else
{
wchar_t *end = NULL;
errno = 0;
int old_fd = fish_wcstoi(target.c_str(), &end, 10);
if (old_fd < 0 || errno || *end)
{
error(SYNTAX_ERROR,
redirect_node.source_start,
_(L"Requested redirection to something that is not a file descriptor %ls"),
target.c_str());
}
else
{
new_io.reset(new io_fd_t(source_fd, old_fd));
}
}
break;
}
case TOK_REDIRECT_OUT:
case TOK_REDIRECT_APPEND:
case TOK_REDIRECT_IN:
case TOK_REDIRECT_NOCLOB:
{
int oflags = oflags_for_redirection_type(redirect_type);
io_file_t *new_io_file = new io_file_t(source_fd, target, oflags);
new_io.reset(new_io_file);
break;
}
default:
{
// Should be unreachable
fprintf(stderr, "Unexpected redirection type %ld. aborting.\n", (long)redirect_type);
PARSER_DIE();
break;
}
}
/* Append the new_io if we got one */
if (new_io.get() != NULL)
{
result.push_back(new_io);
}
}
return result;
}
/* Returns a process_t allocated with new. It's the caller's responsibility to delete it (!) */
process_t *parser_t::create_job_process(job_t *job, const parse_node_t &statement_node, const parser_context_t &ctx)
{
assert(statement_node.type == symbol_statement);
assert(statement_node.child_count == 1);
// We may skip this job entirely, e.g. with an 'and' statement
bool skip_job = false;
// Get the "specific statement" which is boolean / block / if / switch / decorated
const parse_node_t &specific_statement = *ctx.tree.get_child(statement_node, 0);
@ -1732,7 +1996,7 @@ process_t *parser_t::create_job_process(job_t *job, const parse_node_t &statemen
break;
case symbol_begin_header:
result = this->create_begin_process(job, specific_header, specific_statement, ctx);
break;
default:
@ -1740,17 +2004,22 @@ process_t *parser_t::create_job_process(job_t *job, const parse_node_t &statemen
PARSER_DIE();
break;
}
break;
}
case symbol_decorated_statement:
{
const parse_node_t &plain_statement = ctx.tree.find_child(specific_statement, symbol_plain_statement);
result = this->create_plain_process(job, plain_statement, ctx);
break;
}
default:
fprintf(stderr, "'%ls' not handled by new parser yet\n", specific_statement.describe().c_str());
}
// expand_one command
// handle booleans (and, not, or)
// set INTERNAL_EXEC
// implicit CD
return proc;
return result;
}
#endif
/**
Fully parse a single job. Does not call exec on it, but any command substitutions in the job will be executed.
@ -2456,7 +2725,6 @@ static bool job_should_skip_elseif(const job_t *job, const block_t *current_bloc
Evaluates a job from a node tree.
*/
#if 0
void parser_t::eval_job(const parse_node_t &job_node, const parser_context_t &ctx)
{
assert(job_node.type == symbol_job);
@ -2499,7 +2767,7 @@ void parser_t::eval_job(const parse_node_t &job_node, const parser_context_t &ct
|| is_event \
|| (!get_is_interactive()));
current_block->job = j;
current_block()->job = j;
/* Tell the job what its command is */
j->set_command(job_node.get_source(ctx.src));
@ -2533,124 +2801,7 @@ void parser_t::eval_job(const parse_node_t &job_node, const parser_context_t &ct
last_process = last_process->next;
job_cont = ctx.tree.get_child(*job_cont, 2, symbol_job_continuation);
}
bool skip = false;
if (this->parse_job(j->first_process, j, job_node, ctx) && j->first_process->get_argv())
{
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;
}
#endif
/**
Evaluates a job from the specified tokenizer. First calls
@ -2889,57 +3040,85 @@ void parser_t::eval_job(tokenizer_t *tok)
}
job_reap(0);
}
#if 0
int parser_t::eval2(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type)
static void push_all_children(std::vector<node_offset_t> *execution_stack, const parse_node_t &node)
{
parser_context_t mut_ctx;
mut_ctx.src = cmd_str;
/* Parse the tree */
if (! parse_t::parse(cmd_str, parse_flag_none, &mut_ctx.tree, NULL))
// push nodes in reverse order, so the first node ends up on top
unsigned child_idx = node.child_count;
while (child_idx--)
{
return 1;
execution_stack->push_back(node.child_offset(child_idx));
}
}
/* Make a const version for safety's sake */
const parser_context_t &ctx = mut_ctx;
void parser_t::execute_next(std::vector<node_offset_t> *execution_stack, const parser_context_t &ctx)
{
assert(execution_stack != NULL);
assert(! execution_stack->empty());
CHECK_BLOCK(1);
/* Get the offset of the next node and remove it from the stack */
node_offset_t next_offset = execution_stack->back();
execution_stack->pop_back();
/* 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);
const size_t forbid_count = forbidden_function.size();
const block_t *start_current_block = current_block;
/* Get the node */
assert(next_offset < ctx.tree.size());
const parse_node_t &node = ctx.tree.at(next_offset);
/* Do some stuff I haven't figured out yet */
job_reap(0);
/* Only certain blocks are allowed */
if ((block_type != TOP) &&
(block_type != SUBST))
/* Do something with it */
switch (node.type)
{
debug(1,
INVALID_SCOPE_ERR_MSG,
parser_t::get_block_desc(block_type));
bugreport();
return 1;
case symbol_job_list:
// These correspond to the three productions of job_list
switch (node.production_idx)
{
case 0: // empty
break;
case 1: //job, job_list
push_all_children(execution_stack, node);
break;
case 2: //blank line, job_list
execution_stack->push_back(node.child_offset(1));
break;
default: //if we get here, it means more productions have been added to job_list, which is bad
PARSER_DIE();
break;
}
break;
case symbol_job: //statement, job_continuation
push_all_children(execution_stack, node);
break;
case symbol_job_continuation:
switch (node.production_idx)
{
case 0: //empty
break;
case 1: //pipe, statement, job_continuation
execution_stack->push_back(node.child_offset(2));
execution_stack->push_back(node.child_offset(1));
break;
default:
PARSER_DIE();
break;
}
break;
}
}
eval_level++;
/* Executes the job list at the given node offset */
void parser_t::execute_job_list(node_offset_t idx, const parser_context_t &ctx)
{
assert(idx < ctx.tree.size());
this->push_block(new scope_block_t(block_type));
error_code = 0;
event_fire(NULL);
/* Execute the top job list */
assert(! ctx.tree.empty());
const parse_node_t *job_list = &ctx.tree.at(0);
const parse_node_t *job_list = &ctx.tree.at(idx);
assert(job_list->type == symbol_job_list);
while (job_list != NULL)
{
@ -2971,12 +3150,60 @@ int parser_t::eval2(const wcstring &cmd_str, const io_chain_t &io, enum block_ty
this->eval_job(*job, ctx);
}
}
}
int parser_t::eval2(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type)
{
parser_context_t mut_ctx;
mut_ctx.src = cmd_str;
/* Parse the tree */
if (! parse_t::parse(cmd_str, parse_flag_none, &mut_ctx.tree, NULL))
{
return 1;
}
/* Make a const version for safety's sake */
const parser_context_t &ctx = mut_ctx;
CHECK_BLOCK(1);
/* 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);
const size_t forbid_count = forbidden_function.size();
const block_t * const start_current_block = this->current_block();
/* Do some stuff I haven't figured out yet */
job_reap(0);
/* Only certain blocks are allowed */
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));
error_code = 0;
event_fire(NULL);
/* Execute the top level job list */
execute_job_list(0, ctx);
parser_t::pop_block();
while (start_current_block != current_block)
while (start_current_block != this->current_block())
{
if (current_block == 0)
if (this->current_block() == NULL)
{
debug(0,
_(L"End of block mismatch. Program terminating."));
@ -2991,7 +3218,7 @@ int parser_t::eval2(const wcstring &cmd_str, const io_chain_t &io, enum block_ty
//debug( 2, L"Status %d\n", proc_get_last_status() );
debug(1,
L"%ls", parser_t::get_block_desc(current_block->type()));
L"%ls", parser_t::get_block_desc(current_block()->type()));
debug(1,
BLOCK_END_ERR_MSG);
fwprintf(stderr, L"%ls", parser_t::current_line());
@ -3022,7 +3249,6 @@ int parser_t::eval2(const wcstring &cmd_str, const io_chain_t &io, enum block_ty
return code;
}
#endif
int parser_t::eval(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type)
{

View file

@ -96,37 +96,16 @@ public:
bool had_command; /**< Set to non-zero once a command has been executed in this block */
int tok_pos; /**< The start index of the block */
/**
Status for the current loop block. Can be any of the values from the loop_status enum.
*/
/** Status for the current loop block. Can be any of the values from the loop_status enum. */
int loop_status;
/**
The job that is currently evaluated in the specified block.
*/
/** The job that is currently evaluated in the specified block. */
job_t *job;
#if 0
union
{
int while_state; /**< True if the loop condition has not yet been evaluated*/
wchar_t *for_variable; /**< Name of the variable to loop over */
int if_state; /**< The state of the if block, can be one of IF_STATE_UNTESTED, IF_STATE_FALSE, IF_STATE_TRUE */
wchar_t *switch_value; /**< The value to test in a switch block */
const wchar_t *source_dest; /**< The name of the file to source*/
event_t *event; /**<The event that triggered this block */
wchar_t *function_call_name;
} param1;
#endif
/**
Name of file that created this block
*/
/** Name of file that created this block */
const wchar_t *src_filename;
/**
Line number where this block was created
*/
/** Line number where this block was created */
int src_lineno;
/** Whether we should pop the environment variable stack when we're popped off of the block stack */
@ -347,6 +326,14 @@ private:
process_t *create_job_process(job_t *job, const parse_node_t &statement_node, const parser_context_t &ctx);
process_t *create_boolean_process(job_t *job, const parse_node_t &bool_statement, const parser_context_t &ctx);
process_t *create_for_process(job_t *job, const parse_node_t &header, const parse_node_t &statement, const parser_context_t &ctx);
process_t *create_while_process(job_t *job, const parse_node_t &header, const parse_node_t &statement, const parser_context_t &ctx);
process_t *create_begin_process(job_t *job, const parse_node_t &header, const parse_node_t &statement, const parser_context_t &ctx);
process_t *create_plain_process(job_t *job, const parse_node_t &statement, const parser_context_t &ctx);
wcstring_list_t determine_arguments(const parse_node_t &statement, const parse_node_t **out_unmatched_wildcard_node, const parser_context_t &ctx);
io_chain_t determine_io_chain(const parse_node_t &statement,const parser_context_t &ctx);
void parse_job_argument_list(process_t *p, job_t *j, tokenizer_t *tok, std::vector<completion_t>&, bool);
int parse_job(process_t *p, job_t *j, tokenizer_t *tok);
@ -400,6 +387,9 @@ public:
int eval(const wcstring &cmdStr, const io_chain_t &io, enum block_type_t block_type);
int eval2(const wcstring &cmd_str, const io_chain_t &io, enum block_type_t block_type);
void execute_job_list(node_offset_t idx, const parser_context_t &ctx);
void execute_next(std::vector<node_offset_t> *execution_stack, const parser_context_t &ctx);
/**
Evaluate line as a list of parameters, i.e. tokenize it and perform parameter expansion and cmdsubst execution on the tokens.
The output is inserted into output.

View file

@ -14,7 +14,7 @@ segments.
#include <wctype.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "fallback.h"
#include "util.h"
@ -522,7 +522,7 @@ static size_t read_redirection_or_fd_pipe(const wchar_t *buff, enum token_type *
return idx;
}
enum token_type redirection_type_for_string(const wcstring &str)
enum token_type redirection_type_for_string(const wcstring &str, int *out_fd)
{
enum token_type mode = TOK_NONE;
int fd = 0;
@ -530,9 +530,25 @@ enum token_type redirection_type_for_string(const wcstring &str)
/* Redirections only, no pipes */
if (mode == TOK_PIPE || fd < 0)
mode = TOK_NONE;
if (out_fd != NULL)
*out_fd = fd;
return mode;
}
int oflags_for_redirection_type(enum token_type type)
{
switch (type)
{
case TOK_REDIRECT_APPEND: return O_CREAT | O_APPEND | O_WRONLY;
case TOK_REDIRECT_OUT: return O_CREAT | O_WRONLY | O_TRUNC;
case TOK_REDIRECT_NOCLOB: return O_CREAT | O_EXCL | O_WRONLY;
case TOK_REDIRECT_IN: return O_RDONLY;
default:
return -1;
}
}
wchar_t tok_last_quote(tokenizer_t *tok)
{
CHECK(tok, 0);

View file

@ -187,8 +187,11 @@ const wchar_t *tok_get_desc(int type);
*/
int tok_get_error(tokenizer_t *tok);
/* Helper function to determine redirection type from a string, or TOK_NONE if the redirection is invalid */
enum token_type redirection_type_for_string(const wcstring &str);
/* Helper function to determine redirection type from a string, or TOK_NONE if the redirection is invalid. Also returns the fd by reference. */
enum token_type redirection_type_for_string(const wcstring &str, int *out_fd = NULL);
/* Helper function to return oflags (as in open(2)) for a redirection type */
int oflags_for_redirection_type(enum token_type type);
enum move_word_style_t
{