⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cmd.c

📁 ReactOS是一些高手根据Windows XP的内核编写出的类XP。内核实现机理和API函数调用几乎相同。甚至可以兼容XP的程序。喜欢研究系统内核的人可以看一看。
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 *  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)
 *        First ReactOS release.
 *        Extended length of commandline buffers to 512.
 *
 *    13-Dec-1998 (Eric Kohl)
 *        Added COMSPEC environment variable.
 *        Added "/t" support (color) on cmd command line.
 *
 *    07-Jan-1999 (Eric Kohl)
 *        Added help text ("cmd /?").
 *
 *    25-Jan-1999 (Eric Kohl)
 *        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)
 *        Replaced spawnl() by CreateProcess().
 *
 *    22-Oct-1999 (Eric Kohl)
 *        Added break handler.
 *
 *    15-Dec-1999 (Eric Kohl)
 *        Fixed current directory
 *
 *    28-Dec-1999 (Eric Kohl)
 *        Restore window title after program/batch execution
 *
 *    03-Feb-2001 (Eric Kohl)
 *        Workaround because argc[0] is NULL under ReactOS
 *
 *    23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.se>)
 *        %envvar% replacement conflicted with for.
 *
 *    30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
 *       Make MakeSureDirectoryPathExistsEx unicode safe.
 *
 *    28-Mai-2004
 *       Removed MakeSureDirectoryPathExistsEx.
 *       Use the current directory if GetTempPath fails.
 *
 *    12-Jul-2004 (Jens Collin <jens.collin@lakhei.com>)
 *       Added ShellExecute call when all else fails to be able to "launch" any file.
 *
 *    02-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
 *        Remove all hardcode string to En.rc
 *
 *    06-May-2005 (Klemens Friedl <frik85@gmail.com>)
 *        Add 'help' command (list all commands plus description)
 *
 *    06-jul-2005 (Magnus Olsen <magnus@greatlord.com>)
 *        translate '%errorlevel%' to the internal value.
 *        Add proper memmory alloc ProcessInput, the error
 *        handling for memmory handling need to be improve
 */

#include <precomp.h>

#ifndef NT_SUCCESS
#define NT_SUCCESS(StatCode)  ((NTSTATUS)(StatCode) >= 0)
#endif

typedef NTSTATUS (WINAPI *NtQueryInformationProcessProc)(HANDLE, PROCESSINFOCLASS,
                                                          PVOID, ULONG, PULONG);
typedef NTSTATUS (WINAPI *NtReadVirtualMemoryProc)(HANDLE, PVOID, PVOID, ULONG, PULONG);

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;
HANDLE hIn;
HANDLE hOut;
HANDLE hConsole;
HANDLE CMD_ModuleHandle;
HMODULE NtDllModule;

static NtQueryInformationProcessProc NtQueryInformationProcessPtr = NULL;
static NtReadVirtualMemoryProc       NtReadVirtualMemoryPtr = NULL;

#ifdef INCLUDE_CMD_COLOR
WORD wColor;              /* current color */
WORD wDefColor;           /* default color */
#endif

/*
 * convert
 *
 * insert commas into a number
 */
INT
ConvertULargeInteger (ULARGE_INTEGER num, LPTSTR des, INT len, BOOL bPutSeperator)
{
	TCHAR temp[32];
	INT c = 0;
	INT n = 0;

	if (len <= 1)
		return 0;

	if (num.QuadPart == 0)
	{
		des[0] = _T('0');
		des[1] = _T('\0');
		n = 1;
	}
	else
	{
		temp[31] = 0;
		while (num.QuadPart > 0)
		{
			if ((((c + 1) % (nNumberGroups + 1)) == 0) && (bPutSeperator))
				temp[30 - c++] = cThousandSeparator;
                        temp[30 - c++] = (TCHAR)(num.QuadPart % 10) + _T('0');
			num.QuadPart /= 10;
		}
        if (c>len)
			c=len;

		for (n = 0; n <= c; n++)
			des[n] = temp[31 - c + n];
	}

	return n;
}

/*
 * is character a delimeter when used on first word?
 *
 */
static BOOL IsDelimiter (TCHAR c)
{
	return (c == _T('/') || c == _T('=') || c == _T('\0') || _istspace (c));
}

/*
 * Is a process a console process?
 */
static BOOL IsConsoleProcess(HANDLE Process)
{
	NTSTATUS Status;
	PROCESS_BASIC_INFORMATION Info;
	PEB ProcessPeb;
	ULONG BytesRead;

	if (NULL == NtQueryInformationProcessPtr || NULL == NtReadVirtualMemoryPtr)
	{
		return TRUE;
	}

	Status = NtQueryInformationProcessPtr (
		Process, ProcessBasicInformation,
		&Info, sizeof(PROCESS_BASIC_INFORMATION), NULL);
	if (! NT_SUCCESS(Status))
	{
#ifdef _DEBUG
		DebugPrintf (_T("NtQueryInformationProcess failed with status %08x\n"), Status);
#endif
		return TRUE;
	}
	Status = NtReadVirtualMemoryPtr (
		Process, Info.PebBaseAddress, &ProcessPeb,
		sizeof(PEB), &BytesRead);
	if (! NT_SUCCESS(Status) || sizeof(PEB) != BytesRead)
	{
#ifdef _DEBUG
		DebugPrintf (_T("Couldn't read virt mem status %08x bytes read %lu\n"), Status, BytesRead);
#endif
		return TRUE;
	}

	return IMAGE_SUBSYSTEM_WINDOWS_CUI == ProcessPeb.ImageSubSystem;
}



