use std::io; use std::io::{Seek, SeekFrom}; use std::fs; use std::fmt::Write; use std::io::Read; use std::env; use std::path::Path; extern crate termios; use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr}; extern crate terminal_size; use terminal_size::{Width, Height, terminal_size}; fn main() { println!("hello"); if let Some((Width(w), Height(h))) = terminal_size() { if let Some(arg1) = env::args().nth(1) { println!("You would like to work with {}.", arg1); launch_interface(w, h, &arg1); } else { usage(); } } else { println!("Wasn't able to get terminal dimensions for some reason"); } } fn usage() { println!("usage: this_thing filename"); } fn launch_interface(w: u16, h: u16, filename: &String) { println!("Launching interface with width {} and height {}...", w, h); // 4 = columns per group, 3 = xx xx xx xx , ... // + (x + 7) / 8 is for ascii at the end of line, rounded up to 8 characters // width = 8-20 + (3 * 4) * x + (x + 7) / 8 // 8 * width = 87 + 57x // x = (8 * width - 87) / 57 let hex_cols = (w * 8 - 87) / 57; println!("0x________: {} {}", hex_cols, hex_cols); match fs::metadata(&filename) { Ok(fs_meta) => { println!("{} is {} bytes, and is it readable? {}", filename, fs_meta.len(), true); let termios = Termios::from_fd(0).unwrap(); let mut new_termios = termios.clone(); // fix terminal to not echo, thanks new_termios.c_lflag &= !(ICANON | ECHO); tcsetattr(0, TCSANOW, &mut new_termios).unwrap(); interface_loop(w, h, filename); tcsetattr(0, TCSANOW, &termios).unwrap(); }, Err(e) => println!("{} does not exist.", filename), } } fn interface_loop(w: u16, h: u16, filename: &String) { let mut cursor: u64 = 0; let mut buffer = [0; 0x200]; populate_buf(filename, cursor, &mut buffer); render_interface(w, h, cursor, &buffer); let stdin = io::stdin(); for byte in stdin.lock().bytes() { match byte { Ok(value) => { render_interface(w, h, cursor, &buffer); }, Err(e) => { println!("Err: {}", e); } } } } fn ascii_or_dot(c: u8) -> u8 { let u = c as char; if (u >= 'a' && u <= 'z' || u >= '0' && u <= '9' || u >= 'A' && u <= 'Z' || u == '~' || u == '!' || u == '@' || u == '#' || u == '$' || u == '%' || u == '^' || u == '&' || u == '*' || u == '(' || u == ')' || u == '_' || u == '+' || u == '{' || u == '}' || u == '|' || u == ':' || u == '"' || u == '>' || u == '?' || u == '-' || u == '=' || u == '[' || u == ']' || u == '\\' || u == ';' || u == '\'' || u == '.' || u == '/' || u == '<' || u == ',' || u == '`') { c } else { '.' as u8 } } fn render_interface(w: u16, h: u16, cursor: u64, buffer: &[u8]) { println!("Dimensions: {}x{} Cursor: {} cursor_pos: ({}, {})", w, h, cursor, 0, 0); let addr_width = 8; // w = 10 + 1 + 1 + 4 * 3 * x + x + 1 + 4 * x // w = 13 + 17 * x // w - 13 / 17 = x let cols = (w - 13) / 17; let col_width = 4; // let ascii = bytes_width * cols; let mut idx: usize = 0; for i in 1..(h-1) { let mut hex_line = String::new(); let mut ascii_line = String::new(); for col in 0..cols { for c in 0..col_width { if (idx < buffer.len()) { let chr = buffer[idx]; write!(&mut hex_line, "{:02x} ", chr).unwrap(); write!(&mut ascii_line, "{}", ascii_or_dot(chr) as char).unwrap(); } else { write!(&mut hex_line, " ").unwrap(); write!(&mut ascii_line, " ").unwrap(); } idx += 1; } write!(&mut hex_line, " ").unwrap(); } let lineaddr = ((i as u64) - 1) * (cols as u64 * 4); let addr = if (lineaddr as usize) < buffer.len() { format!("0x{:08x}:", lineaddr) } else { " ".to_string() }; println!("{} {} {}", addr, hex_line, ascii_line); } } fn populate_buf(arg: &String, cursor: u64, buffer: &mut [u8]) { match fs::File::open(&arg) { Ok(mut fd) => { /*..*/ fd.seek(SeekFrom::Start(cursor)); fd.read(buffer); for b in buffer.into_iter() { print!("{:02x}", b); } println!(""); }, Err(_) => println!("Failed to open {}", arg) } }