mirror of
https://github.com/uutils/findutils.git
synced 2026-06-10 15:48:30 -07:00
Implement -fprint (#421)
This commit is contained in:
@@ -32,6 +32,7 @@ mod user;
|
||||
use ::regex::Regex;
|
||||
use chrono::{DateTime, Datelike, NaiveDateTime, Utc};
|
||||
use fs::FileSystemMatcher;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::time::SystemTime;
|
||||
use std::{error::Error, str::FromStr};
|
||||
@@ -202,7 +203,7 @@ pub fn build_top_level_matcher(
|
||||
if !top_level_matcher.has_side_effects() {
|
||||
let mut new_and_matcher = AndMatcherBuilder::new();
|
||||
new_and_matcher.new_and_condition(top_level_matcher);
|
||||
new_and_matcher.new_and_condition(Printer::new(PrintDelimiter::Newline));
|
||||
new_and_matcher.new_and_condition(Printer::new(PrintDelimiter::Newline, None));
|
||||
return Ok(new_and_matcher.build());
|
||||
}
|
||||
Ok(top_level_matcher)
|
||||
@@ -339,6 +340,13 @@ fn parse_str_to_newer_args(input: &str) -> Option<(String, String)> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a file if it doesn't exist.
|
||||
/// If it does exist, it will be overwritten.
|
||||
fn get_or_create_file(path: &str) -> Result<File, Box<dyn Error>> {
|
||||
let file = File::create(path)?;
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
/// The main "translate command-line args into a matcher" function. Will call
|
||||
/// itself recursively if it encounters an opening bracket. A successful return
|
||||
/// consists of a tuple containing the new index into the args array to use (if
|
||||
@@ -361,8 +369,8 @@ fn build_matcher_tree(
|
||||
let mut invert_next_matcher = false;
|
||||
while i < args.len() {
|
||||
let possible_submatcher = match args[i] {
|
||||
"-print" => Some(Printer::new(PrintDelimiter::Newline).into_box()),
|
||||
"-print0" => Some(Printer::new(PrintDelimiter::Null).into_box()),
|
||||
"-print" => Some(Printer::new(PrintDelimiter::Newline, None).into_box()),
|
||||
"-print0" => Some(Printer::new(PrintDelimiter::Null, None).into_box()),
|
||||
"-printf" => {
|
||||
if i >= args.len() - 1 {
|
||||
return Err(From::from(format!("missing argument to {}", args[i])));
|
||||
@@ -370,6 +378,15 @@ fn build_matcher_tree(
|
||||
i += 1;
|
||||
Some(Printf::new(args[i])?.into_box())
|
||||
}
|
||||
"-fprint" => {
|
||||
if i >= args.len() - 1 {
|
||||
return Err(From::from(format!("missing argument to {}", args[i])));
|
||||
}
|
||||
i += 1;
|
||||
|
||||
let file = get_or_create_file(args[i])?;
|
||||
Some(Printer::new(PrintDelimiter::Newline, Some(file)).into_box())
|
||||
}
|
||||
"-true" => Some(TrueMatcher.into_box()),
|
||||
"-false" => Some(FalseMatcher.into_box()),
|
||||
"-lname" | "-ilname" => {
|
||||
@@ -1518,4 +1535,30 @@ mod tests {
|
||||
.expect("-version should stop parsing");
|
||||
assert!(config.version_requested);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_or_create_file_test() {
|
||||
use std::fs;
|
||||
|
||||
// remove file if hard link file exist.
|
||||
// But you can't delete a file that doesn't exist,
|
||||
// so ignore the error returned here.
|
||||
let _ = fs::remove_file("test_data/get_or_create_file_test");
|
||||
|
||||
// test create file
|
||||
let file = get_or_create_file("test_data/get_or_create_file_test");
|
||||
assert!(file.is_ok());
|
||||
|
||||
let file = get_or_create_file("test_data/get_or_create_file_test");
|
||||
assert!(file.is_ok());
|
||||
|
||||
// test error when file no permission
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let result = get_or_create_file("/etc/shadow");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
let _ = fs::remove_file("test_data/get_or_create_file_test");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{stderr, Write},
|
||||
};
|
||||
|
||||
use walkdir::DirEntry;
|
||||
|
||||
use super::{Matcher, MatcherIO};
|
||||
@@ -25,25 +30,53 @@ impl std::fmt::Display for PrintDelimiter {
|
||||
/// This matcher just prints the name of the file to stdout.
|
||||
pub struct Printer {
|
||||
delimiter: PrintDelimiter,
|
||||
output_file: Option<File>,
|
||||
}
|
||||
|
||||
impl Printer {
|
||||
pub fn new(delimiter: PrintDelimiter) -> Self {
|
||||
Self { delimiter }
|
||||
pub fn new(delimiter: PrintDelimiter, output_file: Option<File>) -> Self {
|
||||
Self {
|
||||
delimiter,
|
||||
output_file,
|
||||
}
|
||||
}
|
||||
|
||||
fn print(&self, file_info: &DirEntry, mut out: impl Write, print_error_message: bool) {
|
||||
match write!(
|
||||
out,
|
||||
"{}{}",
|
||||
file_info.path().to_string_lossy(),
|
||||
self.delimiter
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
if print_error_message {
|
||||
writeln!(
|
||||
&mut stderr(),
|
||||
"Error writing {:?} for {}",
|
||||
file_info.path().to_string_lossy(),
|
||||
e
|
||||
)
|
||||
.unwrap();
|
||||
uucore::error::set_exit_code(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
out.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Matcher for Printer {
|
||||
fn matches(&self, file_info: &DirEntry, matcher_io: &mut MatcherIO) -> bool {
|
||||
let mut out = matcher_io.deps.get_output().borrow_mut();
|
||||
write!(
|
||||
out,
|
||||
"{}{}",
|
||||
file_info.path().to_string_lossy(),
|
||||
self.delimiter
|
||||
)
|
||||
.unwrap();
|
||||
out.flush().unwrap();
|
||||
if let Some(file) = &self.output_file {
|
||||
self.print(file_info, file, true);
|
||||
} else {
|
||||
self.print(
|
||||
file_info,
|
||||
&mut *matcher_io.deps.get_output().borrow_mut(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
@@ -64,7 +97,7 @@ mod tests {
|
||||
fn prints_newline() {
|
||||
let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
|
||||
|
||||
let matcher = Printer::new(PrintDelimiter::Newline);
|
||||
let matcher = Printer::new(PrintDelimiter::Newline, None);
|
||||
let deps = FakeDependencies::new();
|
||||
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
||||
assert_eq!(
|
||||
@@ -77,7 +110,7 @@ mod tests {
|
||||
fn prints_null() {
|
||||
let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
|
||||
|
||||
let matcher = Printer::new(PrintDelimiter::Null);
|
||||
let matcher = Printer::new(PrintDelimiter::Null, None);
|
||||
let deps = FakeDependencies::new();
|
||||
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
||||
assert_eq!(
|
||||
@@ -85,4 +118,22 @@ mod tests {
|
||||
deps.get_output_as_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn prints_error_message() {
|
||||
let dev_full = File::open("/dev/full").unwrap();
|
||||
let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
|
||||
|
||||
let matcher = Printer::new(PrintDelimiter::Newline, Some(dev_full));
|
||||
let deps = FakeDependencies::new();
|
||||
|
||||
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
||||
|
||||
// Reset the exit code global variable in case we run another test after this one
|
||||
// See https://github.com/uutils/coreutils/issues/5777
|
||||
uucore::error::set_exit_code(0);
|
||||
|
||||
assert!(deps.get_output_as_string().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1300,4 +1300,21 @@ mod tests {
|
||||
|
||||
assert_eq!(rc, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_fprint() {
|
||||
let deps = FakeDependencies::new();
|
||||
let rc = find_main(
|
||||
&[
|
||||
"find",
|
||||
"./test_data/simple",
|
||||
"-fprint",
|
||||
"test_data/find_fprint",
|
||||
],
|
||||
&deps,
|
||||
);
|
||||
assert_eq!(rc, 0);
|
||||
|
||||
let _ = fs::remove_file("test_data/find_fprint");
|
||||
}
|
||||
}
|
||||
|
||||
+24
-2
@@ -11,8 +11,8 @@
|
||||
use assert_cmd::Command;
|
||||
use predicates::prelude::*;
|
||||
use serial_test::serial;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::{env, io::ErrorKind};
|
||||
use tempfile::Builder;
|
||||
|
||||
@@ -946,3 +946,25 @@ fn find_daystart() {
|
||||
.success()
|
||||
.stderr(predicate::str::is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(working_dir)]
|
||||
fn find_fprint() {
|
||||
let _ = fs::remove_file("test_data/find_fprint");
|
||||
|
||||
Command::cargo_bin("find")
|
||||
.expect("found binary")
|
||||
.args(["test_data/simple", "-fprint", "test_data/find_fprint"])
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::is_empty())
|
||||
.stderr(predicate::str::is_empty());
|
||||
|
||||
// read test_data/find_fprint
|
||||
let mut f = File::open("test_data/find_fprint").unwrap();
|
||||
let mut contents = String::new();
|
||||
f.read_to_string(&mut contents).unwrap();
|
||||
assert!(contents.contains("test_data/simple"));
|
||||
|
||||
let _ = fs::remove_file("test_data/find_fprint");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user