Author: jmorlan
Date: Tue Feb 24 23:29:18 2009
New Revision: 39742
URL:
http://svn.reactos.org/svn/reactos?rev=39742&view=rev
Log:
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.
Modified:
trunk/reactos/base/shell/cmd/batch.c
trunk/reactos/base/shell/cmd/batch.h
trunk/reactos/base/shell/cmd/call.c
trunk/reactos/base/shell/cmd/cmd.c
trunk/reactos/base/shell/cmd/cmd.h
trunk/reactos/base/shell/cmd/for.c
trunk/reactos/base/shell/cmd/parser.c
Modified: trunk/reactos/base/shell/cmd/batch.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/batch.c?rev…
==============================================================================
--- trunk/reactos/base/shell/cmd/batch.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/batch.c [iso-8859-1] Tue Feb 24 23:29:18 2009
@@ -187,12 +187,6 @@
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 @@
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 @@
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");
Modified: trunk/reactos/base/shell/cmd/batch.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/batch.h?rev…
==============================================================================
--- trunk/reactos/base/shell/cmd/batch.h [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/batch.h [iso-8859-1] Tue Feb 24 23:29:18 2009
@@ -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;
Modified: trunk/reactos/base/shell/cmd/call.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/call.c?rev=…
==============================================================================
--- trunk/reactos/base/shell/cmd/call.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/call.c [iso-8859-1] Tue Feb 24 23:29:18 2009
@@ -84,7 +84,7 @@
bc->params = NULL;
bc->shiftlevel = 0;
bc->forvar = 0; /* HBP004 */
- bc->forproto = NULL; /* HBP004 */
+ bc->forvarcount = 0;
bc->RedirList = NULL;
ParseCommandLine (param);
Modified: trunk/reactos/base/shell/cmd/cmd.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/cmd.c?rev=3…
==============================================================================
--- trunk/reactos/base/shell/cmd/cmd.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/cmd.c [iso-8859-1] Tue Feb 24 23:29:18 2009
@@ -913,6 +913,9 @@
break;
case C_IF:
Success = ExecuteIf(Cmd);
+ break;
+ case C_FOR:
+ Success = ExecuteFor(Cmd);
break;
}
@@ -1271,14 +1274,15 @@
{
/* 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;
}
Modified: trunk/reactos/base/shell/cmd/cmd.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/cmd.h?rev=3…
==============================================================================
--- trunk/reactos/base/shell/cmd/cmd.h [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/cmd.h [iso-8859-1] Tue Feb 24 23:29:18 2009
@@ -237,7 +237,12 @@
/* 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 @@
/* 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 @@
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);
Modified: trunk/reactos/base/shell/cmd/for.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/for.c?rev=3…
==============================================================================
--- trunk/reactos/base/shell/cmd/for.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/for.c [iso-8859-1] Tue Feb 24 23:29:18 2009
@@ -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 @@
return 0;
}
- /* Check that first element is % then an alpha char followed by space */
- if ((*param != _T('%')) || !_istalpha (*(param + 1)) || !_istspace (*(param +
2)))
- {
- LoadString( CMD_ModuleHandle, STRING_FOR_ERROR, szMsg, RC_STRING_MAX_SIZE);
- error_syntax (szMsg);
- return 1;
- }
-
- param++;
- var = *param++; /* Save FOR var name */
-
- while (_istspace (*param))
- param++;
-
- /* Check next element is 'IN' */
- if ((_tcsnicmp (param, _T("in"), 2) != 0) || !_istspace (*(param + 2)))
- {
- LoadString(CMD_ModuleHandle, STRING_FOR_ERROR1, szMsg, RC_STRING_MAX_SIZE);
- error_syntax(szMsg);
- return 1;
- }
-
- param += 2;
- while (_istspace (*param))
- param++;
-
- /* Folowed by a '(', find also matching ')' */
- if ((*param != _T('(')) || (NULL == (pp = _tcsrchr (param, _T(')')))))
- {
- LoadString(CMD_ModuleHandle, STRING_FOR_ERROR2, szMsg, RC_STRING_MAX_SIZE);
- error_syntax(szMsg);
- return 1;
- }
-
- *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)))
- {
- LoadString(CMD_ModuleHandle, STRING_FOR_ERROR3, szMsg, RC_STRING_MAX_SIZE);
- error_syntax(szMsg);
- return 1;
- }
-
- pp += 2;
- while (_istspace (*pp))
- pp++;
-
- /* Check that command tail is not empty */
- if (*pp == _T('\0'))
- {
- LoadString(CMD_ModuleHandle, STRING_FOR_ERROR4, szMsg, RC_STRING_MAX_SIZE);
- error_syntax(szMsg);
- return 1;
- }
-
- /* OK all is correct, build a bcontext.... */
- lpNew = (LPBATCH_CONTEXT)cmd_alloc (sizeof (BATCH_CONTEXT));
+ 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)
+ {
+ 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;
+ }
+
+ Contents[Len] = _T('\0');
+ return Contents;
+}
+
+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;
+
+ if (Cmd->For.Params)
+ {
+ 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;
+ }
+ }
+ }
+
+ /* 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;
+
+ if (*List == StringQuote || *List == CommandQuote)
+ {
+ /* Treat the entire "list" as one single element */
+ Start = List;
+ End = &List[_tcslen(List)];
+ goto single_element;
+ }
+
+ End = List;
+ while (GetNextElement(&Start, &End))
+ {
+ 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);
+ }
+
+ return TRUE;
+}
+
+/* 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])))
+ {
+ _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');
+ }
+
+ 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;
-
- return 0;
+ 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);
+ }
+
+ /* Remove our context, unless someone already did that */
+ if (bc == lpNew)
+ ExitBatch(NULL);
+
+ cmd_free(List);
+ return Success;
}
/* EOF */
Modified: trunk/reactos/base/shell/cmd/parser.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/parser.c?re…
==============================================================================
--- trunk/reactos/base/shell/cmd/parser.c [iso-8859-1] (original)
+++ trunk/reactos/base/shell/cmd/parser.c [iso-8859-1] Tue Feb 24 23:29:18 2009
@@ -413,6 +413,115 @@
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 @@
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 @@
FreeRedirection(RedirList);
return NULL;
}
- return ParseIf();
+ return _totlower(*ParsedLine) == _T('f') ? ParseFor() : ParseIf();
}
Pos = _stpcpy(Pos, _T(" /?"));
}
@@ -665,6 +775,17 @@
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 @@
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);
}