1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

Merge pull request #6951 from fuad1502/csplit-pipe-input

csplit: support reading from pipe
This commit is contained in:
Daniel Hofstetter 2025-01-09 15:02:10 +01:00 committed by GitHub
commit 50571c7346
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 76 additions and 18 deletions

View file

@ -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);
@ -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 {
@ -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);
@ -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.
@ -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>,
@ -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 {
@ -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;
@ -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))?)
}
}

View file

@ -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 {
@ -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,
}
}
}

View file

@ -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]
@ -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");
}