master
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const unicode = std.unicode;
4const mem = std.mem;
5const fs = std.fs;
6const native_os = builtin.os.tag;
7const posix = std.posix;
8
9pub const GetAppDataDirError = error{
10 OutOfMemory,
11 AppDataDirUnavailable,
12};
13
14/// Caller owns returned memory.
15/// TODO determine if we can remove the allocator requirement
16pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
17 switch (native_os) {
18 .windows => {
19 const local_app_data_dir = std.process.getEnvVarOwned(allocator, "LOCALAPPDATA") catch |err| switch (err) {
20 error.OutOfMemory => |e| return e,
21 else => return error.AppDataDirUnavailable,
22 };
23 defer allocator.free(local_app_data_dir);
24 return fs.path.join(allocator, &[_][]const u8{ local_app_data_dir, appname });
25 },
26 .maccatalyst, .macos => {
27 const home_dir = posix.getenv("HOME") orelse {
28 // TODO look in /etc/passwd
29 return error.AppDataDirUnavailable;
30 };
31 return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname });
32 },
33 .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .illumos, .serenity => {
34 if (posix.getenv("XDG_DATA_HOME")) |xdg| {
35 if (xdg.len > 0) {
36 return fs.path.join(allocator, &[_][]const u8{ xdg, appname });
37 }
38 }
39
40 const home_dir = posix.getenv("HOME") orelse {
41 // TODO look in /etc/passwd
42 return error.AppDataDirUnavailable;
43 };
44 return fs.path.join(allocator, &[_][]const u8{ home_dir, ".local", "share", appname });
45 },
46 .haiku => {
47 var dir_path_buf: [std.fs.max_path_bytes]u8 = undefined;
48 const rc = std.c.find_directory(.B_USER_SETTINGS_DIRECTORY, -1, true, &dir_path_buf, dir_path_buf.len);
49 const settings_dir = try allocator.dupeZ(u8, mem.sliceTo(&dir_path_buf, 0));
50 defer allocator.free(settings_dir);
51 switch (rc) {
52 0 => return fs.path.join(allocator, &[_][]const u8{ settings_dir, appname }),
53 else => return error.AppDataDirUnavailable,
54 }
55 },
56 else => @compileError("Unsupported OS"),
57 }
58}
59
60test getAppDataDir {
61 if (native_os == .wasi) return error.SkipZigTest;
62
63 // We can't actually validate the result
64 const dir = getAppDataDir(std.testing.allocator, "zig") catch return;
65 defer std.testing.allocator.free(dir);
66}