From b6af3e51abd54963948d5523ce01459fadedebbf Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 23 Dec 2013 14:53:56 -0800 Subject: [PATCH] Very early work in adopting new parser for actual execution of commands. Not turned on yet. --- highlight.cpp | 2 +- parse_tree.cpp | 18 +- parse_tree.h | 6 +- parser.cpp | 576 ++++++++++++++++++++++++++++++++++--------------- parser.h | 40 ++-- tokenizer.cpp | 20 +- tokenizer.h | 7 +- 7 files changed, 461 insertions(+), 208 deletions(-) diff --git a/highlight.cpp b/highlight.cpp index 3c60150cd..e9923fb00 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -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); diff --git a/parse_tree.cpp b/parse_tree.cpp index 81b6c1633..d970200b0 100644 --- a/parse_tree.cpp +++ b/parse_tree.cpp @@ -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) { diff --git a/parse_tree.h b/parse_tree.h index bdc622d93..87e3ecec8 100644 --- a/parse_tree.h +++ b/parse_tree.h @@ -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); diff --git a/parser.cpp b/parser.cpp index 31aff34f9..2a34f0dc3 100644 --- a/parser.cpp +++ b/parser.cpp @@ -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 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 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(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 tokenizer_pos_push(¤t_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(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(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 *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; +} - CHECK_BLOCK(1); +void parser_t::execute_next(std::vector *execution_stack, const parser_context_t &ctx) +{ + assert(execution_stack != NULL); + assert(! execution_stack->empty()); - /* Record the current chain so we can put it back later */ - scoped_push block_io_push(&block_io, io); - scoped_push forbidden_function_push(&forbidden_function); - const size_t forbid_count = forbidden_function.size(); - const block_t *start_current_block = current_block; + /* 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(); - /* Do some stuff I haven't figured out yet */ - job_reap(0); + /* Get the node */ + assert(next_offset < ctx.tree.size()); + const parse_node_t &node = ctx.tree.at(next_offset); - /* 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++; - - 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); +/* 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()); + + 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 block_io_push(&block_io, io); + scoped_push 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) { diff --git a/parser.h b/parser.h index 8ee2fb82a..fab73acfd 100644 --- a/parser.h +++ b/parser.h @@ -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; /**&, bool); int parse_job(process_t *p, job_t *j, tokenizer_t *tok); @@ -399,6 +386,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 *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. diff --git a/tokenizer.cpp b/tokenizer.cpp index 0705e620a..0b0032836 100644 --- a/tokenizer.cpp +++ b/tokenizer.cpp @@ -14,7 +14,7 @@ segments. #include #include #include - +#include #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); diff --git a/tokenizer.h b/tokenizer.h index 8e130f0e7..17b1bcb96 100644 --- a/tokenizer.h +++ b/tokenizer.h @@ -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 {