mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
Merge pull request #749 from mitchellh/command-args
Command args, `-e` flag
This commit is contained in:
@ -64,6 +64,11 @@ pub fn parse(comptime T: type, alloc: Allocator, dst: *T, iter: anytype) !void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
while (iter.next()) |arg| {
|
while (iter.next()) |arg| {
|
||||||
|
// Do manual parsing if we have a hook for it.
|
||||||
|
if (@hasDecl(T, "parseManuallyHook")) {
|
||||||
|
if (!try dst.parseManuallyHook(arena_alloc, arg, iter)) return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mem.startsWith(u8, arg, "--")) {
|
if (mem.startsWith(u8, arg, "--")) {
|
||||||
var key: []const u8 = arg[2..];
|
var key: []const u8 = arg[2..];
|
||||||
const value: ?[]const u8 = value: {
|
const value: ?[]const u8 = value: {
|
||||||
|
@ -268,6 +268,16 @@ palette: Palette = .{},
|
|||||||
///
|
///
|
||||||
command: ?[]const u8 = null,
|
command: ?[]const u8 = null,
|
||||||
|
|
||||||
|
/// A single argument to pass to the command. This can be repeated to
|
||||||
|
/// pass multiple arguments. This slightly clunky configuration style is
|
||||||
|
/// so that Ghostty doesn't have to perform any sort of shell parsing
|
||||||
|
/// to find argument boundaries.
|
||||||
|
///
|
||||||
|
/// This cannot be used to override argv[0]. argv[0] will always be
|
||||||
|
/// set by Ghostty to be the command (possibly with a hyphen-prefix to
|
||||||
|
/// indicate that it is a login shell, depending on the OS).
|
||||||
|
@"command-arg": RepeatableString = .{},
|
||||||
|
|
||||||
/// The directory to change to after starting the command.
|
/// The directory to change to after starting the command.
|
||||||
///
|
///
|
||||||
/// The default is "inherit" except in special scenarios listed next.
|
/// The default is "inherit" except in special scenarios listed next.
|
||||||
@ -1135,6 +1145,37 @@ pub fn finalize(self: *Config) !void {
|
|||||||
if (self.@"window-height" > 0) self.@"window-height" = @max(4, self.@"window-height");
|
if (self.@"window-height" > 0) self.@"window-height" = @max(4, self.@"window-height");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Callback for src/cli/args.zig to allow us to handle special cases
|
||||||
|
/// like `--help` or `-e`. Returns "false" if the CLI parsing should halt.
|
||||||
|
pub fn parseManuallyHook(self: *Config, alloc: Allocator, arg: []const u8, iter: anytype) !bool {
|
||||||
|
// If it isn't "-e" then we just continue parsing normally.
|
||||||
|
if (!std.mem.eql(u8, arg, "-e")) return true;
|
||||||
|
|
||||||
|
// The first value is the command to run.
|
||||||
|
if (iter.next()) |command| {
|
||||||
|
self.command = try alloc.dupe(u8, command);
|
||||||
|
} else {
|
||||||
|
try self._errors.add(alloc, .{
|
||||||
|
.message = try std.fmt.allocPrintZ(
|
||||||
|
alloc,
|
||||||
|
"missing command after -e",
|
||||||
|
.{},
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All further arguments are parameters
|
||||||
|
self.@"command-arg".list.clearRetainingCapacity();
|
||||||
|
while (iter.next()) |param| {
|
||||||
|
try self.@"command-arg".parseCLI(alloc, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not continue, we consumed everything.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a shallow copy of this config. This will share all the memory
|
/// Create a shallow copy of this config. This will share all the memory
|
||||||
/// allocated with the previous config but will have a new arena for
|
/// allocated with the previous config but will have a new arena for
|
||||||
/// any changes or new allocations. The config should have `deinit`
|
/// any changes or new allocations. The config should have `deinit`
|
||||||
@ -1268,6 +1309,69 @@ pub const ChangeIterator = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TestIterator = struct {
|
||||||
|
data: []const []const u8,
|
||||||
|
i: usize = 0,
|
||||||
|
|
||||||
|
pub fn next(self: *TestIterator) ?[]const u8 {
|
||||||
|
if (self.i >= self.data.len) return null;
|
||||||
|
const result = self.data[self.i];
|
||||||
|
self.i += 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "parse hook: invalid command" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var cfg = try Config.default(testing.allocator);
|
||||||
|
defer cfg.deinit();
|
||||||
|
const alloc = cfg._arena.?.allocator();
|
||||||
|
|
||||||
|
var it: TestIterator = .{ .data = &.{"foo"} };
|
||||||
|
try testing.expect(try cfg.parseManuallyHook(alloc, "--command", &it));
|
||||||
|
try testing.expect(cfg.command == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse e: command only" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var cfg = try Config.default(testing.allocator);
|
||||||
|
defer cfg.deinit();
|
||||||
|
const alloc = cfg._arena.?.allocator();
|
||||||
|
|
||||||
|
var it: TestIterator = .{ .data = &.{"foo"} };
|
||||||
|
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
||||||
|
try testing.expectEqualStrings("foo", cfg.command.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse e: command and args" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var cfg = try Config.default(testing.allocator);
|
||||||
|
defer cfg.deinit();
|
||||||
|
const alloc = cfg._arena.?.allocator();
|
||||||
|
|
||||||
|
var it: TestIterator = .{ .data = &.{ "echo", "foo", "bar baz" } };
|
||||||
|
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
||||||
|
try testing.expectEqualStrings("echo", cfg.command.?);
|
||||||
|
try testing.expectEqual(@as(usize, 2), cfg.@"command-arg".list.items.len);
|
||||||
|
try testing.expectEqualStrings("foo", cfg.@"command-arg".list.items[0]);
|
||||||
|
try testing.expectEqualStrings("bar baz", cfg.@"command-arg".list.items[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse e: command replaces args" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var cfg = try Config.default(testing.allocator);
|
||||||
|
defer cfg.deinit();
|
||||||
|
const alloc = cfg._arena.?.allocator();
|
||||||
|
|
||||||
|
try cfg.@"command-arg".parseCLI(alloc, "foo");
|
||||||
|
try testing.expectEqual(@as(usize, 1), cfg.@"command-arg".list.items.len);
|
||||||
|
|
||||||
|
var it: TestIterator = .{ .data = &.{"echo"} };
|
||||||
|
try testing.expect(!try cfg.parseManuallyHook(alloc, "-e", &it));
|
||||||
|
try testing.expectEqualStrings("echo", cfg.command.?);
|
||||||
|
try testing.expectEqual(@as(usize, 0), cfg.@"command-arg".list.items.len);
|
||||||
|
}
|
||||||
|
|
||||||
test "clone default" {
|
test "clone default" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
@ -744,24 +744,25 @@ const Subprocess = struct {
|
|||||||
env.remove("GHOSTTY_MAC_APP");
|
env.remove("GHOSTTY_MAC_APP");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're NOT in a flatpak (usually!), then we just exec the
|
// Build our args list
|
||||||
// process directly. If we are in a flatpak, we use flatpak-spawn
|
const args = args: {
|
||||||
// to escape the sandbox.
|
const cap = 1 + opts.full_config.@"command-arg".list.items.len;
|
||||||
const args = if (!internal_os.isFlatpak()) try alloc.dupe(
|
var args = try std.ArrayList([]const u8).initCapacity(alloc, cap);
|
||||||
[]const u8,
|
|
||||||
&[_][]const u8{argv0_override orelse path},
|
|
||||||
) else args: {
|
|
||||||
var args = try std.ArrayList([]const u8).initCapacity(alloc, 8);
|
|
||||||
defer args.deinit();
|
defer args.deinit();
|
||||||
|
|
||||||
// We run our shell wrapped in a /bin/sh login shell because
|
if (!internal_os.isFlatpak()) {
|
||||||
// some systems do not properly initialize the env vars unless
|
try args.append(argv0_override orelse path);
|
||||||
// we start this way (NixOS!)
|
} else {
|
||||||
try args.append("/bin/sh");
|
// We run our shell wrapped in a /bin/sh login shell because
|
||||||
try args.append("-l");
|
// some systems do not properly initialize the env vars unless
|
||||||
try args.append("-c");
|
// we start this way (NixOS!)
|
||||||
try args.append(path);
|
try args.append("/bin/sh");
|
||||||
|
try args.append("-l");
|
||||||
|
try args.append("-c");
|
||||||
|
try args.append(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
try args.appendSlice(opts.full_config.@"command-arg".list.items);
|
||||||
break :args try args.toOwnedSlice();
|
break :args try args.toOwnedSlice();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user