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