diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index 162e524d960..70bd0315923 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -24,13 +24,13 @@ use nix::sys::signal::{SigHandler::SigIgn, Signal, signal}; use std::borrow::Cow; use std::env; use std::ffi::{OsStr, OsString}; -use std::io::{self, Write}; +use std::io; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; #[cfg(unix)] use std::os::unix::process::CommandExt; -use uucore::display::{OsWrite, Quotable}; +use uucore::display::{Quotable, print_all_env_vars}; use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError}; use uucore::line_ending::LineEnding; #[cfg(unix)] @@ -99,19 +99,6 @@ struct Options<'a> { ignore_signal: Vec, } -/// print `name=value` env pairs on screen -fn print_env(line_ending: LineEnding) -> io::Result<()> { - let stdout_raw = io::stdout(); - let mut stdout = stdout_raw.lock(); - for (n, v) in env::vars_os() { - stdout.write_all_os(&n)?; - stdout.write_all(b"=")?; - stdout.write_all_os(&v)?; - write!(stdout, "{line_ending}")?; - } - Ok(()) -} - fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a OsStr) -> UResult { // is it a NAME=VALUE like opt ? let wrap = NativeStr::<'a>::new(opt); @@ -552,7 +539,7 @@ impl EnvAppData { if opts.program.is_empty() { // no program provided, so just dump all env vars to stdout - print_env(opts.line_ending)?; + print_all_env_vars(opts.line_ending)?; } else { return self.run_program(&opts, self.do_debug_printing); } diff --git a/src/uu/printenv/src/printenv.rs b/src/uu/printenv/src/printenv.rs index fb022474875..bfdf6934cd7 100644 --- a/src/uu/printenv/src/printenv.rs +++ b/src/uu/printenv/src/printenv.rs @@ -8,9 +8,10 @@ use std::io::Write; use clap::{Arg, ArgAction, Command}; +use uucore::display::{OsWrite, print_all_env_vars}; use uucore::error::UResult; use uucore::line_ending::LineEnding; -use uucore::{format_usage, os_str_as_bytes, translate}; +use uucore::{format_usage, translate}; static OPT_NULL: &str = "null"; @@ -28,14 +29,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let separator = LineEnding::from_zero_flag(matches.get_flag(OPT_NULL)); if variables.is_empty() { - for (env_var, value) in env::vars_os() { - let env_bytes = os_str_as_bytes(&env_var)?; - let val_bytes = os_str_as_bytes(&value)?; - std::io::stdout().lock().write_all(env_bytes)?; - print!("="); - std::io::stdout().lock().write_all(val_bytes)?; - print!("{separator}"); - } + print_all_env_vars(separator)?; return Ok(()); } @@ -47,9 +41,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { continue; } if let Some(var) = env::var_os(env_var) { - let val_bytes = os_str_as_bytes(&var)?; - std::io::stdout().lock().write_all(val_bytes)?; - print!("{separator}"); + let mut stdout = std::io::stdout().lock(); + stdout.write_all_os(&var)?; + write!(stdout, "{separator}")?; } else { error_found = true; } diff --git a/src/uucore/src/lib/mods/display.rs b/src/uucore/src/lib/mods/display.rs index 78ffe7a4f7e..ee259ef596c 100644 --- a/src/uucore/src/lib/mods/display.rs +++ b/src/uucore/src/lib/mods/display.rs @@ -24,7 +24,9 @@ //! # Ok::<(), std::io::Error>(()) //! ``` +use std::env; use std::ffi::OsStr; +use std::fmt; use std::fs::File; use std::io::{self, BufWriter, Stdout, StdoutLock, Write as IoWrite}; @@ -117,3 +119,18 @@ impl OsWrite for Box { this.write_all_os(buf) } } + +/// Print all environment variables in the format `name=value` with the specified line ending. +/// +/// This function handles non-UTF-8 environment variable names and values correctly by using +/// raw bytes on Unix systems. +pub fn print_all_env_vars(line_ending: T) -> io::Result<()> { + let mut stdout = io::stdout().lock(); + for (name, value) in env::vars_os() { + stdout.write_all_os(&name)?; + stdout.write_all(b"=")?; + stdout.write_all_os(&value)?; + write!(stdout, "{line_ending}")?; + } + Ok(()) +} diff --git a/tests/by-util/test_printenv.rs b/tests/by-util/test_printenv.rs index 71f22c98407..28ca045bd5c 100644 --- a/tests/by-util/test_printenv.rs +++ b/tests/by-util/test_printenv.rs @@ -117,3 +117,16 @@ fn test_non_utf8_value() { ); result.stdout_is_bytes(b"/tmp/lib.so\xff\n"); } + +#[test] +#[cfg(unix)] +fn test_non_utf8_env_vars() { + use std::ffi::OsString; + use std::os::unix::ffi::OsStringExt; + + let non_utf8_value = OsString::from_vec(b"hello\x80world".to_vec()); + new_ucmd!() + .env("NON_UTF8_VAR", &non_utf8_value) + .succeeds() + .stdout_contains_bytes(b"NON_UTF8_VAR=hello\x80world"); +}