[PATCH 2/5] [programs/cmd] RESEND: Handle single line 'if' as nested if or with redirects

Jason Edmeades us at edmeades.me.uk
Mon Aug 27 14:41:50 CDT 2018


Fixes bugs#28037 and #41914 and a few todo's in the tests

A single line if statement causes problems when it has redirects and/or continuation type operators (|, &&, || etc)
because it is expected that if there is more than one command in the 'if', then it will use brackets. This patch
changes the 'if' parsing to emulate brackets at a continuation character. In addition, 'for' and 'if' statements
do not have their output redirected immediately, instead it is redirected on the individual commands being executed
not the statement itself. We were opening the redirect once for the 'if' and once for the processing of the
statement inside the if.

A few points to note...

The patch is larger than I would have liked because I had to shuffle the WCMD_execute routine a little, which
hit problems with (uselessly named) variable reuse, and I also had to add a condition around the redirects
processing to avoid doing it for an 'if' or 'for' statement.

There are still some more issues remaining, which will be easier to progress when Bernhard Übelacker's patch
for bug#44338 is committed, when we will know the end of the 'if' condition.

Signed-off-by: Jason Edmeades <us at edmeades.me.uk>
---
 programs/cmd/builtins.c                  |  16 +-
 programs/cmd/tests/test_builtins.cmd     |   9 +
 programs/cmd/tests/test_builtins.cmd.exp |   7 +-
 programs/cmd/wcmdmain.c                  | 309 +++++++++++++----------
 4 files changed, 200 insertions(+), 141 deletions(-)

diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 1da61556d9..5026f9c1a5 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1544,8 +1544,8 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
   CMD_LIST *curPosition = *cmdList;
   int myDepth = (*cmdList)->bracketDepth;
 
-  WINE_TRACE("cmdList(%p), firstCmd(%s), doIt(%d)\n", cmdList, wine_dbgstr_w(firstcmd),
-             executecmds);
+  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++;
@@ -1592,7 +1592,8 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
       } else if ((*cmdList)->bracketDepth > myDepth) {
         if (processThese) {
           *cmdList = WCMD_process_commands(*cmdList, TRUE, FALSE);
-          WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
+        } else {
+          WINE_TRACE("Skipping command %p due to stack depth\n", *cmdList);
         }
         if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
 
@@ -1626,9 +1627,16 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
               processThese = TRUE;
           }
           if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+
+        /* If we were in an IF statement and we didnt 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 && (*cmdList)->bracketDepth == myDepth) {
+          WINE_TRACE("Found end of this nested IF statement, ending this if\n");
+          break;
         } else if (!processThese) {
           if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
-          WINE_TRACE("Ignore the next command as well (next = %p)\n", *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;
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd
index 775f21bbdc..0738f2da6e 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -179,6 +179,8 @@ if exist foo (type foo) else echo not supported
 echo --- redirections within IF statements
 if 1==1 echo foo1>bar
 type bar & del bar
+if 1==1 echo foo2>>bar
+type bar & del bar
 echo -----
 if 1==1 (echo foo2>bar) else echo baz2>bar
 type bar & del bar
@@ -925,6 +927,13 @@ if %elseIF% == 1 (
 ) else (
   echo else if seems to be broken
 )
+if "x" == "a" (
+  echo broken1
+) else (
+  echo expected1
+  if "y" == "b" echo broken2
+  echo expected post-embedded if
+)
 echo --- case sensitivity with and without /i option
 if bar==BAR echo if does not default to case sensitivity
 if not bar==BAR echo if seems to default to case sensitivity
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index 35e0cf2fa5..7ec83d7acb 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -205,7 +205,8 @@ food21
 @todo_wine at foo7@space@@space@@or_broken at not supported at space@
 @todo_wine at foo@or_broken at not supported
 --- redirections within IF statements
- at todo_wine@foo1
+foo1
+foo2
 -----
 foo2
 foo3
@@ -430,7 +431,7 @@ p1
 q1
 @todo_wine at ---
 --- chain else (if false)
- at todo_wine@j3
+j3
 ---
 k3
 l3
@@ -661,6 +662,8 @@ if seems not to detect /c as parameter
 else if seems to work
 else if seems to work
 else if seems to work
+expected1
+expected post-embedded if
 --- case sensitivity with and without /i option
 if seems to default to case sensitivity
 if /i seems to work
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index b269635c75..7a7ee16f7d 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -1277,8 +1277,9 @@ void WCMD_run_program (WCHAR *command, BOOL called)
 void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
                    CMD_LIST **cmdList, BOOL retrycall)
 {
-    WCHAR *cmd, *p, *redir;
-    int status, i;
+    WCHAR *cmd, *parms_start, *redir;
+    WCHAR *pos;
+    int status, i, cmd_index;
     DWORD count, creationDisposition;
     HANDLE h;
     WCHAR *whichcmd;
@@ -1296,13 +1297,42 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
     WINE_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 = heap_alloc(MAXSTRING * sizeof(WCHAR));
+    strcpyW(new_cmd, command);
+    cmd = new_cmd;
+
+    /* Move copy of the redirects onto the heap so it can be expanded */
+    new_redir = heap_alloc(MAXSTRING * sizeof(WCHAR));
+    redir = new_redir;
+
+    /* Strip leading whitespaces, and a '@' if supplied */
+    whichcmd = WCMD_skip_leading_spaces(cmd);
+    WINE_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 (i=0; i<=WCMD_EXIT; i++) {
+      if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+        whichcmd, count, inbuilt[i], -1) == CSTR_EQUAL) break;
+    }
+    cmd_index = i;
+    parms_start = WCMD_skip_leading_spaces (&whichcmd[count]);
+
     /* 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  */
