From 649136315719668a8691285404ad8667b701dd94 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Fri, 27 Mar 2026 10:08:20 -0500 Subject: [PATCH 1/2] cli: +edit-config works properly when editor command contains arguments If `$EDITOR` or `$VISUAL` contained arguments, not just the path to an editor (e.g. `zed --new`) `+edit-config` would fail because we were treating the whole command as a path. Instead, wrap the command with `/bin/sh -c ` so that the shell can separate the path from the arguments. Fixes #11897 --- src/cli/edit_config.zig | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/cli/edit_config.zig b/src/cli/edit_config.zig index 056aecc0d..6f34a5fff 100644 --- a/src/cli/edit_config.zig +++ b/src/cli/edit_config.zig @@ -136,19 +136,23 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 { return 1; } + const command = try std.fmt.allocPrintSentinel( + alloc, + "{s} {s}", + .{ editor, path }, + 0, + ); + 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 editorZ = try alloc.dupeZ(u8, editor); - defer alloc.free(editorZ); - const pathZ = try alloc.dupeZ(u8, path); - defer alloc.free(pathZ); const err = std.posix.execvpeZ( - editorZ, - &.{ editorZ, pathZ }, + "/bin/sh", + &.{ "/bin/sh", "-c", command }, std.c.environ, ); From cb3c20befef1653862d8322df6dfc9e1b73da2f2 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Fri, 27 Mar 2026 12:24:26 -0500 Subject: [PATCH 2/2] cli: escape path in +edit-config --- src/cli/edit_config.zig | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/cli/edit_config.zig b/src/cli/edit_config.zig index 6f34a5fff..c08651a06 100644 --- a/src/cli/edit_config.zig +++ b/src/cli/edit_config.zig @@ -136,12 +136,20 @@ fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 { return 1; } - const command = try std.fmt.allocPrintSentinel( - alloc, - "{s} {s}", - .{ editor, path }, - 0, - ); + const command = command: { + var buffer: std.io.Writer.Allocating = .init(alloc); + defer buffer.deinit(); + const writer = &buffer.writer; + try writer.writeAll(editor); + try writer.writeByte(' '); + { + var sh: internal_os.ShellEscapeWriter = .init(writer); + try sh.writer.writeAll(path); + try sh.writer.flush(); + } + try writer.flush(); + break :command try buffer.toOwnedSliceSentinel(0); + }; defer alloc.free(command); // We require libc because we want to use std.c.environ for envp