reactos/base/shell/cmd/if.c
Hermès Bélusca-Maïto 6eb1cae348
[CMD] Fixes for Batch error execution control flow.
CORE-13713 CORE-13736

- In case execution of all batch contexts is stopped (by selecting "All"
  at the Ctrl-C/Ctrl-Break prompt), notify as well the CheckCtrlBreak()
  signal handler once there are no more batch contexts (this in effect
  resets the internal 'bLeaveAll' static flag in CheckCtrlBreak).
  This is an adaptation of the fix present in FreeCOM 1.5, first
  described in https://gcfl.net/FreeDOS/command.com/bugs074g.html .

- Introduce a ParseErrorEx() helper that sets the 'bParseError' flag and
  displays a customized syntax-error message, only for the first syntax
  error encountered. Implement ParseError() around the *Ex function.

- In batch mode, echo the original pre-parsed batch file line if a parse
  error has been encountered.

- When running a compound command - including IF, FOR, command blocks -,
  and that control flow is modified by any CALL/GOTO/EXIT command,
  detect this while running the compound command so as to stop it and go
  back to the main batch execution loop, that will then set up the actual
  new command to run.

- In GOTO, do not process any more parts of a compound command only when
  we have found a valid label.
2020-08-19 20:35:58 +02:00

195 lines
4.8 KiB
C

/*
* IF.C - if internal batch command.
*
*
* History:
*
* 16 Jul 1998 (Hans B Pufal)
* started.
*
* 16 Jul 1998 (John P Price)
* Separated commands into individual files.
*
* 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
* added config.h include
*
* 07-Jan-1999 (Eric Kohl)
* Added help text ("if /?") and cleaned up.
*
* 21-Jan-1999 (Eric Kohl)
* Unicode and redirection ready!
*
* 01-Sep-1999 (Eric Kohl)
* Fixed help text.
*
* 17-Feb-2001 (ea)
* IF DEFINED variable command
*
* 28-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>
* Remove all hardcoded strings in En.rc
*
*/
#include "precomp.h"
static INT GenericCmp(INT (*StringCmp)(LPCTSTR, LPCTSTR),
LPCTSTR Left, LPCTSTR Right)
{
TCHAR *end;
INT nLeft = _tcstol(Left, &end, 0);
if (*end == _T('\0'))
{
INT nRight = _tcstol(Right, &end, 0);
if (*end == _T('\0'))
{
/* both arguments are numeric */
return (nLeft < nRight) ? -1 : (nLeft > nRight);
}
}
return StringCmp(Left, Right);
}
INT cmd_if(LPTSTR param)
{
TRACE("cmd_if(\'%s\')\n", debugstr_aw(param));
if (!_tcsncmp (param, _T("/?"), 2))
{
ConOutResPaging(TRUE, STRING_IF_HELP1);
return 0;
}
error_syntax(param);
return 1;
}
INT ExecuteIf(PARSED_COMMAND *Cmd)
{
INT result = FALSE; /* when set cause 'then' clause to be executed */
LPTSTR param;
LPTSTR Left = NULL, Right;
if (Cmd->If.LeftArg)
{
Left = DoDelayedExpansion(Cmd->If.LeftArg);
if (!Left)
return 1;
}
Right = DoDelayedExpansion(Cmd->If.RightArg);
if (!Right)
{
cmd_free(Left);
return 1;
}
if (Cmd->If.Operator == IF_CMDEXTVERSION)
{
/* IF CMDEXTVERSION n: check if Command Extensions version
* is greater or equal to n */
DWORD n = _tcstoul(Right, &param, 10);
if (*param != _T('\0'))
{
error_syntax(Right);
cmd_free(Right);
return 1;
}
result = (2 >= n);
}
else if (Cmd->If.Operator == IF_DEFINED)
{
/* IF DEFINED var: check if environment variable exists */
result = (GetEnvVarOrSpecial(Right) != NULL);
}
else if (Cmd->If.Operator == IF_ERRORLEVEL)
{
/* IF ERRORLEVEL n: check if last exit code is greater or equal to n */
INT n = _tcstol(Right, &param, 10);
if (*param != _T('\0'))
{
error_syntax(Right);
cmd_free(Right);
return 1;
}
result = (nErrorLevel >= n);
}
else if (Cmd->If.Operator == IF_EXIST)
{
BOOL IsDir;
INT Size;
WIN32_FIND_DATA f;
HANDLE hFind;
DWORD attrs;
/* IF EXIST filename: check if file exists (wildcards allowed) */
StripQuotes(Right);
Size = _tcslen(Right);
IsDir = (Right[Size - 1] == '\\');
if (IsDir)
Right[Size - 1] = 0;
hFind = FindFirstFile(Right, &f);
if (hFind != INVALID_HANDLE_VALUE)
{
attrs = f.dwFileAttributes;
FindClose(hFind);
}
else
{
/* FindFirstFile fails at the root directory. */
attrs = GetFileAttributes(Right);
}
if (attrs == 0xFFFFFFFF)
result = FALSE;
else if (IsDir)
result = ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY);
else
result = TRUE;
if (IsDir)
Right[Size - 1] = '\\';
}
else
{
/* Do case-insensitive string comparisons if /I specified */
INT (*StringCmp)(LPCTSTR, LPCTSTR) =
(Cmd->If.Flags & IFFLAG_IGNORECASE) ? _tcsicmp : _tcscmp;
if (Cmd->If.Operator == IF_STRINGEQ)
{
/* IF str1 == str2 */
result = StringCmp(Left, Right) == 0;
}
else
{
result = GenericCmp(StringCmp, Left, Right);
switch (Cmd->If.Operator)
{
case IF_EQU: result = (result == 0); break;
case IF_NEQ: result = (result != 0); break;
case IF_LSS: result = (result < 0); break;
case IF_LEQ: result = (result <= 0); break;
case IF_GTR: result = (result > 0); break;
case IF_GEQ: result = (result >= 0); break;
}
}
}
cmd_free(Left);
cmd_free(Right);
if (result ^ ((Cmd->If.Flags & IFFLAG_NEGATE) != 0))
{
/* Full condition was true, do the command */
return ExecuteCommand(Cmd->Subcommands);
}
else
{
/* Full condition was false, do the "else" command if there is one */
return ExecuteCommand(Cmd->Subcommands->Next);
}
}
/* EOF */