8 my $err = shift and select STDERR;
9 print "usage: csvdiff [--no-color] [--html] file.csv file.csv\n",
10 " provides colorized diff on sorted CSV files\n",
11 " assuming first line is header and first field is the key\n";
15 use Getopt::Long qw(:config bundling nopermute );
20 "help|?" => sub { usage (0); },
22 "c|color|colour!" => \$opt_c,
25 "o|output=s" => \$opt_o,
28 @ARGV == 2 or usage (1);
31 open STDOUT, ">", $opt_o or die "$opt_o: $!\n";
35 use Term::ANSIColor qw(:constants);
37 my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 0 });
40 binmode STDOUT, ":encoding(utf-8)";
42 <?xml version="1.0" encoding="utf-8"?>
43 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
44 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
46 <title>CFI School updates</title>
47 <meta name="Generator" content="perl $]" />
48 <meta name="Author" content="@{[scalar getpwuid $<]}" />
49 <meta name="Description" content="CSV diff @ARGV" />
50 <style type="text/css">
51 .rd { background: #ffe0e0; }
52 .gr { background: #e0ffe0; }
53 .hd { background: #e0e0ff; }
54 .b0 { background: #e0e0e0; }
55 .b1 { background: #f0f0f0; }
62 <h1>CSV diff @ARGV</h1>
66 $::{RED} = sub { "\cA\rr"; };
67 $::{GREEN} = sub { "\cA\rg"; };
68 $::{RESET} = sub { ""; };
71 $::{$_} = sub { "" } for qw( RED GREEN RESET );
76 foreach my $x (0, 1) {
77 open my $fh, "<", $ARGV[$x] or die "$ARGV[$x]: $!\n";
80 my $row = $csv->getline ($fh) or last;
81 @$row and push @{$f[$x]}, $row;
82 $n++ && $row->[0] =~ m/\D/ and $opt_n = 0;
85 my @n = map { $#{$f[$_]} } 0, 1;
87 my $hdr = "# csvdiff < $ARGV[0] > $ARGV[1]\n";
89 $f[$_][1+$n[$_]][0] = $opt_n ? 2147483647 : "\xff\xff\xff\xff" for 0, 1;
97 "<" => sub { $cls{b} ^= 1; "b$cls{b}" },
98 ">" => sub { "b$cls{b}" },
104 my $row = $f[$x][$i[$x]++];
107 my $bg = $cls{$pfx}->();
108 print qq{ <tr class="$bg">},
109 (map{"<td".(s/^\cA\r([gr])//?qq{ class="$1"}:"").">$_</td>"}@$row),
114 print $hdr, $pfx, " ", $pfx eq "-" ? RED : $pfx eq "+" ? GREEN : "";
115 $csv->print (*STDOUT, $row);
120 # Skip first line of both are same: it probably is a header
121 my @h0 = @{$f[0][0]};
122 my @h1 = @{$f[1][0]};
123 if ("@h0" eq "@h1") {
133 while ($i[0] <= $n[0] || $i[1] <= $n[1]) {
134 my @r0 = @{$f[0][$i[0]]};
135 my @r1 = @{$f[1][$i[1]]};
138 $r0[0] < $r1[0] and show ("-", 0), next;
139 $r0[0] > $r1[0] and show ("+", 1), next;
142 $r0[0] lt $r1[0] and show ("-", 0), next;
143 $r0[0] gt $r1[0] and show ("+", 1), next;
146 "@r0" eq "@r1" and $i[0]++, $i[1]++, next;
148 foreach my $c (1 .. $#h0) {
149 my ($L, $R) = map { defined $_ ? $_ : "" } $r0[$c], $r1[$c];
151 $f[0][$i[0]][$c] = RED . $L . RESET;
152 $f[1][$i[1]][$c] = GREEN . $R . RESET;
159 $opt_h and print " </table>\n</body>\n</html>\n";