mirror of
https://github.com/reactos/reactos.git
synced 2025-02-20 15:35:04 +00:00
- Make IF command a special form; necessary to make nested multi-line IF blocks work properly
- Implement IF /I option, IF CMDEXTVERSION, and generic comparisons (EQU etc) - Make IF ERRORLEVEL return true if the errorlevel is greater than the number - Remove hacked support for multi-line IF blocks from batch.c svn path=/trunk/; revision=38280
This commit is contained in:
parent
940dd12ea9
commit
781ae61f03
7 changed files with 289 additions and 224 deletions
|
@ -269,7 +269,6 @@ BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, BOOL forcenew)
|
|||
SetFilePointer (bc->hBatchFile, 0, NULL, FILE_BEGIN);
|
||||
bc->bEcho = bEcho; /* Preserve echo across batch calls */
|
||||
bc->shiftlevel = 0;
|
||||
bc->bCmdBlock = -1;
|
||||
|
||||
bc->ffind = NULL;
|
||||
bc->forvar = _T('\0');
|
||||
|
@ -445,39 +444,6 @@ LPTSTR ReadBatchLine ()
|
|||
|
||||
first = textline;
|
||||
|
||||
/* cmd block over multiple lines (..) */
|
||||
if (bc->bCmdBlock >= 0)
|
||||
{
|
||||
if (*first == _T(')'))
|
||||
{
|
||||
first++;
|
||||
/* Strip leading spaces and trailing space/control chars */
|
||||
while(_istspace (*first))
|
||||
first++;
|
||||
if ((_tcsncicmp (first, _T("else"), 4) == 0) && (_tcschr(first, _T('('))))
|
||||
{
|
||||
bc->bExecuteBlock[bc->bCmdBlock] = !bc->bExecuteBlock[bc->bCmdBlock];
|
||||
}
|
||||
else
|
||||
{
|
||||
bc->bCmdBlock--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (bc->bCmdBlock < MAX_PATH)
|
||||
if (!bc->bExecuteBlock[bc->bCmdBlock])
|
||||
{
|
||||
/* increase the bCmdBlock count when there is another conditon which opens a new bracket */
|
||||
if ((_tcsncicmp (first, _T("if"), 2) == 0) && _tcschr(first, _T('(')))
|
||||
{
|
||||
bc->bCmdBlock++;
|
||||
if ((bc->bCmdBlock > 0) && (bc->bCmdBlock < MAX_PATH))
|
||||
bc->bExecuteBlock[bc->bCmdBlock] = bc->bExecuteBlock[bc->bCmdBlock - 1];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@ typedef struct tagBATCHCONTEXT
|
|||
HANDLE hFind; /* Preserve find handle when doing a for */
|
||||
REDIRECTION *RedirList;
|
||||
TCHAR forvar;
|
||||
INT bCmdBlock;
|
||||
BOOL bExecuteBlock[MAX_PATH];
|
||||
} BATCH_CONTEXT, *LPBATCH_CONTEXT;
|
||||
|
||||
|
||||
|
|
|
@ -879,7 +879,7 @@ ExecuteCommand(PARSED_COMMAND *Cmd)
|
|||
if(bc)
|
||||
bNewBatch = FALSE;
|
||||
|
||||
Success = DoCommand(Cmd->CommandLine);
|
||||
Success = DoCommand(Cmd->Command.CommandLine);
|
||||
|
||||
if(bNewBatch && bc)
|
||||
AddBatchRedirection(&Cmd->Redirections);
|
||||
|
@ -903,6 +903,9 @@ ExecuteCommand(PARSED_COMMAND *Cmd)
|
|||
case C_PIPE:
|
||||
ExecutePipeline(Cmd);
|
||||
break;
|
||||
case C_IF:
|
||||
Success = ExecuteIf(Cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
UndoRedirection(Cmd->Redirections, NULL);
|
||||
|
|
|
@ -258,6 +258,15 @@ INT CommandHistory (LPTSTR param);
|
|||
#endif
|
||||
|
||||
|
||||
/* Prototypes for IF.C */
|
||||
#define IFFLAG_NEGATE 1 /* NOT */
|
||||
#define IFFLAG_IGNORECASE 2 /* /I */
|
||||
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);
|
||||
|
||||
|
||||
/* Prototypes for INTERNAL.C */
|
||||
VOID InitLastPath (VOID);
|
||||
VOID FreeLastPath (VOID);
|
||||
|
@ -330,15 +339,28 @@ INT CommandMsgbox (LPTSTR);
|
|||
|
||||
|
||||
/* Prototypes from PARSER.C */
|
||||
enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS, C_PIPE };
|
||||
enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS, C_PIPE, C_IF };
|
||||
typedef struct _PARSED_COMMAND
|
||||
{
|
||||
struct _PARSED_COMMAND *Subcommands;
|
||||
struct _PARSED_COMMAND *Next;
|
||||
struct _REDIRECTION *Redirections;
|
||||
TCHAR *Tail;
|
||||
BYTE Type;
|
||||
TCHAR CommandLine[];
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
TCHAR *Tail;
|
||||
TCHAR CommandLine[];
|
||||
} Command;
|
||||
struct
|
||||
{
|
||||
BYTE Flags;
|
||||
BYTE Operator;
|
||||
TCHAR *LeftArg;
|
||||
TCHAR *RightArg;
|
||||
} If;
|
||||
};
|
||||
} PARSED_COMMAND;
|
||||
PARSED_COMMAND *ParseCommand(LPTSTR Line);
|
||||
VOID EchoCommand(PARSED_COMMAND *Cmd);
|
||||
|
|
|
@ -120,7 +120,7 @@ COMMAND cmds[] =
|
|||
{_T("history"), 0, CommandHistory},
|
||||
#endif
|
||||
|
||||
{_T("if"), 0, cmd_if},
|
||||
// {_T("if"), 0, cmd_if},
|
||||
|
||||
#ifdef INCLUDE_CMD_LABEL
|
||||
{_T("label"), 0, cmd_label},
|
||||
|
|
|
@ -32,15 +32,30 @@
|
|||
|
||||
#include <precomp.h>
|
||||
|
||||
|
||||
#define X_EXEC 1
|
||||
#define X_EMPTY 0x80
|
||||
|
||||
INT cmd_if (LPTSTR param)
|
||||
static INT GenericCmp(INT (*StringCmp)(LPCTSTR, LPCTSTR),
|
||||
LPCTSTR Left, LPCTSTR Right)
|
||||
{
|
||||
INT x_flag = 0; /* when set cause 'then' clause to be executed */
|
||||
LPTSTR pp;
|
||||
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);
|
||||
}
|
||||
|
||||
BOOL ExecuteIf(PARSED_COMMAND *Cmd)
|
||||
{
|
||||
INT result = FALSE; /* when set cause 'then' clause to be executed */
|
||||
LPTSTR param;
|
||||
|
||||
#if 0
|
||||
/* FIXME: need to handle IF /?; will require special parsing */
|
||||
TRACE ("cmd_if: (\'%s\')\n", debugstr_aw(param));
|
||||
|
||||
if (!_tcsncmp (param, _T("/?"), 2))
|
||||
|
@ -48,178 +63,89 @@ INT cmd_if (LPTSTR param)
|
|||
ConOutResPaging(TRUE,STRING_IF_HELP1);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* First check if param string begins with 'not' */
|
||||
if (!_tcsnicmp (param, _T("not"), 3) && _istspace (*(param + 3)))
|
||||
if (Cmd->If.Operator == IF_CMDEXTVERSION)
|
||||
{
|
||||
x_flag = X_EXEC; /* Remember 'NOT' */
|
||||
param += 3; /* Step over 'NOT' */
|
||||
while (_istspace (*param)) /* And subsequent spaces */
|
||||
param++;
|
||||
/* IF CMDEXTVERSION n: check if Command Extensions version
|
||||
* is greater or equal to n */
|
||||
DWORD n = _tcstoul(Cmd->If.RightArg, ¶m, 10);
|
||||
if (*param != _T('\0'))
|
||||
{
|
||||
error_syntax(Cmd->If.RightArg);
|
||||
return FALSE;
|
||||
}
|
||||
result = (2 >= n);
|
||||
}
|
||||
|
||||
/* Check for 'exist' form */
|
||||
if (!_tcsnicmp (param, _T("exist"), 5) && _istspace (*(param + 5)))
|
||||
else if (Cmd->If.Operator == IF_DEFINED)
|
||||
{
|
||||
UINT i;
|
||||
BOOL bInside = FALSE;
|
||||
|
||||
param += 5;
|
||||
while (_istspace (*param))
|
||||
param++;
|
||||
|
||||
pp = param;
|
||||
|
||||
/* find the whole path to the file */
|
||||
for(i = 0; i < _tcslen(param); i++)
|
||||
{
|
||||
if(param[i] == _T('\"'))
|
||||
bInside = !bInside;
|
||||
if((param[i] == _T(' ')) && !bInside)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pp++;
|
||||
}
|
||||
*pp++ = _T('\0');
|
||||
i = 0;
|
||||
/* remove quotes */
|
||||
while(i < _tcslen(param))
|
||||
{
|
||||
if(param[i] == _T('\"'))
|
||||
memmove(¶m[i],¶m[i + 1], _tcslen(¶m[i]) * sizeof(TCHAR));
|
||||
else
|
||||
i++;
|
||||
}
|
||||
|
||||
if (*pp)
|
||||
{
|
||||
WIN32_FIND_DATA f;
|
||||
HANDLE hFind;
|
||||
|
||||
hFind = FindFirstFile (param, &f);
|
||||
x_flag ^= (hFind == INVALID_HANDLE_VALUE) ? 0 : X_EXEC;
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose (hFind);
|
||||
}
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
/* IF DEFINED var: check if environment variable exists */
|
||||
result = (GetEnvVarOrSpecial(Cmd->If.RightArg) != NULL);
|
||||
}
|
||||
else if (!_tcsnicmp (param, _T("defined"), 7) && _istspace (*(param + 7)))
|
||||
else if (Cmd->If.Operator == IF_ERRORLEVEL)
|
||||
{
|
||||
/* Check for 'defined' form */
|
||||
TCHAR Value [1];
|
||||
INT ValueSize = 0;
|
||||
|
||||
param += 7;
|
||||
/* IF [NOT] DEFINED var COMMAND */
|
||||
/* ^ */
|
||||
while (_istspace (*param))
|
||||
param++;
|
||||
/* IF [NOT] DEFINED var COMMAND */
|
||||
/* ^ */
|
||||
pp = param;
|
||||
while (*pp && !_istspace (*pp))
|
||||
pp++;
|
||||
/* IF [NOT] DEFINED var COMMAND */
|
||||
/* ^ */
|
||||
if (*pp)
|
||||
/* IF ERRORLEVEL n: check if last exit code is greater or equal to n */
|
||||
INT n = _tcstol(Cmd->If.RightArg, ¶m, 10);
|
||||
if (*param != _T('\0'))
|
||||
{
|
||||
*pp++ = _T('\0');
|
||||
ValueSize = GetEnvironmentVariable(param, Value, sizeof(Value) / sizeof(Value[0]));
|
||||
x_flag ^= (0 == ValueSize)
|
||||
? 0
|
||||
: X_EXEC;
|
||||
x_flag |= X_EMPTY;
|
||||
error_syntax(Cmd->If.RightArg);
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
result = (nErrorLevel >= n);
|
||||
}
|
||||
else if (!_tcsnicmp (param, _T("errorlevel"), 10) && _istspace (*(param + 10)))
|
||||
else if (Cmd->If.Operator == IF_EXIST)
|
||||
{
|
||||
/* Check for 'errorlevel' form */
|
||||
INT n = 0;
|
||||
/* IF EXIST filename: check if file exists (wildcards allowed) */
|
||||
WIN32_FIND_DATA f;
|
||||
HANDLE hFind;
|
||||
|
||||
pp = param + 10;
|
||||
while (_istspace (*pp))
|
||||
pp++;
|
||||
StripQuotes(Cmd->If.RightArg);
|
||||
|
||||
while (_istdigit (*pp))
|
||||
n = n * 10 + (*pp++ - _T('0'));
|
||||
|
||||
x_flag ^= (nErrorLevel != n) ? 0 : X_EXEC;
|
||||
|
||||
x_flag |= X_EMPTY; /* Syntax error if comd empty */
|
||||
hFind = FindFirstFile(Cmd->If.RightArg, &f);
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
result = TRUE;
|
||||
FindClose(hFind);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOL bInQuote = FALSE;
|
||||
INT p1len;
|
||||
pp = param;
|
||||
while ( *pp && ( bInQuote || *pp != _T('=') ) )
|
||||
{
|
||||
if ( *pp == _T('\"') )
|
||||
bInQuote = !bInQuote;
|
||||
++pp;
|
||||
}
|
||||
p1len = pp-param;
|
||||
/* check for "==" */
|
||||
if ( *pp++ != _T('=') || *pp++ != _T('=') )
|
||||
{
|
||||
error_syntax ( NULL );
|
||||
return 1;
|
||||
}
|
||||
while (_istspace (*pp)) /* Skip subsequent spaces */
|
||||
pp++;
|
||||
/* Do case-insensitive string comparisons if /I specified */
|
||||
INT (*StringCmp)(LPCTSTR, LPCTSTR) =
|
||||
(Cmd->If.Flags & IFFLAG_IGNORECASE) ? _tcsicmp : _tcscmp;
|
||||
|
||||
/* are the two sides equal*/
|
||||
if ( !_tcsncmp(param,pp,p1len))
|
||||
x_flag ^= X_EXEC;
|
||||
pp += p1len;
|
||||
|
||||
if ( x_flag )
|
||||
if (Cmd->If.Operator == IF_STRINGEQ)
|
||||
{
|
||||
x_flag |= X_EMPTY;
|
||||
/* IF str1 == str2 */
|
||||
result = StringCmp(Cmd->If.LeftArg, Cmd->If.RightArg) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = GenericCmp(StringCmp, Cmd->If.LeftArg, Cmd->If.RightArg);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (_istspace (*pp)) /* skip spaces */
|
||||
pp++;
|
||||
|
||||
if (*pp == _T('('))
|
||||
if (result ^ ((Cmd->If.Flags & IFFLAG_NEGATE) != 0))
|
||||
{
|
||||
if (bc)
|
||||
{
|
||||
pp++;
|
||||
bc->bCmdBlock++;
|
||||
if ((bc->bCmdBlock >= 0) && (bc->bCmdBlock < MAX_PATH))
|
||||
bc->bExecuteBlock[bc->bCmdBlock] = x_flag & X_EXEC;
|
||||
/* commands are in the next lines */
|
||||
if (*pp == _T('\0'))
|
||||
return 0;
|
||||
}
|
||||
/* full condition was true, do the command */
|
||||
return ExecuteCommand(Cmd->Subcommands);
|
||||
}
|
||||
|
||||
if (x_flag & X_EMPTY)
|
||||
else
|
||||
{
|
||||
while (_istspace (*pp)) /* Then skip spaces */
|
||||
pp++;
|
||||
|
||||
if (*pp == _T('\0')) /* If nothing left then syntax err */
|
||||
{
|
||||
error_syntax (NULL);
|
||||
return 1;
|
||||
}
|
||||
/* full condition was false, do the "else" command if there is one */
|
||||
if (Cmd->Subcommands->Next)
|
||||
return ExecuteCommand(Cmd->Subcommands->Next);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (x_flag & X_EXEC)
|
||||
{
|
||||
ParseCommandLine (pp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* PARSER.C - command parsing.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <precomp.h>
|
||||
|
||||
#define C_OP_LOWEST C_MULTI
|
||||
|
@ -6,10 +11,28 @@ static const TCHAR OpString[][3] = { _T("&"), _T("||"), _T("&&"), _T("|") };
|
|||
|
||||
static const TCHAR RedirString[][3] = { _T("<"), _T(">"), _T(">>") };
|
||||
|
||||
static const TCHAR *const IfOperatorString[] = {
|
||||
_T("cmdextversion"),
|
||||
_T("defined"),
|
||||
_T("errorlevel"),
|
||||
_T("exist"),
|
||||
#define IF_MAX_UNARY IF_EXIST
|
||||
_T("=="),
|
||||
_T("equ"),
|
||||
_T("gtr"),
|
||||
_T("geq"),
|
||||
_T("lss"),
|
||||
_T("leq"),
|
||||
_T("neq"),
|
||||
#define IF_MAX_COMPARISON IF_NEQ
|
||||
};
|
||||
|
||||
/* These three characters act like spaces to the parser in most contexts */
|
||||
#define STANDARD_SEPS _T(",;=")
|
||||
|
||||
static BOOL IsSeparator(TCHAR Char)
|
||||
{
|
||||
/* These three characters act like spaces to the parser */
|
||||
return _istspace(Char) || (Char && _tcschr(_T(",;="), Char));
|
||||
return _istspace(Char) || (Char && _tcschr(STANDARD_SEPS, Char));
|
||||
}
|
||||
|
||||
enum { TOK_END, TOK_NORMAL, TOK_OPERATOR, TOK_REDIRECTION,
|
||||
|
@ -70,36 +93,44 @@ static void ParseError()
|
|||
|
||||
/* Yes, cmd has a Lexical Analyzer. Whenever the parser gives an "xxx was
|
||||
* unexpected at this time." message, it shows what the last token read was */
|
||||
static int ParseToken(TCHAR ExtraEnd, BOOL PreserveSpace)
|
||||
static int ParseToken(TCHAR ExtraEnd, TCHAR *Separators)
|
||||
{
|
||||
TCHAR *Out = CurrentToken;
|
||||
TCHAR Char = CurChar;
|
||||
TCHAR Char;
|
||||
int Type;
|
||||
BOOL bInQuote = FALSE;
|
||||
|
||||
if (!PreserveSpace)
|
||||
{
|
||||
while (Char != _T('\n') && IsSeparator(Char))
|
||||
Char = ParseChar();
|
||||
}
|
||||
|
||||
while (Char && Char != _T('\n'))
|
||||
for (Char = CurChar; Char && Char != _T('\n'); Char = ParseChar())
|
||||
{
|
||||
bInQuote ^= (Char == _T('"'));
|
||||
if (!bInQuote)
|
||||
{
|
||||
/* Check for all the myriad ways in which this token
|
||||
* may be brought to an untimely end. */
|
||||
if (Separators != NULL)
|
||||
{
|
||||
if (_istspace(Char) || _tcschr(Separators, Char))
|
||||
{
|
||||
/* Skip leading separators */
|
||||
if (Out == CurrentToken)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for numbered redirection */
|
||||
if ((Char >= _T('0') && Char <= _T('9') &&
|
||||
(ParsePos == &ParseLine[1] || IsSeparator(ParsePos[-2]))
|
||||
&& (*ParsePos == _T('<') || *ParsePos == _T('>')))
|
||||
|| _tcschr(_T(")&|<>") + (InsideBlock ? 0 : 1), Char)
|
||||
|| (!PreserveSpace && IsSeparator(Char))
|
||||
|| (Char == ExtraEnd))
|
||||
&& (*ParsePos == _T('<') || *ParsePos == _T('>'))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Char == ExtraEnd)
|
||||
break;
|
||||
if (InsideBlock && Char == _T(')'))
|
||||
break;
|
||||
if (_tcschr(_T("&|<>"), Char))
|
||||
break;
|
||||
|
||||
if (Char == _T('^'))
|
||||
{
|
||||
Char = ParseChar();
|
||||
|
@ -112,7 +143,6 @@ static int ParseToken(TCHAR ExtraEnd, BOOL PreserveSpace)
|
|||
if (Out == &CurrentToken[CMDLINE_LENGTH - 1])
|
||||
break;
|
||||
*Out++ = Char;
|
||||
Char = ParseChar();
|
||||
}
|
||||
|
||||
/* Check if we got at least one character before reaching a special one.
|
||||
|
@ -214,7 +244,7 @@ static BOOL ParseRedirection(REDIRECTION **List)
|
|||
if (!*Tok)
|
||||
{
|
||||
/* The file name was not part of this token, so it'll be the next one */
|
||||
if (ParseToken(0, FALSE) != TOK_NORMAL)
|
||||
if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL)
|
||||
goto fail;
|
||||
Tok = CurrentToken;
|
||||
}
|
||||
|
@ -284,7 +314,7 @@ static PARSED_COMMAND *ParseBlock(REDIRECTION *RedirList)
|
|||
InsideBlock--;
|
||||
|
||||
/* Process any trailing redirections */
|
||||
while (ParseToken(0, FALSE) == TOK_REDIRECTION)
|
||||
while (ParseToken(0, STANDARD_SEPS) == TOK_REDIRECTION)
|
||||
{
|
||||
if (!ParseRedirection(&Cmd->Redirections))
|
||||
{
|
||||
|
@ -295,6 +325,94 @@ static PARSED_COMMAND *ParseBlock(REDIRECTION *RedirList)
|
|||
return Cmd;
|
||||
}
|
||||
|
||||
/* Parse an IF statement */
|
||||
static PARSED_COMMAND *ParseIf(void)
|
||||
{
|
||||
PARSED_COMMAND *Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
|
||||
memset(Cmd, 0, sizeof(PARSED_COMMAND));
|
||||
Cmd->Type = C_IF;
|
||||
|
||||
int Type = ParseToken(0, STANDARD_SEPS);
|
||||
if (_tcsicmp(CurrentToken, _T("/I")) == 0)
|
||||
{
|
||||
Cmd->If.Flags |= IFFLAG_IGNORECASE;
|
||||
Type = ParseToken(0, STANDARD_SEPS);
|
||||
}
|
||||
if (_tcsicmp(CurrentToken, _T("not")) == 0)
|
||||
{
|
||||
Cmd->If.Flags |= IFFLAG_NEGATE;
|
||||
Type = ParseToken(0, STANDARD_SEPS);
|
||||
}
|
||||
|
||||
if (Type != TOK_NORMAL)
|
||||
{
|
||||
FreeCommand(Cmd);
|
||||
ParseError();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check for unary operators */
|
||||
for (; Cmd->If.Operator <= IF_MAX_UNARY; Cmd->If.Operator++)
|
||||
{
|
||||
if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
|
||||
{
|
||||
if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL)
|
||||
{
|
||||
FreeCommand(Cmd);
|
||||
ParseError();
|
||||
return NULL;
|
||||
}
|
||||
Cmd->If.RightArg = cmd_dup(CurrentToken);
|
||||
goto condition_done;
|
||||
}
|
||||
}
|
||||
|
||||
/* It must be a two-argument (comparison) operator. It could be ==, so
|
||||
* the equals sign can't be treated as whitespace here. */
|
||||
Cmd->If.LeftArg = cmd_dup(CurrentToken);
|
||||
ParseToken(0, _T(",;"));
|
||||
|
||||
/* The right argument can come immediately after == */
|
||||
if (_tcsnicmp(CurrentToken, _T("=="), 2) == 0 && CurrentToken[2])
|
||||
{
|
||||
Cmd->If.RightArg = cmd_dup(&CurrentToken[2]);
|
||||
goto condition_done;
|
||||
}
|
||||
|
||||
for (; Cmd->If.Operator <= IF_MAX_COMPARISON; Cmd->If.Operator++)
|
||||
{
|
||||
if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
|
||||
{
|
||||
if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL)
|
||||
break;
|
||||
Cmd->If.RightArg = cmd_dup(CurrentToken);
|
||||
goto condition_done;
|
||||
}
|
||||
}
|
||||
FreeCommand(Cmd);
|
||||
ParseError();
|
||||
return NULL;
|
||||
|
||||
condition_done:
|
||||
Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
|
||||
if (Cmd->Subcommands == NULL)
|
||||
{
|
||||
FreeCommand(Cmd);
|
||||
return NULL;
|
||||
}
|
||||
if (_tcsicmp(CurrentToken, _T("else")) == 0)
|
||||
{
|
||||
Cmd->Subcommands->Next = ParseCommandOp(C_OP_LOWEST);
|
||||
if (Cmd->Subcommands->Next == NULL)
|
||||
{
|
||||
FreeCommand(Cmd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
static PARSED_COMMAND *ParseCommandPart(void)
|
||||
{
|
||||
TCHAR ParsedLine[CMDLINE_LENGTH];
|
||||
|
@ -318,7 +436,7 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
|||
{
|
||||
/* "Ignore" the rest of the line.
|
||||
* (Line continuations will still be parsed, though.) */
|
||||
while (ParseToken(0, TRUE) != TOK_END)
|
||||
while (ParseToken(0, NULL) != TOK_END)
|
||||
;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -339,7 +457,7 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
|||
/* Get the head of the command */
|
||||
while (1)
|
||||
{
|
||||
Type = ParseToken(_T('('), FALSE);
|
||||
Type = ParseToken(_T('('), STANDARD_SEPS);
|
||||
if (Type == TOK_NORMAL)
|
||||
{
|
||||
Pos = _stpcpy(ParsedLine, CurrentToken);
|
||||
|
@ -367,12 +485,22 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
|||
}
|
||||
TailOffset = Pos - ParsedLine;
|
||||
|
||||
/* FIXME: FOR, IF, and REM need special processing by the parser. */
|
||||
/* Check for special forms */
|
||||
if (_tcsicmp(ParsedLine, _T("if")) == 0)
|
||||
{
|
||||
if (RedirList)
|
||||
{
|
||||
ParseError();
|
||||
FreeRedirection(RedirList);
|
||||
return NULL;
|
||||
}
|
||||
return ParseIf();
|
||||
}
|
||||
|
||||
/* Now get the tail */
|
||||
while (1)
|
||||
{
|
||||
Type = ParseToken(0, TRUE);
|
||||
Type = ParseToken(0, NULL);
|
||||
if (Type == TOK_NORMAL)
|
||||
{
|
||||
if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH])
|
||||
|
@ -394,13 +522,13 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
|||
}
|
||||
}
|
||||
|
||||
Cmd = cmd_alloc(FIELD_OFFSET(PARSED_COMMAND, CommandLine[Pos + 1 - ParsedLine]));
|
||||
Cmd = cmd_alloc(FIELD_OFFSET(PARSED_COMMAND, Command.CommandLine[Pos + 1 - ParsedLine]));
|
||||
Cmd->Type = C_COMMAND;
|
||||
Cmd->Next = NULL;
|
||||
Cmd->Subcommands = NULL;
|
||||
Cmd->Redirections = RedirList;
|
||||
_tcscpy(Cmd->CommandLine, ParsedLine);
|
||||
Cmd->Tail = Cmd->CommandLine + TailOffset;
|
||||
_tcscpy(Cmd->Command.CommandLine, ParsedLine);
|
||||
Cmd->Command.Tail = Cmd->Command.CommandLine + TailOffset;
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
|
@ -487,7 +615,7 @@ EchoCommand(PARSED_COMMAND *Cmd)
|
|||
switch (Cmd->Type)
|
||||
{
|
||||
case C_COMMAND:
|
||||
ConOutPrintf(_T("%s"), Cmd->CommandLine);
|
||||
ConOutPrintf(_T("%s"), Cmd->Command.CommandLine);
|
||||
break;
|
||||
case C_QUIET:
|
||||
return;
|
||||
|
@ -509,6 +637,23 @@ EchoCommand(PARSED_COMMAND *Cmd)
|
|||
ConOutPrintf(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
|
||||
EchoCommand(Sub->Next);
|
||||
break;
|
||||
case C_IF:
|
||||
ConOutPrintf(_T("if"));
|
||||
if (Cmd->If.Flags & IFFLAG_IGNORECASE)
|
||||
ConOutPrintf(_T(" /I"));
|
||||
if (Cmd->If.Flags & IFFLAG_NEGATE)
|
||||
ConOutPrintf(_T(" not"));
|
||||
if (Cmd->If.LeftArg)
|
||||
ConOutPrintf(_T(" %s"), Cmd->If.LeftArg);
|
||||
ConOutPrintf(_T(" %s %s "), IfOperatorString[Cmd->If.Operator], Cmd->If.RightArg);
|
||||
Sub = Cmd->Subcommands;
|
||||
EchoCommand(Sub);
|
||||
if (Sub->Next)
|
||||
{
|
||||
ConOutPrintf(_T(" else "));
|
||||
EchoCommand(Sub->Next);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
|
||||
|
@ -526,5 +671,10 @@ FreeCommand(PARSED_COMMAND *Cmd)
|
|||
if (Cmd->Next)
|
||||
FreeCommand(Cmd->Next);
|
||||
FreeRedirection(Cmd->Redirections);
|
||||
if (Cmd->Type == C_IF)
|
||||
{
|
||||
cmd_free(Cmd->If.LeftArg);
|
||||
cmd_free(Cmd->If.RightArg);
|
||||
}
|
||||
cmd_free(Cmd);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue