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=3... ============================================================================== --- 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=39... ============================================================================== --- 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=39... ============================================================================== --- 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=39... ============================================================================== --- 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?rev... ============================================================================== --- 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); }