1855 lines
59 KiB
C
1855 lines
59 KiB
C
/*++
|
|
|
|
Copyright (c) 1988-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cext.c
|
|
|
|
Abstract:
|
|
|
|
External command support
|
|
|
|
--*/
|
|
|
|
#include "cmd.h"
|
|
|
|
#define DBENV 0x0080
|
|
#define DBENVSCAN 0x0010
|
|
|
|
HANDLE hChildProcess;
|
|
|
|
unsigned start_type ; /* D64 */
|
|
|
|
extern UINT CurrentCP;
|
|
extern TCHAR Fmt16[] ; /* @@5h */
|
|
|
|
extern unsigned DosErr ;
|
|
extern BOOL CtrlCSeen;
|
|
|
|
extern TCHAR CurDrvDir[] ;
|
|
|
|
extern TCHAR CmdExt[], BatExt[], PathStr[] ;
|
|
extern TCHAR PathExtStr[], PathExtDefaultStr[];
|
|
extern TCHAR ComSpec[] ; /* M033 - Use ComSpec for SM memory */
|
|
extern TCHAR ComSpecStr[] ; /* M033 - Use ComSpec for SM memory */
|
|
extern void tokshrink(TCHAR*);
|
|
|
|
extern TCHAR PathChar ;
|
|
extern TCHAR SwitChar ;
|
|
|
|
extern PTCHAR pszTitleCur;
|
|
extern BOOLEAN fTitleChanged;
|
|
|
|
extern int LastRetCode ;
|
|
extern HANDLE PipePid ; /* M024 - Store PID from piped cmd */
|
|
|
|
extern struct envdata CmdEnv ; // Holds info to manipulate Cmd's environment
|
|
|
|
extern int glBatType; // to distinguish OS/2 vs DOS errorlevel behavior depending on a script file name
|
|
|
|
TCHAR szNameEqExitCodeEnvVar[] = TEXT ("=ExitCode");
|
|
TCHAR szNameEqExitCodeAsciiEnvVar[] = TEXT ("=ExitCodeAscii");
|
|
|
|
|
|
WORD
|
|
GetProcessSubsystemType(
|
|
HANDLE hProcess
|
|
);
|
|
|
|
|
|
/*** ExtCom - controls the execution of external programs
|
|
*
|
|
* Purpose:
|
|
* Synchronously execute an external command. Call ECWork with the
|
|
* appropriate values to have this done.
|
|
*
|
|
* ExtCom(struct cmdnode *n)
|
|
*
|
|
* Args:
|
|
* Parse tree node containing the command to be executed.
|
|
*
|
|
* Returns:
|
|
* Whatever ECWork returns.
|
|
*
|
|
* Notes:
|
|
* During batch processing, labels are ignored. Empty commands are
|
|
* also ignored.
|
|
*
|
|
*/
|
|
|
|
int ExtCom(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
if (CurrentBatchFile && *n->cmdline == COLON)
|
|
return(SUCCESS) ;
|
|
|
|
if (n && n->cmdline && mystrlen(n->cmdline)) {
|
|
return(ECWork(n, AI_SYNC, CW_W_YES)) ; /* M024 */
|
|
} ;
|
|
|
|
return(SUCCESS) ;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/********************* START OF SPECIFICATION **************************/
|
|
/* */
|
|
/* SUBROUTINE NAME: ECWork */
|
|
/* */
|
|
/* DESCRIPTIVE NAME: Execute External Commands Worker */
|
|
/* */
|
|
/* FUNCTION: Execute External Commands */
|
|
/* This routine calls SearchForExecutable routine to search */
|
|
/* for the executable command. If the command ( .EXE, .COM, */
|
|
/* or .CMD file ) is found, the command is executed. */
|
|
/* */
|
|
/* ENTRY POINT: ECWork */
|
|
/* LINKAGE: NEAR */
|
|
/* */
|
|
/* INPUT: n - the parse tree node containing the command to be executed*/
|
|
/* */
|
|
/* ai - the asynchronous indicator */
|
|
/* - 0 = Exec synchronous with parent */
|
|
/* - 1 = Exec asynchronous and discard child return code */
|
|
/* - 2 = Exec asynchronous and save child return code */
|
|
/* */
|
|
/* wf - the wait flag */
|
|
/* - 0 = Wait for process completion */
|
|
/* - 1 = Return immediately (Pipes) */
|
|
/* */
|
|
/* OUTPUT: None. */
|
|
/* */
|
|
/* EXIT-NORMAL: */
|
|
/* If synchronous execution, the return code of the command is */
|
|
/* returned. */
|
|
/* */
|
|
/* If asynchronous execution, the return code of the exec call */
|
|
/* is returned. */
|
|
/* */
|
|
/* EXIT-ERROR: */
|
|
/* Return FAILURE to the caller. */
|
|
/* */
|
|
/* */
|
|
/********************** END OF SPECIFICATION **************************/
|
|
/*** ECWork - begins the execution of external commands
|
|
*
|
|
* Purpose:
|
|
* To search for and execute an external command. Update LastRetCode
|
|
* if an external program was executed.
|
|
*
|
|
* int ECWork(struct cmdnode *n, unsigned ai, unsigned wf)
|
|
*
|
|
* Args:
|
|
* n - the parse tree node containing the command to be executed
|
|
* ai - the asynchronous indicator
|
|
* - 0 = Exec synchronous with parent
|
|
* - 1 = Exec asynchronous and discard child return code
|
|
* - 2 = Exec asynchronous and save child return code
|
|
* wf - the wait flag
|
|
* - 0 = Wait for process completion
|
|
* - 1 = Return immediately (Pipes)
|
|
*
|
|
* Returns:
|
|
* If syncronous execution, the return code of the command is returned.
|
|
* If asyncronous execution, the return code of the exec call is returned.
|
|
*
|
|
* Notes:
|
|
* The pid of a program that will be waited on is placed in the global
|
|
* variable Retcds.ptcod so that WaitProc can use it and so SigHand()
|
|
* can kill it if necessary (only during SYNC exec's).
|
|
* M024 - Added wait flag parm so pipes can get immediate return while
|
|
* still doing an AI_KEEP async exec.
|
|
* - Considerable revisions to structure.
|
|
*/
|
|
|
|
int ECWork(n, ai, wf)
|
|
struct cmdnode *n ;
|
|
unsigned ai ;
|
|
unsigned wf ;
|
|
{
|
|
TCHAR *fnptr, /* Ptr to filename */
|
|
*argptr, /* Command Line String */
|
|
*tptr; /* M034 - Temps */
|
|
int i ; /* Work variable */
|
|
TCHAR * onb ; /* M035 - Obj name buffer */
|
|
ULONG rc;
|
|
|
|
|
|
if (!(fnptr = mkstr(MAX_PATH*sizeof(TCHAR)+sizeof(TCHAR)))) /* Loc/nam of file to exec */
|
|
return(FAILURE) ;
|
|
|
|
argptr = GetTitle( n );
|
|
tptr = argptr;
|
|
if (argptr == NULL) {
|
|
|
|
return( FAILURE );
|
|
|
|
}
|
|
|
|
i = SearchForExecutable(n, fnptr) ;
|
|
if (i == SFE_ISEXECOM) { /* Found .com or .exe file */
|
|
|
|
if (!(onb = (TCHAR *)mkstr(MAX_PATH*sizeof(TCHAR)+sizeof(TCHAR)))) /* M035 */
|
|
{
|
|
return(FAILURE) ;
|
|
|
|
}
|
|
|
|
SetConTitle( tptr );
|
|
rc = ExecPgm( n, onb, ai, wf, argptr, fnptr, tptr);
|
|
ResetConTitle( pszTitleCur );
|
|
|
|
return( rc );
|
|
|
|
} ;
|
|
|
|
if (i == SFE_ISBAT) { /* Found .cmd file, call BatProc */
|
|
|
|
SetConTitle(tptr);
|
|
rc = BatProc(n, fnptr, BT_CHN) ;
|
|
ResetConTitle( pszTitleCur );
|
|
return(rc) ;
|
|
} ;
|
|
|
|
if (i == SFE_ISDIR) {
|
|
return ChangeDir2(fnptr, TRUE);
|
|
}
|
|
|
|
LastRetCode = DosErr;
|
|
if (i == SFE_FAIL) { /* Out of Memory Error */
|
|
return(FAILURE) ;
|
|
} ;
|
|
|
|
if (DosErr == MSG_DIR_BAD_COMMAND_OR_FILE) {
|
|
PutStdErr(DosErr, ONEARG, n->cmdline);
|
|
}
|
|
else {
|
|
PutStdErr(DosErr, NOARGS);
|
|
}
|
|
return(FAILURE) ;
|
|
}
|
|
|
|
|
|
#ifndef WIN95_CMD
|
|
VOID
|
|
RestoreCurrentDirectories( VOID )
|
|
|
|
/* this routine sets the current process's current directories to
|
|
those of the child if the child was the VDM to fix DOS batch files.
|
|
*/
|
|
|
|
{
|
|
ULONG cchCurDirs;
|
|
UINT PreviousErrorMode;
|
|
PTCHAR pCurDirs,pCurCurDir;
|
|
BOOL CurDrive=TRUE;
|
|
#ifdef UNICODE
|
|
PCHAR pT;
|
|
#endif
|
|
|
|
cchCurDirs = GetVDMCurrentDirectories( 0,
|
|
NULL
|
|
);
|
|
if (cchCurDirs == 0)
|
|
return;
|
|
|
|
pCurDirs = gmkstr(cchCurDirs*sizeof(TCHAR));
|
|
#ifdef UNICODE
|
|
pT = gmkstr(cchCurDirs);
|
|
#endif
|
|
|
|
GetVDMCurrentDirectories( cchCurDirs,
|
|
#ifdef UNICODE
|
|
pT
|
|
#else
|
|
pCurDirs
|
|
#endif
|
|
);
|
|
#ifdef UNICODE
|
|
MultiByteToWideChar(CurrentCP, MB_PRECOMPOSED, pT, -1, pCurDirs, cchCurDirs);
|
|
#endif
|
|
|
|
// set error mode so we don't get popup if trying to set curdir
|
|
// on empty floppy drive
|
|
|
|
PreviousErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
for (pCurCurDir=pCurDirs;*pCurCurDir!=NULLC;pCurCurDir+=(_tcslen(pCurCurDir)+1)) {
|
|
ChangeDir2(pCurCurDir,CurDrive);
|
|
CurDrive=FALSE;
|
|
}
|
|
SetErrorMode(PreviousErrorMode);
|
|
//free(pCurDirs);
|
|
#ifdef UNICODE
|
|
FreeStr((PTCHAR)pT);
|
|
#endif
|
|
}
|
|
#endif // WIN95_CMD
|
|
|
|
|
|
/********************* START OF SPECIFICATION **************************/
|
|
/* */
|
|
/* SUBROUTINE NAME: ExecPgm */
|
|
/* */
|
|
/* DESCRIPTIVE NAME: Call DosExecPgm to execute External Command */
|
|
/* */
|
|
/* FUNCTION: Execute External Commands with DosExecPgm */
|
|
/* This routine calls DosExecPgm to execute the command. */
|
|
/* */
|
|
/* */
|
|
/* NOTES: This is a new routine added for OS/2 1.1 release. */
|
|
/* */
|
|
/* */
|
|
/* ENTRY POINT: ExecPgm */
|
|
/* LINKAGE: NEAR */
|
|
/* */
|
|
/* INPUT: n - the parse tree node containing the command to be executed*/
|
|
/* */
|
|
/* ai - the asynchronous indicator */
|
|
/* - 0 = Exec synchronous with parent */
|
|
/* - 1 = Exec asynchronous and discard child return code */
|
|
/* - 2 = Exec asynchronous and save child return code */
|
|
/* */
|
|
/* wf - the wait flag */
|
|
/* - 0 = Wait for process completion */
|
|
/* - 1 = Return immediately (Pipes) */
|
|
/* */
|
|
/* OUTPUT: None. */
|
|
/* */
|
|
/* EXIT-NORMAL: */
|
|
/* If synchronous execution, the return code of the command is */
|
|
/* returned. */
|
|
/* */
|
|
/* If asynchronous execution, the return code of the exec call */
|
|
/* is returned. */
|
|
/* */
|
|
/* EXIT-ERROR: */
|
|
/* Return FAILURE to the caller. */
|
|
/* */
|
|
/* EFFECTS: */
|
|
/* */
|
|
/* INTERNAL REFERENCES: */
|
|
/* ROUTINES: */
|
|
/* ExecError - Handles execution error */
|
|
/* PutStdErr - Print an error message */
|
|
/* WaitProc - wait for the termination of the specified process, */
|
|
/* its child process, and related pipelined */
|
|
/* processes. */
|
|
/* */
|
|
/* */
|
|
/* EXTERNAL REFERENCES: */
|
|
/* ROUTINES: */
|
|
/* DOSEXECPGM - Execute the specified program */
|
|
/* DOSSMSETTITLE - Set title for the presentation manager */
|
|
/* */
|
|
/********************** END OF SPECIFICATION **************************/
|
|
|
|
int
|
|
ExecPgm(
|
|
IN struct cmdnode *n,
|
|
IN TCHAR *onb,
|
|
IN unsigned int ai,
|
|
IN unsigned int wf,
|
|
IN TCHAR * argptr,
|
|
IN TCHAR * fnptr,
|
|
IN TCHAR * tptr
|
|
)
|
|
{
|
|
int i ; /* Work variable */
|
|
BOOL b;
|
|
BOOL VdmProcess = FALSE;
|
|
BOOL WowProcess = FALSE;
|
|
BOOL GuiProcess = FALSE;
|
|
LPTSTR CopyCmdValue;
|
|
|
|
HDESK hdesk;
|
|
LPTSTR lpDesktop;
|
|
DWORD cbDesktop = 0;
|
|
HWINSTA hwinsta;
|
|
LPTSTR p;
|
|
DWORD cbWinsta = 0;
|
|
|
|
TCHAR szValEqExitCodeEnvVar [20];
|
|
TCHAR szValEqExitCodeAsciiEnvVar [12];
|
|
|
|
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ChildProcessInfo;
|
|
|
|
memset( &StartupInfo, 0, sizeof( StartupInfo ) );
|
|
StartupInfo.cb = sizeof( StartupInfo );
|
|
StartupInfo.lpTitle = tptr;
|
|
StartupInfo.dwX = 0;
|
|
StartupInfo.dwY = 1;
|
|
StartupInfo.dwXSize = 100;
|
|
StartupInfo.dwYSize = 100;
|
|
StartupInfo.dwFlags = 0;//STARTF_SHELLOVERRIDE;
|
|
StartupInfo.wShowWindow = SW_SHOWNORMAL;
|
|
|
|
// Pass current Desktop to a new process.
|
|
|
|
hwinsta = GetProcessWindowStation();
|
|
GetUserObjectInformation( hwinsta, UOI_NAME, NULL, 0, &cbWinsta );
|
|
|
|
hdesk = GetThreadDesktop ( GetCurrentThreadId() );
|
|
GetUserObjectInformation (hdesk, UOI_NAME, NULL, 0, &cbDesktop);
|
|
|
|
if ((lpDesktop = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, cbDesktop + cbWinsta + 32) ) != NULL ) {
|
|
p = lpDesktop;
|
|
if ( GetUserObjectInformation (hwinsta, UOI_NAME, p, cbWinsta, &cbWinsta) ) {
|
|
if (cbWinsta > 0) {
|
|
p += ((cbWinsta/sizeof(TCHAR))-1);
|
|
*p++ = L'\\';
|
|
}
|
|
if ( GetUserObjectInformation (hdesk, UOI_NAME, p, cbDesktop, &cbDesktop) ) {
|
|
StartupInfo.lpDesktop = lpDesktop;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Incredibly ugly hack for Win95 compatibility.
|
|
//
|
|
// XCOPY in Win95 reaches up into it's parent process to see if it was
|
|
// invoked in a batch file. If so, then XCOPY pretends COPYCMD=/Y
|
|
//
|
|
// There is no way we can do this for NT. The best that can be done is
|
|
// to detect if we're in a batch file starting XCOPY and then temporarily
|
|
// change COPYCMD=/Y.
|
|
//
|
|
|
|
{
|
|
const TCHAR *p = MyGetEnvVarPtr( TEXT( "COPYCMD" ));
|
|
|
|
if (p == NULL) {
|
|
p = TEXT( "" );
|
|
}
|
|
|
|
CopyCmdValue = malloc( (mystrlen( p ) + 1) * sizeof( TCHAR ));
|
|
if (CopyCmdValue == NULL) {
|
|
PutStdErr( MSG_NO_MEMORY, NOARGS );
|
|
return FAILURE;
|
|
}
|
|
mystrcpy( CopyCmdValue, p );
|
|
|
|
if ((SingleBatchInvocation
|
|
|| SingleCommandInvocation
|
|
|| CurrentBatchFile )
|
|
&& (p = mystrrchr( fnptr, TEXT( '\\' ))) != NULL
|
|
&& !lstrcmp( p, TEXT( "\\XCOPY.EXE" ))
|
|
) {
|
|
|
|
SetEnvVar( TEXT( "COPYCMD" ), TEXT( "/Y" ), NULL );
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the restricted token exists then create the process with the
|
|
// restricted token.
|
|
// Else create the process without any restrictions.
|
|
//
|
|
|
|
if ((CurrentBatchFile != NULL) && (CurrentBatchFile->hRestrictedToken != NULL)) {
|
|
|
|
b = CreateProcessAsUser( CurrentBatchFile->hRestrictedToken,
|
|
fnptr,
|
|
argptr,
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
TRUE,
|
|
0,
|
|
NULL,
|
|
CurDrvDir,
|
|
&StartupInfo,
|
|
&ChildProcessInfo
|
|
);
|
|
|
|
|
|
} else {
|
|
b = CreateProcess( fnptr,
|
|
argptr,
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
(LPSECURITY_ATTRIBUTES) NULL,
|
|
TRUE,
|
|
0,
|
|
NULL,
|
|
CurDrvDir,
|
|
&StartupInfo,
|
|
&ChildProcessInfo
|
|
);
|
|
}
|
|
|
|
if (!b) {
|
|
DosErr = i = GetLastError();
|
|
} else {
|
|
hChildProcess = ChildProcessInfo.hProcess;
|
|
#if !defined( WIN95_CMD ) && DBG
|
|
if (hChildProcess == NULL) {
|
|
DbgBreakPoint( );
|
|
}
|
|
#endif
|
|
CloseHandle(ChildProcessInfo.hThread);
|
|
}
|
|
|
|
//
|
|
// Undo ugly hack
|
|
//
|
|
|
|
SetEnvVar( TEXT( "COPYCMD" ), CopyCmdValue, NULL );
|
|
free( CopyCmdValue );
|
|
|
|
HeapFree (GetProcessHeap(), 0, lpDesktop);
|
|
if (!b) {
|
|
|
|
if (fEnableExtensions && DosErr == ERROR_BAD_EXE_FORMAT) {
|
|
SHELLEXECUTEINFO sei;
|
|
|
|
memset(&sei, 0, sizeof(sei));
|
|
|
|
//
|
|
// Use the DDEWAIT flag so apps can finish their DDE conversation
|
|
// before ShellExecuteEx returns. Otherwise, apps like Word will
|
|
// complain when they try to exit, confusing the user.
|
|
//
|
|
|
|
sei.cbSize = sizeof(sei);
|
|
sei.fMask = SEE_MASK_HASTITLE |
|
|
SEE_MASK_NO_CONSOLE |
|
|
SEE_MASK_FLAG_DDEWAIT |
|
|
SEE_MASK_NOCLOSEPROCESS;
|
|
sei.lpFile = fnptr;
|
|
sei.lpParameters = n->argptr;
|
|
sei.lpDirectory = CurDrvDir;
|
|
sei.nShow = StartupInfo.wShowWindow;
|
|
|
|
try {
|
|
b = ShellExecuteEx( &sei );
|
|
|
|
if (b) {
|
|
hChildProcess = sei.hProcess;
|
|
leave;
|
|
}
|
|
|
|
if (sei.hInstApp == NULL) {
|
|
DosErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
} else if ((DWORD_PTR)sei.hInstApp == HINSTANCE_ERROR) {
|
|
DosErr = ERROR_FILE_NOT_FOUND;
|
|
} else {
|
|
DosErr = HandleToUlong(sei.hInstApp);
|
|
}
|
|
|
|
} except( DosErr = GetExceptionCode( ), EXCEPTION_EXECUTE_HANDLER ) {
|
|
b = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
if (!b) {
|
|
mystrcpy( onb, fnptr );
|
|
ExecError( onb ) ;
|
|
|
|
return (FAILURE) ;
|
|
}
|
|
}
|
|
|
|
#ifndef WIN95_CMD
|
|
VdmProcess = ((UINT_PTR)(hChildProcess) & 1) ? TRUE : FALSE;
|
|
WowProcess = ((UINT_PTR)(hChildProcess) & 2) ? TRUE : FALSE;
|
|
#endif // WIN95_CMD
|
|
if (hChildProcess == NULL
|
|
|| (fEnableExtensions
|
|
&& CurrentBatchFile == 0
|
|
&& !SingleBatchInvocation
|
|
&& !SingleCommandInvocation
|
|
&& ai == AI_SYNC
|
|
&& (WowProcess
|
|
|| GetProcessSubsystemType(hChildProcess) == IMAGE_SUBSYSTEM_WINDOWS_GUI
|
|
)
|
|
)
|
|
) {
|
|
//
|
|
// If extensions enabled and doing a synchronous exec of a GUI
|
|
// application, then change it into an asynchronous exec, with the
|
|
// return code discarded, ala Win 3.x and Win 95 COMMAND.COM
|
|
//
|
|
ai = AI_DSCD;
|
|
GuiProcess = TRUE;
|
|
}
|
|
|
|
i = SUCCESS;
|
|
start_type = EXECPGM;
|
|
|
|
if (ai == AI_SYNC) { /* Async exec... */
|
|
LastRetCode = WaitProc( hChildProcess );
|
|
i = LastRetCode ;
|
|
|
|
//
|
|
// this is added initially to support new network batch scripts
|
|
// but it can be used by any process.
|
|
//
|
|
// always set "=ExitCode" env. variable as string of 8 hex digits
|
|
// representing LastRetCode
|
|
|
|
_stprintf (szValEqExitCodeEnvVar, TEXT("%08X"), i);
|
|
SetEnvVar(szNameEqExitCodeEnvVar, szValEqExitCodeEnvVar, &CmdEnv);
|
|
|
|
// IF LastRetCode printable
|
|
// THEN set "=ExitCodeAscii" env. variable equal LastRetCode
|
|
// ELSE remove "=ExitCodeAscii" env. variable
|
|
|
|
if ( (i >= 0x20) && (i <= 0x7e) ) { // isprint
|
|
_stprintf (szValEqExitCodeAsciiEnvVar, TEXT("%01C"), i);
|
|
SetEnvVar(szNameEqExitCodeAsciiEnvVar, szValEqExitCodeAsciiEnvVar, &CmdEnv);
|
|
} else
|
|
SetEnvVar(szNameEqExitCodeAsciiEnvVar, TEXT("\0"), &CmdEnv);
|
|
|
|
|
|
#ifndef WIN95_CMD
|
|
if (VdmProcess) {
|
|
RestoreCurrentDirectories();
|
|
}
|
|
#endif // WIN95_CMD
|
|
}
|
|
|
|
/* If real async (discarding retcode), just print PID and return. If
|
|
* piped process (keeping retcode but not waiting) just store PID for
|
|
* ePipe and return SUCCESS from exec. Else, wait around for the return
|
|
* code before going back.
|
|
*/
|
|
if (ai == AI_DSCD) { /* DETach only */
|
|
if (!GuiProcess)
|
|
PutStdErr(MSG_PID_IS, ONEARG, argstr1(TEXT("%u"), HandleToUlong(hChildProcess)));
|
|
if (hChildProcess != NULL) {
|
|
CloseHandle( hChildProcess );
|
|
hChildProcess = NULL;
|
|
}
|
|
} else if (ai == AI_KEEP) { /* Async exec... */
|
|
if (wf == CW_W_YES) { /* ...normal type */
|
|
LastRetCode = WaitProc(hChildProcess);
|
|
i = LastRetCode ;
|
|
} else { /* Async exec... */
|
|
PipePid = hChildProcess ; /* M035 */
|
|
}
|
|
} else {
|
|
}
|
|
|
|
|
|
return (i) ; /* i == return from DOSEXECPGM */
|
|
}
|
|
|
|
/********************* START OF SPECIFICATION **************************/
|
|
/* */
|
|
/* SUBROUTINE NAME: SearchForExecutable */
|
|
/* */
|
|
/* DESCRIPTIVE NAME: Search for Executable File */
|
|
/* */
|
|
/* FUNCTION: This routine searches the specified executable file. */
|
|
/* If the file extension is specified, */
|
|
/* CMD.EXE searches for the file with the file extension */
|
|
/* to execute. If the specified file with the extension */
|
|
/* is not found, CMD.EXE will display an error message to */
|
|
/* indicate that the file is not found. */
|
|
/* If the file extension is not specified, */
|
|
/* CMD.EXE searches for the file with the order of these */
|
|
/* file extensions, .COM, .EXE, .CMD, and .BAT. */
|
|
/* The file which is found first will be executed. */
|
|
/* */
|
|
/* NOTES: 1) If a path is given, only the specified directory is */
|
|
/* searched. */
|
|
/* 2) If no path is given, the current directory of the */
|
|
/* drive specified is searched followed by the */
|
|
/* directories in the PATH environment variable. */
|
|
/* 3) If no executable file is found, an error message is */
|
|
/* printed. */
|
|
/* */
|
|
/* ENTRY POINT: SearchForExecutable */
|
|
/* LINKAGE: NEAR */
|
|
/* */
|
|
/* INPUT: */
|
|
/* n - parse tree node containing the command to be searched for */
|
|
/* loc - the string in which the location of the command is to be */
|
|
/* placed */
|
|
/* */
|
|
/* OUTPUT: None. */
|
|
/* */
|
|
/* EXIT-NORMAL: */
|
|
/* Returns: */
|
|
/* SFE_EXECOM, if a .EXE or .COM file is found. */
|
|
/* SFE_ISBAT, if a .CMD file is found. */
|
|
/* SFE_ISDIR, if a directory is found. */
|
|
/* SFE_NOTFND, if no executable file is found. */
|
|
/* */
|
|
/* EXIT-ERROR: */
|
|
/* Return FAILURE or */
|
|
/* SFE_FAIL, if out of memory. */
|
|
/* */
|
|
/* EFFECTS: None. */
|
|
/* */
|
|
/* INTERNAL REFERENCES: */
|
|
/* ROUTINES: */
|
|
/* DoFind - Find the specified file. */
|
|
/* GetEnvVar - Get full path. */
|
|
/* FullPath - build a full path name. */
|
|
/* TokStr - tokenize argument strings. */
|
|
/* mkstr - allocate space for a string. */
|
|
/* */
|
|
/* EXTERNAL REFERENCES: */
|
|
/* ROUTINES: */
|
|
/* None */
|
|
/* */
|
|
/********************** END OF SPECIFICATION **************************/
|
|
|
|
SearchForExecutable(n, loc)
|
|
struct cmdnode *n ;
|
|
TCHAR *loc ;
|
|
{
|
|
TCHAR *tokpath ;
|
|
TCHAR *extPath ;
|
|
TCHAR *extPathWrk ;
|
|
TCHAR *fname;
|
|
TCHAR *p1;
|
|
TCHAR *tmps01;
|
|
TCHAR *tmps02 = NULL;
|
|
TCHAR pcstr[3];
|
|
LONG BinaryType;
|
|
|
|
size_t cName; // number of characters in file name.
|
|
|
|
int tplen; // Length of the current tokpath token
|
|
int dotloc; // loc offset where extension is appended
|
|
int pcloc; // M014 - Flag. True=user had partial path
|
|
int addpchar; // True - append PathChar to string @@5g
|
|
TCHAR *j ;
|
|
|
|
TCHAR wrkcmdline[MAX_PATH] ;
|
|
unsigned tokpathlen;
|
|
|
|
//
|
|
// Test for name too long first. If it is, we avoid wasting time
|
|
//
|
|
p1 = StripQuotes( n->cmdline );
|
|
if ((cName = mystrlen(p1)) >= MAX_PATH) {
|
|
DosErr = MSG_LINES_TOO_LONG;
|
|
return(SFE_NOTFND) ;
|
|
}
|
|
|
|
//
|
|
// If cmd extensions enable, then treat CMD without an extension
|
|
// or path as a reference to COMSPEC variable. Guarantees we dont
|
|
// get a random copy of CMD.EXE
|
|
//
|
|
|
|
if (fEnableExtensions && (p1 == NULL || !_tcsnicmp( p1, TEXT( "cmd " ), 4))) {
|
|
p1 = GetEnvVar( ComSpecStr );
|
|
if (p1 == NULL) {
|
|
DosErr = MSG_INVALID_COMSPEC;
|
|
return SFE_NOTFND;
|
|
}
|
|
}
|
|
|
|
mytcsnset(wrkcmdline, NULLC, MAX_PATH);
|
|
mystrcpy(wrkcmdline, p1);
|
|
FixPChar( wrkcmdline, SwitChar );
|
|
|
|
//
|
|
// Create the path character string, this will be search string
|
|
//
|
|
pcstr[0] = PathChar;
|
|
pcstr[1] = COLON;
|
|
pcstr[2] = NULLC;
|
|
|
|
//
|
|
// The variable pcloc is used as a flag to indicate whether the user
|
|
// did or didn't specify a drive or a partial path in his original
|
|
// input. It will be NZ if drive or path was specified.
|
|
//
|
|
|
|
pcloc = ((mystrcspn(wrkcmdline,pcstr)) < cName) ;
|
|
pcstr[1] = NULLC ; // Fixup pathchar string
|
|
|
|
//
|
|
// handle the case of the user typing in a file name of
|
|
// ".", "..", or ending in "\"
|
|
// pcloc true say string has to have either a pathchar or colon
|
|
//
|
|
if ( pcloc ) {
|
|
if (!(p1 = mystrrchr( wrkcmdline, PathChar ))) {
|
|
p1 = mystrchr( wrkcmdline, COLON );
|
|
}
|
|
p1++; // move to terminator if hanging ":" or "\"
|
|
} else {
|
|
p1 = wrkcmdline;
|
|
}
|
|
|
|
//
|
|
// p1 is guaranteed to be non-zero
|
|
//
|
|
if ( !(*p1) || !_tcscmp( p1, TEXT(".") ) || !_tcscmp( p1, TEXT("..") ) ) {
|
|
//
|
|
// If CMD.EXE extensions enable, see if name matches
|
|
// subdirectory name.
|
|
//
|
|
if (fEnableExtensions) {
|
|
DWORD dwFileAttributes;
|
|
|
|
dwFileAttributes = GetFileAttributes( loc );
|
|
if (dwFileAttributes != 0xFFFFFFFF &&
|
|
(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
|
|
) {
|
|
return(SFE_ISDIR);
|
|
}
|
|
}
|
|
|
|
DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
|
|
return(SFE_NOTFND) ;
|
|
}
|
|
|
|
if (!(tmps01 = mkstr(2*sizeof(TCHAR)*MAX_PATH))) {
|
|
DosErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
return(SFE_FAIL) ;
|
|
}
|
|
|
|
//
|
|
// Handle the case of file..ext on a fat drive
|
|
//
|
|
mystrcpy( loc, wrkcmdline );
|
|
loc[ &p1[0] - &wrkcmdline[0] ] = 0;
|
|
mystrcat( loc, TEXT(".") );
|
|
|
|
//
|
|
// Check for a malformed name
|
|
//
|
|
if (FullPath(tmps01, loc,MAX_PATH*2)) {
|
|
//
|
|
// If CMD.EXE extensions enable, see if name matches
|
|
// subdirectory name.
|
|
//
|
|
if (fEnableExtensions) {
|
|
DWORD dwFileAttributes;
|
|
|
|
dwFileAttributes = GetFileAttributes( loc );
|
|
if (dwFileAttributes != 0xFFFFFFFF &&
|
|
(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0
|
|
) {
|
|
return(SFE_ISDIR);
|
|
}
|
|
}
|
|
DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
|
|
return(SFE_NOTFND);
|
|
}
|
|
|
|
if ( *lastc(tmps01) != PathChar ) {
|
|
mystrcat( tmps01, TEXT("\\") );
|
|
}
|
|
|
|
//
|
|
// tmps01 contains full path + file name
|
|
//
|
|
mystrcat( tmps01, p1 );
|
|
|
|
tmps01 = resize(tmps01, (mystrlen(tmps01)+1)*sizeof(TCHAR)) ;
|
|
if (tmps01 == NULL) {
|
|
DosErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
return( SFE_FAIL ) ;
|
|
}
|
|
|
|
//
|
|
// fname will point to last '\'
|
|
// tmps01 is to be path and fname is to be name
|
|
//
|
|
fname = mystrrchr(tmps01,PathChar) ;
|
|
*fname++ = NULLC ;
|
|
|
|
DEBUG((DBENV, DBENVSCAN, "SearchForExecutable: Command:%ws",fname));
|
|
|
|
//
|
|
// If only fname type in get path string
|
|
//
|
|
if (!pcloc) {
|
|
tmps02 = GetEnvVar(PathStr) ;
|
|
}
|
|
|
|
DEBUG((DBENV, DBENVSCAN, "SearchForExecutable: PATH:%ws",tmps02));
|
|
|
|
//
|
|
// tmps02 is PATH environment variable
|
|
// compute enough for PATH environment plus file default path
|
|
//
|
|
tokpath = mkstr( (2 + mystrlen(tmps01) + mystrlen(tmps02) + 2)*sizeof(TCHAR)) ;
|
|
if ( ! tokpath ) {
|
|
DosErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
return( SFE_FAIL ) ;
|
|
}
|
|
|
|
//
|
|
// Copy default path
|
|
//
|
|
mystrcat(tokpath,TEXT("\""));
|
|
mystrcat(tokpath,tmps01) ;
|
|
mystrcat(tokpath,TEXT("\""));
|
|
|
|
//
|
|
// If only name type in get also delim and path string
|
|
//
|
|
if (!pcloc) {
|
|
mystrcat(tokpath, TEXT(";")) ;
|
|
mystrcat(tokpath,tmps02) ;
|
|
}
|
|
|
|
//
|
|
// Shift left string at ';;'
|
|
//
|
|
tokshrink(tokpath);
|
|
tokpath = TokStr(tokpath, TEXT(";"), TS_WSPACE) ;
|
|
cName = mystrlen(fname) + 1 ; // file spec. length
|
|
|
|
//
|
|
// Build up the list of extensions we are going to search
|
|
// for. If extensions are enabled, get the list from the PATHEXT
|
|
// variable, otherwise use the hard coded default.
|
|
//
|
|
extPath = NULL;
|
|
if (fEnableExtensions)
|
|
extPath = GetEnvVar(PathExtStr);
|
|
|
|
if (extPath == NULL)
|
|
extPath = PathExtDefaultStr;
|
|
|
|
tokshrink(extPath);
|
|
extPath = TokStr(extPath, TEXT(";"), TS_WSPACE) ;
|
|
|
|
//
|
|
// Everything is now set up. Var tokpath contains a sequential series
|
|
// of asciz strings terminated by an extra null. If the user specified
|
|
// a drive or partial path, it contains only that one converted to full
|
|
// root-based form. If the user typed only a filename (pcloc = 0) it
|
|
// begins with the current directory and contains each directory that
|
|
// was contained in the PATH variable. This loop will search each of
|
|
// the tokpath elements once for each possible executable extention.
|
|
// Note that 'i' is used as a constant to test for excessive string
|
|
// length prior to performing the string copies.
|
|
//
|
|
for ( ; ; ) {
|
|
|
|
//
|
|
// Length of current path
|
|
//
|
|
tplen = mystrlen(tokpath) ;
|
|
mystrcpy( tokpath, StripQuotes( tokpath ) );
|
|
tokpathlen = mystrlen(tokpath);
|
|
|
|
if (*lastc(tokpath) != PathChar) {
|
|
addpchar = TRUE;
|
|
tplen++;
|
|
tokpathlen++;
|
|
} else {
|
|
addpchar = FALSE;
|
|
}
|
|
/* path + name too long */
|
|
//
|
|
// Check if path + name is too long
|
|
//
|
|
if (*tokpath && (tokpathlen + cName) > MAX_PATH) {
|
|
tokpath += addpchar ? tplen : tplen+1; // get next path
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If no more paths to search return descriptive error
|
|
//
|
|
if (*(tokpath) == NULLC) {
|
|
if (pcloc) {
|
|
if (DosErr == 0 || DosErr == ERROR_FILE_NOT_FOUND)
|
|
DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
|
|
} else { /* return generic message */
|
|
DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
|
|
}
|
|
return(SFE_NOTFND) ;
|
|
}
|
|
|
|
//
|
|
// Install this path and setup for next one
|
|
//
|
|
mystrcpy(loc, tokpath) ;
|
|
tokpath += addpchar ? tplen : tplen+1;
|
|
|
|
|
|
if (addpchar) {
|
|
mystrcat(loc, pcstr) ;
|
|
}
|
|
|
|
mystrcat(loc, fname) ;
|
|
mystrcpy(loc, StripQuotes(loc) );
|
|
dotloc = mystrlen(loc) ;
|
|
|
|
DEBUG((DBENV, DBENVSCAN, "SearchForExecutable: PATH:%ws",loc));
|
|
|
|
//
|
|
// Check drive in each path to insure it is valid before searching
|
|
//
|
|
if (*(loc+1) == COLON) {
|
|
if (!IsValidDrv(*loc))
|
|
continue ;
|
|
};
|
|
|
|
//
|
|
// If fname has ext & ext > "." look for given filename
|
|
// this says that all executable files must have an extension
|
|
//
|
|
j = mystrrchr( fname, DOT );
|
|
if ( j && j[1] ) {
|
|
//
|
|
// If access was denied and the user included a path,
|
|
// then say we found it. This handles the case where
|
|
// we don't have permission to do the findfirst and so we
|
|
// can't see the binary, but it actually exists -- if we
|
|
// have execute permission, CreateProcess will work
|
|
// just fine.
|
|
//
|
|
if (exists_ex(loc,TRUE) || (pcloc && (DosErr == ERROR_ACCESS_DENIED))) {
|
|
//
|
|
// Recompute j as exists_ex trims trailing spaces
|
|
//
|
|
j = mystrrchr( loc, DOT );
|
|
if (j != NULL) {
|
|
if ( !_tcsicmp(j,CmdExt) ) {
|
|
return(SFE_ISBAT) ;
|
|
} else if ( !_tcsicmp(j,BatExt) ) {
|
|
return(SFE_ISBAT) ;
|
|
} else {
|
|
return(SFE_ISEXECOM) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((DosErr != ERROR_FILE_NOT_FOUND) && DosErr)
|
|
continue; // Try next path
|
|
}
|
|
if (mystrchr( fname, STAR ) || mystrchr( fname, QMARK ) ) {
|
|
DosErr = MSG_DIR_BAD_COMMAND_OR_FILE;
|
|
return(SFE_NOTFND);
|
|
}
|
|
|
|
//
|
|
// Search for each type of extension
|
|
//
|
|
|
|
extPathWrk = extPath;
|
|
if (DoFind(loc, dotloc, TEXT(".*"), FALSE)) // Found anything?
|
|
while (*extPathWrk) {
|
|
//
|
|
// if name + path + ext is less then max path length
|
|
//
|
|
if ( (cName + tokpathlen + mystrlen(extPathWrk)) <= MAX_PATH) {
|
|
//
|
|
// See if this extension is a match.
|
|
//
|
|
|
|
if (DoFind(loc, dotloc, extPathWrk, TRUE)) {
|
|
if (!_tcsicmp(extPathWrk, BatExt) || !_tcsicmp(extPathWrk, CmdExt))
|
|
return(SFE_ISBAT) ; // found .bat or .cmd
|
|
else
|
|
return(SFE_ISEXECOM) ; // found executable
|
|
|
|
} else {
|
|
//
|
|
// Any kind of error other than file not found, bail from
|
|
// search and try next element in path
|
|
//
|
|
if ((DosErr != ERROR_FILE_NOT_FOUND) && DosErr)
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not this extension, try next.
|
|
|
|
while (*extPathWrk++)
|
|
;
|
|
}
|
|
|
|
//
|
|
// If we get here, then no match with list of extensions.
|
|
// If no wierd errors, deal with NUll extension case.
|
|
//
|
|
|
|
if (DosErr == NO_ERROR || DosErr == ERROR_FILE_NOT_FOUND) {
|
|
if (DoFind(loc, dotloc, TEXT("\0"), TRUE)) {
|
|
if (GetBinaryType(loc,&BinaryType) &&
|
|
BinaryType == SCS_POSIX_BINARY) { // Found .
|
|
return(SFE_ISEXECOM) ;
|
|
}
|
|
}
|
|
}
|
|
} // end for
|
|
|
|
return(SFE_NOTFND);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** DoFind - does indiviual findfirsts during searching
|
|
*
|
|
* Purpose:
|
|
* Add the extension to loc and do the find first for
|
|
* SearchForExecutable().
|
|
*
|
|
* DoFind(TCHAR *loc, int dotloc, TCHAR *ext)
|
|
*
|
|
* Args:
|
|
* loc - the string in which the location of the command is to be placed
|
|
* dotloc - the location loc at which the extension is to be appended
|
|
* ext - the extension to append to loc
|
|
*
|
|
* Returns:
|
|
* 1 if the file is found.
|
|
* 0 if the file isn't found.
|
|
*
|
|
*/
|
|
|
|
int DoFind(loc, dotloc, ext, metas)
|
|
TCHAR *loc ;
|
|
int dotloc ;
|
|
TCHAR *ext ;
|
|
BOOL metas;
|
|
{
|
|
*(loc+dotloc) = NULLC ;
|
|
mystrcat(loc, ext) ;
|
|
|
|
DEBUG((DBENV, DBENVSCAN, "DoFind: exists_ex(%ws)",loc));
|
|
|
|
return(exists_ex(loc,metas)) ; /*@@4*/
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** ExecError - handles exec errors
|
|
*
|
|
* Purpose:
|
|
* Print the exec error message corresponding to the error number in the
|
|
* global variable DosErr.
|
|
* @@ lots of error codes added.
|
|
* ExecError()
|
|
*
|
|
*/
|
|
|
|
void ExecError( onb )
|
|
TCHAR *onb;
|
|
{
|
|
unsigned int errmsg;
|
|
unsigned int count;
|
|
|
|
count = ONEARG;
|
|
|
|
switch (DosErr) {
|
|
|
|
case ERROR_BAD_DEVICE:
|
|
errmsg = MSG_DIR_BAD_COMMAND_OR_FILE;
|
|
count = NOARGS;
|
|
break;
|
|
|
|
case ERROR_LOCK_VIOLATION:
|
|
errmsg = ERROR_SHARING_VIOLATION;
|
|
break ;
|
|
|
|
case ERROR_NO_PROC_SLOTS:
|
|
errmsg = ERROR_NO_PROC_SLOTS;
|
|
count = NOARGS;
|
|
break ;
|
|
|
|
case ERROR_NOT_DOS_DISK:
|
|
errmsg = ERROR_NOT_DOS_DISK;
|
|
break ;
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
errmsg = ERROR_NOT_ENOUGH_MEMORY;
|
|
count = NOARGS;
|
|
break ;
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
errmsg = MSG_CMD_FILE_NOT_FOUND;
|
|
break ;
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
errmsg = MSG_CMD_FILE_NOT_FOUND;
|
|
break ;
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
errmsg = ERROR_ACCESS_DENIED;
|
|
break ;
|
|
|
|
case ERROR_EXE_MACHINE_TYPE_MISMATCH:
|
|
errmsg = ERROR_EXE_MACHINE_TYPE_MISMATCH;
|
|
break;
|
|
|
|
case ERROR_DRIVE_LOCKED:
|
|
errmsg = ERROR_DRIVE_LOCKED;
|
|
break ;
|
|
|
|
case ERROR_INVALID_STARTING_CODESEG:
|
|
errmsg = ERROR_INVALID_STARTING_CODESEG;
|
|
break ;
|
|
|
|
case ERROR_INVALID_STACKSEG:
|
|
errmsg = ERROR_INVALID_STACKSEG;
|
|
break ;
|
|
|
|
case ERROR_INVALID_MODULETYPE:
|
|
errmsg = ERROR_INVALID_MODULETYPE;
|
|
break ;
|
|
|
|
case ERROR_INVALID_EXE_SIGNATURE:
|
|
errmsg = ERROR_INVALID_EXE_SIGNATURE;
|
|
break ;
|
|
|
|
case ERROR_EXE_MARKED_INVALID:
|
|
errmsg = ERROR_EXE_MARKED_INVALID;
|
|
break ;
|
|
|
|
case ERROR_BAD_EXE_FORMAT:
|
|
errmsg = ERROR_BAD_EXE_FORMAT;
|
|
break ;
|
|
|
|
case ERROR_INVALID_MINALLOCSIZE:
|
|
errmsg = ERROR_INVALID_MINALLOCSIZE;
|
|
break ;
|
|
|
|
case ERROR_SHARING_VIOLATION:
|
|
errmsg = ERROR_SHARING_VIOLATION;
|
|
break ;
|
|
|
|
case ERROR_BAD_ENVIRONMENT:
|
|
errmsg = ERROR_INFLOOP_IN_RELOC_CHAIN;
|
|
count = NOARGS;
|
|
break ;
|
|
|
|
case ERROR_INVALID_ORDINAL:
|
|
errmsg = ERROR_INVALID_ORDINAL;
|
|
break ;
|
|
|
|
case ERROR_CHILD_NOT_COMPLETE:
|
|
errmsg = ERROR_CHILD_NOT_COMPLETE;
|
|
break ;
|
|
|
|
case ERROR_DIRECTORY:
|
|
errmsg = MSG_BAD_CURDIR;
|
|
count = NOARGS;
|
|
break;
|
|
|
|
case ERROR_NOT_ENOUGH_QUOTA:
|
|
errmsg = ERROR_NOT_ENOUGH_QUOTA;
|
|
count = NOARGS;
|
|
break;
|
|
|
|
|
|
case MSG_REAL_MODE_ONLY:
|
|
errmsg = MSG_REAL_MODE_ONLY;
|
|
count = NOARGS;
|
|
break ;
|
|
|
|
default:
|
|
// printf( "Exec failed code %x\n", DosErr );
|
|
count = NOARGS;
|
|
errmsg = MSG_EXEC_FAILURE ; /* M031 */
|
|
|
|
}
|
|
|
|
|
|
LastRetCode = errmsg;
|
|
PutStdErr(errmsg, count, onb );
|
|
}
|
|
|
|
/*
|
|
* tokshrink @@4
|
|
*
|
|
* remove duplicate ';' in a path
|
|
*/
|
|
|
|
void tokshrink( tokpath )
|
|
TCHAR *tokpath;
|
|
{
|
|
int i, j;
|
|
|
|
i = 0;
|
|
do {
|
|
if ( tokpath[i] == QUOTE ) {
|
|
do {
|
|
i++;
|
|
} while ( tokpath[i] && tokpath[i] != QUOTE );
|
|
}
|
|
if ( tokpath[i] && tokpath[i] != TEXT(';') ) {
|
|
i++;
|
|
}
|
|
if ( tokpath[i] == TEXT(';') ) {
|
|
j = i;
|
|
while ( tokpath[j+1] == TEXT(';') ) {
|
|
j++;
|
|
}
|
|
if ( j > i ) {
|
|
mystrcpy( &tokpath[i], &tokpath[j] );
|
|
}
|
|
i++;
|
|
}
|
|
} while ( tokpath[i] );
|
|
}
|
|
|
|
|
|
|
|
/*** eAssoc - execute an Assoc command
|
|
*
|
|
* Purpose:
|
|
* To set/modify the file associations stored in the registry under the
|
|
* HKEY_LOCAL_MACHINE\Software\Classes key
|
|
*
|
|
* int eAssoc(struct cmdnode *n)
|
|
*
|
|
* Args:
|
|
* n - the parse tree node containing the set command
|
|
*
|
|
* Returns:
|
|
* If setting and the command is syntactically correct, whatever SetAssoc()
|
|
* returns. Otherwise, FAILURE.
|
|
*
|
|
* If displaying, SUCCESS is always returned.
|
|
*
|
|
*/
|
|
|
|
int eAssoc(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
if (glBatType != CMD_TYPE) {
|
|
// if set command is executed from .bat file OR entered at command prompt
|
|
return( SetLastRetCodeIfError(AssocWork( n )));
|
|
}
|
|
else {
|
|
return( LastRetCode = AssocWork( n ) );
|
|
}
|
|
}
|
|
|
|
int AssocWork(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
HKEY hKeyClasses;
|
|
TCHAR *tas ; /* Tokenized argument string */
|
|
TCHAR *wptr ; /* Work pointer */
|
|
int i ; /* Work variable */
|
|
int rc ;
|
|
|
|
|
|
rc = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("Software\\Classes"), &hKeyClasses);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
|
|
tas = TokStr(n->argptr, ONEQSTR, TS_WSPACE|TS_SDTOKENS) ;
|
|
if (!*tas)
|
|
rc = DisplayAssoc(hKeyClasses, NULL) ;
|
|
|
|
else {
|
|
for (wptr = tas, i = 0 ; *wptr ; wptr += mystrlen(wptr)+1, i++)
|
|
;
|
|
/* If too many parameters were given, the second parameter */
|
|
/* wasn't an equal sign, or they didn't specify a string */
|
|
/* return an error message. */
|
|
if ( i > 3 || *(wptr = tas+mystrlen(tas)+1) != EQ ||
|
|
!mystrlen(mystrcpy(tas, StripQuotes(tas))) ) {
|
|
if (i==1) {
|
|
rc =DisplayAssoc(hKeyClasses, tas);
|
|
} else {
|
|
PutStdErr(MSG_BAD_SYNTAX, NOARGS);
|
|
rc = FAILURE ;
|
|
}
|
|
} else {
|
|
rc = SetAssoc(hKeyClasses, tas, wptr+2) ;
|
|
}
|
|
} ;
|
|
|
|
RegCloseKey(hKeyClasses) ;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** DisplayAssoc - display a specific file association or all
|
|
*
|
|
* Purpose:
|
|
* To display a specific file association or all
|
|
*
|
|
* int DisplayAssoc( hKeyClasses, tas )
|
|
*
|
|
* Returns:
|
|
* SUCCESS if all goes well
|
|
* FAILURE if it runs out of memory or cannot lock the env. segment
|
|
*/
|
|
|
|
int DisplayAssoc(hKeyClasses, tas)
|
|
HKEY hKeyClasses;
|
|
TCHAR *tas;
|
|
{
|
|
int i;
|
|
int rc = SUCCESS;
|
|
TCHAR NameBuffer[MAX_PATH];
|
|
TCHAR ValueBuffer[MAX_PATH];
|
|
TCHAR *vstr ;
|
|
DWORD cb;
|
|
|
|
if (tas == NULL) {
|
|
for (i=0 ; rc == SUCCESS ; i++) {
|
|
rc =RegEnumKey(hKeyClasses, i, NameBuffer, sizeof( NameBuffer ) / sizeof( TCHAR ));
|
|
if (rc != 0) {
|
|
if (rc==ERROR_NO_MORE_ITEMS)
|
|
rc = SUCCESS;
|
|
break;
|
|
} else
|
|
if (NameBuffer[0] == DOT) {
|
|
cb = sizeof(ValueBuffer);
|
|
rc = RegQueryValue(hKeyClasses, NameBuffer, ValueBuffer, &cb);
|
|
if (rc != 0) {
|
|
break;
|
|
}
|
|
|
|
rc = cmd_printf(Fmt16, NameBuffer, ValueBuffer);
|
|
}
|
|
|
|
if (CtrlCSeen) {
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
tas = EatWS(tas, NULL);
|
|
if ((vstr = mystrrchr(tas, ' ')) != NULL)
|
|
*vstr = NULLC;
|
|
|
|
cb = sizeof(ValueBuffer);
|
|
rc = RegQueryValue(hKeyClasses, tas, ValueBuffer, &cb);
|
|
if (rc == 0)
|
|
rc = cmd_printf(Fmt16, tas, ValueBuffer);
|
|
else
|
|
PutStdErr(MSG_ASSOC_NOT_FOUND, ONEARG, tas);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
/*** SetAssoc - controls adding/changing a file association
|
|
*
|
|
* Purpose:
|
|
* Add/replace a file association
|
|
*
|
|
* int SetAssoc(HKEY hKeyClasses, TCHAR *fileext, TCHAR *filetype)
|
|
*
|
|
* Args:
|
|
* hKeyClasses - handle to HKEY_LOCAL_MACHINE\Software\Classes key
|
|
* fileext - file extension string to associate
|
|
* filetype - file type associate
|
|
*
|
|
* Returns:
|
|
* SUCCESS if the association could be added/replaced.
|
|
* FAILURE otherwise.
|
|
*
|
|
*/
|
|
|
|
int SetAssoc(hKeyClasses, fileext, filetype)
|
|
HKEY hKeyClasses;
|
|
TCHAR *fileext ;
|
|
TCHAR *filetype ;
|
|
{
|
|
int rc;
|
|
int i;
|
|
DWORD cb;
|
|
|
|
if (filetype==NULL || *filetype==NULLC) {
|
|
rc = RegDeleteKey(hKeyClasses, fileext);
|
|
if (rc != 0)
|
|
PutStdErr(MSG_ASSOC_NOT_FOUND, ONEARG, fileext);
|
|
}
|
|
else {
|
|
rc = RegSetValue(hKeyClasses, fileext, REG_SZ, filetype, _tcslen(filetype));
|
|
if (rc == 0) {
|
|
try {
|
|
SHChangeNotify( SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL );
|
|
cmd_printf( Fmt16, fileext, filetype );
|
|
} except (DosErr = GetExceptionCode( ), EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
|
|
}
|
|
else
|
|
PutStdErr(MSG_ERR_PROC_ARG, ONEARG, fileext);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*** eFType - execute an FType command
|
|
*
|
|
* Purpose:
|
|
* To set/modify the file types stored in the registry under the
|
|
* HKEY_LOCAL_MACHINE\Software\Classes key
|
|
*
|
|
* int eFType(struct cmdnode *n)
|
|
*
|
|
* Args:
|
|
* n - the parse tree node containing the set command
|
|
*
|
|
* Returns:
|
|
* If setting and the command is syntactically correct, whatever SetFType()
|
|
* returns. Otherwise, FAILURE.
|
|
*
|
|
* If displaying, SUCCESS is always returned.
|
|
*
|
|
*/
|
|
|
|
int eFType(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
if (glBatType != CMD_TYPE) {
|
|
// if set command is executed from .bat file OR entered at command prompt
|
|
return( SetLastRetCodeIfError(FTypeWork( n )));
|
|
}
|
|
else {
|
|
return( LastRetCode = FTypeWork( n ) );
|
|
}
|
|
}
|
|
|
|
int FTypeWork(n)
|
|
struct cmdnode *n ;
|
|
{
|
|
HKEY hKeyClasses;
|
|
TCHAR *tas ; /* Tokenized argument string */
|
|
TCHAR *wptr ; /* Work pointer */
|
|
int i ; /* Work variable */
|
|
int rc ;
|
|
|
|
|
|
rc = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("Software\\Classes"), &hKeyClasses);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
|
|
tas = TokStr(n->argptr, ONEQSTR, TS_WSPACE|TS_SDTOKENS) ;
|
|
if (!*tas)
|
|
rc = DisplayFType(hKeyClasses, NULL) ;
|
|
|
|
else {
|
|
for (wptr = tas, i = 0 ; *wptr ; wptr += mystrlen(wptr)+1, i++)
|
|
;
|
|
/* If too many parameters were given, the second parameter */
|
|
/* wasn't an equal sign, or they didn't specify a string */
|
|
/* return an error message. */
|
|
if ( i > 3 || *(wptr = tas+mystrlen(tas)+1) != EQ ||
|
|
!mystrlen(mystrcpy(tas, StripQuotes(tas))) ) {
|
|
if (i==1) {
|
|
rc =DisplayFType(hKeyClasses, tas);
|
|
} else {
|
|
PutStdErr(MSG_BAD_SYNTAX, NOARGS);
|
|
rc = FAILURE ;
|
|
}
|
|
} else {
|
|
rc = SetFType(hKeyClasses, tas, wptr+2) ;
|
|
}
|
|
} ;
|
|
|
|
RegCloseKey(hKeyClasses) ;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*** DisplayFType - display a specific file type or all
|
|
*
|
|
* Purpose:
|
|
* To display a specific file type or all
|
|
*
|
|
* int DisplayFType( hKeyClasses, tas )
|
|
*
|
|
* Returns:
|
|
* SUCCESS if all goes well
|
|
* FAILURE if it runs out of memory or cannot lock the env. segment
|
|
*/
|
|
|
|
int DisplayFType(hKeyClasses, tas)
|
|
HKEY hKeyClasses;
|
|
TCHAR *tas;
|
|
{
|
|
int i;
|
|
int rc;
|
|
HKEY hKeyOpenCmd;
|
|
TCHAR NameBuffer[MAX_PATH];
|
|
TCHAR ValueBuffer[MAX_PATH];
|
|
TCHAR *vstr ;
|
|
DWORD cb, j, Type;
|
|
|
|
if (tas == NULL) {
|
|
for (i=0;;i++) {
|
|
rc =RegEnumKey(hKeyClasses, i, NameBuffer, sizeof( NameBuffer ) / sizeof( TCHAR ));
|
|
if (rc != 0) {
|
|
if (rc==ERROR_NO_MORE_ITEMS)
|
|
rc = SUCCESS;
|
|
break;
|
|
} else
|
|
if (NameBuffer[0] != DOT) {
|
|
j = _tcslen(NameBuffer);
|
|
_tcscat(NameBuffer, TEXT("\\Shell\\Open\\Command"));
|
|
_tcscpy(ValueBuffer,TEXT("*** no open command defined ***"));
|
|
rc = RegOpenKey(hKeyClasses, NameBuffer, &hKeyOpenCmd);
|
|
if (!rc) {
|
|
NameBuffer[j] = TEXT('\0');
|
|
cb = sizeof(ValueBuffer);
|
|
rc = RegQueryValueEx(hKeyOpenCmd, TEXT(""), NULL, &Type, (LPBYTE)ValueBuffer, &cb);
|
|
RegCloseKey(hKeyOpenCmd);
|
|
}
|
|
|
|
if (!rc) {
|
|
cmd_printf(Fmt16, NameBuffer, ValueBuffer);
|
|
}
|
|
}
|
|
if (CtrlCSeen) {
|
|
return(FAILURE);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (*tas == DOT) {
|
|
PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, tas);
|
|
return ERROR_INVALID_NAME;
|
|
}
|
|
|
|
tas = EatWS(tas, NULL);
|
|
if ((vstr = mystrrchr(tas, ' ')) != NULL)
|
|
*vstr = NULLC;
|
|
|
|
_sntprintf(NameBuffer, MAX_PATH, TEXT("%s\\Shell\\Open\\Command"), tas);
|
|
rc = RegOpenKey(hKeyClasses, NameBuffer, &hKeyOpenCmd);
|
|
if (rc) {
|
|
PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, tas);
|
|
return rc;
|
|
}
|
|
|
|
cb = sizeof(ValueBuffer);
|
|
rc = RegQueryValueEx(hKeyOpenCmd, TEXT(""), NULL, &Type, (LPBYTE)ValueBuffer, &cb);
|
|
if (rc == 0) {
|
|
ValueBuffer[ (cb / sizeof( TCHAR )) - 1 ];
|
|
cmd_printf(Fmt16, tas, ValueBuffer);
|
|
}
|
|
else
|
|
PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, tas);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
/*** SetFType - controls adding/changing the open command associated with a file type
|
|
*
|
|
* Purpose:
|
|
* Add/replace an open command string associated with a file type
|
|
*
|
|
* int SetFType(HKEY hKeyOpenCmd, TCHAR *filetype TCHAR *opencmd)
|
|
*
|
|
* Args:
|
|
* hKeyClasses - handle to HKEY_LOCAL_MACHINE\Software\Classes
|
|
* filetype - file type name
|
|
* opencmd - open command string
|
|
*
|
|
* Returns:
|
|
* SUCCESS if the file type could be added/replaced.
|
|
* FAILURE otherwise.
|
|
*
|
|
*/
|
|
|
|
int SetFType(hKeyClasses, filetype, opencmd)
|
|
HKEY hKeyClasses;
|
|
TCHAR *filetype ;
|
|
TCHAR *opencmd ;
|
|
{
|
|
HKEY hKeyOpenCmd;
|
|
TCHAR NameBuffer[MAX_PATH];
|
|
TCHAR c, *s;
|
|
DWORD Disposition;
|
|
int rc;
|
|
int i;
|
|
DWORD cb;
|
|
|
|
_sntprintf(NameBuffer, MAX_PATH, TEXT("%s\\Shell\\Open\\Command"), filetype);
|
|
rc = RegOpenKey(hKeyClasses, NameBuffer, &hKeyOpenCmd);
|
|
if (rc) {
|
|
if (opencmd==NULL || *opencmd==NULLC) {
|
|
PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, filetype);
|
|
return rc;
|
|
}
|
|
|
|
s = NameBuffer;
|
|
while (TRUE) {
|
|
while (*s && *s != TEXT('\\')) {
|
|
s += 1;
|
|
}
|
|
c = *s;
|
|
*s = TEXT('\0');
|
|
rc = RegCreateKeyEx(hKeyClasses,
|
|
NameBuffer,
|
|
0,
|
|
NULL,
|
|
0,
|
|
(REGSAM)MAXIMUM_ALLOWED,
|
|
NULL,
|
|
&hKeyOpenCmd,
|
|
&Disposition
|
|
);
|
|
if (rc) {
|
|
PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, filetype);
|
|
return rc;
|
|
}
|
|
|
|
if (c == TEXT('\0')) {
|
|
break;
|
|
}
|
|
|
|
*s++ = c;
|
|
RegCloseKey(hKeyOpenCmd);
|
|
}
|
|
}
|
|
|
|
if (opencmd==NULL || *opencmd==NULLC) {
|
|
rc = RegDeleteKey(hKeyOpenCmd, NULL);
|
|
if (rc != 0)
|
|
PutStdErr(MSG_FTYPE_NOT_FOUND, ONEARG, filetype);
|
|
}
|
|
else {
|
|
rc = RegSetValueEx(hKeyOpenCmd, TEXT(""), 0, REG_EXPAND_SZ, (LPBYTE)opencmd, (_tcslen(opencmd)+1)*sizeof(TCHAR));
|
|
if (rc == 0)
|
|
cmd_printf(Fmt16, filetype, opencmd);
|
|
else
|
|
PutStdErr(MSG_ERR_PROC_ARG, ONEARG, filetype);
|
|
}
|
|
|
|
RegCloseKey(hKeyOpenCmd);
|
|
return rc;
|
|
}
|
|
|
|
|
|
typedef
|
|
NTSTATUS
|
|
(NTAPI *PNTQUERYINFORMATIONPROCESS)(
|
|
IN HANDLE ProcessHandle,
|
|
IN PROCESSINFOCLASS ProcessInformationClass,
|
|
OUT PVOID ProcessInformation,
|
|
IN ULONG ProcessInformationLength,
|
|
OUT PULONG ReturnLength OPTIONAL
|
|
);
|
|
|
|
HMODULE hNtDllModule;
|
|
PNTQUERYINFORMATIONPROCESS lpNtQueryInformationProcess;
|
|
|
|
WORD
|
|
GetProcessSubsystemType(
|
|
HANDLE hProcess
|
|
)
|
|
{
|
|
PIMAGE_NT_HEADERS NtHeader;
|
|
PPEB PebAddress;
|
|
PEB Peb;
|
|
SIZE_T SizeOfPeb;
|
|
NTSTATUS Status;
|
|
PROCESS_BASIC_INFORMATION ProcessInfo;
|
|
BOOL b;
|
|
PVOID ImageBaseAddress;
|
|
LONG e_lfanew;
|
|
WORD Subsystem;
|
|
|
|
Subsystem = IMAGE_SUBSYSTEM_UNKNOWN;
|
|
if (hNtDllModule == NULL) {
|
|
hNtDllModule = LoadLibrary( TEXT("NTDLL.DLL") );
|
|
if (hNtDllModule != NULL) {
|
|
lpNtQueryInformationProcess = (PNTQUERYINFORMATIONPROCESS)
|
|
GetProcAddress( hNtDllModule,
|
|
"NtQueryInformationProcess"
|
|
);
|
|
}
|
|
else {
|
|
hNtDllModule = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
if (lpNtQueryInformationProcess != NULL) {
|
|
//
|
|
// Get the Peb address
|
|
//
|
|
|
|
Status = (*lpNtQueryInformationProcess)( hProcess,
|
|
ProcessBasicInformation,
|
|
&ProcessInfo,
|
|
sizeof( ProcessInfo ),
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
PebAddress = ProcessInfo.PebBaseAddress;
|
|
|
|
//
|
|
// Read the subsystem type from the Peb
|
|
//
|
|
|
|
if (ReadProcessMemory( hProcess,
|
|
PebAddress,
|
|
&Peb,
|
|
sizeof( Peb ),
|
|
&SizeOfPeb
|
|
)
|
|
) {
|
|
//
|
|
// See if we are running on a system that has the image subsystem
|
|
// type captured in the PEB. If so use it. Otherwise go the slow
|
|
// way and try to get it from the image header.
|
|
//
|
|
if (SizeOfPeb >= FIELD_OFFSET( PEB, ImageSubsystem ) &&
|
|
((UINT_PTR)Peb.ProcessHeaps - (UINT_PTR)PebAddress) > FIELD_OFFSET( PEB, ImageSubsystem )
|
|
) {
|
|
Subsystem = (WORD)Peb.ImageSubsystem;
|
|
}
|
|
else {
|
|
//
|
|
// read e_lfanew from imageheader
|
|
//
|
|
|
|
if (ReadProcessMemory( hProcess,
|
|
&((PIMAGE_DOS_HEADER)Peb.ImageBaseAddress)->e_lfanew,
|
|
&e_lfanew,
|
|
sizeof( e_lfanew ),
|
|
NULL
|
|
)
|
|
) {
|
|
//
|
|
// Read subsystem version info
|
|
//
|
|
|
|
NtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)Peb.ImageBaseAddress + e_lfanew);
|
|
if (ReadProcessMemory( hProcess,
|
|
&NtHeader->OptionalHeader.Subsystem,
|
|
&Subsystem,
|
|
sizeof( Subsystem ),
|
|
NULL
|
|
)
|
|
) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Subsystem;
|
|
}
|