mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-24 05:40:15 +00:00
os: use GetTempPathW for allocTmpDir on Windows
`allocTmpDir` previously read `%TMP%` via `getenvW` and returned `null` if the variable wasn't set, requiring each caller to to deal with the nullable. Unfortunately, there isn't a platform-neutral default value that makes sense for those cases (i.e. `/tmp` is POSIX-y). We now use `GetTempPathW` on Windows, which is the official way to get this directory: `TMP` → `TEMP` → `USERPROFILE` → `GetWindowsDirectoryW`. With a real system call behind it, the function no longer needs to be nullable: the only remaining failure modes are OOM (propagated) and the syscall itself failing or returning data we can't decode. In those later cases, we use `C:\Windows\Temp` as a fallback, similar to how we use `/tmp` in the POSIX case. The Windows path always allocates so it still must be paired with `freeTmpDir`, which matches the existing contract.
This commit is contained in:
@@ -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, .{});
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user