?? cmd.c
字號:
/* $Id: cmd.c,v 1.3 2004/02/16 16:13:36 pfalcon Exp $ * * CMD.C - command-line interface. * * * History: * * 17 Jun 1994 (Tim Norman) * started. * * 08 Aug 1995 (Matt Rains) * I have cleaned up the source code. changes now bring this source * into guidelines for recommended programming practice. * * A added the the standard FreeDOS GNU licence test to the * initialize() function. * * Started to replace puts() with printf(). this will help * standardize output. please follow my lead. * * I have added some constants to help making changes easier. * * 15 Dec 1995 (Tim Norman) * major rewrite of the code to make it more efficient and add * redirection support (finally!) * * 06 Jan 1996 (Tim Norman) * finished adding redirection support! Changed to use our own * exec code (MUCH thanks to Svante Frey!!) * * 29 Jan 1996 (Tim Norman) * added support for CHDIR, RMDIR, MKDIR, and ERASE, as per * suggestion of Steffan Kaiser * * changed "file not found" error message to "bad command or * filename" thanks to Dustin Norman for noticing that confusing * message! * * changed the format to call internal commands (again) so that if * they want to split their commands, they can do it themselves * (none of the internal functions so far need that much power, anyway) * * 27 Aug 1996 (Tim Norman) * added in support for Oliver Mueller's ALIAS command * * 14 Jun 1997 (Steffan Kaiser) * added ctrl-break handling and error level * * 16 Jun 1998 (Rob Lake) * Runs command.com if /P is specified in command line. Command.com * also stays permanent. If /C is in the command line, starts the * program next in the line. * * 21 Jun 1998 (Rob Lake) * Fixed up /C so that arguments for the program * * 08-Jul-1998 (John P. Price) * Now sets COMSPEC environment variable * misc clean up and optimization * added date and time commands * changed to using spawnl instead of exec. exec does not copy the * environment to the child process! * * 14 Jul 1998 (Hans B Pufal) * Reorganised source to be more efficient and to more closely * follow MS-DOS conventions. (eg %..% environment variable * replacement works form command line as well as batch file. * * New organisation also properly support nested batch files. * * New command table structure is half way towards providing a * system in which COMMAND will find out what internal commands * are loaded * * 24 Jul 1998 (Hans B Pufal) [HBP_003] * Fixed return value when called with /C option * * 27 Jul 1998 John P. Price * added config.h include * * 28 Jul 1998 John P. Price * added showcmds function to show commands and options available * * 07-Aug-1998 (John P Price <linux-guru@gcfl.net>) * Fixed carrage return output to better match MSDOS with echo * on or off. (marked with "JPP 19980708") * * 07-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>) * First ReactOS release. * Extended length of commandline buffers to 512. * * 13-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>) * Added COMSPEC environment variable. * Added "/t" support (color) on cmd command line. * * 07-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>) * Added help text ("cmd /?"). * * 25-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>) * Unicode and redirection safe! * Fixed redirections and piping. * Piping is based on temporary files, but basic support * for anonymous pipes already exists. * * 27-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>) * Replaced spawnl() by CreateProcess(). * * 22-Oct-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>) * Added break handler. * * 15-Dec-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>) * Fixed current directory * * 28-Dec-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>) * Restore window title after program/batch execution * * 03-Feb-2001 (Eric Kohl <ekohl@rz-online.de>) * Workaround because argc[0] is NULL under ReactOS * * 23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>) * %envvar% replacement conflicted with for. */#include "config.h"#include "cmd.h"#undef fileno#define fileno _filenoBOOL GetStdioPathW(DWORD id, PWSTR pwszBuf, LPDWORD lpdwLen);BOOL bExit = FALSE; /* indicates EXIT was typed */BOOL bCanExit = TRUE; /* indicates if this shell is exitable */BOOL bCtrlBreak = FALSE; /* Ctrl-Break or Ctrl-C hit */BOOL bIgnoreEcho = FALSE; /* Ignore 'newline' before 'cls' */INT nErrorLevel = 0; /* Errorlevel of last launched external program */BOOL bChildProcessRunning = FALSE;DWORD dwChildProcessId = 0;OSVERSIONINFO osvi;#ifdef INCLUDE_CMD_COLORWORD wColor; /* current color */WORD wDefColor; /* default color */#endif/* * is character a delimeter when used on first word? * */static BOOL IsDelimiter (TCHAR c){ return (c == _T('/') || c == _T('=') || c == _T('\0') || _istspace (c));}/* * This command (in first) was not found in the command table * * first - first word on command line * rest - rest of command line */static VOIDExecute (LPTSTR first, LPTSTR rest){ TCHAR szFullName[MAX_PATH]; TCHAR szWindowTitle[MAX_PATH]; DWORD dwExitCode = 0;#ifdef _DEBUG DebugPrintf (_T("Execute: \'%s\' \'%s\'\n"), first, rest);#endif /* check for a drive change */ if ((_istalpha (first[0])) && (!_tcscmp (first + 1, _T(":")))) { BOOL working = TRUE; if (!SetCurrentDirectory(first)) /* Guess they changed disc or something, handle that gracefully and get to root */ { TCHAR str[4]; str[0]=first[0]; str[1]=_T(':'); str[2]=_T('\\'); str[3]=0; working = SetCurrentDirectory(str); } if (!working) ConErrPuts (INVALIDDRIVE); return; } /* get the PATH environment variable and parse it */ /* search the PATH environment variable for the binary */ if (!SearchForExecutable (first, szFullName)) { error_bad_command (); return; } GetConsoleTitle (szWindowTitle, MAX_PATH); /* check if this is a .BAT or .CMD file */ if (!_tcsicmp (_tcsrchr (szFullName, _T('.')), _T(".bat")) || !_tcsicmp (_tcsrchr (szFullName, _T('.')), _T(".cmd"))) {#ifdef _DEBUG DebugPrintf (_T("[BATCH: %s %s]\n"), szFullName, rest);#endif Batch (szFullName, first, rest); } else { /* exec the program */ TCHAR szFullCmdLine [CMDLINE_LENGTH]; PROCESS_INFORMATION prci;#ifdef _DEBUG DebugPrintf (_T("[EXEC: %s %s]\n"), szFullName, rest);#endif /* build command line for CreateProcess() */ _tcscpy (szFullCmdLine, szFullName); _tcscat (szFullCmdLine, _T(" ")); _tcscat (szFullCmdLine, rest); if (CreateProcess (szFullName, rest, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &prci)) { /* FIXME: Protect this with critical section */ bChildProcessRunning = TRUE; dwChildProcessId = prci.dwProcessId; WaitForSingleObject (prci.hProcess, INFINITE); /* FIXME: Protect this with critical section */ bChildProcessRunning = TRUE; GetExitCodeProcess (prci.hProcess, &dwExitCode); nErrorLevel = (INT)dwExitCode; CloseHandle (prci.hThread); CloseHandle (prci.hProcess); } else { ErrorMessage (GetLastError (), _T("Error executing CreateProcess()!!\n")); } } SetConsoleTitle (szWindowTitle);}/* * look through the internal commands and determine whether or not this * command is one of them. If it is, call the command. If not, call * execute to run it as an external program. * * line - the command line of the program to run * */static VOIDDoCommand (LPTSTR line){ TCHAR com[MAX_PATH]; /* the first word in the command */ LPTSTR cp = com; LPTSTR cstart; LPTSTR rest = line; /* pointer to the rest of the command line */ INT cl; LPCOMMAND cmdptr;#ifdef _DEBUG DebugPrintf (_T("DoCommand: (\'%s\')\n"), line);#endif /* DEBUG */ /* Skip over initial white space */ while (isspace (*rest)) rest++; cstart = rest; /* Anything to do ? */ if (*rest) { /* Copy over 1st word as lower case */ while (!IsDelimiter (*rest)) *cp++ = _totlower (*rest++); /* Terminate first word */ *cp = _T('\0'); /* Skip over whitespace to rest of line */ while (_istspace (*rest)) rest++; /* Scan internal command table */ for (cmdptr = cmds;; cmdptr++) { /* If end of table execute ext cmd */ if (cmdptr->name == NULL) { Execute (com, rest); break; } if (!_tcscmp (com, cmdptr->name)) { cmdptr->func (com, rest); break; } /* The following code handles the case of commands like CD which * are recognised even when the command name and parameter are * not space separated. * * e.g dir.. * cd\freda */ /* Get length of command name */ cl = _tcslen (cmdptr->name); if ((cmdptr->flags & CMD_SPECIAL) && (!_tcsncmp (cmdptr->name, com, cl)) && (_tcschr (_T("\\.-"), *(com + cl)))) { /* OK its one of the specials...*/ /* Terminate first word properly */ com[cl] = _T('\0'); /* Call with new rest */ cmdptr->func (com, cstart + cl); break; } } }}/* * process the command line and execute the appropriate functions * full input/output redirection and piping are supported */VOID ParseCommandLine (LPTSTR cmd){ TCHAR cmdline[CMDLINE_LENGTH]; LPTSTR s;#ifdef FEATURE_REDIRECTION TCHAR in[CMDLINE_LENGTH] = _T(""); TCHAR out[CMDLINE_LENGTH] = _T(""); TCHAR err[CMDLINE_LENGTH] = _T(""); TCHAR szTempPath[MAX_PATH] = _T(".\\"); TCHAR szFileName[2][MAX_PATH] = {_T(""), _T("")}; HANDLE hFile[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; LPTSTR t = NULL; INT num = 0; INT nRedirFlags = 0; DWORD dwSize; WCHAR szOldConIn[MAX_PATH]; WCHAR szOldConOut[MAX_PATH]; WCHAR szOldConErr[MAX_PATH];#endif /* FEATURE_REDIRECTION */ _tcscpy (cmdline, cmd); s = &cmdline[0];#ifdef _DEBUG DebugPrintf (_T("ParseCommandLine: (\'%s\')\n"), s);#endif /* DEBUG */#ifdef FEATURE_ALIASES /* expand all aliases */ ExpandAlias (s, CMDLINE_LENGTH);#endif /* FEATURE_ALIAS */#ifdef FEATURE_REDIRECTION /* find the temp path to store temporary files */ GetTempPath (MAX_PATH, szTempPath); if (szTempPath[_tcslen (szTempPath) - 1] != _T('\\')) _tcscat (szTempPath, _T("\\")); /* get the redirections from the command line */ num = GetRedirection (s, in, out, err, &nRedirFlags); /* more efficient, but do we really need to do this? */ for (t = in; _istspace (*t); t++) ; _tcscpy (in, t); for (t = out; _istspace (*t); t++) ; _tcscpy (out, t); for (t = err; _istspace (*t); t++) ; _tcscpy (err, t); /* Set up the initial conditions ... */ /* preserve STDIN, STDOUT and STDERR handles */ GetStdioPathW(0, szOldConIn, &dwSize); GetStdioPathW(1, szOldConOut, &dwSize); GetStdioPathW(2, szOldConErr, &dwSize); /* redirect STDIN */ if (in[0]) { HANDLE hFile = fileno(_wfreopen(in, L"rb", stdin)); if (hFile == INVALID_HANDLE_VALUE) { ConErrPrintf (_T("Can't redirect input from file %s\n"), in); return; } #ifdef _DEBUG DebugPrintf (_T("Input redirected from: %s\n"), in);#endif } /* Now do all but the last pipe command */ *szFileName[0] = '\0'; hFile[0] = INVALID_HANDLE_VALUE; while (num-- > 1) { /* Create unique temporary file name */ GetTempFileName (szTempPath, _T("CMD"), 0, szFileName[1]); /* Set current stdout to temporary file */ hFile[1] = fileno(_wfreopen(szFileName[1], L"wb", stdout)); DoCommand (s); /* close stdout file */ _wfreopen(szOldConOut, L"wb", stdout); hFile[1] = INVALID_HANDLE_VALUE; /* close old stdin file */ _wfreopen(szOldConIn, L"rb", stdin); if ((hFile[0] != INVALID_HANDLE_VALUE) && wcscmp(szFileName[0], szOldConIn)) { /* delete old stdin file, if it is a real file */ hFile[0] = INVALID_HANDLE_VALUE; DeleteFile (szFileName[0]); *szFileName[0] = _T('\0'); } /* copy stdout file name to stdin file name */ _tcscpy (szFileName[0], szFileName[1]); *szFileName[1] = _T('\0'); /* open new stdin file */ hFile[0] = fileno(_wfreopen(szFileName[0], L"rb", stdin)); s = s + _tcslen (s) + 1; } /* Now set up the end conditions... */ /* redirect STDOUT */ if (out[0]) { /* Final output to here */ FILE *file; if (nRedirFlags & OUTPUT_APPEND) file = _wfreopen(out, L"ab", stdout); else file = _wfreopen(out, L"wb", stdout); if (!file) { ConErrPrintf (_T("Can't redirect to file %s\n"), out); return; }#ifdef _DEBUG DebugPrintf (_T("Output redirected to: %s\n"), out);#endif } else if (*szOldConOut) { /* Restore original stdout */ WCHAR szOut[MAX_PATH]; GetStdioPathW (STD_OUTPUT_HANDLE, szOut, &dwSize); _wfreopen( szOldConOut, L"wb", stdout); } /* redirect STDERR */ if (err[0]) { /* Final output to here */ FILE *file; if (!_tcscmp (err, out))
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -