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