-    if (cmdList && (*cmdList)->nextcommand &&
+    if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF) &&
+        cmdList && (*cmdList)->nextcommand &&
         (*cmdList)->nextcommand->prevDelim == CMD_PIPE) {
 
         WCHAR temp_path[MAX_PATH];
@@ -1319,13 +1349,6 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
                    wine_dbgstr_w((*cmdList)->nextcommand->pipeFile));
     }
 
-    /* Move copy of the command onto the heap so it can be expanded */
-    new_cmd = heap_alloc(MAXSTRING * sizeof(WCHAR));
-    strcpyW(new_cmd, command);
-
-    /* Move copy of the redirects onto the heap so it can be expanded */
-    new_redir = heap_alloc(MAXSTRING * sizeof(WCHAR));
-
     /* If piped output, send stdout to the pipe by appending >filename to redirects */
     if (piped) {
         static const WCHAR redirOut[] = {'%','s',' ','>',' ','%','s','\0'};
@@ -1339,7 +1362,6 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
        be expanded as the line is read in, except for 'for' loops) */
     handleExpansion(new_cmd, (context != NULL), delayedsubst);
     handleExpansion(new_redir, (context != NULL), delayedsubst);
-    cmd = new_cmd;
 
 /*
  * Changing default drive has to be handled as a special case, anything
@@ -1376,16 +1398,33 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
     sa.lpSecurityDescriptor = NULL;
     sa.bInheritHandle = TRUE;
 
-/*
- *	Redirect stdin, stdout and/or stderr if required.
- */
+    /*
+     *  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.
+     */
+    if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF)) {
+      /* STDIN could come from a preceding pipe, so delete on close if it does */
+      if (cmdList && (*cmdList)->pipeFile[0] != 0x00) {
+          WINE_TRACE("Input coming from %s\n", wine_dbgstr_w((*cmdList)->pipeFile));
+          h = CreateFileW((*cmdList)->pipeFile, 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) {
+            WCMD_print_error ();
+            heap_free(cmd);
+            heap_free(new_redir);
+            return;
+          }
+          SetStdHandle (STD_INPUT_HANDLE, h);
+
+          /* No need to remember the temporary name any longer once opened */
+          (*cmdList)->pipeFile[0] = 0x00;
 
-    /* STDIN could come from a preceding pipe, so delete on close if it does */
-    if (cmdList && (*cmdList)->pipeFile[0] != 0x00) {
-        WINE_TRACE("Input coming from %s\n", wine_dbgstr_w((*cmdList)->pipeFile));
-        h = CreateFileW((*cmdList)->pipeFile, GENERIC_READ,
-                  FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
-                  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
+      /* Otherwise STDIN could come from a '<' redirect */
+      } else if ((pos = strchrW(new_redir,'<')) != NULL) {
+        h = CreateFileW(WCMD_parameter(++pos, 0, NULL, FALSE, FALSE), GENERIC_READ, FILE_SHARE_READ,
+                        &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
         if (h == INVALID_HANDLE_VALUE) {
           WCMD_print_error ();
           heap_free(cmd);
@@ -1393,122 +1432,87 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
           return;
         }
         SetStdHandle (STD_INPUT_HANDLE, h);
-
-        /* No need to remember the temporary name any longer once opened */
-        (*cmdList)->pipeFile[0] = 0x00;
-
-    /* Otherwise STDIN could come from a '<' redirect */
-    } else if ((p = strchrW(new_redir,'<')) != NULL) {
-      h = CreateFileW(WCMD_parameter(++p, 0, NULL, FALSE, FALSE), GENERIC_READ, FILE_SHARE_READ,
-                      &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-      if (h == INVALID_HANDLE_VALUE) {
-	WCMD_print_error ();
-        heap_free(cmd);
-        heap_free(new_redir);
-	return;
       }
-      SetStdHandle (STD_INPUT_HANDLE, h);
-    }
-
-    /* Scan the whole command looking for > and 2> */
-    redir = new_redir;
-    while (redir != NULL && ((p = strchrW(redir,'>')) != NULL)) {
-      int handle = 0;
-
-      if (p > redir && (*(p-1)=='2'))
-        handle = 2;
-      else
-        handle = 1;
 
-      p++;
-      if ('>' == *p) {
-        creationDisposition = OPEN_ALWAYS;
-        p++;
-      }
-      else {
-        creationDisposition = CREATE_ALWAYS;
-      }
+      /* Scan the whole command looking for > and 2> */
+      while (redir != NULL && ((pos = strchrW(redir,'>')) != NULL)) {
+        int handle = 0;
 
-      /* Add support for 2>&1 */
-      redir = p;
-      if (*p == '&') {
-        int idx = *(p+1) - '0';
-
-        if (DuplicateHandle(GetCurrentProcess(),
-                        GetStdHandle(idx_stdhandles[idx]),
-                        GetCurrentProcess(),
-                        &h,
-                        0, TRUE, DUPLICATE_SAME_ACCESS) == 0) {
-          WINE_FIXME("Duplicating handle failed with gle %d\n", GetLastError());
-        }
-        WINE_TRACE("Redirect %d (%p) to %d (%p)\n", handle, GetStdHandle(idx_stdhandles[idx]), idx, h);
+        if (pos > redir && (*(pos-1)=='2'))
+          handle = 2;
+        else
+          handle = 1;
 
-      } else {
-        WCHAR *param = WCMD_parameter(p, 0, NULL, FALSE, FALSE);
-        h = CreateFileW(param, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE,
-                        &sa, creationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
-        if (h == INVALID_HANDLE_VALUE) {
-          WCMD_print_error ();
-          heap_free(cmd);
-          heap_free(new_redir);
-          return;
+        pos++;
+        if ('>' == *pos) {
+          creationDisposition = OPEN_ALWAYS;
+          pos++;
         }
-        if (SetFilePointer (h, 0, NULL, FILE_END) ==
-              INVALID_SET_FILE_POINTER) {
-          WCMD_print_error ();
+        else {
+          creationDisposition = CREATE_ALWAYS;
         }
-        WINE_TRACE("Redirect %d to '%s' (%p)\n", handle, wine_dbgstr_w(param), h);
-      }
-
-      SetStdHandle (idx_stdhandles[handle], h);
-    }
 
-/*
- * Strip leading whitespaces, and a '@' if supplied
- */
-    whichcmd = WCMD_skip_leading_spaces(cmd);
-    WINE_TRACE("Command: '%s'\n", wine_dbgstr_w(cmd));
-    if (whichcmd[0] == '@') whichcmd++;
+        /* Add support for 2>&1 */
+        redir = pos;
+        if (*pos == '&') {
+          int idx = *(pos+1) - '0';
+
+          if (DuplicateHandle(GetCurrentProcess(),
+                          GetStdHandle(idx_stdhandles[idx]),
+                          GetCurrentProcess(),
+                          &h,
+                          0, TRUE, DUPLICATE_SAME_ACCESS) == 0) {
+            WINE_FIXME("Duplicating handle failed with gle %d\n", GetLastError());
+          }
+          WINE_TRACE("Redirect %d (%p) to %d (%p)\n", handle, GetStdHandle(idx_stdhandles[idx]), idx, h);
 
-/*
- *	Check if the command entered is internal. If it is, pass the rest of the
- *	line down to the command. If not try to run a program.
- */
+        } else {
+          WCHAR *param = WCMD_parameter(pos, 0, NULL, FALSE, FALSE);
+          h = CreateFileW(param, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE,
+                          &sa, creationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
+          if (h == INVALID_HANDLE_VALUE) {
+            WCMD_print_error ();
+            heap_free(cmd);
+            heap_free(new_redir);
+            return;
+          }
+          if (SetFilePointer (h, 0, NULL, FILE_END) ==
+                INVALID_SET_FILE_POINTER) {
+            WCMD_print_error ();
+          }
+          WINE_TRACE("Redirect %d to '%s' (%p)\n", handle, wine_dbgstr_w(param), h);
+        }
 
-    count = 0;
-    while (IsCharAlphaNumericW(whichcmd[count])) {
-      count++;
-    }
-    for (i=0; i<=WCMD_EXIT; i++) {
-      if (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
-        whichcmd, count, inbuilt[i], -1) == CSTR_EQUAL) break;
+        SetStdHandle (idx_stdhandles[handle], h);
+      }
+    } else {
+      WINE_TRACE("Not touching redirects for a FOR or IF command\n");
     }
-    p = WCMD_skip_leading_spaces (&whichcmd[count]);
-    WCMD_parse (p, quals, param1, param2);
+    WCMD_parse (parms_start, quals, param1, param2);
     WINE_TRACE("param1: %s, param2: %s\n", wine_dbgstr_w(param1), wine_dbgstr_w(param2));
 
-    if (i <= WCMD_EXIT && (p[0] == '/') && (p[1] == '?')) {
+    if (i <= WCMD_EXIT && (parms_start[0] == '/') && (parms_start[1] == '?')) {
       /* this is a help request for a builtin program */
       i = WCMD_HELP;
-      memcpy(p, whichcmd, count * sizeof(WCHAR));
-      p[count] = '\0';
+      memcpy(parms_start, whichcmd, count * sizeof(WCHAR));
+      parms_start[count] = '\0';
 
     }
 
     switch (i) {
 
       case WCMD_CALL:
-        WCMD_call (p);
+        WCMD_call (parms_start);
         break;
       case WCMD_CD:
       case WCMD_CHDIR:
-        WCMD_setshow_default (p);
+        WCMD_setshow_default (parms_start);
         break;
       case WCMD_CLS:
         WCMD_clear_screen ();
         break;
       case WCMD_COPY:
-        WCMD_copy (p);
+        WCMD_copy (parms_start);
         break;
       case WCMD_CTTY:
         WCMD_change_tty ();
@@ -1518,10 +1522,10 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
 	break;
       case WCMD_DEL:
       case WCMD_ERASE:
-        WCMD_delete (p);
+        WCMD_delete (parms_start);
         break;
       case WCMD_DIR:
-        WCMD_directory (p);
+        WCMD_directory (parms_start);
         break;
       case WCMD_ECHO:
         WCMD_echo(&whichcmd[count]);
@@ -1530,20 +1534,20 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
         WCMD_goto (cmdList);
         break;
       case WCMD_HELP:
-        WCMD_give_help (p);
-	break;
+        WCMD_give_help (parms_start);
+        break;
       case WCMD_LABEL:
-        WCMD_volume (TRUE, p);
+        WCMD_volume (TRUE, parms_start);
         break;
       case WCMD_MD:
       case WCMD_MKDIR:
-        WCMD_create_dir (p);
-	break;
+        WCMD_create_dir (parms_start);
+        break;
       case WCMD_MOVE:
         WCMD_move ();
         break;
       case WCMD_PATH:
-        WCMD_setshow_path (p);
+        WCMD_setshow_path (parms_start);
         break;
       case WCMD_PAUSE:
         WCMD_pause ();
@@ -1559,22 +1563,22 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
 	break;
       case WCMD_RD:
       case WCMD_RMDIR:
-        WCMD_remove_dir (p);
+        WCMD_remove_dir (parms_start);
         break;
       case WCMD_SETLOCAL:
-        WCMD_setlocal(p);
+        WCMD_setlocal(parms_start);
         break;
       case WCMD_ENDLOCAL:
         WCMD_endlocal();
         break;
       case WCMD_SET:
-        WCMD_setshow_env (p);
-	break;
+        WCMD_setshow_env (parms_start);
+        break;
       case WCMD_SHIFT:
-        WCMD_shift (p);
+        WCMD_shift (parms_start);
         break;
       case WCMD_START:
-        WCMD_start (p);
+        WCMD_start (parms_start);
         break;
       case WCMD_TIME:
         WCMD_setshow_time ();
@@ -1584,41 +1588,41 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
           WCMD_title(&whichcmd[count+1]);
         break;
       case WCMD_TYPE:
-        WCMD_type (p);
-	break;
+        WCMD_type (parms_start);
+        break;
       case WCMD_VER:
         WCMD_output_asis(newlineW);
         WCMD_version ();
         break;
       case WCMD_VERIFY:
-        WCMD_verify (p);
+        WCMD_verify (parms_start);
         break;
       case WCMD_VOL:
-        WCMD_volume (FALSE, p);
+        WCMD_volume (FALSE, parms_start);
         break;
       case WCMD_PUSHD:
-        WCMD_pushd(p);
+        WCMD_pushd(parms_start);
         break;
       case WCMD_POPD:
         WCMD_popd();
         break;
       case WCMD_ASSOC:
-        WCMD_assoc(p, TRUE);
+        WCMD_assoc(parms_start, TRUE);
         break;
       case WCMD_COLOR:
         WCMD_color();
         break;
       case WCMD_FTYPE:
-        WCMD_assoc(p, FALSE);
+        WCMD_assoc(parms_start, FALSE);
         break;
       case WCMD_MORE:
-        WCMD_more(p);
+        WCMD_more(parms_start);
         break;
       case WCMD_CHOICE:
-        WCMD_choice(p);
+        WCMD_choice(parms_start);
         break;
       case WCMD_MKLINK:
-        WCMD_mklink(p);
+        WCMD_mklink(parms_start);
         break;
       case WCMD_EXIT:
         WCMD_exit (cmdList);
@@ -1629,8 +1633,8 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
            these two commands, neither 'for' nor 'if' is supported when called,
            i.e. 'call if 1==1...' will fail.                                    */
         if (!retrycall) {
-          if (i==WCMD_FOR) WCMD_for (p, cmdList);
-          else if (i==WCMD_IF) WCMD_if (p, cmdList);
+          if (i==WCMD_FOR) WCMD_for (parms_start, cmdList);
+          else if (i==WCMD_IF) WCMD_if (parms_start, cmdList);
           break;
         }
         /* else: drop through */
@@ -1838,6 +1842,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
     BOOL      lastWasElse = FALSE;
     BOOL      lastWasRedirect = TRUE;
     BOOL      lastWasCaret = FALSE;
+    int       lineCurDepth;                  /* Bracket depth when line was read in */
+    BOOL      resetAtEndOfLine = FALSE;      /* Do we need to reset curdepth at EOL */
 
     /* Allocate working space for a command read from keyboard, file etc */
     if (!extraSpace)
@@ -1905,6 +1911,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
     curCopyTo    = curString;
     curLen       = &curStringLen;
     lastWasRedirect = FALSE;  /* Required e.g. for spaces between > and filename */
+    lineCurDepth = curDepth;  /* What was the curdepth at the beginning of the line */
 
     /* Parse every character on the line being processed */
     while (*curPos != 0x00) {
@@ -1950,6 +1957,15 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
           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;
+            curDepth = lineCurDepth;
+          }
           continue;
 
         /* In a for loop, the DO command will follow a close bracket followed by
@@ -2073,6 +2089,14 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
                   } else {
                     prevDelim = CMD_PIPE;
                   }
+
+                  /* 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) && curDepth == lineCurDepth) {
+                    curDepth++;
+                    resetAtEndOfLine = TRUE;
+                  }
                 } else {
                   curCopyTo[(*curLen)++] = *curPos;
                 }
@@ -2170,6 +2194,13 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
                   } else {
                     prevDelim = CMD_NONE;
                   }
+                  /* 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) && curDepth == lineCurDepth) {
+                    curDepth++;
+                    resetAtEndOfLine = TRUE;
+                  }
                 } else {
                   curCopyTo[(*curLen)++] = *curPos;
                 }
@@ -2232,7 +2263,15 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
                 &curCopyTo, &curLen,
                 prevDelim, curDepth,
                 &lastEntry, output);
-      }
+
+          /* 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;
+            curDepth = lineCurDepth;
+          }
+        }
 
       /* If we have reached the end of the string, see if bracketing or
          final caret is outstanding */
-- 
2.17.1




More information about the wine-devel mailing list