mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
seq: implement -f FORMAT option
Add support for the `-f FORMAT` option to `seq`. This option instructs the program to render each value in the generated sequence using a given `printf`-style floating point format. For example, $ seq -f %.2f 0.0 0.1 0.5 0.00 0.10 0.20 0.30 0.40 0.50 Fixes issue #2616.
This commit is contained in:
parent
f1dde86f9b
commit
4fbe2b2b5e
3 changed files with 68 additions and 10 deletions
|
@ -20,7 +20,7 @@ bigdecimal = "0.3"
|
||||||
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
|
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
|
||||||
num-bigint = "0.4.0"
|
num-bigint = "0.4.0"
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
|
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["memo"] }
|
||||||
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|
|
@ -11,6 +11,7 @@ use num_traits::Zero;
|
||||||
|
|
||||||
use uucore::error::FromIo;
|
use uucore::error::FromIo;
|
||||||
use uucore::error::UResult;
|
use uucore::error::UResult;
|
||||||
|
use uucore::memo::Memo;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod extendedbigdecimal;
|
mod extendedbigdecimal;
|
||||||
|
@ -27,6 +28,7 @@ static ABOUT: &str = "Display numbers from FIRST to LAST, in steps of INCREMENT.
|
||||||
static OPT_SEPARATOR: &str = "separator";
|
static OPT_SEPARATOR: &str = "separator";
|
||||||
static OPT_TERMINATOR: &str = "terminator";
|
static OPT_TERMINATOR: &str = "terminator";
|
||||||
static OPT_WIDTHS: &str = "widths";
|
static OPT_WIDTHS: &str = "widths";
|
||||||
|
static OPT_FORMAT: &str = "format";
|
||||||
|
|
||||||
static ARG_NUMBERS: &str = "numbers";
|
static ARG_NUMBERS: &str = "numbers";
|
||||||
|
|
||||||
|
@ -39,10 +41,11 @@ fn usage() -> String {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct SeqOptions {
|
struct SeqOptions<'a> {
|
||||||
separator: String,
|
separator: String,
|
||||||
terminator: String,
|
terminator: String,
|
||||||
widths: bool,
|
widths: bool,
|
||||||
|
format: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A range of integers.
|
/// A range of integers.
|
||||||
|
@ -66,6 +69,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
separator: matches.value_of(OPT_SEPARATOR).unwrap_or("\n").to_string(),
|
separator: matches.value_of(OPT_SEPARATOR).unwrap_or("\n").to_string(),
|
||||||
terminator: matches.value_of(OPT_TERMINATOR).unwrap_or("\n").to_string(),
|
terminator: matches.value_of(OPT_TERMINATOR).unwrap_or("\n").to_string(),
|
||||||
widths: matches.is_present(OPT_WIDTHS),
|
widths: matches.is_present(OPT_WIDTHS),
|
||||||
|
format: matches.value_of(OPT_FORMAT),
|
||||||
};
|
};
|
||||||
|
|
||||||
let first = if numbers.len() > 1 {
|
let first = if numbers.len() > 1 {
|
||||||
|
@ -115,6 +119,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
options.terminator,
|
options.terminator,
|
||||||
options.widths,
|
options.widths,
|
||||||
padding,
|
padding,
|
||||||
|
options.format,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(first, increment, last) => print_seq(
|
(first, increment, last) => print_seq(
|
||||||
|
@ -128,6 +133,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
options.terminator,
|
options.terminator,
|
||||||
options.widths,
|
options.widths,
|
||||||
padding,
|
padding,
|
||||||
|
options.format,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
match result {
|
match result {
|
||||||
|
@ -165,6 +171,14 @@ pub fn uu_app<'a>() -> App<'a> {
|
||||||
.long("widths")
|
.long("widths")
|
||||||
.help("Equalize widths of all numbers by padding with zeros"),
|
.help("Equalize widths of all numbers by padding with zeros"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(OPT_FORMAT)
|
||||||
|
.short('f')
|
||||||
|
.long(OPT_FORMAT)
|
||||||
|
.help("use printf style floating-point FORMAT")
|
||||||
|
.takes_value(true)
|
||||||
|
.number_of_values(1),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(ARG_NUMBERS)
|
Arg::new(ARG_NUMBERS)
|
||||||
.multiple_occurrences(true)
|
.multiple_occurrences(true)
|
||||||
|
@ -254,6 +268,7 @@ fn print_seq(
|
||||||
terminator: String,
|
terminator: String,
|
||||||
pad: bool,
|
pad: bool,
|
||||||
padding: usize,
|
padding: usize,
|
||||||
|
format: Option<&str>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
let stdout = stdout();
|
let stdout = stdout();
|
||||||
let mut stdout = stdout.lock();
|
let mut stdout = stdout.lock();
|
||||||
|
@ -265,13 +280,34 @@ fn print_seq(
|
||||||
if !is_first_iteration {
|
if !is_first_iteration {
|
||||||
write!(stdout, "{}", separator)?;
|
write!(stdout, "{}", separator)?;
|
||||||
}
|
}
|
||||||
write_value_float(
|
// If there was an argument `-f FORMAT`, then use that format
|
||||||
&mut stdout,
|
// template instead of the default formatting strategy.
|
||||||
&value,
|
//
|
||||||
padding,
|
// The `Memo::run_all()` function takes in the template and
|
||||||
largest_dec,
|
// the current value and writes the result to `stdout`.
|
||||||
is_first_iteration,
|
//
|
||||||
)?;
|
// TODO The `run_all()` method takes a string as its second
|
||||||
|
// parameter but we have an `ExtendedBigDecimal`. In order to
|
||||||
|
// satisfy the signature of the function, we convert the
|
||||||
|
// `ExtendedBigDecimal` into a string. The `Memo::run_all()`
|
||||||
|
// logic will subsequently parse that string into something
|
||||||
|
// similar to an `ExtendedBigDecimal` again before rendering
|
||||||
|
// it as a string and ultimately writing to `stdout`. We
|
||||||
|
// shouldn't have to do so much converting back and forth via
|
||||||
|
// strings.
|
||||||
|
match format {
|
||||||
|
Some(f) => {
|
||||||
|
let s = format!("{}", value);
|
||||||
|
Memo::run_all(f, &[s]);
|
||||||
|
}
|
||||||
|
None => write_value_float(
|
||||||
|
&mut stdout,
|
||||||
|
&value,
|
||||||
|
padding,
|
||||||
|
largest_dec,
|
||||||
|
is_first_iteration,
|
||||||
|
)?,
|
||||||
|
}
|
||||||
// TODO Implement augmenting addition.
|
// TODO Implement augmenting addition.
|
||||||
value = value + increment.clone();
|
value = value + increment.clone();
|
||||||
is_first_iteration = false;
|
is_first_iteration = false;
|
||||||
|
@ -303,6 +339,7 @@ fn print_seq_integers(
|
||||||
terminator: String,
|
terminator: String,
|
||||||
pad: bool,
|
pad: bool,
|
||||||
padding: usize,
|
padding: usize,
|
||||||
|
format: Option<&str>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
let stdout = stdout();
|
let stdout = stdout();
|
||||||
let mut stdout = stdout.lock();
|
let mut stdout = stdout.lock();
|
||||||
|
@ -313,7 +350,20 @@ fn print_seq_integers(
|
||||||
if !is_first_iteration {
|
if !is_first_iteration {
|
||||||
write!(stdout, "{}", separator)?;
|
write!(stdout, "{}", separator)?;
|
||||||
}
|
}
|
||||||
write_value_int(&mut stdout, &value, padding, pad, is_first_iteration)?;
|
// If there was an argument `-f FORMAT`, then use that format
|
||||||
|
// template instead of the default formatting strategy.
|
||||||
|
//
|
||||||
|
// The `Memo::run_all()` function takes in the template and
|
||||||
|
// the current value and writes the result to `stdout`.
|
||||||
|
//
|
||||||
|
// TODO See similar comment about formatting in `print_seq()`.
|
||||||
|
match format {
|
||||||
|
Some(f) => {
|
||||||
|
let s = format!("{}", value);
|
||||||
|
Memo::run_all(f, &[s]);
|
||||||
|
}
|
||||||
|
None => write_value_int(&mut stdout, &value, padding, pad, is_first_iteration)?,
|
||||||
|
}
|
||||||
// TODO Implement augmenting addition.
|
// TODO Implement augmenting addition.
|
||||||
value = value + increment.clone();
|
value = value + increment.clone();
|
||||||
is_first_iteration = false;
|
is_first_iteration = false;
|
||||||
|
|
|
@ -693,3 +693,11 @@ fn test_parse_error_hex() {
|
||||||
.fails()
|
.fails()
|
||||||
.usage_error("invalid hexadecimal argument: '0xlmnop'");
|
.usage_error("invalid hexadecimal argument: '0xlmnop'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_option() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-f", "%.2f", "0.0", "0.1", "0.5"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only("0.00\n0.10\n0.20\n0.30\n0.40\n0.50\n");
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue