mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-02 21:59:05 +00:00
Update to Zig 0.16.0
This commit represents the majority of the work necessary to upgrade Ghostty to use Zig 0.16.0. Key parts: * In addition to its previous responsibilities, the global state now houses state for global I/O implementations and the process environment. It is now also utilized in the main application along with the C library. Where necessary, global state is isolated from key parts of the implementation (e.g., in libghostty subsystems), and it's expected that this list will grow. * We currently manage our own C translation layer where necessary. In these cases, cImport has been removed in favor of the new external translate-c package. Due to fixes that have needed be made to properly translate the dependencies that were swapped out, as mentioned, we have had to backport fixes from the current translate-c package (and the upstream Arocc dependency). We will host this ourselves until Zig 0.17.0 is released with these fixes. * Where necessary (only a small number of cases), some stdlib code from 0.15.2 (and even from 0.17.0) has been taken, adopted, and vendored in lib/compat. Co-authored-by: Leah Amelia Chen <hi@pluie.me>
This commit is contained in:
@@ -6,29 +6,31 @@
|
||||
//! Setting either env var to an empty string disables paging.
|
||||
//! If stdout is not a TTY, writes go directly to stdout.
|
||||
const Pager = @This();
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
/// The pager child process, if one was spawned.
|
||||
child: ?std.process.Child = null,
|
||||
|
||||
/// The buffered file writer used for both the pager pipe and direct
|
||||
/// stdout paths.
|
||||
file_writer: std.fs.File.Writer = undefined,
|
||||
file_writer: std.Io.File.Writer = undefined,
|
||||
|
||||
/// Initialize the pager. If stdout is a TTY, this spawns the pager
|
||||
/// process. Otherwise, output goes directly to stdout.
|
||||
pub fn init(alloc: Allocator) Pager {
|
||||
return .{ .child = initPager(alloc) };
|
||||
pub fn init() Pager {
|
||||
return .{ .child = initPager() };
|
||||
}
|
||||
|
||||
/// Writes to the pager process if available; otherwise, stdout.
|
||||
pub fn writer(self: *Pager, buffer: []u8) *std.Io.Writer {
|
||||
if (self.child) |child| {
|
||||
self.file_writer = child.stdin.?.writer(buffer);
|
||||
self.file_writer = child.stdin.?.writer(global.io(), buffer);
|
||||
} else {
|
||||
self.file_writer = std.fs.File.stdout().writer(buffer);
|
||||
self.file_writer = std.Io.File.stdout().writer(global.io(), buffer);
|
||||
}
|
||||
return &self.file_writer.interface;
|
||||
}
|
||||
@@ -40,45 +42,44 @@ pub fn deinit(self: *Pager) void {
|
||||
// pager sees EOF, then wait for it to exit.
|
||||
self.file_writer.interface.flush() catch {};
|
||||
if (child.stdin) |stdin| {
|
||||
stdin.close();
|
||||
stdin.close(global.io());
|
||||
child.stdin = null;
|
||||
}
|
||||
_ = child.wait() catch {};
|
||||
_ = child.wait(global.io()) catch {};
|
||||
}
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn initPager(alloc: Allocator) ?std.process.Child {
|
||||
const stdout_file: std.fs.File = .stdout();
|
||||
if (!stdout_file.isTty()) return null;
|
||||
fn initPager() ?std.process.Child {
|
||||
const stdout_file: std.Io.File = .stdout();
|
||||
if (!(stdout_file.isTty(global.io()) catch return null)) return null;
|
||||
|
||||
var env = global.environMap() catch return null;
|
||||
defer env.deinit();
|
||||
|
||||
// Resolve the pager command: $GHOSTTY_PAGER > $PAGER > `less`.
|
||||
// An empty value for either env var disables paging.
|
||||
const ghostty_var = internal_os.getenv(alloc, "GHOSTTY_PAGER") catch null;
|
||||
defer if (ghostty_var) |v| v.deinit(alloc);
|
||||
const pager_var = internal_os.getenv(alloc, "PAGER") catch null;
|
||||
defer if (pager_var) |v| v.deinit(alloc);
|
||||
|
||||
const ghostty_var = env.get("GHOSTTY_PAGER");
|
||||
const pager_var = env.get("PAGER");
|
||||
const cmd: ?[]const u8 = cmd: {
|
||||
if (ghostty_var) |v| break :cmd if (v.value.len > 0) v.value else null;
|
||||
if (pager_var) |v| break :cmd if (v.value.len > 0) v.value else null;
|
||||
if (ghostty_var) |v| break :cmd if (v.len > 0) v else null;
|
||||
if (pager_var) |v| break :cmd if (v.len > 0) v else null;
|
||||
break :cmd "less";
|
||||
};
|
||||
|
||||
if (cmd == null) return null;
|
||||
|
||||
var child: std.process.Child = .init(&.{cmd.?}, alloc);
|
||||
child.stdin_behavior = .Pipe;
|
||||
child.stdout_behavior = .Inherit;
|
||||
child.stderr_behavior = .Inherit;
|
||||
|
||||
child.spawn() catch return null;
|
||||
return child;
|
||||
return std.process.spawn(global.io(), .{
|
||||
.argv = &.{cmd.?},
|
||||
.stdin = .pipe,
|
||||
.stdout = .inherit,
|
||||
.stderr = .inherit,
|
||||
}) catch null;
|
||||
}
|
||||
|
||||
test "pager: non-tty" {
|
||||
var pager: Pager = .init(std.testing.allocator);
|
||||
var pager: Pager = .init();
|
||||
defer pager.deinit();
|
||||
try std.testing.expect(pager.child == null);
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ pub const DetectError = error{
|
||||
};
|
||||
|
||||
/// Detect the action from CLI args.
|
||||
pub fn detectArgs(comptime E: type, alloc: Allocator) !?E {
|
||||
var iter = try std.process.argsWithAllocator(alloc);
|
||||
pub fn detectArgs(comptime E: type, alloc: Allocator, args: std.process.Args) !?E {
|
||||
var iter = try args.iterateAllocator(alloc);
|
||||
defer iter.deinit();
|
||||
return try detectIter(E, &iter);
|
||||
}
|
||||
@@ -83,7 +83,7 @@ test "detect direct match" {
|
||||
const alloc = testing.allocator;
|
||||
const Enum = enum { foo, bar, baz };
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+foo",
|
||||
);
|
||||
@@ -97,7 +97,7 @@ test "detect invalid match" {
|
||||
const alloc = testing.allocator;
|
||||
const Enum = enum { foo, bar, baz };
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+invalid",
|
||||
);
|
||||
@@ -113,7 +113,7 @@ test "detect multiple actions" {
|
||||
const alloc = testing.allocator;
|
||||
const Enum = enum { foo, bar, baz };
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+foo +bar",
|
||||
);
|
||||
@@ -129,7 +129,7 @@ test "detect no match" {
|
||||
const alloc = testing.allocator;
|
||||
const Enum = enum { foo, bar, baz };
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--some-flag",
|
||||
);
|
||||
@@ -154,7 +154,7 @@ test "detect special case action" {
|
||||
};
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--special +bar",
|
||||
);
|
||||
@@ -164,7 +164,7 @@ test "detect special case action" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+bar --special",
|
||||
);
|
||||
@@ -174,7 +174,7 @@ test "detect special case action" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+bar",
|
||||
);
|
||||
@@ -200,7 +200,7 @@ test "detect special case fallback" {
|
||||
};
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--special",
|
||||
);
|
||||
@@ -210,7 +210,7 @@ test "detect special case fallback" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+bar --special",
|
||||
);
|
||||
@@ -220,7 +220,7 @@ test "detect special case fallback" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--special +bar",
|
||||
);
|
||||
@@ -246,7 +246,7 @@ test "detect special case abort_if_no_action" {
|
||||
};
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"-e",
|
||||
);
|
||||
@@ -256,7 +256,7 @@ test "detect special case abort_if_no_action" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+foo -e",
|
||||
);
|
||||
@@ -266,7 +266,7 @@ test "detect special case abort_if_no_action" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"-e +bar",
|
||||
);
|
||||
|
||||
@@ -489,18 +489,13 @@ pub fn parseTaggedUnion(comptime T: type, alloc: Allocator, v: []const u8) !T {
|
||||
|
||||
// We need to create a struct that looks like this union field.
|
||||
// This lets us use parseIntoField as if its a dedicated struct.
|
||||
const Target = @Type(.{ .@"struct" = .{
|
||||
.layout = .auto,
|
||||
.fields = &.{.{
|
||||
.name = field.name,
|
||||
.type = field.type,
|
||||
.default_value_ptr = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(field.type),
|
||||
}},
|
||||
.decls = &.{},
|
||||
.is_tuple = false,
|
||||
} });
|
||||
const Target = @Struct(
|
||||
.auto,
|
||||
null,
|
||||
&.{field.name},
|
||||
&.{field.type},
|
||||
&.{.{ .@"align" = @alignOf(field.type) }},
|
||||
);
|
||||
|
||||
// Parse the value into the struct
|
||||
var t: Target = undefined;
|
||||
@@ -677,7 +672,7 @@ test "parse: simple" {
|
||||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--a=42 --b --b-f=false",
|
||||
);
|
||||
@@ -689,7 +684,7 @@ test "parse: simple" {
|
||||
try testing.expect(!data.@"b-f");
|
||||
|
||||
// Reparsing works
|
||||
var iter2 = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter2 = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--a=84",
|
||||
);
|
||||
@@ -711,7 +706,7 @@ test "parse: quoted value" {
|
||||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--a=\"42\" --b=\"hello!\"",
|
||||
);
|
||||
@@ -731,7 +726,7 @@ test "parse: empty value resets to default" {
|
||||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--a= --b=",
|
||||
);
|
||||
@@ -750,7 +745,7 @@ test "parse: positional arguments are invalid" {
|
||||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--a=84 what",
|
||||
);
|
||||
@@ -774,7 +769,7 @@ test "parse: diagnostic tracking" {
|
||||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--what --a=42",
|
||||
);
|
||||
@@ -858,7 +853,7 @@ test "parse: compatibility handler" {
|
||||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--a=yuh",
|
||||
);
|
||||
@@ -884,7 +879,7 @@ test "parse: compatibility renamed" {
|
||||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--old=true --b=true",
|
||||
);
|
||||
@@ -1363,8 +1358,11 @@ pub fn ArgsIterator(comptime Iterator: type) type {
|
||||
}
|
||||
|
||||
/// Create an args iterator for the process args. This will skip argv0.
|
||||
pub fn argsIterator(alloc_gpa: Allocator) internal_os.args.ArgIterator.InitError!ArgsIterator(internal_os.args.ArgIterator) {
|
||||
var iter = try internal_os.args.iterator(alloc_gpa);
|
||||
pub fn argsIterator(
|
||||
alloc_gpa: Allocator,
|
||||
args: std.process.Args,
|
||||
) std.process.Args.Iterator.InitError!ArgsIterator(std.process.Args.Iterator) {
|
||||
var iter: std.process.Args.Iterator = try .initAllocator(args, alloc_gpa);
|
||||
errdefer iter.deinit();
|
||||
_ = iter.next(); // skip argv0
|
||||
return .{ .iterator = iter };
|
||||
@@ -1373,7 +1371,7 @@ pub fn argsIterator(alloc_gpa: Allocator) internal_os.args.ArgIterator.InitError
|
||||
test "ArgsIterator" {
|
||||
const testing = std.testing;
|
||||
|
||||
const child = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
const child = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
testing.allocator,
|
||||
"--what +list-things --a=42",
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ const builtin = @import("builtin");
|
||||
const args = @import("args.zig");
|
||||
const Action = @import("ghostty.zig").Action;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const global = @import("../global.zig");
|
||||
const vaxis = @import("vaxis");
|
||||
|
||||
const framedata = @import("framedata").compressed;
|
||||
@@ -173,6 +174,9 @@ const Boo = struct {
|
||||
|
||||
/// The `boo` command is used to display the animation from the Ghostty website in the terminal
|
||||
pub fn run(gpa: Allocator) !u8 {
|
||||
var env_map = try global.environMap();
|
||||
defer env_map.deinit();
|
||||
|
||||
// Disable on non-desktop systems.
|
||||
switch (builtin.os.tag) {
|
||||
.windows, .macos, .linux, .freebsd => {},
|
||||
@@ -183,7 +187,7 @@ pub fn run(gpa: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(gpa);
|
||||
var iter = try args.argsIterator(gpa, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, gpa, &opts, &iter);
|
||||
}
|
||||
@@ -194,7 +198,7 @@ pub fn run(gpa: Allocator) !u8 {
|
||||
gpa.free(decompressed_data);
|
||||
}
|
||||
|
||||
var app = try vxfw.App.init(gpa);
|
||||
var app = try vxfw.App.init(global.io(), gpa, &env_map, &.{});
|
||||
defer app.deinit();
|
||||
|
||||
var boo: Boo = undefined;
|
||||
|
||||
@@ -4,6 +4,7 @@ const args = @import("args.zig");
|
||||
const Action = @import("ghostty.zig").Action;
|
||||
const Config = @import("../config.zig").Config;
|
||||
const crash = @import("../crash/main.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
pub fn deinit(self: Options) void {
|
||||
@@ -33,14 +34,14 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(alloc_gpa);
|
||||
var iter = try args.argsIterator(alloc_gpa, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, alloc_gpa, &opts, &iter);
|
||||
}
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&buffer);
|
||||
var stdout_file: std.Io.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(global.io(), &buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
const result = runInner(alloc, &stdout_file, stdout);
|
||||
@@ -50,7 +51,7 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
|
||||
fn runInner(
|
||||
alloc: Allocator,
|
||||
stdout_file: *std.fs.File,
|
||||
stdout_file: *std.Io.File,
|
||||
stdout: *std.Io.Writer,
|
||||
) !u8 {
|
||||
const crash_dir = try crash.defaultDir(alloc);
|
||||
@@ -66,7 +67,7 @@ fn runInner(
|
||||
// If we have no reports, then we're done. If we have a tty then we
|
||||
// print a message, otherwise we do nothing.
|
||||
if (reports.items.len == 0) {
|
||||
if (std.posix.isatty(stdout_file.handle)) {
|
||||
if (try stdout_file.isTty(global.io())) {
|
||||
try stdout.writeAll("No crash reports! 👻\n");
|
||||
}
|
||||
return 0;
|
||||
@@ -76,7 +77,7 @@ fn runInner(
|
||||
|
||||
for (reports.items) |report| {
|
||||
var buf: [128]u8 = undefined;
|
||||
const now = std.time.nanoTimestamp();
|
||||
const now = std.Io.Timestamp.now(global.io(), .real).toNanoseconds();
|
||||
const diff = now - report.mtime;
|
||||
const since = if (diff <= 0) "now" else s: {
|
||||
const d = Config.Duration{ .duration = @intCast(diff) };
|
||||
|
||||
@@ -93,7 +93,7 @@ pub const Location = union(enum) {
|
||||
/// and potentially in the future structure them differently.
|
||||
pub const DiagnosticList = struct {
|
||||
/// The list of diagnostics.
|
||||
list: std.ArrayListUnmanaged(Diagnostic) = .{},
|
||||
list: std.ArrayList(Diagnostic) = .empty,
|
||||
|
||||
/// Precomputed data for diagnostics. This is used specifically
|
||||
/// when we build libghostty so that we can precompute the messages
|
||||
@@ -111,7 +111,7 @@ pub const DiagnosticList = struct {
|
||||
};
|
||||
|
||||
const Precompute = if (precompute_enabled) struct {
|
||||
messages: std.ArrayListUnmanaged([:0]const u8) = .{},
|
||||
messages: std.ArrayList([:0]const u8) = .empty,
|
||||
|
||||
pub fn clone(
|
||||
self: *const Precompute,
|
||||
|
||||
@@ -7,6 +7,7 @@ const Action = @import("ghostty.zig").Action;
|
||||
const configpkg = @import("../config.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const Config = configpkg.Config;
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
pub fn deinit(self: Options) void {
|
||||
@@ -48,14 +49,14 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
// critical where setting up the defer cleanup is a problem.
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
var stderr_writer = std.Io.File.stderr().writer(global.io(), &buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
var opts: Options = .{};
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
@@ -67,6 +68,12 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
}
|
||||
|
||||
fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||
// We require libc because we want to use std.c.environ for envp
|
||||
// and not have to build that ourselves. We can remove this
|
||||
// limitation later but Ghostty already heavily requires libc
|
||||
// so this is not a big deal.
|
||||
comptime assert(builtin.link_libc);
|
||||
|
||||
// We load the configuration once because that will write our
|
||||
// default configuration files to disk. We don't use the config.
|
||||
var config = try Config.load(alloc);
|
||||
@@ -91,22 +98,19 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||
}
|
||||
|
||||
// Get our editor
|
||||
const get_env_: ?internal_os.GetEnvResult = env: {
|
||||
const editor = env: {
|
||||
// VISUAL vs. EDITOR: https://unix.stackexchange.com/questions/4859/visual-vs-editor-what-s-the-difference
|
||||
if (try internal_os.getenv(alloc, "VISUAL")) |v| {
|
||||
if (v.value.len > 0) break :env v;
|
||||
v.deinit(alloc);
|
||||
if (try global.environ().containsUnempty(alloc, "VISUAL")) {
|
||||
break :env try global.environ().getAlloc(alloc, "VISUAL");
|
||||
}
|
||||
|
||||
if (try internal_os.getenv(alloc, "EDITOR")) |v| {
|
||||
if (v.value.len > 0) break :env v;
|
||||
v.deinit(alloc);
|
||||
if (try global.environ().containsUnempty(alloc, "EDITOR")) {
|
||||
break :env try global.environ().getAlloc(alloc, "EDITOR");
|
||||
}
|
||||
|
||||
break :env null;
|
||||
break :env "";
|
||||
};
|
||||
defer if (get_env_) |v| v.deinit(alloc);
|
||||
const editor: []const u8 = if (get_env_) |v| v.value else "";
|
||||
defer alloc.free(editor);
|
||||
|
||||
// If we don't have `$EDITOR` set then we can't do anything
|
||||
// but we can still print a helpful message.
|
||||
@@ -136,8 +140,9 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Build the command
|
||||
const command = command: {
|
||||
var buffer: std.io.Writer.Allocating = .init(alloc);
|
||||
var buffer: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buffer.deinit();
|
||||
const writer = &buffer.writer;
|
||||
try writer.writeAll(editor);
|
||||
@@ -152,21 +157,14 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||
};
|
||||
defer alloc.free(command);
|
||||
|
||||
// We require libc because we want to use std.c.environ for envp
|
||||
// and not have to build that ourselves. We can remove this
|
||||
// limitation later but Ghostty already heavily requires libc
|
||||
// so this is not a big deal.
|
||||
comptime assert(builtin.link_libc);
|
||||
|
||||
const err = std.posix.execvpeZ(
|
||||
"/bin/sh",
|
||||
&.{ "/bin/sh", "-c", command },
|
||||
std.c.environ,
|
||||
);
|
||||
// Run/replace process (using execve)
|
||||
const argv = &.{ "/bin/sh", "-c", command };
|
||||
const envp = std.c.environ;
|
||||
const err = std.posix.errno(std.posix.system.execve(argv[0], @ptrCast(argv), envp));
|
||||
|
||||
// If we reached this point then exec failed.
|
||||
try stderr.print(
|
||||
\\Failed to execute the editor. Error code={}.
|
||||
\\Failed to execute the editor (E{s}).
|
||||
\\
|
||||
\\This is usually due to the executable path not existing, invalid
|
||||
\\permissions, or the shell environment not being set up
|
||||
@@ -175,6 +173,6 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||
\\Editor: {s}
|
||||
\\Path: {s}
|
||||
\\
|
||||
, .{ err, editor, path });
|
||||
, .{ @tagName(err), editor, path });
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ const Config = @import("../config/Config.zig");
|
||||
const ConfigKey = @import("../config/key.zig").Key;
|
||||
const KeybindAction = @import("../input/Binding.zig").Action;
|
||||
const Pager = @import("Pager.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// The config option to explain. For example:
|
||||
@@ -51,7 +52,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
var positional: ?[]const u8 = null;
|
||||
var no_pager: bool = false;
|
||||
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
defer if (option_name) |s| alloc.free(s);
|
||||
defer if (keybind_name) |s| alloc.free(s);
|
||||
@@ -75,9 +76,9 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
// respective lookup. A bare positional argument tries config
|
||||
// options first, then keybind actions as a fallback.
|
||||
const name = keybind_name orelse option_name orelse positional orelse {
|
||||
var stderr: std.fs.File = .stderr();
|
||||
var stderr: std.Io.File = .stderr();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stderr_writer = stderr.writer(&buffer);
|
||||
var stderr_writer = stderr.writer(global.io(), &buffer);
|
||||
try stderr_writer.interface.writeAll("Usage: ghostty +explain-config <option>\n");
|
||||
try stderr_writer.interface.writeAll(" ghostty +explain-config --option=<option>\n");
|
||||
try stderr_writer.interface.writeAll(" ghostty +explain-config --keybind=<action>\n");
|
||||
@@ -92,7 +93,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
else
|
||||
explainOption(name) orelse explainKeybind(name);
|
||||
|
||||
var pager: Pager = if (!no_pager) .init(alloc) else .{};
|
||||
var pager: Pager = if (!no_pager) .init() else .{};
|
||||
defer pager.deinit();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
const writer = pager.writer(&buffer);
|
||||
|
||||
@@ -22,6 +22,7 @@ const show_face = @import("show_face.zig");
|
||||
const boo = @import("boo.zig");
|
||||
const new_window = @import("new_window.zig");
|
||||
const toggle_quick_terminal = @import("toggle_quick_terminal.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
/// Special commands that can be invoked via CLI flags. These are all
|
||||
/// invoked by using `+<action>` as a CLI flag. The only exception is
|
||||
@@ -120,7 +121,10 @@ pub const Action = enum {
|
||||
|
||||
if (std.mem.eql(u8, field.name, @tagName(self))) {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
var stdout_writer = std.Io.File.stdout().writer(
|
||||
global.io(),
|
||||
&buffer,
|
||||
);
|
||||
const stdout = &stdout_writer.interface;
|
||||
const text = @field(help_strings.Action, field.name) ++ "\n";
|
||||
stdout.writeAll(text) catch |write_err| {
|
||||
@@ -213,7 +217,7 @@ test "parse action none" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--a=42 --b --b-f=false",
|
||||
);
|
||||
@@ -227,7 +231,7 @@ test "parse action version" {
|
||||
const alloc = testing.allocator;
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--a=42 --b --b-f=false --version",
|
||||
);
|
||||
@@ -237,7 +241,7 @@ test "parse action version" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--version --a=42 --b --b-f=false",
|
||||
);
|
||||
@@ -247,7 +251,7 @@ test "parse action version" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--c=84 --d --version --a=42 --b --b-f=false",
|
||||
);
|
||||
@@ -262,7 +266,7 @@ test "parse action plus" {
|
||||
const alloc = testing.allocator;
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--a=42 --b --b-f=false +version",
|
||||
);
|
||||
@@ -272,7 +276,7 @@ test "parse action plus" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+version --a=42 --b --b-f=false",
|
||||
);
|
||||
@@ -282,7 +286,7 @@ test "parse action plus" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--c=84 --d +version --a=42 --b --b-f=false",
|
||||
);
|
||||
@@ -297,7 +301,7 @@ test "parse action plus ignores -e" {
|
||||
const alloc = testing.allocator;
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"--a=42 -e +version",
|
||||
);
|
||||
@@ -307,7 +311,7 @@ test "parse action plus ignores -e" {
|
||||
}
|
||||
|
||||
{
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(
|
||||
alloc,
|
||||
"+list-fonts --a=42 -e +version",
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const args = @import("args.zig");
|
||||
const Action = @import("ghostty.zig").Action;
|
||||
const global = @import("../global.zig");
|
||||
|
||||
// Note that this options struct doesn't implement the `help` decl like other
|
||||
// actions. That is because the help command is special and wants to handle its
|
||||
@@ -25,13 +26,13 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
var buffer: [2048]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
var stdout_writer = std.Io.File.stdout().writer(global.io(), &buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
try stdout.writeAll(
|
||||
\\Usage: ghostty [+action] [options]
|
||||
|
||||
@@ -3,6 +3,7 @@ const args = @import("args.zig");
|
||||
const Action = @import("ghostty.zig").Action;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const helpgen_actions = @import("../input/helpgen_actions.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// If `true`, print out documentation about the action associated with the
|
||||
@@ -32,14 +33,14 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
var stdout: std.fs.File = .stdout();
|
||||
var stdout: std.Io.File = .stdout();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
var stdout_writer = stdout.writer(global.io(), &buffer);
|
||||
try helpgen_actions.generate(
|
||||
&stdout_writer.interface,
|
||||
.plaintext,
|
||||
|
||||
@@ -6,6 +6,7 @@ const args = @import("args.zig");
|
||||
const x11_color = @import("../terminal/main.zig").x11_color;
|
||||
const vaxis = @import("vaxis");
|
||||
const tui = @import("tui.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
pub fn deinit(self: Options) void {
|
||||
@@ -34,7 +35,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
@@ -49,15 +50,14 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
}
|
||||
}.lessThan);
|
||||
|
||||
// Despite being under the posix namespace, this also works on Windows as of zig 0.13.0
|
||||
var stdout: std.fs.File = .stdout();
|
||||
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) {
|
||||
var stdout: std.Io.File = .stdout();
|
||||
if (tui.can_pretty_print and !opts.plain and try stdout.isTty(global.io())) {
|
||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
return prettyPrint(arena.allocator(), keys.items);
|
||||
} else {
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
var stdout_writer = stdout.writer(global.io(), &buffer);
|
||||
const writer = &stdout_writer.interface;
|
||||
for (keys.items) |name| {
|
||||
const rgb = x11_color.map.get(name).?;
|
||||
@@ -74,11 +74,14 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
}
|
||||
|
||||
fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
||||
var env_map = try global.environMap();
|
||||
defer env_map.deinit();
|
||||
|
||||
// Set up vaxis
|
||||
var buf: [1024]u8 = undefined;
|
||||
var tty = try vaxis.Tty.init(&buf);
|
||||
var tty = try vaxis.Tty.init(global.io(), &buf);
|
||||
defer tty.deinit();
|
||||
var vx = try vaxis.init(alloc, .{});
|
||||
var vx = try vaxis.init(global.io(), alloc, &env_map, .{});
|
||||
defer vx.deinit(alloc, tty.writer());
|
||||
|
||||
// We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an
|
||||
@@ -97,7 +100,7 @@ fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
||||
.y_pixel = 768,
|
||||
},
|
||||
|
||||
else => try vaxis.Tty.getWinsize(tty.fd),
|
||||
else => try tty.getWinsize(),
|
||||
};
|
||||
try vx.resize(alloc, tty.writer(), winsize);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const Action = @import("ghostty.zig").Action;
|
||||
const args = @import("args.zig");
|
||||
const font = @import("../font/main.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
const log = std.log.scoped(.list_fonts);
|
||||
|
||||
@@ -60,7 +61,7 @@ pub const Options = struct {
|
||||
/// is identical to the `font-family` set of Ghostty configuration values, so
|
||||
/// this can be used to debug why your desired font may not be loading.
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
return try runArgs(alloc, &iter);
|
||||
}
|
||||
@@ -78,7 +79,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
// Its possible to build Ghostty without font discovery!
|
||||
if (comptime font.Discover == void) {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
var stderr_writer = std.Io.File.stderr().writer(global.io(), &buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
try stderr.print(
|
||||
\\Ghostty was built without a font discovery mechanism. This is a compile-time
|
||||
@@ -92,7 +93,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
}
|
||||
|
||||
var buffer: [2048]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
var stdout_writer = std.Io.File.stdout().writer(global.io(), &buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
// We'll be putting our fonts into a list categorized by family
|
||||
@@ -133,7 +134,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
const gop = try map.getOrPut(family);
|
||||
if (!gop.found_existing) {
|
||||
try families.append(alloc, family);
|
||||
gop.value_ptr.* = .{};
|
||||
gop.value_ptr.* = .empty;
|
||||
}
|
||||
try gop.value_ptr.append(alloc, full_name);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ const vaxis = @import("vaxis");
|
||||
const input = @import("../input.zig");
|
||||
const tui = @import("tui.zig");
|
||||
const Binding = input.Binding;
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// If `true`, print out the default keybinds instead of the ones configured
|
||||
@@ -56,7 +57,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
@@ -65,11 +66,11 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
defer config.deinit();
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const stdout: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
const stdout: std.Io.File = .stdout();
|
||||
var stdout_writer = stdout.writer(global.io(), &buffer);
|
||||
const writer = &stdout_writer.interface;
|
||||
|
||||
if (tui.can_pretty_print and !opts.plain and stdout.isTty()) {
|
||||
if (tui.can_pretty_print and !opts.plain and try stdout.isTty(global.io())) {
|
||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
return prettyPrint(arena.allocator(), config.keybind);
|
||||
@@ -218,11 +219,14 @@ const ChordBinding = struct {
|
||||
};
|
||||
|
||||
fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
||||
var env_map = try global.environMap();
|
||||
defer env_map.deinit();
|
||||
|
||||
// Set up vaxis
|
||||
var buf: [1024]u8 = undefined;
|
||||
var tty = try vaxis.Tty.init(&buf);
|
||||
var tty = try vaxis.Tty.init(global.io(), &buf);
|
||||
defer tty.deinit();
|
||||
var vx = try vaxis.init(alloc, .{});
|
||||
var vx = try vaxis.init(global.io(), alloc, &env_map, .{});
|
||||
const writer = tty.writer();
|
||||
defer vx.deinit(alloc, writer);
|
||||
|
||||
@@ -242,7 +246,7 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
||||
.y_pixel = 768,
|
||||
},
|
||||
|
||||
else => try vaxis.Tty.getWinsize(tty.fd),
|
||||
else => try tty.getWinsize(),
|
||||
};
|
||||
try vx.resize(alloc, writer, winsize);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const Config = @import("../config/Config.zig");
|
||||
const configpkg = @import("../config.zig");
|
||||
const themepkg = @import("../config/theme.zig");
|
||||
const tui = @import("tui.zig");
|
||||
const global_state = &@import("../global.zig").state;
|
||||
const global = @import("../global.zig");
|
||||
|
||||
const vaxis = @import("vaxis");
|
||||
const zf = @import("zf");
|
||||
@@ -108,7 +108,7 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(gpa_alloc);
|
||||
var iter = try args.argsIterator(gpa_alloc, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, gpa_alloc, &opts, &iter);
|
||||
}
|
||||
@@ -117,15 +117,15 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var stdout_buf: [4096]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&stdout_buf);
|
||||
var stdout_file: std.Io.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(global.io(), &stdout_buf);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buf: [4096]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&stderr_buf);
|
||||
var stderr_writer = std.Io.File.stderr().writer(global.io(), &stderr_buf);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const resources_dir = global_state.resources_dir.app();
|
||||
const resources_dir = global.resourcesDir().app();
|
||||
if (resources_dir == null)
|
||||
try stderr.print("Could not find the Ghostty resources directory. Please ensure " ++
|
||||
"that Ghostty is installed correctly.\n", .{});
|
||||
@@ -137,18 +137,22 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
var it: themepkg.LocationIterator = .{ .arena_alloc = arena.allocator() };
|
||||
|
||||
while (try it.next()) |loc| {
|
||||
var dir = std.fs.cwd().openDir(loc.dir, .{ .iterate = true }) catch |err| switch (err) {
|
||||
var dir = std.Io.Dir.cwd().openDir(
|
||||
global.io(),
|
||||
loc.dir,
|
||||
.{ .iterate = true },
|
||||
) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => {
|
||||
std.debug.print("error trying to open {s}: {}\n", .{ loc.dir, err });
|
||||
continue;
|
||||
},
|
||||
};
|
||||
defer dir.close();
|
||||
defer dir.close(global.io());
|
||||
|
||||
var walker = dir.iterate();
|
||||
|
||||
while (try walker.next()) |entry| {
|
||||
while (try walker.next(global.io())) |entry| {
|
||||
switch (entry.kind) {
|
||||
.file, .sym_link => {
|
||||
if (std.mem.eql(u8, entry.name, ".DS_Store"))
|
||||
@@ -174,7 +178,7 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
|
||||
std.mem.sortUnstable(ThemeListElement, themes.items, {}, ThemeListElement.lessThan);
|
||||
|
||||
if (tui.can_pretty_print and !opts.plain and stdout_file.isTty()) {
|
||||
if (tui.can_pretty_print and !opts.plain and try stdout_file.isTty(global.io())) {
|
||||
try preview(gpa_alloc, themes.items, opts.color);
|
||||
return 0;
|
||||
}
|
||||
@@ -210,14 +214,18 @@ fn writeAutoThemeFile(alloc: std.mem.Allocator, theme_name: []const u8) !void {
|
||||
defer alloc.free(auto_path);
|
||||
|
||||
if (std.fs.path.dirname(auto_path)) |dir| {
|
||||
try std.fs.cwd().makePath(dir);
|
||||
try std.Io.Dir.cwd().createDirPath(global.io(), dir);
|
||||
}
|
||||
|
||||
var f = try std.fs.createFileAbsolute(auto_path, .{ .truncate = true });
|
||||
defer f.close();
|
||||
var f = try std.Io.Dir.createFileAbsolute(
|
||||
global.io(),
|
||||
auto_path,
|
||||
.{ .truncate = true },
|
||||
);
|
||||
defer f.close(global.io());
|
||||
|
||||
var buf: [128]u8 = undefined;
|
||||
var w = f.writer(&buf);
|
||||
var w = f.writer(global.io(), &buf);
|
||||
try w.interface.print("theme = {s}\n", .{theme_name});
|
||||
try w.interface.flush();
|
||||
}
|
||||
@@ -256,13 +264,16 @@ const Preview = struct {
|
||||
theme_filter: ColorScheme,
|
||||
buf: []u8,
|
||||
) !*Preview {
|
||||
var env_map = try global.environMap();
|
||||
defer env_map.deinit();
|
||||
|
||||
const self = try allocator.create(Preview);
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.should_quit = false,
|
||||
.tty = try .init(buf),
|
||||
.vx = try vaxis.init(allocator, .{}),
|
||||
.tty = try .init(global.io(), buf),
|
||||
.vx = try vaxis.init(global.io(), allocator, &env_map, .{}),
|
||||
.mouse = null,
|
||||
.themes = themes,
|
||||
.filtered = try .initCapacity(allocator, themes.len),
|
||||
@@ -290,18 +301,12 @@ const Preview = struct {
|
||||
}
|
||||
|
||||
pub fn run(self: *Preview) !void {
|
||||
var loop: vaxis.Loop(Event) = .{
|
||||
.tty = &self.tty,
|
||||
.vaxis = &self.vx,
|
||||
};
|
||||
try loop.init();
|
||||
try loop.start();
|
||||
|
||||
var loop: vaxis.Loop(Event) = .init(global.io(), &self.tty, &self.vx);
|
||||
const writer = self.tty.writer();
|
||||
|
||||
try self.vx.enterAltScreen(writer);
|
||||
try self.vx.setTitle(writer, "👻 Ghostty Theme Preview 👻");
|
||||
try self.vx.queryTerminal(writer, 1 * std.time.ns_per_s);
|
||||
try self.vx.queryTerminal(writer, .fromSeconds(1));
|
||||
try self.vx.setMouseMode(writer, true);
|
||||
if (self.vx.caps.color_scheme_updates)
|
||||
try self.vx.subscribeToColorSchemeUpdates(writer);
|
||||
@@ -311,8 +316,8 @@ const Preview = struct {
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
loop.pollEvent();
|
||||
while (loop.tryEvent()) |event| {
|
||||
try loop.pollEvent();
|
||||
while (try loop.tryEvent()) |event| {
|
||||
try self.update(event, alloc);
|
||||
}
|
||||
try self.draw(alloc);
|
||||
@@ -366,7 +371,12 @@ const Preview = struct {
|
||||
if (!shouldIncludeTheme(self.theme_filter, theme_config)) continue;
|
||||
|
||||
theme.rank = zf.rank(theme.theme, tokens.items, .{
|
||||
.to_lower = true,
|
||||
// NOTE: Changed from ".to_lower = true" (the option was
|
||||
// renamed). I think this is the correct analog for case
|
||||
// insensitive ranking (which is what I'm guessing
|
||||
// ".to_lower = true" implies, but this comment serves as a
|
||||
// hint if there's a regression.
|
||||
.case_sensitive = false,
|
||||
.plain = true,
|
||||
});
|
||||
if (theme.rank != null) try self.filtered.append(self.allocator, i);
|
||||
@@ -620,13 +630,21 @@ const Preview = struct {
|
||||
self.down(1);
|
||||
}
|
||||
if (theme_list.hasMouse(mouse)) |_| {
|
||||
// NOTE: mouse co-ordinates can be negative (see
|
||||
// https://github.com/rockorager/libvaxis/pull/276).
|
||||
// Working around it in this case, but putting this here in
|
||||
// case there are issues.
|
||||
if (mouse.button == .left and mouse.type == .release) {
|
||||
const selection = self.window + mouse.row;
|
||||
const selection: usize = selection: {
|
||||
var window: i32 = @min(self.window, std.math.maxInt(i32));
|
||||
window += mouse.row;
|
||||
break :selection @max(0, window);
|
||||
};
|
||||
if (selection < self.filtered.items.len) {
|
||||
self.current = selection;
|
||||
}
|
||||
}
|
||||
highlight = mouse.row;
|
||||
highlight = @max(0, mouse.row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,12 @@ const args = @import("args.zig");
|
||||
const diagnostics = @import("diagnostics.zig");
|
||||
const lib = @import("../lib/main.zig");
|
||||
const homedir = @import("../os/homedir.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// We store an I/O implementation to make maintenance easier.
|
||||
_io: std.Io,
|
||||
|
||||
/// This is set by the CLI parser for deinit.
|
||||
_arena: ?ArenaAllocator = null,
|
||||
|
||||
@@ -26,8 +30,18 @@ pub const Options = struct {
|
||||
/// there is a "normal" config setting on the cli.
|
||||
_diagnostics: diagnostics.DiagnosticList = .{},
|
||||
|
||||
pub const ParseManuallyHookError = error{InvalidValue} ||
|
||||
homedir.ExpandError ||
|
||||
std.Io.Dir.RealPathFileAllocError ||
|
||||
Allocator.Error;
|
||||
|
||||
/// Manual parse hook, collect all of the arguments after `+new-window`.
|
||||
pub fn parseManuallyHook(self: *Options, alloc: Allocator, arg: []const u8, iter: anytype) (error{InvalidValue} || homedir.ExpandError || std.fs.Dir.RealPathAllocError || Allocator.Error)!bool {
|
||||
pub fn parseManuallyHook(
|
||||
self: *Options,
|
||||
alloc: Allocator,
|
||||
arg: []const u8,
|
||||
iter: anytype,
|
||||
) ParseManuallyHookError!bool {
|
||||
var e_seen: bool = std.mem.eql(u8, arg, "-e");
|
||||
|
||||
// Include the argument that triggered the manual parse hook.
|
||||
@@ -50,7 +64,12 @@ pub const Options = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn checkArg(self: *Options, alloc: Allocator, arg: []const u8) (error{InvalidValue} || homedir.ExpandError || std.fs.Dir.RealPathAllocError || Allocator.Error)!?[:0]const u8 {
|
||||
const CheckArgError = error{InvalidValue} ||
|
||||
homedir.ExpandError ||
|
||||
std.Io.Dir.RealPathFileAllocError ||
|
||||
Allocator.Error;
|
||||
|
||||
fn checkArg(self: *Options, alloc: Allocator, arg: []const u8) CheckArgError!?[:0]const u8 {
|
||||
if (lib.cutPrefix(u8, arg, "--class=")) |rest| {
|
||||
self.class = try alloc.dupeZ(u8, std.mem.trim(u8, rest, &std.ascii.whitespace));
|
||||
return null;
|
||||
@@ -60,11 +79,15 @@ pub const Options = struct {
|
||||
const stripped = std.mem.trim(u8, rest, &std.ascii.whitespace);
|
||||
if (std.mem.eql(u8, stripped, "home")) return try alloc.dupeZ(u8, arg);
|
||||
if (std.mem.eql(u8, stripped, "inherit")) return try alloc.dupeZ(u8, arg);
|
||||
const cwd: std.fs.Dir = std.fs.cwd();
|
||||
const cwd: std.Io.Dir = .cwd();
|
||||
var expandhome_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const expanded = try homedir.expandHome(stripped, &expandhome_buf);
|
||||
const expanded = expanded: {
|
||||
var environ_map = try global.environMap();
|
||||
defer environ_map.deinit();
|
||||
break :expanded try homedir.expandHome(&environ_map, stripped, &expandhome_buf);
|
||||
};
|
||||
var realpath_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const realpath = try cwd.realpath(expanded, &realpath_buf);
|
||||
const realpath = realpath_buf[0..try cwd.realPathFile(self._io, expanded, &realpath_buf)];
|
||||
self._working_directory_seen = true;
|
||||
return try std.fmt.allocPrintSentinel(alloc, "--working-directory={s}", .{realpath}, 0);
|
||||
}
|
||||
@@ -147,11 +170,11 @@ pub const Options = struct {
|
||||
///
|
||||
/// Available since: 1.2.0
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
var stderr_writer = std.Io.File.stderr().writer(global.io(), &buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const result = runArgs(alloc, &iter, stderr);
|
||||
@@ -164,7 +187,7 @@ fn runArgs(
|
||||
argsIter: anytype,
|
||||
stderr: *std.Io.Writer,
|
||||
) !u8 {
|
||||
var opts: Options = .{};
|
||||
var opts: Options = .{ ._io = global.io() };
|
||||
defer opts.deinit();
|
||||
|
||||
args.parse(Options, alloc_gpa, &opts, argsIter) catch |err| switch (err) {
|
||||
@@ -195,11 +218,16 @@ fn runArgs(
|
||||
|
||||
if (!opts._working_directory_seen) {
|
||||
const alloc = opts._arena.?.allocator();
|
||||
const cwd: std.fs.Dir = std.fs.cwd();
|
||||
const cwd: std.Io.Dir = .cwd();
|
||||
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const wd = try cwd.realpath(".", &buf);
|
||||
const wd = buf[0..try cwd.realPathFile(global.io(), ".", &buf)];
|
||||
// This should be inserted at the beginning of the list, just in case `-e` was used.
|
||||
try opts._arguments.insert(alloc, 0, try std.fmt.allocPrintSentinel(alloc, "--working-directory={s}", .{wd}, 0));
|
||||
try opts._arguments.insert(alloc, 0, try std.fmt.allocPrintSentinel(
|
||||
alloc,
|
||||
"--working-directory={s}",
|
||||
.{wd},
|
||||
0,
|
||||
));
|
||||
}
|
||||
|
||||
var arena = ArenaAllocator.init(alloc_gpa);
|
||||
|
||||
@@ -5,6 +5,7 @@ const Action = @import("ghostty.zig").Action;
|
||||
const configpkg = @import("../config.zig");
|
||||
const Config = configpkg.Config;
|
||||
const Pager = @import("Pager.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// If true, do not load the user configuration, only load the defaults.
|
||||
@@ -66,7 +67,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
@@ -81,7 +82,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
.docs = opts.docs,
|
||||
};
|
||||
|
||||
var pager: Pager = if (!opts.@"no-pager") .init(alloc) else .{};
|
||||
var pager: Pager = if (!opts.@"no-pager") .init() else .{};
|
||||
defer pager.deinit();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
const writer = pager.writer(&buffer);
|
||||
|
||||
@@ -7,6 +7,7 @@ const diagnostics = @import("diagnostics.zig");
|
||||
const font = @import("../font/main.zig");
|
||||
const configpkg = @import("../config.zig");
|
||||
const Config = configpkg.Config;
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// This is set by the CLI parser for deinit.
|
||||
@@ -62,15 +63,15 @@ pub const Options = struct {
|
||||
/// style. Valid options are `text` and `emoji`. If unset, the presentation
|
||||
/// style of a codepoint will be inferred from the Unicode standard.
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
|
||||
var stdout_buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
|
||||
var stdout_writer = std.Io.File.stdout().writer(global.io(), &stdout_buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stdout().writer(&stderr_buffer);
|
||||
var stderr_writer = std.Io.File.stdout().writer(global.io(), &stderr_buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const result = runArgs(
|
||||
|
||||
@@ -10,6 +10,7 @@ const Allocator = std.mem.Allocator;
|
||||
const internal_os = @import("../../os/main.zig");
|
||||
const xdg = internal_os.xdg;
|
||||
const Entry = @import("Entry.zig");
|
||||
const global = @import("../../global.zig");
|
||||
|
||||
// 512KB - sufficient for approximately 10k entries
|
||||
const MAX_CACHE_SIZE = 512 * 1024;
|
||||
@@ -26,8 +27,11 @@ pub fn defaultPath(
|
||||
alloc: Allocator,
|
||||
program: []const u8,
|
||||
) ![]const u8 {
|
||||
var environ_map = try global.environMap();
|
||||
defer environ_map.deinit();
|
||||
const state_dir: []const u8 = xdg.state(
|
||||
alloc,
|
||||
&environ_map,
|
||||
.{ .subdir = program },
|
||||
) catch |err| return switch (err) {
|
||||
error.OutOfMemory => error.OutOfMemory,
|
||||
@@ -41,7 +45,7 @@ pub fn defaultPath(
|
||||
/// This removes the cache file from disk, effectively clearing all cached
|
||||
/// SSH terminfo entries.
|
||||
pub fn clear(self: DiskCache) !void {
|
||||
std.fs.cwd().deleteFile(self.path) catch |err| switch (err) {
|
||||
std.Io.Dir.cwd().deleteFile(global.io(), self.path) catch |err| switch (err) {
|
||||
error.FileNotFound => {},
|
||||
else => return err,
|
||||
};
|
||||
@@ -59,36 +63,43 @@ pub fn add(
|
||||
|
||||
// Create cache directory if needed
|
||||
if (std.fs.path.dirname(self.path)) |dir| {
|
||||
std.fs.cwd().makePath(dir) catch |err| switch (err) {
|
||||
std.Io.Dir.cwd().createDirPath(global.io(), dir) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
|
||||
// Open or create cache file with secure permissions
|
||||
const file = std.fs.createFileAbsolute(self.path, .{
|
||||
const file = std.Io.Dir.createFileAbsolute(global.io(), self.path, .{
|
||||
.read = true,
|
||||
.truncate = false,
|
||||
.mode = 0o600,
|
||||
.permissions = if (builtin.os.tag != .windows and std.posix.mode_t != u0)
|
||||
.fromMode(0o600)
|
||||
else
|
||||
.default_file,
|
||||
}) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => blk: {
|
||||
const existing_file = try std.fs.openFileAbsolute(
|
||||
const existing_file = try std.Io.Dir.openFileAbsolute(
|
||||
global.io(),
|
||||
self.path,
|
||||
.{ .mode = .read_write },
|
||||
);
|
||||
errdefer existing_file.close();
|
||||
errdefer existing_file.close(global.io());
|
||||
try fixupPermissions(existing_file);
|
||||
break :blk existing_file;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
defer file.close();
|
||||
defer file.close(global.io());
|
||||
|
||||
// Lock
|
||||
// Causes a compile failure in the Zig std library on Windows, see:
|
||||
// https://github.com/ziglang/zig/issues/18430
|
||||
if (comptime builtin.os.tag != .windows) _ = file.tryLock(.exclusive) catch return error.CacheLocked;
|
||||
defer if (comptime builtin.os.tag != .windows) file.unlock();
|
||||
if (comptime builtin.os.tag != .windows) _ = file.tryLock(
|
||||
global.io(),
|
||||
.exclusive,
|
||||
) catch return error.CacheLocked;
|
||||
defer if (comptime builtin.os.tag != .windows) file.unlock(global.io());
|
||||
|
||||
var entries = try readEntries(alloc, file);
|
||||
defer deinitEntries(alloc, &entries);
|
||||
@@ -125,21 +136,25 @@ pub fn remove(
|
||||
if (!isValidCacheKey(key)) return error.InvalidCacheKey;
|
||||
|
||||
// Open our file
|
||||
const file = std.fs.openFileAbsolute(
|
||||
const file = std.Io.Dir.openFileAbsolute(
|
||||
global.io(),
|
||||
self.path,
|
||||
.{ .mode = .read_write },
|
||||
) catch |err| switch (err) {
|
||||
error.FileNotFound => return false,
|
||||
else => return err,
|
||||
};
|
||||
defer file.close();
|
||||
defer file.close(global.io());
|
||||
try fixupPermissions(file);
|
||||
|
||||
// Lock
|
||||
// Causes a compile failure in the Zig std library on Windows, see:
|
||||
// https://github.com/ziglang/zig/issues/18430
|
||||
if (comptime builtin.os.tag != .windows) _ = file.tryLock(.exclusive) catch return error.CacheLocked;
|
||||
defer if (comptime builtin.os.tag != .windows) file.unlock();
|
||||
if (comptime builtin.os.tag != .windows) _ = file.tryLock(
|
||||
global.io(),
|
||||
.exclusive,
|
||||
) catch return error.CacheLocked;
|
||||
defer if (comptime builtin.os.tag != .windows) file.unlock(global.io());
|
||||
|
||||
// Read existing entries
|
||||
var entries = try readEntries(alloc, file);
|
||||
@@ -165,28 +180,32 @@ pub fn prune(
|
||||
alloc: Allocator,
|
||||
max_age_s: u64,
|
||||
) !usize {
|
||||
const file = std.fs.openFileAbsolute(
|
||||
const file = std.Io.Dir.openFileAbsolute(
|
||||
global.io(),
|
||||
self.path,
|
||||
.{ .mode = .read_write },
|
||||
) catch |err| switch (err) {
|
||||
error.FileNotFound => return 0,
|
||||
else => return err,
|
||||
};
|
||||
defer file.close();
|
||||
defer file.close(global.io());
|
||||
try fixupPermissions(file);
|
||||
|
||||
// Lock
|
||||
// Causes a compile failure in the Zig std library on Windows, see:
|
||||
// https://github.com/ziglang/zig/issues/18430
|
||||
if (comptime builtin.os.tag != .windows) _ = file.tryLock(.exclusive) catch return error.CacheLocked;
|
||||
defer if (comptime builtin.os.tag != .windows) file.unlock();
|
||||
if (comptime builtin.os.tag != .windows) _ = file.tryLock(
|
||||
global.io(),
|
||||
.exclusive,
|
||||
) catch return error.CacheLocked;
|
||||
defer if (comptime builtin.os.tag != .windows) file.unlock(global.io());
|
||||
|
||||
// Read existing entries
|
||||
var entries = try readEntries(alloc, file);
|
||||
defer deinitEntries(alloc, &entries);
|
||||
|
||||
// Drop expired entries from the map, then persist what remains.
|
||||
const now = std.time.timestamp();
|
||||
const now = std.Io.Timestamp.now(global.io(), .real).toSeconds();
|
||||
var expired: std.ArrayList([]const u8) = .empty;
|
||||
defer expired.deinit(alloc);
|
||||
var iter = entries.iterator();
|
||||
@@ -215,14 +234,15 @@ pub fn contains(
|
||||
if (!isValidCacheKey(key)) return error.InvalidCacheKey;
|
||||
|
||||
// Open our file
|
||||
const file = std.fs.openFileAbsolute(
|
||||
const file = std.Io.Dir.openFileAbsolute(
|
||||
global.io(),
|
||||
self.path,
|
||||
.{},
|
||||
) catch |err| switch (err) {
|
||||
error.FileNotFound => return false,
|
||||
else => return err,
|
||||
};
|
||||
defer file.close();
|
||||
defer file.close(global.io());
|
||||
|
||||
// Read existing entries
|
||||
var entries = try readEntries(alloc, file);
|
||||
@@ -231,15 +251,15 @@ pub fn contains(
|
||||
return entries.contains(key);
|
||||
}
|
||||
|
||||
fn fixupPermissions(file: std.fs.File) !void {
|
||||
fn fixupPermissions(file: std.Io.File) !void {
|
||||
// Windows does not support chmod
|
||||
if (comptime builtin.os.tag == .windows) return;
|
||||
|
||||
// Ensure file has correct permissions (readable/writable by
|
||||
// owner only)
|
||||
const stat = try file.stat();
|
||||
if (stat.mode & 0o777 != 0o600) {
|
||||
try file.chmod(0o600);
|
||||
const stat = try file.stat(global.io());
|
||||
if (stat.permissions.toMode() & 0o777 != 0o600) {
|
||||
try file.setPermissions(global.io(), .fromMode(0o600));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,22 +270,27 @@ fn writeCacheFile(
|
||||
const cache_dir = std.fs.path.dirname(self.path) orelse return error.InvalidCachePath;
|
||||
const cache_basename = std.fs.path.basename(self.path);
|
||||
|
||||
var dir = try std.fs.cwd().openDir(cache_dir, .{});
|
||||
defer dir.close();
|
||||
var dir = try std.Io.Dir.cwd().openDir(global.io(), cache_dir, .{});
|
||||
defer dir.close(global.io());
|
||||
|
||||
var buf: [1024]u8 = undefined;
|
||||
var atomic_file = try dir.atomicFile(cache_basename, .{
|
||||
.mode = 0o600,
|
||||
.write_buffer = &buf,
|
||||
var atomic_file = try dir.createFileAtomic(global.io(), cache_basename, .{
|
||||
.permissions = if (builtin.os.tag != .windows and std.posix.mode_t != u0)
|
||||
.fromMode(0o600)
|
||||
else
|
||||
.default_file,
|
||||
.replace = true,
|
||||
});
|
||||
defer atomic_file.deinit();
|
||||
defer atomic_file.deinit(global.io());
|
||||
var file_writer = atomic_file.file.writer(global.io(), &buf);
|
||||
|
||||
var iter = entries.iterator();
|
||||
while (iter.next()) |kv| {
|
||||
try kv.value_ptr.format(&atomic_file.file_writer.interface);
|
||||
try kv.value_ptr.format(&file_writer.interface);
|
||||
}
|
||||
|
||||
try atomic_file.finish();
|
||||
try file_writer.flush();
|
||||
try atomic_file.replace(global.io());
|
||||
}
|
||||
|
||||
/// List all entries in the cache.
|
||||
@@ -276,14 +301,15 @@ pub fn list(
|
||||
alloc: Allocator,
|
||||
) !std.StringHashMap(Entry) {
|
||||
// Open our file
|
||||
const file = std.fs.openFileAbsolute(
|
||||
const file = std.Io.Dir.openFileAbsolute(
|
||||
global.io(),
|
||||
self.path,
|
||||
.{},
|
||||
) catch |err| switch (err) {
|
||||
error.FileNotFound => return .init(alloc),
|
||||
else => return err,
|
||||
};
|
||||
defer file.close();
|
||||
defer file.close(global.io());
|
||||
return readEntries(alloc, file);
|
||||
}
|
||||
|
||||
@@ -306,9 +332,9 @@ pub fn deinitEntries(
|
||||
|
||||
fn readEntries(
|
||||
alloc: Allocator,
|
||||
file: std.fs.File,
|
||||
file: std.Io.File,
|
||||
) !std.StringHashMap(Entry) {
|
||||
var reader = file.reader(&.{});
|
||||
var reader = file.reader(global.io(), &.{});
|
||||
const content = try reader.interface.allocRemaining(
|
||||
alloc,
|
||||
.limited(MAX_CACHE_SIZE),
|
||||
@@ -392,7 +418,7 @@ fn isValidHost(host: []const u8) bool {
|
||||
// We also accept valid IP addresses. In practice, IPv4 addresses are also
|
||||
// considered valid hostnames due to their overlapping syntax, so we can
|
||||
// simplify this check to be IPv6-specific.
|
||||
if (std.net.Address.parseIp6(host, 0)) |_| {
|
||||
if (std.Io.net.IpAddress.parseIp6(host, 0)) |_| {
|
||||
return true;
|
||||
} else |_| {
|
||||
return false;
|
||||
@@ -428,12 +454,12 @@ test "disk cache clear" {
|
||||
defer tmp.cleanup();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try tmp.dir.createFile("cache", .{});
|
||||
defer file.close();
|
||||
var file_writer = file.writer(&buf);
|
||||
var file = try tmp.dir.createFile(testing.io, "cache", .{});
|
||||
defer file.close(testing.io);
|
||||
var file_writer = file.writer(testing.io, &buf);
|
||||
try file_writer.interface.writeAll("HELLO!");
|
||||
}
|
||||
const path = try tmp.dir.realpathAlloc(alloc, "cache");
|
||||
const path = try tmp.dir.realPathFileAlloc(testing.io, "cache", alloc);
|
||||
defer alloc.free(path);
|
||||
|
||||
// Setup our cache
|
||||
@@ -443,7 +469,7 @@ test "disk cache clear" {
|
||||
// Verify the file is gone
|
||||
try testing.expectError(
|
||||
error.FileNotFound,
|
||||
tmp.dir.openFile("cache", .{}),
|
||||
tmp.dir.openFile(testing.io, "cache", .{}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -456,21 +482,21 @@ test "disk cache operations" {
|
||||
defer tmp.cleanup();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try tmp.dir.createFile("cache", .{});
|
||||
defer file.close();
|
||||
var file_writer = file.writer(&buf);
|
||||
var file = try tmp.dir.createFile(testing.io, "cache", .{});
|
||||
defer file.close(testing.io);
|
||||
var file_writer = file.writer(testing.io, &buf);
|
||||
const writer = &file_writer.interface;
|
||||
try writer.writeAll("HELLO!");
|
||||
try writer.flush();
|
||||
}
|
||||
const path = try tmp.dir.realpathAlloc(alloc, "cache");
|
||||
const path = try tmp.dir.realPathFileAlloc(testing.io, "cache", alloc);
|
||||
defer alloc.free(path);
|
||||
|
||||
// Setup our cache. Adding the same key twice exercises both the new
|
||||
// and existing-entry paths.
|
||||
const cache: DiskCache = .{ .path = path };
|
||||
try cache.add(alloc, "example.com", std.time.timestamp());
|
||||
try cache.add(alloc, "example.com", std.time.timestamp());
|
||||
try cache.add(alloc, "example.com", std.Io.Timestamp.now(testing.io, .real).toSeconds());
|
||||
try cache.add(alloc, "example.com", std.Io.Timestamp.now(testing.io, .real).toSeconds());
|
||||
try testing.expect(try cache.contains(alloc, "example.com"));
|
||||
|
||||
// List
|
||||
@@ -482,7 +508,7 @@ test "disk cache operations" {
|
||||
try testing.expect(try cache.remove(alloc, "example.com"));
|
||||
try testing.expect(!try cache.remove(alloc, "example.com"));
|
||||
try testing.expect(!(try cache.contains(alloc, "example.com")));
|
||||
try cache.add(alloc, "example.com", std.time.timestamp());
|
||||
try cache.add(alloc, "example.com", std.Io.Timestamp.now(testing.io, .real).toSeconds());
|
||||
}
|
||||
|
||||
test "disk cache cleans up temp files" {
|
||||
@@ -492,19 +518,19 @@ test "disk cache cleans up temp files" {
|
||||
var tmp = testing.tmpDir(.{ .iterate = true });
|
||||
defer tmp.cleanup();
|
||||
|
||||
const tmp_path = try tmp.dir.realpathAlloc(alloc, ".");
|
||||
const tmp_path = try tmp.dir.realPathFileAlloc(testing.io, ".", alloc);
|
||||
defer alloc.free(tmp_path);
|
||||
const cache_path = try std.fs.path.join(alloc, &.{ tmp_path, "cache" });
|
||||
defer alloc.free(cache_path);
|
||||
|
||||
const cache: DiskCache = .{ .path = cache_path };
|
||||
try cache.add(alloc, "example.com", std.time.timestamp());
|
||||
try cache.add(alloc, "example.org", std.time.timestamp());
|
||||
try cache.add(alloc, "example.com", std.Io.Timestamp.now(testing.io, .real).toSeconds());
|
||||
try cache.add(alloc, "example.org", std.Io.Timestamp.now(testing.io, .real).toSeconds());
|
||||
|
||||
// Verify only the cache file exists and no temp files left behind
|
||||
var count: usize = 0;
|
||||
var iter = tmp.dir.iterate();
|
||||
while (try iter.next()) |entry| {
|
||||
while (try iter.next(testing.io)) |entry| {
|
||||
count += 1;
|
||||
try testing.expectEqualStrings("cache", entry.name);
|
||||
}
|
||||
@@ -514,10 +540,11 @@ test "disk cache cleans up temp files" {
|
||||
test "disk cache prune" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const io = testing.io;
|
||||
|
||||
var tmp = testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
const tmp_path = try tmp.dir.realpathAlloc(alloc, ".");
|
||||
const tmp_path = try tmp.dir.realPathFileAlloc(io, ".", alloc);
|
||||
defer alloc.free(tmp_path);
|
||||
const cache_path = try std.fs.path.join(alloc, &.{ tmp_path, "cache" });
|
||||
defer alloc.free(cache_path);
|
||||
@@ -527,7 +554,7 @@ test "disk cache prune" {
|
||||
// Back-date one entry an hour old and one 100 days old.
|
||||
const day = std.time.s_per_day;
|
||||
const hour = std.time.s_per_hour;
|
||||
const now = std.time.timestamp();
|
||||
const now = std.Io.Timestamp.now(testing.io, .real).toSeconds();
|
||||
try cache.add(alloc, "recent.com", now - hour);
|
||||
try cache.add(alloc, "old.com", now - 100 * day);
|
||||
|
||||
@@ -547,11 +574,12 @@ test "disk cache prune" {
|
||||
test "disk cache prune missing file" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const io = testing.io;
|
||||
|
||||
var tmp = testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
const tmp_path = try tmp.dir.realpathAlloc(alloc, ".");
|
||||
const tmp_path = try tmp.dir.realPathFileAlloc(io, ".", alloc);
|
||||
defer alloc.free(tmp_path);
|
||||
const cache_path = try std.fs.path.join(alloc, &.{ tmp_path, "cache" });
|
||||
defer alloc.free(cache_path);
|
||||
@@ -563,6 +591,7 @@ test "disk cache prune missing file" {
|
||||
test "disk cache reads duplicate keys" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const io = testing.io;
|
||||
|
||||
var tmp = testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
@@ -571,16 +600,16 @@ test "disk cache reads duplicate keys" {
|
||||
// key with the updated entry and ensure (via testing.allocator) that
|
||||
// we don't double-free or leak.
|
||||
{
|
||||
var file = try tmp.dir.createFile("cache", .{});
|
||||
defer file.close();
|
||||
var file = try tmp.dir.createFile(io, "cache", .{});
|
||||
defer file.close(io);
|
||||
var buf: [256]u8 = undefined;
|
||||
var file_writer = file.writer(&buf);
|
||||
var file_writer = file.writer(io, &buf);
|
||||
try file_writer.interface.writeAll(
|
||||
"example.com|100|xterm-ghostty\nexample.com|200|xterm-newer\n",
|
||||
);
|
||||
try file_writer.interface.flush();
|
||||
}
|
||||
const path = try tmp.dir.realpathAlloc(alloc, "cache");
|
||||
const path = try tmp.dir.realPathFileAlloc(io, "cache", alloc);
|
||||
defer alloc.free(path);
|
||||
|
||||
const cache: DiskCache = .{ .path = path };
|
||||
@@ -602,10 +631,10 @@ test "disk cache reads survive allocation failure" {
|
||||
// Exercise a populated cache containing a duplicate key to ensure
|
||||
// that we hit all of the possible allocation behaviors below.
|
||||
{
|
||||
var file = try tmp.dir.createFile("cache", .{});
|
||||
defer file.close();
|
||||
var file = try tmp.dir.createFile(testing.io, "cache", .{});
|
||||
defer file.close(testing.io);
|
||||
var buf: [256]u8 = undefined;
|
||||
var file_writer = file.writer(&buf);
|
||||
var file_writer = file.writer(testing.io, &buf);
|
||||
try file_writer.interface.writeAll(
|
||||
"a.com|100|xterm-ghostty\n" ++
|
||||
"b.com|100|xterm-ghostty\n" ++
|
||||
@@ -614,7 +643,7 @@ test "disk cache reads survive allocation failure" {
|
||||
);
|
||||
try file_writer.interface.flush();
|
||||
}
|
||||
const path = try tmp.dir.realpathAlloc(testing.allocator, "cache");
|
||||
const path = try tmp.dir.realPathFileAlloc(testing.io, "cache", testing.allocator);
|
||||
defer testing.allocator.free(path);
|
||||
|
||||
const cache: DiskCache = .{ .path = path };
|
||||
@@ -647,7 +676,7 @@ test "disk cache add survives allocation failure" {
|
||||
|
||||
var tmp = testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
const tmp_path = try tmp.dir.realpathAlloc(testing.allocator, ".");
|
||||
const tmp_path = try tmp.dir.realPathFileAlloc(testing.io, ".", testing.allocator);
|
||||
defer testing.allocator.free(tmp_path);
|
||||
const path = try std.fs.path.join(testing.allocator, &.{ tmp_path, "cache" });
|
||||
defer testing.allocator.free(path);
|
||||
@@ -660,7 +689,7 @@ test "disk cache add survives allocation failure" {
|
||||
// from a clean cache file.
|
||||
var fail_index: usize = 0;
|
||||
while (true) : (fail_index += 1) {
|
||||
std.fs.cwd().deleteFile(path) catch {};
|
||||
std.Io.Dir.cwd().deleteFile(testing.io, path) catch {};
|
||||
var failing = std.testing.FailingAllocator.init(
|
||||
testing.allocator,
|
||||
.{ .fail_index = fail_index },
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const cli_args = @import("args.zig");
|
||||
@@ -7,6 +8,7 @@ const Action = @import("ghostty.zig").Action;
|
||||
const DiskCache = @import("ssh_cache.zig").DiskCache;
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const ghostty_terminfo = @import("../terminfo/main.zig").ghostty;
|
||||
const global = @import("../global.zig");
|
||||
|
||||
const log = std.log.scoped(.ssh);
|
||||
|
||||
@@ -181,14 +183,14 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try cli_args.argsIterator(alloc_gpa);
|
||||
var iter = try cli_args.argsIterator(alloc_gpa, global.args());
|
||||
defer iter.deinit();
|
||||
try cli_args.parse(Options, alloc_gpa, &opts, &iter);
|
||||
}
|
||||
|
||||
var stderr_buffer: [1024]u8 = undefined;
|
||||
var stderr_file: std.fs.File = .stderr();
|
||||
var stderr_writer = stderr_file.writer(&stderr_buffer);
|
||||
var stderr_file: std.Io.File = .stderr();
|
||||
var stderr_writer = stderr_file.writer(global.io(), &stderr_buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
// Any diagnostic from the arg parser is an unknown flag or bad
|
||||
@@ -294,7 +296,7 @@ fn runInner(
|
||||
});
|
||||
verbosePrint(opts, stderr, "exec: {f}", .{Joined{ .items = argv }});
|
||||
|
||||
const exit_code = childExec(alloc, argv) catch |err| {
|
||||
const exit_code = childExec(argv) catch |err| {
|
||||
try stderr.print("Error: failed to run {s}: {}\n", .{ argv[0], err });
|
||||
return 1;
|
||||
};
|
||||
@@ -302,7 +304,10 @@ fn runInner(
|
||||
|
||||
// Attempt to cache (if needed) on a successful ssh execution.
|
||||
if (exit_code == 0) if (session.to_cache) |entry| {
|
||||
if (entry.cache.add(alloc, entry.dest, std.time.timestamp())) |_| {
|
||||
if (entry.cache.add(alloc, entry.dest, std.Io.Timestamp.now(
|
||||
global.io(),
|
||||
.real,
|
||||
).toSeconds())) |_| {
|
||||
verbosePrint(opts, stderr, "cache: wrote {s}", .{entry.dest});
|
||||
} else |err| {
|
||||
log.debug("cache add failed for '{s}': {}", .{ entry.dest, err });
|
||||
@@ -370,7 +375,7 @@ const Joined = struct {
|
||||
|
||||
fn checkExit(term: std.process.Child.Term, label: []const u8) error{ChildFailed}!void {
|
||||
switch (term) {
|
||||
.Exited => |rc| if (rc != 0) {
|
||||
.exited => |rc| if (rc != 0) {
|
||||
log.warn("{s} exited with non-zero status: {d}", .{ label, rc });
|
||||
return error.ChildFailed;
|
||||
},
|
||||
@@ -393,10 +398,11 @@ fn resolveDestination(
|
||||
&.{ ssh, "-G" },
|
||||
args,
|
||||
}) catch return null;
|
||||
const result = std.process.Child.run(.{
|
||||
.allocator = alloc,
|
||||
.argv = argv,
|
||||
}) catch |err| {
|
||||
const result = std.process.run(
|
||||
alloc,
|
||||
global.io(),
|
||||
.{ .argv = argv },
|
||||
) catch |err| {
|
||||
log.warn("ssh -G spawn failed: {}", .{err});
|
||||
return null;
|
||||
};
|
||||
@@ -491,23 +497,23 @@ fn installRemoteTerminfo(
|
||||
});
|
||||
verbosePrint(opts, stderr, "exec: {f}", .{Joined{ .items = argv }});
|
||||
|
||||
var child: std.process.Child = .init(argv, alloc);
|
||||
child.stdin_behavior = .Pipe;
|
||||
child.stdout_behavior = .Ignore;
|
||||
child.stderr_behavior = if (opts.verbose) .Inherit else .Ignore;
|
||||
|
||||
child.spawn() catch |err| {
|
||||
var child = std.process.spawn(global.io(), .{
|
||||
.argv = argv,
|
||||
.stdin = .pipe,
|
||||
.stdout = .ignore,
|
||||
.stderr = if (opts.verbose) .inherit else .ignore,
|
||||
}) catch |err| {
|
||||
log.warn("terminfo install spawn failed: {}", .{err});
|
||||
return error.InstallFailed;
|
||||
};
|
||||
|
||||
if (child.stdin) |stdin| {
|
||||
stdin.writeAll(terminfo) catch {};
|
||||
stdin.close();
|
||||
stdin.writeStreamingAll(global.io(), terminfo) catch {};
|
||||
stdin.close(global.io());
|
||||
child.stdin = null;
|
||||
}
|
||||
|
||||
const term = child.wait() catch |err| {
|
||||
const term = child.wait(global.io()) catch |err| {
|
||||
log.warn("terminfo install wait failed: {}", .{err});
|
||||
return error.InstallFailed;
|
||||
};
|
||||
@@ -515,23 +521,24 @@ fn installRemoteTerminfo(
|
||||
}
|
||||
|
||||
/// Returns `128 + signum` for signal-killed children, matching shell convention.
|
||||
fn childExec(alloc: Allocator, argv: []const []const u8) !u8 {
|
||||
var child: std.process.Child = .init(argv, alloc);
|
||||
child.stdin_behavior = .Inherit;
|
||||
child.stdout_behavior = .Inherit;
|
||||
child.stderr_behavior = .Inherit;
|
||||
fn childExec(argv: []const []const u8) !u8 {
|
||||
var child = try std.process.spawn(global.io(), .{
|
||||
.argv = argv,
|
||||
.stdin = .inherit,
|
||||
.stdout = .inherit,
|
||||
.stderr = .inherit,
|
||||
});
|
||||
|
||||
try child.spawn();
|
||||
const term = try child.wait();
|
||||
const term = try child.wait(global.io());
|
||||
return switch (term) {
|
||||
.Exited => |rc| rc,
|
||||
.Signal => |sig| @as(u8, 128) + @as(u8, @intCast(@min(sig, 127))),
|
||||
.Stopped, .Unknown => 1,
|
||||
.exited => |rc| rc,
|
||||
.signal => |sig| @as(u8, 128) + @as(u8, @intCast(@min(@intFromEnum(sig), 127))),
|
||||
.stopped, .unknown => 1,
|
||||
};
|
||||
}
|
||||
|
||||
fn parseTestArgs(alloc: Allocator, opts: *Options, line: []const u8) !void {
|
||||
var iter = try std.process.ArgIteratorGeneral(.{}).init(alloc, line);
|
||||
var iter = try std.process.Args.IteratorGeneral(.{}).init(alloc, line);
|
||||
defer iter.deinit();
|
||||
try cli_args.parse(Options, alloc, opts, &iter);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const fs = std.fs;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const args = @import("args.zig");
|
||||
const global = @import("../global.zig");
|
||||
const Action = @import("ghostty.zig").Action;
|
||||
const Duration = @import("../config.zig").Config.Duration;
|
||||
pub const Entry = @import("ssh-cache/Entry.zig");
|
||||
@@ -64,13 +65,13 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
var stdout_buffer: [1024]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&stdout_buffer);
|
||||
var stdout_file: std.Io.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(global.io(), &stdout_buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buffer: [1024]u8 = undefined;
|
||||
var stderr_file: std.fs.File = .stderr();
|
||||
var stderr_writer = stderr_file.writer(&stderr_buffer);
|
||||
var stderr_file: std.Io.File = .stderr();
|
||||
var stderr_writer = stderr_file.writer(global.io(), &stderr_buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
// The cache is queried by a positional destination (`user@host` or a
|
||||
@@ -81,7 +82,7 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
||||
var query: ?[]const u8 = null;
|
||||
var flags: std.ArrayList([]const u8) = .empty;
|
||||
{
|
||||
var iter = try args.argsIterator(alloc_gpa);
|
||||
var iter = try args.argsIterator(alloc_gpa, global.args());
|
||||
defer iter.deinit();
|
||||
while (iter.next()) |arg| {
|
||||
const is_host_flag = std.mem.startsWith(u8, arg, "--host=");
|
||||
@@ -174,7 +175,11 @@ pub fn runInner(
|
||||
}
|
||||
|
||||
if (opts.add) |dest| {
|
||||
cache.add(alloc, dest, std.time.timestamp()) catch |err| switch (err) {
|
||||
cache.add(
|
||||
alloc,
|
||||
dest,
|
||||
std.Io.Timestamp.now(global.io(), .real).toSeconds(),
|
||||
) catch |err| switch (err) {
|
||||
error.InvalidCacheKey => {
|
||||
try stderr.print(
|
||||
"Error: Invalid destination '{s}' (expected hostname or user@hostname)\n",
|
||||
@@ -295,7 +300,7 @@ fn listEntries(
|
||||
widest = @max(widest, entry.hostname.len);
|
||||
}
|
||||
|
||||
const now = std.time.timestamp();
|
||||
const now = std.Io.Timestamp.now(global.io(), .real).toSeconds();
|
||||
for (items.items) |entry| {
|
||||
try writer.print("{s}", .{entry.hostname});
|
||||
try writer.splatByteAll(' ', widest - entry.hostname.len + 2);
|
||||
|
||||
@@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Action = @import("../cli.zig").ghostty.Action;
|
||||
const apprt = @import("../apprt.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// If set, connect to a custom instance of Ghostty.
|
||||
@@ -39,7 +40,7 @@ pub const Options = struct {
|
||||
/// Available since: 1.4.0
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
var buf: [256]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buf);
|
||||
var stderr_writer = std.Io.File.stderr().writer(global.io(), &buf);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
if (apprt.App.performIpc(
|
||||
|
||||
@@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;
|
||||
const args = @import("args.zig");
|
||||
const Action = @import("ghostty.zig").Action;
|
||||
const Config = @import("../config.zig").Config;
|
||||
const global = @import("../global.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// The path of the config file to validate. If this isn't specified,
|
||||
@@ -34,13 +35,13 @@ pub fn run(alloc: std.mem.Allocator) !u8 {
|
||||
defer opts.deinit();
|
||||
|
||||
{
|
||||
var iter = try args.argsIterator(alloc);
|
||||
var iter = try args.argsIterator(alloc, global.args());
|
||||
defer iter.deinit();
|
||||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
var stdout_writer = std.Io.File.stdout().writer(global.io(), &buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
const result = runInner(alloc, opts, stdout);
|
||||
try stdout_writer.end();
|
||||
@@ -58,7 +59,11 @@ fn runInner(
|
||||
// If a config path is passed, validate it, otherwise validate default configs
|
||||
if (opts.@"config-file") |config_path| {
|
||||
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const abs_path = try std.fs.cwd().realpath(config_path, &buf);
|
||||
const abs_path = buf[0..try std.Io.Dir.cwd().realPathFile(
|
||||
global.io(),
|
||||
config_path,
|
||||
&buf,
|
||||
)];
|
||||
try cfg.loadFile(alloc, abs_path);
|
||||
try cfg.loadRecursiveFiles(alloc);
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@ const build_config = @import("../build_config.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const xev = @import("../global.zig").xev;
|
||||
const renderer = @import("../renderer.zig");
|
||||
const global = @import("../global.zig");
|
||||
|
||||
const gtk_version = @import("../apprt/gtk/gtk_version.zig");
|
||||
const adw_version = @import("../apprt/gtk/adw_version.zig");
|
||||
@@ -16,11 +17,14 @@ pub const Options = struct {};
|
||||
/// either `+version` or `--version`.
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&buffer);
|
||||
const stdout_file: std.Io.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(global.io(), &buffer);
|
||||
|
||||
var environ_map = try global.environMap();
|
||||
defer environ_map.deinit();
|
||||
|
||||
const stdout = &stdout_writer.interface;
|
||||
const tty = stdout_file.isTty();
|
||||
const tty = try stdout_file.isTty(global.io());
|
||||
|
||||
if (tty) if (build_config.version.build) |commit_hash| {
|
||||
try stdout.print(
|
||||
@@ -48,7 +52,7 @@ pub fn run(alloc: Allocator) !u8 {
|
||||
defer if (kernel_info) |k| alloc.free(k);
|
||||
try stdout.print(" - kernel version: {s}\n", .{kernel_info orelse "Kernel information unavailable"});
|
||||
}
|
||||
try stdout.print(" - desktop env : {t}\n", .{internal_os.desktopEnvironment()});
|
||||
try stdout.print(" - desktop env : {t}\n", .{internal_os.desktopEnvironment(&environ_map)});
|
||||
try stdout.print(" - GTK version :\n", .{});
|
||||
try stdout.print(" build : {f}\n", .{gtk_version.comptime_version});
|
||||
try stdout.print(" runtime : {f}\n", .{gtk_version.getRuntimeVersion()});
|
||||
|
||||
Reference in New Issue
Block a user