od: several small changes after review

* update status in README.md
* enable busybox tests
  Adding `CONFIG_DESKTOP` and `CONFIG_LONG_OPTS` to busybox config.
  These flags also enable other tests, but those utilities are not
  included in `TEST_PROGS`. (eg. awk)
* fix whitespace and small issues
* fix Eq imp for FormatWriter on nightly + beta
* fix indention in multifilereader.rs
* fix intermittent errors in tests
This commit is contained in:
Wim Hueskes 2016-11-09 20:26:55 +01:00
parent 99f70ba648
commit 2550e0f3c7
16 changed files with 180 additions and 247 deletions

View file

@ -1,2 +1,4 @@
CONFIG_FEATURE_FANCY_HEAD=y CONFIG_FEATURE_FANCY_HEAD=y
CONFIG_UNICODE_SUPPORT=y CONFIG_UNICODE_SUPPORT=y
CONFIG_DESKTOP=y
CONFIG_LONG_OPTS=y

View file

@ -201,7 +201,7 @@ To do
* [x] nohup * [x] nohup
* [x] nproc * [x] nproc
* [ ] numfmt * [ ] numfmt
* [ ] od (in progress, needs lots of work) * [ ] od (almost complete, `--strings` and 128-bit datatypes are missing)
* [x] paste * [x] paste
* [x] pathchk * [x] pathchk
* [x] pinky * [x] pinky

View file

@ -1,6 +1,6 @@
use std::fmt; use std::fmt;
#[derive(Copy, Eq)] #[derive(Copy)]
pub enum FormatWriter { pub enum FormatWriter {
IntWriter(fn(u64) -> String), IntWriter(fn(u64) -> String),
FloatWriter(fn(f64) -> String), FloatWriter(fn(f64) -> String),
@ -27,6 +27,8 @@ impl PartialEq for FormatWriter {
} }
} }
impl Eq for FormatWriter {}
impl fmt::Debug for FormatWriter { impl fmt::Debug for FormatWriter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {

View file

@ -26,12 +26,11 @@ pub struct InputDecoder<'a, I> where I: 'a {
} }
impl<'a, I> InputDecoder<'a, I> { impl<'a, I> InputDecoder<'a, I> {
/// Creates a new `InputDecoder` with an allocated buffer of `normal_length`+`peek_length` bytes. /// Creates a new `InputDecoder` with an allocated buffer of `normal_length` + `peek_length` bytes.
/// `byte_order` determines how to read multibyte formats from the buffer. /// `byte_order` determines how to read multibyte formats from the buffer.
pub fn new(input: &mut I, normal_length: usize, peek_length: usize, byte_order: ByteOrder) -> InputDecoder<I> { pub fn new(input: &mut I, normal_length: usize, peek_length: usize, byte_order: ByteOrder) -> InputDecoder<I> {
let mut bytes: Vec<u8> = Vec::with_capacity(normal_length + peek_length);
let mut bytes: Vec<u8> = Vec::with_capacity(normal_length+peek_length); unsafe { bytes.set_len(normal_length + peek_length); } // fast but uninitialized
unsafe { bytes.set_len(normal_length+peek_length); } // fast but uninitialized
InputDecoder { InputDecoder {
input: input, input: input,
@ -45,7 +44,7 @@ impl<'a, I> InputDecoder<'a, I> {
} }
impl<'a, I> InputDecoder<'a, I> where I : PeekRead { impl<'a, I> InputDecoder<'a, I> where I: PeekRead {
/// calls `peek_read` on the internal stream to (re)fill the buffer. Returns a /// calls `peek_read` on the internal stream to (re)fill the buffer. Returns a
/// MemoryDecoder providing access to the result or returns an i/o error. /// MemoryDecoder providing access to the result or returns an i/o error.
pub fn peek_read(&mut self) -> io::Result<MemoryDecoder> { pub fn peek_read(&mut self) -> io::Result<MemoryDecoder> {
@ -66,7 +65,7 @@ impl<'a, I> InputDecoder<'a, I> where I : PeekRead {
} }
} }
impl<'a, I> HasError for InputDecoder<'a, I> where I : HasError { impl<'a, I> HasError for InputDecoder<'a, I> where I: HasError {
/// calls has_error on the internal stream. /// calls has_error on the internal stream.
fn has_error(&self) -> bool { fn has_error(&self) -> bool {
self.input.has_error() self.input.has_error()
@ -112,7 +111,7 @@ impl<'a> MemoryDecoder<'a> {
/// Returns a slice to the internal buffer including the peek data starting at `start`. /// Returns a slice to the internal buffer including the peek data starting at `start`.
pub fn get_full_buffer(&self, start: usize) -> &[u8] { pub fn get_full_buffer(&self, start: usize) -> &[u8] {
&self.data[start..self.used_normal_length+self.used_peek_length] &self.data[start..self.used_normal_length + self.used_peek_length]
} }
/// Returns a u8/u16/u32/u64 from the internal buffer at position `start`. /// Returns a u8/u16/u32/u64 from the internal buffer at position `start`.
@ -147,8 +146,8 @@ mod tests {
#[test] #[test]
fn smoke_test() { fn smoke_test() {
let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xff, 0xff]; let data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xff, 0xff];
let mut input=PeekReader::new(Cursor::new(&data)); let mut input = PeekReader::new(Cursor::new(&data));
let mut sut=InputDecoder::new(&mut input, 8, 2, ByteOrder::Little); let mut sut = InputDecoder::new(&mut input, 8, 2, ByteOrder::Little);
match sut.peek_read() { match sut.peek_read() {
Ok(mut mem) => { Ok(mut mem) => {
@ -165,7 +164,7 @@ mod tests {
let mut copy: Vec<u8> = Vec::new(); let mut copy: Vec<u8> = Vec::new();
mem.clone_buffer(&mut copy); mem.clone_buffer(&mut copy);
assert_eq!(vec!{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0}, copy); assert_eq!(vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0], copy);
mem.zero_out_buffer(7, 8); mem.zero_out_buffer(7, 8);
assert_eq!(&[0, 0, 0xff, 0xff], mem.get_full_buffer(6)); assert_eq!(&[0, 0, 0xff, 0xff], mem.get_full_buffer(6));

View file

@ -62,8 +62,7 @@ impl FailingMockStream {
fn error(&mut self) -> Result<usize> { fn error(&mut self) -> Result<usize> {
if self.repeat_count == 0 { if self.repeat_count == 0 {
return Ok(0) return Ok(0)
} } else {
else {
if self.repeat_count > 0 { if self.repeat_count > 0 {
self.repeat_count -= 1; self.repeat_count -= 1;
} }

View file

@ -6,7 +6,7 @@ use std::io::Write;
use std::vec::Vec; use std::vec::Vec;
pub enum InputSource<'a> { pub enum InputSource<'a> {
FileName(&'a str ), FileName(&'a str),
Stdin, Stdin,
#[allow(dead_code)] #[allow(dead_code)]
Stream(Box<io::Read>), Stream(Box<io::Read>),
@ -31,26 +31,26 @@ impl<'b> MultifileReader<'b> {
any_err: false, any_err: false,
}; };
mf.next_file(); mf.next_file();
return mf; mf
} }
fn next_file(&mut self) { fn next_file(&mut self) {
// loop retries with subsequent files if err - normally 'loops' once // loop retries with subsequent files if err - normally 'loops' once
loop { loop {
if self.ni.len() == 0 { if self.ni.len() == 0 {
self.curr_file = None; self.curr_file = None;
return; break;
} }
match self.ni.remove(0) { match self.ni.remove(0) {
InputSource::Stdin => { InputSource::Stdin => {
self.curr_file = Some(Box::new(BufReader::new(std::io::stdin()))); self.curr_file = Some(Box::new(BufReader::new(std::io::stdin())));
return; break;
} }
InputSource::FileName(fname) => { InputSource::FileName(fname) => {
match File::open(fname) { match File::open(fname) {
Ok(f) => { Ok(f) => {
self.curr_file = Some(Box::new(BufReader::new(f))); self.curr_file = Some(Box::new(BufReader::new(f)));
return; break;
} }
Err(e) => { Err(e) => {
// If any file can't be opened, // If any file can't be opened,
@ -66,7 +66,7 @@ impl<'b> MultifileReader<'b> {
} }
InputSource::Stream(s) => { InputSource::Stream(s) => {
self.curr_file = Some(s); self.curr_file = Some(s);
return; break;
} }
} }
} }
@ -74,7 +74,6 @@ impl<'b> MultifileReader<'b> {
} }
impl<'b> io::Read for MultifileReader<'b> { impl<'b> io::Read for MultifileReader<'b> {
// Fill buf with bytes read from the list of files // Fill buf with bytes read from the list of files
// Returns Ok(<number of bytes read>) // Returns Ok(<number of bytes read>)
// Handles io errors itself, thus always returns OK // Handles io errors itself, thus always returns OK
@ -192,5 +191,4 @@ mod tests {
assert_eq!(sut.read(v.as_mut()).unwrap(), 3); assert_eq!(sut.read(v.as_mut()).unwrap(), 3);
assert_eq!(v, [0x42, 0x43, 0x44, 0x64, 0x41]); // last two bytes are not overwritten assert_eq!(v, [0x42, 0x43, 0x44, 0x64, 0x41]); // last two bytes are not overwritten
} }
} }

View file

@ -147,8 +147,8 @@ fn create_getopts_options() -> getopts::Options {
struct OdOptions { struct OdOptions {
byte_order: ByteOrder, byte_order: ByteOrder,
skip_bytes : usize, skip_bytes: usize,
read_bytes : Option<usize>, read_bytes: Option<usize>,
label: Option<usize>, label: Option<usize>,
input_strings: Vec<String>, input_strings: Vec<String>,
formats: Vec<ParsedFormatterItemInfo>, formats: Vec<ParsedFormatterItemInfo>,
@ -187,7 +187,7 @@ impl OdOptions {
Ok(CommandLineInputs::FileAndOffset((f, s, l))) => { Ok(CommandLineInputs::FileAndOffset((f, s, l))) => {
skip_bytes = s; skip_bytes = s;
label = l; label = l;
vec!{f} vec![f]
}, },
Err(e) => { Err(e) => {
return Err(format!("Invalid inputs: {}", e)); return Err(format!("Invalid inputs: {}", e));
@ -311,7 +311,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
/// Loops through the input line by line, calling print_bytes to take care of the output. /// Loops through the input line by line, calling print_bytes to take care of the output.
fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>, fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>,
output_info: &OutputInfo) -> i32 output_info: &OutputInfo) -> i32
where I : PeekRead+HasError { where I: PeekRead + HasError {
let mut duplicate_line = false; let mut duplicate_line = false;
let mut previous_bytes: Vec<u8> = Vec::new(); let mut previous_bytes: Vec<u8> = Vec::new();
let line_bytes = output_info.byte_size_line; let line_bytes = output_info.byte_size_line;
@ -321,7 +321,7 @@ fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>
match input_decoder.peek_read() { match input_decoder.peek_read() {
Ok(mut memory_decoder) => { Ok(mut memory_decoder) => {
let length=memory_decoder.length(); let length = memory_decoder.length();
if length == 0 { if length == 0 {
input_offset.print_final_offset(); input_offset.print_final_offset();
@ -346,8 +346,7 @@ fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>
duplicate_line = true; duplicate_line = true;
println!("*"); println!("*");
} }
} } else {
else {
duplicate_line = false; duplicate_line = false;
if length == line_bytes { if length == line_bytes {
// save a copy of the input unless it is the last line // save a copy of the input unless it is the last line
@ -409,15 +408,14 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
output_text.push_str(&format!("{:>width$} {}", output_text.push_str(&format!("{:>width$} {}",
"", "",
format_ascii_dump(input_decoder.get_buffer(0)), format_ascii_dump(input_decoder.get_buffer(0)),
width=missing_spacing)); width = missing_spacing));
} }
if first { if first {
print!("{}", prefix); // print offset print!("{}", prefix); // print offset
// if printing in multiple formats offset is printed only once // if printing in multiple formats offset is printed only once
first = false; first = false;
} } else {
else {
// this takes the space of the file offset on subsequent // this takes the space of the file offset on subsequent
// lines of multi-format rasters. // lines of multi-format rasters.
print!("{:>width$}", "", width=prefix.chars().count()); print!("{:>width$}", "", width=prefix.chars().count());
@ -426,13 +424,13 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
} }
} }
/// returns a reader implementing `PeekRead+Read+HasError` providing the combined input /// returns a reader implementing `PeekRead + Read + HasError` providing the combined input
/// ///
/// `skip_bytes` is the number of bytes skipped from the 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 /// `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, fn open_input_peek_reader<'a>(input_strings: &'a Vec<String>, skip_bytes: usize,
read_bytes: Option<usize>) -> PeekReader<PartialReader<MultifileReader<'a>>> { read_bytes: Option<usize>) -> PeekReader<PartialReader<MultifileReader<'a>>> {
// should return "impl PeekRead+Read+HasError" when supported in (stable) rust // should return "impl PeekRead + Read + HasError" when supported in (stable) rust
let inputs = input_strings let inputs = input_strings
.iter() .iter()
.map(|w| match w as &str { .map(|w| match w as &str {

View file

@ -49,7 +49,6 @@ impl OutputInfo {
/// Creates a new `OutputInfo` based on the parameters /// Creates a new `OutputInfo` based on the parameters
pub fn new(line_bytes: usize, formats: &[ParsedFormatterItemInfo], output_duplicates: bool) -> OutputInfo { pub fn new(line_bytes: usize, formats: &[ParsedFormatterItemInfo], output_duplicates: bool) -> OutputInfo {
let byte_size_block = formats.iter().fold(1, |max, next| cmp::max(max, next.formatter_item_info.byte_size)); let byte_size_block = formats.iter().fold(1, |max, next| cmp::max(max, next.formatter_item_info.byte_size));
let print_width_block = formats let print_width_block = formats
.iter() .iter()
@ -129,7 +128,6 @@ impl OutputInfo {
/// Increase MAX_BYTES_PER_UNIT to allow larger types. /// Increase MAX_BYTES_PER_UNIT to allow larger types.
fn calculate_alignment(sf: &TypeSizeInfo, byte_size_block: usize, fn calculate_alignment(sf: &TypeSizeInfo, byte_size_block: usize,
print_width_block: usize) -> [usize; MAX_BYTES_PER_UNIT] { print_width_block: usize) -> [usize; MAX_BYTES_PER_UNIT] {
if byte_size_block > MAX_BYTES_PER_UNIT { if byte_size_block > MAX_BYTES_PER_UNIT {
panic!("{}-bits types are unsupported. Current max={}-bits.", panic!("{}-bits types are unsupported. Current max={}-bits.",
8 * byte_size_block, 8 * byte_size_block,
@ -181,7 +179,6 @@ impl TypeSizeInfo for TypeInfo {
#[test] #[test]
fn test_calculate_alignment() { fn test_calculate_alignment() {
// For this example `byte_size_block` is 8 and 'print_width_block' is 23: // For this example `byte_size_block` is 8 and 'print_width_block' is 23:
// 1777777777777777777777 1777777777777777777777 // 1777777777777777777777 1777777777777777777777
// 4294967295 4294967295 4294967295 4294967295 // 4294967295 4294967295 4294967295 4294967295
@ -214,31 +211,31 @@ fn test_calculate_alignment() {
// 9 tests where 8 .. 16 spaces are spread across 8 positions // 9 tests where 8 .. 16 spaces are spread across 8 positions
assert_eq!([1, 1, 1, 1, 1, 1, 1, 1], assert_eq!([1, 1, 1, 1, 1, 1, 1, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+8)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 8));
assert_eq!([2, 1, 1, 1, 1, 1, 1, 1], assert_eq!([2, 1, 1, 1, 1, 1, 1, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+9)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 9));
assert_eq!([2, 1, 1, 1, 2, 1, 1, 1], assert_eq!([2, 1, 1, 1, 2, 1, 1, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+10)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 10));
assert_eq!([3, 1, 1, 1, 2, 1, 1, 1], assert_eq!([3, 1, 1, 1, 2, 1, 1, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+11)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 11));
assert_eq!([2, 1, 2, 1, 2, 1, 2, 1], assert_eq!([2, 1, 2, 1, 2, 1, 2, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+12)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 12));
assert_eq!([3, 1, 2, 1, 2, 1, 2, 1], assert_eq!([3, 1, 2, 1, 2, 1, 2, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+13)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 13));
assert_eq!([3, 1, 2, 1, 3, 1, 2, 1], assert_eq!([3, 1, 2, 1, 3, 1, 2, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+14)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 14));
assert_eq!([4, 1, 2, 1, 3, 1, 2, 1], assert_eq!([4, 1, 2, 1, 3, 1, 2, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+15)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 15));
assert_eq!([2, 2, 2, 2, 2, 2, 2, 2], assert_eq!([2, 2, 2, 2, 2, 2, 2, 2],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+16)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 16));
// 4 tests where 15 spaces are spread across 8, 4, 2 or 1 position(s) // 4 tests where 15 spaces are spread across 8, 4, 2 or 1 position(s)
assert_eq!([4, 1, 2, 1, 3, 1, 2, 1], assert_eq!([4, 1, 2, 1, 3, 1, 2, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16+15)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 15));
assert_eq!([5, 0, 3, 0, 4, 0, 3, 0], assert_eq!([5, 0, 3, 0, 4, 0, 3, 0],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:2, print_width:4}, 8, 16+15)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:2, print_width:4}, 8, 16 + 15));
assert_eq!([8, 0, 0, 0, 7, 0, 0, 0], assert_eq!([8, 0, 0, 0, 7, 0, 0, 0],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:4, print_width:8}, 8, 16+15)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:4, print_width:8}, 8, 16 + 15));
assert_eq!([15, 0, 0, 0, 0, 0, 0, 0], assert_eq!([15, 0, 0, 0, 0, 0, 0, 0],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:8, print_width:16}, 8, 16+15)); OutputInfo::calculate_alignment(&TypeInfo{byte_size:8, print_width:16}, 8, 16 + 15));
} }

View file

@ -115,8 +115,7 @@ pub fn parse_format_flags(args: &Vec<String>) -> Result<Vec<ParsedFormatterItemI
Err(e) => return Err(e), Err(e) => return Err(e),
} }
expect_type_string = false; expect_type_string = false;
} } else if arg.starts_with("--") {
else if arg.starts_with("--") {
if arg.len() == 2 { if arg.len() == 2 {
break; break;
} }
@ -130,26 +129,20 @@ pub fn parse_format_flags(args: &Vec<String>) -> Result<Vec<ParsedFormatterItemI
if arg == "--format" { if arg == "--format" {
expect_type_string = true; 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(); let mut format_spec = String::new();
while let Some(c) = flags.next() { while let Some(c) = flags.next() {
if expect_type_string { if expect_type_string {
format_spec.push(c); format_spec.push(c);
} } else if od_argument_with_option(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 { // not every option is a format
match od_argument_traditional_format(c) { if let Some(r) = od_argument_traditional_format(c) {
None => {} // not every option is a format formats.push(ParsedFormatterItemInfo::new(r, false))
Some(r) => {
formats.push(ParsedFormatterItemInfo::new(r, false))
}
} }
} }
} }
@ -217,7 +210,6 @@ fn format_type_category(t: FormatType) -> FormatTypeCategory {
} }
fn is_format_size_char(ch: Option<char>, format_type: FormatTypeCategory, byte_size: &mut u8) -> bool { fn is_format_size_char(ch: Option<char>, format_type: FormatTypeCategory, byte_size: &mut u8) -> bool {
match (format_type, ch) { match (format_type, ch) {
(FormatTypeCategory::Integer, Some('C')) => { (FormatTypeCategory::Integer, Some('C')) => {
*byte_size = 1; *byte_size = 1;
@ -274,7 +266,7 @@ fn is_format_dump_char(ch: Option<char>, show_ascii_dump: &mut bool) -> bool {
fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, String> { fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, String> {
let mut formats = Vec::new(); let mut formats = Vec::new();
let mut chars=params.chars(); let mut chars = params.chars();
let mut ch = chars.next(); let mut ch = chars.next();
while ch.is_some() { while ch.is_some() {
@ -294,14 +286,13 @@ fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, St
let mut show_ascii_dump = false; let mut show_ascii_dump = false;
if is_format_size_char(ch, type_cat, &mut byte_size) { if is_format_size_char(ch, type_cat, &mut byte_size) {
ch = chars.next(); ch = chars.next();
} } else {
else {
let mut decimal_size = String::new(); let mut decimal_size = String::new();
while is_format_size_decimal(ch, type_cat, &mut decimal_size) { while is_format_size_decimal(ch, type_cat, &mut decimal_size) {
ch = chars.next(); ch = chars.next();
} }
if !decimal_size.is_empty() { if !decimal_size.is_empty() {
byte_size=match decimal_size.parse() { byte_size = match decimal_size.parse() {
Err(_) => return Err(format!("invalid number '{}' in format specification '{}'", decimal_size, params)), Err(_) => return Err(format!("invalid number '{}' in format specification '{}'", decimal_size, params)),
Ok(n) => n, Ok(n) => n,
} }

View file

@ -40,7 +40,6 @@ pub enum CommandLineInputs {
/// '-' is used as filename if stdin is meant. This is also returned if /// '-' is used as filename if stdin is meant. This is also returned if
/// there is no input, as stdin is the default input. /// there is no input, as stdin is the default input.
pub fn parse_inputs(matches: &CommandLineOpts) -> Result<CommandLineInputs, String> { pub fn parse_inputs(matches: &CommandLineOpts) -> Result<CommandLineInputs, String> {
let mut input_strings: Vec<String> = matches.inputs(); let mut input_strings: Vec<String> = matches.inputs();
if matches.opts_present(&["traditional"]) { if matches.opts_present(&["traditional"]) {
@ -53,7 +52,7 @@ pub fn parse_inputs(matches: &CommandLineOpts) -> Result<CommandLineInputs, Stri
// if any of the options -A, -j, -N, -t, -v or -w are present there is no offset // if any of the options -A, -j, -N, -t, -v or -w are present there is no offset
if !matches.opts_present(&["A", "j", "N", "t", "v", "w"]) { if !matches.opts_present(&["A", "j", "N", "t", "v", "w"]) {
// test if the last input can be parsed as an offset. // test if the last input can be parsed as an offset.
let offset=parse_offset_operand(&input_strings[input_strings.len()-1]); let offset = parse_offset_operand(&input_strings[input_strings.len()-1]);
match offset { match offset {
Ok(n) => { Ok(n) => {
// if there is just 1 input (stdin), an offset must start with '+' // if there is just 1 input (stdin), an offset must start with '+'
@ -80,22 +79,22 @@ pub fn parse_inputs(matches: &CommandLineOpts) -> Result<CommandLineInputs, Stri
/// interprets inputs when --traditional is on the commandline /// interprets inputs when --traditional is on the commandline
/// ///
/// normally returns CommandLineInputs::FileAndOffset, but if no offset is found, /// normally returns CommandLineInputs::FileAndOffset, but if no offset is found,
/// it returns CommandLineInputs::FileNames (also to differentiate from the offset==0) /// it returns CommandLineInputs::FileNames (also to differentiate from the offset == 0)
pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLineInputs, String> { pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLineInputs, String> {
match input_strings.len() { match input_strings.len() {
0 => { 0 => {
Ok(CommandLineInputs::FileNames(vec!{"-".to_string()})) Ok(CommandLineInputs::FileNames(vec!["-".to_string()]))
} }
1 => { 1 => {
let offset0=parse_offset_operand(&input_strings[0]); let offset0 = parse_offset_operand(&input_strings[0]);
Ok(match offset0 { Ok(match offset0 {
Ok(n) => CommandLineInputs::FileAndOffset(("-".to_string(), n, None)), Ok(n) => CommandLineInputs::FileAndOffset(("-".to_string(), n, None)),
_ => CommandLineInputs::FileNames(input_strings), _ => CommandLineInputs::FileNames(input_strings),
}) })
} }
2 => { 2 => {
let offset0=parse_offset_operand(&input_strings[0]); let offset0 = parse_offset_operand(&input_strings[0]);
let offset1=parse_offset_operand(&input_strings[1]); let offset1 = parse_offset_operand(&input_strings[1]);
match (offset0, offset1) { match (offset0, offset1) {
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset(("-".to_string(), n, Some(m)))), (Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset(("-".to_string(), n, Some(m)))),
(_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((input_strings[0].clone(), m, None))), (_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((input_strings[0].clone(), m, None))),
@ -103,8 +102,8 @@ pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLin
} }
} }
3 => { 3 => {
let offset=parse_offset_operand(&input_strings[1]); let offset = parse_offset_operand(&input_strings[1]);
let label=parse_offset_operand(&input_strings[2]); let label = parse_offset_operand(&input_strings[2]);
match (offset, label) { match (offset, label) {
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((input_strings[0].clone(), n, Some(m)))), (Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((input_strings[0].clone(), n, Some(m)))),
(Err(_), _) => Err(format!("invalid offset: {}", input_strings[1])), (Err(_), _) => Err(format!("invalid offset: {}", input_strings[1])),
@ -131,8 +130,7 @@ pub fn parse_offset_operand(s: &String) -> Result<usize, &'static str> {
if s[start..len].starts_with("0x") || s[start..len].starts_with("0X") { if s[start..len].starts_with("0x") || s[start..len].starts_with("0X") {
start += 2; start += 2;
radix = 16; radix = 16;
} } else {
else {
if s[start..len].ends_with("b") { if s[start..len].ends_with("b") {
len -= 1; len -= 1;
multiply = 512; multiply = 512;
@ -178,7 +176,7 @@ mod tests {
fn opts_present(&self, opts: &[&str]) -> bool { fn opts_present(&self, opts: &[&str]) -> bool {
for expected in opts.iter() { for expected in opts.iter() {
for actual in self.option_names.iter() { for actual in self.option_names.iter() {
if *expected==*actual { if *expected == *actual {
return true; return true;
} }
} }
@ -189,31 +187,30 @@ mod tests {
#[test] #[test]
fn test_parse_inputs_normal() { fn test_parse_inputs_normal() {
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]),
assert_eq!(CommandLineInputs::FileNames(vec!{"-".to_string()}),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{}, vec![],
vec!{})).unwrap()); vec![])).unwrap());
assert_eq!(CommandLineInputs::FileNames(vec!{"-".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"-"}, vec!["-"],
vec!{})).unwrap()); vec![])).unwrap());
assert_eq!(CommandLineInputs::FileNames(vec!{"file1".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1"}, vec!["file1"],
vec!{})).unwrap()); vec![])).unwrap());
assert_eq!(CommandLineInputs::FileNames(vec!{"file1".to_string(), "file2".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "file2".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1", "file2"}, vec!["file1", "file2"],
vec!{})).unwrap()); vec![])).unwrap());
assert_eq!(CommandLineInputs::FileNames(vec!{"-".to_string(), "file1".to_string(), "file2".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string(), "file1".to_string(), "file2".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"-", "file1", "file2"}, vec!["-", "file1", "file2"],
vec!{})).unwrap()); vec![])).unwrap());
} }
#[test] #[test]
@ -221,113 +218,112 @@ mod tests {
// offset is found without filename, so stdin will be used. // offset is found without filename, so stdin will be used.
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)), assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"+10"}, vec!["+10"],
vec!{})).unwrap()); vec![])).unwrap());
// offset must start with "+" if no input is specified. // offset must start with "+" if no input is specified.
assert_eq!(CommandLineInputs::FileNames(vec!{"10".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["10".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"10"}, vec!["10"],
vec!{""})).unwrap()); vec![""])).unwrap());
// offset is not valid, so it is considered a filename. // offset is not valid, so it is considered a filename.
assert_eq!(CommandLineInputs::FileNames(vec!{"+10a".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["+10a".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"+10a"}, vec!["+10a"],
vec!{""})).unwrap()); vec![""])).unwrap());
// if -j is included in the commandline, there cannot be an offset. // if -j is included in the commandline, there cannot be an offset.
assert_eq!(CommandLineInputs::FileNames(vec!{"+10".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"+10"}, vec!["+10"],
vec!{"j"})).unwrap()); vec!["j"])).unwrap());
// if -v is included in the commandline, there cannot be an offset. // if -v is included in the commandline, there cannot be an offset.
assert_eq!(CommandLineInputs::FileNames(vec!{"+10".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"+10"}, vec!["+10"],
vec!{"o", "v"})).unwrap()); vec!["o", "v"])).unwrap());
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)), assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1", "+10"}, vec!["file1", "+10"],
vec!{})).unwrap()); vec![])).unwrap());
// offset does not need to start with "+" if a filename is included. // offset does not need to start with "+" if a filename is included.
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)), assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1", "10"}, vec!["file1", "10"],
vec!{})).unwrap()); vec![])).unwrap());
assert_eq!(CommandLineInputs::FileNames(vec!{"file1".to_string(), "+10a".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "+10a".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1", "+10a"}, vec!["file1", "+10a"],
vec!{""})).unwrap()); vec![""])).unwrap());
assert_eq!(CommandLineInputs::FileNames(vec!{"file1".to_string(), "+10".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "+10".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1", "+10"}, vec!["file1", "+10"],
vec!{"j"})).unwrap()); vec!["j"])).unwrap());
// offset must be last on the commandline // offset must be last on the commandline
assert_eq!(CommandLineInputs::FileNames(vec!{"+10".to_string(), "file1".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string(), "file1".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"+10", "file1"}, vec!["+10", "file1"],
vec!{""})).unwrap()); vec![""])).unwrap());
} }
#[test] #[test]
fn test_parse_inputs_traditional() { fn test_parse_inputs_traditional() {
// it should not return FileAndOffset to signal no offset was entered on the commandline. // it should not return FileAndOffset to signal no offset was entered on the commandline.
assert_eq!(CommandLineInputs::FileNames(vec!{"-".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{}, vec![],
vec!{"traditional"})).unwrap()); vec!["traditional"])).unwrap());
assert_eq!(CommandLineInputs::FileNames(vec!{"file1".to_string()}), assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string()]),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1"}, vec!["file1"],
vec!{"traditional"})).unwrap()); vec!["traditional"])).unwrap());
// offset does not need to start with a + // offset does not need to start with a +
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)), assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"10"}, vec!["10"],
vec!{"traditional"})).unwrap()); vec!["traditional"])).unwrap());
// valid offset and valid label // valid offset and valid label
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, Some(8))), assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, Some(8))),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"10", "10"}, vec!["10", "10"],
vec!{"traditional"})).unwrap()); vec!["traditional"])).unwrap());
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)), assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1", "10"}, vec!["file1", "10"],
vec!{"traditional"})).unwrap()); vec!["traditional"])).unwrap());
// only one file is allowed, it must be the first // only one file is allowed, it must be the first
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"10", "file1"}, vec!["10", "file1"],
vec!{"traditional"})).unwrap_err(); vec!["traditional"])).unwrap_err();
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, Some(8))), assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, Some(8))),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"file1", "10", "10"}, vec!["file1", "10", "10"],
vec!{"traditional"})).unwrap()); vec!["traditional"])).unwrap());
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"10", "file1", "10"}, vec!["10", "file1", "10"],
vec!{"traditional"})).unwrap_err(); vec!["traditional"])).unwrap_err();
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"10", "10", "file1"}, vec!["10", "10", "file1"],
vec!{"traditional"})).unwrap_err(); vec!["traditional"])).unwrap_err();
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!{"10", "10", "10", "10"}, vec!["10", "10", "10", "10"],
vec!{"traditional"})).unwrap_err(); vec!["traditional"])).unwrap_err();
} }
fn parse_offset_operand_str(s: &str) -> Result<usize, &'static str> { fn parse_offset_operand_str(s: &str) -> Result<usize, &'static str> {
@ -363,5 +359,4 @@ mod tests {
assert_eq!(5120, parse_offset_operand_str("+10.b").unwrap()); // b suffix = *512 assert_eq!(5120, parse_offset_operand_str("+10.b").unwrap()); // b suffix = *512
assert_eq!(267, parse_offset_operand_str("0x10b").unwrap()); // hex assert_eq!(267, parse_offset_operand_str("0x10b").unwrap()); // hex
} }
} }

