File manager - Edit - /home/newsbmcs.com/public_html/static/img/logo/libalgorithm-diff-perl.tar
Back
README 0000644 00000006410 15030517304 0005423 0 ustar 00 This is a module for computing the difference between two files, two strings, or any other two lists of things. It uses an intelligent algorithm similar to (or identical to) the one used by the Unix "diff" program. It is guaranteed to find the *smallest possible* set of differences. This package contains a few parts. Algorithm::Diff is the module that contains several interfaces for which computing the differences betwen two lists. The several "diff" programs also included in this package use Algorithm::Diff to find the differences and then they format the output. Algorithm::Diff also includes some other useful functions such as "LCS", which computes the longest common subsequence of two lists. A::D is suitable for many uses. You can use it for finding the smallest set of differences between two strings, or for computing the most efficient way to update the screen if you were replacing "curses". Algorithm::DiffOld is a previous version of the module which is included primarilly for those wanting to use a custom comparison function rather than a key generating function (and who don't mind the significant performance penalty of perhaps 20-fold). diff.pl implements a "diff" in Perl that is as simple as (was previously) possible so that you can see how it works. The output format is not compatible with regular "diff". It needs to be reimplemented using the OO interface to greatly simplify the code. diffnew.pl implements a "diff" in Perl with full bells and whistles. By Mark-Jason, with code from cdiff.pl included. cdiff.pl implements "diff" that generates real context diffs in either traditional format or GNU unified format. Original contextless "context" diff supplied by Christian Murphy. Modifications to make it into a real full-featured diff with -c and -u options supplied by Amir D. Karger. Yes, you can use this program to generate patches. OTHER RESOURCES "Longest Common Subsequences", at http://www.ics.uci.edu/~eppstein/161/960229.html This code was adapted from the Smalltalk code of Mario Wolczko <mario@wolczko.com>, which is available at ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st THANKS SECTION Thanks to Ned Konz's for rewriting the module to greatly improve performance, for maintaining it over the years, and for readilly handing it over to me so I could plod along with my improvements. (From Ned Konz's earlier versions): Thanks to Mark-Jason Dominus for doing the original Perl version and maintaining it over the last couple of years. Mark-Jason has been a huge contributor to the Perl community and CPAN; it's because of people like him that Perl has become a success. Thanks to Mario Wolczko <mario@wolczko.com> for writing and making publicly available his Smalltalk version of diff, which this Perl version is heavily based on. Thanks to Mike Schilli <m@perlmeister.com> for writing sdiff and traverse_balanced and making them available for the Algorithm::Diff distribution. (From Mark-Jason Dominus' earlier versions): Huge thanks to Amir Karger for adding full context diff supprt to "cdiff.pl", and then for waiting patiently for five months while I let it sit in a closet and didn't release it. Thank you thank you thank you, Amir! Thanks to Christian Murphy for adding the first context diff format support to "cdiff.pl". copyright 0000644 00000003433 15030517304 0006500 0 ustar 00 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Algorithm-Diff Upstream-Contact: Ricardo Signes <rjbs@cpan.org> Source: https://metacpan.org/release/Algorithm-Diff Files: * Copyright: 2000-2004, Ned Konz <perl@bike-nomad.com> Tye McQueen <tyemq@cpan.org> Mark-Jason Dominus <mjd-perl-diff@plover.com> License: Artistic or GPL-1+ Comment: The upstream distribution does not contain an explicit statement of copyright ownership. Pursuant to the Berne Convention for the Protection of Literary and Artistic Works, it is assumed that all content is copyright by its respective authors unless otherwise stated. Files: bin/cdiff.pl bin/diff.pl bin/diffnew.pl Copyright: 1998, M-J. Dominus <mjd-perl-diff@plover.com> License: Artistic or GPL-1+ Files: debian/* Copyright: 2000, 2001, 2002, 2003, Michael Alan Dorman <mdorman@debian.org> 2004, 2005, Florian Weimer <fw@deneb.enyo.de> 2008-2020, gregor herrmann <gregoa@debian.org> 2009, Jonathan Yu <jawnsy@cpan.org> 2010-2015, Salvatore Bonaccorso <carnil@debian.org> 2012-2018, Xavier Guimard <yadd@debian.org> License: Artistic or GPL-1+ License: Artistic This program is free software; you can redistribute it and/or modify it under the terms of the Artistic License, which comes with Perl. . On Debian GNU/Linux systems, the complete text of the Artistic License can be found in `/usr/share/common-licenses/Artistic'. License: GPL-1+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. . On Debian GNU/Linux systems, the complete text of version 1 of the General Public License can be found in `/usr/share/common-licenses/GPL-1'. changelog.Debian.gz 0000644 00000005305 15030517304 0010217 0 ustar 00 � �Y�r�8}�W�f�*vb¤dَ&��'�I�=Y;���"! k�� �dM*��� H]�Ȟ��d �r��iH�Wm���I!��F�^�zi�d��T��?Bc&���(E!���#��¹I^�^I� ��_>���V(�d�M>����ea��|L�1%�������AS[g/a&���o�K>�w��*��&�� nyY+67�v���b" ���Z���P� :���y�F״(� { m���]\q# �t40�H�n�J�8b��{Irǽ����;�6�� ���d't<��^�� {�a?�gi����YI��ʿ�L ?5�䦈!~[r� Dg��$y ^+f���리WN�B�`lt Z���4d��kᖋ� w]^1-����\Ԣ*l�r%J=�GV+��K� ot�x��"�.�y�f��#<x� W�e���+*Go+y�( =���'�^9���5�at�c;�ѳ#�c�T:F�B%D!��Ȏ�І�X��ri{��� R�夂���e�� �4�J��9����>� x�(5�}��Sݨ��D�b���5F��c �m�����؈�Bpc�k��� Ufӵ���kIL'�q3 ���%Ԯ%lr%�h��Z�!��mAlFk���텮�w�𧻓u{:L�n{��6{��~һ�r���qG�z���sm�^R��3Z �3B�O��^Y��h�s�`��|�m�t��� ��0��.~��Ӹ�!S<���q���o04���1嫈�]�~�Լ��.�"|�8��݄��\+��<C �Ս�~.tc`�����a��"�_���gXX��re9���{�u1~�� �~��Z�|>���_:�ο@��|<�d;��{P���tK��)�U��YQ���������?\ps�|���\��j�����T��j��I��ĠV����-�������EH*�c�qŲ����v�Qo�B�Y�t#\/n�$||�T%��`�^sl��5,��7��a�=�J��O�M(����>dt]��� �&� �;��+`��D�k���e��F��a�r��u�0���vo�Rό�mbk�H!%VmY%ٚO�D�|ՐeZ�SA��^�C�9�Ϟ����@x�t}3qº'�d���Z��j�ĭK��![K'ʤ�gR5�H�;~���h�*r:��R�Bԃd�%Ǒ�sn*\�<p�HoPEKim�#F����Ή0�5����е�5\���9��Zd'���@H�Ĝ�*���D�"�C�z��Fyx���z��h�;z�{I����˘�q�BԎwl�Uv� 82!��}��y���h9�>���],�y�c�/K� �����g�ʅj�T�dVK=f��4P!��䈃�<M7�A2�%�A��Q� ;������i �s�_�"�O0���ɱ�r��G�ON��<���zF��A���q���ۉ��V����v��WS�,b���[�S<��0�a2�����>;e}�[ �֟�>��q���۳�0����A�s�Kt5�h#�&)c�2��!U�� -1>pӜ����ֆ��hVx�C�o |�����,Q���l"nkO՞�Ȃnx��l{���ً<l���#۾H1��r`�O��O���\�N��Wd�|�otT�<����* <���%�hzOþ0[C�L��TDI�� �k�W������Ε�G�c��0�Ⱥ�R�� �T^�옥�w����h��)k��Nn�cJ�pژv<�c�[�Dן�Q���̣�����f`˂2����6�s�#�oeF��?���$�����bľ��8����vU��<�)W=���?�i���A[xIZQ�1j,/L�7f��Tޖ=�MdlMܧ�bͶ�/�V�-�K��� ��s��� ߶߲Q�� CjA�M~��(���)N����~ �� �<��G_��Z��!?�ԄT4�v��yb1]4(�9L��Q�*i�]����<=L�����i:��+�.!R��U��6��UΊ�k��>)[������`�̵��]�KI�y�ž @U�ʓ��=ԔCx�0Cx�oB����9��J�������v��ivZ��a!u�ز6Y�P���(��[�����"���6�s���Q�����Z��=<v�Z���������;Eu�ћ�\B���#��[�"���B|)�ۡ�n�8qp��8Q�N|d�Eµ�7aޡ�FR�,B<F��f�1f�E��.M��,Wo*�a��Ҭ�bϜݥ��VP�Sd��7a����i��=M������x��.�)�Ta�uL˪����k\�l���H�ա�to(�q����g�Y0��O��F�w4�4 ��tD �C!���� �l���͓�Hc��e2���M�\�ᗋ@�h%:��#���Bq/�Q��M(]V&̑���e�� �Զ�|?��N��u<j&���'��w�c��f�z~�2:G�p��Y�-\��Y�ǩ @6�����C����l������^K��D�wr������6����#/���g���?�?��N��}(��Z�>�[�D�0����'��=�vW�g���yB�;S�nިX�EY������Ci�������t@�@$�"��W������LS�^���Z>��'����, examples/diff.pl 0000644 00000001721 15030517304 0007626 0 ustar 00 #!/usr/bin/perl # # `Diff' program in Perl # Copyright 1998 M-J. Dominus. (mjd-perl-diff@plover.com) # # This program is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # use Algorithm::Diff qw(diff); bag("Usage: $0 oldfile newfile") unless @ARGV == 2; my ($file1, $file2) = @ARGV; # -f $file1 or bag("$file1: not a regular file"); # -f $file2 or bag("$file2: not a regular file"); -T $file1 or bag("$file1: binary"); -T $file2 or bag("$file2: binary"); open (F1, $file1) or bag("Couldn't open $file1: $!"); open (F2, $file2) or bag("Couldn't open $file2: $!"); chomp(@f1 = <F1>); close F1; chomp(@f2 = <F2>); close F2; $diffs = diff(\@f1, \@f2); exit 0 unless @$diffs; foreach $chunk (@$diffs) { foreach $line (@$chunk) { my ($sign, $lineno, $text) = @$line; printf "%4d$sign %s\n", $lineno+1, $text; } print "--------\n"; } exit 1; sub bag { my $msg = shift; $msg .= "\n"; warn $msg; exit 2; } examples/cdiff.pl 0000644 00000030044 15030517304 0007771 0 ustar 00 #!/usr/bin/perl -w # # `Diff' program in Perl # Copyright 1998 M-J. Dominus. (mjd-perl-diff@plover.com) # # This program is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # Altered to output in `context diff' format (but without context) # September 1998 Christian Murphy (cpm@muc.de) # # Command-line arguments and context lines feature added # September 1998 Amir D. Karger (karger@bead.aecom.yu.edu) # # In this file, "item" usually means "line of text", and "item number" usually # means "line number". But theoretically the code could be used more generally use strict; use Algorithm::Diff qw(diff); use File::stat; use vars qw ($opt_C $opt_c $opt_u $opt_U); use Getopt::Std; my $usage = << "ENDUSAGE"; Usage: $0 [{-c | -u}] [{-C | -U} lines] oldfile newfile -c will do a context diff with 3 lines of context -C will do a context diff with 'lines' lines of context -u will do a unified diff with 3 lines of context -U will do a unified diff with 'lines' lines of context ENDUSAGE getopts('U:C:cu') or bag("$usage"); bag("$usage") unless @ARGV == 2; my ($file1, $file2) = @ARGV; if (defined $opt_C || defined $opt_c) { $opt_c = ""; # -c on if -C given on command line $opt_u = undef; } elsif (defined $opt_U || defined $opt_u) { $opt_u = ""; # -u on if -U given on command line $opt_c = undef; } else { $opt_c = ""; # by default, do context diff, not old diff } my ($char1, $char2); # string to print before file names my $Context_Lines; # lines of context to print if (defined $opt_c) { $Context_Lines = defined $opt_C ? $opt_C : 3; $char1 = '*' x 3; $char2 = '-' x 3; } elsif (defined $opt_u) { $Context_Lines = defined $opt_U ? $opt_U : 3; $char1 = '-' x 3; $char2 = '+' x 3; } # After we've read up to a certain point in each file, the number of items # we've read from each file will differ by $FLD (could be 0) my $File_Length_Difference = 0; open (F1, $file1) or bag("Couldn't open $file1: $!"); open (F2, $file2) or bag("Couldn't open $file2: $!"); my (@f1, @f2); chomp(@f1 = <F1>); close F1; chomp(@f2 = <F2>); close F2; # diff yields lots of pieces, each of which is basically a Block object my $diffs = diff(\@f1, \@f2); exit 0 unless @$diffs; my $st = stat($file1); print "$char1 $file1\t", scalar localtime($st->mtime), "\n"; $st = stat($file2); print "$char2 $file2\t", scalar localtime($st->mtime), "\n"; my ($hunk,$oldhunk); # Loop over hunks. If a hunk overlaps with the last hunk, join them. # Otherwise, print out the old one. foreach my $piece (@$diffs) { $hunk = new Hunk ($piece, $Context_Lines); next unless $oldhunk; if ($hunk->does_overlap($oldhunk)) { $hunk->prepend_hunk($oldhunk); } else { $oldhunk->output_diff(\@f1, \@f2); } } continue { $oldhunk = $hunk; } # print the last hunk $oldhunk->output_diff(\@f1, \@f2); exit 1; # END MAIN PROGRAM sub bag { my $msg = shift; $msg .= "\n"; warn $msg; exit 2; } # Package Hunk. A Hunk is a group of Blocks which overlap because of the # context surrounding each block. (So if we're not using context, every # hunk will contain one block.) { package Hunk; sub new { # Arg1 is output from &LCS::diff (which corresponds to one Block) # Arg2 is the number of items (lines, e.g.,) of context around each block # # This subroutine changes $File_Length_Difference # # Fields in a Hunk: # blocks - a list of Block objects # start - index in file 1 where first block of the hunk starts # end - index in file 1 where last block of the hunk ends # # Variables: # before_diff - how much longer file 2 is than file 1 due to all hunks # until but NOT including this one # after_diff - difference due to all hunks including this one my ($class, $piece, $context_items) = @_; my $block = new Block ($piece); # this modifies $FLD! my $before_diff = $File_Length_Difference; # BEFORE this hunk my $after_diff = $before_diff + $block->{"length_diff"}; $File_Length_Difference += $block->{"length_diff"}; # @remove_array and @insert_array hold the items to insert and remove # Save the start & beginning of each array. If the array doesn't exist # though (e.g., we're only adding items in this block), then figure # out the line number based on the line number of the other file and # the current difference in file lengths my @remove_array = $block->remove; my @insert_array = $block->insert; my ($a1, $a2, $b1, $b2, $start1, $start2, $end1, $end2); $a1 = @remove_array ? $remove_array[0 ]->{"item_no"} : -1; $a2 = @remove_array ? $remove_array[-1]->{"item_no"} : -1; $b1 = @insert_array ? $insert_array[0 ]->{"item_no"} : -1; $b2 = @insert_array ? $insert_array[-1]->{"item_no"} : -1; $start1 = $a1 == -1 ? $b1 - $before_diff : $a1; $end1 = $a2 == -1 ? $b2 - $after_diff : $a2; $start2 = $b1 == -1 ? $a1 + $before_diff : $b1; $end2 = $b2 == -1 ? $a2 + $after_diff : $b2; # At first, a hunk will have just one Block in it my $hunk = { "start1" => $start1, "start2" => $start2, "end1" => $end1, "end2" => $end2, "blocks" => [$block], }; bless $hunk, $class; $hunk->flag_context($context_items); return $hunk; } # Change the "start" and "end" fields to note that context should be added # to this hunk sub flag_context { my ($hunk, $context_items) = @_; return unless $context_items; # no context # add context before my $start1 = $hunk->{"start1"}; my $num_added = $context_items > $start1 ? $start1 : $context_items; $hunk->{"start1"} -= $num_added; $hunk->{"start2"} -= $num_added; # context after my $end1 = $hunk->{"end1"}; $num_added = ($end1+$context_items > $#f1) ? $#f1 - $end1 : $context_items; $hunk->{"end1"} += $num_added; $hunk->{"end2"} += $num_added; } # Is there an overlap between hunk arg0 and old hunk arg1? # Note: if end of old hunk is one less than beginning of second, they overlap sub does_overlap { my ($hunk, $oldhunk) = @_; return "" unless $oldhunk; # first time through, $oldhunk is empty # Do I actually need to test both? return ($hunk->{"start1"} - $oldhunk->{"end1"} <= 1 || $hunk->{"start2"} - $oldhunk->{"end2"} <= 1); } # Prepend hunk arg1 to hunk arg0 # Note that arg1 isn't updated! Only arg0 is. sub prepend_hunk { my ($hunk, $oldhunk) = @_; $hunk->{"start1"} = $oldhunk->{"start1"}; $hunk->{"start2"} = $oldhunk->{"start2"}; unshift (@{$hunk->{"blocks"}}, @{$oldhunk->{"blocks"}}); } # DIFF OUTPUT ROUTINES. THESE ROUTINES CONTAIN DIFF FORMATTING INFO... sub output_diff { if (defined $main::opt_u) {&output_unified_diff(@_)} elsif (defined $main::opt_c) {&output_context_diff(@_)} else {die "unknown diff"} } sub output_unified_diff { my ($hunk, $fileref1, $fileref2) = @_; my @blocklist; # Calculate item number range. my $range1 = $hunk->unified_range(1); my $range2 = $hunk->unified_range(2); print "@@ -$range1 +$range2 @@\n"; # Outlist starts containing the hunk of file 1. # Removing an item just means putting a '-' in front of it. # Inserting an item requires getting it from file2 and splicing it in. # We splice in $num_added items. Remove blocks use $num_added because # splicing changed the length of outlist. # We remove $num_removed items. Insert blocks use $num_removed because # their item numbers---corresponding to positions in file *2*--- don't take # removed items into account. my $low = $hunk->{"start1"}; my $hi = $hunk->{"end1"}; my ($num_added, $num_removed) = (0,0); my @outlist = @$fileref1[$low..$hi]; map {s/^/ /} @outlist; # assume it's just context foreach my $block (@{$hunk->{"blocks"}}) { foreach my $item ($block->remove) { my $op = $item->{"sign"}; # - my $offset = $item->{"item_no"} - $low + $num_added; $outlist[$offset] =~ s/^ /$op/; $num_removed++; } foreach my $item ($block->insert) { my $op = $item->{"sign"}; # + my $i = $item->{"item_no"}; my $offset = $i - $hunk->{"start2"} + $num_removed; splice(@outlist,$offset,0,"$op$$fileref2[$i]"); $num_added++; } } map {s/$/\n/} @outlist; # add \n's print @outlist; } sub output_context_diff { my ($hunk, $fileref1, $fileref2) = @_; my @blocklist; print "***************\n"; # Calculate item number range. my $range1 = $hunk->context_range(1); my $range2 = $hunk->context_range(2); # Print out file 1 part for each block in context diff format if there are # any blocks that remove items print "*** $range1 ****\n"; my $low = $hunk->{"start1"}; my $hi = $hunk->{"end1"}; if (@blocklist = grep {$_->remove} @{$hunk->{"blocks"}}) { my @outlist = @$fileref1[$low..$hi]; map {s/^/ /} @outlist; # assume it's just context foreach my $block (@blocklist) { my $op = $block->op; # - or ! foreach my $item ($block->remove) { $outlist[$item->{"item_no"} - $low] =~ s/^ /$op/; } } map {s/$/\n/} @outlist; # add \n's print @outlist; } print "--- $range2 ----\n"; $low = $hunk->{"start2"}; $hi = $hunk->{"end2"}; if (@blocklist = grep {$_->insert} @{$hunk->{"blocks"}}) { my @outlist = @$fileref2[$low..$hi]; map {s/^/ /} @outlist; # assume it's just context foreach my $block (@blocklist) { my $op = $block->op; # + or ! foreach my $item ($block->insert) { $outlist[$item->{"item_no"} - $low] =~ s/^ /$op/; } } map {s/$/\n/} @outlist; # add \n's print @outlist; } } sub context_range { # Generate a range of item numbers to print. Only print 1 number if the range # has only one item in it. Otherwise, it's 'start,end' my ($hunk, $flag) = @_; my ($start, $end) = ($hunk->{"start$flag"},$hunk->{"end$flag"}); $start++; $end++; # index from 1, not zero my $range = ($start < $end) ? "$start,$end" : $end; return $range; } sub unified_range { # Generate a range of item numbers to print for unified diff # Print number where block starts, followed by number of lines in the block # (don't print number of lines if it's 1) my ($hunk, $flag) = @_; my ($start, $end) = ($hunk->{"start$flag"},$hunk->{"end$flag"}); $start++; $end++; # index from 1, not zero my $length = $end - $start + 1; my $first = $length < 2 ? $end : $start; # strange, but correct... my $range = $length== 1 ? $first : "$first,$length"; return $range; } } # end Package Hunk # Package Block. A block is an operation removing, adding, or changing # a group of items. Basically, this is just a list of changes, where each # change adds or deletes a single item. # (Change could be a separate class, but it didn't seem worth it) { package Block; sub new { # Input is a chunk from &Algorithm::LCS::diff # Fields in a block: # length_diff - how much longer file 2 is than file 1 due to this block # Each change has: # sign - '+' for insert, '-' for remove # item_no - number of the item in the file (e.g., line number) # We don't bother storing the text of the item # my ($class,$chunk) = @_; my @changes = (); # This just turns each change into a hash. foreach my $item (@$chunk) { my ($sign, $item_no, $text) = @$item; my $hashref = {"sign" => $sign, "item_no" => $item_no}; push @changes, $hashref; } my $block = { "changes" => \@changes }; bless $block, $class; $block->{"length_diff"} = $block->insert - $block->remove; return $block; } # LOW LEVEL FUNCTIONS sub op { # what kind of block is this? my $block = shift; my $insert = $block->insert; my $remove = $block->remove; $remove && $insert and return '!'; $remove and return '-'; $insert and return '+'; warn "unknown block type"; return '^'; # context block } # Returns a list of the changes in this block that remove items # (or the number of removals if called in scalar context) sub remove { return grep {$_->{"sign"} eq '-'} @{shift->{"changes"}}; } # Returns a list of the changes in this block that insert items sub insert { return grep {$_->{"sign"} eq '+'} @{shift->{"changes"}}; } } # end of package Block examples/diffnew.pl 0000644 00000042220 15030517304 0010337 0 ustar 00 #!/usr/bin/perl # # `Diff' program in Perl # Copyright 1998 M-J. Dominus. (mjd-perl-diff@plover.com) # # This program is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. # # Altered to output in `context diff' format (but without context) # September 1998 Christian Murphy (cpm@muc.de) # # Context lines feature added # Unified, "Old" (Standard UNIX), Ed diff added September 1998 # Reverse_Ed (-f option) added March 1999 # Amir D. Karger (karger@bead.aecom.yu.edu) # # Modular functions integrated into program # February 1999 M-J. Dominus (mjd-perl-diff@plover.com) # # In this file, "item" usually means "line of text", and "item number" usually # means "line number". But theoretically the code could be used more generally use strict; use Algorithm::Diff qw(diff); # GLOBAL VARIABLES #### # After we've read up to a certain point in each file, the number of items # we've read from each file will differ by $FLD (could be 0) my $File_Length_Difference = 0; #ed diff outputs hunks *backwards*, so we need to save hunks when doing ed diff my @Ed_Hunks = (); ######################## my $usage = << "ENDUSAGE"; Usage: $0 [{-c | -C lines -e | -f | -u | -U lines |-q | -i | -w}] oldfile newfile -c do a context diff with 3 lines of context -C do a context diff with 'lines' lines of context (implies -c) -e create a script for the ed editor to change oldfile to newfile -f like -e but in reverse order -u do a unified diff with 3 lines of context -U do a unified diff with 'lines' lines of context (implies -u) -q report only whether or not the files differ -i ignore differences in Upper/lower-case -w ignore differences in white-space (space and TAB characters) By default it will do an "old-style" diff, with output like UNIX diff ENDUSAGE my $Context_Lines = 0; # lines of context to print. 0 for old-style diff my $Diff_Type = "OLD"; # by default, do standard UNIX diff my ($opt_c, $opt_u, $opt_e, $opt_f, $opt_q, $opt_i, $opt_w); my $compareRoutineRef = undef; while ($ARGV[0] =~ /^-/) { my $opt = shift; last if $opt eq '--'; if ($opt =~ /^-C(.*)/) { $Context_Lines = $1 || shift; $opt_c = 1; $Diff_Type = "CONTEXT"; } elsif ($opt =~ /^-c$/) { $Context_Lines = 3; $opt_c = 1; $Diff_Type = "CONTEXT"; } elsif ($opt =~ /^-e$/) { $opt_e = 1; $Diff_Type = "ED"; } elsif ($opt =~ /^-f$/) { $opt_f = 1; $Diff_Type = "REVERSE_ED"; } elsif ($opt =~ /^-U(.*)$/) { $Context_Lines = $1 || shift; $opt_u = 1; $Diff_Type = "UNIFIED"; } elsif ($opt =~ /^-u$/) { $Context_Lines = 3; $opt_u = 1; $Diff_Type = "UNIFIED"; } elsif ($opt =~ /^-q$/) { $Context_Lines = 0; $opt_q = 1; $opt_e = 1; $Diff_Type = "ED"; } elsif ($opt =~ /^-i$/) { $opt_i = 1; $compareRoutineRef = \&compareRoutine; } elsif ($opt =~ /^-w$/) { $opt_w = 1; $compareRoutineRef = \&compareRoutine; } else { $opt =~ s/^-//; bag("Illegal option -- $opt"); } } if ($opt_q and grep($_,($opt_c, $opt_f, $opt_u)) > 1) { bag("Combining -q with other options is nonsensical"); } if (grep($_,($opt_c, $opt_e, $opt_f, $opt_u)) > 1) { bag("Only one of -c, -u, -f, -e are allowed"); } bag($usage) unless @ARGV == 2; ######## DO THE DIFF! my ($file1, $file2) = @ARGV; my ($char1, $char2); # string to print before file names if ($Diff_Type eq "CONTEXT") { $char1 = '*' x 3; $char2 = '-' x 3; } elsif ($Diff_Type eq "UNIFIED") { $char1 = '-' x 3; $char2 = '+' x 3; } open (F1, $file1) or bag("Couldn't open $file1: $!"); open (F2, $file2) or bag("Couldn't open $file2: $!"); my (@f1, @f2); chomp(@f1 = <F1>); close F1; chomp(@f2 = <F2>); close F2; # diff yields lots of pieces, each of which is basically a Block object my $diffs = diff(\@f1, \@f2, $compareRoutineRef); exit 0 unless @$diffs; if ($opt_q and @$diffs) { print "Files $file1 and $file2 differ\n"; exit 1; } if ($Diff_Type =~ /UNIFIED|CONTEXT/) { my @st = stat($file1); my $MTIME = 9; print "$char1 $file1\t", scalar localtime($st[$MTIME]), "\n"; @st = stat($file2); print "$char2 $file2\t", scalar localtime($st[$MTIME]), "\n"; } my ($hunk,$oldhunk); # Loop over hunks. If a hunk overlaps with the last hunk, join them. # Otherwise, print out the old one. foreach my $piece (@$diffs) { $hunk = new Hunk ($piece, $Context_Lines); next unless $oldhunk; # first time through # Don't need to check for overlap if blocks have no context lines if ($Context_Lines && $hunk->does_overlap($oldhunk)) { $hunk->prepend_hunk($oldhunk); } else { $oldhunk->output_diff(\@f1, \@f2, $Diff_Type); } } continue { $oldhunk = $hunk; } # print the last hunk $oldhunk->output_diff(\@f1, \@f2, $Diff_Type); # Print hunks backwards if we're doing an ed diff map {$_->output_ed_diff(\@f1, \@f2, $Diff_Type)} @Ed_Hunks if @Ed_Hunks; exit 1; # END MAIN PROGRAM sub bag { my $msg = shift; $msg .= "\n"; warn $msg; exit 2; } sub compareRoutine { my $line = shift; $line =~ s/[ \t\r\n]+//g if ($opt_w); $line = uc( $line ) if ($opt_i); return $line; } ######## # Package Hunk. A Hunk is a group of Blocks which overlap because of the # context surrounding each block. (So if we're not using context, every # hunk will contain one block.) { package Hunk; sub new { # Arg1 is output from &LCS::diff (which corresponds to one Block) # Arg2 is the number of items (lines, e.g.,) of context around each block # # This subroutine changes $File_Length_Difference # # Fields in a Hunk: # blocks - a list of Block objects # start - index in file 1 where first block of the hunk starts # end - index in file 1 where last block of the hunk ends # # Variables: # before_diff - how much longer file 2 is than file 1 due to all hunks # until but NOT including this one # after_diff - difference due to all hunks including this one my ($class, $piece, $context_items) = @_; my $block = new Block ($piece); # this modifies $FLD! my $before_diff = $File_Length_Difference; # BEFORE this hunk my $after_diff = $before_diff + $block->{"length_diff"}; $File_Length_Difference += $block->{"length_diff"}; # @remove_array and @insert_array hold the items to insert and remove # Save the start & beginning of each array. If the array doesn't exist # though (e.g., we're only adding items in this block), then figure # out the line number based on the line number of the other file and # the current difference in file lengths my @remove_array = $block->remove; my @insert_array = $block->insert; my ($a1, $a2, $b1, $b2, $start1, $start2, $end1, $end2); $a1 = @remove_array ? $remove_array[0 ]->{"item_no"} : -1; $a2 = @remove_array ? $remove_array[-1]->{"item_no"} : -1; $b1 = @insert_array ? $insert_array[0 ]->{"item_no"} : -1; $b2 = @insert_array ? $insert_array[-1]->{"item_no"} : -1; $start1 = $a1 == -1 ? $b1 - $before_diff : $a1; $end1 = $a2 == -1 ? $b2 - $after_diff : $a2; $start2 = $b1 == -1 ? $a1 + $before_diff : $b1; $end2 = $b2 == -1 ? $a2 + $after_diff : $b2; # At first, a hunk will have just one Block in it my $hunk = { "start1" => $start1, "start2" => $start2, "end1" => $end1, "end2" => $end2, "blocks" => [$block], }; bless $hunk, $class; $hunk->flag_context($context_items); return $hunk; } # Change the "start" and "end" fields to note that context should be added # to this hunk sub flag_context { my ($hunk, $context_items) = @_; return unless $context_items; # no context # add context before my $start1 = $hunk->{"start1"}; my $num_added = $context_items > $start1 ? $start1 : $context_items; $hunk->{"start1"} -= $num_added; $hunk->{"start2"} -= $num_added; # context after my $end1 = $hunk->{"end1"}; $num_added = ($end1+$context_items > $#f1) ? $#f1 - $end1 : $context_items; $hunk->{"end1"} += $num_added; $hunk->{"end2"} += $num_added; } # Is there an overlap between hunk arg0 and old hunk arg1? # Note: if end of old hunk is one less than beginning of second, they overlap sub does_overlap { my ($hunk, $oldhunk) = @_; return "" unless $oldhunk; # first time through, $oldhunk is empty # Do I actually need to test both? return ($hunk->{"start1"} - $oldhunk->{"end1"} <= 1 || $hunk->{"start2"} - $oldhunk->{"end2"} <= 1); } # Prepend hunk arg1 to hunk arg0 # Note that arg1 isn't updated! Only arg0 is. sub prepend_hunk { my ($hunk, $oldhunk) = @_; $hunk->{"start1"} = $oldhunk->{"start1"}; $hunk->{"start2"} = $oldhunk->{"start2"}; unshift (@{$hunk->{"blocks"}}, @{$oldhunk->{"blocks"}}); } # DIFF OUTPUT ROUTINES. THESE ROUTINES CONTAIN DIFF FORMATTING INFO... sub output_diff { # First arg is the current hunk of course # Next args are refs to the files # last arg is type of diff my $diff_type = $_[-1]; my %funchash = ("OLD" => \&output_old_diff, "CONTEXT" => \&output_context_diff, "ED" => \&store_ed_diff, "REVERSE_ED" => \&output_ed_diff, "UNIFIED" => \&output_unified_diff, ); if (exists $funchash{$diff_type}) { &{$funchash{$diff_type}}(@_); # pass in all args } else {die "unknown diff type $diff_type"} } sub output_old_diff { # Note that an old diff can't have any context. Therefore, we know that # there's only one block in the hunk. my ($hunk, $fileref1, $fileref2) = @_; my %op_hash = ('+' => 'a', '-' => 'd', '!' => 'c'); my @blocklist = @{$hunk->{"blocks"}}; warn ("Expecting one block in an old diff hunk!") if scalar @blocklist != 1; my $block = $blocklist[0]; my $op = $block->op; # +, -, or ! # Calculate item number range. # old diff range is just like a context diff range, except the ranges # are on one line with the action between them. my $range1 = $hunk->context_range(1); my $range2 = $hunk->context_range(2); my $action = $op_hash{$op} || warn "unknown op $op"; print "$range1$action$range2\n"; # If removing anything, just print out all the remove lines in the hunk # which is just all the remove lines in the block if ($block->remove) { my @outlist = @$fileref1[$hunk->{"start1"}..$hunk->{"end1"}]; map {$_ = "< $_\n"} @outlist; # all lines will be '< text\n' print @outlist; } print "---\n" if $op eq '!'; # only if inserting and removing if ($block->insert) { my @outlist = @$fileref2[$hunk->{"start2"}..$hunk->{"end2"}]; map {$_ = "> $_\n"} @outlist; # all lines will be '> text\n' print @outlist; } } sub output_unified_diff { my ($hunk, $fileref1, $fileref2) = @_; my @blocklist; # Calculate item number range. my $range1 = $hunk->unified_range(1); my $range2 = $hunk->unified_range(2); print "@@ -$range1 +$range2 @@\n"; # Outlist starts containing the hunk of file 1. # Removing an item just means putting a '-' in front of it. # Inserting an item requires getting it from file2 and splicing it in. # We splice in $num_added items. Remove blocks use $num_added because # splicing changed the length of outlist. # We remove $num_removed items. Insert blocks use $num_removed because # their item numbers---corresponding to positions in file *2*--- don't take # removed items into account. my $low = $hunk->{"start1"}; my $hi = $hunk->{"end1"}; my ($num_added, $num_removed) = (0,0); my @outlist = @$fileref1[$low..$hi]; map {s/^/ /} @outlist; # assume it's just context foreach my $block (@{$hunk->{"blocks"}}) { foreach my $item ($block->remove) { my $op = $item->{"sign"}; # - my $offset = $item->{"item_no"} - $low + $num_added; $outlist[$offset] =~ s/^ /$op/; $num_removed++; } foreach my $item ($block->insert) { my $op = $item->{"sign"}; # + my $i = $item->{"item_no"}; my $offset = $i - $hunk->{"start2"} + $num_removed; splice(@outlist,$offset,0,"$op$$fileref2[$i]"); $num_added++; } } map {s/$/\n/} @outlist; # add \n's print @outlist; } sub output_context_diff { my ($hunk, $fileref1, $fileref2) = @_; my @blocklist; print "***************\n"; # Calculate item number range. my $range1 = $hunk->context_range(1); my $range2 = $hunk->context_range(2); # Print out file 1 part for each block in context diff format if there are # any blocks that remove items print "*** $range1 ****\n"; my $low = $hunk->{"start1"}; my $hi = $hunk->{"end1"}; if (@blocklist = grep {$_->remove} @{$hunk->{"blocks"}}) { my @outlist = @$fileref1[$low..$hi]; map {s/^/ /} @outlist; # assume it's just context foreach my $block (@blocklist) { my $op = $block->op; # - or ! foreach my $item ($block->remove) { $outlist[$item->{"item_no"} - $low] =~ s/^ /$op/; } } map {s/$/\n/} @outlist; # add \n's print @outlist; } print "--- $range2 ----\n"; $low = $hunk->{"start2"}; $hi = $hunk->{"end2"}; if (@blocklist = grep {$_->insert} @{$hunk->{"blocks"}}) { my @outlist = @$fileref2[$low..$hi]; map {s/^/ /} @outlist; # assume it's just context foreach my $block (@blocklist) { my $op = $block->op; # + or ! foreach my $item ($block->insert) { $outlist[$item->{"item_no"} - $low] =~ s/^ /$op/; } } map {s/$/\n/} @outlist; # add \n's print @outlist; } } sub store_ed_diff { # ed diff prints out diffs *backwards*. So save them while we're generating # them, then print them out at the end my $hunk = shift; unshift @Ed_Hunks, $hunk; } sub output_ed_diff { # This sub is used for ed ('diff -e') OR reverse_ed ('diff -f'). # last arg is type of diff my $diff_type = $_[-1]; my ($hunk, $fileref1, $fileref2) = @_; my %op_hash = ('+' => 'a', '-' => 'd', '!' => 'c'); # Can't be any context for this kind of diff, so each hunk has one block my @blocklist = @{$hunk->{"blocks"}}; warn ("Expecting one block in an ed diff hunk!") if scalar @blocklist != 1; my $block = $blocklist[0]; my $op = $block->op; # +, -, or ! # Calculate item number range. # old diff range is just like a context diff range, except the ranges # are on one line with the action between them. my $range1 = $hunk->context_range(1); $range1 =~ s/,/ / if $diff_type eq "REVERSE_ED"; my $action = $op_hash{$op} || warn "unknown op $op"; print ($diff_type eq "ED" ? "$range1$action\n" : "$action$range1\n"); if ($block->insert) { my @outlist = @$fileref2[$hunk->{"start2"}..$hunk->{"end2"}]; map {s/$/\n/} @outlist; # add \n's print @outlist; print ".\n"; # end of ed 'c' or 'a' command } } sub context_range { # Generate a range of item numbers to print. Only print 1 number if the range # has only one item in it. Otherwise, it's 'start,end' # Flag is the number of the file (1 or 2) my ($hunk, $flag) = @_; my ($start, $end) = ($hunk->{"start$flag"},$hunk->{"end$flag"}); $start++; $end++; # index from 1, not zero my $range = ($start < $end) ? "$start,$end" : $end; return $range; } sub unified_range { # Generate a range of item numbers to print for unified diff # Print number where block starts, followed by number of lines in the block # (don't print number of lines if it's 1) my ($hunk, $flag) = @_; my ($start, $end) = ($hunk->{"start$flag"},$hunk->{"end$flag"}); $start++; $end++; # index from 1, not zero my $length = $end - $start + 1; my $first = $length < 2 ? $end : $start; # strange, but correct... my $range = $length== 1 ? $first : "$first,$length"; return $range; } } # end Package Hunk ######## # Package Block. A block is an operation removing, adding, or changing # a group of items. Basically, this is just a list of changes, where each # change adds or deletes a single item. # (Change could be a separate class, but it didn't seem worth it) { package Block; sub new { # Input is a chunk from &Algorithm::LCS::diff # Fields in a block: # length_diff - how much longer file 2 is than file 1 due to this block # Each change has: # sign - '+' for insert, '-' for remove # item_no - number of the item in the file (e.g., line number) # We don't bother storing the text of the item # my ($class,$chunk) = @_; my @changes = (); # This just turns each change into a hash. foreach my $item (@$chunk) { my ($sign, $item_no, $text) = @$item; my $hashref = {"sign" => $sign, "item_no" => $item_no}; push @changes, $hashref; } my $block = { "changes" => \@changes }; bless $block, $class; $block->{"length_diff"} = $block->insert - $block->remove; return $block; } # LOW LEVEL FUNCTIONS sub op { # what kind of block is this? my $block = shift; my $insert = $block->insert; my $remove = $block->remove; $remove && $insert and return '!'; $remove and return '-'; $insert and return '+'; warn "unknown block type"; return '^'; # context block } # Returns a list of the changes in this block that remove items # (or the number of removals if called in scalar context) sub remove { return grep {$_->{"sign"} eq '-'} @{shift->{"changes"}}; } # Returns a list of the changes in this block that insert items sub insert { return grep {$_->{"sign"} eq '+'} @{shift->{"changes"}}; } } # end of package Block examples/htmldiff.pl 0000644 00000003475 15030517304 0010523 0 ustar 00 #!/usr/bin/perl -w # diffs two files and writes an HTML output file. use strict; use CGI qw(:standard :html3); use Algorithm::Diff 'traverse_sequences'; use Text::Tabs; my ( @a, @b ); # Take care of whitespace. sub preprocess { my $arrayRef = shift; chomp(@$arrayRef); @$arrayRef = expand(@$arrayRef); } # This will be called with both lines are the same sub match { my ( $ia, $ib ) = @_; print pre( $a[$ia] ), "\n"; } # This will be called when there is a line in A that isn't in B sub only_a { my ( $ia, $ib ) = @_; print pre( { -class => 'onlyA' }, $a[$ia] ), "\n"; } # This will be called when there is a line in B that isn't in A sub only_b { my ( $ia, $ib ) = @_; print pre( { -class => 'onlyB' }, $b[$ib] ), "\n"; } # MAIN PROGRAM # Check for two arguments. print "usage: $0 file1 file2 > diff.html\n" if @ARGV != 2; $tabstop = 4; # For Text::Tabs # Read each file into an array. open FH, $ARGV[0]; @a = <FH>; close FH; open FH, $ARGV[1]; @b = <FH>; close FH; # Expand whitespace preprocess( \@a ); preprocess( \@b ); # inline style my $style = <<EOS; PRE { margin-left: 24pt; font-size: 12pt; font-family: Courier, monospaced; white-space: pre } PRE.onlyA { color: red } PRE.onlyB { color: blue } EOS # Print out the starting HTML print # header(), start_html( { -title => "$ARGV[0] vs. $ARGV[1]", -style => { -code => $style } } ), h1( { -style => 'margin-left: 24pt' }, span( { -style => 'color: red' }, $ARGV[0] ), span(" <i>vs.</i> "), span( { -style => 'color: blue' }, $ARGV[1] ) ), "\n"; # And compare the arrays traverse_sequences( \@a, # first sequence \@b, # second sequence { MATCH => \&match, # callback on identical lines DISCARD_A => \&only_a, # callback on A-only DISCARD_B => \&only_b, # callback on B-only } ); print end_html();
| ver. 1.4 |
Github
|
.
| PHP 8.2.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings