diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 0f35b5787..9419ce97b 100644 --- a/src/termio/Exec.zig +++ b/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 `. 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.