diff options
Diffstat (limited to 'src/cmd/dist/windows.c')
-rw-r--r-- | src/cmd/dist/windows.c | 910 |
1 files changed, 910 insertions, 0 deletions
diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c new file mode 100644 index 000000000..557e4b003 --- /dev/null +++ b/src/cmd/dist/windows.c @@ -0,0 +1,910 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// These #ifdefs are being used as a substitute for +// build configuration, so that on any system, this +// tool can be built with the local equivalent of +// cc *.c +// +#ifdef WIN32 + +// Portability layer implemented for Windows. +// See unix.c for doc comments about exported functions. + +#include "a.h" +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +/* + * Windows uses 16-bit rune strings in the APIs. + * Define conversions between Rune* and UTF-8 char*. + */ + +typedef unsigned char uchar; +typedef unsigned short Rune; // same as Windows + +// encoderune encodes the rune r into buf and returns +// the number of bytes used. +static int +encoderune(char *buf, Rune r) +{ + if(r < 0x80) { // 7 bits + buf[0] = r; + return 1; + } + if(r < 0x800) { // 5+6 bits + buf[0] = 0xc0 | (r>>6); + buf[1] = 0x80 | (r&0x3f); + return 2; + } + buf[0] = 0xe0 | (r>>12); + buf[1] = 0x80 | ((r>>6)&0x3f); + buf[2] = 0x80 | (r&0x3f); + return 3; +} + +// decoderune decodes the rune encoding at sbuf into r +// and returns the number of bytes used. +static int +decoderune(Rune *r, char *sbuf) +{ + uchar *buf; + + buf = (uchar*)sbuf; + if(buf[0] < 0x80) { + *r = buf[0]; + return 1; + } + if((buf[0]&0xe0) == 0xc0 && (buf[1]&0xc0) == 0x80) { + *r = (buf[0]&~0xc0)<<6 | (buf[1]&~0x80); + if(*r < 0x80) + goto err; + return 2; + } + if((buf[0]&0xf0) == 0xe0 && (buf[1]&0xc0) == 0x80 && (buf[2]&0xc0) == 0x80) { + *r = (buf[0]&~0xc0)<<12 | (buf[1]&~0x80)<<6 | (buf[2]&~0x80); + if(*r < 0x800) + goto err; + return 3; + } +err: + *r = 0xfffd; + return 1; +} + +// toutf replaces b with the UTF-8 encoding of the rune string r. +static void +toutf(Buf *b, Rune *r) +{ + int i, n; + char buf[4]; + + breset(b); + for(i=0; r[i]; i++) { + n = encoderune(buf, r[i]); + bwrite(b, buf, n); + } +} + +// torune replaces *rp with a pointer to a newly allocated +// rune string equivalent of the UTF-8 string p. +static void +torune(Rune **rp, char *p) +{ + Rune *r, *w; + + r = xmalloc((strlen(p)+1) * sizeof r[0]); + w = r; + while(*p) + p += decoderune(w++, p); + *w = 0; + *rp = r; +} + +// errstr returns the most recent Windows error, in string form. +static char* +errstr(void) +{ + DWORD code; + Rune *r; + Buf b; + + binit(&b); + code = GetLastError(); + r = nil; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + nil, code, 0, (Rune*)&r, 0, nil); + toutf(&b, r); + return bstr(&b); // leak but we're dying anyway +} + +void +xgetenv(Buf *b, char *name) +{ + Rune *buf; + int n; + Rune *r; + + breset(b); + torune(&r, name); + n = GetEnvironmentVariableW(r, NULL, 0); + if(n > 0) { + buf = xmalloc((n+1)*sizeof buf[0]); + GetEnvironmentVariableW(r, buf, n+1); + buf[n] = '\0'; + toutf(b, buf); + xfree(buf); + } + xfree(r); +} + +void +xsetenv(char *name, char *value) +{ + Rune *rname, *rvalue; + + torune(&rname, name); + torune(&rvalue, value); + SetEnvironmentVariableW(rname, rvalue); + xfree(rname); + xfree(rvalue); +} + +char* +bprintf(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + breset(b); + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); + return bstr(b); +} + +void +bwritef(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + // no reset + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); +} + +// bpathf is like bprintf but replaces / with \ in the result, +// to make it a canonical windows file path. +char* +bpathf(Buf *b, char *fmt, ...) +{ + int i; + va_list arg; + char buf[4096]; + + breset(b); + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); + + for(i=0; i<b->len; i++) + if(b->p[i] == '/') + b->p[i] = '\\'; + + return bstr(b); +} + + +static void +breadfrom(Buf *b, HANDLE h) +{ + DWORD n; + + for(;;) { + if(b->len > 1<<22) + fatal("unlikely file size in readfrom"); + bgrow(b, 4096); + n = 0; + if(!ReadFile(h, b->p+b->len, 4096, &n, nil)) { + // Happens for pipe reads. + break; + } + if(n == 0) + break; + b->len += n; + } +} + +void +run(Buf *b, char *dir, int mode, char *cmd, ...) +{ + va_list arg; + Vec argv; + char *p; + + vinit(&argv); + vadd(&argv, cmd); + va_start(arg, cmd); + while((p = va_arg(arg, char*)) != nil) + vadd(&argv, p); + va_end(arg); + + runv(b, dir, mode, &argv); + + vfree(&argv); +} + +static void genrun(Buf*, char*, int, Vec*, int); + +void +runv(Buf *b, char *dir, int mode, Vec *argv) +{ + genrun(b, dir, mode, argv, 1); +} + +void +bgrunv(char *dir, int mode, Vec *argv) +{ + genrun(nil, dir, mode, argv, 0); +} + +#define MAXBG 4 /* maximum number of jobs to run at once */ + +static struct { + PROCESS_INFORMATION pi; + int mode; + char *cmd; +} bg[MAXBG]; + +static int nbg; + +static void bgwait1(void); + +static void +genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) +{ + int i, j, nslash; + Buf cmd; + char *q; + Rune *rcmd, *rexe, *rdir; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + HANDLE p[2]; + + while(nbg >= nelem(bg)) + bgwait1(); + + binit(&cmd); + + for(i=0; i<argv->len; i++) { + if(i > 0) + bwritestr(&cmd, " "); + q = argv->p[i]; + if(contains(q, " ") || contains(q, "\t") || contains(q, "\"") || contains(q, "\\\\") || hassuffix(q, "\\")) { + bwritestr(&cmd, "\""); + nslash = 0; + for(; *q; q++) { + if(*q == '\\') { + nslash++; + continue; + } + if(*q == '"') { + for(j=0; j<2*nslash+1; j++) + bwritestr(&cmd, "\\"); + nslash = 0; + } + for(j=0; j<nslash; j++) + bwritestr(&cmd, "\\"); + nslash = 0; + bwrite(&cmd, q, 1); + } + for(j=0; j<2*nslash; j++) + bwritestr(&cmd, "\\"); + bwritestr(&cmd, "\""); + } else { + bwritestr(&cmd, q); + } + } + if(vflag > 1) + xprintf("%s\n", bstr(&cmd)); + + torune(&rcmd, bstr(&cmd)); + rexe = nil; + rdir = nil; + if(dir != nil) + torune(&rdir, dir); + + memset(&si, 0, sizeof si); + si.cb = sizeof si; + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = INVALID_HANDLE_VALUE; + if(b == nil) { + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + } else { + SECURITY_ATTRIBUTES seci; + + memset(&seci, 0, sizeof seci); + seci.nLength = sizeof seci; + seci.bInheritHandle = 1; + breset(b); + if(!CreatePipe(&p[0], &p[1], &seci, 0)) + fatal("CreatePipe: %s", errstr()); + si.hStdOutput = p[1]; + si.hStdError = p[1]; + } + + if(!CreateProcessW(rexe, rcmd, nil, nil, TRUE, 0, nil, rdir, &si, &pi)) { + if(mode!=CheckExit) + return; + fatal("%s: %s", argv->p[0], errstr()); + } + if(rexe != nil) + xfree(rexe); + xfree(rcmd); + if(rdir != nil) + xfree(rdir); + if(b != nil) { + CloseHandle(p[1]); + breadfrom(b, p[0]); + CloseHandle(p[0]); + } + + if(nbg < 0) + fatal("bad bookkeeping"); + bg[nbg].pi = pi; + bg[nbg].mode = mode; + bg[nbg].cmd = btake(&cmd); + nbg++; + + if(wait) + bgwait(); + + bfree(&cmd); +} + +// closes the background job for bgwait1 +static void +bgwaitclose(int i) +{ + if(i < 0 || i >= nbg) + return; + + CloseHandle(bg[i].pi.hProcess); + CloseHandle(bg[i].pi.hThread); + + bg[i] = bg[--nbg]; +} + +// bgwait1 waits for a single background job +static void +bgwait1(void) +{ + int i, mode; + char *cmd; + HANDLE bgh[MAXBG]; + DWORD code; + + if(nbg == 0) + fatal("bgwait1: nothing left"); + + for(i=0; i<nbg; i++) + bgh[i] = bg[i].pi.hProcess; + i = WaitForMultipleObjects(nbg, bgh, FALSE, INFINITE); + if(i < 0 || i >= nbg) + fatal("WaitForMultipleObjects: %s", errstr()); + + cmd = bg[i].cmd; + mode = bg[i].mode; + if(!GetExitCodeProcess(bg[i].pi.hProcess, &code)) { + bgwaitclose(i); + fatal("GetExitCodeProcess: %s", errstr()); + return; + } + + if(mode==CheckExit && code != 0) { + bgwaitclose(i); + fatal("FAILED: %s", cmd); + return; + } + + bgwaitclose(i); +} + +void +bgwait(void) +{ + while(nbg > 0) + bgwait1(); +} + +// rgetwd returns a rune string form of the current directory's path. +static Rune* +rgetwd(void) +{ + int n; + Rune *r; + + n = GetCurrentDirectoryW(0, nil); + r = xmalloc((n+1)*sizeof r[0]); + GetCurrentDirectoryW(n+1, r); + r[n] = '\0'; + return r; +} + +void +xgetwd(Buf *b) +{ + Rune *r; + + r = rgetwd(); + breset(b); + toutf(b, r); + xfree(r); +} + +void +xrealwd(Buf *b, char *path) +{ + Rune *old; + Rune *rnew; + + old = rgetwd(); + torune(&rnew, path); + if(!SetCurrentDirectoryW(rnew)) + fatal("chdir %s: %s", path, errstr()); + free(rnew); + xgetwd(b); + if(!SetCurrentDirectoryW(old)) { + breset(b); + toutf(b, old); + fatal("chdir %s: %s", bstr(b), errstr()); + } +} + +bool +isdir(char *p) +{ + DWORD attr; + Rune *r; + + torune(&r, p); + attr = GetFileAttributesW(r); + xfree(r); + return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY); +} + +bool +isfile(char *p) +{ + DWORD attr; + Rune *r; + + torune(&r, p); + attr = GetFileAttributesW(r); + xfree(r); + return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY); +} + +Time +mtime(char *p) +{ + HANDLE h; + WIN32_FIND_DATAW data; + Rune *r; + FILETIME *ft; + + torune(&r, p); + h = FindFirstFileW(r, &data); + xfree(r); + if(h == INVALID_HANDLE_VALUE) + return 0; + FindClose(h); + ft = &data.ftLastWriteTime; + return (Time)ft->dwLowDateTime + ((Time)ft->dwHighDateTime<<32); +} + +bool +isabs(char *p) +{ + // c:/ or c:\ at beginning + if(('A' <= p[0] && p[0] <= 'Z') || ('a' <= p[0] && p[0] <= 'z')) + return p[1] == ':' && (p[2] == '/' || p[2] == '\\'); + // / or \ at beginning + return p[0] == '/' || p[0] == '\\'; +} + +void +readfile(Buf *b, char *file) +{ + HANDLE h; + Rune *r; + + if(vflag > 2) + xprintf("read %s\n", file); + torune(&r, file); + h = CreateFileW(r, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); + if(h == INVALID_HANDLE_VALUE) + fatal("open %s: %s", file, errstr()); + breadfrom(b, h); + CloseHandle(h); +} + +void +writefile(Buf *b, char *file, int exec) +{ + HANDLE h; + Rune *r; + DWORD n; + + USED(exec); + + if(vflag > 2) + xprintf("write %s\n", file); + torune(&r, file); + h = CreateFileW(r, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0); + if(h == INVALID_HANDLE_VALUE) + fatal("create %s: %s", file, errstr()); + n = 0; + if(!WriteFile(h, b->p, b->len, &n, 0)) + fatal("write %s: %s", file, errstr()); + CloseHandle(h); +} + + +void +xmkdir(char *p) +{ + Rune *r; + + torune(&r, p); + if(!CreateDirectoryW(r, nil)) + fatal("mkdir %s: %s", p, errstr()); + xfree(r); +} + +void +xmkdirall(char *p) +{ + int c; + char *q, *q2; + + if(isdir(p)) + return; + q = strrchr(p, '/'); + q2 = strrchr(p, '\\'); + if(q2 != nil && (q == nil || q < q2)) + q = q2; + if(q != nil) { + c = *q; + *q = '\0'; + xmkdirall(p); + *q = c; + } + xmkdir(p); +} + +void +xremove(char *p) +{ + int attr; + Rune *r; + + torune(&r, p); + attr = GetFileAttributesW(r); + if(attr >= 0) { + if(attr & FILE_ATTRIBUTE_DIRECTORY) + RemoveDirectoryW(r); + else + DeleteFileW(r); + } + xfree(r); +} + +void +xreaddir(Vec *dst, char *dir) +{ + Rune *r; + Buf b; + HANDLE h; + WIN32_FIND_DATAW data; + char *p, *q; + + binit(&b); + vreset(dst); + + bwritestr(&b, dir); + bwritestr(&b, "\\*"); + torune(&r, bstr(&b)); + + h = FindFirstFileW(r, &data); + xfree(r); + if(h == INVALID_HANDLE_VALUE) + goto out; + do{ + toutf(&b, data.cFileName); + p = bstr(&b); + q = xstrrchr(p, '\\'); + if(q != nil) + p = q+1; + if(!streq(p, ".") && !streq(p, "..")) + vadd(dst, p); + }while(FindNextFileW(h, &data)); + FindClose(h); + +out: + bfree(&b); +} + +char* +xworkdir(void) +{ + Rune buf[1024]; + Rune tmp[MAX_PATH]; + Rune go[3] = {'g', 'o', '\0'}; + int n; + Buf b; + + n = GetTempPathW(nelem(buf), buf); + if(n <= 0) + fatal("GetTempPath: %s", errstr()); + buf[n] = '\0'; + + if(GetTempFileNameW(buf, go, 0, tmp) == 0) + fatal("GetTempFileName: %s", errstr()); + DeleteFileW(tmp); + if(!CreateDirectoryW(tmp, nil)) + fatal("create tempdir: %s", errstr()); + + binit(&b); + toutf(&b, tmp); + return btake(&b); +} + +void +xremoveall(char *p) +{ + int i; + Buf b; + Vec dir; + Rune *r; + + binit(&b); + vinit(&dir); + + torune(&r, p); + if(isdir(p)) { + xreaddir(&dir, p); + for(i=0; i<dir.len; i++) { + bprintf(&b, "%s/%s", p, dir.p[i]); + xremoveall(bstr(&b)); + } + RemoveDirectoryW(r); + } else { + DeleteFileW(r); + } + xfree(r); + + bfree(&b); + vfree(&dir); +} + +void +fatal(char *msg, ...) +{ + static char buf1[1024]; + va_list arg; + + va_start(arg, msg); + vsnprintf(buf1, sizeof buf1, msg, arg); + va_end(arg); + + xprintf("go tool dist: %s\n", buf1); + + bgwait(); + ExitProcess(1); +} + +// HEAP is the persistent handle to the default process heap. +static HANDLE HEAP = INVALID_HANDLE_VALUE; + +void* +xmalloc(int n) +{ + void *p; + + if(HEAP == INVALID_HANDLE_VALUE) + HEAP = GetProcessHeap(); + p = HeapAlloc(HEAP, 0, n); + if(p == nil) + fatal("out of memory allocating %d: %s", n, errstr()); + memset(p, 0, n); + return p; +} + +char* +xstrdup(char *p) +{ + char *q; + + q = xmalloc(strlen(p)+1); + strcpy(q, p); + return q; +} + +void +xfree(void *p) +{ + if(HEAP == INVALID_HANDLE_VALUE) + HEAP = GetProcessHeap(); + HeapFree(HEAP, 0, p); +} + +void* +xrealloc(void *p, int n) +{ + if(p == nil) + return xmalloc(n); + if(HEAP == INVALID_HANDLE_VALUE) + HEAP = GetProcessHeap(); + p = HeapReAlloc(HEAP, 0, p, n); + if(p == nil) + fatal("out of memory reallocating %d", n); + return p; +} + +bool +hassuffix(char *p, char *suffix) +{ + int np, ns; + + np = strlen(p); + ns = strlen(suffix); + return np >= ns && strcmp(p+np-ns, suffix) == 0; +} + +bool +hasprefix(char *p, char *prefix) +{ + return strncmp(p, prefix, strlen(prefix)) == 0; +} + +bool +contains(char *p, char *sep) +{ + return strstr(p, sep) != nil; +} + +bool +streq(char *p, char *q) +{ + return strcmp(p, q) == 0; +} + +char* +lastelem(char *p) +{ + char *out; + + out = p; + for(; *p; p++) + if(*p == '/' || *p == '\\') + out = p+1; + return out; +} + +void +xmemmove(void *dst, void *src, int n) +{ + memmove(dst, src, n); +} + +int +xmemcmp(void *a, void *b, int n) +{ + return memcmp(a, b, n); +} + +int +xstrlen(char *p) +{ + return strlen(p); +} + +void +xexit(int n) +{ + ExitProcess(n); +} + +void +xatexit(void (*f)(void)) +{ + atexit(f); +} + +void +xprintf(char *fmt, ...) +{ + va_list arg; + char *p; + DWORD n, w; + + va_start(arg, fmt); + n = vsnprintf(NULL, 0, fmt, arg); + p = xmalloc(n+1); + vsnprintf(p, n+1, fmt, arg); + va_end(arg); + w = 0; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), p, n, &w, 0); + xfree(p); +} + +int +main(int argc, char **argv) +{ + SYSTEM_INFO si; + + setvbuf(stdout, nil, _IOLBF, 0); + setvbuf(stderr, nil, _IOLBF, 0); + + slash = "\\"; + gohostos = "windows"; + + GetSystemInfo(&si); + switch(si.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + gohostarch = "amd64"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + gohostarch = "386"; + break; + default: + fatal("unknown processor architecture"); + } + + init(); + + xmain(argc, argv); + return 0; +} + +void +xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*)) +{ + qsort(data, n, elemsize, cmp); +} + +int +xstrcmp(char *a, char *b) +{ + return strcmp(a, b); +} + +char* +xstrstr(char *a, char *b) +{ + return strstr(a, b); +} + +char* +xstrrchr(char *p, int c) +{ + char *ep; + + ep = p+strlen(p); + for(ep=p+strlen(p); ep >= p; ep--) + if(*ep == c) + return ep; + return nil; +} + +#endif // __WINDOWS__ |