Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 6 additions & 38 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,17 @@ faccess = "0.2.4"
nix = { version = "0.31", features = ["fs", "user"] }
onig = { version = "6.5", default-features = false }
regex = "1.12"
uucore = { version = "0.10.0", features = ["entries", "fs", "fsext", "mode"] }
uucore = { version = "0.12.0", features = ["entries", "fs", "fsext", "mode"] }
walkdir = "2.5"
itertools = "0.15.0"
thiserror = "2.0.12"

# uucore depends on rustix without its `std` feature, which leaves
# `uucore::fs` unable to build for WASI. Turn it on through feature
# unification until uucore does it itself.
[target.'cfg(target_os = "wasi")'.dependencies]
rustix = { version = "1.1.4", default-features = false, features = ["std"] }

[dev-dependencies]
criterion = { version = "4.7.0", package = "codspeed-criterion-compat" }
assert_cmd = "2"
Expand Down
2 changes: 1 addition & 1 deletion src/find/matchers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1740,7 +1740,7 @@ mod tests {
fn build_top_level_matcher_perm_bad() {
let mut config = Config::default();
if let Err(e) = build_top_level_matcher(&["-perm", "foo"], &mut config) {
assert!(e.to_string().contains("invalid operator"));
assert_eq!(e.to_string(), "invalid mode 'foo'");
} else {
panic!("-perm with bad mode pattern should fail");
}
Expand Down
48 changes: 28 additions & 20 deletions src/find/matchers/perm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl ComparisonType {

#[cfg(unix)]
mod parsing {
use super::{parse_numeric, parse_symbolic, ComparisonType, Error};
use super::{parse_numeric, parse_symbolic, ComparisonType};

pub fn split_comparison_type(pattern: &str) -> (ComparisonType, &str) {
let mut chars = pattern.chars();
Expand All @@ -52,24 +52,20 @@ mod parsing {
}
}

pub fn parse_mode(pattern: &str, for_dir: bool) -> Result<u32, Box<dyn Error>> {
pub fn parse_mode(pattern: &str, for_dir: bool) -> Option<u32> {
// GNU rejects the old `-perm +MODE` octal form; a leading + needs a symbolic mode.
if let Some(rest) = pattern.strip_prefix('+') {
if rest.contains(|c: char| c.is_ascii_digit()) {
return Err(From::from(format!("invalid mode '+{rest}'")));
}
}

let mode = if pattern.contains(|c: char| c.is_ascii_digit()) {
parse_numeric(0, pattern, for_dir)?
let plus_octal = pattern
.strip_prefix('+')
.is_some_and(|rest| rest.contains(|c: char| c.is_ascii_digit()));
if plus_octal {
None
} else if pattern.contains(|c: char| c.is_ascii_digit()) {
parse_numeric(0, pattern, for_dir).ok()
} else {
let mut mode = 0;
for chunk in pattern.split(',') {
mode = parse_symbolic(mode, chunk, 0, for_dir)?;
}
mode
};
Ok(mode)
pattern.split(',').try_fold(0, |mode, chunk| {
parse_symbolic(mode, chunk, 0, for_dir).ok()
})
}
}
}

Expand All @@ -87,9 +83,11 @@ pub struct PermMatcher {}
impl PermMatcher {
#[cfg(unix)]
pub fn new(pattern: &str) -> Result<Self, Box<dyn Error>> {
let (comparison_type, pattern) = parsing::split_comparison_type(pattern);
let file_pattern = parsing::parse_mode(pattern, false)?;
let dir_pattern = parsing::parse_mode(pattern, false)?;
// Like GNU, name the whole argument rather than say what is wrong with it.
let invalid_mode = || format!("invalid mode '{pattern}'");
let (comparison_type, mode) = parsing::split_comparison_type(pattern);
let file_pattern = parsing::parse_mode(mode, false).ok_or_else(invalid_mode)?;
let dir_pattern = parsing::parse_mode(mode, false).ok_or_else(invalid_mode)?;
Ok(Self {
comparison_type,
file_pattern,
Expand Down Expand Up @@ -236,6 +234,16 @@ mod tests {
// .expect_err("missing comma should fail");
}

#[test]
fn parsing_failure_names_the_mode() {
// Whatever the parser choked on, the message is the same plain English
// line: it must not depend on uucore's localizer being set up.
for pattern in ["g+q", "a", "-0o9", "/u=z", "+17"] {
let error = PermMatcher::new(pattern).expect_err("mode should be rejected");
assert_eq!(error.to_string(), format!("invalid mode '{pattern}'"));
}
}

#[test]
fn comparison_type_matching() {
let c = ComparisonType::Exact;
Expand Down
Loading