From: Qian Hong Subject: [PATCH 2/2] cmd: Add support for || and &&. Message-Id: <5706BF48.1090805@codeweavers.com> Date: Fri, 08 Apr 2016 04:12:56 +0800 Slightly improved based on Mikhail Bystryantsev's patch, add support for those built-in commands which does not change %ERRORLEVEL%. Signed-off-by: Qian Hong --- programs/cmd/builtins.c | 14 +++++++++++--- programs/cmd/tests/test_builtins.cmd | 13 +++++++++++++ programs/cmd/tests/test_builtins.cmd.exp | 13 ++++++++++++- programs/cmd/tests/test_cmdline.cmd | 20 ++++++++++++++++++++ programs/cmd/tests/test_cmdline.cmd.exp | 22 ++++++++++++++++++++++ programs/cmd/wcmd.h | 1 + programs/cmd/wcmdmain.c | 26 ++++++++++++++++++++++++-- 7 files changed, 103 insertions(+), 6 deletions(-) diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index deaa831..7884929 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1502,6 +1502,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, { CMD_LIST *curPosition = *cmdList; int myDepth = (*cmdList)->bracketDepth; + BOOL skipAppended = FALSE; WINE_TRACE("cmdList(%p), firstCmd(%s), doIt(%d)\n", cmdList, wine_dbgstr_w(firstcmd), executecmds); @@ -1536,10 +1537,17 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, (*cmdList)->bracketDepth, myDepth); /* Execute any statements appended to the line */ - /* FIXME: Only if previous call worked for && or failed for || */ if ((*cmdList)->prevDelim == CMD_ONFAILURE || (*cmdList)->prevDelim == CMD_ONSUCCESS) { - if (processThese && (*cmdList)->command) { + + if (!skipAppended && + (((*cmdList)->prevDelim == CMD_ONFAILURE && (!get_errorlevel() || !is_errorlevel_changed())) || + ((*cmdList)->prevDelim == CMD_ONSUCCESS && (get_errorlevel() && is_errorlevel_changed())))) { + WINE_TRACE("Prev delim %d errorlevel %d changed %d, skipping appended statements\n", (*cmdList)->prevDelim, get_errorlevel(), is_errorlevel_changed()); + skipAppended = TRUE; + } + + if (!skipAppended && processThese && (*cmdList)->command) { WCMD_execute ((*cmdList)->command, (*cmdList)->redirects, cmdList, FALSE); } @@ -1563,7 +1571,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, processThese = !executecmds; /* Process the ELSE part */ - if (processThese) { + if (!skipAppended && processThese) { const int keyw_len = sizeof(ifElse)/sizeof(ifElse[0]) + 1; WCHAR *cmd = ((*cmdList)->command) + keyw_len; diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index eac5dca..a9d9939 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -202,6 +202,19 @@ type C (if 1==0 (echo A > B) else echo C) cd .. & rd /s/q foobar +echo --------- Testing ONSUCCESS and ONFAILURE delimeters -------------- +if 1 == 1 (echo 1) || echo 2 +echo -- +if 1 == 1 (echo 1) && echo 2 +echo -- +if 1 == 1 (cmd /c exit 0) && echo 1 +echo -- +if 1 == 1 (cmd /c exit 1) && echo 1 +echo -- +if 1 == 1 (cmd /c exit 0) || echo 1 +echo -- +if 1 == 1 (cmd /c exit 1) || echo 1 + echo ------------ Testing circumflex escape character ------------ rem Using something like "echo foo^" asks for an additional char after a "More?" prompt on the following line; it's not possible to currently test that non-interactively echo ^hell^o, world diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 82b13f7..a99211a 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -215,6 +215,17 @@ foo A B C +--------- Testing ONSUCCESS and ONFAILURE delimeters -------------- +1 +-- +1 +2 +-- +1 +-- +-- +-- +1 ------------ Testing circumflex escape character ------------ hello, world hello, world @@ -400,7 +411,7 @@ bar2@space@ foo2 foobar deleted --- on success conditional and -@todo_wine@foo3 not created +foo3 not created bar4@space@ foo4 --- on failure conditional or diff --git a/programs/cmd/tests/test_cmdline.cmd b/programs/cmd/tests/test_cmdline.cmd index 32a1ef2..6968f53 100644 --- a/programs/cmd/tests/test_cmdline.cmd +++ b/programs/cmd/tests/test_cmdline.cmd @@ -266,6 +266,26 @@ call tell 1 2 call tell(1234) call tell(12(34) call tell(12;34) +echo --------- Testing ONSUCCESS and ONFAILURE delimeters -------------- +echo 1&&echo 2 +echo -- +echo 1||echo 2 +echo -- +echo 1||echo 2&&echo 3 +echo -- +echo 1&&echo 2||echo 3 +echo -- +echo 1&&echo 2&&echo 3 +echo -- +echo 1||echo 2||echo 3 +echo -- +cmd /c exit 0 && echo 1 +echo -- +cmd /c exit 1 && echo 1 +echo -- +cmd /c exit 0 || echo 1 +echo -- +cmd /c exit 1 || echo 1 echo --------- Finished -------------- del tell.bat say*.* bazbaz*.bat exit diff --git a/programs/cmd/tests/test_cmdline.cmd.exp b/programs/cmd/tests/test_cmdline.cmd.exp index 980f674..a300a49 100644 --- a/programs/cmd/tests/test_cmdline.cmd.exp +++ b/programs/cmd/tests/test_cmdline.cmd.exp @@ -130,4 +130,26 @@ THIS FAILS: cmd ignoreme/c say one 0:tell,1:(1234),2:,All:'(1234)' 0:tell,1:(12(34),2:,All:'(12(34)' 0:tell,1:(12,2:34),All:'(12;34)' +--------- Testing ONSUCCESS and ONFAILURE delimeters -------------- +1 +2 +-- +1 +-- +1 +-- +1 +2 +-- +1 +2 +3 +-- +1 +-- +1 +-- +-- +-- +1 --------- Finished -------------- diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 5019f9e..faa3102 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -206,6 +206,7 @@ typedef struct _FOR_CONTEXT { extern WCHAR quals[MAX_PATH], param1[MAXSTRING], param2[MAXSTRING]; extern DWORD get_errorlevel(void); extern DWORD set_errorlevel(DWORD); +extern BOOL is_errorlevel_changed(void); extern BATCH_CONTEXT *context; extern FOR_CONTEXT forloopcontext; extern BOOL delayedsubst; diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index feed9ca..4f47627 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -65,8 +65,14 @@ static int max_width; static int numChars; struct errorlevel { + BOOL changed; DWORD code; -} errorlevel = {0}; +} errorlevel = {FALSE, 0}; + +void begin_errorlevel(void) +{ + errorlevel.changed = FALSE; +} DWORD get_errorlevel(void) { @@ -75,9 +81,15 @@ DWORD get_errorlevel(void) DWORD set_errorlevel(DWORD code) { + errorlevel.changed = TRUE; return (errorlevel.code = code); } +BOOL is_errorlevel_changed(void) +{ + return errorlevel.changed; +} + #define MAX_WRITECONSOLE_SIZE 65535 /* @@ -1296,6 +1308,8 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, WINE_TRACE("command on entry:%s (%p)\n", wine_dbgstr_w(command), cmdList); + begin_errorlevel(); + /* 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. @@ -2285,6 +2299,7 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, BOOL retrycall) { int bdepth = -1; + BOOL skipAppended = FALSE; if (thisCmd && oneBracket) bdepth = thisCmd->bracketDepth; @@ -2302,10 +2317,17 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, return thisCmd->nextcommand; } + if (!skipAppended && + ((thisCmd->prevDelim == CMD_ONFAILURE && (!get_errorlevel() || !is_errorlevel_changed())) || + (thisCmd->prevDelim == CMD_ONSUCCESS && (get_errorlevel() && is_errorlevel_changed())))) { + WINE_TRACE("Prev delim %d errorlevel %d changed %d, skip following commands\n", thisCmd->prevDelim, get_errorlevel(), is_errorlevel_changed()); + skipAppended = TRUE; + } + /* 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 (thisCmd->command && thisCmd->command[0] != ':') { + if (!skipAppended && thisCmd->command && thisCmd->command[0] != ':') { WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(thisCmd->command)); WCMD_execute (thisCmd->command, thisCmd->redirects, &thisCmd, retrycall); }