Implement complete support for FOR command (including /D, /F, /L, and /R switches). cmd is now close to being able to run RosBE 1.3's initialization without errors.

svn path=/trunk/; revision=39742
This commit is contained in:
Jeffrey Morlan 2009-02-24 20:29:18 +00:00
parent 93ce0dce1f
commit b18a547c1b
7 changed files with 574 additions and 156 deletions

View file

@ -188,12 +188,6 @@ VOID ExitBatch (LPTSTR msg)
if (bc->params)
cmd_free(bc->params);
if (bc->forproto)
cmd_free(bc->forproto);
if (bc->ffind)
cmd_free(bc->ffind);
UndoRedirection(bc->RedirList, NULL);
FreeRedirection(bc->RedirList);
@ -270,9 +264,8 @@ BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, BOOL forcenew)
bc->bEcho = bEcho; /* Preserve echo across batch calls */
bc->shiftlevel = 0;
bc->ffind = NULL;
bc->forvar = _T('\0');
bc->forproto = NULL;
bc->forvarcount = 0;
bc->params = BatchParams (firstword, param);
//
// Allocate enough memory to hold the params and copy them over without modifications
@ -351,76 +344,6 @@ LPTSTR ReadBatchLine ()
if (bc == NULL)
return NULL;
/* If its a FOR context... */
if (bc->forvar)
{
LPTSTR sp = bc->forproto; /* pointer to prototype command */
LPTSTR dp = textline; /* Place to expand protoype */
LPTSTR fv = FindArg (0); /* Next list element */
/* End of list so... */
if ((fv == NULL) || (*fv == _T('\0')))
{
/* just exit this context */
ExitBatch (NULL);
continue;
}
if (_tcscspn (fv, _T("?*")) == _tcslen (fv))
{
/* element is wild file */
bc->shiftlevel++; /* No use it and shift list */
}
else
{
/* Wild file spec, find first (or next) file name */
if (bc->ffind)
{
/* First already done so do next */
fv = FindNextFile (bc->hFind, bc->ffind) ? bc->ffind->cFileName : NULL;
}
else
{
/* For first find, allocate a find first block */
if ((bc->ffind = (LPWIN32_FIND_DATA)cmd_alloc (sizeof (WIN32_FIND_DATA))) == NULL)
{
error_out_of_memory();
return NULL;
}
bc->hFind = FindFirstFile (fv, bc->ffind);
fv = !(bc->hFind==INVALID_HANDLE_VALUE) ? bc->ffind->cFileName : NULL;
}
if (fv == NULL)
{
/* Null indicates no more files.. */
cmd_free (bc->ffind); /* free the buffer */
bc->ffind = NULL;
bc->shiftlevel++; /* On to next list element */
continue;
}
}
/* At this point, fv points to parameter string */
bc->forvalue = fv;
/* Double up % signs so they will get through the parser intact */
while (*sp)
{
if (*sp == _T('%'))
*dp++ = _T('%');
*dp++ = *sp++;
}
*dp++ = _T('\n');
*dp = _T('\0');
return textline;
}
if (!FileGetString (bc->hBatchFile, textline, sizeof (textline) / sizeof (textline[0]) - 1))
{
TRACE ("ReadBatchLine(): Reached EOF!\n");

View file

@ -10,18 +10,16 @@
typedef struct tagBATCHCONTEXT
{
struct tagBATCHCONTEXT *prev;
LPWIN32_FIND_DATA ffind;
HANDLE hBatchFile;
TCHAR BatchFilePath[MAX_PATH];
LPTSTR forproto;
LPTSTR params;
LPTSTR raw_params; /* Holds the raw params given by the input */
INT shiftlevel;
BOOL bEcho; /* Preserve echo flag across batch calls */
HANDLE hFind; /* Preserve find handle when doing a for */
REDIRECTION *RedirList;
TCHAR forvar;
LPTSTR forvalue;
UINT forvarcount;
LPTSTR *forvalues;
} BATCH_CONTEXT, *LPBATCH_CONTEXT;

View file

@ -84,7 +84,7 @@ INT cmd_call (LPTSTR param)
bc->params = NULL;
bc->shiftlevel = 0;
bc->forvar = 0; /* HBP004 */
bc->forproto = NULL; /* HBP004 */
bc->forvarcount = 0;
bc->RedirList = NULL;
ParseCommandLine (param);

View file

@ -914,6 +914,9 @@ ExecuteCommand(PARSED_COMMAND *Cmd)
case C_IF:
Success = ExecuteIf(Cmd);
break;
case C_FOR:
Success = ExecuteFor(Cmd);
break;
}
UndoRedirection(Cmd->Redirections, NULL);
@ -1271,14 +1274,15 @@ SubstituteForVars(TCHAR *Src, TCHAR *Dest)
{
/* This might be a variable. Search the list of contexts for it */
BATCH_CONTEXT *Ctx = bc;
while (Ctx && Ctx->forvar != Src[1])
while (Ctx && (UINT)(Src[1] - Ctx->forvar) >= Ctx->forvarcount)
Ctx = Ctx->prev;
if (Ctx)
{
/* Found it */
if (Dest + _tcslen(Ctx->forvalue) > DestEnd)
LPTSTR Value = Ctx->forvalues[Src[1] - Ctx->forvar];
if (Dest + _tcslen(Value) > DestEnd)
return FALSE;
Dest = _stpcpy(Dest, Ctx->forvalue);
Dest = _stpcpy(Dest, Value);
Src += 2;
continue;
}

View file

@ -237,7 +237,12 @@ VOID CompleteFilename (LPTSTR, BOOL, LPTSTR, UINT);
/* Prototypes for FOR.C */
#define FOR_DIRS 1 /* /D */
#define FOR_F 2 /* /F */
#define FOR_LOOP 4 /* /L */
#define FOR_RECURSIVE 8 /* /R */
INT cmd_for (LPTSTR);
BOOL ExecuteFor(struct _PARSED_COMMAND *Cmd);
/* Prototypes for FREE.C */
@ -342,7 +347,7 @@ INT CommandMsgbox (LPTSTR);
/* Prototypes from PARSER.C */
enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS, C_PIPE, C_IF };
enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS, C_PIPE, C_IF, C_FOR };
typedef struct _PARSED_COMMAND
{
struct _PARSED_COMMAND *Subcommands;
@ -363,6 +368,14 @@ typedef struct _PARSED_COMMAND
TCHAR *LeftArg;
TCHAR *RightArg;
} If;
struct
{
BYTE Switches;
TCHAR Variable;
LPTSTR Params;
LPTSTR List;
struct tagBATCHCONTEXT *Context;
} For;
};
} PARSED_COMMAND;
PARSED_COMMAND *ParseCommand(LPTSTR Line);

