Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenbatch.c
Go to the documentation of this file.
00001 /* 00002 * BATCH.C - batch file processor for CMD.EXE. 00003 * 00004 * 00005 * History: 00006 * 00007 * ??/??/?? (Evan Jeffrey) 00008 * started. 00009 * 00010 * 15 Jul 1995 (Tim Norman) 00011 * modes and bugfixes. 00012 * 00013 * 08 Aug 1995 (Matt Rains) 00014 * i have cleaned up the source code. changes now bring this 00015 * source into guidelines for recommended programming practice. 00016 * 00017 * i have added some constants to help making changes easier. 00018 * 00019 * 29 Jan 1996 (Steffan Kaiser) 00020 * made a few cosmetic changes 00021 * 00022 * 05 Feb 1996 (Tim Norman) 00023 * changed to comply with new first/rest calling scheme 00024 * 00025 * 14 Jun 1997 (Steffen Kaiser) 00026 * bug fixes. added error level expansion %?. ctrl-break handling 00027 * 00028 * 16 Jul 1998 (Hans B Pufal) 00029 * Totally reorganised in conjunction with COMMAND.C (cf) to 00030 * implement proper BATCH file nesting and other improvements. 00031 * 00032 * 16 Jul 1998 (John P Price <linux-guru@gcfl.net>) 00033 * Seperated commands into individual files. 00034 * 00035 * 19 Jul 1998 (Hans B Pufal) [HBP_001] 00036 * Preserve state of echo flag across batch calls. 00037 * 00038 * 19 Jul 1998 (Hans B Pufal) [HBP_002] 00039 * Implementation of FOR command 00040 * 00041 * 20-Jul-1998 (John P Price <linux-guru@gcfl.net>) 00042 * added error checking after cmd_alloc calls 00043 * 00044 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>) 00045 * added config.h include 00046 * 00047 * 02-Aug-1998 (Hans B Pufal) [HBP_003] 00048 * Fixed bug in ECHO flag restoration at exit from batch file 00049 * 00050 * 26-Jan-1999 Eric Kohl 00051 * Replaced CRT io functions by Win32 io functions. 00052 * Unicode safe! 00053 * 00054 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.es>) 00055 * Fixes made to get "for" working. 00056 * 00057 * 02-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>) 00058 * Remove all hardcode string to En.rc 00059 */ 00060 00061 #include <precomp.h> 00062 00063 00064 /* The stack of current batch contexts. 00065 * NULL when no batch is active 00066 */ 00067 LPBATCH_CONTEXT bc = NULL; 00068 00069 BOOL bEcho = TRUE; /* The echo flag */ 00070 00071 00072 00073 /* Buffer for reading Batch file lines */ 00074 TCHAR textline[BATCH_BUFFSIZE]; 00075 00076 00077 /* 00078 * Returns a pointer to the n'th parameter of the current batch file. 00079 * If no such parameter exists returns pointer to empty string. 00080 * If no batch file is current, returns NULL 00081 * 00082 */ 00083 00084 LPTSTR FindArg(TCHAR Char, BOOL *IsParam0) 00085 { 00086 LPTSTR pp; 00087 INT n = Char - _T('0'); 00088 00089 TRACE ("FindArg: (%d)\n", n); 00090 00091 if (n < 0 || n > 9) 00092 return NULL; 00093 00094 n = bc->shiftlevel[n]; 00095 *IsParam0 = (n == 0); 00096 pp = bc->params; 00097 00098 /* Step up the strings till we reach the end */ 00099 /* or the one we want */ 00100 while (*pp && n--) 00101 pp += _tcslen (pp) + 1; 00102 00103 return pp; 00104 } 00105 00106 00107 /* 00108 * Batch_params builds a parameter list in newlay allocated memory. 00109 * The parameters consist of null terminated strings with a final 00110 * NULL character signalling the end of the parameters. 00111 * 00112 */ 00113 00114 LPTSTR BatchParams (LPTSTR s1, LPTSTR s2) 00115 { 00116 LPTSTR dp = (LPTSTR)cmd_alloc ((_tcslen(s1) + _tcslen(s2) + 3) * sizeof (TCHAR)); 00117 00118 /* JPP 20-Jul-1998 added error checking */ 00119 if (dp == NULL) 00120 { 00121 error_out_of_memory(); 00122 return NULL; 00123 } 00124 00125 if (s1 && *s1) 00126 { 00127 s1 = _stpcpy (dp, s1); 00128 *s1++ = _T('\0'); 00129 } 00130 else 00131 s1 = dp; 00132 00133 while (*s2) 00134 { 00135 BOOL inquotes = FALSE; 00136 00137 /* Find next parameter */ 00138 while (_istspace(*s2) || (*s2 && _tcschr(_T(",;="), *s2))) 00139 s2++; 00140 if (!*s2) 00141 break; 00142 00143 /* Copy it */ 00144 do 00145 { 00146 if (!inquotes && (_istspace(*s2) || _tcschr(_T(",;="), *s2))) 00147 break; 00148 inquotes ^= (*s2 == _T('"')); 00149 *s1++ = *s2++; 00150 } while (*s2); 00151 *s1++ = _T('\0'); 00152 } 00153 00154 *s1 = _T('\0'); 00155 00156 return dp; 00157 } 00158 00159 /* 00160 * free the allocated memory of a batch file 00161 */ 00162 VOID ClearBatch() 00163 { 00164 TRACE ("ClearBatch mem = %08x free = %d\n", bc->mem, bc->memfree); 00165 00166 if (bc->mem && bc->memfree) 00167 cmd_free(bc->mem); 00168 00169 if (bc->raw_params) 00170 cmd_free(bc->raw_params); 00171 00172 if (bc->params) 00173 cmd_free(bc->params); 00174 } 00175 00176 /* 00177 * If a batch file is current, exits it, freeing the context block and 00178 * chaining back to the previous one. 00179 * 00180 * If no new batch context is found, sets ECHO back ON. 00181 * 00182 * If the parameter is non-null or not empty, it is printed as an exit 00183 * message 00184 */ 00185 00186 VOID ExitBatch() 00187 { 00188 ClearBatch(); 00189 00190 TRACE ("ExitBatch\n"); 00191 00192 UndoRedirection(bc->RedirList, NULL); 00193 FreeRedirection(bc->RedirList); 00194 00195 /* Preserve echo state across batch calls */ 00196 bEcho = bc->bEcho; 00197 00198 while (bc->setlocal) 00199 cmd_endlocal(_T("")); 00200 00201 bc = bc->prev; 00202 } 00203 00204 /* 00205 * Load batch file into memory 00206 * 00207 */ 00208 void BatchFile2Mem(HANDLE hBatchFile) 00209 { 00210 TRACE ("BatchFile2Mem ()\n"); 00211 00212 bc->memsize = GetFileSize(hBatchFile, NULL); 00213 bc->mem = (char *)cmd_alloc(bc->memsize+1); /* 1 extra for '\0' */ 00214 00215 /* if memory is available, read it in and close the file */ 00216 if (bc->mem != NULL) 00217 { 00218 TRACE ("BatchFile2Mem memory %08x - %08x\n",bc->mem,bc->memsize); 00219 SetFilePointer (hBatchFile, 0, NULL, FILE_BEGIN); 00220 ReadFile(hBatchFile, (LPVOID)bc->mem, bc->memsize, &bc->memsize, NULL); 00221 bc->mem[bc->memsize]='\0'; /* end this, so you can dump it as a string */ 00222 bc->memfree=TRUE; /* this one needs to be freed */ 00223 } 00224 else 00225 { 00226 bc->memsize=0; /* this will prevent mem being accessed */ 00227 bc->memfree=FALSE; 00228 } 00229 bc->mempos = 0; /* set position to the start */ 00230 } 00231 00232 /* 00233 * Start batch file execution 00234 * 00235 * The firstword parameter is the full filename of the batch file. 00236 * 00237 */ 00238 00239 INT Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd) 00240 { 00241 BATCH_CONTEXT new; 00242 LPFOR_CONTEXT saved_fc; 00243 INT i; 00244 INT ret = 0; 00245 BOOL same_fn = FALSE; 00246 00247 HANDLE hFile = 0; 00248 SetLastError(0); 00249 if (bc && bc->mem) 00250 { 00251 TCHAR fpname[MAX_PATH]; 00252 GetFullPathName(fullname, sizeof(fpname) / sizeof(TCHAR), fpname, NULL); 00253 if (_tcsicmp(bc->BatchFilePath,fpname)==0) 00254 same_fn=TRUE; 00255 } 00256 TRACE ("Batch: (\'%s\', \'%s\', \'%s\') same_fn = %d\n", 00257 debugstr_aw(fullname), debugstr_aw(firstword), debugstr_aw(param), same_fn); 00258 00259 if (!same_fn) 00260 { 00261 hFile = CreateFile (fullname, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, 00262 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | 00263 FILE_FLAG_SEQUENTIAL_SCAN, NULL); 00264 00265 if (hFile == INVALID_HANDLE_VALUE) 00266 { 00267 ConErrResPuts(STRING_BATCH_ERROR); 00268 return 1; 00269 } 00270 } 00271 00272 if (bc != NULL && Cmd == bc->current) 00273 { 00274 /* Then we are transferring to another batch */ 00275 ClearBatch(); 00276 AddBatchRedirection(&Cmd->Redirections); 00277 } 00278 else 00279 { 00280 struct _SETLOCAL *setlocal = NULL; 00281 00282 if (Cmd == NULL) 00283 { 00284 /* This is a CALL. CALL will set errorlevel to our return value, so 00285 * in order to keep the value of errorlevel unchanged in the case 00286 * of calling an empty batch file, we must return that same value. */ 00287 ret = nErrorLevel; 00288 } 00289 else if (bc) 00290 { 00291 /* If a batch file runs another batch file as part of a compound command 00292 * (e.g. "x.bat & somethingelse") then the first file gets terminated. */ 00293 00294 /* Get its SETLOCAL stack so it can be migrated to the new context */ 00295 setlocal = bc->setlocal; 00296 bc->setlocal = NULL; 00297 ExitBatch(); 00298 } 00299 00300 /* Create a new context. This function will not 00301 * return until this context has been exited */ 00302 new.prev = bc; 00303 /* copy some fields in the new structure if it is the same file */ 00304 if (same_fn) { 00305 new.mem = bc->mem; 00306 new.memsize = bc->memsize; 00307 new.mempos = 0; 00308 new.memfree = FALSE; /* don't free this, being used before this */ 00309 } 00310 bc = &new; 00311 bc->RedirList = NULL; 00312 bc->setlocal = setlocal; 00313 } 00314 00315 GetFullPathName(fullname, sizeof(bc->BatchFilePath) / sizeof(TCHAR), bc->BatchFilePath, NULL); 00316 /* if a new batch file, load it into memory and close the file */ 00317 if (!same_fn) 00318 { 00319 BatchFile2Mem(hFile); 00320 CloseHandle(hFile); 00321 } 00322 00323 bc->mempos = 0; /* goto begin of batch file */ 00324 bc->bEcho = bEcho; /* Preserve echo across batch calls */ 00325 for (i = 0; i < 10; i++) 00326 bc->shiftlevel[i] = i; 00327 00328 bc->params = BatchParams (firstword, param); 00329 // 00330 // Allocate enough memory to hold the params and copy them over without modifications 00331 // 00332 bc->raw_params = cmd_dup(param); 00333 if (bc->raw_params == NULL) 00334 { 00335 error_out_of_memory(); 00336 return 1; 00337 } 00338 00339 /* Check if this is a "CALL :label" */ 00340 if (*firstword == _T(':')) 00341 cmd_goto(firstword); 00342 00343 /* If we are calling from inside a FOR, hide the FOR variables */ 00344 saved_fc = fc; 00345 fc = NULL; 00346 00347 /* If we have created a new context, don't return 00348 * until this batch file has completed. */ 00349 while (bc == &new && !bExit) 00350 { 00351 Cmd = ParseCommand(NULL); 00352 if (!Cmd) 00353 continue; 00354 00355 /* JPP 19980807 */ 00356 /* Echo batch file line */ 00357 if (bEcho && !bDisableBatchEcho && Cmd->Type != C_QUIET) 00358 { 00359 if (!bIgnoreEcho) 00360 ConOutChar(_T('\n')); 00361 PrintPrompt(); 00362 EchoCommand(Cmd); 00363 ConOutChar(_T('\n')); 00364 } 00365 00366 bc->current = Cmd; 00367 ret = ExecuteCommand(Cmd); 00368 FreeCommand(Cmd); 00369 } 00370 00371 TRACE ("Batch: returns TRUE\n"); 00372 00373 fc = saved_fc; 00374 return ret; 00375 } 00376 00377 VOID AddBatchRedirection(REDIRECTION **RedirList) 00378 { 00379 REDIRECTION **ListEnd; 00380 00381 /* Prepend the list to the batch context's list */ 00382 ListEnd = RedirList; 00383 while (*ListEnd) 00384 ListEnd = &(*ListEnd)->Next; 00385 *ListEnd = bc->RedirList; 00386 bc->RedirList = *RedirList; 00387 00388 /* Null out the pointer so that the list will not be cleared prematurely. 00389 * These redirections should persist until the batch file exits. */ 00390 *RedirList = NULL; 00391 } 00392 00393 /* 00394 * Read a single line from the batch file from the current batch/memory position. 00395 * Almost a copy of FileGetString with same UNICODE handling 00396 */ 00397 BOOL BatchGetString (LPTSTR lpBuffer, INT nBufferLength) 00398 { 00399 LPSTR lpString; 00400 INT len = 0; 00401 #ifdef _UNICODE 00402 lpString = cmd_alloc(nBufferLength); 00403 #else 00404 lpString = lpBuffer; 00405 #endif 00406 /* read all chars from memory until a '\n' is encountered */ 00407 if (bc->mem) 00408 { 00409 for (; (bc->mempos < bc->memsize && len < (nBufferLength-1)); len++) 00410 { 00411 lpString[len] = bc->mem[bc->mempos++]; 00412 if (lpString[len] == '\n' ) 00413 { 00414 len++; 00415 break; 00416 } 00417 } 00418 } 00419 00420 if (!len) 00421 { 00422 #ifdef _UNICODE 00423 cmd_free(lpString); 00424 #endif 00425 return FALSE; 00426 } 00427 00428 lpString[len++] = '\0'; 00429 #ifdef _UNICODE 00430 MultiByteToWideChar(OutputCodePage, 0, lpString, -1, lpBuffer, len); 00431 cmd_free(lpString); 00432 #endif 00433 return TRUE; 00434 } 00435 00436 /* 00437 * Read and return the next executable line form the current batch file 00438 * 00439 * If no batch file is current or no further executable lines are found 00440 * return NULL. 00441 * 00442 * Set eflag to 0 if line is not to be echoed else 1 00443 */ 00444 LPTSTR ReadBatchLine () 00445 { 00446 TRACE ("ReadBatchLine ()\n"); 00447 00448 /* User halt */ 00449 if (CheckCtrlBreak (BREAK_BATCHFILE)) 00450 { 00451 while (bc) 00452 ExitBatch(); 00453 return NULL; 00454 } 00455 00456 if (!BatchGetString (textline, sizeof (textline) / sizeof (textline[0]) - 1)) 00457 { 00458 TRACE ("ReadBatchLine(): Reached EOF!\n"); 00459 /* End of file.... */ 00460 ExitBatch(); 00461 return NULL; 00462 } 00463 00464 TRACE ("ReadBatchLine(): textline: \'%s\'\n", debugstr_aw(textline)); 00465 00466 if (textline[_tcslen(textline) - 1] != _T('\n')) 00467 _tcscat(textline, _T("\n")); 00468 00469 return textline; 00470 } 00471 00472 /* EOF */ Generated on Sun May 27 2012 04:18:10 for ReactOS by
1.7.6.1
|