Fixed bugs in batch file execution.
[reactos.git] / rosapps / 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
108 #include "config.h"
109
110 #include <windows.h>
111 #include <tchar.h>
112 #include <string.h>
113 #include <stdlib.h>
114 #include <ctype.h>
115 #include <stdio.h>
116
117 #include "cmd.h"
118 #include "batch.h"
119
120
121 #define CMDLINE_LENGTH 512
122
123
124 BOOL bExit = FALSE; /* indicates EXIT was typed */
125 BOOL bCanExit = TRUE; /* indicates if this shell is exitable */
126 BOOL bCtrlBreak = FALSE; /* Ctrl-Break or Ctrl-C hit */
127 BOOL bIgnoreEcho = FALSE; /* Ignore 'newline' before 'cls' */
128 INT nErrorLevel = 0; /* Errorlevel of last launched external program */
129 OSVERSIONINFO osvi;
130 HANDLE hIn;
131 HANDLE hOut;
132
133 #ifdef INCLUDE_CMD_COLOR
134 WORD wColor = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN; /* current color */
135 WORD wDefColor = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN; /* default color */
136 #endif
137
138
139 extern COMMAND cmds[]; /* The internal command table */
140
141
142 /*
143 * is character a delimeter when used on first word?
144 *
145 */
146
147 static BOOL IsDelimiter (TCHAR c)
148 {
149 return (c == _T('/') || c == _T('=') || c == _T('\0') || _istspace (c));
150 }
151
152
153 /*
154 * This command (in first) was not found in the command table
155 *
156 * first - first word on command line
157 * rest - rest of command line
158 */
159
160 static VOID
161 Execute (LPTSTR first, LPTSTR rest)
162 {
163 TCHAR szFullName[MAX_PATH];
164
165 /* check for a drive change */
166 if (!_tcscmp (first + 1, _T(":")) && _istalpha (*first))
167 {
168 TCHAR szPath[MAX_PATH];
169
170 _tcscpy (szPath, _T("A:"));
171 szPath[0] = _totupper (*first);
172 SetCurrentDirectory (szPath);
173 GetCurrentDirectory (MAX_PATH, szPath);
174 if (szPath[0] != (TCHAR)_totupper (*first))
175 ConErrPuts (INVALIDDRIVE);
176
177 return;
178 }
179
180 /* get the PATH environment variable and parse it */
181 /* search the PATH environment variable for the binary */
182 if (!SearchForExecutable (first, szFullName))
183 {
184 error_bad_command ();
185 return;
186 }
187
188 /* check if this is a .BAT or .CMD file */
189 if (!_tcsicmp (_tcsrchr (szFullName, _T('.')), _T(".bat")) ||
190 !_tcsicmp (_tcsrchr (szFullName, _T('.')), _T(".cmd")))
191 {
192 #ifdef _DEBUG
193 DebugPrintf ("[BATCH: %s %s]\n", szFullName, rest);
194 #endif
195 Batch (szFullName, first, rest);
196 }
197 else
198 {
199 /* exec the program */
200 #ifndef __REACTOS__
201 TCHAR szFullCmdLine [1024];
202 #endif
203 PROCESS_INFORMATION prci;
204 STARTUPINFO stui;
205
206 #ifdef _DEBUG
207 DebugPrintf ("[EXEC: %s %s]\n", szFullName, rest);
208 #endif
209 #ifndef __REACTOS__
210 /* build command line for CreateProcess() */
211 _tcscpy (szFullCmdLine, szFullName);
212 _tcscat (szFullCmdLine, _T(" "));
213 _tcscat (szFullCmdLine, rest);
214 #endif
215
216 /* fill startup info */
217 memset (&stui, 0, sizeof (STARTUPINFO));
218 stui.cb = sizeof (STARTUPINFO);
219 stui.dwFlags = STARTF_USESHOWWINDOW;
220 stui.wShowWindow = SW_SHOWDEFAULT;
221
222 #ifndef __REACTOS__
223 if (CreateProcess (NULL, szFullCmdLine, NULL, NULL, FALSE,
224 0, NULL, NULL, &stui, &prci))
225 #else
226 if (CreateProcess (szFullName, rest, NULL, NULL, FALSE,
227 0, NULL, NULL, &stui, &prci))
228 #endif
229 {
230 DWORD dwExitCode;
231 WaitForSingleObject (prci.hProcess, INFINITE);
232 GetExitCodeProcess (prci.hProcess, &dwExitCode);
233 nErrorLevel = (INT)dwExitCode;
234 CloseHandle (prci.hThread);
235 CloseHandle (prci.hProcess);
236 }
237 else
238 {
239 ErrorMessage (GetLastError (),
240 "Error executing CreateProcess()!!\n");
241 }
242 }
243 }
244
245
246 /*
247 * look through the internal commands and determine whether or not this
248 * command is one of them. If it is, call the command. If not, call
249 * execute to run it as an external program.
250 *
251 * line - the command line of the program to run
252 *
253 */
254
255 static VOID
256 DoCommand (LPTSTR line)
257 {
258 TCHAR com[MAX_PATH]; /* the first word in the command */
259 LPTSTR cp = com;
260 LPTSTR cstart;
261 LPTSTR rest = line; /* pointer to the rest of the command line */
262 INT cl;
263 LPCOMMAND cmdptr;
264
265 /* Skip over initial white space */
266 while (isspace (*rest))
267 rest++;
268
269 cstart = rest;
270
271 /* Anything to do ? */
272 if (*rest)
273 {
274 /* Copy over 1st word as lower case */
275 while (!IsDelimiter (*rest))
276 *cp++ = _totlower (*rest++);
277
278 /* Terminate first word */
279 *cp = _T('\0');
280
281 /* Skip over whitespace to rest of line */
282 while (_istspace (*rest))
283 rest++;
284
285 /* Scan internal command table */
286 for (cmdptr = cmds;; cmdptr++)
287 {
288 /* If end of table execute ext cmd */
289 if (cmdptr->name == NULL)
290 {
291 Execute (com, rest);
292 break;
293 }
294
295 if (!_tcscmp (com, cmdptr->name))
296 {
297 cmdptr->func (com, rest);
298 break;
299 }
300
301 /* The following code handles the case of commands like CD which
302 * are recognised even when the command name and parameter are
303 * not space separated.
304 *
305 * e.g dir..
306 * cd\freda
307 */
308
309 /* Get length of command name */
310 cl = _tcslen (cmdptr->name);
311
312 if ((cmdptr->flags & CMD_SPECIAL) &&
313 (!_tcsncmp (cmdptr->name, com, cl)) &&
314 (_tcschr (_T("\\.-"), *(com + cl))))
315 {
316 /* OK its one of the specials...*/
317
318 /* Terminate first word properly */
319 com[cl] = _T('\0');
320
321 /* Call with new rest */
322 cmdptr->func (com, cstart + cl);
323 break;
324 }
325 }
326 }
327 }
328
329
330 /*
331 * process the command line and execute the appropriate functions
332 * full input/output redirection and piping are supported
333 */
334
335 VOID ParseCommandLine (LPTSTR s)
336 {
337 #ifdef FEATURE_REDIRECTION
338 TCHAR in[CMDLINE_LENGTH] = "";
339 TCHAR out[CMDLINE_LENGTH] = "";
340 TCHAR err[CMDLINE_LENGTH] = "";
341 TCHAR szTempPath[MAX_PATH] = _T(".\\");
342 TCHAR szFileName[2][MAX_PATH] = {"", ""};
343 HANDLE hFile[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
344 LPTSTR t = NULL;
345 INT num = 0;
346 INT nRedirFlags = 0;
347
348 HANDLE hOldConIn;
349 HANDLE hOldConOut;
350 HANDLE hOldConErr;
351 #endif /* FEATURE_REDIRECTION */
352
353 #ifdef _DEBUG
354 DebugPrintf ("ParseCommandLine: (\'%s\')]\n", s);
355 #endif /* _DEBUG */
356
357 #ifdef FEATURE_ALIASES
358 /* expand all aliases */
359 ExpandAlias (s, CMDLINE_LENGTH);
360 #endif /* FEATURE_ALIAS */
361
362 #ifdef FEATURE_REDIRECTION
363 /* find the temp path to store temporary files */
364 GetTempPath (MAX_PATH, szTempPath);
365 if (szTempPath[_tcslen (szTempPath) - 1] != _T('\\'))
366 _tcscat (szTempPath, _T("\\"));
367
368 /* get the redirections from the command line */
369 num = GetRedirection (s, in, out, err, &nRedirFlags);
370
371 /* more efficient, but do we really need to do this? */
372 for (t = in; _istspace (*t); t++)
373 ;
374 _tcscpy (in, t);
375
376 for (t = out; _istspace (*t); t++)
377 ;
378 _tcscpy (out, t);
379
380 for (t = err; _istspace (*t); t++)
381 ;
382 _tcscpy (err, t);
383
384 /* Set up the initial conditions ... */
385 /* preserve STDIN, STDOUT and STDERR handles */
386 hOldConIn = GetStdHandle (STD_INPUT_HANDLE);
387 hOldConOut = GetStdHandle (STD_OUTPUT_HANDLE);
388 hOldConErr = GetStdHandle (STD_ERROR_HANDLE);
389
390 /* redirect STDIN */
391 if (in[0])
392 {
393 HANDLE hFile;
394
395 hFile = CreateFile (in, GENERIC_READ, 0, NULL, OPEN_EXISTING,
396 FILE_ATTRIBUTE_NORMAL, NULL);
397 if (hFile == INVALID_HANDLE_VALUE)
398 {
399 ConErrPrintf ("Can't redirect input from file %s\n", in);
400 return;
401 }
402
403 if (!SetStdHandle (STD_INPUT_HANDLE, hFile))
404 {
405 ConErrPrintf ("Can't redirect input from file %s\n", in);
406 return;
407 }
408 #ifdef _DEBUG
409 DebugPrintf (_T("Input redirected from: %s\n"), in);
410 #endif
411 }
412
413 /* Now do all but the last pipe command */
414 *szFileName[0] = '\0';
415 hFile[0] = INVALID_HANDLE_VALUE;
416
417 while (num-- > 1)
418 {
419 /* Create unique temporary file name */
420 GetTempFileName (szTempPath, "CMD", 0, szFileName[1]);
421
422 /* Set current stdout to temporary file */
423 hFile[1] = CreateFile (szFileName[1], GENERIC_WRITE, 0, NULL,
424 TRUNCATE_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL);
425 SetStdHandle (STD_OUTPUT_HANDLE, hFile[1]);
426
427 DoCommand (s);
428
429 /* close stdout file */
430 SetStdHandle (STD_OUTPUT_HANDLE, hOldConOut);
431 if ((hFile[1] != INVALID_HANDLE_VALUE) && (hFile[1] != hOldConOut))
432 {
433 CloseHandle (hFile[1]);
434 hFile[1] = INVALID_HANDLE_VALUE;
435 }
436
437 /* close old stdin file */
438 SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
439 if ((hFile[0] != INVALID_HANDLE_VALUE) && (hFile[0] != hOldConIn))
440 {
441 /* delete old stdin file, if it is a real file */
442 CloseHandle (hFile[0]);
443 hFile[0] = INVALID_HANDLE_VALUE;
444 DeleteFile (szFileName[0]);
445 *szFileName[0] = _T('\0');
446 }
447
448 /* copy stdout file name to stdin file name */
449 _tcscpy (szFileName[0], szFileName[1]);
450 *szFileName[1] = _T('\0');
451
452 /* open new stdin file */
453 hFile[0] = CreateFile (szFileName[0], GENERIC_READ, 0, NULL,
454 OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL);
455 SetStdHandle (STD_INPUT_HANDLE, hFile[0]);
456
457 s = s + _tcslen (s) + 1;
458 }
459
460 /* Now set up the end conditions... */
461 /* redirect STDOUT */
462 if (out[0])
463 {
464 /* Final output to here */
465 HANDLE hFile;
466
467 hFile = CreateFile (out, GENERIC_WRITE, 0, NULL,
468 (nRedirFlags & OUTPUT_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS,
469 FILE_ATTRIBUTE_NORMAL, NULL);
470 if (hFile == INVALID_HANDLE_VALUE)
471 {
472 ConErrPrintf ("Can't redirect to file %s\n", out);
473 return;
474 }
475
476 if (!SetStdHandle (STD_OUTPUT_HANDLE, hFile))
477 {
478 ConErrPrintf ("Can't redirect to file %s\n", out);
479 return;
480 }
481
482 if (nRedirFlags & OUTPUT_APPEND)
483 {
484 LONG lHighPos = 0;
485
486 if (GetFileType (hFile) == FILE_TYPE_DISK)
487 SetFilePointer (hFile, 0, &lHighPos, FILE_END);
488 }
489 #ifdef _DEBUG
490 DebugPrintf (_T("Output redirected to: %s\n"), out);
491 #endif
492 }
493 else if (hOldConOut != INVALID_HANDLE_VALUE)
494 {
495 /* Restore original stdout */
496 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
497 SetStdHandle (STD_OUTPUT_HANDLE, hOldConOut);
498 if (hOldConOut != hOut)
499 CloseHandle (hOut);
500 hOldConOut = INVALID_HANDLE_VALUE;
501 }
502
503 /* redirect STDERR */
504 if (err[0])
505 {
506 /* Final output to here */
507 HANDLE hFile;
508
509 if (!_tcscmp (err, out))
510 {
511 #ifdef _DEBUG
512 DebugPrintf (_T("Stdout and stderr will use the same file!!\n"));
513 #endif
514 DuplicateHandle (GetCurrentProcess (), GetStdHandle (STD_OUTPUT_HANDLE), GetCurrentProcess (),
515 &hFile, 0, TRUE, DUPLICATE_SAME_ACCESS);
516 }
517 else
518 {
519 hFile =
520 CreateFile (err, GENERIC_WRITE, 0, NULL,
521 (nRedirFlags & ERROR_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS,
522 FILE_ATTRIBUTE_NORMAL, NULL);
523 if (hFile == INVALID_HANDLE_VALUE)
524 {
525 ConErrPrintf ("Can't redirect to file %s\n", err);
526 return;
527 }
528 }
529 if (!SetStdHandle (STD_ERROR_HANDLE, hFile))
530 {
531 ConErrPrintf ("Can't redirect to file %s\n", err);
532 return;
533 }
534
535 if (nRedirFlags & ERROR_APPEND)
536 {
537 LONG lHighPos = 0;
538
539 if (GetFileType (hFile) == FILE_TYPE_DISK)
540 SetFilePointer (hFile, 0, &lHighPos, FILE_END);
541 }
542 #ifdef _DEBUG
543 DebugPrintf (_T("Error redirected to: %s\n"), err);
544 #endif
545 }
546 else if (hOldConErr != INVALID_HANDLE_VALUE)
547 {
548 /* Restore original stderr */
549 HANDLE hErr = GetStdHandle (STD_ERROR_HANDLE);
550 SetStdHandle (STD_ERROR_HANDLE, hOldConErr);
551 if (hOldConErr != hErr)
552 CloseHandle (hErr);
553 hOldConErr = INVALID_HANDLE_VALUE;
554 }
555 #endif
556
557 /* process final command */
558 DoCommand (s);
559
560 #ifdef FEATURE_REDIRECTION
561 /* close old stdin file */
562 #if 0 /* buggy implementation */
563 SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
564 if ((hFile[0] != INVALID_HANDLE_VALUE) &&
565 (hFile[0] != hOldConIn))
566 {
567 /* delete old stdin file, if it is a real file */
568 CloseHandle (hFile[0]);
569 hFile[0] = INVALID_HANDLE_VALUE;
570 DeleteFile (szFileName[0]);
571 *szFileName[0] = _T('\0');
572 }
573
574 /* Restore original STDIN */
575 if (hOldConIn != INVALID_HANDLE_VALUE)
576 {
577 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
578 SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
579 if (hOldConIn != hIn)
580 CloseHandle (hIn);
581 hOldConIn = INVALID_HANDLE_VALUE;
582 }
583 else
584 {
585 #ifdef _DEBUG
586 DebugPrintf (_T("Can't restore STDIN! Is invalid!!\n"), out);
587 #endif
588 }
589 #endif /* buggy implementation */
590
591
592 if (hOldConIn != INVALID_HANDLE_VALUE)
593 {
594 HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE);
595 SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
596 if (hIn == INVALID_HANDLE_VALUE)
597 {
598 #ifdef _DEBUG
599 DebugPrintf (_T("Previous STDIN is invalid!!\n"));
600 #endif
601 }
602 else
603 {
604 if (GetFileType (hIn) == FILE_TYPE_DISK)
605 {
606 if (hFile[0] == hIn)
607 {
608 CloseHandle (hFile[0]);
609 hFile[0] = INVALID_HANDLE_VALUE;
610 DeleteFile (szFileName[0]);
611 *szFileName[0] = _T('\0');
612 }
613 else
614 {
615 #ifdef _DEBUG
616 DebugPrintf (_T("hFile[0] and hIn dont match!!!\n"));
617 #endif
618
619 }
620 }
621 }
622 }
623
624
625 /* Restore original STDOUT */
626 if (hOldConOut != INVALID_HANDLE_VALUE)
627 {
628 HANDLE hOut = GetStdHandle (STD_OUTPUT_HANDLE);
629 SetStdHandle (STD_OUTPUT_HANDLE, hOldConOut);
630 if (hOldConOut != hOut)
631 CloseHandle (hOut);
632 hOldConOut = INVALID_HANDLE_VALUE;
633 }
634
635 /* Restore original STDERR */
636 if (hOldConErr != INVALID_HANDLE_VALUE)
637 {
638 HANDLE hErr = GetStdHandle (STD_ERROR_HANDLE);
639 SetStdHandle (STD_ERROR_HANDLE, hOldConErr);
640 if (hOldConErr != hErr)
641 CloseHandle (hErr);
642 hOldConErr = INVALID_HANDLE_VALUE;
643 }
644 #endif /* FEATURE_REDIRECTION */
645 }
646
647
648 /*
649 * do the prompt/input/process loop
650 *
651 */
652
653 static INT
654 ProcessInput (BOOL bFlag)
655 {
656 TCHAR commandline[CMDLINE_LENGTH];
657 TCHAR readline[CMDLINE_LENGTH];
658 LPTSTR tp;
659 LPTSTR ip;
660 LPTSTR cp;
661
662 /* JPP 19980807 - changed name so not to conflict with echo global */
663 BOOL bEchoThisLine;
664
665 do
666 {
667 /* if no batch input then... */
668 if (!(ip = ReadBatchLine (&bEchoThisLine)))
669 {
670 if (bFlag)
671 return 0;
672
673 ReadCommand (readline, CMDLINE_LENGTH);
674 ip = readline;
675 bEchoThisLine = FALSE;
676 }
677
678 cp = commandline;
679 while (*ip)
680 {
681 if (*ip == _T('%'))
682 {
683 switch (*++ip)
684 {
685 case _T('%'):
686 *cp++ = *ip++;
687 break;
688
689 case _T('0'):
690 case _T('1'):
691 case _T('2'):
692 case _T('3'):
693 case _T('4'):
694 case _T('5'):
695 case _T('6'):
696 case _T('7'):
697 case _T('8'):
698 case _T('9'):
699 if ((tp = FindArg (*ip - _T('0'))))
700 {
701 cp = stpcpy (cp, tp);
702 ip++;
703 }
704 else
705 *cp++ = _T('%');
706 break;
707
708 case _T('?'):
709 cp += _stprintf (cp, _T("%u"), nErrorLevel);
710 ip++;
711 break;
712
713 default:
714 if ((tp = _tcschr (ip, _T('%'))))
715 {
716 char evar[512];
717 *tp = _T('\0');
718
719 /* FIXME: This is just a quick hack!! */
720 /* Do a proper memory allocation!! */
721 if (GetEnvironmentVariable (ip, evar, 512))
722 cp = stpcpy (cp, evar);
723
724 ip = tp + 1;
725 }
726 break;
727 }
728 continue;
729 }
730
731 if (_istcntrl (*ip))
732 *ip = _T(' ');
733 *cp++ = *ip++;
734 }
735
736 *cp = _T('\0');
737
738 /* strip trailing spaces */
739 while ((--cp >= commandline) && _istspace (*cp))
740 ;
741
742 *(cp + 1) = _T('\0');
743
744 /* JPP 19980807 */
745 /* Echo batch file line */
746 if (bEchoThisLine)
747 {
748 PrintPrompt ();
749 ConOutPuts (commandline);
750 }
751
752 if (*commandline)
753 {
754 ParseCommandLine (commandline);
755 if (bEcho && !bIgnoreEcho)
756 ConOutChar ('\n');
757 bIgnoreEcho = FALSE;
758 }
759 }
760 while (!bCanExit || !bExit);
761
762 return 0;
763 }
764
765
766 /*
767 * control-break handler.
768 */
769 BOOL BreakHandler (DWORD dwCtrlType)
770 {
771 if ((dwCtrlType == CTRL_C_EVENT) ||
772 (dwCtrlType == CTRL_BREAK_EVENT))
773 {
774 bCtrlBreak = TRUE; /* indicate the break condition */
775 return TRUE;
776 }
777 return FALSE;
778 }
779
780
781 /*
782 * show commands and options that are available.
783 *
784 */
785 static VOID
786 ShowCommands (VOID)
787 {
788 LPCOMMAND cmdptr;
789 INT y;
790
791 ConOutPrintf (_T("\nInternal commands available:\n"));
792 y = 0;
793 cmdptr = cmds;
794 while (cmdptr->name)
795 {
796 if (++y == 8)
797 {
798 ConOutPuts (cmdptr->name);
799 y = 0;
800 }
801 else
802 ConOutPrintf (_T("%-10s"), cmdptr->name);
803
804 cmdptr++;
805 }
806
807 if (y != 0)
808 ConOutChar ('\n');
809
810 /* print feature list */
811 ConOutPuts ("\nFeatures available:");
812 #ifdef FEATURE_ALIASES
813 ConOutPuts (" [aliases]");
814 #endif
815 #ifdef FEATURE_HISTORY
816 ConOutPuts (" [history]");
817 #endif
818 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
819 ConOutPuts (" [unix filename completion]");
820 #endif
821 #ifdef FEATURE_DIRECTORY_STACK
822 ConOutPuts (" [directory stack]");
823 #endif
824 #ifdef FEATURE_REDIRECTION
825 ConOutPuts (" [redirections and piping]");
826 #endif
827 ConOutChar ('\n');
828 }
829
830
831 /*
832 * set up global initializations and process parameters
833 *
834 * argc - number of parameters to command.com
835 * argv - command-line parameters
836 *
837 */
838 static VOID Initialize (int argc, char *argv[])
839 {
840 INT i;
841
842 /* Added by Rob Lake 06/16/98. This enables the command.com
843 * to run the autoexec.bat at startup */
844
845 #ifdef _DEBUG
846 INT x;
847
848 DebugPrintf ("[command args:\n");
849 for (x = 0; x < argc; x++)
850 {
851 DebugPrintf ("%d. %s\n", x, argv[x]);
852 }
853 DebugPrintf ("]\n");
854 #endif
855
856 /* get version information */
857 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
858 GetVersionEx (&osvi);
859
860 InitLocale ();
861
862 /* get default input and output console handles */
863 hOut = GetStdHandle (STD_OUTPUT_HANDLE);
864 hIn = GetStdHandle (STD_INPUT_HANDLE);
865
866 #ifdef INCLUDE_CMD_CHDIR
867 InitLastPath ();
868 #endif
869
870 if (argc >= 2)
871 {
872 if (!_tcsncmp (argv[1], _T("/?"), 2))
873 {
874 ConOutPuts (_T("Starts a new instance of the ReactOS command line interpreter\n\n"
875 "CMD [/P][/C]...\n\n"
876 " /P ...\n"
877 " /C ..."));
878 ExitProcess (0);
879 }
880 else
881 {
882 for (i = 1; i < argc; i++)
883 {
884 if (!_tcsicmp (argv[i], _T("/p")))
885 {
886 if (!IsValidFileName (_T("\\autoexec.bat")))
887 {
888 #ifdef INCLUDE_CMD_DATE
889 cmd_date ("", "");
890 #endif
891 #ifdef INCLUDE_CMD_TIME
892 cmd_time ("", "");
893 #endif
894 }
895 else
896 ParseCommandLine ("\\autoexec.bat");
897 bCanExit = FALSE;
898 }
899 else if (!_tcsicmp (argv[i], _T("/c")))
900 {
901 /* This just runs a program and exits, RL: 06/16,21/98 */
902 char commandline[CMDLINE_LENGTH];
903 ++i;
904 strcpy(commandline, argv[i]);
905 while (argv[++i])
906 {
907 strcat(commandline, " ");
908 strcat(commandline, argv[i]);
909 }
910
911 ParseCommandLine(commandline);
912
913 /* HBP_003 { Fix return value when /C used }*/
914 ExitProcess (ProcessInput (TRUE));
915 }
916
917 #ifdef INCLUDE_CMD_COLOR
918 else if (!_tcsnicmp (argv[i], _T("/t:"), 3))
919 {
920 /* process /t (color) argument */
921 wDefColor = (WORD)strtoul (&argv[i][3], NULL, 16);
922 wColor = wDefColor;
923 }
924 #endif
925 }
926 }
927 }
928
929 #ifdef FEATURE_DIR_STACK
930 /* initialize directory stack */
931 InitDirectoryStack ();
932 #endif
933
934 #ifdef INCLUDE_CMD_COLOR
935 /* set default colors */
936 SetScreenColor (wColor);
937 #endif
938
939 ShortVersion ();
940 ShowCommands ();
941
942 /* Set COMSPEC environment variable */
943 #ifndef __REACTOS__
944 if (argv)
945 SetEnvironmentVariable (_T("COMSPEC"), argv[0]);
946 #endif
947 ConOutPrintf("argc: %d\n", argc);
948 if (!argv)
949 {
950 ConOutPrintf("argc is NULL\n");
951 }
952 else
953 {
954
955 }
956
957
958 /* add ctrl handler */
959 #if 0
960 SetConsoleCtrlHandler (NULL, TRUE);
961 #endif
962 }
963
964
965 static VOID Cleanup (VOID)
966 {
967
968 #ifdef FEATURE_DIECTORY_STACK
969 /* destroy directory stack */
970 DestroyDirectoryStack ();
971 #endif
972
973 #ifdef INCLUDE_CMD_CHDIR
974 FreeLastPath ();
975 #endif
976
977 /* remove ctrl handler */
978 #if 0
979 SetConsoleCtrlHandler ((PHANDLER_ROUTINE)&BreakHandler, FALSE);
980 #endif
981 }
982
983
984 /*
985 * main function
986 */
987 int main (int argc, char *argv[])
988 {
989 INT nExitCode;
990
991 AllocConsole ();
992 #ifndef __REACTOS__
993 SetFileApisToOEM ();
994 #endif
995
996 #ifdef __REACTOS__
997 SetCurrentDirectory (_T("C:\\"));
998 #endif
999
1000 /* check switches on command-line */
1001 Initialize (argc, argv);
1002
1003 /* call prompt routine */
1004 nExitCode = ProcessInput (FALSE);
1005
1006 /* do the cleanup */
1007 Cleanup ();
1008 FreeConsole ();
1009
1010 return nExitCode;
1011 }