summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/doc/latex/latexdiff/latexdiff-fast
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/doc/latex/latexdiff/latexdiff-fast')
-rwxr-xr-xMaster/texmf-dist/doc/latex/latexdiff/latexdiff-fast3344
1 files changed, 0 insertions, 3344 deletions
diff --git a/Master/texmf-dist/doc/latex/latexdiff/latexdiff-fast b/Master/texmf-dist/doc/latex/latexdiff/latexdiff-fast
deleted file mode 100755
index ee6c7419ac9..00000000000
--- a/Master/texmf-dist/doc/latex/latexdiff/latexdiff-fast
+++ /dev/null
@@ -1,3344 +0,0 @@
-#!/usr/bin/perl -w
-# latexdiff - differences two latex files on the word level
-# and produces a latex file with the differences marked up.
-#
-# Copyright (C) 2004-2007 F J Tilmann (tilmann@esc.cam.ac.uk)
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License Version 2 as published by
-# the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-# Detailed usage information at the end of the file
-#
-# Version 0.5 A number of minor improvements based on feedback
-# Deleted blocks are now shown before added blocks
-# Package specific processing
-#
-# Version 0.43 unreleased typo in list of styles at the end
-# Add protect to all \cbstart, \cbend commands
-# More robust substitution of deleted math commands
-#
-# Version 0.42 November 06 Bug fixes only
-#
-# Version 0.4 March 06 option for fast differencing using UNIX diff command, several minor bug fixes (\par bug, improved highlighting of textcmds)
-#
-# Version 0.3 August 05 improved parsing of displayed math, --allow-spaces
-# option, several minor bug fixes
-#
-# Version 0.25 October 04 Fix bug with deleted equations, add math mode commands to safecmd, add | to allowed interpunctuation signs
-# Version 0.2 September 04 extension to utf-8 and variable encodings
-# Version 0.1 August 04 First public release
-
-# Inserted block for differenceing
-# use Algorithm::Diff qw(traverse_sequences);
-# in standard version
-# The following BEGIN block contains a verbatim copy of
-# Ned Konz' Algorithm::Diff package version 1.15 except
-# that subroutine _longestCommonSubsequence has been replace by
-# a routine which internally uses the UNIX diff command for
-# the differencing rather than the Perl routines if the
-# length of the sequences exceeds some threshold.
-# Also, all POD documentation has been stripped out.
-#
-# (the distribution on which this modification is based is available
-# from http://search.cpan.org/~nedkonz/Algorithm-Diff-1.15
-# the most recent version can be found via http://search.cpan.org/search?module=Algorithm::Diff )
-# Please note that the LICENCSE for Algorithm::Diff :
-# "Copyright (c) 2000-2002 Ned Konz. All rights reserved.
-# This program is free software;
-# you can redistribute it and/or modify it under the same terms
-# as Perl itself."
-# The fast-differencing version of latexdiff is provided as a convenience
-# for latex users under Unix-like systems which have a 'diff' command.
-# If you believe
-# the inlining of Algorithm::Diff violates its license please contact
-# me and I will modify the latexdiff distribution accordingly.
-# Frederik Tilmann (tilmann@esc.cam.ac.uk)
-# Jonathan Paisley is acknowledged for the idea of using the system diff
-# command to achieve shorter running times
-BEGIN {
-package Algorithm::Diff;
-use strict;
-use vars qw($VERSION @EXPORT_OK @ISA @EXPORT);
-use integer; # see below in _replaceNextLargerWith() for mod to make
- # if you don't use this
-require Exporter;
-@ISA = qw(Exporter);
-@EXPORT = qw();
-@EXPORT_OK = qw(LCS diff traverse_sequences traverse_balanced sdiff);
-$VERSION = sprintf('%d.%02d fast', (q$Revision: 1.15 $ =~ /\d+/g));
-
-# Global parameters
-
-use File::Temp qw/tempfile/;
-# if larger number of elements in longestCommonSubsequence smaller than
-# this number, then use internal algorithm, otherwise use UNIX diff
-use constant THRESHOLD => 100 ;
-# Detect whether diff --minimal option is available
-# if yes we use it
-use constant MINIMAL => ( system('diff','--minimal','/dev/null','/dev/null') >> 8 ==0 ? "--minimal" : "" ) ;
-
-
-
-# McIlroy-Hunt diff algorithm
-# Adapted from the Smalltalk code of Mario I. Wolczko, <mario@wolczko.com>
-# by Ned Konz, perl@bike-nomad.com
-
-
-# Create a hash that maps each element of $aCollection to the set of positions
-# it occupies in $aCollection, restricted to the elements within the range of
-# indexes specified by $start and $end.
-# The fourth parameter is a subroutine reference that will be called to
-# generate a string to use as a key.
-# Additional parameters, if any, will be passed to this subroutine.
-#
-# my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen );
-
-sub _withPositionsOfInInterval
-{
- my $aCollection = shift; # array ref
- my $start = shift;
- my $end = shift;
- my $keyGen = shift;
- my %d;
- my $index;
- for ( $index = $start ; $index <= $end ; $index++ )
- {
- my $element = $aCollection->[$index];
- my $key = &$keyGen( $element, @_ );
- if ( exists( $d{$key} ) )
- {
- unshift ( @{ $d{$key} }, $index );
- }
- else
- {
- $d{$key} = [$index];
- }
- }
- return wantarray ? %d : \%d;
-}
-
-# Find the place at which aValue would normally be inserted into the array. If
-# that place is already occupied by aValue, do nothing, and return undef. If
-# the place does not exist (i.e., it is off the end of the array), add it to
-# the end, otherwise replace the element at that point with aValue.
-# It is assumed that the array's values are numeric.
-# This is where the bulk (75%) of the time is spent in this module, so try to
-# make it fast!
-
-sub _replaceNextLargerWith
-{
- my ( $array, $aValue, $high ) = @_;
- $high ||= $#$array;
-
- # off the end?
- if ( $high == -1 || $aValue > $array->[-1] )
- {
- push ( @$array, $aValue );
- return $high + 1;
- }
-
- # binary search for insertion point...
- my $low = 0;
- my $index;
- my $found;
- while ( $low <= $high )
- {
- $index = ( $high + $low ) / 2;
-
- # $index = int(( $high + $low ) / 2); # without 'use integer'
- $found = $array->[$index];
-
- if ( $aValue == $found )
- {
- return undef;
- }
- elsif ( $aValue > $found )
- {
- $low = $index + 1;
- }
- else
- {
- $high = $index - 1;
- }
- }
-
- # now insertion point is in $low.
- $array->[$low] = $aValue; # overwrite next larger
- return $low;
-}
-
-# This method computes the longest common subsequence in $a and $b.
-
-# Result is array or ref, whose contents is such that
-# $a->[ $i ] == $b->[ $result[ $i ] ]
-# foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined.
-
-# An additional argument may be passed; this is a hash or key generating
-# function that should return a string that uniquely identifies the given
-# element. It should be the case that if the key is the same, the elements
-# will compare the same. If this parameter is undef or missing, the key
-# will be the element as a string.
-
-# By default, comparisons will use "eq" and elements will be turned into keys
-# using the default stringizing operator '""'.
-
-# Additional parameters, if any, will be passed to the key generation routine.
-
-sub _longestCommonSubsequence
-{
- my $a = shift; # array ref
- my $b = shift; # array ref
- my $keyGen = shift; # code ref
- my $compare; # code ref
-
- # set up code refs
- # Note that these are optimized.
- if ( !defined($keyGen) ) # optimize for strings
- {
- $keyGen = sub { $_[0] };
- $compare = sub { my ( $a, $b ) = @_; $a eq $b };
- }
- else
- {
- $compare = sub {
- my $a = shift;
- my $b = shift;
- &$keyGen( $a, @_ ) eq &$keyGen( $b, @_ );
- };
- }
-
- my ( $aStart, $aFinish, $bStart, $bFinish, $matchVector ) =
- ( 0, $#$a, 0, $#$b, [] );
-
- # Check whether to use internal routine (small number of elements)
- # or use it as a wrapper for UNIX diff
- if ( ( $#$a > $#$b ? $#$a : $#$b) < THRESHOLD ) {
- ### print STDERR "DEBUG: regular longestCommonSubsequence\n";
- # First we prune off any common elements at the beginning
- while ( $aStart <= $aFinish
- and $bStart <= $bFinish
- and &$compare( $a->[$aStart], $b->[$bStart], @_ ) )
- {
- $matchVector->[ $aStart++ ] = $bStart++;
- }
-
- # now the end
- while ( $aStart <= $aFinish
- and $bStart <= $bFinish
- and &$compare( $a->[$aFinish], $b->[$bFinish], @_ ) )
- {
- $matchVector->[ $aFinish-- ] = $bFinish--;
- }
-
- # Now compute the equivalence classes of positions of elements
- my $bMatches =
- _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ );
- my $thresh = [];
- my $links = [];
-
- my ( $i, $ai, $j, $k );
- for ( $i = $aStart ; $i <= $aFinish ; $i++ )
- {
- $ai = &$keyGen( $a->[$i], @_ );
- if ( exists( $bMatches->{$ai} ) )
- {
- $k = 0;
- for $j ( @{ $bMatches->{$ai} } )
- {
-
- # optimization: most of the time this will be true
- if ( $k and $thresh->[$k] > $j and $thresh->[ $k - 1 ] < $j )
- {
- $thresh->[$k] = $j;
- }
- else
- {
- $k = _replaceNextLargerWith( $thresh, $j, $k );
- }
-
- # oddly, it's faster to always test this (CPU cache?).
- if ( defined($k) )
- {
- $links->[$k] =
- [ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ];
- }
- }
- }
- }
-
- if (@$thresh)
- {
- for ( my $link = $links->[$#$thresh] ; $link ; $link = $link->[0] )
- {
- $matchVector->[ $link->[1] ] = $link->[2];
- }
- }
- }
- else {
- my ($fha,$fhb,$fna,$fnb,$ele,$key);
- my ($alines,$blines,$alb,$alf,$blb,$blf);
- my ($minimal)=MINIMAL;
- # large number of elements, use system diff
- ### print STDERR "DEBUG: fast (diff) longestCommonSubsequence\n";
-
- ($fha,$fna)=tempfile("DiffA-XXXX") or die "_longestCommonSubsequence: Cannot open tempfile for sequence A";
- ($fhb,$fnb)=tempfile("DiffB-XXXX") or die "_longestCommonSubsequence: Cannot open tempfile for sequence B";
- # prepare sequence A
- foreach $ele ( @$a ) {
- $key=&$keyGen( $ele, @_ );
- $key =~ s/\\/\\\\/g ;
- $key =~ s/\n/\\n/sg ;
- print $fha "$key\n" ;
- }
- close($fha);
- # prepare sequence B
- foreach $ele ( @$b ) {
- $key=&$keyGen( $ele, @_ );
- $key =~ s/\\/\\\\/g ;
- $key =~ s/\n/\\n/sg ;
- print $fhb "$key\n" ;
- }
- close($fhb);
-
- open(DIFFPIPE, "diff $minimal $fna $fnb |") or die "_longestCommonSubsequence: Cannot launch diff process. $!" ;
- # The diff line numbering begins with 1, but Perl subscripts start with 0
- # We follow the diff numbering but substract 1 when assigning to matchVector
- $aStart++; $bStart++ ; $aFinish++ ; $bFinish++ ;
- while( <DIFFPIPE> ) {
- if ( ($alines,$blines) = ( m/^(\d*(?:,\d*)?)?c(\d*(?:,\d*)?)?$/ ) ) {
- ($alb,$alf)=split(/,/,$alines);
- ($blb,$blf)=split(/,/,$blines);
- $alf=$alb unless defined($alf);
- $blf=$blb unless defined($blf);
- while($aStart < $alb ) {
- $matchVector->[ -1 + $aStart++ ] = -1 + $bStart++ ;
- }
- # check for consistency
- $bStart==$blb or die "_longestCommonSubsequence: Fatal error in interpreting diff output: Inconsistency in changed sequence";
- $aStart=$alf+1;
- $bStart=$blf+1;
- }
- elsif ( ($alb,$blines) = ( m/^(\d*)a(\d*(?:,\d*)?)$/ ) ) {
- ($blb,$blf)=split(/,/,$blines);
- $blf=$blb unless defined($blf);
- while ( $bStart < $blb ) {
- $matchVector->[ -1 + $aStart++ ] = -1 + $bStart++ ;
- }
- $aStart==$alb+1 or die "_longestCommonSubsequence: Fatal error in interpreting diff output: Inconsistency in appended sequence near elements $aStart and $bStart";
- $bStart=$blf+1;
- }
- elsif ( ($alines,$blb) = ( m/^(\d*(?:,\d*)?)d(\d*)$/ ) ) {
- ($alb,$alf)=split(/,/,$alines);
- $alf=$alb unless defined($alf);
- while ( $aStart < $alb ) {
- $matchVector->[ -1 + $aStart++ ] = -1 + $bStart++ ;
- }
- $bStart==$blb+1 or die "_longestCommonSubsequence: Fatal error in interpreting diff output: Inconsistency in deleted sequence near elements $aStart and $bStart";
- $aStart=$alf+1;
- }
- elsif ( m/^Binary files/ ) {
- # if diff reports it is a binary file force --text mode. I do not like
- # to always use this option because it is probably only available in GNU diff
- open(DIFFPIPE, "diff --text $fna $fnb |") or die "Cannot launch diff process. $!" ;
- }
- # Default: just skip line
- }
- while ($aStart <= $aFinish ) {
- $matchVector->[ -1 + $aStart++ ] = -1 + $bStart++ ;
- }
- $bStart==$bFinish+1 or die "_longestCommonSubsequence: Fatal error in interpreting diff output: Inconsistency at end";
- close DIFFPIPE;
- # check whether a system error has occurred or return status is greater than or equal to 5
- if ( $! || ($? >> 8) > 5) {
- print STDERR "diff process failed with exit code ", ($? >> 8), " $!\n";
- die;
- }
- unlink $fna,$fnb ;
- }
- return wantarray ? @$matchVector : $matchVector;
-}
-
-sub traverse_sequences
-{
- my $a = shift; # array ref
- my $b = shift; # array ref
- my $callbacks = shift || {};
- my $keyGen = shift;
- my $matchCallback = $callbacks->{'MATCH'} || sub { };
- my $discardACallback = $callbacks->{'DISCARD_A'} || sub { };
- my $finishedACallback = $callbacks->{'A_FINISHED'};
- my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { };
- my $finishedBCallback = $callbacks->{'B_FINISHED'};
- my $matchVector = _longestCommonSubsequence( $a, $b, $keyGen, @_ );
-
- # Process all the lines in @$matchVector
- my $lastA = $#$a;
- my $lastB = $#$b;
- my $bi = 0;
- my $ai;
-
- for ( $ai = 0 ; $ai <= $#$matchVector ; $ai++ )
- {
- my $bLine = $matchVector->[$ai];
- if ( defined($bLine) ) # matched
- {
- &$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine;
- &$matchCallback( $ai, $bi++, @_ );
- }
- else
- {
- &$discardACallback( $ai, $bi, @_ );
- }
- }
-
- # The last entry (if any) processed was a match.
- # $ai and $bi point just past the last matching lines in their sequences.
-
- while ( $ai <= $lastA or $bi <= $lastB )
- {
-
- # last A?
- if ( $ai == $lastA + 1 and $bi <= $lastB )
- {
- if ( defined($finishedACallback) )
- {
- &$finishedACallback( $lastA, @_ );
- $finishedACallback = undef;
- }
- else
- {
- &$discardBCallback( $ai, $bi++, @_ ) while $bi <= $lastB;
- }
- }
-
- # last B?
- if ( $bi == $lastB + 1 and $ai <= $lastA )
- {
- if ( defined($finishedBCallback) )
- {
- &$finishedBCallback( $lastB, @_ );
- $finishedBCallback = undef;
- }
- else
- {
- &$discardACallback( $ai++, $bi, @_ ) while $ai <= $lastA;
- }
- }
-
- &$discardACallback( $ai++, $bi, @_ ) if $ai <= $lastA;
- &$discardBCallback( $ai, $bi++, @_ ) if $bi <= $lastB;
- }
-
- return 1;
-}
-
-sub traverse_balanced
-{
- my $a = shift; # array ref
- my $b = shift; # array ref
- my $callbacks = shift || {};
- my $keyGen = shift;
- my $matchCallback = $callbacks->{'MATCH'} || sub { };
- my $discardACallback = $callbacks->{'DISCARD_A'} || sub { };
- my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { };
- my $changeCallback = $callbacks->{'CHANGE'};
- my $matchVector = _longestCommonSubsequence( $a, $b, $keyGen, @_ );
-
- # Process all the lines in match vector
- my $lastA = $#$a;
- my $lastB = $#$b;
- my $bi = 0;
- my $ai = 0;
- my $ma = -1;
- my $mb;
-
- while (1)
- {
-
- # Find next match indices $ma and $mb
- do { $ma++ } while ( $ma <= $#$matchVector && !defined $matchVector->[$ma] );
-
- last if $ma > $#$matchVector; # end of matchVector?
- $mb = $matchVector->[$ma];
-
- # Proceed with discard a/b or change events until
- # next match
- while ( $ai < $ma || $bi < $mb )
- {
-
- if ( $ai < $ma && $bi < $mb )
- {
-
- # Change
- if ( defined $changeCallback )
- {
- &$changeCallback( $ai++, $bi++, @_ );
- }
- else
- {
- &$discardACallback( $ai++, $bi, @_ );
- &$discardBCallback( $ai, $bi++, @_ );
- }
- }
- elsif ( $ai < $ma )
- {
- &$discardACallback( $ai++, $bi, @_ );
- }
- else
- {
-
- # $bi < $mb
- &$discardBCallback( $ai, $bi++, @_ );
- }
- }
-
- # Match
- &$matchCallback( $ai++, $bi++, @_ );
- }
-
- while ( $ai <= $lastA || $bi <= $lastB )
- {
- if ( $ai <= $lastA && $bi <= $lastB )
- {
-
- # Change
- if ( defined $changeCallback )
- {
- &$changeCallback( $ai++, $bi++, @_ );
- }
- else
- {
- &$discardACallback( $ai++, $bi, @_ );
- &$discardBCallback( $ai, $bi++, @_ );
- }
- }
- elsif ( $ai <= $lastA )
- {
- &$discardACallback( $ai++, $bi, @_ );
- }
- else
- {
-
- # $bi <= $lastB
- &$discardBCallback( $ai, $bi++, @_ );
- }
- }
-
- return 1;
-}
-
-sub LCS
-{
- my $a = shift; # array ref
- my $matchVector = _longestCommonSubsequence( $a, @_ );
- my @retval;
- my $i;
- for ( $i = 0 ; $i <= $#$matchVector ; $i++ )
- {
- if ( defined( $matchVector->[$i] ) )
- {
- push ( @retval, $a->[$i] );
- }
- }
- return wantarray ? @retval : \@retval;
-}
-
-sub diff
-{
- my $a = shift; # array ref
- my $b = shift; # array ref
- my $retval = [];
- my $hunk = [];
- my $discard = sub { push ( @$hunk, [ '-', $_[0], $a->[ $_[0] ] ] ) };
- my $add = sub { push ( @$hunk, [ '+', $_[1], $b->[ $_[1] ] ] ) };
- my $match = sub { push ( @$retval, $hunk ) if scalar(@$hunk); $hunk = [] };
- traverse_sequences( $a, $b,
- { MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add }, @_ );
- &$match();
- return wantarray ? @$retval : $retval;
-}
-
-sub sdiff
-{
- my $a = shift; # array ref
- my $b = shift; # array ref
- my $retval = [];
- my $discard = sub { push ( @$retval, [ '-', $a->[ $_[0] ], "" ] ) };
- my $add = sub { push ( @$retval, [ '+', "", $b->[ $_[1] ] ] ) };
- my $change = sub {
- push ( @$retval, [ 'c', $a->[ $_[0] ], $b->[ $_[1] ] ] );
- };
- my $match = sub {
- push ( @$retval, [ 'u', $a->[ $_[0] ], $b->[ $_[1] ] ] );
- };
- traverse_balanced(
- $a,
- $b,
- {
- MATCH => $match,
- DISCARD_A => $discard,
- DISCARD_B => $add,
- CHANGE => $change,
- },
- @_
- );
- return wantarray ? @$retval : $retval;
-}
-
-1;
-}
-import Algorithm::Diff qw(traverse_sequences);
-# End of inserted block for stand-alone version
-
-
-use Getopt::Long ;
-use strict ;
-use utf8 ;
-
-my ($algodiffversion)=split(/ /,$Algorithm::Diff::VERSION);
-
-
-my ($versionstring)=<<EOF ;
-This is LATEXDIFF 0.5 (Algorithm::Diff $Algorithm::Diff::VERSION)
- (c) 2004-2007 F J Tilmann
-EOF
-
-# Configuration variables: these have to be visible from the subroutines
-my $MINWORDSBLOCK=3; # minimum number of tokens to form an independent block
- # shorter identical blocks will be merged to the previous word
-my $FLOATENV='(?:figure|table|plate)[\w\d*@]*' ; # Environments in which FL variants of defined commands are used
-my $PICTUREENV='(?:picture|DIFnomarkup)[\w\d*@]*' ; # Environments in which all change markup is removed
-my $MATHENV='(?:equation|displaymath|DOLLARDOLLAR)' ; # Environments turning on display math mode (code also knows about \[ and \]
-my $MATHREPL='displaymath'; # Environment introducing deleted maths blocks
-my $MATHARRENV='(?:eqnarray|align|gather|multiline|flalign)[*]?' ; # Environments turning on eqnarray math mode
-my $MATHARRREPL='eqnarray*'; # Environment introducing deleted maths blocks
-my $ARRENV='(?:array|[pbvBV]matrix)'; # Enviroments making arrays in math mode. The underlining style does not cope well with those - as a result in-text math environments are surrounded by \mbox{ } if any of these commands is used in an inline math block
-my $COUNTERCMD='(?:footnote|part|section|subsection|subsubsection|paragraph|subparagraph)'; # textcmds which are associated with a counter
- # If any of these commands occur in a deleted block
- # they will be succeeded by an \addtocounter{...}{-1}
- # for the associated counter such that the overall numbers
- # should be the same as in the new file
-
-# Markup strings
-# If at all possible, do not change these as parts of the program
-# depend on the actual name (particularly post-processing)
-# At the very least adapt subroutine postprocess to new tokens.
-my $ADDMARKOPEN='\DIFaddbegin '; # Token to mark begin of appended text
-my $ADDMARKCLOSE='\DIFaddend '; # Token to mark end of appended text
-my $ADDOPEN='\DIFadd{'; # To mark begin of added text passage
-my $ADDCLOSE='}'; # To mark end of added text passage
-my $ADDCOMMENT='DIF > '; # To mark added comment line
-my $DELMARKOPEN='\DIFdelbegin '; # Token to mark begin of deleted text
-my $DELMARKCLOSE='\DIFdelend '; # Token to mark end of deleted text
-my $DELOPEN='\DIFdel{'; # To mark begin of deleted text passage
-my $DELCLOSE='}'; # To mark end of deleted text passage
-my $DELCMDOPEN='%DIFDELCMD < '; # To mark begin of deleted commands (must begin with %, i.e., be a comment
-my $DELCMDCLOSE="%%%\n"; # To mark end of deleted commands (must end with a new line)
-my $AUXCMD='%DIFAUXCMD' ; # follows auxiliary commands put in by latexdiff to make difference file legal
- # auxiliary commands must be on a line of their own
-my $DELCOMMENT='DIF < '; # To mark deleted comment line
-
-
-# main local variables:
-my @TEXTCMDLIST=(); # array containing patterns of commands with text arguments
-my @TEXTCMDEXCL=(); # array containing patterns of commands without text arguments (if a pattern
- # matches both TEXTCMDLIST and TEXTCMDEXCL it is excluded)
-my @CONTEXT1CMDLIST=(); # array containing patterns of commands with text arguments (subset of text commands),
- # but which cause confusion if used out of context (e.g. \caption).
- # In deleted passages, the command will be disabled but its argument is marked up
- # Otherwise they behave exactly like TEXTCMD's
-my @CONTEXT1CMDEXCL=(); # exclude list for above, but always empty
-my @CONTEXT2CMDLIST=(); # array containing patterns of commands with text arguments, but which fail cause confusion
- # if used out of context (e.g. \title). They and their arguments will be disabled in deleted
- # passages
-my @CONTEXT2CMDEXCL=(); # exclude list for above, but always empty
-
-my @SAFECMDLIST=(); # array containing patterns of safe commands (which do not break when in the argument of DIFadd or DIFDEL)
-my @SAFECMDEXCL=();
-
-my ($i,$j,$l);
-my ($old,$new);
-my ($line);
-my ($newpreamble,$oldpreamble);
-my (@newpreamble,@oldpreamble,@diffpreamble,@diffbody);
-my ($latexdiffpreamble);
-my ($oldbody, $newbody, $diffbo);
-my ($oldpost, $newpost);
-my ($diffall);
-# Option names
-my ($type,$subtype,$floattype,$config,$preamblefile,$encoding,
- $showpreamble,$showsafe,$showtext,$showconfig,$showall,
- $replacesafe,$appendsafe,$excludesafe,
- $replacetext,$appendtext,$excludetext,
- $replacecontext1,$appendcontext1,
- $replacecontext2,$appendcontext2,
- $help,$verbose,$version,$ignorewarnings,$allowspaces,$flatten,$debug);
-my (@configlist,
- @appendsafelist,@excludesafelist,
- @appendtextlist,@excludetextlist,
- @appendcontext1list,@appendcontext2list,
- @packagelist);
-my ($assign,@config);
-# Hash where keys corresponds to the names of all included packages (including the documentclass as another package
-# the optional arguments to the package are the values of the hash elements
-my ($pkg,%packages);
-# Defaults
-$type='UNDERLINE';
-$subtype='SAFE';
-$floattype='FLOATSAFE';
-
-$verbose=0;
-$debug=1;
-# define character properties
-sub IsNonAsciiPunct { return <<'END' # Unicode punctuation but excluding ASCII punctuation
-+utf8::IsPunct
--utf8::IsASCII
-END
-}
-sub IsNonAsciiS { return <<'END' # Unicode symbol but excluding ASCII
-+utf8::IsS
--utf8::IsASCII
-END
-}
-
-
-
- my %verbhash;
-
-Getopt::Long::Configure('bundling');
-GetOptions('type|t=s' => \$type,
- 'subtype|s=s' => \$subtype,
- 'floattype|f=s' => \$floattype,
- 'config|c=s' => \@configlist,
- 'preamble|p=s' => \$preamblefile,
- 'encoding|e=s' => \$encoding,
- 'exclude-safecmd|A=s' => \@excludesafelist,
- 'replace-safecmd=s' => \$replacesafe,
- 'append-safecmd|a=s' => \@appendsafelist,
- 'exclude-textcmd|X=s' => \@excludetextlist,
- 'replace-textcmd=s' => \$replacetext,
- 'append-textcmd|x=s' => \@appendtextlist,
- 'replace-context1cmd=s' => \$replacecontext1,
- 'append-context1cmd=s' => \@appendcontext1list,
- 'replace-context2cmd=s' => \$replacecontext2,
- 'append-context2cmd=s' => \@appendcontext2list,
- 'show-preamble' => \$showpreamble,
- 'show-safecmd' => \$showsafe,
- 'show-textcmd' => \$showtext,
- 'show-config' => \$showconfig,
- 'show-all' => \$showall,
- 'packages=s' => \@packagelist,
- 'verbose|V' => \$verbose,
- 'ignore-warnings' => \$ignorewarnings,
- 'allow-spaces' => \$allowspaces,
- 'flatten' => \$flatten,
- 'version' => \$version,
- 'help|h|H' => \$help);
-
-if ( $help ) {
- usage() ;
-}
-
-
-if ( $version ) {
- die $versionstring ;
-}
-
-print STDERR $versionstring if $verbose;
-
-if (defined($showall)){
- $showpreamble=$showsafe=$showtext=$showconfig=1;
-}
-
-# setting extra preamble commands
-if (defined($preamblefile)) {
- $latexdiffpreamble=join "\n",(extrapream($preamblefile),"");
-} else {
- $latexdiffpreamble=join "\n",(extrapream($type,$subtype,$floattype),"");
-}
-
-# setting up @SAFECMDLIST and @SAFECMDEXCL
-if (defined($replacesafe)) {
- init_regex_arr_ext(\@SAFECMDLIST,$replacesafe);
-} else {
- init_regex_arr_data(\@SAFECMDLIST, "SAFE COMMANDS");
-}
-foreach $appendsafe ( @appendsafelist ) {
- init_regex_arr_ext(\@SAFECMDLIST, $appendsafe);
-}
-foreach $excludesafe ( @excludesafelist ) {
- init_regex_arr_ext(\@SAFECMDEXCL, $excludesafe);
-}
-
-# Special: treat all cite commands as safe except in UNDERLINE and FONTSTRIKE mode
-# (there is a conflict between citation and ulem package, see
-# package documentation)
-if ( uc($type) ne "UNDERLINE" && uc($type) ne "FONTSTRIKE" && uc($type) ne "CULINECHBAR" ) {
- push (@SAFECMDLIST, qr/^cite.*$/);
-}
-
-# setting up @TEXTCMDLIST and @TEXTCMDEXCL
-if (defined($replacetext)) {
- init_regex_arr_ext(\@TEXTCMDLIST,$replacetext);
-} else {
- init_regex_arr_data(\@TEXTCMDLIST, "TEXT COMMANDS");
-}
-foreach $appendtext ( @appendtextlist ) {
- init_regex_arr_ext(\@TEXTCMDLIST, $appendtext);
-}
-foreach $excludetext ( @excludetextlist ) {
- init_regex_arr_ext(\@TEXTCMDEXCL, $excludetext);
-}
-
-
-# setting up @CONTEXT1CMDLIST ( @CONTEXT1CMDEXCL exist but is always empty )
-if (defined($replacecontext1)) {
- init_regex_arr_ext(\@CONTEXT1CMDLIST,$replacecontext1);
-} else {
- init_regex_arr_data(\@CONTEXT1CMDLIST, "CONTEXT1 COMMANDS");
-}
-foreach $appendcontext1 ( @appendcontext1list ) {
- init_regex_arr_ext(\@CONTEXT1CMDLIST, $appendcontext1);
-}
-
-
-# setting up @CONTEXT2CMDLIST ( @CONTEXT2CMDEXCL exist but is always empty )
-if (defined($replacecontext2)) {
- init_regex_arr_ext(\@CONTEXT2CMDLIST,$replacecontext2);
-} else {
- init_regex_arr_data(\@CONTEXT2CMDLIST, "CONTEXT2 COMMANDS");
-}
-foreach $appendcontext2 ( @appendcontext2list ) {
- init_regex_arr_ext(\@CONTEXT2CMDLIST, $appendcontext2);
-}
-
-
-
-
-# setting configuration variables
-@config=();
-foreach $config ( @configlist ) {
- if (-f $config ) {
- open(FILE,$config) or die ("Couldn't open configuration file $config: $!");
- while (<FILE>) {
- chomp;
- next if /^\s*#/ || /^\s*%/ || /^\s*$/ ;
- push (@config,$_);
- }
- close(FILE);
- }
- else {
-# foreach ( split(",",$config) ) {
-# push @config,$_;
-# }
- push @config,split(",",$config)
- }
-}
-foreach $assign ( @config ) {
- $assign=~ m/\s*(\w*)\s*=\s*(\S*)\s*$/ or die "Illegal assignment $assign in configuration list (must be variable=value)";
- if ( $1 eq "MINWORDSBLOCK" ) { $MINWORDSBLOCK = $2; }
- elsif ( $1 eq "FLOATENV" ) { $FLOATENV = $2 ; }
- elsif ( $1 eq "PICTUREENV" ) { $PICTUREENV = $2 ; }
- elsif ( $1 eq "MATHENV" ) { $MATHENV = $2 ; }
- elsif ( $1 eq "MATHREPL" ) { $MATHREPL = $2 ; }
- elsif ( $1 eq "MATHARRENV" ) { $MATHARRENV = $2 ; }
- elsif ( $1 eq "MATHARRREPL" ) { $MATHARRREPL = $2 ; }
- elsif ( $1 eq "ARRENV" ) { $ARRENV = $2 ; }
- elsif ( $1 eq "COUNTERCMD" ) { $COUNTERCMD = $2 ; }
- else { die "Unknown variable $1 in assignment.";}
-}
-
-
-foreach $pkg ( @packagelist ) {
- map { $packages{$_}="" } split(/,/,$pkg) ;
-}
-
-if ($showpreamble) {
- print "\nPreamble commands:\n";
- print $latexdiffpreamble ;
-}
-
-if ($showsafe) {
- print "\nCommands safe within scope of $ADDOPEN $ADDCLOSE and $DELOPEN $DELCLOSE (unless excluded):\n";
- print_regex_arr(@SAFECMDLIST);
- print "\nCommands not safe within scope of $ADDOPEN $ADDCLOSE and $DELOPEN $DELCLOSE :\n";
- print_regex_arr(@SAFECMDEXCL);
-}
-
-if ($showtext) {
- print "\nCommands with last argument textual (unless excluded) and safe in every context:\n";
- print_regex_arr(@TEXTCMDLIST);
- print "\nContext1 commands (last argument textual, command will be disabled in deleted passages, last argument will be shown as plain text):\n";
- print_regex_arr(@CONTEXT1CMDLIST);
- print "\nContext2 commands (last argument textual, command ant its argument will be disabled in deleted passages):\n";
- print_regex_arr(@CONTEXT2CMDLIST);
- print "\nExclude list of Commands with last argument not textual (overrides patterns above):\n";
- print_regex_arr(@TEXTCMDEXCL);
-}
-
-if ($showconfig) {
- print "Configuration variables:\n";
- print "MINWORDSBLOCK=$MINWORDSBLOCK\n";
- print "FLOATENV=$FLOATENV\n";
- print "PICTUREENV=$PICTUREENV\n";
- print "MATHENV=$MATHENV\n";
- print "MATHREPL=$MATHREPL\n";
- print "MATHARRENV=$MATHARRENV\n";
- print "MATHARRREPL=$MATHARRREPL\n";
- print "ARRENV=$ARRENV\n";
- print "COUNTERCMD=$COUNTERCMD\n";
-}
-if ($showconfig || $showtext || $showsafe || $showpreamble) {
- exit 0; }
-if ( @ARGV != 2 ) {
- print STDERR "2 and only 2 non-option arguments required. Write latexdiff -h to get help\n";
- exit(2);
-}
-
-# Are extra spaces between command arguments permissible?
-my $extraspace;
-if ($allowspaces) {
- $extraspace='\s*';
-} else {
- $extraspace='';
-}
-
-# append context lists to text lists (as text property is implied)
-push @TEXTCMDLIST, @CONTEXT1CMDLIST;
-push @TEXTCMDLIST, @CONTEXT2CMDLIST;
-
-
-# Patterns. These are used by some of the subroutines, too
-# I can only define them down here because value of extraspace depends on an option
- my $pat0 = '(?:[^{}]|\\\{|\\\})*';
- my $pat1 = '(?:[^{}]|\\\{|\\\}|\{'.$pat0.'\})*';
- my $pat2 = '(?:[^{}]|\\\{|\\\}|\{'.$pat1.'\})*'; #
- my $pat3 = '(?:[^{}]|\\\{|\\\}|\{'.$pat2.'\})*';
- my $pat4 = '(?:[^{}]|\\\{|\\\}|\{'.$pat3.'\})*';
- my $brat0 = '(?:[^\[\]]|\\\[|\\\])*';
-
- my $quotemarks = '(?:\'\')|(?:\`\`)';
- my $punct='[0.,\/\'\`:;\"\?\(\)\[\]!~\p{IsNonAsciiPunct}\p{IsNonAsciiS}]';
- my $number='-?\d*\.\d*';
- my $mathpunct='[+=<>\-\|]';
- my $and = '&';
- my $coords= '[\-.,\s\d]*';
-# word: sequence of letters or accents followed by letter
- my $word='(?:[-\w\d*]|\\\\[\"\'\`~^][A-Za-z\*])+';
- my $cmdleftright='\\\\(?:left|right)\s*(?:[()\[\]|]|\\\\(?:[|{}]|\w+))';
-
- my $cmdoptseq='\\\\[\w\d\*]+'.$extraspace.'(?:(?:\['.$brat0.'\]|\{'. $pat4 . '\}|\(' . $coords .'\))'.$extraspace.')*';
- my $oneletcmd='\\\\.(?:\['.$brat0.'\]|\{'. $pat4 . '\})*';
- my $math='\$(?:[^$]|\\\$)*?\$|\\\\[(].*?\\\\[)]';
-# my $math='\$(?:[^$]|\\\$)*\$';
- my $comment='%.*?\n';
- my $pat=qr/(?:\A\s*)?(?:${and}|${quotemarks}|${number}|${word}|$cmdleftright|${cmdoptseq}|${math}|${oneletcmd}|${comment}|${punct}|${mathpunct}|\{|\})\s*/ ;
-
-
-
-# now we are done setting up and can start working
-my ($oldfile, $newfile) = @ARGV;
-
-$encoding=guess_encoding($newfile) unless defined($encoding);
-
-$encoding = "utf8" if $encoding =~ m/^utf8/i ;
-if (lc($encoding) eq "utf8" ) {
- binmode(STDOUT, ":utf8");
- binmode(STDERR, ":utf8");
-}
-
-$old=read_file_with_encoding($oldfile,$encoding);
-$new=read_file_with_encoding($newfile,$encoding);
-
-
-
-
-# reset time
-exetime(1);
-($oldpreamble,$oldbody,$oldpost)=splitdoc($old,'\\\\begin\{document\}','\\\\end\{document\}');
-
-
-($newpreamble,$newbody,$newpost)=splitdoc($new,'\\\\begin\{document\}','\\\\end\{document\}');
-
-
-if ($flatten) {
- $oldbody=flatten($oldbody,$oldpreamble,$oldfile,$encoding);
- $newbody=flatten($newbody,$newpreamble,$newfile,$encoding);
-}
-
-if ( length $oldpreamble && length $newpreamble ) {
- @oldpreamble = split /\n/, $oldpreamble;
- @newpreamble = split /\n/, $newpreamble;
-
- %packages=list_packages(@newpreamble) unless %packages;
- if (defined $packages{"hyperref"} ) {
- print STDERR "hyperref package detected.\n" if $verbose ;
- $latexdiffpreamble =~ s/\{\\DIFadd\}/{\\DIFaddtex}/g;
- $latexdiffpreamble =~ s/\{\\DIFdel\}/{\\DIFdeltex}/g;
- $latexdiffpreamble .= join "\n",(extrapream("HYPERREF"),"");
- }
- # insert dummy first line such that line count begins with line 1 (rather than perl's line 0) - just so that line numbers inserted by linediff are correct
- unshift @newpreamble,'';
- unshift @oldpreamble,'';
- print STDERR "Differencing preamble.\n" if $verbose;
- @diffpreamble = linediff(\@oldpreamble, \@newpreamble);
- # remove dummy line again
- shift @diffpreamble;
- push @diffpreamble,$latexdiffpreamble;
- push @diffpreamble,'\begin{document}';
-}
-elsif ( !length $oldpreamble && !length $newpreamble ) {
- @diffpreamble=();
-} else {
- print STDERR "Either both texts must have preamble or neither text must have the preamble.\n";
- exit(2);
-}
-
-if (defined $packages{"amsmath"} ) {
- print STDERR "amsmath package detected.\n" if $verbose ;
- $MATHARRREPL='align*';
-}
-
-print STDERR "Preprocessing body. " if $verbose;
-my ($oldleadin,$newleadin)=preprocess($oldbody,$newbody);
-
-
-# run difference algorithm
-@diffbody=bodydiff($oldbody, $newbody);
-$diffbo=join("",@diffbody);
-print STDERR "(",exetime()," s)\n","Postprocessing body. \n " if $verbose;
-postprocess($diffbo);
-$diffall =join("\n",@diffpreamble) ;
-$diffall .= "$newleadin$diffbo" ;
-$diffall .= "\\end{document}$newpost" if length $newpreamble ;
-if ( lc($encoding) ne "utf8" && lc($encoding) ne "ascii" ) {
- print STDERR "Encoding output file to $encoding\n" if $verbose;
- $diffall=Encode::encode($encoding,$diffall);
- binmode STDOUT;
-}
-print $diffall;
-
-
-print STDERR "(",exetime()," s)\n","Done.\n" if $verbose;
-
-
-
-## guess_encoding(filename)
-## reads the first 20 lines of filename and looks for call of inputenc package
-## if found, return the option of this package (encoding), otherwise return ascii
-sub guess_encoding {
- my ($filename)=@_;
- my ($i,$enc);
- open (FH, $filename) or die("Couldn't open $filename: $!");
- $i=0;
- while (<FH>) {
- next if /^\s*%/; # skip comment lines
- if (m/\\usepackage\[(\w*?)\]\{inputenc\}/) {
- close(FH);
- return($1);
- }
- last if (++$i > 20 ); # scan at most 20 non-comment lines
- }
- close(FH);
- return("ascii");
-}
-
-
-sub read_file_with_encoding {
- my ($output);
- my ($filename, $encoding) = @_;
-
- if (lc($encoding) eq "utf8" ) {
- open (FILE, "<:utf8",$filename) or die("Couldn't open $filename: $!");
- local $/ ; # locally set record operator to undefined, ie. enable whole-file mode
- $output=<FILE>;
- } elsif ( lc($encoding) eq "ascii") {
- open (FILE, $filename) or die("Couldn't open $filename: $!");
- local $/ ; # locally set record operator to undefined, ie. enable whole-file mode
- $output=<FILE>;
- } else {
- require Encode;
- open (FILE, "<",$filename) or die("Couldn't open $filename: $!");
- local $/ ; # locally set record operator to undefined, ie. enable whole-file mode
- $output=<FILE>;
- print STDERR "Converting $filename from $encoding to utf8\n" if $verbose;
- $output=Encode::decode($encoding,$output);
- }
- close FILE;
- return $output;
-}
-
-# %packages=list_packages(@preamble)
-# scans the arguments for \documentclass and \usepackage statements and constructs a hash
-# whose keys are the included packages, and whose values are the associated optional arguments
-sub list_packages {
- my (@preamble)=@_;
- my %packages=();
- foreach $line ( @preamble ) {
- # get rid of comments
- $line=~s/(?<!\\)%.*$// ;
- if ( $line =~ m/\\(?:documentclass|usepackage)(?:\[(.+?)\])?\{(.*?)\}/ ) {
-# print STDERR "Found something: |$line|\n";
- if (defined($1)) {
- $packages{$2}=$1;
- } else {
- $packages{$2}="";
- }
- }
- }
- return (%packages);
-}
-
-# flatten($text,$preamble,$filename,$encoding)
-# expands \input and \include commands within text
-# premable is scanned for includeonly commands
-# encoding is the encoding
-sub flatten {
- my ($text,$preamble,$filename,$encoding)=@_;
- my ($includeonly,$dirname,$fname,$newpage,$replacement);
- require File::Basename ;
- require File::Spec ;
- $dirname = File::Basename::dirname($filename);
-
- if ( ($includeonly) = ($preamble =~ m/\\includeonly{(.*?)}/ ) ) {
- $includeonly =~ s/,/|/g;
- } else {
- $includeonly = '.*?';
- }
-
- print STDERR "DEBUG: includeonly $includeonly\n";
-
- $text=~s/\\input{(.*?)}|\\include{(${includeonly}(?:\.tex)?)}/{
- $fname = $1 if defined($1) ;
- $fname = $2 if defined($2) ;
- # # add tex extension unless there is a three letter extension already
- $fname .= ".tex" unless $fname =~ m|\.\w{3}|;
- print STDERR "Include file $fname\n" if $verbose;
- print STDERR "DEBUG looking for file ",File::Spec->catfile($dirname,$fname), "\n";
- # content of file becomes replacement value, add \newpage if the command was include
- $replacement=read_file_with_encoding(File::Spec->catfile($dirname,$fname), $encoding) or die
- "Couldn't find file ",File::Spec->catfile($dirname,$fname),": $!";
- $newpage=(defined($2)? " \\newpage " : "") ;
- "$newpage$replacement$newpage";
- }/exg;
-
- return($text);
-}
-
-
-# print_regex_arr(@arr)
-# prints regex array without x-ism expansion put in by pearl to stdout
-sub print_regex_arr {
- my $dumstring;
- $dumstring = join(" ",@_); # PERL generates string (?-xism:^ref$) for quoted refex ^ref$
- $dumstring =~ s/\(\?-xism:\^(.*?)\$\)/$1/g; # remove string and ^,$ marks before output
- print $dumstring,"\n";
-}
-
-
-# @lines=extrapream($type)
-# reads line from appendix (end of file after __END__ token)
-sub extrapream {
- my $type;
- my @retval=("%DIF PREAMBLE EXTENSION ADDED BY LATEXDIFF") ;
- my ($copy);
-
- while (@_) {
- $copy=0;
- $type=shift ;
- if ( -f $type ) {
- open (FILE,$type) or die "Cannot open preamble file $type: $!";
- print STDERR "Reading preamble file $type\n" if $verbose ;
- while (<FILE>) {
- chomp ;
- if ( $_ =~ m/%DIF PREAMBLE/ ) {
- push (@retval,"$_");
- } else {
- push (@retval,"$_ %DIF PREAMBLE");
- }
- }
- }
- else { # not (-f $type)
- $type=uc($type); # upcase argument
- print STDERR "Preamble Internal Type $type\n" if $verbose;
- while (<DATA>) {
- if ( m/^%DIF $type/ ) {
- $copy=1; }
- elsif ( m/^%DIF END $type/ ) {
- last; }
- chomp;
- push (@retval,"$_ %DIF PREAMBLE") if $copy;
- }
- if ( $copy == 0 ) {
- print STDERR "\nPreamble style $type not implemented.\n";
- print STDERR "Write latexdiff -h to get help with available styles\n";
- exit(2);
- }
- seek DATA,0,0; # rewind DATA handle to file begin
- }
- }
- push (@retval,"%DIF END PREAMBLE EXTENSION ADDED BY LATEXDIFF") ;
- return @retval;
-}
-
-
-# ($part1,$part2,$part3)=splitdoc($text,$word1,$word2)
-# splits $text into 3 parts at $word1 and $word2.
-# if neither $word1 nor $word2 exist, $part1 and $part3 are empty, $part2 is $text
-# If only $word1 or $word2 exist but not the other, output an error message.
-
-# NB this version avoids $` and $' for performance reason although it only makes a tiny difference
-# (in one test gain a tenth of a second for a 30s run)
-sub splitdoc {
- my ($text,$word1,$word2)=@_;
- my ($part1,$part2,$part3)=("","","");
- my ($rest,$pos);
-
- if ( $text =~ m/(^[^%]*)($word1)/mg ) {
- $pos=pos $text;
- $part1=substr($text,0,$pos-length($2));
- $rest=substr($text,$pos);
- if ( $rest =~ m/(^[^%]*)($word2)/mg ) {
- $pos=pos $rest;
- $part2=substr($rest,0,$pos-length($2));
- $part3=substr($rest,$pos);
- }
- else {
- die "$word1 and $word2 not in the correct order or not present as a pair." ;
- }
- } else {
- $part2=$text;
- die "$word2 present but not $word1." if ( $text =~ m/(^[^%]*)$word2/ms );
- }
- return ($part1,$part2,$part3);
-}
-
-
-
-
-
-# bodydiff($old,$new)
-sub bodydiff {
- my ($oldwords, $newwords) = @_;
- my @retwords;
-
- print STDERR "(",exetime()," s)\n","Splitting into latex tokens \n" if $verbose;
- print STDERR "Parsing $oldfile \n" if $verbose;
- my @oldwords = splitlatex($oldwords);
- print STDERR "Parsing $newfile \n" if $verbose;
- my @newwords = splitlatex($newwords);
- my $token;
-
- print STDERR "(",exetime()," s)\n","Pass 1: Expanding text commands and merging isolated identities with changed blocks " if $verbose;
- pass1(\@oldwords, \@newwords);
-
-
- print STDERR "(",exetime()," s)\n","Pass 2: inserting DIF tokens and mark up. " if $verbose;
-
- @retwords=pass2(\@oldwords, \@newwords);
-
- return(@retwords);
-}
-
-
-
-
-# @words=splitlatex($string)
-# split string according to latex rules
-# Each element of words is either
-# a word (including trailing spaces and punctuation)
-# a latex command
-sub splitlatex {
- my ($string) = @_ ;
- my @retval=($string =~ m/$pat/osg);
-
- if (length($string) != length(join("",@retval))) {
- print STDERR "\nWARNING: Inconsistency in length of input string and parsed string:\n This often indicates faulty or non-standard latex code.\n In many cases you can ignore this and the following warning messages.\n Note that character numbers in the following are counted beginning after \\begin{document} and are only approximate." unless $ignorewarnings;
- print STDERR "DEBUG Original length ",length($string)," Parsed length ",length(join("",@retval)),"\n" if $debug;
- print STDERR "DEBUG Input string: |$string|\n" if (length($string)<500) && $debug;
- print STDERR "DEBUG Token parsing: |",join("+",@retval),"|\n" if (length($string)<500) && $debug ;
- @retval=();
- # slow way only do this if other m//sg method fails
- my $last = 0;
- while ( $string =~ m/$pat/osg ) {
- my $match=$&;
- if ($last + length $& != pos $string ) {
- my $pos=pos($string);
- my $offset=30<$last ? 30 : $last;
- my $dum=substr($string,$last-$offset,$pos-$last+2*$offset);
- my $dum1=$dum;
- my $cnt=$#retval;
- my $i;
- $dum1 =~ s/\n/ /g;
- unless ($ignorewarnings) {
- print STDERR "\n$dum1\n";
- print STDERR " " x 30,"^" x ($pos-$last)," " x 30,"\n";
- print STDERR "Missing characters near word " . (scalar @retval) . " character index: " . $last . "-" . pos($string) . " Length: " . length($match) . " Match: |$match| (expected match marked above).\n";
- }
- # put in missing characters `by hand'
- push (@retval, substr($dum,$offset,$pos-$last-length($match)));
-# Note: there seems to be a bug in substr with utf8 that made the following line output substr which were too long,
-# using dum instead appears to work
-# push (@retval, substr($string,$last, pos($string)-$last-length($match)));
- }
- push (@retval, $match);
- $last=pos $string;
- }
-
- }
- return @retval;
-}
-
-
-# pass1( \@seq1,\@seq2)
-# Look for differences between seq1 and seq2.
-# Where an common-subsequence block is flanked by deleted or appended blocks,
-# and is shorter than $MINWORDSBLOCK words it is appended
-# to the last deleted or appended word. If the block contains tokens other than words
-# or punctuation it is not merged.
-# Deleted or appended block consisting of words and safe commands only are
-# also merged, to prevent break-up in pass2 (after previous isolated words have been removed)
-# If there are commands with textual arguments (e.g. \caption) both in corresponding
-# appended and deleted blocks split them such that the command and opening bracket
-# are one token, then the rest is split up following standard rules, and the closing
-# bracket is a separate token, ie. turn
-# "\caption{This is a textual argument}" into
-# ("\caption{","This ","is ","a ","textual ","argument","}")
-# No return value. Destructively changes sequences
-sub pass1 {
- my $seq1 = shift ;
- my $seq2 = shift ;
-
- my $len1 = scalar @$seq1;
- my $len2 = scalar @$seq2;
- my $wpat=qr/^(?:[a-zA-Z.,'`:;?()!]*)[\s~]*$/; #'
-
- my ($last1,$last2)=(-1,-1) ;
- my $cnt=0;
- my $block=[];
- my $addblock=[];
- my $delblock=[];
- my $todo=[];
- my $instruction=[];
- my $i;
- my (@delmid,@addmid,@dummy);
-
- my ($addcmds,$delcmds,$matchindex);
- my ($addtextblocks,$deltextblocks);
- my ($addtokcnt,$deltokcnt,$mattokcnt)=(0,0,0);
- my ($addblkcnt,$delblkcnt,$matblkcnt)=(0,0,0);
-
- my $adddiscard = sub {
- if ($cnt > 0 ) {
- $matblkcnt++;
- # just after an unchanged block
-# print STDERR "Unchanged block $cnt, $last1,$last2 \n";
- if ($cnt < $MINWORDSBLOCK
- && $cnt==scalar (
- grep { /^$wpat/ || ( /^\\([\w\d\*]+)((?:\[$brat0\]|\{$pat4\})*)/o
- && iscmd($1,\@SAFECMDLIST,\@SAFECMDEXCL)
- && scalar(@dummy=split(" ",$2))<3 ) }
- @$block) ) {
- # merge identical blocks shorter than $MINWORDSBLOCK
- # and only containing ordinary words
- # with preceding different word
- # We cannot carry out this merging immediately as this
- # would change the index numbers of seq1 and seq2 and confuse
- # the algorithm, instead we store in @$todo where we have to merge
- push(@$todo, [ $last1,$last2,$cnt,@$block ]);
- }
- $block = [];
- $cnt=0; $last1=-1; $last2=-1;
- }
- };
- my $discard=sub { $deltokcnt++;
- &$adddiscard; #($_[0],$_[1]);
- push(@$delblock,[ $seq1->[$_[0]],$_[0] ]);
- $last1=$_[0] };
-
- my $add = sub { $addtokcnt++;
- &$adddiscard; #($_[0],$_[1]);
- push(@$addblock,[ $seq2->[$_[1]],$_[1] ]);
- $last2=$_[1] };
-
- my $match = sub { $mattokcnt++;
- if ($cnt==0) { # first word of matching sequence after changed sequence or at beginning of word sequence
- $deltextblocks = extracttextblocks($delblock);
- $delblkcnt++ if scalar @$delblock;
- $addtextblocks = extracttextblocks($addblock);
- $addblkcnt++ if scalar @$addblock;
-
- $delcmds = extractcommands($delblock);
- $addcmds = extractcommands($addblock);
- # keygen(third argument of _longestCommonSubsequence) implies to sort on command (0th elements of $addcmd elements)
- # the calling format for longestCommonSubsequence has changed between versions of
- # Algorithm::Diff so we need to check which one we are using
- if ( $algodiffversion > 1.15 ) {
- ### Algorithm::Diff 1.19
- $matchindex=Algorithm::Diff::_longestCommonSubsequence($delcmds,$addcmds, 0, sub { $_[0]->[0] } );
- } else {
- ### Algorithm::Diff 1.15
- $matchindex=Algorithm::Diff::_longestCommonSubsequence($delcmds,$addcmds, sub { $_[0]->[0] } );
- }
-
- for ($i=0 ; $i<=$#$matchindex ; $i++) {
- if (defined($matchindex->[$i])){
- $j=$matchindex->[$i];
- @delmid=splitlatex($delcmds->[$i][3]);
- @addmid=splitlatex($addcmds->[$j][3]);
- while (scalar(@$deltextblocks) && $deltextblocks->[0][0]<$delcmds->[$i][1]) {
- my ($index,$block,$cnt)=@{ shift(@$deltextblocks) };
- push(@$todo, [$index,-1,$cnt,@$block]);
- }
- push(@$todo, [ $delcmds->[$i][1],-1,-1,$delcmds->[$i][2],@delmid,$delcmds->[$i][4]]);
-
- while (scalar(@$addtextblocks) && $addtextblocks->[0][0]<$addcmds->[$j][1]) {
- my ($index,$block,$cnt)=@{ shift(@$addtextblocks) };
- push(@$todo, [-1,$index,$cnt,@$block]);
- }
- push(@$todo, [ -1,$addcmds->[$j][1],-1,$addcmds->[$j][2],@addmid,$addcmds->[$j][4]]);
- }
- }
- # mop up remaining textblocks
- while (scalar(@$deltextblocks)) {
- my ($index,$block,$cnt)=@{ shift(@$deltextblocks) } ;
- push(@$todo, [$index,-1,$cnt,@$block]);
- }
- while (scalar(@$addtextblocks)) {
- my ($index,$block,$cnt)=@{ shift(@$addtextblocks) };
- push(@$todo, [-1,$index,$cnt,@$block]);
- }
-
- $addblock=[];
- $delblock=[];
- }
- push(@$block,$seq2->[$_[1]]);
- $cnt++ };
-
- my $keyfunc = sub { join(" ",split(" ",shift())) };
-
- traverse_sequences($seq1,$seq2, { MATCH=>$match, DISCARD_A=>$discard, DISCARD_B=>$add }, $keyfunc );
-
-
- # now carry out the merging/splitting. Refer to elements relative from
- # the end (with negative indices) as these offsets don't change before the instruction is executed
- # cnt>0: merged small unchanged groups with previous changed blocks
- # cnt==-1: split textual commands into components
- foreach $instruction ( @$todo) {
- ($last1,$last2,$cnt,@$block)=@$instruction ;
- if ($cnt>=0) {
- splice(@$seq1,$last1-$len1,1+$cnt,join("",$seq1->[$last1-$len1],@$block)) if $last1>=0;
- splice(@$seq2,$last2-$len2,1+$cnt,join("",$seq2->[$last2-$len2],@$block)) if $last2>=0;
- } else {
- splice(@$seq1,$last1-$len1,1,@$block) if $last1>=0;
- splice(@$seq2,$last2-$len2,1,@$block) if $last2>=0;
- }
- }
-
- if ($verbose) {
- print STDERR "\n";
- print STDERR " $mattokcnt matching tokens in $matblkcnt blocks.\n";
- print STDERR " $deltokcnt discarded tokens in $delblkcnt blocks.\n";
- print STDERR " $addtokcnt appended tokens in $addblkcnt blocks.\n";
- }
-}
-
-
-# extracttextblocks(\@blockindex)
-# $blockindex has the following format
-# [ [ token1, index1 ], [token2, index2],.. ]
-# where index refers to the index in the original old or new word sequence
-# Returns: reference to an array of the form
-# [[ $index, $textblock, $cnt ], ..
-# where $index index of block to be merged
-# $textblock contains all the words to be merged with the word at $index (but does not contain this word)
-# $cnt is length of block
-#
-# requires: iscmd
-#
-sub extracttextblocks {
- my $block=shift;
- my ($i,$token,$index);
- my $textblock=[];
- my $last=-1;
- my $wpat=qr/^(?:[a-zA-Z.,'`:;?()!]*)[\s~]*$/; #'
- my $retval=[];
-
- for ($i=0;$i< scalar @$block;$i++) {
- ($token,$index)=@{ $block->[$i] };
- # store pure text blocks
- if ($token =~ /$wpat/ || ( $token =~/^\\([\w\d\*]+)((?:${extraspace}\[$brat0\]${extraspace}|${extraspace}\{$pat4\})*)/o
- && iscmd($1,\@SAFECMDLIST,\@SAFECMDEXCL)
- && !iscmd($1,\@TEXTCMDLIST,\@TEXTCMDEXCL))) {
- # we have text or a command which can be treated as text
- if ($last<0) {
- # new pure-text block
- $last=$index;
- } else {
- # add to pure-text block
- push(@$textblock, $token);
- }
- } else {
- # it is not text
- if (scalar(@$textblock)) {
- push(@$retval,[ $last, $textblock, scalar(@$textblock) ]);
- }
- $textblock=[];
- $last=-1;
- }
- }
- # finish processing a possibly unfinished block before returning
- if (scalar(@$textblock)) {
- push(@$retval,[ $last, $textblock, scalar(@$textblock) ]);
- }
- return($retval)
-}
-
-
-
-# extractcommands( \@blockindex )
-# $blockindex has the following format
-# [ [ token1, index1 ], [token2, index2],.. ]
-# where index refers to the index in the original old or new word sequence
-# Returns: reference to an array of the form
-# [ [ "\cmd1", index, "\cmd1[optarg]{arg1}{", "arg2" ,"} " ],..
-# where index is just taken from input array
-# command must have a textual argument as last argument
-#
-# requires: iscmd
-#
-sub extractcommands {
- my $block=shift;
- my ($i,$token,$index,$cmd,$open,$mid,$closing);
- my $retval=[];
-
- for ($i=0;$i< scalar @$block;$i++) {
- ($token,$index)=@{ $block->[$i] };
- # check if token is an alphanumeric command sequence with at least one non-optional argument
- # \cmd[...]{...}{last argument}
- # Capturing in the following results in these associations
- # $1: \cmd[...]{...}{
- # $2: \cmd
- # $3: last argument
- # $4: } + trailing spaces
- if ( ( $token =~ m/^(\\([\w\d\*]+)(?:${extraspace}\[$brat0\]|${extraspace}\{$pat4\})*${extraspace}\{)($pat4)(\}\s*)$/so )
- && iscmd($2,\@TEXTCMDLIST,\@TEXTCMDEXCL) ) {
- # push(@$retval,[ $2,$index,$1,$3,$4 ]);
- ($cmd,$open,$mid,$closing) = ($2,$1,$3,$4) ;
- $closing =~ s/\}/\\RIGHTBRACE/ ;
- push(@$retval,[ $cmd,$index,$open,$mid,$closing ]);
- }
- }
- return $retval;
-}
-
-# iscmd($cmd,\@regexarray,\@regexexcl) checks
-# return 1 if $cmd matches any of the patterns in the
-# array $@regexarray, and none of the patterns in \@regexexcl, otherwise return 0
-sub iscmd {
- my ($cmd,$regexar,$regexexcl)=@_;
- my ($ret)=0;
- foreach $pat ( @$regexar ) {
- if ( $cmd =~ m/^${pat}$/ ) {
- $ret=1 ;
- last;
- }
- }
- return 0 unless $ret;
- foreach $pat ( @$regexexcl ) {
- return 0 if ( $cmd =~ m/^${pat}$/ );
- }
- return 1;
-}
-
-
-# pass2( \@seq1,\@seq2)
-# Look for differences between seq1 and seq2.
-# Mark begin and end of deleted and appended sequences with tags $DELOPEN and $DELCLOSE
-# and $ADDOPEN and $ADDCLOSE, respectively, however exclude { } & and all comands, unless
-# they match an element of the whitelist (SAFECMD)
-# For words in TEXTCMD but not in SAFECMD, enclose interior with $ADDOPEN and $ADDCLOSE brackets
-# Deleted comment lines are marked with %DIF <
-# Added comment lines are marked with %DIF >
-sub pass2 {
- my $seq1 = shift ;
- my $seq2 = shift ;
-
- my ($addtokcnt,$deltokcnt,$mattokcnt)=(0,0,0);
- my ($addblkcnt,$delblkcnt,$matblkcnt)=(0,0,0);
-
- my $retval = [];
- my $delhunk = [];
- my $addhunk = [];
-
- my $discard = sub { $deltokcnt++;
- push ( @$delhunk, $seq1->[$_[0]]) };
-
- my $add = sub { $addtokcnt++;
- push ( @$addhunk, $seq2->[$_[1]]) };
-
- my $match = sub { $mattokcnt++;
- if ( scalar @$delhunk ) {
- $delblkcnt++;
- # mark up changes, but comment out commands
- push @$retval,marktags($DELMARKOPEN,$DELMARKCLOSE,$DELOPEN,$DELCLOSE,$DELCMDOPEN,$DELCMDCLOSE,$DELCOMMENT,$delhunk);
- $delhunk = [];
- }
- if ( scalar @$addhunk ) {
- $addblkcnt++;
- # we mark up changes, but simply quote commands
- push @$retval,marktags($ADDMARKOPEN,$ADDMARKCLOSE,$ADDOPEN,$ADDCLOSE,"","",$ADDCOMMENT,$addhunk);
- $addhunk = [];
- }
- push(@$retval,$seq2->[$_[1]]) };
-
- my $keyfunc = sub { join(" ",split(" ",shift())) };
-
- traverse_sequences($seq1,$seq2, { MATCH=>$match, DISCARD_A=>$discard, DISCARD_B=>$add }, $keyfunc );
- # clear up unprocessed hunks
- push @$retval,marktags($DELMARKOPEN,$DELMARKCLOSE,$DELOPEN,$DELCLOSE,$DELCMDOPEN,$DELCMDCLOSE,$DELCOMMENT,$delhunk) if scalar @$delhunk;
- push @$retval,marktags($ADDMARKOPEN,$ADDMARKCLOSE,$ADDOPEN,$ADDCLOSE,"","",$ADDCOMMENT,$addhunk) if scalar @$addhunk;
-
-
- if ($verbose) {
- print STDERR "\n";
- print STDERR " $mattokcnt matching tokens. \n";
- print STDERR " $deltokcnt discarded tokens in $delblkcnt blocks.\n";
- print STDERR " $addtokcnt appended tokens in $addblkcnt blocks.\n";
- }
-
- return(@$retval);
-}
-
-# marktags($openmark,$closemark,$open,$close,$opencmd,$closecmd,$comment,\@block)
-# returns ($openmark,$open,$block,$close,$closemark) if @block only contains no commands (except white-listed ones),
-# braces, ampersands, or comments
-# mark comments with $comment
-# exclude all other exceptions from scope of open, close like this
-# ($openmark, $open,...,$close, $opencomd,command, command,$closecmd, $open, ..., $close, $closemark)
-# If $opencmd begins with "%" marktags assumes it is operating on a deleted block, otherwise on an added block
-sub marktags {
- my ($openmark,$closemark,$open,$close,$opencmd,$closecmd,$comment,$block)=@_;
- my $word;
- my (@argtext);
- my $retval=[];
- my $noncomment=0;
- my $cmd=-1; # -1 at beginning 0: last token written is a ordinary word
- # 1: last token written is a command
- # for keeping track whether we are just in a command sequence or in a word sequence
- my $cmdcomment= ($opencmd =~ m/^%/); # Flag to indicate whether opencmd is a comment (i.e. if we intend to simply comment out changed commands)
- my ($command,$commandword,$closingbracket) ; # temporary variables needed below to remember sub-pattern matches
-
-# split this block to flatten out sequences joined in pass1
- @$block=splitlatex(join "",@$block);
- foreach (@$block) {
- $word=$_;
- if ( $word =~ s/^%/%$comment/ ) {
- # a comment
- if ($cmd==1) {
- push (@$retval,$closecmd) ;
- $cmd=-1;
- }
- push (@$retval,$word);
- next;
- }
- if (! $noncomment) {
- push (@$retval,$openmark);
- $noncomment=1;
- }
- # negative lookahead pattern (?!) in second clause is put in to avoid mathcing \( .. \) patterns
- if ( $word =~ /^[&{}\[\]]/ || ( $word =~ /^\\(?!\()([\w*@]*)/ && !iscmd($1,\@SAFECMDLIST,\@SAFECMDEXCL)) ) {
- # word is a command or other significant token (not in SAFECMDLIST)
- ## same conditions as in subroutine extractcommand:
- # check if token is an alphanumeric command sequence with at least one non-optional argument
- # \cmd[...]{...}{last argument}
- # Capturing in the following results in these associations
- # $1: \cmd[...]{...}{
- # $2: \cmd
- # $3: last argument
- # $4: } + trailing spaces
- ### pre-0.3 if ( ( $token =~ m/^(\\([\w\d\*]+)(?:\[$brat0\]|\{$pat4\})*\{)($pat4)(\}\s*)$/so )
- if ( ( $word =~ m/^(\\([\w\d\*]+)(?:${extraspace}\[$brat0\]|${extraspace}\{$pat4\})*${extraspace}\{)($pat4)(\}\s*)$/so )
- && iscmd($2,\@TEXTCMDLIST,\@TEXTCMDEXCL) && ( !$cmdcomment || !iscmd($2,\@CONTEXT2CMDLIST, \@CONTEXT2CMDEXCL) ) ) {
- # word is a text command - we mark up the interior of the word. But if we are in a deleted block ($cmdcomment=1) and
- # $2 (the command) is in context2, just treat it as an ordinary command (i.e. comment it open with $opencmd)
- # Because we do not want to disable this command
- # here we do not use $opencmd and $closecmd($opencmd is empty)
- if ($cmd==1) {
- push (@$retval,$closecmd) ;
- } elsif ($cmd==0) {
- push (@$retval,$close) ;
- }
- $command=$1; $commandword=$2; $closingbracket=$4;
- @argtext=splitlatex($3); # split textual argument into tokens
- # and mark it up (but we do not need openmark and closemark)
- # insert command with initial arguments, marked-up final argument, and closing bracket
- if ( $cmdcomment && iscmd($commandword,\@CONTEXT1CMDLIST, \@CONTEXT1CMDEXCL) ) {
- # context1cmd in a deleted environment; delete command itself but keep last argument, marked up
- push (@$retval,$opencmd);
- $command =~ s/\n/\n${opencmd}/sg ; # repeat opencmd at the beginning of each line
- # argument, note that the additional comment character is included
- # to suppress linebreak after opening parentheses, which is important
- # for latexrevise
- push (@$retval,$command,"%\n{$AUXCMD\n",marktags("","",$open,$close,$opencmd,$closecmd,$comment,\@argtext),$closingbracket);
- } else {
- # normal textcmd or context1cmd in an added block
- push (@$retval,$command,marktags("","",$open,$close,$opencmd,$closecmd,$comment,\@argtext),$closingbracket);
- }
- push (@$retval,$AUXCMD,"\n") if $cmdcomment ;
- $cmd=-1 ;
- } else {
- # ordinary command
- push (@$retval,$opencmd) if $cmd==-1 ;
- push (@$retval,$close,$opencmd) if $cmd==0 ;
- $word =~ s/\n/\n${opencmd}/sg if $cmdcomment ; # if opencmd is a comment, repeat this at the beginning of every line
- push (@$retval,$word);
- $cmd=1;
- }
- } else {
- # just an ordinary word or word in SAFECMD
- push (@$retval,$open) if $cmd==-1 ;
- push (@$retval,$closecmd,$open) if $cmd==1 ;
- push (@$retval,$word);
- $cmd=0;
- }
- }
- push (@$retval,$close) if $cmd==0;
- push (@$retval,$closecmd) if $cmd==1;
-
- push (@$retval,$closemark) if ($noncomment);
- return @$retval;
-}
-
-# preprocess($string, ..)
-# carry out the following pre-processing steps for all arguments:
-# 1. Remove leading white-space
-# 2. mark all first empty line (in block of several) with \PAR tokens
-# 3. Convert all '\%' into '\PERCENTAGE ' to make parsing regular expressions easier
-# 4. Convert all \verb|some verbatim text| commands (where | can be an arbitrary character)
-# into \verb{hash}
-# 5. Convert \begin{verbatim} some verbatim text \end{verbatim} into \verbatim{hash}
-# 6. Convert _n into \SUBSCRIPTNB{n} and _{nnn} into \SUBSCRIPT{nn}
-# 7. Convert ^n into \SUPERSCRIPTNB{n} and ^{nnn} into \SUPERSCRIPT{nn}
-# 8. a. Convert $$ $$ into \begin{DOLLARDOLLAR} \end{DOLLARDOLLAR}
-# b. Convert \[ \] into \begin{SQUAREBRACKET} \end{SQUAREBRACKET}
-
-# 9. Add final token STOP to the very end. This is put in because the algorithm works better if the last token is identical. This is removed again in postprocessing.
-#
-# NB: step 6 and 7 is likely to convert some "_" inappropriately, e.g. in file
-# names or labels but it does not matter because they are converted back in the postprocessing step
-# Returns: leading white space removed in step 1
-sub preprocess {
- my @leadin=() ;
- for (@_) {
- s/^(\s*)//s;
- push(@leadin,$1);
- s/\n(\s*?)\n((?:\s*\n)*)/\n$1\\PAR\n$2/g ;
- s/(?<!\\)\\%/\\PERCENTAGE /g ; # (?<! is negative lookbehind assertion to prevent \\% from being converted
- s/(\\verb\*?)(\S)(.*?)\2/"${1}{". tohash(\%verbhash,"${2}${3}${2}") ."}"/esg;
- s/\\begin\{(verbatim\*?)\}(.*?)\\end\{\1\}/"\\${1}{". tohash(\%verbhash,"${2}") . "}"/esg;
- # Convert _n or _\cmd into \SUBSCRIPTNB{n} or \SUBSCRIPTNB{\cmd} and _{nnn} into \SUBSCRIPT{nn}
- s/_([^{\\]|\\\w+)/\\SUBSCRIPTNB{$1}/g ;
- s/_{($pat4)}/\\SUBSCRIPT{$1}/g ;
- # Convert ^n into \SUPERSCRIPTNB{n} and ^{nnn} into \SUPERSCRIPT{nn}
- s/\^([^{\\]|\\\w+)/\\SUPERSCRIPTNB{$1}/g ;
- s/\^{($pat4)}/\\SUPERSCRIPT{$1}/g ;
- # Convert $$ $$ into \begin{DOLLARDOLLAR} \end{DOLLARDOLLAR}
- s/\$\$(.*?)\$\$/\\begin{DOLLARDOLLAR}$1\\end{DOLLARDOLLAR}/sg;
- # Convert \[ \] into \begin{SQUAREBRACKET} \end{SQUAREBRACKET}
- s/(?<!\\)\\\[/\\begin{SQUAREBRACKET}/sg;
- s/\\\]/\\end{SQUAREBRACKET}/sg;
- # add final token " STOP"
- $_ .= " STOP"
- }
- return(@leadin);
-}
-
-#hashstring=tohash(\%hash,$string)
-# creates a hash value based on string and stores in %hash
-sub tohash {
- my ($hash,$string)=@_;
- my (@arr,$val);
- my ($sum,$i)=(0,1);
- my ($hstr);
-
- @arr=unpack('c*',$string);
-
- foreach $val (@arr) {
- $sum += $i*$val;
- $i++;
- }
- $hstr= "$sum";
- if (defined($hash->{$hstr}) && $string ne $hash->{$hstr}) {
- warn "Repeated hash value for verbatim mode in spite of different content.";
- $hstr="-$hstr";
- }
- $hash->{$hstr}=$string;
- return($hstr);
-}
-
-#string=fromhash(\%hash,$fromstring)
-# restores string value stored in hash
-#string=fromhash(\%hash,$fromstring,$prependstring)
-# additionally begins each line with prependstring
-sub fromhash {
- my ($hash,$hstr)=($_[0],$_[1]);
- my $retstr=$hash->{$hstr};
- if ( $#_ >= 2) {
- $retstr =~ s/^/$_[2]/mg;
- }
- return $retstr;
-}
-
-
-# postprocess($string, ..)
-# carry out the following post-processing steps for all arguments:
-# * Remove STOP token from the end
-# * Replace \RIGHTBRACE by }
-# 1. Check all deleted blocks:
-# a.where a deleted block contains a matching \begin and
-# \end environment (these will be disabled by a %DIFDELCMD statements), for selected environment enable enable
-# these commands again (such that for example displayed math in a deleted equation
-# is properly within math mode. For math mode environments replace numbered equation
-# environments with their display only variety (so that equation numbers in new file and
-# diff file are identical). Where the correct type of math environment cannot be determined
-# use a place holder MATHMODE
-# b.where one of the commands matching $COUNTERCMD is used as a DIFAUXCMD, add a statement
-# subtracting one from the respective counter to keep numbering consistent with new file
-# Replace all MATHMODE environment commands by the correct environment to achieve matching
-# pairs
-# c. If in-line math mode contains array environment, enclose the whole environment in \mbox'es
-# 2. Remove DIFadd, DIFdel, DIFFaddbegin , ... from picture environments
-# 3. Convert DIFadd, DIFdel, DIFFaddbegin , ... into FL varieties
-# within floats (currently recognised float environments: plate,table,figure
-# plus starred varieties).
-# 4. Remove empty %DIFDELCMD < lines
-# 4. Convert \begin{SQUAREBRACKET} \end{SQUAREBRACKET} into \[ \]
-# Convert \begin{DOLLARDOLLAR} \end{DOLLARDOLLAR} into $$ $$
-# 5. Convert \SUPERSCRIPTNB{n} into ^n and \SUPERSCRIPT{nn} into ^{nnn}
-# 6. Convert \SUBSCRIPTNB{n} into _n and \SUBCRIPT{nn} into _{nnn}
-# 7. Expand hashes of verb and verbatim environments
-# 8. Convert '\PERCENTAGE ' back into '\%'
-# 9.. remove all \PAR tokens
-# 10. package specific processing: endfloat: make sure \begin{figure} and \end{figure} are always
-# on a line by themselves, similarly for table environment
-# 4, undo renaming of the \begin and \end in comments
-
-#
-# Note have to manually synchronize substitution commands below and
-# DIF.. command names in the header
-sub postprocess {
- my ($begin,$len,$cnt,$float,$delblock,$addblock);
- # second level blocks
- my ($begin2,$cnt2,$len2,$eqarrayblock,$mathblock);
-
- for (@_) {
-
- # change $'s in comments to something harmless
- 1 while s/(%.*)\$/$1DOLLARDIF/mg ;
-
- # Remove final STOP token
- s/ STOP$//;
- # Replace \RIGHTBRACE by }
- s/\\RIGHTBRACE/}/g;
- # Check all deleted blocks: where a deleted block contains a matching \begin and
- # \end environment (these will be disabled by a %DIFDELCMD statements), enable
- # these commands again (such that for example displayed math in a deleted equation
- # is properly within math mode. For math mode environments replace numbered equation
- # environments with their display only variety (so that equation numbers in new file and
- # diff file are identical
- while ( m/\\DIFdelbegin.*?\\DIFdelend/sg ) {
- $cnt=0;
- $len=length($&);
- $begin=pos($_) - $len;
- $delblock=$&;
-
-
- ### (.*?[^\n]?)\n? construct is necessary to avoid empty lines in math mode, which result in
- ### an error
- # displayed math environments
- $delblock=~ s/(\%DIFDELCMD < \s*\\begin\{((?:$MATHENV)|SQUAREBRACKET)\}\s*?(?:$DELCMDCLOSE|\n))(.*?[^\n]?)\n?(\%DIFDELCMD < \s*\\end\{\2\})/\\begin{$MATHREPL}$AUXCMD\n$1$3\n\\end{$MATHREPL}$AUXCMD\n$4/sg;
- # also transform the opposite pair \end{displaymath} .. \begin{displaymath} but we have to be careful not to interfere with the results of the transformation in the line directly above
- $delblock=~ s/(?<!${AUXCMD}\n)(\%DIFDELCMD < \s*\\end\{((?:$MATHENV)|SQUAREBRACKET)\}\s*?(?:$DELCMDCLOSE|\n))(.*?[^\n]?)\n?(?<!${AUXCMD}\n)(\%DIFDELCMD < \s*\\begin\{\2\})/\\end{MATHMODE}$AUXCMD\n$1$3\n\\begin{MATHMODE}$AUXCMD\n$4/sg;
-
- # now look for unpaired %DIFDELCMD < \begin{MATHENV}; if found add \begin{$MATHREPL} and insert \end{$MATHREPL}
- # just before end of block; again we use look-behind assertion to avoid matching constructions which have already been converted
- if ($delblock=~ s/(?<!${AUXCMD}\n)(\%DIFDELCMD < \s*\\begin\{((?:$MATHENV)|SQUAREBRACKET)\}\s*?(?:$DELCMDCLOSE|\n))/$1\\begin{$MATHREPL}$AUXCMD\n/sg ) {
- $delblock =~ s/(\\DIFdelend$)/\\end{$MATHREPL}$AUXCMD\n$1/s ;
- }
- # now look for unpaired %DIFDELCMD < \end{MATHENV}; if found add \end{MATHMODE} and insert \begin{MATHMODE}
- # just before end of block; again we use look-behind assertion to avoid matching constructions which have already been converted
- if ($delblock=~ s/(?<!${AUXCMD}\n)(\%DIFDELCMD < \s*\\end\{((?:$MATHENV)|SQUAREBRACKET)\}\s*?(?:$DELCMDCLOSE|\n))/$1\\end{MATHMODE}$AUXCMD\n/sg ) {
- $delblock =~ s/(\\DIFdelend$)/\\begin{MATHMODE}$AUXCMD\n$1/s ;
- }
-
-
- # equation array environment
- $delblock=~ s/(\%DIFDELCMD < \s*\\begin\{($MATHARRENV)\}\s*?(?:$DELCMDCLOSE|\n))(.*?[^\n]?)\n?(\%DIFDELCMD < \s*\\end\{\2\})/\\begin{$MATHARRREPL}$AUXCMD\n$1$3\n\\end{$MATHARRREPL}$AUXCMD\n$4/sg;
- $delblock=~ s/(?<!${AUXCMD}\n)(\%DIFDELCMD < \s*\\end\{($MATHARRENV)\}\s*?(?:$DELCMDCLOSE|\n))(.*?[^\n]?)\n?(?<!${AUXCMD}\n)(\%DIFDELCMD < \s*\\begin\{\2\})/\\end{MATHMODE}$AUXCMD\n$1$3\n\\begin{MATHMODE}$AUXCMD\n$4/sg;
-
- # now look for unpaired %DIFDELCMD < \begin{MATHARRENV}; if found add \begin{$MATHARRREPL} and insert \end{$MATHARRREPL}
- # just before end of block; again we use look-behind assertion to avoid matching constructions which have already been converted
- if ($delblock=~ s/(?<!${AUXCMD}\n)(\%DIFDELCMD < \s*\\begin\{($MATHARRENV)\}\s*?(?:$DELCMDCLOSE|\n))/$1\\begin{$MATHARRREPL}$AUXCMD\n/sg ) {
- $delblock =~ s/(\\DIFdelend$)/\\end{$MATHARRREPL}$AUXCMD\n$1/s ;
- }
- # now look for unpaired %DIFDELCMD < \end{MATHENV}; if found add \end{MATHMODE} and insert \begin{MATHMODE}
- # just before end of block; again we use look-behind assertion to avoid matching constructions which have already been converted
- if ($delblock=~ s/(?<!${AUXCMD}\n)(\%DIFDELCMD < \s*\\end\{($MATHARRENV)\}\s*?(?:$DELCMDCLOSE|\n))/$1\\end{MATHMODE}$AUXCMD\n/sg ) {
- $delblock =~ s/(\\DIFdelend$)/\\begin{MATHMODE}$AUXCMD\n$1/s ;
- }
-
- # parse $delblock for deleted and reinstated eqnarray* environments - within those reinstate \\ and & commands
- while ( $delblock =~ m/\\begin\Q{$MATHARRREPL}$AUXCMD\E\n.*?\n\\end\Q{$MATHARRREPL}$AUXCMD\E\n/sg ) {
- $cnt2=0;
- $len2=length($&);
- $begin2=pos($delblock) - $len2;
- $eqarrayblock=$&;
- # reinstate deleted & and \\ commands
- $eqarrayblock=~ s/(\%DIFDELCMD < \s*(\&|\\\\)\s*?(?:$DELCMDCLOSE|\n))/$1$2$AUXCMD\n/sg ;
-
- substr($delblock,$begin2,$len2)=$eqarrayblock;
- pos($delblock) = $begin2 + length($eqarrayblock);
- }
-
-
-
-# b.where one of the commands matching $COUNTERCMD is used as a DIFAUXCMD, add a statement
-# subtracting one from the respective counter to keep numbering consistent with new file
- $delblock=~ s/\\($COUNTERCMD)((?:${extraspace}\[$brat0\]${extraspace}|${extraspace}\{$pat4\})*\s*${AUXCMD}\n)/\\$1$2\\addtocounter{$1}{-1}${AUXCMD}\n/sg ;
-# c. If in-line math mode contains array environment, enclose the whole environment in \mbox'es
- while ( $delblock =~ m/($math)(\s*)/sg ) {
- $cnt2=0;
- $len2=length($&);
- $begin2=pos($delblock) - $len2;
- $mathblock="%\n\\mbox{$AUXCMD\n$1\n}$AUXCMD\n";
- next unless $mathblock =~ m/\{$ARRENV\}/ ;
- substr($delblock,$begin2,$len2)=$mathblock;
- pos($delblock) = $begin2 + length($mathblock);
- }
-
-# splice in modified delblock
- substr($_,$begin,$len)=$delblock;
- pos = $begin + length($delblock);
- }
- # make the array modification in added blocks
- while ( m/\\DIFaddbegin.*?\\DIFaddend/sg ) {
- $cnt=0;
- $len=length($&);
- $begin=pos($_) - $len;
- $addblock=$&;
- while ( $addblock =~ m/($math)(\s*)/sg ) {
- $cnt2=0;
- $len2=length($&);
- $begin2=pos($addblock) - $len2;
- $mathblock="%\n\\mbox{$AUXCMD\n$1\n}$AUXCMD\n";
- next unless $mathblock =~ m/\{$ARRENV\}/ ;
- substr($addblock,$begin2,$len2)=$mathblock;
- pos($addblock) = $begin2 + length($mathblock);
- }
-
-# splice in modified addblock
- substr($_,$begin,$len)=$addblock;
- pos = $begin + length($addblock);
- }
-
-
- ### old place for BEGINDIF, ENDDIF replacement
- # change begin and end commands within comments such that they
- # don't disturb the pattern matching (if there are several \begin or \end in one line
- # this substitution is insufficient but that appears unlikely)
- s/(%.*)\\begin\{(.*)$/$1\\BEGINDIF\{$2/mg ;
- s/(%.*)\\end\{(.*)$/$1\\ENDDIF\{$2/mg ;
-
- # Replace MATHMODE environments from step 1a above by the correct Math environment
-
- # The next line is complicated. The negative look-ahead insertion makes sure that no \end{$MATHENV} (or other mathematical
- # environments) are between the \begin{$MATHENV} and \end{MATHMODE} commands. This is necessary as the minimal matching
- # is not globally minimal but only 'locally' (matching is beginning from the left side of the string)
- 1 while s/\\begin{((?:$MATHENV)|(?:$MATHARRENV)|SQUAREBRACKET)}((?:.(?!(?:\\end{(?:(?:$MATHENV)|(?:$MATHARRENV)|SQUAREBRACKET)}|\\begin{MATHMODE})))*?)\\end{MATHMODE}/\\begin{$1}$2\\end{$1}/s;
- 1 while s/\\begin{MATHMODE}((?:.(?!\\end{MATHMODE}))*?)\\end{((?:$MATHENV)|(?:$MATHARRENV)|SQUAREBRACKET)}/\\begin{$2}$1\\end{$2}/s;
- # convert remaining \begin{MATHMODE} \end{MATHMODE} (and not containing & or \\ )into MATHREPL environments
- s/\\begin{MATHMODE}((?:(.(?!(?<!\\)\&|\\\\))*)?)\\end{MATHMODE}/\\begin{$MATHREPL}$1\\end{$MATHREPL}/sg;
- # others into MATHARRREPL
- s/\\begin{MATHMODE}(.*?)\\end{MATHMODE}/\\begin{$MATHARRREPL}$1\\end{$MATHARRREPL}/sg;
-
- # now look for AUXCMD math-mode pairs which have only comments (or empty lines between them), and remove the added commands
- s/\\begin{((?:$MATHENV)|(?:$MATHARRENV)|SQUAREBRACKET)}$AUXCMD\n((?:\s*%.[^\n]*\n)*)\\end{\1}$AUXCMD\n/$2/sg;
-
-
- # Remove all mark up within picture environments
- while ( m/\\begin\{($PICTUREENV)\}.*?\\end\{\1\}/sg ) {
- $cnt=0;
- $len=length($&);
- $begin=pos($_) - $len;
- $float=$&;
- $float =~ s/\\DIFaddbegin //g;
- $float =~ s/\\DIFaddend //g;
- $float =~ s/\\DIFadd\{($pat4)\}/$1/g;
- $float =~ s/\\DIFdelbegin //g;
- $float =~ s/\\DIFdelend //g;
- $float =~ s/\\DIFdel\{($pat4)\}//g;
- substr($_,$begin,$len)=$float;
- pos = $begin + length($float);
- }
- # Convert DIFadd, DIFdel, DIFFaddbegin , ... into varieties
- # within floats (currently recognised float environments: plate,table,figure
- # plus starred varieties).
- while ( m/\\begin\{($FLOATENV)\}.*?\\end\{\1\}/sg ) {
- $cnt=0;
- $len=length($&);
- $begin=pos($_) - $len;
- $float=$&;
- $float =~ s/\\DIFaddbegin /\\DIFaddbeginFL /g;
- $float =~ s/\\DIFaddend /\\DIFaddendFL /g;
- $float =~ s/\\DIFadd\{/\\DIFaddFL{/g;
- $float =~ s/\\DIFdelbegin /\\DIFdelbeginFL /g;
- $float =~ s/\\DIFdelend /\\DIFdelendFL /g;
- $float =~ s/\\DIFdel\{/\\DIFdelFL{/g;
- substr($_,$begin,$len)=$float;
- pos = $begin + length($float);
- }
- ### former location of undo renaming of \begin and \end in comments
-
- # remove empty DIFCMD < lines
- s/^\Q${DELCMDOPEN}\E\n//msg;
-
- # Expand hashes of verb and verbatim environments (note negative look behind assertion to not leak out of DIFDELCMD comments
- s/(\\verb\*?)\{([-\d]*?)\}/"${1}". fromhash(\%verbhash,$2)/esg;
- s/${DELCMDOPEN}\\(verbatim\*?)\{([-\d]*?)\}/"${DELCMDOPEN}\\begin{${1}}".fromhash(\%verbhash,$2,$DELCMDOPEN)."${DELCMDOPEN}\\end{${1}}"/esg;
- s/\\(verbatim\*?)\{([-\d]*?)\}/"\\begin{${1}}".fromhash(\%verbhash,$2)."\\end{${1}}"/esg;
- # Convert '\PERCENTAGE ' back into '\%'
- s/\\PERCENTAGE /\\%/g;
- # remove all \PAR tokens (taking care to properly keep commented out PAR's
- # from introducing uncommented newlines - next line)
- s/(%DIF < )([^\n]*?)\\PAR\n/$1$2\n$1\n/sg;
- # convert PAR commands which are on a line by themselves
- s/\n(\s*?)\\PAR\n/\n\n/sg;
- # convert remaining PAR commands (which are preceded by non-white space characters, usually "}" ($ADDCLOSE)
- s/\\PAR\n/\n\n/sg;
-
- # package specific processing:
- if ( defined($packages{"endfloat"})) {
- #endfloat: make sure \begin{figure} and \end{figure} are always
- # on a line by themselves, similarly for table environment
- print STDERR "endfloat package detected.\n" if $verbose ;
- # eliminate whitespace before and after
- s/^(\s*)(\\(?:begin|end)\{(?:figure|table)\})(\s*)$/$2/mg;
- # split lines with remaining characters before float enviroment conmmand
- s/^(.+)(\\(?:begin|end)\{(?:figure|table)\})/$1\n$2/mg;
- # split lines with remaining characters after float enviroment conmmand
- s/(\\(?:begin|end)\{(?:figure|table)\})(.+)$/$1\n$2/mg;
- }
- # undo renaming of the \begin and \end and dollars in comments
- s/(%.*)\\BEGINDIF\{(.*)$/$1\\begin\{$2/mg ;
- s/(%.*)\\ENDDIF\{(.*)$/$1\\end\{$2/mg ;
- 1 while s/(%.*)DOLLARDIF/$1\$/mg ;
-# Convert \begin{SQUAREBRACKET} \end{SQUAREBRACKET} into \[ \]
- s/\\end{SQUAREBRACKET}/\\\]/sg;
- s/\\begin{SQUAREBRACKET}/\\\[/sg;
-# 4. Convert \begin{DOLLARDOLLAR} \end{DOLLARDOLLAR} into $$ $$
- s/\\begin\{DOLLARDOLLAR\}(.*?)\\end\{DOLLARDOLLAR\}/\$\$$1\$\$/sg;
-# 5. Convert \SUPERSCRIPTNB{n} into ^n and \SUPERSCRIPT{nn} into ^{nnn}
- s/\\SUPERSCRIPTNB{($pat0)}/^$1/g ;
- s/\\SUPERSCRIPT{($pat4)}/^{$1}/g ;
- # Convert \SUBSCRIPNB{n} into _n and \SUBCRIPT{nn} into _{nnn}
- s/\\SUBSCRIPTNB{($pat0)}/_$1/g ;
- s/\\SUBSCRIPT{($pat4)}/_{$1}/g ;
-
- return;
- }
-}
-
-
-# @diffs=linediff(\@seq1, \@seq2)
-# mark up lines like this
-#%DIF mm-mmdnn
-#%< old deleted line(s)
-#%DIF -------
-#%DIF mmann-nn
-#new appended line %<
-#%DIF -------
-# Future extension: mark change explicitly
-# Assumes: traverse_sequence traverses deletions before insertions in changed sequences
-# all line numbers relative to line 0 (first line of real file)
-sub linediff {
- my $seq1 = shift ;
- my $seq2 = shift ;
-
- my $block = [];
- my $retseq = [];
- my @begin=('','',''); # dummy initialisation
- my $instring ;
-
- my $discard = sub { @begin=('d',$_[0],$_[1]) unless scalar @$block ;
- push(@$block, "%DIF < " . $seq1->[$_[0]]) };
- my $add = sub { if (! scalar @$block) {
- @begin=('a',$_[0],$_[1]) ;}
- elsif ( $begin[0] eq 'd' ) {
- $begin[0]='c'; $begin[2]=$_[1];
- push(@$block, "%DIF -------") }
- push(@$block, $seq2->[$_[1]] . " %DIF > " ) };
- my $match = sub { if ( scalar @$block ) {
- if ( $begin[0] eq 'd' && $begin[1]!=$_[0]-1) {
- $instring = sprintf "%%DIF %d-%dd%d",$begin[1],$_[0]-1,$begin[2]; }
- elsif ( $begin[0] eq 'a' && $begin[2]!=$_[1]-1) {
- $instring = sprintf "%%DIF %da%d-%d",$begin[1],$begin[2],$_[1]-1; }
- elsif ( $begin[0] eq 'c' ) {
- $instring = sprintf "%%DIF %sc%s",
- ($begin[1]==$_[0]-1) ? "$begin[1]" : $begin[1]."-".($_[0]-1) ,
- ($begin[2]==$_[1]-1) ? "$begin[2]" : $begin[2]."-".($_[1]-1) ; }
- else {
- $instring = sprintf "%%DIF %d%s%d",$begin[1],$begin[0],$begin[2]; }
- push @$retseq, $instring,@$block, "%DIF -------" ;
- $block = [];
- }
- push @$retseq, $seq2->[$_[1]]
- };
- # key function: remove multiple spaces (such that insertion or deletion of redundant white space is not reported)
- my $keyfunc = sub { join(" ",split(" ",shift())) };
-
- traverse_sequences($seq1,$seq2, { MATCH=>$match, DISCARD_A=>$discard, DISCARD_B=>$add }, $keyfunc );
- push @$retseq, @$block if scalar @$block;
-
- return wantarray ? @$retseq : $retseq ;
-}
-
-
-
-# init_regex_arr_data(\@array,"TOKEN INIT")
-# scans DATA file handel for line "%% TOKEN INIT" line
-# then appends each line not beginning with % into array (as a quoted regex)
-sub init_regex_arr_data {
- my ($arr,$token)=@_;
- my ($copy);
- while (<DATA>) {
- if ( m/^%%BEGIN $token\s*$/ ) {
- $copy=1; }
- elsif ( m/^%%END $token\s*/ ) {
- last; }
- chomp;
- push (@$arr,qr/^$_$/) if ( $copy && !/^%/ ) ;
- }
- seek DATA,0,0; # rewind DATA handle to file begin
-}
-
-
-# init_regex_arr_ext(\@array,$arg)
-# fills array with regular expressions.
-# if arg is a file name, then read in list of regular expressions from that file
-# (one expression per line)
-# Otherwise treat arg as a comma separated list of regular expressions
-sub init_regex_arr_ext {
- my ($arr,$arg)=@_;
- my $regex;
- if ( -f $ arg ) {
- open(FILE,"$arg") or die ("Couldn't open $arg: $!");
- while (<FILE>) {
- chomp;
- next if /^\s*#/ || /^\s*%/ || /^\s*$/ ;
- push (@$arr,qr/^$_$/);
- }
- close(FILE);
- }
- else {
- # assume it is a comma-separated list of reg-ex
- foreach $regex (split(qr/(?<!\\),/,$arg)) {
- $regex =~ s/\\,/,/g;
- push (@$arr,qr/^$regex$/);
- }
- }
-}
-
-#exetime() returns time since last execution of this command
-#exetime(1) resets this time
-my $lasttime=-1; # global variable for persistence
-sub exetime {
- my $reset=0;
- my $retval;
- if ((scalar @_) >=1) {
- $reset=shift;
- }
- if ($reset) {
- $lasttime=times();
- }
- else {
- $retval=times()-$lasttime;
- $lasttime=$lasttime+$retval;
- return($retval);
- }
-}
-
-
-sub usage {
- die <<"EOF";
-Usage: $0 [options] old.tex new.tex > diff.tex
-
-Compares two latex files and writes tex code to stdout, which has the same
-format as new.tex but has all changes relative to old.tex marked up or commented.
-
---type=markupstyle
--t markupstyle Add code to preamble for selected markup style
- Available styles: UNDERLINE CTRADITIONAL TRADITIONAL CFONT FONTSTRIKE INVISIBLE
- CHANGEBAR CCHANGEBAR CULINECHBAR CFONTCBHBAR
- [ Default: UNDERLINE ]
-
---subtype=markstyle
--s markstyle Add code to preamble for selected style for bracketing
- commands (e.g. to mark changes in margin)
- Available styles: SAFE MARGINAL DVIPSCOL
- [ Default: SAFE ]
-
---floattype=markstyle
--f markstyle Add code to preamble for selected style which
- replace standard marking and markup commands within floats
- (e.g., marginal remarks cause an error within floats
- so marginal marking can be disabled thus)
- Available styles: FLOATSAFE IDENTICAL
- [ Default: FLOATSAFE ]
-
---encoding=enc
--e enc Specify encoding of old.tex and new.tex. Typical encodings are
- ascii, utf8, latin1, latin9. A list of available encodings can be
- obtained by executing
- perl -MEncode -e 'print join ("\\n",Encode->encodings( ":all" )) ;'
- [Default encoding is utf8 unless the first few lines of the preamble contain
- an invocation "\\usepackage[..]{inputenc} in which case the
- encoding chosen by this command is asssumed. Note that ASCII (standard
- latex) is a subset of utf8]
-
---preamble=file
--p file Insert file at end of preamble instead of auto-generating
- preamble. The preamble must define the following commands
- \\DIFaddbegin,\\DIFaddend,\\DIFadd{..},
- \\DIFdelbegin,\\DIFdelend,\\DIFdel{..},
- and varieties for use within floats
- \\DIFaddbeginFL,\\DIFaddendFL,\\DIFaddFL{..},
- \\DIFdelbeginFL,\\DIFdelendFL,\\DIFdelFL{..}
- (If this option is set -t, -s, and -f options
- are ignored.)
-
---exclude-safecmd=exclude-file
---exclude-safecmd="cmd1,cmd2,..."
--A exclude-file
---replace-safecmd=replace-file
---append-safecmd=append-file
---append-safecmd="cmd1,cmd2,..."
--a append-file Exclude from, replace or append to the list of regex
- matching commands which are safe to use within the
- scope of a \\DIFadd or \\DIFdel command. The file must contain
- one Perl-RegEx per line (Comment lines beginning with # or % are
- ignored). A literal comma within the comma-separated list must be
- escaped thus "\\,", Note that the RegEx needs to match the whole of
- the token, i.e., /^regex\$/ is implied and that the initial
- "\\" of the command is not included. The --exclude-safecmd
- and --append-safecmd options can be combined with the --replace-safecmd
- option and can be used repeatedly to add cumulatively to the lists.
-
---exclude-textcmd=exclude-file
---exclude-textcmd="cmd1,cmd2,..."
--X exclude-file
---replace-textcmd=replace-file
---append-textcmd=append-file
---append-textcmd="cmd1,cmd2,..."
--x append-file Exclude from, replace or append to the list of regex
- matching commands whose last argument is text. See
- entry for --exclude-safecmd directly above for further details.
-
---replace-context1cmd=replace-file
---append-context1cmd=append-file
---append-context1cmd="cmd1,cmd2,..."
- Replace or append to the list of regex matching commands
- whose last argument is text but which require a particular
- context to work, e.g. \\caption will only work within a figure
- or table. These commands behave like text commands, except when
- they occur in a deleted section, when they are disabled, but their
- argument is shown as deleted text.
-
---replace-context2cmd=replace-file
---append-context2cmd=append-file
---append-context2cmd="cmd1,cmd2,..."
- As corresponding commands for context1. The only difference is that
- context2 commands are completely disabled in deleted sections, including
- their arguments.
-
-
---config var1=val1,var2=val2,...
--c var1=val1,.. Set configuration variables.
--c configfile Available variables:
- MINWORDSBLOCK (integer)
- FLOATENV (RegEx)
- PICTUREENV (RegEx)
- MATHENV (RegEx)
- MATHREPL (String)
- MATHARRENV (RegEx)
- MATHARRREPL (String)
- ARRENV (RegEx)
- COUNTERCMD (RegEx)
- This option can be repeated.
-
-
---packages=pkg1,pkg2,..
- Tell latexdiff that .tex file is processed with the packages in list
- loaded. This is normally not necessary if the .tex file includes the
- preamble, as the preamble is automatically scanned for \\usepackage commands.
- Use of the --packages option disables automatic scanning, so if for any
- reason package specific parsing needs to be switched off, use --packages=none.
- The following packages trigger special behaviour:
- endfloat hyperref amsmath
- [ Default: scan the preamble for \\usepackage commands to determine
- loaded packages.]
-
---show-preamble Print generated or included preamble commands to stdout.
-
---show-safecmd Print list of regex matching and excluding safe commands.
-
---show-textcmd Print list of regex matching and excluding commands with text argument.
-
---show-config Show values of configuration variables
-
---show-all Show all of the above
-
- NB For all --show commands, no old.tex or new.tex file needs to be given, and no
- differencing takes place.
-
---allow-spaces Allow spaces between bracketed or braced arguments to commands
- [Default requires arguments to directly follow each other without
- intervening spaces]
-
---flatten Replace \\input and \\include commands within body by the content
- of the files in their argument. If \\includeonly is present in the
- preamble, only those files are expanded into the document. However,
- no recursion is done, i.e. \\input and \\include commands within
- included sections are not expanded. The included files are assumed to
- be located in the same directories as the old and new master files,
- respectively, making it possible to organise files into old and new directories.
-
---help
--h Show this help text.
-
---ignore-warnings Suppress warnings about inconsistencies in length between input
- and parsed strings and missing characters.
-
---verbose
--V Output various status information to stderr during processing.
- Default is to work silently.
-
---version Show version number.
-EOF
-}
-
-=head1 NAME
-
-latexdiff - determine and markup differences between two latex files
-
-=head1 SYNOPSIS
-
-B<latexdiff> [ B<OPTIONS> ] F<old.tex> F<new.tex> > F<diff.tex>
-
-=head1 DESCRIPTION
-
-Briefly, I<latexdiff> is a utility program to aid in the management of
-revisions of latex documents. It compares two valid latex files, here
-called C<old.tex> and C<new.tex>, finds significant differences
-between them (i.e., ignoring the number of white spaces and position
-of line breaks), and adds special commands to highlight the
-differences. Where visual highlighting is not possible, e.g. for changes
-in the formatting, the differences are
-nevertheless marked up in the source.
-
-The program treats the preamble differently from the main document.
-Differences between the preambles are found using line-based
-differencing (similarly to the Unix diff command, but ignoring white
-spaces). A comment, "S<C<%DIF E<gt>>>" is appended to each added line, i.e. a
-line present in C<new.tex> but not in C<old.tex>. Discarded lines
- are deactivated by prepending "S<C<%DIF E<lt>>>". Changed blocks are preceded by
-comment lines giving information about line numbers in the original files. Where there are insignificant
-differences, the resulting file C<diff.tex> will be similar to
-C<new.tex>. At the end of the preamble, the definitions for I<latexdiff> markup commands are inserted.
-In differencing the main body of the text, I<latexdiff> attempts to
-satisfy the following guidelines (in order of priority):
-
-=over 3
-
-=item 1
-
-If both C<old.tex> and C<new.tex> are valid LaTeX, then the resulting
-C<diff.tex> should also be valid LateX. (NB If a few plain TeX commands
-are used within C<old.tex> or C<new.tex> then C<diff.tex> is not
-guaranteed to work but usually will).
-
-=item 2
-
-Significant differences are determined on the level of
-individual words. All significant differences, including differences
-between comments should be clearly marked in the resulting source code
-C<diff.tex>.
-
-=item 3
-
-If a changed passage contains text or text-producing commands, then
-running C<diff.tex> through LateX should produce output where added
-and discarded passages are highlighted.
-
-=item 4
-
-Where there are insignificant differences, e.g. in the positioning of
-line breaks, C<diff.tex> should follow the formatting of C<new.tex>
-
-=back
-
-For differencing the same algorithm as I<diff> is used but words
-instead of lines are compared. An attempt is made to recognize
-blocks which are completely changed such that they can be marked up as a unit.
-Comments are differenced line by line
-but the number of spaces within comments is ignored. Commands including
-all their arguments are generally compared as one unit, i.e., no mark-up
-is inserted into the arguments of commands. However, for a selected
-number of commands (for example, C<\caption> and all sectioning
-commands) the last argument is known to be text. This text is
-split into words and differenced just as ordinary text (use options to
-show and change the list of text commands, see below). As the
-algorithm has no detailed knowledge of LaTeX, it assumes all pairs of
-curly braces immediately following a command (i.e. a sequence of
-letters beginning with a backslash) are arguments for that command.
-As a restriction to condition 1 above it is thus necessary to surround
-all arguments with curly braces, and to not insert
-extraneous spaces. For example, write
-
- \section{\textem{This is an emphasized section title}}
-
-and not
-
- \section {\textem{This is an emphasized section title}}
-
-or
-
- \section\textem{This is an emphasized section title}
-
-even though all varieties are the same to LaTeX (but see
-B<--allow-spaces> option which allows the second variety).
-
-For environments whose content does not conform to standard LaTeX or
-where graphical markup does not make sense all markup commands can be
-removed by setting the PICTUREENV configuration variable, set by
-default to C<picture> and C<DIFnomarkup> environments; see B<--config>
-option). The latter environment (C<DIFnomarkup>) can be used to
-protect parts of the latex file where the markup results in illegal
-markup. You have to surround the offending passage in both the old and
-new file by C<\begin{DIFnomarkup}> and C<\end{DIFnomarkup}>. You must
-define the environment in the preambles of both old and new
-documents. I prefer to define it as a null-environment,
-
-C<\newenvironment{DIFnomarkup}{}{}>
-
-but the choice is yours. Any markup within the environment will be
-removed, and generally everything within the environment will just be
-taken from the new file.
-
-It is also possible to difference files which do not have a preamble.
- In this case, the file is processed in the main document
-mode, but the definitions of the markup commands are not inserted.
-
-All markup commands inserted by I<latexdiff> begin with "C<\DIF>". Added
-blocks containing words, commands or comments which are in C<new.tex>
-but not in C<old.tex> are marked by C<\DIFaddbegin> and C<\DIFaddend>.
-Discarded blocks are marked by C<\DIFdelbegin> and C<\DIFdelend>.
-Within added blocks all text is highlighted with C<\DIFadd> like this:
-C<\DIFadd{Added text block}>
-Selected `safe' commands can be contained in these text blocks as well
-(use options to show and change the list of safe commands, see below).
-All other commands as well as braces "{" and "}" are never put within
-the scope of C<\DIFadd>. Added comments are marked by prepending
-"S<C<%DIF E<gt> >>".
-
-Within deleted blocks text is highlighted with C<\DIFdel>. Deleted
-comments are marked by prepending "S<C<%DIF E<lt> >>". Non-safe command
-and curly braces within deleted blocks are commented out with
-"S<C<%DIFDELCMD E<lt> >>".
-
-
-
-=head1 OPTIONS
-
-=head2 Preamble
-
-The following options determine the visual markup style by adding the appropriate
-command definitions to the preamble. See the end of this section for a description of
-available styles.
-
-=over 4
-
-=item B<--type=markupstyle> or
-B<-t markupstyle>
-
-Add code to preamble for selected markup style. This option defines
-C<\DIFadd> and C<\DIFdel> commands.
-Available styles:
-
-C<UNDERLINE CTRADITIONAL TRADITIONAL CFONT FONTSTRIKE INVISIBLE
-CHANGEBAR CCHANGEBAR CULINECHBAR CFONTCBHBAR>
-
-[ Default: C<UNDERLINE> ]
-
-=item B<--subtype=markstyle> or
-B<-s markstyle>
-
-Add code to preamble for selected style for bracketing
-commands (e.g. to mark changes in margin). This option defines
-C<\DIFaddbegin>, C<\DIFaddend>, C<\DIFdelbegin> and C<\DIFdelend> commands.
-Available styles: C<SAFE MARGINAL DVIPSCOL>
-
-[ Default: C<SAFE> ]
-
-=item B<--floattype=markstyle> or
-B<-f markstyle>
-
-Add code to preamble for selected style which
-replace standard marking and markup commands within floats
-(e.g., marginal remarks cause an error within floats
-so marginal marking can be disabled thus). This option defines all
-C<\DIF...FL> commands.
-Available styles: C<FLOATSAFE TRADITIONALSAFE IDENTICAL>
-
-[ Default: C<FLOATSAFE> ]
-
-=item B<--encoding=enc> or
-B<-e enc>
-
-Specify encoding of old.tex and new.tex. Typical encodings are
-C<ascii>, C<utf8>, C<latin1>, C<latin9>. A list of available encodings can be
-obtained by executing
-
-C<perl -MEncode -e 'print join ("\n",Encode->encodings( ":all" )) ;' >
-
-[Default encoding is utf8 unless the first few lines of the preamble contain
-an invocation C<\usepackage[..]{inputenc}> in which case the
-encoding chosen by this command is asssumed. Note that ASCII (standard
-latex) is a subset of utf8]
-
-=item B<--preamble=file> or
-B<-p file>
-
-Insert file at end of preamble instead of generating
-preamble. The preamble must define the following commands
-C<\DIFaddbegin, \DIFaddend, \DIFadd{..},
-\DIFdelbegin,\DIFdelend,\DIFdel{..},>
-and varieties for use within floats
-C<\DIFaddbeginFL, \DIFaddendFL, \DIFaddFL{..},
-\DIFdelbeginFL, \DIFdelendFL, \DIFdelFL{..}>
-(If this option is set B<-t>, B<-s>, and B<-f> options
-are ignored.)
-
-=item B<--packages=pkg1,pkg2,..>
-
-Tell latexdiff that .tex file is processed with the packages in list
-loaded. This is normally not necessary if the .tex file includes the
-preamble, as the preamble is automatically scanned for C<\usepackage> commands.
-Use of the B<--packages> option disables automatic scanning, so if for any
-reason package specific parsing needs to be switched off, use B<--packages=none>.
-The following packages trigger special behaviour:
-
-=over 8
-
-=item C<amsmath>
-
-Configuration variable amsmath is set to C<align*> (Default: C<eqnarray*>)
-
-=item C<endfloat>
-
-Ensure that C<\begin{figure}> and C<\end{figure}> always appear by themselves on a line.
-
-=item C<hyperref>
-
-Change name of C<\DIFadd> and C<\DIFdel> commands to C<\DIFaddtex> and C<\DIFdeltex> and
-define new C<\DIFadd> and C<\DIFdel> commands, which provide a wrapper for these commands,
-using them for the text but not for the link defining command (where any markup would cause
-errors).
-
-=back
-
-[ Default: scan the preamble for C<\\usepackage> commands to determine
- loaded packages.]
-
-
-
-=item B<--show-preamble>
-
-Print generated or included preamble commands to stdout.
-
-=back
-
-=head2 Configuration
-
-=over 4
-
-=item B<--exclude-safecmd=exclude-file> or
-B<-A exclude-file> or B<--exclude-safecmd="cmd1,cmd2,...">
-
-=item B<--replace-safecmd=replace-file>
-
-=item B<--append-safecmd=append-file> or
-B<-a append-file> or B<--append-safecmd="cmd1,cmd2,...">
-
-Exclude from, replace or append to the list of regular expressions (RegEx)
-matching commands which are safe to use within the
-scope of a C<\DIFadd> or C<\DIFdel> command. The file must contain
-one Perl-RegEx per line (Comment lines beginning with # or % are
-ignored). Note that the RegEx needs to match the whole of
-the token, i.e., /^regex$/ is implied and that the initial
-"\" of the command is not included.
-The B<--exclude-safecmd> and B<--append-safecmd> options can be combined with the -B<--replace-safecmd>
-option and can be used repeatedly to add cumulatively to the lists.
- B<--exclude-safecmd>
-and B<--append-safecmd> can also take a comma separated list as input. If a
-comma for one of the regex is required, escape it thus "\,". In most cases it
-will be necessary to protect the comma-separated list from the shell by putting
-it in quotation marks.
-
-=item B<--exclude-textcmd=exclude-file> or
-B<-X exclude-file> or B<--exclude-textcmd="cmd1,cmd2,...">
-
-=item B<--replace-textcmd=replace-file>
-
-=item B<--append-textcmd=append-file> or
-B<-x append-file> or B<--append-textcmd="cmd1,cmd2,...">
-
-Exclude from, replace or append to the list of regular expressions
-matching commands whose last argument is text. See
-entry for B<--exclude-safecmd> directly above for further details.
-
-
-=item B<--replace-context1cmd=replace-file>
-
-=item B<--append-context1cmd=append-file> or
-=item B<--append-context1cmd="cmd1,cmd2,...">
-
-Replace or append to the list of regex matching commands
-whose last argument is text but which require a particular
-context to work, e.g. \caption will only work within a figure
-or table. These commands behave like text commands, except when
-they occur in a deleted section, when they are disabled, but their
-argument is shown as deleted text.
-
-=item B<--replace-context1cmd=replace-file>
-
-=item B<--append-context2cmd=append-file> or
-=item B<--append-context2cmd="cmd1,cmd2,...">
-As corresponding commands for context1. The only difference is that
-context2 commands are completely disabled in deleted sections, including
-their arguments.
-
-
-
-=item B<--config var1=val1,var2=val2,...> or B<-c var1=val1,..>
-
-=item B<-c configfile>
-
-Set configuration variables. The option can be repeated to set different
-variables (as an alternative to the comma-separated list).
-Available variables (see below for further explanations):
-
-C<MINWORDSBLOCK> (integer)
-
-C<FLOATENV> (RegEx)
-
-C<PICTUREENV> (RegEx)
-
-C<MATHENV> (RegEx)
-
-C<MATHREPL> (String)
-
-C<MATHARRENV> (RegEx)
-
-C<MATHARRREPL> (String)
-
-C<ARRENV> (RegEx)
-
-C<COUNTERCMD> (RegEx)
-
-=item B<--show-safecmd>
-
-Print list of RegEx matching and excluding safe commands.
-
-=item B<--show-textcmd>
-
-Print list of RegEx matching and excluding commands with text argument.
-
-=item B<--show-config>
-
-Show values of configuration variables.
-
-=item B<--show-all>
-
-Combine all --show commands.
-
-NB For all --show commands, no C<old.tex> or C<new.tex> file needs to be specified, and no
-differencing takes place.
-
-
-=back
-
-=head2 Miscellaneous
-
-=over 4
-
-=item B<--verbose> or B<-V>
-
-Output various status information to stderr during processing.
-Default is to work silently.
-
-=item B<--ignore-warnings>
-
-Suppress warnings about inconsistencies in length between input and
-parsed strings and missing characters. These warning messages are
-often related to non-standard latex or latex constructions with a
-syntax unknown to C<latexdiff> but the resulting difference argument
-is often fully functional anyway, particularly if the non-standard
-latex only occurs in parts of the text which have not changed.
-
-=item B<--allow-spaces>
-
-Allow spaces between bracketed or braced arguments to commands. Note
-that this option might have undesirable side effects (unrelated scope
-might get lumpeded with preceding commands) so should only be used if the
-default produces erroneous results. (Default requires arguments to
-directly follow each other without intervening spaces).
-
-=item B<--flatten>
-
-Replace C<\input> and C<\include> commands within body by the content
-of the files in their argument. If C<\includeonly> is present in the
-preamble, only those files are expanded into the document. However,
-no recursion is done, i.e. C<\input> and C<\include> commands within
-included sections are not expanded. The included files are assumed to
- be located in the same directories as the old and new master files,
-respectively, making it possible to organise files into old and new directories.
-
-Use of this option is not recommended
-primarily the processing time for the large documents is prohibitive, and
-the resulting difference document no longer reflects the structure of the
-input documents.
-
-=item B<--help> or
-B<-h>
-
-Show help text
-
-=item B<--version>
-
-Show version number
-
-=back
-
-=head2 Predefined styles
-
-=head2 Major types
-
-The major type determine the markup of plain text and some selected latex commands outside floats by defining the markup commands C<\DIFadd{...}> and C<\DIFdel{...}> .
-
-=over 10
-
-=item C<UNDERLINE>
-
-Added text is wavy-underlined and blue, discarded text is struck out and red
-(Requires color and ulem packages). Overstriking does not work in displayed math equations such that deleted parts of equation are underlined, not struck out (this is a shortcoming inherent to the ulem package).
-
-=item C<CTRADITIONAL>
-
-Added text is blue and set in sans-serif, and a red footnote is created for each discarded
-piece of text. (Requires color package)
-
-=item C<TRADITIONAL>
-
-Like C<CTRADITIONAL> but without the use of color.
-
-=item C<CFONT>
-
-Added text is blue and set in sans-serif, and discarded text is red and very small size.
-
-=item C<FONTSTRIKE>
-
-Added tex is set in sans-serif, discarded text small and struck out
-
-=item C<CCHANGEBAR>
-
-Added text is blue, and discarded text is red. Additionally, the changed text is marked with a bar in the margin (Requires color and changebar packages).
-
-=item C<CFONTCHBAR>
-
-Like C<CFONT> but with additional changebars (Requires color and changebar packages).
-
-=item C<CULINECHBAR>
-
-Like C<UNDERLINE> but with additional changebars (Requires color, ulem and changebar packages).
-
-=item C<CHANGEBAR>
-
-No mark up of text, but mark margins with changebars (Requires changebar package).
-
-=item C<INVISIBLE>
-
-No visible markup (but generic markup commands will still be inserted.
-
-=back
-
-=head2 Subtypes
-
-The subtype defines the commands that are inserted at the begin and end of added or discarded blocks, irrespectively of whether these blocks contain text or commands (Defined commands: C<\DIFaddbegin, \DIFaddend, \DIFdelbegin, \DIFdelend>)
-
-=over 10
-
-=item C<SAFE>
-
-No additional markup (Recommended choice)
-
-=item C<MARGIN>
-
-Mark beginning and end of changed blocks with symbols in the margin nearby (using
-the standard C<\marginpar> command - note that this sometimes moves somewhat
-from the intended position.
-
-=item C<DVIPSCOL>
-
-An alternative way of marking added passages in blue, and deleted ones in red. Note
-that C<DVIPSCOL> only works with the dvips converter, e.g. not pdflatex.
-(it is recommeneded to use instead the main types to effect colored markup,
-although in some cases coloring with dvipscol can be more complete).
-
-=back
-
-=head2 Float Types
-
-Some of the markup used in the main text might cause problems when used within
-floats (e.g. figures or tables). For this reason alternative versions of all
-markup commands are used within floats. The float type defines these alternative commands.
-
-=over 10
-
-=item C<FLOATSAFE>
-
-Use identical markup for text as in the main body, but set all commands marking the begin and end of changed blocks to null-commands. You have to choose this float type if your subtype is C<MARGIN> as C<\marginpar> does not work properly within floats.
-
-=item C<TRADITIONALSAFE>
-
-Mark additions the same way as in the main text. Deleted environments are marked by angular brackets \[ and \] and the deleted text is set in scriptscript size. This float type should always be used with the C<TRADITIONAL> and C<CTRADITIONAL> markup types as the \footnote command does not work properly in floating environments.
-
-=item C<IDENTICAL>
-
-Make no difference between the main text and floats.
-
-=back
-
-
-=head2 Configuration Variables
-
-=over 10
-
-=item C<MINWORDSBLOCK>
-
-Minimum number of tokens required to form an independent block. This value is
-used in the algorithm to detect changes of complete blocks by merging identical text parts of less than C<MINWORDSBLOCK> to the preceding added and discarded parts.
-
-[ Default: 3 ]
-
-=item C<FLOATENV>
-
-Environments whose name matches the regular expression in C<FLOATENV> are
-considered floats. Within these environments, the I<latexdiff> markup commands
-are replaced by their FL variaties.
-
-[ Default: S<C<(?:figure|table|plate)[\w\d*@]*> >]
-
-=item C<PICTUREENV>
-
-Within environments whose name matches the regular expression in C<PICTUREENV>
-all latexdiff markup is removed (in pathologic cases this might lead to
- inconsistent markup but this situation should be rare).
-
-[ Default: S<C<(?:picture|DIFnomarkup)[\w\d*@]*> >]
-
-=item C<MATHENV>,C<MATHREPL>
-
-If both \begin and \end for a math environment (environment name matching C<MATHENV>
-or \[ and \])
-are within the same deleted block, they are replaced by a \begin and \end commands for C<MATHREPL>
-rather than being commented out.
-
-[ Default: C<MATHENV>=S<C<(?:displaymath|equation)> >, C<MATHREPL>=S<C<displaymath> >]
-
-=item C<MATHARRENV>,C<MATHARRREPL>
-
-as C<MATHENV>,C<MATHREPL> but for equation arrays
-
-[ Default: C<MATHARRENV>=S<C<eqnarray\*?> >, C<MATHREPL>=S<C<eqnarray> >]
-
-=item C<ARRENV>
-
-If a match to C<ARRENV> is found within an inline math environment within a deleted or added block, then the inlined math
-is surrounded by C<\mbox{>...C<}>. This is necessary as underlining does not work within inlined array environments.
-
-[ Default: C<ARRENV>=S<C<(?:array|[pbvBV]matrix)> >
-
-=item C<COUNTERCMD>
-
-If a command in a deleted block which is also in the textcmd list matches C<COUNTERCMD> then an
-additional command C<\addtocounter{>F<cntcmd>C<}{-1}>, where F<cntcmd> is the matching command, is appended in the diff file such that the numbering in the diff file remains synchronized with the
-numbering in the new file.
-
-[ Default: C<COUNTERCMD>=C<(?:footnote|part|section|subsection> ...
-
-C<|subsubsection|paragraph|subparagraph)> ]
-
-=back
-
-=head1 BUGS
-
-UTF-8 support requires a relatively new version of perl (5.8.0 is sufficient
-but 5.6.2 up would work OK, too).
-
-Option allow-spaces not implemented entirely consistently. It breaks
-the rules that number and type of white space does not matter, as
-different numbers of inter-argument spaces are treated as significant.
-
-Please send bug reports
-to I<tilmann@esc.cam.ac.uk>. Include the serial number of I<latexdiff>
-(from comments at the top of the source or use B<--version>). If you come across latex
-files that are error-free and conform to the specifications set out
-above, and whose differencing still does not result in error-free
-latex, please send me those files, ideally edited to only contain the
-offending passage as long as that still reproduces the problem.
-
-=head1 SEE ALSO
-
-L<latexrevise>
-
-=head1 PORTABILITY
-
-I<latexdiff> does not make use of external commands and thus should run
-on any platform supporting Perl 5.6 or higher. If files with encodings
-other than ASCII or UTF-8 are processed, Perl 5.8 or higher is required.
-
-The standard version of I<latexdiff> requires installation of the Perl package
-C<Algorithm::Diff> (available from I<www.cpan.org> -
-I<http://search.cpan.org/~nedkonz/Algorithm-Diff-1.15>) but a stand-alone
-version, I<latexdiff-so>, which has this package inlined, is available, too.
-I<latexdiff-fast> requires the I<diff> command to be present.
-
-=head1 AUTHOR
-
-Copyright (C) 2004-2007 Frederik Tilmann
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License Version 2
-
-Contributors of fixes and additions: V. Kuhlmann, J. Paisley, N. Becker, T. Doerges, K. Huebner
-Thanks to everyone who send in bug reports.
-
-=cut
-
-__END__
-%%BEGIN SAFE COMMANDS
-% Regex matching commands which can safely be in the
-% argument of a \DIFadd or \DIFdel command (leave out the \)
-arabic
-dashbox
-emph
-fbox
-framebox
-hspace
-math.*
-makebox
-mbox
-pageref
-ref
-symbol
-raisebox
-rule
-text.*
-shortstack
-usebox
-dag
-ddag
-copyright
-pounds
-S
-P
-oe
-OE
-ae
-AE
-aa
-AA
-o
-O
-l
-L
-frac
-ss
-sqrt
-ldots
-cdots
-vdots
-ddots
-alpha
-beta
-gamma
-delta
-epsilon
-varepsilon
-zeta
-eta
-theta
-vartheta
-iota
-kappa
-lambda
-mu
-nu
-xi
-pi
-varpi
-rho
-varrho
-sigma
-varsigma
-tau
-upsilon
-phi
-varphi
-chi
-psi
-omega
-Gamma
-Delta
-Theta
-Lambda
-Xi
-Pi
-Sigma
-Upsilon
-Phi
-Psi
-Omega
-ps
-mp
-times
-div
-ast
-star
-circ
-bullet
-cdot
-cap
-cup
-uplus
-sqcap
-vee
-wedge
-setminus
-wr
-diamond
-(?:big)?triangle.*
-lhd
-rhd
-unlhd
-unrhd
-oplus
-ominus
-otimes
-oslash
-odot
-bigcirc
-d?dagger
-amalg
-leq
-prec
-preceq
-ll
-(?:sq)?su[bp]set(?:eq)?
-in
-vdash
-geq
-succ(?:eq)?
-gg
-ni
-dashv
-equiv
-sim(?:eq)?
-asymp
-approx
-cong
-neq
-doteq
-propto
-models
-perp
-mid
-parallel
-bowtie
-Join
-smile
-frown
-.*arrow
-(?:long)?mapsto
-.*harpoon.*
-leadsto
-aleph
-hbar
-imath
-jmath
-ell
-wp
-Re
-Im
-mho
-prime
-emptyset
-nabla
-surd
-top
-bot
-angle
-forall
-exists
-neg
-flat
-natural
-sharp
-backslash
-partial
-infty
-Box
-Diamond
-triangle
-clubsuit
-diamondsuit
-heartsuit
-spadesuit
-sum
-prod
-coprod
-int
-oint
-big(?:sq)?c[au]p
-bigvee
-bigwedge
-bigodot
-bigotimes
-bigoplus
-biguplus
-(?:arc)?(?:cos|sin|tan|cot)h?
-csc
-arg
-deg
-det
-dim
-exp
-gcd
-hom
-inf
-ker
-lg
-lim
-liminf
-limsup
-ln
-log
-max
-min
-Pr
-sec
-sup
-(SUPER|SUB)SCRIPTNB
-(SUPER|SUB)SCRIPT
-%%END SAFE COMMANDS
-
-%%BEGIN TEXT COMMANDS
-% Regex matching commands with a text argument (leave out the \)
-addcontents.*
-cc
-closing
-chapter
-dashbox
-emph
-encl
-fbox
-framebox
-footnote
-footnotetext
-framebox
-part
-(sub){0,2}section\*?
-(sub)?paragraph\*?
-makebox
-mbox
-opening
-parbox
-raisebox
-savebox
-sbox
-shortstack
-signature
-text.*
-value
-underline
-sqrt
-(SUPER|SUB)SCRIPT
-%%END TEXT COMMANDS
-
-%%BEGIN CONTEXT1 COMMANDS
-% Regex matching commands with a text argument (leave out the \), which will fail out of context, but who's arguemtn should be printed as plain text
-caption
-%%END CONTEXT1 COMMANDS
-
-%%BEGIN CONTEXT2 COMMANDS
-% Regex matching commands with a text argument (leave out the \), which will fail out of context, but who's arguemtn should be printed as plain text
-title
-author
-%%END CONTEXT2 COMMANDS
-
-
-%% TYPES (Commands for highlighting changed blocks)
-
-%DIF UNDERLINE PREAMBLE
-\RequirePackage[normalem]{ulem}
-\RequirePackage{color}\definecolor{RED}{rgb}{1,0,0}\definecolor{BLUE}{rgb}{0,0,1}
-\providecommand{\DIFadd}[1]{{\protect\color{blue}\uwave{#1}}}
-\providecommand{\DIFdel}[1]{{\protect\color{red}\sout{#1}}}
-%DIF END UNDERLINE PREAMBLE
-
-%DIF CTRADITIONAL PREAMBLE
-\RequirePackage{color}\definecolor{RED}{rgb}{1,0,0}\definecolor{BLUE}{rgb}{0,0,1}
-\RequirePackage[stable]{footmisc}
-\providecommand{\DIFadd}[1]{{\protect\color{blue} \sf #1}}
-\providecommand{\DIFdel}[1]{{\protect\color{red} [..\footnote{removed: #1} ]}}
-%DIF END CTRADITIONAL PREAMBLE
-
-%DIF TRADITIONAL PREAMBLE
-\RequirePackage[stable]{footmisc}
-\providecommand{\DIFadd}[1]{{\sf #1}}
-\providecommand{\DIFdel}[1]{{[..\footnote{removed: #1} ]}}
-%DIF END TRADITIONAL PREAMBLE
-
-%DIF CFONT PREAMBLE
-\RequirePackage{color}\definecolor{RED}{rgb}{1,0,0}\definecolor{BLUE}{rgb}{0,0,1}
-\providecommand{\DIFadd}[1]{{\protect\color{blue} \sf #1}}
-\providecommand{\DIFdel}[1]{{\protect\color{red} \scriptsize #1}}
-%DIF END CFONT PREAMBLE
-
-%DIF FONTSTRIKE PREAMBLE
-\RequirePackage[normalem]{ulem}
-\providecommand{\DIFadd}[1]{{\sf #1}}
-\providecommand{\DIFdel}[1]{{\footnotesize \sout{#1}}}
-%DIF END FONTSTRIKE PREAMBLE
-
-%DIF CCHANGEBAR PREAMBLE
-\RequirePackage[dvips]{changebar}
-\RequirePackage{color}\definecolor{RED}{rgb}{1,0,0}\definecolor{BLUE}{rgb}{0,0,1}
-\providecommand{\DIFadd}[1]{\protect\cbstart{\protect\color{blue}#1}\protect\cbend}
-\providecommand{\DIFdel}[1]{\protect\cbdelete{\protect\color{red}#1}\protect\cbdelete}
-%DIF END CCHANGEBAR PREAMBLE
-
-%DIF CFONTCHBAR PREAMBLE
-\RequirePackage[dvips]{changebar}
-\RequirePackage{color}\definecolor{RED}{rgb}{1,0,0}\definecolor{BLUE}{rgb}{0,0,1}
-\providecommand{\DIFadd}[1]{\protect\cbstart{\protect\color{blue}\sf #1}\protect\cbend}
-\providecommand{\DIFdel}[1]{\protect\cbdelete{\protect\color{red}\scriptsize #1}\protect\cbdelete}
-%DIF END CFONTCHBAR PREAMBLE
-
-%DIF CULINECHBAR PREAMBLE
-\RequirePackage[normalem]{ulem}
-\RequirePackage[dvips]{changebar}
-\RequirePackage{color}
-\providecommand{\DIFadd}[1]{\protect\cbstart{\protect\color{blue}\uwave{#1}}\protect\cbend}
-\providecommand{\DIFdel}[1]{\protect\cbdelete{\protect\color{red}\sout{#1}}\protect\cbdelete}
-%DIF END CULINECHBAR PREAMBLE
-
-%DIF CHANGEBAR PREAMBLE
-\RequirePackage[dvips]{changebar}
-\providecommand{\DIFadd}[1]{\protect\cbstart{#1}\protect\cbend}
-\providecommand{\DIFdel}[1]{\protect\cbdelete}
-%DIF END CHANGEBAR PREAMBLE
-
-%DIF INVISIBLE PREAMBLE
-\providecommand{\DIFadd}[1]{#1}
-\providecommand{\DIFdel}[1]{}
-%DIF END INVISIBLE PREAMBLE
-
-
-%% SUBTYPES (Markers for beginning and end of changed blocks)
-
-%DIF SAFE PREAMBLE
-\providecommand{\DIFaddbegin}{}
-\providecommand{\DIFaddend}{}
-\providecommand{\DIFdelbegin}{}
-\providecommand{\DIFdelend}{}
-%DIF END SAFE PREAMBLE
-
-%DIF MARGIN PREAMBLE
-\providecommand{\DIFaddbegin}{\protect\marginpar{a[}}
-\providecommand{\DIFaddend}{\protect\marginpar{]}}
-\providecommand{\DIFdelbegin}{\protect\marginpar{d[}}
-\providecommand{\DIFdelend}{\protect\marginpar{]}}
-%DIF END BRACKET PREAMBLE
-
-%DIF DVIPSCOL PREAMBLE
-%Note: only works with dvips converter
-\RequirePackage{color}
-\RequirePackage{dvipscol}
-\providecommand{\DIFaddbegin}{\protect\nogroupcolor{blue}}
-\providecommand{\DIFaddend}{\protect\nogroupcolor{black}}
-\providecommand{\DIFdelbegin}{\protect\nogroupcolor{red}}
-\providecommand{\DIFdelend}{\protect\nogroupcolor{black}}
-%DIF END DVIPSCOL PREAMBLE
-
-
-%% FLOAT TYPES
-
-%DIF FLOATSAFE PREAMBLE
-\providecommand{\DIFaddFL}[1]{\DIFadd{#1}}
-\providecommand{\DIFdelFL}[1]{\DIFdel{#1}}
-\providecommand{\DIFaddbeginFL}{}
-\providecommand{\DIFaddendFL}{}
-\providecommand{\DIFdelbeginFL}{}
-\providecommand{\DIFdelendFL}{}
-%DIF END FLOATSAFE PREAMBLE
-
-%DIF IDENTICAL PREAMBLE
-\providecommand{\DIFaddFL}[1]{\DIFadd{#1}}
-\providecommand{\DIFdelFL}[1]{\DIFdel{#1}}
-\providecommand{\DIFaddbeginFL}{\DIFaddbegin}
-\providecommand{\DIFaddendFL}{\DIFaddend}
-\providecommand{\DIFdelbeginFL}{\DIFdelbegin}
-\providecommand{\DIFdelendFL}{\DIFdelend}
-%DIF END IDENTICAL PREAMBLE
-
-%DIF TRADITIONALSAFE PREAMBLE
-% procidecommand color to make this work for TRADITIONAL and CTRADITIONAL
-\providecommand{\color}[1]{}
-\providecommand{\DIFaddFL}[1]{\DIFadd{#1}}
-\providecommand{\DIFdel}[1]{{\protect\color{red}[..{\scriptsize {removed: #1}} ]}}
-\providecommand{\DIFaddbeginFL}{}
-\providecommand{\DIFaddendFL}{}
-\providecommand{\DIFdelbeginFL}{}
-\providecommand{\DIFdelendFL}{}
-%DIF END FLOATSAFE PREAMBLE
-
-%% SPECIAL PACKAGE PREAMBLE COMMANDS
-
-% Standard \DIFadd and \DIFdel are redefined as \DIFaddtex and \DIFdeltex
-% when hyperref package is included.
-%DIF HYPERREF PREAMBLE
-\providecommand{\DIFadd}[1]{\texorpdfstring{\DIFaddtex{#1}}{#1}}
-\providecommand{\DIFdel}[1]{\texorpdfstring{\DIFdeltex{#1}}{}}
-%DIF END HYPERREF PACKAGE