@@ -14,8 +14,7 @@ use crate::options::{
1414use crate :: units:: { Result , Unit } ;
1515use clap:: { Arg , ArgAction , ArgMatches , Command , builder:: ValueParser , parser:: ValueSource } ;
1616use std:: ffi:: OsString ;
17- use std:: io:: { BufRead , Error , Write as _, stderr} ;
18- use std:: result:: Result as StdResult ;
17+ use std:: io:: { BufRead , Write as _, stderr} ;
1918use std:: str:: FromStr ;
2019
2120use units:: { IEC_BASES , SI_BASES } ;
@@ -33,44 +32,59 @@ mod units;
3332
3433fn handle_args < ' a > ( args : impl Iterator < Item = & ' a [ u8 ] > , options : & NumfmtOptions ) -> UResult < ( ) > {
3534 let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
35+ let terminator = if options. zero_terminated { 0u8 } else { b'\n' } ;
3636 for l in args {
37- write_line ( & mut stdout, l, options) ?;
37+ write_line ( & mut stdout, l, options, Some ( terminator ) ) ?;
3838 }
3939 Ok ( ( ) )
4040}
4141
42- fn handle_buffer < R > ( input : R , options : & NumfmtOptions ) -> UResult < ( ) >
43- where
44- R : BufRead ,
45- {
42+ fn handle_buffer < R : BufRead > ( mut input : R , options : & NumfmtOptions ) -> UResult < ( ) > {
4643 let terminator = if options. zero_terminated { 0u8 } else { b'\n' } ;
47- handle_buffer_iterator ( input. split ( terminator) , options, terminator)
48- }
49-
50- fn handle_buffer_iterator (
51- iter : impl Iterator < Item = StdResult < Vec < u8 > , Error > > ,
52- options : & NumfmtOptions ,
53- terminator : u8 ,
54- ) -> UResult < ( ) > {
5544 let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
56- for ( idx, line_result) in iter. enumerate ( ) {
57- match line_result {
58- Ok ( line) if idx < options. header => {
59- stdout. write_all ( & line) ?;
60- stdout. write_all ( & [ terminator] ) ?;
61- Ok ( ( ) )
45+ let mut buf = Vec :: new ( ) ;
46+ let mut idx = 0 ;
47+
48+ loop {
49+ buf. clear ( ) ;
50+ let n = input
51+ . read_until ( terminator, & mut buf)
52+ . map_err ( |e| NumfmtError :: IoError ( e. to_string ( ) ) ) ?;
53+ if n == 0 {
54+ break ;
55+ }
56+
57+ let has_terminator = buf. last ( ) == Some ( & terminator) ;
58+ let line = if has_terminator {
59+ & buf[ ..buf. len ( ) - 1 ]
60+ } else {
61+ & buf[ ..]
62+ } ;
63+
64+ // Emit the terminator only if the input line had one.
65+ // i.e. if the last line of the input does not end with a newline, we should not add one.
66+ let eol = has_terminator. then_some ( terminator) ;
67+
68+ if idx < options. header {
69+ stdout. write_all ( line) ?;
70+ if let Some ( t) = eol {
71+ stdout. write_all ( & [ t] ) ?;
6272 }
63- Ok ( line) => write_line ( & mut stdout, & line, options) ,
64- Err ( err) => return Err ( Box :: new ( NumfmtError :: IoError ( err. to_string ( ) ) ) ) ,
65- } ?;
73+ } else {
74+ write_line ( & mut stdout, line, options, eol) ?;
75+ }
76+
77+ idx += 1 ;
6678 }
79+
6780 Ok ( ( ) )
6881}
6982
7083fn write_line < W : std:: io:: Write > (
7184 writer : & mut W ,
7285 input_line : & [ u8 ] ,
7386 options : & NumfmtOptions ,
87+ eol : Option < u8 > ,
7488) -> UResult < ( ) > {
7589 // Read lines only up to null byte (as GNU does)
7690 let line = input_line
@@ -80,11 +94,11 @@ fn write_line<W: std::io::Write>(
8094 . collect :: < Vec < u8 > > ( ) ;
8195
8296 let handled_line = if options. delimiter . is_some ( ) {
83- write_formatted_with_delimiter ( writer, & line, options)
97+ write_formatted_with_delimiter ( writer, & line, options, eol )
8498 } else {
8599 // Whitespace mode requires valid UTF-8
86100 match std:: str:: from_utf8 ( & line) {
87- Ok ( s) => write_formatted_with_whitespace ( writer, s, options) ,
101+ Ok ( s) => write_formatted_with_whitespace ( writer, s, options, eol ) ,
88102 Err ( _) => Err ( translate ! ( "numfmt-error-invalid-input" ) ) ,
89103 }
90104 } ;
@@ -104,12 +118,9 @@ fn write_line<W: std::io::Write>(
104118 }
105119 writer. write_all ( input_line) ?;
106120
107- let eol = if options. zero_terminated {
108- b"\0 "
109- } else {
110- b"\n "
111- } ;
112- writer. write_all ( eol) ?;
121+ if let Some ( eol) = eol {
122+ writer. write_all ( & [ eol] ) ?;
123+ }
113124 }
114125
115126 Ok ( ( ) )
0 commit comments