Esempio n. 1
0
File: ntfunc.c Progetto: phase/tcsh
int nt_texec(char *prog, char**args ) {

	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	HANDLE htemp;
	DWORD type=0;
	DWORD dwCreationflags;
	unsigned int priority;
	char *argv0 = NULL, *savepath = NULL;
	char *cmdstr,*cmdend ;
	char *originalPtr = NULL;
	unsigned int cmdsize,cmdlen;
	char *p2;
	char **savedargs;
	int retries=0;
	int hasdot =0;
	int is_winnt=0;
	int retval = 1;

	memset(&si,0,sizeof(si));
	savedargs = args;

	/* MUST FREE !! */
	originalPtr = cmdstr= heap_alloc(MAX_PATH<<2);
	cmdsize = MAX_PATH<<2;

	is_winnt = (gdwPlatform != VER_PLATFORM_WIN32_WINDOWS);


	p2 = cmdstr;

	cmdlen = 0;
	cmdlen += copy_quote_and_fix_slashes(prog,cmdstr,&hasdot);
	p2 += cmdlen;

	if (*cmdstr != '"') {
		// If not quoted, skip initial character we left for quote
		*cmdstr = 'A';
		cmdstr++; 
		cmdsize--;
	}
	*p2 = 0; 
	cmdend = p2;

	if (!is_winnt) {
		argv0 = NULL;
	}
	else {
		argv0= heap_alloc(MAX_PATH);
		(void)StringCbPrintf(argv0,MAX_PATH,"%s",prog);
	}

	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES;
	htemp= (HANDLE)_get_osfhandle(SHIN);
	DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(),
			&si.hStdInput,0,TRUE,DUPLICATE_SAME_ACCESS);
	htemp= (HANDLE)_get_osfhandle(SHOUT);
	DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(),
			&si.hStdOutput,0,TRUE,DUPLICATE_SAME_ACCESS);
	htemp= (HANDLE)_get_osfhandle(SHDIAG);
	DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(),
			&si.hStdError,0,TRUE,DUPLICATE_SAME_ACCESS);


	/* 
	   quotespace hack needed since execv() would have separated args, but
	   createproces doesnt
	   -amol 9/14/96
	 */

	args++; // the first arg is the command

	dprintf("nt_texec calling c_a_a_q");
	if(concat_args_and_quote(args,&originalPtr,&cmdstr,&cmdlen,&cmdend,&cmdsize) == NULL)
	{
		retval = 1;
		errno  = ENOMEM;
		heap_free(originalPtr);
		goto free_mem;
	}

	*cmdend = 0;

	dwCreationflags = GetPriorityClass(GetCurrentProcess());
	priority = GetThreadPriority(GetCurrentThread());

	if (is_winnt) {
		retries = 0;
		// For NT, try ShellExecuteEx first
		do {
			if (GetBinaryType(argv0,&type)) 
				break;
			if (GetLastError() == ERROR_BAD_EXE_FORMAT){
				errno = ENOEXEC;
				if (!__nt_only_start_exes)
					try_shell_ex(savedargs,0,FALSE);
				if (errno) {
					retval = 1; 
					goto free_mem;
				}
				else {
					retval = 0;
					goto free_mem;
				}
			}
			// only try shellex again after appending ".exe fails
			else if ( retries > 1 ){
				if (
						( (argv0[0] == '\\') ||(argv0[0] == '/') ) &&
						( (argv0[1] == '\\') ||(argv0[1] == '/') ) &&
						(!args[1])
				   )
					if (!__nt_only_start_exes)
						try_shell_ex(savedargs,0,FALSE);
				errno  = ENOENT;
			}
			if (retries == 0)
				(void)StringCbPrintf(argv0,MAX_PATH,"%s.exe",prog);
			else if (retries == 1) {
				(void)StringCbPrintf(argv0,MAX_PATH,"%s.EXE",prog);
			}
			retries++;
		}while(retries < 3);
	}
	savepath = fix_path_for_child();
