Index: src/cmd/dist/windows.c |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/src/cmd/dist/windows.c |
@@ -0,0 +1,774 @@ |
+// 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) |
+{ |
+ int i, n; |
+ Rune *r, *w, r1; |
+ |
+ 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) |
+{ |
+ char *p; |
+ 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); |
+} |
+ |
+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)) |
+ fatal("ReadFile: %s", errstr()); |
+ if(n == 0) |
+ break; |
+ b->len += n; |
+ } |
+} |
+ |
+void |
+runv(Buf *b, char *dir, int mode, Vec *argv) |
+{ |
+ int i, j, nslash; |
+ Buf cmd; |
+ char *e, *q; |
+ Rune *rcmd, *rexe, *rdir; |
+ STARTUPINFOW si; |
+ PROCESS_INFORMATION pi; |
+ HANDLE p[2]; |
+ DWORD code; |
+ |
+ binit(&cmd); |
+ for(i=0; i<argv->len; i++) { |
+ if(i > 0) |
+ bwritestr(&cmd, " "); |
+ q = argv->p[i]; |
+ if(workdir != nil && hasprefix(q, workdir)) { |
+ bwritestr(&cmd, "$WORK"); |
+ q += strlen(workdir); |
+ } |
+ bwritestr(&cmd, q); |
+ } |
+ //xprintf("%s\n", bstr(&cmd)); |
+ |
+ breset(&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, "\"")) { |
+ 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); |
+ } |
+ } |
+ |
+ 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 { |
+ breset(b); |
+ if(!CreatePipe(&p[0], &p[1], nil, 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]); |
+ } |
+ WaitForSingleObject(pi.hProcess, INFINITE); |
+ |
+ if(!GetExitCodeProcess(pi.hProcess, &code)) |
+ fatal("GetExitCodeProcess: %s", errstr()); |
+ if(mode==CheckExit && code != 0) |
+ fatal("%s failed", argv->p[0]); |
+} |
+ |
+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); |
+} |
+ |
+// rgetwd returns a rune string form of the current directory's path. |
+static Rune* |
+rgetwd(void) |
+{ |
+ int n; |
+ Rune *r; |
+ |
+ n = GetCurrentDirectory(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) |
+{ |
+ int n; |
+ 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) |
+{ |
+ int attr; |
+ Rune *r; |
+ |
+ torune(&r, p); |
+ attr = GetFileAttributesW(r); |
+ xfree(r); |
+ return attr >= 0 && (attr & FILE_ATTRIBUTE_DIRECTORY); |
+} |
+ |
+bool |
+isfile(char *p) |
+{ |
+ int attr; |
+ Rune *r; |
+ |
+ torune(&r, p); |
+ attr = GetFileAttributesW(r); |
+ xfree(r); |
+ return attr >= 0 && !(attr & FILE_ATTRIBUTE_DIRECTORY); |
+} |
+ |
+Time |
+mtime(char *p) |
+{ |
+ HANDLE h; |
+ WIN32_FIND_DATAW data; |
+ Rune *r; |
+ Time t; |
+ FILETIME *ft; |
+ |
+ torune(&r, p); |
+ h = FindFirstFileW(r, &data); |
+ xfree(r); |
+ if(h == INVALID_HANDLE_VALUE) |
+ return 0; |
+ ft = &data.ftLastWriteTime; |
+ return (Time)ft->dwLowDateTime + ((Time)ft->dwHighDateTime<<32); |
+} |
+ |
+bool |
+isabs(char *p) |
+{ |
+ // c:/ or c:\ |
+ if(('A' <= p[0] && p[0] <= 'Z') || ('a' <= p[0] && p[0] <= 'z')) |
+ return p[1] == ':' && (p[2] == '/' || p[2] == '\\'); |
+ // / or \ |
+ return p[0] == '/' || p[0] == '\\'; |
+} |
+ |
+void |
+readfile(Buf *b, char *file) |
+{ |
+ HANDLE h; |
+ Rune *r; |
+ |
+ 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) |
+{ |
+ HANDLE h; |
+ Rune *r; |
+ DWORD n; |
+ |
+ 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); |
+ |
+ fprintf(stderr, "cbuild: %s\n", buf1); |
+ fflush(stderr); |
+ 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, HEAP_GENERATE_EXCEPTIONS, 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) |
+{ |
+ exit(n); |
+} |
+ |
+void |
+xatexit(void (*f)(void)) |
+{ |
+ atexit(f); |
+} |
+ |
+void |
+xprintf(char *fmt, ...) |
+{ |
+ va_list arg; |
+ static char buf[1024]; |
+ DWORD n; |
+ |
+ va_start(arg, fmt); |
+ vsnprintf(buf, sizeof buf, fmt, arg); |
+ va_end(arg); |
+ n = 0; |
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &n, 0); |
+ fflush(stdout); |
+} |
+ |
+int |
+main(int argc, char **argv) |
+{ |
+ char *p; |
+ |
+ setvbuf(stdout, nil, _IOLBF, 0); |
+ setvbuf(stderr, nil, _IOLBF, 0); |
+ |
+ p = argv[0]; |
+ if(hassuffix(p, "bin/go-tool/dist.exe") || hassuffix(p, "bin\\go-tool\\dist.exe")) { |
+ default_goroot = xstrdup(p); |
+ default_goroot[strlen(p)-strlen("bin/go-tool/dist.exe")] = '\0'; |
+ } |
+ |
+ slash = "\\"; |
+ gohostos = "windows"; |
+ 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__ |