diff options
Diffstat (limited to 'utils/rcsh/Nt.c')
| -rw-r--r-- | utils/rcsh/Nt.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/utils/rcsh/Nt.c b/utils/rcsh/Nt.c new file mode 100644 index 00000000..dc6ad8a9 --- /dev/null +++ b/utils/rcsh/Nt.c @@ -0,0 +1,549 @@ +#include "rc.h" +#include <windows.h> + +enum { + Nchild = 100, +}; + +typedef struct Child Child; + +struct Child { + int pid; + HANDLE handle; +}; + +static Child child[Nchild]; + +static void +winerror(void) +{ + int e, r; + char buf[100], *p, *q; + + e = GetLastError(); + + r = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buf, sizeof(buf), 0); + + if(r == 0) + snprint(buf, sizeof(buf), "windows error %d", e); + + for(p=q=buf; *p; p++) { + if(*p == '\r') + continue; + if(*p == '\n') + *q++ = ' '; + else + *q++ = *p; + } + *q = 0; + errstr(buf, sizeof buf); +} + +static int +badentry(char *filename) +{ + if(*filename == 0) + return 1; + if(filename[0] == '.'){ + if(filename[1] == 0) + return 1; + if(filename[1] == '.' && filename[2] == 0) + return 1; + } + return 0; +} + +Direntry* +readdirect(char *path) +{ + long n; + HANDLE h; + Direntry *d; + char fullpath[MAX_PATH]; + WIN32_FIND_DATA data; + + snprint(fullpath, MAX_PATH, "%s\\*.*", path); + + h = FindFirstFile(fullpath, &data); + if(h == INVALID_HANDLE_VALUE) + return 0; + + n = 0; + d = 0; + for(;;){ + if(!badentry(data.cFileName)){ + d = realloc(d, (n+2)*sizeof(Direntry)); + if(d == 0){ + werrstr("memory allocation"); + return 0; + } + d[n].name = malloc(strlen(data.cFileName)+1); + if(d[n].name == 0){ + werrstr("memory allocation"); + return 0; + } + strcpy(d[n].name, data.cFileName); + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + d[n].isdir = 1; + else + d[n].isdir = 0; + n++; + } + if(FindNextFile(h, &data) == 0) + break; + } + FindClose(h); + if(d){ + d[n].name = 0; + d[n].isdir = 0; + } + return d; +} + +void +fatal(char *fmt, ...) +{ + char buf[512]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + + fprint(2, "rc: %s\n", buf); + _exits(buf); +} + +static int +tas(int *p) +{ + int v; + + _asm { + mov eax, p + mov ebx, 1 + xchg ebx, [eax] + mov v, ebx + } + + return v; +} + +static void +lock(Lock *lk) +{ + int i; + + /* easy case */ + if(!tas(&lk->val)) + return; + + /* for muli processor machines */ + for(i=0; i<100; i++) + if(!tas(&lk->val)) + return; + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); + for(;;) { + for(i=0; i<10000; i++) { + Sleep(0); + if(!tas(&lk->val)) { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + return; + } + } + } +} + +static void +unlock(Lock *lk) +{ + lk->val = 0; +} + +int +refinc(Ref *r) +{ + int i; + + lock(&r->lk); + i = r->ref; + r->ref++; + unlock(&r->lk); + return i; +} + +int +refdec(Ref *r) +{ + int i; + + lock(&r->lk); + r->ref--; + i = r->ref; + unlock(&r->lk); + + return i; +} + +/* + * windows quoting rules - I think + * Words are seperated 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 * +proccmd(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); + for(i=0,p=cmd; argv[i]; i++) { + p = dblquote(p, argv[i]); + *p++ = ' '; + } + if(p != cmd) + p--; + *p = 0; + + return cmd; +} + +static char * +exportenv(char **e) +{ + int i, j, n; + char *buf; + + if(e == 0 || *e == 0) + return 0; + + buf = 0; + n = 0; + for(i = 0; *e; e++, i++) { + j = strlen(*e)+1; + buf = realloc(buf, n+j); + strcpy(buf+n, *e); + n += j; + } + /* final null */ + buf = realloc(buf, n+1); + buf[n] = 0; + + return buf; +} + +static int +setpath(char *path, char *file) +{ + char *p, *last, tmp[MAX_PATH+1]; + int n; + + if(strlen(file) >= MAX_PATH){ + werrstr("file name too long"); + return -1; + } + strcpy(tmp, file); + + for(p=tmp; *p; p++) { + if(*p == '/') + *p = '\\'; + } + + if(tmp[0] != 0 && tmp[1] == ':') { + if(tmp[2] == 0) { + tmp[2] = '\\'; + tmp[3] = 0; + } else if(tmp[2] != '\\') { + /* don't allow c:foo - only c:\foo */ + werrstr("illegal file name"); + return -1; + } + } + + path[0] = 0; + n = GetFullPathName(tmp, MAX_PATH, path, &last); + if(n >= MAX_PATH) { + werrstr("file name too long"); + return -1; + } + if(n == 0 && tmp[0] == '\\' && tmp[1] == '\\' && tmp[2] != 0) { + strcpy(path, tmp); + return -1; + } + + if(n == 0) { + werrstr("bad file name"); + return -1; + } + + for(p=path; *p; p++) { + if(*p < 32 || *p == '*' || *p == '?') { + werrstr("file not found"); + return -1; + } + } + + /* get rid of trailling \ */ + if(path[n-1] == '\\') { + if(n <= 2) { + werrstr("illegal file name"); + return -1; + } + path[n-1] = 0; + n--; + } + + if(path[1] == ':' && path[2] == 0) { + path[2] = '\\'; + path[3] = '.'; + path[4] = 0; + return -1; + } + + if(path[0] != '\\' || path[1] != '\\') + return 0; + + for(p=path+2,n=0; *p; p++) + if(*p == '\\') + n++; + if(n == 0) + return -1; + if(n == 1) + return -1; + return 0; +} + + +static int +execpath(char *path, char *file) +{ + int n; + + if(setpath(path, file) < 0) + return 0; + + n = strlen(path)-4; + if(path[n] == '.') { + if(GetFileAttributes(path) != -1) + return 1; + } + strncat(path, ".exe", MAX_PATH); + path[MAX_PATH-1] = 0; + if(GetFileAttributes(path) != -1) + return 1; + return 0; +} + +static HANDLE +fdexport(int fd) +{ + HANDLE h, r; + + if(fd < 0) + return INVALID_HANDLE_VALUE; + + h = (HANDLE)_get_osfhandle(fd); + if(h < 0) + return INVALID_HANDLE_VALUE; + + if(!DuplicateHandle(GetCurrentProcess(), h, + GetCurrentProcess(), &r, DUPLICATE_SAME_ACCESS, + 1, DUPLICATE_SAME_ACCESS)) + return INVALID_HANDLE_VALUE; + return r; +} + +static int +addchild(int pid, HANDLE handle) +{ + int i; + + for(i=0; i<Nchild; i++) { + if(child[i].handle == 0) { + child[i].handle = handle; + child[i].pid = pid; + return 1; + } + } + werrstr("child table full"); + return 0; +} + +int +procwait(uint pid) +{ + HANDLE h; + int i, exit; + + if(pid == 0) + return 0; + + h = 0; + for(i = 0; i < Nchild; i++){ + if(child[i].pid == pid){ + h = child[i].handle; + child[i].pid = 0; + child[i].handle = 0; + break; + } + } + + if(h == 0){ /* we don't know about this one - let the system try to find it */ + h = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); + if(h == 0) + return 0; /* can't find it */ + } + + if(WaitForSingleObject(h, INFINITE) == WAIT_FAILED) { + winerror(); + fatal("procwait: "); + } + + if(!GetExitCodeProcess(h, &exit)) { + winerror(); + exit = 1; + } + + CloseHandle(h); + return exit; +} + +uint +proc(char **argv, int stdin, int stdout, int stderr) +{ + char *p, *arg0, *q, buf[MAX_PATH], path[MAX_PATH], *cmd, *eb; + STARTUPINFO si; + PROCESS_INFORMATION pi; + int r, found, full; + extern char **_environ; + Word *w; + + arg0 = argv[0]; + if(arg0 == 0) { + werrstr("null argv[0]"); + return 0; + } + + full = arg0[0] == '\\' || arg0[0] == '/' || arg0[0] == '.'; + found = execpath(path, arg0); + + if(!found && !full) { + w = vlook("path")->val; + if(w) + p = w->word; + else + p = getenv("path"); + for(; p && *p; p = q){ + q = strchr(p, ';'); + if(q) + *q = 0; + snprint(buf, sizeof(buf), "%s/%s", p, arg0); + if(q) + *q++ = ';'; + found = execpath(path, buf); + if(found) + break; + } + } + + if(!found) { + werrstr("file not found"); + return 0; + } + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; + si.wShowWindow = SW_SHOW; + si.hStdInput = fdexport(stdin); + si.hStdOutput = fdexport(stdout); + si.hStdError = fdexport(stderr); + + eb = exportenv(_environ); + + cmd = proccmd(argv); + + r = CreateProcess(path, cmd, 0, 0, 1, 0, eb, 0, &si, &pi); + + /* allow child to run */ + Sleep(0); + + free(cmd); + free(eb); + + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + if(!r) { + winerror(); + return 0; + } + + CloseHandle(pi.hThread); + + if(addchild(pi.dwProcessId, pi.hProcess) == 0) + return 0; + + return pi.dwProcessId; +} + +int +pipe(int *fd) +{ + return _pipe(fd, 0, _O_BINARY); +} |
