Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions src/uu/numfmt/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ pub fn write_formatted_with_delimiter<W: std::io::Write>(
writer: &mut W,
input: &[u8],
options: &NumfmtOptions,
eol: Option<u8>,
) -> Result<()> {
let delimiter = options.delimiter.as_deref().unwrap();

Expand All @@ -497,12 +498,9 @@ pub fn write_formatted_with_delimiter<W: std::io::Write>(
}
}

let eol = if options.zero_terminated {
b"\0"
} else {
b"\n"
};
writer.write_all(eol).unwrap();
if let Some(eol) = eol {
writer.write_all(&[eol]).unwrap();
}

Ok(())
}
Expand All @@ -511,6 +509,7 @@ pub fn write_formatted_with_whitespace<W: std::io::Write>(
writer: &mut W,
s: &str,
options: &NumfmtOptions,
eol: Option<u8>,
) -> Result<()> {
for (n, (prefix, field)) in (1..).zip(WhitespaceSplitter { s: Some(s) }) {
let field_selected = uucore::ranges::contain(&options.fields, n);
Expand Down Expand Up @@ -548,12 +547,9 @@ pub fn write_formatted_with_whitespace<W: std::io::Write>(
}
}

let eol = if options.zero_terminated {
b"\0"
} else {
b"\n"
};
writer.write_all(eol).unwrap();
if let Some(eol) = eol {
writer.write_all(&[eol]).unwrap();
}

Ok(())
}
Expand Down
84 changes: 51 additions & 33 deletions src/uu/numfmt/src/numfmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +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 as _, stderr};
use std::result::Result as StdResult;
use std::io::{BufRead, Write as _, stderr};
use std::str::FromStr;

use units::{IEC_BASES, SI_BASES};
Expand All @@ -33,51 +32,73 @@ mod units;

fn handle_args<'a>(args: impl Iterator<Item = &'a [u8]>, options: &NumfmtOptions) -> UResult<()> {
let mut stdout = std::io::stdout().lock();
let terminator = if options.zero_terminated { 0u8 } else { b'\n' };
for l in args {
write_line(&mut stdout, l, options)?;
write_line(&mut stdout, l, options, Some(terminator))?;
}
Ok(())
}

fn handle_buffer<R>(input: R, options: &NumfmtOptions) -> UResult<()>
where
R: BufRead,
{
fn handle_buffer<R: BufRead>(mut input: R, options: &NumfmtOptions) -> UResult<()> {
let terminator = if options.zero_terminated { 0u8 } else { b'\n' };
handle_buffer_iterator(input.split(terminator), options, terminator)
}

fn handle_buffer_iterator(
iter: impl Iterator<Item = StdResult<Vec<u8>, Error>>,
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 => {
stdout.write_all(&line)?;
stdout.write_all(&[terminator])?;
Ok(())
let mut buf = Vec::new();
let mut idx = 0;

loop {
buf.clear();
let n = input
.read_until(terminator, &mut buf)
.map_err(|e| NumfmtError::IoError(e.to_string()))?;
if n == 0 {
break;
}

let has_terminator = buf.last() == Some(&terminator);
let line = if has_terminator {
&buf[..buf.len() - 1]
} else {
&buf[..]
};

// Emit the terminator only if the input line had one.
// i.e. if the last line of the input does not end with a newline, we should not add one.
let eol = has_terminator.then_some(terminator);

if idx < options.header {
stdout.write_all(line)?;
if let Some(t) = eol {
stdout.write_all(&[t])?;
}
Ok(line) => write_line(&mut stdout, &line, options),
Err(err) => return Err(Box::new(NumfmtError::IoError(err.to_string()))),
}?;
} else {
write_line(&mut stdout, line, options, eol)?;
}

idx += 1;
}

Ok(())
}

fn write_line<W: std::io::Write>(
writer: &mut W,
input_line: &[u8],
options: &NumfmtOptions,
eol: Option<u8>,
) -> UResult<()> {
// Read lines only up to null byte (as GNU does)
let line = input_line
.iter()
.take_while(|&&b| b != b'\0')
.copied()
.collect::<Vec<u8>>();

let handled_line = if options.delimiter.is_some() {
write_formatted_with_delimiter(writer, input_line, options)
write_formatted_with_delimiter(writer, &line, options, eol)
} else {
// Whitespace mode requires valid UTF-8
match std::str::from_utf8(input_line) {
Ok(s) => write_formatted_with_whitespace(writer, s, options),
match std::str::from_utf8(&line) {
Ok(s) => write_formatted_with_whitespace(writer, s, options, eol),
Err(_) => Err(translate!("numfmt-error-invalid-input")),
}
};
Expand All @@ -97,12 +118,9 @@ fn write_line<W: std::io::Write>(
}
writer.write_all(input_line)?;

let eol = if options.zero_terminated {
b"\0"
} else {
b"\n"
};
writer.write_all(eol)?;
if let Some(eol) = eol {
writer.write_all(&[eol])?;
}
}

Ok(())
Expand Down
Loading
Loading