1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 12:37:49 +00:00

Merge pull request #4137 from jfinkels/dd-bytes-suffix

dd: allow B as a suffix for count, seek, and skip
This commit is contained in:
Terts Diepraam 2022-11-19 12:23:43 +01:00 committed by GitHub
commit 1b35e467ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 126 additions and 51 deletions

View file

@ -14,7 +14,7 @@ use crate::conversion_tables::ConversionTable;
use std::error::Error; use std::error::Error;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::UError; use uucore::error::UError;
use uucore::parse_size::ParseSizeError; use uucore::parse_size::{ParseSizeError, Parser as SizeParser};
use uucore::show_warning; use uucore::show_warning;
/// Parser Errors describe errors with parser input /// Parser Errors describe errors with parser input
@ -499,8 +499,11 @@ fn parse_bytes_only(s: &str) -> Result<u64, ParseError> {
/// assert_eq!(parse_bytes_no_x("2k", "2k").unwrap(), 2 * 1024); /// assert_eq!(parse_bytes_no_x("2k", "2k").unwrap(), 2 * 1024);
/// ``` /// ```
fn parse_bytes_no_x(full: &str, s: &str) -> Result<u64, ParseError> { fn parse_bytes_no_x(full: &str, s: &str) -> Result<u64, ParseError> {
let parser = SizeParser {
capital_b_bytes: true,
};
let (num, multiplier) = match (s.find('c'), s.rfind('w'), s.rfind('b')) { let (num, multiplier) = match (s.find('c'), s.rfind('w'), s.rfind('b')) {
(None, None, None) => match uucore::parse_size::parse_size(s) { (None, None, None) => match parser.parse(s) {
Ok(n) => (n, 1), Ok(n) => (n, 1),
Err(ParseSizeError::InvalidSuffix(_)) | Err(ParseSizeError::ParseFailure(_)) => { Err(ParseSizeError::InvalidSuffix(_)) | Err(ParseSizeError::ParseFailure(_)) => {
return Err(ParseError::InvalidNumber(full.to_string())) return Err(ParseError::InvalidNumber(full.to_string()))

View file

@ -10,29 +10,39 @@ use std::fmt;
use crate::display::Quotable; use crate::display::Quotable;
/// Parse a size string into a number of bytes. /// Parser for sizes in SI or IEC units (multiples of 1000 or 1024 bytes).
/// ///
/// A size string comprises an integer and an optional unit. The unit /// The [`Parser::parse`] function performs the parse.
/// may be K, M, G, T, P, E, Z or Y (powers of 1024), or KB, MB, #[derive(Default)]
/// etc. (powers of 1000), or b which is 512. pub struct Parser {
/// Binary prefixes can be used, too: KiB=K, MiB=M, and so on. /// Whether to treat the suffix "B" as meaning "bytes".
/// pub capital_b_bytes: bool,
/// # Errors }
///
/// Will return `ParseSizeError` if it's not possible to parse this impl Parser {
/// string into a number, e.g. if the string does not begin with a /// Parse a size string into a number of bytes.
/// numeral, or if the unit is not one of the supported units described ///
/// in the preceding section. /// A size string comprises an integer and an optional unit. The unit
/// /// may be K, M, G, T, P, E, Z or Y (powers of 1024), or KB, MB,
/// # Examples /// etc. (powers of 1000), or b which is 512.
/// /// Binary prefixes can be used, too: KiB=K, MiB=M, and so on.
/// ```rust ///
/// use uucore::parse_size::parse_size; /// # Errors
/// assert_eq!(Ok(123), parse_size("123")); ///
/// assert_eq!(Ok(9 * 1000), parse_size("9kB")); // kB is 1000 /// Will return `ParseSizeError` if it's not possible to parse this
/// assert_eq!(Ok(2 * 1024), parse_size("2K")); // K is 1024 /// string into a number, e.g. if the string does not begin with a
/// ``` /// numeral, or if the unit is not one of the supported units described
pub fn parse_size(size: &str) -> Result<u64, ParseSizeError> { /// in the preceding section.
///
/// # Examples
///
/// ```rust
/// use uucore::parse_size::parse_size;
/// assert_eq!(Ok(123), parse_size("123"));
/// assert_eq!(Ok(9 * 1000), parse_size("9kB")); // kB is 1000
/// assert_eq!(Ok(2 * 1024), parse_size("2K")); // K is 1024
/// ```
pub fn parse(&self, size: &str) -> Result<u64, ParseSizeError> {
if size.is_empty() { if size.is_empty() {
return Err(ParseSizeError::parse_failure(size)); return Err(ParseSizeError::parse_failure(size));
} }
@ -52,10 +62,15 @@ pub fn parse_size(size: &str) -> Result<u64, ParseSizeError> {
// the factor it represents. For example, if the argument is "123K", // the factor it represents. For example, if the argument is "123K",
// then the unit part is "K" and the factor is 1024. This may be the // then the unit part is "K" and the factor is 1024. This may be the
// empty string, in which case, the factor is 1. // empty string, in which case, the factor is 1.
//
// The lowercase "b" (used by `od`, `head`, `tail`, etc.) means
// "block" and the Posix block size is 512. The uppercase "B"
// means "byte".
let unit = &size[numeric_string.len()..]; let unit = &size[numeric_string.len()..];
let (base, exponent): (u128, u32) = match unit { let (base, exponent): (u128, u32) = match unit {
"" => (1, 0), "" => (1, 0),
"b" => (512, 1), // (`od`, `head` and `tail` use "b") "B" if self.capital_b_bytes => (1, 0),
"b" => (512, 1),
"KiB" | "kiB" | "K" | "k" => (1024, 1), "KiB" | "kiB" | "K" | "k" => (1024, 1),
"MiB" | "miB" | "M" | "m" => (1024, 2), "MiB" | "miB" | "M" | "m" => (1024, 2),
"GiB" | "giB" | "G" | "g" => (1024, 3), "GiB" | "giB" | "G" | "g" => (1024, 3),
@ -82,6 +97,33 @@ pub fn parse_size(size: &str) -> Result<u64, ParseSizeError> {
number number
.checked_mul(factor) .checked_mul(factor)
.ok_or_else(|| ParseSizeError::size_too_big(size)) .ok_or_else(|| ParseSizeError::size_too_big(size))
}
}
/// Parse a size string into a number of bytes.
///
/// A size string comprises an integer and an optional unit. The unit
/// may be K, M, G, T, P, E, Z or Y (powers of 1024), or KB, MB,
/// etc. (powers of 1000), or b which is 512.
/// Binary prefixes can be used, too: KiB=K, MiB=M, and so on.
///
/// # Errors
///
/// Will return `ParseSizeError` if it's not possible to parse this
/// string into a number, e.g. if the string does not begin with a
/// numeral, or if the unit is not one of the supported units described
/// in the preceding section.
///
/// # Examples
///
/// ```rust
/// use uucore::parse_size::parse_size;
/// assert_eq!(Ok(123), parse_size("123"));
/// assert_eq!(Ok(9 * 1000), parse_size("9kB")); // kB is 1000
/// assert_eq!(Ok(2 * 1024), parse_size("2K")); // K is 1024
/// ```
pub fn parse_size(size: &str) -> Result<u64, ParseSizeError> {
Parser::default().parse(size)
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]

View file

@ -1295,3 +1295,33 @@ fn test_big_multiplication() {
.fails() .fails()
.stderr_contains("invalid number"); .stderr_contains("invalid number");
} }
/// Test for count, seek, and skip given in units of bytes.
#[test]
fn test_bytes_suffix() {
new_ucmd!()
.args(&["count=3B", "status=none"])
.pipe_in("abcdef")
.succeeds()
.stdout_only("abc");
new_ucmd!()
.args(&["skip=3B", "status=none"])
.pipe_in("abcdef")
.succeeds()
.stdout_only("def");
new_ucmd!()
.args(&["iseek=3B", "status=none"])
.pipe_in("abcdef")
.succeeds()
.stdout_only("def");
new_ucmd!()
.args(&["seek=3B", "status=none"])
.pipe_in("abcdef")
.succeeds()
.stdout_only("\0\0\0abcdef");
new_ucmd!()
.args(&["oseek=3B", "status=none"])
.pipe_in("abcdef")
.succeeds()
.stdout_only("\0\0\0abcdef");
}