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