[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" */
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;
}

View file

@ -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;
}

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 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 */

View file

@ -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