diff --git a/src/os/TempDir.zig b/src/os/TempDir.zig index 0bc398d94..c3d344d9d 100644 --- a/src/os/TempDir.zig +++ b/src/os/TempDir.zig @@ -28,7 +28,7 @@ pub fn init() !TempDir { const dir = dir: { const cwd = std.fs.cwd(); - const tmp_dir = file.allocTmpDir(std.heap.page_allocator) orelse break :dir cwd; + const tmp_dir = try file.allocTmpDir(std.heap.page_allocator); defer file.freeTmpDir(std.heap.page_allocator, tmp_dir); break :dir try cwd.openDir(tmp_dir, .{}); }; diff --git a/src/os/file.zig b/src/os/file.zig index d5241f21d..6007ae495 100644 --- a/src/os/file.zig +++ b/src/os/file.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const posix = std.posix; +const windows = @import("windows.zig"); const log = std.log.scoped(.os); @@ -57,27 +58,38 @@ pub fn restoreMaxFiles(lim: rlimit) void { /// path separator is stripped so callers can safely join with their /// own separator (e.g. `"{tmp}/{name}"`). /// -/// This may not actually allocate memory; use `freeTmpDir` to -/// properly free the memory when applicable. -pub fn allocTmpDir(allocator: std.mem.Allocator) ?[]const u8 { +/// On Windows this calls `GetTempPathW` and allocates a UTF-8 copy +/// (or duplicates a hard-fallback string if the syscall fails). On +/// POSIX this returns `$TMPDIR`/`$TMP` (or `"/tmp"` as a fallback) +/// without allocating. Always pair with `freeTmpDir` to release any +/// allocation. +pub fn allocTmpDir(allocator: std.mem.Allocator) std.mem.Allocator.Error![]const u8 { if (builtin.os.tag == .windows) { - // TODO: what is a good fallback path on windows? - const v = std.process.getenvW(std.unicode.utf8ToUtf16LeStringLiteral("TMP")) orelse return null; - return std.unicode.utf16LeToUtf8Alloc(allocator, v) catch |e| { - log.warn("failed to convert temp dir path from windows string: {}", .{e}); - return null; - }; + // GetTempPathW guarantees the result fits in MAX_PATH+1. + var buf: [windows.MAX_PATH + 1:0]u16 = undefined; + const len = windows.exp.kernel32.GetTempPathW(buf.len, &buf); + if (len > 0) { + // Trim the UTF-16 string before encoding as UT8-8 so that the + // returned slice's length matches its underlying allocation. + const trimmed = std.mem.trimEnd(u16, buf[0..len], &.{std.fs.path.sep}); + if (std.unicode.utf16LeToUtf8Alloc(allocator, trimmed)) |utf8| { + return utf8; + } else |e| switch (e) { + error.OutOfMemory => return error.OutOfMemory, + else => log.warn("failed to convert temp dir path from windows string: {}", .{e}), + } + } + return allocator.dupe(u8, "C:\\Windows\\Temp"); } const tmpdir = posix.getenv("TMPDIR") orelse posix.getenv("TMP") orelse return "/tmp"; return std.mem.trimEnd(u8, tmpdir, &.{std.fs.path.sep}); } -/// Free a path returned by tmpDir if it allocated memory. -/// This is a "no-op" for all platforms except windows. +/// Free a path returned by `allocTmpDir` if it allocated memory. +/// This is a no-op on POSIX. pub fn freeTmpDir(allocator: std.mem.Allocator, dir: []const u8) void { - if (builtin.os.tag == .windows) { - allocator.free(dir); - } + if (builtin.os.tag != .windows) return; + allocator.free(dir); } const random_basename_bytes = 16; @@ -109,7 +121,7 @@ pub fn randomTmpPath( allocator: std.mem.Allocator, prefix: []const u8, ) std.mem.Allocator.Error![]u8 { - const tmp_dir = allocTmpDir(allocator) orelse "/tmp"; + const tmp_dir = try allocTmpDir(allocator); defer freeTmpDir(allocator, tmp_dir); var name_buf: [random_basename_len]u8 = undefined; const basename = randomBasename(&name_buf) catch unreachable; diff --git a/src/os/windows.zig b/src/os/windows.zig index e92a54537..6e452fb73 100644 --- a/src/os/windows.zig +++ b/src/os/windows.zig @@ -18,6 +18,7 @@ pub const HANDLE = windows.HANDLE; pub const HANDLE_FLAG_INHERIT = windows.HANDLE_FLAG_INHERIT; pub const INFINITE = windows.INFINITE; pub const INVALID_HANDLE_VALUE = windows.INVALID_HANDLE_VALUE; +pub const MAX_PATH = windows.MAX_PATH; pub const OPEN_EXISTING = windows.OPEN_EXISTING; pub const PIPE_ACCESS_OUTBOUND = windows.PIPE_ACCESS_OUTBOUND; pub const PIPE_TYPE_BYTE = windows.PIPE_TYPE_BYTE; @@ -104,6 +105,11 @@ pub const exp = struct { lpBuffer: windows.LPSTR, nSize: *windows.DWORD, ) callconv(.winapi) windows.BOOL; + /// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw + pub extern "kernel32" fn GetTempPathW( + nBufferLength: windows.DWORD, + lpBuffer: windows.LPWSTR, + ) callconv(.winapi) windows.DWORD; }; pub const PROC_THREAD_ATTRIBUTE_NUMBER = 0x0000FFFF; diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index 1397401ff..8243a6323 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -330,17 +330,16 @@ pub const LoadingImage = struct { fn isPathInTempDir(path: []const u8) bool { if (std.mem.startsWith(u8, path, "/tmp")) return true; if (std.mem.startsWith(u8, path, "/dev/shm")) return true; - if (temp_dir.allocTmpDir(std.heap.page_allocator)) |dir| { - defer temp_dir.freeTmpDir(std.heap.page_allocator, dir); - if (std.mem.startsWith(u8, path, dir)) return true; + const dir = temp_dir.allocTmpDir(std.heap.page_allocator) catch return false; + defer temp_dir.freeTmpDir(std.heap.page_allocator, dir); + if (std.mem.startsWith(u8, path, dir)) return true; - // The temporary dir is sometimes a symlink. On macOS for - // example /tmp is /private/var/... - var buf: [std.fs.max_path_bytes]u8 = undefined; - if (posix.realpath(dir, &buf)) |real_dir| { - if (std.mem.startsWith(u8, path, real_dir)) return true; - } else |_| {} - } + // The temporary dir is sometimes a symlink. On macOS for + // example /tmp is /private/var/... + var buf: [std.fs.max_path_bytes]u8 = undefined; + if (posix.realpath(dir, &buf)) |real_dir| { + if (std.mem.startsWith(u8, path, real_dir)) return true; + } else |_| {} return false; }