mirror of
https://github.com/reactos/reactos.git
synced 2025-07-28 11:31:54 +00:00
- Begin writing a real parser for cmd. &, &&, ||, and () are implemented. The parenthesized blocks aren't too useful yet as the parser can't read additional lines; doing this will require some restructuring in cmd.c.
- Remove ^-removing hacks in echo and set. svn path=/trunk/; revision=35514
This commit is contained in:
parent
07912f2b23
commit
4cdc27c1a2
7 changed files with 550 additions and 229 deletions
|
@ -323,7 +323,7 @@ static BOOL RunFile(LPTSTR filename)
|
|||
* Rest - rest of command line
|
||||
*/
|
||||
|
||||
static VOID
|
||||
static BOOL
|
||||
Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
||||
{
|
||||
TCHAR *szFullName=NULL;
|
||||
|
@ -345,7 +345,7 @@ Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
|||
{
|
||||
error_out_of_memory();
|
||||
nErrorLevel = 1;
|
||||
return ;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rest = cmd_alloc ( (_tcslen(Rest) + 512) * sizeof(TCHAR));
|
||||
|
@ -354,7 +354,7 @@ Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
|||
cmd_free (first);
|
||||
error_out_of_memory();
|
||||
nErrorLevel = 1;
|
||||
return ;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
full = cmd_alloc ( (_tcslen(Full) + 512) * sizeof(TCHAR));
|
||||
|
@ -364,7 +364,7 @@ Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
|||
cmd_free (rest);
|
||||
error_out_of_memory();
|
||||
nErrorLevel = 1;
|
||||
return ;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
szFullName = cmd_alloc ( (_tcslen(Full) + 512) * sizeof(TCHAR));
|
||||
|
@ -375,7 +375,7 @@ Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
|||
cmd_free (full);
|
||||
error_out_of_memory();
|
||||
nErrorLevel = 1;
|
||||
return ;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -446,7 +446,7 @@ Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
|||
cmd_free (full);
|
||||
cmd_free (szFullName);
|
||||
nErrorLevel = 1;
|
||||
return;
|
||||
return working;
|
||||
}
|
||||
|
||||
/* get the PATH environment variable and parse it */
|
||||
|
@ -459,7 +459,7 @@ Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
|||
cmd_free (full);
|
||||
cmd_free (szFullName);
|
||||
nErrorLevel = 1;
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
|
@ -554,6 +554,7 @@ Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
|||
cmd_free(rest);
|
||||
cmd_free(full);
|
||||
cmd_free (szFullName);
|
||||
return nErrorLevel == 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -566,7 +567,7 @@ Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
|
|||
*
|
||||
*/
|
||||
|
||||
VOID
|
||||
BOOL
|
||||
DoCommand (LPTSTR line)
|
||||
{
|
||||
TCHAR *com = NULL; /* the first word in the command */
|
||||
|
@ -575,6 +576,7 @@ DoCommand (LPTSTR line)
|
|||
LPTSTR rest; /* pointer to the rest of the command line */
|
||||
INT cl;
|
||||
LPCOMMAND cmdptr;
|
||||
BOOL ret = TRUE;
|
||||
|
||||
TRACE ("DoCommand: (\'%s\')\n", debugstr_aw(line));
|
||||
|
||||
|
@ -582,7 +584,7 @@ DoCommand (LPTSTR line)
|
|||
if (com == NULL)
|
||||
{
|
||||
error_out_of_memory();
|
||||
return;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cp = com;
|
||||
|
@ -640,7 +642,7 @@ DoCommand (LPTSTR line)
|
|||
/* If end of table execute ext cmd */
|
||||
if (cmdptr->name == NULL)
|
||||
{
|
||||
Execute (line, com, rest);
|
||||
ret = Execute (line, com, rest);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -677,6 +679,7 @@ DoCommand (LPTSTR line)
|
|||
}
|
||||
}
|
||||
cmd_free(com);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -687,25 +690,28 @@ DoCommand (LPTSTR line)
|
|||
|
||||
VOID ParseCommandLine (LPTSTR cmd)
|
||||
{
|
||||
TCHAR cmdline[CMDLINE_LENGTH];
|
||||
LPTSTR s;
|
||||
PARSED_COMMAND *Cmd = ParseCommand(cmd);
|
||||
if (Cmd)
|
||||
{
|
||||
ExecuteCommand(Cmd);
|
||||
FreeCommand(Cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID
|
||||
ExecutePipeline(PARSED_COMMAND *Cmd)
|
||||
{
|
||||
#ifdef FEATURE_REDIRECTION
|
||||
REDIRECTION *RedirList = NULL;
|
||||
TCHAR szTempPath[MAX_PATH] = _T(".\\");
|
||||
TCHAR szFileName[2][MAX_PATH] = {_T(""), _T("")};
|
||||
HANDLE hFile[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
|
||||
INT num = 0;
|
||||
INT Length;
|
||||
UINT Attributes;
|
||||
BOOL bNewBatch = TRUE;
|
||||
HANDLE hOldConIn;
|
||||
HANDLE hOldConOut;
|
||||
#endif /* FEATURE_REDIRECTION */
|
||||
|
||||
_tcscpy (cmdline, cmd);
|
||||
s = &cmdline[0];
|
||||
|
||||
TRACE ("ParseCommandLine: (\'%s\')\n", debugstr_aw(s));
|
||||
//TRACE ("ParseCommandLine: (\'%s\')\n", debugstr_aw(s));
|
||||
|
||||
#ifdef FEATURE_REDIRECTION
|
||||
/* find the temp path to store temporary files */
|
||||
|
@ -726,15 +732,6 @@ VOID ParseCommandLine (LPTSTR cmd)
|
|||
if (szTempPath[_tcslen (szTempPath) - 1] != _T('\\'))
|
||||
_tcscat (szTempPath, _T("\\"));
|
||||
|
||||
/* get the redirections from the command line */
|
||||
num = GetRedirection (s, &RedirList);
|
||||
|
||||
if (!PerformRedirection(RedirList))
|
||||
{
|
||||
FreeRedirection(RedirList);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set up the initial conditions ... */
|
||||
/* preserve STDIN and STDOUT handles */
|
||||
hOldConIn = GetStdHandle (STD_INPUT_HANDLE);
|
||||
|
@ -744,7 +741,7 @@ VOID ParseCommandLine (LPTSTR cmd)
|
|||
*szFileName[0] = _T('\0');
|
||||
hFile[0] = INVALID_HANDLE_VALUE;
|
||||
|
||||
while (num-- > 1)
|
||||
while (Cmd->Type == C_PIPE)
|
||||
{
|
||||
SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
|
||||
|
||||
|
@ -766,7 +763,7 @@ VOID ParseCommandLine (LPTSTR cmd)
|
|||
|
||||
SetStdHandle (STD_OUTPUT_HANDLE, hFile[1]);
|
||||
|
||||
DoCommand (s);
|
||||
ExecuteCommand(Cmd->Subcommands);
|
||||
|
||||
/* close stdout file */
|
||||
SetStdHandle (STD_OUTPUT_HANDLE, hOldConOut);
|
||||
|
@ -799,22 +796,18 @@ VOID ParseCommandLine (LPTSTR cmd)
|
|||
OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL);
|
||||
SetStdHandle (STD_INPUT_HANDLE, hFile[0]);
|
||||
|
||||
s = s + _tcslen (s) + 1;
|
||||
Cmd = Cmd->Subcommands->Next;
|
||||
}
|
||||
|
||||
/* Now set up the end conditions... */
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, hOldConOut);
|
||||
|
||||
if(bc)
|
||||
bNewBatch = FALSE;
|
||||
#endif
|
||||
|
||||
/* process final command */
|
||||
DoCommand (s);
|
||||
ExecuteCommand(Cmd);
|
||||
|
||||
#ifdef FEATURE_REDIRECTION
|
||||
if(bNewBatch && bc)
|
||||
AddBatchRedirection(&RedirList);
|
||||
/* close old stdin file */
|
||||
#if 0 /* buggy implementation */
|
||||
SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
|
||||
|
@ -870,12 +863,55 @@ VOID ParseCommandLine (LPTSTR cmd)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
UndoRedirection(RedirList, NULL);
|
||||
FreeRedirection(RedirList);
|
||||
#endif /* FEATURE_REDIRECTION */
|
||||
}
|
||||
|
||||
BOOL
|
||||
ExecuteCommand(PARSED_COMMAND *Cmd)
|
||||
{
|
||||
BOOL bNewBatch = TRUE;
|
||||
PARSED_COMMAND *Sub;
|
||||
BOOL Success = TRUE;
|
||||
|
||||
if (!PerformRedirection(Cmd->Redirections))
|
||||
return FALSE;
|
||||
|
||||
switch (Cmd->Type)
|
||||
{
|
||||
case C_COMMAND:
|
||||
if(bc)
|
||||
bNewBatch = FALSE;
|
||||
|
||||
Success = DoCommand(Cmd->CommandLine);
|
||||
|
||||
if(bNewBatch && bc)
|
||||
AddBatchRedirection(&Cmd->Redirections);
|
||||
break;
|
||||
case C_QUIET:
|
||||
case C_BLOCK:
|
||||
case C_MULTI:
|
||||
for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next)
|
||||
Success = ExecuteCommand(Sub);
|
||||
break;
|
||||
case C_IFFAILURE:
|
||||
case C_IFSUCCESS:
|
||||
Sub = Cmd->Subcommands;
|
||||
Success = ExecuteCommand(Sub);
|
||||
if (Success == (Cmd->Type - C_IFFAILURE))
|
||||
{
|
||||
Sub = Sub->Next;
|
||||
Success = ExecuteCommand(Sub);
|
||||
}
|
||||
break;
|
||||
case C_PIPE:
|
||||
ExecutePipeline(Cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
UndoRedirection(Cmd->Redirections, NULL);
|
||||
return Success;
|
||||
}
|
||||
|
||||
BOOL
|
||||
GrowIfNecessary_dbg ( UINT needed, LPTSTR* ret, UINT* retlen, const char *file, int line )
|
||||
{
|
||||
|
|
|
@ -97,10 +97,12 @@ INT cmd_cls (LPTSTR, LPTSTR);
|
|||
/* Prototypes for CMD.C */
|
||||
INT ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len, BOOL bPutSeperator);
|
||||
VOID ParseCommandLine (LPTSTR);
|
||||
struct _PARSED_COMMAND;
|
||||
BOOL ExecuteCommand(struct _PARSED_COMMAND *Cmd);
|
||||
LPCTSTR GetEnvVarOrSpecial ( LPCTSTR varName );
|
||||
VOID AddBreakHandler (VOID);
|
||||
VOID RemoveBreakHandler (VOID);
|
||||
VOID DoCommand (LPTSTR line);
|
||||
BOOL DoCommand (LPTSTR line);
|
||||
int cmd_main (int argc, const TCHAR *argv[]);
|
||||
|
||||
extern HANDLE CMD_ModuleHandle;
|
||||
|
@ -326,6 +328,21 @@ INT cmd_move (LPTSTR, LPTSTR);
|
|||
INT CommandMsgbox (LPTSTR, LPTSTR);
|
||||
|
||||
|
||||
/* Prototypes from PARSER.C */
|
||||
enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS, C_PIPE };
|
||||
typedef struct _PARSED_COMMAND
|
||||
{
|
||||
struct _PARSED_COMMAND *Subcommands;
|
||||
struct _PARSED_COMMAND *Next;
|
||||
struct _REDIRECTION *Redirections;
|
||||
TCHAR *Tail;
|
||||
BYTE Type;
|
||||
TCHAR CommandLine[];
|
||||
} PARSED_COMMAND;
|
||||
PARSED_COMMAND *ParseCommand(LPTSTR Line);
|
||||
VOID FreeCommand(PARSED_COMMAND *Cmd);
|
||||
|
||||
|
||||
/* Prototypes from PATH.C */
|
||||
INT cmd_path (LPTSTR, LPTSTR);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
<file>misc.c</file>
|
||||
<file>move.c</file>
|
||||
<file>msgbox.c</file>
|
||||
<file>parser.c</file>
|
||||
<file>path.c</file>
|
||||
<file>pause.c</file>
|
||||
<file>prompt.c</file>
|
||||
|
|
|
@ -83,16 +83,6 @@ INT CommandEcho (LPTSTR cmd, LPTSTR param)
|
|||
}
|
||||
if (*p1 != _T('\0'))
|
||||
{
|
||||
p1 = param;
|
||||
while (NULL != (p1 = _tcschr(p1, _T('^'))))
|
||||
{
|
||||
memmove(p1, p1 + 1, (_tcslen(p1 + 1) + 1) * sizeof(TCHAR));
|
||||
if (*p1)
|
||||
{
|
||||
//skip past the char being escaped
|
||||
p1++;
|
||||
}
|
||||
}
|
||||
ConOutPuts (param);
|
||||
}
|
||||
else
|
||||
|
|
456
reactos/base/shell/cmd/parser.c
Normal file
456
reactos/base/shell/cmd/parser.c
Normal file
|
@ -0,0 +1,456 @@
|
|||
#include <precomp.h>
|
||||
|
||||
#define C_OP_LOWEST C_MULTI
|
||||
#define C_OP_HIGHEST C_PIPE
|
||||
static const TCHAR OpString[][3] = { _T("&"), _T("||"), _T("&&"), _T("|") };
|
||||
|
||||
static const TCHAR RedirString[][3] = { _T("<"), _T(">"), _T(">>") };
|
||||
|
||||
static BOOL IsSeparator(TCHAR Char)
|
||||
{
|
||||
/* These three characters act like spaces to the parser */
|
||||
return _istspace(Char) || (Char && _tcschr(_T(",;="), Char));
|
||||
}
|
||||
|
||||
enum { TOK_END, TOK_NORMAL, TOK_OPERATOR, TOK_REDIRECTION,
|
||||
TOK_BEGIN_BLOCK, TOK_END_BLOCK };
|
||||
|
||||
static BOOL bParseError;
|
||||
static BOOL bLineContinuations;
|
||||
static TCHAR ParseLine[CMDLINE_LENGTH];
|
||||
static TCHAR *ParsePos;
|
||||
static TCHAR CurChar;
|
||||
|
||||
static TCHAR CurrentToken[CMDLINE_LENGTH];
|
||||
static int CurrentTokenType;
|
||||
static int InsideBlock;
|
||||
|
||||
static TCHAR ParseChar()
|
||||
{
|
||||
TCHAR Char;
|
||||
|
||||
//restart:
|
||||
/* Although CRs can be injected into a line via an environment
|
||||
* variable substitution, the parser ignores them - they won't
|
||||
* even separate tokens. */
|
||||
do
|
||||
Char = *ParsePos++;
|
||||
while (Char == _T('\r'));
|
||||
|
||||
if (!Char)
|
||||
{
|
||||
/*if (bLineContinuations)
|
||||
if (ReadLine(ParseLine, TRUE) && *(ParsePos = ParseLine))
|
||||
goto restart;*/
|
||||
}
|
||||
return CurChar = Char;
|
||||
}
|
||||
|
||||
static void ParseError()
|
||||
{
|
||||
if (CurrentTokenType == TOK_END)
|
||||
ConOutResPuts(STRING_SYNTAX_COMMAND_INCORRECT);
|
||||
else
|
||||
ConOutPrintf(_T("%s was unexpected at this time.\n"), CurrentToken);
|
||||
bParseError = TRUE;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
TCHAR *Out = CurrentToken;
|
||||
TCHAR Char = CurChar;
|
||||
int Type;
|
||||
BOOL bInQuote = FALSE;
|
||||
|
||||
if (!PreserveSpace)
|
||||
{
|
||||
while (Char != _T('\n') && IsSeparator(Char))
|
||||
Char = ParseChar();
|
||||
}
|
||||
|
||||
while (Char && Char != _T('\n'))
|
||||
{
|
||||
bInQuote ^= (Char == _T('"'));
|
||||
if (!bInQuote)
|
||||
{
|
||||
/* Check for all the myriad ways in which this token
|
||||
* may be brought to an untimely end. */
|
||||
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))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Char == _T('^'))
|
||||
{
|
||||
Char = ParseChar();
|
||||
/* Eat up a \n, allowing line continuation */
|
||||
if (Char == _T('\n'))
|
||||
Char = ParseChar();
|
||||
/* Next character is a forced literal */
|
||||
}
|
||||
}
|
||||
/* FIXME: potential buffer overflow here */
|
||||
*Out++ = Char;
|
||||
Char = ParseChar();
|
||||
}
|
||||
|
||||
/* Check if we got at least one character before reaching a special one.
|
||||
* If so, return them and leave the special for the next call. */
|
||||
if (Out != CurrentToken)
|
||||
{
|
||||
Type = TOK_NORMAL;
|
||||
}
|
||||
else if (Char == _T('('))
|
||||
{
|
||||
Type = TOK_BEGIN_BLOCK;
|
||||
*Out++ = Char;
|
||||
ParseChar();
|
||||
}
|
||||
else if (Char == _T(')'))
|
||||
{
|
||||
Type = TOK_END_BLOCK;
|
||||
*Out++ = Char;
|
||||
ParseChar();
|
||||
}
|
||||
else if (Char == _T('&') || Char == _T('|'))
|
||||
{
|
||||
Type = TOK_OPERATOR;
|
||||
*Out++ = Char;
|
||||
Char = ParseChar();
|
||||
/* check for && or || */
|
||||
if (Char == Out[-1])
|
||||
{
|
||||
*Out++ = Char;
|
||||
ParseChar();
|
||||
}
|
||||
}
|
||||
else if ((Char >= _T('0') && Char <= _T('9'))
|
||||
|| (Char == _T('<') || Char == _T('>')))
|
||||
{
|
||||
Type = TOK_REDIRECTION;
|
||||
if (Char >= _T('0') && Char <= _T('9'))
|
||||
{
|
||||
*Out++ = Char;
|
||||
Char = ParseChar();
|
||||
}
|
||||
*Out++ = Char;
|
||||
Char = ParseChar();
|
||||
if (Char == Out[-1])
|
||||
{
|
||||
/* Strangely, the tokenizer allows << as well as >>... (it
|
||||
* will cause an error when trying to parse it though) */
|
||||
*Out++ = Char;
|
||||
Char = ParseChar();
|
||||
}
|
||||
if (Char == _T('&'))
|
||||
{
|
||||
*Out++ = Char;
|
||||
while (IsSeparator(Char = ParseChar()))
|
||||
;
|
||||
if (Char >= _T('0') && Char <= _T('9'))
|
||||
{
|
||||
*Out++ = Char;
|
||||
ParseChar();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Type = TOK_END;
|
||||
}
|
||||
*Out = _T('\0');
|
||||
return CurrentTokenType = Type;
|
||||
}
|
||||
|
||||
static BOOL ParseRedirection(REDIRECTION **List)
|
||||
{
|
||||
TCHAR *Tok = CurrentToken;
|
||||
BYTE Number;
|
||||
BYTE RedirType;
|
||||
REDIRECTION *Redir;
|
||||
|
||||
if (*Tok >= _T('0') && *Tok <= _T('9'))
|
||||
Number = *Tok++ - _T('0');
|
||||
else
|
||||
Number = *Tok == _T('<') ? 0 : 1;
|
||||
|
||||
if (*Tok++ == _T('<'))
|
||||
{
|
||||
RedirType = REDIR_READ;
|
||||
if (*Tok == _T('<'))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
RedirType = REDIR_WRITE;
|
||||
if (*Tok == _T('>'))
|
||||
{
|
||||
RedirType = REDIR_APPEND;
|
||||
Tok++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*Tok)
|
||||
{
|
||||
/* The file name was not part of this token, so it'll be the next one */
|
||||
if (ParseToken(0, FALSE) != TOK_NORMAL)
|
||||
goto fail;
|
||||
Tok = CurrentToken;
|
||||
}
|
||||
|
||||
/* If a redirection for this handle number already exists, delete it */
|
||||
while ((Redir = *List))
|
||||
{
|
||||
if (Redir->Number == Number)
|
||||
{
|
||||
*List = Redir->Next;
|
||||
cmd_free(Redir);
|
||||
continue;
|
||||
}
|
||||
List = &Redir->Next;
|
||||
}
|
||||
|
||||
Redir = cmd_alloc(FIELD_OFFSET(REDIRECTION, Filename[_tcslen(Tok) + 1]));
|
||||
Redir->Next = NULL;
|
||||
Redir->OldHandle = INVALID_HANDLE_VALUE;
|
||||
Redir->Number = Number;
|
||||
Redir->Type = RedirType;
|
||||
_tcscpy(Redir->Filename, Tok);
|
||||
*List = Redir;
|
||||
return TRUE;
|
||||
fail:
|
||||
ParseError();
|
||||
FreeRedirection(*List);
|
||||
*List = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static PARSED_COMMAND *ParseCommandOp(int OpType);
|
||||
|
||||
/* Parse a parenthesized block */
|
||||
static PARSED_COMMAND *ParseBlock(REDIRECTION *RedirList)
|
||||
{
|
||||
PARSED_COMMAND *Cmd, *Sub, **NextPtr;
|
||||
Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
|
||||
Cmd->Type = C_BLOCK;
|
||||
Cmd->Next = NULL;
|
||||
Cmd->Subcommands = NULL;
|
||||
Cmd->Redirections = RedirList;
|
||||
|
||||
/* Read the block contents */
|
||||
NextPtr = &Cmd->Subcommands;
|
||||
InsideBlock++;
|
||||
do
|
||||
{
|
||||
Sub = ParseCommandOp(C_OP_LOWEST);
|
||||
if (Sub)
|
||||
{
|
||||
*NextPtr = Sub;
|
||||
NextPtr = &Sub->Next;
|
||||
}
|
||||
else if (bParseError)
|
||||
{
|
||||
InsideBlock--;
|
||||
FreeCommand(Cmd);
|
||||
return NULL;
|
||||
}
|
||||
} while (CurrentTokenType != TOK_END_BLOCK);
|
||||
InsideBlock--;
|
||||
|
||||
/* Process any trailing redirections */
|
||||
while (ParseToken(0, FALSE) == TOK_REDIRECTION)
|
||||
{
|
||||
if (!ParseRedirection(&Cmd->Redirections))
|
||||
{
|
||||
FreeCommand(Cmd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
static PARSED_COMMAND *ParseCommandPart(void)
|
||||
{
|
||||
TCHAR ParsedLine[CMDLINE_LENGTH];
|
||||
TCHAR *Pos;
|
||||
DWORD TailOffset;
|
||||
PARSED_COMMAND *Cmd;
|
||||
REDIRECTION *RedirList = NULL;
|
||||
int Type;
|
||||
|
||||
while (IsSeparator(CurChar))
|
||||
{
|
||||
if (CurChar == _T('\n'))
|
||||
return NULL;
|
||||
ParseChar();
|
||||
}
|
||||
|
||||
if (!CurChar)
|
||||
return NULL;
|
||||
|
||||
if (CurChar == _T(':'))
|
||||
{
|
||||
/* "Ignore" the rest of the line.
|
||||
* (Line continuations will still be parsed, though.) */
|
||||
while (ParseToken(0, TRUE) != TOK_END)
|
||||
;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (CurChar == _T('@'))
|
||||
{
|
||||
ParseChar();
|
||||
Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
|
||||
Cmd->Type = C_QUIET;
|
||||
Cmd->Next = NULL;
|
||||
/* @ acts like a unary operator with low precedence,
|
||||
* so call the top-level parser */
|
||||
Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
|
||||
Cmd->Redirections = NULL;
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
/* Get the head of the command */
|
||||
while (1)
|
||||
{
|
||||
Type = ParseToken(_T('('), FALSE);
|
||||
if (Type == TOK_NORMAL)
|
||||
{
|
||||
Pos = _stpcpy(ParsedLine, CurrentToken);
|
||||
break;
|
||||
}
|
||||
else if (Type == TOK_REDIRECTION)
|
||||
{
|
||||
if (!ParseRedirection(&RedirList))
|
||||
return NULL;
|
||||
}
|
||||
else if (Type == TOK_BEGIN_BLOCK)
|
||||
{
|
||||
return ParseBlock(RedirList);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseError();
|
||||
FreeRedirection(RedirList);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
TailOffset = Pos - ParsedLine;
|
||||
|
||||
/* FIXME: FOR, IF, and REM need special processing by the parser. */
|
||||
|
||||
/* Now get the tail */
|
||||
while (1)
|
||||
{
|
||||
Type = ParseToken(0, TRUE);
|
||||
if (Type == TOK_NORMAL)
|
||||
{
|
||||
/* FIXME: potential buffer overflow here */
|
||||
Pos = _stpcpy(Pos, CurrentToken);
|
||||
}
|
||||
else if (Type == TOK_REDIRECTION)
|
||||
{
|
||||
if (!ParseRedirection(&RedirList))
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Cmd = cmd_alloc(FIELD_OFFSET(PARSED_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;
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
static PARSED_COMMAND *ParseCommandOp(int OpType)
|
||||
{
|
||||
PARSED_COMMAND *Cmd, *Left, *Right;
|
||||
|
||||
if (OpType == C_OP_HIGHEST)
|
||||
Cmd = ParseCommandPart();
|
||||
else
|
||||
Cmd = ParseCommandOp(OpType + 1);
|
||||
|
||||
if (Cmd && !_tcscmp(CurrentToken, OpString[OpType - C_OP_LOWEST]))
|
||||
{
|
||||
Left = Cmd;
|
||||
Right = ParseCommandOp(OpType);
|
||||
if (!Right)
|
||||
{
|
||||
if (!bParseError)
|
||||
{
|
||||
/* & is allowed to have an empty RHS */
|
||||
if (OpType == C_MULTI)
|
||||
return Left;
|
||||
ParseError();
|
||||
}
|
||||
FreeCommand(Left);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
|
||||
Cmd->Type = OpType;
|
||||
Cmd->Next = NULL;
|
||||
Cmd->Redirections = NULL;
|
||||
Cmd->Subcommands = Left;
|
||||
Left->Next = Right;
|
||||
Right->Next = NULL;
|
||||
}
|
||||
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
PARSED_COMMAND *
|
||||
ParseCommand(LPTSTR Line)
|
||||
{
|
||||
PARSED_COMMAND *Cmd;
|
||||
|
||||
if (Line)
|
||||
{
|
||||
_tcscpy(ParseLine, Line);
|
||||
bLineContinuations = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*if (!ReadLine(ParseLine, FALSE))*/
|
||||
return NULL;
|
||||
bLineContinuations = TRUE;
|
||||
}
|
||||
bParseError = FALSE;
|
||||
ParsePos = ParseLine;
|
||||
CurChar = _T(' ');
|
||||
|
||||
Cmd = ParseCommandOp(C_OP_LOWEST);
|
||||
if (Cmd && CurrentTokenType != TOK_END)
|
||||
{
|
||||
ParseError();
|
||||
FreeCommand(Cmd);
|
||||
Cmd = NULL;
|
||||
}
|
||||
return Cmd;
|
||||
}
|
||||
|
||||
VOID
|
||||
FreeCommand(PARSED_COMMAND *Cmd)
|
||||
{
|
||||
if (Cmd->Subcommands)
|
||||
FreeCommand(Cmd->Subcommands);
|
||||
if (Cmd->Next)
|
||||
FreeCommand(Cmd->Next);
|
||||
FreeRedirection(Cmd->Redirections);
|
||||
cmd_free(Cmd);
|
||||
}
|
|
@ -30,175 +30,6 @@
|
|||
#ifdef FEATURE_REDIRECTION
|
||||
|
||||
|
||||
static BOOL
|
||||
IsRedirection (TCHAR c)
|
||||
{
|
||||
return (c == _T('<')) || (c == _T('>')) || (c == _T('|'));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Gets the redirection info from the command line and copies the
|
||||
* file names into ifn, ofn and efn removing them from the command
|
||||
* line.
|
||||
*
|
||||
* Converts remaining command line into a series of null terminated
|
||||
* strings defined by the pipe char '|'. Each string corresponds
|
||||
* to a single executable command. A double null terminates the
|
||||
* command strings.
|
||||
*
|
||||
* Return number of command strings found.
|
||||
*
|
||||
*/
|
||||
|
||||
INT GetRedirection (LPTSTR s, REDIRECTION **RedirList)
|
||||
{
|
||||
INT num = 1;
|
||||
LPTSTR dp = s;
|
||||
LPTSTR sp = s;
|
||||
TCHAR Filename[MAX_PATH];
|
||||
|
||||
#ifdef INCLUDE_CMD_REM
|
||||
|
||||
TCHAR * line = s;
|
||||
|
||||
|
||||
while (_istspace (*line))
|
||||
line++;
|
||||
|
||||
/*first thing first. check to see if this is "rem" and hope out*/
|
||||
if(!_tcsncmp (line, _T("rem "), 4))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
/* find and remove all the redirections first */
|
||||
while (*sp)
|
||||
{
|
||||
if (*sp == _T('^'))
|
||||
{
|
||||
*dp++ = *sp++;
|
||||
*dp++ = *sp++;
|
||||
continue;
|
||||
}
|
||||
if ((*sp == _T('"')) || (*sp == _T('\'')))
|
||||
{
|
||||
/* No redirects inside quotes */
|
||||
TCHAR qc = *sp;
|
||||
|
||||
do
|
||||
*dp++ = *sp++;
|
||||
while (*sp && *sp != qc);
|
||||
|
||||
*dp++ = *sp++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int NumberGiven = (*sp >= _T('0') && *sp <= _T('9')) ? 1 : 0;
|
||||
if (sp[NumberGiven] == _T('<') || sp[NumberGiven] == _T('>'))
|
||||
{
|
||||
BYTE HandleNumber;
|
||||
BYTE Type;
|
||||
BOOL bInQuote = FALSE;
|
||||
TCHAR *fn = Filename;
|
||||
REDIRECTION *Redir, **ListPtr;
|
||||
|
||||
if (NumberGiven)
|
||||
HandleNumber = *sp++ - _T('0');
|
||||
else
|
||||
HandleNumber = *sp == _T('<') ? 0 : 1;
|
||||
|
||||
if (*sp == _T('<'))
|
||||
{
|
||||
/* input redirection */
|
||||
Type = REDIR_READ;
|
||||
sp++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* output redirection */
|
||||
Type = REDIR_WRITE;
|
||||
sp++;
|
||||
|
||||
/* append request ? */
|
||||
if (*sp == _T('>'))
|
||||
{
|
||||
Type = REDIR_APPEND;
|
||||
sp++;
|
||||
}
|
||||
}
|
||||
|
||||
while (_istspace(*sp))
|
||||
sp++;
|
||||
|
||||
/* copy file name */
|
||||
while (*sp && (bInQuote || (!IsRedirection (*sp) && !_istspace (*sp))))
|
||||
{
|
||||
bInQuote ^= (*sp == _T('"'));
|
||||
*fn++ = *sp++;
|
||||
}
|
||||
*fn++ = _T('\0');
|
||||
|
||||
/* Delete any existing redirection for the same handle number */
|
||||
ListPtr = RedirList;
|
||||
while ((Redir = *ListPtr))
|
||||
{
|
||||
if (Redir->Number == HandleNumber)
|
||||
{
|
||||
*ListPtr = Redir->Next;
|
||||
cmd_free(Redir);
|
||||
continue;
|
||||
}
|
||||
ListPtr = &Redir->Next;
|
||||
}
|
||||
|
||||
Redir = cmd_alloc(FIELD_OFFSET(REDIRECTION, Filename[fn - Filename]));
|
||||
Redir->Next = NULL;
|
||||
Redir->OldHandle = INVALID_HANDLE_VALUE;
|
||||
Redir->Number = HandleNumber;
|
||||
Redir->Type = Type;
|
||||
_tcscpy(Redir->Filename, Filename);
|
||||
*ListPtr = Redir;
|
||||
}
|
||||
else
|
||||
*dp++ = *sp++;
|
||||
}
|
||||
|
||||
*dp++ = _T('\0');
|
||||
*dp = _T('\0');
|
||||
|
||||
/* now go for the pipes */
|
||||
sp = s;
|
||||
while (*sp)
|
||||
{
|
||||
if (*sp == _T('^'))
|
||||
{
|
||||
sp++;
|
||||
sp++;
|
||||
continue;
|
||||
}
|
||||
else if ((*sp == _T('"')) || (*sp == _T('\'')))
|
||||
{
|
||||
TCHAR qc = *sp;
|
||||
|
||||
do
|
||||
sp++;
|
||||
while (*sp && *sp != qc);
|
||||
|
||||
sp++;
|
||||
}
|
||||
else if (*sp == _T('|'))
|
||||
{
|
||||
*sp++ = _T('\0');
|
||||
num++;
|
||||
}
|
||||
else
|
||||
sp++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/* cmd allows redirection of handles numbered 3-9 even though these don't
|
||||
* correspond to any STD_ constant. */
|
||||
static HANDLE ExtraHandles[10 - 3];
|
||||
|
|
|
@ -53,7 +53,6 @@ skip_ws ( LPCTSTR p )
|
|||
|
||||
INT cmd_set (LPTSTR cmd, LPTSTR param)
|
||||
{
|
||||
INT i;
|
||||
LPTSTR p;
|
||||
|
||||
if ( !_tcsncmp (param, _T("/?"), 2) )
|
||||
|
@ -62,15 +61,6 @@ INT cmd_set (LPTSTR cmd, LPTSTR param)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* remove escapes */
|
||||
if ( param[0] ) for ( i = 0; param[i+1]; i++ )
|
||||
{
|
||||
if ( param[i] == _T('^') )
|
||||
{
|
||||
memmove ( ¶m[i], ¶m[i+1], _tcslen(¶m[i]) * sizeof(TCHAR) );
|
||||
}
|
||||
}
|
||||
|
||||
/* if no parameters, show the environment */
|
||||
if (param[0] == _T('\0'))
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue