From 781ae61f03fc8c2e31d04ddbb0841ca36f7d9386 Mon Sep 17 00:00:00 2001 From: Jeffrey Morlan Date: Mon, 22 Dec 2008 22:34:51 +0000 Subject: [PATCH] - 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 --- reactos/base/shell/cmd/batch.c | 34 ----- reactos/base/shell/cmd/batch.h | 2 - reactos/base/shell/cmd/cmd.c | 5 +- reactos/base/shell/cmd/cmd.h | 28 +++- reactos/base/shell/cmd/cmdtable.c | 2 +- reactos/base/shell/cmd/if.c | 236 ++++++++++-------------------- reactos/base/shell/cmd/parser.c | 206 ++++++++++++++++++++++---- 7 files changed, 289 insertions(+), 224 deletions(-) diff --git a/reactos/base/shell/cmd/batch.c b/reactos/base/shell/cmd/batch.c index ef282689b3e..93fc56dee23 100644 --- a/reactos/base/shell/cmd/batch.c +++ b/reactos/base/shell/cmd/batch.c @@ -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; } diff --git a/reactos/base/shell/cmd/batch.h b/reactos/base/shell/cmd/batch.h index 998f85cb026..74be92f6b69 100644 --- a/reactos/base/shell/cmd/batch.h +++ b/reactos/base/shell/cmd/batch.h @@ -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; diff --git a/reactos/base/shell/cmd/cmd.c b/reactos/base/shell/cmd/cmd.c index 116066c25ff..39593108f7a 100644 --- a/reactos/base/shell/cmd/cmd.c +++ b/reactos/base/shell/cmd/cmd.c @@ -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); diff --git a/reactos/base/shell/cmd/cmd.h b/reactos/base/shell/cmd/cmd.h index e89c3aeb7a8..5a243b843f9 100644 --- a/reactos/base/shell/cmd/cmd.h +++ b/reactos/base/shell/cmd/cmd.h @@ -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); diff --git a/reactos/base/shell/cmd/cmdtable.c b/reactos/base/shell/cmd/cmdtable.c index 516844fa312..9fc7cd25357 100644 --- a/reactos/base/shell/cmd/cmdtable.c +++ b/reactos/base/shell/cmd/cmdtable.c @@ -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}, diff --git a/reactos/base/shell/cmd/if.c b/reactos/base/shell/cmd/if.c index 061173bedd3..c462ce02710 100644 --- a/reactos/base/shell/cmd/if.c +++ b/reactos/base/shell/cmd/if.c @@ -32,15 +32,30 @@ #include - -#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 */ diff --git a/reactos/base/shell/cmd/parser.c b/reactos/base/shell/cmd/parser.c index 287110a2dfa..7c35a57e5d6 100644 --- a/reactos/base/shell/cmd/parser.c +++ b/reactos/base/shell/cmd/parser.c @@ -1,3 +1,8 @@ +/* + * PARSER.C - command parsing. + * + */ + #include #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); }