re_cp:
	dprintf("nt_texec cmdstr %s\n",cmdstr);


	if (!CreateProcess(argv0,
				cmdstr,
				NULL,
				NULL,
				TRUE, // need this for redirecting std handles
				dwCreationflags,
				NULL,//envcrap,
				NULL,
				&si,
				&pi) ){

		if (GetLastError() == ERROR_BAD_EXE_FORMAT) {
			errno  = ENOEXEC;
		}
		else if (GetLastError() == ERROR_INVALID_PARAMETER) {
			errno = ENAMETOOLONG;
		}else {
			errno  = ENOENT;
		}
		if (!is_winnt && !hasdot) { //append '.' to the end if needed
			StringCbCat(cmdstr,cmdsize,".");
			hasdot=1;
			goto re_cp;
		}
		retval = 1;
	}
	else{
		int gui_app ;
		DWORD exitcode;
		char guivar[50];


		if (GetEnvironmentVariable("TCSH_NOASYNCGUI",guivar,50))
			gui_app=0;
		else
			gui_app= is_gui(argv0);

		if(!gui_app) {
			WaitForSingleObject(pi.hProcess,INFINITE);
			(void)GetExitCodeProcess(pi.hProcess,&exitcode);
			setv(STRstatus, putn(exitcode), VAR_READWRITE);/*FIXRESET*/
		}
		retval = 0;
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
	}