View file

@ -8,8 +8,7 @@ pub fn parse_number_of_bytes(s: &String) -> Result<usize, &'static str> {
if s.starts_with("0x") || s.starts_with("0X") { if s.starts_with("0x") || s.starts_with("0X") {
start = 2; start = 2;
radix = 16; radix = 16;
} } else if s.starts_with("0") {
else if s.starts_with("0") {
radix = 8; radix = 8;
} }
@ -24,40 +23,40 @@ pub fn parse_number_of_bytes(s: &String) -> Result<usize, &'static str> {
len -= 1; len -= 1;
} }
Some('m') | Some('M') => { Some('m') | Some('M') => {
multiply = 1024*1024; multiply = 1024 * 1024;
len -= 1; len -= 1;
} }
Some('G') => { Some('G') => {
multiply = 1024*1024*1024; multiply = 1024 * 1024 * 1024;
len -= 1; len -= 1;
} }
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
Some('T') => { Some('T') => {
multiply = 1024*1024*1024*1024; multiply = 1024 * 1024 * 1024 * 1024;
len -= 1; len -= 1;
} }
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
Some('P') => { Some('P') => {
multiply = 1024*1024*1024*1024*1024; multiply = 1024 * 1024 * 1024 * 1024 * 1024;
len -= 1; len -= 1;
} }
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
Some('E') => { Some('E') => {
multiply = 1024*1024*1024*1024*1024*1024; multiply = 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
len -= 1; len -= 1;
} }
Some('B') if radix != 16 => { Some('B') if radix != 16 => {
len -= 2; len -= 2;
multiply = match ends_with.next() { multiply = match ends_with.next() {
Some('k') | Some('K') => 1000, Some('k') | Some('K') => 1000,
Some('m') | Some('M') => 1000*1000, Some('m') | Some('M') => 1000 * 1000,
Some('G') => 1000*1000*1000, Some('G') => 1000 * 1000 * 1000,
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
Some('T') => 1000*1000*1000*1000, Some('T') => 1000 * 1000 * 1000 * 1000,
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
Some('P') => 1000*1000*1000*1000*1000, Some('P') => 1000 * 1000 * 1000 * 1000 * 1000,
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
Some('E') => 1000*1000*1000*1000*1000*1000, Some('E') => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
_ => return Err("parse failed"), _ => return Err("parse failed"),
} }
}, },
@ -81,11 +80,11 @@ fn test_parse_number_of_bytes() {
assert_eq!(0, parse_number_of_bytes_str("0").unwrap()); assert_eq!(0, parse_number_of_bytes_str("0").unwrap());
assert_eq!(5, parse_number_of_bytes_str("5").unwrap()); assert_eq!(5, parse_number_of_bytes_str("5").unwrap());
assert_eq!(999, parse_number_of_bytes_str("999").unwrap()); assert_eq!(999, parse_number_of_bytes_str("999").unwrap());
assert_eq!(2*512, parse_number_of_bytes_str("2b").unwrap()); assert_eq!(2 * 512, parse_number_of_bytes_str("2b").unwrap());
assert_eq!(2*1024, parse_number_of_bytes_str("2k").unwrap()); assert_eq!(2 * 1024, parse_number_of_bytes_str("2k").unwrap());
assert_eq!(4*1024, parse_number_of_bytes_str("4K").unwrap()); assert_eq!(4 * 1024, parse_number_of_bytes_str("4K").unwrap());
assert_eq!(2*1048576, parse_number_of_bytes_str("2m").unwrap()); assert_eq!(2 * 1048576, parse_number_of_bytes_str("2m").unwrap());
assert_eq!(4*1048576, parse_number_of_bytes_str("4M").unwrap()); assert_eq!(4 * 1048576, parse_number_of_bytes_str("4M").unwrap());
assert_eq!(1073741824, parse_number_of_bytes_str("1G").unwrap()); assert_eq!(1073741824, parse_number_of_bytes_str("1G").unwrap());
assert_eq!(2000, parse_number_of_bytes_str("2kB").unwrap()); assert_eq!(2000, parse_number_of_bytes_str("2kB").unwrap());
assert_eq!(4000, parse_number_of_bytes_str("4KB").unwrap()); assert_eq!(4000, parse_number_of_bytes_str("4KB").unwrap());
@ -95,16 +94,16 @@ fn test_parse_number_of_bytes() {
// octal input // octal input
assert_eq!(8, parse_number_of_bytes_str("010").unwrap()); assert_eq!(8, parse_number_of_bytes_str("010").unwrap());
assert_eq!(8*512, parse_number_of_bytes_str("010b").unwrap()); assert_eq!(8 * 512, parse_number_of_bytes_str("010b").unwrap());
assert_eq!(8*1024, parse_number_of_bytes_str("010k").unwrap()); assert_eq!(8 * 1024, parse_number_of_bytes_str("010k").unwrap());
assert_eq!(8*1048576, parse_number_of_bytes_str("010m").unwrap()); assert_eq!(8 * 1048576, parse_number_of_bytes_str("010m").unwrap());
// hex input // hex input
assert_eq!(15, parse_number_of_bytes_str("0xf").unwrap()); assert_eq!(15, parse_number_of_bytes_str("0xf").unwrap());
assert_eq!(15, parse_number_of_bytes_str("0XF").unwrap()); assert_eq!(15, parse_number_of_bytes_str("0XF").unwrap());
assert_eq!(27, parse_number_of_bytes_str("0x1b").unwrap()); assert_eq!(27, parse_number_of_bytes_str("0x1b").unwrap());
assert_eq!(16*1024, parse_number_of_bytes_str("0x10k").unwrap()); assert_eq!(16 * 1024, parse_number_of_bytes_str("0x10k").unwrap());
assert_eq!(16*1048576, parse_number_of_bytes_str("0x10m").unwrap()); assert_eq!(16 * 1048576, parse_number_of_bytes_str("0x10m").unwrap());
// invalid input // invalid input
parse_number_of_bytes_str("").unwrap_err(); parse_number_of_bytes_str("").unwrap_err();

View file

@ -5,7 +5,7 @@ use multifilereader::HasError;
/// When a large number of bytes must be skipped, it will be read into a /// When a large number of bytes must be skipped, it will be read into a
/// dynamically allocated buffer. The buffer will be limited to this size. /// dynamically allocated buffer. The buffer will be limited to this size.
const MAX_SKIP_BUFFER: usize = 64*1024; const MAX_SKIP_BUFFER: usize = 64 * 1024;
/// Wrapper for `std::io::Read` which can skip bytes at the beginning /// Wrapper for `std::io::Read` which can skip bytes at the beginning
/// of the input, and it can limit the returned bytes to a particular /// of the input, and it can limit the returned bytes to a particular

View file

@ -91,8 +91,7 @@ impl<R: Read> PeekRead for PeekReader<R> {
let unused = out.len() - bytes_in_buffer; let unused = out.len() - bytes_in_buffer;
if peek_size <= unused { if peek_size <= unused {
Ok((bytes_in_buffer, 0)) Ok((bytes_in_buffer, 0))
} } else {
else {
let actual_peek_size = peek_size - unused; let actual_peek_size = peek_size - unused;
let real_size = bytes_in_buffer - actual_peek_size; let real_size = bytes_in_buffer - actual_peek_size;
self.write_to_tempbuffer(&out[real_size..bytes_in_buffer]); self.write_to_tempbuffer(&out[real_size..bytes_in_buffer]);

View file

@ -14,7 +14,7 @@ pub static FORMAT_ITEM_C: FormatterItemInfo = FormatterItemInfo {
}; };
static A_CHRS : [&'static str; 128] = static A_CHRS: [&'static str; 128] =
["nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", ["nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
"bs", "ht", "nl", "vt", "ff", "cr", "so", "si", "bs", "ht", "nl", "vt", "ff", "cr", "so", "si",
"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
@ -40,7 +40,7 @@ fn format_item_a(p: u64) -> String {
} }
static C_CHRS : [&'static str; 128] = [ static C_CHRS: [&'static str; 128] = [
"\\0", "001", "002", "003", "004", "005", "006", "\\a", "\\0", "001", "002", "003", "004", "005", "006", "\\a",
"\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "016", "017", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "016", "017",
"020", "021", "022", "023", "024", "025", "026", "027", "020", "021", "022", "023", "024", "025", "026", "027",
@ -68,33 +68,28 @@ fn format_item_c(bytes: &[u8]) -> String {
Some(s) => format!("{:>4}", s), Some(s) => format!("{:>4}", s),
None => format!("{:>4}", b), None => format!("{:>4}", b),
} }
} } else if (b & 0xc0) == 0x80 {
else if (b & 0xc0) == 0x80 {
// second or subsequent octet of an utf-8 sequence // second or subsequent octet of an utf-8 sequence
String::from(" **") String::from(" **")
} } else if ((b & 0xe0) == 0xc0) && (bytes.len() >= 2) {
else if ((b & 0xe0) == 0xc0) && (bytes.len() >= 2) {
// start of a 2 octet utf-8 sequence // start of a 2 octet utf-8 sequence
match from_utf8(&bytes[0..2]) { match from_utf8(&bytes[0..2]) {
Ok(s) => { format!("{:>4}", s) }, Ok(s) => { format!("{:>4}", s) },
Err(_) => { format!(" {:03o}", b) }, Err(_) => { format!(" {:03o}", b) },
} }
} } else if ((b & 0xf0) == 0xe0) && (bytes.len() >= 3) {
else if ((b & 0xf0) == 0xe0) && (bytes.len() >= 3) {
// start of a 3 octet utf-8 sequence // start of a 3 octet utf-8 sequence
match from_utf8(&bytes[0..3]) { match from_utf8(&bytes[0..3]) {
Ok(s) => { format!("{:>4}", s) }, Ok(s) => { format!("{:>4}", s) },
Err(_) => { format!(" {:03o}", b) }, Err(_) => { format!(" {:03o}", b) },
} }
} } else if ((b & 0xf8) == 0xf0) && (bytes.len() >= 4) {
else if ((b & 0xf8) == 0xf0) && (bytes.len() >= 4) {
// start of a 4 octet utf-8 sequence // start of a 4 octet utf-8 sequence
match from_utf8(&bytes[0..4]) { match from_utf8(&bytes[0..4]) {
Ok(s) => { format!("{:>4}", s) }, Ok(s) => { format!("{:>4}", s) },
Err(_) => { format!(" {:03o}", b) }, Err(_) => { format!(" {:03o}", b) },
} }
} } else {
else {
// invalid utf-8 // invalid utf-8
format!(" {:03o}", b) format!(" {:03o}", b)
} }
@ -107,8 +102,7 @@ pub fn format_ascii_dump(bytes: &[u8]) -> String {
for c in bytes.iter() { for c in bytes.iter() {
if *c >= 0x20 && *c <= 0x7e { if *c >= 0x20 && *c <= 0x7e {
result.push_str(C_CHRS[*c as usize]); result.push_str(C_CHRS[*c as usize]);
} } else {
else {
result.push('.'); result.push('.');
} }
} }

View file

@ -47,8 +47,7 @@ fn format_flo32(f: f32) -> String {
if f.classify() == FpCategory::Subnormal { if f.classify() == FpCategory::Subnormal {
// subnormal numbers will be normal as f64, so will print with a wrong precision // subnormal numbers will be normal as f64, so will print with a wrong precision
format!("{:width$e}", f, width = width) // subnormal numbers format!("{:width$e}", f, width = width) // subnormal numbers
} } else {
else {
format_float(f as f64, width, precision) format_float(f as f64, width, precision)
} }
} }
@ -58,7 +57,6 @@ fn format_flo64(f: f64) -> String {
} }
fn format_float(f: f64, width: usize, precision: usize) -> String { fn format_float(f: f64, width: usize, precision: usize) -> String {
if !f.is_normal() { if !f.is_normal() {
if f == -0.0 && f.is_sign_negative() { return format!("{:>width$}", "-0", width = width) } if f == -0.0 && f.is_sign_negative() { return format!("{:>width$}", "-0", width = width) }
if f == 0.0 || !f.is_finite() { return format!("{:width$}", f, width = width) } if f == 0.0 || !f.is_finite() { return format!("{:width$}", f, width = width) }
@ -77,13 +75,11 @@ fn format_float(f: f64, width: usize, precision: usize) -> String {
format!("{:width$.dec$}", f, format!("{:width$.dec$}", f,
width = width, width = width,
dec = (precision-1) - l as usize) dec = (precision-1) - l as usize)
} } else if l == -1 {
else if l == -1 {
format!("{:width$.dec$}", f, format!("{:width$.dec$}", f,
width = width, width = width,
dec = precision) dec = precision)
} } else {
else {
format!("{:width$.dec$e}", f, format!("{:width$.dec$e}", f,
width = width, width = width,
dec = precision - 1) dec = precision - 1)

View file

@ -89,20 +89,17 @@ fn test_no_file() {
// Test that od reads from stdin instead of a file // Test that od reads from stdin instead of a file
#[test] #[test]
fn test_from_stdin() { fn test_from_stdin() {
let input = "abcdefghijklmnopqrstuvwxyz\n"; let input = "abcdefghijklmnopqrstuvwxyz\n";
let result = new_ucmd!().arg("--endian=little").run_piped_stdin(input.as_bytes()); let result = new_ucmd!().arg("--endian=little").run_piped_stdin(input.as_bytes());
assert_empty_stderr!(result); assert_empty_stderr!(result);
assert!(result.success); assert!(result.success);
assert_eq!(result.stdout, unindent(ALPHA_OUT)); assert_eq!(result.stdout, unindent(ALPHA_OUT));
} }
// Test that od reads from stdin and also from files // Test that od reads from stdin and also from files
#[test] #[test]
fn test_from_mixed() { fn test_from_mixed() {
let temp = env::temp_dir(); let temp = env::temp_dir();
let tmpdir = Path::new(&temp); let tmpdir = Path::new(&temp);
let file1 = tmpdir.join("test-1"); let file1 = tmpdir.join("test-1");
@ -122,12 +119,10 @@ fn test_from_mixed() {
assert_empty_stderr!(result); assert_empty_stderr!(result);
assert!(result.success); assert!(result.success);
assert_eq!(result.stdout, unindent(ALPHA_OUT)); assert_eq!(result.stdout, unindent(ALPHA_OUT));
} }
#[test] #[test]
fn test_multiple_formats() { fn test_multiple_formats() {
let input = "abcdefghijklmnopqrstuvwxyz\n"; let input = "abcdefghijklmnopqrstuvwxyz\n";
let result = new_ucmd!().arg("-c").arg("-b").run_piped_stdin(input.as_bytes()); let result = new_ucmd!().arg("-c").arg("-b").run_piped_stdin(input.as_bytes());
@ -140,12 +135,10 @@ fn test_multiple_formats() {
161 162 163 164 165 166 167 170 171 172 012 161 162 163 164 165 166 167 170 171 172 012
0000033 0000033
")); "));
} }
#[test] #[test]
fn test_dec() { fn test_dec() {
let input = [ let input = [
0u8, 0u8, 0u8, 0u8,
1u8, 0u8, 1u8, 0u8,
@ -163,13 +156,11 @@ fn test_dec() {
assert_empty_stderr!(result); assert_empty_stderr!(result);
assert!(result.success); assert!(result.success);
assert_eq!(result.stdout, expected_output); assert_eq!(result.stdout, expected_output);
} }
#[test] #[test]
fn test_hex16(){ fn test_hex16(){
let input: [u8; 9] = [
let input : [u8; 9] = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff]; 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff];
let expected_output = unindent(" let expected_output = unindent("
0000000 2301 6745 ab89 efcd 00ff 0000000 2301 6745 ab89 efcd 00ff
@ -184,8 +175,7 @@ fn test_hex16(){
#[test] #[test]
fn test_hex32(){ fn test_hex32(){
let input: [u8; 9] = [
let input : [u8; 9] = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff]; 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff];
let expected_output = unindent(" let expected_output = unindent("
0000000 67452301 efcdab89 000000ff 0000000 67452301 efcdab89 000000ff
@ -200,8 +190,7 @@ fn test_hex32(){
#[test] #[test]
fn test_f16(){ fn test_f16(){
let input: [u8; 14] = [
let input : [u8; 14] = [
0x00, 0x3c, // 0x3C00 1.0 0x00, 0x3c, // 0x3C00 1.0
0x00, 0x00, // 0x0000 0.0 0x00, 0x00, // 0x0000 0.0
0x00, 0x80, // 0x8000 -0.0 0x00, 0x80, // 0x8000 -0.0
@ -223,8 +212,7 @@ fn test_f16(){
#[test] #[test]
fn test_f32(){ fn test_f32(){
let input: [u8; 28] = [
let input : [u8; 28] = [
0x52, 0x06, 0x9e, 0xbf, // 0xbf9e0652 -1.2345679 0x52, 0x06, 0x9e, 0xbf, // 0xbf9e0652 -1.2345679
0x4e, 0x61, 0x3c, 0x4b, // 0x4b3c614e 12345678 0x4e, 0x61, 0x3c, 0x4b, // 0x4b3c614e 12345678
0x0f, 0x9b, 0x94, 0xfe, // 0xfe949b0f -9.876543E37 0x0f, 0x9b, 0x94, 0xfe, // 0xfe949b0f -9.876543E37
@ -246,8 +234,7 @@ fn test_f32(){
#[test] #[test]
fn test_f64(){ fn test_f64(){
let input: [u8; 40] = [
let input : [u8; 40] = [
0x27, 0x6b, 0x0a, 0x2f, 0x2a, 0xee, 0x45, 0x43, // 0x4345EE2A2F0A6B27 12345678912345678 0x27, 0x6b, 0x0a, 0x2f, 0x2a, 0xee, 0x45, 0x43, // 0x4345EE2A2F0A6B27 12345678912345678
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000000 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000000 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, // 0x8010000000000000 -2.2250738585072014e-308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, // 0x8010000000000000 -2.2250738585072014e-308
@ -268,7 +255,6 @@ fn test_f64(){
#[test] #[test]
fn test_multibyte() { fn test_multibyte() {
let result = new_ucmd!().arg("-c").arg("-w12").run_piped_stdin("Universität Tübingen \u{1B000}".as_bytes()); let result = new_ucmd!().arg("-c").arg("-w12").run_piped_stdin("Universität Tübingen \u{1B000}".as_bytes());
assert_empty_stderr!(result); assert_empty_stderr!(result);
@ -283,8 +269,7 @@ fn test_multibyte() {
#[test] #[test]
fn test_width(){ fn test_width(){
let input: [u8; 8] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let input : [u8; 8] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let expected_output = unindent(" let expected_output = unindent("
0000000 000000 000000 0000000 000000 000000
0000004 000000 000000 0000004 000000 000000
@ -300,8 +285,7 @@ fn test_width(){
#[test] #[test]
fn test_invalid_width(){ fn test_invalid_width(){
let input: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
let input : [u8; 4] = [0x00, 0x00, 0x00, 0x00];
let expected_output = unindent(" let expected_output = unindent("
0000000 000000 0000000 000000
0000002 000000 0000002 000000
@ -317,8 +301,7 @@ fn test_invalid_width(){
#[test] #[test]
fn test_zero_width(){ fn test_zero_width(){
let input: [u8; 4] = [0x00, 0x00, 0x00, 0x00];
let input : [u8; 4] = [0x00, 0x00, 0x00, 0x00];
let expected_output = unindent(" let expected_output = unindent("
0000000 000000 0000000 000000
0000002 000000 0000002 000000
@ -334,8 +317,7 @@ fn test_zero_width(){
#[test] #[test]
fn test_width_without_value(){ fn test_width_without_value(){
let input: [u8; 40] = [0 ; 40];
let input : [u8; 40] = [0 ; 40];
let expected_output = unindent(" let expected_output = unindent("
0000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 0000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000 000000
0000040 000000 000000 000000 000000 0000040 000000 000000 000000 000000
@ -351,7 +333,6 @@ fn test_width_without_value(){
#[test] #[test]
fn test_suppress_duplicates(){ fn test_suppress_duplicates(){
let input: [u8; 41] = [ let input: [u8; 41] = [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@ -387,8 +368,7 @@ fn test_suppress_duplicates(){
#[test] #[test]
fn test_big_endian() { fn test_big_endian() {
let input: [u8; 8] = [
let input : [u8; 8] = [
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];// 0xc000000000000000 -2 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];// 0xc000000000000000 -2
let expected_output = unindent(" let expected_output = unindent("
@ -409,8 +389,7 @@ fn test_big_endian() {
#[test] #[test]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn test_alignment_Xxa() { fn test_alignment_Xxa() {
let input: [u8; 8] = [
let input : [u8; 8] = [
0x0A, 0x0D, 0x65, 0x66, 0x67, 0x00, 0x9e, 0x9f]; 0x0A, 0x0D, 0x65, 0x66, 0x67, 0x00, 0x9e, 0x9f];
let expected_output = unindent(" let expected_output = unindent("
@ -431,8 +410,7 @@ fn test_alignment_Xxa() {
#[test] #[test]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn test_alignment_Fx() { fn test_alignment_Fx() {
let input: [u8; 8] = [
let input : [u8; 8] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0];// 0xc000000000000000 -2 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0];// 0xc000000000000000 -2
let expected_output = unindent(" let expected_output = unindent("
@ -451,7 +429,6 @@ fn test_alignment_Fx() {
#[test] #[test]
fn test_maxuint(){ fn test_maxuint(){
let input = [0xFFu8 ; 8]; let input = [0xFFu8 ; 8];
let expected_output = unindent(" let expected_output = unindent("
0000000 1777777777777777777777 0000000 1777777777777777777777
@ -474,7 +451,6 @@ fn test_maxuint(){
#[test] #[test]
fn test_hex_offset(){ fn test_hex_offset(){
let input = [0u8 ; 0x1F]; let input = [0u8 ; 0x1F];
let expected_output = unindent(" let expected_output = unindent("
000000 00000000 00000000 00000000 00000000 000000 00000000 00000000 00000000 00000000
@ -493,7 +469,6 @@ fn test_hex_offset(){
#[test] #[test]
fn test_dec_offset(){ fn test_dec_offset(){
let input = [0u8 ; 19]; let input = [0u8 ; 19];
let expected_output = unindent(" let expected_output = unindent("
0000000 00000000 00000000 00000000 00000000 0000000 00000000 00000000 00000000 00000000
@ -512,7 +487,6 @@ fn test_dec_offset(){
#[test] #[test]
fn test_no_offset(){ fn test_no_offset(){
let input = [0u8 ; 31]; let input = [0u8 ; 31];
const LINE: &'static str = " 00000000 00000000 00000000 00000000\n"; const LINE: &'static str = " 00000000 00000000 00000000 00000000\n";
let expected_output = [LINE, LINE, LINE, LINE].join(""); let expected_output = [LINE, LINE, LINE, LINE].join("");
@ -526,17 +500,13 @@ fn test_no_offset(){
#[test] #[test]
fn test_invalid_offset(){ fn test_invalid_offset(){
let result = new_ucmd!().arg("-Ab").run();
let input = [0u8 ; 4];
let result = new_ucmd!().arg("-Ab").run_piped_stdin(&input[..]);
assert!(!result.success); assert!(!result.success);
} }
#[test] #[test]
fn test_skip_bytes(){ fn test_skip_bytes(){
let input = "abcdefghijklmnopq"; let input = "abcdefghijklmnopq";
let result = new_ucmd!().arg("-c").arg("--skip-bytes=5").run_piped_stdin(input.as_bytes()); let result = new_ucmd!().arg("-c").arg("--skip-bytes=5").run_piped_stdin(input.as_bytes());
@ -550,7 +520,6 @@ fn test_skip_bytes(){
#[test] #[test]
fn test_skip_bytes_error(){ fn test_skip_bytes_error(){
let input = "12345"; let input = "12345";
let result = new_ucmd!().arg("--skip-bytes=10").run_piped_stdin(input.as_bytes()); let result = new_ucmd!().arg("--skip-bytes=10").run_piped_stdin(input.as_bytes());
@ -559,7 +528,6 @@ fn test_skip_bytes_error(){
#[test] #[test]
fn test_read_bytes(){ fn test_read_bytes(){
let input = "abcdefghijklmnopqrstuvwxyz\n12345678"; let input = "abcdefghijklmnopqrstuvwxyz\n12345678";
let result = new_ucmd!().arg("--endian=little").arg("--read-bytes=27").run_piped_stdin(input.as_bytes()); let result = new_ucmd!().arg("--endian=little").arg("--read-bytes=27").run_piped_stdin(input.as_bytes());
@ -570,8 +538,7 @@ fn test_read_bytes(){
#[test] #[test]
fn test_ascii_dump(){ fn test_ascii_dump(){
let input: [u8; 22] = [
let input : [u8; 22] = [
0x00, 0x01, 0x0a, 0x0d, 0x10, 0x1f, 0x20, 0x61, 0x62, 0x63, 0x7d, 0x00, 0x01, 0x0a, 0x0d, 0x10, 0x1f, 0x20, 0x61, 0x62, 0x63, 0x7d,
0x7e, 0x7f, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff]; 0x7e, 0x7f, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff];
let result = new_ucmd!().arg("-tx1zacz").run_piped_stdin(&input[..]); let result = new_ucmd!().arg("-tx1zacz").run_piped_stdin(&input[..]);
@ -607,7 +574,6 @@ fn test_filename_parsing(){
#[test] #[test]
fn test_stdin_offset(){ fn test_stdin_offset(){
let input = "abcdefghijklmnopq"; let input = "abcdefghijklmnopq";
let result = new_ucmd!().arg("-c").arg("+5").run_piped_stdin(input.as_bytes()); let result = new_ucmd!().arg("-c").arg("+5").run_piped_stdin(input.as_bytes());
@ -621,7 +587,6 @@ fn test_stdin_offset(){
#[test] #[test]
fn test_file_offset(){ fn test_file_offset(){
let result = new_ucmd!().arg("-c").arg("--").arg("-f").arg("10").run(); let result = new_ucmd!().arg("-c").arg("--").arg("-f").arg("10").run();
assert_empty_stderr!(result); assert_empty_stderr!(result);
@ -678,8 +643,7 @@ fn test_traditional_with_skip_bytes_non_override(){
#[test] #[test]
fn test_traditional_error(){ fn test_traditional_error(){
// file "0" exists - don't fail on that, but --traditional only accepts a single input // file "0" exists - don't fail on that, but --traditional only accepts a single input
let input = "abcdefghijklmnopq"; let result = new_ucmd!().arg("--traditional").arg("0").arg("0").arg("0").arg("0").run();
let result = new_ucmd!().arg("--traditional").arg("0").arg("0").arg("0").arg("0").run_piped_stdin(input.as_bytes());
assert!(!result.success); assert!(!result.success);
} }