diff --git a/src/uu/printf/src/printf.rs b/src/uu/printf/src/printf.rs index ad42e38941f..090573c538e 100644 --- a/src/uu/printf/src/printf.rs +++ b/src/uu/printf/src/printf.rs @@ -6,12 +6,13 @@ // spell-checker:ignore (change!) each's // spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr -use std::io::stdout; +use std::io::{stdout, Write}; use std::ops::ControlFlow; use clap::{crate_version, Arg, ArgAction, Command}; -use uucore::error::{UResult, UUsageError}; -use uucore::format::{parse_spec_and_escape, FormatArgument}; +use uucore::display::Quotable; +use uucore::error::{UResult, USimpleError, UUsageError}; +use uucore::format::{parse_spec_and_escape, ArgumentIter, FormatArgument}; use uucore::{format_usage, help_about, help_section, help_usage}; const VERSION: &str = "version"; @@ -39,6 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; let mut args = values.iter().peekable(); + let first_arg = args.peek().cloned(); for item in parse_spec_and_escape(format_string.as_ref()) { match item?.write(stdout(), &mut args)? { ControlFlow::Continue(()) => {} @@ -46,6 +48,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; } + if let Some(arg) = args.peek() { + // Check if no arguments were used while parsing the format. + if Some(arg) == first_arg.as_ref() { + stdout().flush()?; + let warning_message = format!( + "warning: ignoring excess arguments, starting with {}", + args.get_str().quote() + ); + return Err(USimpleError::new(0, warning_message.as_str())); + } + } + while args.peek().is_some() { for item in parse_spec_and_escape(format_string.as_ref()) { match item?.write(stdout(), &mut args)? { @@ -54,6 +68,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; } } + Ok(()) } diff --git a/src/uucore/src/lib/features/format/argument.rs b/src/uucore/src/lib/features/format/argument.rs index db18cf51890..f711b4364f7 100644 --- a/src/uucore/src/lib/features/format/argument.rs +++ b/src/uucore/src/lib/features/format/argument.rs @@ -14,7 +14,7 @@ use crate::{error::set_exit_code, show_warning}; /// /// The [`FormatArgument::Unparsed`] variant contains a string that can be /// parsed into other types. This is used by the `printf` utility. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum FormatArgument { Char(char), String(String), diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index dfd13159043..808f908002c 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -72,6 +72,15 @@ fn escaped_unrecognized() { new_ucmd!().args(&["c\\d"]).succeeds().stdout_only("c\\d"); } +#[test] +fn ignore_excess_arguments() { + new_ucmd!() + .args(&["a", "b"]) + .succeeds() + .stdout_is("a") + .stderr_is("printf: warning: ignoring excess arguments, starting with 'b'\n"); +} + #[test] fn sub_string() { new_ucmd!()