authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2022-12-16 22:34:08-08:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2022-12-17 03:36:45-08:00
logd3242408d4bed933c589a8143f4a06851795bdd1
treefe006391494c1d85497f9ebd584ce570dd518165
parent5843b7987e705ec01d07cd98a7f7bb10aa579587

spawnWindows: If an exe is found but fails to exec, retry with PATHEXT values appended

This matches `cmd.exe` behavior. For example, if there is only a file named `mycommand` in the cwd but it is a Linux executable, then running the command `mycommand` will result in: 'mycommand' is not recognized as an internal or external command, operable program or batch file. However, if there is *both* a `mycommand` (that is a Linux executable) and a `mycommand.exe` that is a valid Windows exe, then running the command `mycommand` will successfully run `mycommand.exe`.

1 files changed, 76 insertions(+), 42 deletions(-)

lib/std/child_process.zig+76-42
...@@ -965,56 +965,90 @@ pub const ChildProcess = struct {...@@ -965,56 +965,90 @@ pub const ChildProcess = struct {
965 const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);965 const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);
966 defer self.allocator.free(cmd_line_w);966 defer self.allocator.free(cmd_line_w);
967967
968 windowsCreateProcess(app_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {968 exec: {
969 if (no_path_err != error.FileNotFound and no_path_err != error.InvalidExe) return no_path_err;969 windowsCreateProcess(app_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
970970 switch (no_path_err) {
971 const PATH: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};971 error.FileNotFound, error.InvalidExe => {},
972 const PATHEXT: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};972 else => |e| return e,
973
974 var path_buf = std.ArrayListUnmanaged(u16){};
975 defer path_buf.deinit(self.allocator);
976
977 const app_name = self.argv[0];
978 const app_name_trimmed_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, mem.trimLeft(u8, app_name, "\\/"));
979 defer self.allocator.free(app_name_trimmed_w);
980
981 var it = mem.tokenize(u16, PATH, &[_]u16{';'});
982 retry: while (it.next()) |search_path| {
983 path_buf.clearRetainingCapacity();
984 const search_path_trimmed = mem.trimRight(u16, search_path, &[_]u16{ '\\', '/' });
985 try path_buf.appendSlice(self.allocator, search_path_trimmed);
986 try path_buf.append(self.allocator, fs.path.sep);
987 try path_buf.appendSlice(self.allocator, app_name_trimmed_w);
988 try path_buf.append(self.allocator, 0);
989 const path_no_ext = path_buf.items[0 .. path_buf.items.len - 1 :0];
990
991 if (windowsCreateProcess(path_no_ext.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
992 break :retry;
993 } else |err| switch (err) {
994 error.FileNotFound, error.AccessDenied, error.InvalidExe => {},
995 else => return err,
996 }973 }
997974
998 var ext_it = mem.tokenize(u16, PATHEXT, &[_]u16{';'});975 const PATH: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
999 while (ext_it.next()) |ext| {976 const PATHEXT: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
1000 path_buf.shrinkRetainingCapacity(path_no_ext.len);977
1001 try path_buf.appendSlice(self.allocator, ext);978 var path_buf = std.ArrayListUnmanaged(u16){};
979 defer path_buf.deinit(self.allocator);
980
981 // Try again with PATHEXT's extensions appended
982 {
983 try path_buf.appendSlice(self.allocator, app_path_w);
984 var ext_it = mem.tokenize(u16, PATHEXT, &[_]u16{';'});
985 while (ext_it.next()) |ext| {
986 path_buf.shrinkRetainingCapacity(app_path_w.len);
987 try path_buf.appendSlice(self.allocator, ext);
988 try path_buf.append(self.allocator, 0);
989 const path_with_ext = path_buf.items[0 .. path_buf.items.len - 1 :0];
990
991 if (windowsCreateProcess(path_with_ext.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
992 break :exec;
993 } else |err| switch (err) {
994 error.FileNotFound, error.AccessDenied, error.InvalidExe => {},
995 else => return err,
996 }
997 }
998 }
999
1000 // app_path_w has the cwd prepended to it if cwd is non-null, so when
1001 // searching the PATH we should make sure we use the app_name verbatim.
1002 var app_name_w_needs_free = false;
1003 const app_name_w = x: {
1004 if (self.cwd) |_| {
1005 app_name_w_needs_free = true;
1006 break :x try unicode.utf8ToUtf16LeWithNull(self.allocator, self.argv[0]);
1007 } else {
1008 break :x app_path_w;
1009 }
1010 };
1011 defer if (app_name_w_needs_free) self.allocator.free(app_name_w);
1012
1013 var it = mem.tokenize(u16, PATH, &[_]u16{';'});
1014 while (it.next()) |search_path| {
1015 path_buf.clearRetainingCapacity();
1016 const search_path_trimmed = mem.trimRight(u16, search_path, &[_]u16{ '\\', '/' });
1017 try path_buf.appendSlice(self.allocator, search_path_trimmed);
1018 try path_buf.append(self.allocator, fs.path.sep);
1019 const app_name_trimmed = mem.trimLeft(u16, app_name_w, &[_]u16{ '\\', '/' });
1020 try path_buf.appendSlice(self.allocator, app_name_trimmed);
1002 try path_buf.append(self.allocator, 0);1021 try path_buf.append(self.allocator, 0);
1003 const joined_path = path_buf.items[0 .. path_buf.items.len - 1 :0];1022 const path_no_ext = path_buf.items[0 .. path_buf.items.len - 1 :0];
10041023
1005 if (windowsCreateProcess(joined_path.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {1024 if (windowsCreateProcess(path_no_ext.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
1006 break :retry;1025 break :exec;
1007 } else |err| switch (err) {1026 } else |err| switch (err) {
1008 error.FileNotFound => continue,1027 error.FileNotFound, error.AccessDenied, error.InvalidExe => {},
1009 error.AccessDenied => continue,
1010 error.InvalidExe => continue,
1011 else => return err,1028 else => return err,
1012 }1029 }
1030
1031 var ext_it = mem.tokenize(u16, PATHEXT, &[_]u16{';'});
1032 while (ext_it.next()) |ext| {
1033 path_buf.shrinkRetainingCapacity(path_no_ext.len);
1034 try path_buf.appendSlice(self.allocator, ext);
1035 try path_buf.append(self.allocator, 0);
1036 const joined_path = path_buf.items[0 .. path_buf.items.len - 1 :0];
1037
1038 if (windowsCreateProcess(joined_path.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
1039 break :exec;
1040 } else |err| switch (err) {
1041 error.FileNotFound => continue,
1042 error.AccessDenied => continue,
1043 error.InvalidExe => continue,
1044 else => return err,
1045 }
1046 }
1047 } else {
1048 return no_path_err; // return the original error
1013 }1049 }
1014 } else {1050 };
1015 return no_path_err; // return the original error1051 }
1016 }
1017 };
10181052
1019 if (g_hChildStd_IN_Wr) |h| {1053 if (g_hChildStd_IN_Wr) |h| {
1020 self.stdin = File{ .handle = h };1054 self.stdin = File{ .handle = h };