evenmoreutils/src/bin/bytediff.rs

137 lines
3.4 KiB
Rust

use std::fs::File;
use std::io::{prelude::*, BufReader};
use anyhow::{anyhow, Context, Result};
struct ZipLonger<A: Iterator, B: Iterator> {
a: A,
b: B,
}
impl<A: Iterator, B: Iterator> Iterator for ZipLonger<A, B> {
type Item = (Option<A::Item>, Option<B::Item>);
fn next(&mut self) -> Option<Self::Item> {
let a_next = self.a.next();
let b_next = self.b.next();
if a_next.is_none() && b_next.is_none() {
return None;
}
Some((a_next, b_next))
}
}
impl<A: Iterator, B: Iterator> ZipLonger<A, B> {
fn new(a: A, b: B) -> Self {
Self { a, b }
}
}
struct Chunks<I: Iterator> {
n: usize,
inner: I,
}
impl<T, F, I: Iterator<Item = Result<T, F>>> Iterator for Chunks<I> {
type Item = Result<Vec<T>, F>;
fn next(&mut self) -> Option<Self::Item> {
let mut values = Vec::new();
for _ in 0..(self.n) {
let next = self.inner.next();
match next {
Some(Ok(v)) => {
values.push(v);
}
Some(Err(e)) => {
return Some(Err(e));
}
None => {
break;
}
}
}
if values.is_empty() {
return None;
}
Some(Ok(values))
}
}
impl<I: Iterator> Chunks<I> {
fn new(inner: I, n: usize) -> Self {
Self { inner, n }
}
}
fn color_byte_display(this: &[u8], that: &[u8], color: u8) -> String {
let mut result = String::new();
for (i, b) in this.iter().enumerate() {
if i % 2 == 0 && i > 0 {
result.push(' ');
}
if that.get(i) == Some(b) {
result.push_str(&format!("{:02x}", b));
} else {
result.push_str(&format!("\x1b[1;{}m{:02x}\x1b[0m", color, b));
}
}
result
}
fn main() -> Result<()> {
let mut args = std::env::args();
let name = args
.next()
.ok_or_else(|| anyhow!("Missing executable name in args"))?;
let (a, b) = match (args.next(), args.next()) {
(Some(a), Some(b)) => (a, b),
_ => return Err(anyhow!("Usage: {} file file", name)),
};
let a_bytes = BufReader::new(
File::open(a.clone()).with_context(|| format!("Failed to open input file {}", a))?,
)
.bytes();
let b_bytes = BufReader::new(
File::open(b.clone()).with_context(|| format!("Failed to open input file {}", b))?,
)
.bytes();
let mut in_differing_region = false;
for (i, (rva, rvb)) in
ZipLonger::new(Chunks::new(a_bytes, 16), Chunks::new(b_bytes, 16)).enumerate()
{
let va: Vec<u8> = rva
.transpose()
.with_context(|| format!("Error reading from input file {}", a))?
.unwrap_or_default();
let vb: Vec<u8> = rvb
.transpose()
.with_context(|| format!("Error reading from input file {}", b))?
.unwrap_or_default();
if va == vb {
if in_differing_region {
in_differing_region = false;
println!("---");
}
continue;
}
in_differing_region = true;
println!(
"{0:08x}: {1}\n{0:08x}: {2}",
i * 16,
color_byte_display(&va, &vb, 31),
color_byte_display(&vb, &va, 32),
);
}
Ok(())
}