[v3] cmd: Added support for "&&" and "||" condition checking.

Alex Coffin alexcoffin1999 at gmail.com
Sun Nov 5 19:35:08 CST 2017


Fixes: https://bugs.winehq.org/show_bug.cgi?id=32679

This version (v3) is now completely c89 compliant. The previous
had a one line issue that was not.

Before "&&" and "||" were supported, but they also didn't check if past
commands had failed. This commit removes direct access to errorlevel.
Now errorlevel can only be accessed using getters and setters, but the
it seemed like the most efficient solution. Also please note I added one
TODO task. Currently this commit doesn't replicate a certain windows
bug with setting errorlevel to 1 in some cases. See test_builtins.cmd
for more details.

I would have checked the success/failure of commands that don't set the
errorlevel if that was possible, but Winows doesn't do that. It doesn't
care about the most recent statement, it only cares about the
errorlevel if it changes, otherwise it assumes success (if the
errorlevel doesn't change).

Also tests in test_builtins.cmd were added in order to validate these
changes.

Finally I think I changed some random tabs to spaces that were making it
had to read the "break;"s. Hopefully that doesn't cause commit issues.

Tested on Linux Mint, and a Windows 10 Virtual Machine (Also Wine
TestBot, not sure if I list that too)

Signed-off-by: Alexander Coffin <alexcoffin1999 at gmail.com>
---
 programs/cmd/batch.c                     |   2 +-
 programs/cmd/builtins.c                  | 105 +++++++++++++++----------------
 programs/cmd/directory.c                 |  20 +++---
 programs/cmd/tests/test_builtins.cmd     |  70 +++++++++++++++++++--
 programs/cmd/tests/test_builtins.cmd.exp |  38 +++++++++--
 programs/cmd/wcmd.h                      |  14 ++++-
 programs/cmd/wcmdmain.c                  |  67 ++++++++++++++++----
 7 files changed, 227 insertions(+), 89 deletions(-)

diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index 1a78b55..c6ed196 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -697,7 +697,7 @@ void WCMD_call (WCHAR *command) {
   if (*command != ':') {
     WCMD_run_program(command, TRUE);
     /* If the thing we try to run does not exist, call returns 1 */
-    if (errorlevel) errorlevel=1;
+    if (get_errorlevel()) set_errorlevel(1);
   } else {

     WCHAR gotoLabel[MAX_PATH];
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 1da1f67..3373025 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -285,7 +285,7 @@ void WCMD_choice (const WCHAR * args) {
     BOOL opt_s = FALSE;

     have_console = GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldmode);
-    errorlevel = 0;
+    set_errorlevel(0);

     my_command = heap_strdupW(WCMD_skip_leading_spaces((WCHAR*) args));

@@ -413,8 +413,8 @@ void WCMD_choice (const WCHAR * args) {
             if (have_console)
                 SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldmode);

-            errorlevel = (ptr - opt_c) + 1;
-            WINE_TRACE("answer: %d\n", errorlevel);
+            set_errorlevel((ptr - opt_c) + 1);
+            WINE_TRACE("answer: %d\n", get_errorlevel());
             heap_free(my_command);
             return;
         }
@@ -619,12 +619,12 @@ void WCMD_copy(WCHAR * args) {
   COPY_FILES *prevcopy      = NULL;

   /* Assume we were successful! */
-  errorlevel = 0;
+  set_errorlevel(0);

   /* If no args supplied at all, report an error */
   if (param1[0] == 0x00) {
     WCMD_output_stderr (WCMD_LoadMessage(WCMD_NOARG));
-    errorlevel = 1;
+    set_errorlevel(1);
     return;
   }

@@ -704,7 +704,7 @@ void WCMD_copy(WCHAR * args) {
     if (*thisparam=='+') {
       if (lastcopyentry == NULL) {
         WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
-        errorlevel = 1;
+        set_errorlevel(1);
         goto exitreturn;
       } else {
         concatnextfilename = TRUE;
@@ -759,7 +759,7 @@ void WCMD_copy(WCHAR * args) {
     } else {
       /* We have processed sources and destinations and still found
more to do - invalid */
       WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
-      errorlevel = 1;
+      set_errorlevel(1);
       goto exitreturn;
     }
     concatnextfilename    = FALSE;
@@ -776,7 +776,7 @@ void WCMD_copy(WCHAR * args) {
   /* Ensure we have at least one source file */
   if (!sourcelist) {
     WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
-    errorlevel = 1;
+    set_errorlevel(1);
     goto exitreturn;
   }

@@ -1024,7 +1024,7 @@ void WCMD_copy(WCHAR * args) {
             }
             if (!status) {
               WCMD_print_error ();
-              errorlevel = 1;
+              set_errorlevel(1);
             } else {
               WINE_TRACE("Copied successfully\n");
               if (anyconcats) writtenoneconcat = TRUE;
@@ -1036,7 +1036,7 @@ void WCMD_copy(WCHAR * args) {
               if (!destination->binarycopy && !anyconcats &&
!thiscopy->binarycopy) {
                 if (!WCMD_AppendEOF(outname)) {
                   WCMD_print_error ();
-                  errorlevel = 1;
+                  set_errorlevel(1);
                 }
               }
             }
@@ -1048,7 +1048,7 @@ void WCMD_copy(WCHAR * args) {
       /* Error if the first file was not found */
       if (!anyconcats || !writtenoneconcat) {
         WCMD_print_error ();
-        errorlevel = 1;
+        set_errorlevel(1);
       }
     }

@@ -1057,10 +1057,10 @@ void WCMD_copy(WCHAR * args) {
   }

   /* Append EOF if ascii destination and we were concatenating */
-  if (!errorlevel && !destination->binarycopy && anyconcats &&
writtenoneconcat) {
+  if (!get_errorlevel() && !destination->binarycopy && anyconcats &&
writtenoneconcat) {
     if (!WCMD_AppendEOF(destination->name)) {
       WCMD_print_error ();
-      errorlevel = 1;
+      set_errorlevel(1);
     }
   }

@@ -1143,7 +1143,7 @@ void WCMD_create_dir (WCHAR *args) {
         if (!argN) break;
         if (!create_full_path(thisArg)) {
             WCMD_print_error ();
-            errorlevel = 1;
+            set_errorlevel(1);
         }
     }
 }
@@ -1435,7 +1435,7 @@ BOOL WCMD_delete (WCHAR *args) {
     BOOL  argsProcessed = FALSE;
     BOOL  foundAny      = FALSE;

-    errorlevel = 0;
+    set_errorlevel(0);

     for (argno=0; ; argno++) {
         BOOL found;
@@ -1575,18 +1575,8 @@ static void WCMD_part_execute(CMD_LIST
**cmdList, const WCHAR *firstcmd,
                  (*cmdList)->prevDelim,
                  (*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) {
-          WCMD_execute ((*cmdList)->command, (*cmdList)->redirects,
-                        cmdList, FALSE);
-        }
-        if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
-
       /* Execute any appended to the statement with (...) */
-      } else if ((*cmdList)->bracketDepth > myDepth) {
+      if ((*cmdList)->bracketDepth > myDepth) {
         if (processThese) {
           *cmdList = WCMD_process_commands(*cmdList, TRUE, FALSE);
           WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
@@ -2002,7 +1992,7 @@ static void WCMD_parse_line(CMD_LIST    *cmdStart,
     forloopcontext.variable[varidx + varoffset] = heap_strdupW(parm);
   }

-  /* Execute the body of the foor loop with these values */
+  /* Execute the body of the for loop with these values */
   if (forloopcontext.variable[varidx] &&
forloopcontext.variable[varidx][0] != forf_eol) {
     CMD_LIST *thisCmdStart = cmdStart;
     *doExecuted = TRUE;
@@ -2383,7 +2373,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
             if (input == INVALID_HANDLE_VALUE) {
               WCMD_print_error ();
               WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), item);
-              errorlevel = 1;
+              set_errorlevel(1);
               return; /* FOR loop aborts at first failure here */

             } else {
@@ -2635,9 +2625,9 @@ void WCMD_pushd (const WCHAR *args)
     /* Change directory using CD code with /D parameter */
     strcpyW(quals, parmD);
     GetCurrentDirectoryW (1024, thisdir);
-    errorlevel = 0;
+    set_errorlevel(0);
     WCMD_setshow_default(args);
-    if (errorlevel) {
+    if (get_errorlevel()) {
       LocalFree(curdir);
       LocalFree(thisdir);
       return;
@@ -2799,7 +2789,7 @@ void WCMD_if (WCHAR *p, CMD_LIST **cmdList)
     WCHAR *endptr;
     long int param_int = strtolW(param, &endptr, 10);
     if (*endptr) goto syntax_err;
-    test = ((long int)errorlevel >= param_int);
+    test = ((long int)get_errorlevel() >= param_int);
     WCMD_parameter(p, 2+negate, &command, FALSE, FALSE);
   }
   else if (!lstrcmpiW (condition, existW)) {
@@ -2955,7 +2945,7 @@ void WCMD_move (void)
         if (ok) {
           if (!DeleteFileW(dest)) {
             WCMD_print_error ();
-            errorlevel = 1;
+            set_errorlevel(1);
             ok = FALSE;
           }
         }
@@ -2970,7 +2960,7 @@ void WCMD_move (void)

     if (!status) {
       WCMD_print_error ();
-      errorlevel = 1;
+      set_errorlevel(1);
     }
   } while (FindNextFileW(hff, &fd) != 0);

@@ -3088,12 +3078,12 @@ void WCMD_rename (void)
   WCHAR            fname[MAX_PATH];
   WCHAR            ext[MAX_PATH];

-  errorlevel = 0;
+  set_errorlevel(0);

   /* Must be at least two args */
   if (param1[0] == 0x00 || param2[0] == 0x00) {
     WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOARG));
-    errorlevel = 1;
+    set_errorlevel(1);
     return;
   }

@@ -3101,7 +3091,7 @@ void WCMD_rename (void)
   if ((strchrW(param2,':') != NULL) || (strchrW(param2,'\\') != NULL)) {
       SetLastError(ERROR_INVALID_PARAMETER);
       WCMD_print_error();
-      errorlevel = 1;
+      set_errorlevel(1);
       return;
   }

@@ -3164,7 +3154,7 @@ void WCMD_rename (void)

     if (!status) {
       WCMD_print_error ();
-      errorlevel = 1;
+      set_errorlevel(1);
     }
   } while (FindNextFileW(hff, &fd) != 0);

@@ -3411,7 +3401,7 @@ void WCMD_setshow_default (const WCHAR *args) {

     status = SetCurrentDirectoryW(string);
     if (!status) {
-      errorlevel = 1;
+      set_errorlevel(1);
       WCMD_print_error ();
       return;
     } else {
@@ -4168,7 +4158,7 @@ void WCMD_setshow_env (WCHAR *s) {
       env = GetEnvironmentStringsW();
       if (WCMD_setshow_sortenv( env, s ) == 0) {
         WCMD_output_stderr(WCMD_LoadMessage(WCMD_MISSINGENV), s);
-        errorlevel = 1;
+        set_errorlevel(1);
       }
       return;
     }
@@ -4180,9 +4170,9 @@ void WCMD_setshow_env (WCHAR *s) {
     status = SetEnvironmentVariableW(s, p);
     gle = GetLastError();
     if ((!status) & (gle == ERROR_ENVVAR_NOT_FOUND)) {
-      errorlevel = 1;
+      set_errorlevel(1);
     } else if (!status) WCMD_print_error();
-    else errorlevel = 0;
+    else set_errorlevel(0);
   }
 }

@@ -4418,8 +4408,13 @@ void WCMD_start(WCHAR *args)
     if (CreateProcessW( file, cmdline, NULL, NULL, TRUE, 0, NULL,
NULL, &st, &pi ))
     {
         WaitForSingleObject( pi.hProcess, INFINITE );
-        GetExitCodeProcess( pi.hProcess, &errorlevel );
-        if (errorlevel == STILL_ACTIVE) errorlevel = 0;
+        GetExitCodeProcess (pi.hProcess, &errorlevel);
+        if (errorlevel == STILL_ACTIVE) {
+          set_errorlevel(0);
+        } else {
+          /* This is just to set the errorlevel_changed to true */
+          set_errorlevel(errorlevel);
+        }
         CloseHandle(pi.hProcess);
         CloseHandle(pi.hThread);
     }
@@ -4427,7 +4422,7 @@ void WCMD_start(WCHAR *args)
     {
         SetLastError(ERROR_FILE_NOT_FOUND);
         WCMD_print_error ();
-        errorlevel = 9009;
+        set_errorlevel(9009);
     }
     heap_free(cmdline);
 }
@@ -4461,7 +4456,7 @@ void WCMD_type (WCHAR *args) {
   if (param2[0] != 0x00) writeHeaders = TRUE;

   /* Loop through all args */
-  errorlevel = 0;
+  set_errorlevel(0);
   while (argN) {
     WCHAR *thisArg = WCMD_parameter (args, argno++, &argN, FALSE, FALSE);

@@ -4477,7 +4472,7 @@ void WCMD_type (WCHAR *args) {
     if (h == INVALID_HANDLE_VALUE) {
       WCMD_print_error ();
       WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), thisArg);
-      errorlevel = 1;
+      set_errorlevel(1);
     } else {
       if (writeHeaders) {
         static const WCHAR fmt[] = {'\n','%','1','\n','\n','\0'};
@@ -4514,7 +4509,7 @@ void WCMD_more (WCHAR *args) {
   static const WCHAR conInW[]    = {'C','O','N','I','N','$','\0'};

   /* Prefix the NLS more with '-- ', then load the text */
-  errorlevel = 0;
+  set_errorlevel(0);
   strcpyW(moreStr, moreStart);
   LoadStringW(hinst, WCMD_MORESTR, &moreStr[3],
               (sizeof(moreStr)/sizeof(WCHAR))-3);
@@ -4579,7 +4574,7 @@ void WCMD_more (WCHAR *args) {
       if (h == INVALID_HANDLE_VALUE) {
         WCMD_print_error ();
         WCMD_output_stderr(WCMD_LoadMessage(WCMD_READFAIL), thisArg);
-        errorlevel = 1;
+        set_errorlevel(1);
       } else {
         ULONG64 curPos  = 0;
         ULONG64 fileLen = 0;
@@ -4727,7 +4722,7 @@ void WCMD_exit (CMD_LIST **cmdList) {
     int rc = atoiW(param1); /* Note: atoi of empty parameter is 0 */

     if (context && lstrcmpiW(quals, parmB) == 0) {
-        errorlevel = rc;
+        set_errorlevel(rc);
         context -> skip_rest = TRUE;
         *cmdList = NULL;
     } else {
@@ -4755,7 +4750,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) {

'O','p','e','n','\\','C','o','m','m','a','n','d','\0'};

     /* See if parameter includes '=' */
-    errorlevel = 0;
+    set_errorlevel(0);
     newValue = strchrW(args, '=');
     if (newValue) accessOptions |= KEY_WRITE;

@@ -4843,7 +4838,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) {
             LoadStringW(hinst, WCMD_NOFTYPE, msgbuffer,
sizeof(msgbuffer)/sizeof(WCHAR));
           }
           WCMD_output_stderr(msgbuffer, keyValue);
-          errorlevel = 2;
+          set_errorlevel(2);
         }

       /* Not a query - it's a set or clear of a value */
@@ -4868,7 +4863,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) {

           } else if (assoc && rc != ERROR_FILE_NOT_FOUND) {
             WCMD_print_error();
-            errorlevel = 2;
+            set_errorlevel(2);

           } else {
             WCHAR  msgbuffer[MAXSTRING];
@@ -4882,7 +4877,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) {
                           sizeof(msgbuffer)/sizeof(WCHAR));
             }
             WCMD_output_stderr(msgbuffer, keyValue);
-            errorlevel = 2;
+            set_errorlevel(2);
           }

         /* It really is a set value = contents */
@@ -4898,7 +4893,7 @@ void WCMD_assoc (const WCHAR *args, BOOL assoc) {

           if (rc != ERROR_SUCCESS) {
             WCMD_print_error();
-            errorlevel = 2;
+            set_errorlevel(2);
           } else {
             WCMD_output_asis(args);
             WCMD_output_asis(equalW);
@@ -4949,7 +4944,7 @@ void WCMD_color (void) {

       /* Fail if fg == bg color */
       if (((color & 0xF0) >> 4) == (color & 0x0F)) {
-        errorlevel = 1;
+        set_errorlevel(1);
         return;
       }

diff --git a/programs/cmd/directory.c b/programs/cmd/directory.c
index 91142be..ebcd6aa 100644
--- a/programs/cmd/directory.c
+++ b/programs/cmd/directory.c
@@ -302,7 +302,7 @@ static DIRECTORY_STACK *WCMD_list_directory
(DIRECTORY_STACK *inputparms, int le
         if (fd == NULL) {
           FindClose (hff);
           WINE_ERR("Out of memory\n");
-          errorlevel = 1;
+          set_errorlevel(1);
           return parms->next;
         }
       } while (FindNextFileW(hff, &fd[entry_count]) != 0);
@@ -543,7 +543,7 @@ static DIRECTORY_STACK *WCMD_list_directory
(DIRECTORY_STACK *inputparms, int le
   if ((file_total + dir_total == 0) && (level == 0)) {
     SetLastError (ERROR_FILE_NOT_FOUND);
     WCMD_print_error ();
-    errorlevel = 1;
+    set_errorlevel(1);
   }

   return parms;
@@ -564,7 +564,7 @@ static void WCMD_dir_trailer(WCHAR drive) {
     WINE_TRACE("Writing trailer for '%s' gave %d(%d)\n",
wine_dbgstr_w(driveName),
                status, GetLastError());

-    if (errorlevel==0 && !bare) {
+    if (get_errorlevel() == 0 && !bare) {
       if (recurse) {
         static const WCHAR fmt1[] = {'\n',' ',' ',' ',' ','
','T','o','t','a','l',' ','f','i','l','e','s',
                                      '
','l','i','s','t','e','d',':','\n','%','1','!','8','d','!','
','f','i','l','e',
@@ -609,7 +609,7 @@ void WCMD_directory (WCHAR *args)
   WCHAR ext[MAX_PATH];
   static const WCHAR dircmdW[] = {'D','I','R','C','M','D','\0'};

-  errorlevel = 0;
+  set_errorlevel(0);

   /* Prefill quals with (uppercased) DIRCMD env var */
   if (GetEnvironmentVariableW(dircmdW, string, sizeof(string)/sizeof(WCHAR))) {
@@ -696,7 +696,7 @@ void WCMD_directory (WCHAR *args)
               } else {
                 SetLastError(ERROR_INVALID_PARAMETER);
                 WCMD_print_error();
-                errorlevel = 1;
+                set_errorlevel(1);
                 return;
               }
               break;
@@ -716,7 +716,7 @@ void WCMD_directory (WCHAR *args)
                 default:
                     SetLastError(ERROR_INVALID_PARAMETER);
                     WCMD_print_error();
-                    errorlevel = 1;
+                    set_errorlevel(1);
                     return;
                 }
                 p++;
@@ -747,7 +747,7 @@ void WCMD_directory (WCHAR *args)
                 default:
                     SetLastError(ERROR_INVALID_PARAMETER);
                     WCMD_print_error();
-                    errorlevel = 1;
+                    set_errorlevel(1);
                     return;
                 }

@@ -766,7 +766,7 @@ void WCMD_directory (WCHAR *args)
     default:
               SetLastError(ERROR_INVALID_PARAMETER);
               WCMD_print_error();
-              errorlevel = 1;
+              set_errorlevel(1);
               return;
     }
     p = p + 1;
@@ -903,7 +903,7 @@ void WCMD_directory (WCHAR *args)
          status = WCMD_volume (0, drive);
          trailerReqd = TRUE;
          if (!status) {
-           errorlevel = 1;
+           set_errorlevel(1);
            goto exit;
          }
       }
@@ -913,7 +913,7 @@ void WCMD_directory (WCHAR *args)
     }

     /* Clear any errors from previous invocations, and process it */
-    errorlevel = 0;
+    set_errorlevel(0);
     prevEntry = thisEntry;
     thisEntry = WCMD_list_directory (thisEntry, 0);
   }
diff --git a/programs/cmd/tests/test_builtins.cmd
b/programs/cmd/tests/test_builtins.cmd
index 62334b1..1f32792 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -286,6 +286,8 @@ echo k1||echo k2&&echo k3
 echo ---
 echo l1||echo l2||echo l3
 echo ---
+call call echo m1&&call call echo m2
+call call echo n1||call call echo n2
 echo --- chain failure
 call :cfail a1&call :cfail a2
 call :cfail b1&&call :cfail b2
@@ -305,6 +307,9 @@ call :cfail j1||call :cfail j2&call :cfail j3
 call :cfail k1||call :cfail k2&&call :cfail k3
 echo ---
 call :cfail l1||call :cfail l2||call :cfail l3
+echo ---
+call call :cfail m1&&call call :cfail m2
+call call :cfail n1||call call :cfail n2
 echo --- chain brackets
 rem Brackets are like regular commands, they support redirections
 rem and have the same precedence as regular commands.
@@ -737,12 +742,69 @@ if exist foo3 (
 echo bar4 && echo foo4
 echo --- on failure conditional or
 call :setError 789 || echo foo5
-echo foo6 || echo bar6 > bar6
-if exist bar6 (
-    echo bar6 created
-    del bar6
+
+rem Testing to make sure that '||' and '&&' both only evalute what they
+rem should and no more.
+
+rem Each of the following "if" statements should always evaluate to be
+rem true (remember to think about the "not")
+
+echo --- logical conditional and
+echo foobar && echo bar7 > bar7
+
+if exist bar7 (
+    echo bar7 created
+    del bar7
+)
+
+rem the "nul" is to silence the intentional failure
+fail_failfail > nul 2> nul && echo bar8 > bar8
+
+if not exist bar8 (
+    echo bar8 missing
+) else (
+    del bar8
+)
+
+echo --- logical conditional or
+echo foobar || echo bar9 > bar9
+
+if not exist bar9 (
+    echo bar9 missing
+) else (
+    del bar9
 )

+rem the "nul" is to silence the intentional failure
+fail_failfail > nul 2> nul || echo bar10 > bar10
+
+if exist bar10 (
+    echo bar10 created
+    del bar10
+)
+
+echo --- logical conditional or resets errorlevel to 1
+rem TODO This is is a Windows bug, but it seems like a good idea to copy it
+fail_failfail >nul 2>nul
+echo %ERRORLEVEL%
+fail_failfail >nul 2>nul || echo %ERRORLEVEL% || echo %ERRORLEVEL%
+echo %ERRORLEVEL%
+
+echo --- logical conditional and does NOT reset errorlevel to 1
+fail_failfail >nul 2>nul
+echo %ERRORLEVEL% && fail_failfail >nul 2>nul && echo %ERRORLEVEL%
+echo %ERRORLEVEL%
+
+echo --- logical conditional or does not reset errorlevel to 1 without error
+fail_failfail >nul 2>nul
+echo %ERRORLEVEL% || echo %ERRORLEVEL%
+echo %ERRORLEVEL%
+
+echo ---
+goto :skipand && echo foo
+:skipand
+echo bar
+
 echo ------------ Testing cd ------------
 mkdir foobar
 cd foobar
diff --git a/programs/cmd/tests/test_builtins.cmd.exp
b/programs/cmd/tests/test_builtins.cmd.exp
index 796550e..f2ae490 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -275,12 +275,15 @@ i1
 i2
 @todo_wine at ---
 j1
- at todo_wine@j3
+j3
 @todo_wine at ---
 k1
 @todo_wine at ---
 l1
 @todo_wine at ---
+m1
+m2
+n1
 --- chain failure
 a1
 a2
@@ -298,12 +301,12 @@ f1
 f2
 f3
 g1
- at todo_wine@g3
+g3
 @todo_wine at ---
 h1
 @todo_wine at ---
 i1
- at todo_wine@i3
+i3
 @todo_wine at ---
 j1
 j2
@@ -314,6 +317,10 @@ k2
 l1
 l2
 l3
+---
+m1
+n1
+n2
 --- chain brackets
 a1
 a2
@@ -600,13 +607,32 @@ bar2 at space@
 foo2
 foobar deleted
 --- on success conditional and
- at todo_wine@foo3 not created
+foo3 not created
 bar4 at space@
 foo4
 --- on failure conditional or
 foo5
-foo6 at space@
- at todo_wine@------------ Testing cd ------------
+--- logical conditional and
+foobar at space@
+bar7 created
+bar8 missing
+--- logical conditional or
+foobar at space@
+bar9 missing
+bar10 created
+--- logical conditional or resets errorlevel to 1
+9009
+9009 at space@
+ at todo_wine@1
+--- logical conditional and does NOT reset errorlevel to 1
+9009 at space@
+9009
+--- logical conditional or does not reset errorlevel to 1 without error
+9009 at space@
+9009
+---
+bar
+------------ Testing cd ------------
 singleFile
 Current dir: @drive@@path at foobar@or_broken at Current dir:@space@
 @drive@@path at foobar
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index 557be14..fbe713a 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -204,11 +204,23 @@ typedef struct _FOR_CONTEXT {
  * variables and batch parameters substitution already done.
  */
 extern WCHAR quals[MAX_PATH], param1[MAXSTRING], param2[MAXSTRING];
-extern DWORD errorlevel;
 extern BATCH_CONTEXT *context;
 extern FOR_CONTEXT forloopcontext;
 extern BOOL delayedsubst;

+/* Do not access errorlevel directly if at all possible, please use
+   get_errorlevel() and set_errorlevel(). This is due to the fact
+   that conditonal logical conditional operators (&& and ||) rely
+   on knowing if the values changed. Blame Windows if you are mad. */
+DWORD errorlevel;
+/* If the errorlevel just changed (last command) */
+BOOL errorlevel_changed;
+/* If the errorlevel changed ANYWHERE in a call */
+BOOL errorlevel_changed_call;
+
+DWORD get_errorlevel(void);
+void set_errorlevel(DWORD errorlevel);
+
 #endif /* !RC_INVOKED */

 /*
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index cec6df0..625f026 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -37,7 +37,6 @@ extern const WCHAR inbuilt[][10];
 extern struct env_stack *pushd_directories;

 BATCH_CONTEXT *context = NULL;
-DWORD errorlevel;
 WCHAR quals[MAX_PATH], param1[MAXSTRING], param2[MAXSTRING];
 BOOL  interactive;
 FOR_CONTEXT forloopcontext; /* The 'for' loop context */
@@ -79,6 +78,15 @@ static char *get_file_buffer(void)
     return output_bufA;
 }

+DWORD get_errorlevel(void) {
+  return errorlevel;
+}
+void set_errorlevel(DWORD tmp_errorlevel) {
+  errorlevel_changed = TRUE;
+  errorlevel_changed_call = TRUE;
+  errorlevel = tmp_errorlevel;
+}
+
 /*******************************************************************
  * WCMD_output_asis_len - send output to current standard output
  *
@@ -622,7 +630,7 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start,
WCHAR startchar)
     /* override if existing env var called that name              */
     if (WCMD_is_magic_envvar(thisVar, ErrorLvl)) {
       static const WCHAR fmt[] = {'%','d','\0'};
-      wsprintfW(thisVarContents, fmt, errorlevel);
+      wsprintfW(thisVarContents, fmt, get_errorlevel());
       len = strlenW(thisVarContents);
     } else if (WCMD_is_magic_envvar(thisVar, Date)) {
       GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL,
@@ -1221,7 +1229,12 @@ void WCMD_run_program (WCHAR *command, BOOL called)
         if (!interactive || (console && !HIWORD(console)))
             WaitForSingleObject (pe.hProcess, INFINITE);
         GetExitCodeProcess (pe.hProcess, &errorlevel);
-        if (errorlevel == STILL_ACTIVE) errorlevel = 0;
+        if (errorlevel == STILL_ACTIVE) {
+          set_errorlevel(0);
+        } else {
+          /* This is just to set the errorlevel_changed to true */
+          set_errorlevel(errorlevel);
+        }

         CloseHandle(pe.hProcess);
         CloseHandle(pe.hThread);
@@ -1247,7 +1260,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)

   /* If a command fails to launch, it sets errorlevel 9009 - which
      does not seem to have any associated constant definition     */
-  errorlevel = 9009;
+  set_errorlevel(9009);
   return;

 }
@@ -1281,6 +1294,23 @@ void WCMD_execute (const WCHAR *command, const
WCHAR *redirects,
     WINE_TRACE("command on entry:%s (%p)\n",
                wine_dbgstr_w(command), cmdList);

+    if (cmdList && ((*cmdList)->prevDelim == CMD_ONFAILURE ||
+        (*cmdList)->prevDelim == CMD_ONSUCCESS)) {
+      WINE_TRACE("Processing conditional operators - delim(%d)
errorlevel(%d) changed(%d)\n",
+                 (*cmdList)->prevDelim,
+                 get_errorlevel(),
+                 errorlevel_changed);
+        if ((((*cmdList)->prevDelim == CMD_ONSUCCESS) &&
(!errorlevel_changed || get_errorlevel() == 0)) ||
+            (((*cmdList)->prevDelim == CMD_ONFAILURE) &&
(errorlevel_changed && get_errorlevel() != 0))) {
+          WINE_TRACE("Continuing\n");
+        } else {
+          WINE_TRACE("Returning\n");
+          return;
+        }
+    }
+    // Reset the errorlevel_changed status
+    errorlevel_changed = FALSE;
+
     /* 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.
@@ -1478,7 +1508,17 @@ void WCMD_execute (const WCHAR *command, const
WCHAR *redirects,
     switch (i) {

       case WCMD_CALL:
-        WCMD_call (p);
+        /* The compiler won't allow this statement unless it is at
the beginning
+           of a statement so I had to put the ";" above */
+        {
+            BOOL tmp_errorlevel_changed_call = errorlevel_changed_call;
+            errorlevel_changed_call = FALSE;
+            WCMD_call (p);
+            /* errorlevel_changed is always FALSE (see after && and || check)
+               thus we don't have to check if it's true when setting
errorlevel_changed */
+            errorlevel_changed = tmp_errorlevel_changed_call |
errorlevel_changed_call;
+            errorlevel_changed_call = errorlevel_changed;
+        }
         break;
       case WCMD_CD:
       case WCMD_CHDIR:
@@ -1495,7 +1535,7 @@ void WCMD_execute (const WCHAR *command, const
WCHAR *redirects,
         break;
       case WCMD_DATE:
         WCMD_setshow_date ();
-    break;
+  break;
       case WCMD_DEL:
       case WCMD_ERASE:
         WCMD_delete (p);
@@ -1511,14 +1551,14 @@ void WCMD_execute (const WCHAR *command, const
WCHAR *redirects,
         break;
       case WCMD_HELP:
         WCMD_give_help (p);
-    break;
+        break;
       case WCMD_LABEL:
         WCMD_volume (TRUE, p);
         break;
       case WCMD_MD:
       case WCMD_MKDIR:
         WCMD_create_dir (p);
-    break;
+        break;
       case WCMD_MOVE:
         WCMD_move ();
         break;
@@ -1536,7 +1576,7 @@ void WCMD_execute (const WCHAR *command, const
WCHAR *redirects,
       case WCMD_REN:
       case WCMD_RENAME:
         WCMD_rename ();
-    break;
+        break;
       case WCMD_RD:
       case WCMD_RMDIR:
         WCMD_remove_dir (p);
@@ -1549,7 +1589,7 @@ void WCMD_execute (const WCHAR *command, const
WCHAR *redirects,
         break;
       case WCMD_SET:
         WCMD_setshow_env (p);
-    break;
+        break;
       case WCMD_SHIFT:
         WCMD_shift (p);
         break;
@@ -1565,7 +1605,7 @@ void WCMD_execute (const WCHAR *command, const
WCHAR *redirects,
         break;
       case WCMD_TYPE:
         WCMD_type (p);
-    break;
+        break;
       case WCMD_VER:
         WCMD_output_asis(newlineW);
         WCMD_version ();
@@ -2344,6 +2384,9 @@ int wmain (int argc, WCHAR *argvW[])
   OSVERSIONINFOW osv;
   char osver[50];

+  errorlevel_changed = FALSE;
+  errorlevel_changed_call = FALSE;
+
   srand(time(NULL));

   /* Get the windows version being emulated */
@@ -2593,7 +2636,7 @@ int wmain (int argc, WCHAR *argvW[])
       toExecute = NULL;

       heap_free(cmd);
-      return errorlevel;
+      return get_errorlevel();
   }

   SetConsoleTitleW(WCMD_LoadMessage(WCMD_CONSTITLE));
-- 
2.7.4



More information about the wine-patches mailing list