diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index a3e1a194c01..4385ae152dc 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -3,9 +3,9 @@ name: CICD # spell-checker:ignore (abbrev/names) CACHEDIR CICD CodeCOV MacOS MinGW MSVC musl taiki # spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic Dwarnings RUSTDOCFLAGS RUSTFLAGS Zpanic CARGOFLAGS CLEVEL nodocs # spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain fuzzers dedupe devel profdata -# spell-checker:ignore (people) Peltoche rivy dtolnay Anson dawidd +# spell-checker:ignore (people) Peltoche rivy Anson dawidd # spell-checker:ignore (shell/tools) binutils choco clippy dmake esac fakeroot fdesc fdescfs gmake grcov halium lcov libclang libfuse libssl limactl nextest nocross pacman popd printf pushd redoxer rsync rustc rustfmt rustup shopt sccache utmpdump xargs zstd -# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils defconfig DESTDIR gecos getenforce gnueabihf issuecomment maint manpages msys multisize noconfirm nofeatures nullglob onexitbegin onexitend pell runtest Swatinem tempfile testsuite toybox uutils libsystemd codspeed wasip libexecinfo +# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils defconfig DESTDIR gecos getenforce gnueabihf issuecomment maint manpages msys multisize noconfirm nofeatures nullglob onexitbegin onexitend pell runtest tempfile testsuite toybox uutils libsystemd codspeed wasip libexecinfo env: PROJECT_NAME: coreutils diff --git a/.github/workflows/FixPR.yml b/.github/workflows/FixPR.yml index 0024aee5883..224617c6d3e 100644 --- a/.github/workflows/FixPR.yml +++ b/.github/workflows/FixPR.yml @@ -1,6 +1,6 @@ name: FixPR -# spell-checker:ignore Swatinem dtolnay dedupe +# spell-checker:ignore dedupe # Trigger automated fixes for PRs being merged (with associated commits) diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index e063c70552a..f91941feb8d 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -1,10 +1,10 @@ name: GnuTests -# spell-checker:ignore (abbrev/names) CodeCov gnulib GnuTests Swatinem +# spell-checker:ignore (abbrev/names) CodeCov gnulib GnuTests # spell-checker:ignore (jargon) submodules devel # spell-checker:ignore (libs/utils) chksum dpkg getenforce gperf lcov libexpect limactl pyinotify setenforce shopt valgrind libattr libcap taiki-e zstd cpio # spell-checker:ignore (options) Ccodegen Coverflow Cpanic Zpanic -# spell-checker:ignore (people) Dawid Dziurla * dawidd dtolnay +# spell-checker:ignore (people) Dawid Dziurla * dawidd # spell-checker:ignore (vars) FILESET SUBDIRS XPASS # spell-checker:ignore userns nodocs diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index eeb68292d00..2837c79f60a 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -1,6 +1,6 @@ name: Android -# spell-checker:ignore (people) reactivecircus Swatinem dtolnay juliangruber +# spell-checker:ignore (people) reactivecircus juliangruber # spell-checker:ignore (shell/tools) TERMUX nextest udevadm pkill # spell-checker:ignore (misc) swiftshader playstore DATALOSS noaudio diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index d2d13275266..17c37e41a93 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -1,6 +1,6 @@ name: Benchmarks -# spell-checker:ignore (people) dtolnay Swatinem taiki-e +# spell-checker:ignore (people) taiki-e # spell-checker:ignore (misc) codspeed sccache on: diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index e5bad3e1d50..7b7bf87e06a 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -1,6 +1,6 @@ name: Code Quality -# spell-checker:ignore (people) dtolnay juliangruber pell reactivecircus Swatinem taiki-e taplo +# spell-checker:ignore (people) juliangruber pell reactivecircus taiki-e taplo # spell-checker:ignore (misc) TERMUX noaudio pkill swiftshader esac sccache pcoreutils shopt subshell dequote libsystemd on: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 53f830a7911..ae5e8d3c8c1 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -1,4 +1,4 @@ -# spell-checker:ignore dtolnay libsystemd libattr libcap gsub +# spell-checker:ignore libsystemd libattr libcap gsub name: Check uudoc Documentation Generation diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml index f31af77adc2..91879538877 100644 --- a/.github/workflows/freebsd.yml +++ b/.github/workflows/freebsd.yml @@ -1,6 +1,6 @@ name: FreeBSD -# spell-checker:ignore sshfs usesh vmactions taiki Swatinem esac fdescfs fdesc nextest copyback logind +# spell-checker:ignore sshfs usesh vmactions taiki esac fdescfs fdesc nextest copyback logind env: # * style job configuration diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index 034f5384f38..47ee6477191 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -1,6 +1,6 @@ name: Fuzzing -# spell-checker:ignore (people) dtolnay Swatinem taiki-e +# spell-checker:ignore (people) taiki-e # spell-checker:ignore (misc) fuzzer env: diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml index 1ebe630c124..936b60669ca 100644 --- a/.github/workflows/make.yml +++ b/.github/workflows/make.yml @@ -5,7 +5,7 @@ name: make # spell-checker:ignore (jargon) deps softprops toolchain # spell-checker:ignore (people) dawidd # spell-checker:ignore (shell/tools) nextest sccache zstd -# spell-checker:ignore (misc) bindir busytest defconfig DESTDIR manpages multisize runtest Swatinem testsuite toybox uutils +# spell-checker:ignore (misc) bindir busytest defconfig DESTDIR manpages multisize runtest testsuite toybox uutils env: PROJECT_NAME: coreutils diff --git a/.github/workflows/manpage-lint.yml b/.github/workflows/manpage-lint.yml index fe30d79afa7..c294e01a26b 100644 --- a/.github/workflows/manpage-lint.yml +++ b/.github/workflows/manpage-lint.yml @@ -1,4 +1,4 @@ -# spell-checker:ignore mandoc uudoc manpages dtolnay libsystemd libattr libcap DESTDIR +# spell-checker:ignore mandoc uudoc manpages libsystemd libattr libcap DESTDIR name: Manpage Validation diff --git a/.github/workflows/openbsd.yml b/.github/workflows/openbsd.yml index 56b10c1d746..797185e04c9 100644 --- a/.github/workflows/openbsd.yml +++ b/.github/workflows/openbsd.yml @@ -1,6 +1,6 @@ name: OpenBSD -# spell-checker:ignore sshfs usesh vmactions taiki Swatinem esac fdescfs fdesc sccache nextest copyback logind bindgen libclang +# spell-checker:ignore sshfs usesh vmactions taiki esac fdescfs fdesc sccache nextest copyback logind bindgen libclang env: # * style job configuration diff --git a/.github/workflows/wasi.yml b/.github/workflows/wasi.yml new file mode 100644 index 00000000000..c4c08974de4 --- /dev/null +++ b/.github/workflows/wasi.yml @@ -0,0 +1,39 @@ +# spell-checker:ignore wasip wasmtime +name: WASI + +# spell-checker:ignore TRIGGERPATH +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +# End the current execution if there is a new changeset in the PR. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + test_wasi: + name: Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-wasip1 + - uses: Swatinem/rust-cache@v2 + - name: Install wasmtime + run: | + curl https://wasmtime.dev/install.sh -sSf | bash + echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH + - name: Run tests + env: + CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime + run: | + cargo test --target wasm32-wasip1 --no-default-features -p uu_echo -p uu_cut diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 2f26340acdc..e4c1592b475 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -215,6 +215,10 @@ vals inval nofield +# ci +dtolnay +Swatinem + # * clippy uninlined nonminimal diff --git a/Cargo.toml b/Cargo.toml index cb27c7806a8..1c63055dd78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,15 +180,18 @@ feat_Tier1 = [ # It is bit complex to deduplicate with other lists feat_wasm = [ "arch", - "basename", "base32", "base64", "basenc", + "basename", "cat", + "comm", "cp", + "csplit", "cut", "ls", "date", + "dir", "dircolors", "dirname", "echo", @@ -197,10 +200,15 @@ feat_wasm = [ "false", "fmt", "fold", + "head", "join", "link", + "ln", + "ls", + "mkdir", "mv", "nl", + "nproc", "numfmt", "od", "paste", @@ -211,7 +219,13 @@ feat_wasm = [ "printf", "ptx", "pwd", + "readlink", + "realpath", + "rm", + "rmdir", "seq", + "sort", + "split", "shred", "shuf", "sleep", @@ -219,13 +233,16 @@ feat_wasm = [ "sum", "tail", "tee", + "touch", "tr", "true", "truncate", + "tsort", "uname", "unexpand", "uniq", "unlink", + "vdir", "wc", "yes", # cksum family diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index af1243779bf..c769feceea9 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -1896,17 +1896,17 @@ pub(crate) fn copy_attributes( fn symlink_file( source: &Path, dest: &Path, - symlinked_files: &mut HashSet, + #[cfg(not(target_os = "wasi"))] symlinked_files: &mut HashSet, + #[cfg(target_os = "wasi")] _symlinked_files: &mut HashSet, ) -> CopyResult<()> { #[cfg(target_os = "wasi")] { - return Err(CpError::IoErrContext( - std::io::Error::new(std::io::ErrorKind::Unsupported, "symlinks not supported"), + Err(CpError::IoErrContext( + io::Error::new(io::ErrorKind::Unsupported, "symlinks not supported"), translate!("cp-error-cannot-create-symlink", "dest" => get_filename(dest).unwrap_or("?").quote(), "source" => get_filename(source).unwrap_or("?").quote()), - ) - .into()); + )) } #[cfg(not(any(windows, target_os = "wasi")))] { @@ -1930,10 +1930,13 @@ fn symlink_file( ) })?; } - if let Ok(file_info) = FileInformation::from_path(dest, false) { - symlinked_files.insert(file_info); + #[cfg(not(target_os = "wasi"))] + { + if let Ok(file_info) = FileInformation::from_path(dest, false) { + symlinked_files.insert(file_info); + } + Ok(()) } - Ok(()) } fn context_for(src: &Path, dest: &Path) -> String { diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index e577d081c49..b82b620a415 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -718,12 +718,19 @@ fn format_date_with_locale_aware_months( let broken_down = BrokenDownTime::from(date); + // When the i18n-datetime feature is enabled (default), use ICU locale-aware + // formatting if the locale requires it. Without the feature (e.g. wasi/wasm + // builds that use --no-default-features), skip localization entirely and + // format with the raw strftime string. + #[cfg(feature = "i18n-datetime")] let result = if !should_use_icu_locale() || skip_localization { broken_down.to_string_with_config(config, format_string) } else { let fmt = localize_format_string(format_string, date.date()); broken_down.to_string_with_config(config, &fmt) }; + #[cfg(not(feature = "i18n-datetime"))] + let result = broken_down.to_string_with_config(config, format_string); result.map_err(|e| e.to_string()) } diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index 1c7d40b8476..f7c953908a1 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -488,3 +488,11 @@ pub fn symlink, P2: AsRef>(src: P1, dst: P2) -> std::io::R symlink_file(src, dst) } } + +#[cfg(target_os = "wasi")] +fn symlink, P2: AsRef>(_src: P1, _dst: P2) -> std::io::Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "symlinks not supported on this platform", + )) +} diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index fa106ac82c0..28901767e7b 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -929,11 +929,8 @@ fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> { } #[cfg(target_os = "wasi")] -fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - translate!("mv-error-no-symlink-support"), - )) +fn rename_symlink_fallback(_from: &Path, _to: &Path) -> io::Result<()> { + Err(io::Error::other(translate!("mv-error-no-symlink-support"))) } fn rename_dir_fallback( diff --git a/src/uu/sort/src/merge.rs b/src/uu/sort/src/merge.rs index d2bb3056b92..df03a3da612 100644 --- a/src/uu/sort/src/merge.rs +++ b/src/uu/sort/src/merge.rs @@ -571,6 +571,8 @@ impl MergeInput for CompressedTmpMergeInput { type InnerRead = ChildStdout; fn finished_reading(self) -> UResult<()> { + // Explicitly close stdout before waiting on the child process. + #[allow(clippy::drop_non_drop)] drop(self.child_stdout); check_child_success(self.child, &self.compress_prog)?; let _ = fs::remove_file(self.path); diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index c3ba3dcb27a..6fe6e4ee7ff 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -37,7 +37,9 @@ use std::ffi::{OsStr, OsString}; use std::fs::{File, OpenOptions}; use std::hash::{Hash, Hasher}; use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout}; -use std::num::{IntErrorKind, NonZero}; +use std::num::IntErrorKind; +#[cfg(not(target_os = "wasi"))] +use std::num::NonZero; use std::ops::Range; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; @@ -2124,12 +2126,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { settings.threads = matches .get_one::(options::PARALLEL) .map_or_else(|| "0".to_string(), String::from); - let num_threads = match settings.threads.parse::() { - Ok(0) | Err(_) => std::thread::available_parallelism().map_or(1, NonZero::get), - Ok(n) => n, - }; #[cfg(not(target_os = "wasi"))] { + let num_threads = match settings.threads.parse::() { + Ok(0) | Err(_) => std::thread::available_parallelism().map_or(1, NonZero::get), + Ok(n) => n, + }; let _ = rayon::ThreadPoolBuilder::new() .num_threads(num_threads) .build_global(); diff --git a/src/uu/sort/src/tmp_dir.rs b/src/uu/sort/src/tmp_dir.rs index cf484e2ff70..0e5bd1f34d4 100644 --- a/src/uu/sort/src/tmp_dir.rs +++ b/src/uu/sort/src/tmp_dir.rs @@ -3,9 +3,9 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use std::path::Path; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use std::sync::atomic::{AtomicBool, Ordering}; use std::{ fs::File, @@ -15,7 +15,7 @@ use std::{ use tempfile::TempDir; use uucore::error::UResult; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use uucore::{error::USimpleError, show_error, translate}; use crate::{SortError, current_open_fd_count, fd_soft_limit}; @@ -105,6 +105,7 @@ fn ensure_signal_handler_installed(state: Arc>) -> UR } #[cfg(any(target_os = "redox", target_os = "wasi"))] +#[allow(clippy::unnecessary_wraps)] fn ensure_signal_handler_installed(_state: Arc>) -> UResult<()> { Ok(()) } @@ -184,7 +185,7 @@ impl Drop for TmpDirWrapper { /// Remove the directory at `path` by deleting its child files and then itself. /// Errors while deleting child files are ignored. -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] fn remove_tmp_dir(path: &Path) -> std::io::Result<()> { if let Ok(read_dir) = std::fs::read_dir(path) { for file in read_dir.flatten() { diff --git a/src/uu/split/src/platform/mod.rs b/src/uu/split/src/platform/mod.rs index 5afc43eeb7f..33dc874586e 100644 --- a/src/uu/split/src/platform/mod.rs +++ b/src/uu/split/src/platform/mod.rs @@ -12,6 +12,34 @@ pub use self::windows::instantiate_current_writer; #[cfg(windows)] pub use self::windows::paths_refer_to_same_file; +// WASI: no process spawning (filter) or device/inode comparison. +#[cfg(target_os = "wasi")] +pub fn paths_refer_to_same_file(_p1: &std::ffi::OsStr, _p2: &std::ffi::OsStr) -> bool { + false +} + +#[cfg(target_os = "wasi")] +pub fn instantiate_current_writer( + _filter: Option<&str>, + filename: &std::ffi::OsStr, + is_new: bool, +) -> std::io::Result>> { + let file = if is_new { + std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(std::path::Path::new(filename))? + } else { + std::fs::OpenOptions::new() + .append(true) + .open(std::path::Path::new(filename))? + }; + Ok(std::io::BufWriter::new( + Box::new(file) as Box + )) +} + #[cfg(unix)] mod unix; diff --git a/src/uu/tail/src/follow/mod.rs b/src/uu/tail/src/follow/mod.rs index 71d65d4a96c..2cd9b3af7fc 100644 --- a/src/uu/tail/src/follow/mod.rs +++ b/src/uu/tail/src/follow/mod.rs @@ -3,6 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +#[cfg(not(target_os = "wasi"))] mod files; #[cfg(not(target_os = "wasi"))] mod watch; @@ -21,21 +22,19 @@ mod wasi_stubs { pub struct Observer { pub use_polling: bool, - pub pid: super::super::platform::Pid, } impl Observer { - pub fn from(settings: &Settings) -> Self { - Self { - use_polling: false, - pid: settings.pid, - } + pub fn from(_settings: &Settings) -> Self { + Self { use_polling: false } } + #[allow(clippy::unnecessary_wraps)] pub fn start(&mut self, _settings: &Settings) -> UResult<()> { Ok(()) } + #[allow(clippy::unnecessary_wraps)] pub fn add_path( &mut self, _path: &Path, @@ -46,6 +45,7 @@ mod wasi_stubs { Ok(()) } + #[allow(clippy::unnecessary_wraps)] pub fn add_bad_path( &mut self, _path: &Path, diff --git a/src/uu/tail/src/paths.rs b/src/uu/tail/src/paths.rs index 4fd7dd51c39..8682cb571af 100644 --- a/src/uu/tail/src/paths.rs +++ b/src/uu/tail/src/paths.rs @@ -12,6 +12,7 @@ use std::io::{Seek, SeekFrom}; #[cfg(unix)] use std::os::unix::fs::{FileTypeExt, MetadataExt}; use std::path::{Path, PathBuf}; +#[cfg(not(target_os = "wasi"))] use uucore::error::UResult; use uucore::translate; @@ -157,7 +158,9 @@ impl FileExtTail for File { pub trait MetadataExtTail { fn is_tailable(&self) -> bool; + #[cfg(not(target_os = "wasi"))] fn got_truncated(&self, other: &Metadata) -> UResult; + #[cfg(not(target_os = "wasi"))] fn file_id_eq(&self, other: &Metadata) -> bool; } @@ -175,10 +178,12 @@ impl MetadataExtTail for Metadata { } /// Return true if the file was modified and is now shorter + #[cfg(not(target_os = "wasi"))] fn got_truncated(&self, other: &Metadata) -> UResult { Ok(other.len() < self.len() && other.modified()? != self.modified()?) } + #[cfg(not(target_os = "wasi"))] fn file_id_eq(&self, #[cfg(unix)] other: &Metadata, #[cfg(not(unix))] _: &Metadata) -> bool { #[cfg(unix)] { @@ -197,19 +202,17 @@ impl MetadataExtTail for Metadata { // } false } - #[cfg(not(any(unix, windows)))] - { - false - } } } +#[cfg(not(target_os = "wasi"))] pub trait PathExtTail { fn is_stdin(&self) -> bool; fn is_orphan(&self) -> bool; fn is_tailable(&self) -> bool; } +#[cfg(not(target_os = "wasi"))] impl PathExtTail for Path { fn is_stdin(&self) -> bool { self.eq(Self::new(text::DASH)) diff --git a/src/uu/tail/src/platform/mod.rs b/src/uu/tail/src/platform/mod.rs index 77a9b311e25..bb77501fdcb 100644 --- a/src/uu/tail/src/platform/mod.rs +++ b/src/uu/tail/src/platform/mod.rs @@ -18,19 +18,6 @@ pub use self::windows::{Pid, ProcessChecker, supports_pid_checks}; #[cfg(target_os = "wasi")] pub type Pid = u64; -#[cfg(target_os = "wasi")] -pub struct ProcessChecker; - -#[cfg(target_os = "wasi")] -impl ProcessChecker { - pub fn new(_pid: Pid) -> Self { - Self - } - pub fn is_dead(&mut self) -> bool { - true - } -} - #[cfg(target_os = "wasi")] pub fn supports_pid_checks(_pid: Pid) -> bool { false diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index d1080c1dddf..4cf4dce2299 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -883,6 +883,10 @@ fn pathbuf_from_stdout() -> Result { .map_err(|e| TouchError::WindowsStdoutPathError(e.to_string()))? .into()) } + #[cfg(target_os = "wasi")] + { + Ok(PathBuf::from("/dev/stdout")) + } } #[cfg(test)] diff --git a/src/uu/tsort/src/tsort.rs b/src/uu/tsort/src/tsort.rs index 5b2fb66d6d5..1868af85c3a 100644 --- a/src/uu/tsort/src/tsort.rs +++ b/src/uu/tsort/src/tsort.rs @@ -82,7 +82,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { target_os = "linux", target_os = "android", target_os = "fuchsia", - target_os = "wasi", target_env = "uclibc", target_os = "freebsd", ))] diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 4b2cb9b502b..27548688987 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -142,6 +142,8 @@ use std::io::{BufRead, BufReader}; use std::iter; #[cfg(unix)] use std::os::unix::ffi::{OsStrExt, OsStringExt}; +#[cfg(target_os = "wasi")] +use std::os::wasi::ffi::{OsStrExt, OsStringExt}; use std::str; use std::str::Utf8Chunk; use std::sync::{LazyLock, atomic::Ordering}; @@ -434,12 +436,12 @@ impl error::UError for NonUtf8OsStrError {} /// /// This always succeeds on unix platforms, /// and fails on other platforms if the string can't be coerced to UTF-8. -#[cfg_attr(unix, expect(clippy::unnecessary_wraps))] +#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))] pub fn os_str_as_bytes(os_string: &OsStr) -> Result<&[u8], NonUtf8OsStrError> { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] return Ok(os_string.as_bytes()); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] os_string .to_str() .ok_or_else(|| NonUtf8OsStrError { @@ -453,10 +455,10 @@ pub fn os_str_as_bytes(os_string: &OsStr) -> Result<&[u8], NonUtf8OsStrError> { /// This is always lossless on unix platforms, /// and wraps [`OsStr::to_string_lossy`] on non-unix platforms. pub fn os_str_as_bytes_lossy(os_string: &OsStr) -> Cow<'_, [u8]> { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] return Cow::from(os_string.as_bytes()); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] match os_string.to_string_lossy() { Cow::Borrowed(slice) => Cow::from(slice.as_bytes()), Cow::Owned(owned) => Cow::from(owned.into_bytes()), @@ -468,12 +470,12 @@ pub fn os_str_as_bytes_lossy(os_string: &OsStr) -> Cow<'_, [u8]> { /// /// This always succeeds on unix platforms, /// and fails on other platforms if the bytes can't be parsed as UTF-8. -#[cfg_attr(unix, expect(clippy::unnecessary_wraps))] +#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))] pub fn os_str_from_bytes(bytes: &[u8]) -> error::UResult> { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] return Ok(Cow::Borrowed(OsStr::from_bytes(bytes))); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] Ok(Cow::Owned(OsString::from(str::from_utf8(bytes).map_err( |_| error::UUsageError::new(1, "Unable to transform bytes into OsStr"), )?))) @@ -483,12 +485,12 @@ pub fn os_str_from_bytes(bytes: &[u8]) -> error::UResult> { /// /// This always succeeds on unix platforms, /// and fails on other platforms if the bytes can't be parsed as UTF-8. -#[cfg_attr(unix, expect(clippy::unnecessary_wraps))] +#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))] pub fn os_string_from_vec(vec: Vec) -> error::UResult { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] return Ok(OsString::from_vec(vec)); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] Ok(OsString::from(String::from_utf8(vec).map_err(|_| { error::UUsageError::new(1, "invalid UTF-8 was detected in one or more arguments") })?)) @@ -498,11 +500,11 @@ pub fn os_string_from_vec(vec: Vec) -> error::UResult { /// /// This always succeeds on unix platforms, /// and fails on other platforms if the bytes can't be parsed as UTF-8. -#[cfg_attr(unix, expect(clippy::unnecessary_wraps))] +#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))] pub fn os_string_to_vec(s: OsString) -> error::UResult> { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] let v = s.into_vec(); - #[cfg(not(unix))] + #[cfg(not(any(unix, target_os = "wasi")))] let v = s .into_string() .map_err(|_| { diff --git a/tests/by-util/test_echo.rs b/tests/by-util/test_echo.rs index 3398708f176..c3c9400995d 100644 --- a/tests/by-util/test_echo.rs +++ b/tests/by-util/test_echo.rs @@ -825,4 +825,14 @@ fn test_emoji_output() { .args(&["πŸ¦€", "loves", "πŸš€", "and", "🌟"]) .succeeds() .stdout_only("πŸ¦€ loves πŸš€ and 🌟\n"); + + new_ucmd!() + .arg("🍎,🍌,πŸ’,πŸ₯") + .succeeds() + .stdout_only("🍎,🍌,πŸ’,πŸ₯\n"); + + new_ucmd!() + .arg("γ“γ‚“γ«γ‘γ―δΈ–η•Œ") + .succeeds() + .stdout_only("γ“γ‚“γ«γ‘γ―δΈ–η•Œ\n"); }