diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index 684611b3cee..3a5db2ba060 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -2,7 +2,9 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. + // spell-checker:ignore powf + use uucore::display::Quotable; use uucore::translate; @@ -467,21 +469,19 @@ fn split_bytes<'a>(input: &'a [u8], delim: &'a [u8]) -> impl Iterator Result<()> { - let delimiter = options.delimiter.as_ref().unwrap(); - let mut output: Vec = Vec::new(); - let eol = if options.zero_terminated { - b'\0' - } else { - b'\n' - }; +pub fn write_formatted_with_delimiter( + writer: &mut W, + input: &[u8], + options: &NumfmtOptions, +) -> Result<()> { + let delimiter = options.delimiter.as_deref().unwrap(); for (n, field) in (1..).zip(split_bytes(input, delimiter)) { let field_selected = uucore::ranges::contain(&options.fields, n); // add delimiter before second and subsequent fields if n > 1 { - output.extend_from_slice(delimiter); + writer.write_all(delimiter).unwrap(); } if field_selected { @@ -490,21 +490,28 @@ pub fn format_and_print_delimited(input: &[u8], options: &NumfmtOptions) -> Resu .map_err(|_| translate!("numfmt-error-invalid-number", "input" => String::from_utf8_lossy(field).into_owned().quote()))? .trim_start(); let formatted = format_string(field_str, options, None)?; - output.extend_from_slice(formatted.as_bytes()); + writer.write_all(formatted.as_bytes()).unwrap(); } else { // add unselected field without conversion - output.extend_from_slice(field); + writer.write_all(field).unwrap(); } } - output.push(eol); - std::io::Write::write_all(&mut std::io::stdout(), &output).map_err(|e| e.to_string())?; + let eol = if options.zero_terminated { + b"\0" + } else { + b"\n" + }; + writer.write_all(eol).unwrap(); Ok(()) } -pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<()> { - let mut output = String::new(); +pub fn write_formatted_with_whitespace( + writer: &mut W, + s: &str, + options: &NumfmtOptions, +) -> Result<()> { for (n, (prefix, field)) in (1..).zip(WhitespaceSplitter { s: Some(s) }) { let field_selected = uucore::ranges::contain(&options.fields, n); @@ -513,7 +520,7 @@ pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<( // add delimiter before second and subsequent fields let prefix = if n > 1 { - output.push(' '); + writer.write_all(b" ").unwrap(); &prefix[1..] } else { prefix @@ -525,24 +532,28 @@ pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<( None }; - output.push_str(&format_string(field, options, implicit_padding)?); + let formatted = format_string(field, options, implicit_padding)?; + writer.write_all(formatted.as_bytes()).unwrap(); } else { // the -z option converts an initial \n into a space let prefix = if options.zero_terminated && prefix.starts_with('\n') { - output.push(' '); + writer.write_all(b" ").unwrap(); &prefix[1..] } else { prefix }; // add unselected field without conversion - output.push_str(prefix); - output.push_str(field); + writer.write_all(prefix.as_bytes()).unwrap(); + writer.write_all(field.as_bytes()).unwrap(); } } - let eol = if options.zero_terminated { '\0' } else { '\n' }; - output.push(eol); - print!("{output}"); + let eol = if options.zero_terminated { + b"\0" + } else { + b"\n" + }; + writer.write_all(eol).unwrap(); Ok(()) } diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index 2d53d41ce4c..d0bedfa49fb 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -4,7 +4,7 @@ // file that was distributed with this source code. use crate::errors::NumfmtError; -use crate::format::{format_and_print_delimited, format_and_print_whitespace}; +use crate::format::{write_formatted_with_delimiter, write_formatted_with_whitespace}; use crate::options::{ DEBUG, DELIMITER, FIELD, FIELD_DEFAULT, FORMAT, FROM, FROM_DEFAULT, FROM_UNIT, FROM_UNIT_DEFAULT, FormatOptions, HEADER, HEADER_DEFAULT, INVALID, InvalidModes, NUMBER, @@ -14,7 +14,7 @@ use crate::options::{ use crate::units::{Result, Unit}; use clap::{Arg, ArgAction, ArgMatches, Command, builder::ValueParser, parser::ValueSource}; use std::ffi::OsString; -use std::io::{BufRead, Error, Write, stderr}; +use std::io::{BufRead, Error, Write as _, stderr}; use std::result::Result as StdResult; use std::str::FromStr; @@ -32,8 +32,9 @@ pub mod options; mod units; fn handle_args<'a>(args: impl Iterator, options: &NumfmtOptions) -> UResult<()> { + let mut stdout = std::io::stdout().lock(); for l in args { - format_and_handle_validation(l, options)?; + write_line(&mut stdout, l, options)?; } Ok(()) } @@ -51,33 +52,32 @@ fn handle_buffer_iterator( options: &NumfmtOptions, terminator: u8, ) -> UResult<()> { + let mut stdout = std::io::stdout().lock(); for (idx, line_result) in iter.enumerate() { match line_result { Ok(line) if idx < options.header => { - std::io::stdout().write_all(&line)?; - std::io::stdout().write_all(&[terminator])?; + stdout.write_all(&line)?; + stdout.write_all(&[terminator])?; Ok(()) } - Ok(line) => format_and_handle_validation(&line, options), + Ok(line) => write_line(&mut stdout, &line, options), Err(err) => return Err(Box::new(NumfmtError::IoError(err.to_string()))), }?; } Ok(()) } -fn format_and_handle_validation(input_line: &[u8], options: &NumfmtOptions) -> UResult<()> { - let eol = if options.zero_terminated { - b'\0' - } else { - b'\n' - }; - +fn write_line( + writer: &mut W, + input_line: &[u8], + options: &NumfmtOptions, +) -> UResult<()> { let handled_line = if options.delimiter.is_some() { - format_and_print_delimited(input_line, options) + write_formatted_with_delimiter(writer, input_line, options) } else { // Whitespace mode requires valid UTF-8 match std::str::from_utf8(input_line) { - Ok(s) => format_and_print_whitespace(s, options), + Ok(s) => write_formatted_with_whitespace(writer, s, options), Err(_) => Err(translate!("numfmt-error-invalid-input")), } }; @@ -95,8 +95,14 @@ fn format_and_handle_validation(input_line: &[u8], options: &NumfmtOptions) -> U } InvalidModes::Ignore => {} } - std::io::stdout().write_all(input_line)?; - std::io::stdout().write_all(&[eol])?; + writer.write_all(input_line)?; + + let eol = if options.zero_terminated { + b"\0" + } else { + b"\n" + }; + writer.write_all(eol)?; } Ok(())