View file

@ -33,27 +33,9 @@
#include <precomp.h>
/*
* Perform FOR command.
*
* First check syntax is correct : FOR %v IN ( <list> ) DO <command>
* v must be alphabetic, <command> must not be empty.
*
* If all is correct build a new bcontext structure which preserves
* the necessary information so that readbatchline can expand
* each the command prototype for each list element.
*
* You might look on a FOR as being a called batch file with one line
* per list element.
*/
/* FOR is a special command, so this function is only used for showing help now */
INT cmd_for (LPTSTR param)
{
LPBATCH_CONTEXT lpNew;
LPTSTR pp;
TCHAR var;
TCHAR szMsg[RC_STRING_MAX_SIZE];
TRACE ("cmd_for (\'%s\')\n", debugstr_aw(param));
if (!_tcsncmp (param, _T("/?"), 2))
@ -62,87 +44,459 @@ INT cmd_for (LPTSTR param)
return 0;
}
/* Check that first element is % then an alpha char followed by space */
if ((*param != _T('%')) || !_istalpha (*(param + 1)) || !_istspace (*(param + 2)))
error_syntax(param);
return 1;
}
/* Get the next element of the FOR's list */
static BOOL GetNextElement(TCHAR **pStart, TCHAR **pEnd)
{
TCHAR *p = *pEnd;
BOOL InQuotes = FALSE;
while (_istspace(*p))
p++;
if (!*p)
return FALSE;
*pStart = p;
while (*p && (InQuotes || !_istspace(*p)))
InQuotes ^= (*p++ == _T('"'));
*pEnd = p;
return TRUE;
}
/* Execute a single instance of a FOR command */
static void RunInstance(PARSED_COMMAND *Cmd)
{
if (bEcho && Cmd->Subcommands->Type != C_QUIET)
{
LoadString( CMD_ModuleHandle, STRING_FOR_ERROR, szMsg, RC_STRING_MAX_SIZE);
error_syntax (szMsg);
return 1;
ConOutChar(_T('\n'));
PrintPrompt();
EchoCommand(Cmd->Subcommands);
ConOutChar(_T('\n'));
}
/* Just run the command (variable expansion is done in DoDelayedExpansion) */
ExecuteCommand(Cmd->Subcommands);
}
/* Check if this FOR should be terminated early */
static BOOL Exiting(PARSED_COMMAND *Cmd)
{
/* Someone might have removed our context by calling ExitBatch */
return bCtrlBreak || bc != Cmd->For.Context;
}
/* Read the contents of a text file into memory,
* dynamically allocating enough space to hold it all */
static LPTSTR ReadFileContents(FILE *InputFile, TCHAR *Buffer)
{
DWORD Len = 0;
DWORD AllocLen = 1000;
LPTSTR Contents = cmd_alloc(AllocLen * sizeof(TCHAR));
if (!Contents)
return NULL;
while (_fgetts(Buffer, CMDLINE_LENGTH, InputFile))
{
DWORD CharsRead = _tcslen(Buffer);
while (Len + CharsRead >= AllocLen)
{
Contents = cmd_realloc(Contents, (AllocLen *= 2) * sizeof(TCHAR));
if (!Contents)
return NULL;
}
_tcscpy(&Contents[Len], Buffer);
Len += CharsRead;
}
param++;
var = *param++; /* Save FOR var name */
Contents[Len] = _T('\0');
return Contents;
}
while (_istspace (*param))
param++;
static BOOL ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
{
LPTSTR Delims = _T(" \t");
TCHAR Eol = _T(';');
INT SkipLines = 0;
DWORD Tokens = (1 << 1);
BOOL RemainderVar = FALSE;
TCHAR StringQuote = _T('"');
TCHAR CommandQuote = _T('\'');
LPTSTR Variables[32];
TCHAR *Start, *End;
INT i;
/* Check next element is 'IN' */
if ((_tcsnicmp (param, _T("in"), 2) != 0) || !_istspace (*(param + 2)))
if (Cmd->For.Params)
{
LoadString(CMD_ModuleHandle, STRING_FOR_ERROR1, szMsg, RC_STRING_MAX_SIZE);
error_syntax(szMsg);
return 1;
TCHAR Quote = 0;
TCHAR *Param = Cmd->For.Params;
if (*Param == _T('"') || *Param == _T('\''))
Quote = *Param++;
while (*Param && *Param != Quote)
{
if (*Param <= _T(' '))
{
Param++;
}
else if (_tcsnicmp(Param, _T("delims="), 7) == 0)
{
Param += 7;
/* delims=xxx: Specifies the list of characters that separate tokens */
Delims = Param;
while (*Param && *Param != Quote)
{
if (*Param == _T(' '))
{
TCHAR *FirstSpace = Param;
Param += _tcsspn(Param, _T(" "));
/* Exclude trailing spaces if this is not the last parameter */
if (*Param && *Param != Quote)
*FirstSpace = _T('\0');
break;
}
Param++;
}
if (*Param == Quote)
*Param++ = _T('\0');
}
else if (_tcsnicmp(Param, _T("eol="), 4) == 0)
{
Param += 4;
/* eol=c: Lines starting with this character (may be
* preceded by delimiters) are skipped. */
Eol = *Param;
if (Eol != _T('\0'))
Param++;
}
else if (_tcsnicmp(Param, _T("skip="), 5) == 0)
{
/* skip=n: Number of lines to skip at the beginning of each file */
SkipLines = _tcstol(Param + 5, &Param, 0);
if (SkipLines <= 0)
goto error;
}
else if (_tcsnicmp(Param, _T("tokens="), 7) == 0)
{
Param += 7;
/* tokens=x,y,m-n: List of token numbers (must be between
* 1 and 31) that will be assigned into variables. */
Tokens = 0;
while (*Param && *Param != Quote && *Param != _T('*'))
{
INT First = _tcstol(Param, &Param, 0);
INT Last = First;
if (First < 1)
goto error;
if (*Param == _T('-'))
{
/* It's a range of tokens */
Last = _tcstol(Param + 1, &Param, 0);
if (Last < First || Last > 31)
goto error;
}
Tokens |= (2 << Last) - (1 << First);
if (*Param != _T(','))
break;
Param++;
}
/* With an asterisk at the end, an additional variable
* will be created to hold the remainder of the line
* (after the last token specified). */
if (*Param == _T('*'))
{
RemainderVar = TRUE;
Param++;
}
}
else if (_tcsnicmp(Param, _T("useback"), 7) == 0)
{
Param += 7;
/* usebackq: Use alternate quote characters */
StringQuote = _T('\'');
CommandQuote = _T('`');
/* Can be written as either "useback" or "usebackq" */
if (_totlower(*Param) == _T('q'))
Param++;
}
else
{
error:
error_syntax(Param);
return FALSE;
}
}
}
param += 2;
while (_istspace (*param))
param++;
/* Count how many variables will be set: one for each token,
* plus maybe one for the remainder */
bc->forvarcount = RemainderVar;
for (i = 1; i < 32; i++)
bc->forvarcount += (Tokens >> i & 1);
bc->forvalues = Variables;
/* Folowed by a '(', find also matching ')' */
if ((*param != _T('(')) || (NULL == (pp = _tcsrchr (param, _T(')')))))
if (*List == StringQuote || *List == CommandQuote)
{
LoadString(CMD_ModuleHandle, STRING_FOR_ERROR2, szMsg, RC_STRING_MAX_SIZE);
error_syntax(szMsg);
return 1;
/* Treat the entire "list" as one single element */
Start = List;
End = &List[_tcslen(List)];
goto single_element;
}
*pp++ = _T('\0');
param++; /* param now points at null terminated list */
while (_istspace (*pp))
pp++;
/* Check DO follows */
if ((_tcsnicmp (pp, _T("do"), 2) != 0) || !_istspace (*(pp + 2)))
End = List;
while (GetNextElement(&Start, &End))
{
LoadString(CMD_ModuleHandle, STRING_FOR_ERROR3, szMsg, RC_STRING_MAX_SIZE);
error_syntax(szMsg);
return 1;
FILE *InputFile;
LPTSTR FullInput, In, NextLine;
INT Skip;
single_element:
if (*Start == StringQuote && End[-1] == StringQuote)
{
/* Input given directly as a string */
End[-1] = _T('\0');
FullInput = cmd_dup(Start + 1);
}
else if (*Start == CommandQuote && End[-1] == CommandQuote)
{
/* Read input from a command */
End[-1] = _T('\0');
InputFile = _tpopen(Start + 1, _T("r"));
if (!InputFile)
{
error_bad_command();
return FALSE;
}
FullInput = ReadFileContents(InputFile, Buffer);
_pclose(InputFile);
}
else
{
/* Read input from a file */
TCHAR Temp = *End;
*End = _T('\0');
StripQuotes(Start);
InputFile = _tfopen(Start, _T("r"));
*End = Temp;
if (!InputFile)
{
error_sfile_not_found(Start);
return FALSE;
}
FullInput = ReadFileContents(InputFile, Buffer);
fclose(InputFile);
}
if (!FullInput)
{
error_out_of_memory();
return FALSE;
}
/* Loop over the input line by line */
In = FullInput;
Skip = SkipLines;
do
{
DWORD RemainingTokens = Tokens;
LPTSTR *CurVar = Variables;
NextLine = _tcschr(In, _T('\n'));
if (NextLine)
*NextLine++ = _T('\0');
if (--Skip >= 0)
continue;
/* Ignore lines where the first token starts with the eol character */
In += _tcsspn(In, Delims);
if (*In == Eol)
continue;
while ((RemainingTokens >>= 1) != 0)
{
/* Save pointer to this token in a variable if requested */
if (RemainingTokens & 1)
*CurVar++ = In;
/* Find end of token */
In += _tcscspn(In, Delims);
/* Nul-terminate it and advance to next token */
if (*In)
{
*In++ = _T('\0');
In += _tcsspn(In, Delims);
}
}
/* Save pointer to remainder of line */
*CurVar = In;
/* Don't run unless the line had enough tokens to fill at least one variable */
if (*Variables[0])
RunInstance(Cmd);
} while (!Exiting(Cmd) && (In = NextLine) != NULL);
cmd_free(FullInput);
}
pp += 2;
while (_istspace (*pp))
pp++;
return TRUE;
}
/* Check that command tail is not empty */
if (*pp == _T('\0'))
/* FOR /L: Do a numeric loop */
static void ForLoop(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
{
enum { START, STEP, END };
INT params[3] = { 0, 0, 0 };
INT i;
TCHAR *Start, *End = List;
for (i = 0; i < 3 && GetNextElement(&Start, &End); i++)
params[i] = _tcstol(Start, NULL, 0);
i = params[START];
while (!Exiting(Cmd) &&
(params[STEP] >= 0 ? (i <= params[END]) : (i >= params[END])))
{
LoadString(CMD_ModuleHandle, STRING_FOR_ERROR4, szMsg, RC_STRING_MAX_SIZE);
error_syntax(szMsg);
return 1;
_itot(i, Buffer, 10);
RunInstance(Cmd);
i += params[STEP];
}
}
/* Process a FOR in one directory. Stored in Buffer (up to BufPos) is a
* string which is prefixed to each element of the list. In a normal FOR
* it will be empty, but in FOR /R it will be the directory name. */
static void ForDir(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos)
{
TCHAR *Start, *End = List;
while (!Exiting(Cmd) && GetNextElement(&Start, &End))
{
if (BufPos + (End - Start) > &Buffer[CMDLINE_LENGTH])
continue;
memcpy(BufPos, Start, (End - Start) * sizeof(TCHAR));
BufPos[End - Start] = _T('\0');
if (_tcschr(BufPos, _T('?')) || _tcschr(BufPos, _T('*')))
{
WIN32_FIND_DATA w32fd;
HANDLE hFind;
TCHAR *FilePart;
StripQuotes(BufPos);
hFind = FindFirstFile(Buffer, &w32fd);
if (hFind == INVALID_HANDLE_VALUE)
continue;
FilePart = _tcsrchr(BufPos, _T('\\'));
FilePart = FilePart ? FilePart + 1 : BufPos;
do
{
if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
continue;
if (!(w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
!= !(Cmd->For.Switches & FOR_DIRS))
continue;
if (_tcscmp(w32fd.cFileName, _T(".")) == 0 ||
_tcscmp(w32fd.cFileName, _T("..")) == 0)
continue;
_tcscpy(FilePart, w32fd.cFileName);
RunInstance(Cmd);
} while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd));
FindClose(hFind);
}
else
{
RunInstance(Cmd);
}
}
}
/* FOR /R: Process a FOR in each directory of a tree, recursively. */
static void ForRecursive(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos)
{
HANDLE hFind;
WIN32_FIND_DATA w32fd;
if (BufPos[-1] != _T('\\'))
{
*BufPos++ = _T('\\');
*BufPos = _T('\0');
}
/* OK all is correct, build a bcontext.... */
lpNew = (LPBATCH_CONTEXT)cmd_alloc (sizeof (BATCH_CONTEXT));
ForDir(Cmd, List, Buffer, BufPos);
_tcscpy(BufPos, _T("*"));
hFind = FindFirstFile(Buffer, &w32fd);
if (hFind == INVALID_HANDLE_VALUE)
return;
do
{
if (!(w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
continue;
if (_tcscmp(w32fd.cFileName, _T(".")) == 0 ||
_tcscmp(w32fd.cFileName, _T("..")) == 0)
continue;
ForRecursive(Cmd, List, Buffer, _stpcpy(BufPos, w32fd.cFileName));
} while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd));
FindClose(hFind);
}
BOOL
ExecuteFor(PARSED_COMMAND *Cmd)
{
TCHAR Buffer[CMDLINE_LENGTH]; /* Buffer to hold the variable value */
LPTSTR BufferPtr = Buffer;
LPBATCH_CONTEXT lpNew;
BOOL Success = TRUE;
LPTSTR List = DoDelayedExpansion(Cmd->For.List);
if (!List)
return FALSE;
/* Create our pseudo-batch-context */
lpNew = cmd_alloc(sizeof(BATCH_CONTEXT));
if (!lpNew)
return FALSE;
Cmd->For.Context = lpNew;
lpNew->prev = bc;
bc = lpNew;
bc->hBatchFile = INVALID_HANDLE_VALUE;
bc->ffind = NULL;
bc->raw_params = NULL;
bc->params = BatchParams (_T(""), param); /* Split out list */
bc->params = NULL;
bc->shiftlevel = 0;
bc->forvar = var;
bc->forproto = cmd_dup (pp);
bc->forvar = Cmd->For.Variable;
bc->forvarcount = 1;
bc->forvalues = &BufferPtr;
if (bc->prev)
bc->bEcho = bc->prev->bEcho;
else
bc->bEcho = bEcho;
bc->RedirList = NULL;
if (Cmd->For.Switches & FOR_F)
{
Success = ForF(Cmd, List, Buffer);
}
else if (Cmd->For.Switches & FOR_LOOP)
{
ForLoop(Cmd, List, Buffer);
}
else if (Cmd->For.Switches & FOR_RECURSIVE)
{
DWORD Len = GetFullPathName(Cmd->For.Params ? Cmd->For.Params : _T("."),
MAX_PATH, Buffer, NULL);
ForRecursive(Cmd, List, Buffer, &Buffer[Len]);
}
else
{
ForDir(Cmd, List, Buffer, Buffer);
}
return 0;
/* Remove our context, unless someone already did that */
if (bc == lpNew)
ExitBatch(NULL);
cmd_free(List);
return Success;
}
/* EOF */

