- Implement ProtocolResetComplete
[reactos.git] / base / shell / cmd / cmd.c
1 /*
2 * CMD.C - command-line interface.
3 *
4 *
5 * History:
6 *
7 * 17 Jun 1994 (Tim Norman)
8 * started.
9 *
10 * 08 Aug 1995 (Matt Rains)
11 * I have cleaned up the source code. changes now bring this source
12 * into guidelines for recommended programming practice.
13 *
14 * A added the the standard FreeDOS GNU licence test to the
15 * initialize() function.
16 *
17 * Started to replace puts() with printf(). this will help
18 * standardize output. please follow my lead.
19 *
20 * I have added some constants to help making changes easier.
21 *
22 * 15 Dec 1995 (Tim Norman)
23 * major rewrite of the code to make it more efficient and add
24 * redirection support (finally!)
25 *
26 * 06 Jan 1996 (Tim Norman)
27 * finished adding redirection support! Changed to use our own
28 * exec code (MUCH thanks to Svante Frey!!)
29 *
30 * 29 Jan 1996 (Tim Norman)
31 * added support for CHDIR, RMDIR, MKDIR, and ERASE, as per
32 * suggestion of Steffan Kaiser
33 *
34 * changed "file not found" error message to "bad command or
35 * filename" thanks to Dustin Norman for noticing that confusing
36 * message!
37 *
38 * changed the format to call internal commands (again) so that if
39 * they want to split their commands, they can do it themselves
40 * (none of the internal functions so far need that much power, anyway)
41 *
42 * 27 Aug 1996 (Tim Norman)
43 * added in support for Oliver Mueller's ALIAS command
44 *
45 * 14 Jun 1997 (Steffan Kaiser)
46 * added ctrl-break handling and error level
47 *
48 * 16 Jun 1998 (Rob Lake)
49 * Runs command.com if /P is specified in command line. Command.com
50 * also stays permanent. If /C is in the command line, starts the
51 * program next in the line.
52 *
53 * 21 Jun 1998 (Rob Lake)
54 * Fixed up /C so that arguments for the program
55 *
56 * 08-Jul-1998 (John P. Price)
57 * Now sets COMSPEC environment variable
58 * misc clean up and optimization
59 * added date and time commands
60 * changed to using spawnl instead of exec. exec does not copy the
61 * environment to the child process!
62 *
63 * 14 Jul 1998 (Hans B Pufal)
64 * Reorganised source to be more efficient and to more closely
65 * follow MS-DOS conventions. (eg %..% environment variable
66 * replacement works form command line as well as batch file.
67 *
68 * New organisation also properly support nested batch files.
69 *
70 * New command table structure is half way towards providing a
71 * system in which COMMAND will find out what internal commands
72 * are loaded
73 *
74 * 24 Jul 1998 (Hans B Pufal) [HBP_003]
75 * Fixed return value when called with /C option
76 *
77 * 27 Jul 1998 John P. Price
78 * added config.h include
79 *
80 * 28 Jul 1998 John P. Price
81 * added showcmds function to show commands and options available
82 *
83 * 07-Aug-1998 (John P Price <linux-guru@gcfl.net>)
84 * Fixed carrage return output to better match MSDOS with echo
85 * on or off. (marked with "JPP 19980708")
86 *
87 * 07-Dec-1998 (Eric Kohl)
88 * First ReactOS release.
89 * Extended length of commandline buffers to 512.
90 *
91 * 13-Dec-1998 (Eric Kohl)
92 * Added COMSPEC environment variable.
93 * Added "/t" support (color) on cmd command line.
94 *
95 * 07-Jan-1999 (Eric Kohl)
96 * Added help text ("cmd /?").
97 *
98 * 25-Jan-1999 (Eric Kohl)
99 * Unicode and redirection safe!
100 * Fixed redirections and piping.
101 * Piping is based on temporary files, but basic support
102 * for anonymous pipes already exists.
103 *
104 * 27-Jan-1999 (Eric Kohl)
105 * Replaced spawnl() by CreateProcess().
106 *
107 * 22-Oct-1999 (Eric Kohl)
108 * Added break handler.
109 *
110 * 15-Dec-1999 (Eric Kohl)
111 * Fixed current directory
112 *
113 * 28-Dec-1999 (Eric Kohl)
114 * Restore window title after program/batch execution
115 *
116 * 03-Feb-2001 (Eric Kohl)
117 * Workaround because argc[0] is NULL under ReactOS
118 *
119 * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>)
120 * %envvar% replacement conflicted with for.
121 *
122 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
123 * Make MakeSureDirectoryPathExistsEx unicode safe.
124 *
125 * 28-Mai-2004
126 * Removed MakeSureDirectoryPathExistsEx.
127 * Use the current directory if GetTempPath fails.
128 *
129 * 12-Jul-2004 (Jens Collin <jens.collin@lakhei.com>)
130 * Added ShellExecute call when all else fails to be able to "launch" any file.
131 *
132 * 02-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
133 * Remove all hardcode string to En.rc
134 *
135 * 06-May-2005 (Klemens Friedl <frik85@gmail.com>)
136 * Add 'help' command (list all commands plus description)
137 *
138 * 06-jul-2005 (Magnus Olsen <magnus@greatlord.com>)
139 * translate '%errorlevel%' to the internal value.
140 * Add proper memmory alloc ProcessInput, the error
141 * handling for memmory handling need to be improve
142 */
143
144 #include <precomp.h>
145
146 #ifndef NT_SUCCESS
147 #define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode) >= 0)
148 #endif
149
150 typedef NTSTATUS (WINAPI *NtQueryInformationProcessProc)(HANDLE, PROCESSINFOCLASS,
151 PVOID, ULONG, PULONG);
152 typedef NTSTATUS (WINAPI *NtReadVirtualMemoryProc)(HANDLE, PVOID, PVOID, ULONG, PULONG);
153
154 BOOL bExit = FALSE; /* indicates EXIT was typed */
155 BOOL bCanExit = TRUE; /* indicates if this shell is exitable */
156 BOOL bCtrlBreak = FALSE; /* Ctrl-Break or Ctrl-C hit */
157 BOOL bIgnoreEcho = FALSE; /* Set this to TRUE to prevent a newline, when executing a command */
158 INT nErrorLevel = 0; /* Errorlevel of last launched external program */
159 BOOL bChildProcessRunning = FALSE;
160 DWORD dwChildProcessId = 0;
161 OSVERSIONINFO osvi;
162 HANDLE hIn;
163 HANDLE hOut;
164 HANDLE hConsole;
165 HANDLE CMD_ModuleHandle;
166 HMODULE NtDllModule;
167
168 static NtQueryInformationProcessProc NtQueryInformationProcessPtr = NULL;
169 static NtReadVirtualMemoryProc NtReadVirtualMemoryPtr = NULL;
170
171 #ifdef INCLUDE_CMD_COLOR
172 WORD wColor; /* current color */
173 WORD wDefColor; /* default color */
174 #endif
175
176 /*
177 * convert
178 *
179 * insert commas into a number
180 */
181 INT
182 ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len, BOOL bPutSeperator)
183 {
184 TCHAR temp[32];
185 UINT n, iTarget;
186
187 if (len <= 1)
188 return 0;
189
190 if (num.QuadPart == 0)
191 {
192 des[0] = _T('0');
193 des[1] = _T('\0');
194 return 1;
195 }
196
197 n = 0;
198 iTarget = nNumberGroups;
199 if (!nNumberGroups)
200 bPutSeperator = FALSE;
201
202 while (num.QuadPart > 0)
203 {
204 if (iTarget == n && bPutSeperator)
205 {
206 iTarget += nNumberGroups + 1;
207 temp[31 - n++] = cThousandSeparator;
208 }
209 temp[31 - n++] = (TCHAR)(num.QuadPart % 10) + _T('0');
210 num.QuadPart /= 10;
211 }
212 if (n > len-1)
213 n = len-1;
214
215 memcpy(des, temp + 32 - n, n * sizeof(TCHAR));
216 des[n] = _T('\0');
217
218 return n;
219 }
220
221 /*
222 * is character a delimeter when used on first word?
223 *
224 */
225 static BOOL IsDelimiter (TCHAR c)
226 {
227 return (c == _T('/') || c == _T('=') || c == _T('\0') || _istspace (c));
228 }
229
230 /*
231 * Is a process a console process?
232 */
233 static BOOL IsConsoleProcess(HANDLE Process)
234 {
235 NTSTATUS Status;
236 PROCESS_BASIC_INFORMATION Info;
237 PEB ProcessPeb;
238 ULONG BytesRead;
239
240 if (NULL == NtQueryInformationProcessPtr || NULL == NtReadVirtualMemoryPtr)
241 {
242 return TRUE;
243 }
244
245 Status = NtQueryInformationProcessPtr (
246 Process, ProcessBasicInformation,
247 &Info, sizeof(PROCESS_BASIC_INFORMATION), NULL);
248 if (! NT_SUCCESS(Status))
249 {
250 WARN ("NtQueryInformationProcess failed with status %08x\n", Status);
251 return TRUE;
252 }
253 Status = NtReadVirtualMemoryPtr (
254 Process, Info.PebBaseAddress, &ProcessPeb,
255 sizeof(PEB), &BytesRead);
256 if (! NT_SUCCESS(Status) || sizeof(PEB) != BytesRead)
257 {
258 WARN ("Couldn't read virt mem status %08x bytes read %lu\n", Status, BytesRead);
259 return TRUE;
260 }
261
262 return IMAGE_SUBSYSTEM_WINDOWS_CUI == ProcessPeb.ImageSubSystem;
263 }
264
265
266
267 #ifdef _UNICODE
268 #define SHELLEXECUTETEXT "ShellExecuteW"
269 #else
270 #define SHELLEXECUTETEXT "ShellExecuteA"
271 #endif
272
273 typedef HINSTANCE (WINAPI *MYEX)(
274 HWND hwnd,
275 LPCTSTR lpOperation,
276 LPCTSTR lpFile,
277 LPCTSTR lpParameters,
278 LPCTSTR lpDirectory,
279 INT nShowCmd
280 );
281
282
283
284 static BOOL RunFile(LPTSTR filename)
285 {
286 HMODULE hShell32;
287 MYEX hShExt;
288 HINSTANCE ret;
289
290 TRACE ("RunFile(%s)\n", debugstr_aw(filename));
291 hShell32 = LoadLibrary(_T("SHELL32.DLL"));
292 if (!hShell32)
293 {
294 WARN ("RunFile: couldn't load SHELL32.DLL!\n");
295 return FALSE;
296 }
297
298 hShExt = (MYEX)(FARPROC)GetProcAddress(hShell32, SHELLEXECUTETEXT);
299 if (!hShExt)
300 {
301 WARN ("RunFile: couldn't find ShellExecuteA/W in SHELL32.DLL!\n");
302 FreeLibrary(hShell32);
303 return FALSE;
304 }
305
306 TRACE ("RunFile: ShellExecuteA/W is at %x\n", hShExt);
307
308 ret = (hShExt)(NULL, _T("open"), filename, NULL, NULL, SW_SHOWNORMAL);
309
310 TRACE ("RunFile: ShellExecuteA/W returned %d\n", (DWORD)ret);
311
312 FreeLibrary(hShell32);
313 return (((DWORD)ret) > 32);
314 }
315
316
317
318 /*
319 * This command (in first) was not found in the command table
320 *
321 * Full - whole command line
322 * First - first word on command line
323 * Rest - rest of command line
324 */
325
326 static VOID
327 Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
328 {
329 TCHAR *szFullName=NULL;
330 TCHAR *first = NULL;
331 TCHAR *rest = NULL;
332 TCHAR *full = NULL;
333 TCHAR *dot = NULL;
334 TCHAR szWindowTitle[MAX_PATH];
335 DWORD dwExitCode = 0;
336
337 TRACE ("Execute: \'%s\' \'%s\'\n", debugstr_aw(first), debugstr_aw(rest));
338
339 /* we need biger buffer that First, Rest, Full are already
340 need rewrite some code to use cmd_realloc when it need instead
341 of add 512bytes extra */
342
343 first = cmd_alloc ( (_tcslen(First) + 512) * sizeof(TCHAR));
344 if (first == NULL)
345 {
346 error_out_of_memory();
347 nErrorLevel = 1;
348 return ;
349 }
350
351 rest = cmd_alloc ( (_tcslen(Rest) + 512) * sizeof(TCHAR));
352 if (rest == NULL)
353 {
354 cmd_free (first);
355 error_out_of_memory();
356 nErrorLevel = 1;
357 return ;
358 }
359
360 full = cmd_alloc ( (_tcslen(Full) + 512) * sizeof(TCHAR));
361 if (full == NULL)
362 {
363 cmd_free (first);
364 cmd_free (rest);
365 error_out_of_memory();
366 nErrorLevel = 1;
367 return ;
368 }
369
370 szFullName = cmd_alloc ( (_tcslen(Full) + 512) * sizeof(TCHAR));
371 if (full == NULL)
372 {
373 cmd_free (first);
374 cmd_free (rest);
375 cmd_free (full);
376 error_out_of_memory();
377 nErrorLevel = 1;
378 return ;
379 }
380
381
382 /* Though it was already parsed once, we have a different set of rules
383 for parsing before we pass to CreateProccess */
384 if(!_tcschr(Full,_T('\"')))
385 {
386 _tcscpy(first,First);
387 _tcscpy(rest,Rest);
388 _tcscpy(full,Full);
389 }
390 else
391 {
392 UINT i = 0;
393 BOOL bInside = FALSE;
394 rest[0] = _T('\0');
395 full[0] = _T('\0');
396 first[0] = _T('\0');
397 _tcscpy(first,Full);
398 /* find the end of the command and start of the args */
399 for(i = 0; i < _tcslen(first); i++)
400 {
401 if(!_tcsncmp(&first[i], _T("\""), 1))
402 bInside = !bInside;
403 if(!_tcsncmp(&first[i], _T(" "), 1) && !bInside)
404 {
405 _tcscpy(rest,&first[i]);
406 first[i] = _T('\0');
407 break;
408 }
409
410 }
411 i = 0;
412 /* remove any slashes */
413 while(i < _tcslen(first))
414 {
415 if(first[i] == _T('\"'))
416 memmove(&first[i],&first[i + 1], _tcslen(&first[i]) * sizeof(TCHAR));
417 else
418 i++;
419 }
420 /* Drop quotes around it just in case there is a space */
421 _tcscpy(full,_T("\""));
422 _tcscat(full,first);
423 _tcscat(full,_T("\" "));
424 _tcscat(full,rest);
425 }
426
427 /* check for a drive change */
428 if ((_istalpha (first[0])) && (!_tcscmp (first + 1, _T(":"))))
429 {
430 BOOL working = TRUE;
431 if (!SetCurrentDirectory(first))
432 /* Guess they changed disc or something, handle that gracefully and get to root */
433 {
434 TCHAR str[4];
435 str[0]=first[0];
436 str[1]=_T(':');
437 str[2]=_T('\\');
438 str[3]=0;
439 working = SetCurrentDirectory(str);
440 }
441
442 if (!working) ConErrResPuts (STRING_FREE_ERROR1);
443
444 cmd_free (first);
445 cmd_free (rest);
446 cmd_free (full);
447 cmd_free (szFullName);
448 nErrorLevel = 1;
449 return;
450 }
451
452 /* get the PATH environment variable and parse it */
453 /* search the PATH environment variable for the binary */
454 if (!SearchForExecutable (first, szFullName))
455 {
456 error_bad_command ();
457 cmd_free (first);
458 cmd_free (rest);
459 cmd_free (full);
460 cmd_free (szFullName);
461 nErrorLevel = 1;
462 return;
463
464 }
465
466 GetConsoleTitle (szWindowTitle, MAX_PATH);
467
468 /* check if this is a .BAT or .CMD file */
469 dot = _tcsrchr (szFullName, _T('.'));
470 if (dot && (!_tcsicmp (dot, _T(".bat")) || !_tcsicmp (dot, _T(".cmd"))))
471 {
472 TRACE ("[BATCH: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(rest));
473 Batch (szFullName, first, rest);
474 }
475 else
476 {
477 /* exec the program */
478 PROCESS_INFORMATION prci;
479 STARTUPINFO stui;
480
481 TRACE ("[EXEC: %s %s]\n", debugstr_aw(full), debugstr_aw(rest));
482 /* build command line for CreateProcess() */
483
484 /* fill startup info */
485 memset (&stui, 0, sizeof (STARTUPINFO));
486 stui.cb = sizeof (STARTUPINFO);
487 stui.dwFlags = STARTF_USESHOWWINDOW;
488 stui.wShowWindow = SW_SHOWDEFAULT;
489
490 // return console to standard mode
491 SetConsoleMode (GetStdHandle(STD_INPUT_HANDLE),
492 ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT );
493
494 if (CreateProcess (szFullName,
495 full,
496 NULL,
497 NULL,
498 TRUE,
499 0, /* CREATE_NEW_PROCESS_GROUP */
500 NULL,
501 NULL,
502 &stui,
503 &prci))
504
505 {
506 if (IsConsoleProcess(prci.hProcess))
507 {
508 /* FIXME: Protect this with critical section */
509 bChildProcessRunning = TRUE;
510 dwChildProcessId = prci.dwProcessId;
511
512 WaitForSingleObject (prci.hProcess, INFINITE);
513
514 /* FIXME: Protect this with critical section */
515 bChildProcessRunning = FALSE;
516
517 GetExitCodeProcess (prci.hProcess, &dwExitCode);
518 nErrorLevel = (INT)dwExitCode;
519 }
520 else
521 {
522 nErrorLevel = 0;
523 }
524 CloseHandle (prci.hThread);
525 CloseHandle (prci.hProcess);
526 }
527 else
528 {
529 TRACE ("[ShellExecute: %s]\n", debugstr_aw(full));
530 // See if we can run this with ShellExecute() ie myfile.xls
531 if (!RunFile(full))
532 {
533 TRACE ("[ShellExecute failed!: %s]\n", debugstr_aw(full));
534 error_bad_command ();
535 nErrorLevel = 1;
536 }
537 else
538 {
539 nErrorLevel = 0;
540 }
541 }
542 // restore console mode
543 SetConsoleMode (
544 GetStdHandle( STD_INPUT_HANDLE ),
545 ENABLE_PROCESSED_INPUT );
546 }
547
548 /* Get code page if it has been change */
549 InputCodePage= GetConsoleCP();
550 OutputCodePage = GetConsoleOutputCP();
551 SetConsoleTitle (szWindowTitle);
552
553 cmd_free(first);
554 cmd_free(rest);
555 cmd_free(full);
556 cmd_free (szFullName);
557 }
558
559
560 /*
561 * look through the internal commands and determine whether or not this
562 * command is one of them. If it is, call the command. If not, call
563 * execute to run it as an external program.
564 *
565 * line - the command line of the program to run
566 *
567 */
568
569 VOID
570 DoCommand (LPTSTR line)
571 {
572 TCHAR *com = NULL; /* the first word in the command */
573 TCHAR *cp = NULL;
574 LPTSTR cstart;
575 LPTSTR rest; /* pointer to the rest of the command line */
576 INT cl;
577 LPCOMMAND cmdptr;
578
579 TRACE ("DoCommand: (\'%s\')\n", debugstr_aw(line));
580
581 com = cmd_alloc( (_tcslen(line) +512)*sizeof(TCHAR) );
582 if (com == NULL)
583 {
584 error_out_of_memory();
585 return;
586 }
587
588 cp = com;
589 /* Skip over initial white space */
590 while (_istspace (*line))
591 line++;
592 rest = line;
593
594 cstart = rest;
595
596 /* Anything to do ? */
597 if (*rest)
598 {
599 if (*rest == _T('"'))
600 {
601 /* treat quoted words specially */
602
603 rest++;
604
605 while(*rest != _T('\0') && *rest != _T('"'))
606 *cp++ = _totlower (*rest++);
607 if (*rest == _T('"'))
608 rest++;
609 }
610 else
611 {
612 while (!IsDelimiter (*rest))
613 *cp++ = _totlower (*rest++);
614 }
615
616
617 /* Terminate first word */
618 *cp = _T('\0');
619
620 /* Do not limit commands to MAX_PATH */
621 /*
622 if(_tcslen(com) > MAX_PATH)
623 {
624 error_bad_command();
625 cmd_free(com);
626 return;
627 }
628 */
629
630 /* Skip over whitespace to rest of line, exclude 'echo' command */
631 if (_tcsicmp (com, _T("echo")))
632 {
633 while (_istspace (*rest))
634 rest++;
635 }
636
637 /* Scan internal command table */
638 for (cmdptr = cmds;; cmdptr++)
639 {
640 /* If end of table execute ext cmd */
641 if (cmdptr->name == NULL)
642 {
643 Execute (line, com, rest);
644 break;
645 }
646
647 if (!_tcscmp (com, cmdptr->name))
648 {
649 cmdptr->func (com, rest);
650 break;
651 }
652
653 /* The following code handles the case of commands like CD which
654 * are recognised even when the command name and parameter are
655 * not space separated.
656 *
657 * e.g dir..
658 * cd\freda
659 */
660
661 /* Get length of command name */
662 cl = _tcslen (cmdptr->name);
663
664 if ((cmdptr->flags & CMD_SPECIAL) &&
665 (!_tcsncmp (cmdptr->name, com, cl)) &&
666 (_tcschr (_T("\\.-"), *(com + cl))))
667 {
668 /* OK its one of the specials...*/
669
670 /* Terminate first word properly */
671 com[cl] = _T('\0');
672
673 /* Call with new rest */
674 cmdptr->func (com, cstart + cl);
675 break;
676 }
677 }
678 }
679 cmd_free(com);
680 }
681
682
683 /*
684 * process the command line and execute the appropriate functions
685 * full input/output redirection and piping are supported
686 */
687
688 VOID ParseCommandLine (LPTSTR cmd)
689 {
690 TCHAR cmdline[CMDLINE_LENGTH];
691 LPTSTR s;
692 #ifdef FEATURE_REDIRECTION
693 TCHAR in[CMDLINE_LENGTH] = _T("");
694 TCHAR out[CMDLINE_LENGTH] = _T("");
695 TCHAR err[CMDLINE_LENGTH] = _T("");
696 TCHAR szTempPath[MAX_PATH] = _T(".\\");
697 TCHAR szFileName[2][MAX_PATH] = {_T(""), _T("")};
698 HANDLE hFile[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
699 LPTSTR t = NULL;
700 INT num = 0;
701 INT nRedirFlags = 0;
702 INT Length;
703 UINT Attributes;
704 BOOL bNewBatch = TRUE;
705 HANDLE hOldConIn;
706 HANDLE hOldConOut;
707 HANDLE hOldConErr;
708 #endif /* FEATURE_REDIRECTION */
709
710 _tcscpy (cmdline, cmd);
711 s = &cmdline[0];
712
713 TRACE ("ParseCommandLine: (\'%s\')\n", debugstr_aw(s));
714
715 #ifdef FEATURE_ALIASES
716 /* expand all aliases */
717 ExpandAlias (s, CMDLINE_LENGTH);
718 #endif /* FEATURE_ALIAS */
719
720 #ifdef FEATURE_REDIRECTION
721 /* find the temp path to store temporary files */
722 Length = GetTempPath (MAX_PATH, szTempPath);
723 if (Length > 0 && Length < MAX_PATH)
724 {
725 Attributes = GetFileAttributes(szTempPath);
726 if (Attributes == 0xffffffff ||
727 !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
728 {
729 Length = 0;
730 }
731 }
732 if (Length == 0 || Length >= MAX_PATH)
733 {
734 _tcscpy(szTempPath, _T(".\\"));
735 }
736 if (szTempPath[_tcslen (szTempPath) - 1] != _T('\\'))
737 _tcscat (szTempPath, _T("\\"));
738
739 /* get the redirections from the command line */
740 num = GetRedirection (s, in, out, err, &nRedirFlags);
741
742 /* more efficient, but do we really need to do this? */
743 for (t = in; _istspace (*t); t++)
744 ;
745 _tcscpy (in, t);
746
747 for (t = out; _istspace (*t); t++)
748 ;
749 _tcscpy (out, t);
750
751 for (t = err; _istspace (*t); t++)
752 ;
753 _tcscpy (err, t);
754
755 if(bc && !_tcslen (in) && _tcslen (bc->In))
756 _tcscpy(in, bc->In);
757 if(bc && !out[0] && _tcslen(bc->Out))
758 {
759 nRedirFlags |= OUTPUT_APPEND;
760 _tcscpy(out, bc->Out);
761 }
762 if(bc && !_tcslen (err) && _tcslen (bc->Err))
763 {
764 nRedirFlags |= ERROR_APPEND;
765 _tcscpy(err, bc->Err);
766 }
767
768 /* Set up the initial conditions ... */
769 /* preserve STDIN, STDOUT and STDERR handles */
770 hOldConIn = GetStdHandle (STD_INPUT_HANDLE);
771 hOldConOut = GetStdHandle (STD_OUTPUT_HANDLE);
772 hOldConErr = GetStdHandle (STD_ERROR_HANDLE);
773
774 /* redirect STDIN */
775 if (in[0])
776 {
777 HANDLE hFile;
778 SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
779
780 /* we need make sure the LastError msg is zero before calling CreateFile */
781 SetLastError(0);
782
783 /* Set up pipe for the standard input handler */
784 hFile = CreateFile (in, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING,
785 FILE_ATTRIBUTE_NORMAL, NULL);
786 if (hFile == INVALID_HANDLE_VALUE)
787 {
788 ConErrResPrintf(STRING_CMD_ERROR1, in);
789 return;
790 }
791
792 if (!SetStdHandle (STD_INPUT_HANDLE, hFile))
793 {
794 ConErrResPrintf(STRING_CMD_ERROR1, in);
795 return;
796 }
797 TRACE ("Input redirected from: %s\n", debugstr_aw(in));
798 }
799
800 /* Now do all but the last pipe command */
801 *szFileName[0] = _T('\0');
802 hFile[0] = INVALID_HANDLE_VALUE;
803
804 while (num-- > 1)
805 {
806 SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
807
808 /* Create unique temporary file name */
809 GetTempFileName (szTempPath, _T("CMD"), 0, szFileName[1]);
810
811 /* we need make sure the LastError msg is zero before calling CreateFile */
812 SetLastError(0);
813
814 /* Set current stdout to temporary file */
815 hFile[1] = CreateFile (szFileName[1], GENERIC_WRITE, 0, &sa,
816 TRUNCATE_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL);
817
818 if (hFile[1] == INVALID_HANDLE_VALUE)
819 {
820 ConErrResPrintf(STRING_CMD_ERROR2);
821 return;
822 }
823
824 SetStdHandle (STD_OUTPUT_HANDLE, hFile[1]);
825
826 DoCommand (s);
827
828 /* close stdout file */
829 SetStdHandle (STD_OUTPUT_HANDLE, hOldConOut);
830 if ((hFile[1] != INVALID_HANDLE_VALUE) && (hFile[1] != hOldConOut))
831 {
832 CloseHandle (hFile[1]);
833 hFile[1] = INVALID_HANDLE_VALUE;
834 }
835
836 /* close old stdin file */
837 SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
838 if ((hFile[0] != INVALID_HANDLE_VALUE) && (hFile[0] != hOldConIn))
839 {
840 /* delete old stdin file, if it is a real file */
841 CloseHandle (hFile[0]);
842 hFile[0] = INVALID_HANDLE_VALUE;
843 DeleteFile (szFileName[0]);
844 *szFileName[0] = _T('\0');
845 }
846
847 /* copy stdout file name to stdin file name */
848 _tcscpy (szFileName[0], szFileName[1]);
849 *szFileName[1] = _T('\0');
850
851 /* we need make sure the LastError msg is zero before calling CreateFile */
852 SetLastError(0);
853
854 /* open new stdin file */
855 hFile[0] = CreateFile (szFileName[0], GENERIC_READ, 0, &sa,
856 OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL);
857 SetStdHandle (STD_INPUT_HANDLE, hFile[0]);
858
859 s = s + _tcslen (s) + 1;
860 }
861
862 /* Now set up the end conditions... */
863 /* redirect STDOUT */
864 if (out[0])
865 {
866 /* Final output to here */
867 HANDLE hFile;
868 SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
869
870 /* we need make sure the LastError msg is zero before calling CreateFile */
871 SetLastError(0);
872
873 hFile = CreateFile (out, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, &sa,
874 (nRedirFlags & OUTPUT_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS,
875 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
876
877 if (hFile == INVALID_HANDLE_VALUE)
878 {
879 INT size = _tcslen(out)-1;
880
881 if (out[size] != _T(':'))
882 {
883 ConErrResPrintf(STRING_CMD_ERROR3, out);
884 return;
885 }
886
887 out[size]=_T('\0');
888 hFile = CreateFile (out, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, &sa,
889 (nRedirFlags & OUTPUT_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS,
890 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
891
892 if (hFile == INVALID_HANDLE_VALUE)
893 {
894 ConErrResPrintf(STRING_CMD_ERROR3, out);
895 return;
896 }
897
898 }
899
900 if (!SetStdHandle (STD_OUTPUT_HANDLE, hFile))
901 {
902 ConErrResPrintf(STRING_CMD_ERROR3, out);
903 return;
904 }
905
906 if (nRedirFlags & OUTPUT_APPEND)
907 {
908 LONG lHighPos = 0;
909
910 if (GetFileType (hFile) == FILE_TYPE_DISK)
911 SetFilePointer (hFile, 0, &lHighPos, FILE_END);
912 }
913 TRACE ("Output redirected to: %s\n", debugstr_aw(out));
914 }
915 else if (hOldConOut != INVALID_HANDLE_VALUE)
916 {
917 /* Restore original stdout */
918 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
919 SetStdHandle (STD_OUTPUT_HANDLE, hOldConOut);
920 if (hOldConOut != hOut)
921 CloseHandle (hOut);
922 hOldConOut = INVALID_HANDLE_VALUE;
923 }
924
925 /* redirect STDERR */
926 if (err[0])
927 {
928 /* Final output to here */
929 HANDLE hFile;
930 SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
931
932 if (!_tcscmp (err, out))
933 {
934 TRACE ("Stdout and stderr will use the same file!!\n");
935 DuplicateHandle (GetCurrentProcess (),
936 GetStdHandle (STD_OUTPUT_HANDLE),
937 GetCurrentProcess (),
938 &hFile, 0, TRUE, DUPLICATE_SAME_ACCESS);
939 }
940 else
941 {
942 hFile = CreateFile (err,
943 GENERIC_WRITE,
944 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
945 &sa,
946 (nRedirFlags & ERROR_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS,
947 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
948 NULL);
949 if (hFile == INVALID_HANDLE_VALUE)
950 {
951 ConErrResPrintf(STRING_CMD_ERROR3, err);
952 return;
953 }
954 }
955
956 if (!SetStdHandle (STD_ERROR_HANDLE, hFile))
957 {
958 ConErrResPrintf(STRING_CMD_ERROR3, err);
959 return;
960 }
961
962 if (nRedirFlags & ERROR_APPEND)
963 {
964 LONG lHighPos = 0;
965
966 if (GetFileType (hFile) == FILE_TYPE_DISK)
967 SetFilePointer (hFile, 0, &lHighPos, FILE_END);
968 }
969 TRACE ("Error redirected to: %s\n", debugstr_aw(err));
970 }
971 else if (hOldConErr != INVALID_HANDLE_VALUE)
972 {
973 /* Restore original stderr */
974 HANDLE hErr = GetStdHandle (STD_ERROR_HANDLE);
975 SetStdHandle (STD_ERROR_HANDLE, hOldConErr);
976 if (hOldConErr != hErr)
977 CloseHandle (hErr);
978 hOldConErr = INVALID_HANDLE_VALUE;
979 }
980
981 if(bc)
982 bNewBatch = FALSE;
983 #endif
984
985 /* process final command */
986 DoCommand (s);
987
988 #ifdef FEATURE_REDIRECTION
989 if(bNewBatch && bc)
990 AddBatchRedirection(in, out, err);
991 /* close old stdin file */
992 #if 0 /* buggy implementation */
993 SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
994 if ((hFile[0] != INVALID_HANDLE_VALUE) &&
995 (hFile[0] != hOldConIn))
996 {
997 /* delete old stdin file, if it is a real file */
998 CloseHandle (hFile[0]);
999 hFile[0] = INVALID_HANDLE_VALUE;
1000 DeleteFile (szFileName[0]);
1001 *szFileName[0] = _T('\0');
1002 }
1003
1004 /* Restore original STDIN */
1005 if (hOldConIn != INVALID_HANDLE_VALUE)
1006 {
1007 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1008 SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
1009 if (hOldConIn != hIn)
1010 CloseHandle (hIn);
1011 hOldConIn = INVALID_HANDLE_VALUE;
1012 }
1013 else
1014 {
1015 WARN ("Can't restore STDIN! Is invalid!!\n", out);
1016 }
1017 #endif /* buggy implementation */
1018
1019
1020 if (hOldConIn != INVALID_HANDLE_VALUE)
1021 {
1022 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
1023 SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
1024 if (hIn == INVALID_HANDLE_VALUE)
1025 {
1026 WARN ("Previous STDIN is invalid!!\n");
1027 }
1028 else
1029 {
1030 if (GetFileType (hIn) == FILE_TYPE_DISK)
1031 {
1032 if (hFile[0] == hIn)
1033 {
1034 CloseHandle (hFile[0]);
1035 hFile[0] = INVALID_HANDLE_VALUE;
1036 DeleteFile (szFileName[0]);
1037 *szFileName[0] = _T('\0');
1038 }
1039 else
1040 {
1041 WARN ("hFile[0] and hIn dont match!!!\n");
1042 }
1043 }
1044 }
1045 }
1046
1047
1048 /* Restore original STDOUT */
1049 if (hOldConOut != INVALID_HANDLE_VALUE)
1050 {
1051 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1052 SetStdHandle (STD_OUTPUT_HANDLE, hOldConOut);
1053 if (hOldConOut != hOut)
1054 CloseHandle (hOut);
1055 hOldConOut = INVALID_HANDLE_VALUE;
1056 }
1057
1058 /* Restore original STDERR */
1059 if (hOldConErr != INVALID_HANDLE_VALUE)
1060 {
1061 HANDLE hErr = GetStdHandle (STD_ERROR_HANDLE);
1062 SetStdHandle (STD_ERROR_HANDLE, hOldConErr);
1063 if (hOldConErr != hErr)
1064 CloseHandle (hErr);
1065 hOldConErr = INVALID_HANDLE_VALUE;
1066 }
1067 #endif /* FEATURE_REDIRECTION */
1068 }
1069
1070 BOOL
1071 GrowIfNecessary_dbg ( UINT needed, LPTSTR* ret, UINT* retlen, const char *file, int line )
1072 {
1073 if ( *ret && needed < *retlen )
1074 return TRUE;
1075 *retlen = needed;
1076 if ( *ret )
1077 cmd_free ( *ret );
1078 #ifdef _DEBUG_MEM
1079 *ret = (LPTSTR)cmd_alloc_dbg ( *retlen * sizeof(TCHAR), file, line );
1080 #else
1081 *ret = (LPTSTR)cmd_alloc ( *retlen * sizeof(TCHAR) );
1082 #endif
1083 if ( !*ret )
1084 SetLastError ( ERROR_OUTOFMEMORY );
1085 return *ret != NULL;
1086 }
1087 #define GrowIfNecessary(x, y, z) GrowIfNecessary_dbg(x, y, z, __FILE__, __LINE__)
1088
1089 LPCTSTR
1090 GetEnvVarOrSpecial ( LPCTSTR varName )
1091 {
1092 static LPTSTR ret = NULL;
1093 static UINT retlen = 0;
1094 UINT size;
1095 TCHAR varNameFixed[MAX_PATH];
1096 TCHAR ReturnValue[MAX_PATH];
1097 LPTSTR position;
1098 LPTSTR Token;
1099 SIZE_T i = 0;
1100 INT StringPart[1] = {0};
1101
1102 position = _tcsstr(varName, _T(":~"));
1103 if (position)
1104 _tcsncpy(varNameFixed, varName, (int) (position - varName));
1105 else
1106 _tcscpy(varNameFixed, varName);
1107
1108 size = GetEnvironmentVariable ( varNameFixed, ret, retlen );
1109 if ( size > retlen )
1110 {
1111 if ( !GrowIfNecessary ( size, &ret, &retlen ) )
1112 return NULL;
1113 size = GetEnvironmentVariable ( varNameFixed, ret, retlen );
1114 }
1115 if ( size )
1116 {
1117 if (position)
1118 {
1119 position += 2;
1120 if (_tcschr(position, _T(',')) != NULL)
1121 {
1122 Token = _tcstok(position, _T(","));
1123 while ((Token != NULL) && (i < 2))
1124 {
1125 StringPart[i] = _ttoi(Token);
1126 i++;
1127 Token = _tcstok (NULL, _T(","));
1128 }
1129 if (i > 0)
1130 {
1131 if (StringPart[1] < 0)
1132 StringPart[1] = _tcslen(ret + StringPart[0]) + StringPart[1];
1133 _tcsncpy(ReturnValue, ret + StringPart[0], StringPart[1]);
1134 _tcscpy(ret, ReturnValue);
1135 }
1136 }
1137 return ret;
1138 }
1139 else
1140 return ret;
1141 }
1142
1143 /* env var doesn't exist, look for a "special" one */
1144 /* %CD% */
1145 if (_tcsicmp(varNameFixed,_T("cd")) ==0)
1146 {
1147 size = GetCurrentDirectory ( retlen, ret );
1148 if ( size > retlen )
1149 {
1150 if ( !GrowIfNecessary ( size, &ret, &retlen ) )
1151 return NULL;
1152 size = GetCurrentDirectory ( retlen, ret );
1153 }
1154 if ( !size )
1155 return NULL;
1156 return ret;
1157 }
1158 /* %TIME% */
1159 else if (_tcsicmp(varNameFixed,_T("time")) ==0)
1160 {
1161 SYSTEMTIME t;
1162 if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
1163 return NULL;
1164 GetSystemTime(&t);
1165 _sntprintf ( ret, retlen, _T("%02d%c%02d%c%02d%c%02d"),
1166 t.wHour, cTimeSeparator, t.wMinute, cTimeSeparator,
1167 t.wSecond, cDecimalSeparator, t.wMilliseconds / 10);
1168 return ret;
1169 }
1170 /* %DATE% */
1171 else if (_tcsicmp(varNameFixed,_T("date")) ==0)
1172 {
1173
1174 if ( !GrowIfNecessary ( GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, NULL, 0), &ret, &retlen ) )
1175 return NULL;
1176
1177 size = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, ret, retlen);
1178
1179 if ( !size )
1180 return NULL;
1181 return ret;
1182 }
1183
1184 /* %RANDOM% */
1185 else if (_tcsicmp(varNameFixed,_T("random")) ==0)
1186 {
1187 if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
1188 return NULL;
1189 /* Get random number */
1190 _itot(rand(),ret,10);
1191 return ret;
1192 }
1193
1194 /* %CMDCMDLINE% */
1195 else if (_tcsicmp(varNameFixed,_T("cmdcmdline")) ==0)
1196 {
1197 return GetCommandLine();
1198 }
1199
1200 /* %CMDEXTVERSION% */
1201 else if (_tcsicmp(varNameFixed,_T("cmdextversion")) ==0)
1202 {
1203 if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
1204 return NULL;
1205 /* Set version number to 2 */
1206 _itot(2,ret,10);
1207 return ret;
1208 }
1209
1210 /* %ERRORLEVEL% */
1211 else if (_tcsicmp(varNameFixed,_T("errorlevel")) ==0)
1212 {
1213 if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
1214 return NULL;
1215 _itot(nErrorLevel,ret,10);
1216 return ret;
1217 }
1218
1219 GrowIfNecessary(_tcslen(varNameFixed) + 3, &ret, &retlen);
1220 _stprintf(ret,_T("%%%s%%"),varNameFixed);
1221 return ret; /* not found - return orginal string */
1222 }
1223
1224 LPCTSTR
1225 GetParsedEnvVar ( LPCTSTR varName, UINT* varNameLen, BOOL ModeSetA )
1226 {
1227 static LPTSTR ret = NULL;
1228 static UINT retlen = 0;
1229 LPTSTR p, tmp;
1230 UINT size;
1231 TCHAR c;
1232
1233 if ( varNameLen )
1234 *varNameLen = 0;
1235 SetLastError(0);
1236 c = *varName;
1237
1238 if ( (*varName != '!') && (*varName++ != '%') )
1239 return NULL;
1240
1241 if (c == _T('!'))
1242 varName++;
1243
1244 switch ( *varName )
1245 {
1246 case _T('~'):
1247 varName++;
1248 if (_tcsncicmp(varName, _T("dp0"), 3) == 0)
1249 {
1250 if ( varNameLen )
1251 *varNameLen = 5;
1252 return bc->BatchFilePath;
1253 }
1254 case _T('0'):
1255 case _T('1'):
1256 case _T('2'):
1257 case _T('3'):
1258 case _T('4'):
1259 case _T('5'):
1260 case _T('6'):
1261 case _T('7'):
1262 case _T('8'):
1263 case _T('9'):
1264 if ((tmp = FindArg (*varName - _T('0'))))
1265 {
1266 if ( varNameLen )
1267 *varNameLen = 2;
1268 if ( !*tmp )
1269 return _T("");
1270 if ( !GrowIfNecessary ( _tcslen(tmp)+1, &ret, &retlen ) )
1271 return NULL;
1272 _tcscpy ( ret, tmp );
1273 return ret;
1274 }
1275 if ( !GrowIfNecessary ( 3, &ret, &retlen ) )
1276 return NULL;
1277 ret[0] = _T('%');
1278 ret[1] = *varName;
1279 ret[2] = 0;
1280 if ( varNameLen )
1281 *varNameLen = 2;
1282 return ret;
1283
1284 case _T('*'):
1285 if(bc == NULL)
1286 {
1287 //
1288 // No batch file to see here, move along
1289 //
1290 if ( !GrowIfNecessary ( 3, &ret, &retlen ) )
1291 return NULL;
1292 ret[0] = _T('%');
1293 ret[1] = _T('*');
1294 ret[2] = 0;
1295 if ( varNameLen )
1296 *varNameLen = 2;
1297 return ret;
1298 }
1299
1300 //
1301 // Copy over the raw params(not including the batch file name
1302 //
1303 if ( !GrowIfNecessary ( _tcslen(bc->raw_params)+1, &ret, &retlen ) )
1304 return NULL;
1305 if ( varNameLen )
1306 *varNameLen = 2;
1307 _tcscpy ( ret, bc->raw_params );
1308 return ret;
1309
1310 case _T('%'):
1311 if ( !GrowIfNecessary ( 2, &ret, &retlen ) )
1312 return NULL;
1313 ret[0] = _T('%');
1314 ret[1] = 0;
1315 if ( varNameLen )
1316 *varNameLen = 2;
1317 return ret;
1318
1319 case _T('?'):
1320 /* TODO FIXME 10 is only max size for 32-bit */
1321 if ( !GrowIfNecessary ( 11, &ret, &retlen ) )
1322 return NULL;
1323 _sntprintf ( ret, retlen, _T("%u"), nErrorLevel);
1324 ret[retlen-1] = 0;
1325 if ( varNameLen )
1326 *varNameLen = 2;
1327 return ret;
1328 }
1329 if ( ModeSetA )
1330 {
1331 /* HACK for set/a */
1332 if ( !GrowIfNecessary ( 2, &ret, &retlen ) )
1333 return NULL;
1334 ret[0] = _T('%');
1335 ret[1] = 0;
1336 if ( varNameLen )
1337 *varNameLen = 1;
1338 return ret;
1339 }
1340 p = _tcschr ( varName, c );
1341 if ( !p )
1342 {
1343 SetLastError ( ERROR_INVALID_PARAMETER );
1344 return NULL;
1345 }
1346 size = p-varName;
1347 if ( varNameLen )
1348 *varNameLen = size + 2;
1349 p = alloca ( (size+1) * sizeof(TCHAR) );
1350 memmove ( p, varName, size * sizeof(TCHAR) );
1351 p[size] = 0;
1352 varName = p;
1353 return GetEnvVarOrSpecial ( varName );
1354 }
1355
1356
1357 /*
1358 * do the prompt/input/process loop
1359 *
1360 */
1361
1362 static INT
1363 ProcessInput (BOOL bFlag)
1364 {
1365 TCHAR commandline[CMDLINE_LENGTH];
1366 TCHAR readline[CMDLINE_LENGTH];
1367 LPTSTR ip;
1368 LPTSTR cp;
1369 LPCTSTR tmp;
1370 BOOL bEchoThisLine;
1371 BOOL bModeSetA;
1372 BOOL bIsBatch;
1373
1374 do
1375 {
1376 /* if no batch input then... */
1377 if (!(ip = ReadBatchLine (&bEchoThisLine)))
1378 {
1379 if (bFlag)
1380 return nErrorLevel;
1381
1382 ReadCommand (readline, CMDLINE_LENGTH);
1383 ip = readline;
1384 bEchoThisLine = FALSE;
1385 bIsBatch = FALSE;
1386 }
1387 else
1388 {
1389 bIsBatch = TRUE;
1390 }
1391
1392 /* skip leading blanks */
1393 while ( _istspace(*ip) )
1394 ++ip;
1395
1396 cp = commandline;
1397 bModeSetA = FALSE;
1398 while (*ip)
1399 {
1400 if ( (*ip == _T('%')) || (*ip == _T('!')) )
1401 {
1402 UINT envNameLen;
1403 LPCTSTR envVal = GetParsedEnvVar ( ip, &envNameLen, bModeSetA );
1404 if ( envVal )
1405 {
1406 ip += envNameLen;
1407 cp = _stpcpy ( cp, envVal );
1408 }
1409 }
1410
1411 if (*ip != _T('\0') && (_istcntrl (*ip)))
1412 *ip = _T(' ');
1413 *cp++ = *ip++;
1414
1415 /* HACK HACK HACK check whether bModeSetA needs to be toggled */
1416 *cp = 0;
1417 tmp = commandline;
1418 tmp += _tcsspn(tmp,_T(" \t"));
1419 /* first we find and skip and pre-redirections... */
1420 while (( tmp ) &&
1421 ( _tcschr(_T("<>"),*tmp)
1422 || !_tcsncmp(tmp,_T("1>"),2)
1423 || !_tcsncmp(tmp,_T("2>"),2) ))
1424 {
1425 if ( _istdigit(*tmp) )
1426 tmp += 2;
1427 else
1428 tmp++;
1429 tmp += _tcsspn(tmp,_T(" \t"));
1430 if ( *tmp == _T('\"') )
1431 {
1432 tmp = _tcschr(tmp+1,_T('\"'));
1433 if ( tmp )
1434 ++tmp;
1435 }
1436 else
1437 tmp = _tcspbrk(tmp,_T(" \t"));
1438 if ( tmp )
1439 tmp += _tcsspn(tmp,_T(" \t"));
1440 }
1441 /* we should now be pointing to the actual command
1442 * (if there is one yet)*/
1443 if ( tmp )
1444 {
1445 /* if we're currently substituting ( which is default )
1446 * check to see if we've parsed out a set/a. if so, we
1447 * need to disable substitution until we come across a
1448 * redirection */
1449 if ( !bModeSetA )
1450 {
1451 /* look for set /a */
1452 if ( !_tcsnicmp(tmp,_T("set"),3) )
1453 {
1454 tmp += 3;
1455 tmp += _tcsspn(tmp,_T(" \t"));
1456 if ( !_tcsnicmp(tmp,_T("/a"),2) )
1457 bModeSetA = TRUE;
1458 }
1459 }
1460 /* if we're not currently substituting, it means we're
1461 * already inside a set /a. now we need to look for
1462 * a redirection in order to turn redirection back on */
1463 else
1464 {
1465 /* look for redirector of some kind after the command */
1466 while ( (tmp = _tcspbrk ( tmp, _T("^<>|") )) )
1467 {
1468 if ( *tmp == _T('^') )
1469 {
1470 if ( _tcschr(_T("<>|&"), *++tmp ) && *tmp )
1471 ++tmp;
1472 }
1473 else
1474 {
1475 bModeSetA = FALSE;
1476 break;
1477 }
1478 }
1479 }
1480 }
1481 }
1482
1483 *cp = _T('\0');
1484
1485 /* JPP 19980807 */
1486 /* Echo batch file line */
1487 if (bEchoThisLine)
1488 {
1489 PrintPrompt ();
1490 ConOutPuts (commandline);
1491 }
1492
1493 if (!CheckCtrlBreak(BREAK_INPUT) && *commandline)
1494 {
1495 ParseCommandLine (commandline);
1496 if (bEcho && !bIgnoreEcho && (!bIsBatch || bEchoThisLine))
1497 ConOutChar ('\n');
1498 bIgnoreEcho = FALSE;
1499 }
1500 }
1501 while (!bCanExit || !bExit);
1502
1503 return nErrorLevel;
1504 }
1505
1506
1507 /*
1508 * control-break handler.
1509 */
1510 BOOL WINAPI BreakHandler (DWORD dwCtrlType)
1511 {
1512
1513 DWORD dwWritten;
1514 INPUT_RECORD rec;
1515 static BOOL SelfGenerated = FALSE;
1516
1517 if ((dwCtrlType != CTRL_C_EVENT) &&
1518 (dwCtrlType != CTRL_BREAK_EVENT))
1519 {
1520 return FALSE;
1521 }
1522 else
1523 {
1524 if(SelfGenerated)
1525 {
1526 SelfGenerated = FALSE;
1527 return TRUE;
1528 }
1529 }
1530
1531 if (bChildProcessRunning == TRUE)
1532 {
1533 SelfGenerated = TRUE;
1534 GenerateConsoleCtrlEvent (dwCtrlType, 0);
1535 return TRUE;
1536 }
1537
1538
1539 rec.EventType = KEY_EVENT;
1540 rec.Event.KeyEvent.bKeyDown = TRUE;
1541 rec.Event.KeyEvent.wRepeatCount = 1;
1542 rec.Event.KeyEvent.wVirtualKeyCode = _T('C');
1543 rec.Event.KeyEvent.wVirtualScanCode = _T('C') - 35;
1544 rec.Event.KeyEvent.uChar.AsciiChar = _T('C');
1545 rec.Event.KeyEvent.uChar.UnicodeChar = _T('C');
1546 rec.Event.KeyEvent.dwControlKeyState = RIGHT_CTRL_PRESSED;
1547
1548 WriteConsoleInput(
1549 hIn,
1550 &rec,
1551 1,
1552 &dwWritten);
1553
1554 bCtrlBreak = TRUE;
1555 /* FIXME: Handle batch files */
1556
1557 //ConOutPrintf(_T("^C"));
1558
1559
1560 return TRUE;
1561 }
1562
1563
1564 VOID AddBreakHandler (VOID)
1565 {
1566 SetConsoleCtrlHandler ((PHANDLER_ROUTINE)BreakHandler, TRUE);
1567 }
1568
1569
1570 VOID RemoveBreakHandler (VOID)
1571 {
1572 SetConsoleCtrlHandler ((PHANDLER_ROUTINE)BreakHandler, FALSE);
1573 }
1574
1575
1576 /*
1577 * show commands and options that are available.
1578 *
1579 */
1580 #if 0
1581 static VOID
1582 ShowCommands (VOID)
1583 {
1584 /* print command list */
1585 ConOutResPuts(STRING_CMD_HELP1);
1586 PrintCommandList();
1587
1588 /* print feature list */
1589 ConOutResPuts(STRING_CMD_HELP2);
1590
1591 #ifdef FEATURE_ALIASES
1592 ConOutResPuts(STRING_CMD_HELP3);
1593 #endif
1594 #ifdef FEATURE_HISTORY
1595 ConOutResPuts(STRING_CMD_HELP4);
1596 #endif
1597 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
1598 ConOutResPuts(STRING_CMD_HELP5);
1599 #endif
1600 #ifdef FEATURE_DIRECTORY_STACK
1601 ConOutResPuts(STRING_CMD_HELP6);
1602 #endif
1603 #ifdef FEATURE_REDIRECTION
1604 ConOutResPuts(STRING_CMD_HELP7);
1605 #endif
1606 ConOutChar(_T('\n'));
1607 }
1608 #endif
1609
1610 static VOID
1611 ExecuteAutoRunFile (VOID)
1612 {
1613 TCHAR autorun[MAX_PATH];
1614 DWORD len = MAX_PATH;
1615 HKEY hkey;
1616
1617 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1618 _T("SOFTWARE\\Microsoft\\Command Processor"),
1619 0,
1620 KEY_READ,
1621 &hkey ) == ERROR_SUCCESS)
1622 {
1623 if(RegQueryValueEx(hkey,
1624 _T("AutoRun"),
1625 0,
1626 0,
1627 (LPBYTE)autorun,
1628 &len) == ERROR_SUCCESS)
1629 {
1630 ParseCommandLine (autorun);
1631 }
1632 }
1633
1634 RegCloseKey(hkey);
1635 }
1636
1637 /*
1638 * set up global initializations and process parameters
1639 *
1640 * argc - number of parameters to command.com
1641 * argv - command-line parameters
1642 *
1643 */
1644 static VOID
1645 Initialize (int argc, const TCHAR* argv[])
1646 {
1647 TCHAR commandline[CMDLINE_LENGTH];
1648 TCHAR ModuleName[_MAX_PATH + 1];
1649 INT i;
1650 TCHAR lpBuffer[2];
1651
1652 //INT len;
1653 //TCHAR *ptr, *cmdLine;
1654
1655 /* get version information */
1656 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1657 GetVersionEx (&osvi);
1658
1659 /* Some people like to run ReactOS cmd.exe on Win98, it helps in the
1660 * build process. So don't link implicitly against ntdll.dll, load it
1661 * dynamically instead */
1662
1663 if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
1664 {
1665 /* ntdll is always present on NT */
1666 NtDllModule = GetModuleHandle(TEXT("ntdll.dll"));
1667 }
1668 else
1669 {
1670 /* not all 9x versions have a ntdll.dll, try to load it */
1671 NtDllModule = LoadLibrary(TEXT("ntdll.dll"));
1672 }
1673
1674 if (NtDllModule != NULL)
1675 {
1676 NtQueryInformationProcessPtr = (NtQueryInformationProcessProc)GetProcAddress(NtDllModule, "NtQueryInformationProcess");
1677 NtReadVirtualMemoryPtr = (NtReadVirtualMemoryProc)GetProcAddress(NtDllModule, "NtReadVirtualMemory");
1678 }
1679
1680
1681 TRACE ("[command args:\n");
1682 for (i = 0; i < argc; i++)
1683 {
1684 TRACE ("%d. %s\n", i, debugstr_aw(argv[i]));
1685 }
1686 TRACE ("]\n");
1687
1688 InitLocale ();
1689
1690 /* get default input and output console handles */
1691 hOut = GetStdHandle (STD_OUTPUT_HANDLE);
1692 hIn = GetStdHandle (STD_INPUT_HANDLE);
1693
1694 /* Set EnvironmentVariable PROMPT if it does not exists any env value.
1695 for you can change the EnvirommentVariable for prompt before cmd start
1696 this patch are not 100% right, if it does not exists a PROMPT value cmd should use
1697 $P$G as defualt not set EnvirommentVariable PROMPT to $P$G if it does not exists */
1698 if (GetEnvironmentVariable(_T("PROMPT"),lpBuffer, sizeof(lpBuffer) / sizeof(lpBuffer[0])) == 0)
1699 SetEnvironmentVariable (_T("PROMPT"), _T("$P$G"));
1700
1701
1702 if (argc >= 2 && !_tcsncmp (argv[1], _T("/?"), 2))
1703 {
1704 ConOutResPaging(TRUE,STRING_CMD_HELP8);
1705 cmd_exit(0);
1706 }
1707 SetConsoleMode (hIn, ENABLE_PROCESSED_INPUT);
1708
1709 #ifdef INCLUDE_CMD_CHDIR
1710 InitLastPath ();
1711 #endif
1712
1713 if (argc >= 2)
1714 {
1715 for (i = 1; i < argc; i++)
1716 {
1717 if (!_tcsicmp (argv[i], _T("/p")))
1718 {
1719 if (!IsExistingFile (_T("\\autoexec.bat")))
1720 {
1721 #ifdef INCLUDE_CMD_DATE
1722 cmd_date (_T(""), _T(""));
1723 #endif
1724 #ifdef INCLUDE_CMD_TIME
1725 cmd_time (_T(""), _T(""));
1726 #endif
1727 }
1728 else
1729 {
1730 ParseCommandLine (_T("\\autoexec.bat"));
1731 }
1732 bCanExit = FALSE;
1733 }
1734 else if (!_tcsicmp (argv[i], _T("/c")))
1735 {
1736 /* This just runs a program and exits */
1737 ++i;
1738 if (i < argc)
1739 {
1740 _tcscpy (commandline, argv[i]);
1741 while (++i < argc)
1742 {
1743 _tcscat (commandline, _T(" "));
1744 _tcscat (commandline, argv[i]);
1745 }
1746
1747 ParseCommandLine(commandline);
1748 cmd_exit (ProcessInput (TRUE));
1749 }
1750 else
1751 {
1752 cmd_exit (0);
1753 }
1754 }
1755 else if (!_tcsicmp (argv[i], _T("/k")))
1756 {
1757 /* This just runs a program and remains */
1758 ++i;
1759 if (i < argc)
1760 {
1761 _tcscpy (commandline, _T("\""));
1762 _tcscat (commandline, argv[i]);
1763 _tcscat (commandline, _T("\""));
1764 while (++i < argc)
1765 {
1766 _tcscat (commandline, _T(" "));
1767 _tcscat (commandline, argv[i]);
1768 }
1769 ParseCommandLine(commandline);
1770 }
1771 }
1772 #ifdef INCLUDE_CMD_COLOR
1773 else if (!_tcsnicmp (argv[i], _T("/t:"), 3))
1774 {
1775 /* process /t (color) argument */
1776 wDefColor = (WORD)_tcstoul (&argv[i][3], NULL, 16);
1777 wColor = wDefColor;
1778 SetScreenColor (wColor, TRUE);
1779 }
1780 #endif
1781 }
1782 }
1783 else
1784 {
1785 /* Display a simple version string */
1786 ConOutPrintf(_T("ReactOS Operating System [Version %s-%s]\n"),
1787 _T(KERNEL_RELEASE_STR),
1788 _T(KERNEL_VERSION_BUILD_STR));
1789
1790 ConOutPuts (_T("(C) Copyright 1998-") _T(COPYRIGHT_YEAR) _T(" ReactOS Team.\n"));
1791 }
1792
1793 ExecuteAutoRunFile ();
1794
1795 #ifdef FEATURE_DIR_STACK
1796 /* initialize directory stack */
1797 InitDirectoryStack ();
1798 #endif
1799
1800
1801 #ifdef FEATURE_HISTORY
1802 /*initialize history*/
1803 InitHistory();
1804 #endif
1805
1806 /* Set COMSPEC environment variable */
1807 if (0 != GetModuleFileName (NULL, ModuleName, _MAX_PATH + 1))
1808 {
1809 ModuleName[_MAX_PATH] = _T('\0');
1810 SetEnvironmentVariable (_T("COMSPEC"), ModuleName);
1811 }
1812
1813 /* add ctrl break handler */
1814 AddBreakHandler ();
1815 }
1816
1817
1818 static VOID Cleanup (int argc, const TCHAR *argv[])
1819 {
1820 /* run cmdexit.bat */
1821 if (IsExistingFile (_T("cmdexit.bat")))
1822 {
1823 ConErrResPuts(STRING_CMD_ERROR5);
1824
1825 ParseCommandLine (_T("cmdexit.bat"));
1826 }
1827 else if (IsExistingFile (_T("\\cmdexit.bat")))
1828 {
1829 ConErrResPuts (STRING_CMD_ERROR5);
1830 ParseCommandLine (_T("\\cmdexit.bat"));
1831 }
1832
1833 #ifdef FEATURE_DIECTORY_STACK
1834 /* destroy directory stack */
1835 DestroyDirectoryStack ();
1836 #endif
1837
1838 #ifdef INCLUDE_CMD_CHDIR
1839 FreeLastPath ();
1840 #endif
1841
1842 #ifdef FEATURE_HISTORY
1843 CleanHistory();
1844 #endif
1845
1846
1847 /* remove ctrl break handler */
1848 RemoveBreakHandler ();
1849 SetConsoleMode( GetStdHandle( STD_INPUT_HANDLE ),
1850 ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT );
1851
1852 if (NtDllModule != NULL)
1853 {
1854 FreeLibrary(NtDllModule);
1855 }
1856 }
1857
1858 /*
1859 * main function
1860 */
1861 int cmd_main (int argc, const TCHAR *argv[])
1862 {
1863 TCHAR startPath[MAX_PATH];
1864 CONSOLE_SCREEN_BUFFER_INFO Info;
1865 INT nExitCode;
1866
1867 GetCurrentDirectory(MAX_PATH,startPath);
1868 _tchdir(startPath);
1869
1870 SetFileApisToOEM();
1871 InputCodePage= 0;
1872 OutputCodePage = 0;
1873
1874 hConsole = CreateFile(_T("CONOUT$"), GENERIC_READ|GENERIC_WRITE,
1875 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
1876 OPEN_EXISTING, 0, NULL);
1877 if (GetConsoleScreenBufferInfo(hConsole, &Info) == FALSE)
1878 {
1879 ConErrFormatMessage(GetLastError());
1880 return(1);
1881 }
1882 wColor = Info.wAttributes;
1883 wDefColor = wColor;
1884
1885 InputCodePage= GetConsoleCP();
1886 OutputCodePage = GetConsoleOutputCP();
1887 CMD_ModuleHandle = GetModuleHandle(NULL);
1888
1889 /* check switches on command-line */
1890 Initialize(argc, argv);
1891
1892 /* call prompt routine */
1893 nExitCode = ProcessInput(FALSE);
1894
1895 /* do the cleanup */
1896 Cleanup(argc, argv);
1897
1898 cmd_exit(nExitCode);
1899 return(nExitCode);
1900 }
1901
1902 /* EOF */