-
Notifications
You must be signed in to change notification settings - Fork 59
Implement -fprint
#421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement -fprint
#421
Changes from all commits
63e6b33
236b9cd
c3ec477
e40ae10
0a6a9e9
9303508
823949c
b255b4e
3e1b180
2823662
d02b2c4
ff45c06
423291f
bc6b06f
315b42f
695e220
64a3416
10c3dd5
7726ad5
65d23fb
267db8a
da394ad
b38a963
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,15 +369,24 @@ 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]))); | ||
} | ||
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])?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For future reference: GNU find de-duplicates files, so if you do |
||
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"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
} | ||
} | ||
} | ||
|
||
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!( | ||
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 | ||
) | ||
.unwrap(); | ||
) { | ||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be a method on |
||
} | ||
} | ||
} | ||
out.flush().unwrap(); | ||
} | ||
} | ||
|
||
impl Matcher for Printer { | ||
fn matches(&self, file_info: &DirEntry, matcher_io: &mut MatcherIO) -> bool { | ||
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,12 +110,30 @@ 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!( | ||
fix_up_slashes("./test_data/simple/abbbc\0"), | ||
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()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn get_or_create_file(path: &str) -> Result<File, Box> {
let file = File::create(path)?;
Ok(file)
}
isn't good ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to give the user a more friendly prompt here? But yes, it is better to use "?" for now.
I would like to keep
get_or_create_file
function first, so that it is convenient to modify these unexpected behaviors in a unified way later. :)Changes have been committed at d02b2c4. thanks.