1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

Merge pull request #6028 from RenjiSann/feature/printf-byte

printf: Make printf operate on bytes instead of strings
This commit is contained in:
Daniel Hofstetter 2024-02-29 16:17:16 +01:00 committed by GitHub
commit 75b2bfb111
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 48 additions and 18 deletions

View file

@ -31,7 +31,7 @@ pub enum FormatArgument {
}
pub trait ArgumentIter<'a>: Iterator<Item = &'a FormatArgument> {
fn get_char(&mut self) -> char;
fn get_char(&mut self) -> u8;
fn get_i64(&mut self) -> i64;
fn get_u64(&mut self) -> u64;
fn get_f64(&mut self) -> f64;
@ -39,14 +39,14 @@ pub trait ArgumentIter<'a>: Iterator<Item = &'a FormatArgument> {
}
impl<'a, T: Iterator<Item = &'a FormatArgument>> ArgumentIter<'a> for T {
fn get_char(&mut self) -> char {
fn get_char(&mut self) -> u8 {
let Some(next) = self.next() else {
return '\0';
return b'\0';
};
match next {
FormatArgument::Char(c) => *c,
FormatArgument::Unparsed(s) => s.bytes().next().map_or('\0', char::from),
_ => '\0',
FormatArgument::Char(c) => *c as u8,
FormatArgument::Unparsed(s) => s.bytes().next().unwrap_or(b'\0'),
_ => b'\0',
}
}

View file

@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (vars) intmax ptrdiff
// spell-checker:ignore (vars) intmax ptrdiff padlen
use crate::quoting_style::{escape_name, QuotingStyle};
@ -14,7 +14,7 @@ use super::{
},
parse_escape_only, ArgumentIter, FormatChar, FormatError,
};
use std::{fmt::Display, io::Write, ops::ControlFlow};
use std::{io::Write, ops::ControlFlow};
/// A parsed specification for formatting a value
///
@ -312,7 +312,7 @@ impl Spec {
match self {
Self::Char { width, align_left } => {
let width = resolve_asterisk(*width, &mut args)?.unwrap_or(0);
write_padded(writer, args.get_char(), width, false, *align_left)
write_padded(writer, &[args.get_char()], width, *align_left)
}
Self::String {
width,
@ -333,7 +333,7 @@ impl Spec {
Some(p) if p < s.len() => &s[..p],
_ => s,
};
write_padded(writer, truncated, width, false, *align_left)
write_padded(writer, truncated.as_bytes(), width, *align_left)
}
Self::EscapedString => {
let s = args.get_str();
@ -445,16 +445,17 @@ fn resolve_asterisk<'a>(
fn write_padded(
mut writer: impl Write,
text: impl Display,
text: &[u8],
width: usize,
pad_zero: bool,
left: bool,
) -> Result<(), FormatError> {
match (left, pad_zero) {
(false, false) => write!(writer, "{text: >width$}"),
(false, true) => write!(writer, "{text:0>width$}"),
// 0 is ignored if we pad left.
(true, _) => write!(writer, "{text: <width$}"),
let padlen = width.saturating_sub(text.len());
if left {
writer.write_all(text)?;
write!(writer, "{: <padlen$}", "")
} else {
write!(writer, "{: >padlen$}", "")?;
writer.write_all(text)
}
.map_err(FormatError::IoError)
}

View file

@ -673,7 +673,11 @@ fn sub_alternative_upper_hex() {
#[test]
fn char_as_byte() {
new_ucmd!().args(&["%c", "🙃"]).succeeds().stdout_only("ð");
new_ucmd!()
.args(&["%c", "🙃"])
.succeeds()
.no_stderr()
.stdout_is_bytes(b"\xf0");
}
#[test]
@ -736,3 +740,28 @@ fn pad_unsigned_three() {
.stdout_only(expected);
}
}
#[test]
fn pad_char() {
for (format, expected) in [("%3c", " X"), ("%1c", "X"), ("%-1c", "X"), ("%-3c", "X ")] {
new_ucmd!()
.args(&[format, "X"])
.succeeds()
.stdout_only(expected);
}
}
#[test]
fn pad_string() {
for (format, expected) in [
("%8s", " bottle"),
("%-8s", "bottle "),
("%6s", "bottle"),
("%-6s", "bottle"),
] {
new_ucmd!()
.args(&[format, "bottle"])
.succeeds()
.stdout_only(expected);
}
}