View file

@ -413,6 +413,115 @@ condition_done:
return Cmd;
}
/* Parse a FOR command.
* Syntax is: FOR [options] %var IN (list) DO command */
static PARSED_COMMAND *ParseFor(void)
{
PARSED_COMMAND *Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
TCHAR List[CMDLINE_LENGTH];
TCHAR *Pos = List;
memset(Cmd, 0, sizeof(PARSED_COMMAND));
Cmd->Type = C_FOR;
while (1)
{
if (_tcsicmp(CurrentToken, _T("/D")) == 0)
Cmd->For.Switches |= FOR_DIRS;
else if (_tcsicmp(CurrentToken, _T("/F")) == 0)
{
Cmd->For.Switches |= FOR_F;
if (!Cmd->For.Params)
{
ParseToken(0, STANDARD_SEPS);
if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
break;
Cmd->For.Params = cmd_dup(CurrentToken);
}
}
else if (_tcsicmp(CurrentToken, _T("/L")) == 0)
Cmd->For.Switches |= FOR_LOOP;
else if (_tcsicmp(CurrentToken, _T("/R")) == 0)
{
Cmd->For.Switches |= FOR_RECURSIVE;
if (!Cmd->For.Params)
{
ParseToken(0, STANDARD_SEPS);
if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
break;
StripQuotes(CurrentToken);
Cmd->For.Params = cmd_dup(CurrentToken);
}
}
else
break;
ParseToken(0, STANDARD_SEPS);
}
/* Make sure there aren't two different switches specified
* at the same time, unless they're /D and /R */
if ((Cmd->For.Switches & (Cmd->For.Switches - 1)) != 0
&& Cmd->For.Switches != (FOR_DIRS | FOR_RECURSIVE))
{
goto error;
}
/* Variable name should be % and just one other character */
if (CurrentToken[0] != _T('%') || _tcslen(CurrentToken) != 2)
goto error;
Cmd->For.Variable = CurrentToken[1];
ParseToken(0, STANDARD_SEPS);
if (_tcsicmp(CurrentToken, _T("in")) != 0)
goto error;
if (ParseToken(_T('('), STANDARD_SEPS) != TOK_BEGIN_BLOCK)
goto error;
while (1)
{
int Type;
/* Pretend we're inside a block so the tokenizer will stop on ')' */
InsideBlock++;
Type = ParseToken(0, STANDARD_SEPS);
InsideBlock--;
if (Type == TOK_END_BLOCK)
break;
if (Type != TOK_NORMAL)
goto error;
if (Pos != List)
*Pos++ = _T(' ');
if (Pos + _tcslen(CurrentToken) >= &List[CMDLINE_LENGTH])
goto error;
Pos = _stpcpy(Pos, CurrentToken);
}
*Pos = _T('\0');
Cmd->For.List = cmd_dup(List);
ParseToken(0, STANDARD_SEPS);
if (_tcsicmp(CurrentToken, _T("do")) != 0)
goto error;
Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
if (Cmd->Subcommands == NULL)
{
FreeCommand(Cmd);
return NULL;
}
return Cmd;
error:
FreeCommand(Cmd);
ParseError();
return NULL;
}
static PARSED_COMMAND *ParseCommandPart(void)
{
TCHAR ParsedLine[CMDLINE_LENGTH];
@ -486,7 +595,8 @@ static PARSED_COMMAND *ParseCommandPart(void)
TailOffset = Pos - ParsedLine;
/* Check for special forms */
if (_tcsicmp(ParsedLine, _T("if")) == 0)
if (_tcsicmp(ParsedLine, _T("for")) == 0 ||
_tcsicmp(ParsedLine, _T("if")) == 0)
{
ParseToken(0, STANDARD_SEPS);
/* Do special parsing only if it's not followed by /? */
@ -498,7 +608,7 @@ static PARSED_COMMAND *ParseCommandPart(void)
FreeRedirection(RedirList);
return NULL;
}
return ParseIf();
return _totlower(*ParsedLine) == _T('f') ? ParseFor() : ParseIf();
}
Pos = _stpcpy(Pos, _T(" /?"));
}
@ -665,6 +775,17 @@ EchoCommand(PARSED_COMMAND *Cmd)
EchoCommand(Sub->Next);
}
break;
case C_FOR:
ConOutPrintf(_T("for"));
if (Cmd->For.Switches & FOR_DIRS) ConOutPrintf(_T(" /D"));
if (Cmd->For.Switches & FOR_F) ConOutPrintf(_T(" /F"));
if (Cmd->For.Switches & FOR_LOOP) ConOutPrintf(_T(" /L"));
if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPrintf(_T(" /R"));
if (Cmd->For.Params)
ConOutPrintf(_T(" %s"), Cmd->For.Params);
ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
EchoCommand(Cmd->Subcommands);
break;
}
for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
@ -688,5 +809,10 @@ FreeCommand(PARSED_COMMAND *Cmd)
cmd_free(Cmd->If.LeftArg);
cmd_free(Cmd->If.RightArg);
}
else if (Cmd->Type == C_FOR)
{
cmd_free(Cmd->For.Params);
cmd_free(Cmd->For.List);
}
cmd_free(Cmd);
}