From 3f227adc6120b5e3c9d953373d909d3ef254fab2 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 2 Apr 2026 21:36:07 +0200 Subject: [PATCH] uucore: use wasi OsStrExt/OsStringExt for byte-level access on wasm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WASI provides std::os::wasi::ffi::{OsStrExt, OsStringExt} with the same as_bytes()/from_bytes()/into_vec()/from_vec() API as Unix. Without this, os_str_as_bytes() fell into the not(unix) path requiring UTF-8 validation via to_str(), causing failures like: echo '🍎,🍌,🍒,🥝' => "invalid UTF-8 input encountered" --- src/uucore/src/lib/lib.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 4b2cb9b50..275486889 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(|_| {