printf rewrite: fix compilation

This commit is contained in:
Terts Diepraam 2023-10-28 17:34:04 +02:00
parent 28810906a3
commit f117fc1bab
7 changed files with 85 additions and 49 deletions

View file

@ -13,8 +13,8 @@ use std::io::Write;
use std::sync::mpsc; use std::sync::mpsc;
use std::time::Duration; use std::time::Duration;
use uucore::error::UResult;
use uucore::format::sprintf; use uucore::format::sprintf;
use uucore::{error::UResult, format::FormatArgument};
use crate::numbers::{to_magnitude_and_suffix, SuffixType}; use crate::numbers::{to_magnitude_and_suffix, SuffixType};
@ -152,7 +152,9 @@ impl ProgUpdate {
let (carriage_return, newline) = if rewrite { ("\r", "") } else { ("", "\n") }; let (carriage_return, newline) = if rewrite { ("\r", "") } else { ("", "\n") };
// The duration should be formatted as in `printf %g`. // The duration should be formatted as in `printf %g`.
let duration_str = sprintf("%g", &[duration.to_string()])?; // TODO: remove unwrap and make FormatError implement UError
let duration_str = sprintf("%g", &[FormatArgument::Float(duration)])?;
let duration_str = std::str::from_utf8(&duration_str).unwrap();
// If the number of bytes written is sufficiently large, then // If the number of bytes written is sufficiently large, then
// print a more concise representation of the number, like // print a more concise representation of the number, like

View file

@ -8,7 +8,7 @@
use clap::{crate_version, Arg, ArgAction, Command}; use clap::{crate_version, Arg, ArgAction, Command};
use uucore::error::{UResult, UUsageError}; use uucore::error::{UResult, UUsageError};
use uucore::format::printf; use uucore::format::{printf, FormatArgument};
use uucore::{format_usage, help_about, help_section, help_usage}; use uucore::{format_usage, help_about, help_section, help_usage};
const VERSION: &str = "version"; const VERSION: &str = "version";
@ -30,12 +30,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let format_string = matches let format_string = matches
.get_one::<String>(options::FORMATSTRING) .get_one::<String>(options::FORMATSTRING)
.ok_or_else(|| UUsageError::new(1, "missing operand"))?; .ok_or_else(|| UUsageError::new(1, "missing operand"))?;
let values: Vec<String> = match matches.get_many::<String>(options::ARGUMENT) { let values: Vec<_> = match matches.get_many::<String>(options::ARGUMENT) {
Some(s) => s.map(|s| s.to_string()).collect(), Some(s) => s.map(|s| FormatArgument::Unparsed(s.to_string())).collect(),
None => vec![], None => vec![],
}; };
printf(format_string, &values[..])?; printf(format_string, &values)?;
Ok(()) Ok(())
} }

View file

