1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

seq: improve error handling for invalid -f values

Improve the error message produced by `seq` when given invalid format
specifiers for the `-f` option. Before this commit:

    $ seq -f "%" 1
    seq: %: invalid conversion specification
    $ seq -f "%g%" 1
    seq: %: invalid conversion specification

After this commit:

    $ seq -f "%" 1
    seq: format '%' ends in %
    $ seq -f "%g%" 1
    seq: format '%g%' has too many % directives

This matches the behavior of GNU `seq`.
This commit is contained in:
Jeffrey Finkelstein 2024-12-30 12:23:36 -05:00
parent 00d1866060
commit cccab35337
2 changed files with 25 additions and 4 deletions

View file

@ -22,7 +22,7 @@
//! 3. Parse both `printf` specifiers and escape sequences (for e.g. `printf`) //! 3. Parse both `printf` specifiers and escape sequences (for e.g. `printf`)
//! //!
//! This module aims to combine all three use cases. An iterator parsing each //! This module aims to combine all three use cases. An iterator parsing each
//! of these cases is provided by [`parse_escape_only`], [`parse_spec_only`] //! of these cases is provided by [`parse_spec_only`], [`parse_escape_only`]
//! and [`parse_spec_and_escape`], respectively. //! and [`parse_spec_and_escape`], respectively.
//! //!
//! There is a special [`Format`] type, which can be used to parse a format //! There is a special [`Format`] type, which can be used to parse a format
@ -46,6 +46,8 @@ use std::{
ops::ControlFlow, ops::ControlFlow,
}; };
use os_display::Quotable;
use crate::error::UError; use crate::error::UError;
pub use self::{ pub use self::{
@ -63,6 +65,8 @@ pub enum FormatError {
NeedAtLeastOneSpec(Vec<u8>), NeedAtLeastOneSpec(Vec<u8>),
WrongSpecType, WrongSpecType,
InvalidPrecision(String), InvalidPrecision(String),
/// The format specifier ends with a %, as in `%f%`.
EndsWithPercent(Vec<u8>),
} }
impl Error for FormatError {} impl Error for FormatError {}
@ -92,6 +96,9 @@ impl Display for FormatError {
"format '{}' has no % directive", "format '{}' has no % directive",
String::from_utf8_lossy(s) String::from_utf8_lossy(s)
), ),
Self::EndsWithPercent(s) => {
write!(f, "format {} ends in %", String::from_utf8_lossy(s).quote())
}
Self::InvalidPrecision(precision) => write!(f, "invalid precision: '{precision}'"), Self::InvalidPrecision(precision) => write!(f, "invalid precision: '{precision}'"),
// TODO: Error message below needs some work // TODO: Error message below needs some work
Self::WrongSpecType => write!(f, "wrong % directive type was given"), Self::WrongSpecType => write!(f, "wrong % directive type was given"),
@ -190,6 +197,7 @@ pub fn parse_spec_only(
let mut current = fmt; let mut current = fmt;
std::iter::from_fn(move || match current { std::iter::from_fn(move || match current {
[] => None, [] => None,
[b'%'] => Some(Err(FormatError::EndsWithPercent(fmt.to_vec()))),
[b'%', b'%', rest @ ..] => { [b'%', b'%', rest @ ..] => {
current = rest; current = rest;
Some(Ok(FormatItem::Char(b'%'))) Some(Ok(FormatItem::Char(b'%')))
@ -323,11 +331,14 @@ impl<F: Formatter> Format<F> {
let mut suffix = Vec::new(); let mut suffix = Vec::new();
for item in &mut iter { for item in &mut iter {
match item? { match item {
FormatItem::Spec(_) => { // If the `format_string` is of the form `%f%f` or
// `%f%`, then return an error.
Ok(FormatItem::Spec(_)) | Err(FormatError::EndsWithPercent(_)) => {
return Err(FormatError::TooManySpecs(format_string.as_ref().to_vec())); return Err(FormatError::TooManySpecs(format_string.as_ref().to_vec()));
} }
FormatItem::Char(c) => suffix.push(c), Ok(FormatItem::Char(c)) => suffix.push(c),
Err(e) => return Err(e),
} }
} }

View file

@ -787,6 +787,16 @@ fn test_invalid_format() {
.fails() .fails()
.no_stdout() .no_stdout()
.stderr_contains("format '%g%g' has too many % directives"); .stderr_contains("format '%g%g' has too many % directives");
new_ucmd!()
.args(&["-f", "%g%", "1"])
.fails()
.no_stdout()
.stderr_contains("format '%g%' has too many % directives");
new_ucmd!()
.args(&["-f", "%", "1"])
.fails()
.no_stdout()
.stderr_contains("format '%' ends in %");
} }
#[test] #[test]