mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
[windows] fix which (#8791)
* fix which * [autofix.ci] apply automated fixes * refactor --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1006,10 +1006,10 @@ pub fn GlobWalker_(
|
||||
}
|
||||
|
||||
inline fn startsWithDot(filepath: []const u8) bool {
|
||||
if (comptime !isWindows) {
|
||||
return filepath[0] == '.';
|
||||
if (comptime isWindows) {
|
||||
return filepath.len > 1 and filepath[1] == '.';
|
||||
} else {
|
||||
return filepath[1] == '.';
|
||||
return filepath.len > 0 and filepath[0] == '.';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2120,7 +2120,7 @@ pub const PosixToWinNormalizer = struct {
|
||||
return maybe_posix_path;
|
||||
}
|
||||
|
||||
fn resolveCWDWithExternalBuf(
|
||||
pub fn resolveCWDWithExternalBuf(
|
||||
buf: *Buf,
|
||||
maybe_posix_path: []const u8,
|
||||
) ![]const u8 {
|
||||
|
||||
14
src/sys.zig
14
src/sys.zig
@@ -1715,18 +1715,24 @@ pub fn getMaxPipeSizeOnLinux() usize {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn existsOSPath(path: bun.OSPathSliceZ) bool {
|
||||
pub fn existsOSPath(path: bun.OSPathSliceZ, file_only: bool) bool {
|
||||
if (comptime Environment.isPosix) {
|
||||
return system.access(path, 0) == 0;
|
||||
}
|
||||
|
||||
if (comptime Environment.isWindows) {
|
||||
assertIsValidWindowsPath(bun.OSPathChar, path);
|
||||
const result = kernel32.GetFileAttributesW(path.ptr);
|
||||
const attributes = kernel32.GetFileAttributesW(path.ptr);
|
||||
if (Environment.isDebug) {
|
||||
log("GetFileAttributesW({}) = {d}", .{ bun.fmt.fmtUTF16(path), result });
|
||||
log("GetFileAttributesW({}) = {d}", .{ bun.fmt.fmtUTF16(path), attributes });
|
||||
}
|
||||
return result != windows.INVALID_FILE_ATTRIBUTES;
|
||||
if (attributes == windows.INVALID_FILE_ATTRIBUTES) {
|
||||
return false;
|
||||
}
|
||||
if (file_only and attributes & windows.FILE_ATTRIBUTE_DIRECTORY != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@compileError("TODO: existsOSPath");
|
||||
|
||||
106
src/which.zig
106
src/which.zig
@@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const bun = @import("root").bun;
|
||||
const PosixToWinNormalizer = bun.path.PosixToWinNormalizer;
|
||||
|
||||
fn isValid(buf: *bun.PathBuffer, segment: []const u8, bin: []const u8) ?u16 {
|
||||
bun.copy(u8, buf, segment);
|
||||
@@ -70,66 +71,75 @@ pub fn endsWithExtension(str: []const u8) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Check if the WPathBuffer holds a existing file path, checking also for windows extensions variants like .exe, .cmd and .bat (internally used by whichWin)
|
||||
fn searchBin(buf: *bun.WPathBuffer, path_size: usize, check_windows_extensions: bool) ?[:0]const u16 {
|
||||
if (bun.sys.existsOSPath(buf[0..path_size :0], true))
|
||||
return buf[0..path_size :0];
|
||||
|
||||
if (check_windows_extensions) {
|
||||
buf[path_size] = '.';
|
||||
buf[path_size + 1 + 3] = 0;
|
||||
inline for (win_extensionsW) |ext| {
|
||||
@memcpy(buf[path_size + 1 .. path_size + 1 + 3], ext);
|
||||
if (bun.sys.existsOSPath(buf[0 .. path_size + 1 + ext.len :0], true))
|
||||
return buf[0 .. path_size + 1 + ext.len :0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Check if bin file exists in this path (internally used by whichWin)
|
||||
fn searchBinInPath(buf: *bun.WPathBuffer, path_buf: *[bun.MAX_PATH_BYTES]u8, path: []const u8, bin: []const u8, check_windows_extensions: bool) ?[:0]const u16 {
|
||||
if (path.len == 0) return null;
|
||||
const segment = if (std.fs.path.isAbsolute(path)) (PosixToWinNormalizer.resolveCWDWithExternalBuf(path_buf, path) catch return null) else path;
|
||||
const segment_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf, segment);
|
||||
|
||||
var segment_len = segment.len;
|
||||
var segment_utf16_len = segment_utf16.len;
|
||||
if (buf[segment.len - 1] != std.fs.path.sep) {
|
||||
buf[segment.len] = std.fs.path.sep;
|
||||
segment_len += 1;
|
||||
segment_utf16_len += 1;
|
||||
}
|
||||
|
||||
const bin_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf[segment_len..], bin);
|
||||
const path_size = segment_utf16_len + bin_utf16.len;
|
||||
buf[path_size] = 0;
|
||||
|
||||
return searchBin(buf, path_size, check_windows_extensions);
|
||||
}
|
||||
|
||||
/// This is the windows version of `which`.
|
||||
/// It operates on wide strings.
|
||||
/// It is similar to Get-Command in powershell.
|
||||
pub fn whichWin(buf: *bun.WPathBuffer, path: []const u8, cwd: []const u8, bin: []const u8) ?[:0]const u16 {
|
||||
_ = cwd;
|
||||
if (bin.len == 0) return null;
|
||||
var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
|
||||
const check_windows_extensions = !endsWithExtension(bin);
|
||||
|
||||
// handle absolute paths
|
||||
if (std.fs.path.isAbsolute(bin)) {
|
||||
const bin_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf, bin);
|
||||
if (endsWithExtension(bin)) {
|
||||
buf[bin_utf16.len] = 0;
|
||||
if (bun.sys.existsOSPath(buf[0..bin.len :0]))
|
||||
return buf[0..bin.len :0];
|
||||
}
|
||||
buf[bin_utf16.len] = '.';
|
||||
buf[bin_utf16.len + 1 + 3] = 0;
|
||||
inline for (win_extensionsW) |ext| {
|
||||
@memcpy(buf[bin.len + 1 .. bin_utf16.len + 1 + ext.len], ext);
|
||||
if (bun.sys.existsOSPath(buf[0 .. bin.len + 1 + ext.len :0]))
|
||||
return buf[0 .. bin.len + 1 + ext.len :0];
|
||||
}
|
||||
return null;
|
||||
const normalized_bin = PosixToWinNormalizer.resolveCWDWithExternalBuf(&path_buf, bin) catch return null;
|
||||
const bin_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf, normalized_bin);
|
||||
buf[bin_utf16.len] = 0;
|
||||
return searchBin(buf, bin_utf16.len, check_windows_extensions);
|
||||
}
|
||||
|
||||
// TODO: cwd. This snippet does not work yet.
|
||||
// if (cwd.len > 0) {
|
||||
// const cwd_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf, cwd);
|
||||
// const bin_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf[cwd_utf16.len + 1 ..], bin);
|
||||
// if (endsWithExtension(bin)) {
|
||||
// buf[cwd_utf16.len + 1 + bin_utf16.len] = 0;
|
||||
// if (bun.sys.existsOSPath(buf[0 .. cwd_utf16.len + 1 + bin_utf16.len :0]))
|
||||
// return buf[0 .. cwd_utf16.len + 1 + bin_utf16.len :0];
|
||||
// }
|
||||
// buf[cwd_utf16.len + 1 + bin_utf16.len] = '.';
|
||||
// buf[cwd_utf16.len + 1 + bin_utf16.len + 1 + 3] = 0;
|
||||
// inline for (win_extensionsW) |ext| {
|
||||
// @memcpy(buf[cwd_utf16.len + 1 + bin_utf16.len + 1 .. cwd_utf16.len + 1 + bin_utf16.len + 1 + 3], ext);
|
||||
// if (bun.sys.existsOSPath(buf[0 .. cwd_utf16.len + 1 + bin_utf16.len + 1 + ext.len :0]))
|
||||
// return buf[0 .. cwd_utf16.len + 1 + bin_utf16.len + 1 + ext.len :0];
|
||||
// }
|
||||
// }
|
||||
// check if bin is in cwd
|
||||
if (searchBinInPath(buf, &path_buf, cwd, bin, check_windows_extensions)) |bin_path| {
|
||||
return bin_path;
|
||||
}
|
||||
|
||||
const check_without_append_ext = endsWithExtension(bin);
|
||||
// iterate over system path delimiter
|
||||
var path_iter = std.mem.tokenizeScalar(u8, path, std.fs.path.delimiter);
|
||||
while (path_iter.next()) |segment| {
|
||||
const segment_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf, segment);
|
||||
buf[segment.len] = std.fs.path.sep;
|
||||
const bin_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf[segment.len + 1 ..], bin);
|
||||
if (check_without_append_ext) {
|
||||
buf[segment_utf16.len + 1 + bin_utf16.len] = 0;
|
||||
if (bun.sys.existsOSPath(buf[0 .. segment_utf16.len + 1 + bin_utf16.len :0]))
|
||||
return buf[0 .. segment_utf16.len + 1 + bin_utf16.len :0];
|
||||
}
|
||||
buf[segment_utf16.len + 1 + bin_utf16.len] = '.';
|
||||
buf[segment_utf16.len + 1 + bin_utf16.len + 1 + 3] = 0;
|
||||
inline for (win_extensionsW) |ext| {
|
||||
@memcpy(buf[segment_utf16.len + 1 + bin_utf16.len + 1 .. segment_utf16.len + 1 + bin_utf16.len + 1 + 3], ext);
|
||||
if (bun.sys.existsOSPath(buf[0 .. segment_utf16.len + 1 + bin_utf16.len + 1 + ext.len :0]))
|
||||
return buf[0 .. segment_utf16.len + 1 + bin_utf16.len + 1 + ext.len :0];
|
||||
while (path_iter.next()) |_segment| {
|
||||
// we also iterate over ':' to match linux behavior
|
||||
var segment_iter = std.mem.tokenizeScalar(u8, _segment, ':');
|
||||
while (segment_iter.next()) |segment_part| {
|
||||
if (searchBinInPath(buf, &path_buf, segment_part, bin, check_windows_extensions)) |bin_path| {
|
||||
return bin_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { which } from "bun";
|
||||
import { mkdtempSync, rmSync, chmodSync, mkdirSync, unlinkSync, realpathSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { rmdirSync } from "js/node/fs/export-star-from";
|
||||
|
||||
test("which", () => {
|
||||
{
|
||||
@@ -15,6 +16,7 @@ test("which", () => {
|
||||
}
|
||||
|
||||
let basedir = join(tmpdir(), "which-test-" + Math.random().toString(36).slice(2));
|
||||
|
||||
rmSync(basedir, { recursive: true, force: true });
|
||||
mkdirSync(basedir, { recursive: true });
|
||||
writeFixture(join(basedir, "myscript.sh"));
|
||||
@@ -30,6 +32,9 @@ test("which", () => {
|
||||
|
||||
const orig = process.cwd();
|
||||
process.chdir(tmpdir());
|
||||
try {
|
||||
rmdirSync("myscript.sh");
|
||||
} catch {}
|
||||
// Our cwd is not /tmp
|
||||
expect(which("myscript.sh")).toBe(null);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user