mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
od: refactor: reduce arguments of odfunc
Pass the input stream itself instead of the parameters required to open it. Create InputOffset to handle functionality required for the byte offset.
This commit is contained in:
parent
d705dc46ce
commit
f2db897c47
2 changed files with 166 additions and 76 deletions
129
src/od/inputoffset.rs
Normal file
129
src/od/inputoffset.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Radix { Decimal, Hexadecimal, Octal, NoPrefix }
|
||||
|
||||
/// provides the byte offset printed at the left margin
|
||||
pub struct InputOffset {
|
||||
/// The radix to print the byte offset. NoPrefix will not print a byte offset.
|
||||
radix: Radix,
|
||||
/// The current position. Initialize at `new`, increase using `increase_position`.
|
||||
byte_pos: usize,
|
||||
/// An optional label printed in parentheses, typically different from `byte_pos`,
|
||||
/// but will increase with the same value if `byte_pos` in increased.
|
||||
label: Option<usize>,
|
||||
}
|
||||
|
||||
impl InputOffset {
|
||||
/// creates a new `InputOffset` using the provided values.
|
||||
pub fn new(radix: Radix, byte_pos: usize, label: Option<usize>) -> InputOffset {
|
||||
InputOffset {
|
||||
radix: radix,
|
||||
byte_pos: byte_pos,
|
||||
label: label,
|
||||
}
|
||||
}
|
||||
|
||||
/// Increase `byte_pos` and `label` if a label is used.
|
||||
pub fn increase_position(&mut self, n: usize) {
|
||||
self.byte_pos += n;
|
||||
if let Some(l) = self.label {
|
||||
self.label = Some(l + n);
|
||||
}
|
||||
}
|
||||
|
||||
/// set `self.radix` to the value provided by the --address-radix commandline option
|
||||
pub fn parse_radix_from_commandline(&mut self, radix_str: Option<String>) -> Result<(), &'static str> {
|
||||
match radix_str {
|
||||
None => self.radix = Radix::Octal,
|
||||
Some(s) => {
|
||||
let st = s.into_bytes();
|
||||
if st.len() != 1 {
|
||||
return Err("Radix must be one of [d, o, n, x]\n")
|
||||
} else {
|
||||
let radix: char = *(st.get(0)
|
||||
.expect("byte string of length 1 lacks a 0th elem")) as char;
|
||||
match radix {
|
||||
'd' => self.radix = Radix::Decimal,
|
||||
'x' => self.radix = Radix::Hexadecimal,
|
||||
'o' => self.radix = Radix::Octal,
|
||||
'n' => self.radix = Radix::NoPrefix,
|
||||
_ => return Err("Radix must be one of [d, o, n, x]\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// returns a string with the current byte offset
|
||||
pub fn format_byte_offset(&self) -> String {
|
||||
match (self.radix, self.label) {
|
||||
(Radix::Decimal, None) => format!("{:07}", self.byte_pos),
|
||||
(Radix::Decimal, Some(l)) => format!("{:07} ({:07})", self.byte_pos, l),
|
||||
(Radix::Hexadecimal, None) => format!("{:06X}", self.byte_pos),
|
||||
(Radix::Hexadecimal, Some(l)) => format!("{:06X} ({:06X})", self.byte_pos, l),
|
||||
(Radix::Octal, None) => format!("{:07o}", self.byte_pos),
|
||||
(Radix::Octal, Some(l)) => format!("{:07o} ({:07o})", self.byte_pos, l),
|
||||
(Radix::NoPrefix, None) => String::from(""),
|
||||
(Radix::NoPrefix, Some(l)) => format!("({:07o})", l),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints the byte offset followed by a newline, or nothing at all if
|
||||
/// both `Radix::NoPrefix` was set and no label (--traditional) is used.
|
||||
pub fn print_final_offset(&self) {
|
||||
if self.radix != Radix::NoPrefix || self.label.is_some() {
|
||||
print!("{}\n", self.format_byte_offset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_offset() {
|
||||
let mut sut = InputOffset::new(Radix::Hexadecimal, 10, None);
|
||||
assert_eq!("00000A", &sut.format_byte_offset());
|
||||
sut.increase_position(10);
|
||||
assert_eq!("000014", &sut.format_byte_offset());
|
||||
|
||||
// note normally the radix will not change after initialisation
|
||||
sut.parse_radix_from_commandline(Some("d".to_string())).unwrap();
|
||||
assert_eq!("0000020", &sut.format_byte_offset());
|
||||
|
||||
sut.parse_radix_from_commandline(Some("x".to_string())).unwrap();
|
||||
assert_eq!("000014", &sut.format_byte_offset());
|
||||
|
||||
sut.parse_radix_from_commandline(Some("o".to_string())).unwrap();
|
||||
assert_eq!("0000024", &sut.format_byte_offset());
|
||||
|
||||
sut.parse_radix_from_commandline(Some("n".to_string())).unwrap();
|
||||
assert_eq!("", &sut.format_byte_offset());
|
||||
|
||||
sut.increase_position(10);
|
||||
sut.parse_radix_from_commandline(None).unwrap();
|
||||
assert_eq!("0000036", &sut.format_byte_offset());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_offset_with_label() {
|
||||
let mut sut = InputOffset::new(Radix::Hexadecimal, 10, Some(20));
|
||||
assert_eq!("00000A (000014)", &sut.format_byte_offset());
|
||||
sut.increase_position(10);
|
||||
assert_eq!("000014 (00001E)", &sut.format_byte_offset());
|
||||
|
||||
// note normally the radix will not change after initialisation
|
||||
sut.parse_radix_from_commandline(Some("d".to_string())).unwrap();
|
||||
assert_eq!("0000020 (0000030)", &sut.format_byte_offset());
|
||||
|
||||
sut.parse_radix_from_commandline(Some("x".to_string())).unwrap();
|
||||
assert_eq!("000014 (00001E)", &sut.format_byte_offset());
|
||||
|
||||
sut.parse_radix_from_commandline(Some("o".to_string())).unwrap();
|
||||
assert_eq!("0000024 (0000036)", &sut.format_byte_offset());
|
||||
|
||||
sut.parse_radix_from_commandline(Some("n".to_string())).unwrap();
|
||||
assert_eq!("(0000036)", &sut.format_byte_offset());
|
||||
|
||||
sut.increase_position(10);
|
||||
sut.parse_radix_from_commandline(None).unwrap();
|
||||
assert_eq!("0000036 (0000050)", &sut.format_byte_offset());
|
||||
}
|
113
src/od/od.rs
113
src/od/od.rs
|
@ -26,6 +26,7 @@ mod prn_float;
|
|||
mod parse_nrofbytes;
|
||||
mod parse_formats;
|
||||
mod parse_inputs;
|
||||
mod inputoffset;
|
||||
#[cfg(test)]
|
||||
mod mockstream;
|
||||
|
||||
|
@ -40,14 +41,12 @@ use parse_nrofbytes::parse_number_of_bytes;
|
|||
use parse_formats::{parse_format_flags, ParsedFormatterItemInfo};
|
||||
use prn_char::format_ascii_dump;
|
||||
use parse_inputs::{parse_inputs, CommandLineInputs};
|
||||
use inputoffset::{InputOffset, Radix};
|
||||
|
||||
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
const MAX_BYTES_PER_UNIT: usize = 8;
|
||||
const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
enum Radix { Decimal, Hexadecimal, Octal, NoPrefix }
|
||||
|
||||
static USAGE: &'static str =
|
||||
r#"Usage:
|
||||
od [OPTION]... [--] [FILENAME]...
|
||||
|
@ -156,14 +155,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
|||
return 0;
|
||||
}
|
||||
|
||||
let input_offset_base = match parse_radix(matches.opt_str("A")) {
|
||||
Ok(r) => r,
|
||||
Err(f) => {
|
||||
disp_err!("Invalid -A/--address-radix\n{}", f);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
let byte_order = match matches.opt_str("endian").as_ref().map(String::as_ref) {
|
||||
None => { ByteOrder::Native },
|
||||
Some("little") => { ByteOrder::Little },
|
||||
|
@ -201,13 +192,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
|||
return 1;
|
||||
}
|
||||
};
|
||||
let inputs = input_strings
|
||||
.iter()
|
||||
.map(|w| match w as &str {
|
||||
"-" => InputSource::Stdin,
|
||||
x => InputSource::FileName(x),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let formats = match parse_format_flags(&args) {
|
||||
Ok(f) => f,
|
||||
|
@ -247,19 +231,23 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
|||
}
|
||||
};
|
||||
|
||||
odfunc(line_bytes, input_offset_base, byte_order, inputs, &formats[..],
|
||||
output_duplicates, skip_bytes, read_bytes, label)
|
||||
let mut input = open_input_peek_reader(&input_strings, skip_bytes, read_bytes);
|
||||
|
||||
let mut input_offset = InputOffset::new(Radix::Octal, skip_bytes, label);
|
||||
if let Err(e) = input_offset.parse_radix_from_commandline(matches.opt_str("A")) {
|
||||
disp_err!("Invalid -A/--address-radix\n{}", e);
|
||||
return 1;
|
||||
}
|
||||
|
||||
odfunc(&mut input, &mut input_offset, line_bytes, byte_order, &formats[..],
|
||||
output_duplicates)
|
||||
}
|
||||
|
||||
// TODO: refactor, too many arguments
|
||||
fn odfunc(line_bytes: usize, input_offset_base: Radix, byte_order: ByteOrder,
|
||||
fnames: Vec<InputSource>, formats: &[ParsedFormatterItemInfo], output_duplicates: bool,
|
||||
skip_bytes: usize, read_bytes: Option<usize>, mut label: Option<usize>) -> i32 {
|
||||
fn odfunc<I>(input: &mut I, input_offset: &mut InputOffset, line_bytes: usize, byte_order: ByteOrder,
|
||||
formats: &[ParsedFormatterItemInfo], output_duplicates: bool) -> i32
|
||||
where I : PeekRead+HasError {
|
||||
|
||||
let mf = MultifileReader::new(fnames);
|
||||
let pr = PartialReader::new(mf, skip_bytes, read_bytes);
|
||||
let mut input = PeekReader::new(pr);
|
||||
let mut addr = skip_bytes;
|
||||
let mut duplicate_line = false;
|
||||
let mut previous_bytes: Vec<u8> = Vec::new();
|
||||
let mut bytes: Vec<u8> = Vec::with_capacity(line_bytes + PEEK_BUFFER_SIZE);
|
||||
|
@ -305,11 +293,10 @@ fn odfunc(line_bytes: usize, input_offset_base: Radix, byte_order: ByteOrder,
|
|||
|
||||
loop {
|
||||
// print each line data (or multi-format raster of several lines describing the same data).
|
||||
// TODO: we need to read more data in case a multi-byte sequence starts at the end of the line
|
||||
|
||||
match input.peek_read(bytes.as_mut_slice(), PEEK_BUFFER_SIZE) {
|
||||
Ok((0, _)) => {
|
||||
print_final_offset(input_offset_base, addr, label);
|
||||
input_offset.print_final_offset();
|
||||
break;
|
||||
}
|
||||
Ok((n, peekbytes)) => {
|
||||
|
@ -343,18 +330,15 @@ fn odfunc(line_bytes: usize, input_offset_base: Radix, byte_order: ByteOrder,
|
|||
}
|
||||
|
||||
print_bytes(byte_order, &bytes, n, peekbytes,
|
||||
&print_with_radix(input_offset_base, addr, label),
|
||||
&input_offset.format_byte_offset(),
|
||||
&spaced_formatters, byte_size_block, print_width_line);
|
||||
}
|
||||
|
||||
addr += n;
|
||||
if let Some(l) = label {
|
||||
label = Some(l + n);
|
||||
}
|
||||
input_offset.increase_position(n);
|
||||
}
|
||||
Err(e) => {
|
||||
show_error!("{}", e);
|
||||
print_final_offset(input_offset_base, addr, label);
|
||||
input_offset.print_final_offset();
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
@ -441,48 +425,25 @@ fn print_bytes(byte_order: ByteOrder, bytes: &[u8], length: usize, peekbytes: us
|
|||
}
|
||||
}
|
||||
|
||||
// For file byte offset printed at left margin.
|
||||
fn parse_radix(radix_str: Option<String>) -> Result<Radix, &'static str> {
|
||||
match radix_str {
|
||||
None => Ok(Radix::Octal),
|
||||
Some(s) => {
|
||||
let st = s.into_bytes();
|
||||
if st.len() != 1 {
|
||||
Err("Radix must be one of [d, o, n, x]\n")
|
||||
} else {
|
||||
let radix: char = *(st.get(0)
|
||||
.expect("byte string of length 1 lacks a 0th elem")) as char;
|
||||
match radix {
|
||||
'd' => Ok(Radix::Decimal),
|
||||
'x' => Ok(Radix::Hexadecimal),
|
||||
'o' => Ok(Radix::Octal),
|
||||
'n' => Ok(Radix::NoPrefix),
|
||||
_ => Err("Radix must be one of [d, o, n, x]\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// returns a reader implementing `PeekRead+Read+HasError` providing the combined input
|
||||
///
|
||||
/// `skip_bytes` is the number of bytes skipped from the input
|
||||
/// `read_bytes` is an optinal limit to the number of bytes to read
|
||||
fn open_input_peek_reader<'a>(input_strings: &'a Vec<String>, skip_bytes: usize,
|
||||
read_bytes: Option<usize>) -> PeekReader<PartialReader<MultifileReader<'a>>> {
|
||||
// should return "impl PeekRead+Read+HasError" when supported in (stable) rust
|
||||
let inputs = input_strings
|
||||
.iter()
|
||||
.map(|w| match w as &str {
|
||||
"-" => InputSource::Stdin,
|
||||
x => InputSource::FileName(x),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
fn print_with_radix(r: Radix, x: usize, label: Option<usize>) -> String{
|
||||
match (r, label) {
|
||||
(Radix::Decimal, None) => format!("{:07}", x),
|
||||
(Radix::Decimal, Some(l)) => format!("{:07} ({:07})", x, l),
|
||||
(Radix::Hexadecimal, None) => format!("{:06X}", x),
|
||||
(Radix::Hexadecimal, Some(l)) => format!("{:06X} ({:06X})", x, l),
|
||||
(Radix::Octal, None) => format!("{:07o}", x),
|
||||
(Radix::Octal, Some(l)) => format!("{:07o} ({:07o})", x, l),
|
||||
(Radix::NoPrefix, None) => String::from(""),
|
||||
(Radix::NoPrefix, Some(l)) => format!("({:07o})", l),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints the byte offset followed by a newline, or nothing at all if
|
||||
/// both `Radix::NoPrefix` was set and no label (--traditional) is used.
|
||||
fn print_final_offset(r: Radix, x: usize, label: Option<usize>) {
|
||||
if r != Radix::NoPrefix || label.is_some() {
|
||||
print!("{}\n", print_with_radix(r, x, label));
|
||||
}
|
||||
let mf = MultifileReader::new(inputs);
|
||||
let pr = PartialReader::new(mf, skip_bytes, read_bytes);
|
||||
let input = PeekReader::new(pr);
|
||||
input
|
||||
}
|
||||
|
||||
struct SpacedFormatterItemInfo {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue