diff --git a/src/uu/sum/src/sum.rs b/src/uu/sum/src/sum.rs index 2366a59d3..5832df778 100644 --- a/src/uu/sum/src/sum.rs +++ b/src/uu/sum/src/sum.rs @@ -6,6 +6,7 @@ // spell-checker:ignore (ToDO) sysv use clap::{Arg, ArgAction, Command}; +use std::ffi::OsString; use std::fs::File; use std::io::{ErrorKind, Read, Write, stdin, stdout}; use std::path::Path; @@ -67,27 +68,26 @@ fn sysv_sum(mut reader: impl Read) -> std::io::Result<(usize, u16)> { Ok((blocks_read, ret as u16)) } -fn open(name: &str) -> UResult> { - match name { - "-" => Ok(Box::new(stdin()) as Box), - _ => { - let path = &Path::new(name); - if path.is_dir() { - return Err(USimpleError::new( - 2, - translate!("sum-error-is-directory", "name" => name.maybe_quote()), - )); - } - // Silent the warning as we want to the error message - if path.metadata().is_err() { - return Err(USimpleError::new( - 2, - translate!("sum-error-no-such-file-or-directory", "name" => name.maybe_quote()), - )); - } - let f = File::open(path).map_err_context(String::new)?; - Ok(Box::new(f) as Box) +fn open(name: &OsString) -> UResult> { + if name == "-" { + Ok(Box::new(stdin()) as Box) + } else { + let path = Path::new(name); + if path.is_dir() { + return Err(USimpleError::new( + 2, + translate!("sum-error-is-directory", "name" => name.to_string_lossy().maybe_quote()), + )); } + // Silent the warning as we want to the error message + if path.metadata().is_err() { + return Err(USimpleError::new( + 2, + translate!("sum-error-no-such-file-or-directory", "name" => name.to_string_lossy().maybe_quote()), + )); + } + let f = File::open(path).map_err_context(String::new)?; + Ok(Box::new(f) as Box) } } @@ -101,9 +101,9 @@ mod options { pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().get_matches_from_localized(args); - let files: Vec = match matches.get_many::(options::FILE) { + let files: Vec = match matches.get_many::(options::FILE) { Some(v) => v.cloned().collect(), - None => vec!["-".to_owned()], + None => vec![OsString::from("-")], }; let sysv = matches.get_flag(options::SYSTEM_V_COMPATIBLE); @@ -127,7 +127,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let mut stdout = stdout().lock(); if print_names { - writeln!(stdout, "{sum:0width$} {blocks:width$} {file}")?; + writeln!( + stdout, + "{sum:0width$} {blocks:width$} {}", + file.to_string_lossy() + )?; } else { writeln!(stdout, "{sum:0width$} {blocks:width$}")?; } @@ -146,7 +150,8 @@ pub fn uu_app() -> Command { Arg::new(options::FILE) .action(ArgAction::Append) .hide(true) - .value_hint(clap::ValueHint::FilePath), + .value_hint(clap::ValueHint::FilePath) + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(options::BSD_COMPATIBLE) diff --git a/tests/by-util/test_sum.rs b/tests/by-util/test_sum.rs index 89c454aff..d4e4e8c55 100644 --- a/tests/by-util/test_sum.rs +++ b/tests/by-util/test_sum.rs @@ -2,6 +2,8 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +#[cfg(target_os = "linux")] +use std::os::unix::ffi::OsStringExt; use uutests::at_and_ucmd; use uutests::new_ucmd; @@ -80,3 +82,14 @@ fn test_invalid_metadata() { .fails() .stderr_is("sum: b: No such file or directory\n"); } + +#[test] +#[cfg(target_os = "linux")] +fn test_sum_non_utf8_paths() { + let (at, mut ucmd) = at_and_ucmd!(); + + let filename = std::ffi::OsString::from_vec(vec![0xFF, 0xFE]); + std::fs::write(at.plus(&filename), b"test content").unwrap(); + + ucmd.arg(&filename).succeeds(); +}