mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-05 15:18:40 +00:00
This removes use of global state from the crash reporting functionality (everything in src/crash). This particularly ensures that there are no races on the system environment during the execution of the initialization thread that would possibly cause crashes, particularly in any (albeit unsupported) 3rd party integrations of libghostty-internal. Ultimately, this pushes any coupling of I/O and environment to places that would more correctly interface with global state, such as the same-thread global.init, and the crash report CLI. Note that similar de-coupling actions have been taken on XDG and home directory functionality, pushing their coupling points up the stack in a similar way.
177 lines
5.8 KiB
Zig
177 lines
5.8 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const assert = @import("../quirks.zig").inlineAssert;
|
|
const Allocator = std.mem.Allocator;
|
|
const internal_os = @import("../os/main.zig");
|
|
const global = @import("../global.zig");
|
|
|
|
const log = std.log.scoped(.config);
|
|
|
|
/// Default path for the XDG home configuration file. Returned value
|
|
/// must be freed by the caller.
|
|
pub fn defaultXdgPath(alloc: Allocator) ![]const u8 {
|
|
var environ_map = try global.environMap();
|
|
defer environ_map.deinit();
|
|
return try internal_os.xdg.config(
|
|
global.io(),
|
|
alloc,
|
|
&environ_map,
|
|
.{ .subdir = "ghostty/config.ghostty" },
|
|
);
|
|
}
|
|
|
|
/// Ghostty <1.3.0 default path for the XDG home configuration file.
|
|
/// Returned value must be freed by the caller.
|
|
pub fn legacyDefaultXdgPath(alloc: Allocator) ![]const u8 {
|
|
var environ_map = try global.environMap();
|
|
defer environ_map.deinit();
|
|
return try internal_os.xdg.config(
|
|
global.io(),
|
|
alloc,
|
|
&environ_map,
|
|
.{ .subdir = "ghostty/config" },
|
|
);
|
|
}
|
|
|
|
/// Preferred default path for the XDG home configuration file.
|
|
/// Returned value must be freed by the caller.
|
|
pub fn preferredXdgPath(alloc: Allocator) ![]const u8 {
|
|
// If the XDG path exists, use that.
|
|
const xdg_path = try defaultXdgPath(alloc);
|
|
if (open(global.io(), xdg_path)) |f| {
|
|
f.close(global.io());
|
|
return xdg_path;
|
|
} else |_| {}
|
|
|
|
// Try the legacy path
|
|
errdefer alloc.free(xdg_path);
|
|
const legacy_xdg_path = try legacyDefaultXdgPath(alloc);
|
|
if (open(global.io(), legacy_xdg_path)) |f| {
|
|
f.close(global.io());
|
|
alloc.free(xdg_path);
|
|
return legacy_xdg_path;
|
|
} else |_| {}
|
|
|
|
// Legacy path and XDG path both don't exist. Return the
|
|
// new one.
|
|
alloc.free(legacy_xdg_path);
|
|
return xdg_path;
|
|
}
|
|
|
|
/// Default path for the macOS Application Support configuration file.
|
|
/// Returned value must be freed by the caller.
|
|
pub fn defaultAppSupportPath(alloc: Allocator) ![]const u8 {
|
|
return try internal_os.macos.appSupportDir(alloc, "config.ghostty");
|
|
}
|
|
|
|
/// Ghostty <1.3.0 default path for the macOS Application Support
|
|
/// configuration file. Returned value must be freed by the caller.
|
|
pub fn legacyDefaultAppSupportPath(alloc: Allocator) ![]const u8 {
|
|
return try internal_os.macos.appSupportDir(alloc, "config");
|
|
}
|
|
|
|
/// Preferred default path for the macOS Application Support configuration file.
|
|
/// Returned value must be freed by the caller.
|
|
pub fn preferredAppSupportPath(alloc: Allocator) ![]const u8 {
|
|
// If the app support path exists, use that.
|
|
const app_support_path = try defaultAppSupportPath(alloc);
|
|
if (open(global.io(), app_support_path)) |f| {
|
|
f.close(global.io());
|
|
return app_support_path;
|
|
} else |_| {}
|
|
|
|
// Try the legacy path
|
|
errdefer alloc.free(app_support_path);
|
|
const legacy_app_support_path = try legacyDefaultAppSupportPath(alloc);
|
|
if (open(global.io(), legacy_app_support_path)) |f| {
|
|
f.close(global.io());
|
|
alloc.free(app_support_path);
|
|
return legacy_app_support_path;
|
|
} else |_| {}
|
|
|
|
// Legacy path and app support path both don't exist. Return the
|
|
// new one.
|
|
alloc.free(legacy_app_support_path);
|
|
return app_support_path;
|
|
}
|
|
|
|
/// Returns the path to the preferred default configuration file.
|
|
/// This is the file where users should place their configuration.
|
|
///
|
|
/// This doesn't create or populate the file with any default
|
|
/// contents; downstream callers must handle this.
|
|
///
|
|
/// The returned value must be freed by the caller.
|
|
pub fn preferredDefaultFilePath(alloc: Allocator) ![]const u8 {
|
|
switch (builtin.os.tag) {
|
|
.macos => {
|
|
// macOS prefers the Application Support directory
|
|
// if it exists.
|
|
const app_support_path = try preferredAppSupportPath(alloc);
|
|
const app_support_file = open(global.io(), app_support_path) catch {
|
|
// Try the XDG path if it exists
|
|
const xdg_path = try preferredXdgPath(alloc);
|
|
const xdg_file = open(global.io(), xdg_path) catch {
|
|
// If neither file exists, use app support
|
|
alloc.free(xdg_path);
|
|
return app_support_path;
|
|
};
|
|
xdg_file.close(global.io());
|
|
alloc.free(app_support_path);
|
|
return xdg_path;
|
|
};
|
|
app_support_file.close(global.io());
|
|
return app_support_path;
|
|
},
|
|
|
|
// All other platforms use XDG only
|
|
else => return try preferredXdgPath(alloc),
|
|
}
|
|
}
|
|
|
|
const OpenFileError = error{
|
|
FileNotFound,
|
|
FileIsEmpty,
|
|
FileOpenFailed,
|
|
NotAFile,
|
|
};
|
|
|
|
/// Opens the file at the given path and returns the file handle
|
|
/// if it exists and is non-empty. This also constrains the possible
|
|
/// errors to a smaller set that we can explicitly handle.
|
|
pub fn open(io: std.Io, path: []const u8) OpenFileError!std.Io.File {
|
|
assert(std.fs.path.isAbsolute(path));
|
|
|
|
var file = std.Io.Dir.openFileAbsolute(
|
|
io,
|
|
path,
|
|
.{},
|
|
) catch |err| switch (err) {
|
|
error.FileNotFound => return OpenFileError.FileNotFound,
|
|
else => {
|
|
log.warn("unexpected file open error path={s} err={}", .{
|
|
path,
|
|
err,
|
|
});
|
|
return OpenFileError.FileOpenFailed;
|
|
},
|
|
};
|
|
errdefer file.close(io);
|
|
|
|
const stat = file.stat(io) catch |err| {
|
|
log.warn("error getting file stat path={s} err={}", .{
|
|
path,
|
|
err,
|
|
});
|
|
return OpenFileError.FileOpenFailed;
|
|
};
|
|
switch (stat.kind) {
|
|
.file => {},
|
|
else => return OpenFileError.NotAFile,
|
|
}
|
|
|
|
if (stat.size == 0) return OpenFileError.FileIsEmpty;
|
|
|
|
return file;
|
|
}
|