@ -9,7 +9,7 @@ use clap::{crate_version, Arg, ArgAction, Command};
use num_traits::Zero; use num_traits::Zero;
use uucore::error::UResult; use uucore::error::UResult;
use uucore::format::printf; use uucore::format::{printf, FormatArgument};
use uucore::{format_usage, help_about, help_usage}; use uucore::{format_usage, help_about, help_usage};
mod error; mod error;
@ -144,8 +144,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}; };
match result { match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()), _ => todo!(),
Err(e) => Err(e.map_err_context(|| "write error".into())), // Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()),
// Err(e) => Err(e.map_err_context(|| "write error".into())),
} }
} }
@ -270,7 +271,7 @@ fn print_seq(
match format { match format {
Some(f) => { Some(f) => {
let s = format!("{value}"); let s = format!("{value}");
printf(f, &[s])?; printf(f, &[FormatArgument::String(s)])?;
} }
None => write_value_float(&mut stdout, &value, padding, largest_dec)?, None => write_value_float(&mut stdout, &value, padding, largest_dec)?,
} }
@ -326,7 +327,7 @@ fn print_seq_integers(
match format { match format {
Some(f) => { Some(f) => {
let s = format!("{value}"); let s = format!("{value}");
printf(f, &[s])?; printf(f, &[FormatArgument::String(s)])?;
} }
None => write_value_int(&mut stdout, &value, padding, pad)?, None => write_value_int(&mut stdout, &value, padding, pad)?,
} }

View file

@ -8,16 +8,14 @@
pub mod backup_control; pub mod backup_control;
#[cfg(feature = "encoding")] #[cfg(feature = "encoding")]
pub mod encoding; pub mod encoding;
#[cfg(feature = "format")]
pub mod format;
#[cfg(feature = "fs")] #[cfg(feature = "fs")]
pub mod fs; pub mod fs;
#[cfg(feature = "fsext")] #[cfg(feature = "fsext")]
pub mod fsext; pub mod fsext;
#[cfg(feature = "lines")] #[cfg(feature = "lines")]
pub mod lines; pub mod lines;
#[cfg(feature = "format")]
pub mod format;
#[cfg(feature = "memo")]
pub mod memo;
#[cfg(feature = "quoting-style")] #[cfg(feature = "quoting-style")]
pub mod quoting_style; pub mod quoting_style;
#[cfg(feature = "ranges")] #[cfg(feature = "ranges")]
@ -26,8 +24,6 @@ pub mod ranges;
pub mod ringbuffer; pub mod ringbuffer;
#[cfg(feature = "sum")] #[cfg(feature = "sum")]
pub mod sum; pub mod sum;
#[cfg(feature = "memo")]
mod tokenize;
#[cfg(feature = "update-control")] #[cfg(feature = "update-control")]
pub mod update_control; pub mod update_control;
#[cfg(feature = "version-cmp")] #[cfg(feature = "version-cmp")]

View file

@ -14,8 +14,15 @@
mod spec; mod spec;
use spec::Spec; use spec::Spec;
use std::io::{stdout, Write}; use std::{
error::Error,
fmt::Display,
io::{stdout, Write},
};
use crate::error::UError;
#[derive(Debug)]
pub enum FormatError { pub enum FormatError {
SpecError, SpecError,
IoError(std::io::Error), IoError(std::io::Error),
@ -23,6 +30,21 @@ pub enum FormatError {
InvalidArgument(FormatArgument), InvalidArgument(FormatArgument),
} }
impl Error for FormatError {}
impl UError for FormatError {}
impl Display for FormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// TODO: Be more precise about these
match self {
FormatError::SpecError => write!(f, "invalid spec"),
FormatError::IoError(_) => write!(f, "io error"),
FormatError::NoMoreArguments => write!(f, "no more arguments"),
FormatError::InvalidArgument(_) => write!(f, "invalid argument"),
}
}
}
/// A single item to format /// A single item to format
enum FormatItem { enum FormatItem {
/// A format specifier /// A format specifier
@ -35,16 +57,23 @@ enum FormatItem {
Char(u8), Char(u8),
} }
#[derive(Clone, Debug)]
pub enum FormatArgument { pub enum FormatArgument {
Char(char), Char(char),
String(String), String(String),
UnsignedInt(u64), UnsignedInt(u64),
SignedInt(i64), SignedInt(i64),
Float(f64), Float(f64),
// Special argument that gets coerced into the other variants
Unparsed(String),
} }
impl FormatItem { impl FormatItem {
fn write<'a>(&self, mut writer: impl Write, args: &mut impl Iterator<Item = FormatArgument>) -> Result<(), FormatError> { fn write<'a>(
&self,
mut writer: impl Write,
args: &mut impl Iterator<Item = &'a FormatArgument>,
) -> Result<(), FormatError> {
match self { match self {
FormatItem::Spec(spec) => spec.write(writer, args), FormatItem::Spec(spec) => spec.write(writer, args),
FormatItem::Text(bytes) => writer.write_all(bytes).map_err(FormatError::IoError), FormatItem::Text(bytes) => writer.write_all(bytes).map_err(FormatError::IoError),
@ -110,13 +139,20 @@ fn parse_iter(fmt: &[u8]) -> impl Iterator<Item = Result<FormatItem, FormatError
/// printf("hello %s", &["world".to_string()]).unwrap(); /// printf("hello %s", &["world".to_string()]).unwrap();
/// // prints "hello world" /// // prints "hello world"
/// ``` /// ```
pub fn printf(format_string: &[u8], arguments: impl IntoIterator<Item = FormatArgument>) -> Result<(), FormatError> { pub fn printf<'a>(
format_string: impl AsRef<[u8]>,
arguments: impl IntoIterator<Item = &'a FormatArgument>,
) -> Result<(), FormatError> {
printf_writer(stdout(), format_string, arguments) printf_writer(stdout(), format_string, arguments)
} }
fn printf_writer(mut writer: impl Write, format_string: &[u8], args: impl IntoIterator<Item = FormatArgument>) -> Result<(), FormatError> { fn printf_writer<'a>(
mut writer: impl Write,
format_string: impl AsRef<[u8]>,
args: impl IntoIterator<Item = &'a FormatArgument>,
) -> Result<(), FormatError> {
let mut args = args.into_iter(); let mut args = args.into_iter();
for item in parse_iter(format_string) { for item in parse_iter(format_string.as_ref()) {
item?.write(&mut writer, &mut args)?; item?.write(&mut writer, &mut args)?;
} }
Ok(()) Ok(())
@ -137,7 +173,10 @@ fn printf_writer(mut writer: impl Write, format_string: &[u8], args: impl IntoIt
/// let s = sprintf("hello %s", &["world".to_string()]).unwrap(); /// let s = sprintf("hello %s", &["world".to_string()]).unwrap();
/// assert_eq!(s, "hello world".to_string()); /// assert_eq!(s, "hello world".to_string());
/// ``` /// ```
pub fn sprintf(format_string: &[u8], arguments: impl IntoIterator<Item = FormatArgument>) -> Result<Vec<u8>, FormatError> { pub fn sprintf<'a>(
format_string: impl AsRef<[u8]>,
arguments: impl IntoIterator<Item = &'a FormatArgument>,
) -> Result<Vec<u8>, FormatError> {
let mut writer = Vec::new(); let mut writer = Vec::new();
printf_writer(&mut writer, format_string, arguments)?; printf_writer(&mut writer, format_string, arguments)?;
Ok(writer) Ok(writer)

View file

@ -257,7 +257,7 @@ impl Spec {
pub fn write<'a>( pub fn write<'a>(
&self, &self,
mut writer: impl Write, mut writer: impl Write,
mut args: impl Iterator<Item = FormatArgument>, mut args: impl Iterator<Item = &'a FormatArgument>,
) -> Result<(), FormatError> { ) -> Result<(), FormatError> {
match self { match self {
&Spec::Char { width, align_left } => { &Spec::Char { width, align_left } => {
@ -265,7 +265,7 @@ impl Spec {
let arg = next_arg(&mut args)?; let arg = next_arg(&mut args)?;
match arg { match arg {
FormatArgument::Char(c) => write_padded(writer, c, width, false, align_left), FormatArgument::Char(c) => write_padded(writer, c, width, false, align_left),
_ => Err(FormatError::InvalidArgument(arg)), _ => Err(FormatError::InvalidArgument(arg.clone())),
} }
} }
&Spec::String { width, align_left } => { &Spec::String { width, align_left } => {
@ -273,7 +273,7 @@ impl Spec {
let arg = next_arg(&mut args)?; let arg = next_arg(&mut args)?;
match arg { match arg {
FormatArgument::String(s) => write_padded(writer, s, width, false, align_left), FormatArgument::String(s) => write_padded(writer, s, width, false, align_left),
_ => Err(FormatError::InvalidArgument(arg)), _ => Err(FormatError::InvalidArgument(arg.clone())),
} }
} }
&Spec::SignedInt { &Spec::SignedInt {
@ -285,10 +285,10 @@ impl Spec {
let arg = next_arg(&mut args)?; let arg = next_arg(&mut args)?;
let FormatArgument::SignedInt(i) = arg else { let FormatArgument::SignedInt(i) = arg else {
return Err(FormatError::InvalidArgument(arg)); return Err(FormatError::InvalidArgument(arg.clone()));
}; };
if i >= 0 { if *i >= 0 {
match positive_sign { match positive_sign {
PositiveSign::None => Ok(()), PositiveSign::None => Ok(()),
PositiveSign::Plus => write!(writer, "+"), PositiveSign::Plus => write!(writer, "+"),
@ -313,7 +313,7 @@ impl Spec {
let arg = next_arg(args)?; let arg = next_arg(args)?;
let FormatArgument::SignedInt(i) = arg else { let FormatArgument::SignedInt(i) = arg else {
return Err(FormatError::InvalidArgument(arg)); return Err(FormatError::InvalidArgument(arg.clone()));
}; };
let s = match variant { let s = match variant {
@ -355,7 +355,7 @@ impl Spec {
let arg = next_arg(args)?; let arg = next_arg(args)?;
let FormatArgument::Float(f) = arg else { let FormatArgument::Float(f) = arg else {
return Err(FormatError::InvalidArgument(arg)); return Err(FormatError::InvalidArgument(arg.clone()));
}; };
if f.is_sign_positive() { if f.is_sign_positive() {
@ -369,16 +369,16 @@ impl Spec {
let s = match variant { let s = match variant {
FloatVariant::Decimal => { FloatVariant::Decimal => {
format_float_decimal(f, precision, case, force_decimal) format_float_decimal(*f, precision, case, force_decimal)
} }
FloatVariant::Scientific => { FloatVariant::Scientific => {
format_float_scientific(f, precision, case, force_decimal) format_float_scientific(*f, precision, case, force_decimal)
} }
FloatVariant::Shortest => { FloatVariant::Shortest => {
format_float_shortest(f, precision, case, force_decimal) format_float_shortest(*f, precision, case, force_decimal)
} }
FloatVariant::Hexadecimal => { FloatVariant::Hexadecimal => {
format_float_hexadecimal(f, precision, case, force_decimal) format_float_hexadecimal(*f, precision, case, force_decimal)
} }
}; };
@ -490,7 +490,7 @@ fn format_float_hexadecimal(
let mut s = match (precision, force_decimal) { let mut s = match (precision, force_decimal) {
(0, ForceDecimal::No) => format!("0x{first_digit}p{exponent:+x}"), (0, ForceDecimal::No) => format!("0x{first_digit}p{exponent:+x}"),
(0, ForceDecimal::Yes) => format!("0x{first_digit}.p{exponent:+x}"), (0, ForceDecimal::Yes) => format!("0x{first_digit}.p{exponent:+x}"),
_ => format!("0x{first_digit}.{mantissa:0>13x}p{exponent:+x}") _ => format!("0x{first_digit}.{mantissa:0>13x}p{exponent:+x}"),
}; };
if case == Case::Uppercase { if case == Case::Uppercase {
@ -500,29 +500,29 @@ fn format_float_hexadecimal(
return s; return s;
} }
fn resolve_asterisk( fn resolve_asterisk<'a>(
option: Option<CanAsterisk<usize>>, option: Option<CanAsterisk<usize>>,
args: impl Iterator<Item = FormatArgument>, args: impl Iterator<Item = &'a FormatArgument>,
) -> Result<Option<usize>, FormatError> { ) -> Result<Option<usize>, FormatError> {
Ok(match option { Ok(match option {
None => None, None => None,
Some(CanAsterisk::Asterisk) => { Some(CanAsterisk::Asterisk) => {
let arg = next_arg(args)?; let arg = next_arg(args)?;
match arg { match arg {
FormatArgument::UnsignedInt(u) => match usize::try_from(u) { FormatArgument::UnsignedInt(u) => match usize::try_from(*u) {
Ok(u) => Some(u), Ok(u) => Some(u),
Err(_) => return Err(FormatError::InvalidArgument(arg)), Err(_) => return Err(FormatError::InvalidArgument(arg.clone())),
}, },
_ => return Err(FormatError::InvalidArgument(arg)), _ => return Err(FormatError::InvalidArgument(arg.clone())),
} }
} }
Some(CanAsterisk::Fixed(w)) => Some(w), Some(CanAsterisk::Fixed(w)) => Some(w),
}) })
} }
fn next_arg( fn next_arg<'a>(
mut arguments: impl Iterator<Item = FormatArgument>, mut arguments: impl Iterator<Item = &'a FormatArgument>,
) -> Result<FormatArgument, FormatError> { ) -> Result<&'a FormatArgument, FormatError> {
arguments.next().ok_or(FormatError::NoMoreArguments) arguments.next().ok_or(FormatError::NoMoreArguments)
} }

View file

@ -37,16 +37,14 @@ pub use crate::parser::shortcut_value_parser;
pub use crate::features::backup_control; pub use crate::features::backup_control;
#[cfg(feature = "encoding")] #[cfg(feature = "encoding")]
pub use crate::features::encoding; pub use crate::features::encoding;
#[cfg(feature = "format")]
pub use crate::features::format;
#[cfg(feature = "fs")] #[cfg(feature = "fs")]
pub use crate::features::fs; pub use crate::features::fs;
#[cfg(feature = "fsext")] #[cfg(feature = "fsext")]
pub use crate::features::fsext; pub use crate::features::fsext;
#[cfg(feature = "lines")] #[cfg(feature = "lines")]
pub use crate::features::lines; pub use crate::features::lines;
#[cfg(feature = "format")]
pub use crate::features::format;
#[cfg(feature = "memo")]
pub use crate::features::memo;
#[cfg(feature = "quoting-style")] #[cfg(feature = "quoting-style")]
pub use crate::features::quoting_style; pub use crate::features::quoting_style;
#[cfg(feature = "ranges")] #[cfg(feature = "ranges")]