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::fs::File;
|
||||||
use std::io::{prelude::*, BufReader};
|
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> {
|
struct ZipLonger<A: Iterator, B: Iterator> {
|
||||||
a: A,
|
a: A,
|
||||||
|
@ -83,53 +129,89 @@ fn color_byte_display(this: &[u8], that: &[u8], color: u8) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let mut args = std::env::args();
|
let cli = Cli::parse();
|
||||||
let name = args
|
|
||||||
.next()
|
let a = cli.file1;
|
||||||
.ok_or_else(|| anyhow!("Missing executable name in args"))?;
|
let b = cli.file2;
|
||||||
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(
|
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();
|
.bytes();
|
||||||
let b_bytes = BufReader::new(
|
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();
|
.bytes();
|
||||||
|
|
||||||
let mut in_differing_region = false;
|
let mut diff_state = DiffState::Same;
|
||||||
|
let mut region_start: usize = 0;
|
||||||
|
|
||||||
for (i, (rva, rvb)) in
|
for (i, (rva, rvb)) in
|
||||||
ZipLonger::new(Chunks::new(a_bytes, 16), Chunks::new(b_bytes, 16)).enumerate()
|
ZipLonger::new(Chunks::new(a_bytes, 16), Chunks::new(b_bytes, 16)).enumerate()
|
||||||
{
|
{
|
||||||
let va: Vec<u8> = rva
|
let va: Vec<u8> = rva
|
||||||
.transpose()
|
.transpose()
|
||||||
.with_context(|| format!("Error reading from input file {}", a))?
|
.with_context(|| format!("Error reading from input file {}", a.display()))?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let vb: Vec<u8> = rvb
|
let vb: Vec<u8> = rvb
|
||||||
.transpose()
|
.transpose()
|
||||||
.with_context(|| format!("Error reading from input file {}", b))?
|
.with_context(|| format!("Error reading from input file {}", b.display()))?
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
if va == vb {
|
let new_diff_state = compare_byte_vecs(&va, &vb);
|
||||||
if in_differing_region {
|
|
||||||
in_differing_region = false;
|
|
||||||
println!("---");
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
in_differing_region = true;
|
|
||||||
|
|
||||||
println!(
|
if cli.detailed {
|
||||||
"{0:08x}: {1}\n{0:08x}: {2}",
|
if new_diff_state == DiffState::Same {
|
||||||
i * 16,
|
if diff_state != DiffState::Same {
|
||||||
color_byte_display(&va, &vb, 31),
|
println!("---");
|
||||||
color_byte_display(&vb, &va, 32),
|
}
|
||||||
);
|
} 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(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue