Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygencmd.c
Go to the documentation of this file.
00001 /* 00002 * CMD.C - command-line interface. 00003 * 00004 * 00005 * History: 00006 * 00007 * 17 Jun 1994 (Tim Norman) 00008 * started. 00009 * 00010 * 08 Aug 1995 (Matt Rains) 00011 * I have cleaned up the source code. changes now bring this source 00012 * into guidelines for recommended programming practice. 00013 * 00014 * A added the the standard FreeDOS GNU licence test to the 00015 * initialize() function. 00016 * 00017 * Started to replace puts() with printf(). this will help 00018 * standardize output. please follow my lead. 00019 * 00020 * I have added some constants to help making changes easier. 00021 * 00022 * 15 Dec 1995 (Tim Norman) 00023 * major rewrite of the code to make it more efficient and add 00024 * redirection support (finally!) 00025 * 00026 * 06 Jan 1996 (Tim Norman) 00027 * finished adding redirection support! Changed to use our own 00028 * exec code (MUCH thanks to Svante Frey!!) 00029 * 00030 * 29 Jan 1996 (Tim Norman) 00031 * added support for CHDIR, RMDIR, MKDIR, and ERASE, as per 00032 * suggestion of Steffan Kaiser 00033 * 00034 * changed "file not found" error message to "bad command or 00035 * filename" thanks to Dustin Norman for noticing that confusing 00036 * message! 00037 * 00038 * changed the format to call internal commands (again) so that if 00039 * they want to split their commands, they can do it themselves 00040 * (none of the internal functions so far need that much power, anyway) 00041 * 00042 * 27 Aug 1996 (Tim Norman) 00043 * added in support for Oliver Mueller's ALIAS command 00044 * 00045 * 14 Jun 1997 (Steffan Kaiser) 00046 * added ctrl-break handling and error level 00047 * 00048 * 16 Jun 1998 (Rob Lake) 00049 * Runs command.com if /P is specified in command line. Command.com 00050 * also stays permanent. If /C is in the command line, starts the 00051 * program next in the line. 00052 * 00053 * 21 Jun 1998 (Rob Lake) 00054 * Fixed up /C so that arguments for the program 00055 * 00056 * 08-Jul-1998 (John P. Price) 00057 * Now sets COMSPEC environment variable 00058 * misc clean up and optimization 00059 * added date and time commands 00060 * changed to using spawnl instead of exec. exec does not copy the 00061 * environment to the child process! 00062 * 00063 * 14 Jul 1998 (Hans B Pufal) 00064 * Reorganised source to be more efficient and to more closely 00065 * follow MS-DOS conventions. (eg %..% environment variable 00066 * replacement works form command line as well as batch file. 00067 * 00068 * New organisation also properly support nested batch files. 00069 * 00070 * New command table structure is half way towards providing a 00071 * system in which COMMAND will find out what internal commands 00072 * are loaded 00073 * 00074 * 24 Jul 1998 (Hans B Pufal) [HBP_003] 00075 * Fixed return value when called with /C option 00076 * 00077 * 27 Jul 1998 John P. Price 00078 * added config.h include 00079 * 00080 * 28 Jul 1998 John P. Price 00081 * added showcmds function to show commands and options available 00082 * 00083 * 07-Aug-1998 (John P Price <linux-guru@gcfl.net>) 00084 * Fixed carrage return output to better match MSDOS with echo 00085 * on or off. (marked with "JPP 19980708") 00086 * 00087 * 07-Dec-1998 (Eric Kohl) 00088 * First ReactOS release. 00089 * Extended length of commandline buffers to 512. 00090 * 00091 * 13-Dec-1998 (Eric Kohl) 00092 * Added COMSPEC environment variable. 00093 * Added "/t" support (color) on cmd command line. 00094 * 00095 * 07-Jan-1999 (Eric Kohl) 00096 * Added help text ("cmd /?"). 00097 * 00098 * 25-Jan-1999 (Eric Kohl) 00099 * Unicode and redirection safe! 00100 * Fixed redirections and piping. 00101 * Piping is based on temporary files, but basic support 00102 * for anonymous pipes already exists. 00103 * 00104 * 27-Jan-1999 (Eric Kohl) 00105 * Replaced spawnl() by CreateProcess(). 00106 * 00107 * 22-Oct-1999 (Eric Kohl) 00108 * Added break handler. 00109 * 00110 * 15-Dec-1999 (Eric Kohl) 00111 * Fixed current directory 00112 * 00113 * 28-Dec-1999 (Eric Kohl) 00114 * Restore window title after program/batch execution 00115 * 00116 * 03-Feb-2001 (Eric Kohl) 00117 * Workaround because argc[0] is NULL under ReactOS 00118 * 00119 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>) 00120 * %envvar% replacement conflicted with for. 00121 * 00122 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>) 00123 * Make MakeSureDirectoryPathExistsEx unicode safe. 00124 * 00125 * 28-Mai-2004 00126 * Removed MakeSureDirectoryPathExistsEx. 00127 * Use the current directory if GetTempPath fails. 00128 * 00129 * 12-Jul-2004 (Jens Collin <jens.collin@lakhei.com>) 00130 * Added ShellExecute call when all else fails to be able to "launch" any file. 00131 * 00132 * 02-Apr-2005 (Magnus Olsen <magnus@greatlord.com>) 00133 * Remove all hardcode string to En.rc 00134 * 00135 * 06-May-2005 (Klemens Friedl <frik85@gmail.com>) 00136 * Add 'help' command (list all commands plus description) 00137 * 00138 * 06-jul-2005 (Magnus Olsen <magnus@greatlord.com>) 00139 * translate '%errorlevel%' to the internal value. 00140 * Add proper memmory alloc ProcessInput, the error 00141 * handling for memmory handling need to be improve 00142 */ 00143 00144 #include <precomp.h> 00145 #include <reactos/buildno.h> 00146 #include <reactos/version.h> 00147 00148 #ifndef NT_SUCCESS 00149 #define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode) >= 0) 00150 #endif 00151 00152 typedef NTSTATUS (WINAPI *NtQueryInformationProcessProc)(HANDLE, PROCESSINFOCLASS, 00153 PVOID, ULONG, PULONG); 00154 typedef NTSTATUS (WINAPI *NtReadVirtualMemoryProc)(HANDLE, PVOID, PVOID, ULONG, PULONG); 00155 00156 BOOL bExit = FALSE; /* indicates EXIT was typed */ 00157 BOOL bCanExit = TRUE; /* indicates if this shell is exitable */ 00158 BOOL bCtrlBreak = FALSE; /* Ctrl-Break or Ctrl-C hit */ 00159 BOOL bIgnoreEcho = FALSE; /* Set this to TRUE to prevent a newline, when executing a command */ 00160 INT nErrorLevel = 0; /* Errorlevel of last launched external program */ 00161 CRITICAL_SECTION ChildProcessRunningLock; 00162 BOOL bUnicodeOutput = FALSE; 00163 BOOL bDisableBatchEcho = FALSE; 00164 BOOL bDelayedExpansion = FALSE; 00165 DWORD dwChildProcessId = 0; 00166 OSVERSIONINFO osvi; 00167 HANDLE hIn; 00168 HANDLE hOut; 00169 LPTSTR lpOriginalEnvironment; 00170 HANDLE CMD_ModuleHandle; 00171 00172 static NtQueryInformationProcessProc NtQueryInformationProcessPtr = NULL; 00173 static NtReadVirtualMemoryProc NtReadVirtualMemoryPtr = NULL; 00174 00175 #ifdef INCLUDE_CMD_COLOR 00176 WORD wDefColor; /* default color */ 00177 #endif 00178 00179 /* 00180 * convert 00181 * 00182 * insert commas into a number 00183 */ 00184 INT 00185 ConvertULargeInteger(ULONGLONG num, LPTSTR des, UINT len, BOOL bPutSeperator) 00186 { 00187 TCHAR temp[39]; /* maximum length with nNumberGroups == 1 */ 00188 UINT n, iTarget; 00189 00190 if (len <= 1) 00191 return 0; 00192 00193 n = 0; 00194 iTarget = nNumberGroups; 00195 if (!nNumberGroups) 00196 bPutSeperator = FALSE; 00197 00198 do 00199 { 00200 if (iTarget == n && bPutSeperator) 00201 { 00202 iTarget += nNumberGroups + 1; 00203 temp[38 - n++] = cThousandSeparator; 00204 } 00205 temp[38 - n++] = (TCHAR)(num % 10) + _T('0'); 00206 num /= 10; 00207 } while (num > 0); 00208 if (n > len-1) 00209 n = len-1; 00210 00211 memcpy(des, temp + 39 - n, n * sizeof(TCHAR)); 00212 des[n] = _T('\0'); 00213 00214 return n; 00215 } 00216 00217 /* 00218 * Is a process a console process? 00219 */ 00220 static BOOL IsConsoleProcess(HANDLE Process) 00221 { 00222 NTSTATUS Status; 00223 PROCESS_BASIC_INFORMATION Info; 00224 PEB ProcessPeb; 00225 ULONG BytesRead; 00226 00227 if (NULL == NtQueryInformationProcessPtr || NULL == NtReadVirtualMemoryPtr) 00228 { 00229 return TRUE; 00230 } 00231 00232 Status = NtQueryInformationProcessPtr ( 00233 Process, ProcessBasicInformation, 00234 &Info, sizeof(PROCESS_BASIC_INFORMATION), NULL); 00235 if (! NT_SUCCESS(Status)) 00236 { 00237 WARN ("NtQueryInformationProcess failed with status %08x\n", Status); 00238 return TRUE; 00239 } 00240 Status = NtReadVirtualMemoryPtr ( 00241 Process, Info.PebBaseAddress, &ProcessPeb, 00242 sizeof(PEB), &BytesRead); 00243 if (! NT_SUCCESS(Status) || sizeof(PEB) != BytesRead) 00244 { 00245 WARN ("Couldn't read virt mem status %08x bytes read %lu\n", Status, BytesRead); 00246 return TRUE; 00247 } 00248 00249 return IMAGE_SUBSYSTEM_WINDOWS_CUI == ProcessPeb.ImageSubsystem; 00250 } 00251 00252 00253 00254 #ifdef _UNICODE 00255 #define SHELLEXECUTETEXT "ShellExecuteExW" 00256 #else 00257 #define SHELLEXECUTETEXT "ShellExecuteExA" 00258 #endif 00259 00260 typedef BOOL (WINAPI *MYEX)(LPSHELLEXECUTEINFO lpExecInfo); 00261 00262 HANDLE RunFile(DWORD flags, LPTSTR filename, LPTSTR params, 00263 LPTSTR directory, INT show) 00264 { 00265 SHELLEXECUTEINFO sei; 00266 HMODULE hShell32; 00267 MYEX hShExt; 00268 BOOL ret; 00269 00270 TRACE ("RunFile(%s)\n", debugstr_aw(filename)); 00271 hShell32 = LoadLibrary(_T("SHELL32.DLL")); 00272 if (!hShell32) 00273 { 00274 WARN ("RunFile: couldn't load SHELL32.DLL!\n"); 00275 return NULL; 00276 } 00277 00278 hShExt = (MYEX)(FARPROC)GetProcAddress(hShell32, SHELLEXECUTETEXT); 00279 if (!hShExt) 00280 { 00281 WARN ("RunFile: couldn't find ShellExecuteExA/W in SHELL32.DLL!\n"); 00282 FreeLibrary(hShell32); 00283 return NULL; 00284 } 00285 00286 TRACE ("RunFile: ShellExecuteExA/W is at %x\n", hShExt); 00287 00288 memset(&sei, 0, sizeof sei); 00289 sei.cbSize = sizeof sei; 00290 sei.fMask = flags; 00291 sei.lpFile = filename; 00292 sei.lpParameters = params; 00293 sei.lpDirectory = directory; 00294 sei.nShow = show; 00295 ret = hShExt(&sei); 00296 00297 TRACE ("RunFile: ShellExecuteExA/W returned 0x%p\n", ret); 00298 00299 FreeLibrary(hShell32); 00300 return ret ? sei.hProcess : NULL; 00301 } 00302 00303 00304 00305 /* 00306 * This command (in first) was not found in the command table 00307 * 00308 * Full - buffer to hold whole command line 00309 * First - first word on command line 00310 * Rest - rest of command line 00311 */ 00312 00313 static INT 00314 Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd) 00315 { 00316 TCHAR szFullName[MAX_PATH]; 00317 TCHAR *first, *rest, *dot; 00318 TCHAR szWindowTitle[MAX_PATH]; 00319 DWORD dwExitCode = 0; 00320 TCHAR *FirstEnd; 00321 TCHAR szFullCmdLine [CMDLINE_LENGTH]; 00322 00323 TRACE ("Execute: \'%s\' \'%s\'\n", debugstr_aw(First), debugstr_aw(Rest)); 00324 00325 /* Though it was already parsed once, we have a different set of rules 00326 for parsing before we pass to CreateProccess */ 00327 if (First[0] == _T('/') || (First[0] && First[1] == _T(':'))) 00328 { 00329 /* Use the entire first word as the program name (no change) */ 00330 FirstEnd = First + _tcslen(First); 00331 } 00332 else 00333 { 00334 /* If present in the first word, spaces and ,;=/ end the program 00335 * name and become the beginning of its parameters. */ 00336 BOOL bInside = FALSE; 00337 for (FirstEnd = First; *FirstEnd; FirstEnd++) 00338 { 00339 if (!bInside && (_istspace(*FirstEnd) || _tcschr(_T(",;=/"), *FirstEnd))) 00340 break; 00341 bInside ^= *FirstEnd == _T('"'); 00342 } 00343 } 00344 00345 /* Copy the new first/rest into the buffer */ 00346 first = Full; 00347 rest = &Full[FirstEnd - First + 1]; 00348 _tcscpy(rest, FirstEnd); 00349 _tcscat(rest, Rest); 00350 *FirstEnd = _T('\0'); 00351 _tcscpy(first, First); 00352 00353 /* check for a drive change */ 00354 if ((_istalpha (first[0])) && (!_tcscmp (first + 1, _T(":")))) 00355 { 00356 BOOL working = TRUE; 00357 if (!SetCurrentDirectory(first)) 00358 /* Guess they changed disc or something, handle that gracefully and get to root */ 00359 { 00360 TCHAR str[4]; 00361 str[0]=first[0]; 00362 str[1]=_T(':'); 00363 str[2]=_T('\\'); 00364 str[3]=0; 00365 working = SetCurrentDirectory(str); 00366 } 00367 00368 if (!working) ConErrResPuts (STRING_FREE_ERROR1); 00369 return !working; 00370 } 00371 00372 /* get the PATH environment variable and parse it */ 00373 /* search the PATH environment variable for the binary */ 00374 StripQuotes(First); 00375 if (!SearchForExecutable(First, szFullName)) 00376 { 00377 error_bad_command(first); 00378 return 1; 00379 } 00380 00381 GetConsoleTitle (szWindowTitle, MAX_PATH); 00382 00383 /* check if this is a .BAT or .CMD file */ 00384 dot = _tcsrchr (szFullName, _T('.')); 00385 if (dot && (!_tcsicmp (dot, _T(".bat")) || !_tcsicmp (dot, _T(".cmd")))) 00386 { 00387 while (*rest == _T(' ')) 00388 rest++; 00389 TRACE ("[BATCH: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(rest)); 00390 dwExitCode = Batch(szFullName, first, rest, Cmd); 00391 } 00392 else 00393 { 00394 /* exec the program */ 00395 PROCESS_INFORMATION prci; 00396 STARTUPINFO stui; 00397 00398 /* build command line for CreateProcess(): FullName + " " + rest */ 00399 _tcscpy(szFullCmdLine, szFullName); 00400 00401 if (*rest) 00402 { 00403 _tcsncat(szFullCmdLine, _T(" "), CMDLINE_LENGTH - _tcslen(szFullCmdLine)); 00404 _tcsncat(szFullCmdLine, rest, CMDLINE_LENGTH - _tcslen(szFullCmdLine)); 00405 } 00406 00407 TRACE ("[EXEC: %s]\n", debugstr_aw(szFullCmdLine)); 00408 00409 /* fill startup info */ 00410 memset (&stui, 0, sizeof (STARTUPINFO)); 00411 stui.cb = sizeof (STARTUPINFO); 00412 stui.dwFlags = STARTF_USESHOWWINDOW; 00413 stui.wShowWindow = SW_SHOWDEFAULT; 00414 00415 // return console to standard mode 00416 SetConsoleMode (GetStdHandle(STD_INPUT_HANDLE), 00417 ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT ); 00418 00419 if (CreateProcess (szFullName, 00420 szFullCmdLine, 00421 NULL, 00422 NULL, 00423 TRUE, 00424 0, /* CREATE_NEW_PROCESS_GROUP */ 00425 NULL, 00426 NULL, 00427 &stui, 00428 &prci)) 00429 00430 { 00431 CloseHandle(prci.hThread); 00432 } 00433 else 00434 { 00435 // See if we can run this with ShellExecute() ie myfile.xls 00436 prci.hProcess = RunFile(SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE, 00437 szFullName, 00438 rest, 00439 NULL, 00440 SW_SHOWNORMAL); 00441 } 00442 00443 if (prci.hProcess != NULL) 00444 { 00445 if (IsConsoleProcess(prci.hProcess)) 00446 { 00447 EnterCriticalSection(&ChildProcessRunningLock); 00448 dwChildProcessId = prci.dwProcessId; 00449 00450 WaitForSingleObject (prci.hProcess, INFINITE); 00451 00452 LeaveCriticalSection(&ChildProcessRunningLock); 00453 00454 GetExitCodeProcess (prci.hProcess, &dwExitCode); 00455 nErrorLevel = (INT)dwExitCode; 00456 } 00457 CloseHandle (prci.hProcess); 00458 } 00459 else 00460 { 00461 TRACE ("[ShellExecute failed!: %s]\n", debugstr_aw(Full)); 00462 error_bad_command (first); 00463 dwExitCode = 1; 00464 } 00465 00466 // restore console mode 00467 SetConsoleMode ( 00468 GetStdHandle( STD_INPUT_HANDLE ), 00469 ENABLE_PROCESSED_INPUT ); 00470 } 00471 00472 /* Get code page if it has been change */ 00473 InputCodePage= GetConsoleCP(); 00474 OutputCodePage = GetConsoleOutputCP(); 00475 SetConsoleTitle (szWindowTitle); 00476 00477 return dwExitCode; 00478 } 00479 00480 00481 /* 00482 * look through the internal commands and determine whether or not this 00483 * command is one of them. If it is, call the command. If not, call 00484 * execute to run it as an external program. 00485 * 00486 * first - first word on command line 00487 * rest - rest of command line 00488 */ 00489 00490 INT 00491 DoCommand(LPTSTR first, LPTSTR rest, PARSED_COMMAND *Cmd) 00492 { 00493 TCHAR *com; 00494 TCHAR *cp; 00495 LPTSTR param; /* pointer to command's parameters */ 00496 INT cl; 00497 LPCOMMAND cmdptr; 00498 BOOL nointernal = FALSE; 00499 INT ret; 00500 00501 TRACE ("DoCommand: (\'%s\' \'%s\')\n", debugstr_aw(first), debugstr_aw(rest)); 00502 00503 /* full command line */ 00504 com = cmd_alloc((_tcslen(first) + _tcslen(rest) + 2) * sizeof(TCHAR)); 00505 if (com == NULL) 00506 { 00507 error_out_of_memory(); 00508 return 1; 00509 } 00510 00511 /* If present in the first word, these characters end the name of an 00512 * internal command and become the beginning of its parameters. */ 00513 cp = first + _tcscspn(first, _T("\t +,/;=[]")); 00514 00515 for (cl = 0; cl < (cp - first); cl++) 00516 { 00517 /* These characters do it too, but if one of them is present, 00518 * then we check to see if the word is a file name and skip 00519 * checking for internal commands if so. 00520 * This allows running programs with names like "echo.exe" */ 00521 if (_tcschr(_T(".:\\"), first[cl])) 00522 { 00523 TCHAR tmp = *cp; 00524 *cp = _T('\0'); 00525 nointernal = IsExistingFile(first); 00526 *cp = tmp; 00527 break; 00528 } 00529 } 00530 00531 /* Scan internal command table */ 00532 for (cmdptr = cmds; !nointernal && cmdptr->name; cmdptr++) 00533 { 00534 if (!_tcsnicmp(first, cmdptr->name, cl) && cmdptr->name[cl] == _T('\0')) 00535 { 00536 _tcscpy(com, first); 00537 _tcscat(com, rest); 00538 param = &com[cl]; 00539 00540 /* Skip over whitespace to rest of line, exclude 'echo' command */ 00541 if (_tcsicmp(cmdptr->name, _T("echo")) != 0) 00542 while (_istspace(*param)) 00543 param++; 00544 ret = cmdptr->func(param); 00545 cmd_free(com); 00546 return ret; 00547 } 00548 } 00549 00550 ret = Execute(com, first, rest, Cmd); 00551 cmd_free(com); 00552 return ret; 00553 } 00554 00555 00556 /* 00557 * process the command line and execute the appropriate functions 00558 * full input/output redirection and piping are supported 00559 */ 00560 00561 INT ParseCommandLine (LPTSTR cmd) 00562 { 00563 INT Ret = 0; 00564 PARSED_COMMAND *Cmd = ParseCommand(cmd); 00565 if (Cmd) 00566 { 00567 Ret = ExecuteCommand(Cmd); 00568 FreeCommand(Cmd); 00569 } 00570 return Ret; 00571 } 00572 00573 /* Execute a command without waiting for it to finish. If it's an internal 00574 * command or batch file, we must create a new cmd.exe process to handle it. 00575 * TODO: For now, this just always creates a cmd.exe process. 00576 * This works, but is inefficient for running external programs, 00577 * which could just be run directly. */ 00578 static HANDLE 00579 ExecuteAsync(PARSED_COMMAND *Cmd) 00580 { 00581 TCHAR CmdPath[MAX_PATH]; 00582 TCHAR CmdParams[CMDLINE_LENGTH], *ParamsEnd; 00583 STARTUPINFO stui; 00584 PROCESS_INFORMATION prci; 00585 00586 /* Get the path to cmd.exe */ 00587 GetModuleFileName(NULL, CmdPath, MAX_PATH); 00588 00589 /* Build the parameter string to pass to cmd.exe */ 00590 ParamsEnd = _stpcpy(CmdParams, _T("/S/D/C\"")); 00591 ParamsEnd = Unparse(Cmd, ParamsEnd, &CmdParams[CMDLINE_LENGTH - 2]); 00592 if (!ParamsEnd) 00593 { 00594 error_out_of_memory(); 00595 return NULL; 00596 } 00597 _tcscpy(ParamsEnd, _T("\"")); 00598 00599 memset(&stui, 0, sizeof stui); 00600 stui.cb = sizeof(STARTUPINFO); 00601 if (!CreateProcess(CmdPath, CmdParams, NULL, NULL, TRUE, 0, 00602 NULL, NULL, &stui, &prci)) 00603 { 00604 ErrorMessage(GetLastError(), NULL); 00605 return NULL; 00606 } 00607 00608 CloseHandle(prci.hThread); 00609 return prci.hProcess; 00610 } 00611 00612 static VOID 00613 ExecutePipeline(PARSED_COMMAND *Cmd) 00614 { 00615 #ifdef FEATURE_REDIRECTION 00616 HANDLE hInput = NULL; 00617 HANDLE hOldConIn = GetStdHandle(STD_INPUT_HANDLE); 00618 HANDLE hOldConOut = GetStdHandle(STD_OUTPUT_HANDLE); 00619 HANDLE hProcess[MAXIMUM_WAIT_OBJECTS]; 00620 INT nProcesses = 0; 00621 DWORD dwExitCode; 00622 00623 /* Do all but the last pipe command */ 00624 do 00625 { 00626 HANDLE hPipeRead, hPipeWrite; 00627 if (nProcesses > (MAXIMUM_WAIT_OBJECTS - 2)) 00628 { 00629 error_too_many_parameters(_T("|")); 00630 goto failed; 00631 } 00632 00633 /* Create the pipe that this process will write into. 00634 * Make the handles non-inheritable initially, because this 00635 * process shouldn't inherit the reading handle. */ 00636 if (!CreatePipe(&hPipeRead, &hPipeWrite, NULL, 0)) 00637 { 00638 error_no_pipe(); 00639 goto failed; 00640 } 00641 00642 /* The writing side of the pipe is STDOUT for this process */ 00643 SetHandleInformation(hPipeWrite, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); 00644 SetStdHandle(STD_OUTPUT_HANDLE, hPipeWrite); 00645 00646 /* Execute it (error check is done later for easier cleanup) */ 00647 hProcess[nProcesses] = ExecuteAsync(Cmd->Subcommands); 00648 CloseHandle(hPipeWrite); 00649 if (hInput) 00650 CloseHandle(hInput); 00651 00652 /* The reading side of the pipe will be STDIN for the next process */ 00653 SetHandleInformation(hPipeRead, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); 00654 SetStdHandle(STD_INPUT_HANDLE, hPipeRead); 00655 hInput = hPipeRead; 00656 00657 if (!hProcess[nProcesses]) 00658 goto failed; 00659 nProcesses++; 00660 00661 Cmd = Cmd->Subcommands->Next; 00662 } while (Cmd->Type == C_PIPE); 00663 00664 /* The last process uses the original STDOUT */ 00665 SetStdHandle(STD_OUTPUT_HANDLE, hOldConOut); 00666 hProcess[nProcesses] = ExecuteAsync(Cmd); 00667 if (!hProcess[nProcesses]) 00668 goto failed; 00669 nProcesses++; 00670 CloseHandle(hInput); 00671 SetStdHandle(STD_INPUT_HANDLE, hOldConIn); 00672 00673 /* Wait for all processes to complete */ 00674 EnterCriticalSection(&ChildProcessRunningLock); 00675 WaitForMultipleObjects(nProcesses, hProcess, TRUE, INFINITE); 00676 LeaveCriticalSection(&ChildProcessRunningLock); 00677 00678 /* Use the exit code of the last process in the pipeline */ 00679 GetExitCodeProcess(hProcess[nProcesses - 1], &dwExitCode); 00680 nErrorLevel = (INT)dwExitCode; 00681 00682 while (--nProcesses >= 0) 00683 CloseHandle(hProcess[nProcesses]); 00684 return; 00685 00686 failed: 00687 if (hInput) 00688 CloseHandle(hInput); 00689 while (--nProcesses >= 0) 00690 { 00691 TerminateProcess(hProcess[nProcesses], 0); 00692 CloseHandle(hProcess[nProcesses]); 00693 } 00694 SetStdHandle(STD_INPUT_HANDLE, hOldConIn); 00695 SetStdHandle(STD_OUTPUT_HANDLE, hOldConOut); 00696 #endif 00697 } 00698 00699 INT 00700 ExecuteCommand(PARSED_COMMAND *Cmd) 00701 { 00702 PARSED_COMMAND *Sub; 00703 LPTSTR First, Rest; 00704 INT Ret = 0; 00705 00706 if (!PerformRedirection(Cmd->Redirections)) 00707 return 1; 00708 00709 switch (Cmd->Type) 00710 { 00711 case C_COMMAND: 00712 Ret = 1; 00713 First = DoDelayedExpansion(Cmd->Command.First); 00714 if (First) 00715 { 00716 Rest = DoDelayedExpansion(Cmd->Command.Rest); 00717 if (Rest) 00718 { 00719 Ret = DoCommand(First, Rest, Cmd); 00720 cmd_free(Rest); 00721 } 00722 cmd_free(First); 00723 } 00724 break; 00725 case C_QUIET: 00726 case C_BLOCK: 00727 case C_MULTI: 00728 for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next) 00729 Ret = ExecuteCommand(Sub); 00730 break; 00731 case C_IFFAILURE: 00732 Sub = Cmd->Subcommands; 00733 Ret = ExecuteCommand(Sub); 00734 if (Ret != 0) 00735 { 00736 nErrorLevel = Ret; 00737 Ret = ExecuteCommand(Sub->Next); 00738 } 00739 break; 00740 case C_IFSUCCESS: 00741 Sub = Cmd->Subcommands; 00742 Ret = ExecuteCommand(Sub); 00743 if (Ret == 0) 00744 Ret = ExecuteCommand(Sub->Next); 00745 break; 00746 case C_PIPE: 00747 ExecutePipeline(Cmd); 00748 break; 00749 case C_IF: 00750 Ret = ExecuteIf(Cmd); 00751 break; 00752 case C_FOR: 00753 Ret = ExecuteFor(Cmd); 00754 break; 00755 } 00756 00757 UndoRedirection(Cmd->Redirections, NULL); 00758 return Ret; 00759 } 00760 00761 LPTSTR 00762 GetEnvVar(LPCTSTR varName) 00763 { 00764 static LPTSTR ret = NULL; 00765 UINT size; 00766 00767 cmd_free(ret); 00768 ret = NULL; 00769 size = GetEnvironmentVariable(varName, NULL, 0); 00770 if (size > 0) 00771 { 00772 ret = cmd_alloc(size * sizeof(TCHAR)); 00773 if (ret != NULL) 00774 GetEnvironmentVariable(varName, ret, size + 1); 00775 } 00776 return ret; 00777 } 00778 00779 LPCTSTR 00780 GetEnvVarOrSpecial(LPCTSTR varName) 00781 { 00782 static TCHAR ret[MAX_PATH]; 00783 00784 LPTSTR var = GetEnvVar(varName); 00785 if (var) 00786 return var; 00787 00788 /* env var doesn't exist, look for a "special" one */ 00789 /* %CD% */ 00790 if (_tcsicmp(varName,_T("cd")) ==0) 00791 { 00792 GetCurrentDirectory(MAX_PATH, ret); 00793 return ret; 00794 } 00795 /* %TIME% */ 00796 else if (_tcsicmp(varName,_T("time")) ==0) 00797 { 00798 return GetTimeString(); 00799 } 00800 /* %DATE% */ 00801 else if (_tcsicmp(varName,_T("date")) ==0) 00802 { 00803 return GetDateString(); 00804 } 00805 00806 /* %RANDOM% */ 00807 else if (_tcsicmp(varName,_T("random")) ==0) 00808 { 00809 /* Get random number */ 00810 _itot(rand(),ret,10); 00811 return ret; 00812 } 00813 00814 /* %CMDCMDLINE% */ 00815 else if (_tcsicmp(varName,_T("cmdcmdline")) ==0) 00816 { 00817 return GetCommandLine(); 00818 } 00819 00820 /* %CMDEXTVERSION% */ 00821 else if (_tcsicmp(varName,_T("cmdextversion")) ==0) 00822 { 00823 /* Set version number to 2 */ 00824 _itot(2,ret,10); 00825 return ret; 00826 } 00827 00828 /* %ERRORLEVEL% */ 00829 else if (_tcsicmp(varName,_T("errorlevel")) ==0) 00830 { 00831 _itot(nErrorLevel,ret,10); 00832 return ret; 00833 } 00834 00835 return NULL; 00836 } 00837 00838 /* Handle the %~var syntax */ 00839 static LPTSTR 00840 GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *)) 00841 { 00842 static const TCHAR ModifierTable[] = _T("dpnxfsatz"); 00843 enum { 00844 M_DRIVE = 1, /* D: drive letter */ 00845 M_PATH = 2, /* P: path */ 00846 M_NAME = 4, /* N: filename */ 00847 M_EXT = 8, /* X: extension */ 00848 M_FULL = 16, /* F: full path (drive+path+name+ext) */ 00849 M_SHORT = 32, /* S: full path (drive+path+name+ext), use short names */ 00850 M_ATTR = 64, /* A: attributes */ 00851 M_TIME = 128, /* T: modification time */ 00852 M_SIZE = 256, /* Z: file size */ 00853 } Modifiers = 0; 00854 00855 TCHAR *Format, *FormatEnd; 00856 TCHAR *PathVarName = NULL; 00857 LPTSTR Variable; 00858 TCHAR *VarEnd; 00859 BOOL VariableIsParam0; 00860 TCHAR FullPath[MAX_PATH]; 00861 TCHAR FixedPath[MAX_PATH], *Filename, *Extension; 00862 HANDLE hFind; 00863 WIN32_FIND_DATA w32fd; 00864 TCHAR *In, *Out; 00865 00866 static TCHAR Result[CMDLINE_LENGTH]; 00867 00868 /* There is ambiguity between modifier characters and FOR variables; 00869 * the rule that cmd uses is to pick the longest possible match. 00870 * For example, if there is a %n variable, then out of %~anxnd, 00871 * %~anxn will be substituted rather than just %~an. */ 00872 00873 /* First, go through as many modifier characters as possible */ 00874 FormatEnd = Format = *pFormat; 00875 while (*FormatEnd && _tcschr(ModifierTable, _totlower(*FormatEnd))) 00876 FormatEnd++; 00877 00878 if (*FormatEnd == _T('$')) 00879 { 00880 /* $PATH: syntax */ 00881 PathVarName = FormatEnd + 1; 00882 FormatEnd = _tcschr(PathVarName, _T(':')); 00883 if (!FormatEnd) 00884 return NULL; 00885 00886 /* Must be immediately followed by the variable */ 00887 Variable = GetVar(*++FormatEnd, &VariableIsParam0); 00888 if (!Variable) 00889 return NULL; 00890 } 00891 else 00892 { 00893 /* Backtrack if necessary to get a variable name match */ 00894 while (!(Variable = GetVar(*FormatEnd, &VariableIsParam0))) 00895 { 00896 if (FormatEnd == Format) 00897 return NULL; 00898 FormatEnd--; 00899 } 00900 } 00901 00902 for (; Format < FormatEnd && *Format != _T('$'); Format++) 00903 Modifiers |= 1 << (_tcschr(ModifierTable, _totlower(*Format)) - ModifierTable); 00904 00905 *pFormat = FormatEnd + 1; 00906 00907 /* Exclude the leading and trailing quotes */ 00908 VarEnd = &Variable[_tcslen(Variable)]; 00909 if (*Variable == _T('"')) 00910 { 00911 Variable++; 00912 if (VarEnd > Variable && VarEnd[-1] == _T('"')) 00913 VarEnd--; 00914 } 00915 00916 if ((char *)VarEnd - (char *)Variable >= sizeof Result) 00917 return _T(""); 00918 memcpy(Result, Variable, (char *)VarEnd - (char *)Variable); 00919 Result[VarEnd - Variable] = _T('\0'); 00920 00921 if (PathVarName) 00922 { 00923 /* $PATH: syntax - search the directories listed in the 00924 * specified environment variable for the file */ 00925 LPTSTR PathVar; 00926 FormatEnd[-1] = _T('\0'); 00927 PathVar = GetEnvVar(PathVarName); 00928 FormatEnd[-1] = _T(':'); 00929 if (!PathVar || 00930 !SearchPath(PathVar, Result, NULL, MAX_PATH, FullPath, NULL)) 00931 { 00932 return _T(""); 00933 } 00934 } 00935 else if (Modifiers == 0) 00936 { 00937 /* For plain %~var with no modifiers, just return the variable without quotes */ 00938 return Result; 00939 } 00940 else if (VariableIsParam0) 00941 { 00942 /* Special case: If the variable is %0 and modifier characters are present, 00943 * use the batch file's path (which includes the .bat/.cmd extension) 00944 * rather than the actual %0 variable (which might not). */ 00945 _tcscpy(FullPath, bc->BatchFilePath); 00946 } 00947 else 00948 { 00949 /* Convert the variable, now without quotes, to a full path */ 00950 if (!GetFullPathName(Result, MAX_PATH, FullPath, NULL)) 00951 return _T(""); 00952 } 00953 00954 /* Next step is to change the path to fix letter case (e.g. 00955 * C:\ReAcToS -> C:\ReactOS) and, if requested with the S modifier, 00956 * replace long filenames with short. */ 00957 00958 In = FullPath; 00959 Out = FixedPath; 00960 00961 /* Copy drive letter */ 00962 *Out++ = *In++; 00963 *Out++ = *In++; 00964 *Out++ = *In++; 00965 /* Loop over each \-separated component in the path */ 00966 do { 00967 TCHAR *Next = _tcschr(In, _T('\\')); 00968 if (Next) 00969 *Next++ = _T('\0'); 00970 /* Use FindFirstFile to get the correct name */ 00971 if (Out + _tcslen(In) + 1 >= &FixedPath[MAX_PATH]) 00972 return _T(""); 00973 _tcscpy(Out, In); 00974 hFind = FindFirstFile(FixedPath, &w32fd); 00975 /* If it doesn't exist, just leave the name as it was given */ 00976 if (hFind != INVALID_HANDLE_VALUE) 00977 { 00978 LPTSTR FixedComponent = w32fd.cFileName; 00979 if (*w32fd.cAlternateFileName && 00980 ((Modifiers & M_SHORT) || !_tcsicmp(In, w32fd.cAlternateFileName))) 00981 { 00982 FixedComponent = w32fd.cAlternateFileName; 00983 } 00984 FindClose(hFind); 00985 00986 if (Out + _tcslen(FixedComponent) + 1 >= &FixedPath[MAX_PATH]) 00987 return _T(""); 00988 _tcscpy(Out, FixedComponent); 00989 } 00990 Filename = Out; 00991 Out += _tcslen(Out); 00992 *Out++ = _T('\\'); 00993 00994 In = Next; 00995 } while (In != NULL); 00996 Out[-1] = _T('\0'); 00997 00998 /* Build the result string. Start with attributes, modification time, and 00999 * file size. If the file didn't exist, these fields will all be empty. */ 01000 Out = Result; 01001 if (hFind != INVALID_HANDLE_VALUE) 01002 { 01003 if (Modifiers & M_ATTR) 01004 { 01005 static const struct { 01006 TCHAR Character; 01007 WORD Value; 01008 } *Attrib, Table[] = { 01009 { _T('d'), FILE_ATTRIBUTE_DIRECTORY }, 01010 { _T('r'), FILE_ATTRIBUTE_READONLY }, 01011 { _T('a'), FILE_ATTRIBUTE_ARCHIVE }, 01012 { _T('h'), FILE_ATTRIBUTE_HIDDEN }, 01013 { _T('s'), FILE_ATTRIBUTE_SYSTEM }, 01014 { _T('c'), FILE_ATTRIBUTE_COMPRESSED }, 01015 { _T('o'), FILE_ATTRIBUTE_OFFLINE }, 01016 { _T('t'), FILE_ATTRIBUTE_TEMPORARY }, 01017 { _T('l'), FILE_ATTRIBUTE_REPARSE_POINT }, 01018 }; 01019 for (Attrib = Table; Attrib != &Table[9]; Attrib++) 01020 { 01021 *Out++ = w32fd.dwFileAttributes & Attrib->Value 01022 ? Attrib->Character 01023 : _T('-'); 01024 } 01025 *Out++ = _T(' '); 01026 } 01027 if (Modifiers & M_TIME) 01028 { 01029 FILETIME ft; 01030 SYSTEMTIME st; 01031 FileTimeToLocalFileTime(&w32fd.ftLastWriteTime, &ft); 01032 FileTimeToSystemTime(&ft, &st); 01033 01034 Out += FormatDate(Out, &st, TRUE); 01035 *Out++ = _T(' '); 01036 Out += FormatTime(Out, &st); 01037 *Out++ = _T(' '); 01038 } 01039 if (Modifiers & M_SIZE) 01040 { 01041 ULARGE_INTEGER Size; 01042 Size.LowPart = w32fd.nFileSizeLow; 01043 Size.HighPart = w32fd.nFileSizeHigh; 01044 Out += _stprintf(Out, _T("%I64u "), Size.QuadPart); 01045 } 01046 } 01047 01048 /* When using the path-searching syntax or the S modifier, 01049 * at least part of the file path is always included. 01050 * If none of the DPNX modifiers are present, include the full path */ 01051 if (PathVarName || (Modifiers & M_SHORT)) 01052 if ((Modifiers & (M_DRIVE | M_PATH | M_NAME | M_EXT)) == 0) 01053 Modifiers |= M_FULL; 01054 01055 /* Now add the requested parts of the name. 01056 * With the F modifier, add all parts to form the full path. */ 01057 Extension = _tcsrchr(Filename, _T('.')); 01058 if (Modifiers & (M_DRIVE | M_FULL)) 01059 { 01060 *Out++ = FixedPath[0]; 01061 *Out++ = FixedPath[1]; 01062 } 01063 if (Modifiers & (M_PATH | M_FULL)) 01064 { 01065 memcpy(Out, &FixedPath[2], (char *)Filename - (char *)&FixedPath[2]); 01066 Out += Filename - &FixedPath[2]; 01067 } 01068 if (Modifiers & (M_NAME | M_FULL)) 01069 { 01070 while (*Filename && Filename != Extension) 01071 *Out++ = *Filename++; 01072 } 01073 if (Modifiers & (M_EXT | M_FULL)) 01074 { 01075 if (Extension) 01076 Out = _stpcpy(Out, Extension); 01077 } 01078 01079 /* Trim trailing space which otherwise would appear as a 01080 * result of using the A/T/Z modifiers but no others. */ 01081 while (Out != &Result[0] && Out[-1] == _T(' ')) 01082 Out--; 01083 *Out = _T('\0'); 01084 01085 return Result; 01086 } 01087 01088 LPCTSTR 01089 GetBatchVar(TCHAR *varName, UINT *varNameLen) 01090 { 01091 LPCTSTR ret; 01092 TCHAR *varNameEnd; 01093 BOOL dummy; 01094 01095 *varNameLen = 1; 01096 01097 switch ( *varName ) 01098 { 01099 case _T('~'): 01100 varNameEnd = varName + 1; 01101 ret = GetEnhancedVar(&varNameEnd, FindArg); 01102 if (!ret) 01103 { 01104 error_syntax(varName); 01105 return NULL; 01106 } 01107 *varNameLen = varNameEnd - varName; 01108 return ret; 01109 case _T('0'): 01110 case _T('1'): 01111 case _T('2'): 01112 case _T('3'): 01113 case _T('4'): 01114 case _T('5'): 01115 case _T('6'): 01116 case _T('7'): 01117 case _T('8'): 01118 case _T('9'): 01119 return FindArg(*varName, &dummy); 01120 01121 case _T('*'): 01122 // 01123 // Copy over the raw params(not including the batch file name 01124 // 01125 return bc->raw_params; 01126 01127 case _T('%'): 01128 return _T("%"); 01129 } 01130 return NULL; 01131 } 01132 01133 BOOL 01134 SubstituteVars(TCHAR *Src, TCHAR *Dest, TCHAR Delim) 01135 { 01136 #define APPEND(From, Length) { \ 01137 if (Dest + (Length) > DestEnd) \ 01138 goto too_long; \ 01139 memcpy(Dest, From, (Length) * sizeof(TCHAR)); \ 01140 Dest += Length; } 01141 #define APPEND1(Char) { \ 01142 if (Dest >= DestEnd) \ 01143 goto too_long; \ 01144 *Dest++ = Char; } 01145 01146 TCHAR *DestEnd = Dest + CMDLINE_LENGTH - 1; 01147 const TCHAR *Var; 01148 int VarLength; 01149 TCHAR *SubstStart; 01150 TCHAR EndChr; 01151 while (*Src) 01152 { 01153 if (*Src != Delim) 01154 { 01155 APPEND1(*Src++) 01156 continue; 01157 } 01158 01159 Src++; 01160 if (bc && Delim == _T('%')) 01161 { 01162 UINT NameLen; 01163 Var = GetBatchVar(Src, &NameLen); 01164 if (Var != NULL) 01165 { 01166 VarLength = _tcslen(Var); 01167 APPEND(Var, VarLength) 01168 Src += NameLen; 01169 continue; 01170 } 01171 } 01172 01173 /* Find the end of the variable name. A colon (:) will usually 01174 * end the name and begin the optional modifier, but not if it 01175 * is immediately followed by the delimiter (%VAR:%). */ 01176 SubstStart = Src; 01177 while (*Src != Delim && !(*Src == _T(':') && Src[1] != Delim)) 01178 { 01179 if (!*Src) 01180 goto bad_subst; 01181 Src++; 01182 } 01183 01184 EndChr = *Src; 01185 *Src = _T('\0'); 01186 Var = GetEnvVarOrSpecial(SubstStart); 01187 *Src++ = EndChr; 01188 if (Var == NULL) 01189 { 01190 /* In a batch file, %NONEXISTENT% "expands" to an empty string */ 01191 if (bc) 01192 continue; 01193 goto bad_subst; 01194 } 01195 VarLength = _tcslen(Var); 01196 01197 if (EndChr == Delim) 01198 { 01199 /* %VAR% - use as-is */ 01200 APPEND(Var, VarLength) 01201 } 01202 else if (*Src == _T('~')) 01203 { 01204 /* %VAR:~[start][,length]% - substring 01205 * Negative values are offsets from the end */ 01206 int Start = _tcstol(Src + 1, &Src, 0); 01207 int End = VarLength; 01208 if (Start < 0) 01209 Start += VarLength; 01210 Start = max(Start, 0); 01211 Start = min(Start, VarLength); 01212 if (*Src == _T(',')) 01213 { 01214 End = _tcstol(Src + 1, &Src, 0); 01215 End += (End < 0) ? VarLength : Start; 01216 End = max(End, Start); 01217 End = min(End, VarLength); 01218 } 01219 if (*Src++ != Delim) 01220 goto bad_subst; 01221 APPEND(&Var[Start], End - Start); 01222 } 01223 else 01224 { 01225 /* %VAR:old=new% - replace all occurrences of old with new 01226 * %VAR:*old=new% - replace first occurrence only, 01227 * and remove everything before it */ 01228 TCHAR *Old, *New; 01229 DWORD OldLength, NewLength; 01230 BOOL Star = FALSE; 01231 int LastMatch = 0, i = 0; 01232 01233 if (*Src == _T('*')) 01234 { 01235 Star = TRUE; 01236 Src++; 01237 } 01238 01239 /* the string to replace may contain the delimiter */ 01240 Src = _tcschr(Old = Src, _T('=')); 01241 if (Src == NULL) 01242 goto bad_subst; 01243 OldLength = Src++ - Old; 01244 if (OldLength == 0) 01245 goto bad_subst; 01246 01247 Src = _tcschr(New = Src, Delim); 01248 if (Src == NULL) 01249 goto bad_subst; 01250 NewLength = Src++ - New; 01251 01252 while (i < VarLength) 01253 { 01254 if (_tcsnicmp(&Var[i], Old, OldLength) == 0) 01255 { 01256 if (!Star) 01257 APPEND(&Var[LastMatch], i - LastMatch) 01258 APPEND(New, NewLength) 01259 i += OldLength; 01260 LastMatch = i; 01261 if (Star) 01262 break; 01263 continue; 01264 } 01265 i++; 01266 } 01267 APPEND(&Var[LastMatch], VarLength - LastMatch) 01268 } 01269 continue; 01270 01271 bad_subst: 01272 Src = SubstStart; 01273 if (!bc) 01274 APPEND1(Delim) 01275 } 01276 *Dest = _T('\0'); 01277 return TRUE; 01278 too_long: 01279 ConOutResPrintf(STRING_ALIAS_ERROR); 01280 nErrorLevel = 9023; 01281 return FALSE; 01282 #undef APPEND 01283 #undef APPEND1 01284 } 01285 01286 /* Search the list of FOR contexts for a variable */ 01287 static LPTSTR FindForVar(TCHAR Var, BOOL *IsParam0) 01288 { 01289 FOR_CONTEXT *Ctx; 01290 *IsParam0 = FALSE; 01291 for (Ctx = fc; Ctx != NULL; Ctx = Ctx->prev) 01292 if ((UINT)(Var - Ctx->firstvar) < Ctx->varcount) 01293 return Ctx->values[Var - Ctx->firstvar]; 01294 return NULL; 01295 } 01296 01297 BOOL 01298 SubstituteForVars(TCHAR *Src, TCHAR *Dest) 01299 { 01300 TCHAR *DestEnd = &Dest[CMDLINE_LENGTH - 1]; 01301 while (*Src) 01302 { 01303 if (Src[0] == _T('%')) 01304 { 01305 BOOL Dummy; 01306 LPTSTR End = &Src[2]; 01307 LPTSTR Value = NULL; 01308 01309 if (Src[1] == _T('~')) 01310 Value = GetEnhancedVar(&End, FindForVar); 01311 01312 if (!Value) 01313 Value = FindForVar(Src[1], &Dummy); 01314 01315 if (Value) 01316 { 01317 if (Dest + _tcslen(Value) > DestEnd) 01318 return FALSE; 01319 Dest = _stpcpy(Dest, Value); 01320 Src = End; 01321 continue; 01322 } 01323 } 01324 /* Not a variable; just copy the character */ 01325 if (Dest >= DestEnd) 01326 return FALSE; 01327 *Dest++ = *Src++; 01328 } 01329 *Dest = _T('\0'); 01330 return TRUE; 01331 } 01332 01333 LPTSTR 01334 DoDelayedExpansion(LPTSTR Line) 01335 { 01336 TCHAR Buf1[CMDLINE_LENGTH]; 01337 TCHAR Buf2[CMDLINE_LENGTH]; 01338 01339 /* First, substitute FOR variables */ 01340 if (!SubstituteForVars(Line, Buf1)) 01341 return NULL; 01342 01343 if (!bDelayedExpansion || !_tcschr(Buf1, _T('!'))) 01344 return cmd_dup(Buf1); 01345 01346 /* FIXME: Delayed substitutions actually aren't quite the same as 01347 * immediate substitutions. In particular, it's possible to escape 01348 * the exclamation point using ^. */ 01349 if (!SubstituteVars(Buf1, Buf2, _T('!'))) 01350 return NULL; 01351 return cmd_dup(Buf2); 01352 } 01353 01354 01355 /* 01356 * do the prompt/input/process loop 01357 * 01358 */ 01359 01360 BOOL 01361 ReadLine (TCHAR *commandline, BOOL bMore) 01362 { 01363 TCHAR readline[CMDLINE_LENGTH]; 01364 LPTSTR ip; 01365 01366 /* if no batch input then... */ 01367 if (bc == NULL) 01368 { 01369 if (bMore) 01370 { 01371 ConOutResPrintf(STRING_MORE); 01372 } 01373 else 01374 { 01375 /* JPP 19980807 - if echo off, don't print prompt */ 01376 if (bEcho) 01377 { 01378 if (!bIgnoreEcho) 01379 ConOutChar('\n'); 01380 PrintPrompt(); 01381 } 01382 } 01383 01384 if (!ReadCommand(readline, CMDLINE_LENGTH - 1)) 01385 { 01386 bExit = TRUE; 01387 return FALSE; 01388 } 01389 01390 if (CheckCtrlBreak(BREAK_INPUT)) 01391 { 01392 ConOutPuts(_T("\n")); 01393 return FALSE; 01394 } 01395 ip = readline; 01396 } 01397 else 01398 { 01399 ip = ReadBatchLine(); 01400 if (!ip) 01401 return FALSE; 01402 } 01403 01404 return SubstituteVars(ip, commandline, _T('%')); 01405 } 01406 01407 static VOID 01408 ProcessInput() 01409 { 01410 PARSED_COMMAND *Cmd; 01411 01412 while (!bCanExit || !bExit) 01413 { 01414 Cmd = ParseCommand(NULL); 01415 if (!Cmd) 01416 continue; 01417 01418 ExecuteCommand(Cmd); 01419 FreeCommand(Cmd); 01420 } 01421 } 01422 01423 01424 /* 01425 * control-break handler. 01426 */ 01427 BOOL WINAPI BreakHandler (DWORD dwCtrlType) 01428 { 01429 01430 DWORD dwWritten; 01431 INPUT_RECORD rec; 01432 static BOOL SelfGenerated = FALSE; 01433 01434 if ((dwCtrlType != CTRL_C_EVENT) && 01435 (dwCtrlType != CTRL_BREAK_EVENT)) 01436 { 01437 return FALSE; 01438 } 01439 else 01440 { 01441 if(SelfGenerated) 01442 { 01443 SelfGenerated = FALSE; 01444 return TRUE; 01445 } 01446 } 01447 01448 if (!TryEnterCriticalSection(&ChildProcessRunningLock)) 01449 { 01450 SelfGenerated = TRUE; 01451 GenerateConsoleCtrlEvent (dwCtrlType, 0); 01452 return TRUE; 01453 } 01454 else 01455 { 01456 LeaveCriticalSection(&ChildProcessRunningLock); 01457 } 01458 01459 rec.EventType = KEY_EVENT; 01460 rec.Event.KeyEvent.bKeyDown = TRUE; 01461 rec.Event.KeyEvent.wRepeatCount = 1; 01462 rec.Event.KeyEvent.wVirtualKeyCode = _T('C'); 01463 rec.Event.KeyEvent.wVirtualScanCode = _T('C') - 35; 01464 rec.Event.KeyEvent.uChar.AsciiChar = _T('C'); 01465 rec.Event.KeyEvent.uChar.UnicodeChar = _T('C'); 01466 rec.Event.KeyEvent.dwControlKeyState = RIGHT_CTRL_PRESSED; 01467 01468 WriteConsoleInput( 01469 hIn, 01470 &rec, 01471 1, 01472 &dwWritten); 01473 01474 bCtrlBreak = TRUE; 01475 /* FIXME: Handle batch files */ 01476 01477 //ConOutPrintf(_T("^C")); 01478 01479 01480 return TRUE; 01481 } 01482 01483 01484 VOID AddBreakHandler (VOID) 01485 { 01486 SetConsoleCtrlHandler ((PHANDLER_ROUTINE)BreakHandler, TRUE); 01487 } 01488 01489 01490 VOID RemoveBreakHandler (VOID) 01491 { 01492 SetConsoleCtrlHandler ((PHANDLER_ROUTINE)BreakHandler, FALSE); 01493 } 01494 01495 01496 /* 01497 * show commands and options that are available. 01498 * 01499 */ 01500 #if 0 01501 static VOID 01502 ShowCommands (VOID) 01503 { 01504 /* print command list */ 01505 ConOutResPuts(STRING_CMD_HELP1); 01506 PrintCommandList(); 01507 01508 /* print feature list */ 01509 ConOutResPuts(STRING_CMD_HELP2); 01510 01511 #ifdef FEATURE_ALIASES 01512 ConOutResPuts(STRING_CMD_HELP3); 01513 #endif 01514 #ifdef FEATURE_HISTORY 01515 ConOutResPuts(STRING_CMD_HELP4); 01516 #endif 01517 #ifdef FEATURE_UNIX_FILENAME_COMPLETION 01518 ConOutResPuts(STRING_CMD_HELP5); 01519 #endif 01520 #ifdef FEATURE_DIRECTORY_STACK 01521 ConOutResPuts(STRING_CMD_HELP6); 01522 #endif 01523 #ifdef FEATURE_REDIRECTION 01524 ConOutResPuts(STRING_CMD_HELP7); 01525 #endif 01526 ConOutChar(_T('\n')); 01527 } 01528 #endif 01529 01530 static VOID 01531 ExecuteAutoRunFile(HKEY hkeyRoot) 01532 { 01533 TCHAR autorun[2048]; 01534 DWORD len = sizeof autorun; 01535 HKEY hkey; 01536 01537 if (RegOpenKeyEx(hkeyRoot, 01538 _T("SOFTWARE\\Microsoft\\Command Processor"), 01539 0, 01540 KEY_READ, 01541 &hkey ) == ERROR_SUCCESS) 01542 { 01543 if(RegQueryValueEx(hkey, 01544 _T("AutoRun"), 01545 0, 01546 0, 01547 (LPBYTE)autorun, 01548 &len) == ERROR_SUCCESS) 01549 { 01550 if (*autorun) 01551 ParseCommandLine(autorun); 01552 } 01553 RegCloseKey(hkey); 01554 } 01555 } 01556 01557 /* Get the command that comes after a /C or /K switch */ 01558 static VOID 01559 GetCmdLineCommand(TCHAR *commandline, TCHAR *ptr, BOOL AlwaysStrip) 01560 { 01561 TCHAR *LastQuote; 01562 01563 while (_istspace(*ptr)) 01564 ptr++; 01565 01566 /* Remove leading quote, find final quote */ 01567 if (*ptr == _T('"') && 01568 (LastQuote = _tcsrchr(++ptr, _T('"'))) != NULL) 01569 { 01570 TCHAR *Space; 01571 /* Under certain circumstances, all quotes are preserved. 01572 * CMD /? documents these conditions as follows: 01573 * 1. No /S switch 01574 * 2. Exactly two quotes 01575 * 3. No "special characters" between the quotes 01576 * (CMD /? says &<>()@^| but parentheses did not 01577 * trigger this rule when I tested them.) 01578 * 4. Whitespace exists between the quotes 01579 * 5. Enclosed string is an executable filename 01580 */ 01581 *LastQuote = _T('\0'); 01582 for (Space = ptr + 1; Space < LastQuote; Space++) 01583 { 01584 if (_istspace(*Space)) /* Rule 4 */ 01585 { 01586 if (!AlwaysStrip && /* Rule 1 */ 01587 !_tcspbrk(ptr, _T("\"&<>@^|")) && /* Rules 2, 3 */ 01588 SearchForExecutable(ptr, commandline)) /* Rule 5 */ 01589 { 01590 /* All conditions met: preserve both the quotes */ 01591 *LastQuote = _T('"'); 01592 _tcscpy(commandline, ptr - 1); 01593 return; 01594 } 01595 break; 01596 } 01597 } 01598 01599 /* The conditions were not met: remove both the 01600 * leading quote and the last quote */ 01601 _tcscpy(commandline, ptr); 01602 _tcscpy(&commandline[LastQuote - ptr], LastQuote + 1); 01603 return; 01604 } 01605 01606 /* No quotes; just copy */ 01607 _tcscpy(commandline, ptr); 01608 } 01609 01610 /* 01611 * set up global initializations and process parameters 01612 */ 01613 static VOID 01614 Initialize() 01615 { 01616 HMODULE NtDllModule; 01617 TCHAR commandline[CMDLINE_LENGTH]; 01618 TCHAR ModuleName[_MAX_PATH + 1]; 01619 TCHAR lpBuffer[2]; 01620 INT nExitCode; 01621 01622 //INT len; 01623 TCHAR *ptr, *cmdLine, option = 0; 01624 BOOL AlwaysStrip = FALSE; 01625 BOOL AutoRun = TRUE; 01626 01627 /* get version information */ 01628 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 01629 GetVersionEx (&osvi); 01630 01631 /* Some people like to run ReactOS cmd.exe on Win98, it helps in the 01632 * build process. So don't link implicitly against ntdll.dll, load it 01633 * dynamically instead */ 01634 NtDllModule = GetModuleHandle(TEXT("ntdll.dll")); 01635 if (NtDllModule != NULL) 01636 { 01637 NtQueryInformationProcessPtr = (NtQueryInformationProcessProc)GetProcAddress(NtDllModule, "NtQueryInformationProcess"); 01638 NtReadVirtualMemoryPtr = (NtReadVirtualMemoryProc)GetProcAddress(NtDllModule, "NtReadVirtualMemory"); 01639 } 01640 01641 InitLocale (); 01642 01643 /* get default input and output console handles */ 01644 hOut = GetStdHandle (STD_OUTPUT_HANDLE); 01645 hIn = GetStdHandle (STD_INPUT_HANDLE); 01646 01647 /* Set EnvironmentVariable PROMPT if it does not exists any env value. 01648 for you can change the EnvirommentVariable for prompt before cmd start 01649 this patch are not 100% right, if it does not exists a PROMPT value cmd should use 01650 $P$G as defualt not set EnvirommentVariable PROMPT to $P$G if it does not exists */ 01651 if (GetEnvironmentVariable(_T("PROMPT"),lpBuffer, sizeof(lpBuffer) / sizeof(lpBuffer[0])) == 0) 01652 SetEnvironmentVariable (_T("PROMPT"), _T("$P$G")); 01653 01654 #ifdef FEATURE_DIR_STACK 01655 /* initialize directory stack */ 01656 InitDirectoryStack (); 01657 #endif 01658 01659 #ifdef FEATURE_HISTORY 01660 /*initialize history*/ 01661 InitHistory(); 01662 #endif 01663 01664 /* Set COMSPEC environment variable */ 01665 if (0 != GetModuleFileName (NULL, ModuleName, _MAX_PATH + 1)) 01666 { 01667 ModuleName[_MAX_PATH] = _T('\0'); 01668 SetEnvironmentVariable (_T("COMSPEC"), ModuleName); 01669 } 01670 01671 /* add ctrl break handler */ 01672 AddBreakHandler (); 01673 01674 01675 SetConsoleMode (hIn, ENABLE_PROCESSED_INPUT); 01676 01677 cmdLine = GetCommandLine(); 01678 TRACE ("[command args: %s]\n", debugstr_aw(cmdLine)); 01679 01680 for (ptr = cmdLine; *ptr; ptr++) 01681 { 01682 if (*ptr == _T('/')) 01683 { 01684 option = _totupper(ptr[1]); 01685 if (option == _T('?')) 01686 { 01687 ConOutResPaging(TRUE,STRING_CMD_HELP8); 01688 nErrorLevel = 1; 01689 bExit = TRUE; 01690 return; 01691 } 01692 else if (option == _T('P')) 01693 { 01694 if (!IsExistingFile (_T("\\autoexec.bat"))) 01695 { 01696 #ifdef INCLUDE_CMD_DATE 01697 cmd_date (_T("")); 01698 #endif 01699 #ifdef INCLUDE_CMD_TIME 01700 cmd_time (_T("")); 01701 #endif 01702 } 01703 else 01704 { 01705 ParseCommandLine (_T("\\autoexec.bat")); 01706 } 01707 bCanExit = FALSE; 01708 } 01709 else if (option == _T('A')) 01710 { 01711 bUnicodeOutput = FALSE; 01712 } 01713 else if (option == _T('C') || option == _T('K') || option == _T('R')) 01714 { 01715 /* Remainder of command line is a command to be run */ 01716 break; 01717 } 01718 else if (option == _T('D')) 01719 { 01720 AutoRun = FALSE; 01721 } 01722 else if (option == _T('Q')) 01723 { 01724 bDisableBatchEcho = TRUE; 01725 } 01726 else if (option == _T('S')) 01727 { 01728 AlwaysStrip = TRUE; 01729 } 01730 #ifdef INCLUDE_CMD_COLOR 01731 else if (!_tcsnicmp(ptr, _T("/T:"), 3)) 01732 { 01733 /* process /t (color) argument */ 01734 wDefColor = (WORD)_tcstoul(&ptr[3], &ptr, 16); 01735 SetScreenColor(wDefColor, TRUE); 01736 } 01737 #endif 01738 else if (option == _T('U')) 01739 { 01740 bUnicodeOutput = TRUE; 01741 } 01742 else if (option == _T('V')) 01743 { 01744 bDelayedExpansion = _tcsnicmp(&ptr[2], _T(":OFF"), 4); 01745 } 01746 } 01747 } 01748 01749 if (!*ptr) 01750 { 01751 /* If neither /C or /K was given, display a simple version string */ 01752 ConOutResPrintf(STRING_REACTOS_VERSION, 01753 _T(KERNEL_RELEASE_STR), 01754 _T(KERNEL_VERSION_BUILD_STR)); 01755 ConOutPuts(_T("(C) Copyright 1998-") _T(COPYRIGHT_YEAR) _T(" ReactOS Team.\n")); 01756 } 01757 01758 if (AutoRun) 01759 { 01760 ExecuteAutoRunFile(HKEY_LOCAL_MACHINE); 01761 ExecuteAutoRunFile(HKEY_CURRENT_USER); 01762 } 01763 01764 if (*ptr) 01765 { 01766 /* Do the /C or /K command */ 01767 GetCmdLineCommand(commandline, &ptr[2], AlwaysStrip); 01768 nExitCode = ParseCommandLine(commandline); 01769 if (option != _T('K')) 01770 { 01771 nErrorLevel = nExitCode; 01772 bExit = TRUE; 01773 } 01774 } 01775 } 01776 01777 01778 static VOID Cleanup() 01779 { 01780 /* run cmdexit.bat */ 01781 if (IsExistingFile (_T("cmdexit.bat"))) 01782 { 01783 ConErrResPuts(STRING_CMD_ERROR5); 01784 01785 ParseCommandLine (_T("cmdexit.bat")); 01786 } 01787 else if (IsExistingFile (_T("\\cmdexit.bat"))) 01788 { 01789 ConErrResPuts (STRING_CMD_ERROR5); 01790 ParseCommandLine (_T("\\cmdexit.bat")); 01791 } 01792 01793 #ifdef FEATURE_DIECTORY_STACK 01794 /* destroy directory stack */ 01795 DestroyDirectoryStack (); 01796 #endif 01797 01798 #ifdef FEATURE_HISTORY 01799 CleanHistory(); 01800 #endif 01801 01802 /* free GetEnvVar's buffer */ 01803 GetEnvVar(NULL); 01804 01805 /* remove ctrl break handler */ 01806 RemoveBreakHandler (); 01807 SetConsoleMode( GetStdHandle( STD_INPUT_HANDLE ), 01808 ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT ); 01809 DeleteCriticalSection(&ChildProcessRunningLock); 01810 } 01811 01812 /* 01813 * main function 01814 */ 01815 int cmd_main (int argc, const TCHAR *argv[]) 01816 { 01817 HANDLE hConsole; 01818 TCHAR startPath[MAX_PATH]; 01819 CONSOLE_SCREEN_BUFFER_INFO Info; 01820 01821 InitializeCriticalSection(&ChildProcessRunningLock); 01822 lpOriginalEnvironment = DuplicateEnvironment(); 01823 01824 GetCurrentDirectory(MAX_PATH,startPath); 01825 _tchdir(startPath); 01826 01827 SetFileApisToOEM(); 01828 InputCodePage= 0; 01829 OutputCodePage = 0; 01830 01831 hConsole = CreateFile(_T("CONOUT$"), GENERIC_READ|GENERIC_WRITE, 01832 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 01833 OPEN_EXISTING, 0, NULL); 01834 if (hConsole != INVALID_HANDLE_VALUE) 01835 { 01836 if (!GetConsoleScreenBufferInfo(hConsole, &Info)) 01837 { 01838 ConErrFormatMessage(GetLastError()); 01839 return(1); 01840 } 01841 wDefColor = Info.wAttributes; 01842 CloseHandle(hConsole); 01843 } 01844 01845 InputCodePage= GetConsoleCP(); 01846 OutputCodePage = GetConsoleOutputCP(); 01847 CMD_ModuleHandle = GetModuleHandle(NULL); 01848 01849 /* check switches on command-line */ 01850 Initialize(); 01851 01852 /* call prompt routine */ 01853 ProcessInput(); 01854 01855 /* do the cleanup */ 01856 Cleanup(); 01857 01858 cmd_free(lpOriginalEnvironment); 01859 01860 cmd_exit(nErrorLevel); 01861 return(nErrorLevel); 01862 } 01863 01864 /* EOF */ Generated on Sun May 27 2012 04:18:10 for ReactOS by
1.7.6.1
|