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   35841 use v5.14;
  9         23  
12 9     9   32 use warnings;
  9         10  
  9         452  
13              
14 9     9   3515 use utf8;
  9         2089  
  9         43  
15 9     9   248 use Carp;
  9         27  
  9         802  
16 9     9   4167 use Encode;
  9         125484  
  9         710  
17 9     9   3744 use Encode::Guess;
  9         30426  
  9         25  
18              
19 9     9   4901 use Pod::Usage;
  9         384281  
  9         1233  
20 9     9   105 use List::Util qw(first sum pairmap);
  9         25  
  9         1198  
21 9     9   4392 use Text::ParseWords qw(shellwords);
  9         12021  
  9         536  
22 9     9   3698 use IO::Divert;
  9         5808  
  9         334  
23 9     9   14396 use Text::VisualWidth::PP qw(vwidth);
  9         29612  
  9         573  
24 9     9   4429 use Data::Dumper; {
  9         59519  
  9         659  
25 9     9   1318462 no warnings 'redefine';
  9         59  
  9         11  
  9         833  
26 9     0   103 *Data::Dumper::qquote = sub { qq["${\(shift)}"] };
  0         0  
  0         0  
27 9         25 $Data::Dumper::Terse = 1;
28 9         21 $Data::Dumper::Sortkeys = 1;
29 9         21 $Data::Dumper::Useperl = 1;
30             }
31              
32 9     9   4237 use App::sdif;
  9         20  
  9         533  
33 9         26 our $VERSION = $App::sdif::VERSION;
34              
35 9     9   3643 use App::sdif::Util;
  9         24  
  9         1038  
36 9     9   3646 use App::cdif::Command::Mecab;
  9         27  
  9         381  
37              
38             ##
39             ## options
40             ##
41              
42 9     9   3910 use charnames ':full';
  9         66377  
  9         50  
43 9         506 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         117 my %opt_visible = do {
66 9         111 map { $_ => splice @{$visible{$_}}, 0, 1 }
  180         140  
  180         255  
67             keys %visible;
68             };
69              
70 9         37 my @rcsopt;
71             my @diffopts;
72 9         0 my @sub_diffopts;
73              
74 9     9   287 binmode STDOUT, ":encoding(utf8)";
  9         6703  
  9         124  
  9         51  
