aboutsummaryrefslogtreecommitdiff
path: root/std/os/get_app_data_dir.zig
blob: ae133bb4b1f31c1c0a0ffa88592467ba79fd17a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
const std = @import("../index.zig");
const builtin = @import("builtin");
const unicode = std.unicode;
const mem = std.mem;
const os = std.os;

pub const GetAppDataDirError = error{
    OutOfMemory,
    AppDataDirUnavailable,
};

/// Caller owns returned memory.
/// TODO determine if we can remove the allocator requirement
pub 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 = unicode.utf16leToUtf8Alloc(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) {
                        error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
                        error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
                        error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,
                        error.OutOfMemory => return error.OutOfMemory,
                    };
                    defer allocator.free(global_dir);
                    return os.path.join(allocator, global_dir, appname);
                },
                os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
                else => return error.AppDataDirUnavailable,
            }
        },
        builtin.Os.macosx => {
            const home_dir = os.getEnvPosix("HOME") orelse {
                // TODO look in /etc/passwd
                return error.AppDataDirUnavailable;
            };
            return os.path.join(allocator, home_dir, "Library", "Application Support", appname);
        },
        builtin.Os.linux, builtin.Os.freebsd => {
            const home_dir = os.getEnvPosix("HOME") orelse {
                // TODO look in /etc/passwd
                return error.AppDataDirUnavailable;
            };
            return os.path.join(allocator, home_dir, ".local", "share", appname);
        },
        else => @compileError("Unsupported OS"),
    }
}

fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
    var index: usize = 0;
    while (ptr[index] != 0) : (index += 1) {}
    return ptr[0..index];
}

test "std.os.getAppDataDir" {
    var buf: [512]u8 = undefined;
    const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;

    // We can't actually validate the result
    _ = getAppDataDir(allocator, "zig") catch return;
}