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