Repository: uutils/coreutils (language: rust)

Issue:
csplit: panic when empty file is suppressed with % pattern with offset
Environment: Ubuntu 24.04, uutils `main` branch (git commit c2505841e005da57f4c5ff9d3e09ffb5dc9ca2b8), GNU coreutils v9.4.

Steps to reproduce:
```
echo abc > f
csplit -z f %abc%1
```
(`-z` means suppress empty files, `%abc%` means scan to the first line that matches the pattern `abc`, and `1` means advance by one more line after the match is found.)

What happens now: uutils `csplit` panics with the following message:
```
thread 'main' panicked at src/uu/csplit/src/csplit.rs:205:36:
Failed to elide split: Os { code: 2, kind: NotFound, message: "No such file or directory" }
```

What I expected to happen: GNU `csplit` succeeds with no output.

Notes: this is causing the GNU test file `tests/csplit/csplit-heap.sh` to be skipped.


Discussion / hints:

Fix patch:
diff --git a/src/uu/csplit/src/csplit.rs b/src/uu/csplit/src/csplit.rs
index 501f97582ec..b0005e75ed7 100644
--- a/src/uu/csplit/src/csplit.rs
+++ b/src/uu/csplit/src/csplit.rs
@@ -202,7 +202,12 @@ impl Drop for SplitWriter<'_> {
     fn drop(&mut self) {
         if self.options.elide_empty_files && self.size == 0 {
             let file_name = self.options.split_name.get(self.counter);
-            remove_file(file_name).expect("Failed to elide split");
+            // In the case of `echo a | csplit -z - %a%1`, the file
+            // `xx00` does not exist because the positive offset
+            // advanced past the end of the input. Since there is no
+            // file to remove in that case, `remove_file` would return
+            // an error, so we just ignore it.
+            let _ = remove_file(file_name);
         }
     }
 }


Test patch:
diff --git a/tests/by-util/test_csplit.rs b/tests/by-util/test_csplit.rs
index e062b6d551f..7c0036dedc1 100644
--- a/tests/by-util/test_csplit.rs
+++ b/tests/by-util/test_csplit.rs
@@ -335,6 +335,16 @@ fn test_skip_to_match_offset() {
     }
 }
 
+#[test]
+fn test_skip_to_match_offset_suppress_empty() {
+    let (at, mut ucmd) = at_and_ucmd!();
+    ucmd.args(&["-z", "-", "%a%1"])
+        .pipe_in("a\n")
+        .succeeds()
+        .no_output();
+    assert!(!at.file_exists("xx00"));
+}
+
 #[test]
 fn test_skip_to_match_negative_offset() {
     let (at, mut ucmd) = at_and_ucmd!();