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   33110 use v5.14;
  9         24  
12 9     9   32 use warnings;
  9         9  
  9         438  
13              
14 9     9   3200 use utf8;
  9         1990  
  9         40  
15 9     9   238 use Carp;
  9         27  
  9         624  
16 9     9   4105 use Encode;
  9         120814  
  9         656  
17 9     9   3669 use Encode::Guess;
  9         28451  
  9         24  
18              
19 9     9   4607 use Pod::Usage;
  9         365079  
  9         1246  
20 9     9   79 use List::Util qw(first sum pairmap);
  9         25  
  9         1064  
21 9     9   3707 use Text::ParseWords qw(shellwords);
  9         11070  
  9         513  
22 9     9   3271 use IO::Divert;
  9         5445  
  9         309  
23 9     9   13606 use Text::VisualWidth::PP qw(vwidth);
  9         30609  
  9         560  
24 9     9   4418 use Data::Dumper; {
  9         58296  
  9         569  
25 9     9   1255568 no warnings 'redefine';
  9         48  
  9         11  
  9         819  
26 9     0   83 *Data::Dumper::qquote = sub { qq["${\(shift)}"] };
  0         0  
  0         0  
27 9         19 $Data::Dumper::Terse = 1;
28 9         21 $Data::Dumper::Sortkeys = 1;
29 9         19 $Data::Dumper::Useperl = 1;
30             }
31              
32 9     9   3510 use App::sdif;
  9         17  
  9         522  
33 9         25 our $VERSION = $App::sdif::VERSION;
34              
35 9     9   3238 use App::sdif::Util;
  9         22  
  9         863  
36 9     9   3184 use App::cdif::Command::Mecab;
  9         21  
  9         332  
37              
38             ##
39             ## options
40             ##
41              
42 9     9   3574 use charnames ':full';
  9         61435  
  9         40  
43 9         323 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         111 my %opt_visible = do {
66 9         99 map { $_ => splice @{$visible{$_}}, 0, 1 }
  180         187  
  180         232  
67             keys %visible;
68             };
69              
70 9         38 my @rcsopt;
71             my @diffopts;
72 9         0 my @sub_diffopts;
73              
74 9     9   272 binmode STDOUT, ":encoding(utf8)";
  9         5774  
  9         124  
  9         46  
