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