[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.
This commit is contained in:
Hermès Bélusca-Maïto 2020-07-30 01:44:43 +02:00
parent 4c9d322c68
commit d78e8029b8
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
4 changed files with 68 additions and 40 deletions

View file

@ -349,7 +349,7 @@ INT Batch(LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd)
/* Check if this is a "CALL :label" */ /* Check if this is a "CALL :label" */
if (*firstword == _T(':')) if (*firstword == _T(':'))
cmd_goto(firstword); ret = cmd_goto(firstword);
/* If we are calling from inside a FOR, hide the FOR variables */ /* If we are calling from inside a FOR, hide the FOR variables */
saved_fc = fc; saved_fc = fc;
@ -376,6 +376,7 @@ INT Batch(LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd)
} }
/* Stop all execution */ /* Stop all execution */
ExitAllBatches(); ExitAllBatches();
ret = 1;
break; break;
} }
@ -386,12 +387,11 @@ INT Batch(LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd)
FreeCommand(Cmd); FreeCommand(Cmd);
} }
/* Always return the current errorlevel */ /* Restore the FOR variables */
ret = nErrorLevel;
TRACE("Batch: returns TRUE\n");
fc = saved_fc; fc = saved_fc;
/* Always return the last command's return code */
TRACE("Batch: returns %d\n", ret);
return ret; return ret;
} }

View file

@ -69,10 +69,15 @@ INT cmd_call(LPTSTR param)
if (*first == _T(':') && bc) if (*first == _T(':') && bc)
{ {
INT ret;
/* CALL :label - call a subroutine of the current batch file */ /* CALL :label - call a subroutine of the current batch file */
while (*param == _T(' ')) while (*param == _T(' '))
param++; param++;
nErrorLevel = Batch(bc->BatchFilePath, first, param, NULL);
ret = Batch(bc->BatchFilePath, first, param, NULL);
nErrorLevel = (ret != 0 ? ret : nErrorLevel);
return nErrorLevel; return nErrorLevel;
} }

View file