75 9         7745 binmode STDERR, ":encoding(utf8)";
76              
77 9 50       196 if (my $env = $ENV{'CDIFOPTS'}) {
78 0         0 unshift(@ARGV, shellwords($env));
79             }
80              
81 9         14 my $unit_default = 'word';
82 9         13 my $prefix_default = q/(?:\\| )*(?: )*/;
83              
84 9         13 my $app;
85              
86 9     9   183462 use Getopt::EX::Hashed; {
  9         54985  
  9         46  
87              
88 9         13 Getopt::EX::Hashed->configure( DEFAULT => [ is => 'rw' ] ) ;
  9         117  
89              
90 9         305 has help => ' ' ;
91 9         465 has version => ' ' ;
92 9         305 has man => ' ' ;
93 9         279 has debug => ' d =s@ ' , default => [] ;
94 9         288 has diff => ' =s ' ;
95 9         286 has subdiff => ' =s ' , default => 'diff' ;
96 9         274 has color => ' ! ' , default => 1 ;
97 9         311 has 256 => ' ! ' , default => 1 ;
98 9         284 has commandcolor => ' cc ! ' , default => 1 ;
99 9         283 has markcolor => ' mc ! ' , default => 1 ;
100 9         347 has textcolor => ' tc ! ' , default => 1 ;
101 9         317 has unknowncolor => ' uc ! ' , default => 1 ;
102 9         338 has colormap => ' cm =s@ ' , default => [] ;
103 9         332 has colordump => ' ' ;
104 9         320 has sdif => ' ' ;
105 9         313 has stat => ' ! ' ;
106 9         343 has unit => ' by :s ' , default => $unit_default ,
107             , any => [ qw(char word letter mecab 0), '' ];
108 9         347 has show_old => ' ! ' , default => 1 , alias => 'old' ;
109 9         348 has show_new => ' ! ' , default => 1 , alias => 'new' ;
110 9         350 has show_mrg => ' ! ' , default => 1 , alias => 'mrg' ;
111 9         367 has command => ' ! ' , default => 1 ;
112 9         381 has unknown => ' ! ' , default => 1 ;
113 9         441 has mark => ' ! ' , default => 1 ;
114 9         436 has prefix => ' ! ' , default => 1 ;
115 9         436 has prefix_pattern => ' =s ' , default => $prefix_default ;
116 9         397 has visible => ' =s@ ' , default => [] ;
117 9         445 has lenience => ' ! ' , default => 1 ;
118 9         423 has limit => ' =s% ' , default => { length => 1000 } ;
119 9         401 has linebyline => ' :2 ' , alias => 'lxl';
120 9         441 has style => ' =s ' , default => '' ;
121              
122 9         419 has ignore_case => 'i' ;
123 9         459 has ignore_space_change => 'b' ;
124 9         431 has ignore_all_space => 'w' ;
125 9         447 has expand_tabs => 't' ;
126              
127 9         482 has rcs => '' ;
128              
129             has '+sdif' => sub {
130 4     4   35663 $app->commandcolor = 0;
131 4         26 $app->markcolor = 0;
132 4         15 $app->textcolor = 0;
133 4         15 $app->unknowncolor = 0;
134 9         476 } ;
135             has mecab => '!', action => sub {
136 0 0   0   0 $app->unit = $_[1] ? 'mecab' : $unit_default;
137 9         348 } ;
138 9     0   491 has visible_cr => ' vcr ! ' , action => sub { $opt_visible{cr} = $_[1] } ;
  0         0  
139 9     0   478 has visible_esc => ' vesc ! ' , action => sub { $opt_visible{esc} = $_[1] } ;
  0         0  
140 9     0   556 has c => ' ' , action => sub { push @diffopts, "-c" } ;
  0         0  
141 9     0   581 has u => ' ' , action => sub { push @diffopts, "-u" } ;
  0         0  
142 9     0   649 has context => ' C =i ' , action => sub { push @diffopts, "-C" . $_[1] } ;
  0         0  
143 9     0   548 has unified => ' U =i ' , action => sub { push @diffopts, "-U" . $_[1] } ;
  0         0  
144 9     0   497 has r => ' =s ' , action => sub { push @rcsopt, "-r$_[1]" } ;
  0         0  
145 9     0   543 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         524 };
152             has '+man' => sub {
153 0     0   0 pod2usage -verbose => 2;
154 9         266 };
155             has '+version' => sub {
156 0     0   0 print "$VERSION\n";
157 0         0 exit;
158 9         235 };
159              
160 9     9   5950 } no Getopt::EX::Hashed;
  9         11  
  9         32  
161              
162 9 50       228 $app = Getopt::EX::Hashed->new() or die;
163              
164 9     9   4359 use Getopt::EX::Long qw(:DEFAULT Configure ExConfigure);
  9         639477  
  9         4284  
