From 76bbc1df6a0519606b0ffdcc3d98f163e0fa01d6 Mon Sep 17 00:00:00 2001 From: Anthony DePasquale Date: Fri, 2 Oct 2026 19:21:02 +0200 Subject: [PATCH] sed: write appended text, R text and e output as GNU sed does --- src/sed/fast_io.rs | 19 ++++++--- src/sed/processor.rs | 4 +- tests/by-util/test_sed.rs | 83 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/src/sed/fast_io.rs b/src/sed/fast_io.rs index 5ea8bf3d..1fe73c87 100644 --- a/src/sed/fast_io.rs +++ b/src/sed/fast_io.rs @@ -678,13 +678,20 @@ impl OutputBuffer { ))) } - /// Copy the specified file to the output. - pub fn copy_file(&mut self, path: &PathBuf) -> io::Result<()> { - // Flush mmap writes, if any. + /// Schedule the specified text for output unchanged, after any + /// deferred newline. + pub fn write_raw(&mut self, bytes: &[u8]) -> io::Result<()> { + self.flush_pending_newline()?; #[cfg(unix)] - { - self.flush_mmap(WriteRange::Complete)?; - } + self.flush_mmap(WriteRange::Complete)?; + self.out.write_all(bytes) + } + + /// Copy the specified file to the output, after any deferred newline. + pub fn copy_file(&mut self, path: &PathBuf) -> io::Result<()> { + // As in GNU sed, write the deferred newline even when the file is + // empty or cannot be read. + self.write_raw(b"")?; let Ok(file) = File::open(path) else { // Per POSIX, if the file can't be read treat it as empty. diff --git a/src/sed/processor.rs b/src/sed/processor.rs index 298a974e..7f3dc42b 100644 --- a/src/sed/processor.rs +++ b/src/sed/processor.rs @@ -467,7 +467,7 @@ fn flush_appends(output: &mut OutputBuffer, context: &mut ProcessingContext) -> for elem in &context.append_elements { match elem { AppendElement::Text(text) => { - output.write_bytes(text.as_ref())?; + output.write_raw(text.as_ref())?; } AppendElement::Path(path) => { output.copy_file(path)?; @@ -738,7 +738,7 @@ fn process_file( } CommandData::Text(cmd_bytes) => { let shell_out = shell_stdout(cmd_bytes.to_vec(), &command, context)?; - output.write_bytes(&shell_out)?; + output.write_raw(&shell_out)?; } _ => panic!("invalid 'e' command data"), }, diff --git a/tests/by-util/test_sed.rs b/tests/by-util/test_sed.rs index 5597d2fe..10477a7f 100644 --- a/tests/by-util/test_sed.rs +++ b/tests/by-util/test_sed.rs @@ -3132,3 +3132,86 @@ fn test_posix_reject_flags() { .code_is(1) .stderr_is("sed: