22//
33// For the full copyright and license information, please view the LICENSE
44// file that was distributed with this source code.
5+ // spell-checker:ignore behaviour
56
67use crate :: errors:: NumfmtError ;
78use crate :: format:: { escape_line, write_formatted_with_delimiter, write_formatted_with_whitespace} ;
@@ -64,19 +65,20 @@ fn format_and_write<W: std::io::Write>(
6465 }
6566 } ;
6667
67- if let Err ( error_message ) = handled_line {
68+ if let Err ( msg ) = result {
6869 match options. invalid {
6970 InvalidModes :: Abort => {
70- return Err ( Box :: new ( NumfmtError :: FormattingError ( error_message ) ) ) ;
71+ return Err ( Box :: new ( NumfmtError :: FormattingError ( msg ) ) ) ;
7172 }
7273 InvalidModes :: Fail => {
73- show ! ( NumfmtError :: FormattingError ( error_message ) ) ;
74+ show ! ( NumfmtError :: FormattingError ( msg ) ) ;
7475 }
7576 InvalidModes :: Warn => {
76- let _ = writeln ! ( stderr( ) , "numfmt: {error_message }" ) ;
77+ let _ = writeln ! ( stderr( ) , "numfmt: {msg }" ) ;
7778 }
7879 InvalidModes :: Ignore => { }
7980 }
81+ // On error, echo the original line unchanged.
8082 writer. write_all ( input_line) ?;
8183 if let Some ( eol) = eol {
8284 writer. write_all ( & [ eol] ) ?;
@@ -90,6 +92,64 @@ fn format_and_write<W: std::io::Write>(
9092 Ok ( false )
9193}
9294
95+ /// Process command-line number arguments.
96+ ///
97+ /// Returns `true` if any line contained invalid input.
98+ fn handle_args < ' a > ( args : impl Iterator < Item = & ' a [ u8 ] > , options : & NumfmtOptions ) -> UResult < bool > {
99+ let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
100+ let terminator = if options. zero_terminated { 0u8 } else { b'\n' } ;
101+ let mut saw_invalid = false ;
102+ for l in args {
103+ saw_invalid |= format_and_write ( & mut stdout, l, options, Some ( terminator) ) ?;
104+ }
105+ Ok ( saw_invalid)
106+ }
107+
108+ /// Process lines read from stdin.
109+ ///
110+ /// Returns `true` if any line contained invalid input.
111+ fn handle_buffer < R : BufRead > ( mut input : R , options : & NumfmtOptions ) -> UResult < bool > {
112+ let terminator = if options. zero_terminated { 0u8 } else { b'\n' } ;
113+ let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
114+ let mut buf = Vec :: new ( ) ;
115+ let mut line_idx = 0 ;
116+ let mut saw_invalid = false ;
117+
118+ loop {
119+ buf. clear ( ) ;
120+ let n = input
121+ . read_until ( terminator, & mut buf)
122+ . map_err ( |e| NumfmtError :: IoError ( e. to_string ( ) ) ) ?;
123+ if n == 0 {
124+ break ;
125+ }
126+
127+ let has_terminator = buf. last ( ) == Some ( & terminator) ;
128+ let line = if has_terminator {
129+ & buf[ ..buf. len ( ) - 1 ]
130+ } else {
131+ & buf[ ..]
132+ } ;
133+ // Emit the terminator only when the input line had one (preserve
134+ // missing final newline).
135+ let eol = has_terminator. then_some ( terminator) ;
136+
137+ if line_idx < options. header {
138+ // Pass header lines through unchanged.
139+ stdout. write_all ( line) ?;
140+ if let Some ( t) = eol {
141+ stdout. write_all ( & [ t] ) ?;
142+ }
143+ } else {
144+ saw_invalid |= format_and_write ( & mut stdout, line, options, eol) ?;
145+ }
146+
147+ line_idx += 1 ;
148+ }
149+
150+ Ok ( saw_invalid)
151+ }
152+
93153fn parse_unit ( s : & str ) -> Result < Unit > {
94154 match s {
95155 "auto" => Ok ( Unit :: Auto ) ,
@@ -285,6 +345,34 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
285345 } )
286346}
287347
348+ fn print_debug_warnings ( options : & NumfmtOptions , matches : & ArgMatches ) {
349+ fn print_warning ( msg_key : & str ) {
350+ let _ = writeln ! ( stderr( ) , "numfmt: {}" , translate!( msg_key) ) ;
351+ }
352+
353+ // Warn if no conversion option is specified
354+ // 2>/dev/full does not abort
355+ if options. transform . from == Unit :: None
356+ && options. transform . to == Unit :: None
357+ && options. padding == 0
358+ && !options. grouping
359+ {
360+ print_warning ( "numfmt-debug-no-conversion" ) ;
361+ }
362+
363+ if options. grouping && locale_grouping_separator ( ) . is_empty ( ) {
364+ print_warning ( "numfmt-debug-grouping-no-effect" ) ;
365+ }
366+
367+ // Warn if --header is used with command-line input
368+ if options. header > 0 && matches. get_many :: < OsString > ( NUMBER ) . is_some ( ) {
369+ print_warning ( "numfmt-debug-header-ignored" ) ;
370+ }
371+ }
372+
373+ #[ uucore:: main]
374+ pub fn uumain ( args : impl uucore:: Args ) -> UResult < ( ) > {
375+ let matches = uucore:: clap_localization:: handle_clap_result ( uu_app ( ) , args) ?;
288376 let options = parse_options ( & matches) . map_err ( NumfmtError :: IllegalArgument ) ?;
289377
290378 if options. debug {
@@ -296,10 +384,17 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
296384 . map ( |s| os_str_as_bytes ( s) . map_err ( |e| e. to_string ( ) ) )
297385 . collect :: < std:: result:: Result < Vec < _ > , _ > > ( )
298386 . map_err ( NumfmtError :: IllegalArgument ) ?;
387+ handle_args ( byte_args. into_iter ( ) , & options)
388+ } else {
389+ let stdin = std:: io:: stdin ( ) ;
390+ handle_buffer ( stdin. lock ( ) , & options)
391+ } ;
299392
300393 match result {
301394 Err ( e) => {
302- std:: io:: stdout ( ) . flush ( ) . expect ( "error flushing stdout" ) ;
395+ // Flush stdout before returning the error so any partial output is
396+ // visible (matches GNU behaviour).
397+ let _ = std:: io:: stdout ( ) . flush ( ) ;
303398 Err ( e)
304399 }
305400 Ok ( saw_invalid) => {
0 commit comments