mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
printf: coerce missing and invalid arguments to 0
This commit is contained in:
parent
c43ee01d19
commit
066d8ba73d
2 changed files with 75 additions and 90 deletions
|
@ -9,84 +9,103 @@ pub enum FormatArgument {
|
|||
Unparsed(String),
|
||||
}
|
||||
|
||||
impl FormatArgument {
|
||||
pub fn get_char(&self) -> Option<char> {
|
||||
match self {
|
||||
Self::Char(c) => Some(*c),
|
||||
Self::Unparsed(s) => {
|
||||
pub trait ArgumentIter<'a>: Iterator<Item = &'a FormatArgument> {
|
||||
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<Item = &'a FormatArgument>> 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<u64> {
|
||||
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<i64> {
|
||||
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<f64> {
|
||||
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,
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Item = &'a FormatArgument>,
|
||||
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<CanAsterisk<usize>>,
|
||||
args: impl Iterator<Item = &'a FormatArgument>,
|
||||
mut args: impl ArgumentIter<'a>,
|
||||
) -> Result<Option<usize>, 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<Item = &'a FormatArgument>,
|
||||
) -> Result<&'a FormatArgument, FormatError> {
|
||||
arguments.next().ok_or(FormatError::NoMoreArguments)
|
||||
}
|
||||
|
||||
fn write_padded(
|
||||
mut writer: impl Write,
|
||||
text: impl Display,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue