File Coverage

script/cdif
Criterion Covered Total %
statement 447 623 71.7
branch 125 264 47.3
condition 31 78 39.7
subroutine 47 76 61.8
pod n/a
total 650 1041 62.4


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3             ##
4             ## cdif: word context diff
5             ##
6             ## Copyright (c) 1992- Kazumasa Utashiro
7             ##
8             ## Original version on Mar 11 1992
9             ##
10              
11 9     9   36820 use v5.14;
  9         24  
12 9     9   34 use warnings;
  9         10  
  9         437  
13              
14 9     9   3612 use utf8;
  9         2025  
  9         38  
15 9     9   259 use Carp;
  9         13  
  9         716  
16 9     9   4371 use Encode;
  9         129826  
  9         706  
17 9     9   3836 use Encode::Guess;
  9         29552  
  9         28  
18              
19 9     9   4734 use Pod::Usage;
  9         372757  
  9         1257  
20 9     9   110 use List::Util qw(first sum pairmap);
  9         21  
  9         1109  
21 9     9   4326 use Text::ParseWords qw(shellwords);
  9         11594  
  9         477  
22 9     9   3375 use IO::Divert;
  9         5709  
  9         305  
23 9     9   16614 use Text::VisualWidth::PP qw(vwidth);
  9         27847  
  9         578  
24 9     9   4515 use Data::Dumper; {
  9         55653  
  9         564  
25 9     9   1403098 no warnings 'redefine';
  9         84  
  9         12  
  9         858  
26 9     0   146 *Data::Dumper::qquote = sub { qq["${\(shift)}"] };
  0         0  
  0         0  
27 9         27 $Data::Dumper::Terse = 1;
28 9         23 $Data::Dumper::Sortkeys = 1;
29 9         24 $Data::Dumper::Useperl = 1;
30             }
31              
32 9     9   3815 use App::sdif;
  9         19  
  9         599  
33 9         29 our $VERSION = $App::sdif::VERSION;
34              
35 9     9   3366 use App::sdif::Util;
  9         24  
  9         1041  
36 9     9   3202 use App::cdif::Command::Mecab;
  9         27  
  9         343  
37              
38             ##
39             ## options
40             ##
41              
42 9     9   3679 use charnames ':full';
  9         61938  
  9         44  
43 9         516 my %visible = (
44             nul => [ 1, "\000" => "\N{SYMBOL FOR NULL}" ],
45             soh => [ 1, "\001" => "\N{SYMBOL FOR START OF HEADING}" ],
46             bel => [ 1, "\007" => "\N{SYMBOL FOR BELL}" ],
47             bs => [ 1, "\010" => "\N{SYMBOL FOR BACKSPACE}" ],
48             ht => [ 0, "\011" => "\N{SYMBOL FOR HORIZONTAL TABULATION}" ],
49             nl => [ 0, "\012" => "\N{SYMBOL FOR NEWLINE}" . "\n" ],
50             vt => [ 1, "\013" => "\N{SYMBOL FOR VERTICAL TABULATION}" ],
51             np => [ 1, "\014" => "\N{SYMBOL FOR FORM FEED}" ],
52             cr => [ 1, "\015" => "\N{SYMBOL FOR CARRIAGE RETURN}" ],
53             esc => [ 1, "\033" => "\N{SYMBOL FOR ESCAPE}" ],
54             sp => [ 0, "\040" => "\N{SYMBOL FOR SPACE}" ],
55             del => [ 1, "\177" => "\N{SYMBOL FOR DELETE}" ],
56             nbsp => [ 1, "\x{00A0}" => "\N{OPEN BOX}" ],
57             nnbsp => [ 1, "\x{202F}" => "\N{OPEN BOX}" ],
58             ensp => [ 1, "\x{2002}" => "\N{OPEN BOX}" ],
59             emsp => [ 1, "\x{2003}" => "\N{OPEN BOX}" ],
60             thinsp => [ 1, "\x{2009}" => "\N{OPEN BOX}" ],
61             hairsp => [ 1, "\x{200A}" => "\N{OPEN BOX}" ],
62             zwsp => [ 1, "\x{200B}" => "\N{OPEN BOX}" ],
63             idesp => [ 1, "\x{3000}" => "\N{OPEN BOX}" ],
64             );
65 9         144 my %opt_visible = do {
66 9         85 map { $_ => splice @{$visible{$_}}, 0, 1 }
  180         161  
  180         320  
67             keys %visible;
68             };
69              
70 9         44 my @rcsopt;
71             my @diffopts;
72 9         0 my @sub_diffopts;
73              
74 9     9   378 binmode STDOUT, ":encoding(utf8)";
  9         7167  
  9         167  
  9         62  
75 9         9059 binmode STDERR, ":encoding(utf8)";
76              
77 9 50       239 if (my $env = $ENV{'CDIFOPTS'}) {
78 0         0 unshift(@ARGV, shellwords($env));
79             }
80              
81 9         18 my $unit_default = 'word';
82 9         68 my $prefix_default = q/(?:\\| )*(?: )*/;
83              
84 9         16 my $app;
85              
86 9     9   202960 use Getopt::EX::Hashed; {
  9         57082  
  9         51  
87              
88 9         20 Getopt::EX::Hashed->configure( DEFAULT => [ is => 'rw' ] ) ;
  9         114  
89              
90 9         382 has help => ' ' ;
91 9         590 has version => ' ' ;
92 9         425 has man => ' ' ;
93 9         304 has debug => ' d =s@ ' , default => [] ;
94 9         327 has diff => ' =s ' ;
95 9         303 has subdiff => ' =s ' , default => 'diff' ;
96 9         317 has color => ' ! ' , default => 1 ;
97 9         324 has 256 => ' ! ' , default => 1 ;
98 9         337 has commandcolor => ' cc ! ' , default => 1 ;
99 9         331 has markcolor => ' mc ! ' , default => 1 ;
100 9         361 has textcolor => ' tc ! ' , default => 1 ;
101 9         377 has unknowncolor => ' uc ! ' , default => 1 ;
102 9         432 has colormap => ' cm =s@ ' , default => [] ;
103 9         403 has colordump => ' ' ;
104 9         369 has sdif => ' ' ;
105 9         374 has stat => ' ! ' ;
106 9         433 has unit => ' by :s ' , default => $unit_default ,
107             , any => [ qw(char word letter mecab 0), '' ];
108 9         460 has show_old => ' ! ' , default => 1 , alias => 'old' ;
109 9         434 has show_new => ' ! ' , default => 1 , alias => 'new' ;
110 9         451 has show_mrg => ' ! ' , default => 1 , alias => 'mrg' ;
111 9         467 has command => ' ! ' , default => 1 ;
112 9         426 has unknown => ' ! ' , default => 1 ;
113 9         424 has mark => ' ! ' , default => 1 ;
114 9         450 has prefix => ' ! ' , default => 1 ;
115 9         478 has prefix_pattern => ' =s ' , default => $prefix_default ;
116 9         504 has visible => ' =s@ ' , default => [] ;
117 9         520 has lenience => ' ! ' , default => 1 ;
118 9         512 has limit => ' =s% ' , default => { length => 1000 } ;
119 9         488 has linebyline => ' :2 ' , alias => 'lxl';
120 9         521 has style => ' =s ' , default => '' ;
121              
122 9         486 has ignore_case => 'i' ;
123 9         481 has ignore_space_change => 'b' ;
124 9         484 has ignore_all_space => 'w' ;
125 9         519 has expand_tabs => 't' ;
126              
127 9         582 has rcs => '' ;
128              
129             has '+sdif' => sub {
130 4     4   38174 $app->commandcolor = 0;
131 4         30 $app->markcolor = 0;
132 4         20 $app->textcolor = 0;
133 4         42 $app->unknowncolor = 0;
134 9         598 } ;
135             has mecab => '!', action => sub {
136 0 0   0   0 $app->unit = $_[1] ? 'mecab' : $unit_default;
137 9         478 } ;
138 9     0   573 has visible_cr => ' vcr ! ' , action => sub { $opt_visible{cr} = $_[1] } ;
  0         0  
139 9     0   559 has visible_esc => ' vesc ! ' , action => sub { $opt_visible{esc} = $_[1] } ;
  0         0  
140 9     0   690 has c => ' ' , action => sub { push @diffopts, "-c" } ;
  0         0  
141 9     0   784 has u => ' ' , action => sub { push @diffopts, "-u" } ;
  0         0  
142 9     0   631 has context => ' C =i ' , action => sub { push @diffopts, "-C" . $_[1] } ;
  0         0  
143 9     0   596 has unified => ' U =i ' , action => sub { push @diffopts, "-U" . $_[1] } ;
  0         0  
144 9     0   608 has r => ' =s ' , action => sub { push @rcsopt, "-r$_[1]" } ;
  0         0  
145 9     0   652 has q => ' ' , action => sub { push @rcsopt, "-q" } ;
  0         0  
146              
147             has '+help' => sub {
148 0     0   0 pod2usage
149             -verbose => 99,
150             -sections => [ qw(SYNOPSIS VERSION) ];
151 9         647 };
152             has '+man' => sub {
153 0     0   0 pod2usage -verbose => 2;
154 9         307 };
155             has '+version' => sub {
156 0     0   0 print "$VERSION\n";
157 0         0 exit;
158 9         416 };
159              
160 9     9   6495 } no Getopt::EX::Hashed;
  9         14  
  9         34  
161              
162 9 50       284 $app = Getopt::EX::Hashed->new() or die;
163              
164 9     9   4365 use Getopt::EX::Long qw(:DEFAULT Configure ExConfigure);
  9         689399  
  9         4629  
165 9         11874 ExConfigure BASECLASS => [ "App::cdif", "Getopt::EX" ];
166 9         342 Configure("bundling");
167 9 50       446 $app->getopt || usage({status => 1});
168              
169 9         50543 my %debug = map { $_ => 1 } map { split // } @{$app->debug};
  0         0  
  0         0  
  9         37  
170              
171 9         73 $App::sdif::Util::NO_WARNINGS = $app->lenience;
172 9 50       54 $App::cdif::Command::Mecab::debug = 1 if $debug{m};
173              
174 9 50       56 $app->rcs++ if @rcsopt;
175 9   33     25 my $diff = $app->diff || ($app->rcs ? 'rcsdiff' : 'diff');
176              
177 9         126 push @diffopts, map { $_->[1] } grep { $_->[0] } (
  0         0  
  36         160  
178             [ $app->ignore_case, "-i" ],
179             [ $app->ignore_space_change, "-b" ],
180             [ $app->ignore_all_space, "-w" ],
181             [ $app->expand_tabs, "-t" ],
182             );
183              
184 9         65 push @sub_diffopts, map { $_->[1] } grep { $_->[0] } (
  0         0  
  18         91  
185             [ $app->ignore_case, "-i" ],
186             [ $app->ignore_all_space, "-w" ],
187             );
188              
189 9         29 my %colormap = do {
190 9 50       67 my $col = $app->{256} ? 0 : 1;
191 153 100   153   359 pairmap { $a => (ref $b eq 'ARRAY') ? $b->[$col] : $b } (
192 9         137 UNKNOWN => "" ,
193             UMARK => [ "/444" , "/w" ] ,
194             CMARK => "GS" ,
195             OMARK => "CS" ,
196             NMARK => "MS" ,
197             MMARK => "YS" ,
198             UTEXT => "" ,
199             CTEXT => "G" ,
200             OTEXT => "C" ,
201             NTEXT => "M" ,
202             MTEXT => "Y" ,
203             COMMAND => [ "555/222E" , "w/kE" ] ,
204             OCHANGE => [ "K/445" , "B/w" ] ,
205             NCHANGE => [ "K/445" , "B/w" ] ,
206             DELETE => [ "K/544" , "R/w" ] ,
207             APPEND => [ "K/544" , "R/w" ] ,
208             VISIBLE => "",
209             );
210             };
211              
212 9     9   76 use Getopt::EX::Colormap qw(ansi_code);
  9         12  
  9         7370  
213             my $color_handler = Getopt::EX::Colormap
214             ->new(HASH => \%colormap)
215 9         171 ->load_params(@{$app->colormap});
  9         603  
216              
217 9         245 for (
218             [ $app->unknowncolor => q/UNKNOWN/ ],
219             [ $app->commandcolor => q/COMMAND/ ],
220             [ $app->markcolor => q/MARK/ ],
221             [ $app->textcolor => q/TEXT/ ],
222             ) {
223 36         150 my($color, $label) = @$_;
224 36 100       60 $color and next;
225 16         258 for (grep /$label/, keys %colormap) {
226 48         78 $colormap{$_} = '';
227             }
228             }
229              
230 9 50       41 warn 'colormap = ', Dumper \%colormap if $debug{c};
231              
232 9 50       24 if ($app->colordump) {
233 0         0 print $color_handler->colormap(
234             name => '--changeme', option => '--colormap');
235 0         0 exit;
236             }
237              
238             sub color {
239 594     594   8659 $color_handler->color(@_);
240             }
241              
242 9         42 my $prefix_re = do {
243 9 50       26 if ($app->prefix) {
244 9         193 qr/$app->{prefix_pattern}/;
245             } else {
246 0         0 "";
247             }
248             };
249              
250 9         32 my $DIFF;
251             my $OLD;
252 9         0 my $NEW;
253              
254 9 50       23 if ($app->rcs) {
    50          
    50          
255 0   0     0 my $rcsfile = shift || usage("No RCS filename\n\n");
256 0         0 $DIFF = "$diff @diffopts @rcsopt $rcsfile|";
257             } elsif (@ARGV == 2) {
258 0         0 ($OLD, $NEW) = splice(@ARGV, 0, 2);
259 0         0 $DIFF = "$diff @diffopts $OLD $NEW |";
260             } elsif (@ARGV < 2) {
261 9   100     121 $DIFF = shift || '-';
262             } else {
263 0 0       0 usage("Arguments error.\n\n") if @ARGV;
264             }
265 9 50       33 warn "DIFF = \"$DIFF\"\n" if $debug{f};
266              
267 9         12 my %func = do {
268 9 100       26 my $col = $app->color ? 0 : 1;
269 45     45   96 pairmap { $a => $b->[$col] } (
270 0     0   0 DELETE => [ sub { color("DELETE", @_) }, \&bd ],
271 0     0   0 APPEND => [ sub { color("APPEND", @_) }, \&bd ],
272 48     48   287 OLD => [ sub { color("OCHANGE", @_) }, \&ul ],
273 48     48   92 NEW => [ sub { color("NCHANGE", @_) }, \&ul ],
274 9     72   161 UNKNOWN => [ sub { color("UNKNOWN", @_) }, undef ],
  72         154  
275             );
276             };
277              
278 9         36 my $w_pattern = do {
279 9 50       25 if ($app->unit =~ /^char/) {
280 0         0 qr/\X/s;
281             } else {
282 9 50       59 my $w = $app->unit eq 'letter' ? '' : '_';
283 9         1598 qr{
284             \p{Han} | \p{InHiragana}+ | \p{InKatakana}+ |
285             [$w\p{Latin}]+ |
286             [$w\p{Hang}]+ |
287             [$w\p{Cyrillic}]+ |
288             [$w\p{Arabic}]+ |
289             [$w\p{Thai}]+ |
290             \d+ |
291             # (\p{Punct})\g{-1}* |
292             [\h\r\f]*\n | \s+ | (\X)\g{-1}*
293             }x;
294             }
295             };
296              
297             ##
298             ## Converter/Effector function for visible characters
299             ##
300 9         26 my($converter, $effector);
301 9     9   64 use Getopt::EX::LabeledParam;
  9         16  
  9         5164  
302             Getopt::EX::LabeledParam
303             ->new(HASH => \%opt_visible)
304 9         43 ->load_params (@{$app->visible});
  9         211  
305 9 50       272 if (my @names = grep $opt_visible{$_}, keys %opt_visible) {
306 9         149 my @chars = map $visible{$_}->[0], @names;
307 9         26 my %hash = map { @$_ } values %visible;
  180         324  
308 9         21 my $re = do { local $" = ''; qr/[\Q@chars\E]/ };
  9         20  
  9         245  
309 9     0   35 my $sub0 = sub { s/($re)/$hash{$1}/g };
  0         0  
310 9     0   22 my $sub1 = sub { $_[0] =~ s/($re)/$hash{$1}/gr };
  0         0  
311             my $sub2 = sub {
312 81     81   100 my $mark_re = shift;
313 81         126 for (@_) {
314 204   100     294 $_ // next;
315 200         1699 s{^$mark_re\K(?=.*$re)(.*\R?)}{
316 0         0 $sub1->($1);
317             }mge;
318             }
319 9         29 };
320 9         27 $converter = $sub2;
321              
322 9 50       62 if (my $color = $colormap{'VISIBLE'}) {
323 0         0 my $s = ansi_code($color);
324 0         0 my $e = ansi_code($color =~ s/(?=.)/~/gr); # cancel the effect
325             my $symbols = join('',
326 0         0 map { $visible{$_}->[-1] =~ s/\s+//gr }
  0         0  
327             @names);
328 0     0   0 $effector = sub { s/([\Q$symbols\E]+)/${s}${1}${e}/g };
  0         0  
329             }
330             }
331              
332             ##
333             ## Temporary files
334             ##
335 9     9   78 use Command::Run::Tmpfile;
  9         15  
  9         40366  
336 9         111 my $T1 = Command::Run::Tmpfile->new;
337 9         5104 my $T2 = Command::Run::Tmpfile->new;
338              
339             ##
340             ## Total statistic info
341             ##
342 9         2339 my %stat;
343 9         173 @stat{'a', 'd', 'c', 'anl', 'dnl', 'cnl'} = (0, 0, 0, 0, 0, 0);
344 9         35 @stat{'anlb', 'dnlb', 'cnlb'} = (0, 0, 0);
345              
346 9 50       193 open(DIFF, $DIFF) || die "$DIFF: $!\n";
347 9         206 binmode DIFF, ":encoding(utf8)";
348              
349 9         302 my $stdout = IO::Divert->new;
350 59     59   94 sub sprintln { map { s/(?<=[^\n])\z/\n/r } @_ }
  145         1420  
351 59     59   355 sub println { print sprintln @_ }
352             sub print_lxl {
353 0     0   0 my($l1, $l2) = (sprintln($_[0]), sprintln($_[1]));
354 0 0       0 if ($app->style eq 'diff') {
355 0         0 printf "1c1\n". "< $l1". "---\n". "> $l2",
356             } else {
357 0         0 print $l1, $l2;
358             }
359             }
360              
361 9         2607 while () {
362             #
363             # --linebyline, --lxl option
364             #
365 97 50       3113 if ($app->linebyline) {
    100          
    100          
    100          
    50          
366 0         0 my $old = $_;
367 0   0     0 my $new = // do {
368 0         0 print $old;
369 0         0 next;
370             };
371 0 0       0 compare(\$old, \$new) if $app->unit;
372 0         0 print_lxl(color("OTEXT", $old),
373             color("NTEXT", $new));
374             }
375             #
376             # normal diff
377             #
378             elsif (/^([\d,]+)([adc])([\d,]+)\r?$/) {
379 10         225 my($left, $cmd, $right) = ($1, $2, $3);
380 10         15 my $command_line = $_;
381 10         14 my($old, $del, $new);
382             eval {
383 10 100       44 if ($cmd =~ /[cd]/) {
384 8         34 $old = read_diff(*DIFF, scalar(range($left)));
385 8 50       69 $old =~ /^(?!<)/m and die;
386             }
387 10 100       40 if ($cmd =~ /[c]/) {
388 6         14 $del = read_diff(*DIFF, 1);
389 6 50       29 $del =~ /^(?!---)/m and die;
390             }
391 10 100       36 if ($cmd =~ /[ca]/) {
392 8         25 $new = read_diff(*DIFF, scalar(range($right)));
393 8 50       29 $new =~ /^(?!>)/m and die;
394             }
395 10         26 1;
396             }
397 10 50       26 or do {
398 0   0     0 defined and print for ($command_line, $old, $del, $new);
399 0         0 next;
400             };
401              
402 10         51 print_command($command_line);
403              
404 10 50       23 if ($converter) {
405 10         99 $converter->(qr/[<>][ \t]/, $old, $new);
406             }
407              
408 10 100       42 if ($cmd eq 'c') {
409 6 50       53 compare(\$old, \$new, qr/<[ \t]/, qr/>[ \t]/) if $app->unit;
410             }
411              
412 10 50       143 if ($app->color) {
413 10 100       126 $old =~ s{^(<[ \t])(.*)}{
414 12         264 color("OMARK", $1) . color("OTEXT", $2)
415             }mge if $old;
416 10 100       395 $new =~ s{^(>[ \t])(.*)}{
417 12         246 color("NMARK", $1) . color("NTEXT", $2)
418             }mge if $new;
419             }
420              
421 10 100 66     462 println $old if $old and $app->show_old;
422 10 100       33 println $del if $del;
423 10 100 66     48 println $new if $new and $app->show_new;
424             }
425             #
426             # diff -c
427             #
428             elsif (/^\*\*\* ([\d,]+) \*\*\*\*\r?$/) {
429 2         33 my $left = $1;
430 2         12 print_command($_);
431 2         3 my(@old, @new);
432 2         11 my $oline = range($left);
433 2         9 @old = read_diffc(*DIFF, $oline);
434 2         3 my $new;
435 2 50 33     13 if (@old and $old[0] =~ /^--- /) {
436 0         0 $new = shift @old;
437 0         0 @old = ("");
438             } else {
439 2         5 $new = ;
440             }
441 2         3 my $dline = map { /^-/mg } @old;
  14         22  
442 2 50       20 if ($new =~ /^--- ([\d,]+) ----$/) {
443 2         37 my $right = $1;
444 2         6 my $nline = range($right);
445 2 50 33     9 if (@old == 1 and $old[0] ne "" and $oline - $dline == $nline) {
      33        
446 0         0 @new = ("");
447             } else {
448 2         5 @new = read_diffc(*DIFF, $nline);
449             }
450 2 50       5 if ($converter) {
451 2         9 $converter->(qr/[\-\+\!\ ][ \t]/, @old, @new);
452             }
453 2         6 my $mark_re = qr/![ \t]/;
454 2         8 for my $i (keys @old) {
455 14         331 my $cmark = "! ";
456 14 100       30 if ($i % 2) {
457 6 50       26 compare(\$old[$i], \$new[$i], $mark_re) if $app->unit;
458             }
459 14 50       89 if ($app->color) {
460 14         144 $old[$i] =~ s{^([\-\!][ \t])(.*)}{
461 12         301 color("OMARK", $1) . color("OTEXT", $2)
462             }mge;
463 14         301 $new[$i] =~ s{^([\+\!][ \t])(.*)}{
464 12         126 color("NMARK", $1) . color("NTEXT", $2)
465             }mge;
466             }
467             }
468             }
469 2 50       179 println @old if $app->show_old;
470 2         12 println $new;
471 2 50       14 println @new if $app->show_new;
472             }
473             #
474             # diff --combined (generic)
475             #
476             elsif (m{^
477             (? $prefix_re)
478             (?
479             (? \@{2,} ) [ ]
480             (? (?: [-+]\d+(?:,\d+)? [ ] ){2,} )
481             \g{mark}
482             (?s:.*)
483             )
484             }x) {
485 11         431 my($prefix, $command, $lines) = @+{qw(prefix command lines)};
486 11         66 my $column = length $+{mark};
487             my @lines = map {
488 11 100       122 $_ eq ' ' ? 1 : int $_
  22         178  
489             } $lines =~ /\d+(?|,(\d+)|( ))/g;
490              
491 11 50       45 if (@lines != $column) {
492 0         0 print;
493 0         0 next;
494             }
495              
496 11         19 my($divert, %read_opt);
497 11 100       27 if ($prefix) {
498 8         22 $read_opt{prefix} = $prefix;
499 8     8   121 $divert = IO::Divert->new(FINAL => sub { s/^/$prefix/mg });
  8         537  
500             }
501              
502 11         762 print_command($command);
503              
504 11         73 my @buf = read_unified \%read_opt, *DIFF, @lines;
505              
506 11         18 state @mark_re;
507 11   66     49 my $mark_re = $mark_re[$column] //= do {
508 5         16 my $mark = '.' x ($column - 1);
509 5         110 qr/$mark/;
510             };
511              
512 11 50       30 if ($converter) {
513 69         118 map { $converter->($mark_re, @$_) }
514 11         24 map { $_->lists }
  31         85  
515             @buf;
516             }
517              
518 11         23 for my $buf (@buf) {
519 31         105 my @result = compare_unified($column, $buf, $mark_re);
520 31 50       101 if (@result == 3) {
521 31 50       133 $app->show_new or splice @result, 2, 1;
522 31 50       211 $app->show_old or splice @result, 1, 1;
523             }
524 31         172 println @result;
525             }
526             }
527             #
528             # conflict marker
529             #
530             elsif (/^<<<<<<<\s+(.*)/) {
531             CONFLICT:
532             {
533 0         0 my $c1 = $_;
  0         0  
534              
535 0     0   0 my @old = read_until { /^=======$/ } *DIFF;
  0         0  
536 0   0     0 my $c2 = pop @old // do {
537 0         0 print $c1, @old;
538 0         0 last;
539             };
540              
541 0     0   0 my @new = read_until { /^>>>>>>>\s+(.*)/ } *DIFF;
  0         0  
542 0   0     0 my $c3 = pop @new // do {
543 0         0 print $c1, @old, $c2, @new;
544 0         0 last;
545             };
546              
547 0     0   0 my $i = first { $old[$_] =~ /^\Q|||||||\E/ } keys @old;
  0         0  
548 0 0       0 my @mrg = defined $i ? splice @old, $i : ();
549              
550 0         0 my $old = join '', @old;
551 0         0 my $new = join '', @new;
552 0 0       0 $converter->('', $old, $new) if $converter;
553 0 0       0 compare(\$old, \$new) if $app->unit;
554              
555 0 0       0 print $c1 if $app->mark;
556 0 0       0 print color("OTEXT", $old) if $app->show_old;
557 0 0       0 if (@mrg) {
558 0         0 my $c4 = shift @mrg;
559 0 0       0 print $c4 if $app->mark;
560 0         0 my $mrg = join '', @mrg;
561 0 0       0 $converter->('', $mrg) if $converter;
562 0 0       0 print color("MTEXT", $mrg) if $app->show_mrg;
563             }
564 0 0       0 println $c2 if $app->mark;
565 0 0       0 println color("NTEXT", $new) if $app->show_new;
566 0 0       0 println $c3 if $app->mark;
567             }
568             }
569             else {
570 74 50       1866 if ($app->unknown) {
571 74 100       431 if (my $f = $func{UNKNOWN}) {
572 72         121 print $f->($_);
573             } else {
574 2         6 print;
575             }
576             }
577             }
578             }
579             continue {
580 97         1865 $stdout->fh->flush;
581 97         1216 local *_ = $stdout->buffer;
582 97 50       535 if (length) {
583 97         492 $_ = decode 'utf8', $_;
584 97 50       2127 $effector->() if $effector;
585 97         492 STDOUT->printflush($_);
586 97         10582 $stdout->clear;
587             }
588             }
589 9         565 close DIFF;
590 9 50       121 my $exit = $DIFF =~ /\|$/ ? $? >> 8 : 0;
591              
592 9         43 select STDOUT;
593              
594 9 50       83 if ($app->stat) {
595 0         0 select STDERR;
596              
597 0         0 print("TOTAL: ");
598             printf("append=%d delete=%d change=%d\n",
599 0         0 $stat{'a'}, $stat{'d'}, $stat{'c'});
600 0         0 print("IGNORE WHITESPACE: ");
601             printf("append=%d delete=%d change=%d\n",
602             $stat{'anl'},
603             $stat{'dnl'},
604 0         0 $stat{'cnl'});
605 0         0 print("IGNORE WHITESPACE (bytes): ");
606             printf("append=%d delete=%d change=%d\n",
607             $stat{'anlb'},
608             $stat{'dnlb'},
609 0         0 $stat{'cnlb'});
610             }
611              
612 9         166 exit($exit > 1);
613              
614             ######################################################################
615              
616             sub compare_unified {
617 31     31   40 my $column = shift;
618 31 50       72 goto &compare_unified_3 if $column == 3;
619 31         104 goto &compare_unified_generic;
620             }
621              
622             sub compare_unified_generic {
623 31     31   68 my($buf, $mark_re) = @_;
624 31         321 my $c = $buf->collect(qr/^[\t ]+$/);
625 31         112 my $o = $buf->collect(qr/[-]/);
626 31         86 my $n = $buf->collect(qr/[+]/);
627 31 50       95 compare(\$o, \$n, $mark_re) if $app->unit;
628 31 100       224 if ($app->color) {
629             map {
630 30         302 my($buf, $m, $t) = @$_;
  90         1011  
631 90         483 $$buf =~ s{^($mark_re)(.*)}{
632 154 50       2644 ($app->mark ? color($m, $1) : '') . color($t, $2)
633             }mge;
634             } ( [ \$c, 'UMARK', 'UTEXT' ],
635             [ \$o, 'OMARK', 'OTEXT' ],
636             [ \$n, 'NMARK', 'NTEXT' ] );
637             } else {
638 1 50       6 map { s/^$mark_re//mg } $c, $o, $n if not $app->mark;
  0         0  
639             }
640 31         1077 ($c, $o, $n);
641             }
642              
643             sub compare_unified_3 {
644 0     0   0 my($buf, $mark_re) = @_;
645              
646 0         0 my @mark = ( " ", "--", "- ", " -", "+ ", " +", "++" );
647 0         0 my %buf = map { $_ => scalar($buf->collect($_)) } @mark;
  0         0  
648              
649 0 0       0 goto SKIP unless $app->unit;
650              
651 0         0 my @r;
652 0         0 $r[0] = compare(\@buf{q/--/, q/++/}, $mark_re);
653 0         0 $r[1] = compare(\@buf{q/- /, q/ -/}, $mark_re);
654 0 0       0 if ($r[1] == 0) {
655 0         0 $r[2] = compare(\@buf{q/- /, q/+ /}, $mark_re);
656 0         0 $r[3] = compare(\@buf{q/ -/, q/ +/}, $mark_re);
657             }
658 0 0       0 if (sum(@r) == 0) {
659 0         0 $r[4] = compare(\@buf{q/- /, q/++/}, $mark_re);
660 0 0       0 $r[5] = compare(\@buf{q/ -/, q/++/}, $mark_re) unless $r[4];
661 0         0 $r[6] = compare(\@buf{q/--/, q/+ /}, $mark_re);
662 0 0       0 $r[7] = compare(\@buf{q/--/, q/ +/}, $mark_re) unless $r[6];
663             }
664              
665             SKIP:
666              
667 0 0       0 if ($app->color) {
668 0 0       0 map { s/^$mark_re//mg } $buf{q/ /} if not $app->mark;
  0         0  
669             map {
670 0         0 my($s, $m, $t) = @$_;
671 0 0       0 $$s =~ s{^($mark_re)(.*)}{
672 0 0       0 ($app->mark ? color($m, $1) : '') . color($t, $2)
673             }mge if $$s;
674             } ( [ \$buf{q/--/}, 'CMARK', 'CTEXT' ],
675             [ \$buf{q/- /}, 'OMARK', 'OTEXT' ],
676             [ \$buf{q/ -/}, 'NMARK', 'NTEXT' ],
677             [ \$buf{q/+ /}, 'MMARK', 'MTEXT' ],
678             [ \$buf{q/ +/}, 'MMARK', 'MTEXT' ],
679 0         0 [ \$buf{q/++/}, 'MMARK', 'MTEXT' ] );
680             } else {
681 0 0       0 map { s/^$mark_re//mg } @buf{@mark} if not $app->mark;
  0         0  
682             }
683              
684 0         0 @buf{$buf->labels};
685             }
686              
687             sub print_command {
688 23 50   23   94 return unless $app->command;
689 23         151 my $line = shift;
690 23 100       61 if ($app->color) {
691 22         163 $line = color($colormap{COMMAND}, $line);
692             }
693 23         5564 print $line;
694             }
695              
696             sub compare {
697 43     43   402 my($old, $new) = splice @_, 0, 2;
698 43 100 33     410 return 0 unless $old && $new && $$old && $$new;
      66        
      100        
699              
700             # skip word comparison for lines exceeding max length
701 25 50       131 if (my $max = $app->limit->{length}) {
702 25 100 66     563 return 0 if $$old =~ /^.{$max}/m or $$new =~ /^.{$max}/m;
703             }
704              
705 24   50     61 my $omark_re = shift || undef;
706 24   66     65 my $nmark_re = shift || $omark_re;
707              
708 24         29 my(@omark, @nmark);
709 24 50       53 if ($omark_re) {
710 24         239 $$old =~ s/^($omark_re)/push(@omark, $1), ""/mge;
  32         171  
711 24         193 $$new =~ s/^($nmark_re)/push(@nmark, $1), ""/mge;
  32         121  
712             }
713              
714 24         103 ($$old, $$new) = context($$old, $$new);
715              
716 24 50       151 $$old =~ s/^/shift @omark/mge if @omark;
  32         156  
717 24 50       109 $$new =~ s/^/shift @nmark/mge if @nmark;
  32         105  
718              
719 24         127 1;
720             }
721              
722 9     9   68 use Getopt::EX::Colormap qw(colorize);
  9         13  
  9         50824  
723              
724             sub debug_list {
725 0     0   0 my $i = 0;
726 0         0 my @cmap = qw( K/444 K/333 );
727 0         0 join "", map { colorize $cmap[$i++ % @cmap], $_ } @_;
  0         0  
728             }
729              
730             sub context {
731 24     24   58 my($old, $new) = @_;
732 24         31 local $_;
733              
734 24 50       73 if ($debug{s}) {
735 0         0 print STDERR "****************************** Comparing ...\n";
736 0         0 print STDERR $old;
737 0         0 print STDERR "****************************** and\n";
738 0         0 print STDERR $new;
739 0         0 print STDERR "****************************** .\n";
740             }
741              
742 24         75 my @owlist = wordlist($old);
743 24         52 my @nwlist = wordlist($new);
744              
745 24 50       67 if ($debug{w}) {
746 0         0 print STDERR "****************************** Comparing ...\n";
747 0         0 print STDERR debug_list @owlist;
748 0         0 print STDERR "****************************** and\n";
749 0         0 print STDERR debug_list @nwlist;
750 0         0 print STDERR "****************************** .\n";
751             }
752              
753 24         97 maketmp($T1, @owlist);
754 24         1634 maketmp($T2, @nwlist);
755              
756 24         1579 my $diff = sprintf "$app->{subdiff} @sub_diffopts %s %s", $T1->path, $T2->path;
757 24 50       101481 open my $cdif, "$diff |" or die "diff: $!\n";
758 24         1670 binmode $cdif, ":encoding(utf8)";
759 24         2982 my @command;
760 24         426 my %c = (a => 0, d => 0, c => 0);
761 24         22254 while (<$cdif>) {
762 320 50       1066 warn $_ if $debug{d};
763              
764             ## Quick hack to read `git diff` unified output
765 320 50       535 if (/^\@\@ \s+ \-(\d+)(?:,(\d+))? \s+ \+(\d+)(?:,(\d+))? \s+ \@\@/x) {
766 0   0     0 my($o, $ol, $n, $nl) = ($1, $2//1, $3, $4//1);
      0        
767 0 0       0 my $cmd = ($ol == 0) ? "a" : ($nl == 0) ? "d" : "c";
    0          
768 0 0       0 my $old = ($ol <= 1) ? $o : sprintf "%d,%d", $o, $o + $ol - 1;
769 0 0       0 my $new = ($nl <= 1) ? $n : sprintf "%d,%d", $n, $n + $nl - 1;
770 0         0 $_ = sprintf "%s%s%s\n", $old, $cmd, $new;
771             }
772              
773 320 100       1319 if (/^[\d,]+([adc])[\d,]+$/) {
774 48         181 push @command, $_;
775 48         392 $c{$1}++;
776             }
777             }
778 24         1397 close $cdif;
779              
780 24 50       184 if ($debug{d}) {
781 0         0 printf(STDERR "old=%d new=%d command=%d\n",
782             @owlist+0, @nwlist+0, @command+0);
783 0         0 printf(STDERR "append=$c{a} delete=$c{d} change=$c{c}\n");
784             }
785              
786 24         286 my($obuf, $nbuf) = makebuf(\@owlist, \@nwlist, \@command);
787 24         238 my $status = $?>>8;
788 24 50       71 die "Unexpected status of subprocess ($status)\n" if $status > 1;
789              
790 24         633 ($obuf, $nbuf);
791             }
792              
793             sub wordlist {
794 48     48   70 my $text = shift;
795 48 50 33     99 if ($app->unit eq 'mecab' and $text =~ /\P{ASCII}/) {
796 0         0 mecab_words($text);
797             } else {
798 48         268 normal_words($text);
799             }
800             }
801              
802             sub normal_words {
803 48     48   54 my $text = shift;
804 48         77 my @words;
805 48         1700 while ($text =~ /\G($w_pattern)/g) {
806 768         2715 push @words, $1;
807             }
808 48         361 @words;
809             }
810              
811             sub mecab_words {
812 0     0   0 my $text = shift;
813 0         0 state $mecab = App::cdif::Command::Mecab->new;
814 0         0 $mecab->wordlist($text);
815             }
816              
817             sub maketmp {
818 48     48   240 my($tmpfile, @list) = @_;
819 48         294 $tmpfile->reset;
820 48         3978 for (@list) {
821 768 50 33     7541 s/[ \t]+// if $app->ignore_space_change || $app->ignore_all_space;
822 768         5308 $tmpfile->write($_);
823 768 100       9179 $tmpfile->write("\n") unless /\n\z/;
824             }
825 48         97 $tmpfile->flush->rewind;
826             }
827              
828             ##
829             ## @owlist: old word list
830             ## @nwlist: new word list
831             ## @command: how different these lists (`diff' result command lines)
832             ##
833             sub makebuf {
834 24     24   182 my($ol, $nl, $c) = @_;
835 24         404 my @owlist = @$ol;
836 24         274 my @nwlist = @$nl;
837 24         79 my @command = @$c;
838              
839 24         71 my($o, $n) = (0, 0); # pointers
840 24         58 my(@obuf, @nbuf);
841              
842 24         66 for (@command) {
843 48 50       456 my($old, $cmd, $new) = /([\d,]+)([adc])([\d,]+)/ or do {
844 0         0 die "Panic! Unexpected diff output";
845             };
846 48         411 my($o1, $o2) = range($old);
847 48         97 my($n1, $n2) = range($new);
848 48         152 map { $_-- } $o1, $o2, $n1, $n2; # make them zero origined
  192         303  
849              
850 48 100       212 push(@obuf, @owlist[$o .. $o1 - 1]), $o = $o1 if $o < $o1;
851 48 100       218 push(@nbuf, @nwlist[$n .. $n1 - 1]), $n = $n1 if $n < $n1;
852              
853 48         158 $stat{$cmd}++;
854              
855 48 50       200 if ($cmd eq 'd') {
    50          
    0          
856 0         0 my $os = join('', @owlist[$o1 .. $o2]);
857 0 0       0 if ($owlist[$o2] =~ /\S/) {
858 0         0 $stat{'dnl'}++;
859 0         0 $stat{'dnlb'} += length($os);
860             }
861 0         0 push(@obuf, $func{DELETE}->($os));
862 0         0 $o = $o2 + 1;
863             }
864             elsif ($cmd eq 'c') {
865 48         238 my $os = join('', @owlist[$o1 .. $o2]);
866 48         143 my $ns = join('', @nwlist[$n1 .. $n2]);
867 48 50 33     269 if (($owlist[$o2] =~ /\S/) || ($nwlist[$n2] =~ /\S/)) {
868 48         97 $stat{'cnl'}++;
869 48         147 $stat{'cnlb'} += length($os);
870 48         82 $stat{'cnlb'} += length($ns);
871             }
872 48         231 push(@obuf, $func{OLD}->($os));
873 48         9989 push(@nbuf, $func{NEW}->($ns));
874 48         2346 $o = $o2 + 1;
875 48         124 $n = $n2 + 1;
876             }
877             elsif ($cmd eq 'a') {
878 0         0 my $ns = join('', @nwlist[$n1 .. $n2]);
879 0 0       0 if ($nwlist[$n2] =~ /\S/) {
880 0         0 $stat{'anl'}++;
881 0         0 $stat{'anlb'} += length($ns);
882             }
883 0         0 push(@nbuf, $func{APPEND}->($ns));
884 0         0 $n = $n2 + 1;
885             }
886             }
887 24         125 push(@obuf, @owlist[$o .. $#owlist]);
888 24         144 push(@nbuf, @nwlist[$n .. $#nwlist]);
889              
890 24         234 (join('', @obuf), join('', @nbuf));
891             }
892              
893             sub read_diff {
894 22     22   78 my($FH, $c) = @_;
895 22         81 my @buf = ();
896 22         39 while ($c-- > 0) {
897 30         133 push @buf, scalar <$FH>;
898             }
899 22 50       125 wantarray ? @buf : join '', @buf;
900             }
901              
902             sub read_diffc {
903 4     4   11 my($FH, $n) = @_;
904 4         4 my @buf;
905 4         5 local $_;
906 4         5 my $i = 0;
907 4   66     22 while ($n-- && ($_ = <$FH>)) {
908 56 100       86 $i++ if ($i % 2) != /^!/;
909 56         62 $buf[$i] .= $_;
910 56 50       142 last if /^--- /;
911             }
912 4   50     5 map { $_ // "" } @buf;
  28         76  
913             }
914              
915             sub ul {
916 0     0     local $_ = join '', @_;
917 0           s/(.)/["", "_\010", "__\010\010"]->[vwidth($1)].$1/ge;
  0            
918 0           $_;
919             }
920             sub bd {
921 0     0     local $_ = join '', @_;
922 0           s/(\S)/$1.["", "\010", "\010\010"]->[vwidth($1)].$1/ge;
  0            
923 0           $_;
924             }
925              
926             sub wc_l {
927 0     0     my $file = shift;
928 0           my $line;
929 0           $file->rewind;
930 0           $line++ while $file->fh->getline;
931 0           $file->rewind;
932 0           $line;
933             }
934              
935             sub eval {
936 0 0 0 0     print STDERR &unctrl($_[0]), "\n" x ($_[0] !~ /\n$/) if $_[1] || $debug{e};
937 0           CORE::eval shift;
938 0 0         die sprintf("eval failed in file %s on line %s\n$@", (caller)[1,2]) if $@;
939             }
940              
941             ######################################################################
942              
943             =head1 NAME
944              
945             cdif - word context diff
946              
947             =head1 VERSION
948              
949             Version 4.4501
950              
951             =head1 SYNOPSIS
952              
953             cdif [option] file1 file2
954              
955             cdif [option] [diff-data]
956              
957             Options:
958              
959             -c, -Cn context diff
960             -u, -Un unified diff
961             -i ignore case
962             -b ignore space change
963             -w ignore whitespace
964             -t expand tabs
965              
966             --diff=command specify diff command
967             --subdiff=command specify backend diff command
968             --stat show statistical information
969             --colormap=s specify color map
970             --sdif sdif friendly option
971             --[no]color color or not (default true)
972             --[no]256 ANSI 256 color mode (default true)
973             --[no]cc color command line (default true)
974             --[no]mc color diff mark (default true)
975             --[no]tc color normal text (default true)
976             --[no]uc color unknown text (default true)
977             --[no]old print old text (default true)
978             --[no]new print new text (default true)
979             --[no]mrg print merged text (default true)
980             --[no]command print diff command line (default true)
981             --[no]unknown print unknown line (default true)
982             --[no]mark print mark or not (default true)
983             --[no]prefix read git --graph output (default true)
984             --unit=s word/letter/char/mecab (default word)
985             --[no]mecab use mecab tokenizer (default false)
986             --prefix-pattern prefix pattern
987             --visible char=? set visible attributes
988             --[no]lenience suppress unexpected input warning (default true)
989             --limit length=# skip word comparison for long lines (default 1000)
990             --lxl compare input data line-by-line
991             --style=diff print --lxl output in diff style
992             --version show version
993              
994              
995             =head1 DESCRIPTION
996              
997             B is a post-processor of the Unix diff command. It highlights
998             deleted, changed and added words based on word context (B<--unit=word>
999             by default). If you want to compare text character-by-character, use
1000             option B<--unit=char>. Option B<--unit=mecab> tells to use external
1001             B command as a tokenizer for Japanese text.
1002              
1003             If single or no file is specified, B reads that file or STDIN as
1004             an output from diff command. In addition to normal diff, context
1005             diff, and unified (combined) diff, the L-compatible conflict
1006             marker format is supported as input format.
1007              
1008             Lines that don't look like diff output are simply ignored and
1009             printed.
1010              
1011             =head2 STARTUP and MODULE
1012              
1013             B utilizes Perl L module, and reads I<~/.cdifrc>
1014             file if available when starting up. You can define original and
1015             default option there. The next line enables B<--mecab> option and adds
1016             crossed-out effect for deleted words.
1017              
1018             option default --mecab --cm DELETE=+X
1019              
1020             Modules under B can be loaded by B<-M> option without
1021             prefix. The next command loads B module.
1022              
1023             $ cdif -Mcolors
1024              
1025             You can also define options in module file. Read `perldoc
1026             Getopt::EX::Module` for detail.
1027              
1028             =head2 COLOR
1029              
1030             Each line is displayed in different colors. Each text segment has
1031             own labels, and color for them can be specified by B<--colormap>
1032             option. Read `perldoc Getopt::EX::Colormap` for detail.
1033              
1034             Standard module B<-Mcolors> is loaded by default, and defines several
1035             color maps for light and dark screen. If you want to use CMY colors in
1036             dark screen, place next line in your F<~/.cdifrc>.
1037              
1038             option default --dark-cmy
1039              
1040             Option B<--autocolor> is defined in B module to call
1041             L module. It sets B<--light> or B<--dark>
1042             option according to the brightness of the terminal screen. You can
1043             set preferred color in your F<~/.cdifrc> like:
1044              
1045             option --light --cmy
1046             option --dark --dark-cmy
1047              
1048             Automatic setting is done by L module and it
1049             works with macOS Terminal.app and iTerm.app, and other XTerm
1050             compatible terminals. This module accepts environment variable
1051             L as a terminal background color. For example, use
1052             C<000> or C<#000000> for black and C<555> or C<#FFFFFF> for white.
1053              
1054             Option B<--autocolor> is set by default, so override it to do nothing
1055             to disable.
1056              
1057             option --autocolor --nop
1058              
1059             =head2 EXIT STATUS
1060              
1061             B always exits with status zero unless an error occurs.
1062              
1063             =head1 OPTIONS
1064              
1065             =over 7
1066              
1067             =item B<->[B]
1068              
1069             Almost same as B command.
1070              
1071             =begin deprecated
1072              
1073             =item B<--rcs>, B<-r>I, B<-q>
1074              
1075             Use rcsdiff instead of normal diff. Option B<--rcs> is not required
1076             when B<-r>I is supplied.
1077              
1078             =end deprecated
1079              
1080             =item B<-->B=[C,C,C,C,C<0>,C<>]
1081              
1082             =item B<-->B=[C,C,C,C,C<0>,C<>]
1083              
1084             Specify the comparing unit. Default is I and compare each line
1085             word-by-word. Specify C if you want to compare them
1086             character-by-character. Unit C is almost same as C but
1087             does not include underscore.
1088              
1089             When C is given as an unit, B command is called as a
1090             tokenizer for non-ASCII text. ASCII text is compared word-by-word.
1091             External B command has to be installed.
1092              
1093             If you give empty string like C<--unit=>, or C<0>, B does not
1094             compare text in any way. You'll still get colorization effect.
1095              
1096             =item B<--mecab>
1097              
1098             Shortcut for B<--unit=mecab>.
1099              
1100             =item B<--diff>=I
1101              
1102             Specify the diff command to use.
1103              
1104             =item B<--subdiff>=I
1105              
1106             Specify the backend diff command to get word differences. Accept
1107             normal and unified diff format.
1108              
1109             If you want to use B command, don't forget to set I<-U0>
1110             option.
1111              
1112             --subdiff="git diff -U0 --no-index --histogram"
1113              
1114             =item B<-->[B]B
1115              
1116             Use ANSI color escape sequence for output.
1117              
1118             =item B<--colormap>=I, B<--cm>=I
1119              
1120             Basic I format is :
1121              
1122             FIELD=COLOR
1123              
1124             where the FIELD is one from these :
1125              
1126             COMMAND Command line
1127             OMARK Old mark
1128             NMARK New mark
1129             UTEXT Same text
1130             OTEXT Old text
1131             NTEXT New text
1132             OCHANGE Old change part
1133             NCHANGE New change part
1134             APPEND Appended part
1135             DELETE Deleted part
1136              
1137             =for comment
1138             VISIBLE Visualized invisible chars
1139              
1140             and additional I and I FIELDs for git-diff combined
1141             format.
1142              
1143             CMARK Common mark
1144             CTEXT Common text
1145             MMARK Merged mark
1146             MTEXT Merged text
1147              
1148             You can make multiple fields same color joining them by = :
1149              
1150             FIELD1=FIELD2=...=COLOR
1151              
1152             Also wildcard can be used for field name :
1153              
1154             *CHANGE=BDw
1155              
1156             Multiple fields can be specified by repeating options
1157              
1158             --cm FIELD1=COLOR1 --cm FIELD2=COLOR2 ...
1159              
1160             or combined with comma (,) :
1161              
1162             --cm FIELD1=COLOR1,FIELD2=COLOR2, ...
1163              
1164             Color specification is a combination of single uppercase character
1165             representing 8 colors :
1166              
1167             R Red
1168             G Green
1169             B Blue
1170             C Cyan
1171             M Magenta
1172             Y Yellow
1173             K Black
1174             W White
1175              
1176             and alternative (usually brighter) colors in lowercase :
1177              
1178             r, g, b, c, m, y, k, w
1179              
1180             or RGB values and 24 grey levels if using ANSI 256 or full color
1181             terminal :
1182              
1183             (255,255,255) : 24bit decimal RGB colors
1184             #000000 .. #FFFFFF : 24bit hex RGB colors
1185             #000 .. #FFF : 12bit hex RGB 4096 colors
1186             000 .. 555 : 6x6x6 RGB 216 colors
1187             L00 .. L25 : Black (L00), 24 grey levels, White (L25)
1188              
1189             or color names enclosed by angle bracket :
1190              
1191            
1192            
1193            
1194              
1195             with other special effects :
1196              
1197             D Double-struck (boldface)
1198             I Italic
1199             U Underline
1200             S Stand-out (reverse video)
1201              
1202             Above color spec is simplified summary so if you want complete
1203             information, read L.
1204              
1205             Defaults are :
1206              
1207             COMMAND => "555/222E"
1208             OMARK => "CS"
1209             NMARK => "MS"
1210             UTEXT => ""
1211             OTEXT => "C"
1212             NTEXT => "M"
1213             OCHANGE => "K/445"
1214             NCHANGE => "K/445"
1215             DELETE => "K/544"
1216             APPEND => "K/544"
1217              
1218             CMARK => "GS"
1219             MMARK => "YS"
1220             CTEXT => "G"
1221             MTEXT => "Y"
1222              
1223             This is equivalent to :
1224              
1225             cdif --cm 'COMMAND=555/222E,OMARK=CS,NMARK=MS' \
1226             --cm 'UTEXT=,OTEXT=C,NTEXT=M,*CHANGE=BD/445,DELETE=APPEND=RD/544' \
1227             --cm 'CMARK=GS,MMARK=YS,CTEXT=G,MTEXT=Y'
1228              
1229             =item B<--colormap>=C<&func>
1230              
1231             =item B<--colormap>=C
1232              
1233             You can also set the name of perl subroutine name or definition to be
1234             called handling matched words. Target word is passed as variable
1235             C<$_>, and the return value of the subroutine will be displayed.
1236              
1237             Next option produces L-like formatted output.
1238              
1239             --cm '*'= \
1240             --cm DELETE=OCHANGE='sub{"[-$_-]"}' \
1241             --cm APPEND=NCHANGE='sub{"{+$_+}"}'
1242              
1243             See L for detail.
1244              
1245             =item B<-->[B]B, B<-->[B]B
1246              
1247             =item B<-->[B]B, B<-->[B]B
1248              
1249             =item B<-->[B]B, B<-->[B]B
1250              
1251             =item B<-->[B]B, B<-->[B]B
1252              
1253             Enable/Disable using color for the corresponding field.
1254              
1255             =item B<--sdif>
1256              
1257             Disable options appropriate to use for B's input:
1258             B<--commandcolor>, B<--markcolor>, B<--textcolor> and
1259             B<--unknowncolor>.
1260              
1261             =item B<-->[B]B, B<-->[B]B, B<-->[B]B
1262              
1263             Print or not old/new/mrg text in diff output.
1264              
1265             =item B<-->[B]B
1266              
1267             Print or not command lines preceding diff output.
1268              
1269             =item B<-->[B]B
1270              
1271             Print or not lines that do not look like diff output.
1272              
1273             =item B<-->[B]B
1274              
1275             Print or not marks at the top of diff output lines. At this point,
1276             this option is effective only for unified diff.
1277              
1278             The next example produces output exactly the same as I except for
1279             visual effects.
1280              
1281             cdif -U100 --no-mark --no-old --no-command --no-unknown old new
1282              
1283             These options are prepared for watchdiff(1) command.
1284              
1285             =item B<-->[B]B
1286              
1287             Understand prefix for diff output including B B<--graph> option.
1288             True by default.
1289              
1290             =item B<--prefix-pattern>=I
1291              
1292             Specify prefix pattern in regex. Default pattern is:
1293              
1294             (?:\| )*(?: )*
1295              
1296             This pattern matches B graph style and whitespace indented diff
1297             output.
1298              
1299             =item B<--visible> I=[0,1]
1300              
1301             Set visible attribute for specified characters. Visible character is
1302             converted to corresponding Unicode symbol character. Default visible:
1303             nul, bel, bs, vt, np, cr, esc, del, and all non-breaking/special
1304             spaces. Default invisible: ht, nl, sp.
1305              
1306             NAME CODE Unicode NAME DEFAULT
1307             ------ ------ -------------------------------- -------
1308             nul \000 SYMBOL FOR NULL YES
1309             soh \001 SYMBOL FOR SOH YES
1310             bel \007 SYMBOL FOR BELL YES
1311             bs \010 SYMBOL FOR BACKSPACE YES
1312             ht \011 SYMBOL FOR HORIZONTAL TABULATION NO
1313             nl \012 SYMBOL FOR NEWLINE NO
1314             vt \013 SYMBOL FOR VERTICAL TABULATION YES
1315             np \014 SYMBOL FOR FORM FEED YES
1316             cr \015 SYMBOL FOR CARRIAGE RETURN YES
1317             esc \033 SYMBOL FOR ESCAPE YES
1318             sp \040 SYMBOL FOR SPACE NO
1319             del \177 SYMBOL FOR DELETE YES
1320             nbsp \240 OPEN BOX (No-Break Space) YES
1321             nnbsp U+202F OPEN BOX (Narrow No-Break Space) YES
1322             ensp U+2002 OPEN BOX (En Space) YES
1323             emsp U+2003 OPEN BOX (Em Space) YES
1324             thinsp U+2009 OPEN BOX (Thin Space) YES
1325             hairsp U+200A OPEN BOX (Hair Space) YES
1326             zwsp U+200B OPEN BOX (Zero Width Space) YES
1327             idesp U+3000 OPEN BOX (Ideographic Space) YES
1328              
1329             Since there are no dedicated Unicode symbols for nbsp and other
1330             special space characters, OPEN BOX is used instead.
1331              
1332             Multiple characters can be specified at once, by assembling them by
1333             comma (C<,>) like C<--visible ht=1,sp=1>; or connecting them by equal
1334             sign (C<=>) like C<--visible ht=sp=1>. Character names accept
1335             wildcard; C<--visible '*=1'>.
1336              
1337             =begin comment
1338              
1339             Colormap label C is applied to those characters. Default
1340             setting is C, and visible characters are displayed in reverse
1341             video. Unlike other colormaps, only special effects can be set to
1342             this label. Effect C (double-struck) is exception (See
1343             L).
1344              
1345             =end comment
1346              
1347             B command also supports B<--visible> option for horizontal tab
1348             with better visibility.
1349              
1350             =begin deprecated
1351              
1352             =item B<-->[B]B
1353              
1354             =item B<-->[B]B
1355              
1356             Set CARRIAGE-RETURN and ESCAPE visible attributes. These options will
1357             be deprecated soon. Use B<--visible> option instead.
1358              
1359             =end deprecated
1360              
1361             =item B<--stat>
1362              
1363             Print statistical information at the end of output. It shows number
1364             of total appended/deleted/changed words in the context of cdif. It's
1365             common to have many insertions and deletions of newlines because of
1366             text filling process. So normal information is followed by modified
1367             number which ignores insert/delete newlines.
1368              
1369             =item B<-->[B]B
1370              
1371             Suppress warning message for unexpected input from diff command. True
1372             by default.
1373              
1374             =item B<--limit> I=I
1375              
1376             Set resource limits. Available keys are:
1377              
1378             =over 4
1379              
1380             =item B=I<#>
1381              
1382             Skip word comparison for lines longer than I<#> characters. Default
1383             is 1000. Extremely long lines, such as those found in SVG data, can
1384             make word-level diff processing very slow with no useful result. When
1385             this limit is exceeded, the section is still displayed with basic
1386             colorization but without fine-grained word highlighting. Set to 0 to
1387             disable this limit.
1388              
1389             =back
1390              
1391             =item B<--linebyline>, B<--lxl>
1392              
1393             =item B<--style>=I