mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +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),
|
Unparsed(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatArgument {
|
pub trait ArgumentIter<'a>: Iterator<Item = &'a FormatArgument> {
|
||||||
pub fn get_char(&self) -> Option<char> {
|
fn get_char(&mut self) -> char;
|
||||||
match self {
|
fn get_i64(&mut self) -> i64;
|
||||||
Self::Char(c) => Some(*c),
|
fn get_u64(&mut self) -> u64;
|
||||||
Self::Unparsed(s) => {
|
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 mut chars = s.chars();
|
||||||
let Some(c) = chars.next() else {
|
let Some(c) = chars.next() else {
|
||||||
return None;
|
return '\0';
|
||||||
};
|
};
|
||||||
let None = chars.next() else {
|
let None = chars.next() else {
|
||||||
return None;
|
return '\0';
|
||||||
};
|
};
|
||||||
Some(c)
|
c
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => '\0',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_u64(&self) -> Option<u64> {
|
fn get_u64(&mut self) -> u64 {
|
||||||
match self {
|
let Some(next) = self.next() else {
|
||||||
Self::UnsignedInt(n) => Some(*n),
|
return 0;
|
||||||
Self::Unparsed(s) => {
|
};
|
||||||
if let Some(s) = s.strip_prefix("0x") {
|
match next {
|
||||||
u64::from_str_radix(s, 16).ok()
|
FormatArgument::UnsignedInt(n) => *n,
|
||||||
} else if let Some(s) = s.strip_prefix("0") {
|
FormatArgument::Unparsed(s) => if let Some(s) = s.strip_prefix("0x") {
|
||||||
u64::from_str_radix(s, 8).ok()
|
u64::from_str_radix(s, 16).ok()
|
||||||
} else if let Some(s) = s.strip_prefix('\'') {
|
} else if let Some(s) = s.strip_prefix("0") {
|
||||||
Some(s.chars().next()? as u64)
|
u64::from_str_radix(s, 8).ok()
|
||||||
} else {
|
} else if let Some(s) = s.strip_prefix('\'') {
|
||||||
s.parse().ok()
|
s.chars().next().map(|c| c as u64)
|
||||||
}
|
} else {
|
||||||
|
s.parse().ok()
|
||||||
}
|
}
|
||||||
_ => None,
|
.unwrap_or(0),
|
||||||
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_i64(&self) -> Option<i64> {
|
fn get_i64(&mut self) -> i64 {
|
||||||
match self {
|
let Some(next) = self.next() else {
|
||||||
Self::SignedInt(n) => Some(*n),
|
return 0;
|
||||||
Self::Unparsed(s) => {
|
};
|
||||||
|
match next {
|
||||||
|
FormatArgument::SignedInt(n) => *n,
|
||||||
|
FormatArgument::Unparsed(s) => {
|
||||||
// For hex, we parse `u64` because we do not allow another
|
// For hex, we parse `u64` because we do not allow another
|
||||||
// minus sign. We might need to do more precise parsing here.
|
// minus sign. We might need to do more precise parsing here.
|
||||||
if let Some(s) = s.strip_prefix("-0x") {
|
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") {
|
} 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') {
|
} else if s.starts_with("-0") || s.starts_with('0') {
|
||||||
i64::from_str_radix(s, 8).ok()
|
i64::from_str_radix(s, 8).ok()
|
||||||
} else if let Some(s) = s.strip_prefix('\'') {
|
} else if let Some(s) = s.strip_prefix('\'') {
|
||||||
Some(s.chars().next()? as i64)
|
s.chars().next().map(|x| x as i64)
|
||||||
} else {
|
} else {
|
||||||
s.parse().ok()
|
s.parse().ok()
|
||||||
}
|
}
|
||||||
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_f64(&self) -> Option<f64> {
|
fn get_f64(&mut self) -> f64 {
|
||||||
match self {
|
let Some(next) = self.next() else {
|
||||||
Self::Float(n) => Some(*n),
|
return 0.0;
|
||||||
Self::Unparsed(s) => {
|
};
|
||||||
if s.starts_with("0x") || s.starts_with("-0x") {
|
match next {
|
||||||
unimplemented!("Hexadecimal floats are unimplemented!")
|
FormatArgument::Float(n) => *n,
|
||||||
} else if let Some(s) = s.strip_prefix('\'') {
|
FormatArgument::Unparsed(s) => if s.starts_with("0x") || s.starts_with("-0x") {
|
||||||
Some(s.chars().next()? as u64 as f64)
|
unimplemented!("Hexadecimal floats are unimplemented!")
|
||||||
} else {
|
} else if let Some(s) = s.strip_prefix('\'') {
|
||||||
s.parse().ok()
|
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> {
|
fn get_str(&mut self) -> &'a str {
|
||||||
match self {
|
match self.next() {
|
||||||
Self::Unparsed(s) | Self::String(s) => Some(s),
|
Some(FormatArgument::Unparsed(s) | FormatArgument::String(s)) => s,
|
||||||
_ => None,
|
_ => "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use super::{
|
||||||
self, Case, FloatVariant, ForceDecimal, Formatter, NumberAlignment, PositiveSign, Prefix,
|
self, Case, FloatVariant, ForceDecimal, Formatter, NumberAlignment, PositiveSign, Prefix,
|
||||||
UnsignedIntVariant,
|
UnsignedIntVariant,
|
||||||
},
|
},
|
||||||
parse_escape_only, FormatArgument, FormatChar, FormatError,
|
parse_escape_only, ArgumentIter, FormatChar, FormatError,
|
||||||
};
|
};
|
||||||
use std::{fmt::Display, io::Write, ops::ControlFlow};
|
use std::{fmt::Display, io::Write, ops::ControlFlow};
|
||||||
|
|
||||||
|
@ -244,16 +244,12 @@ impl Spec {
|
||||||
pub fn write<'a>(
|
pub fn write<'a>(
|
||||||
&self,
|
&self,
|
||||||
writer: impl Write,
|
writer: impl Write,
|
||||||
mut args: impl Iterator<Item = &'a FormatArgument>,
|
mut args: impl ArgumentIter<'a>,
|
||||||
) -> Result<(), FormatError> {
|
) -> Result<(), FormatError> {
|
||||||
match self {
|
match self {
|
||||||
&Spec::Char { width, align_left } => {
|
&Spec::Char { width, align_left } => {
|
||||||
let width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
let width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
||||||
let arg = next_arg(&mut args)?;
|
write_padded(writer, args.get_char(), width, false, align_left)
|
||||||
match arg.get_char() {
|
|
||||||
Some(c) => write_padded(writer, c, width, false, align_left),
|
|
||||||
_ => Err(FormatError::InvalidArgument(arg.clone())),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&Spec::String {
|
&Spec::String {
|
||||||
width,
|
width,
|
||||||
|
@ -263,10 +259,7 @@ impl Spec {
|
||||||
} => {
|
} => {
|
||||||
let width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
let width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
||||||
let precision = resolve_asterisk(precision, &mut args)?;
|
let precision = resolve_asterisk(precision, &mut args)?;
|
||||||
let arg = next_arg(&mut args)?;
|
let s = args.get_str();
|
||||||
let Some(s) = arg.get_str() else {
|
|
||||||
return Err(FormatError::InvalidArgument(arg.clone()));
|
|
||||||
};
|
|
||||||
if parse_escape {
|
if parse_escape {
|
||||||
let mut parsed = Vec::new();
|
let mut parsed = Vec::new();
|
||||||
for c in parse_escape_only(s.as_bytes()) {
|
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 width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
||||||
let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(0);
|
let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(0);
|
||||||
|
let i = args.get_i64();
|
||||||
let arg = next_arg(&mut args)?;
|
|
||||||
let Some(i) = arg.get_i64() else {
|
|
||||||
return Err(FormatError::InvalidArgument(arg.clone()));
|
|
||||||
};
|
|
||||||
|
|
||||||
num_format::SignedInt {
|
num_format::SignedInt {
|
||||||
width,
|
width,
|
||||||
|
@ -334,11 +323,7 @@ impl Spec {
|
||||||
} => {
|
} => {
|
||||||
let width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
let width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
||||||
let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(0);
|
let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(0);
|
||||||
|
let i = args.get_u64();
|
||||||
let arg = next_arg(args)?;
|
|
||||||
let Some(i) = arg.get_u64() else {
|
|
||||||
return Err(FormatError::InvalidArgument(arg.clone()));
|
|
||||||
};
|
|
||||||
|
|
||||||
num_format::UnsignedInt {
|
num_format::UnsignedInt {
|
||||||
variant,
|
variant,
|
||||||
|
@ -360,11 +345,7 @@ impl Spec {
|
||||||
} => {
|
} => {
|
||||||
let width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
let width = resolve_asterisk(width, &mut args)?.unwrap_or(0);
|
||||||
let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(6);
|
let precision = resolve_asterisk(precision, &mut args)?.unwrap_or(6);
|
||||||
|
let f = args.get_f64();
|
||||||
let arg = next_arg(args)?;
|
|
||||||
let Some(f) = arg.get_f64() else {
|
|
||||||
return Err(FormatError::InvalidArgument(arg.clone()));
|
|
||||||
};
|
|
||||||
|
|
||||||
num_format::Float {
|
num_format::Float {
|
||||||
variant,
|
variant,
|
||||||
|
@ -384,30 +365,15 @@ impl Spec {
|
||||||
|
|
||||||
fn resolve_asterisk<'a>(
|
fn resolve_asterisk<'a>(
|
||||||
option: Option<CanAsterisk<usize>>,
|
option: Option<CanAsterisk<usize>>,
|
||||||
args: impl Iterator<Item = &'a FormatArgument>,
|
mut args: impl ArgumentIter<'a>,
|
||||||
) -> Result<Option<usize>, FormatError> {
|
) -> Result<Option<usize>, FormatError> {
|
||||||
Ok(match option {
|
Ok(match option {
|
||||||
None => None,
|
None => None,
|
||||||
Some(CanAsterisk::Asterisk) => {
|
Some(CanAsterisk::Asterisk) => Some(usize::try_from(args.get_u64()).ok().unwrap_or(0)),
|
||||||
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::Fixed(w)) => Some(w),
|
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(
|
fn write_padded(
|
||||||
mut writer: impl Write,
|
mut writer: impl Write,
|
||||||
text: impl Display,
|
text: impl Display,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue