Skip to content

Commit 0474e55

Browse files
aweinstock314aweinstock
andauthored
uucore: Permit 'D' as a decimal suffix modifier in size parsing for GNU compatibility. (#11354)
truncate: Reject 'b' as a unit. Co-authored-by: aweinstock <avi@zellic.io>
1 parent 3b3d5a7 commit 0474e55

3 files changed

Lines changed: 43 additions & 21 deletions

File tree

src/uu/truncate/src/truncate.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
1616
use uucore::format_usage;
1717
use uucore::translate;
1818

19-
use uucore::parser::parse_size::{ParseSizeError, parse_size_u64};
19+
use uucore::parser::parse_size::{ParseSizeError, Parser, allow_list_with_all_suffixes};
2020

2121
#[derive(Debug, Eq, PartialEq)]
2222
enum TruncateMode {
@@ -298,7 +298,7 @@ fn is_modifier(c: char) -> bool {
298298

299299
/// Parse a size string with optional modifier symbol as its first character.
300300
///
301-
/// A size string is as described in [`parse_size_u64`]. The first character
301+
/// A size string is as described in [`Parser::parse_u64`]. The first character
302302
/// of `size_string` might be a modifier symbol, like `'+'` or
303303
/// `'<'`. The first element of the pair returned by this function
304304
/// indicates which modifier symbol was present, or
@@ -323,15 +323,20 @@ fn parse_mode_and_size(size_string: &str) -> Result<TruncateMode, ParseSizeError
323323
if is_modifier(c) {
324324
size_string = &size_string[1..];
325325
}
326-
parse_size_u64(size_string).map(match c {
327-
'+' => TruncateMode::Extend,
328-
'-' => TruncateMode::Reduce,
329-
'<' => TruncateMode::AtMost,
330-
'>' => TruncateMode::AtLeast,
331-
'/' => TruncateMode::RoundDown,
332-
'%' => TruncateMode::RoundUp,
333-
_ => TruncateMode::Absolute,
334-
})
326+
let allow_list = allow_list_with_all_suffixes("EgGkKmMPQRtTYZ");
327+
let allow_list_ref = allow_list.iter().map(AsRef::as_ref).collect::<Vec<&str>>();
328+
Parser::default()
329+
.with_allow_list(&allow_list_ref)
330+
.parse_u64(size_string)
331+
.map(match c {
332+
'+' => TruncateMode::Extend,
333+
'-' => TruncateMode::Reduce,
334+
'<' => TruncateMode::AtMost,
335+
'>' => TruncateMode::AtLeast,
336+
'/' => TruncateMode::RoundDown,
337+
'%' => TruncateMode::RoundUp,
338+
_ => TruncateMode::Absolute,
339+
})
335340
} else {
336341
Err(ParseSizeError::ParseFailure(size_string.to_string()))
337342
}
@@ -351,6 +356,9 @@ mod tests {
351356
assert_eq!(parse_mode_and_size(">10"), Ok(TruncateMode::AtLeast(10)));
352357
assert_eq!(parse_mode_and_size("/10"), Ok(TruncateMode::RoundDown(10)));
353358
assert_eq!(parse_mode_and_size("%10"), Ok(TruncateMode::RoundUp(10)));
359+
assert_eq!(parse_mode_and_size("1kB"), Ok(TruncateMode::Absolute(1000)));
360+
assert_eq!(parse_mode_and_size("1kD"), Ok(TruncateMode::Absolute(1000)));
361+
assert!(parse_mode_and_size("1b").is_err());
354362
}
355363

356364
#[test]

src/uucore/src/lib/features/parser/parse_size.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -244,16 +244,16 @@ impl<'parser> Parser<'parser> {
244244
"YiB" | "yiB" | "Y" | "y" => (1024, 8),
245245
"RiB" | "riB" | "R" | "r" => (1024, 9),
246246
"QiB" | "qiB" | "Q" | "q" => (1024, 10),
247-
"KB" | "kB" => (1000, 1),
248-
"MB" | "mB" => (1000, 2),
249-
"GB" | "gB" => (1000, 3),
250-
"TB" | "tB" => (1000, 4),
251-
"PB" | "pB" => (1000, 5),
252-
"EB" | "eB" => (1000, 6),
253-
"ZB" | "zB" => (1000, 7),
254-
"YB" | "yB" => (1000, 8),
255-
"RB" | "rB" => (1000, 9),
256-
"QB" | "qB" => (1000, 10),
247+
"KB" | "kB" | "KD" | "kD" => (1000, 1),
248+
"MB" | "mB" | "MD" | "mD" => (1000, 2),
249+
"GB" | "gB" | "GD" | "gD" => (1000, 3),
250+
"TB" | "tB" | "TD" | "tD" => (1000, 4),
251+
"PB" | "pB" | "PD" | "pD" => (1000, 5),
252+
"EB" | "eB" | "ED" | "eD" => (1000, 6),
253+
"ZB" | "zB" | "ZD" | "zD" => (1000, 7),
254+
"YB" | "yB" | "YD" | "yD" => (1000, 8),
255+
"RB" | "rB" | "RD" | "rD" => (1000, 9),
256+
"QB" | "qB" | "QD" | "qD" => (1000, 10),
257257
_ if numeric_string.is_empty() => return Err(ParseSizeError::parse_failure(size)),
258258
_ => return Err(ParseSizeError::invalid_suffix(size)),
259259
};
@@ -373,6 +373,16 @@ impl<'parser> Parser<'parser> {
373373
}
374374
}
375375

376+
pub fn allow_list_with_all_suffixes(units: &str) -> Vec<String> {
377+
let mut allow_list = Vec::with_capacity(4 * units.len());
378+
for unit in units.chars() {
379+
for suffix in &["", "iB", "B", "D"] {
380+
allow_list.push(format!("{unit}{suffix}"));
381+
}
382+
}
383+
allow_list
384+
}
385+
376386
/// Parse a size string into a number of bytes
377387
/// using Default Parser (no custom settings)
378388
///

tests/by-util/test_truncate.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ fn test_invalid_numbers() {
254254
.args(&["-s", "0B", "file"])
255255
.fails()
256256
.stderr_contains("Invalid number: '0B'");
257+
new_ucmd!()
258+
.args(&["-s", "1b", "file"])
259+
.fails()
260+
.stderr_contains("Invalid number: '1b'");
257261
}
258262

259263
#[test]

0 commit comments

Comments
 (0)