rm: support non-UTF-8 paths

Addresses #4868.
This commit is contained in:
Jed Denlea 2023-05-16 19:20:24 -07:00
parent d458f3e594
commit db5696f64b
2 changed files with 40 additions and 5 deletions

View file

@ -7,8 +7,9 @@
// spell-checker:ignore (path) eacces // spell-checker:ignore (path) eacces
use clap::{crate_version, parser::ValueSource, Arg, ArgAction, Command}; use clap::{builder::ValueParser, crate_version, parser::ValueSource, Arg, ArgAction, Command};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ffi::{OsStr, OsString};
use std::fs::{self, File, Metadata}; use std::fs::{self, File, Metadata};
use std::io::ErrorKind; use std::io::ErrorKind;
use std::ops::BitOr; use std::ops::BitOr;
@ -59,9 +60,9 @@ static ARG_FILES: &str = "files";
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().after_help(AFTER_HELP).try_get_matches_from(args)?; let matches = uu_app().after_help(AFTER_HELP).try_get_matches_from(args)?;
let files: Vec<String> = matches let files: Vec<&OsStr> = matches
.get_many::<String>(ARG_FILES) .get_many::<OsString>(ARG_FILES)
.map(|v| v.map(ToString::to_string).collect()) .map(|v| v.map(OsString::as_os_str).collect())
.unwrap_or_default(); .unwrap_or_default();
let force_flag = matches.get_flag(OPT_FORCE); let force_flag = matches.get_flag(OPT_FORCE);
@ -231,13 +232,14 @@ pub fn uu_app() -> Command {
.arg( .arg(
Arg::new(ARG_FILES) Arg::new(ARG_FILES)
.action(ArgAction::Append) .action(ArgAction::Append)
.value_parser(ValueParser::os_string())
.num_args(1..) .num_args(1..)
.value_hint(clap::ValueHint::AnyPath), .value_hint(clap::ValueHint::AnyPath),
) )
} }
// TODO: implement one-file-system (this may get partially implemented in walkdir) // TODO: implement one-file-system (this may get partially implemented in walkdir)
fn remove(files: &[String], options: &Options) -> bool { fn remove(files: &[&OsStr], options: &Options) -> bool {
let mut had_err = false; let mut had_err = false;
for filename in files { for filename in files {

View file

@ -54,6 +54,8 @@ fn test_rm_interactive() {
at.touch(file_a); at.touch(file_a);
at.touch(file_b); at.touch(file_b);
assert!(at.file_exists(file_a));
assert!(at.file_exists(file_b));
scene scene
.ucmd() .ucmd()
@ -84,6 +86,11 @@ fn test_rm_force() {
let file_a = "test_rm_force_a"; let file_a = "test_rm_force_a";
let file_b = "test_rm_force_b"; let file_b = "test_rm_force_b";
at.touch(file_a);
at.touch(file_b);
assert!(at.file_exists(file_a));
assert!(at.file_exists(file_b));
ucmd.arg("-f") ucmd.arg("-f")
.arg(file_a) .arg(file_a)
.arg(file_b) .arg(file_b)
@ -100,6 +107,11 @@ fn test_rm_force_multiple() {
let file_a = "test_rm_force_a"; let file_a = "test_rm_force_a";
let file_b = "test_rm_force_b"; let file_b = "test_rm_force_b";
at.touch(file_a);
at.touch(file_b);
assert!(at.file_exists(file_a));
assert!(at.file_exists(file_b));
ucmd.arg("-f") ucmd.arg("-f")
.arg("-f") .arg("-f")
.arg("-f") .arg("-f")
@ -578,3 +590,24 @@ fn test_fifo_removal() {
.timeout(Duration::from_secs(2)) .timeout(Duration::from_secs(2))
.succeeds(); .succeeds();
} }
#[test]
#[cfg(any(unix, target_os = "wasi"))]
#[cfg(not(target_os = "macos"))]
fn test_non_utf8() {
use std::ffi::OsStr;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
#[cfg(target_os = "wasi")]
use std::os::wasi::ffi::OsStrExt;
let file = OsStr::from_bytes(b"not\xffutf8"); // spell-checker:disable-line
let (at, mut ucmd) = at_and_ucmd!();
at.touch(file);
assert!(at.file_exists(file));
ucmd.arg(file).succeeds();
assert!(!at.file_exists(file));
}