summaryrefslogtreecommitdiff
path: root/src/cmd/dist/windows.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/dist/windows.c')
-rw-r--r--src/cmd/dist/windows.c910
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__