diff --git a/Cargo.toml b/Cargo.toml index 4f98541268e..9cdfab875e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -185,6 +185,7 @@ feat_wasm = [ "base64", "basenc", "cut", + "ls", "date", "dircolors", "dirname", diff --git a/src/uu/ls/Cargo.toml b/src/uu/ls/Cargo.toml index a82ad13c397..b8688819ef8 100644 --- a/src/uu/ls/Cargo.toml +++ b/src/uu/ls/Cargo.toml @@ -24,7 +24,6 @@ path = "src/ls.rs" ansi-width = { workspace = true } clap = { workspace = true, features = ["env"] } glob = { workspace = true } -hostname = { workspace = true } lscolors = { workspace = true } rustc-hash = { workspace = true } selinux = { workspace = true, optional = true } @@ -46,6 +45,10 @@ uucore = { workspace = true, features = [ uutils_term_grid = { workspace = true } fluent = { workspace = true } +# hostname crate does not support WASI (no OS-level hostname API) +[target.'cfg(not(target_os = "wasi"))'.dependencies] +hostname = { workspace = true } + [[bin]] name = "ls" path = "src/main.rs" diff --git a/src/uu/ls/src/display.rs b/src/uu/ls/src/display.rs index 929ce7e4610..d1c35e45c7a 100644 --- a/src/uu/ls/src/display.rs +++ b/src/uu/ls/src/display.rs @@ -1146,7 +1146,12 @@ fn classify_file(path: &PathData) -> Option { } fn create_hyperlink(name: &OsStr, path: &PathData) -> OsString { + // The `hostname` crate does not support WASI (no OS-level hostname API), + // so we use an empty string for hyperlinks on WASI. + #[cfg(not(target_os = "wasi"))] static HOSTNAME: LazyLock = LazyLock::new(|| hostname::get().unwrap_or_default()); + #[cfg(target_os = "wasi")] + static HOSTNAME: LazyLock = LazyLock::new(OsString::new); // OSC 8 hyperlink format: \x1b]8;;URL\x1b\\TEXT\x1b]8;;\x1b\\ // \x1b = ESC, \x1b\\ = ESC backslash diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index 6ef229d175c..bc7f92dd78b 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -45,6 +45,8 @@ macro_rules! has { pub struct FileInformation( #[cfg(unix)] nix::sys::stat::FileStat, #[cfg(windows)] winapi_util::file::Information, + // WASI does not have nix::sys::stat, so we store std::fs::Metadata instead. + #[cfg(target_os = "wasi")] std::fs::Metadata, ); impl FileInformation { @@ -91,6 +93,16 @@ impl FileInformation { let file = open_options.read(true).open(path.as_ref())?; Self::from_file(&file) } + // WASI: use std::fs::metadata / symlink_metadata since nix is not available + #[cfg(target_os = "wasi")] + { + let metadata = if dereference { + std::fs::metadata(path.as_ref()) + } else { + std::fs::symlink_metadata(path.as_ref()) + }; + Ok(Self(metadata?)) + } } pub fn file_size(&self) -> u64 { @@ -103,6 +115,10 @@ impl FileInformation { { self.0.file_size() } + #[cfg(target_os = "wasi")] + { + self.0.len() + } } #[cfg(windows)] @@ -153,6 +169,9 @@ impl FileInformation { return self.0.st_nlink.try_into().unwrap(); #[cfg(windows)] return self.0.number_of_links(); + // WASI: nlink is not available in std::fs::Metadata, return 1 + #[cfg(target_os = "wasi")] + return 1; } #[cfg(unix)] @@ -172,6 +191,15 @@ impl PartialEq for FileInformation { } } +// WASI: compare by file type and size as a basic heuristic since +// device/inode numbers are not available through std::fs::Metadata. +#[cfg(target_os = "wasi")] +impl PartialEq for FileInformation { + fn eq(&self, other: &Self) -> bool { + self.0.file_type() == other.0.file_type() && self.0.len() == other.0.len() + } +} + #[cfg(target_os = "windows")] impl PartialEq for FileInformation { fn eq(&self, other: &Self) -> bool { @@ -194,6 +222,10 @@ impl Hash for FileInformation { self.0.volume_serial_number().hash(state); self.0.file_index().hash(state); } + #[cfg(target_os = "wasi")] + { + self.0.len().hash(state); + } } } @@ -778,11 +810,18 @@ pub fn is_stdin_directory(stdin: &Stdin) -> bool { } false } + + // WASI: stdin is never a directory + #[cfg(target_os = "wasi")] + { + let _ = stdin; + false + } } pub mod sane_blksize { - #[cfg(not(target_os = "windows"))] + #[cfg(unix)] use std::os::unix::fs::MetadataExt; use std::{fs::metadata, path::Path}; @@ -809,12 +848,12 @@ pub mod sane_blksize { #[cfg(unix)] metadata: &std::fs::Metadata, #[cfg(not(unix))] _: &std::fs::Metadata, ) -> u64 { - #[cfg(not(target_os = "windows"))] + #[cfg(unix)] { sane_blksize(metadata.blksize()) } - #[cfg(target_os = "windows")] + #[cfg(not(unix))] { DEFAULT } diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index 5eacc7a4c49..26070b108de 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -535,10 +535,11 @@ pub fn read_fs_list() -> UResult> { target_os = "aix", target_os = "redox", target_os = "illumos", - target_os = "solaris" + target_os = "solaris", + target_os = "wasi" ))] { - // No method to read mounts, yet + // No method to read mounts on these platforms Ok(Vec::new()) } } diff --git a/src/uucore/src/lib/mods/io.rs b/src/uucore/src/lib/mods/io.rs index c83c3d98744..a9e4a7bcbec 100644 --- a/src/uucore/src/lib/mods/io.rs +++ b/src/uucore/src/lib/mods/io.rs @@ -73,10 +73,17 @@ impl OwnedFileDescriptorOrHandle { } /// instantiates a corresponding `Stdio` + #[cfg(not(target_os = "wasi"))] pub fn into_stdio(self) -> Stdio { Stdio::from(self.fx) } + /// WASI: Stdio::from(OwnedFd) is not available, convert via File instead. + #[cfg(target_os = "wasi")] + pub fn into_stdio(self) -> Stdio { + Stdio::from(File::from(self.fx)) + } + /// clones self. useful when needing another /// owned reference to same file pub fn try_clone(&self) -> io::Result {