diff options
author | sitkevij <sitkevij@gmail.com> | 2018-03-22 15:30:56 -0700 |
---|---|---|
committer | sitkevij <sitkevij@gmail.com> | 2018-03-22 15:30:56 -0700 |
commit | b3b80d8f2904ee889b97a520f6e7dd49a8fe6fb1 (patch) | |
tree | 46f86c24b03ff52a25ceb3baf0adc19a818c60cf /src | |
parent | c8113431c54999216fdfbb6d48281a4ea45aded9 (diff) |
commit core lib and tests
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 365 | ||||
-rw-r--r-- | src/main.rs | 31 |
2 files changed, 387 insertions, 9 deletions
@@ -1,20 +1,371 @@ - +// Attribute to hide warnings for unused code. +#![allow(dead_code)] extern crate clap; use clap::ArgMatches; use std::fs; use std::fs::File; -use std::io::{Cursor, Read}; -use std::vec::Vec; +use std::io::{Read}; +use std::io::BufReader; + +/// evaulate for traits implementation +/// https://stackoverflow.com/questions/27650312/show-u8-slice-in-hex-representation +#[derive(Copy, Clone)] +pub enum Format { + Octal, + LowerHex, + UpperHex, + Pointer, + Binary, + LowerExp, + UpperExp, +} + +/// Line structure for hex output +#[derive(Clone, Debug)] +pub struct Line { + pub offset: u64, + pub hex_body: Vec<u8>, + pub ascii: Vec<char>, + pub bytes: u64 +} +/// Line implementation +impl Line { + pub fn new() -> Line { + Line { + offset: 0x0, + hex_body: Vec::new(), + ascii: Vec::new(), + bytes: 0x0 + } + } +} + +/// Page structure +#[derive(Clone)] +pub struct Page { + pub offset: u64, + pub body: Vec<Line>, + pub bytes: u64 +} + +/// Page implementation +impl Page { + pub fn new() -> Page { + Page { + offset: 0x0, + body: Vec::new(), + bytes: 0x0 + } + } +} + +/// nothing ⇒ Display +/// ? ⇒ Debug +/// o ⇒ Octal +/// x ⇒ LowerHex +/// X ⇒ UpperHex +/// p ⇒ Pointer +/// b ⇒ Binary +/// e ⇒ LowerExp +/// E ⇒ UpperExp +fn read1() { + let f = File::open("src/main.rs").unwrap(); + let f = BufReader::new(f); + let mut column_count: i32 = 0x0; + let column_width: i32 = 10; + // let format_lower_hex: &'static str = "{:#x} "; + // let FORMAT_OCTAL = "{:#o} "; + for b in f.bytes() { + //if b.ok() == Some(b'a') { + // count += 1 + //} + //format!("{:#o}", b.unwrap()); + print!("{:#b} ", b.unwrap()); + column_count += 1; + if column_count > column_width { + println!("{}", ""); + column_count = 0; + } + } + println!("{}", ""); +} + +// macro_rules! cell_format { ( $e:expr ) => ( println!("{:<10}", $e) ); } +macro_rules! cell_format { + () => ("{:<10}") +} // Pads with spaces on right to fill up 10 characters + +//macro_rules! offset_format { +// () => ("{:#08x}:") +//} + +/// offset column +pub fn offset(b: u64) -> String { + format!("{:#08x}", b) +} + +/// print offset to std out +pub fn print_offset(b: u64) { + print!("{}: ", offset(b)); +} + +/// hex octal, takes u8 +pub fn hex_octal(b: u8) -> String { + format!("{:#06o}", b) +} + +/// hex lower hex, takes u8 +pub fn hex_lower_hex(b: u8) -> String { + format!("{:#04x}", b) +} + +/// hex upper hex, takes u8 +pub fn hex_upper_hex(b: u8) -> String { + format!("{:#04X}", b) +} + +/// hex binary, takes u8 +pub fn hex_binary(b: u8) -> String { + format!("{:#010b}", b) +} + +/// print byte to std out +pub fn print_byte(b: u8, format: Format) { + match format { + Format::Octal => print!("{} ", hex_octal(b)), + Format::LowerHex => print!("{} ", hex_lower_hex(b)), + Format::UpperHex => print!("{} ", hex_upper_hex(b)), + Format::Binary => print!("{} ", hex_binary(b)), + _ => print!("{}", "unk_fmt "), + } +} + +/// leading zeros +/// let flags = 0b0000000000101100u16; +/// println!("flags: {:#018b}", flags); +pub fn print_byte1(b: u8, format: Format) { + // println!(cell_format!(), b); + match format { + Format::Octal => print!("{:#o} ", b), + Format::LowerHex => print!("{:#x} ", b), + Format::UpperHex => print!("{:#X} ", b), + // Format::Pointer => print!("{:#p} ", b), + Format::Binary => print!("{:#b} ", b), + // Format::Binary => print!("{:#08b} ", b), + // Format::LowerExp => format!("{:e}", b), + // Format::UpperExp => print!("{:#E} ", b), + _ => println!("{}", "wtf"), + } +} -pub fn run(matches: ArgMatches) -> Result<(), Box<::std::error::Error>> { +/// In most hex editor applications, the data of the computer file is +/// represented as hexadecimal values grouped in 4 groups of 4 bytes +/// (or two groups of 8 bytes), followed by one group of 16 printable ASCII +/// characters which correspond to each pair of hex values (each byte). +/// Non-printable ASCII characters (e.g., Bell) and characters that would take +/// more than one character space (e.g., tab) are typically represented by a +/// dot (".") in the following ASCII field. +/// +/// # Arguments +/// +/// * `matches` - Argument matches from command line. +pub fn run(matches: ArgMatches) -> Result<(), Box<::std::error::Error>> { + let mut column_count: u64 = 0x0; + let mut column_width: u64 = 10; + + if let Some(file) = matches.value_of("INPUTFILE") { + let f = File::open(file).unwrap(); + let buf_len = fs::metadata(file)?.len(); + let mut buf = BufReader::new(f); + let mut format_out = Format::LowerHex; + + if let Some(columns) = matches.value_of("cols") { + column_width = columns.parse::<u64>().unwrap(); //turbofish + } + + if let Some(format) = matches.value_of("format") { + // o, x, X, p, b, e, E + match format { + "o" => format_out = Format::Octal, + "x" => format_out = Format::LowerHex, + "X" => format_out = Format::UpperHex, + "p" => format_out = Format::Pointer, + "b" => format_out = Format::Binary, + "e" => format_out = Format::LowerExp, + "E" => format_out = Format::UpperExp, + _ => print!("{}", "unk"), // Err("Unknown Format") + } + } + + match matches.occurrences_of("v") { + 0 => print!(""), + 1 => println!("Some verbose info"), + 2 => println!("Tons of verbose info"), + 3 | _ => println!("Don't be crazy"), + } + // array output mode is mutually exclusive + if let Some(array) = matches.value_of("array") { + let mut array_format = array; + let mut page = buf_to_array(&mut buf, buf_len, column_width).unwrap(); + match array_format { + "r" => println!("let ARRAY: [u8; {}] = [", page.bytes), + "c" => println!("unsigned char ARRAY[{}] = {{", page.bytes), + "g" => println!("a := [{}]byte{{", page.bytes), + _ => println!("unknown array format"), + } + let mut i:u64 = 0x0; + for line in page.body.iter() { + print!(" "); + for hex in line.hex_body.iter() { + i += 1; + if i == buf_len && array_format != "g" { + print!("{}", hex_lower_hex(*hex)); + } else { + print!("{}, ", hex_lower_hex(*hex)); + } + } + println!(""); + } + match array_format { + "r" => println!("{}", "];"), + "c" => println!("{}", "};"), + "g" => println!("{}", "}"), + _ => println!("unknown array format"), + } + } else { + // or, to be safe, match the `Err` + // match "foobar".parse::<i32>() { + // Ok(n) => do_something_with(n), + // Err(e) => weep_and_moan(), + // } + + // Transforms this Read instance to an Iterator over its bytes. + // The returned type implements Iterator where the Item is + // Result<u8, R::Err>. The yielded item is Ok if a byte was + // successfully read and Err otherwise for I/O errors. EOF is mapped + // to returning None from this iterator. + // (https://doc.rust-lang.org/1.16.0/std/io/trait.Read.html#method.bytes) + let mut line: Line = Line::new(); + let mut offset_counter: u64 = 0x0; + let mut total_bytes: u64 = 0x0; + line.offset = offset_counter; + print_offset(offset_counter); + for b in buf.bytes() { + let b1: u8 = b.unwrap(); + line.bytes += 1; + total_bytes += 1; + line.hex_body.push( b1 ); + + if b1 > 31 && b1 < 127 { + line.ascii.push(b1 as char); + } else { + line.ascii.push('.'); + } + + if column_count > column_width-1 { + let s: String = line.ascii.iter().cloned().collect(); + line = Line::new(); + print!("{}", s); + println!("{}", ""); + offset_counter += column_count as u64; + print_offset(offset_counter); + column_count = 0; + } + print_byte(b1, format_out); + column_count += 1; + } + println!("{}", ""); + if true { + println!("bytes: {}", total_bytes); + println!(" max: {}", <u64>::max_value()) + } + } + } Ok(()) } +/// Buffer to array. +/// +/// (https://rustbyexample.com/primitives/array.html) +/// (https://stackoverflow.com/questions/39464237/whats-the-idiomatic-way-reference-bufreader-bufwriter-when-passing-between-funct) +/// (https://stackoverflow.com/questions/39935158/bufreader-move-after-for-loop-with-bufreader-lines) +/// +/// # Arguments +/// +/// * `buf` - Buffer to be read. +/// * `buf_len` - Buffer length. +/// * `column_width` - column width for output. +pub fn buf_to_array(buf: &mut Read, buf_len: u64, column_width: u64) -> Result<Page, Box<::std::error::Error>> { + let mut column_count: u64 = 0x0; + let max_array_size: u16 = <u16>::max_value(); // 2^16; + let mut page: Page = Page::new(); + let mut line: Line = Line::new(); + for b in buf.bytes() { + let b1: u8 = b.unwrap(); + line.bytes += 1; + page.bytes += 1; + line.hex_body.push( b1 ); + column_count += 1; + // println!("page.bytes = {}", page.bytes); + // println!("column_count = {}", column_count); + // println!("column_width = {}", column_width); + // println!("buf_len = {}", buf_len); + // println!("buf_len - column_width = {}", buf_len - column_width); + // println!(""); + if column_count >= column_width { + page.body.push(line); + line = Line::new(); + column_count = 0; + } + if page.bytes == buf_len || max_array_size as u64 == buf_len { + page.body.push(line); + break; + } + } + Ok(page) +} + #[cfg(test)] mod tests { + use super::*; + /// @see (https://users.rust-lang.org/t/how-to-test-output-to-stdout/4877/6) #[test] - fn it_works() { - assert_eq!(2 + 2, 4); + fn test_offset() { + let b: u64 = 0x6; + assert_eq!( offset(b), "0x000006" ); + assert_eq!( offset(b), format!("{:#08x}", b) ); } -} + + /// hex octal, takes u8 + #[test] + pub fn test_hex_octal() { + let b: u8 = 0x6; + assert_eq!( hex_octal(b), "0o0006"); + assert_eq!( hex_octal(b), format!("{:#06o}", b)); + } + + /// hex lower hex, takes u8 + #[test] + fn test_hex_lower_hex() { + let b: u8 = <u8>::max_value(); // 255 + assert_eq!( hex_lower_hex(b), "0xff"); + assert_eq!( hex_lower_hex(b), format!("{:#04x}", b)); + } + + /// hex upper hex, takes u8 + #[test] + fn test_hex_upper_hex() { + let b: u8 = <u8>::max_value(); + assert_eq!( hex_upper_hex(b), "0xFF"); + assert_eq!( hex_upper_hex(b), format!("{:#04X}", b)); + } + + /// hex binary, takes u8 + #[test] + fn test_hex_binary() { + let b: u8 = <u8>::max_value(); + assert_eq!( hex_binary(b), "0b11111111"); + assert_eq!( hex_binary(b), format!("{:#010b}", b)); + } +}
\ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 20aa264..d4d025a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,10 +9,37 @@ fn main() { .version(env!("CARGO_PKG_VERSION")) .about(env!("CARGO_PKG_DESCRIPTION")) // CARGO_PKG_HOMEPAGE .author(env!("CARGO_PKG_AUTHORS")) - .arg(Arg::with_name("FILE") - .help("Pass file path as an argument for inspection") + .arg(Arg::with_name("cols") + .short("c") + .long("cols") + .value_name("columns") + .help("Sets the column length") + .takes_value(true)) + .arg(Arg::with_name("len") + .short("l") + .long("len") + .value_name("len") + .help("Stop after <len> bytes") + .takes_value(true)) + .arg(Arg::with_name("format") + .short("f") + .long("format") + .help("Format of octet, possible values: o, x, X, p, b, e, E") + .takes_value(true)) + .arg(Arg::with_name("INPUTFILE") + .help("Pass file path as an argument for hex dump") .required(true) .index(1)) + .arg(Arg::with_name("v") + .short("v") + .multiple(true) + .help("Sets verbosity level")) + .arg(Arg::with_name("array") + .short("a") + .long("array") + .value_name("array_format") + .help("Output array in source code format: -a<value> accepts values `r` for rust, `c` for C, `g` for golang.") + .takes_value(true)) .get_matches(); match lib::run(matches) { |