Skip to content

Commit 0bfbbc0

Browse files
authored
Fix printenv non-UTF8 (#9728)
* printenv: Handle invalid UTF-8 encoding in variables * test(printenv): Add test for non-UTF8 content in variable
1 parent b9b9655 commit 0bfbbc0

File tree

2 files changed

+46
-12
lines changed

2 files changed

+46
-12
lines changed

src/uu/printenv/src/printenv.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6-
use clap::{Arg, ArgAction, Command};
76
use std::env;
8-
use uucore::translate;
9-
use uucore::{error::UResult, format_usage};
7+
use std::io::Write;
8+
9+
use clap::{Arg, ArgAction, Command};
10+
11+
use uucore::error::UResult;
12+
use uucore::line_ending::LineEnding;
13+
use uucore::{format_usage, os_str_as_bytes, translate};
1014

1115
static OPT_NULL: &str = "null";
1216

@@ -21,15 +25,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
2125
.map(|v| v.map(ToString::to_string).collect())
2226
.unwrap_or_default();
2327

24-
let separator = if matches.get_flag(OPT_NULL) {
25-
"\x00"
26-
} else {
27-
"\n"
28-
};
28+
let separator = LineEnding::from_zero_flag(matches.get_flag(OPT_NULL));
2929

3030
if variables.is_empty() {
31-
for (env_var, value) in env::vars() {
32-
print!("{env_var}={value}{separator}");
31+
for (env_var, value) in env::vars_os() {
32+
let env_bytes = os_str_as_bytes(&env_var)?;
33+
let val_bytes = os_str_as_bytes(&value)?;
34+
std::io::stdout().lock().write_all(env_bytes)?;
35+
print!("=");
36+
std::io::stdout().lock().write_all(val_bytes)?;
37+
print!("{separator}");
3338
}
3439
return Ok(());
3540
}
@@ -41,8 +46,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
4146
error_found = true;
4247
continue;
4348
}
44-
if let Ok(var) = env::var(env_var) {
45-
print!("{var}{separator}");
49+
if let Some(var) = env::var_os(env_var) {
50+
let val_bytes = os_str_as_bytes(&var)?;
51+
std::io::stdout().lock().write_all(val_bytes)?;
52+
print!("{separator}");
4653
} else {
4754
error_found = true;
4855
}

tests/by-util/test_printenv.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,30 @@ fn test_null_separator() {
9090
.stdout_is("FOO\x00VALUE\x00");
9191
}
9292
}
93+
94+
#[test]
95+
#[cfg(unix)]
96+
#[cfg(not(any(target_os = "freebsd", target_os = "android", target_os = "openbsd")))]
97+
fn test_non_utf8_value() {
98+
use std::ffi::OsStr;
99+
use std::os::unix::ffi::OsStrExt;
100+
// Environment variable values can contain non-UTF-8 bytes on Unix.
101+
// printenv should output them correctly, matching GNU behavior.
102+
// Reproduces: LD_PRELOAD=$'/tmp/lib.so\xff' printenv LD_PRELOAD
103+
let value_with_invalid_utf8 = OsStr::from_bytes(b"/tmp/lib.so\xff");
104+
105+
let result = new_ucmd!()
106+
.env("LD_PRELOAD", value_with_invalid_utf8)
107+
.arg("LD_PRELOAD")
108+
.run();
109+
110+
// Use byte-based assertions to avoid UTF-8 conversion issues
111+
// when the test framework tries to format error messages
112+
assert!(
113+
result.succeeded(),
114+
"Command failed with exit code: {:?}, stderr: {:?}",
115+
result.code(),
116+
String::from_utf8_lossy(result.stderr())
117+
);
118+
result.stdout_is_bytes(b"/tmp/lib.so\xff\n");
119+
}

0 commit comments

Comments
 (0)