summaryrefslogtreecommitdiff
path: root/emu/Nt/cmd.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2009-03-25 15:55:14 +0000
committerCharles.Forsyth <devnull@localhost>2009-03-25 15:55:14 +0000
commitdfd1934d5e1ddbeb326f77fc0e52307c801a1a3e (patch)
treef1e8b23278caae95e01d88b00421d6c3642357ef /emu/Nt/cmd.c
parent78dfdcbd59dc8f36975e7695933e3f753957474c (diff)
x20090325-1554
Diffstat (limited to 'emu/Nt/cmd.c')
-rw-r--r--emu/Nt/cmd.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/emu/Nt/cmd.c b/emu/Nt/cmd.c
new file mode 100644
index 00000000..764d328b
--- /dev/null
+++ b/emu/Nt/cmd.c
@@ -0,0 +1,243 @@
+#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 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);
+}