75 9         8270 binmode STDERR, ":encoding(utf8)";
76              
77 9 50       204 if (my $env = $ENV{'CDIFOPTS'}) {
78 0         0 unshift(@ARGV, shellwords($env));
79             }
80              
81 9         19 my $unit_default = 'word';
82 9         16 my $prefix_default = q/(?:\\| )*(?: )*/;
83              
84 9         12 my $app;
85              
86 9     9   214642 use Getopt::EX::Hashed; {
  9         62277  
  9         53  
87              
88 9         16 Getopt::EX::Hashed->configure( DEFAULT => [ is => 'rw' ] ) ;
  9         105  
89              
90 9         315 has help => ' ' ;
91 9         492 has version => ' ' ;
92 9         377 has man => ' ' ;
93 9         270 has debug => ' d =s@ ' , default => [] ;
94 9         309 has diff => ' =s ' ;
95 9         276 has subdiff => ' =s ' , default => 'diff' ;
96 9         314 has color => ' ! ' , default => 1 ;
97 9         303 has 256 => ' ! ' , default => 1 ;
98 9         313 has commandcolor => ' cc ! ' , default => 1 ;
99 9         323 has markcolor => ' mc ! ' , default => 1 ;
100 9         315 has textcolor => ' tc ! ' , default => 1 ;
101 9         392 has unknowncolor => ' uc ! ' , default => 1 ;
102 9         424 has colormap => ' cm =s@ ' , default => [] ;
103 9         359 has colordump => ' ' ;
104 9         393 has sdif => ' ' ;
105 9         343 has stat => ' ! ' ;
106 9         372 has unit => ' by :s ' , default => $unit_default ,
107             , any => [ qw(char word letter mecab 0), '' ];
108 9         381 has show_old => ' ! ' , default => 1 , alias => 'old' ;
109 9         405 has show_new => ' ! ' , default => 1 , alias => 'new' ;
110 9         402 has show_mrg => ' ! ' , default => 1 , alias => 'mrg' ;
111 9         425 has command => ' ! ' , default => 1 ;
112 9         421 has unknown => ' ! ' , default => 1 ;
113 9         424 has mark => ' ! ' , default => 1 ;
114 9         380 has prefix => ' ! ' , default => 1 ;
115 9         372 has prefix_pattern => ' =s ' , default => $prefix_default ;
116 9         417 has visible => ' =s@ ' , default => [] ;
117 9         451 has lenience => ' ! ' , default => 1 ;
118 9         423 has limit => ' =s% ' , default => { length => 1000 } ;
119 9         432 has linebyline => ' :2 ' , alias => 'lxl';
120 9         418 has style => ' =s ' , default => '' ;
121              
122 9         425 has ignore_case => 'i' ;
123 9         468 has ignore_space_change => 'b' ;
124 9         528 has ignore_all_space => 'w' ;
125 9         538 has expand_tabs => 't' ;
126              
127 9         514 has rcs => '' ;
128              
129             has '+sdif' => sub {
130 4     4   38848 $app->commandcolor = 0;
131 4         30 $app->markcolor = 0;
132 4         21 $app->textcolor = 0;
133 4         17 $app->unknowncolor = 0;
134 9         524 } ;
135             has mecab => '!', action => sub {
136 0 0   0   0 $app->unit = $_[1] ? 'mecab' : $unit_default;
137 9         394 } ;
138 9     0   500 has visible_cr => ' vcr ! ' , action => sub { $opt_visible{cr} = $_[1] } ;
  0         0  
139 9     0   522 has visible_esc => ' vesc ! ' , action => sub { $opt_visible{esc} = $_[1] } ;
  0         0  
140 9     0   546 has c => ' ' , action => sub { push @diffopts, "-c" } ;
  0         0  
141 9     0   557 has u => ' ' , action => sub { push @diffopts, "-u" } ;
  0         0  
142 9     0   641 has context => ' C =i ' , action => sub { push @diffopts, "-C" . $_[1] } ;
  0         0  
143 9     0   510 has unified => ' U =i ' , action => sub { push @diffopts, "-U" . $_[1] } ;
  0         0  
144 9     0   510 has r => ' =s ' , action => sub { push @rcsopt, "-r$_[1]" } ;
  0         0  
145 9     0   579 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         610 };
152             has '+man' => sub {
153 0     0   0 pod2usage -verbose => 2;
154 9         287 };
155             has '+version' => sub {
156 0     0   0 print "$VERSION\n";
157 0         0 exit;
158 9         277 };
159              
160 9     9   7101 } no Getopt::EX::Hashed;
  9         16  
  9         64  
161              
162 9 50       248 $app = Getopt::EX::Hashed->new() or die;
163              
164 9     9   4765 use Getopt::EX::Long qw(:DEFAULT Configure ExConfigure);
  9         689734  
  9         4770  
