Files
ghostty/src/config/file_load.zig
Qwerasd 6d5b4a3426 perf: replace std.debug.assert with inlined version
See doc comment in `quirks.zig` for reasoning
2025-11-17 12:13:56 -07:00

167 lines
5.3 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 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 {
return try internal_os.xdg.config(
alloc,
.{ .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 {
return try internal_os.xdg.config(
alloc,
.{ .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(xdg_path)) |f| {
f.close();
return xdg_path;
} else |_| {}
// Try the legacy path
errdefer alloc.free(xdg_path);
const legacy_xdg_path = try legacyDefaultXdgPath(alloc);
if (open(legacy_xdg_path)) |f| {
f.close();
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(app_support_path)) |f| {
f.close();
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(legacy_app_support_path)) |f| {
f.close();
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(app_support_path) catch {
// Try the XDG path if it exists
const xdg_path = try preferredXdgPath(alloc);
const xdg_file = open(xdg_path) catch {
// If neither file exists, use app support
alloc.free(xdg_path);
return app_support_path;
};
xdg_file.close();
alloc.free(app_support_path);
return xdg_path;
};
app_support_file.close();
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(path: []const u8) OpenFileError!std.fs.File {
assert(std.fs.path.isAbsolute(path));
var file = std.fs.openFileAbsolute(
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();
const stat = file.stat() 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;
}