aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/termio/Exec.zig
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-07 18:29:21 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-07 18:29:21 +0300
commitdcfe11a7588fcd42471159453810abc9b7720a57 (patch)
treec32a77266028e73ca2382b96c96f8438a5fcd621 /vendor/libghostty-vt/src/termio/Exec.zig
parent2dfed860103c4e4505ac7f25def9fe37da1e1e6a (diff)
build: update libghostty-vt vendor
vendor ghostty 063ac3e and remove the musl libc++ link workaround. expose the new selection formatter and hyperlink uri apis for follow-up rendering work. add manual artifact validation for static linux builds.
Diffstat (limited to 'vendor/libghostty-vt/src/termio/Exec.zig')
-rw-r--r--vendor/libghostty-vt/src/termio/Exec.zig133
1 files changed, 115 insertions, 18 deletions
diff --git a/vendor/libghostty-vt/src/termio/Exec.zig b/vendor/libghostty-vt/src/termio/Exec.zig
index 0f35b578..87d47807 100644
--- a/vendor/libghostty-vt/src/termio/Exec.zig
+++ b/vendor/libghostty-vt/src/termio/Exec.zig
@@ -237,6 +237,9 @@ pub fn focusGained(
assert(td.backend == .exec);
const execdata = &td.backend.exec;
+ // Windows has no termios, so there is nothing to poll.
+ if (comptime builtin.os.tag == .windows) return;
+
if (!focused) {
// Flag the timer to end on the next iteration. This is
// a lot cheaper than doing full timer cancellation.
@@ -1552,26 +1555,39 @@ fn execCommand(
defer args.deinit(alloc);
if (comptime builtin.os.tag == .windows) {
- // We run our shell wrapped in `cmd.exe` so that we don't have
- // to parse the command line ourselves if it has arguments.
-
+ // On Windows we run the shell value directly rather than
+ // wrapping in `cmd.exe /C <shell>`. An intermediate cmd
+ // process is wasteful for the common case (`wsl ~`,
+ // `pwsh -NoLogo`, etc.) and has visible side effects
+ // (extra process in the tree, per-process cmd AutoRun
+ // state not reaching the user's actual shell).
+ //
+ // Values with arguments are split on whitespace. This
+ // does not honor Windows CLI quoting rules; users who
+ // need quoted arguments should use the direct command
+ // form, which takes an argv array as-is.
+ //
// Note we don't free any of the memory below since it is
// allocated in the arena.
- const windir = std.process.getEnvVarOwned(
- alloc,
- "WINDIR",
- ) catch |err| {
- log.warn("failed to get WINDIR, cannot run shell command err={}", .{err});
- return error.SystemError;
- };
- const cmd = try std.fs.path.joinZ(alloc, &[_][]const u8{
- windir,
- "System32",
- "cmd.exe",
- });
-
- try args.append(alloc, cmd);
- try args.append(alloc, "/C");
+ if (std.mem.indexOfAny(u8, v, " \t") == null) {
+ // No arguments. If the shell is literally "cmd.exe"
+ // (the default), resolve via %COMSPEC% which is the
+ // documented path to the current command processor.
+ // Other values are passed as-is and resolved by
+ // `internal_os.path.expand` in Command.startWindows.
+ const argv0 = if (std.ascii.eqlIgnoreCase(v, "cmd.exe"))
+ std.process.getEnvVarOwned(alloc, "COMSPEC") catch
+ try alloc.dupe(u8, v)
+ else
+ try alloc.dupe(u8, v);
+ try args.append(alloc, try alloc.dupeZ(u8, argv0));
+ } else {
+ var it = std.mem.tokenizeAny(u8, v, " \t");
+ while (it.next()) |tok| {
+ try args.append(alloc, try alloc.dupeZ(u8, tok));
+ }
+ }
+ break :shell try args.toOwnedSlice(alloc);
} else {
// We run our shell wrapped in `/bin/sh` so that we don't have
// to parse the command line ourselves if it has arguments.
@@ -1759,3 +1775,84 @@ test "execCommand: direct command, config freed" {
try testing.expectEqualStrings(result[0], "foo");
try testing.expectEqualStrings(result[1], "bar baz");
}
+
+test "execCommand windows: bare cmd.exe resolves via COMSPEC" {
+ if (comptime builtin.os.tag != .windows) return error.SkipZigTest;
+
+ const testing = std.testing;
+ var arena = ArenaAllocator.init(testing.allocator);
+ defer arena.deinit();
+ const alloc = arena.allocator();
+
+ const result = try execCommand(alloc, .{ .shell = "cmd.exe" }, struct {
+ fn get(_: Allocator) !PasswdEntry {
+ return .{};
+ }
+ });
+
+ try testing.expectEqual(1, result.len);
+
+ // Expect COMSPEC if available, otherwise the documented fallback.
+ const expected = std.process.getEnvVarOwned(alloc, "COMSPEC") catch
+ try alloc.dupe(u8, "C:\\Windows\\System32\\cmd.exe");
+ try testing.expectEqualStrings(expected, result[0]);
+}
+
+test "execCommand windows: bare non-cmd shell is passed through" {
+ if (comptime builtin.os.tag != .windows) return error.SkipZigTest;
+
+ const testing = std.testing;
+ var arena = ArenaAllocator.init(testing.allocator);
+ defer arena.deinit();
+ const alloc = arena.allocator();
+
+ const result = try execCommand(alloc, .{ .shell = "pwsh.exe" }, struct {
+ fn get(_: Allocator) !PasswdEntry {
+ return .{};
+ }
+ });
+
+ try testing.expectEqual(1, result.len);
+ try testing.expectEqualStrings("pwsh.exe", result[0]);
+}
+
+test "execCommand windows: shell with args is split on whitespace" {
+ if (comptime builtin.os.tag != .windows) return error.SkipZigTest;
+
+ const testing = std.testing;
+ var arena = ArenaAllocator.init(testing.allocator);
+ defer arena.deinit();
+ const alloc = arena.allocator();
+
+ const result = try execCommand(alloc, .{ .shell = "wsl ~" }, struct {
+ fn get(_: Allocator) !PasswdEntry {
+ return .{};
+ }
+ });
+
+ try testing.expectEqual(2, result.len);
+ try testing.expectEqualStrings("wsl", result[0]);
+ try testing.expectEqualStrings("~", result[1]);
+}
+
+test "execCommand windows: direct command is passed through unchanged" {
+ if (comptime builtin.os.tag != .windows) return error.SkipZigTest;
+
+ const testing = std.testing;
+ var arena = ArenaAllocator.init(testing.allocator);
+ defer arena.deinit();
+ const alloc = arena.allocator();
+
+ const result = try execCommand(alloc, .{ .direct = &.{
+ "C:\\tools\\foo.exe",
+ "arg with spaces",
+ } }, struct {
+ fn get(_: Allocator) !PasswdEntry {
+ return .{};
+ }
+ });
+
+ try testing.expectEqual(2, result.len);
+ try testing.expectEqualStrings("C:\\tools\\foo.exe", result[0]);
+ try testing.expectEqualStrings("arg with spaces", result[1]);
+}