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

od: implement --format / -t

This commit is contained in:
Wim Hueskes 2016-08-11 13:45:39 +02:00
parent d15604b2e4
commit cea4297fdf
4 changed files with 372 additions and 31 deletions

View file

@ -84,7 +84,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
opts.optflagmulti("f", "", "floating point single precision (32-bit) units"); opts.optflagmulti("f", "", "floating point single precision (32-bit) units");
opts.optflagmulti("F", "", "floating point double precision (64-bit) units"); opts.optflagmulti("F", "", "floating point double precision (64-bit) units");
opts.optopt("t", "format", "select output format or formats", "TYPE"); opts.optmulti("t", "format", "select output format or formats", "TYPE");
opts.optflag("v", "output-duplicates", "do not use * to mark line suppression"); opts.optflag("v", "output-duplicates", "do not use * to mark line suppression");
opts.optflagopt("w", "width", opts.optflagopt("w", "width",
("output BYTES bytes per output line. 32 is implied when BYTES is not \ ("output BYTES bytes per output line. 32 is implied when BYTES is not \
@ -146,7 +146,13 @@ pub fn uumain(args: Vec<String>) -> i32 {
inputs.push(InputSource::Stdin); inputs.push(InputSource::Stdin);
} }
let formats = parse_format_flags(&args); let formats = match parse_format_flags(&args) {
Ok(f) => f,
Err(e) => {
disp_err!("{}", e);
return 1;
}
};
let mut line_bytes = match matches.opt_default("w", "32") { let mut line_bytes = match matches.opt_default("w", "32") {
None => 16, None => 16,

View file

@ -24,10 +24,8 @@ macro_rules! hashmap {
/// arguments with parameters like -w16 can only appear at the end: -fvoxw16 /// arguments with parameters like -w16 can only appear at the end: -fvoxw16
/// parameters of -t/--format specify 1 or more formats. /// parameters of -t/--format specify 1 or more formats.
/// if -- appears on the commandline, parsing should stop. /// if -- appears on the commandline, parsing should stop.
pub fn parse_format_flags(args: &Vec<String>) -> Vec<FormatterItemInfo> { pub fn parse_format_flags(args: &Vec<String>) -> Result<Vec<FormatterItemInfo>, String> {
// Gather up format flags, we don't use getopts becase we need keep them in order.
// TODO: -t fmts
let known_formats = hashmap![ let known_formats = hashmap![
'a' => FORMAT_ITEM_A, 'a' => FORMAT_ITEM_A,
'B' => FORMAT_ITEM_OCT16, 'B' => FORMAT_ITEM_OCT16,
@ -57,38 +55,240 @@ pub fn parse_format_flags(args: &Vec<String>) -> Vec<FormatterItemInfo> {
// args[0] is the name of the binary // args[0] is the name of the binary
let mut arg_iter = args.iter().skip(1); let mut arg_iter = args.iter().skip(1);
let mut expect_type_string = false;
while let Some(arg) = arg_iter.next() { while let Some(arg) = arg_iter.next() {
if arg.starts_with("--") { if expect_type_string {
match parse_type_string(arg) {
Ok(v) => formats.extend(v.into_iter()),
Err(e) => return Err(e),
}
expect_type_string = false;
}
else if arg.starts_with("--") {
if arg.len() == 2 { if arg.len() == 2 {
break; break;
} }
if arg.starts_with("--format=") {
let params: String = arg.chars().skip_while(|c| *c != '=').skip(1).collect();
match parse_type_string(&params) {
Ok(v) => formats.extend(v.into_iter()),
Err(e) => return Err(e),
}
}
if arg == "--format" {
expect_type_string = true;
}
} }
else if arg.starts_with("-") { else if arg.starts_with("-") {
let mut flags = arg.chars().skip(1); let mut flags = arg.chars().skip(1);
let mut format_spec = String::new();
while let Some(c) = flags.next() { while let Some(c) = flags.next() {
if ignored_arg_opts.contains(&c) { if expect_type_string {
format_spec.push(c);
}
else if ignored_arg_opts.contains(&c) {
break; break;
} }
match known_formats.get(&c) { else if c=='t' {
None => {} // not every option is a format expect_type_string = true;
Some(r) => { }
formats.push(*r) else {
match known_formats.get(&c) {
None => {} // not every option is a format
Some(r) => {
formats.push(*r)
}
} }
} }
} }
if !format_spec.is_empty() {
match parse_type_string(&format_spec) {
Ok(v) => formats.extend(v.into_iter()),
Err(e) => return Err(e),
}
expect_type_string = false;
}
} }
} }
if expect_type_string {
return Err(format!("missing format specification after '--format' / '-t'"));
}
if formats.is_empty() { if formats.is_empty() {
formats.push(FORMAT_ITEM_OCT16); // 2 byte octal is the default formats.push(FORMAT_ITEM_OCT16); // 2 byte octal is the default
} }
formats Ok(formats)
}
#[derive(PartialEq, Eq, Debug)]
enum ParseState {
ExpectSize, // expect optional size character like L for long.
ExpectDecimal, // expect optional additional digits, like for 16.
ExpectDump, // expect optional 'z'.
Finished // no more characters may appear.
}
fn parse_type_string(params: &String) -> Result<Vec<FormatterItemInfo>, String> {
let type_chars: HashSet<_> = ['a', 'c'].iter().cloned().collect();
let type_ints: HashSet<_> = ['d', 'o', 'u', 'x'].iter().cloned().collect();
let type_floats: HashSet<_> = ['f'].iter().cloned().collect();
let type_all: HashSet<_> =
type_chars.iter()
.chain(type_ints.iter())
.chain(type_floats.iter())
.collect();
let mut formats = Vec::new();
// first split a type string into parts refering a single type
let mut type_parts = Vec::new();
let mut s = String::new();
for c in params.chars() {
if type_all.contains(&c) {
if !s.is_empty() {
type_parts.push(s);
s = String::new();
}
s.push(c);
}
else {
if s.is_empty() {
return Err(format!("unexpected char '{}' in format specification '{}'", c, params));
}
s.push(c);
}
}
if !s.is_empty() {
type_parts.push(s);
}
for format_type in type_parts.iter() {
let mut chars=format_type.chars();
let type_char = chars.next().unwrap();
let mut parse_state = ParseState::ExpectSize;
let mut decimal_size = String::new();
let mut byte_size = 0u8;
let mut show_ascii_dump = false;
if type_chars.contains(&type_char) {
parse_state = ParseState::ExpectDump;
}
loop {
match chars.next() {
None => break,
Some('z') if parse_state != ParseState::Finished => {
show_ascii_dump = true;
parse_state = ParseState::Finished;
},
Some(d) if d.is_digit(10)
&& (parse_state == ParseState::ExpectSize || parse_state == ParseState::ExpectDecimal) => {
decimal_size.push(d);
parse_state = ParseState::ExpectDecimal;
},
Some('C') if type_ints.contains(&type_char) && parse_state == ParseState::ExpectSize => {
byte_size = 1;
parse_state = ParseState::ExpectDump;
},
Some('S') if type_ints.contains(&type_char) && parse_state == ParseState::ExpectSize => {
byte_size = 2;
parse_state = ParseState::ExpectDump;
},
Some('I') if type_ints.contains(&type_char) && parse_state == ParseState::ExpectSize => {
byte_size = 4;
parse_state = ParseState::ExpectDump;
},
Some('L') if type_ints.contains(&type_char) && parse_state == ParseState::ExpectSize => {
byte_size = 8;
parse_state = ParseState::ExpectDump;
},
Some('F') if type_char == 'f' && parse_state == ParseState::ExpectSize => {
byte_size = 4;
parse_state = ParseState::ExpectDump;
},
Some('D') if type_char == 'f' && parse_state == ParseState::ExpectSize => {
byte_size = 8;
parse_state = ParseState::ExpectDump;
},
// Some('L') if type_char == 'f' => byte_size = 16, // TODO support f128
Some(c) => {
return Err(format!("unexpected char '{}' in format specification '{}'", c, format_type));
}
}
}
if !decimal_size.is_empty() {
byte_size=match decimal_size.parse() {
Err(_) => return Err(format!("invalid number '{}' in format specification '{}'", decimal_size, format_type)),
Ok(n) => n,
}
}
match type_char {
'a' => formats.push(FORMAT_ITEM_A),
'c' => formats.push(FORMAT_ITEM_C),
'd' => {
formats.push(match byte_size {
1 => FORMAT_ITEM_DEC8S,
2 => FORMAT_ITEM_DEC16S,
4|0 => FORMAT_ITEM_DEC32S,
8 => FORMAT_ITEM_DEC64S,
_ => return Err(format!("invalid size '{}' in format specification '{}'", byte_size, format_type)),
});
},
'o' => {
formats.push(match byte_size {
1 => FORMAT_ITEM_OCT8,
2 => FORMAT_ITEM_OCT16,
4|0 => FORMAT_ITEM_OCT32,
8 => FORMAT_ITEM_OCT64,
_ => return Err(format!("invalid size '{}' in format specification '{}'", byte_size, format_type)),
});
},
'u' => {
formats.push(match byte_size {
1 => FORMAT_ITEM_DEC8U,
2 => FORMAT_ITEM_DEC16U,
4|0 => FORMAT_ITEM_DEC32U,
8 => FORMAT_ITEM_DEC64U,
_ => return Err(format!("invalid size '{}' in format specification '{}'", byte_size, format_type)),
});
},
'x' => {
formats.push(match byte_size {
1 => FORMAT_ITEM_HEX8,
2 => FORMAT_ITEM_HEX16,
4|0 => FORMAT_ITEM_HEX32,
8 => FORMAT_ITEM_HEX64,
_ => return Err(format!("invalid size '{}' in format specification '{}'", byte_size, format_type)),
});
},
'f' => {
formats.push(match byte_size {
4|0 => FORMAT_ITEM_F32,
8 => FORMAT_ITEM_F64,
_ => return Err(format!("invalid size '{}' in format specification '{}'", byte_size, format_type)),
});
},
_ => unreachable!(),
}
if show_ascii_dump { /*TODO*/ }
}
Ok(formats)
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn parse_format_flags_str(args_str: &Vec<&'static str>) -> Vec<FormatterItemInfo> { pub fn parse_format_flags_str(args_str: &Vec<&'static str>) -> Result<Vec<FormatterItemInfo>, String> {
let args = args_str.iter().map(|s| s.to_string()).collect(); let args = args_str.iter().map(|s| s.to_string()).collect();
parse_format_flags(&args) parse_format_flags(&args)
} }
@ -96,48 +296,188 @@ pub fn parse_format_flags_str(args_str: &Vec<&'static str>) -> Vec<FormatterItem
#[test] #[test]
fn test_no_options() { fn test_no_options() {
assert_eq!(parse_format_flags_str( assert_eq!(parse_format_flags_str(
&vec!("od")), &vec!("od")).unwrap(),
vec!(FORMAT_ITEM_OCT16)); vec!(FORMAT_ITEM_OCT16));
} }
#[test] #[test]
fn test_one_option() { fn test_one_option() {
assert_eq!(parse_format_flags_str( assert_eq!(parse_format_flags_str(
&vec!("od", "-F")), &vec!("od", "-F")).unwrap(),
vec!(FORMAT_ITEM_F64)); vec!(FORMAT_ITEM_F64));
} }
#[test] #[test]
fn test_two_separate_options() { fn test_two_separate_options() {
assert_eq!(parse_format_flags_str( assert_eq!(parse_format_flags_str(
&vec!("od", "-F", "-x")), &vec!("od", "-F", "-x")).unwrap(),
vec!(FORMAT_ITEM_F64, FORMAT_ITEM_HEX16)); vec!(FORMAT_ITEM_F64, FORMAT_ITEM_HEX16));
} }
#[test] #[test]
fn test_two_combined_options() { fn test_two_combined_options() {
assert_eq!(parse_format_flags_str( assert_eq!(parse_format_flags_str(
&vec!("od", "-Fx")), &vec!("od", "-Fx")).unwrap(),
vec!(FORMAT_ITEM_F64, FORMAT_ITEM_HEX16)); vec!(FORMAT_ITEM_F64, FORMAT_ITEM_HEX16));
} }
#[test] #[test]
fn test_ignore_non_format_parameters() { fn test_ignore_non_format_parameters() {
assert_eq!(parse_format_flags_str( assert_eq!(parse_format_flags_str(
&vec!("od", "-d", "-Ax")), &vec!("od", "-d", "-Ax")).unwrap(),
vec!(FORMAT_ITEM_DEC16U)); vec!(FORMAT_ITEM_DEC16U));
} }
#[test] #[test]
fn test_ignore_separate_parameters() { fn test_ignore_separate_parameters() {
assert_eq!(parse_format_flags_str( assert_eq!(parse_format_flags_str(
&vec!("od", "-I", "-A", "x")), &vec!("od", "-I", "-A", "x")).unwrap(),
vec!(FORMAT_ITEM_DEC64S)); vec!(FORMAT_ITEM_DEC64S));
} }
#[test] #[test]
fn test_ignore_trailing_vals() { fn test_ignore_trailing_vals() {
assert_eq!(parse_format_flags_str( assert_eq!(parse_format_flags_str(
&vec!("od", "-D", "--", "-x")), &vec!("od", "-D", "--", "-x")).unwrap(),
vec!(FORMAT_ITEM_DEC32U)); vec!(FORMAT_ITEM_DEC32U));
} }
#[test]
fn test_invalid_long_format() {
parse_format_flags_str(&vec!("od", "--format=X")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xX")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=aC")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=fI")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xD")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xC1")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=x1C")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xz1")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xzC")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xzz")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xCC")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=c1")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=x256")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=d5")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=f2")).unwrap_err();
}
#[test]
fn test_long_format_a() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=a")).unwrap(),
vec!(FORMAT_ITEM_A));
}
#[test]
fn test_long_format_cz() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=cz")).unwrap(),
vec!(FORMAT_ITEM_C)); // TODO 'z'
}
#[test]
fn test_long_format_d() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=d8")).unwrap(),
vec!(FORMAT_ITEM_DEC64S));
}
#[test]
fn test_long_format_d_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=d")).unwrap(),
vec!(FORMAT_ITEM_DEC32S));
}
#[test]
fn test_long_format_o_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=o")).unwrap(),
vec!(FORMAT_ITEM_OCT32));
}
#[test]
fn test_long_format_u_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=u")).unwrap(),
vec!(FORMAT_ITEM_DEC32U));
}
#[test]
fn test_long_format_x_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=x")).unwrap(),
vec!(FORMAT_ITEM_HEX32));
}
#[test]
fn test_long_format_f_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=f")).unwrap(),
vec!(FORMAT_ITEM_F32));
}
#[test]
fn test_long_format_next_arg() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format", "f8")).unwrap(),
vec!(FORMAT_ITEM_F64));
}
#[test]
fn test_short_format_next_arg() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-t", "x8")).unwrap(),
vec!(FORMAT_ITEM_HEX64));
}
#[test]
fn test_short_format_combined_arg() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-tu8")).unwrap(),
vec!(FORMAT_ITEM_DEC64U));
}
#[test]
fn test_format_next_arg_invalid() {
parse_format_flags_str(&vec!("od", "--format", "-v")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format")).unwrap_err();
parse_format_flags_str(&vec!("od", "-t", "-v")).unwrap_err();
parse_format_flags_str(&vec!("od", "-t")).unwrap_err();
}
#[test]
fn test_mixed_formats() {
assert_eq!(parse_format_flags_str(
&vec!(
"od",
"--skip-bytes=2",
"-vItu1z",
"-N",
"1000",
"-xt",
"acdx1",
"--format=u2c",
"--format",
"f",
"-xAx",
"--",
"-h",
"--format=f8")).unwrap(),
vec!(
FORMAT_ITEM_DEC64S, // I
FORMAT_ITEM_DEC8U, // tu1z
FORMAT_ITEM_HEX16, // x
FORMAT_ITEM_A, // ta
FORMAT_ITEM_C, // tc
FORMAT_ITEM_DEC32S, // td
FORMAT_ITEM_HEX8, // tx1
FORMAT_ITEM_DEC16U, // tu2
FORMAT_ITEM_C, // tc
FORMAT_ITEM_F32, // tf
FORMAT_ITEM_HEX16, // x
));
}

View file

@ -18,14 +18,12 @@ pub static FORMAT_ITEM_OCT32: FormatterItemInfo = FormatterItemInfo {
formatter: FormatWriter::IntWriter(format_item_oct), formatter: FormatWriter::IntWriter(format_item_oct),
}; };
#[allow(dead_code)]
pub static FORMAT_ITEM_OCT64: FormatterItemInfo = FormatterItemInfo { pub static FORMAT_ITEM_OCT64: FormatterItemInfo = FormatterItemInfo {
byte_size: 8, byte_size: 8,
print_width: 23, // max: 2000000000000000000000 print_width: 23, // max: 1777777777777777777777
formatter: FormatWriter::IntWriter(format_item_oct), formatter: FormatWriter::IntWriter(format_item_oct),
}; };
#[allow(dead_code)]
pub static FORMAT_ITEM_HEX8: FormatterItemInfo = FormatterItemInfo { pub static FORMAT_ITEM_HEX8: FormatterItemInfo = FormatterItemInfo {
byte_size: 1, byte_size: 1,
print_width: 3, // max: ff print_width: 3, // max: ff
@ -44,7 +42,6 @@ pub static FORMAT_ITEM_HEX32: FormatterItemInfo = FormatterItemInfo {
formatter: FormatWriter::IntWriter(format_item_hex), formatter: FormatWriter::IntWriter(format_item_hex),
}; };
#[allow(dead_code)]
pub static FORMAT_ITEM_HEX64: FormatterItemInfo = FormatterItemInfo { pub static FORMAT_ITEM_HEX64: FormatterItemInfo = FormatterItemInfo {
byte_size: 8, byte_size: 8,
print_width: 17, // max: ffffffffffffffff print_width: 17, // max: ffffffffffffffff
@ -52,7 +49,6 @@ pub static FORMAT_ITEM_HEX64: FormatterItemInfo = FormatterItemInfo {
}; };
#[allow(dead_code)]
pub static FORMAT_ITEM_DEC8U: FormatterItemInfo = FormatterItemInfo { pub static FORMAT_ITEM_DEC8U: FormatterItemInfo = FormatterItemInfo {
byte_size: 1, byte_size: 1,
print_width: 4, // max: 255 print_width: 4, // max: 255
@ -71,7 +67,6 @@ pub static FORMAT_ITEM_DEC32U: FormatterItemInfo = FormatterItemInfo {
formatter: FormatWriter::IntWriter(format_item_dec_u), formatter: FormatWriter::IntWriter(format_item_dec_u),
}; };
#[allow(dead_code)]
pub static FORMAT_ITEM_DEC64U: FormatterItemInfo = FormatterItemInfo { pub static FORMAT_ITEM_DEC64U: FormatterItemInfo = FormatterItemInfo {
byte_size: 8, byte_size: 8,
print_width: 21, // max: 18446744073709551615 print_width: 21, // max: 18446744073709551615
@ -79,7 +74,6 @@ pub static FORMAT_ITEM_DEC64U: FormatterItemInfo = FormatterItemInfo {
}; };
#[allow(dead_code)]
pub static FORMAT_ITEM_DEC8S: FormatterItemInfo = FormatterItemInfo { pub static FORMAT_ITEM_DEC8S: FormatterItemInfo = FormatterItemInfo {
byte_size: 1, byte_size: 1,
print_width: 5, // max: -128 print_width: 5, // max: -128
@ -92,14 +86,12 @@ pub static FORMAT_ITEM_DEC16S: FormatterItemInfo = FormatterItemInfo {
formatter: FormatWriter::IntWriter(format_item_dec_s), formatter: FormatWriter::IntWriter(format_item_dec_s),
}; };
#[allow(dead_code)]
pub static FORMAT_ITEM_DEC32S: FormatterItemInfo = FormatterItemInfo { pub static FORMAT_ITEM_DEC32S: FormatterItemInfo = FormatterItemInfo {
byte_size: 4, byte_size: 4,
print_width: 12, // max: -2147483648 print_width: 12, // max: -2147483648
formatter: FormatWriter::IntWriter(format_item_dec_s), formatter: FormatWriter::IntWriter(format_item_dec_s),
}; };
#[allow(dead_code)]
pub static FORMAT_ITEM_DEC64S: FormatterItemInfo = FormatterItemInfo { pub static FORMAT_ITEM_DEC64S: FormatterItemInfo = FormatterItemInfo {
byte_size: 8, byte_size: 8,
print_width: 21, // max: -9223372036854775808 print_width: 21, // max: -9223372036854775808

View file

@ -414,15 +414,18 @@ fn test_maxuint(){
let input = [0xFFu8 ; 8]; let input = [0xFFu8 ; 8];
let expected_output = unindent(" let expected_output = unindent("
0000000 37777777777 37777777777 0000000 1777777777777777777777
37777777777 37777777777
177777 177777 177777 177777 177777 177777 177777 177777
377 377 377 377 377 377 377 377 377 377 377 377 377 377 377 377
18446744073709551615
4294967295 4294967295 4294967295 4294967295
65535 65535 65535 65535 65535 65535 65535 65535
255 255 255 255 255 255 255 255
0000010 0000010
"); ");
let result = new_ucmd!().arg("-O").arg("-o").arg("-b").arg("-D").arg("-d").run_piped_stdin(&input[..]); let result = new_ucmd!().arg("--format=o8").arg("-Oobtu8").arg("-Dd").arg("--format=u1").run_piped_stdin(&input[..]);
assert_empty_stderr!(result); assert_empty_stderr!(result);
assert!(result.success); assert!(result.success);