Commit 3bb00eac37

Andrew Kelley <superjoe30@gmail.com>
2018-07-17 06:01:36
self-hosted: implement getAppDataDir for windows
1 parent 97bfeac
Changed files (2)
src-self-hosted
std
os
windows
src-self-hosted/compilation.zig
@@ -237,6 +237,7 @@ pub const Compilation = struct {
         ReadOnlyFileSystem,
         LinkQuotaExceeded,
         EnvironmentVariableNotFound,
+        AppDataDirUnavailable,
     };
 
     pub const Event = union(enum) {
@@ -944,13 +945,64 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
 }
 
 fn getZigDir(allocator: *mem.Allocator) ![]u8 {
-    const home_dir = try getHomeDir(allocator);
-    defer allocator.free(home_dir);
+    return getAppDataDir(allocator, "zig");
+}
+
+
+const GetAppDataDirError = error{
+    OutOfMemory,
+    AppDataDirUnavailable,
+};
+
+
+/// Caller owns returned memory.
+/// TODO move to zig std lib
+fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
+    switch (builtin.os) {
+        builtin.Os.windows => {
+            var dir_path_ptr: [*]u16 = undefined;
+            switch (os.windows.SHGetKnownFolderPath(&os.windows.FOLDERID_LocalAppData, os.windows.KF_FLAG_CREATE,
+                null, &dir_path_ptr,))
+            {
+                os.windows.S_OK => {
+                    defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
+                    const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr));
+                    defer allocator.free(global_dir);
+                    return os.path.join(allocator, global_dir, appname);
+                },
+                os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
+                else => return error.AppDataDirUnavailable,
+            }
+        },
+        // TODO for macos it should be "~/Library/Application Support/<APPNAME>" 
+        else => {
+            const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) {
+                error.OutOfMemory => return error.OutOfMemory,
+                error.EnvironmentVariableNotFound => return error.AppDataDirUnavailable, // TODO look in /etc/passwd
+            };
+            defer allocator.free(home_dir);
+            return os.path.join(allocator, home_dir, ".local", "share", appname);
+        },
+    }
+}
+
+test "getAppDataDir" {
+    const result = try getAppDataDir(std.debug.global_allocator, "zig");
+    std.debug.warn("{}...", result);
+}
 
-    return os.path.join(allocator, home_dir, ".zig");
+// TODO full utf-16 LE support
+fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
+    const utf8_bytes = try allocator.alloc(u8, utf16le.len);
+    for (utf16le) |codepoint, i| {
+        assert(codepoint < 127); // TODO full utf-16 LE support
+        utf8_bytes[i] = @intCast(u8, codepoint);
+    }
+    return utf8_bytes;
 }
 
-/// TODO move to zig std lib, and make it work for other OSes
-fn getHomeDir(allocator: *mem.Allocator) ![]u8 {
-    return os.getEnvVarOwned(allocator, "HOME");
+fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
+    var index: usize = 0;
+    while (ptr[index] != 0) : (index += 1) {}
+    return ptr[0..index];
 }
std/os/windows/index.zig
@@ -1,3 +1,5 @@
+const std = @import("../../index.zig");
+const assert = std.debug.assert;
 test "import" {
     _ = @import("util.zig");
 }
@@ -439,3 +441,86 @@ pub const SYSTEM_INFO = extern struct {
     wProcessorLevel: WORD,
     wProcessorRevision: WORD,
 };
+
+pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
+
+pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;
+
+pub const HRESULT = c_long;
+
+pub const KNOWNFOLDERID = GUID;
+pub const GUID = extern struct {
+    Data1: c_ulong,
+    Data2: c_ushort,
+    Data3: c_ushort,
+    Data4: [8]u8,
+
+    pub fn parse(str: []const u8) GUID {
+        var guid: GUID = undefined;
+        var index: usize = 0;
+        assert(str[index] == '{');
+        index += 1;
+
+        guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable;
+        index += 8;
+
+        assert(str[index] == '-');
+        index += 1;
+
+        guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
+        index += 4;
+
+        assert(str[index] == '-');
+        index += 1;
+
+        guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable;
+        index += 4;
+
+        assert(str[index] == '-');
+        index += 1;
+
+        guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
+        index += 2;
+        guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
+        index += 2;
+
+        assert(str[index] == '-');
+        index += 1;
+
+        var i: usize = 2;
+        while (i < guid.Data4.len) : (i += 1) {
+            guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable;
+            index += 2;
+        }
+
+        assert(str[index] == '}');
+        index += 1;
+        return guid;
+    }
+};
+
+pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}");
+
+pub const KF_FLAG_DEFAULT = 0;
+pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536;
+pub const KF_FLAG_CREATE = 32768;
+pub const KF_FLAG_DONT_VERIFY = 16384;
+pub const KF_FLAG_DONT_UNEXPAND = 8192;
+pub const KF_FLAG_NO_ALIAS = 4096;
+pub const KF_FLAG_INIT = 2048;
+pub const KF_FLAG_DEFAULT_PATH = 1024;
+pub const KF_FLAG_NOT_PARENT_RELATIVE = 512;
+pub const KF_FLAG_SIMPLE_IDLIST = 256;
+pub const KF_FLAG_ALIAS_ONLY = -2147483648;
+
+pub const S_OK = 0;
+pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001));
+pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002));
+pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003));
+pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004));
+pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005));
+pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF));
+pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
+pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
+pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
+pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));