@@ -64,19 +64,20 @@ fn format_and_write<W: std::io::Write>(
6464 }
6565 } ;
6666
67- if let Err ( error_message ) = handled_line {
67+ if let Err ( msg ) = result {
6868 match options. invalid {
6969 InvalidModes :: Abort => {
70- return Err ( Box :: new ( NumfmtError :: FormattingError ( error_message ) ) ) ;
70+ return Err ( Box :: new ( NumfmtError :: FormattingError ( msg ) ) ) ;
7171 }
7272 InvalidModes :: Fail => {
73- show ! ( NumfmtError :: FormattingError ( error_message ) ) ;
73+ show ! ( NumfmtError :: FormattingError ( msg ) ) ;
7474 }
7575 InvalidModes :: Warn => {
76- let _ = writeln ! ( stderr( ) , "numfmt: {error_message }" ) ;
76+ let _ = writeln ! ( stderr( ) , "numfmt: {msg }" ) ;
7777 }
7878 InvalidModes :: Ignore => { }
7979 }
80+ // On error, echo the original line unchanged.
8081 writer. write_all ( input_line) ?;
8182 if let Some ( eol) = eol {
8283 writer. write_all ( & [ eol] ) ?;
@@ -90,6 +91,64 @@ fn format_and_write<W: std::io::Write>(
9091 Ok ( false )
9192}
9293
94+ /// Process command-line number arguments.
95+ ///
96+ /// Returns `true` if any line contained invalid input.
97+ fn handle_args < ' a > ( args : impl Iterator < Item = & ' a [ u8 ] > , options : & NumfmtOptions ) -> UResult < bool > {
98+ let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
99+ let terminator = if options. zero_terminated { 0u8 } else { b'\n' } ;
100+ let mut saw_invalid = false ;
101+ for l in args {
102+ saw_invalid |= format_and_write ( & mut stdout, l, options, Some ( terminator) ) ?;
103+ }
104+ Ok ( saw_invalid)
105+ }
106+
107+ /// Process lines read from stdin.
108+ ///
109+ /// Returns `true` if any line contained invalid input.
110+ fn handle_buffer < R : BufRead > ( mut input : R , options : & NumfmtOptions ) -> UResult < bool > {
111+ let terminator = if options. zero_terminated { 0u8 } else { b'\n' } ;
112+ let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
113+ let mut buf = Vec :: new ( ) ;
114+ let mut line_idx = 0 ;
115+ let mut saw_invalid = false ;
116+
117+ loop {
118+ buf. clear ( ) ;
119+ let n = input
120+ . read_until ( terminator, & mut buf)
121+ . map_err ( |e| NumfmtError :: IoError ( e. to_string ( ) ) ) ?;
122+ if n == 0 {
123+ break ;
124+ }
125+
126+ let has_terminator = buf. last ( ) == Some ( & terminator) ;
127+ let line = if has_terminator {
128+ & buf[ ..buf. len ( ) - 1 ]
129+ } else {
130+ & buf[ ..]
131+ } ;
132+ // Emit the terminator only when the input line had one (preserve
133+ // missing final newline).
134+ let eol = has_terminator. then_some ( terminator) ;
135+
136+ if line_idx < options. header {
137+ // Pass header lines through unchanged.
138+ stdout. write_all ( line) ?;
139+ if let Some ( t) = eol {
140+ stdout. write_all ( & [ t] ) ?;
141+ }
142+ } else {
143+ saw_invalid |= format_and_write ( & mut stdout, line, options, eol) ?;
144+ }
145+
146+ line_idx += 1 ;
147+ }
148+
149+ Ok ( saw_invalid)
150+ }
151+
93152fn parse_unit ( s : & str ) -> Result < Unit > {
94153 match s {
95154 "auto" => Ok ( Unit :: Auto ) ,
@@ -285,6 +344,34 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
285344 } )
286345}
287346
347+ fn print_debug_warnings ( options : & NumfmtOptions , matches : & ArgMatches ) {
348+ fn print_warning ( msg_key : & str ) {
349+ let _ = writeln ! ( stderr( ) , "numfmt: {}" , translate!( msg_key) ) ;
350+ }
351+
352+ // Warn if no conversion option is specified
353+ // 2>/dev/full does not abort
354+ if options. transform . from == Unit :: None
355+ && options. transform . to == Unit :: None
356+ && options. padding == 0
357+ && !options. grouping
358+ {
359+ print_warning ( "numfmt-debug-no-conversion" ) ;
360+ }
361+
362+ if options. grouping && locale_grouping_separator ( ) . is_empty ( ) {
363+ print_warning ( "numfmt-debug-grouping-no-effect" ) ;
364+ }
365+
366+ // Warn if --header is used with command-line input
367+ if options. header > 0 && matches. get_many :: < OsString > ( NUMBER ) . is_some ( ) {
368+ print_warning ( "numfmt-debug-header-ignored" ) ;
369+ }
370+ }
371+
372+ #[ uucore:: main]
373+ pub fn uumain ( args : impl uucore:: Args ) -> UResult < ( ) > {
374+ let matches = uucore:: clap_localization:: handle_clap_result ( uu_app ( ) , args) ?;
288375 let options = parse_options ( & matches) . map_err ( NumfmtError :: IllegalArgument ) ?;
289376
290377 if options. debug {
@@ -296,10 +383,17 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
296383 . map ( |s| os_str_as_bytes ( s) . map_err ( |e| e. to_string ( ) ) )
297384 . collect :: < std:: result:: Result < Vec < _ > , _ > > ( )
298385 . map_err ( NumfmtError :: IllegalArgument ) ?;
386+ handle_args ( byte_args. into_iter ( ) , & options)
387+ } else {
388+ let stdin = std:: io:: stdin ( ) ;
389+ handle_buffer ( stdin. lock ( ) , & options)
390+ } ;
299391
300392 match result {
301393 Err ( e) => {
302- std:: io:: stdout ( ) . flush ( ) . expect ( "error flushing stdout" ) ;
394+ // Flush stdout before returning the error so any partial output is
395+ // visible (matches GNU behaviour).
396+ let _ = std:: io:: stdout ( ) . flush ( ) ;
303397 Err ( e)
304398 }
305399 Ok ( saw_invalid) => {
0 commit comments