Skip to content
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

csplit: support reading from pipe #6951

Merged
merged 9 commits into from
Jan 9, 2025
26 changes: 12 additions & 14 deletions src/uu/csplit/src/csplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ pub fn csplit<T>(options: &CsplitOptions, patterns: &[String], input: T) -> Resu
where
T: BufRead,
{
let mut input_iter = InputSplitter::new(input.lines().enumerate());
let enumerated_input_lines = input
.lines()
.map(|line| line.map_err_context(|| "read error".to_string()))
.enumerate();
let mut input_iter = InputSplitter::new(enumerated_input_lines);
let mut split_writer = SplitWriter::new(options);
let patterns: Vec<patterns::Pattern> = patterns::get_patterns(patterns)?;
let ret = do_csplit(&mut split_writer, patterns, &mut input_iter);
Expand Down Expand Up @@ -117,7 +121,7 @@ fn do_csplit<I>(
input_iter: &mut InputSplitter<I>,
) -> Result<(), CsplitError>
where
I: Iterator<Item = (usize, io::Result<String>)>,
I: Iterator<Item = (usize, UResult<String>)>,
{
// split the file based on patterns
for pattern in patterns {
Expand Down Expand Up @@ -305,7 +309,7 @@ impl SplitWriter<'_> {
input_iter: &mut InputSplitter<I>,
) -> Result<(), CsplitError>
where
I: Iterator<Item = (usize, io::Result<String>)>,
I: Iterator<Item = (usize, UResult<String>)>,
{
input_iter.rewind_buffer();
input_iter.set_size_of_buffer(1);
Expand Down Expand Up @@ -358,7 +362,7 @@ impl SplitWriter<'_> {
input_iter: &mut InputSplitter<I>,
) -> Result<(), CsplitError>
where
I: Iterator<Item = (usize, io::Result<String>)>,
I: Iterator<Item = (usize, UResult<String>)>,
{
if offset >= 0 {
// The offset is zero or positive, no need for a buffer on the lines read.
Expand Down Expand Up @@ -470,7 +474,7 @@ impl SplitWriter<'_> {
/// This is used to pass matching lines to the next split and to support patterns with a negative offset.
struct InputSplitter<I>
where
I: Iterator<Item = (usize, io::Result<String>)>,
I: Iterator<Item = (usize, UResult<String>)>,
{
iter: I,
buffer: Vec<<I as Iterator>::Item>,
Expand All @@ -483,7 +487,7 @@ where

impl<I> InputSplitter<I>
where
I: Iterator<Item = (usize, io::Result<String>)>,
I: Iterator<Item = (usize, UResult<String>)>,
{
fn new(iter: I) -> Self {
Self {
Expand Down Expand Up @@ -547,7 +551,7 @@ where

impl<I> Iterator for InputSplitter<I>
where
I: Iterator<Item = (usize, io::Result<String>)>,
I: Iterator<Item = (usize, UResult<String>)>,
{
type Item = <I as Iterator>::Item;

Expand Down Expand Up @@ -581,13 +585,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Ok(csplit(&options, &patterns, stdin.lock())?)
} else {
let file = File::open(file_name)
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
let file_metadata = file
.metadata()
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
if !file_metadata.is_file() {
return Err(CsplitError::NotRegularFile(file_name.to_string()).into());
}
.map_err_context(|| format!("cannot open {} for reading", file_name.quote()))?;
Ok(csplit(&options, &patterns, BufReader::new(file))?)
}
}
Expand Down
13 changes: 12 additions & 1 deletion src/uu/csplit/src/csplit_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub enum CsplitError {
SuffixFormatTooManyPercents,
#[error("{} is not a regular file", ._0.quote())]
NotRegularFile(String),
#[error("{}", _0)]
UError(Box<dyn UError>),
}

impl From<io::Error> for CsplitError {
Expand All @@ -43,8 +45,17 @@ impl From<io::Error> for CsplitError {
}
}

impl From<Box<dyn UError>> for CsplitError {
fn from(error: Box<dyn UError>) -> Self {
Self::UError(error)
}
}

impl UError for CsplitError {
fn code(&self) -> i32 {
1
match self {
Self::UError(e) => e.code(),
_ => 1,
}
}
}
51 changes: 50 additions & 1 deletion tests/by-util/test_csplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,7 @@ fn no_such_file() {
let (_, mut ucmd) = at_and_ucmd!();
ucmd.args(&["in", "0"])
.fails()
.stderr_contains("cannot access 'in': No such file or directory");
.stderr_contains("cannot open 'in' for reading: No such file or directory");
}

#[test]
Expand Down Expand Up @@ -1417,3 +1417,52 @@ fn repeat_everything() {
assert_eq!(at.read("xxz_004"), generate(37, 44 + 1));
assert_eq!(at.read("xxz_005"), generate(46, 50 + 1));
}

#[cfg(unix)]
#[test]
fn test_named_pipe_input_file() {
let (at, mut ucmd) = at_and_ucmd!();

let mut fifo_writer =
create_named_pipe_with_writer(&at.plus_as_string("fifo"), &generate(1, 51));

let result = ucmd.args(&["fifo", "10"]).succeeds();
fifo_writer.kill().unwrap();
fifo_writer.wait().unwrap();
result.stdout_only("18\n123\n");

let count = glob(&at.plus_as_string("xx*"))
.expect("there should be splits created")
.count();
assert_eq!(count, 2);
assert_eq!(at.read("xx00"), generate(1, 10));
assert_eq!(at.read("xx01"), generate(10, 51));
}

#[cfg(unix)]
fn create_named_pipe_with_writer(path: &str, data: &str) -> std::process::Child {
// cSpell:ignore IRWXU
nix::unistd::mkfifo(path, nix::sys::stat::Mode::S_IRWXU).unwrap();
std::process::Command::new("sh")
.arg("-c")
.arg(format!("printf '{}' > {path}", data))
.spawn()
.unwrap()
}

#[test]
fn test_directory_input_file() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("test_directory");

#[cfg(unix)]
ucmd.args(&["test_directory", "1"])
.fails()
.code_is(1)
.stderr_only("csplit: read error: Is a directory\n");
#[cfg(windows)]
ucmd.args(&["test_directory", "1"])
.fails()
.code_is(1)
.stderr_only("csplit: cannot open 'test_directory' for reading: Permission denied\n");
}
Loading