File Coverage

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