Author: jmorlan
Date: Mon Apr 13 03:51:15 2009
New Revision: 40474
URL:
http://svn.reactos.org/svn/reactos?rev=40474&view=rev
Log:
Various fixes to the way cmd handles errorlevel and the "return values" of
commands:
- Changed type of return values from BOOL to INT, with 0 indicating success and any other
value indicating failure. If the left side of an || operator returns nonzero, errorlevel
is set to that value.
- The return value of a batch file or FOR is the return value of the last command executed
in it. An empty batch file returns 0, unless it was CALLed, in which case it returns
errorlevel.
- CALL sets errorlevel to the return value of whatever was called.
- Running a GUI program doesn't change errorlevel, but always returns 0.
- CMD /C uses the command's return value, not errorlevel, as the process exit code.
Modified:
trunk/reactos/base/shell/cmd/batch.c
trunk/reactos/base/shell/cmd/batch.h
trunk/reactos/base/shell/cmd/call.c
trunk/reactos/base/shell/cmd/cmd.c
trunk/reactos/base/shell/cmd/cmd.h
trunk/reactos/base/shell/cmd/for.c
trunk/reactos/base/shell/cmd/if.c
Modified: trunk/reactos/base/shell/cmd/batch.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/batch.c?rev…
==============================================================================
--- trunk/reactos/base/shell/cmd/batch.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/batch.c [iso-8859-1] Mon Apr 13 03:51:15 2009
@@ -203,11 +203,12 @@
*
*/
-BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd)
+INT Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd)
{
BATCH_CONTEXT new;
LPFOR_CONTEXT saved_fc;
INT i;
+ INT ret = 0;
HANDLE hFile;
SetLastError(0);
@@ -221,7 +222,7 @@
if (hFile == INVALID_HANDLE_VALUE)
{
ConErrResPuts(STRING_BATCH_ERROR);
- return FALSE;
+ return 1;
}
if (bc != NULL && Cmd == bc->current)
@@ -238,10 +239,19 @@
else
{
struct _SETLOCAL *setlocal = NULL;
- /* If a batch file runs another batch file as part of a compound command
- * (e.g. "x.bat & somethingelse") then the first file gets terminated.
*/
- if (bc && Cmd != NULL)
+
+ if (Cmd == NULL)
{
+ /* This is a CALL. CALL will set errorlevel to our return value, so
+ * in order to keep the value of errorlevel unchanged in the case
+ * of calling an empty batch file, we must return that same value. */
+ ret = nErrorLevel;
+ }
+ else if (bc)
+ {
+ /* If a batch file runs another batch file as part of a compound command
+ * (e.g. "x.bat & somethingelse") then the first file gets terminated.
*/
+
/* Get its SETLOCAL stack so it can be migrated to the new context */
setlocal = bc->setlocal;
bc->setlocal = NULL;
@@ -272,7 +282,7 @@
if (bc->raw_params == NULL)
{
error_out_of_memory();
- return FALSE;
+ return 1;
}
/* Check if this is a "CALL :label" */
@@ -303,14 +313,14 @@
}
bc->current = Cmd;
- ExecuteCommand(Cmd);
+ ret = ExecuteCommand(Cmd);
FreeCommand(Cmd);
}
TRACE ("Batch: returns TRUE\n");
fc = saved_fc;
- return TRUE;
+ return ret;
}
VOID AddBatchRedirection(REDIRECTION **RedirList)
Modified: trunk/reactos/base/shell/cmd/batch.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/batch.h?rev…
==============================================================================
--- trunk/reactos/base/shell/cmd/batch.h [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/batch.h [iso-8859-1] Mon Apr 13 03:51:15 2009
@@ -47,7 +47,7 @@
LPTSTR FindArg (TCHAR, BOOL *);
LPTSTR BatchParams (LPTSTR, LPTSTR);
VOID ExitBatch ();
-BOOL Batch (LPTSTR, LPTSTR, LPTSTR, PARSED_COMMAND *);
+INT Batch (LPTSTR, LPTSTR, LPTSTR, PARSED_COMMAND *);
LPTSTR ReadBatchLine();
VOID AddBatchRedirection(REDIRECTION **);
Modified: trunk/reactos/base/shell/cmd/call.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/call.c?rev=…
==============================================================================
--- trunk/reactos/base/shell/cmd/call.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/call.c [iso-8859-1] Mon Apr 13 03:51:15 2009
@@ -73,10 +73,12 @@
/* CALL :label - call a subroutine of the current batch file */
while (*param == _T(' '))
param++;
- return !Batch(bc->BatchFilePath, first, param, NULL);
+ nErrorLevel = Batch(bc->BatchFilePath, first, param, NULL);
+ return nErrorLevel;
}
- return !DoCommand(first, param, NULL);
+ nErrorLevel = DoCommand(first, param, NULL);
+ return nErrorLevel;
}
/* EOF */
Modified: trunk/reactos/base/shell/cmd/cmd.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/cmd.c?rev=4…
==============================================================================
--- trunk/reactos/base/shell/cmd/cmd.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/cmd.c [iso-8859-1] Mon Apr 13 03:51:15 2009
@@ -308,7 +308,7 @@
* Rest - rest of command line
*/
-static BOOL
+static INT
Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd)
{
TCHAR szFullName[MAX_PATH];
@@ -363,7 +363,7 @@
}
if (!working) ConErrResPuts (STRING_FREE_ERROR1);
- return working;
+ return !working;
}
/* get the PATH environment variable and parse it */
@@ -372,7 +372,7 @@
if (!SearchForExecutable(First, szFullName))
{
error_bad_command(first);
- return FALSE;
+ return 1;
}
GetConsoleTitle (szWindowTitle, MAX_PATH);
@@ -384,7 +384,7 @@
while (*rest == _T(' '))
rest++;
TRACE ("[BATCH: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(rest));
- Batch (szFullName, first, rest, Cmd);
+ dwExitCode = Batch(szFullName, first, rest, Cmd);
}
else
{
@@ -448,17 +448,13 @@
GetExitCodeProcess (prci.hProcess, &dwExitCode);
nErrorLevel = (INT)dwExitCode;
}
- else
- {
- nErrorLevel = 0;
- }
CloseHandle (prci.hProcess);
}
else
{
TRACE ("[ShellExecute failed!: %s]\n", debugstr_aw(Full));
error_bad_command (first);
- nErrorLevel = 1;
+ dwExitCode = 1;
}
// restore console mode
@@ -472,7 +468,7 @@
OutputCodePage = GetConsoleOutputCP();
SetConsoleTitle (szWindowTitle);
- return nErrorLevel == 0;
+ return dwExitCode;
}
@@ -485,7 +481,7 @@
* rest - rest of command line
*/
-BOOL
+INT
DoCommand(LPTSTR first, LPTSTR rest, PARSED_COMMAND *Cmd)
{
TCHAR com[_tcslen(first) + _tcslen(rest) + 2]; /* full command line */
@@ -530,7 +526,7 @@
if (_tcsicmp(cmdptr->name, _T("echo")) != 0)
while (_istspace(*param))
param++;
- return !cmdptr->func(param);
+ return cmdptr->func(param);
}
}
@@ -543,14 +539,16 @@
* full input/output redirection and piping are supported
*/
-VOID ParseCommandLine (LPTSTR cmd)
-{
+INT ParseCommandLine (LPTSTR cmd)
+{
+ INT Ret = 0;
PARSED_COMMAND *Cmd = ParseCommand(cmd);
if (Cmd)
{
- ExecuteCommand(Cmd);
+ Ret = ExecuteCommand(Cmd);
FreeCommand(Cmd);
}
+ return Ret;
}
/* Execute a command without waiting for it to finish. If it's an internal
@@ -679,27 +677,27 @@
#endif
}
-BOOL
+INT
ExecuteCommand(PARSED_COMMAND *Cmd)
{
PARSED_COMMAND *Sub;
LPTSTR First, Rest;
- BOOL Success = TRUE;
+ INT Ret = 0;
if (!PerformRedirection(Cmd->Redirections))
- return FALSE;
+ return 1;
switch (Cmd->Type)
{
case C_COMMAND:
- Success = FALSE;
+ Ret = 1;
First = DoDelayedExpansion(Cmd->Command.First);
if (First)
{
Rest = DoDelayedExpansion(Cmd->Command.Rest);
if (Rest)
{
- Success = DoCommand(First, Rest, Cmd);
+ Ret = DoCommand(First, Rest, Cmd);
cmd_free(Rest);
}
cmd_free(First);
@@ -709,31 +707,36 @@
case C_BLOCK:
case C_MULTI:
for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next)
- Success = ExecuteCommand(Sub);
+ Ret = ExecuteCommand(Sub);
break;
case C_IFFAILURE:
+ Sub = Cmd->Subcommands;
+ Ret = ExecuteCommand(Sub);
+ if (Ret != 0)
+ {
+ nErrorLevel = Ret;
+ Ret = ExecuteCommand(Sub->Next);
+ }
+ break;
case C_IFSUCCESS:
Sub = Cmd->Subcommands;
- Success = ExecuteCommand(Sub);
- if (Success == (Cmd->Type - C_IFFAILURE))
- {
- Sub = Sub->Next;
- Success = ExecuteCommand(Sub);
- }
+ Ret = ExecuteCommand(Sub);
+ if (Ret == 0)
+ Ret = ExecuteCommand(Sub->Next);
break;
case C_PIPE:
ExecutePipeline(Cmd);
break;
case C_IF:
- Success = ExecuteIf(Cmd);
+ Ret = ExecuteIf(Cmd);
break;
case C_FOR:
- Success = ExecuteFor(Cmd);
+ Ret = ExecuteFor(Cmd);
break;
}
UndoRedirection(Cmd->Redirections, NULL);
- return Success;
+ return Ret;
}
BOOL
@@ -1402,7 +1405,7 @@
return SubstituteVars(ip, commandline, _T('%'));
}
-static INT
+static VOID
ProcessInput()
{
PARSED_COMMAND *Cmd;
@@ -1416,8 +1419,6 @@
ExecuteCommand(Cmd);
FreeCommand(Cmd);
}
-
- return nErrorLevel;
}
@@ -1614,6 +1615,7 @@
TCHAR commandline[CMDLINE_LENGTH];
TCHAR ModuleName[_MAX_PATH + 1];
TCHAR lpBuffer[2];
+ INT nExitCode;
//INT len;
TCHAR *ptr, *cmdLine, option = 0;
@@ -1760,9 +1762,12 @@
{
/* Do the /C or /K command */
GetCmdLineCommand(commandline, &ptr[2], AlwaysStrip);
- ParseCommandLine(commandline);
+ nExitCode = ParseCommandLine(commandline);
if (option != _T('K'))
+ {
+ nErrorLevel = nExitCode;
bExit = TRUE;
+ }
}
}
@@ -1806,7 +1811,6 @@
HANDLE hConsole;
TCHAR startPath[MAX_PATH];
CONSOLE_SCREEN_BUFFER_INFO Info;
- INT nExitCode;
lpOriginalEnvironment = DuplicateEnvironment();
@@ -1839,15 +1843,15 @@
Initialize();
/* call prompt routine */
- nExitCode = ProcessInput();
+ ProcessInput();
/* do the cleanup */
Cleanup();
cmd_free(lpOriginalEnvironment);
- cmd_exit(nExitCode);
- return(nExitCode);
+ cmd_exit(nErrorLevel);
+ return(nErrorLevel);
}
/* EOF */
Modified: trunk/reactos/base/shell/cmd/cmd.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/cmd.h?rev=4…
==============================================================================
--- trunk/reactos/base/shell/cmd/cmd.h [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/cmd.h [iso-8859-1] Mon Apr 13 03:51:15 2009
@@ -102,16 +102,16 @@
/* Prototypes for CMD.C */
INT ConvertULargeInteger(ULONGLONG num, LPTSTR des, INT len, BOOL bPutSeperator);
HANDLE RunFile(DWORD, LPTSTR, LPTSTR, LPTSTR, INT);
-VOID ParseCommandLine (LPTSTR);
+INT ParseCommandLine(LPTSTR);
struct _PARSED_COMMAND;
-BOOL ExecuteCommand(struct _PARSED_COMMAND *Cmd);
+INT ExecuteCommand(struct _PARSED_COMMAND *Cmd);
LPCTSTR GetEnvVarOrSpecial ( LPCTSTR varName );
VOID AddBreakHandler (VOID);
VOID RemoveBreakHandler (VOID);
BOOL SubstituteVars(TCHAR *Src, TCHAR *Dest, TCHAR Delim);
BOOL SubstituteForVars(TCHAR *Src, TCHAR *Dest);
LPTSTR DoDelayedExpansion(LPTSTR Line);
-BOOL DoCommand(LPTSTR first, LPTSTR rest, struct _PARSED_COMMAND *Cmd);
+INT DoCommand(LPTSTR first, LPTSTR rest, struct _PARSED_COMMAND *Cmd);
BOOL ReadLine(TCHAR *commandline, BOOL bMore);
int cmd_main (int argc, const TCHAR *argv[]);
@@ -254,7 +254,7 @@
#define FOR_LOOP 4 /* /L */
#define FOR_RECURSIVE 8 /* /R */
INT cmd_for (LPTSTR);
-BOOL ExecuteFor(struct _PARSED_COMMAND *Cmd);
+INT ExecuteFor(struct _PARSED_COMMAND *Cmd);
/* Prototypes for FREE.C */
@@ -283,7 +283,7 @@
enum { IF_CMDEXTVERSION, IF_DEFINED, IF_ERRORLEVEL, IF_EXIST,
IF_STRINGEQ, /* == */
IF_EQU, IF_GTR, IF_GEQ, IF_LSS, IF_LEQ, IF_NEQ };
-BOOL ExecuteIf(struct _PARSED_COMMAND *Cmd);
+INT ExecuteIf(struct _PARSED_COMMAND *Cmd);
/* Prototypes for INTERNAL.C */
Modified: trunk/reactos/base/shell/cmd/for.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/for.c?rev=4…
==============================================================================
--- trunk/reactos/base/shell/cmd/for.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/for.c [iso-8859-1] Mon Apr 13 03:51:15 2009
@@ -69,7 +69,7 @@
}
/* Execute a single instance of a FOR command */
-static void RunInstance(PARSED_COMMAND *Cmd)
+static INT RunInstance(PARSED_COMMAND *Cmd)
{
if (bEcho && !bDisableBatchEcho && Cmd->Subcommands->Type !=
C_QUIET)
{
@@ -80,7 +80,7 @@
ConOutChar(_T('\n'));
}
/* Just run the command (variable expansion is done in DoDelayedExpansion) */
- ExecuteCommand(Cmd->Subcommands);
+ return ExecuteCommand(Cmd->Subcommands);
}
/* Check if this FOR should be terminated early */
@@ -117,7 +117,7 @@
return Contents;
}
-static BOOL ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
+static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
{
LPTSTR Delims = _T(" \t");
TCHAR Eol = _T(';');
@@ -129,6 +129,7 @@
LPTSTR Variables[32];
TCHAR *Start, *End;
INT i;
+ INT Ret = 0;
if (Cmd->For.Params)
{
@@ -228,7 +229,7 @@
{
error:
error_syntax(Param);
- return FALSE;
+ return 1;
}
}
}
@@ -270,7 +271,7 @@
if (!InputFile)
{
error_bad_command(Start + 1);
- return FALSE;
+ return 1;
}
FullInput = ReadFileContents(InputFile, Buffer);
_pclose(InputFile);
@@ -286,7 +287,7 @@
if (!InputFile)
{
error_sfile_not_found(Start);
- return FALSE;
+ return 1;
}
FullInput = ReadFileContents(InputFile, Buffer);
fclose(InputFile);
@@ -295,7 +296,7 @@
if (!FullInput)
{
error_out_of_memory();
- return FALSE;
+ return 1;
}
/* Loop over the input line by line */
@@ -337,20 +338,21 @@
/* Don't run unless the line had enough tokens to fill at least one variable */
if (*Variables[0])
- RunInstance(Cmd);
+ Ret = RunInstance(Cmd);
} while (!Exiting(Cmd) && (In = NextLine) != NULL);
cmd_free(FullInput);
}
- return TRUE;
+ return Ret;
}
/* FOR /L: Do a numeric loop */
-static void ForLoop(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
+static INT ForLoop(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
{
enum { START, STEP, END };
INT params[3] = { 0, 0, 0 };
INT i;
+ INT Ret = 0;
TCHAR *Start, *End = List;
for (i = 0; i < 3 && GetNextElement(&Start, &End); i++)
@@ -361,17 +363,19 @@
(params[STEP] >= 0 ? (i <= params[END]) : (i >= params[END])))
{
_itot(i, Buffer, 10);
- RunInstance(Cmd);
+ Ret = RunInstance(Cmd);
i += params[STEP];
}
+ return Ret;
}
/* Process a FOR in one directory. Stored in Buffer (up to BufPos) is a
* string which is prefixed to each element of the list. In a normal FOR
* it will be empty, but in FOR /R it will be the directory name. */
-static void ForDir(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos)
+static INT ForDir(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos)
{
TCHAR *Start, *End = List;
+ INT Ret = 0;
while (!Exiting(Cmd) && GetNextElement(&Start, &End))
{
if (BufPos + (End - Start) > &Buffer[CMDLINE_LENGTH])
@@ -402,22 +406,24 @@
_tcscmp(w32fd.cFileName, _T("..")) == 0)
continue;
_tcscpy(FilePart, w32fd.cFileName);
- RunInstance(Cmd);
+ Ret = RunInstance(Cmd);
} while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd));
FindClose(hFind);
}
else
{
- RunInstance(Cmd);
- }
- }
+ Ret = RunInstance(Cmd);
+ }
+ }
+ return Ret;
}
/* FOR /R: Process a FOR in each directory of a tree, recursively. */
-static void ForRecursive(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos)
+static INT ForRecursive(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos)
{
HANDLE hFind;
WIN32_FIND_DATA w32fd;
+ INT Ret = 0;
if (BufPos[-1] != _T('\\'))
{
@@ -425,12 +431,12 @@
*BufPos = _T('\0');
}
- ForDir(Cmd, List, Buffer, BufPos);
+ Ret = ForDir(Cmd, List, Buffer, BufPos);
_tcscpy(BufPos, _T("*"));
hFind = FindFirstFile(Buffer, &w32fd);
if (hFind == INVALID_HANDLE_VALUE)
- return;
+ return Ret;
do
{
if (!(w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
@@ -438,9 +444,10 @@
if (_tcscmp(w32fd.cFileName, _T(".")) == 0 ||
_tcscmp(w32fd.cFileName, _T("..")) == 0)
continue;
- ForRecursive(Cmd, List, Buffer, _stpcpy(BufPos, w32fd.cFileName));
+ Ret = ForRecursive(Cmd, List, Buffer, _stpcpy(BufPos, w32fd.cFileName));
} while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd));
FindClose(hFind);
+ return Ret;
}
BOOL
@@ -449,18 +456,18 @@
TCHAR Buffer[CMDLINE_LENGTH]; /* Buffer to hold the variable value */
LPTSTR BufferPtr = Buffer;
LPFOR_CONTEXT lpNew;
- BOOL Success = TRUE;
+ INT Ret;
LPTSTR List = DoDelayedExpansion(Cmd->For.List);
if (!List)
- return FALSE;
+ return 1;
/* Create our FOR context */
lpNew = cmd_alloc(sizeof(FOR_CONTEXT));
if (!lpNew)
{
cmd_free(List);
- return FALSE;
+ return 1;
}
lpNew->prev = fc;
lpNew->firstvar = Cmd->For.Variable;
@@ -472,21 +479,21 @@
if (Cmd->For.Switches & FOR_F)
{
- Success = ForF(Cmd, List, Buffer);
+ Ret = ForF(Cmd, List, Buffer);
}
else if (Cmd->For.Switches & FOR_LOOP)
{
- ForLoop(Cmd, List, Buffer);
+ Ret = ForLoop(Cmd, List, Buffer);
}
else if (Cmd->For.Switches & FOR_RECURSIVE)
{
DWORD Len = GetFullPathName(Cmd->For.Params ? Cmd->For.Params :
_T("."),
MAX_PATH, Buffer, NULL);
- ForRecursive(Cmd, List, Buffer, &Buffer[Len]);
+ Ret = ForRecursive(Cmd, List, Buffer, &Buffer[Len]);
}
else
{
- ForDir(Cmd, List, Buffer, Buffer);
+ Ret = ForDir(Cmd, List, Buffer, Buffer);
}
/* Remove our context, unless someone already did that */
@@ -495,7 +502,7 @@
cmd_free(lpNew);
cmd_free(List);
- return Success;
+ return Ret;
}
/* EOF */
Modified: trunk/reactos/base/shell/cmd/if.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/if.c?rev=40…
==============================================================================
--- trunk/reactos/base/shell/cmd/if.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/if.c [iso-8859-1] Mon Apr 13 03:51:15 2009
@@ -63,7 +63,7 @@
return 1;
}
-BOOL ExecuteIf(PARSED_COMMAND *Cmd)
+INT ExecuteIf(PARSED_COMMAND *Cmd)
{
INT result = FALSE; /* when set cause 'then' clause to be executed */
LPTSTR param;
@@ -73,13 +73,13 @@
{
Left = DoDelayedExpansion(Cmd->If.LeftArg);
if (!Left)
- return FALSE;
+ return 1;
}
Right = DoDelayedExpansion(Cmd->If.RightArg);
if (!Right)
{
cmd_free(Left);
- return FALSE;
+ return 1;
}
if (Cmd->If.Operator == IF_CMDEXTVERSION)
@@ -91,7 +91,7 @@
{
error_syntax(Right);
cmd_free(Right);
- return FALSE;
+ return 1;
}
result = (2 >= n);
}
@@ -108,7 +108,7 @@
{
error_syntax(Right);
cmd_free(Right);
- return FALSE;
+ return 1;
}
result = (nErrorLevel >= n);
}
@@ -166,7 +166,7 @@
/* full condition was false, do the "else" command if there is one */
if (Cmd->Subcommands->Next)
return ExecuteCommand(Cmd->Subcommands->Next);
- return TRUE;
+ return 0;
}
}