1#!/usr/bin/perl 2# 2.2.2010, Sampo Kellomaki (sampo@iki.fi) 3# 20.11.2010, added Algorithm::Diff based ediff support --Sampo 4# $Id$ 5# 6# See also: man diff, diff -u, man sdiff, sdiff -b -w 270, perldoc Algorithm::Diff 7# man cmp, cmp -b 8# 9# Usage: ./diffy.pl [-ediff] file1 file2 10# -ediff tries to use Algorithm::Diff to produce colorized char-by-char diffs a'la emacs ediff 11 12$ediff = 1,shift if $ARGV[0] eq '-ediff'; 13 14$ascii = 2; 15# See https://wiki.archlinux.org/index.php/Color_Bash_Prompt 16#sub red { $ascii > 1 ? "\e[1;31m$_[0]\e[0m" : $_[0]; } # red text 17#sub green { $ascii > 1 ? "\e[1;32m$_[0]\e[0m" : $_[0]; } 18#sub red { $ascii > 1 ? "\e[1;41m$_[0]\e[0m" : $_[0]; } # red background, black bold text 19#sub green { $ascii > 1 ? "\e[1;42m$_[0]\e[0m" : $_[0]; } 20sub redy { $ascii > 1 ? "\e[41m$_[0]\e[0m" : $_[0]; } # red background, black text (no bold) 21sub greeny { $ascii > 1 ? "\e[42m$_[0]\e[0m" : $_[0]; } 22 23sub simple_line_diff { 24 my ($a, $b) = @_; 25 my @a = split /\n/, $a; 26 my @b = split /\n/, $b; 27 print "\t- reference output\n\t+ testrun\n"; 28 my $i,$j,$n=10; 29 print "\toutputs differ in length ($#a vs. $#b)\n" if $#a != $#b; 30 for ($i=0, $j=0; $i <= $#a && $j <= $#b; ++$i, ++$j) { 31 next if $a[$i] eq $b[$j]; 32 if (!--$n) { 33 print "\tToo many differences. Output truncated. Run real diff.\n"; 34 last; 35 } 36 $a[$i] = substr($a[$i],0,80); 37 $b[$j] = substr($b[$j],0,80); 38 if ($a[$i+1] eq $b[$j]) { # Extra line in a 39 print "\t".($i+1).": - $a[$i]\n"; 40 ++$i; 41 } elsif ($a[$i] eq $b[$j+1]) { # Extra line in b 42 print "\t".($j+1).": + $b[$j]\n"; 43 ++$j; 44 } else { 45 print "\t".($i+1).": - $a[$i]\n\t".($j+1).": + $b[$j]\n"; 46 } 47 } 48 if ($n) { 49 for (; $i <= $#a; ++$i) { 50 if (!--$n) { 51 print "\tToo many differences. Output truncated. Run real diff.\n"; 52 last; 53 } 54 print "\t".($i+1).": - $a[$i]\n"; 55 } 56 } 57 if ($n) { 58 for (; $j <= $#b; ++$j) { 59 if (!--$n) { 60 print "\tToo many differences. Output truncated. Run real diff.\n"; 61 last; 62 } 63 print "\t".($j+1).": - $a[$j]\n"; 64 } 65 } 66} 67 68$line_len = 160; 69 70sub char_diff { 71 my ($a, $b) = @_; 72 my @a = split //, $a; 73 my @b = split //, $b; 74 my ($i, $j, $a_line, $b_line, $diff_line); 75 for ($i = $j = 0; $i <= $#a && $j <= $#b; ++$i, ++$j) { 76 #warn "CHAR $i $j $a[$i] $b[$j] " . (($a[$i] eq $b[$j])?'':'***'); 77 if (length $diff_line >= $line_len) { 78 print "$a_line\n"; 79 print "$b_line\n"; 80 print "$diff_line\n"; 81 $a_line = $b_line = $diff_line = ''; 82 } 83 $a_line .= $a[$i]; 84 $b_line .= $b[$j]; 85 if ($a[$i] eq $b[$j]) { 86 $diff_line .= ' '; 87 } else { 88 $diff_line .= '*'; 89 } 90 } 91 92 print "$a_line\n"; 93 print "$b_line\n"; 94 print "$diff_line\n"; 95} 96 97sub ediffy { 98 my ($data1,$data2) = @_; 99 require Algorithm::Diff; 100 my @seq1 = split //, $data1; 101 my @seq2 = split //, $data2; 102 my $diff = Algorithm::Diff->new( \@seq1, \@seq2 ); 103 104 $diff->Base(1); # Return line numbers, not indices 105 while( $diff->Next() ) { 106 if (@sames = $diff->Same()) { 107 print @sames; 108 next; 109 } 110 if (@dels = $diff->Items(1)) { 111 print redy(join '', @dels); 112 } 113 if (@adds = $diff->Items(2)) { 114 print greeny(join '', @adds); 115 } 116 } 117} 118 119sub readall { 120 my ($f) = @_; 121 my ($pkg, $srcfile, $line) = caller; 122 undef $/; # Read all in, without breaking on lines 123 open F, "<$f" or die "$srcfile:$line: Cant read($f): $!"; 124 binmode F; 125 #flock F, 1; 126 my $x = <F>; 127 #flock F, 8; 128 close F; 129 return $x; 130} 131 132($file1, $file2) = @ARGV; 133 134#warn "file1($file1) file2($file2)"; 135 136$data1 = readall $file1; 137$data2 = readall $file2; 138 139if ($ediff) { 140 ediffy($data1,$data2); 141} else { 142 char_diff($data1, $data2); 143 #simple_line_diff($data1, $data2); 144} 145 146__END__ 147