[PATCH] cmd: Fix 'CALL IF' and 'CALL FOR' handling with ()

Flávio J. Saraiva flaviojs2005 at gmail.com
Sun Nov 6 14:45:10 CST 2016


'call if 1==1 (echo 1)' should produce a single error, instead it was
giving an error and executing 'echo 1'

'call for %i in (foo bar baz) do (echo %i)' should produce a single
error, instead it was giving 3 errors and executing 'echo %i'

Based on cmd.exe behavior in Windows XP

Signed-off-by: Flávio J. Saraiva <flaviojs2005 at gmail.com>
---
 programs/cmd/tests/test_builtins.cmd     |  8 ++++--
 programs/cmd/tests/test_builtins.cmd.exp |  6 +++--
 programs/cmd/wcmdmain.c                  | 42 ++++++++++++++++++++++----------
 3 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd
index 38a7700..1ff55eb 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -2116,10 +2116,14 @@ dir /b
 if exist batfile echo batfile shouldn't exist
 rem ... but not for 'if' or 'for'
 call if 1==1 echo bar 2> nul
-echo %ErrorLevel%
+echo ErrorLevel is %ErrorLevel% after call if
+call if 1==1 (echo bar2) 2> nul
+echo ErrorLevel is %ErrorLevel% after call if with ()
 call :setError 0
 call for %%i in (foo bar baz) do echo %%i 2> nul
-echo %ErrorLevel%
+echo ErrorLevel is %ErrorLevel% after call for
+call for %%i in (foo bar baz) do (echo %%i) 2> nul
+echo ErrorLevel is %ErrorLevel% after call for with ()
 rem First look for programs in the path before trying a builtin
 echo echo non-builtin dir> dir.cmd
 call dir /b
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index d01a23e..6a37323 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -1201,8 +1201,10 @@ foo created
 Should expand foobaz
 batfile
 robinfile
-1
-1
+ErrorLevel is 1 after call if
+ErrorLevel is 1 after call if with ()
+ErrorLevel is 1 after call for
+ErrorLevel is 1 after call for with ()
 non-builtin dir
 Line one
 Line two
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 87f5387..bb7b568 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -1236,10 +1236,31 @@ void WCMD_run_program (WCHAR *command, BOOL called)
 
     /* Parse the command string, without reading any more input */
     WCMD_ReadAndParseLine(command, &toExecute, INVALID_HANDLE_VALUE);
-    WCMD_process_commands(toExecute, FALSE, called);
-    WCMD_free_commands(toExecute);
-    toExecute = NULL;
-    return;
+
+    /* 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 (toExecute) {
+      int    len = 0;
+      WCHAR *name;
+      BOOL   canExecute = TRUE;
+
+      name = WCMD_skip_leading_spaces(toExecute->command);
+      while (IsCharAlphaNumericW(*(name+len))) len++;
+
+      if ((CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+           name, len, inbuilt[WCMD_FOR], -1) == CSTR_EQUAL) ||
+          (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+           name, len, inbuilt[WCMD_IF], -1) == CSTR_EQUAL)) {
+        canExecute = FALSE;
+      }
+
+      /* Execute commands or drop to error */
+      if (canExecute) WCMD_process_commands(toExecute, FALSE, called);
+      WCMD_free_commands(toExecute);
+      toExecute = NULL;
+      if (canExecute) return;
+    }
   }
 
   /* Not found anywhere - give up */
@@ -1601,16 +1622,11 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
         WCMD_exit (cmdList);
         break;
       case WCMD_FOR:
+        WCMD_for(p, cmdList);
+        break;
       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 (i==WCMD_FOR) WCMD_for (p, cmdList);
-          else if (i==WCMD_IF) WCMD_if (p, cmdList);
-          break;
-        }
-        /* else: drop through */
+        WCMD_if(p, cmdList);
+        break;
       default:
         prev_echo_mode = echo_mode;
         WCMD_run_program (whichcmd, FALSE);
-- 
2.7.4




More information about the wine-patches mailing list