diff options
Diffstat (limited to 'private/mvdm/dos/command')
-rw-r--r-- | private/mvdm/dos/command/cmd.c | 34 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmd.h | 292 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdconf.c | 636 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmddata.c | 51 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmddisp.c | 56 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdenv.c | 696 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdexec.c | 650 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdexit.c | 31 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdkeyb.c | 251 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdkeyb.h | 18 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdmisc.c | 897 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdpif.c | 289 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdpif.h | 46 | ||||
-rw-r--r-- | private/mvdm/dos/command/cmdredir.c | 664 | ||||
-rw-r--r-- | private/mvdm/dos/command/makefile | 9 | ||||
-rw-r--r-- | private/mvdm/dos/command/sources | 60 |
16 files changed, 4680 insertions, 0 deletions
diff --git a/private/mvdm/dos/command/cmd.c b/private/mvdm/dos/command/cmd.c new file mode 100644 index 000000000..c701f8a86 --- /dev/null +++ b/private/mvdm/dos/command/cmd.c @@ -0,0 +1,34 @@ +/* + * cmd.c - Main Module of Command.lib + * + * Sudeepb 09-Apr-1991 Craeted + */ + +#include "cmd.h" +#include "cmdsvc.h" + + +/* CmdInit - COmmand Initialiazation routine. + * + * Entry + * argc,argv - from softpc as it is. + * Full path name of the dos binary is preceded with + * -a or /a. i.e. -a c:\nt\bin86\kernel.exe reversi.exe + * + * + * Exit + * + */ + +BOOL CMDInit (argc,argv) +INT argc; +PSZ *argv; +{ +CHAR RootDir [MAX_PATH]; +UINT Len; + + Len = GetSystemDirectory (RootDir,MAX_PATH); + if (Len <= MAX_PATH && Len > 0) + cmdHomeDirectory[0] = RootDir[0]; + return TRUE; +} diff --git a/private/mvdm/dos/command/cmd.h b/private/mvdm/dos/command/cmd.h new file mode 100644 index 000000000..8ace02a73 --- /dev/null +++ b/private/mvdm/dos/command/cmd.h @@ -0,0 +1,292 @@ +/* cmd.h - main include file for command.lib + * + * Modification History + * + * Sudeepb 17-Sep-1991 Created + */ + +/* +#define WIN +#define FLAT_32 +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> + +#define _WINDOWS +#include <windows.h> + +*/ + +#ifdef DOS +#define SIGNALS +#endif + +#ifdef OS2_16 +#define OS2 +#define SIGNALS +#endif + +#ifdef OS2_32 +#define OS2 +#define FLAT_32 +#endif + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <malloc.h> +#include <process.h> + +#ifdef WIN_16 +#define WIN +#define API16 +#endif + +#ifdef WIN_32 +#define WIN +#define FLAT_32 +#define TRUE_IF_WIN32 1 +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#else +#define TRUE_IF_WIN32 0 +#endif + +#ifdef FLAT_32 +#ifndef i386 +#define ALIGN_32 +#else +#define NOALIGN_32 +#endif +#endif + +#ifdef WIN +#define _WINDOWS +#include <windows.h> +#endif + +#ifdef SIGNALS +#include <conio.h> +#include <signal.h> +#endif + +#ifdef OS2_32 +#include <excpt.h> +#define XCPT_SIGNAL 0xC0010003 +#endif + +#include <vdmapi.h> + +#define COPY_STD_OUT 1 +#define COPY_STD_ERR 2 + +#define CLOSE_ALL_HANDLES 1 +#define CLOSE_STD_OUT 2 +#define CLOSE_STD_ERR 4 + +#define DEFAULT_REDIRECTION_SIZE 1024 +#define MAX_SHORTCUT_SIZE 128 +#define STDIN_BUF_SIZE 512 + +/** Command Macros **/ + +/** Command Basic Typedefs **/ + +typedef VOID (*PFNSVC)(VOID); + +#pragma pack(1) + +typedef struct _PARAMBLOCK { + USHORT SegEnv; + USHORT OffCmdTail; + USHORT SegCmdTail; + ULONG pFCB1; + ULONG pFCB2; +} PARAMBLOCK,*PPARAMBLOCK; + +typedef struct _SCSINFO { + CHAR SCS_ComSpec [64]; + CHAR SCS_CmdTail [128]; + PARAMBLOCK SCS_ParamBlock; + CHAR SCS_ToSync; +} SCSINFO, *PSCSINFO; + +typedef struct _STD_HANDLES { + ULONG hStdErr; + ULONG hStdOut; + ULONG hStdIn; +} STD_HANDLES, *PSTD_HANDLES; + +#define PIPE_INPUT_BUFFER_SIZE 512 +#define PIPE_OUTPUT_BUFFER_SIZE PIPE_INPUT_BUFFER_SIZE +#define PIPE_INPUT_TIMEOUT 55 +#define PIPE_OUTPUT_TIMEOUT PIPE_INPUT_TIMEOUT + +typedef struct _PIPE_INPUT{ + struct _PIPE_INPUT *Next; + HANDLE hFileRead; + HANDLE hFileWrite; + HANDLE hPipe; + HANDLE hDataEvent; + HANDLE hThread; + CHAR *pFileName; + DWORD BufferSize; + BOOL fEOF; + BOOL WaitData; + BYTE *Buffer; + CRITICAL_SECTION CriticalSection; +} PIPE_INPUT, *PPIPE_INPUT; + +typedef struct _PIPE_OUTPUT { + HANDLE hFile; + HANDLE hPipe; + HANDLE hExitEvent; + CHAR *pFileName; + DWORD BufferSize; + BYTE *Buffer; +} PIPE_OUTPUT, *PPIPE_OUTPUT; + +typedef struct _RedirComplete_Info { + HANDLE ri_hStdErr; + HANDLE ri_hStdOut; + HANDLE ri_hStdIn; + HANDLE ri_hStdErrFile; + HANDLE ri_hStdOutFile; + HANDLE ri_hStdInFile; + HANDLE ri_hStdOutThread; + HANDLE ri_hStdErrThread; + PPIPE_INPUT ri_pPipeStdIn; + PPIPE_OUTPUT ri_pPipeStdOut; + PPIPE_OUTPUT ri_pPipeStdErr; + +} REDIRCOMPLETE_INFO, *PREDIRCOMPLETE_INFO; + +typedef struct _VDMENVBLK { + DWORD cchEnv; + DWORD cchRemain; + CHAR *lpszzEnv; +} VDMENVBLK, *PVDMENVBLK; + +#pragma pack() + +/** Command Function Prototypes */ + + +VOID cmdComSpec (VOID); +VOID cmdGetEnv (VOID); +VOID cmdGetNextCmd (VOID); +VOID cmdGetNextCmdForSeparateWow (VOID); +VOID cmdGetStdHandle (VOID); +VOID cmdExec (VOID); +VOID cmdExecComspec32 (VOID); +VOID cmdExitVDM (VOID); +VOID cmdReturnExitCode (VOID); +VOID cmdSaveWorld (VOID); +VOID cmdSetInfo (VOID); +VOID cmdGetCurrentDir (VOID); +VOID cmdSetDirectories (PCHAR,VDMINFO *); +VOID CheckDotExeForWOW (LPSTR); +BOOL cmdCheckCopyForRedirection (PREDIRCOMPLETE_INFO); +BOOL cmdCreateTempFile (PHANDLE,PCHAR *); +VOID cmdCheckBinary (VOID); +VOID cmdInitConsole (VOID); +VOID nt_init_event_thread (VOID); +VOID cmdExec32 (PCHAR,PCHAR); +VOID cmdCreateProcess (VOID); +USHORT cmdMapCodePage (ULONG); +VOID cmdCheckForPIF (PVDMINFO); +VOID cmdGetConfigSys (VOID); +VOID cmdGetAutoexecBat (VOID); +VOID DeleteConfigFiles (VOID); +VOID cmdGetKbdLayout (VOID); +BOOL cmdXformEnvironment (PCHAR, PANSI_STRING); +VOID cmdGetInitEnvironment (VOID); +VOID cmdUpdateCurrentDirectories (BYTE); +BOOL cmdCreateVDMEnvironment (PVDMENVBLK); +DWORD cmdGetEnvironmentVariable (PVDMENVBLK, PCHAR, PCHAR, DWORD); +BOOL cmdSetEnvironmentVariable (PVDMENVBLK, PCHAR, PCHAR); +DWORD cmdExpandEnvironmentStrings (PVDMENVBLK, PCHAR, PCHAR, DWORD); +PREDIRCOMPLETE_INFO cmdCheckStandardHandles (PVDMINFO,USHORT UNALIGNED *); +VOID cmdGetStartInfo (VOID); +BOOL cmdHandleStdinWithPipe (PREDIRCOMPLETE_INFO); +BOOL cmdHandleStdOutErrWithPipe (PREDIRCOMPLETE_INFO, USHORT); +LPSTR cmdSkipOverPathName (LPSTR); +BOOL cmdPipeFileDataEOF (HANDLE, BOOL *); +BOOL cmdPipeFileEOF (HANDLE); +VOID cmdPipeInThread (LPVOID); +VOID cmdPipeOutThread (LPVOID); + +/** Command Externs **/ + +extern USHORT nDrives; +extern BOOL IsFirstVDMInSystem; +extern BOOL VDMForWOW; +extern CHAR lpszComSpec[]; +extern USHORT cbComSpec; +extern BOOL IsFirstCall; +extern BOOL IsRepeatCall; +extern BOOL IsFirstWOWCheckBinary; +extern BOOL IsFirstVDMInSystem; +extern BOOL SaveWorldCreated; +extern PCHAR pSCS_ToSync; +extern BOOL fBlock; +extern PCHAR pCommand32; +extern PCHAR pEnv32; +extern DWORD dwExitCode32; +extern PSCSINFO pSCSInfo; +extern HANDLE hFileStdOut; +extern HANDLE hFileStdOutDup; +extern HANDLE hFileStdErr; +extern HANDLE hFileStdErrDup; +extern VDMINFO VDMInfo; +extern PSZ pszFileStdOut; +extern PSZ pszFileStdErr; +extern CHAR cmdHomeDirectory[]; +extern HANDLE SCS_hStdIn; +extern HANDLE SCS_hStdOut; +extern HANDLE SCS_hStdErr; +extern CHAR chDefaultDrive; +extern BOOL DontCheckDosBinaryType; +extern WORD Exe32ActiveCount; +extern BOOL fSoftpcRedirection; +extern BOOL fSoftpcRedirectionOnShellOut; +extern VOID nt_std_handle_notification (BOOL); +extern VOID cmdPushExitInConsoleBuffer (VOID); + + +// control handler state, defined in nt_event.h, nt_event.c +extern ULONG CntrlHandlerState; +#define CNTRL_SHELLCOUNT 0x0FFFF // The LOWORD is used for shell count +#define CNTRL_PIFALLOWCLOSE 0x10000 +#define CNTRL_VDMBLOCKED 0x20000 +#define CNTRL_SYSTEMROOTCONSOLE 0x40000 +#define CNTRL_PUSHEXIT 0x80000 + + +// Temporary variable till we standardized on wowexec +extern ULONG iWOWTaskId; +extern CHAR comspec[]; +extern CHAR ShortCutInfo[]; + +extern VOID nt_pif_callout (LPVOID); +extern CHAR *lpszzInitEnvironment; +extern WORD cchInitEnvironment; +extern CHAR *lpszzCurrentDirectories; +extern DWORD cchCurrentDirectories; +extern BYTE * pIsDosBinary; +extern WORD * pFDAccess; +extern CHAR *lpszzcmdEnv16; +extern BOOL DosEnvCreated; +extern BOOL IsFirstVDM; +extern VDMENVBLK cmdVDMEnvBlk; +extern CHAR *lpszzVDMEnv32; +extern DWORD cchVDMEnv32; +extern UINT VdmExitCode; + +// application path name extention type. +#define EXTENTION_STRING_LEN 4 +#define BAT_EXTENTION_STRING ".BAT" +#define EXE_EXTENTION_STRING ".EXE" +#define COM_EXTENTION_STRING ".COM" diff --git a/private/mvdm/dos/command/cmdconf.c b/private/mvdm/dos/command/cmdconf.c new file mode 100644 index 000000000..0b57d2bf6 --- /dev/null +++ b/private/mvdm/dos/command/cmdconf.c @@ -0,0 +1,636 @@ +/* cmdconf.c - handles pre-processing of config.sys\autoexec.bat + * + * Modification History: + * + * 21-Nov-1992 Jonle , Created + */ + +#include "cmd.h" +#include <cmdsvc.h> +#include <demexp.h> +#include <softpc.h> +#include <mvdm.h> +#include <ctype.h> +#include <oemuni.h> + +// +// local stuff +// +CHAR *pchTmpConfigFile; +CHAR *pchTmpAutoexecFile; +CHAR achSYSROOT[] = "%SystemRoot%"; +CHAR achCOMMAND[] = "\\System32\\command.com"; +CHAR achSHELL[] = "shell"; +CHAR achCOUNTRY[] = "country"; +CHAR achREM[] = "rem"; +CHAR achENV[] = "/e:"; +CHAR achEOL[] = "\r\n"; +CHAR achSET[] = "SET"; +CHAR achPROMPT[] = "PROMPT"; +CHAR achPATH[] = "PATH"; + +DWORD dwLenSysRoot; +CHAR achSysRoot[64]; + + + +void ExpandConfigFiles(BOOLEAN bConfig); +DWORD WriteExpanded(HANDLE hFile, CHAR *pch, DWORD dwBytes); +void WriteFileAssert(HANDLE hFile, CHAR *pBuff, DWORD dwBytes); +#define ISEOL(ch) ( !(ch) || ((ch) == '\n') || ((ch) == '\r')) + + + +/** There are still many items we don't supprot for long path name + (1). device, install in config.sys + (2). Third-party shell + (3). lh, loadhigh and any other commands in autoexec.bat + +**/ + +/* cmdGetConfigSys - Creates a temp file to replace c:\config.sys + * + * Entry - Client (DS:DX) pointer to receive file name + * + * EXIT - This routine will Terminate the vdm if it fails + * And will not return + * + * The buffer to receive the file name must be at least 64 bytes + */ +VOID cmdGetConfigSys (VOID) +{ + UNICODE_STRING Unicode; + OEM_STRING OemString; + ANSI_STRING AnsiString; + + ExpandConfigFiles(TRUE); + + RtlInitAnsiString(&AnsiString, pchTmpConfigFile); + if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&Unicode, &AnsiString, TRUE)) ) + goto ErrExit; + + OemString.Buffer = (char *)GetVDMAddr(getDS(),getDX()); + OemString.MaximumLength = 64; + if ( !NT_SUCCESS(RtlUnicodeStringToOemString(&OemString,&Unicode,FALSE)) ) + goto ErrExit; + + RtlFreeUnicodeString(&Unicode); + return; + +ErrExit: + RcErrorDialogBox(ED_INITMEMERR, pchTmpConfigFile, NULL); + TerminateVDM(); +} + + + +/* cmdGetAutoexecBat - Creates a temp file to replace c:\autoexec.bat + * + * Entry - Client (DS:DX) pointer to receive file name + * + * EXIT - This routine will Terminate the vdm if it fails + * And will not return + * + * + * The buffer to receive the file name must be at least 64 bytes + */ +VOID cmdGetAutoexecBat (VOID) +{ + UNICODE_STRING Unicode; + OEM_STRING OemString; + ANSI_STRING AnsiString; + + ExpandConfigFiles(FALSE); + + RtlInitAnsiString(&AnsiString, pchTmpAutoexecFile); + if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,TRUE)) ) + goto ErrExit; + + OemString.Buffer = (char *)GetVDMAddr(getDS(),getDX()); + OemString.MaximumLength = 64; + if (!NT_SUCCESS(RtlUnicodeStringToOemString(&OemString,&Unicode,FALSE)) ) + goto ErrExit; + + RtlFreeUnicodeString(&Unicode); + + return; + +ErrExit: + RcErrorDialogBox(ED_INITMEMERR, pchTmpConfigFile, NULL); + TerminateVDM(); // skip cleanup since I insist that we exit! +} + + + +/* + * DeleteConfigFiles - Deletes the temporray config files created + * by cmdGetAutoexecBat and cmdGetConfigSys + */ +VOID DeleteConfigFiles(VOID) +{ + if (pchTmpConfigFile) { +#if DBG + if (!(fShowSVCMsg & KEEPBOOTFILES)) +#endif + DeleteFile(pchTmpConfigFile); + + free(pchTmpConfigFile); + pchTmpConfigFile = NULL; + } + + if (pchTmpAutoexecFile) { +#if DBG + if (!(fShowSVCMsg & KEEPBOOTFILES)) +#endif + DeleteFile(pchTmpAutoexecFile); + + free(pchTmpAutoexecFile); + pchTmpAutoexecFile = NULL; + } + + return; +} + + + +// if it is a config command +// returns pointer to character immediatly following the equal sign +// else +// returns NULL + +PCHAR IsConfigCommand(PCHAR pConfigCommand, int CmdLen, PCHAR pLine) +{ + PCHAR pch; + + if (!_strnicmp(pLine, pConfigCommand, CmdLen)) { + pch = pLine + CmdLen; + while (!isgraph(*pch) && !ISEOL(*pch)) // skip to "=" + pch++; + + if (*pch++ == '=') { + return pch; + } + } + + return NULL; +} + + + + + + +/* + * Preprocesses the specfied config file (config.sys\autoexec.bat) + * into a temporary file. + * + * - expands %SystemRoot% + * - adds SHELL line for config.sys + * + * entry: BOOLEAN bConfig : TRUE - config.sys + * FALSE - autoexec.bat + */ +void ExpandConfigFiles(BOOLEAN bConfig) +{ + DWORD dw, dwRawFileSize; + + HANDLE hRawFile; + HANDLE hTmpFile; + CHAR **ppTmpFile; + CHAR *pRawBuffer; + CHAR *pLine; + CHAR *pTmp; + CHAR *pEnvParam= NULL; + CHAR *pPartyShell=NULL; + CHAR achRawFile[MAX_PATH+12]; + CHAR *lpszzEnv, *lpszName; + CHAR cchEnv; + + dw = GetWindowsDirectory(achRawFile, sizeof(achRawFile)); + dwLenSysRoot = GetShortPathNameA(achRawFile, achSysRoot, sizeof(achSysRoot)); + if (dwLenSysRoot >= sizeof(achSysRoot)) { + dwLenSysRoot = 0; + achSysRoot[0] = '\0'; + } + GetPIFConfigFiles(bConfig, achRawFile); + ppTmpFile = bConfig ? &pchTmpConfigFile : &pchTmpAutoexecFile; + + hRawFile = CreateFile(achRawFile, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hRawFile == (HANDLE)0xFFFFFFFF + || !dwLenSysRoot + || dwLenSysRoot >= sizeof(achSysRoot) + || !(dwRawFileSize = GetFileSize(hRawFile, NULL)) + || dwRawFileSize == 0xFFFFFFFF ) + { + RcErrorDialogBox(ED_BADSYSFILE, achRawFile, NULL); + TerminateVDM(); // skip cleanup since I insist that we exit! + } + + pRawBuffer = malloc(dwRawFileSize+1); + // allocate buffer to save environment settings in autoexec.nt + // I know this is bad to allocate this amount of memory at this + // moment as we dont know if there are anything we want to keep + // at all. This allocation simply provides the following error + // handling easily. + if(!bConfig) { + lpszzEnv = lpszzcmdEnv16 = (PCHAR)malloc(dwRawFileSize); + cchEnv = 0; + } + if (!pRawBuffer || (!bConfig && lpszzcmdEnv16 == NULL)) { + RcErrorDialogBox(ED_INITMEMERR, achRawFile, NULL); + TerminateVDM(); // skip cleanup since I insist that we exit! + } + + if (!cmdCreateTempFile(&hTmpFile,ppTmpFile) + || !ReadFile(hRawFile, pRawBuffer, dwRawFileSize, &dw, NULL) + || dw != dwRawFileSize ) + { + GetTempPath(MAX_PATH, achRawFile); + achRawFile[63] = '\0'; + RcErrorDialogBox(ED_INITTMPFILE, achRawFile, NULL); + TerminateVDM(); // skip cleanup since I insist that we exit! + } + // CHANGE HERE WHEN YOU CHANGE cmdCreateTempFile !!!!!!!!!! + // we depend on the buffer size allocated for the file name + dw = GetShortPathNameA(*ppTmpFile, *ppTmpFile, MAX_PATH +13); + if (dw == 0 || dw > 63) + { + GetTempPath(MAX_PATH, achRawFile); + achRawFile[63] = '\0'; + RcErrorDialogBox(ED_INITTMPFILE, achRawFile, NULL); + TerminateVDM(); // skip cleanup since I insist that we exit! + } + + + // null terminate the buffer so we can use CRT string functions + *(pRawBuffer+dwRawFileSize) = '\0'; + + // ensure no trailing backslash in System Directory + if (*(achSysRoot+dwLenSysRoot-1) == '\\') { + *(achSysRoot + --dwLenSysRoot) = '\0'; + } + + pLine = pRawBuffer; + while (dwRawFileSize) { + // skip leading white space + while (dwRawFileSize && !isgraph(*pLine)) { + pLine++; + dwRawFileSize -= sizeof(CHAR); + } + if (!dwRawFileSize) // anything left to do ? + break; + + + if (bConfig) { + // + // filter out country= setting we will create our own based + // on current country ID and codepage. + // + pTmp = IsConfigCommand(achCOUNTRY, sizeof(achCOUNTRY) - sizeof(CHAR), pLine); + if (pTmp) { + while (dwRawFileSize && !ISEOL(*pLine)) { + pLine++; + dwRawFileSize -= sizeof(CHAR); + } + continue; + } + + // filter out shell= command, saving /E:nn parameter + pTmp = IsConfigCommand(achSHELL, sizeof(achSHELL) - sizeof(CHAR),pLine); + if (pTmp) { + // skip leading white space + while (!isgraph(*pTmp) && !ISEOL(*pTmp)) { + dwRawFileSize -= sizeof(CHAR); + pTmp++; + } + + /* if for a third party shell (not SCS command.com) + * append the whole thing thru /c parameter + * else + * append user specifed /e: parameter + */ + if (!_strnicmp(achSYSROOT,pTmp,sizeof(achSYSROOT)-sizeof(CHAR))) + { + dw = sizeof(achSYSROOT) - sizeof(CHAR); + } + else if (!_strnicmp(achSysRoot,pTmp, strlen(achSysRoot))) + { + dw = strlen(achSysRoot); + } + else { + dw = 0; + } + + if (!dw || + _strnicmp(achCOMMAND,pTmp+dw,sizeof(achCOMMAND)-sizeof(CHAR)) ) + { + pPartyShell = pTmp; + } + else { + do { + while (*pTmp != '/' && !ISEOL(*pTmp)) // save "/e:" + pTmp++; + + if(ISEOL(*pTmp)) + break; + + if (!_strnicmp(pTmp,achENV,sizeof(achENV)-sizeof(CHAR))) + pEnvParam = pTmp; + + pTmp++; + + } while(1); // was: while (!ISEOL(*pTmp)); + // we have break form this loop now, + // and don't need in additional macro.. + + } + + // skip the "shell=" line + while (dwRawFileSize && !ISEOL(*pLine)) { + pLine++; + dwRawFileSize -= sizeof(CHAR); + } + continue; + + } // END, really is "shell=" line! + } + + + /** Filter out PROMPT, SET and PATH from autoexec.nt + for environment merging. The output we prepare here is + a multiple strings buffer which has the format as : + "EnvName_1 NULL EnvValue_1 NULL[EnvName_n NULL EnvValue_n NULL] NULL + We don't take them out from the file because command.com needs + them. + **/ + if (!bConfig) + if (!_strnicmp(pLine, achPROMPT, sizeof(achPROMPT) - 1)){ + // prompt command found. + // the syntax of prompt can be eithe + // prompt xxyyzz or + // prompt=xxyyzz + // + strcpy(lpszzEnv, achPROMPT); // get the name + lpszzEnv += sizeof(achPROMPT); + cchEnv += sizeof(achPROMPT); + pTmp = pLine + sizeof(achPROMPT) - 1; + // skip possible white chars + while (!isgraph(*pTmp) && !ISEOL(*pTmp)) + pTmp++; + if (*pTmp == '=') { + pTmp++; + while(!isgraph(*pTmp) && !ISEOL(*pTmp)) + pTmp++; + } + while(!ISEOL(*pTmp)){ + *lpszzEnv++ = *pTmp++; + cchEnv++; + } + // null terminate this + // it may be "prompt NULL NULL" for delete + // or "prompt NULL something NULL" + *lpszzEnv++ = '\0'; + cchEnv++; + } + else if (!_strnicmp(pLine, achPATH, sizeof(achPATH) - 1)) { + // PATH was found, it has the same syntax as + // PROMPT + strcpy(lpszzEnv, achPATH); + lpszzEnv += sizeof(achPATH); + cchEnv += sizeof(achPATH); + pTmp = pLine + sizeof(achPATH) - 1; + while (!isgraph(*pTmp) && !ISEOL(*pTmp)) + pTmp++; + if (*pTmp == '=') { + pTmp++; + while(!isgraph(*pTmp) && !ISEOL(*pTmp)) + pTmp++; + } + while(!ISEOL(*pTmp)) { + *lpszzEnv++ = *pTmp++; + cchEnv++; + } + *lpszzEnv++ = '\0'; + cchEnv++; + } + else if(!_strnicmp(pLine, achSET, sizeof(achSET) -1 )) { + // SET was found, first search for name + pTmp = pLine + sizeof(achSET) - 1; + while(!isgraph(*pTmp) && !ISEOL(*pTmp)) + *pTmp ++; + // get the name + lpszName = pTmp; + // looking for the '=' + // note that the name can have white characters + while (!ISEOL(*lpszName) && *lpszName != '=') + lpszName++; + if (!ISEOL(*lpszName)) { + // copy the name + while (pTmp < lpszName) { + *lpszzEnv++ = *pTmp++; + cchEnv++; + } + *lpszzEnv++ = '\0'; + cchEnv++; + // discard the '=' + pTmp++; + // grab the value(may be nothing + while (!ISEOL(*pTmp)) { + *lpszzEnv++ = *pTmp++; + cchEnv++; + } + *lpszzEnv++ = '\0'; + cchEnv++; + } + } + + + dw = WriteExpanded(hTmpFile, pLine, dwRawFileSize); + pLine += dw; + dwRawFileSize -=dw; + + WriteFileAssert(hTmpFile,achEOL,sizeof(achEOL) - sizeof(CHAR)); + + } // END, while (dwRawFileSize) + + + + if (bConfig) { + UINT OemCP; + UINT CtryId; + CHAR szCtryId[64]; // expect "nnn" only + + /* Ensure that the country settings are in sync with NT This is + * especially important for DosKrnl file UPCASE tables. The + * doskrnl default is "CTRY_UNITED_STATES, 437". But we add the + * country= line to config.sys, even if is US,437, so that the DOS + * will know where the default country.sys is. + */ + if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCOUNTRY, + szCtryId, sizeof(szCtryId) - 1) ) + { + CtryId = strtoul(szCtryId,NULL,10); + } + else { + CtryId = CTRY_UNITED_STATES; + } + + OemCP = GetOEMCP(); + + sprintf(achRawFile, + "%s=%3.3u,%3.3u,%s\\system32\\%s.sys%s", + achCOUNTRY, CtryId, OemCP, achSysRoot, achCOUNTRY, achEOL); + WriteFileAssert(hTmpFile,achRawFile,strlen(achRawFile)); + + + + /* We cannot allow the user to set an incorrect shell= command + * so we will contruct the correct shell= command appending + * either (in order of precedence): + * 1. /c ThirdPartyShell + * 2. /e:NNNN + * 3. nothing + * + * If there is a third party shell then we must turn the console + * on now since we no longer have control once system32\command.com + * spawns the third party shell. + */ + + // write shell=.... + sprintf(achRawFile, + "%s=%s%s /p %s\\system32", + achSHELL,achSysRoot, achCOMMAND, achSysRoot); + WriteFileAssert(hTmpFile,achRawFile,strlen(achRawFile)); + + // write extra string (/c ... or /e:nnn) + if (pPartyShell && isgraph(*pPartyShell)) { + pTmp = pPartyShell; + while (!ISEOL(*pTmp)) + pTmp++; + } + else if (pEnvParam && isgraph(*pEnvParam)) { + pTmp = pEnvParam; + while (isgraph(*pTmp)) + pTmp++; + } + else { + pTmp = NULL; + } + + if (pTmp) { + *pTmp = '\0'; + if (pPartyShell) { + cmdInitConsole(); + strcpy(achRawFile, " /c "); + strcat(achRawFile, pPartyShell); + } + else if (pEnvParam) { + strcpy(achRawFile, " "); + strcat(achRawFile, pEnvParam); + } + + WriteExpanded(hTmpFile, achRawFile, strlen(achRawFile)); + } + + WriteFileAssert(hTmpFile,achEOL,sizeof(achEOL) - sizeof(CHAR)); + } + + SetEndOfFile(hTmpFile); + CloseHandle(hTmpFile); + CloseHandle(hRawFile); + free(pRawBuffer); + if (!bConfig) { + // shrink(or free) the memory + if (cchEnv && lpszzcmdEnv16) { + // doubld null terminate it + lpszzcmdEnv16[cchEnv++] = '\0'; + // shrink the memory. If it fails, simple keep + // it as is + lpszzEnv = realloc(lpszzcmdEnv16, cchEnv); + if (lpszzEnv != NULL) + lpszzcmdEnv16 = lpszzEnv; + } + else { + free(lpszzcmdEnv16); + lpszzcmdEnv16 = NULL; + } + } + +} + + + + +/* + * WriteExpanded - writes up to dwChars or EOL, expanding %SystemRoot% + * returns number of CHARs processed in buffer + * (not number of bytes actually written) + */ +DWORD WriteExpanded(HANDLE hFile, CHAR *pch, DWORD dwChars) +{ + DWORD dw; + DWORD dwSave = dwChars; + CHAR *pSave = pch; + + + while (dwChars && !ISEOL(*pch)) { + if (*pch == '%' && + !_strnicmp(pch, achSYSROOT, sizeof(achSYSROOT)-sizeof(CHAR)) ) + { + dw = pch - pSave; + if (dw) { + WriteFileAssert(hFile, pSave, dw); + } + + WriteFileAssert(hFile, achSysRoot, dwLenSysRoot); + + pch += sizeof(achSYSROOT)-sizeof(CHAR); + pSave = pch; + dwChars -= sizeof(achSYSROOT)-sizeof(CHAR); + } + else { + pch++; + dwChars -= sizeof(CHAR); + } + } + + dw = pch - pSave; + if (dw) { + WriteFileAssert(hFile, pSave, dw); + } + + return (dwSave - dwChars); +} + + + + +/* + * WriteFileAssert + * + * Cecks for error in wrtiting the temp boot file, + * If one occurs displays warning popup and terminates the vdm. + * + */ +void WriteFileAssert(HANDLE hFile, CHAR *pBuff, DWORD dwBytes) +{ + DWORD dw; + CHAR ach[MAX_PATH]; + + if (!WriteFile(hFile, pBuff, dwBytes, &dw, NULL) || + dw != dwBytes) + { + + GetTempPath(MAX_PATH, ach); + ach[63] = '\0'; + RcErrorDialogBox(ED_INITTMPFILE, ach, NULL); + TerminateVDM(); // skip cleanup since I insist that we exit! + } +} diff --git a/private/mvdm/dos/command/cmddata.c b/private/mvdm/dos/command/cmddata.c new file mode 100644 index 000000000..05d35ad44 --- /dev/null +++ b/private/mvdm/dos/command/cmddata.c @@ -0,0 +1,51 @@ +/* cmddata.c - Misc. SCS global data + * + * + * Modification History: + * + * Sudeepb 22-Apr-1992 Created + */ + +#include "cmd.h" +#include <mvdm.h> + +CHAR lpszComSpec[64+8]; +USHORT cbComSpec=0; +BOOL IsFirstCall = TRUE; +BOOL IsRepeatCall = FALSE; +BOOL IsFirstWOWCheckBinary = TRUE; +BOOL IsFirstVDMInSystem = FALSE; +BOOL SaveWorldCreated; +PCHAR pSCS_ToSync; +PSCSINFO pSCSInfo; +BOOL fBlock = FALSE; +PCHAR pCommand32; +PCHAR pEnv32; +DWORD dwExitCode32; +CHAR cmdHomeDirectory [] = "C:\\"; +CHAR chDefaultDrive; +CHAR comspec[]="COMSPEC="; +BOOL fSoftpcRedirection; +BOOL fSoftpcRedirectionOnShellOut; +CHAR ShortCutInfo[MAX_SHORTCUT_SIZE]; +BOOL DosEnvCreated = FALSE; + +BOOL IsFirstVDM = TRUE; +// FORCEDOS.EXE supported +BOOL DontCheckDosBinaryType = FALSE; +WORD Exe32ActiveCount = 0; + + + +// Redirection Support variables + +VDMINFO VDMInfo; +CHAR *lpszzInitEnvironment = NULL; +WORD cchInitEnvironment = 0; +CHAR *lpszzCurrentDirectories = NULL; +DWORD cchCurrentDirectories = 0; +BYTE * pIsDosBinary; +CHAR *lpszzcmdEnv16 = NULL; +CHAR *lpszzVDMEnv32 = NULL; +DWORD cchVDMEnv32; +VDMENVBLK cmdVDMEnvBlk; diff --git a/private/mvdm/dos/command/cmddisp.c b/private/mvdm/dos/command/cmddisp.c new file mode 100644 index 000000000..e1b93683f --- /dev/null +++ b/private/mvdm/dos/command/cmddisp.c @@ -0,0 +1,56 @@ +/* + * cmddisp.c - SVC dispatch module of command + * + * Modification History: + * + * Sudeepb 17-Sep-1991 Created + */ + +#include "cmd.h" + +#include <cmdsvc.h> +#include <softpc.h> + + +PFNSVC apfnSVCCmd [] = { + cmdExitVDM, //SVC_CMDEXITVDM + cmdGetNextCmd, //SVC_CMDGETNEXTCMD + cmdComSpec, //SVC_CMDCOMSPEC + cmdSaveWorld, //SVC_CMDSAVEWORLD + cmdGetCurrentDir, //SVC_CMDGETCURDIR + cmdSetInfo, //SVC_CMDSETINFO + cmdGetStdHandle, //SVC_GETSTDHANDLE + cmdCheckBinary, //SVC_CMDCHECKBINARY + cmdExec, //SVC_CMDEXEC + cmdInitConsole, //SVC_CMDINITCONSOLE + cmdExecComspec32, //SVC_EXECCOMSPEC32 + cmdReturnExitCode, //SVC_RETURNEXITCODE + cmdGetConfigSys, //SVC_GETCONFIGSYS + cmdGetAutoexecBat, //SVC_GETAUTOEXECBAT + cmdGetKbdLayout, //SVC_GETKBDLAYOUT + cmdGetInitEnvironment, //SVC_GETINITENVIRONMENT + cmdGetStartInfo //SVC_GETSTARTINFO +}; + + +/* cmdDispatch - Dispatch SVC call to right command handler. + * + * Entry - iSvc (SVC byte following SVCop) + * + * Exit - None + * + */ + +BOOL CmdDispatch (ULONG iSvc) +{ +#if DBG + if (iSvc >= SVC_CMDLASTSVC){ + DbgPrint("Unimplemented SVC index for COMMAND %x\n",iSvc); + setCF(1); + return FALSE; + } +#endif + (apfnSVCCmd [iSvc])(); + + return TRUE; +} diff --git a/private/mvdm/dos/command/cmdenv.c b/private/mvdm/dos/command/cmdenv.c new file mode 100644 index 000000000..6bbf509e2 --- /dev/null +++ b/private/mvdm/dos/command/cmdenv.c @@ -0,0 +1,696 @@ + +/* cmdenv.c - Environment supporting functions for command.lib + * + * + * Modification History: + * + * williamh 13-May-1993 Created + */ + +#include "cmd.h" + +#include <cmdsvc.h> +#include <demexp.h> +#include <softpc.h> +#include <mvdm.h> +#include <ctype.h> +#include <memory.h> +#include <oemuni.h> + +#define VDM_ENV_INC_SIZE 512 + +CHAR windir[] = "windir"; +extern BOOL fSeparateWow; + +// Transform the given DOS environment to 32bits environment. +// WARNING!! The environment block we passed to 32bits must be in sort order. +// Therefore, we call RtlSetEnvironmentVariable to do the work +// The result string must be in ANSI character set. +BOOL cmdXformEnvironment(PCHAR pEnv16, PANSI_STRING Env_A) +{ + UNICODE_STRING Name_U, Value_U, Temp_U; + STRING String; + PWCHAR pwch, NewEnv, CurEnv, CurEnvCopy, pTmp; + NTSTATUS Status; + BOOL fFoundComSpec; + USHORT NewEnvLen; + + if (pEnv16 == NULL) + return FALSE; + + // flag true if we alread found comspec envirnment + // !!!! Do we allow two or more comspec in environment???????? + fFoundComSpec = FALSE; + + CurEnv = GetEnvironmentStringsW(); + pwch = CurEnv; + // figure how long the environment strings is + while (*pwch != UNICODE_NULL || *(pwch + 1) != UNICODE_NULL) + pwch++; + + // plus 2 to include the last two NULL chars + CurEnvCopy = malloc((pwch - CurEnv + 2) * sizeof(WCHAR)); + if (!CurEnvCopy) + return FALSE; + + // make a copy of current process environment so we can walk through + // it. The environment can be changed by any threads in the process + // thus is not safe to walk through without a local copy + RtlMoveMemory(CurEnvCopy, CurEnv, (pwch - CurEnv + 2) * sizeof(WCHAR)); + + // create a new environment block. We don't want to change + // any currnt process environment variables, instead, we are + // preparing a new one for the new process. + Status = RtlCreateEnvironment(FALSE, (PVOID *)&NewEnv); + if (!NT_SUCCESS(Status)) { + free(CurEnvCopy); + return FALSE; + } + NewEnvLen = 0; + // now pick up environment we want from the current environment + // and set it to the new environment block + // the variables we want: + // (1). comspec + // (2). current directories settings + + pwch = CurEnvCopy; + + while (*pwch != UNICODE_NULL) { + if (*pwch == L'=') { + // variable names started with L'=' are current directroy settings + pTmp = wcschr(pwch + 1, L'='); + if (pTmp) { + Name_U.Buffer = pwch; + Name_U.Length = (pTmp - pwch) * sizeof(WCHAR); + RtlInitUnicodeString(&Value_U, pTmp + 1); + Status = RtlSetEnvironmentVariable(&NewEnv, &Name_U, &Value_U); + if (!NT_SUCCESS(Status)) { + RtlDestroyEnvironment(NewEnv); + free(CurEnvCopy); + return FALSE; + } + // <name> + <'='> + <value> + <'\0'> + NewEnvLen += Name_U.Length + Value_U.Length + 2 * sizeof(WCHAR); + } + } + else if (!fFoundComSpec) { + fFoundComSpec = !_wcsnicmp(pwch, L"COMSPEC=", 8); + if (fFoundComSpec) { + Name_U.Buffer = pwch; + Name_U.Length = 7 * sizeof(WCHAR); + RtlInitUnicodeString(&Value_U, pwch + 8); + Status = RtlSetEnvironmentVariable(&NewEnv, + &Name_U, + &Value_U + ); + if (!NT_SUCCESS(Status)) { + RtlDestroyEnvironment(NewEnv); + free(CurEnvCopy); + return FALSE; + } + NewEnvLen += Name_U.Length + Value_U.Length + 2 * sizeof(WCHAR); + } + } + pwch += wcslen(pwch) + 1; + } + // we are done with current process environment. + free(CurEnvCopy); + + // now deal with 16bits settings passed from dos. + // characters in 16bits environment are in OEM character set + + // 16bit comspec environment variable + fFoundComSpec = FALSE; + while (*pEnv16 != '\0') { + RtlInitString(&String, pEnv16); + // discard 16bits comspec + if (!fFoundComSpec) { + fFoundComSpec = !_strnicmp(pEnv16, comspec, 8); + if (fFoundComSpec) { + // ignore 16bits comspec environment + pEnv16 += String.Length + 1; + continue; + } + } + Status = RtlOemStringToUnicodeString(&Temp_U, &String, TRUE); + if (!NT_SUCCESS(Status)) { + RtlDestroyEnvironment(NewEnv); + return FALSE; + } + pwch = wcschr(Temp_U.Buffer, L'='); + if (pwch) { + Name_U.Buffer = Temp_U.Buffer; + Name_U.Length = (pwch - Temp_U.Buffer) * sizeof(WCHAR); + RtlInitUnicodeString(&Value_U, pwch + 1); + Status = RtlSetEnvironmentVariable( &NewEnv, &Name_U, &Value_U); + RtlFreeUnicodeString(&Temp_U); + if (!NT_SUCCESS(Status)) { + RtlDestroyEnvironment(NewEnv); + return FALSE; + } + NewEnvLen += Name_U.Length + Value_U.Length + 2 * sizeof(WCHAR); + } + pEnv16 += String.Length + 1; + } + // count the last terminated null char + Temp_U.Length = NewEnvLen + sizeof(WCHAR); + Temp_U.Buffer = NewEnv; + Status = RtlUnicodeStringToAnsiString(Env_A, &Temp_U, TRUE); + RtlDestroyEnvironment(NewEnv); /* don't need it anymore */ + return(NT_SUCCESS(Status)); +} + + + + +/* get ntvdm initial environment. This initial environment is necessary + * for the first instance of command.com before it processing autoexec.bat + * this function strips off an environment headed with "=" and + * replace the comspec with 16bits comspec and upper case all environment vars. + * + * Entry: Client (ES:0) = buffer to receive the environment + * Client (BX) = size in paragraphs of the given buffer + * + * Exit: (BX) = 0 if nothing to copy + * (BX) <= the given size, function okay + * (BX) > given size, (BX) has the required size + */ + +VOID cmdGetInitEnvironment(VOID) +{ + CHAR *lpszzEnvBuffer, *lpszEnv; + WORD cchEnvBuffer; + CHAR *lpszzEnvStrings, * lpszz; + WORD cchString; + WORD cchRemain; + WORD cchIncrement = MAX_PATH; + BOOL fFoundComSpec = FALSE; + BOOL fFoundWindir = FALSE; + BOOL fVarIsWindir = FALSE; + + // if not during the initialization return nothing + if (!IsFirstCall) { + setBX(0); + return; + } + if (cchInitEnvironment == 0) { + // + // If the PROMPT variable is not set, add it as $P$G. This is to + // keep the command.com shell consistent with SCS cmd.exe(which + // always does this) when we don't have a top level cmd shell. + // + { + CHAR *pPromptStr = "PROMPT"; + char ach[2]; + + if (!GetEnvironmentVariable(pPromptStr,ach,1)) { + SetEnvironmentVariable(pPromptStr, "$P$G"); + } + } + + cchRemain = 0; + fFoundComSpec = FALSE; + lpszEnv = + lpszzEnvStrings = GetEnvironmentStrings(); + while (*lpszEnv) { + cchString = strlen(lpszEnv) + 1; + cchVDMEnv32 += cchString; + lpszEnv += cchString; + } + lpszz = lpszzEnvStrings; + + if (lpszzVDMEnv32 != NULL) + free(lpszzVDMEnv32); + lpszzVDMEnv32 = malloc(++cchVDMEnv32); + if (lpszzVDMEnv32 == NULL) { + RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL, + RMB_ICON_BANG | RMB_ABORT); + TerminateVDM(); + } + + RtlMoveMemory(lpszzVDMEnv32, lpszzEnvStrings, cchVDMEnv32); + + while (*lpszz != '\0') { + cchString = strlen(lpszz) + 1; + if (*lpszz != '=') { + + if (!fFoundComSpec && !_strnicmp(lpszz, comspec, 8)){ + fFoundComSpec = TRUE; + lpszz += cchString; + continue; + } + + if (!fFoundWindir && !_strnicmp(lpszz, windir, 6)) { + fFoundWindir = TRUE; + if (fSeparateWow) { + // starting a separate WOW box - flag this one so its + // name won't be converted to uppercase later. + fVarIsWindir = TRUE; + } else { + // starting a DOS app, so remove "windir" to make sure + // they don't think they are running under Windows. + lpszz += cchString; + continue; + } + } + + if (cchRemain < cchString) { + if (cchIncrement < cchString) + cchIncrement = cchString; + lpszzEnvBuffer = + (CHAR *)realloc(lpszzInitEnvironment, + cchInitEnvironment + cchRemain + cchIncrement + ); + if (lpszzEnvBuffer == NULL) { + if (lpszzInitEnvironment != NULL) { + free(lpszzInitEnvironment); + lpszzInitEnvironment = NULL; + } + cchInitEnvironment = 0; + break; + } + lpszzInitEnvironment = lpszzEnvBuffer; + lpszzEnvBuffer += cchInitEnvironment; + cchRemain += cchIncrement; + } + // the environment strings from base is in ANSI and dos needs OEM + AnsiToOemBuff(lpszz, lpszzEnvBuffer, cchString); + // convert the name to upper case -- ONLY THE NAME, NOT VALUE. + if (!fVarIsWindir && (lpszEnv = strchr(lpszzEnvBuffer, '=')) != NULL){ + *lpszEnv = '\0'; + _strupr(lpszzEnvBuffer); + *lpszEnv = '='; + } else { + fVarIsWindir = FALSE; + } + cchRemain -= cchString; + cchInitEnvironment += cchString ; + lpszzEnvBuffer += cchString; + } + lpszz += cchString; + } + FreeEnvironmentStrings(lpszzEnvStrings); + + lpszzEnvBuffer = (CHAR *) realloc(lpszzInitEnvironment, + cchInitEnvironment + 1 + ); + if (lpszzInitEnvironment != NULL ) { + lpszzInitEnvironment = lpszzEnvBuffer; + lpszzInitEnvironment[cchInitEnvironment++] = '\0'; + } + else { + if (lpszzInitEnvironment != NULL) { + free(lpszzInitEnvironment); + lpszzInitEnvironment = NULL; + } + cchInitEnvironment = 0; + } + } + lpszzEnvBuffer = (CHAR *) GetVDMAddr(getES(), 0); + cchEnvBuffer = (WORD)getBX() << 4; + if (cchEnvBuffer < cchInitEnvironment + cbComSpec) { + setBX((USHORT)((cchInitEnvironment + cbComSpec + 15) >> 4)); + return; + } + else { + strncpy(lpszzEnvBuffer, lpszComSpec, cbComSpec); + lpszzEnvBuffer += cbComSpec; + } + if (lpszzInitEnvironment != NULL) { + setBX((USHORT)((cchInitEnvironment + cbComSpec + 15) >> 4)); + memcpy(lpszzEnvBuffer, lpszzInitEnvironment, cchInitEnvironment); + free(lpszzInitEnvironment); + lpszzInitEnvironment = NULL; + cchInitEnvironment = 0; + + } + else + setBX(0); + + return; +} + + + +/** create a DOS environment for DOS. + This is to get 32bits environment(comes with the dos executanle) + and merge it with the environment settings in autoexec.nt so that + COMMAND.COM gets the expected environment. We already created a + double-null terminated string during autoexec.nt parsing. The string + has mutltiple substring: + "EnvName_1 NULL EnvValue_1 NULL[EnvName_n NULL EnvValue_n NULL] NULL" + When name conflicts happened(a environment name was found in both + 16 bits and 32 bits), we do the merging based on the rules: + get 16bits value, expands any environment variables in the string + by using the current environment. + +WARINING !!! The changes made by applications through directly manipulation + in command.com environment segment will be lost. + +**/ +BOOL cmdCreateVDMEnvironment( +PVDMENVBLK pVDMEnvBlk +) +{ +PCHAR p1, p2; +BOOL fFoundComSpec; +BOOL fFoundWindir; +BOOL fVarIsWindir; +DWORD Length; +PCHAR lpszzVDMEnv, lpszzEnv; +CHAR achBuffer[MAX_PATH + 1]; + + pVDMEnvBlk->lpszzEnv = malloc(cchVDMEnv32 + cbComSpec + 1); + if ((lpszzVDMEnv = pVDMEnvBlk->lpszzEnv) == NULL) + return FALSE; + + pVDMEnvBlk->cchRemain = cchVDMEnv32 + cbComSpec + 1; + pVDMEnvBlk->cchEnv = 0; + + // grab the 16bits comspec first + if (cbComSpec && lpszComSpec && *lpszComSpec) { + RtlCopyMemory(lpszzVDMEnv, lpszComSpec, cbComSpec); + pVDMEnvBlk->cchEnv += cbComSpec; + pVDMEnvBlk->cchRemain -= cbComSpec; + lpszzVDMEnv += cbComSpec; + } + if (lpszzVDMEnv32) { + + // go through the given 32bits environmnet and take what we want: + // everything except: + // (1). variable name begin with '=' + // (2). compsec + // (3). string without a '=' -- malformatted environment variable + // (4). windir, so DOS apps don't think they're running under Windows + // Note that strings pointed by lpszzVDMEnv32 are in ANSI character set + + + fFoundComSpec = FALSE; + fFoundWindir = FALSE; + fVarIsWindir = FALSE; + lpszzEnv = lpszzVDMEnv32; + + while (*lpszzEnv) { + Length = strlen(lpszzEnv) + 1; + if (*lpszzEnv != '=' && + (p1 = strchr(lpszzEnv, '=')) != NULL && + (fFoundComSpec || !(fFoundComSpec = _strnicmp(lpszzEnv, + comspec, + 8 + ) == 0)) ){ + if (!fFoundWindir) { + fFoundWindir = (_strnicmp(lpszzEnv, + windir, + 6) == 0); + fVarIsWindir = fFoundWindir; + } + if (!fVarIsWindir || fSeparateWow) { + if (Length >= pVDMEnvBlk->cchRemain) { + lpszzVDMEnv = realloc(pVDMEnvBlk->lpszzEnv, + pVDMEnvBlk->cchEnv + + pVDMEnvBlk->cchRemain + + VDM_ENV_INC_SIZE + ); + if (lpszzVDMEnv == NULL){ + free(pVDMEnvBlk->lpszzEnv); + return FALSE; + } + pVDMEnvBlk->cchRemain += VDM_ENV_INC_SIZE; + pVDMEnvBlk->lpszzEnv = lpszzVDMEnv; + lpszzVDMEnv += pVDMEnvBlk->cchEnv; + } + AnsiToOemBuff(lpszzEnv, lpszzVDMEnv, Length); + if (!fVarIsWindir) { + *(lpszzVDMEnv + (DWORD)(p1 - lpszzEnv)) = '\0'; + _strupr(lpszzVDMEnv); + *(lpszzVDMEnv + (DWORD)(p1 - lpszzEnv)) = '='; + } else { + fVarIsWindir = FALSE; + } + pVDMEnvBlk->cchEnv += Length; + pVDMEnvBlk->cchRemain -= Length; + lpszzVDMEnv += Length; + } + else + fVarIsWindir = FALSE; + } + lpszzEnv += Length; + } + } + *lpszzVDMEnv = '\0'; + pVDMEnvBlk->cchEnv++; + pVDMEnvBlk->cchRemain--; + + if (lpszzcmdEnv16 != NULL) { + lpszzEnv = lpszzcmdEnv16; + + while (*lpszzEnv) { + p1 = lpszzEnv + strlen(lpszzEnv) + 1; + p2 = NULL; + if (*p1) { + p2 = achBuffer; + // expand the strings pointed by p1 + Length = cmdExpandEnvironmentStrings(pVDMEnvBlk, + p1, + p2, + MAX_PATH + 1 + ); + if (Length && Length > MAX_PATH) { + p2 = (PCHAR) malloc(Length); + if (p2 == NULL) { + free(pVDMEnvBlk->lpszzEnv); + return FALSE; + } + cmdExpandEnvironmentStrings(pVDMEnvBlk, + p1, + p2, + Length + ); + } + } + if (!cmdSetEnvironmentVariable(pVDMEnvBlk, + lpszzEnv, + p2 + )){ + if (p2 && p2 != achBuffer) + free(p2); + free(pVDMEnvBlk->lpszzEnv); + return FALSE; + } + lpszzEnv = p1 + strlen(p1) + 1; + } + } + lpszzVDMEnv = realloc(pVDMEnvBlk->lpszzEnv, pVDMEnvBlk->cchEnv); + if (lpszzVDMEnv != NULL) { + pVDMEnvBlk->lpszzEnv = lpszzVDMEnv; + pVDMEnvBlk->cchRemain = 0; + } + return TRUE; +} + + +BOOL cmdSetEnvironmentVariable( +PVDMENVBLK pVDMEnvBlk, +PCHAR lpszName, +PCHAR lpszValue +) +{ + PCHAR p, p1, pEnd; + DWORD ExtraLength, Length, cchValue, cchOldValue; + + pVDMEnvBlk = (pVDMEnvBlk) ? pVDMEnvBlk : &cmdVDMEnvBlk; + + if (pVDMEnvBlk == NULL || lpszName == NULL) + return FALSE; + if (!(p = pVDMEnvBlk->lpszzEnv)) + return FALSE; + pEnd = p + pVDMEnvBlk->cchEnv - 1; + + cchValue = (lpszValue) ? strlen(lpszValue) : 0; + + Length = strlen(lpszName); + while (*p && ((p1 = strchr(p, '=')) == NULL || + (DWORD)(p1 - p) != Length || + _strnicmp(p, lpszName, Length))) + p += strlen(p) + 1; + + if (*p) { + // name was found in the base environment, replace it + p1++; + cchOldValue = strlen(p1); + if (cchValue <= cchOldValue) { + if (!cchValue) { + RtlMoveMemory(p, + p1 + cchOldValue + 1, + (DWORD)(pEnd - p) - cchOldValue + ); + pVDMEnvBlk->cchRemain += Length + cchOldValue + 2; + pVDMEnvBlk->cchEnv -= Length + cchOldValue + 2; + } + else { + RtlCopyMemory(p1, + lpszValue, + cchValue + ); + if (cchValue != cchOldValue) { + RtlMoveMemory(p1 + cchValue, + p1 + cchOldValue, + (DWORD)(pEnd - p1) - cchOldValue + 1 + ); + pVDMEnvBlk->cchEnv -= cchOldValue - cchValue; + pVDMEnvBlk->cchRemain += cchOldValue - cchValue; + } + } + return TRUE; + } + else { + // need more space for the new value + // we delete it from here and fall through + RtlMoveMemory(p, + p1 + cchOldValue + 1, + (DWORD)(pEnd - p1) - cchOldValue + ); + pVDMEnvBlk->cchRemain += Length + 1 + cchOldValue + 1; + pVDMEnvBlk->cchEnv -= Length + 1 + cchOldValue + 1; + } + } + if (cchValue) { + ExtraLength = Length + 1 + cchValue + 1; + if (pVDMEnvBlk->cchRemain < ExtraLength) { + p = realloc(pVDMEnvBlk->lpszzEnv, + pVDMEnvBlk->cchEnv + pVDMEnvBlk->cchRemain + ExtraLength + ); + if (p == NULL) + return FALSE; + pVDMEnvBlk->lpszzEnv = p; + pVDMEnvBlk->cchRemain += ExtraLength; + } + p = pVDMEnvBlk->lpszzEnv + pVDMEnvBlk->cchEnv - 1; + RtlCopyMemory(p, lpszName, Length + 1); + _strupr(p); + p += Length; + *p++ = '='; + RtlCopyMemory(p, lpszValue, cchValue + 1); + *(p + cchValue + 1) = '\0'; + pVDMEnvBlk->cchEnv += ExtraLength; + pVDMEnvBlk->cchRemain -= ExtraLength; + return TRUE; + } + return FALSE; + +} + + +DWORD cmdExpandEnvironmentStrings( +PVDMENVBLK pVDMEnvBlk, +PCHAR lpszSrc, +PCHAR lpszDst, +DWORD cchDst +) +{ + + + DWORD RequiredLength, RemainLength, Length; + PCHAR p1; + + RequiredLength = 0; + RemainLength = (lpszDst) ? cchDst : 0; + pVDMEnvBlk = (pVDMEnvBlk) ? pVDMEnvBlk : &cmdVDMEnvBlk; + if (pVDMEnvBlk == NULL || lpszSrc == NULL) + return 0; + + while(*lpszSrc) { + if (*lpszSrc == '%') { + p1 = strchr(lpszSrc + 1, '%'); + if (p1 != NULL) { + if (p1 == lpszSrc + 1) { // a "%%" + lpszSrc += 2; + continue; + } + *p1 = '\0'; + Length = cmdGetEnvironmentVariable(pVDMEnvBlk, + lpszSrc + 1, + lpszDst, + RemainLength + ); + *p1 = '%'; + lpszSrc = p1 + 1; + if (Length) { + if (Length < RemainLength) { + RemainLength -= Length; + lpszDst += Length; + } + else { + RemainLength = 0; + Length --; + } + RequiredLength += Length; + } + continue; + } + else { + RequiredLength++; + if (RemainLength) { + *lpszDst++ = *lpszSrc; + RemainLength--; + } + lpszSrc++; + continue; + } + } + else { + RequiredLength++; + if (RemainLength) { + *lpszDst++ = *lpszSrc; + RemainLength--; + } + lpszSrc++; + } + } // while(*lpszSrc) + RequiredLength++; + if (RemainLength) + *lpszDst = '\0'; + return RequiredLength; +} + + +DWORD cmdGetEnvironmentVariable( +PVDMENVBLK pVDMEnvBlk, +PCHAR lpszName, +PCHAR lpszValue, +DWORD cchValue +) +{ + + DWORD RequiredLength, Length; + PCHAR p, p1; + + pVDMEnvBlk = (pVDMEnvBlk) ? pVDMEnvBlk : &cmdVDMEnvBlk; + if (pVDMEnvBlk == NULL || lpszName == NULL) + return 0; + + RequiredLength = 0; + Length = strlen(lpszName); + + // if the name is "windir", get its value from ntvdm process's environment + // for DOS because we took it out of the environment block the application + // will see. + if (Length == 6 && !fSeparateWow && !_strnicmp(lpszName, windir, 6)) { + return(GetEnvironmentVariableOem(lpszName, lpszValue, cchValue)); + } + + if (p = pVDMEnvBlk->lpszzEnv) { + while (*p && ((p1 = strchr(p, '=')) == NULL || + (DWORD)(p1 - p) != Length || + _strnicmp(lpszName, p, Length))) + p += strlen(p) + 1; + if (*p) { + RequiredLength = strlen(p1 + 1); + if (cchValue > RequiredLength && lpszValue) + RtlCopyMemory(lpszValue, p1 + 1, RequiredLength + 1); + else + RequiredLength++; + } + } + return RequiredLength; +} diff --git a/private/mvdm/dos/command/cmdexec.c b/private/mvdm/dos/command/cmdexec.c new file mode 100644 index 000000000..61af4c591 --- /dev/null +++ b/private/mvdm/dos/command/cmdexec.c @@ -0,0 +1,650 @@ +/* cmdexec.c - Misc SCS routines for non-dos exec and re-entering + * the DOS. + * + * + * Modification History: + * + * Sudeepb 22-Apr-1992 Created + */ + +#include "cmd.h" + +#include <cmdsvc.h> +#include <softpc.h> +#include <mvdm.h> +#include <ctype.h> +#include <oemuni.h> +#include <wowcmpat.h> + +//***************************************************************************** +// IsWowAppRunnable +// +// Returns FALSE if the WOW-specific compatibility flags for the specified +// task include the bit WOWCF_NOTDOSSPAWNABLE. This is done mostly for +// "dual mode" executables, e.g., Windows apps that have a real program +// as a DOS stub. Certain apps that are started via a DOS command shell, +// for example PWB, really do expect to be started as a DOS app, not a WOW +// app. For these apps, the compatibility bit should be set in the +// registry. +// +//***************************************************************************** + +BOOL IsWowAppRunnable(LPSTR lpAppName) +{ + BOOL Result = TRUE; + LONG lError; + HKEY hKey = 0; + char szModName[9]; + char szHexAsciiFlags[12]; + DWORD dwType = REG_SZ; + DWORD cbData = sizeof(szHexAsciiFlags); + ULONG ul = 0; + LPSTR pStrt, pEnd; + SHORT len; + + lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows NT\\CurrentVersion\\WOW\\Compatibility", + 0, + KEY_QUERY_VALUE, + &hKey + ); + + if (ERROR_SUCCESS != lError) { + goto Cleanup; + } + + // + // The following code strips the file name (<9 chars) out of a dos + // path name. + // + + pStrt = strrchr (lpAppName, '\\'); + + if (pStrt==NULL) + pStrt = lpAppName; + else + pStrt++; + + if ( (pEnd = strchr (pStrt, '.')) == NULL) + strncpy (szModName, pStrt, 9); + + else { + len = (SHORT) (pEnd - pStrt); + if (len>8) goto Cleanup; + strncpy (szModName, pStrt, len); + szModName[len] = '\0'; + } + + + // + // Look for the file name in the registry + // + + lError = RegQueryValueEx( hKey, + szModName, + 0, + &dwType, + szHexAsciiFlags, + &cbData + ); + + if (ERROR_SUCCESS != lError) { + goto Cleanup; + } + + if (REG_SZ != dwType) { + goto Cleanup; + } + + // + // Force the string to lowercase for the convenience of sscanf. + // + + _strlwr(szHexAsciiFlags); + + // + // sscanf() returns the number of fields converted. + // + + if (1 != sscanf(szHexAsciiFlags, "0x%lx", &ul)) { + goto Cleanup; + } + + if ((ul & WOWCF_NOTDOSSPAWNABLE) != 0) + Result = FALSE; + +Cleanup: + if (hKey) { + RegCloseKey(hKey); + } + + return Result; +} + +/* cmdCheckBinary - check that the supplied binary name is a 32bit binary + * + * + * Entry - Client (DS:DX) - pointer to pathname for the executable to be tested + * Client (ES:BX) - pointer to parameter block + * + * EXIT - SUCCESS Client (CY) clear + * FAILURE Client (CY) set + * Client (AX) - error_not_enough_memory if command tail + * cannot accomodate /z + * - error_file_not_found if patname not found + */ + +VOID cmdCheckBinary (VOID) +{ + + LPSTR lpAppName; + ULONG BinaryType; + PPARAMBLOCK lpParamBlock; + PCHAR lpCommandTail,lpTemp; + ULONG AppNameLen,CommandTailLen = 0; + USHORT CommandTailOff,CommandTailSeg,usTemp; + NTSTATUS Status; + UNICODE_STRING Unicode; + OEM_STRING OemString; + ANSI_STRING AnsiString; + + + if(DontCheckDosBinaryType){ + setCF(0); + return; // DOS Exe + } + + lpAppName = (LPSTR) GetVDMAddr (getDS(),getDX()); + + Unicode.Buffer = NULL; + AnsiString.Buffer = NULL; + RtlInitString((PSTRING)&OemString, lpAppName); + Status = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE); + if ( NT_SUCCESS(Status) ) { + Status = RtlUnicodeStringToAnsiString(&AnsiString, &Unicode, TRUE); + } + if ( !NT_SUCCESS(Status) ) { + Status = RtlNtStatusToDosError(Status); + } + else if (GetBinaryType (AnsiString.Buffer,(LPLONG)&BinaryType) == FALSE) + { + Status = GetLastError(); + } + + if (Unicode.Buffer != NULL) { + RtlFreeUnicodeString( &Unicode ); + } + if (AnsiString.Buffer != NULL) { + RtlFreeAnsiString( &AnsiString); + } + + if (Status){ + setCF(1); + setAX((USHORT)Status); + return; // Invalid path + } + + + if (BinaryType == SCS_DOS_BINARY) { + setCF(0); + return; // DOS Exe + } + // Prevent certain WOW apps from being spawned by DOS exe's + // This is for win31 compatibility + else if (BinaryType == SCS_WOW_BINARY) { + if (!IsWowAppRunnable(lpAppName)) { + setCF(0); + return; // Run as DOS Exe + } + } + + + if (VDMForWOW && BinaryType == SCS_WOW_BINARY && IsFirstWOWCheckBinary) { + IsFirstWOWCheckBinary = FALSE; + setCF(0); + return; // Special Hack for krnl286.exe + } + + // dont allow running 32bit binaries from autoexec.nt. Reason is that + // running non-dos binary requires that we should have read the actual + // command from GetNextVDMCommand. Otherwise the whole design gets into + // synchronization problems. + + if (IsFirstCall) { + setCF(1); + setAX((USHORT)ERROR_FILE_NOT_FOUND); + return; + } + + // Its a 32bit exe, replace the command with "command.com /z" and add the + // original binary name to command tail. + + AppNameLen = strlen (lpAppName); + + lpParamBlock = (PPARAMBLOCK) GetVDMAddr (getES(),getBX()); + + if (lpParamBlock) { + CommandTailOff = FETCHWORD(lpParamBlock->OffCmdTail); + CommandTailSeg = FETCHWORD(lpParamBlock->SegCmdTail); + + lpCommandTail = (PCHAR) GetVDMAddr (CommandTailSeg,CommandTailOff); + + if (lpCommandTail){ + CommandTailLen = *(PCHAR)lpCommandTail; + lpCommandTail++; // point to the actual command tail + if (CommandTailLen) + CommandTailLen++; // For CR + } + + // We are adding 3 below for "/z<space>" and anothre space between + // AppName and CommandTail. + + if ((3 + AppNameLen + CommandTailLen ) > 128){ + setCF(1); + setAX((USHORT)ERROR_NOT_ENOUGH_MEMORY); + return; + } + } + + // copy the stub command.com name + strcpy ((PCHAR)&pSCSInfo->SCS_ComSpec,lpszComSpec+8); + lpTemp = (PCHAR) &pSCSInfo->SCS_ComSpec; + lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0)); + usTemp = (USHORT)((ULONG)lpTemp >> 4); + setDS(usTemp); + usTemp = (USHORT)((ULONG)lpTemp & 0x0f); + setDX((usTemp)); + + // Form the command tail, first "3" is for "/z " + pSCSInfo->SCS_CmdTail [0] = (UCHAR)(3 + + AppNameLen + + CommandTailLen); + RtlCopyMemory ((PCHAR)&pSCSInfo->SCS_CmdTail[1],"/z ",3); + strcpy ((PCHAR)&pSCSInfo->SCS_CmdTail[4],lpAppName); + if (CommandTailLen) { + pSCSInfo->SCS_CmdTail[4+AppNameLen] = ' '; + RtlCopyMemory ((PCHAR)((ULONG)&pSCSInfo->SCS_CmdTail[4]+AppNameLen+1), + lpCommandTail, + CommandTailLen); + } + else { + pSCSInfo->SCS_CmdTail[4+AppNameLen] = 0xd; + } + + // Set the parameter Block + if (lpParamBlock) { + STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,lpParamBlock->SegEnv); + STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,lpParamBlock->pFCB1); + STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,lpParamBlock->pFCB2); + } + else { + STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,0); + STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,0); + STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,0); + } + + lpTemp = (PCHAR) &pSCSInfo->SCS_CmdTail; + lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0)); + usTemp = (USHORT)((ULONG)lpTemp & 0x0f); + STOREWORD(pSCSInfo->SCS_ParamBlock.OffCmdTail,usTemp); + usTemp = (USHORT)((ULONG)lpTemp >> 4); + STOREWORD(pSCSInfo->SCS_ParamBlock.SegCmdTail,usTemp); + + lpTemp = (PCHAR) &pSCSInfo->SCS_ParamBlock; + lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0)); + usTemp = (USHORT)((ULONG)lpTemp >> 4); + setES (usTemp); + usTemp = (USHORT)((ULONG)lpTemp & 0x0f); + setBX (usTemp); + + setCF(0); + return; +} + +#define MAX_DIR 68 + +VOID cmdCreateProcess ( VOID ) +{ + + VDMINFO VDMInfoForCount; + STARTUPINFO StartupInfo; + PROCESS_INFORMATION ProcessInformation; + HANDLE hStd16In,hStd16Out,hStd16Err; + CHAR CurDirVar [] = "=?:"; + CHAR Buffer [MAX_DIR]; + CHAR *CurDir = Buffer; + DWORD dwRet; + BOOL Status; + NTSTATUS NtStatus; + UNICODE_STRING Unicode; + OEM_STRING OemString; + LPVOID lpNewEnv=NULL; + PSTD_HANDLES pStdHandles; + ANSI_STRING Env_A; + + // we have one more 32 executable active + Exe32ActiveCount++; + + // Increment the Re-enterancy count for the VDM + VDMInfoForCount.VDMState = INCREMENT_REENTER_COUNT; + GetNextVDMCommand (&VDMInfoForCount); + + RtlZeroMemory((PVOID)&StartupInfo,sizeof(STARTUPINFO)); + StartupInfo.cb = sizeof(STARTUPINFO); + + CurDirVar [1] = chDefaultDrive; + + dwRet = GetEnvironmentVariable (CurDirVar,Buffer,MAX_DIR); + + if (dwRet == 0 || dwRet == MAX_DIR) + CurDir = NULL; + + pStdHandles = (PSTD_HANDLES) GetVDMAddr (getSS(), getBP()); + if ((hStd16In = (HANDLE) FETCHDWORD(pStdHandles->hStdIn)) != (HANDLE)-1) + SetStdHandle (STD_INPUT_HANDLE, hStd16In); + + if ((hStd16Out = (HANDLE) FETCHDWORD(pStdHandles->hStdOut)) != (HANDLE)-1) + SetStdHandle (STD_OUTPUT_HANDLE, hStd16Out); + + if ((hStd16Err = (HANDLE) FETCHDWORD(pStdHandles->hStdErr)) != (HANDLE)-1) + SetStdHandle (STD_ERROR_HANDLE, hStd16Err); + + /* + * Warning, pEnv32 currently points to an ansi environment. + * The DOS is using an ANSI env which isn't quite correct. + * If the DOS is changed to use an OEM env then we will + * have to convert the env back to ansi before spawning + * non-dos exes ?!? + * 16-Jan-1993 Jonle + */ + + Env_A.Buffer = NULL; + + RtlInitString((PSTRING)&OemString, pCommand32); + NtStatus = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE); + if (NT_SUCCESS(NtStatus)) { + NtStatus = RtlUnicodeStringToAnsiString((PANSI_STRING)&OemString, &Unicode, FALSE); + RtlFreeUnicodeString( &Unicode ); + } + if (!NT_SUCCESS(NtStatus)) { + SetLastError(RtlNtStatusToDosError(NtStatus)); + Status = FALSE; + } + else { + if (pEnv32 != NULL && !cmdXformEnvironment (pEnv32, &Env_A)) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + Status = FALSE; + } + else { + + Status = CreateProcess ( + NULL, + (LPTSTR)pCommand32, + NULL, + NULL, + TRUE, + CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE, + Env_A.Buffer, + (LPTSTR)CurDir, + &StartupInfo, + &ProcessInformation); + } + } + + if (Status == FALSE) + dwExitCode32 = GetLastError (); + + if (hStd16In != (HANDLE)-1) + SetStdHandle (STD_INPUT_HANDLE, SCS_hStdIn); + + if (hStd16Out != (HANDLE)-1) + SetStdHandle (STD_OUTPUT_HANDLE, SCS_hStdOut); + + if (hStd16Err != (HANDLE)-1) + SetStdHandle (STD_ERROR_HANDLE, SCS_hStdErr); + + if (Status) { + ResumeThread (ProcessInformation.hThread); + WaitForSingleObject(ProcessInformation.hProcess, (DWORD)-1); + GetExitCodeProcess (ProcessInformation.hProcess, &dwExitCode32); + CloseHandle (ProcessInformation.hProcess); + CloseHandle (ProcessInformation.hThread); + } + + if (Env_A.Buffer) + RtlFreeAnsiString(&Env_A); + + // Decrement the Re-enterancy count for the VDM + VDMInfoForCount.VDMState = DECREMENT_REENTER_COUNT; + GetNextVDMCommand (&VDMInfoForCount); + + // one less 32 executable active + Exe32ActiveCount--; + + // Kill this thread + ExitThread (0); +} + + +VOID cmdExec32 (PCHAR pCmd32, PCHAR pEnv) +{ + + DWORD dwThreadId; + HANDLE hThread; + + pCommand32 = pCmd32; + pEnv32 = pEnv; + + CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | + (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1); + + nt_block_event_thread(0); + fSoftpcRedirectionOnShellOut = fSoftpcRedirection; + fBlock = TRUE; + + if((hThread = CreateThread (NULL, + 0, + (LPTHREAD_START_ROUTINE)cmdCreateProcess, + NULL, + 0, + &dwThreadId)) == FALSE) { + setCF(0); + setAL((UCHAR)GetLastError()); + nt_resume_event_thread(); + nt_std_handle_notification(fSoftpcRedirectionOnShellOut); + fBlock = FALSE; + CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | + (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1); + return; + } + else + CloseHandle (hThread); + + // Wait for next command to be re-entered + VDMInfo.VDMState = NO_PARENT_TO_WAKE | RETURN_ON_NO_COMMAND; + VDMInfo.EnviornmentSize = 0; + VDMInfo.ErrorCode = 0; + VDMInfo.CmdSize = 0; + VDMInfo.TitleLen = 0; + VDMInfo.ReservedLen = 0; + VDMInfo.DesktopLen = 0; + VDMInfo.CurDirectoryLen = 0; + GetNextVDMCommand (&VDMInfo); + if (VDMInfo.CmdSize > 0){ + setCF(1); + IsRepeatCall = TRUE; + } + else { + setCF(0); + setAL((UCHAR)dwExitCode32); + nt_resume_event_thread(); + nt_std_handle_notification(fSoftpcRedirectionOnShellOut); + fBlock = FALSE; + } + + + CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | + (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1); + return; +} + +/* cmdExecComspec32 - Exec 32bit COMSPEC + * + * + * Entry - Client (ES) - environment segment + * Client (AL) - default drive + * + * EXIT - SUCCESS Client (CY) Clear - AL has return error code + * FAILURE Client (CY) set - means DOS is being re-entered + */ + +VOID cmdExecComspec32 (VOID) +{ + + CHAR Buffer[MAX_PATH]; + DWORD dwRet; + PCHAR pEnv; + + dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH); + + if (dwRet == 0 || dwRet >= MAX_PATH){ + setCF(0); + setAL((UCHAR)ERROR_BAD_ENVIRONMENT); + return; + } + + pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0); + + chDefaultDrive = (CHAR)(getAL() + 'A'); + + cmdExec32 (Buffer,pEnv); + + return; +} + +/* cmdExec - Exec a non-dos binary + * + * + * Entry - Client (DS:SI) - command to execute + * Client (AL) - default drive + * Client (ES) - environment segment + * Client (SS:BP) - Pointer to STD_HANDLES + * Client (AH) - if 1 means do "cmd /c command" else "command" + * + * EXIT - SUCCESS Client (CY) Clear - AL has return error code + * FAILURE Client (CY) set - means DOS is being re-entered + */ + +VOID cmdExec (VOID) +{ + + DWORD i; + DWORD dwRet; + PCHAR pCommandTail; + PCHAR pEnv; + CHAR Buffer[MAX_PATH]; + + pCommandTail = (PCHAR) GetVDMAddr ((USHORT)getDS(),(USHORT)getSI()); + pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0); + for (i=0 ; i<124 ; i++) { + if (pCommandTail[i] == 0x0d){ + pCommandTail[i] = 0; + break; + } + } + + if (i == 124){ + setCF(0); + setAL((UCHAR)ERROR_BAD_FORMAT); + return; + } + + chDefaultDrive = (CHAR)(getAL() + 'A'); + + if (getAH() == 0) { + cmdExec32 (pCommandTail,pEnv); + } + else { + dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH); + + if (dwRet == 0 || dwRet >= MAX_PATH){ + setCF(0); + setAL((UCHAR)ERROR_BAD_ENVIRONMENT); + return; + } + + if ((dwRet + 4 + strlen(pCommandTail)) > MAX_PATH) { + setCF(0); + setAL((UCHAR)ERROR_BAD_ENVIRONMENT); + return; + } + + strcat (Buffer, " /c "); + strcat (Buffer, pCommandTail); + cmdExec32 (Buffer,pEnv); + } + + return; +} + +/* cmdReturnExitCode - command.com has run a dos binary and returing + * the exit code. + * + * Entry - Client (DX) - exit code + * Client (AL) - current drive + * Client (BX:CX) - RdrInfo address + * + * Exit + * Client Carry Set - Reenter i.e. a new DOS binary to execute. + * Client Carry Clear - This shelled out session is over. + */ + +VOID cmdReturnExitCode (VOID) +{ +VDMINFO VDMInfo; +PREDIRCOMPLETE_INFO pRdrInfo; + + VDMInfo.VDMState = RETURN_ON_NO_COMMAND; + VDMInfo.EnviornmentSize = 0; + VDMInfo.ErrorCode = (ULONG)getDX(); + VDMInfo.CmdSize = 0; + VDMInfo.TitleLen = 0; + VDMInfo.ReservedLen = 0; + VDMInfo.DesktopLen = 0; + VDMInfo.CurDirectoryLen = 0; + + + CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | + (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1); + + nt_block_event_thread(0); + fBlock = TRUE; + + // a dos program just terminate, inherit its current directories + // and tell base too. + cmdUpdateCurrentDirectories((BYTE)getAL()); + + // Check for any copying needed for redirection + pRdrInfo = (PREDIRCOMPLETE_INFO) (((ULONG)getBX() << 16) + (ULONG)getCX()); + + if (cmdCheckCopyForRedirection (pRdrInfo) == FALSE) + VDMInfo.ErrorCode = ERROR_NOT_ENOUGH_MEMORY; + + GetNextVDMCommand (&VDMInfo); + if (VDMInfo.CmdSize > 0){ + setCF(1); + IsRepeatCall = TRUE; + } + else { + setCF(0); + setAL((UCHAR)dwExitCode32); + nt_resume_event_thread(); + nt_std_handle_notification(fSoftpcRedirectionOnShellOut); + fBlock = FALSE; + } + + CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) | + (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1); + + return; +} diff --git a/private/mvdm/dos/command/cmdexit.c b/private/mvdm/dos/command/cmdexit.c new file mode 100644 index 000000000..ead4c1434 --- /dev/null +++ b/private/mvdm/dos/command/cmdexit.c @@ -0,0 +1,31 @@ +/* cmdexit.c - Exit related SVC routines + * + * cmdExit + * + * Modification History: + * + * Sudeepb 05-Jul-1991 Created + */ + +#include "cmd.h" + +#include <cmdsvc.h> +#include <softpc.h> +#include <winbase.h> + +/* cmdExitVDM - Terminate the VDM + * + * + * Entry - None + * + * Exit - None + * + * + * + */ + +VOID cmdExitVDM (VOID) +{ + // Kill the VDM process + TerminateVDM(); +} diff --git a/private/mvdm/dos/command/cmdkeyb.c b/private/mvdm/dos/command/cmdkeyb.c new file mode 100644 index 000000000..f32a394d7 --- /dev/null +++ b/private/mvdm/dos/command/cmdkeyb.c @@ -0,0 +1,251 @@ +/* cmdkeyb.c - Keyboard layout support routines + * + * + * Modification History: + * + * YST 14-Jan_1993 Created + */ + +#include "cmd.h" +#include <winconp.h> +#include <cmdsvc.h> +#include <softpc.h> +#include <mvdm.h> +#include <ctype.h> +#include <string.H> +#include "cmdkeyb.h" + +CHAR szPrev[5] = "US"; +INT iPrevCP = 437; +CHAR szPrevKbdID[8] = ""; + +extern BOOL bPifFastPaste; + + + + +/************************************************************************\ +* +* FUNCTION: VOID cmdGetKbdLayout( VOID ) +* +* Input Client (DX) = 0 - Keyb.com not installed +* 1 - Keyb.com installed +* Client (DS:SI) = pointer where exe name has to be placed +* Client (DS:CX) = pointer where command options are placed +* +* Output +* Success (DX = 1 ) +* Client (DS:SI) = Keyb.com execuatable string +* Client (DS:CX) = command options +* +* Failure (DX = 0) +* +* COMMENTS: This function check KEYBOARD ID for Win session +* and if ID != US then return lines with +* filename and options to COMMAND.COM +* +* If bPifFastPaste is FALSE, then we always run kb16 +* for all keyboard ID including US, to give us a more +* bios compatible Int 9 handler. 10-Jun-1993 Jonle +* +* +* HISTORY: 01/05/93 YSt Created. +* +\************************************************************************/ + +VOID cmdGetKbdLayout( VOID ) +{ + INT iSize,iSaveSize; + CHAR szKeybCode[12]; + CHAR szDir[MAX_PATH+15]; + CHAR szBuf[28]; + CHAR szNewKbdID[8]; + CHAR szAutoLine[MAX_PATH+40]; + PCHAR pVDMKeyb; + + INT iKeyb; + HKEY hKey; + DWORD dwType; + DWORD retCode; + INT iNewCP; + DWORD cbData; + OFSTRUCT ofstr; + + + +// Get information about 16 bit KEYB.COM from VDM + iKeyb = getDX(); + + +// Get Keyboard Layout Code (hec string) + + if (!GetConsoleKeyboardLayoutName(szKeybCode)) { + goto NoInstallkb16; + } + + +// if we have US code 0000409 and KB16 not installed then +// do nothing and return + + if(bPifFastPaste && + !strcmp(szKeybCode, US_CODE) && !iKeyb) { + goto NoInstallkb16; + } + +// Get keyboard ID from REGISTRY file. +// Default is US + + // OPEN THE KEY. + sprintf(szAutoLine, "%s%s", KBDLAYOUT_PATH, DOSCODES_PATH); + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, // Key handle at root level. + szAutoLine, // Path name of child key. + 0, // Reserved. + KEY_EXECUTE, // Requesting read access. + &hKey); // Address of key to be returned. + +// If retCode != 0 then we cannot find section in Register file + if (retCode) + { + goto NoInstallkb16; + } + + + dwType = REG_SZ; + cbData = sizeof(szBuf); +// Query for line from REGISTER file + retCode = RegQueryValueEx( hKey, + szKeybCode, + NULL, + &dwType, + szBuf, + &cbData); + + RegCloseKey(hKey); + + if (retCode) + { + goto NoInstallkb16; + } + + // look for keyboard id number. For Daytona, Turkish and Italian both + // have one key code and two layouts. + szNewKbdID[0] = '\0'; + dwType = REG_SZ; + cbData = sizeof(szNewKbdID); + sprintf(szAutoLine, "%s%s", KBDLAYOUT_PATH, DOSIDS_PATH); + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + szAutoLine, + 0, + KEY_EXECUTE, + &hKey + ) == ERROR_SUCCESS) { + if (RegQueryValueEx( hKey, + szKeybCode, + NULL, + &dwType, + szNewKbdID, + &cbData + ) != ERROR_SUCCESS) + szNewKbdID[0] = '\0'; + + RegCloseKey(hKey); + } + + iNewCP = GetConsoleCP(); + + +// If keycode, code page and keyboard id aren't changed, do nothing more + + if(bPifFastPaste && iNewCP == iPrevCP && + !_stricmp(szBuf, szPrev) && + !_stricmp(szNewKbdID, szPrevKbdID)) { + goto NoInstallkb16; + } + + iSaveSize = iSize = GetSystemDirectory(szDir, MAX_PATH); + + if (iSize > MAX_PATH) { + goto NoInstallkb16; + } + + +// If keyboard ID not found or it is US then return + + if( bPifFastPaste && + !_stricmp(szBuf, DEFAULT_KB_ID) && !iKeyb) { + goto NoInstallkb16; + } + +// Create line: SYSTEMROOT\KB16.COM + + szDir[iSaveSize] = '\0'; + +// Copy this line for COMMAND.COM + + pVDMKeyb = (PCHAR) GetVDMAddr((USHORT) getDS(), (USHORT) getSI()); + + if ((iSaveSize+sizeof(KEYB_COM)) > 128){ + goto NoInstallkb16; + } + + + sprintf(szAutoLine, "%s%s", + szDir, // System directory + KEYB_COM // keyb.com + ); + +// if KB16.COM not exist then return + dwType = GetFileAttributes(szAutoLine); + if (dwType == 0xFFFFFFFF || (dwType & FILE_ATTRIBUTE_DIRECTORY) != 0) + { + goto NoInstallkb16; + } + + strcpy(pVDMKeyb,szAutoLine); + +// Checking KEYBOARD.SYS + sprintf(szAutoLine, "%s%s", + szDir, // System directory + KEYBOARD_SYS // keyb.com + ); + + dwType = GetFileAttributes(szAutoLine); + if (dwType == 0xFFFFFFFF || (dwType & FILE_ATTRIBUTE_DIRECTORY) != 0) + { + goto NoInstallkb16; + } + + +// Create line: XX,YYY,SYSTEMROOT\Keyboard.sys + pVDMKeyb = (PCHAR) GetVDMAddr((USHORT) getDS(), (USHORT) getCX()); + + sprintf(szAutoLine, " %s,%d,%s%s", + szBuf, // keyboard code + iNewCP, // new code page + szDir, // System directory + KEYBOARD_SYS // keyboard.sys + ); + iSize = strlen(szAutoLine); + if (szNewKbdID[0] != '\0') { + sprintf(&szAutoLine[iSize], " /ID:%s", szNewKbdID); + iSize = strlen(szAutoLine); + } + szAutoLine[iSize] = 0xd; + *pVDMKeyb = iSize; + RtlMoveMemory(pVDMKeyb + 1, szAutoLine, iSize + 1); + + +// Save new layout ID and code page for next call + strcpy(szPrev, szBuf); + strcpy(szPrevKbdID, szNewKbdID); + iPrevCP = iNewCP; + + setDX(1); + return; + +NoInstallkb16: + setDX(0); + cmdInitConsole(); // make sure conoutput is on + return; + +} diff --git a/private/mvdm/dos/command/cmdkeyb.h b/private/mvdm/dos/command/cmdkeyb.h new file mode 100644 index 000000000..a7c0e5578 --- /dev/null +++ b/private/mvdm/dos/command/cmdkeyb.h @@ -0,0 +1,18 @@ +/* cmdkeyb.h - Keyboard layout support routines + * include file for CMDKEYB.C + * + * Modification History: + * + * YST 14-Jan_1993 Created + */ + + +#define DOSKEYBCODES "DOSKeybCodes" // section name in layout.inf +#define LAYOUT_FILE "\\LAYOUT.INF" // INF file name +#define DEFAULT_KB_ID "US" // Default keyboard ID +#define KEYBOARD_SYS "\\KEYBOARD.SYS" // Data file for KEYB.COM +#define US_CODE "00000409" // Code for US keyboard in REGISTER.INI +#define KEYB_COM "\\KB16.COM" // File name for keyboard program +#define KBDLAYOUT_PATH "System\\CurrentControlSet\\Control\\Keyboard Layout\\" +#define DOSCODES_PATH "DosKeybCodes" +#define DOSIDS_PATH "DosKeybIDs" diff --git a/private/mvdm/dos/command/cmdmisc.c b/private/mvdm/dos/command/cmdmisc.c new file mode 100644 index 000000000..4e8ed2557 --- /dev/null +++ b/private/mvdm/dos/command/cmdmisc.c @@ -0,0 +1,897 @@ +/* cmdmisc.c - Misc. SVC routines of Command.lib + * + * + * Modification History: + * + * Sudeepb 17-Sep-1991 Created + */ + +#include "cmd.h" + +#include <cmdsvc.h> +#include <demexp.h> +#include <softpc.h> +#include <mvdm.h> +#include <ctype.h> +#include <memory.h> +#include "oemuni.h" +#include "nt_pif.h" +#include "nt_uis.h" // For resource id + + +VOID GetWowKernelCmdLine(VOID); +extern ULONG fSeparateWow; + +VOID cmdGetNextCmd (VOID) +{ +LPSTR lpszCmd; +PCMDINFO pCMDInfo; +ULONG cb; +PREDIRCOMPLETE_INFO pRdrInfo; +VDMINFO MyVDMInfo; + +char *pSrc, *pDst; +char achCurDirectory[MAXIMUM_VDM_CURRENT_DIR + 4]; +char CmdLine[MAX_PATH]; + + // + // This routine is called once for WOW VDMs, to retrieve the + // "krnl386" command line. + // 5 + if (VDMForWOW) { + GetWowKernelCmdLine(); + return; + } + + VDMInfo.VDMState = 0; + pCMDInfo = (LPVOID) GetVDMAddr ((USHORT)getDS(),(USHORT)getDX()); + + VDMInfo.ErrorCode = FETCHWORD(pCMDInfo->ReturnCode); + VDMInfo.CmdSize = sizeof(CmdLine); + VDMInfo.CmdLine = CmdLine; + VDMInfo.AppName = (PVOID)GetVDMAddr(FETCHWORD(pCMDInfo->ExecPathSeg), + FETCHWORD(pCMDInfo->ExecPathOff)); + VDMInfo.AppLen = FETCHWORD(pCMDInfo->ExecPathSize); + VDMInfo.PifLen = 0; + VDMInfo.EnviornmentSize = 0; + VDMInfo.Enviornment = NULL; + VDMInfo.CurDrive = 0; + VDMInfo.TitleLen = 0; + VDMInfo.ReservedLen = 0; + VDMInfo.DesktopLen = 0; + VDMInfo.CurDirectoryLen = MAX_PATH + 1; + VDMInfo.CurDirectory = achCurDirectory; + + if(IsFirstCall){ + VDMInfo.VDMState = ASKING_FOR_FIRST_COMMAND; + VDMInfo.ErrorCode = 0; + + DeleteConfigFiles(); // get rid of the temp boot files + + // When COMMAND.COM issues first cmdGetNextCmd, it has + // a completed environment already(cmdGetInitEnvironment), + // Therefore, we don't have to ask environment from BASE + cmdVDMEnvBlk.lpszzEnv = (PVOID)GetVDMAddr(FETCHWORD(pCMDInfo->EnvSeg),0); + cmdVDMEnvBlk.cchEnv = FETCHWORD(pCMDInfo->EnvSize); + + //clear bits that track printer flushing + host_lpt_flush_initialize(); + } + else { + + // program has terminated. If the termiation was issued from + // second(or later) instance of command.com(cmd.exe), don't + // reset the flag. + if (Exe32ActiveCount == 0) + DontCheckDosBinaryType = FALSE; + + // tell the base our new current directories (in ANSI) + // we don't do it on repeat call(the shell out case is handled in + // return exit code + if (!IsRepeatCall) { + cmdUpdateCurrentDirectories((BYTE)pCMDInfo->CurDrive); + } + + VDMInfo.VDMState = 0; + if(!IsRepeatCall){ + demCloseAllPSPRecords (); + + if (!pfdata.CloseOnExit && DosSessionId) + nt_block_event_thread(1); + else + nt_block_event_thread(0); + + if (DosSessionId) { + if (!pfdata.CloseOnExit){ + char achTitle[MAX_PATH]; + char achInactive[60]; //should be plenty for 'inactive' + strcpy (achTitle, "["); + if (!LoadString(GetModuleHandle(NULL), EXIT_NO_CLOSE, + achInactive, 60)) + strcat (achTitle, "Inactive "); + else + strcat(achTitle, achInactive); + cb = strlen(achTitle); + // GetConsoleTitleA and SetConsoleTitleA + // are working on OEM character set. + GetConsoleTitleA(achTitle + cb, MAX_PATH - cb - 1); + cb = strlen(achTitle); + achTitle[cb] = ']'; + achTitle[cb + 1] = '\0'; + SetConsoleTitleA(achTitle); + Sleep(INFINITE); + } + else { + VdmExitCode = VDMInfo.ErrorCode; + TerminateVDM(); + } + } + fBlock = TRUE; + } + } + + if(IsRepeatCall) { + VDMInfo.VDMState |= ASKING_FOR_SECOND_TIME; + if( VDMInfo.ErrorCode != 0 ) + IsRepeatCall = FALSE; + } + + VDMInfo.VDMState |= ASKING_FOR_DOS_BINARY; + + if (!IsFirstCall && !(VDMInfo.VDMState & ASKING_FOR_SECOND_TIME)) { + pRdrInfo = (PREDIRCOMPLETE_INFO) FETCHDWORD(pCMDInfo->pRdrInfo); + if (cmdCheckCopyForRedirection (pRdrInfo) == FALSE) + VDMInfo.ErrorCode = ERROR_NOT_ENOUGH_MEMORY; + } + + // Leave the current directory in a safe place, so that other 32bit + // apps etc. can delnode this directory (and other such operations) later. + if ( IsFirstCall == FALSE && IsRepeatCall == FALSE ) + SetCurrentDirectory (cmdHomeDirectory); + + // TSRExit will be set to 1, only if we are coming from command.com's + // prompt and user typed an exit. We need to kill our parent also, so we + // should write an exit in the console buffer. + if (FETCHWORD(pCMDInfo->fTSRExit)) { + cmdPushExitInConsoleBuffer (); + } + + /** + Merging environment is required if + (1). Not the first comamnd && + (2). NTVDM is running on an existing console || + NTVDM has been shelled out. + Note that WOW doesn't need enviornment merging. + **/ + if (!DosEnvCreated && !IsFirstCall && (!DosSessionId || Exe32ActiveCount)) { + RtlZeroMemory(&MyVDMInfo, sizeof(VDMINFO)); + MyVDMInfo.VDMState = ASKING_FOR_ENVIRONMENT | ASKING_FOR_DOS_BINARY; + if (IsRepeatCall) { + MyVDMInfo.VDMState |= ASKING_FOR_SECOND_TIME; + MyVDMInfo.ErrorCode = 0; + } + else + MyVDMInfo.ErrorCode = VDMInfo.ErrorCode; + MyVDMInfo.Enviornment = lpszzVDMEnv32; + MyVDMInfo.EnviornmentSize = (USHORT) cchVDMEnv32; + if (!GetNextVDMCommand(&MyVDMInfo) && MyVDMInfo.EnviornmentSize > cchVDMEnv32) { + MyVDMInfo.Enviornment = realloc(lpszzVDMEnv32, MyVDMInfo.EnviornmentSize); + if (MyVDMInfo.Enviornment == NULL) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + } + lpszzVDMEnv32 = MyVDMInfo.Enviornment; + cchVDMEnv32 = MyVDMInfo.EnviornmentSize; + MyVDMInfo.VDMState = ASKING_FOR_DOS_BINARY | ASKING_FOR_ENVIRONMENT | + ASKING_FOR_SECOND_TIME; + + MyVDMInfo.TitleLen = + MyVDMInfo.DesktopLen = + MyVDMInfo.ReservedLen = + MyVDMInfo.CmdSize = + MyVDMInfo.AppLen = + MyVDMInfo.PifLen = + MyVDMInfo.CurDirectoryLen = 0; + MyVDMInfo.ErrorCode = 0; + if (!GetNextVDMCommand(&MyVDMInfo)) { + RcErrorDialogBox(EG_ENVIRONMENT_ERR, NULL, NULL); + TerminateVDM(); + } + } + if (!cmdCreateVDMEnvironment(&cmdVDMEnvBlk)) { + RcErrorDialogBox(EG_ENVIRONMENT_ERR, NULL, NULL); + TerminateVDM(); + } + DosEnvCreated = TRUE; + VDMInfo.ErrorCode = 0; + } + if (cmdVDMEnvBlk.cchEnv > FETCHWORD(pCMDInfo->EnvSize)) { + setAX((USHORT)cmdVDMEnvBlk.cchEnv); + setCF(1); + IsFirstCall = FALSE; + IsRepeatCall = TRUE; + return; + } + if (DosEnvCreated) + VDMInfo.VDMState |= ASKING_FOR_SECOND_TIME; + + if(!GetNextVDMCommand(&VDMInfo)){ + RcErrorDialogBox(EG_ENVIRONMENT_ERR, NULL, NULL); + TerminateVDM(); + } + + + IsRepeatCall = FALSE; + IsFirstCall = FALSE; + + if(fBlock){ + nt_resume_event_thread(); + fBlock = FALSE; + } + + // Sync VDMs enviornment variables for current directories + cmdSetDirectories (lpszzVDMEnv32, &VDMInfo); + + // tell DOS that this is a dos executable and no further checking is + // necessary + *pIsDosBinary = 1; + + + // Check for PIF files. If a pif file is being executed extract the + // executable name, command line, current directory and title from the pif + // file and place the stuff appropriately in VDMInfo. Note, if pif file + // is invalid, we dont do any thing to vdminfo. In such a case we + // pass the pif as it is to scs to execute which we know will fail and + // will come back to cmdGettNextCmd with proper error code. + + cmdCheckForPIF (&VDMInfo); + + + // + // if forcedos, then don't check binary type on int 21 exec process, + // so that child spawns stay in dos land. Begining with NT 4.0 forcedos.exe + // no longer uses pif files to force execution of a binary as a dos exe. + // It now uses a bit in CreateProcess (dwCreationFlags). + // + + DontCheckDosBinaryType = (VDMInfo.dwCreationFlags & CREATE_FORCEDOS) != 0; + + + // convert exec path name to upper case. This is what command.com expect + if (_strupr(VDMInfo.AppName) == NULL) { + pSrc = VDMInfo.AppName; + while (*pSrc) + *pSrc++ = (char)toupper((int)*pSrc); + } + // figure out the extention type + // at least one char for the base name plus + // EXTENTION_STRING_LEN for the extention + // plus the NULL char + if (VDMInfo.AppLen > 1 + EXTENTION_STRING_LEN + 1) { + pSrc = (PCHAR)VDMInfo.AppName + VDMInfo.AppLen - 5; + if (!strncmp(pSrc, EXE_EXTENTION_STRING, EXTENTION_STRING_LEN)) + STOREWORD(pCMDInfo->ExecExtType, EXE_EXTENTION); + else if (!strncmp(pSrc, COM_EXTENTION_STRING, EXTENTION_STRING_LEN)) + STOREWORD(pCMDInfo->ExecExtType, COM_EXTENTION); + else if (!strncmp(pSrc, BAT_EXTENTION_STRING, EXTENTION_STRING_LEN)) + STOREWORD(pCMDInfo->ExecExtType, BAT_EXTENTION); + else + STOREWORD(pCMDInfo->ExecExtType, UNKNOWN_EXTENTION); + } + else + STOREWORD(pCMDInfo->ExecExtType, UNKNOWN_EXTENTION); + + // tell command.com the length of the app full path name. + STOREWORD(pCMDInfo->ExecPathSize, VDMInfo.AppLen); + + // + // Prepare ccom's UCOMBUF + // + lpszCmd = (PVOID)GetVDMAddr(FETCHWORD(pCMDInfo->CmdLineSeg), + FETCHWORD(pCMDInfo->CmdLineOff)); + + // Copy filepart of AppName excluding extension to ccom's buffer + pSrc = strrchr(VDMInfo.AppName, '\\'); + if (!pSrc) { + pSrc = VDMInfo.AppName; + } + else { + pSrc++; + } + pDst = lpszCmd + 2; + while (*pSrc && *pSrc != '.') { + *pDst++ = *pSrc++; + } + cb = strlen(CmdLine); + + // cmd line must be terminated with "\0xd\0xa\0". This is either done + // by BASE or cmdCheckForPif function(cmdpif.c). + + ASSERT((cb >= 2) && (0xd == CmdLine[cb - 2]) && (0xa == CmdLine[cb - 1])); + + // if cmd line is not blank, separate program base name and + // cmd line with a SPACE. If it IS blank, do not insert any white chars + // or we end up passing white chars to the applications as cmd line + // and some applications just can not live with that. + // We do not strip leading white characters in the passed command line + // so the application sees the original data. + if (cb > 2) + *pDst++ = ' '; + + // append the command tail(at least, "\0xd\0xa") + strncpy(pDst, CmdLine, cb + 1); + + // set the count + // cb has the cmd line length including the terminated 0xd and 0xa + // It does NOT count the terminated NULL char. + ASSERT((cb + pDst - lpszCmd - 2) <= 127); + + // minus 2 because the real data in lpszCmd start from lpszCmd[2] + lpszCmd[1] = (CHAR)(cb + pDst - lpszCmd - 2); + + + + if (DosEnvCreated) { + VDMInfo.Enviornment = (PVOID)GetVDMAddr(FETCHWORD(pCMDInfo->EnvSeg),0); + RtlMoveMemory(VDMInfo.Enviornment, + cmdVDMEnvBlk.lpszzEnv, + cmdVDMEnvBlk.cchEnv + ); + STOREWORD(pCMDInfo->EnvSize,cmdVDMEnvBlk.cchEnv); + free(cmdVDMEnvBlk.lpszzEnv); + DosEnvCreated = FALSE; + } + + STOREWORD(pCMDInfo->fBatStatus,(USHORT)VDMInfo.fComingFromBat); + STOREWORD(pCMDInfo->CurDrive,VDMInfo.CurDrive); + STOREWORD(pCMDInfo->NumDrives,nDrives); + VDMInfo.CodePage = (ULONG) cmdMapCodePage (VDMInfo.CodePage); + STOREWORD(pCMDInfo->CodePage,(USHORT)VDMInfo.CodePage); + + cmdVDMEnvBlk.lpszzEnv = NULL; + cmdVDMEnvBlk.cchEnv = 0; + + IsFirstVDM = FALSE; + + // Handle Standard IO redirection + pRdrInfo = cmdCheckStandardHandles (&VDMInfo,&pCMDInfo->bStdHandles); + STOREDWORD(pCMDInfo->pRdrInfo,(ULONG)pRdrInfo); + + // Tell DOS that it has to invalidate the CDSs + *pSCS_ToSync = (CHAR)0xff; + setCF(0); + + return; +} + + + +VOID GetWowKernelCmdLine(VOID) +{ +CMDINFO UNALIGNED *pCMDInfo; +PCHAR pch, pEnvStrings; +PCHAR pSlash; +int Len; +LPSTR pszCmdLine; + + + DeleteConfigFiles(); // get rid of the temp boot files + host_lpt_flush_initialize(); + + // + // Only a few things need be set for WOW. + // 1. NumDrives + // 2. Environment (get from current 32-bit env.) + // 3. Kernel CmdLine (get from ntvdm command tail) + // 4. Current drive + // + + pCMDInfo = (LPVOID) GetVDMAddr ((USHORT)getDS(),(USHORT)getDX()); + pCMDInfo->NumDrives = nDrives; + + // + // Get the process's environment into lpszzVDMEnv32 and count + // its size into cchVDMEnv32. + // + + pEnvStrings = pch = lpszzVDMEnv32 = GetEnvironmentStrings(); + cchVDMEnv32 = 0; + while (pch[0] || pch[1]) { + cchVDMEnv32++; + pch++; + } + cchVDMEnv32 += 2; // two terminating nulls not counted in loop. + + // + // Transform environment to suit VDM. cmdCreateVDMEnvironment + // uses lpszzVDMEnv32 and cchVDMEnv32 as the source. + // + + if (!cmdCreateVDMEnvironment(&cmdVDMEnvBlk)) { + RcErrorDialogBox(EG_ENVIRONMENT_ERR, NULL, NULL); + TerminateVDM(); + } + + // + // Copy the transformed environment to real mode mem and then free it. + // + + pch = (PVOID)GetVDMAddr(FETCHWORD(pCMDInfo->EnvSeg),0); + RtlMoveMemory(pch, + cmdVDMEnvBlk.lpszzEnv, + cmdVDMEnvBlk.cchEnv + ); + STOREWORD(pCMDInfo->EnvSize,cmdVDMEnvBlk.cchEnv); + free(cmdVDMEnvBlk.lpszzEnv); + // GetEnvironmentStrings needs us to call its corresponding function + // to free the memory it allocated. + FreeEnvironmentStrings(pEnvStrings); + + // + // Get the command line parameter, which consists of a fully + // qualified short path file name: "-a %SystemRoot%\system32\krnl386.exe". + // + // Note that the first token of cmdline is "%SystemRoot%\system32\ntvdm ", + // and may be a long file name surrounded by quotes. + // + pszCmdLine = GetCommandLine(); + if (pszCmdLine) { + + // skip leading spaces + while (*pszCmdLine && !isgraph(*pszCmdLine)) { + pszCmdLine++; + } + + // skip first token + if (*pszCmdLine == '"') { + pszCmdLine++; + while (*pszCmdLine && *pszCmdLine++ != '"') + ; + } + else { + while (isgraph(*pszCmdLine)) { + pszCmdLine++; + } + } + + // mov to beg of WowKernelPathName + pszCmdLine = strstr(pszCmdLine, " -a "); + pszCmdLine += 4; + while (*pszCmdLine && *pszCmdLine == ' ') { + pszCmdLine++; + } + } + + if (!pszCmdLine || !*pszCmdLine) { + RcErrorDialogBox(EG_ENVIRONMENT_ERR, NULL, NULL); + TerminateVDM(); + } + + + // + // Copy first token to ExecPath, and find the beg of the file part. + // + Len = FETCHWORD(pCMDInfo->ExecPathSize); + pch = (PVOID)GetVDMAddr(FETCHWORD(pCMDInfo->ExecPathSeg), + FETCHWORD(pCMDInfo->ExecPathOff)); + + pSlash = pszCmdLine; + while (--Len && isgraph(*pszCmdLine)) { + if (*pszCmdLine == '\\') { + pSlash = pszCmdLine + 1; + } + *pch++ = *pszCmdLine++; + } + *pch = '\0'; + pCMDInfo->ExecPathSize -= Len; + pCMDInfo->ExecExtType = EXE_EXTENTION; // for WOW, use EXE extention + + pszCmdLine = pSlash; // filepart begins here + + + // + // Copy filepart of first token and rest to CmdLine buffer + // + Len = FETCHWORD(pCMDInfo->CmdLineSize); + pch = (PVOID)GetVDMAddr(FETCHWORD(pCMDInfo->CmdLineSeg), + FETCHWORD(pCMDInfo->CmdLineOff)); + + while (--Len && *pszCmdLine) { + *pch++ = *pszCmdLine++; + } + strcpy(pch, "\x0d\x0a"); + + + *pIsDosBinary = 1; + IsRepeatCall = FALSE; + IsFirstCall = FALSE; + + // Tell DOS that it has to invalidate the CDSs + *pSCS_ToSync = (CHAR)0xff; + setCF(0); + + return; +} + + +/* cmdGetCurrentDir - Return the current directory for a drive. + * + * + * Entry - Client (DS:SI) - buffer to return the directory + * Client (AL) - drive being queried (0 = A) + * + * EXIT - SUCCESS Client (CY) clear + * FAILURE Client (CY) set + * (AX) = 0 (Directory was bigger than 64) + * (AX) = 1 (the drive is not valid) + * + */ + +VOID cmdGetCurrentDir (VOID) +{ +PCHAR lpszCurDir; +UCHAR chDrive; +CHAR EnvVar[] = "=?:"; +CHAR RootName[] = "?:\\"; +DWORD EnvVarLen; +UINT DriveType; + + + lpszCurDir = (PCHAR) GetVDMAddr ((USHORT)getDS(),(USHORT)getSI()); + chDrive = getAL(); + EnvVar[1] = chDrive + 'A'; + RootName[0] = chDrive + 'A'; + + // if the drive doesn't exist, blindly clear the environment var + // and return error + DriveType = demGetPhysicalDriveType(chDrive); + if (DriveType == DRIVE_UNKNOWN) { + DriveType = GetDriveTypeOem(RootName); + } + + if (DriveType == DRIVE_UNKNOWN || DriveType == DRIVE_NO_ROOT_DIR){ + SetEnvironmentVariableOem(EnvVar, NULL); + setCF(1); + setAX(0); + return; + } + + if((EnvVarLen = GetEnvironmentVariableOem (EnvVar,lpszCurDir, + MAXIMUM_VDM_CURRENT_DIR+3)) == 0){ + + // if its not in env then and drive exist then we have'nt + // yet touched it. + strcpy(lpszCurDir, RootName); + SetEnvironmentVariableOem (EnvVar,RootName); + setCF(0); + return; + } + if (EnvVarLen > MAXIMUM_VDM_CURRENT_DIR+3) { + setCF(1); + setAX(0); + } + else { + setCF(0); + } + return; +} + +/* cmdSetInfo - Set the address of SCS_ToSync variable in DOSDATA. + * This variable is set whenever SCS dispatches a new + * command to command.com. Setting of this variable + * indicates to dos to validate all the CDS structures + * for local drives. + * + * + * Entry - Client (DS:DX) - pointer to SCSINFO. + * + * EXIT - None + */ + +VOID cmdSetInfo (VOID) +{ + + pSCSInfo = (PSCSINFO) GetVDMAddr (getDS(),getDX()); + + pSCS_ToSync = (PCHAR) &pSCSInfo->SCS_ToSync; + + pIsDosBinary = (BYTE *) GetVDMAddr(getDS(), getBX()); + + pFDAccess = (WORD *) GetVDMAddr(getDS(), getCX()); + return; +} + + +VOID cmdSetDirectories (PCHAR lpszzEnv, VDMINFO * pVdmInfo) +{ +LPSTR lpszVal; +CHAR ch, chDrive, achEnvDrive[] = "=?:"; + + ch = pVdmInfo->CurDrive + 'A'; + if (pVdmInfo->CurDirectoryLen != 0){ + SetCurrentDirectory(pVdmInfo->CurDirectory); + achEnvDrive[1] = ch; + SetEnvironmentVariable(achEnvDrive, pVdmInfo->CurDirectory); + } + if (lpszzEnv) { + while(*lpszzEnv) { + if(*lpszzEnv == '=' && + (chDrive = toupper(*(lpszzEnv+1))) >= 'A' && + chDrive <= 'Z' && + (*(PCHAR)((ULONG)lpszzEnv+2) == ':') && + chDrive != ch) { + lpszVal = (PCHAR)((ULONG)lpszzEnv + 4); + achEnvDrive[1] = chDrive; + SetEnvironmentVariable (achEnvDrive,lpszVal); + } + lpszzEnv = strchr(lpszzEnv,'\0'); + lpszzEnv++; + } + } +} + +static BOOL fConOutput = FALSE; + +VOID cmdComSpec (VOID) +{ +LPSTR lpszCS; + + + if(IsFirstCall == FALSE) + return; + + lpszCS = (LPVOID) GetVDMAddr ((USHORT)getDS(),(USHORT)getDX()); + strcpy(lpszComSpec,"COMSPEC="); + strcpy(lpszComSpec+8,lpszCS); + cbComSpec = strlen(lpszComSpec) +1; + + setAL((BYTE)(!fConOutput || VDMForWOW)); + + return; +} + + +VOID cmdSaveWorld (VOID) +{ +#ifdef CHECK_IT_LATER +SAVEWORLD VDMState; +HANDLE hFile; +PCHAR pVDM; +DWORD dwBytesWritten; + + if(IsFirstVDMInSystem) { + IsFirstVDMInSystem = FALSE; + if ((hFile = CreateFile("c:\\nt\\bin86\\savevdm.wld", + GENERIC_WRITE, + 0, + NULL, + OPEN_ALWAYS, + 0, + NULL)) == (HANDLE)-1){ + SaveWorldCreated = FALSE; + return; + } + VDMState.ax = getAX(); + VDMState.bx = getBX(); + VDMState.cx = getCX(); + VDMState.dx = getDX(); + VDMState.cs = getCS(); + VDMState.ss = getSS(); + VDMState.ds = getDS(); + VDMState.es = getES(); + VDMState.si = getSI(); + VDMState.di = getDI(); + VDMState.bp = getBP(); + VDMState.sp = getSP(); + VDMState.ip = getIP() + 1; + VDMState.flag = 0; + VDMState.ImageSize = 1024*1024; + + pVDM = (PVOID)GetVDMAddr(0,0); + + if (WriteFile (hFile, + (LPVOID)&VDMState, + (DWORD)sizeof(VDMState), + &dwBytesWritten, + NULL) == FALSE){ + SaveWorldCreated = FALSE; + CloseHandle(hFile); + return; + } + + if (WriteFile (hFile, + (LPVOID)pVDM, + (DWORD)VDMState.ImageSize, + &dwBytesWritten, + NULL) == FALSE){ + SaveWorldCreated = FALSE; + CloseHandle(hFile); + return; + } + CloseHandle(hFile); + } +#endif + return; +} + + +/* cmdInitConsole - Let Video VDD know that it can start console output + * operations. + * + * + * Entry - None + * + * + * EXIT - None + * + */ + +VOID cmdInitConsole (VOID) +{ + if (fConOutput == FALSE) { + fConOutput = TRUE; + nt_init_event_thread (); + } + return; +} + + +/* cmdMapCodePage - Map the Win32 Code page to DOS code page + */ + +USHORT cmdMapCodePage (ULONG CodePage) +{ + // Currently We understand US code page only + if (CodePage == 1252) + return 437; + else + return ((USHORT)CodePage); +} + + + +/* GetWOWShortCutInfo - returns the startupinf.reserved field of + * vdminfo for the first wow task. + * + * Input - Bufsize - pointer to bufsize + * Buf - buffer where the info is returned + * + * Output + * Success - returns TRUE, BufSize has the length of buffer filled in + * Failure - returns FALSE, Bufsize has the required buffer size. + */ + +BOOL GetWOWShortCutInfo (PULONG Bufsize, PVOID Buf) +{ + if (*Bufsize >= VDMInfo.ReservedLen) { + *Bufsize = VDMInfo.ReservedLen; + if (Bufsize) + strncpy (Buf, VDMInfo.Reserved, VDMInfo.ReservedLen); + return TRUE; + } + else { + *Bufsize = VDMInfo.ReservedLen; + return FALSE; + } +} + +VOID cmdUpdateCurrentDirectories(BYTE CurDrive) +{ + DWORD cchRemain, cchCurDir; + CHAR *lpszCurDir; + BYTE Drive; + DWORD DriveType; + CHAR achName[] = "=?:"; + CHAR RootName[] = "?:\\"; + + + // allocate new space for the new current directories + lpszzCurrentDirectories = (CHAR*) malloc(MAX_PATH); + cchCurrentDirectories = 0; + cchRemain = MAX_PATH; + lpszCurDir = lpszzCurrentDirectories; + if (lpszCurDir != NULL) { + Drive = 0; + // current directory is the first entry + achName[1] = CurDrive + 'A'; + cchCurrentDirectories = GetEnvironmentVariable( + achName, + lpszCurDir, + cchRemain + ); + + if (cchCurrentDirectories == 0 || cchCurrentDirectories > MAX_PATH) { + free(lpszzCurrentDirectories); + lpszzCurrentDirectories = NULL; + cchCurrentDirectories = 0; + return; + } + + cchRemain -= ++cchCurrentDirectories; + // we got current directory already. Keep the drive number + lpszCurDir += cchCurrentDirectories; + + while (Drive < 26) { + + // ignore invalid drives and current drive + if (Drive != CurDrive) { + DriveType = demGetPhysicalDriveType(Drive); + if (DriveType == DRIVE_UNKNOWN) { + RootName[0] = (CHAR)('A' + Drive); + DriveType = GetDriveTypeOem(RootName); + } + + if (DriveType != DRIVE_UNKNOWN && + DriveType != DRIVE_NO_ROOT_DIR ) + { + achName[1] = Drive + 'A'; + cchCurDir = GetEnvironmentVariable( + achName, + lpszCurDir, + cchRemain + ); + if(cchCurDir > cchRemain) { + lpszCurDir = (CHAR *)realloc(lpszzCurrentDirectories, + cchRemain + MAX_PATH + cchCurrentDirectories + ); + if (lpszCurDir == NULL) { + free(lpszzCurrentDirectories); + lpszzCurrentDirectories = NULL; + cchCurrentDirectories = 0; + return; + } + lpszzCurrentDirectories = lpszCurDir; + lpszCurDir += cchCurrentDirectories; + cchRemain += MAX_PATH; + cchCurDir = GetEnvironmentVariable( + achName, + lpszCurDir, + cchRemain + ); + } + if (cchCurDir != 0) { + // GetEnvironmentVariable doesn't count the NULL char + lpszCurDir += ++cchCurDir; + cchRemain -= cchCurDir; + cchCurrentDirectories += cchCurDir; + } + } + } + // next drive + Drive++; + } + + + lpszCurDir = lpszzCurrentDirectories; + // need space for the ending NULL and shrink the space if necessary + lpszzCurrentDirectories = (CHAR *) realloc(lpszCurDir, cchCurrentDirectories + 1); + if (lpszzCurrentDirectories != NULL && cchCurrentDirectories != 0){ + lpszzCurrentDirectories[cchCurrentDirectories++] = '\0'; + SetVDMCurrentDirectories(cchCurrentDirectories, lpszzCurrentDirectories); + free(lpszzCurrentDirectories); + lpszzCurrentDirectories = NULL; + cchCurrentDirectories = 0; + } + else { + free(lpszCurDir); + cchCurrentDirectories = 0; + } + + } +} + +/* This SVC function tells command.com, if the VDM was started without an + * existing console. If so, on finding a TSR, command.com will return + * back to GetNextVDMCommand, rather than putting its own popup. + * + * Entry - None + * + * Exit - Client (AL) = 0 if started with an existing console + * Client (AL) = 1 if started with new console + */ + +VOID cmdGetStartInfo (VOID) +{ + setAL((BYTE) (DosSessionId ? 1 : 0)); + return; +} diff --git a/private/mvdm/dos/command/cmdpif.c b/private/mvdm/dos/command/cmdpif.c new file mode 100644 index 000000000..5fa2c2190 --- /dev/null +++ b/private/mvdm/dos/command/cmdpif.c @@ -0,0 +1,289 @@ +/* cmdpif.c - PIF Handling Routines For SCS + * + * + * Modification History: + * + * Sudeepb 19-Aug-1992 Created + * williamh 10-Nov-1992 (1). get params from w386 extention if exist + * (2). null terminate caption + * williamh 27-May-1993 almost rewrote for better pif support + */ + +#include "cmd.h" +#include <ctype.h> +#include <pif.h> +#include <cmdsvc.h> +#include <softpc.h> +#include <mvdm.h> +#include <oemuni.h> +#include "nt_pif.h" + +VOID cmdCheckForPIF (PVDMINFO pvi) +{ +PCHAR pCmdLine = pvi->CmdLine; +PCHAR pDot; +ULONG size; +UCHAR ch; +DWORD dw; +CHAR lpszEnvDir [] = "=?:"; +CHAR FullPathName[MAX_PATH + 1]; +CHAR * pFilePart; +BOOL IsPIFFile, IsFromForceDos; +CHAR AppFullPathName[MAX_PATH + 1]; + + // + // Advance CmdLine pointer to beg of command tail + // + while (*pCmdLine && !isgraph(*pCmdLine)) { // skip to first nonwhite + pCmdLine++; + } + + pDot = strrchr(pvi->AppName, '.'); + if (pDot) + IsPIFFile = pDot && !_strnicmp(pDot, ".pif", 4); + else + IsPIFFile = FALSE; + + + // if the command is not a pif file and it is not + // running on a new console + if (!IsPIFFile && !DosSessionId) + goto CleanUpAndReturn; + + if (IsPIFFile) { + if (!IsFirstVDM) { + // + // Get the pif data. If no pif data, or not from forcedos + // just return -- command.com will receive the pif file + // name and fail. + // + pfdata.AppHasPIFFile = + pfdata.IgnoreStartDirInPIF = + pfdata.IgnoreTitleInPIF = + pfdata.IgnoreCmdLineInPIF = + pfdata.IgnoreConfigAutoexec = 1; + if (!GetPIFData(&pfdata, pvi->AppName)) + goto CleanUpAndReturn; + } + + // we only run a pif file on two occasions: + // (1). it is from a new console + // (2). it is from forcedos. + if (!DosSessionId && pfdata.SubSysId != SUBSYS_DOS) + goto CleanUpAndReturn; + + } + if (IsFirstVDM) { + // if this is the first vdm, take cmdline, startupdir and title + // if they are there + if (pfdata.StartDir){ + dw = cmdExpandEnvironmentStrings(NULL, + pfdata.StartDir, + FullPathName, + MAX_PATH + 1 + ); + if (dw != 0 && dw <= MAX_PATH) { + dw = GetFullPathNameOem(FullPathName, + MAX_PATH + 1, + pfdata.StartDir, + &pFilePart + ); + } + + if (dw != 0 && dw <= MAX_PATH) + dw = GetFileAttributesOem(pfdata.StartDir); + else + dw = 0; + + if (dw == 0 || dw == 0xFFFFFFFF || !(dw & FILE_ATTRIBUTE_DIRECTORY)) + { + RcMessageBox(EG_PIF_STARTDIR_ERR, + NULL, + NULL, + RMB_ICON_BANG | RMB_ABORT); + goto CleanUpAndReturn; + } + + dw = GetShortPathNameOem(pfdata.StartDir, + pfdata.StartDir, + MAX_PATH + 1 + ); + if (dw == 0 || dw > MAX_PATH || dw > 64) { + RcMessageBox(EG_PIF_STARTDIR_ERR, + NULL, + NULL, + RMB_ICON_BANG | RMB_ABORT); + goto CleanUpAndReturn; + } + + lpszEnvDir[1] = pfdata.StartDir[0]; + SetEnvironmentVariableOem(lpszEnvDir, pfdata.StartDir); + SetCurrentDirectoryOem(pfdata.StartDir); + pvi->CurDrive = toupper(pfdata.StartDir[0]) - 'A'; + } + + if (pfdata.WinTitle) { + strcpy(FullPathName, pfdata.WinTitle); + dw = cmdExpandEnvironmentStrings(NULL, + FullPathName, + pfdata.WinTitle, + MAX_PATH + 1 + ); + pfdata.WinTitle[MAX_PATH] = '\0'; + } + + if (!*pCmdLine && pfdata.CmdLine) { + + // if the optinal parameter is '?' + // prompt the user + pDot = pfdata.CmdLine; + while (*pDot && *pDot <= ' ') + pDot++; + + if (*pDot == '?') { + pfdata.CmdLine[0] = '\0'; + RcMessageBox(EG_PIF_ASK_CMDLINE, + NULL, + pfdata.CmdLine, + RMB_EDIT | RMB_ICON_INFO | (128 << 16) + ); + } + + if (*pfdata.CmdLine) { + strcpy(FullPathName, pfdata.CmdLine); + dw = cmdExpandEnvironmentStrings(NULL, + FullPathName, + pfdata.CmdLine, + MAX_PATH + 1 + ); + } + } + } + + + if(IsPIFFile) { + dw = cmdExpandEnvironmentStrings(NULL, + pfdata.StartFile, + FullPathName, + MAX_PATH + 1 + ); + if (!dw || dw > MAX_PATH) { + RcMessageBox(EG_PIF_STARTFILE_ERR, + NULL, NULL, RMB_ICON_BANG | RMB_ABORT); + goto CleanUpAndReturn; + } + + + + // search from the current directory + // note that the startup directory specified in + // the pif file has been set as our current directory + // when we got here + dw = SearchPathOem(".", + FullPathName, + NULL, + MAX_PATH + 1, + AppFullPathName, + &pFilePart + ); + // if couldn't find the file from the current dir + // ask win32api help + if (dw == 0 || dw > MAX_PATH) { + dw = SearchPathOem(NULL, + FullPathName, + NULL, + MAX_PATH + 1, + AppFullPathName, + &pFilePart + ); + } + + // couldn't find the file, give up + if (dw == 0 || dw > MAX_PATH) { + RcMessageBox(EG_PIF_STARTFILE_ERR, + NULL, NULL, RMB_ICON_BANG | RMB_ABORT); + goto CleanUpAndReturn; + } + + dw = GetFileAttributesOem(AppFullPathName); + if (dw == (DWORD)(-1) || (dw & FILE_ATTRIBUTE_DIRECTORY)) { + RcMessageBox(EG_PIF_STARTFILE_ERR, NULL, NULL, + RMB_ICON_BANG | RMB_ABORT + ); + goto CleanUpAndReturn; + } + + // convert to shortfilename + dw = GetShortPathNameOem(AppFullPathName, pvi->AppName, + MAX_PATH + 1); + if (dw == 0 || dw > MAX_PATH) { + + RcMessageBox(EG_PIF_STARTFILE_ERR, NULL, NULL, + RMB_ICON_BANG | RMB_ABORT + ); + goto CleanUpAndReturn; + } + // update the application path name length(including the terminate NULL) + pvi->AppLen = strlen(pvi->AppName) + 1; + + // pvi->AppName contains the application short name. + // verify that it has the correct extension(.EXE, .COM or .BAT). + pDot = (PCHAR)pvi->AppName + pvi->AppLen - 5; + if (pvi->AppLen < 5 || + (_strnicmp(pDot, EXE_EXTENTION_STRING, EXTENTION_STRING_LEN) && + _strnicmp(pDot, COM_EXTENTION_STRING, EXTENTION_STRING_LEN) && + _strnicmp(pDot, BAT_EXTENTION_STRING, EXTENTION_STRING_LEN))) + { + + RcMessageBox(EG_DOS_PROG_EXTENSION,AppFullPathName, NULL, RMB_ICON_BANG | RMB_ABORT); + goto CleanUpAndReturn; + + } + } + + + // + // Copy in pif command tail if original command tail is empty + // + if (!*pCmdLine && pfdata.CmdLine) { + strcpy(FullPathName, pfdata.CmdLine); + strcat(FullPathName, "\x0d\x0a"); + if (strlen(FullPathName) >= 128 - 13) { + // too bad, the command line is too long + RcMessageBox(EG_PIF_CMDLINE_ERR,NULL,NULL,RMB_ICON_BANG | RMB_ABORT); + goto CleanUpAndReturn; + + } + strcpy(pvi->CmdLine, FullPathName); + pvi->CmdSize = strlen(FullPathName) + 1; + } + + if (IsPIFFile) + // we don't know the binary type at this point. + *pIsDosBinary = 0; + + if (pfdata.WinTitle) + SetConsoleTitle(pfdata.WinTitle); + + DontCheckDosBinaryType = (pfdata.SubSysId == SUBSYS_DOS); + +CleanUpAndReturn: + if (pfdata.CmdLine) { + free(pfdata.CmdLine); + pfdata.CmdLine = NULL; + } + if (pfdata.StartDir) { + free(pfdata.StartDir); + pfdata.StartDir = NULL; + } + if (pfdata.StartFile) { + free(pfdata.StartFile); + pfdata.StartFile = NULL; + } + if (pfdata.WinTitle) { + free(pfdata.WinTitle); + pfdata.WinTitle = NULL; + } + return; + +} diff --git a/private/mvdm/dos/command/cmdpif.h b/private/mvdm/dos/command/cmdpif.h new file mode 100644 index 000000000..bb264236f --- /dev/null +++ b/private/mvdm/dos/command/cmdpif.h @@ -0,0 +1,46 @@ + +/*================================================================ +Structure used to hold the data that CONFIG will need from the PIF +file. This is gleaned from both the main data block and from the +file extensions for Windows 286 and 386. +================================================================*/ + + +/* WARNING !!!!!! + This structure is copied from NT_PIF.H in insiginia + hsot\inc\nt_pif.h. Make sure you keep them synchronized + when you make changes. +*/ +#pragma pack() +typedef struct + { + char *WinTitle; /* caption text(Max. 30 chars) + NULL */ + char *CmdLine; /* command line (max 63 hars) + NULL */ + char *StartDir; /* program file name (max 63 chars + NULL */ + char *StartFile; + WORD fullorwin; + WORD graphicsortext; + WORD memreq; + WORD memdes; + WORD emsreq; + WORD emsdes; + WORD xmsreq; + WORD xmsdes; + char menuclose; + char reskey; + WORD ShortMod; + WORD ShortScan; + char idledetect; + char fgprio; + char CloseOnExit; + char AppHasPIFFile; + char IgnoreTitleInPIF; + char IgnoreStartDirInPIF; + char IgnoreShortKeyInPIF; + char IgnoreCmdLineInPIF; + char IgnoreConfigAutoexec; + char SubSysId; + } PIF_DATA; + +extern PIF_DATA pfdata; +BOOL GetPIFData(PIF_DATA *, char *); diff --git a/private/mvdm/dos/command/cmdredir.c b/private/mvdm/dos/command/cmdredir.c new file mode 100644 index 000000000..5cf700c63 --- /dev/null +++ b/private/mvdm/dos/command/cmdredir.c @@ -0,0 +1,664 @@ +/* cmdredir.c - SCS routines for redirection + * + * + * Modification History: + * + * Sudeepb 22-Apr-1992 Created + */ + +#include "cmd.h" + +#include <cmdsvc.h> +#include <softpc.h> +#include <mvdm.h> +#include <ctype.h> + +#define CMDREDIR_DEBUG 1 + +PPIPE_INPUT cmdPipeList = NULL; + +BOOL cmdCheckCopyForRedirection (pRdrInfo) +PREDIRCOMPLETE_INFO pRdrInfo; +{ +PPIPE_INPUT pPipe, pPipePrev; +PPIPE_OUTPUT pPipeOut; + + if (pRdrInfo == NULL) + return TRUE; + if (pRdrInfo->ri_pPipeStdIn != NULL) { + + //Piping and Pipe list is empty? + ASSERT(cmdPipeList != NULL); + + // in most cases, we have only one pipe for stdin + if (pRdrInfo->ri_pPipeStdIn == cmdPipeList){ + pPipe = pRdrInfo->ri_pPipeStdIn; + cmdPipeList = pPipe->Next; + } + // multiple piping + // search for the right one + else { + pPipe = pPipePrev = cmdPipeList; + while (pPipe != NULL && pPipe != pRdrInfo->ri_pPipeStdIn){ + pPipePrev = pPipe; + pPipe = pPipe->Next; + } + if (pPipe != NULL) + // remove it from the list + pPipePrev->Next = pPipe->Next; + } + if (pPipe != NULL) { + // grab the critical section. As soon as we have a + // a hold on the critical section, it is safe to kill + // the piping thread because it is in dormant unless + // it has terminated which is also safe for us. + EnterCriticalSection(&pPipe->CriticalSection); + // if the thread is till running, kill it + if (WaitForSingleObject(pPipe->hThread, 0)) { + TerminateThread(pPipe->hThread, 0); + WaitForSingleObject(pPipe->hThread, INFINITE); + } + LeaveCriticalSection(&pPipe->CriticalSection); + CloseHandle(pPipe->hFileWrite); + CloseHandle(pPipe->hPipe); + CloseHandle(pPipe->hDataEvent); + CloseHandle(pPipe->hThread); + DeleteCriticalSection(&pPipe->CriticalSection); + DeleteFile(pPipe->pFileName); + free(pPipe->pFileName); + free (pPipe); + } + } + // the application is terminating, let the output thread knows + // about it so it can exit appropriately. + // the output thread is responsible for clean up + if (pRdrInfo->ri_pPipeStdOut) { + // The output thread must wait for the event before + // it can exit. + SetEvent((pRdrInfo->ri_pPipeStdOut)->hExitEvent); + // wait 1 seconds for the thread to go away. + // this is done because our parent process may put up + // its prompt before our sibling process has a chance to + // completely display data on its display surface. + // note that we can not wait forever here because + // the sibling process could be the other dos application and + // we will be deadlock if it is the case + WaitForSingleObject(pRdrInfo->ri_hStdOutThread, 1000); + CloseHandle(pRdrInfo->ri_hStdOutThread); + } + if (pRdrInfo->ri_pPipeStdErr) { + SetEvent((pRdrInfo->ri_pPipeStdErr)->hExitEvent); + WaitForSingleObject(pRdrInfo->ri_hStdErrThread, 1000); + CloseHandle(pRdrInfo->ri_hStdErrThread); + } + free (pRdrInfo); + + return TRUE; +} + +BOOL cmdCreateTempFile (phTempFile,ppszTempFile) +PHANDLE phTempFile; +PCHAR *ppszTempFile; +{ + +PCHAR pszTempPath = NULL; +DWORD TempPathSize; +PCHAR pszTempFileName; +HANDLE hTempFile; +SECURITY_ATTRIBUTES sa; + + pszTempPath = malloc(MAX_PATH + 12); + + if (pszTempPath == NULL) + return FALSE; + + if ((TempPathSize = GetTempPath ( + MAX_PATH, + pszTempPath)) == 0){ + free (pszTempPath); + return FALSE; + } + + if (TempPathSize >= MAX_PATH) { + free (pszTempPath); + return FALSE; + } + + // CMDCONF.C depends on the size of this buffer + if ((pszTempFileName = malloc (MAX_PATH + 13)) == NULL){ + free (pszTempPath); + return FALSE; + } + + // if this fails it probably means we have a bad path + if (!GetTempFileName(pszTempPath, "scs", 0, pszTempFileName)) + { + // lets get something else, which should succeed + TempPathSize = GetWindowsDirectory(pszTempPath, MAX_PATH); + if (!TempPathSize || TempPathSize >= MAX_PATH) + strcpy(pszTempPath, "\\"); + + // try again and hope for the best + GetTempFileName(pszTempPath, "scs", 0, pszTempFileName); + } + + + // must have a security descriptor so that the child process + // can inherit this file handle. This is done because when we + // shell out with piping the 32 bits application must have inherited + // the temp filewe created, see cmdGetStdHandle + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + if ((hTempFile = CreateFile (pszTempFileName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + &sa, + OPEN_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY, + NULL)) == (HANDLE)-1){ + free (pszTempFileName); + free (pszTempPath); + return FALSE; + } + + *phTempFile = hTempFile; + *ppszTempFile = pszTempFileName; + free (pszTempPath); + return TRUE; +} + +/* cmdCheckStandardHandles - Check if we have to do anything to support + * standard io redirection, if so save away + * pertaining information. + * + * Entry - pVDMInfo - VDMInfo Structure + * pbStdHandle - pointer to bit array for std handles + * + * EXIT - return NULL if no redirection involved + * return pointer to REDIRECTION_INFO + */ + +PREDIRCOMPLETE_INFO cmdCheckStandardHandles ( + PVDMINFO pVDMInfo, + USHORT UNALIGNED *pbStdHandle + ) +{ +USHORT bTemp = 0; +PREDIRCOMPLETE_INFO pRdrInfo; + + if (pVDMInfo->StdIn) + bTemp |= MASK_STDIN; + + if (pVDMInfo->StdOut) + bTemp |= MASK_STDOUT; + + if (pVDMInfo->StdErr) + bTemp |= MASK_STDERR; + + if(bTemp){ + + if ((pRdrInfo = malloc (sizeof (REDIRCOMPLETE_INFO))) == NULL) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + } + + RtlZeroMemory ((PVOID)pRdrInfo, sizeof(REDIRCOMPLETE_INFO)); + pRdrInfo->ri_hStdErr = pVDMInfo->StdErr; + pRdrInfo->ri_hStdOut = pVDMInfo->StdOut; + pRdrInfo->ri_hStdIn = pVDMInfo->StdIn; + + nt_std_handle_notification(TRUE); + fSoftpcRedirection = TRUE; + } + else{ + pRdrInfo = NULL; + nt_std_handle_notification(FALSE); + fSoftpcRedirection = FALSE; + } + + *pbStdHandle = bTemp; + return pRdrInfo; +} + +/* cmdGetStdHandle - Get the 32 bit NT standard handle for the VDM + * + * + * Entry - Client (CX) - 0,1 or 2 (stdin stdout stderr) + * Client (AX:BX) - redirinfo pointer + * + * EXIT - Client (BX:CX) - 32 bit handle + * Client (DX:AX) - file size + */ + +VOID cmdGetStdHandle (VOID) +{ +USHORT iStdHandle; +PREDIRCOMPLETE_INFO pRdrInfo; + + iStdHandle = getCX(); + pRdrInfo = (PREDIRCOMPLETE_INFO) (((ULONG)getAX() << 16) + (ULONG)getBX()); + + switch (iStdHandle) { + + case HANDLE_STDIN: + + if (GetFileType(pRdrInfo->ri_hStdIn) == FILE_TYPE_PIPE) { + if (!cmdHandleStdinWithPipe (pRdrInfo)) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + setCF(1); + return; + } + setCX ((USHORT)pRdrInfo->ri_hStdInFile); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdInFile >> 16)); + } + else { + setCX ((USHORT)pRdrInfo->ri_hStdIn); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdIn >> 16)); + } + break; + + case HANDLE_STDOUT: + if (GetFileType (pRdrInfo->ri_hStdOut) == FILE_TYPE_PIPE){ + if (!cmdHandleStdOutErrWithPipe(pRdrInfo, HANDLE_STDOUT)) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + setCF(1); + return; + } + setCX ((USHORT)pRdrInfo->ri_hStdOutFile); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdOutFile >> 16)); + + } + else { + // sudeepb 16-Mar-1992; This will be a compatibilty problem. + // If the user gives the command "dosls > lpt1" we will + // inherit the 32 bit handle of lpt1, so the ouput will + // directly go to the LPT1 and a DOS TSR/APP hooking int17 + // wont see this printing. Is this a big deal??? + setCX ((USHORT)pRdrInfo->ri_hStdOut); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdOut >> 16)); + } + break; + + case HANDLE_STDERR: + + if (pRdrInfo->ri_hStdErr == pRdrInfo->ri_hStdOut + && pRdrInfo->ri_hStdOutFile != 0) { + setCX ((USHORT)pRdrInfo->ri_hStdOutFile); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdOutFile >> 16)); + pRdrInfo->ri_hStdErrFile = pRdrInfo->ri_hStdOutFile; + break; + } + + if (GetFileType (pRdrInfo->ri_hStdErr) == FILE_TYPE_PIPE){ + if(!cmdHandleStdOutErrWithPipe(pRdrInfo, HANDLE_STDERR)) { + RcErrorDialogBox(EG_MALLOC_FAILURE, NULL, NULL); + TerminateVDM(); + setCF(1); + return; + } + setCX ((USHORT)pRdrInfo->ri_hStdErrFile); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdErrFile >> 16)); + } + else { + setCX ((USHORT)pRdrInfo->ri_hStdErr); + setBX ((USHORT)((ULONG)pRdrInfo->ri_hStdErr >> 16)); + } + break; + } + setAX(0); + setDX(0); + setCF(0); + return; +} + +BOOL cmdHandleStdOutErrWithPipe( + PREDIRCOMPLETE_INFO pRdrInfo, + USHORT HandleType + ) +{ + + HANDLE hFile; + PCHAR pFileName; + PPIPE_OUTPUT pPipe; + BYTE *Buffer; + DWORD ThreadId; + HANDLE hEvent; + HANDLE hFileWrite; + HANDLE hThread; + + if(!cmdCreateTempFile(&hFile,&pFileName)) + return FALSE; + // must have a different handle so that writter(dos app) and reader(us) + // wont use the same handle object(especially, file position) + hFileWrite = CreateFile(pFileName, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_TEMPORARY, + NULL + ); + if (hFileWrite == INVALID_HANDLE_VALUE) { + CloseHandle(hFile); + DeleteFile(pFileName); + return FALSE; + } + Buffer = malloc(sizeof(PIPE_OUTPUT) + PIPE_OUTPUT_BUFFER_SIZE); + if (Buffer == NULL) { + CloseHandle(hFile); + CloseHandle(hFileWrite); + DeleteFile(pFileName); + return FALSE; + } + pPipe = (PPIPE_OUTPUT)Buffer; + pPipe->Buffer = Buffer + sizeof(PIPE_OUTPUT); + pPipe->BufferSize = PIPE_OUTPUT_BUFFER_SIZE; + pPipe->hFile = hFileWrite; + pPipe->pFileName = pFileName; + pPipe->hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (pPipe->hExitEvent == NULL) { + CloseHandle(hFile); + CloseHandle(hFileWrite); + DeleteFile(pFileName); + free(pPipe); + return FALSE; + } + + if (HandleType == HANDLE_STDOUT) { + pPipe->hPipe = pRdrInfo->ri_hStdOut; + pRdrInfo->ri_pPipeStdOut = pPipe; + pRdrInfo->ri_hStdOutFile = hFile; + + } + else { + pPipe->hPipe = pRdrInfo->ri_hStdErr; + pRdrInfo->ri_pPipeStdErr = pPipe; + pRdrInfo->ri_hStdErrFile = hFile; + + } + hThread = CreateThread ((LPSECURITY_ATTRIBUTES)NULL, + (DWORD)0, + (LPTHREAD_START_ROUTINE)cmdPipeOutThread, + (LPVOID)pPipe, + 0, + &ThreadId + ); + if (hThread == NULL) { + CloseHandle(pPipe->hExitEvent); + CloseHandle(hFileWrite); + CloseHandle(hFile); + DeleteFile(pFileName); + free(Buffer); + return FALSE; + } + if (HandleType == HANDLE_STDOUT) + pRdrInfo->ri_hStdOutThread = hThread; + else + pRdrInfo->ri_hStdErrThread = hThread; + return TRUE; +} + +/* independent thread to read application stdout(file) to NTVDM stdout(PIPE). + The CPU thread would notify us through hExitEvent when the application + is terminating(thus, we can detect EOF and exit + */ + +VOID cmdPipeOutThread(LPVOID lpParam) +{ + PPIPE_OUTPUT pPipe; + DWORD BytesRead; + DWORD BytesWritten; + BOOL ExitPending; + + pPipe = (PPIPE_OUTPUT)lpParam; + + ExitPending = FALSE; + + while(ReadFile(pPipe->hFile, pPipe->Buffer, pPipe->BufferSize, &BytesRead, NULL) ) { + // go nothing doesn't mean it hits EOF!!!!!! + // we can not just exit now, instead, we have to wait and poll + // until the application is terminated. + // + if (BytesRead == 0) { + // if read nothing and the application is gone, we can quit now + if (ExitPending) + break; + if (!WaitForSingleObject(pPipe->hExitEvent, PIPE_OUTPUT_TIMEOUT)) + ExitPending = TRUE; + } + else { + if (!WriteFile(pPipe->hPipe, pPipe->Buffer, BytesRead, &BytesWritten, NULL) || + BytesWritten != BytesRead) + break; + } + } + // if we were out of loop because of errors, wait for the cpu thread. + if (!ExitPending) + WaitForSingleObject(pPipe->hExitEvent, INFINITE); + + CloseHandle(pPipe->hFile); + CloseHandle(pPipe->hPipe); + CloseHandle(pPipe->hExitEvent); + DeleteFile(pPipe->pFileName); + free(pPipe->pFileName); + free(pPipe); + ExitThread(0); +} + +BOOL cmdHandleStdinWithPipe ( + PREDIRCOMPLETE_INFO pRdrInfo + ) +{ + + HANDLE hStdinFile; + PCHAR pStdinFileName; + PPIPE_INPUT pPipe; + BYTE *Buffer; + DWORD ThreadId; + HANDLE hEvent; + HANDLE hFileWrite; + + if(!cmdCreateTempFile(&hStdinFile,&pStdinFileName)) + return FALSE; + + + // must have a different handle so that reader(dos app) and writter(us) + // wont use the same handle object(especially, file position) + hFileWrite = CreateFile(pStdinFileName, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_TEMPORARY, + NULL + ); + if (hFileWrite == INVALID_HANDLE_VALUE) { + CloseHandle(hStdinFile); + DeleteFile(pStdinFileName); + return FALSE; + } + Buffer = malloc(sizeof(PIPE_INPUT) + PIPE_INPUT_BUFFER_SIZE); + if (Buffer == NULL) { + CloseHandle(hStdinFile); + CloseHandle(hFileWrite); + DeleteFile(pStdinFileName); + return FALSE; + } + hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (hEvent == NULL) { + CloseHandle(hStdinFile); + CloseHandle(hFileWrite); + DeleteFile(pStdinFileName); + free(Buffer); + return FALSE; + } + pPipe = (PPIPE_INPUT)Buffer; + pPipe->Buffer = Buffer + sizeof(PIPE_INPUT); + pPipe->BufferSize = PIPE_INPUT_BUFFER_SIZE; + pPipe->fEOF = FALSE; + pPipe->hFileWrite = hFileWrite; + pPipe->hFileRead = hStdinFile; + pPipe->hDataEvent = hEvent; + pPipe->hPipe = pRdrInfo->ri_hStdIn; + pPipe->pFileName = pStdinFileName; + InitializeCriticalSection(&pPipe->CriticalSection); + pPipe->hThread = CreateThread ((LPSECURITY_ATTRIBUTES)NULL, + (DWORD)0, + (LPTHREAD_START_ROUTINE)cmdPipeInThread, + (LPVOID)pPipe, + 0, + &ThreadId + ); + if (pPipe->hThread == NULL) { + CloseHandle(hFileWrite); + CloseHandle(pPipe->hDataEvent); + CloseHandle(hStdinFile); + DeleteFile(pStdinFileName); + free(Buffer); + return FALSE; + } + // always have the new node in the head of the list because + // it is the node used by the top command.com running in the process. + // We may have multiple command.com instances running in the same + // ntvdm proecess and each command.com has a private PREDIRCOMPLETE_INFO + // associated with it if its stdin is redirected to a pipe. + pPipe->Next = cmdPipeList; + cmdPipeList = pPipe; + pRdrInfo->ri_hStdInFile = hStdinFile; + pRdrInfo->ri_pPipeStdIn = pPipe; + return TRUE; +} + +/* Independent thread to read from pipe(NTVDM STDIN) and write to + file(DOS application STDIN) until either the pipe is broken or + there are some errors. + This thread may never terminate itself because it can block + in the ReadFile call to the pipe forever. If this is the case, + we have to rely on the CPU thread to kill it. To allow the CPU + thread safely launching the killing, this thread yields the + critical section when it is safe to be killed and the CPU thread + would claim the critical section first before going for kill. + */ + +VOID cmdPipeInThread(LPVOID lpParam) +{ + PPIPE_INPUT pPipe; + DWORD BytesRead, BytesWritten; + BOOL ReadStatus, WriteStatus; + BOOL ApplicationTerminated, fEOF; + + pPipe = (PPIPE_INPUT)lpParam; + while (TRUE) { + + // this read can take forever without getting back anything + ReadStatus = ReadFile(pPipe->hPipe, pPipe->Buffer, + pPipe->BufferSize, &BytesRead, NULL); + + // claim the critical section so we won't get killed + // by the CPU thread + EnterCriticalSection(&pPipe->CriticalSection); + if (ReadStatus) { + if (BytesRead != 0) { + WriteStatus = WriteFile(pPipe->hFileWrite, + pPipe->Buffer, + BytesRead, + &BytesWritten, + NULL + ); + if (pPipe->WaitData && WriteStatus && BytesWritten != 0) + SetEvent(pPipe->hDataEvent); + } + } + else { + if (GetLastError() == ERROR_BROKEN_PIPE) { + + // pipe is broken and more data to read? + ASSERT(BytesRead == 0); + pPipe->fEOF = TRUE; + LeaveCriticalSection(&pPipe->CriticalSection); + break; + } + } + // as soon as we leave the critical seciton, the CPU thread may + // step in and kill us + LeaveCriticalSection(&pPipe->CriticalSection); + } + ExitThread(0); +} + +/* cmdPipeFileDataEOF - Check for new data or EOF + * + * + * Entry - hFile, DOS application STDIN file handle(file) + * &fEOF, to return if the pipe is broken + * EXIT - TRUE if either there are new data or EOF is true + * *fEOF == TRUE if EOF + */ + +BOOL cmdPipeFileDataEOF(HANDLE hFile, BOOL *fEOF) +{ + PPIPE_INPUT pPipe; + BOOL NewData; + DWORD WaitStatus; + + pPipe = cmdPipeList; + while (pPipe != NULL && pPipe->hFileRead != hFile) + pPipe = pPipe->Next; + + NewData = TRUE; + *fEOF = TRUE; + + if (pPipe != NULL) { + EnterCriticalSection(&pPipe->CriticalSection); + *fEOF = pPipe->fEOF; + if (!(*fEOF)) { + pPipe->WaitData = TRUE; + LeaveCriticalSection(&pPipe->CriticalSection); + WaitStatus = WaitForSingleObject(pPipe->hDataEvent, PIPE_INPUT_TIMEOUT); + EnterCriticalSection(&pPipe->CriticalSection); + *fEOF = pPipe->fEOF; + pPipe->WaitData = FALSE; + NewData = WaitStatus == 0 ? TRUE : FALSE; + } + LeaveCriticalSection(&pPipe->CriticalSection); + } + return(NewData || *fEOF); +} + +/* cmdPipeFileEOF - Check if the pipe is broken + * + * + * Entry - hFile, DOS application STDIN file handle(file) + * + * EXIT - TRUE if the write end of the pipe is closed + */ + + +BOOL cmdPipeFileEOF(HANDLE hFile) +{ + PPIPE_INPUT pPipe; + BOOL fEOF; + + pPipe = cmdPipeList; + while (pPipe != NULL && pPipe->hFileRead != hFile) + pPipe = pPipe->Next; + + fEOF = TRUE; + + if (pPipe != NULL) { + EnterCriticalSection(&pPipe->CriticalSection); + fEOF = pPipe->fEOF; + LeaveCriticalSection(&pPipe->CriticalSection); + } + if (!fEOF) { + Sleep(PIPE_INPUT_TIMEOUT); + EnterCriticalSection(&pPipe->CriticalSection); + fEOF = pPipe->fEOF; + LeaveCriticalSection(&pPipe->CriticalSection); + } + return (fEOF); +} diff --git a/private/mvdm/dos/command/makefile b/private/mvdm/dos/command/makefile new file mode 100644 index 000000000..985386d82 --- /dev/null +++ b/private/mvdm/dos/command/makefile @@ -0,0 +1,9 @@ +# Command makefile +# 17-Sep-1991 Sudeep Bharati Created +# + +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/mvdm/dos/command/sources b/private/mvdm/dos/command/sources new file mode 100644 index 000000000..5d0049299 --- /dev/null +++ b/private/mvdm/dos/command/sources @@ -0,0 +1,60 @@ +!IF 0 + +Copyright (c) 1989-1991 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +History: + Created 17-Sep-1991 by Sudeep Bharati (sudeepb) + from template created 12-Apr-1990 by Steve Wood (stevewo) + + +NOTE: Commented description of this file is in \nt\public\oak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=mvdm +MINORCOMP=command + +TARGETNAME=command +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=LIBRARY +TARGETLIBS= + +SOFTPC_TREE=$(BASEDIR)\private\mvdm\softpc.new + +INCLUDES=..\..\inc;..\..\..\windows\inc;$(SOFTPC_TREE)\base\inc;$(SOFTPC_TREE)\host\inc + + +NTPROFILEINPUT=YES + +SOURCES=cmd.c \ + cmddata.c \ + cmddisp.c \ + cmdexec.c \ + cmdexit.c \ + cmdmisc.c \ + cmdpif.c \ + cmdredir.c \ + cmdconf.c \ + cmdkeyb.c \ + cmdenv.c + +I386_SOURCES= +MIPS_SOURCES= + +C_DEFINES=-DWIN_32 -DNTVDM + +UMTYPE=console +UMTEST= +UMLIBS= |