free_mem:
	CloseHandle(si.hStdInput);
	CloseHandle(si.hStdOutput);
	CloseHandle(si.hStdError);

	if(savepath)
		restore_path(savepath);

	heap_free(originalPtr);
	if (argv0)
		heap_free(argv0);
	return retval;
}
Esempio n. 2
0
File: ntfunc.c Progetto: phase/tcsh
void dostart(Char ** vc, struct command *c) {

	char *cmdstr,*cmdend,*ptr;
	char argv0[256];/*FIXBUF*/
	DWORD cmdsize;
	char *currdir=NULL;
	char *savepath;
	char **v = NULL;
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	DWORD dwCreationFlags=CREATE_NEW_CONSOLE;
	DWORD k,cmdlen,j,jj,ret;


	UNREFERENCED_PARAMETER(c);
	vc++;

	cmdsize = 512;
	cmdstr = heap_alloc(cmdsize);
	cmdend = cmdstr;
	cmdlen = 0;

	memset(&si,0,sizeof(si));
	si.cb = sizeof(si);

	vc = glob_all_or_error(vc);
	v = short2blk(vc);
	if(v == NULL) {
		stderror(ERR_NOMEM);
		return;
	}
	blkfree(vc);
	for (k = 0; v[k] != NULL ; k++){

		if ( v[k][0] == '-' ) {
			/* various options */
			if( (v[k][1] == 'T') || (v[k][1] == 't'))
				si.lpTitle =&( v[k][2]);
			else if ( (v[k][1] == 'D') || (v[k][1] == 'd'))
				currdir =&( v[k][2]);
			else if (!_stricmp(&v[k][1],"MIN") )
				si.wShowWindow = SW_SHOWMINIMIZED;
			else if (!_stricmp(&v[k][1],"MAX") )
				si.wShowWindow = SW_SHOWMAXIMIZED;
			else if (!_stricmp(&v[k][1],"SEPARATE") )
				dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
			else if (!_stricmp(&v[k][1],"SHARED") )
				dwCreationFlags |= CREATE_SHARED_WOW_VDM;
			else if (!_stricmp(&v[k][1],"LOW") )
				dwCreationFlags |= IDLE_PRIORITY_CLASS;
			else if (!_stricmp(&v[k][1],"NORMAL") )
				dwCreationFlags |= NORMAL_PRIORITY_CLASS;
			else if (!_stricmp(&v[k][1],"HIGH") )
				dwCreationFlags |= HIGH_PRIORITY_CLASS;
			else if (!_stricmp(&v[k][1],"REALTIME") )
				dwCreationFlags |= REALTIME_PRIORITY_CLASS;
			else{
				blkfree((Char **)v);
				stderror(ERR_SYSTEM,start_usage,"See CMD.EXE for more info");/*FIXRESET*/
			}
		}
		else{ // non-option arg
			break;
		}
	}
	/* 
	 * Stop the insanity of requiring start "tcsh -l"
	 * Option processing now stops at first non-option arg
	 * -amol 5/30/96
	 */
	for (jj=k;v[jj] != NULL; jj++) {
		j=(lstrlen(v[jj]) + 2);
		if (j + cmdlen > cmdsize) {
			ptr = cmdstr;
			cmdstr = heap_realloc(cmdstr, max(cmdsize << 1, j+cmdlen) );
			if(!cmdstr)
			{
				heap_free(ptr);
				stderror(ERR_NOMEM,"start");/*FIXRESET*/
			}
			cmdend =  cmdstr + (cmdend - ptr);
			cmdsize <<= 1;
		}
		ptr = v[jj];
		while (*ptr) {
			*cmdend++ = *ptr++;
			cmdlen++;
		}
		*cmdend++ = ' ';
		cmdlen++;
	}
	if (jj == k) {
		blkfree((Char **)v);
		stderror(ERR_SYSTEM,start_usage,"See CMD.EXE for more info");/*FIXRESET*/
		return;
	}
	*cmdend = 0;
	StringCbCopy(argv0,sizeof(argv0),v[k]);


	/* 
	 * strictly speaking, it should do no harm to set the path
	 * back to '\'-delimited even in the parent, but in the
	 * interest of consistency, we save the old value and restore it
	 * later
	 */

	savepath = fix_path_for_child();

	if (! CreateProcess(NULL,
				cmdstr,
				NULL,
				NULL,
				FALSE,
				dwCreationFlags,
				NULL,
				currdir,
				&si,
				&pi) ) {

		restore_path(savepath);

		ret = GetLastError();
		if (ret == ERROR_BAD_EXE_FORMAT || ret == ERROR_ACCESS_DENIED ||
				(ret == ERROR_FILE_NOT_FOUND && 
				 (is_url(v[k]) || is_directory(v[k]))
				) 
		   ) {

			char erbuf[MAX_PATH];

			errno = ENOEXEC;

			try_shell_ex(&v[k],0,FALSE);

			heap_free(cmdstr); /* free !! */

			if (errno) {
				strerror_s(erbuf,sizeof(erbuf),errno);
				stderror(ERR_ARCH,argv0,erbuf);/*FIXRESET*/
			}
		}
		else if (ret == ERROR_INVALID_PARAMETER) {

			errno = ENAMETOOLONG;

			heap_free(cmdstr); /* free !! */

			stderror(ERR_TOOLARGE,argv0);/*FIXRESET*/

		}
		else {
			errno = ENOENT;
			if (
					( (v[k][0] == '\\') ||(v[k][0] == '/') ) &&
					( (v[k][1] == '\\') ||(v[k][1] == '/') ) &&
					(!v[k+1])
			   )
				try_shell_ex(&v[k],0,FALSE);

			heap_free(cmdstr); /* free !! */
			if (errno) {
				stderror(ERR_NOTFOUND,argv0);/*FIXRESET*/
			}
		}
	}
	else {
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);

		heap_free(cmdstr);
		restore_path(savepath);
	}
	blkfree((Char **)v);
	return;
}
Esempio n. 3
0
void nt_execve(char *prog, char**args, char**envir ) {

	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	HANDLE htemp;
	BOOL bRet;
    DWORD type=0;
	DWORD dwCreationflags;
	unsigned int priority;
	char *argv0;
	char *cmdstr, *cmdend ;
	char *originalPtr;
	unsigned int cmdsize,cmdlen;
    char *p2;
	char **savedargs;
	int retries=0;
	int hasdot =0;
	int is_winnt ;

	memset(&si,0,sizeof(si));
	savedargs = args;

	/* 
	 * This memory is not freed because we are exec()ed and will
	 * not be alive long.
	 */
	originalPtr = cmdstr= heap_alloc(MAX_PATH<<2);

	is_winnt = (gdwPlatform != VER_PLATFORM_WIN32_WINDOWS);


	cmdsize = MAX_PATH<<2;

	p2 = cmdstr;

	cmdlen = 0;
	cmdlen += copy_quote_and_fix_slashes(prog,cmdstr,&hasdot);

	p2 += cmdlen;

	/* If the command was not quoted ,
	  skip initial character we left for quote */
	if (*cmdstr != '"') {
		*cmdstr = 'A';
		cmdstr++; 
		cmdsize--;
	}
	*p2 = 0;
	cmdend = p2;


	if (!is_winnt){
		argv0 = NULL;
		goto win95_directly_here;
	}
	else {
		argv0 = heap_alloc(MAX_PATH); /* not freed */
		wsprintf(argv0,"%s",prog);
	}

retry:

	bRet=GetBinaryType(argv0,&type);
	dprintf("binary type for %s is %d\n",argv0,bRet);
	//
	// For NT, append .EXE and retry
	//
	if (is_winnt && !bRet ) {
        /* Don't append .EXE if it could be a script file */
		if (GetLastError() == ERROR_BAD_EXE_FORMAT){
			errno = ENOEXEC;
			if (!__nt_only_start_exes)
				try_shell_ex(args,1,FALSE); //can't throw on error
			return;
		}
		else if ( retries ){
			if (
				( (argv0[0] == '\\') ||(argv0[0] == '/') ) &&
				( (argv0[1] == '\\') ||(argv0[1] == '/') ) &&
				(!args[1])
			   )
				if (!__nt_only_start_exes)
					try_shell_ex(args,1,FALSE);
			errno  = ENOENT;
		}
		if (retries > 1){
			return;
		}
		// Try uppercase once and then lower case
		//
		if (!retries)
			wsprintf(argv0,"%s.exe",prog);
		else 
			wsprintf(argv0,"%s.EXE",prog); /* fix for clearcase */
		retries++;
		goto retry;
	}

win95_directly_here:

	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES;
	htemp= (HANDLE)_get_osfhandle(0);
	DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(),
						&si.hStdInput,0,TRUE,DUPLICATE_SAME_ACCESS);
	htemp= (HANDLE)_get_osfhandle(1);
	DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(),
						&si.hStdOutput,0,TRUE,DUPLICATE_SAME_ACCESS);
	htemp= (HANDLE)_get_osfhandle(2);
	DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(),
						&si.hStdError,0,TRUE,DUPLICATE_SAME_ACCESS);



	*args++; // the first arg is the command


	dprintf("nt_execve calling c_a_a_q");
	if(!concat_args_and_quote(args,&originalPtr,&cmdstr,&cmdlen,&cmdend,
				&cmdsize))
	{
        dprintf("concat_args_and_quote failed\n");
		heap_free(originalPtr);
		errno = ENOMEM;
		goto fail_return;
	}

	*cmdend = 0;

	dwCreationflags = GetPriorityClass(GetCurrentProcess());
	if (__nt_child_nohupped) {
		dwCreationflags |= DETACHED_PROCESS;
	}
	priority = GetThreadPriority(GetCurrentThread());

	(void)fix_path_for_child();

	if (is_winnt)
		dwCreationflags |= CREATE_SUSPENDED;