#ifdef _UNICODE
#define SHELLEXECUTETEXT   	"ShellExecuteW"
#else
#define SHELLEXECUTETEXT   	"ShellExecuteA"
#endif

typedef HINSTANCE (WINAPI *MYEX)(
	HWND hwnd,
	LPCTSTR lpOperation,
	LPCTSTR lpFile,
	LPCTSTR lpParameters,
	LPCTSTR lpDirectory,
	INT nShowCmd
);



static BOOL RunFile(LPTSTR filename)
{
	HMODULE     hShell32;
	MYEX        hShExt;
	HINSTANCE   ret;

#ifdef _DEBUG
	DebugPrintf (_T("RunFile(%s)\n"), filename);
#endif
	hShell32 = LoadLibrary(_T("SHELL32.DLL"));
	if (!hShell32)
	{
#ifdef _DEBUG
		DebugPrintf (_T("RunFile: couldn't load SHELL32.DLL!\n"));
#endif
		return FALSE;
	}

	hShExt = (MYEX)(FARPROC)GetProcAddress(hShell32, SHELLEXECUTETEXT);
	if (!hShExt)
	{
#ifdef _DEBUG
		DebugPrintf (_T("RunFile: couldn't find ShellExecuteA/W in SHELL32.DLL!\n"));
#endif
		FreeLibrary(hShell32);
		return FALSE;
	}

#ifdef _DEBUG
	DebugPrintf (_T("RunFile: ShellExecuteA/W is at %x\n"), hShExt);
#endif

	ret = (hShExt)(NULL, _T("open"), filename, NULL, NULL, SW_SHOWNORMAL);

#ifdef _DEBUG
	DebugPrintf (_T("RunFile: ShellExecuteA/W returned %d\n"), (DWORD)ret);
#endif

	FreeLibrary(hShell32);
	return (((DWORD)ret) > 32);
}



/*
 * This command (in first) was not found in the command table
 *
 * Full  - whole command line
 * First - first word on command line
 * Rest  - rest of command line
 */

static VOID
Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
{
	TCHAR *szFullName=NULL;
	TCHAR *first = NULL;
	TCHAR *rest = NULL;
	TCHAR *full = NULL;
	TCHAR *dot = NULL;
	TCHAR szWindowTitle[MAX_PATH];
	DWORD dwExitCode = 0;

#ifdef _DEBUG
	DebugPrintf (_T("Execute: \'%s\' \'%s\'\n"), first, rest);
#endif

	/* we need biger buffer that First, Rest, Full are already
	   need rewrite some code to use cmd_realloc when it need instead
	   of add 512bytes extra */

	first = cmd_alloc ( (_tcslen(First) + 512) * sizeof(TCHAR));
	if (first == NULL)
	{
		error_out_of_memory();
                nErrorLevel = 1;
		return ;
	}

	rest = cmd_alloc ( (_tcslen(Rest) + 512) * sizeof(TCHAR));
	if (rest == NULL)
	{
		cmd_free (first);
		error_out_of_memory();
                nErrorLevel = 1;
		return ;
	}

	full = cmd_alloc ( (_tcslen(Full) + 512) * sizeof(TCHAR));
	if (full == NULL)
	{
		cmd_free (first);
		cmd_free (rest);
		error_out_of_memory();
                nErrorLevel = 1;
		return ;
	}

	szFullName = cmd_alloc ( (_tcslen(Full) + 512) * sizeof(TCHAR));
	if (full == NULL)
	{
		cmd_free (first);
		cmd_free (rest);
		cmd_free (full);
		error_out_of_memory();
                nErrorLevel = 1;
		return ;
	}


	/* Though it was already parsed once, we have a different set of rules
	   for parsing before we pass to CreateProccess */
	if(!_tcschr(Full,_T('\"')))
	{
		_tcscpy(first,First);
		_tcscpy(rest,Rest);
		_tcscpy(full,Full);
	}
	else
	{
		UINT i = 0;
		BOOL bInside = FALSE;
		rest[0] = _T('\0');
		full[0] = _T('\0');
		first[0] = _T('\0');
		_tcscpy(first,Full);
		/* find the end of the command and start of the args */
		for(i = 0; i < _tcslen(first); i++)
		{
			if(!_tcsncmp(&first[i], _T("\""), 1))
				bInside = !bInside;
			if(!_tcsncmp(&first[i], _T(" "), 1) && !bInside)
			{
				_tcscpy(rest,&first[i]);
				first[i] = _T('\0');
				break;
			}

		}
		i = 0;
		/* remove any slashes */
		while(i < _tcslen(first))
		{
			if(first[i] == _T('\"'))
				memmove(&first[i],&first[i + 1], _tcslen(&first[i]) * sizeof(TCHAR));
			else
				i++;
		}
		/* Drop quotes around it just in case there is a space */
		_tcscpy(full,_T("\""));
		_tcscat(full,first);
		_tcscat(full,_T("\" "));
		_tcscat(full,rest);
	}

	/* 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) ConErrResPuts (STRING_FREE_ERROR1);

		cmd_free (first);
		cmd_free (rest);
		cmd_free (full);
		cmd_free (szFullName);
                nErrorLevel = 1;
		return;
	}

	/* get the PATH environment variable and parse it */
	/* search the PATH environment variable for the binary */
	if (!SearchForExecutable (first, szFullName))
	{
			error_bad_command ();
			cmd_free (first);
			cmd_free (rest);
			cmd_free (full);
			cmd_free (szFullName);
                        nErrorLevel = 1;
			return;

	}

	GetConsoleTitle (szWindowTitle, MAX_PATH);

	/* check if this is a .BAT or .CMD file */
	dot = _tcsrchr (szFullName, _T('.'));
	if (dot && (!_tcsicmp (dot, _T(".bat")) || !_tcsicmp (dot, _T(".cmd"))))
	{
#ifdef _DEBUG
		DebugPrintf (_T("[BATCH: %s %s]\n"), szFullName, rest);
#endif
		Batch (szFullName, first, rest);
	}
	else
	{
		/* exec the program */
		PROCESS_INFORMATION prci;
		STARTUPINFO stui;

#ifdef _DEBUG
		DebugPrintf (_T("[EXEC: %s %s]\n"), full, rest);
#endif
		/* build command line for CreateProcess() */

		/* fill startup info */
		memset (&stui, 0, sizeof (STARTUPINFO));
		stui.cb = sizeof (STARTUPINFO);
		stui.dwFlags = STARTF_USESHOWWINDOW;
		stui.wShowWindow = SW_SHOWDEFAULT;

		// return console to standard mode
		SetConsoleMode (GetStdHandle(STD_INPUT_HANDLE),
		                ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT );

		if (CreateProcess (szFullName,
		                   full,
		                   NULL,
		                   NULL,
		                   TRUE,
		                   0,			/* CREATE_NEW_PROCESS_GROUP */
		                   NULL,
		                   NULL,
		                   &stui,
		                   &prci))
						   
		{
			if (IsConsoleProcess(prci.hProcess))
			{
				/* FIXME: Protect this with critical section */
				bChildProcessRunning = TRUE;
				dwChildProcessId = prci.dwProcessId;

				WaitForSingleObject (prci.hProcess, INFINITE);

				/* FIXME: Protect this with critical section */
				bChildProcessRunning = FALSE;

				GetExitCodeProcess (prci.hProcess, &dwExitCode);
				nErrorLevel = (INT)dwExitCode;
			}
                        else
                        {
                            nErrorLevel = 0;
                        }
			CloseHandle (prci.hThread);
			CloseHandle (prci.hProcess);
		}
		else
		{
#ifdef _DEBUG
			DebugPrintf (_T("[ShellExecute: %s]\n"), full);
#endif
			// See if we can run this with ShellExecute() ie myfile.xls
			if (!RunFile(full))
			{
#ifdef _DEBUG
				DebugPrintf (_T("[ShellExecute failed!: %s]\n"), full);
#endif
				error_bad_command ();
                                nErrorLevel = 1;
			}
                        else
                        {
                                nErrorLevel = 0;
                        }
		}
		// restore console mode
		SetConsoleMode (
			GetStdHandle( STD_INPUT_HANDLE ),
			ENABLE_PROCESSED_INPUT );
	}

	/* Get code page if it has been change */
	InputCodePage= GetConsoleCP();
	OutputCodePage = GetConsoleOutputCP();
	SetConsoleTitle (szWindowTitle);

	cmd_free(first);
	cmd_free(rest);
	cmd_free(full);
	cmd_free (szFullName);
}


/*
 * 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
 *
 */

VOID
DoCommand (LPTSTR line)
{
	TCHAR *com = NULL;  /* the first word in the command */
	TCHAR *cp = NULL;
	LPTSTR cstart;
	LPTSTR rest;   /* pointer to the rest of the command line */
	INT cl;
	LPCOMMAND cmdptr;

#ifdef _DEBUG
	DebugPrintf (_T("DoCommand: (\'%s\')\n"), line);
#endif /* DEBUG */

	com = cmd_alloc( (_tcslen(line) +512)*sizeof(TCHAR) );
	if (com == NULL)
	{
		error_out_of_memory();
		return;
	}

	cp = com;
	/* Skip over initial white space */
	while (_istspace (*line))
		line++;
	rest = line;

	cstart = rest;

	/* Anything to do ? */
	if (*rest)
	{
		if (*rest == _T('"'))
		{
			/* treat quoted words specially */

			rest++;

			while(*rest != _T('\0') && *rest != _T('"'))

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -