mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 05:22:57 +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);
|
SetFilePointer (bc->hBatchFile, 0, NULL, FILE_BEGIN);
|
||||||
bc->bEcho = bEcho; /* Preserve echo across batch calls */
|
bc->bEcho = bEcho; /* Preserve echo across batch calls */
|
||||||
bc->shiftlevel = 0;
|
bc->shiftlevel = 0;
|
||||||
bc->bCmdBlock = -1;
|
|
||||||
|
|
||||||
bc->ffind = NULL;
|
bc->ffind = NULL;
|
||||||
bc->forvar = _T('\0');
|
bc->forvar = _T('\0');
|
||||||
|
@ -445,39 +444,6 @@ LPTSTR ReadBatchLine ()
|
||||||
|
|
||||||
first = textline;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,6 @@ typedef struct tagBATCHCONTEXT
|
||||||
HANDLE hFind; /* Preserve find handle when doing a for */
|
HANDLE hFind; /* Preserve find handle when doing a for */
|
||||||
REDIRECTION *RedirList;
|
REDIRECTION *RedirList;
|
||||||
TCHAR forvar;
|
TCHAR forvar;
|
||||||
INT bCmdBlock;
|
|
||||||
BOOL bExecuteBlock[MAX_PATH];
|
|
||||||
} BATCH_CONTEXT, *LPBATCH_CONTEXT;
|
} BATCH_CONTEXT, *LPBATCH_CONTEXT;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -879,7 +879,7 @@ ExecuteCommand(PARSED_COMMAND *Cmd)
|
||||||
if(bc)
|
if(bc)
|
||||||
bNewBatch = FALSE;
|
bNewBatch = FALSE;
|
||||||
|
|
||||||
Success = DoCommand(Cmd->CommandLine);
|
Success = DoCommand(Cmd->Command.CommandLine);
|
||||||
|
|
||||||
if(bNewBatch && bc)
|
if(bNewBatch && bc)
|
||||||
AddBatchRedirection(&Cmd->Redirections);
|
AddBatchRedirection(&Cmd->Redirections);
|
||||||
|
@ -903,6 +903,9 @@ ExecuteCommand(PARSED_COMMAND *Cmd)
|
||||||
case C_PIPE:
|
case C_PIPE:
|
||||||
ExecutePipeline(Cmd);
|
ExecutePipeline(Cmd);
|
||||||
break;
|
break;
|
||||||
|
case C_IF:
|
||||||
|
Success = ExecuteIf(Cmd);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
UndoRedirection(Cmd->Redirections, NULL);
|
UndoRedirection(Cmd->Redirections, NULL);
|
||||||
|
|
|
@ -258,6 +258,15 @@ INT CommandHistory (LPTSTR param);
|
||||||
#endif
|
#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 */
|
/* Prototypes for INTERNAL.C */
|
||||||
VOID InitLastPath (VOID);
|
VOID InitLastPath (VOID);
|
||||||
VOID FreeLastPath (VOID);
|
VOID FreeLastPath (VOID);
|
||||||
|
@ -330,15 +339,28 @@ INT CommandMsgbox (LPTSTR);
|
||||||
|
|
||||||
|
|
||||||
/* Prototypes from PARSER.C */
|
/* 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
|
typedef struct _PARSED_COMMAND
|
||||||
{
|
{
|
||||||
struct _PARSED_COMMAND *Subcommands;
|
struct _PARSED_COMMAND *Subcommands;
|
||||||
struct _PARSED_COMMAND *Next;
|
struct _PARSED_COMMAND *Next;
|
||||||
struct _REDIRECTION *Redirections;
|
struct _REDIRECTION *Redirections;
|
||||||
TCHAR *Tail;
|
|
||||||
BYTE Type;
|
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;
|
||||||
PARSED_COMMAND *ParseCommand(LPTSTR Line);
|
PARSED_COMMAND *ParseCommand(LPTSTR Line);
|
||||||
VOID EchoCommand(PARSED_COMMAND *Cmd);
|
VOID EchoCommand(PARSED_COMMAND *Cmd);
|
||||||
|
|
|
@ -120,7 +120,7 @@ COMMAND cmds[] =
|
||||||
{_T("history"), 0, CommandHistory},
|
{_T("history"), 0, CommandHistory},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{_T("if"), 0, cmd_if},
|
// {_T("if"), 0, cmd_if},
|
||||||
|
|
||||||
#ifdef INCLUDE_CMD_LABEL
|
#ifdef INCLUDE_CMD_LABEL
|
||||||
{_T("label"), 0, cmd_label},
|
{_T("label"), 0, cmd_label},
|
||||||
|
|
|
@ -32,15 +32,30 @@
|
||||||
|
|
||||||
#include <precomp.h>
|
#include <precomp.h>
|
||||||
|
|
||||||
|
static INT GenericCmp(INT (*StringCmp)(LPCTSTR, LPCTSTR),
|
||||||
#define X_EXEC 1
|
LPCTSTR Left, LPCTSTR Right)
|
||||||
#define X_EMPTY 0x80
|
|
||||||
|
|
||||||
INT cmd_if (LPTSTR param)
|
|
||||||
{
|
{
|
||||||
INT x_flag = 0; /* when set cause 'then' clause to be executed */
|
TCHAR *end;
|
||||||
LPTSTR pp;
|
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));
|
TRACE ("cmd_if: (\'%s\')\n", debugstr_aw(param));
|
||||||
|
|
||||||
if (!_tcsncmp (param, _T("/?"), 2))
|
if (!_tcsncmp (param, _T("/?"), 2))
|
||||||
|
@ -48,178 +63,89 @@ INT cmd_if (LPTSTR param)
|
||||||
ConOutResPaging(TRUE,STRING_IF_HELP1);
|
ConOutResPaging(TRUE,STRING_IF_HELP1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* First check if param string begins with 'not' */
|
if (Cmd->If.Operator == IF_CMDEXTVERSION)
|
||||||
if (!_tcsnicmp (param, _T("not"), 3) && _istspace (*(param + 3)))
|
|
||||||
{
|
{
|
||||||
x_flag = X_EXEC; /* Remember 'NOT' */
|
/* IF CMDEXTVERSION n: check if Command Extensions version
|
||||||
param += 3; /* Step over 'NOT' */
|
* is greater or equal to n */
|
||||||
while (_istspace (*param)) /* And subsequent spaces */
|
DWORD n = _tcstoul(Cmd->If.RightArg, ¶m, 10);
|
||||||
param++;
|
if (*param != _T('\0'))
|
||||||
|
{
|
||||||
|
error_syntax(Cmd->If.RightArg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
result = (2 >= n);
|
||||||
}
|
}
|
||||||
|
else if (Cmd->If.Operator == IF_DEFINED)
|
||||||
/* Check for 'exist' form */
|
|
||||||
if (!_tcsnicmp (param, _T("exist"), 5) && _istspace (*(param + 5)))
|
|
||||||
{
|
{
|
||||||
UINT i;
|
/* IF DEFINED var: check if environment variable exists */
|
||||||
BOOL bInside = FALSE;
|
result = (GetEnvVarOrSpecial(Cmd->If.RightArg) != NULL);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
else if (!_tcsnicmp (param, _T("defined"), 7) && _istspace (*(param + 7)))
|
else if (Cmd->If.Operator == IF_ERRORLEVEL)
|
||||||
{
|
{
|
||||||
/* Check for 'defined' form */
|
/* IF ERRORLEVEL n: check if last exit code is greater or equal to n */
|
||||||
TCHAR Value [1];
|
INT n = _tcstol(Cmd->If.RightArg, ¶m, 10);
|
||||||
INT ValueSize = 0;
|
if (*param != _T('\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)
|
|
||||||
{
|
{
|
||||||
*pp++ = _T('\0');
|
error_syntax(Cmd->If.RightArg);
|
||||||
ValueSize = GetEnvironmentVariable(param, Value, sizeof(Value) / sizeof(Value[0]));
|
return FALSE;
|
||||||
x_flag ^= (0 == ValueSize)
|
|
||||||
? 0
|
|
||||||
: X_EXEC;
|
|
||||||
x_flag |= X_EMPTY;
|
|
||||||
}
|
}
|
||||||
else
|
result = (nErrorLevel >= n);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
else if (!_tcsnicmp (param, _T("errorlevel"), 10) && _istspace (*(param + 10)))
|
else if (Cmd->If.Operator == IF_EXIST)
|
||||||
{
|
{
|
||||||
/* Check for 'errorlevel' form */
|
/* IF EXIST filename: check if file exists (wildcards allowed) */
|
||||||
INT n = 0;
|
WIN32_FIND_DATA f;
|
||||||
|
HANDLE hFind;
|
||||||
|
|
||||||
pp = param + 10;
|
StripQuotes(Cmd->If.RightArg);
|
||||||
while (_istspace (*pp))
|
|
||||||
pp++;
|
|
||||||
|
|
||||||
while (_istdigit (*pp))
|
hFind = FindFirstFile(Cmd->If.RightArg, &f);
|
||||||
n = n * 10 + (*pp++ - _T('0'));
|
if (hFind != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
x_flag ^= (nErrorLevel != n) ? 0 : X_EXEC;
|
result = TRUE;
|
||||||
|
FindClose(hFind);
|
||||||
x_flag |= X_EMPTY; /* Syntax error if comd empty */
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BOOL bInQuote = FALSE;
|
/* Do case-insensitive string comparisons if /I specified */
|
||||||
INT p1len;
|
INT (*StringCmp)(LPCTSTR, LPCTSTR) =
|
||||||
pp = param;
|
(Cmd->If.Flags & IFFLAG_IGNORECASE) ? _tcsicmp : _tcscmp;
|
||||||
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++;
|
|
||||||
|
|
||||||
/* are the two sides equal*/
|
if (Cmd->If.Operator == IF_STRINGEQ)
|
||||||
if ( !_tcsncmp(param,pp,p1len))
|
|
||||||
x_flag ^= X_EXEC;
|
|
||||||
pp += p1len;
|
|
||||||
|
|
||||||
if ( x_flag )
|
|
||||||
{
|
{
|
||||||
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 */
|
if (result ^ ((Cmd->If.Flags & IFFLAG_NEGATE) != 0))
|
||||||
pp++;
|
|
||||||
|
|
||||||
if (*pp == _T('('))
|
|
||||||
{
|
{
|
||||||
if (bc)
|
/* full condition was true, do the command */
|
||||||
{
|
return ExecuteCommand(Cmd->Subcommands);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (x_flag & X_EMPTY)
|
|
||||||
{
|
{
|
||||||
while (_istspace (*pp)) /* Then skip spaces */
|
/* full condition was false, do the "else" command if there is one */
|
||||||
pp++;
|
if (Cmd->Subcommands->Next)
|
||||||
|
return ExecuteCommand(Cmd->Subcommands->Next);
|
||||||
if (*pp == _T('\0')) /* If nothing left then syntax err */
|
return TRUE;
|
||||||
{
|
|
||||||
error_syntax (NULL);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x_flag & X_EXEC)
|
|
||||||
{
|
|
||||||
ParseCommandLine (pp);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* PARSER.C - command parsing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include <precomp.h>
|
#include <precomp.h>
|
||||||
|
|
||||||
#define C_OP_LOWEST C_MULTI
|
#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 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)
|
static BOOL IsSeparator(TCHAR Char)
|
||||||
{
|
{
|
||||||
/* These three characters act like spaces to the parser */
|
return _istspace(Char) || (Char && _tcschr(STANDARD_SEPS, Char));
|
||||||
return _istspace(Char) || (Char && _tcschr(_T(",;="), Char));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum { TOK_END, TOK_NORMAL, TOK_OPERATOR, TOK_REDIRECTION,
|
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
|
/* 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 */
|
* 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 *Out = CurrentToken;
|
||||||
TCHAR Char = CurChar;
|
TCHAR Char;
|
||||||
int Type;
|
int Type;
|
||||||
BOOL bInQuote = FALSE;
|
BOOL bInQuote = FALSE;
|
||||||
|
|
||||||
if (!PreserveSpace)
|
for (Char = CurChar; Char && Char != _T('\n'); Char = ParseChar())
|
||||||
{
|
|
||||||
while (Char != _T('\n') && IsSeparator(Char))
|
|
||||||
Char = ParseChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (Char && Char != _T('\n'))
|
|
||||||
{
|
{
|
||||||
bInQuote ^= (Char == _T('"'));
|
bInQuote ^= (Char == _T('"'));
|
||||||
if (!bInQuote)
|
if (!bInQuote)
|
||||||
{
|
{
|
||||||
/* Check for all the myriad ways in which this token
|
if (Separators != NULL)
|
||||||
* may be brought to an untimely end. */
|
{
|
||||||
|
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') &&
|
if ((Char >= _T('0') && Char <= _T('9') &&
|
||||||
(ParsePos == &ParseLine[1] || IsSeparator(ParsePos[-2]))
|
(ParsePos == &ParseLine[1] || IsSeparator(ParsePos[-2]))
|
||||||
&& (*ParsePos == _T('<') || *ParsePos == _T('>')))
|
&& (*ParsePos == _T('<') || *ParsePos == _T('>'))))
|
||||||
|| _tcschr(_T(")&|<>") + (InsideBlock ? 0 : 1), Char)
|
|
||||||
|| (!PreserveSpace && IsSeparator(Char))
|
|
||||||
|| (Char == ExtraEnd))
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Char == ExtraEnd)
|
||||||
|
break;
|
||||||
|
if (InsideBlock && Char == _T(')'))
|
||||||
|
break;
|
||||||
|
if (_tcschr(_T("&|<>"), Char))
|
||||||
|
break;
|
||||||
|
|
||||||
if (Char == _T('^'))
|
if (Char == _T('^'))
|
||||||
{
|
{
|
||||||
Char = ParseChar();
|
Char = ParseChar();
|
||||||
|
@ -112,7 +143,6 @@ static int ParseToken(TCHAR ExtraEnd, BOOL PreserveSpace)
|
||||||
if (Out == &CurrentToken[CMDLINE_LENGTH - 1])
|
if (Out == &CurrentToken[CMDLINE_LENGTH - 1])
|
||||||
break;
|
break;
|
||||||
*Out++ = Char;
|
*Out++ = Char;
|
||||||
Char = ParseChar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if we got at least one character before reaching a special one.
|
/* Check if we got at least one character before reaching a special one.
|
||||||
|
@ -214,7 +244,7 @@ static BOOL ParseRedirection(REDIRECTION **List)
|
||||||
if (!*Tok)
|
if (!*Tok)
|
||||||
{
|
{
|
||||||
/* The file name was not part of this token, so it'll be the next one */
|
/* 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;
|
goto fail;
|
||||||
Tok = CurrentToken;
|
Tok = CurrentToken;
|
||||||
}
|
}
|
||||||
|
@ -284,7 +314,7 @@ static PARSED_COMMAND *ParseBlock(REDIRECTION *RedirList)
|
||||||
InsideBlock--;
|
InsideBlock--;
|
||||||
|
|
||||||
/* Process any trailing redirections */
|
/* Process any trailing redirections */
|
||||||
while (ParseToken(0, FALSE) == TOK_REDIRECTION)
|
while (ParseToken(0, STANDARD_SEPS) == TOK_REDIRECTION)
|
||||||
{
|
{
|
||||||
if (!ParseRedirection(&Cmd->Redirections))
|
if (!ParseRedirection(&Cmd->Redirections))
|
||||||
{
|
{
|
||||||
|
@ -295,6 +325,94 @@ static PARSED_COMMAND *ParseBlock(REDIRECTION *RedirList)
|
||||||
return Cmd;
|
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)
|
static PARSED_COMMAND *ParseCommandPart(void)
|
||||||
{
|
{
|
||||||
TCHAR ParsedLine[CMDLINE_LENGTH];
|
TCHAR ParsedLine[CMDLINE_LENGTH];
|
||||||
|
@ -318,7 +436,7 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
||||||
{
|
{
|
||||||
/* "Ignore" the rest of the line.
|
/* "Ignore" the rest of the line.
|
||||||
* (Line continuations will still be parsed, though.) */
|
* (Line continuations will still be parsed, though.) */
|
||||||
while (ParseToken(0, TRUE) != TOK_END)
|
while (ParseToken(0, NULL) != TOK_END)
|
||||||
;
|
;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -339,7 +457,7 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
||||||
/* Get the head of the command */
|
/* Get the head of the command */
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
Type = ParseToken(_T('('), FALSE);
|
Type = ParseToken(_T('('), STANDARD_SEPS);
|
||||||
if (Type == TOK_NORMAL)
|
if (Type == TOK_NORMAL)
|
||||||
{
|
{
|
||||||
Pos = _stpcpy(ParsedLine, CurrentToken);
|
Pos = _stpcpy(ParsedLine, CurrentToken);
|
||||||
|
@ -367,12 +485,22 @@ static PARSED_COMMAND *ParseCommandPart(void)
|
||||||
}
|
}
|
||||||
TailOffset = Pos - ParsedLine;
|
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 */
|
/* Now get the tail */
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
Type = ParseToken(0, TRUE);
|
Type = ParseToken(0, NULL);
|
||||||
if (Type == TOK_NORMAL)
|
if (Type == TOK_NORMAL)
|
||||||
{
|
{
|
||||||
if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH])
|
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->Type = C_COMMAND;
|
||||||
Cmd->Next = NULL;
|
Cmd->Next = NULL;
|
||||||
Cmd->Subcommands = NULL;
|
Cmd->Subcommands = NULL;
|
||||||
Cmd->Redirections = RedirList;
|
Cmd->Redirections = RedirList;
|
||||||
_tcscpy(Cmd->CommandLine, ParsedLine);
|
_tcscpy(Cmd->Command.CommandLine, ParsedLine);
|
||||||
Cmd->Tail = Cmd->CommandLine + TailOffset;
|
Cmd->Command.Tail = Cmd->Command.CommandLine + TailOffset;
|
||||||
return Cmd;
|
return Cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,7 +615,7 @@ EchoCommand(PARSED_COMMAND *Cmd)
|
||||||
switch (Cmd->Type)
|
switch (Cmd->Type)
|
||||||
{
|
{
|
||||||
case C_COMMAND:
|
case C_COMMAND:
|
||||||
ConOutPrintf(_T("%s"), Cmd->CommandLine);
|
ConOutPrintf(_T("%s"), Cmd->Command.CommandLine);
|
||||||
break;
|
break;
|
||||||
case C_QUIET:
|
case C_QUIET:
|
||||||
return;
|
return;
|
||||||
|
@ -509,6 +637,23 @@ EchoCommand(PARSED_COMMAND *Cmd)
|
||||||
ConOutPrintf(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
|
ConOutPrintf(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
|
||||||
EchoCommand(Sub->Next);
|
EchoCommand(Sub->Next);
|
||||||
break;
|
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)
|
for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
|
||||||
|
@ -526,5 +671,10 @@ FreeCommand(PARSED_COMMAND *Cmd)
|
||||||
if (Cmd->Next)
|
if (Cmd->Next)
|
||||||
FreeCommand(Cmd->Next);
|
FreeCommand(Cmd->Next);
|
||||||
FreeRedirection(Cmd->Redirections);
|
FreeRedirection(Cmd->Redirections);
|
||||||
|
if (Cmd->Type == C_IF)
|
||||||
|
{
|
||||||
|
cmd_free(Cmd->If.LeftArg);
|
||||||
|
cmd_free(Cmd->If.RightArg);
|
||||||
|
}
|
||||||
cmd_free(Cmd);
|
cmd_free(Cmd);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue