From 066d8ba73d0a08eb47c1fea9e1d446b47fbca1f6 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 20 Nov 2023 13:25:20 +0100 Subject: [PATCH] printf: coerce missing and invalid arguments to 0 --- .../src/lib/features/format/argument.rs | 113 ++++++++++-------- src/uucore/src/lib/features/format/spec.rs | 52 ++------ 2 files changed, 75 insertions(+), 90 deletions(-) diff --git a/src/uucore/src/lib/features/format/argument.rs b/src/uucore/src/lib/features/format/argument.rs index 120b59aa4..96cfeddf3 100644 --- a/src/uucore/src/lib/features/format/argument.rs +++ b/src/uucore/src/lib/features/format/argument.rs @@ -9,84 +9,103 @@ pub enum FormatArgument { Unparsed(String), } -impl FormatArgument { - pub fn get_char(&self) -> Option { - match self { - Self::Char(c) => Some(*c), - Self::Unparsed(s) => { +pub trait ArgumentIter<'a>: Iterator { + fn get_char(&mut self) -> char; + fn get_i64(&mut self) -> i64; + fn get_u64(&mut self) -> u64; + fn get_f64(&mut self) -> f64; + fn get_str(&mut self) -> &'a str; +} + +impl<'a, T: Iterator> ArgumentIter<'a> for T { + fn get_char(&mut self) -> char { + let Some(next) = self.next() else { + return '\0'; + }; + match next { + FormatArgument::Char(c) => *c, + FormatArgument::Unparsed(s) => { let mut chars = s.chars(); let Some(c) = chars.next() else { - return None; + return '\0'; }; let None = chars.next() else { - return None; + return '\0'; }; - Some(c) + c } - _ => None, + _ => '\0', } } - pub fn get_u64(&self) -> Option { - match self { - Self::UnsignedInt(n) => Some(*n), - Self::Unparsed(s) => { - if let Some(s) = s.strip_prefix("0x") { - u64::from_str_radix(s, 16).ok() - } else if let Some(s) = s.strip_prefix("0") { - u64::from_str_radix(s, 8).ok() - } else if let Some(s) = s.strip_prefix('\'') { - Some(s.chars().next()? as u64) - } else { - s.parse().ok() - } + fn get_u64(&mut self) -> u64 { + let Some(next) = self.next() else { + return 0; + }; + match next { + FormatArgument::UnsignedInt(n) => *n, + FormatArgument::Unparsed(s) => if let Some(s) = s.strip_prefix("0x") { + u64::from_str_radix(s, 16).ok() + } else if let Some(s) = s.strip_prefix("0") { + u64::from_str_radix(s, 8).ok() + } else if let Some(s) = s.strip_prefix('\'') { + s.chars().next().map(|c| c as u64) + } else { + s.parse().ok() } - _ => None, + .unwrap_or(0), + _ => 0, } } - pub fn get_i64(&self) -> Option { - match self { - Self::SignedInt(n) => Some(*n), - Self::Unparsed(s) => { + fn get_i64(&mut self) -> i64 { + let Some(next) = self.next() else { + return 0; + }; + match next { + FormatArgument::SignedInt(n) => *n, + FormatArgument::Unparsed(s) => { // For hex, we parse `u64` because we do not allow another // minus sign. We might need to do more precise parsing here. if let Some(s) = s.strip_prefix("-0x") { - Some(- (u64::from_str_radix(s, 16).ok()? as i64)) + u64::from_str_radix(s, 16).ok().map(|x| -(x as i64)) } else if let Some(s) = s.strip_prefix("0x") { - Some(u64::from_str_radix(s, 16).ok()? as i64) + u64::from_str_radix(s, 16).ok().map(|x| x as i64) } else if s.starts_with("-0") || s.starts_with('0') { i64::from_str_radix(s, 8).ok() } else if let Some(s) = s.strip_prefix('\'') { - Some(s.chars().next()? as i64) + s.chars().next().map(|x| x as i64) } else { s.parse().ok() } + .unwrap_or(0) } - _ => None, + _ => 0, } } - pub fn get_f64(&self) -> Option { - match self { - Self::Float(n) => Some(*n), - Self::Unparsed(s) => { - if s.starts_with("0x") || s.starts_with("-0x") { - unimplemented!("Hexadecimal floats are unimplemented!") - } else if let Some(s) = s.strip_prefix('\'') { - Some(s.chars().next()? as u64 as f64) - } else { - s.parse().ok() - } + fn get_f64(&mut self) -> f64 { + let Some(next) = self.next() else { + return 0.0; + }; + match next { + FormatArgument::Float(n) => *n, + FormatArgument::Unparsed(s) => if s.starts_with("0x") || s.starts_with("-0x") { + unimplemented!("Hexadecimal floats are unimplemented!") + } else if let Some(s) = s.strip_prefix('\'') { + s.chars().next().map(|x| x as u64 as f64) + } else { + s.parse().ok() } - _ => None, + .unwrap_or(0.0), + _ => 0.0, } } - pub fn get_str(&self) -> Option<&str> { - match self { - Self::Unparsed(s) | Self::String(s) => Some(s), - _ => None, + fn get_str(&mut self) -> &'a str { + match self.next() { + Some(FormatArgument::Unparsed(s) | FormatArgument::String(s)) => s, + _ => "", } } } diff --git a/src/uucore/src/lib/features/format/spec.rs b/src/uucore/src/lib/features/format/spec.rs index 0cd0f03b4..23c68c066 100644 --- a/src/uucore/src/lib/features/format/spec.rs +++ b/src/uucore/src/lib/features/format/spec.rs @@ -5,7 +5,7 @@ use super::{ self, Case, FloatVariant, ForceDecimal, Formatter, NumberAlignment, PositiveSign, Prefix, UnsignedIntVariant, }, - parse_escape_only, FormatArgument, FormatChar, FormatError, + parse_escape_only, ArgumentIter, FormatChar, FormatError, }; use std::{fmt::Display, io::Write, ops::ControlFlow}; @@ -244,16 +244,12 @@ impl Spec { pub fn write<'a>( &self, writer: impl Write, - mut args: impl Iterator, + mut args: impl ArgumentIter<'a>, ) -> Result<(), FormatError> { match self { &Spec::Char { width, align_left } => { let width = resolve_asterisk(width, &mut args)?.unwrap_or(0); - let arg = next_arg(&mut args)?; - match arg.get_char() { - Some(c) => write_padded(writer, c, width, false, align_left), - _ => Err(FormatError::InvalidArgument(arg.clone())), - } + write_padded(writer, args.get_char(), width, false, align_left) } &Spec::String { width, @@ -263,10 +259,7 @@ impl Spec { } => { let width = resolve_asterisk(width, &mut args)?.unwrap_or(0); let precision = resolve_asterisk(precision, &mut args)?; - let arg = next_arg(&mut args)?; - let Some(s) = arg.get_str() else { - return Err(FormatError::InvalidArgument(arg.clone())); - }; + let s = args.get_str(); if parse_escape { let mut parsed = Vec::new(); for c in parse_escape_only(s.as_bytes()) { @@ -311,11 +304,7 @@ impl Spec { } => { let width = resolve_asterisk(width, &mut args)?.unwrap_or(0); let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(0); - - let arg = next_arg(&mut args)?; - let Some(i) = arg.get_i64() else { - return Err(FormatError::InvalidArgument(arg.clone())); - }; + let i = args.get_i64(); num_format::SignedInt { width, @@ -334,11 +323,7 @@ impl Spec { } => { let width = resolve_asterisk(width, &mut args)?.unwrap_or(0); let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(0); - - let arg = next_arg(args)?; - let Some(i) = arg.get_u64() else { - return Err(FormatError::InvalidArgument(arg.clone())); - }; + let i = args.get_u64(); num_format::UnsignedInt { variant, @@ -360,11 +345,7 @@ impl Spec { } => { let width = resolve_asterisk(width, &mut args)?.unwrap_or(0); let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(6); - - let arg = next_arg(args)?; - let Some(f) = arg.get_f64() else { - return Err(FormatError::InvalidArgument(arg.clone())); - }; + let f = args.get_f64(); num_format::Float { variant, @@ -384,30 +365,15 @@ impl Spec { fn resolve_asterisk<'a>( option: Option>, - args: impl Iterator, + mut args: impl ArgumentIter<'a>, ) -> Result, FormatError> { Ok(match option { None => None, - Some(CanAsterisk::Asterisk) => { - let arg = next_arg(args)?; - match arg.get_u64() { - Some(u) => match usize::try_from(u) { - Ok(u) => Some(u), - Err(_) => return Err(FormatError::InvalidArgument(arg.clone())), - }, - _ => return Err(FormatError::InvalidArgument(arg.clone())), - } - } + Some(CanAsterisk::Asterisk) => Some(usize::try_from(args.get_u64()).ok().unwrap_or(0)), Some(CanAsterisk::Fixed(w)) => Some(w), }) } -fn next_arg<'a>( - mut arguments: impl Iterator, -) -> Result<&'a FormatArgument, FormatError> { - arguments.next().ok_or(FormatError::NoMoreArguments) -} - fn write_padded( mut writer: impl Write, text: impl Display,