https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d78e8029b8b499d00776c…
commit d78e8029b8b499d00776c54dd30a95b0afbd285a
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Thu Jul 30 01:44:43 2020 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Wed Aug 19 20:36:03 2020 +0200
[CMD] Additional fixes for ERRORLEVEL and last returned exit code from EXIT, CALL
commands and CMD.
CORE-10495 CORE-13672
- Fix how the ERRORLEVEL and the last returned exit code are set by
EXIT and CALL commands, when batch contexts terminate, and when CMD
runs in single-command mode (with /C).
Addendum to commit 26ff2c8e, and reverts commit 7bd33ac4.
See also commit 8cf11060 (r40474).
More information can be found at:
https://ss64.com/nt/exit.html
https://stackoverflow.com/a/34987886/13530036
https://stackoverflow.com/a/34937706/13530036
- Move the actual execution of the CMD command-line (in /C or /K
single-command mode) from Initialize() to _tmain(), to put it on par
with the ProcessInput() interactive mode.
- Make ProcessInput() also return the last command's exit code.
---
base/shell/cmd/batch.c | 12 +++----
base/shell/cmd/call.c | 7 +++-
base/shell/cmd/cmd.c | 82 +++++++++++++++++++++++++++++------------------
base/shell/cmd/internal.c | 7 ++--
4 files changed, 68 insertions(+), 40 deletions(-)
diff --git a/base/shell/cmd/batch.c b/base/shell/cmd/batch.c
index 184911c60a6..a7dfa2fd11a 100644
--- a/base/shell/cmd/batch.c
+++ b/base/shell/cmd/batch.c
@@ -349,7 +349,7 @@ INT Batch(LPTSTR fullname, LPTSTR firstword, LPTSTR param,
PARSED_COMMAND *Cmd)
/* Check if this is a "CALL :label" */
if (*firstword == _T(':'))
- cmd_goto(firstword);
+ ret = cmd_goto(firstword);
/* If we are calling from inside a FOR, hide the FOR variables */
saved_fc = fc;
@@ -376,6 +376,7 @@ INT Batch(LPTSTR fullname, LPTSTR firstword, LPTSTR param,
PARSED_COMMAND *Cmd)
}
/* Stop all execution */
ExitAllBatches();
+ ret = 1;
break;
}
@@ -386,12 +387,11 @@ INT Batch(LPTSTR fullname, LPTSTR firstword, LPTSTR param,
PARSED_COMMAND *Cmd)
FreeCommand(Cmd);
}
- /* Always return the current errorlevel */
- ret = nErrorLevel;
-
- TRACE("Batch: returns TRUE\n");
-
+ /* Restore the FOR variables */
fc = saved_fc;
+
+ /* Always return the last command's return code */
+ TRACE("Batch: returns %d\n", ret);
return ret;
}
diff --git a/base/shell/cmd/call.c b/base/shell/cmd/call.c
index 2cf2772c3c4..55f39d086f1 100644
--- a/base/shell/cmd/call.c
+++ b/base/shell/cmd/call.c
@@ -69,10 +69,15 @@ INT cmd_call(LPTSTR param)
if (*first == _T(':') && bc)
{
+ INT ret;
+
/* CALL :label - call a subroutine of the current batch file */
while (*param == _T(' '))
param++;
- nErrorLevel = Batch(bc->BatchFilePath, first, param, NULL);
+
+ ret = Batch(bc->BatchFilePath, first, param, NULL);
+ nErrorLevel = (ret != 0 ? ret : nErrorLevel);
+
return nErrorLevel;
}
diff --git a/base/shell/cmd/cmd.c b/base/shell/cmd/cmd.c
index 31910e9e2ef..4bbffa9faf1 100644
--- a/base/shell/cmd/cmd.c
+++ b/base/shell/cmd/cmd.c
@@ -154,6 +154,7 @@ BOOL bCanExit = TRUE; /* Indicates if this shell is exitable */
BOOL bCtrlBreak = FALSE; /* Ctrl-Break or Ctrl-C hit */
BOOL bIgnoreEcho = FALSE; /* Set this to TRUE to prevent a newline, when executing a
command */
static BOOL fSingleCommand = 0; /* When we are executing something passed on the command
line after /C or /K */
+static BOOL bAlwaysStrip = FALSE;
INT nErrorLevel = 0; /* Errorlevel of last launched external program */
CRITICAL_SECTION ChildProcessRunningLock;
BOOL bDisableBatchEcho = FALSE;
@@ -631,12 +632,17 @@ DoCommand(LPTSTR first, LPTSTR rest, PARSED_COMMAND *Cmd)
INT ParseCommandLine(LPTSTR cmd)
{
INT Ret = 0;
- PARSED_COMMAND *Cmd = ParseCommand(cmd);
- if (Cmd)
+ PARSED_COMMAND *Cmd;
+
+ Cmd = ParseCommand(cmd);
+ if (!Cmd)
{
- Ret = ExecuteCommand(Cmd);
- FreeCommand(Cmd);
+ /* Return an adequate error code */
+ return (!bParseError ? 0 : 1);
}
+
+ Ret = ExecuteCommand(Cmd);
+ FreeCommand(Cmd);
return Ret;
}
@@ -1524,9 +1530,10 @@ ReadLine(TCHAR *commandline, BOOL bMore)
return SubstituteVars(ip, commandline, _T('%'));
}
-static VOID
+static INT
ProcessInput(VOID)
{
+ INT Ret = 0;
PARSED_COMMAND *Cmd;
while (!bCanExit || !bExit)
@@ -1538,9 +1545,11 @@ ProcessInput(VOID)
if (!Cmd)
continue;
- ExecuteCommand(Cmd);
+ Ret = ExecuteCommand(Cmd);
FreeCommand(Cmd);
}
+
+ return Ret;
}
@@ -1879,20 +1888,18 @@ GetCmdLineCommand(
/*
- * Set up global initializations and process parameters
+ * Set up global initializations and process parameters.
+ * Return a pointer to the command line if present.
*/
-static VOID
+static LPCTSTR
Initialize(VOID)
{
HMODULE NtDllModule;
- // INT nExitCode;
HANDLE hIn, hOut;
LPTSTR ptr, cmdLine;
TCHAR option = 0;
- BOOL AlwaysStrip = FALSE;
BOOL AutoRun = TRUE;
TCHAR ModuleName[MAX_PATH + 1];
- TCHAR commandline[CMDLINE_LENGTH];
/* Get version information */
InitOSVersion();
@@ -1957,7 +1964,7 @@ Initialize(VOID)
ConOutResPaging(TRUE, STRING_CMD_HELP8);
nErrorLevel = 1;
bExit = TRUE;
- return;
+ return NULL;
}
else if (option == _T('P'))
{
@@ -1996,7 +2003,7 @@ Initialize(VOID)
}
else if (option == _T('S'))
{
- AlwaysStrip = TRUE;
+ bAlwaysStrip = TRUE;
}
#ifdef INCLUDE_CMD_COLOR
else if (!_tcsnicmp(ptr, _T("/T:"), 3))
@@ -2067,18 +2074,8 @@ Initialize(VOID)
ExecuteAutoRunFile(HKEY_CURRENT_USER);
}
- if (*ptr)
- {
- /* Do the /C or /K command */
- GetCmdLineCommand(commandline, &ptr[2], AlwaysStrip);
- /* nExitCode = */ ParseCommandLine(commandline);
- if (fSingleCommand == 1)
- {
- // nErrorLevel = nExitCode;
- bExit = TRUE;
- }
- fSingleCommand = 0;
- }
+ /* Returns the rest of the command line */
+ return ptr;
}
@@ -2125,6 +2122,8 @@ static VOID Cleanup(VOID)
*/
int _tmain(int argc, const TCHAR *argv[])
{
+ INT nExitCode;
+ LPCTSTR pCmdLine;
TCHAR startPath[MAX_PATH];
InitializeCriticalSection(&ChildProcessRunningLock);
@@ -2144,18 +2143,39 @@ int _tmain(int argc, const TCHAR *argv[])
CMD_ModuleHandle = GetModuleHandle(NULL);
- /* Perform general initialization, parse switches on command-line */
- Initialize();
+ /*
+ * Perform general initialization, parse switches on command-line.
+ * Initialize the exit code with the errorlevel as Initialize() can set it.
+ */
+ pCmdLine = Initialize();
+ nExitCode = nErrorLevel;
- /* Call prompt routine */
- ProcessInput();
+ if (pCmdLine && *pCmdLine)
+ {
+ TCHAR commandline[CMDLINE_LENGTH];
+
+ /* Do the /C or /K command */
+ GetCmdLineCommand(commandline, &pCmdLine[2], bAlwaysStrip);
+ nExitCode = ParseCommandLine(commandline);
+ if (fSingleCommand == 1)
+ {
+ // nErrorLevel = nExitCode;
+ bExit = TRUE;
+ }
+ fSingleCommand = 0;
+ }
+ if (!bExit)
+ {
+ /* Call prompt routine */
+ nExitCode = ProcessInput();
+ }
/* Do the cleanup */
Cleanup();
cmd_free(lpOriginalEnvironment);
- cmd_exit(nErrorLevel);
- return nErrorLevel;
+ cmd_exit(nExitCode);
+ return nExitCode;
}
/* EOF */
diff --git a/base/shell/cmd/internal.c b/base/shell/cmd/internal.c
index 9febc003cdc..175f4b452a7 100644
--- a/base/shell/cmd/internal.c
+++ b/base/shell/cmd/internal.c
@@ -548,13 +548,16 @@ INT CommandExit(LPTSTR param)
/* Search for an optional exit code */
while (_istspace(*param))
- param++;
+ ++param;
/* Set the errorlevel to the exit code */
if (_istdigit(*param))
+ {
nErrorLevel = _ttoi(param);
+ // if (fSingleCommand == 1) return nErrorLevel;
+ }
- return 0;
+ return (bExit ? nErrorLevel : 0);
}
#ifdef INCLUDE_CMD_REM