self-hosted: implement getAppDataDir for windows

This commit is contained in:
Andrew Kelley 2018-07-17 00:01:36 -04:00
parent 97bfeac13f
commit 3bb00eac37
2 changed files with 144 additions and 7 deletions

View File

@ -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 os.path.join(allocator, home_dir, ".zig");
return getAppDataDir(allocator, "zig");
}
/// TODO move to zig std lib, and make it work for other OSes
fn getHomeDir(allocator: *mem.Allocator) ![]u8 {
return os.getEnvVarOwned(allocator, "HOME");
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);
}
// 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;
}
fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
var index: usize = 0;
while (ptr[index] != 0) : (index += 1) {}
return ptr[0..index];
}

View File

@ -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));