diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2018-07-17 00:01:36 -0400 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2018-07-17 00:01:36 -0400 |
| commit | 3bb00eac37c6b7be789f7b2f516fc1e44e892c8d (patch) | |
| tree | fa86b4e2475a25e2cfc6f3becfdccd1c6ad9603c | |
| parent | 97bfeac13f89e1b5a22fcd7d4705341b4c3e1950 (diff) | |
| download | zig-3bb00eac37c6b7be789f7b2f516fc1e44e892c8d.tar.gz zig-3bb00eac37c6b7be789f7b2f516fc1e44e892c8d.zip | |
self-hosted: implement getAppDataDir for windows
| -rw-r--r-- | src-self-hosted/compilation.zig | 64 | ||||
| -rw-r--r-- | std/os/windows/index.zig | 85 |
2 files changed, 143 insertions, 6 deletions
diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index d5380a0644..4e7526a199 100644 --- a/src-self-hosted/compilation.zig +++ b/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]; } diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index d3525247c9..96c4d3861c 100644 --- a/std/os/windows/index.zig +++ b/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)); |
