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

od: refactor parse_formats.rs

simplified, more but shorter functions
This commit is contained in:
Wim Hueskes 2016-08-31 23:47:07 +02:00
parent 184c4af76d
commit fd5879dcf2

View file

@ -1,18 +1,8 @@
use std::collections::HashSet;
use formatteriteminfo::FormatterItemInfo; use formatteriteminfo::FormatterItemInfo;
use prn_int::*; use prn_int::*;
use prn_char::*; use prn_char::*;
use prn_float::*; use prn_float::*;
//This is available in some versions of std, but not all that we target.
macro_rules! hashmap {
($( $key: expr => $val: expr ),*) => {{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $val); )*
map
}}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ParsedFormatterItemInfo { pub struct ParsedFormatterItemInfo {
pub formatter_item_info: FormatterItemInfo, pub formatter_item_info: FormatterItemInfo,
@ -28,6 +18,76 @@ impl ParsedFormatterItemInfo {
} }
} }
fn od_argument_traditional_format(ch: char) -> Option<FormatterItemInfo> {
match ch {
'a' => Some(FORMAT_ITEM_A),
'B' => Some(FORMAT_ITEM_OCT16),
'b' => Some(FORMAT_ITEM_OCT8),
'c' => Some(FORMAT_ITEM_C),
'D' => Some(FORMAT_ITEM_DEC32U),
'd' => Some(FORMAT_ITEM_DEC16U),
'e' => Some(FORMAT_ITEM_F64),
'F' => Some(FORMAT_ITEM_F64),
'f' => Some(FORMAT_ITEM_F32),
'H' => Some(FORMAT_ITEM_HEX32),
'h' => Some(FORMAT_ITEM_HEX16),
'i' => Some(FORMAT_ITEM_DEC32S),
'I' => Some(FORMAT_ITEM_DEC64S),
'L' => Some(FORMAT_ITEM_DEC64S),
'l' => Some(FORMAT_ITEM_DEC64S),
'O' => Some(FORMAT_ITEM_OCT32),
'o' => Some(FORMAT_ITEM_OCT16),
's' => Some(FORMAT_ITEM_DEC16S),
'X' => Some(FORMAT_ITEM_HEX32),
'x' => Some(FORMAT_ITEM_HEX16),
_ => None,
}
}
fn od_format_type(type_char: FormatType, byte_size: u8) -> Option<FormatterItemInfo> {
match (type_char, byte_size) {
(FormatType::Ascii, _) => Some(FORMAT_ITEM_A),
(FormatType::Char, _) => Some(FORMAT_ITEM_C),
(FormatType::DecimalInt, 1) => Some(FORMAT_ITEM_DEC8S),
(FormatType::DecimalInt, 2) => Some(FORMAT_ITEM_DEC16S),
(FormatType::DecimalInt, 0) |
(FormatType::DecimalInt, 4) => Some(FORMAT_ITEM_DEC32S),
(FormatType::DecimalInt, 8) => Some(FORMAT_ITEM_DEC64S),
(FormatType::OctalInt, 1) => Some(FORMAT_ITEM_OCT8),
(FormatType::OctalInt, 2) => Some(FORMAT_ITEM_OCT16),
(FormatType::OctalInt, 0) |
(FormatType::OctalInt, 4) => Some(FORMAT_ITEM_OCT32),
(FormatType::OctalInt, 8) => Some(FORMAT_ITEM_OCT64),
(FormatType::UnsignedInt, 1) => Some(FORMAT_ITEM_DEC8U),
(FormatType::UnsignedInt, 2) => Some(FORMAT_ITEM_DEC16U),
(FormatType::UnsignedInt, 0) |
(FormatType::UnsignedInt, 4) => Some(FORMAT_ITEM_DEC32U),
(FormatType::UnsignedInt, 8) => Some(FORMAT_ITEM_DEC64U),
(FormatType::HexadecimalInt, 1) => Some(FORMAT_ITEM_HEX8),
(FormatType::HexadecimalInt, 2) => Some(FORMAT_ITEM_HEX16),
(FormatType::HexadecimalInt, 0) |
(FormatType::HexadecimalInt, 4) => Some(FORMAT_ITEM_HEX32),
(FormatType::HexadecimalInt, 8) => Some(FORMAT_ITEM_HEX64),
(FormatType::Float, 0) |
(FormatType::Float, 4) => Some(FORMAT_ITEM_F32),
(FormatType::Float, 8) => Some(FORMAT_ITEM_F64),
_ => None,
}
}
fn od_argument_with_option(ch:char) -> bool {
match ch {
'A' | 'j' | 'N' | 'S' | 'w' => true,
_ => false,
}
}
/// Parses format flags from commandline /// Parses format flags from commandline
/// ///
@ -41,32 +101,6 @@ impl ParsedFormatterItemInfo {
/// 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>) -> Result<Vec<ParsedFormatterItemInfo>, String> { pub fn parse_format_flags(args: &Vec<String>) -> Result<Vec<ParsedFormatterItemInfo>, String> {
let known_formats = hashmap![
'a' => FORMAT_ITEM_A,
'B' => FORMAT_ITEM_OCT16,
'b' => FORMAT_ITEM_OCT8,
'c' => FORMAT_ITEM_C,
'D' => FORMAT_ITEM_DEC32U,
'd' => FORMAT_ITEM_DEC16U,
'e' => FORMAT_ITEM_F64,
'F' => FORMAT_ITEM_F64,
'f' => FORMAT_ITEM_F32,
'H' => FORMAT_ITEM_HEX32,
'h' => FORMAT_ITEM_HEX16,
'i' => FORMAT_ITEM_DEC32S,
'I' => FORMAT_ITEM_DEC64S,
'L' => FORMAT_ITEM_DEC64S,
'l' => FORMAT_ITEM_DEC64S,
'O' => FORMAT_ITEM_OCT32,
'o' => FORMAT_ITEM_OCT16,
's' => FORMAT_ITEM_DEC16S,
'X' => FORMAT_ITEM_HEX32,
'x' => FORMAT_ITEM_HEX16
];
let ignored_arg_opts: HashSet<_> = ['A', 'j', 'N', 'S', 'w'].iter().cloned().collect();
let mut formats = Vec::new(); let mut formats = Vec::new();
// args[0] is the name of the binary // args[0] is the name of the binary
@ -103,17 +137,17 @@ pub fn parse_format_flags(args: &Vec<String>) -> Result<Vec<ParsedFormatterItemI
if expect_type_string { if expect_type_string {
format_spec.push(c); format_spec.push(c);
} }
else if ignored_arg_opts.contains(&c) { else if od_argument_with_option(c) {
break; break;
} }
else if c=='t' { else if c=='t' {
expect_type_string = true; expect_type_string = true;
} }
else { else {
match known_formats.get(&c) { match od_argument_traditional_format(c) {
None => {} // not every option is a format None => {} // not every option is a format
Some(r) => { Some(r) => {
formats.push(ParsedFormatterItemInfo::new(*r, false)) formats.push(ParsedFormatterItemInfo::new(r, false))
} }
} }
} }
@ -138,166 +172,148 @@ pub fn parse_format_flags(args: &Vec<String>) -> Result<Vec<ParsedFormatterItemI
Ok(formats) Ok(formats)
} }
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug, Copy, Clone)]
enum ParseState { enum FormatType {
ExpectSize, // expect optional size character like L for long. Ascii,
ExpectDecimal, // expect optional additional digits, like for 16. Char,
ExpectDump, // expect optional 'z'. DecimalInt,
Finished // no more characters may appear. OctalInt,
UnsignedInt,
HexadecimalInt,
Float,
}
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
enum FormatTypeCategory {
Char,
Integer,
Float,
}
fn format_type(ch: char) -> Option<FormatType> {
match ch {
'a' => Some(FormatType::Ascii),
'c' => Some(FormatType::Char),
'd' => Some(FormatType::DecimalInt),
'o' => Some(FormatType::OctalInt),
'u' => Some(FormatType::UnsignedInt),
'x' => Some(FormatType::HexadecimalInt),
'f' => Some(FormatType::Float),
_ => None,
}
}
fn format_type_category(t: FormatType) -> FormatTypeCategory {
match t {
FormatType::Ascii | FormatType::Char
=> FormatTypeCategory::Char,
FormatType::DecimalInt | FormatType::OctalInt | FormatType::UnsignedInt | FormatType::HexadecimalInt
=> FormatTypeCategory::Integer,
FormatType::Float
=> FormatTypeCategory::Float,
}
}
fn is_format_size_char(ch: Option<char>, format_type: FormatTypeCategory, byte_size: &mut u8) -> bool {
match (format_type, ch) {
(FormatTypeCategory::Integer, Some('C')) => {
*byte_size = 1;
true
},
(FormatTypeCategory::Integer, Some('S')) => {
*byte_size = 2;
true
},
(FormatTypeCategory::Integer, Some('I')) => {
*byte_size = 4;
true
},
(FormatTypeCategory::Integer, Some('L')) => {
*byte_size = 8;
true
},
(FormatTypeCategory::Float, Some('F')) => {
*byte_size = 4;
true
},
(FormatTypeCategory::Float, Some('D')) => {
*byte_size = 8;
true
},
// FormatTypeCategory::Float, 'L' => *byte_size = 16, // TODO support f128
_ => false,
}
}
fn is_format_size_decimal(ch: Option<char>, format_type: FormatTypeCategory, decimal_size: &mut String) -> bool {
if format_type == FormatTypeCategory::Char { return false; }
match ch {
Some(d) if d.is_digit(10) => {
decimal_size.push(d);
return true;
}
_ => false,
}
}
fn is_format_dump_char(ch: Option<char>, show_ascii_dump: &mut bool) -> bool {
match ch {
Some('z') => {
*show_ascii_dump = true;
return true;
}
_ => false,
}
} }
fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, String> { fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, 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(); let mut formats = Vec::new();
// first split a type string into parts refering a single type let mut chars=params.chars();
let mut type_parts = Vec::new(); let mut ch = chars.next();
let mut s = String::new();
for c in params.chars() { while ch.is_some() {
if type_all.contains(&c) { let type_char = ch.unwrap();
if !s.is_empty() { let type_char = match format_type(type_char) {
type_parts.push(s); Some(t) => t,
s = String::new(); None => {
return Err(format!("unexpected char '{}' in format specification '{}'", type_char, params));
} }
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 type_cat = format_type_category(type_char);
let mut chars=format_type.chars();
let type_char = chars.next().unwrap(); ch = chars.next();
let mut parse_state = ParseState::ExpectSize;
let mut decimal_size = String::new();
let mut byte_size = 0u8; let mut byte_size = 0u8;
let mut show_ascii_dump = false; let mut show_ascii_dump = false;
if is_format_size_char(ch, type_cat, &mut byte_size) {
if type_chars.contains(&type_char) { ch = chars.next();
parse_state = ParseState::ExpectDump;
} }
else {
loop { let mut decimal_size = String::new();
match chars.next() { while is_format_size_decimal(ch, type_cat, &mut decimal_size) {
None => break, ch = chars.next();
Some('z') if parse_state != ParseState::Finished => { }
show_ascii_dump = true; if !decimal_size.is_empty() {
parse_state = ParseState::Finished; byte_size=match decimal_size.parse() {
}, Err(_) => return Err(format!("invalid number '{}' in format specification '{}'", decimal_size, params)),
Some(d) if d.is_digit(10) Ok(n) => n,
&& (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 is_format_dump_char(ch, &mut show_ascii_dump) {
if !decimal_size.is_empty() { ch = chars.next();
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 { match od_format_type(type_char, byte_size) {
'a' => formats.push(ParsedFormatterItemInfo::new(FORMAT_ITEM_A, show_ascii_dump)), Some(ft) => formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump)),
'c' => formats.push(ParsedFormatterItemInfo::new(FORMAT_ITEM_C, show_ascii_dump)), None => return Err(format!("invalid size '{}' in format specification '{}'", byte_size, params)),
'd' => {
formats.push(ParsedFormatterItemInfo::new(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)),
}, show_ascii_dump));
},
'o' => {
formats.push(ParsedFormatterItemInfo::new(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)),
}, show_ascii_dump));
},
'u' => {
formats.push(ParsedFormatterItemInfo::new(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)),
}, show_ascii_dump));
},
'x' => {
formats.push(ParsedFormatterItemInfo::new(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)),
}, show_ascii_dump));
},
'f' => {
formats.push(ParsedFormatterItemInfo::new(match byte_size {
4|0 => FORMAT_ITEM_F32,
8 => FORMAT_ITEM_F64,
_ => return Err(format!("invalid size '{}' in format specification '{}'", byte_size, format_type)),
}, show_ascii_dump));
},
_ => unreachable!(),
} }
if show_ascii_dump { /*TODO*/ }
} }
Ok(formats) Ok(formats)