re_cp:
	dprintf("argv0 %s cmdstr %s\n",argv0,cmdstr);
	bRet = CreateProcessA(argv0, cmdstr,
			NULL, NULL,
			TRUE, // need this for redirecting std handles
			dwCreationflags,
			NULL, NULL,
			&si,
			&pi);
	if (!bRet){
		if (GetLastError() == ERROR_BAD_EXE_FORMAT) {
			if (!__nt_only_start_exes)
				try_shell_ex(savedargs,1,FALSE);
			errno  = ENOEXEC;
		}
		else if (GetLastError() == ERROR_INVALID_PARAMETER) {
			/* can't get invalid parameter, so this must be
			 *  the case when we exceed the command length limit.
			 */
			errno = ENAMETOOLONG;
		}
		else {
			errno  = ENOENT;
		}
		if (!is_winnt && !hasdot) { //append '.' to the end if needed
			lstrcat(cmdstr,".");
			hasdot=1;
			goto re_cp;
		}
	}
	else{
		int gui_app ;
		char guivar[50];

		if (GetEnvironmentVariable("TCSH_NOASYNCGUI",guivar,50))
			gui_app=0;
		else {
			if (is_winnt || hasdot)
				gui_app= is_gui(argv0);
			else
				gui_app = is_9x_gui(prog);
		}

		if (is_winnt && !SetThreadPriority(pi.hThread,priority) ) {
			priority =GetLastError();
		}
		if (is_winnt)
			ResumeThread(pi.hThread);
		errno= 0;
		
		if (__nt_really_exec||__nt_child_nohupped || gui_app){
			ExitProcess(0);
		}
		else {
			DWORD exitcode=0;
			WaitForSingleObject(pi.hProcess,INFINITE);
			(void)GetExitCodeProcess(pi.hProcess,&exitcode);
			CloseHandle(pi.hProcess);
			CloseHandle(pi.hThread);
			/*
			 * If output was redirected to /dev/clipboard,
			 * we need to close the pipe handles
			 */
			if (is_dev_clipboard_active) {
				CloseHandle((HANDLE)_get_osfhandle(0));
				CloseHandle((HANDLE)_get_osfhandle(1));
				CloseHandle((HANDLE)_get_osfhandle(2));
				CloseHandle(si.hStdInput);
				CloseHandle(si.hStdOutput);
				CloseHandle(si.hStdError);
				WaitForSingleObject(ghdevclipthread,60*1000);
			}
			ExitProcess(exitcode);
		}
	}
fail_return:
    CloseHandle(si.hStdInput);
    CloseHandle(si.hStdOutput);
    CloseHandle(si.hStdError);
    return;
}
Esempio n. 4
0
void nt_execve_wrapped(char *prog, char**args, char**envir ) {

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    BOOL bRet;
    DWORD type=0;
    char *argv0;
    char **savedargs = args;
    unsigned int cmdsize,cmdlen;
    char *cmdstr ,*cmdend;
//	int rc=0; // unused variable
    int retries=0;
    int is_winnt;
    int hasdot=0;
    char myself[512];

    memset(&si,0,sizeof(si));

    /*
     * This memory is not freed because we are exec()ed and will
     * not be alive long.
     */

    /* This version avoids realloc in concat_args_and_quote, so it should be a little bit safer */
    /* cmdsize = 65500;
     * cmdstr= heap_alloc(cmdsize);
     */
    cmdsize = MAX_PATH << 2;
    cmdstr= heap_alloc(MAX_PATH<<2);

    is_winnt = (gdwPlatform != VER_PLATFORM_WIN32_WINDOWS);

    /* replace /bin/sh with myself to be executed */
    if( strcmp(prog, "/bin/sh") == 0 ) {
        if (GetModuleFileName(GetModuleHandle(NULL),myself,512) <= 512) {
            prog = myself;
        }
    }

    cmdlen = copy_quote_and_fix_slashes(prog,cmdstr,&hasdot);

    cmdend = cmdstr + cmdlen;
    *cmdend = 0;

    if (!is_winnt) {
        argv0 = NULL;
        goto win95_directly_here;
    }
    else {
        argv0 = heap_alloc(MAX_PATH);
        wsprintf(argv0,"%s",prog);
    }

retry:

    bRet=GetBinaryType(argv0,&type);

    if (is_winnt && !bRet  ) {
        /* Don't append .EXE if it could be a script file */
        if (GetLastError() == ERROR_BAD_EXE_FORMAT) {
            try_shell_ex(args,1);
            errno = ENOEXEC;
            return;
        }
        else if (retries) {
            /* If argv[0] got parsed as \\foo (stupid paths with '/' as one
             * of the components will do it,) and if argv[1] is not the same,
             * then this is not a real UNC name.
             * In other cases, argv[0] and argv[1] here must be the same
             * anyway. -amol 5/1/6/98
             */
            if (
                ( (*prog == '\\') ||(*prog == '/') ) &&
                ( (prog[1] == '\\') ||(prog[1] == '/') ) &&
                ((*args[0] == *prog) && (args[0][1] == prog[1])) &&
                (!args[1])
            )
                try_shell_ex(args,1);
            errno  = ENOENT;
        }
        if (retries > 2) {
            return;
        }
        if (retries == 0) {
            wsprintf(argv0,"%s.EXE",prog);
            retries++;
        }
        else if (prog[0] == '\\' && retries == 1) {
            char ptr[80];
            if(GetEnvironmentVariable("ZSHROOT",ptr,80)) {
                wsprintf(argv0,"%s%s",ptr,prog);
            }
            retries++;
        }
        else if (prog[0] == '\\' && retries == 2) {
            char ptr[80];
            if(GetEnvironmentVariable("ZSHROOT",ptr,80)) {
                wsprintf(argv0,"%s%s.EXE",ptr,prog);
            }
            retries++;
        }
        else
            retries += 2;
        goto retry;
    }
    else if (bRet && retries > 0) { //re-fix argv0
        cmdlen = copy_quote_and_fix_slashes(argv0,cmdstr,&hasdot);

        cmdend = cmdstr + cmdlen;
        *cmdend = 0;
    }

win95_directly_here:

    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdInput = (HANDLE)_get_osfhandle(0);
    si.hStdOutput = (HANDLE)_get_osfhandle(1);
    si.hStdError = (HANDLE)_get_osfhandle(2);

    *args++; // ignore argv[0];

    concat_args_and_quote(args,&cmdstr,&cmdlen,&cmdend,&cmdsize);
    *cmdend = 0;
    if (gdwPlatform != VER_PLATFORM_WIN32_WINDOWS) {
        if (!SetConsoleCtrlHandler(NULL,FALSE)) {
            errno = ENOENT;
        }
    }
    fix_path_for_child();

    if (cmdlen < 1000)
        dprintf("argv0 %s cmdstr %s\n",argv0,cmdstr);
    if (!CreateProcess(argv0,
                       cmdstr,
                       NULL,
                       NULL,
                       TRUE, // need this for redirecting std handles
                       0,
                       NULL,//envcrap,
                       NULL,
                       &si,
                       &pi) ) {

        if (GetLastError() == ERROR_BAD_EXE_FORMAT) {
            try_shell_ex(savedargs,1);
            errno  = ENOEXEC;
        }
        else if (GetLastError() == ERROR_INVALID_PARAMETER) {
            /* exceeded command line */
            errno = ENAMETOOLONG;
        }
        else
            errno  = ENOENT;
    }
    else {
        errno= 0;
        //
        {
            DWORD exitcode=0;
            int gui_app = 0;
            if (gdwPlatform != VER_PLATFORM_WIN32_WINDOWS) {
                SetConsoleCtrlHandler(NULL,TRUE);
            }
            if (isset(WINNTWAITFORGUIAPPS))
                gui_app = 0;
            else if (is_winnt)
                gui_app = is_gui(argv0);
            else
                gui_app = is_9x_gui(prog);

            if (!gui_app) {
                WaitForSingleObject(pi.hProcess,INFINITE);
            }
            (void)GetExitCodeProcess(pi.hProcess,&exitcode);
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
            ExitProcess(exitcode);
        }
    }
}
Esempio n. 5
0
int nt_execve(const char *prog, const char *const *args, const char *const *envir)
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	enum {none, directex, shellex} execmode;
	DWORD exitcode;
	DWORD dwCreationflags;
	int priority;
	char *argv0;
	char *cmdstr, *cmdend;
	unsigned int cmdsize;
	size_t prognamelen, cmdlen;
	int hasext;
	char extension[_MAX_FNAME];
	const char *begin, *end, *extptr;
	static char exts[MAX_PATH];

	UNREFERENCED_PARAMETER(envir);

	/* get default PATHEXT or use empty exts */
	if (!*exts) {
		DWORD rc;
		/* not initialized */
		rc = GetEnvironmentVariable("PATHEXT", exts, sizeof(exts));
		if ((rc == 0) || (rc >= sizeof(exts)))
			/* if error or PATHEXT too big will retry at the next call */
			*exts = 0;
	}

	/* if prog has an extension initialize begin end to skip PATHEXT search */
	prognamelen = strlen(prog);
	extptr = prog + prognamelen - 1;
	hasext = 0;
	while (extptr > prog && !ISPATHSEP(*extptr)) {
		if (*extptr == '.' && *(extptr - 1) != ':' && !ISPATHSEP(*(extptr - 1))) {
			hasext++;
			break;
		}
		extptr--;
	}
	if (hasext) {
		begin = ".";
		end = "";
		strcpy(extension, extptr);
	} else {
		begin = exts;
		end = exts;
		*extension = '\0';
	}

	argv0 = (char *)heap_alloc(MAX_PATH);
	/* (prognamelen + 1) does not really matter, argv0 is '\0' filled */
	memcpy(argv0, prog, prognamelen + 1);

	errno = 0;
	execmode = none;
	/* NOTE: loops over PATHEXT if no extension found */
	while (*begin) {
		size_t extlen;
		if (GetBinaryType(argv0, &exitcode)) {
			/* exists and is executable
			   NOTE: an "xxx.exe" without a correct PE header (i.e. a text file)
			   has type "DOS binary", but execution will generate a WOW error */
			execmode = directex;
			break;
		}
		if (GetLastError() == ERROR_BAD_EXE_FORMAT) {
			/* exists but is not "executable" */
			execmode = shellex;
			break;
		}
		if (hasext)
			break;
		/* get next PATHEXT extension */
		while (*begin && (*begin != '.'))
			begin++;
		while (*end && (*end != ';'))
			end++;
		if (!*begin)
			break;
		extlen = end - begin;
		if (extlen < sizeof(extension)) {
			memcpy(extension, begin, extlen);
			extension[extlen] = '\0';
			/* prognamelen ignores the last '\r' if present */
			memcpy(argv0, prog, prognamelen);
			/* update argv0 adding the extension to prog */
			memcpy(argv0 + prognamelen, extension, extlen + 1);
		}
		begin = end;
		/* skip sequences of ';' */
		while (*end && *end == ';')
			end++;
	};

	cmdstr = (char *)heap_alloc(MAX_PATH << 2);
	cmdsize = MAX_PATH << 2;
	cmdlen = 0;
	cmdend = cmdstr;

	dbgprintf(PR_VERBOSE, "%s(): execute [%s] extension=[%s] mode=%d hasext=%d\n", __FUNCTION__, argv0, extension, execmode, hasext);
	/* skip over program name */
	args++;

	/* the file (after PATHEXT search) exists, but it's not "executable" */
	if (execmode == shellex) {
		/* if prog had no extension or has the extension associated to shell scripts */
		if ((hasext == 0 && *extension == '\0') || is_shell_script(extension)) {
			int res = process_shebang(argv0, (const char *const *)&cmdstr, &cmdlen, &cmdend, &cmdsize);
			if (res < 0) {
				execmode = none;
			} else if (res == 0) {
				char *newargv[2];
				cmdlen = copy_quote_and_fix_slashes(gModuleName, cmdstr);
				cmdend = cmdstr + cmdlen;
				newargv[0] = path_to_slash(argv0);
				newargv[1] = NULL;
				concat_args_and_quote((const char *const *)newargv, &cmdstr, &cmdlen, &cmdend, &cmdsize);
				*cmdend = 0;
				argv0 = gModuleName;
				execmode = directex;
			} else {
				cmdend = cmdstr + cmdlen;
				execmode = directex;
			}
		} else {
			unsigned long shflags = 0L;
			/* if the file extension is in pathext, use the same console
			   and wait for child. StrStrI() is from shlwapi */
			if (StrStrI(exts, extension))
				shflags = SEE_MASK_NO_CONSOLE | SEE_MASK_NOCLOSEPROCESS;
			if (try_shell_ex(argv0, args, shflags, &cmdstr, &cmdsize))
				return (0);
			/* ShellExecute failed, the file has an unknown extension, but it
			    may be a shell script with a shebang */
			if (process_shebang(argv0, (const char *const *)&cmdstr, &cmdlen, &cmdend, &cmdsize) > 0) {
				cmdend = cmdstr + cmdlen;
				execmode = directex;
			} else {
				/* the file extension is NOT known and the file has NO shebang:
				   returns EPERM, see NOTES */
				errno = EPERM;
				return (-1);
			}
		}
	} else if (execmode == directex) {
		cmdlen = copy_quote_and_fix_slashes(prog, cmdstr);
		cmdend = cmdstr + cmdlen;
	}
	if (execmode == none) {
		/* error: prog not found even after trying PATHEXT extensions */
		errno = ENOENT;
		return (-1);
	}

	concat_args_and_quote(args, &cmdstr, &cmdlen, &cmdend, &cmdsize);
	if (*cmdstr == ' ') {
		/* if we left a ' ' for the quote and there is no quote */
		cmdstr++;
		cmdlen--;
	}
	*cmdend = 0;

	init_startupinfo(&si);
	dwCreationflags = GetPriorityClass(GetCurrentProcess());
	priority = GetThreadPriority(GetCurrentThread());

#if defined(W32DEBUG)
	/* DebugView output is very difficult to read with overlong lines */
	if (cmdlen < 128)
		dbgprintf(PR_EXEC, "%s(): CreateProcess(%s, ..) cmdstr=[%s]\n", __FUNCTION__, argv0, cmdstr);
	else {
		char shortbuf[128+4];
		memcpy(shortbuf, cmdstr, 128);
		memcpy(shortbuf + 128, "...", 4);
		dbgprintf(PR_EXEC, "nt_execve(): CreateProcess(%s, ..) cmdstr=[%s]\n", argv0, shortbuf);
	}
#endif
	if (!CreateProcess(argv0, cmdstr, NULL, NULL,
			   TRUE, // need this for redirecting std handles
			   dwCreationflags | CREATE_SUSPENDED,
			   NULL, NULL, &si, &pi)) {
                exitcode = GetLastError();
		if (exitcode == ERROR_BAD_EXE_FORMAT) {
			dbgprintf(PR_ERROR, "!!! CreateProcess(%s, ..) error BAD_EXE_FORMAT in %s\n", argv0, __FUNCTION__);
			errno  = ENOEXEC;
		} else if (exitcode == ERROR_INVALID_PARAMETER) {
			dbgprintf(PR_ERROR, "!!! CreateProcess(%s, ..) error INVALID_PARAMETER in %s, cmdstr len=%u\n", argv0, __FUNCTION__, strlen(cmdstr));
			/* exceeded command line */
			/* return NOT found, ENAMETOOLONG is correct but not understood by
			   the shell that will retry with another path ... */
			errno = ENOENT;
		} else {
			dbgprintf(PR_ERROR, "!!! CreateProcess(%s, ..) error %ld in %s\n", argv0, exitcode, __FUNCTION__);
			errno = ENOENT;
		}
		goto fail_return;
	} else {
		exitcode = 0;
		if (!SetThreadPriority(pi.hThread, priority))
			dbgprintf(PR_ERROR, "!!! SetThreadPriority(0x%p) failed, error %ld\n", pi.hThread, GetLastError());
		ResumeThread(pi.hThread);
		if (!is_gui(argv0)) {
			if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0)
				dbgprintf(PR_ERROR, "!!! error %ld waiting for process %ld\n", GetLastError(), pi.dwProcessId);
			if (!GetExitCodeProcess(pi.hProcess, &exitcode))
				dbgprintf(PR_ERROR, "!!! GetExitCodeProcess(0x%p, ..) error %ld in %s\n", pi.hProcess, GetLastError(), __FUNCTION__);
		}
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
		close_si_handles();
		/* @@@@ should wait for the clipboard ?
		if (is_dev_clipboard_active) {
			CloseHandle((HANDLE)_get_osfhandle(0));
			CloseHandle((HANDLE)_get_osfhandle(1));
			CloseHandle((HANDLE)_get_osfhandle(2));
			...
			WaitForSingleObject(ghdevclipthread,60*1000);
			}
		*/
		dbgprintf(PR_ALL, "--- %s(): Exec'd process %ld terminated with exitcode %ld\n", __FUNCTION__, pi.dwProcessId, exitcode);
		exec_exit((int)exitcode);
	}

fail_return:
        heap_free(cmdstr);
	close_si_handles();
	exec_exit(-1);
	return (-1);
}