165 9         11191 ExConfigure BASECLASS => [ "App::cdif", "Getopt::EX" ];
166 9         314 Configure("bundling");
167 9 50       303 $app->getopt || usage({status => 1});
168              
169 9         52078 my %debug = map { $_ => 1 } map { split // } @{$app->debug};
  0         0  
  0         0  
  9         38  
170              
171 9         71 $App::sdif::Util::NO_WARNINGS = $app->lenience;
172 9 50       57 $App::cdif::Command::Mecab::debug = 1 if $debug{m};
173              
174 9 50       26 $app->rcs++ if @rcsopt;
175 9   33     23 my $diff = $app->diff || ($app->rcs ? 'rcsdiff' : 'diff');
176              
177 9         126 push @diffopts, map { $_->[1] } grep { $_->[0] } (
  0         0  
  36         162  
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         33 push @sub_diffopts, map { $_->[1] } grep { $_->[0] } (
  0         0  
  18         66  
185             [ $app->ignore_case, "-i" ],
186             [ $app->ignore_all_space, "-w" ],
187             );
188              
189 9         20 my %colormap = do {
190 9 50       52 my $col = $app->{256} ? 0 : 1;
191 153 100   153   337 pairmap { $a => (ref $b eq 'ARRAY') ? $b->[$col] : $b } (
192 9         167 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   82 use Getopt::EX::Colormap qw(ansi_code);
  9         14  
  9         7599  
213             my $color_handler = Getopt::EX::Colormap
214             ->new(HASH => \%colormap)
215 9         100 ->load_params(@{$app->colormap});
  9         620  
216              
217 9         287 for (
218             [ $app->unknowncolor => q/UNKNOWN/ ],
219             [ $app->commandcolor => q/COMMAND/ ],
220             [ $app->markcolor => q/MARK/ ],
221             [ $app->textcolor => q/TEXT/ ],
222             ) {
223 36         168 my($color, $label) = @$_;
224 36 100       71 $color and next;
225 16         246 for (grep /$label/, keys %colormap) {
226 48         63 $colormap{$_} = '';
227             }
228             }
229              
230 9 50       35 warn 'colormap = ', Dumper \%colormap if $debug{c};
231              
232 9 50       78 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   8607 $color_handler->color(@_);
240             }
241              
242 9         51 my $prefix_re = do {
243 9 50       30 if ($app->prefix) {
244 9         210 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       38 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     117 $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         17 my %func = do {
268 9 100       27 my $col = $app->color ? 0 : 1;
269 45     45   107 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   306 OLD => [ sub { color("OCHANGE", @_) }, \&ul ],
273 48     48   96 NEW => [ sub { color("NCHANGE", @_) }, \&ul ],
274 9     72   171 UNKNOWN => [ sub { color("UNKNOWN", @_) }, undef ],
  72         114  
275             );
276             };
277              
278 9         39 my $w_pattern = do {
279 9 50       33 if ($app->unit =~ /^char/) {
280 0         0 qr/\X/s;
281             } else {
282 9 50       82 my $w = $app->unit eq 'letter' ? '' : '_';
283 9         1784 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         28 my($converter, $effector);
301 9     9   62 use Getopt::EX::LabeledParam;
  9         16  
  9         5046  
302             Getopt::EX::LabeledParam
303             ->new(HASH => \%opt_visible)
304 9         43 ->load_params (@{$app->visible});
  9         223  
305 9 50       268 if (my @names = grep $opt_visible{$_}, keys %opt_visible) {
306 9         160 my @chars = map $visible{$_}->[0], @names;
307 9         26 my %hash = map { @$_ } values %visible;
  180         362  
308 9         22 my $re = do { local $" = ''; qr/[\Q@chars\E]/ };
  9         22  
  9         278  
309 9     0   36 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   81 my $mark_re = shift;
313 81         118 for (@_) {
314 204   100     268 $_ // next;
315 200         1547 s{^$mark_re\K(?=.*$re)(.*\R?)}{
316 0         0 $sub1->($1);
317             }mge;
318             }
319 9         30 };
320 9         16 $converter = $sub2;
321              
322 9 50       66 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   63 use Command::Run::Tmpfile;
  9         15  
  9         41869  
336 9         228 my $T1 = Command::Run::Tmpfile->new;
337 9         4629 my $T2 = Command::Run::Tmpfile->new;
338              
339             ##
340             ## Total statistic info
341             ##
342 9         2327 my %stat;
343 9         62 @stat{'a', 'd', 'c', 'anl', 'dnl', 'cnl'} = (0, 0, 0, 0, 0, 0);
344 9         39 @stat{'anlb', 'dnlb', 'cnlb'} = (0, 0, 0);
345              
346 9 50       275 open(DIFF, $DIFF) || die "$DIFF: $!\n";
347 9         245 binmode DIFF, ":encoding(utf8)";
348              
349 9         381 my $stdout = IO::Divert->new;
350 59     59   93 sub sprintln { map { s/(?<=[^\n])\z/\n/r } @_ }
  145         1320  
351 59     59   453 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         2569 while () {
362             #
363             # --linebyline, --lxl option
364             #
365 97 50       2440 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         174 my($left, $cmd, $right) = ($1, $2, $3);
380 10         13 my $command_line = $_;
381 10         18 my($old, $del, $new);
382             eval {
383 10 100       40 if ($cmd =~ /[cd]/) {
384 8         30 $old = read_diff(*DIFF, scalar(range($left)));
385 8 50       38 $old =~ /^(?!<)/m and die;
386             }
387 10 100       38 if ($cmd =~ /[c]/) {
388 6         14 $del = read_diff(*DIFF, 1);
389 6 50       17 $del =~ /^(?!---)/m and die;
390             }
391 10 100       39 if ($cmd =~ /[ca]/) {
392 8         20 $new = read_diff(*DIFF, scalar(range($right)));
393 8 50       25 $new =~ /^(?!>)/m and die;
394             }
395 10         23 1;
396             }
397 10 50       30 or do {
398 0   0     0 defined and print for ($command_line, $old, $del, $new);
399 0         0 next;
400             };
401              
402 10         40 print_command($command_line);
403              
404 10 50       21 if ($converter) {
405 10         97 $converter->(qr/[<>][ \t]/, $old, $new);
406             }
407              
408 10 100       35 if ($cmd eq 'c') {
409 6 50       18 compare(\$old, \$new, qr/<[ \t]/, qr/>[ \t]/) if $app->unit;
410             }
411              
412 10 50       198 if ($app->color) {
413 10 100       176 $old =~ s{^(<[ \t])(.*)}{
414 12         284 color("OMARK", $1) . color("OTEXT", $2)
415             }mge if $old;
416 10 100       404 $new =~ s{^(>[ \t])(.*)}{
417 12         143 color("NMARK", $1) . color("NTEXT", $2)
418             }mge if $new;
419             }
420              
421 10 100 66     527 println $old if $old and $app->show_old;
422 10 100       115 println $del if $del;
423 10 100 66     55 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         8 print_command($_);
431 2         3 my(@old, @new);
432 2         13 my $oline = range($left);
433 2         11 @old = read_diffc(*DIFF, $oline);
434 2         38 my $new;
435 2 50 33     15 if (@old and $old[0] =~ /^--- /) {
436 0         0 $new = shift @old;
437 0         0 @old = ("");
438             } else {
439 2         6 $new = ;
440             }
441 2         5 my $dline = map { /^-/mg } @old;
  14         35  
442 2 50       49 if ($new =~ /^--- ([\d,]+) ----$/) {
443 2         6 my $right = $1;
444 2         7 my $nline = range($right);
445 2 50 33     10 if (@old == 1 and $old[0] ne "" and $oline - $dline == $nline) {
      33        
446 0         0 @new = ("");
447             } else {
448 2         6 @new = read_diffc(*DIFF, $nline);
449             }
450 2 50       7 if ($converter) {
451 2         15 $converter->(qr/[\-\+\!\ ][ \t]/, @old, @new);
452             }
453 2         9 my $mark_re = qr/![ \t]/;
454 2         7 for my $i (keys @old) {
455 14         346 my $cmark = "! ";
456 14 100       30 if ($i % 2) {
457 6 50       14 compare(\$old[$i], \$new[$i], $mark_re) if $app->unit;
458             }
459 14 50       104 if ($app->color) {
460 14         133 $old[$i] =~ s{^([\-\!][ \t])(.*)}{
461 12         282 color("OMARK", $1) . color("OTEXT", $2)
462             }mge;
463 14         305 $new[$i] =~ s{^([\+\!][ \t])(.*)}{
464 12         162 color("NMARK", $1) . color("NTEXT", $2)
465             }mge;
466             }
467             }
468             }
469 2 50       94 println @old if $app->show_old;
470 2         14 println $new;
471 2 50       16 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         365 my($prefix, $command, $lines) = @+{qw(prefix command lines)};
486 11         54 my $column = length $+{mark};
487             my @lines = map {
488 11 100       71 $_ eq ' ' ? 1 : int $_
  22         99  
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         19 my($divert, %read_opt);
497 11 100       25 if ($prefix) {
498 8         18 $read_opt{prefix} = $prefix;
499 8     8   57 $divert = IO::Divert->new(FINAL => sub { s/^/$prefix/mg });
  8         526  
500             }
501              
502 11         664 print_command($command);
503              
504 11         82 my @buf = read_unified \%read_opt, *DIFF, @lines;
505              
506 11         19 state @mark_re;
507 11   66     36 my $mark_re = $mark_re[$column] //= do {
508 5         34 my $mark = '.' x ($column - 1);
509 5         97 qr/$mark/;
510             };
511              
512 11 50       25 if ($converter) {
513 69         99 map { $converter->($mark_re, @$_) }
514 11         24 map { $_->lists }
  31         55  
515             @buf;
516             }
517              
518 11         22 for my $buf (@buf) {
519 31         89 my @result = compare_unified($column, $buf, $mark_re);
520 31 50       81 if (@result == 3) {
521 31 50       121 $app->show_new or splice @result, 2, 1;
522 31 50       161 $app->show_old or splice @result, 1, 1;
523             }
524 31         157 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       1423 if ($app->unknown) {
571 74 100       374 if (my $f = $func{UNKNOWN}) {
572 72         106 print $f->($_);
573             } else {
574 2         6 print;
575             }
576             }
577             }
578             }
579             continue {
580 97         1587 $stdout->fh->flush;
581 97         910 local *_ = $stdout->buffer;
582 97 50       414 if (length) {
583 97         410 $_ = decode 'utf8', $_;
584 97 50       1662 $effector->() if $effector;
585 97         442 STDOUT->printflush($_);
586 97         5122 $stdout->clear;
587             }
588             }
589 9         508 close DIFF;
590 9 50       111 my $exit = $DIFF =~ /\|$/ ? $? >> 8 : 0;
591              
592 9         36 select STDOUT;
593              
594 9 50       130 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         123 exit($exit > 1);
613              
614             ######################################################################
615              
616             sub compare_unified {
617 31     31   38 my $column = shift;
618 31 50       64 goto &compare_unified_3 if $column == 3;
619 31         109 goto &compare_unified_generic;
620             }
621              
622             sub compare_unified_generic {
623 31     31   65 my($buf, $mark_re) = @_;
624 31         289 my $c = $buf->collect(qr/^[\t ]+$/);
625 31         111 my $o = $buf->collect(qr/[-]/);
626 31         101 my $n = $buf->collect(qr/[+]/);
627 31 50       102 compare(\$o, \$n, $mark_re) if $app->unit;
628 31 100       284 if ($app->color) {
629             map {
630 30         328 my($buf, $m, $t) = @$_;
  90         1047  
631 90         489 $$buf =~ s{^($mark_re)(.*)}{
632 154 50       2827 ($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       5 map { s/^$mark_re//mg } $c, $o, $n if not $app->mark;
  0         0  
639             }
640 31         970 ($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   88 return unless $app->command;
689 23         134 my $line = shift;
690 23 100       53 if ($app->color) {
691 22         133 $line = color($colormap{COMMAND}, $line);
692             }
693 23         4500 print $line;
694             }
695              
696             sub compare {
697 43     43   478 my($old, $new) = splice @_, 0, 2;
698 43 100 33     411 return 0 unless $old && $new && $$old && $$new;
      66        
      100        
699              
700             # skip word comparison for lines exceeding max length
701 25 50       187 if (my $max = $app->limit->{length}) {
702 25 100 66     535 return 0 if $$old =~ /^.{$max}/m or $$new =~ /^.{$max}/m;
703             }
704              
705 24   50     57 my $omark_re = shift || undef;
706 24   66     100 my $nmark_re = shift || $omark_re;
707              
708 24         33 my(@omark, @nmark);
709 24 50       63 if ($omark_re) {
710 24         255 $$old =~ s/^($omark_re)/push(@omark, $1), ""/mge;
  32         154  
711 24         154 $$new =~ s/^($nmark_re)/push(@nmark, $1), ""/mge;
  32         118  
712             }
713              
714 24         91 ($$old, $$new) = context($$old, $$new);
715              
716 24 50       190 $$old =~ s/^/shift @omark/mge if @omark;
  32         162  
717 24 50       128 $$new =~ s/^/shift @nmark/mge if @nmark;
  32         124  
718              
719 24         196 1;
720             }
721              
722 9     9   90 use Getopt::EX::Colormap qw(colorize);
  9         12  
  9         47257  
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   56 my($old, $new) = @_;
732 24         32 local $_;
733              
734 24 50       56 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         65 my @owlist = wordlist($old);
743 24         50 my @nwlist = wordlist($new);
744              
745 24 50       62 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         84 maketmp($T1, @owlist);
754 24         1756 maketmp($T2, @nwlist);
755              
756 24         1756 my $diff = sprintf "$app->{subdiff} @sub_diffopts %s %s", $T1->path, $T2->path;
757 24 50       95647 open my $cdif, "$diff |" or die "diff: $!\n";
758 24         1930 binmode $cdif, ":encoding(utf8)";
759 24         2878 my @command;
760 24         516 my %c = (a => 0, d => 0, c => 0);
761 24         22645 while (<$cdif>) {
762 320 50       868 warn $_ if $debug{d};
763              
764             ## Quick hack to read `git diff` unified output
765 320 50       540 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       1240 if (/^[\d,]+([adc])[\d,]+$/) {
774 48         183 push @command, $_;
775 48         390 $c{$1}++;
776             }
777             }
778 24         1489 close $cdif;
779              
780 24 50       156 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         262 my($obuf, $nbuf) = makebuf(\@owlist, \@nwlist, \@command);
787 24         218 my $status = $?>>8;
788 24 50       85 die "Unexpected status of subprocess ($status)\n" if $status > 1;
789              
790 24         688 ($obuf, $nbuf);
791             }
792              
793             sub wordlist {
794 48     48   61 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         287 normal_words($text);
799             }
800             }
801              
802             sub normal_words {
803 48     48   53 my $text = shift;
804 48         47 my @words;
805 48         1677 while ($text =~ /\G($w_pattern)/g) {
806 768         2485 push @words, $1;
807             }
808 48         335 @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   236 my($tmpfile, @list) = @_;
819 48         257 $tmpfile->reset;
820 48         4168 for (@list) {
821 768 50 33     7654 s/[ \t]+// if $app->ignore_space_change || $app->ignore_all_space;
822 768         5287 $tmpfile->write($_);
823 768 100       9016 $tmpfile->write("\n") unless /\n\z/;
824             }
825 48         105 $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   123 my($ol, $nl, $c) = @_;
835 24         433 my @owlist = @$ol;
836 24         272 my @nwlist = @$nl;
837 24         90 my @command = @$c;
838              
839 24         73 my($o, $n) = (0, 0); # pointers
840 24         45 my(@obuf, @nbuf);
841              
842 24         63 for (@command) {
843 48 50       462 my($old, $cmd, $new) = /([\d,]+)([adc])([\d,]+)/ or do {
844 0         0 die "Panic! Unexpected diff output";
845             };
846 48         380 my($o1, $o2) = range($old);
847 48         155 my($n1, $n2) = range($new);
848 48         172 map { $_-- } $o1, $o2, $n1, $n2; # make them zero origined
  192         326  
849              
850 48 100       240 push(@obuf, @owlist[$o .. $o1 - 1]), $o = $o1 if $o < $o1;
851 48 100       187 push(@nbuf, @nwlist[$n .. $n1 - 1]), $n = $n1 if $n < $n1;
852              
853 48         152 $stat{$cmd}++;
854              
855 48 50       155 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         218 my $os = join('', @owlist[$o1 .. $o2]);
866 48         153 my $ns = join('', @nwlist[$n1 .. $n2]);
867 48 50 33     398 if (($owlist[$o2] =~ /\S/) || ($nwlist[$n2] =~ /\S/)) {
868 48         103 $stat{'cnl'}++;
869 48         147 $stat{'cnlb'} += length($os);
870 48         147 $stat{'cnlb'} += length($ns);
871             }
872 48         279 push(@obuf, $func{OLD}->($os));
873 48         10044 push(@nbuf, $func{NEW}->($ns));
874 48         2243 $o = $o2 + 1;
875 48         135 $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         105 push(@nbuf, @nwlist[$n .. $#nwlist]);
889              
890 24         281 (join('', @obuf), join('', @nbuf));
891             }
892              
893             sub read_diff {
894 22     22   89 my($FH, $c) = @_;
895 22         25 my @buf = ();
896 22         53 while ($c-- > 0) {
897 30         98 push @buf, scalar <$FH>;
898             }
899 22 50       88 wantarray ? @buf : join '', @buf;
900             }
901              
902             sub read_diffc {
903 4     4   13 my($FH, $n) = @_;
904 4         5 my @buf;
905 4         4 local $_;
906 4         7 my $i = 0;
907 4   66     26 while ($n-- && ($_ = <$FH>)) {
908 56 100       117 $i++ if ($i % 2) != /^!/;
909 56         89 $buf[$i] .= $_;
910 56 50       176 last if /^--- /;
911             }
912 4   50     10 map { $_ // "" } @buf;
  28         96  
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.4701
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