165 9         9867 ExConfigure BASECLASS => [ "App::cdif", "Getopt::EX" ];
166 9         286 Configure("bundling");
167 9 50       347 $app->getopt || usage({status => 1});
168              
169 9         45688 my %debug = map { $_ => 1 } map { split // } @{$app->debug};
  0         0  
  0         0  
  9         34  
170              
171 9         75 $App::sdif::Util::NO_WARNINGS = $app->lenience;
172 9 50       51 $App::cdif::Command::Mecab::debug = 1 if $debug{m};
173              
174 9 50       23 $app->rcs++ if @rcsopt;
175 9   33     19 my $diff = $app->diff || ($app->rcs ? 'rcsdiff' : 'diff');
176              
177 9         111 push @diffopts, map { $_->[1] } grep { $_->[0] } (
  0         0  
  36         159  
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         40 push @sub_diffopts, map { $_->[1] } grep { $_->[0] } (
  0         0  
  18         86  
185             [ $app->ignore_case, "-i" ],
186             [ $app->ignore_all_space, "-w" ],
187             );
188              
189 9         29 my %colormap = do {
190 9 50       29 my $col = $app->{256} ? 0 : 1;
191 153 100   153   379 pairmap { $a => (ref $b eq 'ARRAY') ? $b->[$col] : $b } (
192 9         127 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   70 use Getopt::EX::Colormap qw(ansi_code);
  9         12  
  9         7050  
213             my $color_handler = Getopt::EX::Colormap
214             ->new(HASH => \%colormap)
215 9         109 ->load_params(@{$app->colormap});
  9         555  
216              
217 9         208 for (
218             [ $app->unknowncolor => q/UNKNOWN/ ],
219             [ $app->commandcolor => q/COMMAND/ ],
220             [ $app->markcolor => q/MARK/ ],
221             [ $app->textcolor => q/TEXT/ ],
222             ) {
223 36         141 my($color, $label) = @$_;
224 36 100       72 $color and next;
225 16         234 for (grep /$label/, keys %colormap) {
226 48         59 $colormap{$_} = '';
227             }
228             }
229              
230 9 50       28 warn 'colormap = ', Dumper \%colormap if $debug{c};
231              
232 9 50       38 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   7841 $color_handler->color(@_);
240             }
241              
242 9         57 my $prefix_re = do {
243 9 50       21 if ($app->prefix) {
244 9         201 qr/$app->{prefix_pattern}/;
245             } else {
246 0         0 "";
247             }
248             };
249              
250 9         28 my $DIFF;
251             my $OLD;
252 9         0 my $NEW;
253              
254 9 50       24 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     114 $DIFF = shift || '-';
262             } else {
263 0 0       0 usage("Arguments error.\n\n") if @ARGV;
264             }
265 9 50       30 warn "DIFF = \"$DIFF\"\n" if $debug{f};
266              
267 9         12 my %func = do {
268 9 100       21 my $col = $app->color ? 0 : 1;
269 45     45   97 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   295 OLD => [ sub { color("OCHANGE", @_) }, \&ul ],
273 48     48   92 NEW => [ sub { color("NCHANGE", @_) }, \&ul ],
274 9     72   137 UNKNOWN => [ sub { color("UNKNOWN", @_) }, undef ],
  72         107  
275             );
276             };
277              
278 9         34 my $w_pattern = do {
279 9 50       24 if ($app->unit =~ /^char/) {
280 0         0 qr/\X/s;
281             } else {
282 9 50       55 my $w = $app->unit eq 'letter' ? '' : '_';
283 9         1503 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         23 my($converter, $effector);
301 9     9   60 use Getopt::EX::LabeledParam;
  9         13  
  9         5027  
302             Getopt::EX::LabeledParam
303             ->new(HASH => \%opt_visible)
304 9         39 ->load_params (@{$app->visible});
  9         233  
305 9 50       245 if (my @names = grep $opt_visible{$_}, keys %opt_visible) {
306 9         100 my @chars = map $visible{$_}->[0], @names;
307 9         55 my %hash = map { @$_ } values %visible;
  180         296  
308 9         18 my $re = do { local $" = ''; qr/[\Q@chars\E]/ };
  9         19  
  9         291  
309 9     0   35 my $sub0 = sub { s/($re)/$hash{$1}/g };
  0         0  
310 9     0   24 my $sub1 = sub { $_[0] =~ s/($re)/$hash{$1}/gr };
  0         0  
311             my $sub2 = sub {
312 81     81   67 my $mark_re = shift;
313 81         116 for (@_) {
314 204   100     265 $_ // next;
315 200         1428 s{^$mark_re\K(?=.*$re)(.*\R?)}{
316 0         0 $sub1->($1);
317             }mge;
318             }
319 9         24 };
320 9         15 $converter = $sub2;
321              
322 9 50       102 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   75 use Command::Run::Tmpfile;
  9         12  
  9         39019  
336 9         189 my $T1 = Command::Run::Tmpfile->new;
337 9         4698 my $T2 = Command::Run::Tmpfile->new;
338              
339             ##
340             ## Total statistic info
341             ##
342 9         1999 my %stat;
343 9         48 @stat{'a', 'd', 'c', 'anl', 'dnl', 'cnl'} = (0, 0, 0, 0, 0, 0);
344 9         34 @stat{'anlb', 'dnlb', 'cnlb'} = (0, 0, 0);
345              
346 9 50       184 open(DIFF, $DIFF) || die "$DIFF: $!\n";
347 9         166 binmode DIFF, ":encoding(utf8)";
348              
349 9         276 my $stdout = IO::Divert->new;
350 59     59   93 sub sprintln { map { s/(?<=[^\n])\z/\n/r } @_ }
  145         1375  
351 59     59   319 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         2383 while () {
362             #
363             # --linebyline, --lxl option
364             #
365 97 50       2248 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         226 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       29 if ($cmd =~ /[cd]/) {
384 8         29 $old = read_diff(*DIFF, scalar(range($left)));
385 8 50       35 $old =~ /^(?!<)/m and die;
386             }
387 10 100       24 if ($cmd =~ /[c]/) {
388 6         11 $del = read_diff(*DIFF, 1);
389 6 50       27 $del =~ /^(?!---)/m and die;
390             }
391 10 100       33 if ($cmd =~ /[ca]/) {
392 8         15 $new = read_diff(*DIFF, scalar(range($right)));
393 8 50       25 $new =~ /^(?!>)/m and die;
394             }
395 10         29 1;
396             }
397 10 50       28 or do {
398 0   0     0 defined and print for ($command_line, $old, $del, $new);
399 0         0 next;
400             };
401              
402 10         37 print_command($command_line);
403              
404 10 50       16 if ($converter) {
405 10         84 $converter->(qr/[<>][ \t]/, $old, $new);
406             }
407              
408 10 100       29 if ($cmd eq 'c') {
409 6 50       18 compare(\$old, \$new, qr/<[ \t]/, qr/>[ \t]/) if $app->unit;
410             }
411              
412 10 50       126 if ($app->color) {
413 10 100       99 $old =~ s{^(<[ \t])(.*)}{
414 12         264 color("OMARK", $1) . color("OTEXT", $2)
415             }mge if $old;
416 10 100       289 $new =~ s{^(>[ \t])(.*)}{
417 12         145 color("NMARK", $1) . color("NTEXT", $2)
418             }mge if $new;
419             }
420              
421 10 100 66     439 println $old if $old and $app->show_old;
422 10 100       28 println $del if $del;
423 10 100 66     95 println $new if $new and $app->show_new;
424             }
425             #
426             # diff -c
427             #
428             elsif (/^\*\*\* ([\d,]+) \*\*\*\*\r?$/) {
429 2         23 my $left = $1;
430 2         7 print_command($_);
431 2         2 my(@old, @new);
432 2         10 my $oline = range($left);
433 2         8 @old = read_diffc(*DIFF, $oline);
434 2         26 my $new;
435 2 50 33     12 if (@old and $old[0] =~ /^--- /) {
436 0         0 $new = shift @old;
437 0         0 @old = ("");
438             } else {
439 2         5 $new = ;
440             }
441 2         2 my $dline = map { /^-/mg } @old;
  14         26  
442 2 50       13 if ($new =~ /^--- ([\d,]+) ----$/) {
443 2         36 my $right = $1;
444 2         7 my $nline = range($right);
445 2 50 33     8 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         10 $converter->(qr/[\-\+\!\ ][ \t]/, @old, @new);
452             }
453 2         6 my $mark_re = qr/![ \t]/;
454 2         7 for my $i (keys @old) {
455 14         334 my $cmark = "! ";
456 14 100       27 if ($i % 2) {
457 6 50       16 compare(\$old[$i], \$new[$i], $mark_re) if $app->unit;
458             }
459 14 50       94 if ($app->color) {
460 14         121 $old[$i] =~ s{^([\-\!][ \t])(.*)}{
461 12         271 color("OMARK", $1) . color("OTEXT", $2)
462             }mge;
463 14         289 $new[$i] =~ s{^([\+\!][ \t])(.*)}{
464 12         145 color("NMARK", $1) . color("NTEXT", $2)
465             }mge;
466             }
467             }
468             }
469 2 50       146 println @old if $app->show_old;
470 2         14 println $new;
471 2 50       67 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         288 my($prefix, $command, $lines) = @+{qw(prefix command lines)};
486 11         51 my $column = length $+{mark};
487             my @lines = map {
488 11 100       67 $_ eq ' ' ? 1 : int $_
  22         66  
489             } $lines =~ /\d+(?|,(\d+)|( ))/g;
490              
491 11 50       32 if (@lines != $column) {
492 0         0 print;
493 0         0 next;
494             }
495              
496 11         20 my($divert, %read_opt);
497 11 100       19 if ($prefix) {
498 8         20 $read_opt{prefix} = $prefix;
499 8     8   68 $divert = IO::Divert->new(FINAL => sub { s/^/$prefix/mg });
  8         439  
500             }
501              
502 11         559 print_command($command);
503              
504 11         56 my @buf = read_unified \%read_opt, *DIFF, @lines;
505              
506 11         37 state @mark_re;
507 11   66     43 my $mark_re = $mark_re[$column] //= do {
508 5         15 my $mark = '.' x ($column - 1);
509 5         99 qr/$mark/;
510             };
511              
512 11 50       51 if ($converter) {
513 69         98 map { $converter->($mark_re, @$_) }
514 11         21 map { $_->lists }
  31         52  
515             @buf;
516             }
517              
518 11         21 for my $buf (@buf) {
519 31         79 my @result = compare_unified($column, $buf, $mark_re);
520 31 50       72 if (@result == 3) {
521 31 50       81 $app->show_new or splice @result, 2, 1;
522 31 50       149 $app->show_old or splice @result, 1, 1;
523             }
524 31         139 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       1307 if ($app->unknown) {
571 74 100       295 if (my $f = $func{UNKNOWN}) {
572 72         86 print $f->($_);
573             } else {
574 2         6 print;
575             }
576             }
577             }
578             }
579             continue {
580 97         1468 $stdout->fh->flush;
581 97         853 local *_ = $stdout->buffer;
582 97 50       433 if (length) {
583 97         293 $_ = decode 'utf8', $_;
584 97 50       1570 $effector->() if $effector;
585 97         382 STDOUT->printflush($_);
586 97         4606 $stdout->clear;
587             }
588             }
589 9         518 close DIFF;
590 9 50       113 my $exit = $DIFF =~ /\|$/ ? $? >> 8 : 0;
591              
592 9         36 select STDOUT;
593              
594 9 50       124 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         120 exit($exit > 1);
613              
614             ######################################################################
615              
616             sub compare_unified {
617 31     31   37 my $column = shift;
618 31 50       68 goto &compare_unified_3 if $column == 3;
619 31         94 goto &compare_unified_generic;
620             }
621              
622             sub compare_unified_generic {
623 31     31   69 my($buf, $mark_re) = @_;
624 31         282 my $c = $buf->collect(qr/^[\t ]+$/);
625 31         101 my $o = $buf->collect(qr/[-]/);
626 31         82 my $n = $buf->collect(qr/[+]/);
627 31 50       88 compare(\$o, \$n, $mark_re) if $app->unit;
628 31 100       225 if ($app->color) {
629             map {
630 30         227 my($buf, $m, $t) = @$_;
  90         957  
631 90         462 $$buf =~ s{^($mark_re)(.*)}{
632 154 50       2627 ($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         958 ($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   75 return unless $app->command;
689 23         121 my $line = shift;
690 23 100       48 if ($app->color) {
691 22         105 $line = color($colormap{COMMAND}, $line);
692             }
693 23         4238 print $line;
694             }
695              
696             sub compare {
697 43     43   361 my($old, $new) = splice @_, 0, 2;
698 43 100 33     376 return 0 unless $old && $new && $$old && $$new;
      66        
      100        
699              
700             # skip word comparison for lines exceeding max length
701 25 50       98 if (my $max = $app->limit->{length}) {
702 25 100 66     478 return 0 if $$old =~ /^.{$max}/m or $$new =~ /^.{$max}/m;
703             }
704              
705 24   50     56 my $omark_re = shift || undef;
706 24   66     59 my $nmark_re = shift || $omark_re;
707              
708 24         31 my(@omark, @nmark);
709 24 50       39 if ($omark_re) {
710 24         239 $$old =~ s/^($omark_re)/push(@omark, $1), ""/mge;
  32         147  
711 24         209 $$new =~ s/^($nmark_re)/push(@nmark, $1), ""/mge;
  32         97  
712             }
713              
714 24         62 ($$old, $$new) = context($$old, $$new);
715              
716 24 50       167 $$old =~ s/^/shift @omark/mge if @omark;
  32         152  
717 24 50       109 $$new =~ s/^/shift @nmark/mge if @nmark;
  32         72  
718              
719 24         145 1;
720             }
721              
722 9     9   62 use Getopt::EX::Colormap qw(colorize);
  9         12  
  9         44529  
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   42 my($old, $new) = @_;
732 24         28 local $_;
733              
734 24 50       71 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         52 my @owlist = wordlist($old);
743 24         40 my @nwlist = wordlist($new);
744              
745 24 50       48 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         65 maketmp($T1, @owlist);
754 24         1569 maketmp($T2, @nwlist);
755              
756 24         1436 my $diff = sprintf "$app->{subdiff} @sub_diffopts %s %s", $T1->path, $T2->path;
757 24 50       77154 open my $cdif, "$diff |" or die "diff: $!\n";
758 24         1471 binmode $cdif, ":encoding(utf8)";
759 24         2456 my @command;
760 24         323 my %c = (a => 0, d => 0, c => 0);
761 24         19298 while (<$cdif>) {
762 320 50       901 warn $_ if $debug{d};
763              
764             ## Quick hack to read `git diff` unified output
765 320 50       505 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       1125 if (/^[\d,]+([adc])[\d,]+$/) {
774 48         146 push @command, $_;
775 48         396 $c{$1}++;
776             }
777             }
778 24         1146 close $cdif;
779              
780 24 50       152 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         243 my($obuf, $nbuf) = makebuf(\@owlist, \@nwlist, \@command);
787 24         174 my $status = $?>>8;
788 24 50       59 die "Unexpected status of subprocess ($status)\n" if $status > 1;
789              
790 24         627 ($obuf, $nbuf);
791             }
792              
793             sub wordlist {
794 48     48   77 my $text = shift;
795 48 50 33     87 if ($app->unit eq 'mecab' and $text =~ /\P{ASCII}/) {
796 0         0 mecab_words($text);
797             } else {
798 48         236 normal_words($text);
799             }
800             }
801              
802             sub normal_words {
803 48     48   46 my $text = shift;
804 48         50 my @words;
805 48         1468 while ($text =~ /\G($w_pattern)/g) {
806 768         2425 push @words, $1;
807             }
808 48         308 @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   238 my($tmpfile, @list) = @_;
819 48         249 $tmpfile->reset;
820 48         3642 for (@list) {
821 768 50 33     7433 s/[ \t]+// if $app->ignore_space_change || $app->ignore_all_space;
822 768         5193 $tmpfile->write($_);
823 768 100       8942 $tmpfile->write("\n") unless /\n\z/;
824             }
825 48         115 $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   99 my($ol, $nl, $c) = @_;
835 24         396 my @owlist = @$ol;
836 24         239 my @nwlist = @$nl;
837 24         101 my @command = @$c;
838              
839 24         84 my($o, $n) = (0, 0); # pointers
840 24         46 my(@obuf, @nbuf);
841              
842 24         67 for (@command) {
843 48 50       399 my($old, $cmd, $new) = /([\d,]+)([adc])([\d,]+)/ or do {
844 0         0 die "Panic! Unexpected diff output";
845             };
846 48         383 my($o1, $o2) = range($old);
847 48         106 my($n1, $n2) = range($new);
848 48         121 map { $_-- } $o1, $o2, $n1, $n2; # make them zero origined
  192         294  
849              
850 48 100       194 push(@obuf, @owlist[$o .. $o1 - 1]), $o = $o1 if $o < $o1;
851 48 100       157 push(@nbuf, @nwlist[$n .. $n1 - 1]), $n = $n1 if $n < $n1;
852              
853 48         139 $stat{$cmd}++;
854              
855 48 50       161 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         179 my $os = join('', @owlist[$o1 .. $o2]);
866 48         133 my $ns = join('', @nwlist[$n1 .. $n2]);
867 48 50 33     262 if (($owlist[$o2] =~ /\S/) || ($nwlist[$n2] =~ /\S/)) {
868 48         62 $stat{'cnl'}++;
869 48         122 $stat{'cnlb'} += length($os);
870 48         74 $stat{'cnlb'} += length($ns);
871             }
872 48         229 push(@obuf, $func{OLD}->($os));
873 48         9198 push(@nbuf, $func{NEW}->($ns));
874 48         2361 $o = $o2 + 1;
875 48         140 $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         108 push(@obuf, @owlist[$o .. $#owlist]);
888 24         105 push(@nbuf, @nwlist[$n .. $#nwlist]);
889              
890 24         254 (join('', @obuf), join('', @nbuf));
891             }
892              
893             sub read_diff {
894 22     22   64 my($FH, $c) = @_;
895 22         62 my @buf = ();
896 22         32 while ($c-- > 0) {
897 30         92 push @buf, scalar <$FH>;
898             }
899 22 50       69 wantarray ? @buf : join '', @buf;
900             }
901              
902             sub read_diffc {
903 4     4   10 my($FH, $n) = @_;
904 4         4 my @buf;
905 4         4 local $_;
906 4         4 my $i = 0;
907 4   66     19 while ($n-- && ($_ = <$FH>)) {
908 56 100       86 $i++ if ($i % 2) != /^!/;
909 56         79 $buf[$i] .= $_;
910 56 50       154 last if /^--- /;
911             }
912 4   50     6 map { $_ // "" } @buf;
  28         45  
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.46
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