@ -154,6 +154,7 @@ BOOL bCanExit = TRUE; /* Indicates if this shell is exitable */
BOOL bCtrlBreak = FALSE; /* Ctrl-Break or Ctrl-C hit */ BOOL bCtrlBreak = FALSE; /* Ctrl-Break or Ctrl-C hit */
BOOL bIgnoreEcho = FALSE; /* Set this to TRUE to prevent a newline, when executing a command */ 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 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 */ INT nErrorLevel = 0; /* Errorlevel of last launched external program */
CRITICAL_SECTION ChildProcessRunningLock; CRITICAL_SECTION ChildProcessRunningLock;
BOOL bDisableBatchEcho = FALSE; BOOL bDisableBatchEcho = FALSE;
@ -631,12 +632,17 @@ DoCommand(LPTSTR first, LPTSTR rest, PARSED_COMMAND *Cmd)
INT ParseCommandLine(LPTSTR cmd) INT ParseCommandLine(LPTSTR cmd)
{ {
INT Ret = 0; INT Ret = 0;
PARSED_COMMAND *Cmd = ParseCommand(cmd); PARSED_COMMAND *Cmd;
if (Cmd)
Cmd = ParseCommand(cmd);
if (!Cmd)
{ {
Ret = ExecuteCommand(Cmd); /* Return an adequate error code */
FreeCommand(Cmd); return (!bParseError ? 0 : 1);
} }
Ret = ExecuteCommand(Cmd);
FreeCommand(Cmd);
return Ret; return Ret;
} }
@ -1524,9 +1530,10 @@ ReadLine(TCHAR *commandline, BOOL bMore)
return SubstituteVars(ip, commandline, _T('%')); return SubstituteVars(ip, commandline, _T('%'));
} }
static VOID static INT
ProcessInput(VOID) ProcessInput(VOID)
{ {
INT Ret = 0;
PARSED_COMMAND *Cmd; PARSED_COMMAND *Cmd;
while (!bCanExit || !bExit) while (!bCanExit || !bExit)
@ -1538,9 +1545,11 @@ ProcessInput(VOID)
if (!Cmd) if (!Cmd)
continue; continue;
ExecuteCommand(Cmd); Ret = ExecuteCommand(Cmd);
FreeCommand(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) Initialize(VOID)
{ {
HMODULE NtDllModule; HMODULE NtDllModule;
// INT nExitCode;
HANDLE hIn, hOut; HANDLE hIn, hOut;
LPTSTR ptr, cmdLine; LPTSTR ptr, cmdLine;
TCHAR option = 0; TCHAR option = 0;
BOOL AlwaysStrip = FALSE;
BOOL AutoRun = TRUE; BOOL AutoRun = TRUE;
TCHAR ModuleName[MAX_PATH + 1]; TCHAR ModuleName[MAX_PATH + 1];
TCHAR commandline[CMDLINE_LENGTH];
/* Get version information */ /* Get version information */
InitOSVersion(); InitOSVersion();
@ -1957,7 +1964,7 @@ Initialize(VOID)
ConOutResPaging(TRUE, STRING_CMD_HELP8); ConOutResPaging(TRUE, STRING_CMD_HELP8);
nErrorLevel = 1; nErrorLevel = 1;
bExit = TRUE; bExit = TRUE;
return; return NULL;
} }
else if (option == _T('P')) else if (option == _T('P'))
{ {
@ -1996,7 +2003,7 @@ Initialize(VOID)
} }
else if (option == _T('S')) else if (option == _T('S'))
{ {
AlwaysStrip = TRUE; bAlwaysStrip = TRUE;
} }
#ifdef INCLUDE_CMD_COLOR #ifdef INCLUDE_CMD_COLOR
else if (!_tcsnicmp(ptr, _T("/T:"), 3)) else if (!_tcsnicmp(ptr, _T("/T:"), 3))
@ -2067,18 +2074,8 @@ Initialize(VOID)
ExecuteAutoRunFile(HKEY_CURRENT_USER); ExecuteAutoRunFile(HKEY_CURRENT_USER);
} }
if (*ptr) /* Returns the rest of the command line */
{ return ptr;
/* Do the /C or /K command */
GetCmdLineCommand(commandline, &ptr[2], AlwaysStrip);
/* nExitCode = */ ParseCommandLine(commandline);
if (fSingleCommand == 1)
{
// nErrorLevel = nExitCode;
bExit = TRUE;
}
fSingleCommand = 0;
}
} }
@ -2125,6 +2122,8 @@ static VOID Cleanup(VOID)
*/ */
int _tmain(int argc, const TCHAR *argv[]) int _tmain(int argc, const TCHAR *argv[])
{ {
INT nExitCode;
LPCTSTR pCmdLine;
TCHAR startPath[MAX_PATH]; TCHAR startPath[MAX_PATH];
InitializeCriticalSection(&ChildProcessRunningLock); InitializeCriticalSection(&ChildProcessRunningLock);
@ -2144,18 +2143,39 @@ int _tmain(int argc, const TCHAR *argv[])
CMD_ModuleHandle = GetModuleHandle(NULL); 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 */ if (pCmdLine && *pCmdLine)
ProcessInput(); {
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 */ /* Do the cleanup */
Cleanup(); Cleanup();
cmd_free(lpOriginalEnvironment); cmd_free(lpOriginalEnvironment);
cmd_exit(nErrorLevel); cmd_exit(nExitCode);
return nErrorLevel; return nExitCode;
} }
/* EOF */ /* EOF */

View file

@ -548,13 +548,16 @@ INT CommandExit(LPTSTR param)
/* Search for an optional exit code */ /* Search for an optional exit code */
while (_istspace(*param)) while (_istspace(*param))
param++; ++param;
/* Set the errorlevel to the exit code */ /* Set the errorlevel to the exit code */
if (_istdigit(*param)) if (_istdigit(*param))
{
nErrorLevel = _ttoi(param); nErrorLevel = _ttoi(param);
// if (fSingleCommand == 1) return nErrorLevel;
}
return 0; return (bExit ? nErrorLevel : 0);
} }
#ifdef INCLUDE_CMD_REM #ifdef INCLUDE_CMD_REM