Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenfor.cGo to the documentation of this file.00001 /* 00002 * FOR.C - for internal batch command. 00003 * 00004 * 00005 * History: 00006 * 00007 * 16-Jul-1998 (Hans B Pufal) 00008 * Started. 00009 * 00010 * 16-Jul-1998 (John P Price) 00011 * Seperated commands into individual files. 00012 * 00013 * 19-Jul-1998 (Hans B Pufal) 00014 * Implementation of FOR. 00015 * 00016 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>) 00017 * Added config.h include. 00018 * 00019 * 20-Jan-1999 (Eric Kohl) 00020 * Unicode and redirection safe! 00021 * 00022 * 01-Sep-1999 (Eric Kohl) 00023 * Added help text. 00024 * 00025 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>) 00026 * Implemented preservation of echo flag. Some other for related 00027 * code in other files fixed, too. 00028 * 00029 * 28-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>) 00030 * Remove all hardcode string to En.rc 00031 */ 00032 00033 #include <precomp.h> 00034 00035 00036 /* FOR is a special command, so this function is only used for showing help now */ 00037 INT cmd_for (LPTSTR param) 00038 { 00039 TRACE ("cmd_for (\'%s\')\n", debugstr_aw(param)); 00040 00041 if (!_tcsncmp (param, _T("/?"), 2)) 00042 { 00043 ConOutResPaging(TRUE,STRING_FOR_HELP1); 00044 return 0; 00045 } 00046 00047 error_syntax(param); 00048 return 1; 00049 } 00050 00051 /* The stack of current FOR contexts. 00052 * NULL when no FOR command is active */ 00053 LPFOR_CONTEXT fc = NULL; 00054 00055 /* Get the next element of the FOR's list */ 00056 static BOOL GetNextElement(TCHAR **pStart, TCHAR **pEnd) 00057 { 00058 TCHAR *p = *pEnd; 00059 BOOL InQuotes = FALSE; 00060 while (_istspace(*p)) 00061 p++; 00062 if (!*p) 00063 return FALSE; 00064 *pStart = p; 00065 while (*p && (InQuotes || !_istspace(*p))) 00066 InQuotes ^= (*p++ == _T('"')); 00067 *pEnd = p; 00068 return TRUE; 00069 } 00070 00071 /* Execute a single instance of a FOR command */ 00072 static INT RunInstance(PARSED_COMMAND *Cmd) 00073 { 00074 if (bEcho && !bDisableBatchEcho && Cmd->Subcommands->Type != C_QUIET) 00075 { 00076 if (!bIgnoreEcho) 00077 ConOutChar(_T('\n')); 00078 PrintPrompt(); 00079 EchoCommand(Cmd->Subcommands); 00080 ConOutChar(_T('\n')); 00081 } 00082 /* Just run the command (variable expansion is done in DoDelayedExpansion) */ 00083 return ExecuteCommand(Cmd->Subcommands); 00084 } 00085 00086 /* Check if this FOR should be terminated early */ 00087 static BOOL Exiting(PARSED_COMMAND *Cmd) 00088 { 00089 /* Someone might have removed our context */ 00090 return bCtrlBreak || fc != Cmd->For.Context; 00091 } 00092 00093 /* Read the contents of a text file into memory, 00094 * dynamically allocating enough space to hold it all */ 00095 static LPTSTR ReadFileContents(FILE *InputFile, TCHAR *Buffer) 00096 { 00097 SIZE_T Len = 0; 00098 SIZE_T AllocLen = 1000; 00099 LPTSTR Contents = cmd_alloc(AllocLen * sizeof(TCHAR)); 00100 if (!Contents) 00101 return NULL; 00102 00103 while (_fgetts(Buffer, CMDLINE_LENGTH, InputFile)) 00104 { 00105 ULONG_PTR CharsRead = _tcslen(Buffer); 00106 while (Len + CharsRead >= AllocLen) 00107 { 00108 Contents = cmd_realloc(Contents, (AllocLen *= 2) * sizeof(TCHAR)); 00109 if (!Contents) 00110 return NULL; 00111 } 00112 _tcscpy(&Contents[Len], Buffer); 00113 Len += CharsRead; 00114 } 00115 00116 Contents[Len] = _T('\0'); 00117 return Contents; 00118 } 00119 00120 static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer) 00121 { 00122 LPTSTR Delims = _T(" \t"); 00123 TCHAR Eol = _T(';'); 00124 INT SkipLines = 0; 00125 DWORD Tokens = (1 << 1); 00126 BOOL RemainderVar = FALSE; 00127 TCHAR StringQuote = _T('"'); 00128 TCHAR CommandQuote = _T('\''); 00129 LPTSTR Variables[32]; 00130 TCHAR *Start, *End; 00131 INT i; 00132 INT Ret = 0; 00133 00134 if (Cmd->For.Params) 00135 { 00136 TCHAR Quote = 0; 00137 TCHAR *Param = Cmd->For.Params; 00138 if (*Param == _T('"') || *Param == _T('\'')) 00139 Quote = *Param++; 00140 00141 while (*Param && *Param != Quote) 00142 { 00143 if (*Param <= _T(' ')) 00144 { 00145 Param++; 00146 } 00147 else if (_tcsnicmp(Param, _T("delims="), 7) == 0) 00148 { 00149 Param += 7; 00150 /* delims=xxx: Specifies the list of characters that separate tokens */ 00151 Delims = Param; 00152 while (*Param && *Param != Quote) 00153 { 00154 if (*Param == _T(' ')) 00155 { 00156 TCHAR *FirstSpace = Param; 00157 Param += _tcsspn(Param, _T(" ")); 00158 /* Exclude trailing spaces if this is not the last parameter */ 00159 if (*Param && *Param != Quote) 00160 *FirstSpace = _T('\0'); 00161 break; 00162 } 00163 Param++; 00164 } 00165 if (*Param == Quote) 00166 *Param++ = _T('\0'); 00167 } 00168 else if (_tcsnicmp(Param, _T("eol="), 4) == 0) 00169 { 00170 Param += 4; 00171 /* eol=c: Lines starting with this character (may be 00172 * preceded by delimiters) are skipped. */ 00173 Eol = *Param; 00174 if (Eol != _T('\0')) 00175 Param++; 00176 } 00177 else if (_tcsnicmp(Param, _T("skip="), 5) == 0) 00178 { 00179 /* skip=n: Number of lines to skip at the beginning of each file */ 00180 SkipLines = _tcstol(Param + 5, &Param, 0); 00181 if (SkipLines <= 0) 00182 goto error; 00183 } 00184 else if (_tcsnicmp(Param, _T("tokens="), 7) == 0) 00185 { 00186 Param += 7; 00187 /* tokens=x,y,m-n: List of token numbers (must be between 00188 * 1 and 31) that will be assigned into variables. */ 00189 Tokens = 0; 00190 while (*Param && *Param != Quote && *Param != _T('*')) 00191 { 00192 INT First = _tcstol(Param, &Param, 0); 00193 INT Last = First; 00194 if (First < 1) 00195 goto error; 00196 if (*Param == _T('-')) 00197 { 00198 /* It's a range of tokens */ 00199 Last = _tcstol(Param + 1, &Param, 0); 00200 if (Last < First || Last > 31) 00201 goto error; 00202 } 00203 Tokens |= (2 << Last) - (1 << First); 00204 00205 if (*Param != _T(',')) 00206 break; 00207 Param++; 00208 } 00209 /* With an asterisk at the end, an additional variable 00210 * will be created to hold the remainder of the line 00211 * (after the last token specified). */ 00212 if (*Param == _T('*')) 00213 { 00214 RemainderVar = TRUE; 00215 Param++; 00216 } 00217 } 00218 else if (_tcsnicmp(Param, _T("useback"), 7) == 0) 00219 { 00220 Param += 7; 00221 /* usebackq: Use alternate quote characters */ 00222 StringQuote = _T('\''); 00223 CommandQuote = _T('`'); 00224 /* Can be written as either "useback" or "usebackq" */ 00225 if (_totlower(*Param) == _T('q')) 00226 Param++; 00227 } 00228 else 00229 { 00230 error: 00231 error_syntax(Param); 00232 return 1; 00233 } 00234 } 00235 } 00236 00237 /* Count how many variables will be set: one for each token, 00238 * plus maybe one for the remainder */ 00239 fc->varcount = RemainderVar; 00240 for (i = 1; i < 32; i++) 00241 fc->varcount += (Tokens >> i & 1); 00242 fc->values = Variables; 00243 00244 if (*List == StringQuote || *List == CommandQuote) 00245 { 00246 /* Treat the entire "list" as one single element */ 00247 Start = List; 00248 End = &List[_tcslen(List)]; 00249 goto single_element; 00250 } 00251 00252 End = List; 00253 while (GetNextElement(&Start, &End)) 00254 { 00255 FILE *InputFile; 00256 LPTSTR FullInput, In, NextLine; 00257 INT Skip; 00258 single_element: 00259 00260 if (*Start == StringQuote && End[-1] == StringQuote) 00261 { 00262 /* Input given directly as a string */ 00263 End[-1] = _T('\0'); 00264 FullInput = cmd_dup(Start + 1); 00265 } 00266 else if (*Start == CommandQuote && End[-1] == CommandQuote) 00267 { 00268 /* Read input from a command */ 00269 End[-1] = _T('\0'); 00270 InputFile = _tpopen(Start + 1, _T("r")); 00271 if (!InputFile) 00272 { 00273 error_bad_command(Start + 1); 00274 return 1; 00275 } 00276 FullInput = ReadFileContents(InputFile, Buffer); 00277 _pclose(InputFile); 00278 } 00279 else 00280 { 00281 /* Read input from a file */ 00282 TCHAR Temp = *End; 00283 *End = _T('\0'); 00284 StripQuotes(Start); 00285 InputFile = _tfopen(Start, _T("r")); 00286 *End = Temp; 00287 if (!InputFile) 00288 { 00289 error_sfile_not_found(Start); 00290 return 1; 00291 } 00292 FullInput = ReadFileContents(InputFile, Buffer); 00293 fclose(InputFile); 00294 } 00295 00296 if (!FullInput) 00297 { 00298 error_out_of_memory(); 00299 return 1; 00300 } 00301 00302 /* Loop over the input line by line */ 00303 In = FullInput; 00304 Skip = SkipLines; 00305 do 00306 { 00307 DWORD RemainingTokens = Tokens; 00308 LPTSTR *CurVar = Variables; 00309 00310 NextLine = _tcschr(In, _T('\n')); 00311 if (NextLine) 00312 *NextLine++ = _T('\0'); 00313 00314 if (--Skip >= 0) 00315 continue; 00316 00317 /* Ignore lines where the first token starts with the eol character */ 00318 In += _tcsspn(In, Delims); 00319 if (*In == Eol) 00320 continue; 00321 00322 while ((RemainingTokens >>= 1) != 0) 00323 { 00324 /* Save pointer to this token in a variable if requested */ 00325 if (RemainingTokens & 1) 00326 *CurVar++ = In; 00327 /* Find end of token */ 00328 In += _tcscspn(In, Delims); 00329 /* Nul-terminate it and advance to next token */ 00330 if (*In) 00331 { 00332 *In++ = _T('\0'); 00333 In += _tcsspn(In, Delims); 00334 } 00335 } 00336 /* Save pointer to remainder of line */ 00337 *CurVar = In; 00338 00339 /* Don't run unless the line had enough tokens to fill at least one variable */ 00340 if (*Variables[0]) 00341 Ret = RunInstance(Cmd); 00342 } while (!Exiting(Cmd) && (In = NextLine) != NULL); 00343 cmd_free(FullInput); 00344 } 00345 00346 return Ret; 00347 } 00348 00349 /* FOR /L: Do a numeric loop */ 00350 static INT ForLoop(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer) 00351 { 00352 enum { START, STEP, END }; 00353 INT params[3] = { 0, 0, 0 }; 00354 INT i; 00355 INT Ret = 0; 00356 00357 TCHAR *Start, *End = List; 00358 for (i = 0; i < 3 && GetNextElement(&Start, &End); i++) 00359 params[i] = _tcstol(Start, NULL, 0); 00360 00361 i = params[START]; 00362 while (!Exiting(Cmd) && 00363 (params[STEP] >= 0 ? (i <= params[END]) : (i >= params[END]))) 00364 { 00365 _itot(i, Buffer, 10); 00366 Ret = RunInstance(Cmd); 00367 i += params[STEP]; 00368 } 00369 return Ret; 00370 } 00371 00372 /* Process a FOR in one directory. Stored in Buffer (up to BufPos) is a 00373 * string which is prefixed to each element of the list. In a normal FOR 00374 * it will be empty, but in FOR /R it will be the directory name. */ 00375 static INT ForDir(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos) 00376 { 00377 TCHAR *Start, *End = List; 00378 INT Ret = 0; 00379 while (!Exiting(Cmd) && GetNextElement(&Start, &End)) 00380 { 00381 if (BufPos + (End - Start) > &Buffer[CMDLINE_LENGTH]) 00382 continue; 00383 memcpy(BufPos, Start, (End - Start) * sizeof(TCHAR)); 00384 BufPos[End - Start] = _T('\0'); 00385 00386 if (_tcschr(BufPos, _T('?')) || _tcschr(BufPos, _T('*'))) 00387 { 00388 WIN32_FIND_DATA w32fd; 00389 HANDLE hFind; 00390 TCHAR *FilePart; 00391 00392 StripQuotes(BufPos); 00393 hFind = FindFirstFile(Buffer, &w32fd); 00394 if (hFind == INVALID_HANDLE_VALUE) 00395 continue; 00396 FilePart = _tcsrchr(BufPos, _T('\\')); 00397 FilePart = FilePart ? FilePart + 1 : BufPos; 00398 do 00399 { 00400 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 00401 continue; 00402 if (!(w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 00403 != !(Cmd->For.Switches & FOR_DIRS)) 00404 continue; 00405 if (_tcscmp(w32fd.cFileName, _T(".")) == 0 || 00406 _tcscmp(w32fd.cFileName, _T("..")) == 0) 00407 continue; 00408 _tcscpy(FilePart, w32fd.cFileName); 00409 Ret = RunInstance(Cmd); 00410 } while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd)); 00411 FindClose(hFind); 00412 } 00413 else 00414 { 00415 Ret = RunInstance(Cmd); 00416 } 00417 } 00418 return Ret; 00419 } 00420 00421 /* FOR /R: Process a FOR in each directory of a tree, recursively. */ 00422 static INT ForRecursive(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer, TCHAR *BufPos) 00423 { 00424 HANDLE hFind; 00425 WIN32_FIND_DATA w32fd; 00426 INT Ret = 0; 00427 00428 if (BufPos[-1] != _T('\\')) 00429 { 00430 *BufPos++ = _T('\\'); 00431 *BufPos = _T('\0'); 00432 } 00433 00434 Ret = ForDir(Cmd, List, Buffer, BufPos); 00435 00436 _tcscpy(BufPos, _T("*")); 00437 hFind = FindFirstFile(Buffer, &w32fd); 00438 if (hFind == INVALID_HANDLE_VALUE) 00439 return Ret; 00440 do 00441 { 00442 if (!(w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 00443 continue; 00444 if (_tcscmp(w32fd.cFileName, _T(".")) == 0 || 00445 _tcscmp(w32fd.cFileName, _T("..")) == 0) 00446 continue; 00447 Ret = ForRecursive(Cmd, List, Buffer, _stpcpy(BufPos, w32fd.cFileName)); 00448 } while (!Exiting(Cmd) && FindNextFile(hFind, &w32fd)); 00449 FindClose(hFind); 00450 return Ret; 00451 } 00452 00453 BOOL 00454 ExecuteFor(PARSED_COMMAND *Cmd) 00455 { 00456 TCHAR Buffer[CMDLINE_LENGTH]; /* Buffer to hold the variable value */ 00457 LPTSTR BufferPtr = Buffer; 00458 LPFOR_CONTEXT lpNew; 00459 INT Ret; 00460 LPTSTR List = DoDelayedExpansion(Cmd->For.List); 00461 00462 if (!List) 00463 return 1; 00464 00465 /* Create our FOR context */ 00466 lpNew = cmd_alloc(sizeof(FOR_CONTEXT)); 00467 if (!lpNew) 00468 { 00469 cmd_free(List); 00470 return 1; 00471 } 00472 lpNew->prev = fc; 00473 lpNew->firstvar = Cmd->For.Variable; 00474 lpNew->varcount = 1; 00475 lpNew->values = &BufferPtr; 00476 00477 Cmd->For.Context = lpNew; 00478 fc = lpNew; 00479 00480 if (Cmd->For.Switches & FOR_F) 00481 { 00482 Ret = ForF(Cmd, List, Buffer); 00483 } 00484 else if (Cmd->For.Switches & FOR_LOOP) 00485 { 00486 Ret = ForLoop(Cmd, List, Buffer); 00487 } 00488 else if (Cmd->For.Switches & FOR_RECURSIVE) 00489 { 00490 DWORD Len = GetFullPathName(Cmd->For.Params ? Cmd->For.Params : _T("."), 00491 MAX_PATH, Buffer, NULL); 00492 Ret = ForRecursive(Cmd, List, Buffer, &Buffer[Len]); 00493 } 00494 else 00495 { 00496 Ret = ForDir(Cmd, List, Buffer, Buffer); 00497 } 00498 00499 /* Remove our context, unless someone already did that */ 00500 if (fc == lpNew) 00501 fc = lpNew->prev; 00502 00503 cmd_free(lpNew); 00504 cmd_free(List); 00505 return Ret; 00506 } 00507 00508 /* EOF */ Generated on Tue May 15 04:40:09 2012 for ReactOS by
1.6.3
|