Merge pull request #461 from mitchellh/goonz/windows-path-and-tmpdir

Windows: implement tmpDir and expandPath
This commit is contained in:
Mitchell Hashimoto
2023-09-15 17:15:46 -07:00
committed by GitHub
4 changed files with 38 additions and 15 deletions

View File

@ -223,8 +223,6 @@ pub fn getData(self: Command, comptime DT: type) ?*DT {
/// Search for "cmd" in the PATH and return the absolute path. This will /// Search for "cmd" in the PATH and return the absolute path. This will
/// always allocate if there is a non-null result. The caller must free the /// always allocate if there is a non-null result. The caller must free the
/// resulting value. /// resulting value.
///
/// TODO: windows
pub fn expandPath(alloc: Allocator, cmd: []const u8) !?[]u8 { pub fn expandPath(alloc: Allocator, cmd: []const u8) !?[]u8 {
// If the command already contains a slash, then we return it as-is // If the command already contains a slash, then we return it as-is
// because it is assumed to be absolute or relative. // because it is assumed to be absolute or relative.
@ -232,9 +230,18 @@ pub fn expandPath(alloc: Allocator, cmd: []const u8) !?[]u8 {
return try alloc.dupe(u8, cmd); return try alloc.dupe(u8, cmd);
} }
const PATH = os.getenvZ("PATH") orelse return null; const PATH = switch (builtin.os.tag) {
.windows => blk: {
const win_path = os.getenvW(std.unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse return null;
const path = try std.unicode.utf16leToUtf8Alloc(alloc, win_path);
break :blk path;
},
else => os.getenvZ("PATH") orelse return null,
};
defer if (builtin.os.tag == .windows) alloc.free(PATH);
var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var it = std.mem.tokenize(u8, PATH, ":"); var it = std.mem.tokenize(u8, PATH, &[_]u8{std.fs.path.delimiter});
var seen_eacces = false; var seen_eacces = false;
while (it.next()) |search_path| { while (it.next()) |search_path| {
// We need enough space in our path buffer to store this // We need enough space in our path buffer to store this
@ -243,7 +250,7 @@ pub fn expandPath(alloc: Allocator, cmd: []const u8) !?[]u8 {
// Copy in the full path // Copy in the full path
mem.copy(u8, &path_buf, search_path); mem.copy(u8, &path_buf, search_path);
path_buf[search_path.len] = '/'; path_buf[search_path.len] = std.fs.path.sep;
mem.copy(u8, path_buf[search_path.len + 1 ..], cmd); mem.copy(u8, path_buf[search_path.len + 1 ..], cmd);
path_buf[path_len] = 0; path_buf[path_len] = 0;
const full_path = path_buf[0..path_len :0]; const full_path = path_buf[0..path_len :0];
@ -276,10 +283,12 @@ fn isExecutable(mode: std.fs.File.Mode) bool {
return mode & 0o0111 != 0; return mode & 0o0111 != 0;
} }
test "expandPath: env" { // `hostname` is present on both *nix and windows
const path = (try expandPath(testing.allocator, "env")).?; test "expandPath: hostname" {
const executable = if (builtin.os.tag == .windows) "hostname.exe" else "hostname";
const path = (try expandPath(testing.allocator, executable)).?;
defer testing.allocator.free(path); defer testing.allocator.free(path);
try testing.expect(path.len > 0); try testing.expect(path.len > executable.len);
} }
test "expandPath: does not exist" { test "expandPath: does not exist" {

View File

@ -61,8 +61,9 @@ pub fn name(self: *TempDir) []const u8 {
} }
/// Finish with the temporary directory. This deletes all contents in the /// Finish with the temporary directory. This deletes all contents in the
/// directory. This is safe to call multiple times. /// directory.
pub fn deinit(self: *TempDir) void { pub fn deinit(self: *TempDir) void {
self.dir.close();
self.parent.deleteTree(self.name()) catch |err| self.parent.deleteTree(self.name()) catch |err|
log.err("error deleting temp dir err={}", .{err}); log.err("error deleting temp dir err={}", .{err});
} }
@ -78,13 +79,14 @@ const b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345
test { test {
var td = try init(); var td = try init();
defer td.deinit(); errdefer td.deinit();
const nameval = td.name(); const nameval = td.name();
try testing.expect(nameval.len > 0); try testing.expect(nameval.len > 0);
// Can open a new handle to it proves it exists. // Can open a new handle to it proves it exists.
_ = try td.parent.openDir(nameval, .{}); var dir = try td.parent.openDir(nameval, .{});
dir.close();
// Should be deleted after we deinit // Should be deleted after we deinit
td.deinit(); td.deinit();

View File

@ -53,6 +53,20 @@ pub fn fixMaxFiles() void {
/// Return the recommended path for temporary files. /// Return the recommended path for temporary files.
pub fn tmpDir() ?[]const u8 { pub fn tmpDir() ?[]const u8 {
if (builtin.os.tag == .windows) {
// TODO: what is a good fallback path on windows?
const v = std.os.getenvW(std.unicode.utf8ToUtf16LeStringLiteral("TMP")) orelse return null;
// MAX_PATH is very likely sufficient, but it's theoretically possible for someone to
// configure their os to allow paths as big as std.os.windows.PATH_MAX_WIDE, which is MUCH
// larger. Even if they did that, though, it's very unlikey that their Temp dir will use
// such a long path. We can switch if we see any issues, though it seems fairly unlikely.
var buf = [_]u8{0} ** std.os.windows.MAX_PATH;
const len = std.unicode.utf16leToUtf8(buf[0..], v[0..v.len]) catch |e| {
log.warn("failed to convert temp dir path from windows string: {}", .{e});
return null;
};
return buf[0..len];
}
if (std.os.getenv("TMPDIR")) |v| return v; if (std.os.getenv("TMPDIR")) |v| return v;
if (std.os.getenv("TMP")) |v| return v; if (std.os.getenv("TMP")) |v| return v;
return "/tmp"; return "/tmp";

View File

@ -84,8 +84,7 @@ fn homeWindows(buf: []u8) !?[]u8 {
const fba = fba_instance.allocator(); const fba = fba_instance.allocator();
const drive = std.process.getEnvVarOwned(fba, "HOMEDRIVE") catch |err| switch (err) { const drive = std.process.getEnvVarOwned(fba, "HOMEDRIVE") catch |err| switch (err) {
error.OutOfMemory => return Error.BufferTooSmall, error.OutOfMemory => return Error.BufferTooSmall,
error.InvalidUtf8, error.InvalidUtf8, error.EnvironmentVariableNotFound => return null,
error.EnvironmentVariableNotFound => return null,
}; };
// could shift the contents if this ever happens // could shift the contents if this ever happens
if (drive.ptr != buf.ptr) @panic("codebug"); if (drive.ptr != buf.ptr) @panic("codebug");
@ -98,8 +97,7 @@ fn homeWindows(buf: []u8) !?[]u8 {
const fba = fba_instance.allocator(); const fba = fba_instance.allocator();
const homepath = std.process.getEnvVarOwned(fba, "HOMEPATH") catch |err| switch (err) { const homepath = std.process.getEnvVarOwned(fba, "HOMEPATH") catch |err| switch (err) {
error.OutOfMemory => return Error.BufferTooSmall, error.OutOfMemory => return Error.BufferTooSmall,
error.InvalidUtf8, error.InvalidUtf8, error.EnvironmentVariableNotFound => return null,
error.EnvironmentVariableNotFound => return null,
}; };
// could shift the contents if this ever happens // could shift the contents if this ever happens
if (homepath.ptr != path_buf.ptr) @panic("codebug"); if (homepath.ptr != path_buf.ptr) @panic("codebug");