This is part XI of cmd engine rewrite.
This serie moves the IF and FOR instruction into the new CMD_NODE structure.
I'm fully aware it's a large change (commit #1). It touches simultaneously at several components: - lexer is no longer trying to handle nested instructions; this is now handled over to parser which will take care of it in a recursive fashion - moving the IF and FOR instructions into CMD_NODE; this impacts the parser and the execution part
These three parts are so tightly linked that I can't figure out a simple way to split first commit in smaller bits.
From: Eric Pouech epouech@codeweavers.com
Basically: - IF and FOR command parsing in integrated into node_builder, - CMD_NODE is no longer degenerated (and no longer use NULL CMD_COMMAND to mark end of block) - lexer evolves to delegate a bunch of things to node_builder (esp. command nesting...)
Wine-Bug-Id: https://bugs.winehq.org/show_bug.cgi?id=53190 Wine-Bug-Id: https://bugs.winehq.org/show_bug.cgi?id=50132 (partially) Wine-Bug-Id: https://bugs.winehq.org/show_bug.cgi?id=44063
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 2 +- programs/cmd/builtins.c | 211 ----- programs/cmd/cmd.rc | 1 + programs/cmd/tests/test_builtins.cmd | 1 - programs/cmd/tests/test_builtins.cmd.exp | 17 +- programs/cmd/wcmd.h | 65 +- programs/cmd/wcmdmain.c | 1016 ++++++++++++---------- 7 files changed, 562 insertions(+), 751 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index fe859dd52c4..914d9d7f3e3 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -92,7 +92,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, WCHAR *startLabel, HANDLE pgmHandl /* Note: although this batch program itself may be called, we are not retrying the command as a result of a call failing to find a program, hence the retryCall parameter below is FALSE */ - WCMD_process_commands(toExecute, FALSE, FALSE); + node_execute(toExecute); node_dispose_tree(toExecute); toExecute = NULL; } diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index a39de313986..5a53af28ebe 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1539,140 +1539,6 @@ void WCMD_echo (const WCHAR *args) free(trimmed); }
-/***************************************************************************** - * WCMD_part_execute - * - * Execute a command, and any && or bracketed follow on to the command. The - * first command to be executed may not be at the front of the - * commands->thiscommand string (eg. it may point after a DO or ELSE) - */ -void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, - BOOL isIF, BOOL executecmds) -{ - CMD_NODE *curPosition = *cmdList; - int myDepth = CMD_node_get_depth(*cmdList); - CMD_OPERATOR prev_op = CMD_CONCAT; - - WINE_TRACE("cmdList(%p), firstCmd(%s), doIt(%d), isIF(%d)\n", cmdList, - wine_dbgstr_w(firstcmd), executecmds, isIF); - - /* Skip leading whitespace between condition and the command */ - while (firstcmd && *firstcmd && (*firstcmd==' ' || *firstcmd=='\t')) firstcmd++; - - /* Process the first command, if there is one */ - if (executecmds && firstcmd && *firstcmd) { - WCHAR *command = xstrdupW(firstcmd); - WCMD_execute (firstcmd, CMD_node_get_single_node(*cmdList)->redirects, cmdList, FALSE); - free(command); - } - - - /* If it didn't move the position, step to next command */ - if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList); - - /* Process any other parts of the command */ - if (*cmdList) { - BOOL processThese = executecmds; - - while (*cmdList) { - /* execute all appropriate commands */ - curPosition = *cmdList; - - WINE_TRACE("Processing cmdList(%p) - operator(%d) bd(%d / %d) processThese(%d)\n", - *cmdList, - prev_op, - CMD_node_get_depth(*cmdList), - myDepth, - processThese); - - /* Execute any statements appended to the line */ - /* FIXME: Only if previous call worked for && or failed for || */ - if (prev_op == CMD_ONFAILURE || - prev_op == CMD_ONSUCCESS) { - if (processThese && CMD_node_get_command(*cmdList)->command) { - WCMD_execute (CMD_node_get_command(*cmdList)->command, CMD_node_get_single_node(*cmdList)->redirects, - cmdList, FALSE); - } - if (curPosition == *cmdList) - { - prev_op = (*cmdList)->op; - *cmdList = CMD_node_next(*cmdList); - } - - /* Execute any appended to the statement with (...) */ - } else if (CMD_node_get_depth(*cmdList) > myDepth) { - if (processThese) { - /* FIXME this is wrong, we don't recompute prev_op */ - *cmdList = WCMD_process_commands(*cmdList, TRUE, FALSE); - } else { - WINE_TRACE("Skipping command %p due to stack depth\n", *cmdList); - } - if (curPosition == *cmdList) - { - prev_op = (*cmdList)->op; - *cmdList = CMD_node_next(*cmdList); - } - - /* End of the command - does 'ELSE ' follow as the next command? */ - } else { - if (isIF && WCMD_keyword_ws_found(L"else", CMD_node_get_command(*cmdList)->command)) { - /* Swap between if and else processing */ - processThese = !executecmds; - - /* Process the ELSE part */ - if (processThese) { - const int keyw_len = lstrlenW(L"else") + 1; - WCHAR *cmd = (CMD_node_get_command(*cmdList)->command) + keyw_len; - - /* Skip leading whitespace between condition and the command */ - while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++; - if (*cmd) { - WCMD_execute (cmd, CMD_node_get_single_node(*cmdList)->redirects, cmdList, FALSE); - } - } else { - /* Loop skipping all commands until we get back to the current - depth, including skipping commands and their subsequent - pipes (eg cmd | prog) */ - do { - prev_op = (*cmdList)->op; - *cmdList = CMD_node_next(*cmdList); - } while (*cmdList && - (CMD_node_get_depth(*cmdList) > myDepth || - (prev_op != CMD_SINGLE && prev_op != CMD_CONCAT))); - - /* After the else is complete, we need to now process subsequent commands */ - processThese = TRUE; - } - if (curPosition == *cmdList) - { - prev_op = (*cmdList)->op; - *cmdList = CMD_node_next(*cmdList); - } - - /* If we were in an IF statement and we didn't find an else and yet we get back to - the same bracket depth as the IF, then the IF statement is over. This is required - to handle nested ifs properly */ - } else if (isIF && CMD_node_get_depth(*cmdList) == myDepth) { - if (WCMD_keyword_ws_found(L"do", CMD_node_get_command(*cmdList)->command)) { - WINE_TRACE("Still inside FOR-loop, not an end of IF statement\n"); - prev_op = (*cmdList)->op; - *cmdList = CMD_node_next(*cmdList); - } else { - WINE_TRACE("Found end of this nested IF statement, ending this if\n"); - break; - } - } else if (!processThese) { - if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList); - WINE_TRACE("Skipping this command, as in not process mode (next = %p)\n", *cmdList); - } else { - WINE_TRACE("Found end of this IF statement (next = %p)\n", *cmdList); - break; - } - } - } - } -} - /***************************************************************************** * WCMD_add_dirstowalk * @@ -1849,43 +1715,6 @@ int WCMD_for_nexttoken(int lasttoken, const WCHAR *tokenstr, return nexttoken; }
-/************************************************************************** - * WCMD_for - * - * Batch file loop processing. - * - * On entry: cmdList contains the syntax up to the set - * next cmdList and all in that bracket contain the set data - * next cmdlist contains the DO cmd - * following that is either brackets or && entries (as per if) - * - */ - -void WCMD_for(WCHAR *p, CMD_NODE **cmdList) -{ - CMD_FOR_CONTROL *for_ctrl; - - for_ctrl = for_control_parse(p); - if (!for_ctrl) - { - *cmdList = NULL; - return; - } - - for (*cmdList = CMD_node_next(*cmdList); /* swallow options */ - *cmdList && CMD_node_get_command(*cmdList)->command != NULL; - *cmdList = CMD_node_next(*cmdList)) - { - for_control_append_set(for_ctrl, CMD_node_get_command(*cmdList)->command); - } - - /* step over terminating NULL CMD_NODE of set */ - *cmdList = CMD_node_next(*cmdList); - - for_control_execute(for_ctrl, cmdList); - for_control_dispose(for_ctrl); -} - /************************************************************************** * WCMD_give_help * @@ -2088,46 +1917,6 @@ void WCMD_popd (void) { LocalFree (temp); }
-/**************************************************************************** - * WCMD_if - * - * Batch file conditional. - * - * On entry, cmdlist will point to command containing the IF, and optionally - * the first command to execute (if brackets not found) - * If &&'s were found, this may be followed by a record flagged as isAmpersand - * If ('s were found, execute all within that bracket - * Command may optionally be followed by an ELSE - need to skip instructions - * in the else using the same logic - * - * FIXME: Much more syntax checking needed! - */ -void WCMD_if (WCHAR *p, CMD_NODE **cmdList) -{ - CMD_IF_CONDITION if_cond; - WCHAR *command; - int test; - - if (if_condition_create(p, &command, &if_cond)) - { - TRACE("%s\n", debugstr_if_condition(&if_cond)); - if (if_condition_evaluate(&if_cond, &test)) - { - WINE_TRACE("p: %s, quals: %s, param1: %s, param2: %s, command: %s\n", - wine_dbgstr_w(p), wine_dbgstr_w(quals), wine_dbgstr_w(param1), - wine_dbgstr_w(param2), wine_dbgstr_w(command)); - - /* Process rest of IF statement which is on the same line - Note: This may process all or some of the cmdList (eg a GOTO) */ - WCMD_part_execute(cmdList, command, TRUE, test); - } - if_condition_dispose(&if_cond); - return; - } - - WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR)); -} - /**************************************************************************** * WCMD_move * diff --git a/programs/cmd/cmd.rc b/programs/cmd/cmd.rc index 3bcf0ad4874..9e66f27380d 100644 --- a/programs/cmd/cmd.rc +++ b/programs/cmd/cmd.rc @@ -409,4 +409,5 @@ Enter HELP <command> for further information on any of the above commands.\n" WCMD_FILENAMETOOLONG, "File name is too long.\n" WCMD_BADTOKEN, "Syntax error: unexpected %1\n" WCMD_ENDOFLINE, "End of line" + WCMD_ENDOFFILE, "End of file" } diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index ebcec489b7c..e97d447ba26 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -1458,7 +1458,6 @@ for %%a in ("f" "g" "h" ) do if #==# (echo %%a) -echo ---
mkdir foobar & cd foobar mkdir foo diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 31ed5b92765..b4d85161fbb 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -371,8 +371,8 @@ r1 r2 r3 --- chain pipe -@todo_wine@a@space@ -@todo_wine@a@space@ +a@space@ +a@space@ --- b2 c1 @@ -399,9 +399,9 @@ f4:[f3:[f2:[f1,f2],f3],f4]@or_broken@f4:[f3:[f2:,f3],f4]@or_broken@f4:[f3:,f4] a1 b2 @todo_wine@--- -@todo_wine@c3 +c3 @todo_wine@--- -@todo_wine@d3 +d3 @todo_wine@--- @todo_wine@--- @todo_wine@--- @@ -634,7 +634,7 @@ gotitright @todo_wine@0@or_broken@1 foo !WINE_FOO! -@todo_wine@not empty +not empty --- using /V cmd flag foo foo@or_broken@!WINE_FOO! @@ -1031,10 +1031,9 @@ b 1 3 3-A1 3-B2 --- nesting if/for -@todo_wine@"f" -@todo_wine@"g" -@todo_wine@"h" -@todo_wine@--- +"f" +"g" +"h" --- basic wildcards bazbaz --- wildcards in subdirs diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index f62ee3da6a9..da4eaca21af 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -56,6 +56,8 @@ typedef enum _CMD_OPERATOR CMD_ONFAILURE, /* || */ CMD_ONSUCCESS, /* && */ CMD_PIPE, /* Single | */ + CMD_IF, /* IF command */ + CMD_FOR, /* FOR command */ } CMD_OPERATOR;
/* Data structure to hold commands to be processed */ @@ -109,14 +111,12 @@ typedef struct _CMD_FOR_CONTROL typedef struct _CMD_COMMAND { WCHAR *command; /* Command string to execute */ - int bracketDepth;/* How deep bracketing have we got to */ - WCHAR pipeFile[MAX_PATH]; /* Where to get input from for pipes */ } CMD_COMMAND;
typedef struct _CMD_NODE { CMD_OPERATOR op; /* operator */ - CMD_REDIRECTION *redirects; /* Redirects in place */ + CMD_REDIRECTION *redirects; /* Redirections */ union { CMD_COMMAND *command; /* CMD_SINGLE */ @@ -125,51 +125,23 @@ typedef struct _CMD_NODE struct _CMD_NODE *left; struct _CMD_NODE *right; }; + struct /* CMD_IF */ + { + CMD_IF_CONDITION condition; + struct _CMD_NODE *then_block; + struct _CMD_NODE *else_block; + }; + struct /* CMD_FOR */ + { + CMD_FOR_CONTROL for_ctrl; + struct _CMD_NODE *do_block; + }; }; } CMD_NODE; -/* temporary helpers to fake a list into a tree */ -/* Note: for binary op, left should be a CMD_SINGLE node */ -static inline const CMD_NODE *CMD_node_get_single_node(const CMD_NODE *node) -{ - /* assert(node->left && node->left->op == CMD_SINGLE); */ - return (node->op == CMD_SINGLE) ? node : node->left; -} - -static inline CMD_COMMAND *CMD_node_get_command(const CMD_NODE *node) -{ - return CMD_node_get_single_node(node)->command; -} -static inline CMD_NODE *CMD_node_next(const CMD_NODE *node) -{ - return (node->op == CMD_SINGLE) ? NULL : node->right; -} -static inline int CMD_node_get_depth(const CMD_NODE *node) -{ - CMD_COMMAND *cmd = CMD_node_get_command(node); - return cmd->bracketDepth; -} -/* end temporary */ - -/* temporary helpers for parsing transition */ -BOOL if_condition_create(WCHAR *start, WCHAR **end, CMD_IF_CONDITION *cond); -void if_condition_dispose(CMD_IF_CONDITION *); -BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test); -const char *debugstr_if_condition(const CMD_IF_CONDITION *cond); - -void for_control_create(enum for_control_operator for_op, unsigned flags, const WCHAR *options, int var_idx, CMD_FOR_CONTROL *for_ctrl); -void for_control_create_fileset(unsigned flags, int var_idx, WCHAR eol, int num_lines_to_skip, BOOL use_backq, - const WCHAR *delims, const WCHAR *tokens, - CMD_FOR_CONTROL *for_ctrl); -CMD_FOR_CONTROL *for_control_parse(WCHAR *opts_var); -void for_control_append_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR *string); -void for_control_dump(const CMD_FOR_CONTROL *for_ctrl); -void for_control_dispose(CMD_FOR_CONTROL *for_ctrl); -void for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE **cmdList); int WCMD_for_nexttoken(int lasttoken, const WCHAR *tokenstr, int *totalfound, BOOL *doall, BOOL *duplicates); -void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, - BOOL isIF, BOOL executecmds); + struct _DIRECTORY_STACK; void WCMD_add_dirstowalk(struct _DIRECTORY_STACK *dirsToWalk); struct _DIRECTORY_STACK *WCMD_dir_stack_create(const WCHAR *dir, const WCHAR *file); @@ -200,11 +172,9 @@ void WCMD_echo (const WCHAR *); void WCMD_endlocal (void); void WCMD_enter_paged_mode(const WCHAR *); RETURN_CODE WCMD_exit(void); -void WCMD_for (WCHAR *, CMD_NODE **cmdList); BOOL WCMD_get_fullpath(const WCHAR *, SIZE_T, WCHAR *, WCHAR **); void WCMD_give_help (const WCHAR *args); RETURN_CODE WCMD_goto(void); -void WCMD_if (WCHAR *, CMD_NODE **cmdList); void WCMD_leave_paged_mode(void); void WCMD_more (WCHAR *); void WCMD_move (void); @@ -250,10 +220,8 @@ WCHAR *WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead);
WCHAR *WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_NODE **output, HANDLE readFrom); -CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket, BOOL retrycall); void node_dispose_tree(CMD_NODE *cmds); -void WCMD_execute(const WCHAR *orig_command, CMD_REDIRECTION *redirects, - CMD_NODE **cmdList, BOOL retrycall); +RETURN_CODE node_execute(CMD_NODE *node);
void *xrealloc(void *, size_t) __WINE_ALLOC_SIZE(2) __WINE_DEALLOC(free);
@@ -483,3 +451,4 @@ extern WCHAR version_string[]; #define WCMD_FILENAMETOOLONG 1046 #define WCMD_BADTOKEN 1047 #define WCMD_ENDOFLINE 1048 +#define WCMD_ENDOFFILE 1049 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 12d3db1b012..9ad9bc44866 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -995,7 +995,7 @@ static void command_dispose(CMD_COMMAND *cmd) } }
-void for_control_dispose(CMD_FOR_CONTROL *for_ctrl) +static void for_control_dispose(CMD_FOR_CONTROL *for_ctrl) { free((void*)for_ctrl->set); switch (for_ctrl->operator) @@ -1053,7 +1053,7 @@ const char *debugstr_for_control(const CMD_FOR_CONTROL *for_ctrl) for_var_index_to_char(for_ctrl->variable_index), for_ctrl->set); }
-void for_control_create(enum for_control_operator for_op, unsigned flags, const WCHAR *options, int var_idx, CMD_FOR_CONTROL *for_ctrl) +static void for_control_create(enum for_control_operator for_op, unsigned flags, const WCHAR *options, int var_idx, CMD_FOR_CONTROL *for_ctrl) { for_ctrl->operator = for_op; for_ctrl->flags = flags; @@ -1069,9 +1069,9 @@ void for_control_create(enum for_control_operator for_op, unsigned flags, const } }
-void for_control_create_fileset(unsigned flags, int var_idx, WCHAR eol, int num_lines_to_skip, BOOL use_backq, - const WCHAR *delims, const WCHAR *tokens, - CMD_FOR_CONTROL *for_ctrl) +static void for_control_create_fileset(unsigned flags, int var_idx, WCHAR eol, int num_lines_to_skip, BOOL use_backq, + const WCHAR *delims, const WCHAR *tokens, + CMD_FOR_CONTROL *for_ctrl) { for_ctrl->operator = CMD_FOR_FILE_SET; for_ctrl->flags = flags; @@ -1085,7 +1085,7 @@ void for_control_create_fileset(unsigned flags, int var_idx, WCHAR eol, int num_ for_ctrl->tokens = tokens; }
-void for_control_append_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR *set) +static void for_control_append_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR *set) { if (for_ctrl->set) { @@ -1120,7 +1120,7 @@ void if_condition_dispose(CMD_IF_CONDITION *cond) } }
-BOOL if_condition_create(WCHAR *start, WCHAR **end, CMD_IF_CONDITION *cond) +static BOOL if_condition_parse(WCHAR *start, WCHAR **end, CMD_IF_CONDITION *cond) { WCHAR *param_start; const WCHAR *param_copy; @@ -1215,7 +1215,7 @@ BOOL if_condition_create(WCHAR *start, WCHAR **end, CMD_IF_CONDITION *cond) return cond || *param_copy != L'\0'; }
-const char *debugstr_if_condition(const CMD_IF_CONDITION *cond) +static const char *debugstr_if_condition(const CMD_IF_CONDITION *cond) { const char *header = wine_dbg_sprintf("{{%s%s", cond->negated ? "not " : "", cond->case_insensitive ? "nocase " : "");
@@ -1246,31 +1246,43 @@ const char *debugstr_if_condition(const CMD_IF_CONDITION *cond) * pointer will be modified within the commands, and hence a single free * routine is simpler */ -void node_dispose_tree(CMD_NODE *cmds) +void node_dispose_tree(CMD_NODE *node) { /* Loop through the commands, freeing them one by one */ - while (cmds) + if (!node) return; + switch (node->op) { - CMD_NODE *thisCmd = cmds; - cmds = CMD_node_next(cmds); - if (thisCmd->op == CMD_SINGLE) - { - redirection_dispose_list(thisCmd->redirects); - command_dispose(thisCmd->command); - } - else - node_dispose_tree(thisCmd->left); - free(thisCmd); + case CMD_SINGLE: + command_dispose(node->command); + break; + case CMD_CONCAT: + case CMD_PIPE: + case CMD_ONFAILURE: + case CMD_ONSUCCESS: + node_dispose_tree(node->left); + node_dispose_tree(node->right); + break; + case CMD_IF: + if_condition_dispose(&node->condition); + node_dispose_tree(node->then_block); + node_dispose_tree(node->else_block); + break; + case CMD_FOR: + for_control_dispose(&node->for_ctrl); + node_dispose_tree(node->do_block); + break; } + redirection_dispose_list(node->redirects); + free(node); }
-static CMD_NODE *node_create_single(CMD_COMMAND *c, CMD_REDIRECTION *redir) +static CMD_NODE *node_create_single(CMD_COMMAND *c) { CMD_NODE *new = xalloc(sizeof(CMD_NODE));
new->op = CMD_SINGLE; new->command = c; - new->redirects = redir; + new->redirects = NULL;
return new; } @@ -1287,6 +1299,31 @@ static CMD_NODE *node_create_binary(CMD_OPERATOR op, CMD_NODE *l, CMD_NODE *r) return new; }
+static CMD_NODE *node_create_if(CMD_IF_CONDITION *cond, CMD_NODE *then_block, CMD_NODE *else_block) +{ + CMD_NODE *new = xalloc(sizeof(CMD_NODE)); + + new->op = CMD_IF; + new->condition = *cond; + new->then_block = then_block; + new->else_block = else_block; + new->redirects = NULL; + + return new; +} + +static CMD_NODE *node_create_for(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *do_block) +{ + CMD_NODE *new = xalloc(sizeof(CMD_NODE)); + + new->op = CMD_FOR; + new->for_ctrl = *for_ctrl; + new->do_block = do_block; + new->redirects = NULL; + + return new; +} + static void init_msvcrt_io_block(STARTUPINFOW* st) { STARTUPINFOW st_p; @@ -1338,7 +1375,7 @@ static void init_msvcrt_io_block(STARTUPINFOW* st) } }
-static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOOL iscalled); +static RETURN_CODE execute_single_command(const WCHAR *command);
/****************************************************************************** * WCMD_run_program @@ -1601,7 +1638,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
/* Not found anywhere - were we called? */ if (called) { - execute_single_command(command, NULL, TRUE); + execute_single_command(command); return; }
@@ -1621,30 +1658,18 @@ static inline unsigned clamp_fd(unsigned fd) return fd <= 2 ? fd : 1; }
-static BOOL set_std_redirections(CMD_REDIRECTION *redir, WCHAR *in_pipe) +static BOOL set_std_redirections(CMD_REDIRECTION *redir) { static DWORD std_index[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; static SECURITY_ATTRIBUTES sa = {.nLength = sizeof(sa), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE}; WCHAR expanded_filename[MAXSTRING]; HANDLE h;
- /* STDIN could come from a preceding pipe, so delete on close if it does */ - if (in_pipe) - { - TRACE("Input coming from %s\n", wine_dbgstr_w(in_pipe)); - h = CreateFileW(in_pipe, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); - if (h == INVALID_HANDLE_VALUE) return FALSE; - SetStdHandle(STD_INPUT_HANDLE, h); - /* Otherwise STDIN could come from a '<' redirect */ - } for (; redir; redir = redir->next) { switch (redir->kind) { case REDIR_READ_FROM: - if (in_pipe) continue; /* give precedence to pipe */ wcscpy(expanded_filename, redir->file); handleExpansion(expanded_filename, TRUE); h = CreateFileW(expanded_filename, GENERIC_READ, FILE_SHARE_READ, @@ -1697,7 +1722,7 @@ static BOOL set_std_redirections(CMD_REDIRECTION *redir, WCHAR *in_pipe) * try to run it as an internal command. 'retrycall' represents whether * we are attempting this retry. */ -static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOOL retrycall) +static RETURN_CODE execute_single_command(const WCHAR *command) { RETURN_CODE return_code = NO_ERROR; WCHAR *cmd, *parms_start; @@ -1706,7 +1731,7 @@ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOO WCHAR *new_cmd = NULL; BOOL prev_echo_mode;
- TRACE("command on entry:%s (%p)\n", wine_dbgstr_w(command), cmdList); + TRACE("command on entry:%s\n", wine_dbgstr_w(command));
/* Move copy of the command onto the heap so it can be expanded */ new_cmd = xalloc(MAXSTRING * sizeof(WCHAR)); @@ -1898,135 +1923,15 @@ static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOO case WCMD_EXIT: return_code = WCMD_exit(); break; - case WCMD_FOR: - case WCMD_IF: - /* Very oddly, probably because of all the special parsing required for - these two commands, neither 'for' nor 'if' is supported when called, - i.e. 'call if 1==1...' will fail. */ - if (!retrycall) { - if (cmd_index==WCMD_FOR) WCMD_for (parms_start, cmdList); - else if (cmd_index==WCMD_IF) WCMD_if (parms_start, cmdList); - break; - } - /* else: drop through */ default: prev_echo_mode = echo_mode; WCMD_run_program (whichcmd, FALSE); echo_mode = prev_echo_mode; } -cleanup: - free(cmd); - if (return_code == RETURN_CODE_ABORTED && cmdList) - *cmdList = NULL; -} - -/***************************************************************************** - * Process one command. If the command is EXIT this routine does not return. - * We will recurse through here executing batch files. - * Note: If call is used to a non-existing program, we reparse the line and - * try to run it as an internal command. 'retrycall' represents whether - * we are attempting this retry. - */ -void WCMD_execute(const WCHAR *command, CMD_REDIRECTION *redirects, - CMD_NODE **cmdList, BOOL retrycall) -{ - WCHAR *cmd; - int i, cmd_index, count; - WCHAR *whichcmd; - WCHAR *new_cmd = NULL; - HANDLE old_stdhandles[3] = {GetStdHandle (STD_INPUT_HANDLE), - GetStdHandle (STD_OUTPUT_HANDLE), - GetStdHandle (STD_ERROR_HANDLE)}; - static DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE}; - CMD_REDIRECTION *piped_redir = redirects; - - TRACE("command on entry:%s (%p)\n", wine_dbgstr_w(command), cmdList); - - /* Move copy of the command onto the heap so it can be expanded */ - new_cmd = xalloc(MAXSTRING * sizeof(WCHAR)); - lstrcpyW(new_cmd, command); - cmd = new_cmd; - - /* Strip leading whitespaces, and a '@' if supplied */ - whichcmd = WCMD_skip_leading_spaces(cmd); - TRACE("Command: '%s'\n", wine_dbgstr_w(cmd)); - if (whichcmd[0] == '@') whichcmd++; - - /* Check if the command entered is internal, and identify which one */ - count = 0; - while (IsCharAlphaNumericW(whichcmd[count])) { - count++; - } - for (cmd_index=0; cmd_index<=WCMD_EXIT; cmd_index++) { - if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, - whichcmd, count, inbuilt[cmd_index], -1) == CSTR_EQUAL) break; - } - - /* If the next command is a pipe then we implement pipes by redirecting - the output from this command to a temp file and input into the - next command from that temp file. - Note: Do not do this for a for or if statement as the pipe is for - the individual statements, not the for or if itself. - FIXME: Use of named pipes would make more sense here as currently this - process has to finish before the next one can start but this requires - a change to not wait for the first app to finish but rather the pipe */ - /* FIXME this is wrong: we need to discriminate between redirection in individual - * commands in the blocks, vs redirection of the whole command. - */ - if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF) && - cmdList && (*cmdList)->op == CMD_PIPE) - { - const WCHAR *to; - WCHAR temp_path[MAX_PATH]; - - /* Remember piping is in action */ - TRACE("Output needs to be piped\n"); - - /* Generate a unique temporary filename */ - GetTempPathW(ARRAY_SIZE(temp_path), temp_path); - GetTempFileNameW(temp_path, L"CMD", 0, CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile); - TRACE("Using temporary file of %s\n", - wine_dbgstr_w(CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile)); - /* send stdout to the pipe by appending >filename to redirects */ - to = CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile; - piped_redir = redirection_create_file(REDIR_WRITE_TO, 1, to); - piped_redir->next = redirects; - }
- /* - * Redirect stdin, stdout and/or stderr if required. - * Note: Do not do this for a for or if statement as the pipe is for - * the individual statements, not the for or if itself. - */ - /* FIXME this is wrong (see above) */ - if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF)) { - WCHAR *in_pipe = NULL; - if (cmdList && CMD_node_get_command(*cmdList)->pipeFile[0] != 0x00) - in_pipe = CMD_node_get_command(*cmdList)->pipeFile; - if (!set_std_redirections(piped_redir, in_pipe)) { - WCMD_print_error (); - goto cleanup; - } - if (in_pipe) - /* No need to remember the temporary name any longer once opened */ - in_pipe[0] = 0x00; - } else { - TRACE("Not touching redirects for a FOR or IF command\n"); - } - execute_single_command(command, cmdList, retrycall); cleanup: free(cmd); - if (piped_redir != redirects) free(piped_redir); - - /* Restore old handles */ - for (i = 0; i < 3; i++) - { - if (old_stdhandles[i] != GetStdHandle(idx_stdhandles[i])) - { - CloseHandle(GetStdHandle (idx_stdhandles[i])); - SetStdHandle(idx_stdhandles[i], old_stdhandles[i]); - } - } + return return_code; }
/************************************************************************* @@ -2118,7 +2023,7 @@ static WCHAR *for_fileset_option_split(WCHAR *from, const WCHAR* key) return from; }
-CMD_FOR_CONTROL *for_control_parse(WCHAR *opts_var) +static CMD_FOR_CONTROL *for_control_parse(WCHAR *opts_var) { CMD_FOR_CONTROL *for_ctrl; enum for_control_operator for_op; @@ -2302,7 +2207,8 @@ struct node_builder { enum builder_token { - TKN_EOL, TKN_REDIRECTION, TKN_AMP, TKN_BARBAR, TKN_AMPAMP, TKN_BAR, TKN_COMMAND, + TKN_EOF, TKN_EOL, TKN_REDIRECTION, TKN_FOR, TKN_IN, TKN_DO, TKN_IF, TKN_ELSE, + TKN_OPENPAR, TKN_CLOSEPAR, TKN_AMP, TKN_BARBAR, TKN_AMPAMP, TKN_BAR, TKN_COMMAND, } token; union token_parameter parameter; } *stack; @@ -2312,7 +2218,8 @@ struct node_builder
static const char* debugstr_token(enum builder_token tkn, union token_parameter tkn_pmt) { - static const char *tokens[] = {"EOL", "REDIR", "&", "||", "&&", "|", "CMD"}; + static const char *tokens[] = {"EOF", "EOL", "REDIR", "FOR", "IN", "DO", "IF", "ELSE", + "(", ")", "&", "||", "&&", "|", "CMD"};
if (tkn >= ARRAY_SIZE(tokens)) return "<<<>>>"; switch (tkn) @@ -2343,6 +2250,11 @@ static void node_builder_push_token_parameter(struct node_builder *builder, enum } builder->stack[builder->num].token = tkn; builder->stack[builder->num].parameter = pmt; + + if (tkn == TKN_OPENPAR) + builder->opened_parenthesis++; + if (tkn == TKN_CLOSEPAR) + builder->opened_parenthesis--; builder->num++; }
@@ -2358,7 +2270,7 @@ static enum builder_token node_builder_peek_next_token(struct node_builder *buil
if (builder->pos >= builder->num) { - tkn = TKN_EOL; + tkn = TKN_EOF; if (pmt) pmt->none = NULL; } else @@ -2376,74 +2288,207 @@ static void node_builder_consume(struct node_builder *builder) builder->pos++; }
-static void WCMD_appendCommand(CMD_OPERATOR op, CMD_COMMAND *command, CMD_REDIRECTION *redir, CMD_NODE **node) +static BOOL node_builder_expect_token(struct node_builder *builder, enum builder_token tkn) { - /* append as left to right operators */ - if (*node) - { - CMD_NODE **last = node; - while ((*last)->op != CMD_SINGLE) - last = &(*last)->right; + if (builder->pos >= builder->num || builder->stack[builder->pos].token != tkn) + return FALSE; + node_builder_consume(builder); + return TRUE; +}
- *last = node_create_binary(op, *last, node_create_single(command, redir)); - } - else +static void redirection_list_append(CMD_REDIRECTION **redir, CMD_REDIRECTION *last) +{ + if (last) { - *node = node_create_single(command, redir); + for ( ; *redir; redir = &(*redir)->next) {} + *redir = last; } }
static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) { - CMD_REDIRECTION *redir; + CMD_REDIRECTION *redir = NULL; unsigned bogus_line; - CMD_NODE *left = NULL; + CMD_NODE *left = NULL, *right; union token_parameter pmt; enum builder_token tkn; BOOL done;
#define ERROR_IF(x) if (x) {bogus_line = __LINE__; goto error_handling;} - - for (;;) + do { - CMD_OPERATOR cmd_op; - CMD_COMMAND *cmd; - - done = FALSE; - /* we always get a pair of OP CMMAND */ tkn = node_builder_peek_next_token(builder, &pmt); - switch (tkn) - { - case TKN_EOL: done = TRUE; break; - case TKN_AMP: cmd_op = CMD_CONCAT; break; - case TKN_AMPAMP: cmd_op = CMD_ONSUCCESS; break; - case TKN_BAR: cmd_op = CMD_PIPE; break; - case TKN_BARBAR: cmd_op = CMD_ONFAILURE; break; - case TKN_COMMAND: ERROR_IF(TRUE); - default: ERROR_IF(TRUE); - } - if (done) break; - node_builder_consume(builder); + done = FALSE;
- if ((tkn = node_builder_peek_next_token(builder, &pmt)) == TKN_REDIRECTION) + TRACE("\t%u/%u) %s", builder->pos, builder->num, debugstr_token(tkn, pmt)); + switch (tkn) { - redir = pmt.redirection; + case TKN_EOF: + /* always an error to read past end of tokens */ + ERROR_IF(TRUE); + break; + case TKN_EOL: + done = TRUE; + break; + case TKN_OPENPAR: + ERROR_IF(left); node_builder_consume(builder); - tkn = node_builder_peek_next_token(builder, &pmt); - } - else redir = NULL; - ERROR_IF(tkn != TKN_COMMAND) - cmd = pmt.command; - node_builder_consume(builder); - - if ((tkn = node_builder_peek_next_token(builder, &pmt)) == TKN_REDIRECTION) - { - CMD_REDIRECTION **p = &redir; - for (; *p; p = &(*p)->next) {} - *p = pmt.redirection; + /* empty lines are allowed here */ + while ((tkn = node_builder_peek_next_token(builder, &pmt)) == TKN_EOL) + node_builder_consume(builder); + ERROR_IF(!node_builder_parse(builder, &left)); + /* temp before using precedence in chaining */ + while ((tkn = node_builder_peek_next_token(builder, &pmt)) != TKN_CLOSEPAR) + { + ERROR_IF(tkn != TKN_EOL); + node_builder_consume(builder); + /* FIXME potential empty here?? */ + ERROR_IF(!node_builder_parse(builder, &right)); + left = node_create_binary(CMD_CONCAT, left, right); + } + node_builder_consume(builder); + /* if we had redirection before '(', add them up front */ + if (redir) + { + redirection_list_append(&redir, left->redirects); + left->redirects = redir; + redir = NULL; + } + /* just in case we're handling: "(if ...) > a"... to not trigger errors in TKN_REDIRECTION */ + while (node_builder_peek_next_token(builder, &pmt) == TKN_REDIRECTION) + { + redirection_list_append(&left->redirects, pmt.redirection); + node_builder_consume(builder); + } + break; + /* shouldn't appear here... error handling ? */ + case TKN_IN: + /* following tokens act as a delimiter for inner context; return to upper */ + case TKN_CLOSEPAR: + case TKN_ELSE: + case TKN_DO: + done = TRUE; + break; + case TKN_AMP: + ERROR_IF(!left); + node_builder_consume(builder); + if (node_builder_peek_next_token(builder, &pmt) == TKN_CLOSEPAR) + { + done = TRUE; + break; + } + ERROR_IF(!node_builder_parse(builder, &right)); + left = node_create_binary(CMD_CONCAT, left, right); + break; + case TKN_AMPAMP: + ERROR_IF(!left); + node_builder_consume(builder); + ERROR_IF(!node_builder_parse(builder, &right)); + left = node_create_binary(CMD_ONSUCCESS, left, right); + break; + case TKN_BAR: + ERROR_IF(!left); + node_builder_consume(builder); + ERROR_IF(!node_builder_parse(builder, &right)); + left = node_create_binary(CMD_PIPE, left, right); + break; + case TKN_BARBAR: + ERROR_IF(!left); + node_builder_consume(builder); + ERROR_IF(!node_builder_parse(builder, &right)); + left = node_create_binary(CMD_ONFAILURE, left, right); + break; + case TKN_COMMAND: + ERROR_IF(left); + left = node_create_single(pmt.command); + node_builder_consume(builder); + left->redirects = redir; + redir = NULL; + break; + case TKN_IF: + ERROR_IF(left); + ERROR_IF(redir); + { + WCHAR *end; + CMD_IF_CONDITION cond; + CMD_NODE *then_block; + CMD_NODE *else_block; + + node_builder_consume(builder); + tkn = node_builder_peek_next_token(builder, &pmt); + ERROR_IF(tkn != TKN_COMMAND); + ERROR_IF(!if_condition_parse(pmt.command->command, &end, &cond)); + command_dispose(pmt.command); + node_builder_consume(builder); + if (!node_builder_parse(builder, &then_block)) + { + if_condition_dispose(&cond); + ERROR_IF(TRUE); + } + tkn = node_builder_peek_next_token(builder, NULL); + if (tkn == TKN_ELSE) + { + node_builder_consume(builder); + if (!node_builder_parse(builder, &else_block)) + { + if_condition_dispose(&cond); + node_dispose_tree(then_block); + ERROR_IF(TRUE); + } + } + else + else_block = NULL; + left = node_create_if(&cond, then_block, else_block); + } + break; + case TKN_FOR: + ERROR_IF(left); + ERROR_IF(redir); + { + CMD_FOR_CONTROL *for_ctrl; + CMD_NODE *do_block; + + node_builder_consume(builder); + tkn = node_builder_peek_next_token(builder, &pmt); + ERROR_IF(tkn != TKN_COMMAND); + node_builder_consume(builder); + for_ctrl = for_control_parse(pmt.command->command); + command_dispose(pmt.command); + ERROR_IF(for_ctrl == NULL); + ERROR_IF(!node_builder_expect_token(builder, TKN_IN)); + ERROR_IF(!node_builder_expect_token(builder, TKN_OPENPAR)); + do + { + tkn = node_builder_peek_next_token(builder, &pmt); + switch (tkn) + { + case TKN_COMMAND: + for_control_append_set(for_ctrl, pmt.command->command); + command_dispose(pmt.command); + break; + case TKN_EOL: + case TKN_CLOSEPAR: + break; + default: + ERROR_IF(TRUE); + } + node_builder_consume(builder); + } while (tkn != TKN_CLOSEPAR); + if (!node_builder_expect_token(builder, TKN_DO) || + !node_builder_parse(builder, &do_block)) + { + for_control_dispose(for_ctrl); + ERROR_IF(TRUE); + } + left = node_create_for(for_ctrl, do_block); + } + break; + case TKN_REDIRECTION: + ERROR_IF(left && (left->op == CMD_IF || left->op == CMD_FOR)); + redirection_list_append(left ? &left->redirects : &redir, pmt.redirection); node_builder_consume(builder); + break; } - WCMD_appendCommand(cmd_op, cmd, redir, &left); } while (!done); #undef ERROR_IF *result = left; @@ -2451,6 +2496,7 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) error_handling: TRACE("Parser failed at line %u:token %s\n", bogus_line, debugstr_token(tkn, pmt)); node_dispose_tree(left); + redirection_dispose_list(redir);
return FALSE; } @@ -2460,47 +2506,68 @@ static BOOL node_builder_generate(struct node_builder *builder, CMD_NODE **node) union token_parameter tkn_pmt; enum builder_token tkn;
- if (node_builder_parse(builder, node) && - builder->pos + 1 >= builder->num) /* consumed all tokens? */ - return TRUE; - /* print error on first unused token */ - if (builder->pos < builder->num) + if (builder->opened_parenthesis) { - WCHAR buffer[MAXSTRING]; - const WCHAR *tknstr; - - tkn = node_builder_peek_next_token(builder, &tkn_pmt); - switch (tkn) + TRACE("Brackets do not match, error out without executing.\n"); + WCMD_output_stderr(WCMD_LoadMessage(WCMD_BADPAREN)); + errorlevel = RETURN_CODE_SYNTAX_ERROR; + } + else + { + if (node_builder_parse(builder, node) && + builder->pos + 1 >= builder->num) /* consumed all tokens? */ + return TRUE; + /* print error on first unused token */ + if (builder->pos < builder->num) { - case TKN_COMMAND: - tknstr = tkn_pmt.command->command; - break; - case TKN_EOL: - tknstr = WCMD_LoadMessage(WCMD_ENDOFLINE); - break; - case TKN_REDIRECTION: - MultiByteToWideChar(CP_ACP, 0, debugstr_redirection(tkn_pmt.redirection), -1, buffer, ARRAY_SIZE(buffer)); - tknstr = buffer; - break; - case TKN_AMP: - case TKN_AMPAMP: - case TKN_BAR: - case TKN_BARBAR: - MultiByteToWideChar(CP_ACP, 0, debugstr_token(tkn, tkn_pmt), -1, buffer, ARRAY_SIZE(buffer)); - tknstr = buffer; - break; - default: - FIXME("Unexpected situation\n"); - tknstr = L""; + WCHAR buffer[MAXSTRING]; + const WCHAR *tknstr; + + tkn = node_builder_peek_next_token(builder, &tkn_pmt); + switch (tkn) + { + case TKN_COMMAND: + tknstr = tkn_pmt.command->command; + break; + case TKN_EOF: + tknstr = WCMD_LoadMessage(WCMD_ENDOFFILE); + break; + case TKN_EOL: + tknstr = WCMD_LoadMessage(WCMD_ENDOFLINE); + break; + case TKN_REDIRECTION: + MultiByteToWideChar(CP_ACP, 0, debugstr_redirection(tkn_pmt.redirection), -1, buffer, ARRAY_SIZE(buffer)); + tknstr = buffer; + break; + case TKN_AMP: + case TKN_AMPAMP: + case TKN_BAR: + case TKN_BARBAR: + case TKN_FOR: + case TKN_IN: + case TKN_DO: + case TKN_IF: + case TKN_ELSE: + case TKN_OPENPAR: + case TKN_CLOSEPAR: + MultiByteToWideChar(CP_ACP, 0, debugstr_token(tkn, tkn_pmt), -1, buffer, ARRAY_SIZE(buffer)); + tknstr = buffer; + break; + default: + FIXME("Unexpected situation\n"); + tknstr = L""; + break; + } + WCMD_output_stderr(WCMD_LoadMessage(WCMD_BADTOKEN), tknstr); } - WCMD_output_stderr(WCMD_LoadMessage(WCMD_BADTOKEN), tknstr); } /* free remaining tokens */ for (;;) { tkn = node_builder_peek_next_token(builder, &tkn_pmt); - if (tkn == TKN_EOL) break; + if (tkn == TKN_EOF) break; if (tkn == TKN_COMMAND) command_dispose(tkn_pmt.command); + if (tkn == TKN_REDIRECTION) redirection_dispose_list(tkn_pmt.redirection); node_builder_consume(builder); }
@@ -2508,38 +2575,23 @@ static BOOL node_builder_generate(struct node_builder *builder, CMD_NODE **node) return FALSE; }
-/*************************************************************************** - * WCMD_addCommand - * - * Adds a command to the current command list - */ static void lexer_push_command(struct node_builder *builder, WCHAR *command, int *commandLen, WCHAR *redirs, int *redirLen, WCHAR **copyTo, int **copyToLen) { union token_parameter tkn_pmt; - CMD_COMMAND *thisEntry = NULL; - - /* Allocate storage for command */ - thisEntry = xalloc(sizeof(CMD_COMMAND));
- /* Copy in the command */ - if (command) + /* push first all redirections */ + if (*redirLen) { WCHAR *pos; WCHAR *last = redirs + *redirLen; - CMD_REDIRECTION **insrt;
- thisEntry->command = xalloc((*commandLen + 1) * sizeof(WCHAR)); - memcpy(thisEntry->command, command, *commandLen * sizeof(WCHAR)); - thisEntry->command[*commandLen] = 0x00; + redirs[*redirLen] = 0;
- if (redirs) redirs[*redirLen] = 0; /* Create redirects, keeping order (eg "2>foo 1>&2") */ - insrt = &tkn_pmt.redirection; - *insrt = NULL; - for (pos = redirs; pos; insrt = &(*insrt)->next) + for (pos = redirs; pos; ) { WCHAR *p = find_chr(pos, last, L"<>"); WCHAR *filename; @@ -2549,7 +2601,7 @@ static void lexer_push_command(struct node_builder *builder, if (*p == L'<') { filename = WCMD_parameter(p + 1, 0, NULL, FALSE, FALSE); - *insrt = redirection_create_file(REDIR_READ_FROM, 0, filename); + tkn_pmt.redirection = redirection_create_file(REDIR_READ_FROM, 0, filename); } else { @@ -2560,34 +2612,34 @@ static void lexer_push_command(struct node_builder *builder, if (*++p == L'>') {p++; op = REDIR_WRITE_APPEND;} if (*p == L'&' && (p[1] >= L'0' && p[1] <= L'9')) { - *insrt = redirection_create_clone(fd, p[1] - '0'); + tkn_pmt.redirection = redirection_create_clone(fd, p[1] - '0'); p++; } else { filename = WCMD_parameter(p, 0, NULL, FALSE, FALSE); - *insrt = redirection_create_file(op, fd, filename); + tkn_pmt.redirection = redirection_create_file(op, fd, filename); } } pos = p + 1; - } - if (tkn_pmt.redirection) node_builder_push_token_parameter(builder, TKN_REDIRECTION, tkn_pmt); - - /* Reset the lengths */ - *commandLen = 0; - *redirLen = 0; - *copyToLen = commandLen; - *copyTo = command; + } } - else - thisEntry->command = NULL; + if (*commandLen) + { + tkn_pmt.command = xalloc(sizeof(CMD_COMMAND)); + + tkn_pmt.command->command = xalloc((*commandLen + 1) * sizeof(WCHAR)); + memcpy(tkn_pmt.command->command, command, *commandLen * sizeof(WCHAR)); + tkn_pmt.command->command[*commandLen] = L'\0';
- /* Fill in other fields */ - thisEntry->pipeFile[0] = 0x00; - thisEntry->bracketDepth = builder->opened_parenthesis; - tkn_pmt.command = thisEntry; - node_builder_push_token_parameter(builder, TKN_COMMAND, tkn_pmt); + node_builder_push_token_parameter(builder, TKN_COMMAND, tkn_pmt); + } + /* Reset the lengths */ + *commandLen = 0; + *redirLen = 0; + *copyToLen = commandLen; + *copyTo = command; }
static WCHAR *fetch_next_line(BOOL feed, BOOL first_line, HANDLE from, WCHAR* buffer) @@ -2676,11 +2728,9 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE int curRedirsLen = 0; WCHAR *curCopyTo; int *curLen; - enum builder_token cmd_tkn = TKN_AMP; static WCHAR *extraSpace = NULL; /* Deliberately never freed */ BOOL inOneLine = FALSE; BOOL inFor = FALSE; - BOOL inIn = FALSE; BOOL inIf = FALSE; BOOL inElse= FALSE; BOOL onlyWhiteSpace = FALSE; @@ -2691,10 +2741,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE BOOL lastWasRedirect = TRUE; BOOL ignoreBracket = FALSE; /* Some expressions after if (set) require */ /* handling brackets as a normal character */ - int lineCurDepth; /* Bracket depth when line was read in */ - BOOL resetAtEndOfLine = FALSE; /* Do we need to reset curdepth at EOL */ + BOOL acceptCommand = TRUE; struct node_builder builder; - BOOL ret;
*output = NULL; /* Allocate working space for a command read from keyboard, file etc */ @@ -2717,7 +2765,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE curCopyTo = curString; curLen = &curStringLen; lastWasRedirect = FALSE; /* Required e.g. for spaces between > and filename */ - lineCurDepth = builder.opened_parenthesis; /* What was the curdepth at the beginning of the line */ + onlyWhiteSpace = TRUE;
/* Parse every character on the line being processed */ while (*curPos != 0x00) { @@ -2743,77 +2791,83 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE
} else if (WCMD_keyword_ws_found(L"for", curPos)) { inFor = TRUE; + node_builder_push_token(&builder, TKN_FOR);
+ curPos = WCMD_skip_leading_spaces(curPos + 3); /* "for */ /* If command starts with 'if ' or 'else ', handle ('s mid line. We should ensure this is only true in the command portion of the IF statement, but this should suffice for now. To be able to handle ('s in the condition part take as much as evaluate_if_condition would take and skip parsing it here. */ - } else if (WCMD_keyword_ws_found(L"if", curPos)) { - WCHAR *p, *command; + acceptCommand = FALSE; + } else if (acceptCommand && WCMD_keyword_ws_found(L"if", curPos)) { + WCHAR *command; + + node_builder_push_token(&builder, TKN_IF);
inIf = TRUE;
- p = WCMD_skip_leading_spaces(curPos + 2); /* "if" */ - if (if_condition_create(p, &command, NULL)) + curPos = WCMD_skip_leading_spaces(curPos + 2); /* "if" */ + if (if_condition_parse(curPos, &command, NULL)) { int if_condition_len = command - curPos; - WINE_TRACE("p: %s, quals: %s, param1: %s, param2: %s, command: %s, if_condition_len: %d\n", - wine_dbgstr_w(p), wine_dbgstr_w(quals), wine_dbgstr_w(param1), - wine_dbgstr_w(param2), wine_dbgstr_w(command), if_condition_len); - memcpy(&curCopyTo[*curLen], curPos, if_condition_len*sizeof(WCHAR)); - (*curLen)+=if_condition_len; - curPos+=if_condition_len; + TRACE("p: %s, command: %s, if_condition_len: %d\n", + wine_dbgstr_w(curPos), wine_dbgstr_w(command), if_condition_len); + memcpy(&curCopyTo[*curLen], curPos, if_condition_len * sizeof(WCHAR)); + (*curLen) += if_condition_len; + curPos += if_condition_len; + + /* FIXME we do parsing twice of condition (once here, second time in node_builder_parse) */ + lexer_push_command(&builder, curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen); + } if (WCMD_keyword_ws_found(L"set", curPos)) ignoreBracket = TRUE; - + acceptCommand = TRUE; + onlyWhiteSpace = TRUE; + continue; } else if (WCMD_keyword_ws_found(L"else", curPos)) { - const int keyw_len = lstrlenW(L"else") + 1; inElse = TRUE; lastWasElse = TRUE; + acceptCommand = TRUE; onlyWhiteSpace = TRUE; - memcpy(&curCopyTo[*curLen], curPos, keyw_len*sizeof(WCHAR)); - (*curLen)+=keyw_len; - curPos+=keyw_len; - - /* If we had a single line if XXX which reaches an else (needs odd - syntax like if 1=1 command && (command) else command we pretended - to add brackets for the if, so they are now over */ - if (resetAtEndOfLine) { - WINE_TRACE("Resetting curdepth at end of line to %d\n", lineCurDepth); - resetAtEndOfLine = FALSE; - builder.opened_parenthesis = lineCurDepth; - } + node_builder_push_token(&builder, TKN_ELSE); + + curPos = WCMD_skip_leading_spaces(curPos + 4 /* else */); continue;
/* In a for loop, the DO command will follow a close bracket followed by whitespace, followed by DO, ie closeBracket inserts a NULL entry, curLen is then 0, and all whitespace is skipped */ } else if (inFor && WCMD_keyword_ws_found(L"do", curPos)) { - const int keyw_len = lstrlenW(L"do") + 1; + WINE_TRACE("Found 'DO '\n"); lastWasDo = TRUE; + acceptCommand = TRUE; onlyWhiteSpace = TRUE; - memcpy(&curCopyTo[*curLen], curPos, keyw_len*sizeof(WCHAR)); - (*curLen)+=keyw_len; - curPos+=keyw_len; + + node_builder_push_token(&builder, TKN_DO); + curPos = WCMD_skip_leading_spaces(curPos + 2 /* do */); continue; } } else if (curCopyTo == curString) {
/* Special handling for the 'FOR' command */ - if (inFor && lastWasWhiteSpace) { + if (inFor && lastWasWhiteSpace) { WINE_TRACE("Found 'FOR ', comparing next parm: '%s'\n", wine_dbgstr_w(curPos));
if (WCMD_keyword_ws_found(L"in", curPos)) { - const int keyw_len = lstrlenW(L"in") + 1; WINE_TRACE("Found 'IN '\n"); + + lexer_push_command(&builder, curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen); + node_builder_push_token(&builder, TKN_IN); lastWasIn = TRUE; onlyWhiteSpace = TRUE; - memcpy(&curCopyTo[*curLen], curPos, keyw_len*sizeof(WCHAR)); - (*curLen)+=keyw_len; - curPos+=keyw_len; + curPos = WCMD_skip_leading_spaces(curPos + 2 /* in */); continue; } } @@ -2840,6 +2894,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE /* If finishing off a redirect, add a whitespace delimiter */ if (curCopyTo == curRedirs) { curCopyTo[(*curLen)++] = ' '; + if (curStringLen == 0) + onlyWhiteSpace = TRUE; } curCopyTo = curString; curLen = &curStringLen; @@ -2887,30 +2943,19 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE if (!inQuotes) { lastWasRedirect = FALSE;
- /* Add an entry to the command list */ - if (curStringLen > 0) { - - node_builder_push_token(&builder, cmd_tkn); - /* Add the current command */ - lexer_push_command(&builder, curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen); - } + lexer_push_command(&builder, curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen);
if (*(curPos+1) == '|') { curPos++; /* Skip other | */ - cmd_tkn = TKN_BARBAR; + node_builder_push_token(&builder, TKN_BARBAR); } else { - cmd_tkn = TKN_BAR; - } - - /* If in an IF or ELSE statement, put subsequent chained - commands at a higher depth as if brackets were supplied - but remember to reset to the original depth at EOL */ - if ((inIf || inElse) && builder.opened_parenthesis == lineCurDepth) { - builder.opened_parenthesis++; - resetAtEndOfLine = TRUE; + node_builder_push_token(&builder, TKN_BAR); } + acceptCommand = TRUE; + onlyWhiteSpace = TRUE; + thisChar = L' '; } else { curCopyTo[(*curLen)++] = *curPos; } @@ -2937,12 +2982,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE inIf, inElse, lastWasElse); lastWasRedirect = FALSE;
- /* Ignore open brackets inside the for set */ - if (*curLen == 0 && !inIn) { - builder.opened_parenthesis++; - - /* If in quotes, ignore brackets */ - } else if (inQuotes) { + if (inQuotes) { curCopyTo[(*curLen)++] = *curPos;
/* In a FOR loop, an unquoted '(' may occur straight after @@ -2952,22 +2992,19 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE In an ELSE statement, only allow it straight away after the ELSE and whitespace */ - } else if ((inIf && !ignoreBracket) || + } else if ((acceptCommand && onlyWhiteSpace) || + (inIf && !ignoreBracket) || (inElse && lastWasElse && onlyWhiteSpace) || (inFor && (lastWasIn || lastWasDo) && onlyWhiteSpace)) {
- /* If entering into an 'IN', set inIn */ - if (inFor && lastWasIn && onlyWhiteSpace) { - WINE_TRACE("Inside an IN\n"); - inIn = TRUE; - } - - node_builder_push_token(&builder, cmd_tkn); /* Add the current command */ lexer_push_command(&builder, curString, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen); - builder.opened_parenthesis++; + node_builder_push_token(&builder, TKN_OPENPAR); + acceptCommand = TRUE; + onlyWhiteSpace = TRUE; + thisChar = ' '; } else { curCopyTo[(*curLen)++] = *curPos; } @@ -2998,28 +3035,19 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE lastWasRedirect = FALSE;
/* Add an entry to the command list */ - if (curStringLen > 0) { - - node_builder_push_token(&builder, cmd_tkn); - /* Add the current command */ - lexer_push_command(&builder, curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen); - } + lexer_push_command(&builder, curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen);
if (*(curPos+1) == '&') { curPos++; /* Skip other & */ - cmd_tkn = TKN_AMPAMP; + node_builder_push_token(&builder, TKN_AMPAMP); } else { - cmd_tkn = TKN_AMP; - } - /* If in an IF or ELSE statement, put subsequent chained - commands at a higher depth as if brackets were supplied - but remember to reset to the original depth at EOL */ - if ((inIf || inElse) && builder.opened_parenthesis == lineCurDepth) { - builder.opened_parenthesis++; - resetAtEndOfLine = TRUE; + node_builder_push_token(&builder, TKN_AMP); } + acceptCommand = TRUE; + onlyWhiteSpace = TRUE; + thisChar = ' '; } else { curCopyTo[(*curLen)++] = *curPos; } @@ -3029,26 +3057,13 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE lastWasRedirect = FALSE;
/* Add the current command if there is one */ - if (curStringLen) { - - node_builder_push_token(&builder, cmd_tkn); - /* Add the current command */ - lexer_push_command(&builder, curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen); - } - - /* Add an empty entry to the command list */ - cmd_tkn = TKN_AMP; - node_builder_push_token(&builder, cmd_tkn); - lexer_push_command(&builder, NULL, &curStringLen, + lexer_push_command(&builder, curString, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen); - - builder.opened_parenthesis--; - - /* Leave inIn if necessary */ - if (inIn) inIn = FALSE; + node_builder_push_token(&builder, TKN_CLOSEPAR); + acceptCommand = FALSE; + onlyWhiteSpace = TRUE; + thisChar = ' '; } else { curCopyTo[(*curLen)++] = *curPos; } @@ -3066,64 +3081,41 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE if ((thisChar != ' ') && (thisChar != '\t') && (thisChar != '\n')) onlyWhiteSpace = FALSE;
- /* Flag end of interest in FOR DO and IN parms once something has been processed */ - if (!lastWasWhiteSpace) { - lastWasIn = lastWasDo = FALSE; - } - /* If we have reached the end, add this command into the list Do not add command to list if escape char ^ was last */ - if (*curPos == 0x00 && *curLen > 0) { - - node_builder_push_token(&builder, cmd_tkn); + if (*curPos == L'\0') { /* Add an entry to the command list */ lexer_push_command(&builder, curString, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen); - - /* If we had a single line if or else, and we pretended to add - brackets, end them now */ - if (resetAtEndOfLine) { - WINE_TRACE("Resetting curdepth at end of line to %d\n", lineCurDepth); - resetAtEndOfLine = FALSE; - builder.opened_parenthesis = lineCurDepth; + node_builder_push_token(&builder, TKN_EOL); + + /* If we have reached the end of the string, see if bracketing is outstanding */ + if (builder.opened_parenthesis > 0 && readFrom != INVALID_HANDLE_VALUE) { + TRACE("Need to read more data as outstanding brackets or carets\n"); + inOneLine = FALSE; + ignoreBracket = FALSE; + inQuotes = 0; + acceptCommand = TRUE; + onlyWhiteSpace = TRUE; + + /* fetch next non empty line */ + do { + curPos = fetch_next_line(TRUE, FALSE, readFrom, extraSpace); + } while (curPos && *curPos == L'\0'); + if (!curPos) + curPos = extraSpace; } - } - - /* If we have reached the end of the string, see if bracketing or - final caret is outstanding */ - if (*curPos == 0x00 && builder.opened_parenthesis > 0 && readFrom != INVALID_HANDLE_VALUE) { - - WINE_TRACE("Need to read more data as outstanding brackets or carets\n"); - inOneLine = FALSE; - ignoreBracket = FALSE; - cmd_tkn = TKN_AMP; - inQuotes = 0; - - /* Read more, skipping any blank lines */ - do { - WINE_TRACE("Read more input\n"); - if (!(curPos = fetch_next_line(TRUE, FALSE, readFrom, extraSpace))) - break; - } while (*curPos == L'\0'); - if (!curPos) - curPos = extraSpace; } }
- *output = NULL; - ret = builder.opened_parenthesis <= lineCurDepth && node_builder_generate(&builder, output); + node_builder_generate(&builder, output); node_builder_dispose(&builder); - if (!ret) - { - WINE_TRACE("Brackets do not match, error out without executing.\n"); - errorlevel = RETURN_CODE_SYNTAX_ERROR; - }
return extraSpace; }
-BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) +static BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) { WCHAR expanded_left[MAXSTRING]; WCHAR expanded_right[MAXSTRING]; @@ -3297,8 +3289,7 @@ static CMD_NODE *for_loop_fileset_parse_line(CMD_NODE *cmdList, int varidx, WCHA /* Execute the body of the for loop with these values */ if (forloopcontext->variable[varidx] && forloopcontext->variable[varidx][0] != forf_eol) { - /* +3 for "do " */ - WCMD_part_execute(&cmdList, CMD_node_get_command(cmdList)->command + 3, FALSE, TRUE); + cmdList = node_execute(cmdList) == NO_ERROR ? cmdList : NULL; } else { @@ -3514,18 +3505,16 @@ static CMD_NODE *for_control_execute_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR
if (insert_pos + wcslen(fd.cFileName) + 1 >= ARRAY_SIZE(buffer)) continue; wcscpy(&buffer[insert_pos], fd.cFileName); - body = cmdList; WCMD_set_for_loop_variable(for_ctrl->variable_index, buffer); - WCMD_part_execute(&body, CMD_node_get_command(body)->command + 3, FALSE, TRUE); + body = node_execute(cmdList) == NO_ERROR ? cmdList : NULL; } while (FindNextFileW(hff, &fd) != 0); FindClose(hff); } else { - body = cmdList; WCMD_set_for_loop_variable(for_ctrl->variable_index, buffer); - WCMD_part_execute(&body, CMD_node_get_command(body)->command + 3, FALSE, TRUE); + body = node_execute(cmdList) == NO_ERROR ? cmdList : NULL; } } return body; @@ -3592,80 +3581,145 @@ static CMD_NODE *for_control_execute_numbers(CMD_FOR_CONTROL *for_ctrl, CMD_NODE { WCHAR tmp[32];
- body = cmdList; swprintf(tmp, ARRAY_SIZE(tmp), L"%d", var); WCMD_set_for_loop_variable(for_ctrl->variable_index, tmp); TRACE("Processing FOR number %s\n", wine_dbgstr_w(tmp)); - WCMD_part_execute(&body, CMD_node_get_command(cmdList)->command + 3, FALSE, TRUE); + body = node_execute(cmdList) == NO_ERROR ? cmdList : NULL; } return body; }
-void for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE **cmdList) +static CMD_NODE *for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node) { CMD_NODE *last; + + if (!for_ctrl->set) return NO_ERROR; + WCMD_save_for_loop_context(FALSE);
switch (for_ctrl->operator) { case CMD_FOR_FILETREE: if (for_ctrl->flags & CMD_FOR_FLAG_TREE_RECURSE) - last = for_control_execute_walk_files(for_ctrl, *cmdList); + last = for_control_execute_walk_files(for_ctrl, node); else - last = for_control_execute_set(for_ctrl, NULL, 0, *cmdList); + last = for_control_execute_set(for_ctrl, NULL, 0, node); break; case CMD_FOR_FILE_SET: - last = for_control_execute_fileset(for_ctrl, *cmdList); + last = for_control_execute_fileset(for_ctrl, node); break; case CMD_FOR_NUMBERS: - last = for_control_execute_numbers(for_ctrl, *cmdList); + last = for_control_execute_numbers(for_ctrl, node); break; default: last = NULL; break; } WCMD_restore_for_loop_context(); - *cmdList = last; + return last; }
-/*************************************************************************** - * WCMD_process_commands - * - * Process all the commands read in so far - */ -CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket, - BOOL retrycall) { - - int bdepth = -1; - - if (thisCmd && oneBracket) bdepth = CMD_node_get_depth(thisCmd); - - /* Loop through the commands, processing them one by one */ - while (thisCmd) { +RETURN_CODE node_execute(CMD_NODE *node) +{ + HANDLE old_stdhandles[3] = {GetStdHandle (STD_INPUT_HANDLE), + GetStdHandle (STD_OUTPUT_HANDLE), + GetStdHandle (STD_ERROR_HANDLE)}; + static DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE};
- CMD_NODE *origCmd = thisCmd; + RETURN_CODE return_code; + int i, test;
- /* If processing one bracket only, and we find the end bracket - entry (or less), return */ - if (oneBracket && !CMD_node_get_command(thisCmd)->command && - bdepth <= CMD_node_get_depth(thisCmd)) { - WINE_TRACE("Finished bracket @ %p, next command is %p\n", - thisCmd, CMD_node_next(thisCmd)); - return CMD_node_next(thisCmd); - } + if (!node) return NO_ERROR; + if (!set_std_redirections(node->redirects)) + { + WCMD_print_error(); + /* FIXME potentially leaking here (if first redir created ok, and second failed */ + return ERROR_INVALID_FUNCTION; + } + switch (node->op) + { + case CMD_SINGLE: + if (node->command->command[0] != ':') + return_code = execute_single_command(node->command->command); + else return_code = NO_ERROR; + break; + case CMD_CONCAT: + case CMD_ONSUCCESS: + case CMD_ONFAILURE: + return_code = node_execute(node->left); + if (return_code != RETURN_CODE_ABORTED) + return_code = node_execute(node->right); + break; + case CMD_PIPE: + { + static SECURITY_ATTRIBUTES sa = {.nLength = sizeof(sa), .lpSecurityDescriptor = NULL, .bInheritHandle = TRUE}; + WCHAR temp_path[MAX_PATH]; + WCHAR filename[MAX_PATH]; + CMD_REDIRECTION *output; + + /* FIXME: a real pipe instead of writing to an intermediate file would be + * better. + * But waiting for completion of commands will require more work. + */ + /* FIXME check precedence (eg foo > a | more) + * with following code, | has higher precedence than > a + * (which is likely wrong IIRC, and not what previous code was doing) + */ + /* Generate a unique temporary filename */ + GetTempPathW(ARRAY_SIZE(temp_path), temp_path); + GetTempFileNameW(temp_path, L"CMD", 0, filename); + TRACE("Using temporary file of %ls\n", filename); + + /* set output for left hand side command */ + output = redirection_create_file(REDIR_WRITE_TO, 1, filename); + if (set_std_redirections(output)) + { + return_code = node_execute(node->left);
- /* Ignore the NULL entries a ')' inserts (Only 'if' cares - about them and it will be handled in there) - Also, skip over any batch labels (eg. :fred) */ - if (CMD_node_get_command(thisCmd)->command && CMD_node_get_command(thisCmd)->command[0] != ':') { - WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(CMD_node_get_command(thisCmd)->command)); - WCMD_execute(CMD_node_get_command(thisCmd)->command, CMD_node_get_single_node(thisCmd)->redirects, &thisCmd, retrycall); - } + CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); + SetStdHandle(STD_OUTPUT_HANDLE, old_stdhandles[1]);
- /* Step on unless the command itself already stepped on */ - if (thisCmd == origCmd) thisCmd = CMD_node_next(thisCmd); + if (return_code == NO_ERROR) + { + HANDLE h = CreateFileW(filename, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h != INVALID_HANDLE_VALUE) + { + SetStdHandle(STD_INPUT_HANDLE, h); + return_code = node_execute(node->right); + } + else return_code = ERROR_INVALID_FUNCTION; + } + DeleteFileW(filename); + } + else return_code = ERROR_INVALID_FUNCTION; + redirection_dispose_list(output); + } + break; + case CMD_IF: + if (if_condition_evaluate(&node->condition, &test)) + return_code = node_execute(test ? node->then_block : node->else_block); + else + return_code = ERROR_INVALID_FUNCTION; + break; + case CMD_FOR: + return_code = for_control_execute(&node->for_ctrl, node->do_block) ? NO_ERROR : ERROR_INVALID_FUNCTION; + break; + default: + FIXME("Unexpected operator %u\n", node->op); + return_code = ERROR_INVALID_FUNCTION; } - return NULL; + /* Restore old handles */ + for (i = 0; i < 3; i++) + { + if (old_stdhandles[i] != GetStdHandle(idx_stdhandles[i])) + { + CloseHandle(GetStdHandle(idx_stdhandles[i])); + SetStdHandle(idx_stdhandles[i], old_stdhandles[i]); + } + } + return return_code; }
static BOOL WINAPI my_event_handler(DWORD ctrl) @@ -3946,7 +4000,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[])
/* Parse the command string, without reading any more input */ WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE); - WCMD_process_commands(toExecute, FALSE, FALSE); + node_execute(toExecute); node_dispose_tree(toExecute); toExecute = NULL;
@@ -4027,7 +4081,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[]) if (opt_k) { /* Parse the command string, without reading any more input */ WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE); - WCMD_process_commands(toExecute, FALSE, FALSE); + node_execute(toExecute); node_dispose_tree(toExecute); toExecute = NULL; free(cmd); @@ -4046,7 +4100,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[]) in place, may occur */ if (!WCMD_ReadAndParseLine(NULL, &toExecute, GetStdHandle(STD_INPUT_HANDLE))) break; - WCMD_process_commands(toExecute, FALSE, FALSE); + node_execute(toExecute); node_dispose_tree(toExecute); if (toExecute && echo_mode) WCMD_output_asis(L"\r\n"); toExecute = NULL;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 9ad9bc44866..c891e7525b1 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -986,6 +986,16 @@ static const char *debugstr_redirection(const CMD_REDIRECTION *redir) } }
+static CMD_COMMAND *command_create(const WCHAR *ptr, size_t len) +{ + CMD_COMMAND *ret = xalloc(sizeof(CMD_COMMAND)); + + ret->command = xalloc((len + 1) * sizeof(WCHAR)); + memcpy(ret->command, ptr, len * sizeof(WCHAR)); + ret->command[len] = L'\0'; + return ret; +} + static void command_dispose(CMD_COMMAND *cmd) { if (cmd) @@ -2417,6 +2427,13 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) node_builder_consume(builder); tkn = node_builder_peek_next_token(builder, &pmt); ERROR_IF(tkn != TKN_COMMAND); + if (!wcscmp(pmt.command->command, L"/?")) + { + node_builder_consume(builder); + command_dispose(pmt.command); + left = node_create_single(command_create(L"help if", 7)); + break; + } ERROR_IF(!if_condition_parse(pmt.command->command, &end, &cond)); command_dispose(pmt.command); node_builder_consume(builder); @@ -2451,6 +2468,13 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) node_builder_consume(builder); tkn = node_builder_peek_next_token(builder, &pmt); ERROR_IF(tkn != TKN_COMMAND); + if (!wcscmp(pmt.command->command, L"/?")) + { + node_builder_consume(builder); + command_dispose(pmt.command); + left = node_create_single(command_create(L"help for", 8)); + break; + } node_builder_consume(builder); for_ctrl = for_control_parse(pmt.command->command); command_dispose(pmt.command); @@ -2627,12 +2651,7 @@ static void lexer_push_command(struct node_builder *builder, } if (*commandLen) { - tkn_pmt.command = xalloc(sizeof(CMD_COMMAND)); - - tkn_pmt.command->command = xalloc((*commandLen + 1) * sizeof(WCHAR)); - memcpy(tkn_pmt.command->command, command, *commandLen * sizeof(WCHAR)); - tkn_pmt.command->command[*commandLen] = L'\0'; - + tkn_pmt.command = command_create(command, *commandLen); node_builder_push_token_parameter(builder, TKN_COMMAND, tkn_pmt); } /* Reset the lengths */
From: Eric Pouech epouech@codeweavers.com
Cleaning-up previous method for end-of-block vs error in IF and FOR commands.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 86 ++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 45 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index c891e7525b1..7d8a0ce307f 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -3228,9 +3228,10 @@ static BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) return TRUE; }
-static CMD_NODE *for_loop_fileset_parse_line(CMD_NODE *cmdList, int varidx, WCHAR *buffer, - WCHAR forf_eol, const WCHAR *forf_delims, const WCHAR *forf_tokens) +static RETURN_CODE for_loop_fileset_parse_line(CMD_NODE *node, int varidx, WCHAR *buffer, + WCHAR forf_eol, const WCHAR *forf_delims, const WCHAR *forf_tokens) { + RETURN_CODE return_code = NO_ERROR; WCHAR *parm; int varoffset; int nexttoken, lasttoken = -1; @@ -3308,14 +3309,13 @@ static CMD_NODE *for_loop_fileset_parse_line(CMD_NODE *cmdList, int varidx, WCHA /* Execute the body of the for loop with these values */ if (forloopcontext->variable[varidx] && forloopcontext->variable[varidx][0] != forf_eol) { - cmdList = node_execute(cmdList) == NO_ERROR ? cmdList : NULL; + return_code = node_execute(node); } else { TRACE("Skipping line because of eol\n"); - cmdList = NULL; } - return cmdList; + return return_code; }
void WCMD_save_for_loop_context(BOOL reset) @@ -3371,11 +3371,11 @@ static BOOL match_ending_delim(WCHAR *string) return FALSE; }
-static CMD_NODE *for_control_execute_from_FILE(CMD_FOR_CONTROL *for_ctrl, FILE *input, CMD_NODE *cmdList) +static RETURN_CODE for_control_execute_from_FILE(CMD_FOR_CONTROL *for_ctrl, FILE *input, CMD_NODE *node) { WCHAR buffer[MAXSTRING]; int skip_count = for_ctrl->num_lines_to_skip; - CMD_NODE *body = NULL; + RETURN_CODE return_code = NO_ERROR;
/* Read line by line until end of file */ while (fgetws(buffer, ARRAY_SIZE(buffer), input)) @@ -3396,19 +3396,19 @@ static CMD_NODE *for_control_execute_from_FILE(CMD_FOR_CONTROL *for_ctrl, FILE * break; while (len && (buffer[len - 1] == '\n' || buffer[len - 1] == '\r')) buffer[--len] = L'\0'; - body = for_loop_fileset_parse_line(cmdList, for_ctrl->variable_index, buffer, - for_ctrl->eol, for_ctrl->delims, for_ctrl->tokens); + return_code = for_loop_fileset_parse_line(node, for_ctrl->variable_index, buffer, + for_ctrl->eol, for_ctrl->delims, for_ctrl->tokens); buffer[0] = 0; } - return body; + return return_code; }
-static CMD_NODE *for_control_execute_fileset(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *cmdList) +static RETURN_CODE for_control_execute_fileset(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node) { + RETURN_CODE return_code = NO_ERROR; WCHAR set[MAXSTRING]; WCHAR *args; size_t len; - CMD_NODE *body = NULL; FILE *input; int i;
@@ -3423,8 +3423,8 @@ static CMD_NODE *for_control_execute_fileset(CMD_FOR_CONTROL *for_ctrl, CMD_NODE args++; if (!for_ctrl->num_lines_to_skip) { - body = for_loop_fileset_parse_line(cmdList, for_ctrl->variable_index, args, - for_ctrl->eol, for_ctrl->delims, for_ctrl->tokens); + return_code = for_loop_fileset_parse_line(node, for_ctrl->variable_index, args, + for_ctrl->eol, for_ctrl->delims, for_ctrl->tokens); } } else if (args[0] == (for_ctrl->use_backq ? L'`' : L''') && match_ending_delim(args)) @@ -3439,10 +3439,9 @@ static CMD_NODE *for_control_execute_fileset(CMD_FOR_CONTROL *for_ctrl, CMD_NODE { WCMD_print_error(); WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), args); - errorlevel = ERROR_INVALID_FUNCTION; - return NULL; /* FOR loop aborts at first failure here */ + return errorlevel = ERROR_INVALID_FUNCTION; /* FOR loop aborts at first failure here */ } - body = for_control_execute_from_FILE(for_ctrl, input, cmdList); + return_code = for_control_execute_from_FILE(for_ctrl, input, node); fclose(input); } else @@ -3459,20 +3458,19 @@ static CMD_NODE *for_control_execute_fileset(CMD_FOR_CONTROL *for_ctrl, CMD_NODE { WCMD_print_error(); WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), element); - errorlevel = ERROR_INVALID_FUNCTION; - return NULL; /* FOR loop aborts at first failure here */ + return errorlevel = ERROR_INVALID_FUNCTION; /* FOR loop aborts at first failure here */ } - body = for_control_execute_from_FILE(for_ctrl, input, cmdList); + return_code = for_control_execute_from_FILE(for_ctrl, input, node); fclose(input); } }
- return body; + return return_code; }
-static CMD_NODE *for_control_execute_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR *from_dir, size_t ref_len, CMD_NODE *cmdList) +static RETURN_CODE for_control_execute_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR *from_dir, size_t ref_len, CMD_NODE *node) { - CMD_NODE *body = NULL; + RETURN_CODE return_code = NO_ERROR; size_t len; WCHAR set[MAXSTRING]; WCHAR buffer[MAX_PATH]; @@ -3481,7 +3479,6 @@ static CMD_NODE *for_control_execute_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR if (from_dir) { len = wcslen(from_dir) + 1; - if (len >= ARRAY_SIZE(buffer)) return NULL; wcscpy(buffer, from_dir); wcscat(buffer, L"\"); } @@ -3524,26 +3521,25 @@ static CMD_NODE *for_control_execute_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR
if (insert_pos + wcslen(fd.cFileName) + 1 >= ARRAY_SIZE(buffer)) continue; wcscpy(&buffer[insert_pos], fd.cFileName); - body = cmdList; WCMD_set_for_loop_variable(for_ctrl->variable_index, buffer); - body = node_execute(cmdList) == NO_ERROR ? cmdList : NULL; + return_code = node_execute(node); } while (FindNextFileW(hff, &fd) != 0); FindClose(hff); } else { WCMD_set_for_loop_variable(for_ctrl->variable_index, buffer); - body = node_execute(cmdList) == NO_ERROR ? cmdList : NULL; + return_code = node_execute(node); } } - return body; + return return_code; }
-static CMD_NODE *for_control_execute_walk_files(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *cmdList) +static RETURN_CODE for_control_execute_walk_files(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node) { DIRECTORY_STACK *dirs_to_walk; size_t ref_len; - CMD_NODE *body = NULL; + RETURN_CODE return_code = NO_ERROR;
if (for_ctrl->root_dir) { @@ -3562,19 +3558,19 @@ static CMD_NODE *for_control_execute_walk_files(CMD_FOR_CONTROL *for_ctrl, CMD_N if (for_ctrl->flags & CMD_FOR_FLAG_TREE_RECURSE) WCMD_add_dirstowalk(dirs_to_walk);
- body = for_control_execute_set(for_ctrl, dirs_to_walk->dirName, ref_len, cmdList); + return_code = for_control_execute_set(for_ctrl, dirs_to_walk->dirName, ref_len, node); /* If we are walking directories, move on to any which remain */ dirs_to_walk = WCMD_dir_stack_free(dirs_to_walk); } TRACE("Finished all directories.\n");
- return body; + return return_code; }
-static CMD_NODE *for_control_execute_numbers(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *cmdList) +static RETURN_CODE for_control_execute_numbers(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node) { + RETURN_CODE return_code = NO_ERROR; WCHAR set[MAXSTRING]; - CMD_NODE *body = NULL; int numbers[3] = {0, 0, 0}, var; int i;
@@ -3603,14 +3599,14 @@ static CMD_NODE *for_control_execute_numbers(CMD_FOR_CONTROL *for_ctrl, CMD_NODE swprintf(tmp, ARRAY_SIZE(tmp), L"%d", var); WCMD_set_for_loop_variable(for_ctrl->variable_index, tmp); TRACE("Processing FOR number %s\n", wine_dbgstr_w(tmp)); - body = node_execute(cmdList) == NO_ERROR ? cmdList : NULL; + return_code = node_execute(node); } - return body; + return return_code; }
-static CMD_NODE *for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node) +static RETURN_CODE for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node) { - CMD_NODE *last; + RETURN_CODE return_code;
if (!for_ctrl->set) return NO_ERROR;
@@ -3620,22 +3616,22 @@ static CMD_NODE *for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node) { case CMD_FOR_FILETREE: if (for_ctrl->flags & CMD_FOR_FLAG_TREE_RECURSE) - last = for_control_execute_walk_files(for_ctrl, node); + return_code = for_control_execute_walk_files(for_ctrl, node); else - last = for_control_execute_set(for_ctrl, NULL, 0, node); + return_code = for_control_execute_set(for_ctrl, NULL, 0, node); break; case CMD_FOR_FILE_SET: - last = for_control_execute_fileset(for_ctrl, node); + return_code = for_control_execute_fileset(for_ctrl, node); break; case CMD_FOR_NUMBERS: - last = for_control_execute_numbers(for_ctrl, node); + return_code = for_control_execute_numbers(for_ctrl, node); break; default: - last = NULL; + return_code = NO_ERROR; break; } WCMD_restore_for_loop_context(); - return last; + return return_code; }
RETURN_CODE node_execute(CMD_NODE *node) @@ -3723,7 +3719,7 @@ RETURN_CODE node_execute(CMD_NODE *node) return_code = ERROR_INVALID_FUNCTION; break; case CMD_FOR: - return_code = for_control_execute(&node->for_ctrl, node->do_block) ? NO_ERROR : ERROR_INVALID_FUNCTION; + return_code = for_control_execute(&node->for_ctrl, node->do_block); break; default: FIXME("Unexpected operator %u\n", node->op);