(file) Return to Diff.pm CVS log (file) (dir) Up to [RizwankCVS] / geekymedia_web / twiki / lib / Text

  1 rizwank 1.1 package Text::Diff;
  2             
  3             $VERSION = 0.35;
  4             
  5             =head1 NAME
  6             
  7             Text::Diff - Perform diffs on files and record sets
  8             
  9             =head1 SYNOPSIS
 10             
 11                 use Text::Diff;
 12             
 13                 ## Mix and match filenames, strings, file handles, producer subs,
 14                 ## or arrays of records; returns diff in a string.
 15                 ## WARNING: can return B<large> diffs for large files.
 16                 my $diff = diff "file1.txt", "file2.txt", { STYLE => "Context" };
 17                 my $diff = diff \$string1,   \$string2,   \%options;
 18                 my $diff = diff \*FH1,       \*FH2;
 19                 my $diff = diff \&reader1,   \&reader2;
 20                 my $diff = diff \@records1,  \@records2;
 21             
 22 rizwank 1.1     ## May also mix input types:
 23                 my $diff = diff \@records1,  "file_B.txt";
 24             
 25             =head1 DESCRIPTION
 26             
 27             C<diff()> provides a basic set of services akin to the GNU C<diff> utility.  It
 28             is not anywhere near as feature complete as GNU C<diff>, but it is better
 29             integrated with Perl and available on all platforms.  It is often faster than
 30             shelling out to a system's C<diff> executable for small files, and generally
 31             slower on larger files.
 32             
 33             Relies on L<Algorithm::Diff> for, well, the algorithm.  This may not produce
 34             the same exact diff as a system's local C<diff> executable, but it will be a
 35             valid diff and comprehensible by C<patch>.  We haven't seen any differences
 36             between Algorithm::Diff's logic and GNU diff's, but we have not examined them
 37             to make sure they are indeed identical.
 38             
 39             B<Note>: If you don't want to import the C<diff> function, do one of the
 40             following:
 41             
 42                use Text::Diff ();
 43 rizwank 1.1 
 44                require Text::Diff;
 45             
 46             That's a pretty rare occurence, so C<diff()> is exported by default.
 47             
 48             =cut
 49             
 50             use Exporter;
 51             @ISA = qw( Exporter );
 52             @EXPORT = qw( diff );
 53             
 54             use strict;
 55             use Carp;
 56             use Algorithm::Diff qw( traverse_sequences );
 57             
 58             ## Hunks are made of ops.  An op is the starting index for each
 59             ## sequence and the opcode:
 60             use constant A       => 0;   # Array index before match/discard
 61             use constant B       => 1;
 62             use constant OPCODE  => 2;   # "-", " ", "+"
 63             use constant FLAG    => 3;   # What to display if not OPCODE "!"
 64 rizwank 1.1 
 65             
 66             =head1 OPTIONS
 67             
 68             diff() takes two parameters from which to draw input and a set of
 69             options to control it's output.  The options are:
 70             
 71             =over
 72             
 73             =item FILENAME_A, MTIME_A, FILENAME_B, MTIME_B
 74             
 75             The name of the file and the modification time "files"
 76             
 77             These are filled in automatically for each file when diff() is passed a
 78             filename, unless a defined value is passed in.
 79             
 80             If a filename is not passed in and FILENAME_A and FILENAME_B are not provided
 81             or C<undef>, the header will not be printed.
 82             
 83             Unused on C<OldStyle> diffs.
 84             
 85 rizwank 1.1 =item OFFSET_A, OFFSET_B
 86             
 87             The index of the first line / element.  These default to 1 for all
 88             parameter types except ARRAY references, for which the default is 0.  This
 89             is because ARRAY references are presumed to be data structures, while the
 90             others are line oriented text.
 91             
 92             =item STYLE
 93             
 94             "Unified", "Context", "OldStyle", or an object or class reference for a class
 95             providing C<file_header()>, C<hunk_header()>, C<hunk()>, C<hunk_footer()> and
 96             C<file_footer()> methods.  The two footer() methods are provided for
 97             overloading only; none of the formats provide them.
 98             
 99             Defaults to "Unified" (unlike standard C<diff>, but Unified is what's most
100             often used in submitting patches and is the most human readable of the three.
101             
102             If the package indicated by the STYLE has no hunk() method, c<diff()> will
103             load it automatically (lazy loading).  Since all such packages should inherit
104             from Text::Diff::Base, this should be marvy.
105             
106 rizwank 1.1 Styles may be specified as class names (C<STYLE => "Foo"), in which case they
107             will be C<new()>ed with no parameters, or as objects (C<STYLE => Foo->new>).
108             
109             =item CONTEXT
110             
111             How many lines before and after each diff to display.  Ignored on old-style
112             diffs.  Defaults to 3.
113             
114             =item OUTPUT
115             
116             Examples and their equivalent subroutines:
117             
118                 OUTPUT   => \*FOOHANDLE,   # like: sub { print FOOHANDLE shift() }
119                 OUTPUT   => \$output,      # like: sub { $output .= shift }
120                 OUTPUT   => \@output,      # like: sub { push @output, shift }
121                 OUTPUT   => sub { $output .= shift },
122             
123             If no C<OUTPUT> is supplied, returns the diffs in a string.  If
124             C<OUTPUT> is a C<CODE> ref, it will be called once with the (optional)
125             file header, and once for each hunk body with the text to emit.  If
126             C<OUTPUT> is an L<IO::Handle>, output will be emitted to that handle.
127 rizwank 1.1 
128             =item FILENAME_PREFIX_A, FILENAME_PREFIX_B
129             
130             The string to print before the filename in the header. Unused on C<OldStyle>
131             diffs.  Defaults are C<"---">, C<"+++"> for Unified and C<"***">, C<"+++"> for
132             Context.
133             
134             =item KEYGEN, KEYGEN_ARGS
135             
136             These are passed to L<Algorithm::Diff/traverse_sequences>.
137             
138             =back
139             
140             B<Note>: if neither C<FILENAME_> option is defined, the header will not be
141             printed.  If at one is present, the other and both MTIME_ options must be
142             present or "Use of undefined variable" warnings will be generated (except
143             on C<OldStyle> diffs, which ignores these options).
144             
145             =cut
146             
147             my %internal_styles = (
148 rizwank 1.1     Unified  => undef,
149                 Context  => undef,
150                 OldStyle => undef,
151                 Table    => undef,   ## "internal", but in another module
152             );
153             
154             sub diff {
155                 my @seqs = ( shift, shift );
156                 my $options = shift || {};
157             
158                 for my $i ( 0..1 ) {
159                     my $seq = $seqs[$i];
160             	my $type = ref $seq;
161             
162                     while ( $type eq "CODE" ) {
163             	    $seqs[$i] = $seq = $seq->( $options );
164             	    $type = ref $seq;
165             	}
166             
167             	my $AorB = !$i ? "A" : "B";
168             
169 rizwank 1.1         if ( $type eq "ARRAY" ) {
170                         ## This is most efficient :)
171                         $options->{"OFFSET_$AorB"} = 0
172                             unless defined $options->{"OFFSET_$AorB"};
173                     }
174                     elsif ( $type eq "SCALAR" ) {
175                         $seqs[$i] = [split( /^/m, $$seq )];
176                         $options->{"OFFSET_$AorB"} = 1
177                             unless defined $options->{"OFFSET_$AorB"};
178                     }
179                     elsif ( ! $type ) {
180                         $options->{"OFFSET_$AorB"} = 1
181                             unless defined $options->{"OFFSET_$AorB"};
182             	    $options->{"FILENAME_$AorB"} = $seq
183             	        unless defined $options->{"FILENAME_$AorB"};
184             	    $options->{"MTIME_$AorB"} = (stat($seq))[9]
185             	        unless defined $options->{"MTIME_$AorB"};
186             
187                         local $/ = "\n";
188                         open F, "<$seq" or carp "$!: $seq";
189                         $seqs[$i] = [<F>];
190 rizwank 1.1             close F;
191             
192                     }
193                     elsif ( $type eq "GLOB" || UNIVERSAL::isa( $seq, "IO::Handle" ) ) {
194                         $options->{"OFFSET_$AorB"} = 1
195                             unless defined $options->{"OFFSET_$AorB"};
196                         local $/ = "\n";
197                         $seqs[$i] = [<$seq>];
198                     }
199                     else {
200                         confess "Can't handle input of type ", ref;
201                     }
202                 }
203             
204                 ## Config vars
205                 my $output;
206                 my $output_handler = $options->{OUTPUT};
207                 my $type = ref $output_handler ;
208                 if ( ! defined $output_handler ) {
209                     $output = "";
210                     $output_handler = sub { $output .= shift };
211 rizwank 1.1     }
212                 elsif ( $type eq "CODE" ) {
213                     ## No problems, mate.
214                 }
215                 elsif ( $type eq "SCALAR" ) {
216                     my $out_ref = $output_handler;
217                     $output_handler = sub { $$out_ref .= shift };
218                 }
219                 elsif ( $type eq "ARRAY" ) {
220                     my $out_ref = $output_handler;
221                     $output_handler = sub { push @$out_ref, shift };
222                 }
223                 elsif ( $type eq "GLOB" || UNIVERSAL::isa $output_handler, "IO::Handle" ) {
224                     my $output_handle = $output_handler;
225                     $output_handler = sub { print $output_handle shift };
226                 }
227                 else {
228                     croak "Unrecognized output type: $type";
229                 }
230             
231                 my $style  = $options->{STYLE};
232 rizwank 1.1     $style = "Unified" unless defined $options->{STYLE};
233                 $style = "Text::Diff::$style" if exists $internal_styles{$style};
234             
235                 if ( ! $style->can( "hunk" ) ) {
236             	eval "require $style; 1" or die $@;
237                 }
238             
239                 $style = $style->new
240             	if ! ref $style && $style->can( "new" );
241             
242                 my $ctx_lines = $options->{CONTEXT};
243                 $ctx_lines = 3 unless defined $ctx_lines;
244                 $ctx_lines = 0 if $style->isa( "Text::Diff::OldStyle" );
245             
246                 my @keygen_args = $options->{KEYGEN_ARGS}
247                     ? @{$options->{KEYGEN_ARGS}}
248                     : ();
249             
250                 ## State vars
251                 my $diffs = 0; ## Number of discards this hunk
252                 my $ctx   = 0; ## Number of " " (ctx_lines) ops pushed after last diff.
253 rizwank 1.1     my @ops;       ## ops (" ", +, -) in this hunk
254                 my $hunks = 0; ## Number of hunks
255             
256                 my $emit_ops = sub {
257                     $output_handler->( $style->file_header( @seqs,     $options ) )
258             	    unless $hunks++;
259                     $output_handler->( $style->hunk_header( @seqs, @_, $options ) );
260                     $output_handler->( $style->hunk       ( @seqs, @_, $options ) );
261                     $output_handler->( $style->hunk_footer( @seqs, @_, $options ) );
262                 };
263             
264                 ## We keep 2*ctx_lines so that if a diff occurs
265                 ## at 2*ctx_lines we continue to grow the hunk instead
266                 ## of emitting diffs and context as we go. We
267                 ## need to know the total length of both of the two
268                 ## subsequences so the line count can be printed in the
269                 ## header.
270                 my $dis_a = sub {push @ops, [@_[0,1],"-"]; ++$diffs ; $ctx = 0 };
271                 my $dis_b = sub {push @ops, [@_[0,1],"+"]; ++$diffs ; $ctx = 0 };
272             
273                 traverse_sequences(
274 rizwank 1.1         @seqs,
275                     {
276                         MATCH => sub {
277                             push @ops, [@_[0,1]," "];
278             
279                             if ( $diffs && ++$ctx > $ctx_lines * 2 ) {
280                     	   $emit_ops->( [ splice @ops, 0, $#ops - $ctx_lines ] );
281                     	   $ctx = $diffs = 0;
282                             }
283             
284                             ## throw away context lines that aren't needed any more
285                             shift @ops if ! $diffs && @ops > $ctx_lines;
286                         },
287                         DISCARD_A => $dis_a,
288                         DISCARD_B => $dis_b,
289                     },
290                     $options->{KEYGEN},  # pass in user arguments for key gen function
291                     @keygen_args,
292                 );
293             
294                 if ( $diffs ) {
295 rizwank 1.1         $#ops -= $ctx - $ctx_lines if $ctx > $ctx_lines;
296                     $emit_ops->( \@ops );
297                 }
298             
299                 $output_handler->( $style->file_footer( @seqs, $options ) ) if $hunks;
300             
301                 return defined $output ? $output : $hunks;
302             }
303             
304             
305             sub _header {
306                 my ( $h ) = @_;
307                 my ( $p1, $fn1, $t1, $p2, $fn2, $t2 ) = @{$h}{
308                     "FILENAME_PREFIX_A",
309                     "FILENAME_A",
310                     "MTIME_A",
311                     "FILENAME_PREFIX_B",
312                     "FILENAME_B",
313                     "MTIME_B"
314                 };
315             
316 rizwank 1.1     ## remember to change Text::Diff::Table if this logic is tweaked.
317                 return "" unless defined $fn1 && defined $fn2;
318             
319                 return join( "",
320                     $p1, " ", $fn1, defined $t1 ? "\t" . localtime $t1 : (), "\n",
321                     $p2, " ", $fn2, defined $t2 ? "\t" . localtime $t2 : (), "\n",
322                 );
323             }
324             
325             ## _range encapsulates the building of, well, ranges.  Turns out there are
326             ## a few nuances.
327             sub _range {
328                 my ( $ops, $a_or_b, $format ) = @_;
329             
330                 my $start = $ops->[ 0]->[$a_or_b];
331                 my $after = $ops->[-1]->[$a_or_b];
332             
333                 ## The sequence indexes in the lines are from *before* the OPCODE is
334                 ## executed, so we bump the last index up unless the OP indicates
335                 ## it didn't change.
336                 ++$after
337 rizwank 1.1         unless $ops->[-1]->[OPCODE] eq ( $a_or_b == A ? "+" : "-" );
338             
339                 ## convert from 0..n index to 1..(n+1) line number.  The unless modifier
340                 ## handles diffs with no context, where only one file is affected.  In this
341                 ## case $start == $after indicates an empty range, and the $start must
342                 ## not be incremented.
343                 my $empty_range = $start == $after;
344                 ++$start unless $empty_range;
345             
346                 return
347                     $start == $after
348                         ? $format eq "unified" && $empty_range
349                             ? "$start,0"
350                             : $start
351                         : $format eq "unified"
352                             ? "$start,".($after-$start+1)
353                             : "$start,$after";
354             }
355             
356             
357             sub _op_to_line {
358 rizwank 1.1     my ( $seqs, $op, $a_or_b, $op_prefixes ) = @_;
359             
360                 my $opcode = $op->[OPCODE];
361                 return () unless defined $op_prefixes->{$opcode};
362             
363                 my $op_sym = defined $op->[FLAG] ? $op->[FLAG] : $opcode;
364                 $op_sym = $op_prefixes->{$op_sym};
365                 return () unless defined $op_sym;
366             
367                 $a_or_b = $op->[OPCODE] ne "+" ? 0 : 1 unless defined $a_or_b;
368                 return ( $op_sym, $seqs->[$a_or_b][$op->[$a_or_b]] );
369             }
370             
371             
372             =head1 Formatting Classes
373             
374             These functions implement the output formats.  They are grouped in to classes
375             so diff() can use class names to call the correct set of output routines and so
376             that you may inherit from them easily.  There are no constructors or instance
377             methods for these classes, though subclasses may provide them if need be.
378             
379 rizwank 1.1 Each class has file_header(), hunk_header(), hunk(), and footer() methods
380             identical to those documented in the Text::Diff::Unified section.  header() is
381             called before the hunk() is first called, footer() afterwards.  The default
382             footer function is an empty method provided for overloading:
383             
384                 sub footer { return "End of patch\n" }
385             
386             Some output formats are provided by external modules (which are loaded
387             automatically), such as L<Text::Diff::Table>.  These are
388             are documented here to keep the documentation simple.
389             
390             =over
391             
392             =head2 Text::Diff::Base
393             
394             Returns "" for all methods (other than C<new()>).
395             
396             =cut
397             
398             {
399                 package Text::Diff::Base;
400 rizwank 1.1     sub new         {
401                     my $proto = shift;
402             	return bless { @_ }, ref $proto || $proto;
403                 }
404             
405                 sub file_header { return "" }
406                 sub hunk_header { return "" }
407                 sub hunk        { return "" }
408                 sub hunk_footer { return "" }
409                 sub file_footer { return "" }
410             }
411             
412             
413             =head2 Text::Diff::Unified
414             
415                 --- A   Mon Nov 12 23:49:30 2001
416                 +++ B   Mon Nov 12 23:49:30 2001
417                 @@ -2,13 +2,13 @@
418                  2
419                  3
420                  4
421 rizwank 1.1     -5d
422                 +5a
423                  6
424                  7
425                  8
426                  9
427                 +9a
428                  10
429                  11
430                 -11d
431                  12
432                  13
433             
434             =over
435             
436             =item file_header
437             
438                 $s = Text::Diff::Unified->file_header( $options );
439             
440             Returns a string containing a unified header.  The sole parameter is the
441             options hash passed in to diff(), containing at least:
442 rizwank 1.1 
443                 FILENAME_A  => $fn1,
444                 MTIME_A     => $mtime1,
445                 FILENAME_B  => $fn2,
446                 MTIME_B     => $mtime2
447             
448             May also contain
449             
450                 FILENAME_PREFIX_A    => "---",
451                 FILENAME_PREFIX_B    => "+++",
452             
453             to override the default prefixes (default values shown).
454             
455             =cut
456             
457             @Text::Diff::Unified::ISA = qw( Text::Diff::Base );
458             
459             sub Text::Diff::Unified::file_header {
460                 shift; ## No instance data
461                 my $options = pop ;
462             
463 rizwank 1.1     _header(
464                     { FILENAME_PREFIX_A => "---", FILENAME_PREFIX_B => "+++", %$options }
465                 );
466             }
467             
468             =item hunk_header
469             
470                 Text::Diff::Unified->hunk_header( \@ops, $options );
471             
472             Returns a string containing the output of one hunk of unified diff.
473             
474             =cut
475             
476             sub Text::Diff::Unified::hunk_header {
477                 shift; ## No instance data
478                 pop; ## Ignore options
479                 my $ops = pop;
480             
481                 return join( "",
482                     "@@ -",
483                     _range( $ops, A, "unified" ),
484 rizwank 1.1         " +",
485                     _range( $ops, B, "unified" ),
486                     " @@\n",
487                 );
488             }
489             
490             
491             =item Text::Diff::Unified::hunk
492             
493                 Text::Diff::Unified->hunk( \@seq_a, \@seq_b, \@ops, $options );
494             
495             Returns a string containing the output of one hunk of unified diff.
496             
497             =cut
498             
499             sub Text::Diff::Unified::hunk {
500                 shift; ## No instance data
501                 pop; ## Ignore options
502                 my $ops = pop;
503             
504                 my $prefixes = { "+" => "+", " " => " ", "-" => "-" };
505 rizwank 1.1 
506                 return join "", map _op_to_line( \@_, $_, undef, $prefixes ), @$ops
507             }
508             
509             
510             =back
511             
512             =head2 Text::Diff::Table
513             
514              +--+----------------------------------+--+------------------------------+
515              |  |../Test-Differences-0.2/MANIFEST  |  |../Test-Differences/MANIFEST  |
516              |  |Thu Dec 13 15:38:49 2001          |  |Sat Dec 15 02:09:44 2001      |
517              +--+----------------------------------+--+------------------------------+
518              |  |                                  * 1|Changes                       *
519              | 1|Differences.pm                    | 2|Differences.pm                |
520              | 2|MANIFEST                          | 3|MANIFEST                      |
521              |  |                                  * 4|MANIFEST.SKIP                 *
522              | 3|Makefile.PL                       | 5|Makefile.PL                   |
523              |  |                                  * 6|t/00escape.t                  *
524              | 4|t/00flatten.t                     | 7|t/00flatten.t                 |
525              | 5|t/01text_vs_data.t                | 8|t/01text_vs_data.t            |
526 rizwank 1.1  | 6|t/10test.t                        | 9|t/10test.t                    |
527              +--+----------------------------------+--+------------------------------+
528             
529             This format also goes to some pains to highlight "invisible" characters on
530             differing elements by selectively escaping whitespace:
531             
532              +--+--------------------------+--------------------------+
533              |  |demo_ws_A.txt             |demo_ws_B.txt             |
534              |  |Fri Dec 21 08:36:32 2001  |Fri Dec 21 08:36:50 2001  |
535              +--+--------------------------+--------------------------+
536              | 1|identical                 |identical                 |
537              * 2|        spaced in         |        also spaced in    *
538              * 3|embedded space            |embedded        tab       *
539              | 4|identical                 |identical                 |
540              * 5|        spaced in         |\ttabbed in               *
541              * 6|trailing spaces\s\s\n     |trailing tabs\t\t\n       *
542              | 7|identical                 |identical                 |
543              * 8|lf line\n                 |crlf line\r\n             *
544              * 9|embedded ws               |embedded\tws              *
545              +--+--------------------------+--------------------------+
546             
547 rizwank 1.1 See L</Text::Diff::Table> for more details, including how the whitespace
548             escaping works.
549             
550             =head2 Text::Diff::Context
551             
552                 *** A   Mon Nov 12 23:49:30 2001
553                 --- B   Mon Nov 12 23:49:30 2001
554                 ***************
555                 *** 2,14 ****
556                   2
557                   3
558                   4
559                 ! 5d
560                   6
561                   7
562                   8
563                   9
564                   10
565                   11
566                 - 11d
567                   12
568 rizwank 1.1       13
569                 --- 2,14 ----
570                   2
571                   3
572                   4
573                 ! 5a
574                   6
575                   7
576                   8
577                   9
578                 + 9a
579                   10
580                   11
581                   12
582                   13
583             
584             Note: hunk_header() returns only "***************\n".
585             
586             =cut
587             
588             
589 rizwank 1.1 @Text::Diff::Context::ISA = qw( Text::Diff::Base );
590             
591             sub Text::Diff::Context::file_header {
592                 _header { FILENAME_PREFIX_A=>"***", FILENAME_PREFIX_B=>"---", %{$_[-1]} };
593             }
594             
595             
596             sub Text::Diff::Context::hunk_header {
597                 return "***************\n";
598             }
599             
600             sub Text::Diff::Context::hunk {
601                 shift; ## No instance data
602                 pop; ## Ignore options
603                 my $ops = pop;
604                 ## Leave the sequences in @_[0,1]
605             
606                 my $a_range = _range( $ops, A, "" );
607                 my $b_range = _range( $ops, B, "" );
608             
609                 ## Sigh.  Gotta make sure that differences that aren't adds/deletions
610 rizwank 1.1     ## get prefixed with "!", and that the old opcodes are removed.
611                 my $after;
612                 for ( my $start = 0; $start <= $#$ops ; $start = $after ) {
613                     ## Scan until next difference
614                     $after = $start + 1;
615                     my $opcode = $ops->[$start]->[OPCODE];
616                     next if $opcode eq " ";
617             
618                     my $bang_it;
619                     while ( $after <= $#$ops && $ops->[$after]->[OPCODE] ne " " ) {
620                         $bang_it ||= $ops->[$after]->[OPCODE] ne $opcode;
621                         ++$after;
622                     }
623             
624                     if ( $bang_it ) {
625                         for my $i ( $start..($after-1) ) {
626                             $ops->[$i]->[FLAG] = "!";
627                         }
628                     }
629                 }
630             
631 rizwank 1.1     my $b_prefixes = { "+" => "+ ",  " " => "  ", "-" => undef, "!" => "! " };
632                 my $a_prefixes = { "+" => undef, " " => "  ", "-" => "- ",  "!" => "! " };
633             
634                 return join( "",
635                     "*** ", $a_range, " ****\n",
636                     map( _op_to_line( \@_, $_, A, $a_prefixes ), @$ops ),
637                     "--- ", $b_range, " ----\n",
638                     map( _op_to_line( \@_, $_, B, $b_prefixes ), @$ops ),
639                 );
640             }
641             =head2 Text::Diff::OldStyle
642             
643                 5c5
644                 < 5d
645                 ---
646                 > 5a
647                 9a10
648                 > 9a
649                 12d12
650                 < 11d
651             
652 rizwank 1.1 Note: no file_header().
653             
654             =cut
655             
656             @Text::Diff::OldStyle::ISA = qw( Text::Diff::Base );
657             
658             sub _op {
659                 my $ops = shift;
660                 my $op = $ops->[0]->[OPCODE];
661                 $op = "c" if grep $_->[OPCODE] ne $op, @$ops;
662                 $op = "a" if $op eq "+";
663                 $op = "d" if $op eq "-";
664                 return $op;
665             }
666             
667             sub Text::Diff::OldStyle::hunk_header {
668                 shift; ## No instance data
669                 pop; ## ignore options
670                 my $ops = pop;
671             
672                 my $op = _op $ops;
673 rizwank 1.1 
674                 return join "", _range( $ops, A, "" ), $op, _range( $ops, B, "" ), "\n";
675             }
676             
677             sub Text::Diff::OldStyle::hunk {
678                 shift; ## No instance data
679                 pop; ## ignore options
680                 my $ops = pop;
681                 ## Leave the sequences in @_[0,1]
682             
683                 my $a_prefixes = { "+" => undef,  " " => undef, "-" => "< "  };
684                 my $b_prefixes = { "+" => "> ",   " " => undef, "-" => undef };
685             
686                 my $op = _op $ops;
687             
688                 return join( "",
689                     map( _op_to_line( \@_, $_, A, $a_prefixes ), @$ops ),
690                     $op eq "c" ? "---\n" : (),
691                     map( _op_to_line( \@_, $_, B, $b_prefixes ), @$ops ),
692                 );
693             }
694 rizwank 1.1 
695             =head1 LIMITATIONS
696             
697             Must suck both input files entirely in to memory and store them with a normal
698             amount of Perlish overhead (one array location) per record.  This is implied by
699             the implementation of Algorithm::Diff, which takes two arrays.  If
700             Algorithm::Diff ever offers an incremental mode, this can be changed (contact
701             the maintainers of Algorithm::Diff and Text::Diff if you need this; it
702             shouldn't be too terribly hard to tie arrays in this fashion).
703             
704             Does not provide most of the more refined GNU diff options: recursive directory
705             tree scanning, ignoring blank lines / whitespace, etc., etc.  These can all be
706             added as time permits and need arises, many are rather easy; patches quite
707             welcome.
708             
709             Uses closures internally, this may lead to leaks on C<perl> versions 5.6.1 and
710             prior if used many times over a process' life time.
711             
712             =head1 AUTHOR
713             
714             Barrie Slaymaker <barries@slaysys.com>.
715 rizwank 1.1 
716             =head1 COPYRIGHT & LICENSE
717             
718             Copyright 2001, Barrie Slaymaker.  All Rights Reserved.
719             
720             You may use this under the terms of either the Artistic License or GNU Public
721             License v 2.0 or greater.
722             
723             =cut
724             
725             1;

Rizwan Kassim
Powered by
ViewCVS 0.9.2