This part XII of cmd engine rewrite.
This serie implements proper support for && and || command chaining operators: - implement proper precedence parsing - implement semantics for the operators - for this to work, we need to have expected success/failure from commands; this is supported here for external commands (and calling into a label or another BAT/CMD file), but support has to be added for all builtin commands - that's a big mess as behaviors is far from homogeneous among the builtins: + some builtin set coherent success/failure vs errorlevel information + some report success/failure, but only set errorlevel upon failure (keeping errorlevel from previous commands untouched), + some even report success (even in case of failure) yet set errorlevel in case of failure, - so the next series will now concentrate on testing success/failure and errorlevel for every builtin command (sigh) - for now, to help the transition phase, we can distinguish if a builtin properly reports success/failure; if a builtin with success/failure ability is used in a chaining operator, we implement the intented behavior of the chaining operator; otherwise, we keep old behavior (ie always execute LHS and RHS whatever the success/failure status). This shall be removed when all builtin commands report success/failure.
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 69 ++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 22 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 7d8a0ce307f..9fc78119f3c 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -2240,6 +2240,19 @@ static const char* debugstr_token(enum builder_token tkn, union token_parameter } }
+static unsigned token_get_precedence(enum builder_token tkn) +{ + switch (tkn) + { + case TKN_EOL: return 5; + case TKN_BAR: return 4; + case TKN_AMPAMP: return 3; + case TKN_BARBAR: return 2; + case TKN_AMP: return 1; + default: return 0; + } +} + static void node_builder_init(struct node_builder *builder) { memset(builder, 0, sizeof(*builder)); @@ -2315,7 +2328,7 @@ static void redirection_list_append(CMD_REDIRECTION **redir, CMD_REDIRECTION *la } }
-static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) +static BOOL node_builder_parse(struct node_builder *builder, unsigned precedence, CMD_NODE **result) { CMD_REDIRECTION *redir = NULL; unsigned bogus_line; @@ -2346,14 +2359,14 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) /* 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)); + ERROR_IF(!node_builder_parse(builder, 0, &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)); + ERROR_IF(!node_builder_parse(builder, 0, &right)); left = node_create_binary(CMD_CONCAT, left, right); } node_builder_consume(builder); @@ -2381,32 +2394,44 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) break; case TKN_AMP: ERROR_IF(!left); - node_builder_consume(builder); - if (node_builder_peek_next_token(builder, &pmt) == TKN_CLOSEPAR) + if (!(done = token_get_precedence(tkn) <= precedence)) { - done = TRUE; - break; + node_builder_consume(builder); + if (node_builder_peek_next_token(builder, &pmt) == TKN_CLOSEPAR) + { + done = TRUE; + break; + } + ERROR_IF(!node_builder_parse(builder, token_get_precedence(tkn), &right)); + left = node_create_binary(CMD_CONCAT, left, right); } - 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); + if (!(done = token_get_precedence(tkn) <= precedence)) + { + node_builder_consume(builder); + ERROR_IF(!node_builder_parse(builder, token_get_precedence(tkn), &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); + if (!(done = token_get_precedence(tkn) <= precedence)) + { + node_builder_consume(builder); + ERROR_IF(!node_builder_parse(builder, token_get_precedence(tkn), &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); + if (!(done = token_get_precedence(tkn) <= precedence)) + { + node_builder_consume(builder); + ERROR_IF(!node_builder_parse(builder, token_get_precedence(tkn), &right)); + left = node_create_binary(CMD_ONFAILURE, left, right); + } break; case TKN_COMMAND: ERROR_IF(left); @@ -2437,7 +2462,7 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) 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 (!node_builder_parse(builder, 0, &then_block)) { if_condition_dispose(&cond); ERROR_IF(TRUE); @@ -2446,7 +2471,7 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) if (tkn == TKN_ELSE) { node_builder_consume(builder); - if (!node_builder_parse(builder, &else_block)) + if (!node_builder_parse(builder, 0, &else_block)) { if_condition_dispose(&cond); node_dispose_tree(then_block); @@ -2499,7 +2524,7 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) node_builder_consume(builder); } while (tkn != TKN_CLOSEPAR); if (!node_builder_expect_token(builder, TKN_DO) || - !node_builder_parse(builder, &do_block)) + !node_builder_parse(builder, 0, &do_block)) { for_control_dispose(for_ctrl); ERROR_IF(TRUE); @@ -2538,7 +2563,7 @@ static BOOL node_builder_generate(struct node_builder *builder, CMD_NODE **node) } else { - if (node_builder_parse(builder, node) && + if (node_builder_parse(builder, 0, node) && builder->pos + 1 >= builder->num) /* consumed all tokens? */ return TRUE; /* print error on first unused token */
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd | 23 ++++++++++++++++------- programs/cmd/tests/test_builtins.cmd.exp | 19 +++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index e97d447ba26..d31cb3ef72a 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -451,14 +451,23 @@ if 1==0 (echo p1) else echo p2||echo p3 echo --- if 1==0 (echo q1) else echo q2&echo q3 echo ------------- Testing internal commands return codes -call :setError 0 &&echo SUCCESS||echo FAILURE %errorlevel% -call :setError 33 &&echo SUCCESS||echo FAILURE %errorlevel% -call :setError 666 -echo foo &&echo SUCCESS||echo FAILURE %errorlevel% -echo foo >> h:\i\dont\exist\at\all.txt &&echo SUCCESS||echo FAILURE %errorlevel% -type NUL &&echo SUCCESS||echo FAILURE %errorlevel% -type h:\i\dont\exist\at\all.txt &&echo SUCCESS||echo FAILURE %errorlevel% +setlocal EnableDelayedExpansion + +echo --- call and IF/FOR blocks +call :setError 0 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +call :setError 33 &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel! +call :setError 666 & (echo foo &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & (echo foo >> h:\i\dont\exist\at\all.txt &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((if 1==1 echo "">NUL) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((if 1==0 echo "">NUL) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((if 1==1 (call :setError 33)) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((if 1==0 (call :setError 33) else call :setError 34) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((for %%i in () do echo "") &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((for %%i in () do call :setError 33) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((for %%i in (a) do call :setError 0) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) +call :setError 666 & ((for %%i in (a) do call :setError 33) &&echo SUCCESS !errorlevel!||echo FAILURE !errorlevel!) echo --- +setlocal DisableDelayedExpansion echo ------------ Testing 'set' ------------ call :setError 0 rem Remove any WINE_FOO* WINE_BA* environment variables from shell before proceeding diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index b4d85161fbb..1799bc795fc 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -450,13 +450,20 @@ p2 q2 q3 ------------- Testing internal commands return codes -SUCCESS -FAILURE 0 +--- call and IF/FOR blocks +SUCCESS 0 +@todo_wine@FAILURE 33 @todo_wine@foo@space@ -@todo_wine@SUCCESS -@todo_wine@FAILURE 666 -SUCCESS -@todo_wine@FAILURE 0 +@todo_wine@SUCCESS 666 +@todo_wine@FAILURE 1 +SUCCESS 666 +@todo_wine@SUCCESS 666 +@todo_wine@FAILURE 33 +@todo_wine@FAILURE 34 +SUCCESS 666 +@todo_wine@SUCCESS 666 +@todo_wine@SUCCESS 0 +@todo_wine@FAILURE 33 @todo_wine@--- ------------ Testing 'set' ------------ 1
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 7 ++++--- programs/cmd/wcmd.h | 2 +- programs/cmd/wcmdmain.c | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 5a53af28ebe..708e3c6dce6 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1506,7 +1506,7 @@ static WCHAR *WCMD_strtrim(const WCHAR *s) * in DOS (try typing "ECHO ON AGAIN" for an example). */
-void WCMD_echo (const WCHAR *args) +RETURN_CODE WCMD_echo(const WCHAR *args) { int count; const WCHAR *origcommand = args; @@ -1517,7 +1517,7 @@ void WCMD_echo (const WCHAR *args) args++;
trimmed = WCMD_strtrim(args); - if (!trimmed) return; + if (!trimmed) return NO_ERROR;
count = lstrlenW(trimmed); if (count == 0 && origcommand[0]!='.' && origcommand[0]!=':' @@ -1525,7 +1525,7 @@ void WCMD_echo (const WCHAR *args) if (echo_mode) WCMD_output(WCMD_LoadMessage(WCMD_ECHOPROMPT), L"ON"); else WCMD_output (WCMD_LoadMessage(WCMD_ECHOPROMPT), L"OFF"); free(trimmed); - return; + return NO_ERROR; }
if (lstrcmpiW(trimmed, L"ON") == 0) @@ -1537,6 +1537,7 @@ void WCMD_echo (const WCHAR *args) WCMD_output_asis(L"\r\n"); } free(trimmed); + return NO_ERROR; }
/***************************************************************************** diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index da4eaca21af..7f4bf69979a 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -168,7 +168,7 @@ void WCMD_copy (WCHAR *); void WCMD_create_dir (WCHAR *); BOOL WCMD_delete (WCHAR *); void WCMD_directory (WCHAR *); -void WCMD_echo (const WCHAR *); +RETURN_CODE WCMD_echo(const WCHAR *); void WCMD_endlocal (void); void WCMD_enter_paged_mode(const WCHAR *); RETURN_CODE WCMD_exit(void); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 9fc78119f3c..cbea093f400 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1834,7 +1834,7 @@ static RETURN_CODE execute_single_command(const WCHAR *command) WCMD_directory (parms_start); break; case WCMD_ECHO: - WCMD_echo(&whichcmd[count]); + return_code = WCMD_echo(&whichcmd[count]); break; case WCMD_GOTO: return_code = WCMD_goto();
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 3 ++- programs/cmd/wcmd.h | 2 +- programs/cmd/wcmdmain.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 914d9d7f3e3..37db7fb2b04 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -645,7 +645,7 @@ extern void WCMD_expand(const WCHAR *, WCHAR *); * If there is a leading ':', calls within this batch program * otherwise launches another program. */ -void WCMD_call(WCHAR *command) +RETURN_CODE WCMD_call(WCHAR *command) { WCHAR buffer[MAXSTRING]; WCMD_expand(command, buffer); @@ -680,4 +680,5 @@ void WCMD_call(WCHAR *command) WCMD_restore_for_loop_context(); } else WCMD_output_asis_stderr(WCMD_LoadMessage(WCMD_CALLINSCRIPT)); + return errorlevel ? ERROR_INVALID_FUNCTION : NO_ERROR; } diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 7f4bf69979a..8dfc89534d6 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -159,7 +159,7 @@ typedef int RETURN_CODE;
void WCMD_assoc (const WCHAR *, BOOL); void WCMD_batch(WCHAR *, WCHAR *, WCHAR *, HANDLE); -void WCMD_call (WCHAR *command); +RETURN_CODE WCMD_call(WCHAR *command); void WCMD_change_tty (void); void WCMD_choice (const WCHAR *); void WCMD_clear_screen (void); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index cbea093f400..860ca872ffc 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1808,7 +1808,7 @@ static RETURN_CODE execute_single_command(const WCHAR *command) switch (cmd_index) {
case WCMD_CALL: - WCMD_call (parms_start); + return_code = WCMD_call(parms_start); break; case WCMD_CD: case WCMD_CHDIR:
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd.exp | 106 +++++++++++------------ programs/cmd/wcmd.h | 2 + programs/cmd/wcmdmain.c | 42 ++++++++- 3 files changed, 93 insertions(+), 57 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 1799bc795fc..c2dba03b740 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -262,7 +262,7 @@ a2 b1 b2 c1 -@todo_wine@--- +--- d1 d2 d3 @@ -271,7 +271,7 @@ e2 e3 f1 f2 -@todo_wine@--- +--- g1 g2 g3 @@ -280,19 +280,19 @@ h2 h3 i1 i2 -@todo_wine@--- +--- j1 -@todo_wine@j3 -@todo_wine@--- +j3 +--- k1 -@todo_wine@--- +--- l1 -@todo_wine@--- +--- --- chain failure a1 a2 b1 -@todo_wine@--- +--- c1 c2 d1 @@ -300,24 +300,24 @@ d2 d3 e1 e2 -@todo_wine@--- +--- f1 f2 f3 g1 -@todo_wine@g3 -@todo_wine@--- +g3 +--- h1 -@todo_wine@--- +--- i1 -@todo_wine@i3 -@todo_wine@--- +i3 +--- j1 j2 j3 k1 k2 -@todo_wine@--- +--- l1 l2 l3 @@ -330,7 +330,7 @@ b2 b3 c1 c2 -@todo_wine@--- +--- d1 d2 d3 @@ -339,34 +339,34 @@ e2 e3 f1 f2 -@todo_wine@--- +--- g1 -@todo_wine@--- +--- h1 -@todo_wine@--- +--- i1 -@todo_wine@--- +--- j1 j2 j3 k1 k2 -@todo_wine@--- +--- l1 l2 l3 m1 -@todo_wine@--- +--- n1 -@todo_wine@--- +--- o1 -@todo_wine@--- +--- p1 p2 p3 q1 q2 -@todo_wine@--- +--- r1 r2 r3 @@ -378,13 +378,13 @@ b2 c1 c3 d1 -@todo_wine@--- +--- e1 e3 f2 f3 g2 -@todo_wine@--- +--- h2 h3 i3 @@ -398,44 +398,44 @@ f4:[f3:[f2:[f1,f2],f3],f4]@or_broken@f4:[f3:[f2:,f3],f4]@or_broken@f4:[f3:,f4] --- chain else a1 b2 -@todo_wine@--- +--- c3 -@todo_wine@--- +--- d3 -@todo_wine@--- -@todo_wine@--- -@todo_wine@--- -@todo_wine@--- -@todo_wine@--- +--- +--- +--- +--- +--- --- chain else (if true) a1 else echo a2 b2 else echo b3 c1 c2 else echo c3 d1 -@todo_wine@--- +--- e1 e2 else echo e3 f3 g1 else echo g2 g3 h1 else echo h2 -@todo_wine@--- +--- i1 else echo i2 i3 @todo_wine@j2@space@ -@todo_wine@--- +--- k1 k2 l1 -@todo_wine@--- +--- m1 m2 n1 o1 p1 q1 -@todo_wine@--- +--- --- chain else (if false) j3 --- @@ -446,25 +446,25 @@ n3 o2 o3 p2 -@todo_wine@--- +--- q2 q3 ------------- Testing internal commands return codes --- call and IF/FOR blocks SUCCESS 0 -@todo_wine@FAILURE 33 -@todo_wine@foo@space@ -@todo_wine@SUCCESS 666 -@todo_wine@FAILURE 1 +FAILURE 33 +foo@space@ SUCCESS 666 -@todo_wine@SUCCESS 666 -@todo_wine@FAILURE 33 -@todo_wine@FAILURE 34 +FAILURE 1 SUCCESS 666 -@todo_wine@SUCCESS 666 -@todo_wine@SUCCESS 0 -@todo_wine@FAILURE 33 -@todo_wine@--- +SUCCESS 666 +FAILURE 33 +FAILURE 34 +SUCCESS 666 +SUCCESS 666 +SUCCESS 0 +FAILURE 33 +--- ------------ Testing 'set' ------------ 1 0 @@ -654,13 +654,13 @@ bar2@space@ foo2 foobar deleted --- on success conditional and -@todo_wine@foo3 not created +foo3 not created bar4@space@ foo4 --- on failure conditional or foo5 foo6@space@ -@todo_wine@------------ Testing cd ------------ +------------ Testing cd ------------ singleFile Current dir: @drive@@path@foobar@or_broken@Current dir:@space@ @drive@@path@foobar diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 8dfc89534d6..9f8bdd46044 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -156,6 +156,8 @@ typedef int RETURN_CODE; #define RETURN_CODE_SYNTAX_ERROR 255 #define RETURN_CODE_CANT_LAUNCH 9009 #define RETURN_CODE_ABORTED (-999999) +/* temporary to detect builtin commands not migrated to handle return code */ +#define RETURN_CODE_OLD_CHAINING (-999998)
void WCMD_assoc (const WCHAR *, BOOL); void WCMD_batch(WCHAR *, WCHAR *, WCHAR *, HANDLE); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 860ca872ffc..17702f4f277 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1734,7 +1734,7 @@ static BOOL set_std_redirections(CMD_REDIRECTION *redir) */ static RETURN_CODE execute_single_command(const WCHAR *command) { - RETURN_CODE return_code = NO_ERROR; + RETURN_CODE return_code; WCHAR *cmd, *parms_start; int status, cmd_index, count; WCHAR *whichcmd; @@ -1791,6 +1791,7 @@ static RETURN_CODE execute_single_command(const WCHAR *command) WINE_TRACE("Got directory %s as %s\n", wine_dbgstr_w(envvar), wine_dbgstr_w(cmd)); status = SetCurrentDirectoryW(cmd); if (!status) WCMD_print_error (); + return_code = ERROR_INVALID_FUNCTION; goto cleanup; }
@@ -1805,6 +1806,7 @@ static RETURN_CODE execute_single_command(const WCHAR *command)
}
+ return_code = RETURN_CODE_OLD_CHAINING; switch (cmd_index) {
case WCMD_CALL: @@ -1936,6 +1938,7 @@ static RETURN_CODE execute_single_command(const WCHAR *command) default: prev_echo_mode = echo_mode; WCMD_run_program (whichcmd, FALSE); + return_code = errorlevel; echo_mode = prev_echo_mode; }
@@ -3659,6 +3662,16 @@ static RETURN_CODE for_control_execute(CMD_FOR_CONTROL *for_ctrl, CMD_NODE *node return return_code; }
+static RETURN_CODE temp_fixup_return_code(CMD_NODE *node, RETURN_CODE return_code, RETURN_CODE fallback_return_code) +{ + if (return_code == RETURN_CODE_OLD_CHAINING) + { + FIXME("Not migrated (%ls) used in chaining\n", node->op == CMD_SINGLE ? node->command->command : L"Too complex"); + return_code = fallback_return_code; + } + return return_code; +} + RETURN_CODE node_execute(CMD_NODE *node) { HANDLE old_stdhandles[3] = {GetStdHandle (STD_INPUT_HANDLE), @@ -3674,7 +3687,7 @@ RETURN_CODE node_execute(CMD_NODE *node) { WCMD_print_error(); /* FIXME potentially leaking here (if first redir created ok, and second failed */ - return ERROR_INVALID_FUNCTION; + return errorlevel = ERROR_INVALID_FUNCTION; } switch (node->op) { @@ -3684,11 +3697,27 @@ RETURN_CODE node_execute(CMD_NODE *node) else return_code = NO_ERROR; break; case CMD_CONCAT: + return_code = node_execute(node->left); + if (return_code != RETURN_CODE_ABORTED) + return_code = node_execute(node->right); + break; case CMD_ONSUCCESS: + return_code = node_execute(node->left); + return_code = temp_fixup_return_code(node->left, return_code, NO_ERROR); + if (return_code == NO_ERROR) + { + return_code = node_execute(node->right); + temp_fixup_return_code(node->right, return_code, 0 /* not used */); + } + break; case CMD_ONFAILURE: return_code = node_execute(node->left); - if (return_code != RETURN_CODE_ABORTED) + return_code = temp_fixup_return_code(node->left, return_code, ERROR_INVALID_FUNCTION); + if (return_code != NO_ERROR) + { return_code = node_execute(node->right); + temp_fixup_return_code(node->right, return_code, 0 /* not used */); + } break; case CMD_PIPE: { @@ -3714,11 +3743,13 @@ RETURN_CODE node_execute(CMD_NODE *node) output = redirection_create_file(REDIR_WRITE_TO, 1, filename); if (set_std_redirections(output)) { - return_code = node_execute(node->left); + RETURN_CODE return_code_left = node_execute(node->left); + return_code = NO_ERROR;
CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); SetStdHandle(STD_OUTPUT_HANDLE, old_stdhandles[1]);
+ return_code = temp_fixup_return_code(node->left, return_code, NO_ERROR); if (return_code == NO_ERROR) { HANDLE h = CreateFileW(filename, GENERIC_READ, @@ -3728,10 +3759,13 @@ RETURN_CODE node_execute(CMD_NODE *node) { SetStdHandle(STD_INPUT_HANDLE, h); return_code = node_execute(node->right); + temp_fixup_return_code(node->right, return_code, 0 /* not used */); } else return_code = ERROR_INVALID_FUNCTION; } DeleteFileW(filename); + if (return_code_left != NO_ERROR || return_code != NO_ERROR) + errorlevel = ERROR_INVALID_FUNCTION; } else return_code = ERROR_INVALID_FUNCTION; redirection_dispose_list(output);