add more compact output to bytediff
This commit is contained in:
		
							parent
							
								
									a01fdc7de7
								
							
						
					
					
						commit
						9e62430e2d
					
				
					 1 changed files with 110 additions and 28 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue