Charles.Forsyth20061220965a0ca19 years agoHistory
243 lines · 4.6 KB · C
RawDownload
#define Unknown win_Unknown#define UNICODE#include	<windows.h>#include <winbase.h>#include	<winsock.h>#undef Unknown#include	"dat.h"#include	"fns.h"#include	"error.h" extern int	nth2fd(HANDLE);extern wchar_t	*widen(char*); /* * thanks to rcsh for these. * * windows quoting rules - I think * Words are separated by space or tab * Words containing a space or tab can be quoted using " * 2N backslashes + " ==> N backslashes and end quote * 2N+1 backslashes + " ==> N backslashes + literal " * N backslashes not followed by " ==> N backslashes */static char *dblquote(char *cmd, char *s){	int nb;	char *p; 	for(p=s; *p; p++)		if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"')			break; 	if(*p == 0){				/* easy case */		strcpy(cmd, s);		return cmd+(p-s);	} 	*cmd++ = '"';	for(;;) {		for(nb=0; *s=='\\'; nb++)			*cmd++ = *s++; 		if(*s == 0) {			/* trailing backslashes -> 2N */			while(nb-- > 0)				*cmd++ = '\\';			break;		} 		if(*s == '"') {			/* literal quote -> 2N+1 backslashes */			while(nb-- > 0)				*cmd++ = '\\';			*cmd++ = '\\';		/* escape the quote */		}		*cmd++ = *s++;	} 	*cmd++ = '"';	*cmd = 0; 	return cmd;} static char *ntquotedcmd(char **argv){	int i, n;	char *cmd, *p; 		/* conservatively calculate length of command;		 * backslash expansion can cause growth in dblquote().		 */	for(i=0,n=0; argv[i]; i++)		n += 2*strlen(argv[i]);	n++;		cmd = malloc(n);	if(cmd == nil)		return nil;	for(i=0,p=cmd; argv[i]; i++) {		p = dblquote(p, argv[i]);		*p++ = ' ';	}	if(p != cmd)		p--;	*p = 0; 	return cmd;} static HANDLEexporthandle(HANDLE h, int close){	HANDLE cp, dh;	DWORD flags = DUPLICATE_SAME_ACCESS;	if (close)		flags |= DUPLICATE_CLOSE_SOURCE;	cp = GetCurrentProcess();	if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags))		return nil;	return dh;} /* TO DO: check that oserrstr will have the right text on error */ void*oscmd(char **args, int nice, char *dir, int *fd){	STARTUPINFO si;	SECURITY_ATTRIBUTES sec;	HANDLE rh, wh, eh, srh, swh, seh;	PROCESS_INFORMATION pinfo;	char *cmd;	wchar_t *wcmd, *wdir;	int prio; 	wdir = nil;	if(dir != nil)		wdir = widen(dir); 	cmd = ntquotedcmd(args);	if(cmd == nil)		error(Enomem); 	wcmd = widen(cmd);	sec.nLength = sizeof(sec);	sec.lpSecurityDescriptor = 0;	sec.bInheritHandle = 0;	rh = wh = eh = srh = swh = seh = nil;	if(!CreatePipe(&rh, &swh, &sec, 0))		goto Error;	if(!CreatePipe(&srh, &wh, &sec, 0))		goto Error;	if(!CreatePipe(&seh, &eh, &sec, 0))		goto Error;	rh = exporthandle(rh, 1);	if(rh == nil)		goto Error;	wh = exporthandle(wh, 1);	if(wh == nil)		goto Error;	eh = exporthandle(eh, 1);	if(eh == nil)		goto Error; 	memset(&si, 0, sizeof(si));	si.cb = sizeof(si);	si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;	si.wShowWindow = SW_SHOW;	si.hStdInput = rh;	si.hStdOutput = wh;	si.hStdError = eh; 	prio = 0;	if(nice){		prio = IDLE_PRIORITY_CLASS;		if(nice > 1)			prio |= CREATE_SUSPENDED;	} 	/* default of nil for wpath seems to be what we want; nil for env exports our current one */	if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1,	   CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio,	   0 /*env*/, wdir, &si, &pinfo)){		//print("can't create process '%Q' %d\n", wcmd, GetLastError());		goto Error;	} 	fd[0] = nth2fd(swh);	fd[1] = nth2fd(srh);	fd[2] = nth2fd(seh);	if(fd[1] == 1 || fd[2] == 2)		panic("invalid mapping of handle to fd");	CloseHandle(si.hStdInput);	CloseHandle(si.hStdOutput);	CloseHandle(si.hStdError); 	if(prio & CREATE_SUSPENDED){		if(nice > 1)			SetThreadPriority(pinfo.hThread,				nice>3? THREAD_PRIORITY_IDLE:				nice>2? THREAD_PRIORITY_LOWEST:				THREAD_PRIORITY_BELOW_NORMAL);		ResumeThread(pinfo.hThread);	}	CloseHandle(pinfo.hThread);	/* don't close process handle */	free(cmd);	free(wcmd);	free(wdir);	return pinfo.hProcess; Error:	if(rh)		CloseHandle(rh);	if(wh)		CloseHandle(wh);	if(eh)		CloseHandle(eh);	if(srh)		CloseHandle(srh);	if(swh)		CloseHandle(swh);	if(seh)		CloseHandle(seh);	free(cmd);	free(wcmd);	free(wdir);	return nil;} intoscmdwait(void *v, char *buf, int n){	int status;	HANDLE proc = (HANDLE)v; 	/* need not worry about being interrupted */	if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED)		return -1;	if(!GetExitCodeProcess(proc, &status))		status = 1;	if(status)		n = snprint(buf, n, "0 0 0 0 'status %d'", status);	else		n = snprint(buf, n, "0 0 0 0 ''");	return n; } intoscmdkill(void *v){	if(TerminateProcess((HANDLE)v, 666) == FALSE)		return -1;	return 0;} voidoscmdfree(void *v){	CloseHandle((HANDLE)v);}