@@ -21,7 +21,9 @@ use std::io::{BufRead, BufReader, BufWriter, ErrorKind, Read, Seek, SeekFrom, Wr
2121use std:: path:: Path ;
2222use thiserror:: Error ;
2323use uucore:: display:: Quotable ;
24- use uucore:: error:: { FromIo , UIoError , UResult , USimpleError , UUsageError } ;
24+ use uucore:: error:: {
25+ FromIo , UIoError , UResult , USimpleError , UUsageError , set_exit_code, strip_errno,
26+ } ;
2527use uucore:: translate;
2628
2729use uucore:: parser:: parse_size:: parse_size_u64;
@@ -565,11 +567,54 @@ fn ignorable_io_error(error: &io::Error, settings: &Settings) -> bool {
565567/// If ignorable io error occurs, return number of bytes as if all bytes written
566568/// Should not be used for Kth chunk number sub-strategies
567569/// as those do not work with `--filter` option
568- fn custom_write < T : Write > ( bytes : & [ u8 ] , writer : & mut T , settings : & Settings ) -> io:: Result < usize > {
570+ ///
571+ /// When `error_context` is None, acts as simple write wrapper
572+ /// When `error_context` is Some((filename, error_reported)), enables error reporting and immediate flushing
573+ fn custom_write < T : Write > (
574+ bytes : & [ u8 ] ,
575+ writer : & mut T ,
576+ settings : & Settings ,
577+ error_context : Option < ( & str , & mut bool ) > ,
578+ ) -> io:: Result < usize > {
569579 match writer. write ( bytes) {
570- Ok ( n) => Ok ( n) ,
580+ Ok ( n) => {
581+ // If error reporting is enabled, flush immediately to catch buffered I/O errors
582+ if let Some ( ( filename, error_reported) ) = error_context {
583+ match writer. flush ( ) {
584+ Ok ( ( ) ) => Ok ( n) ,
585+ Err ( e) if ignorable_io_error ( & e, settings) => Ok ( bytes. len ( ) ) ,
586+ Err ( e) => {
587+ if !* error_reported {
588+ uucore:: show_error!( "{}: {}" , filename, strip_errno( & e) ) ;
589+ set_exit_code ( 1 ) ;
590+ * error_reported = true ;
591+ }
592+ Err ( io:: Error :: new (
593+ ErrorKind :: Other ,
594+ translate ! ( "split-error-flush-already-reported" ) ,
595+ ) )
596+ }
597+ }
598+ } else {
599+ Ok ( n)
600+ }
601+ }
571602 Err ( e) if ignorable_io_error ( & e, settings) => Ok ( bytes. len ( ) ) ,
572- Err ( e) => Err ( e) ,
603+ Err ( e) => {
604+ if let Some ( ( filename, error_reported) ) = error_context {
605+ if !* error_reported {
606+ uucore:: show_error!( "{}: {}" , filename, strip_errno( & e) ) ;
607+ set_exit_code ( 1 ) ;
608+ * error_reported = true ;
609+ }
610+ Err ( io:: Error :: new (
611+ ErrorKind :: Other ,
612+ translate ! ( "split-error-write-already-reported" ) ,
613+ ) )
614+ } else {
615+ Err ( e)
616+ }
617+ }
573618 }
574619}
575620
@@ -713,6 +758,12 @@ struct ByteChunkWriter<'a> {
713758
714759 /// Iterator that yields filenames for each chunk.
715760 filename_iterator : FilenameIterator < ' a > ,
761+
762+ /// Current filename being written to.
763+ current_filename : String ,
764+
765+ /// Whether an error has already been reported for the current file.
766+ error_reported : bool ,
716767}
717768
718769impl < ' a > ByteChunkWriter < ' a > {
@@ -732,10 +783,28 @@ impl<'a> ByteChunkWriter<'a> {
732783 num_chunks_written : 0 ,
733784 inner,
734785 filename_iterator,
786+ current_filename : filename,
787+ error_reported : false ,
735788 } )
736789 }
737790}
738791
792+ impl Drop for ByteChunkWriter < ' _ > {
793+ fn drop ( & mut self ) {
794+ // Ensure final flush to catch any buffered write errors, but only report if not already reported
795+ if !self . error_reported {
796+ if let Err ( e) = self . inner . flush ( ) {
797+ uucore:: show_error!(
798+ "split: {}: final flush failed: {}" ,
799+ self . current_filename,
800+ strip_errno( & e)
801+ ) ;
802+ set_exit_code ( 1 ) ;
803+ }
804+ }
805+ }
806+ }
807+
739808impl Write for ByteChunkWriter < ' _ > {
740809 /// Implements `--bytes=SIZE`
741810 fn write ( & mut self , mut buf : & [ u8 ] ) -> io:: Result < usize > {
@@ -751,6 +820,16 @@ impl Write for ByteChunkWriter<'_> {
751820 }
752821
753822 if self . num_bytes_remaining_in_current_chunk == 0 {
823+ // Flush the current writer before switching to a new file to catch any delayed write errors
824+ if let Err ( e) = self . inner . flush ( ) {
825+ uucore:: show_error!( "{}: {}" , self . current_filename, strip_errno( & e) ) ;
826+ set_exit_code ( 1 ) ;
827+ return Err ( io:: Error :: new (
828+ ErrorKind :: Other ,
829+ translate ! ( "split-error-flush-before-file-switch" ) ,
830+ ) ) ;
831+ }
832+
754833 // Increment the chunk number, reset the number of bytes remaining, and instantiate the new underlying writer.
755834 self . num_chunks_written += 1 ;
756835 self . num_bytes_remaining_in_current_chunk = self . chunk_size ;
@@ -763,6 +842,8 @@ impl Write for ByteChunkWriter<'_> {
763842 println ! ( "creating file {}" , filename. quote( ) ) ;
764843 }
765844 self . inner = self . settings . instantiate_current_writer ( & filename, true ) ?;
845+ self . current_filename = filename;
846+ self . error_reported = false ;
766847 }
767848
768849 // If the capacity of this chunk is greater than the number of
@@ -771,7 +852,12 @@ impl Write for ByteChunkWriter<'_> {
771852 // the chunk number and repeat.
772853 let buf_len = buf. len ( ) ;
773854 if ( buf_len as u64 ) < self . num_bytes_remaining_in_current_chunk {
774- let num_bytes_written = custom_write ( buf, & mut self . inner , self . settings ) ?;
855+ let num_bytes_written = custom_write (
856+ buf,
857+ & mut self . inner ,
858+ self . settings ,
859+ Some ( ( & self . current_filename , & mut self . error_reported ) ) ,
860+ ) ?;
775861 self . num_bytes_remaining_in_current_chunk -= num_bytes_written as u64 ;
776862 return Ok ( carryover_bytes_written + num_bytes_written) ;
777863 }
@@ -782,7 +868,12 @@ impl Write for ByteChunkWriter<'_> {
782868 // self.num_bytes_remaining_in_current_chunk is lower than
783869 // n, which is already usize.
784870 let i = self . num_bytes_remaining_in_current_chunk as usize ;
785- let num_bytes_written = custom_write ( & buf[ ..i] , & mut self . inner , self . settings ) ?;
871+ let num_bytes_written = custom_write (
872+ & buf[ ..i] ,
873+ & mut self . inner ,
874+ self . settings ,
875+ Some ( ( & self . current_filename , & mut self . error_reported ) ) ,
876+ ) ?;
786877 self . num_bytes_remaining_in_current_chunk -= num_bytes_written as u64 ;
787878
788879 // It's possible that the underlying writer did not
@@ -799,7 +890,17 @@ impl Write for ByteChunkWriter<'_> {
799890 }
800891 }
801892 fn flush ( & mut self ) -> io:: Result < ( ) > {
802- self . inner . flush ( )
893+ match self . inner . flush ( ) {
894+ Ok ( ( ) ) => Ok ( ( ) ) ,
895+ Err ( e) => {
896+ uucore:: show_error!( "{}: {}" , self . current_filename, strip_errno( & e) ) ;
897+ set_exit_code ( 1 ) ;
898+ Err ( io:: Error :: new (
899+ ErrorKind :: Other ,
900+ translate ! ( "split-error-flush-in-chunk-writer" ) ,
901+ ) )
902+ }
903+ }
803904 }
804905}
805906
@@ -891,7 +992,8 @@ impl Write for LineChunkWriter<'_> {
891992 // Write the line, starting from *after* the previous
892993 // separator character and ending *after* the current
893994 // separator character.
894- let num_bytes_written = custom_write ( & buf[ prev..=i] , & mut self . inner , self . settings ) ?;
995+ let num_bytes_written =
996+ custom_write ( & buf[ prev..=i] , & mut self . inner , self . settings , None ) ?;
895997 total_bytes_written += num_bytes_written;
896998 prev = i + 1 ;
897999 self . num_lines_remaining_in_current_chunk -= 1 ;
@@ -907,7 +1009,7 @@ impl Write for LineChunkWriter<'_> {
9071009 self . num_lines_remaining_in_current_chunk = self . chunk_size ;
9081010 }
9091011 let num_bytes_written =
910- custom_write ( & buf[ prev..buf. len ( ) ] , & mut self . inner , self . settings ) ?;
1012+ custom_write ( & buf[ prev..buf. len ( ) ] , & mut self . inner , self . settings , None ) ?;
9111013 total_bytes_written += num_bytes_written;
9121014 }
9131015 Ok ( total_bytes_written)
@@ -1606,7 +1708,15 @@ fn split(settings: &Settings) -> UResult<()> {
16061708 // allowable filenames, we use `ErrorKind::Other` to
16071709 // indicate that. A special error message needs to be
16081710 // printed in that case.
1609- ErrorKind :: Other => Err ( USimpleError :: new ( 1 , format ! ( "{e}" ) ) ) ,
1711+ ErrorKind :: Other => {
1712+ let error_msg = format ! ( "{e}" ) ;
1713+ if error_msg. is_empty ( ) {
1714+ // This is a handled error, return error to stop processing
1715+ Err ( USimpleError :: new ( 1 , "" ) )
1716+ } else {
1717+ Err ( USimpleError :: new ( 1 , error_msg) )
1718+ }
1719+ }
16101720 _ => Err ( uio_error ! (
16111721 e,
16121722 "{}" ,
0 commit comments