1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-31 13:07:46 +00:00

printf: implement %b

This commit is contained in:
Terts Diepraam 2023-11-17 14:41:14 +01:00
parent a45ff8ca73
commit cd0c24af07
2 changed files with 40 additions and 9 deletions

View file

@ -169,17 +169,17 @@ fn parse_spec_only(fmt: &[u8]) -> impl Iterator<Item = Result<FormatItem<u8>, Fo
}) })
} }
fn parse_escape_only(fmt: &[u8]) -> impl Iterator<Item = Result<EscapedChar, FormatError>> + '_ { fn parse_escape_only(fmt: &[u8]) -> impl Iterator<Item = EscapedChar> + '_ {
let mut current = fmt; let mut current = fmt;
std::iter::from_fn(move || match current { std::iter::from_fn(move || match current {
[] => return None, [] => return None,
[b'\\', rest @ ..] => { [b'\\', rest @ ..] => {
current = rest; current = rest;
Some(Ok(parse_escape_code(&mut current))) Some(parse_escape_code(&mut current))
} }
[c, rest @ ..] => { [c, rest @ ..] => {
current = rest; current = rest;
Some(Ok(EscapedChar::Char(*c))) Some(EscapedChar::Char(*c))
} }
}) })
} }

View file

@ -5,9 +5,9 @@ use super::{
self, Case, FloatVariant, ForceDecimal, Formatter, NumberAlignment, PositiveSign, Prefix, self, Case, FloatVariant, ForceDecimal, Formatter, NumberAlignment, PositiveSign, Prefix,
UnsignedIntVariant, UnsignedIntVariant,
}, },
FormatArgument, FormatError, parse_escape_only, FormatArgument, FormatChar, FormatError,
}; };
use std::{fmt::Display, io::Write}; use std::{fmt::Display, io::Write, ops::ControlFlow};
#[derive(Debug)] #[derive(Debug)]
pub enum Spec { pub enum Spec {
@ -17,6 +17,7 @@ pub enum Spec {
}, },
String { String {
width: Option<CanAsterisk<usize>>, width: Option<CanAsterisk<usize>>,
parse_escape: bool,
align_left: bool, align_left: bool,
}, },
SignedInt { SignedInt {
@ -145,6 +146,12 @@ impl Spec {
}, },
b's' => Spec::String { b's' => Spec::String {
width, width,
parse_escape: false,
align_left: minus,
},
b'b' => Spec::String {
width,
parse_escape: true,
align_left: minus, align_left: minus,
}, },
b'd' | b'i' => Spec::SignedInt { b'd' | b'i' => Spec::SignedInt {
@ -230,12 +237,36 @@ impl Spec {
_ => Err(FormatError::InvalidArgument(arg.clone())), _ => Err(FormatError::InvalidArgument(arg.clone())),
} }
} }
&Spec::String { width, align_left } => { &Spec::String {
width,
parse_escape,
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)?; let arg = next_arg(&mut args)?;
match arg.get_str() { let Some(s) = arg.get_str() else {
Some(s) => write_padded(writer, s, width, false, align_left), return Err(FormatError::InvalidArgument(arg.clone()));
_ => Err(FormatError::InvalidArgument(arg.clone())), };
if parse_escape {
let mut parsed = Vec::new();
for c in parse_escape_only(s.as_bytes()) {
match c.write(&mut parsed)? {
ControlFlow::Continue(()) => {}
ControlFlow::Break(()) => {
// TODO: This should break the _entire execution_ of printf
break;
}
};
}
write_padded(
writer,
std::str::from_utf8(&parsed).expect("TODO: Accept invalid utf8"),
width,
false,
align_left,
)
} else {
write_padded(writer, s, width, false, align_left)
} }
} }
&Spec::SignedInt { &Spec::SignedInt {