#define Unknown win_Unknown #define UNICODE #include #include #include #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 HANDLE exporthandle(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; } int oscmdwait(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; } int oscmdkill(void *v) { if(TerminateProcess((HANDLE)v, 666) == FALSE) return -1; return 0; } void oscmdfree(void *v) { CloseHandle((HANDLE)v); }