add more compact output to bytediff
This commit is contained in:
parent
a01fdc7de7
commit
9e62430e2d
|
@ -1,7 +1,53 @@
|
|||
use std::fs::File;
|
||||
use std::io::{prelude::*, BufReader};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version)]
|
||||
/// diff binary files
|
||||
///
|
||||
/// bytediff checks whether binary files match, and prints out
|
||||
/// a list of differences at the byte level if they don't.
|
||||
struct Cli {
|
||||
/// Print out a full hexdump of differing bytes, not just a summary of differences
|
||||
#[arg(short, long)]
|
||||
detailed: bool,
|
||||
|
||||
/// First file to diff
|
||||
#[arg(required = true)]
|
||||
file1: PathBuf,
|
||||
|
||||
/// Second file to diff
|
||||
#[arg(required = true)]
|
||||
file2: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum DiffState {
|
||||
Same,
|
||||
DifferNonZero,
|
||||
DifferAZero,
|
||||
DifferBZero,
|
||||
}
|
||||
|
||||
fn is_byte_vec_zero(v: &[u8]) -> bool {
|
||||
v.iter().all(|c| *c == 0u8)
|
||||
}
|
||||
|
||||
fn compare_byte_vecs(a: &[u8], b: &[u8]) -> DiffState {
|
||||
if a == b {
|
||||
DiffState::Same
|
||||
} else if is_byte_vec_zero(a) {
|
||||
DiffState::DifferAZero
|
||||
} else if is_byte_vec_zero(b) {
|
||||
DiffState::DifferBZero
|
||||
} else {
|
||||
DiffState::DifferNonZero
|
||||
}
|
||||
}
|
||||
|
||||
struct ZipLonger<A: Iterator, B: Iterator> {
|
||||
a: A,
|
||||
|
@ -83,53 +129,89 @@ fn color_byte_display(this: &[u8], that: &[u8], color: u8) -> String {
|
|||
}
|
||||
|
||||
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 cli = Cli::parse();
|
||||
|
||||
let a = cli.file1;
|
||||
let b = cli.file2;
|
||||
|
||||
let a_bytes = BufReader::new(
|
||||
File::open(a.clone()).with_context(|| format!("Failed to open input file {}", a))?,
|
||||
File::open(a.clone())
|
||||
.with_context(|| format!("Failed to open input file {}", a.display()))?,
|
||||
)
|
||||
.bytes();
|
||||
let b_bytes = BufReader::new(
|
||||
File::open(b.clone()).with_context(|| format!("Failed to open input file {}", b))?,
|
||||
File::open(b.clone())
|
||||
.with_context(|| format!("Failed to open input file {}", b.display()))?,
|
||||
)
|
||||
.bytes();
|
||||
|
||||
let mut in_differing_region = false;
|
||||
let mut diff_state = DiffState::Same;
|
||||
let mut region_start: usize = 0;
|
||||
|
||||
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))?
|
||||
.with_context(|| format!("Error reading from input file {}", a.display()))?
|
||||
.unwrap_or_default();
|
||||
let vb: Vec<u8> = rvb
|
||||
.transpose()
|
||||
.with_context(|| format!("Error reading from input file {}", b))?
|
||||
.with_context(|| format!("Error reading from input file {}", b.display()))?
|
||||
.unwrap_or_default();
|
||||
|
||||
if va == vb {
|
||||
if in_differing_region {
|
||||
in_differing_region = false;
|
||||
println!("---");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
in_differing_region = true;
|
||||
let new_diff_state = compare_byte_vecs(&va, &vb);
|
||||
|
||||
println!(
|
||||
"{0:08x}: {1}\n{0:08x}: {2}",
|
||||
i * 16,
|
||||
color_byte_display(&va, &vb, 31),
|
||||
color_byte_display(&vb, &va, 32),
|
||||
);
|
||||
if cli.detailed {
|
||||
if new_diff_state == DiffState::Same {
|
||||
if diff_state != DiffState::Same {
|
||||
println!("---");
|
||||
}
|
||||
} else {
|
||||
println!(
|
||||
"{0:08x}: {1}\n{0:08x}: {2}",
|
||||
i * 16,
|
||||
color_byte_display(&va, &vb, 31),
|
||||
color_byte_display(&vb, &va, 32),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if new_diff_state != diff_state {
|
||||
if diff_state != DiffState::Same {
|
||||
print!("{0:08x} to {1:08x}: ", region_start, i * 16);
|
||||
}
|
||||
match diff_state {
|
||||
DiffState::Same => (),
|
||||
DiffState::DifferAZero => {
|
||||
println!("First file is zero bytes");
|
||||
}
|
||||
DiffState::DifferBZero => {
|
||||
println!("Second file is zero bytes");
|
||||
}
|
||||
DiffState::DifferNonZero => {
|
||||
println!("Files differ");
|
||||
}
|
||||
}
|
||||
region_start = i * 16;
|
||||
}
|
||||
}
|
||||
diff_state = new_diff_state;
|
||||
}
|
||||
|
||||
match diff_state {
|
||||
DiffState::Same => (),
|
||||
DiffState::DifferAZero => {
|
||||
println!("First file is zero bytes from {0:08x} to end", region_start);
|
||||
}
|
||||
DiffState::DifferBZero => {
|
||||
println!(
|
||||
"Second file is zero bytes from {0:08x} to end",
|
||||
region_start
|
||||
);
|
||||
}
|
||||
DiffState::DifferNonZero => {
|
||||
println!("Files differ from {0:08x} to end", region_start);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in a new issue