From b41810159701157d4c4082233224c298a27f4574 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Mon, 26 Jun 2017 21:22:49 +0000 Subject: latexdiff (26jun17) git-svn-id: svn://tug.org/texlive/trunk@44704 c570f23f-e606-0410-a88d-b1316a301751 --- .../linked_scripts/latexdiff/latexdiff-vc.pl | 235 +++++-- .../texlive/linked_scripts/latexdiff/latexdiff.pl | 760 +++++++++++++++------ 2 files changed, 744 insertions(+), 251 deletions(-) (limited to 'Build/source/texk') diff --git a/Build/source/texk/texlive/linked_scripts/latexdiff/latexdiff-vc.pl b/Build/source/texk/texlive/linked_scripts/latexdiff/latexdiff-vc.pl index fccaaacb564..907f38ece50 100755 --- a/Build/source/texk/texlive/linked_scripts/latexdiff/latexdiff-vc.pl +++ b/Build/source/texk/texlive/linked_scripts/latexdiff/latexdiff-vc.pl @@ -27,7 +27,12 @@ # # TODO/IDEAS: - option to call external pre-processing codes # - choose type of latex processor / bibtex (luatex, xelatex etc) -# versio 1.2.0: +# version 1.2.1 (22 June 2017) +# - bug fix: --hg option was not recognised (partially fixes github issue #93 ) +# - wrap passed-through options to latexdiff in quotation marks (fix github issue #58 ) +# - program names for latexdiff, latex, dvips, bibtex configurable (fixes issue #40) +# +# version 1.2.0: # - depracation fix: left brace in RegEx now needs to be escaped # # version 1.1.1: @@ -63,8 +68,8 @@ use strict ; use warnings ; my $versionstring=< 1); # generate a temp dir, which will automatically be deleted at program exit # Variables my ($file1,$file2,$diff,$diffbase,$rootdir,$answer,$options,$infile,$append,$dirname,$cwd); my (@files,@ldoptions,@tmpfiles,@ptmpfiles,@difffiles,$extracomp); # , + +# keep commands to be used in config hash +my (%CFG)=( + LATEX => "latex", + LATEXDIFF => "latexdiff", + DVI2 => "dvips", + BIBTEX => "bibtex" + ); + + + Getopt::Long::Configure('pass_through','bundling'); GetOptions('revision|r:s' => \@revs, @@ -91,6 +111,8 @@ GetOptions('revision|r:s' => \@revs, 'git' => \$git, 'hg' => \$hg, 'dir|d:s' => \$dir, + 'run' => \$run, + 'dvi' => \$rundvi2, 'fast' => \$fast, 'so' => \$so, 'postscript|ps' => \$postscript, @@ -98,6 +120,8 @@ GetOptions('revision|r:s' => \@revs, 'force' => \$force, 'only-changes' => \$onlychanges, 'flatten:s' => \$flatten, + 'config|c=s' => \@configlist, + 'show-config' => \$showconfig, 'version' => \$version, 'help|h' => \$help, 'debug!' => \$debug); @@ -116,11 +140,11 @@ if ( $version ) { } if ( $so ) { - $latexdiff='latexdiff-so'; + $CFG{LATEXDIFF}='latexdiff-so'; } if ( $fast ) { die "Cannot specify more than one of --fast or --so " if ($so); - $latexdiff='latexdiff-fast'; + $CFG{LATEXDIFF}='latexdiff-fast'; } if ( $cvs ) { @@ -138,6 +162,65 @@ if ( $git ) { die "Cannot specify more than one of --cvs, --rcs, --svn --git or --hg." if ($vc); $vc="GIT"; } +if ( $hg ) { + die "Cannot specify more than one of --cvs, --rcs, --svn --git or --hg." if ($vc); + $vc="HG"; +} + +if ( $pdf ) { + $CFG{LATEX} = "pdflatex"; + $run=1; +} elsif ( $postscript ) { + $CFG{LATEX} = "latex"; + $rundvi2=1; +} + +if ( $rundvi2 ) { + $run=1; +} + +# setting config variables from command line. Explicitly set configuration options take precedence over convenience options, +# therefore this is executed last +@config=(); +foreach $config ( @configlist ) { + if (-f $config || lc $config eq '/dev/null' ) { + open(FILE,$config) or die ("Couldn't open configuration file $config: $!"); + while () { + chomp; + next if /^\s*#/ || /^\s*%/ || /^\s*$/ ; + push (@config,$_); + } + close(FILE); + } + else { +# foreach ( split(",",$config) ) { +# push @config,$_; +# } + push @config,split(",",$config) + } +} + +$configlatexdiff=""; +foreach $assign ( @config ) { + $assign=~ m/\s*(\w*)\s*=\s*(\S*)\s*$/ or die "Illegal assignment $assign in configuration list (must be variable=value)"; + if ( defined($CFG{$1})) { + # known latexdiff-vc option + $CFG{$1}=$2; + } else { + # unknown option, assume it is a latexdiff configuration variable + $configlatexdiff .= "$1=$2,"; + } +} +# delete last comma inv $configlatexdiff if present +$configlatexdiff =~ s/,$// ; + +if ($showconfig) { + print "# Configuration variables (latexdiff-vc):\n"; + foreach ( keys %CFG ) { + print "$_=$CFG{$_}\n"; + } + exit 0; +} # check whether the first file name or first passed-through option for latexdiff got misinterpreted as an option to an empty -r option @@ -156,7 +239,7 @@ if ( defined($flatten) && ( -f $flatten || $flatten =~ /^-/ ) ) { $flatten=""; } -print "DEBUG: PDF $pdf latexdiff-vc command line: ", join(" ",@ARGV), "\n" if $debug; +print STDERR "DEBUG: PDF $pdf latexdiff-vc command line: ", join(" ",@ARGV), "\n" if $debug; $file2=pop @ARGV; ( defined($file2) && $file2 =~ /\.(tex|bbl|flt)$/ ) or pod2usage("Must specify at least one tex, bbl or flt file"); @@ -217,7 +300,7 @@ if ( scalar(@revs)>0 ) { # $diffcmd = "git diff "; # $patchcmd = "patch -R -p1"; } elsif ( $vc eq "HG" ) { - $diffcmd = "hg diff -r"; + $diffcmd = "hg diff --root . -r"; $patchcmd = "patch -R -p1"; } else { print STDERR "Unknown versioning system $vc \n"; @@ -241,6 +324,10 @@ while( $file1=pop @ARGV ) { } } +if ( length $configlatexdiff >0 ) { + push @ldoptions, "--config $configlatexdiff"; +} + if ( defined($flatten) ) { push @ldoptions, "--flatten" ; } @@ -252,15 +339,15 @@ if ( $onlychanges ) { if ( scalar(@revs) == 0 ) { pod2usage("When -r option is not used, two .tex files (old and new) must be given on the command line") unless @files==2; - warn "Option -flatten should normally be combined with -r option (Results will probably not be as expected)" if $flatten; + warn "Option -flatten should normally be combined with -r option (Results will probably not be as expected)" if defined($flatten); # compare two files $file1=shift @files ; } else { # revision control - if ( $flatten ) { + if ( defined($flatten) ) { pod2usage("Only one root file must be given if --flatten option is used with version control") if @files != 1; $tempdir='.' if ( $flatten eq "keep-intermediate" ); - print "flatten tempdir |$tempdir|\n"; + print STDERR "flatten tempdir |$tempdir|\n" if $debug; if ( $vc eq "SVN" ) { my (@infoout)=`svn info`; my (@urlline) = grep(/^URL:/, @infoout); @@ -293,6 +380,12 @@ if ( ($vc eq "SVN" || $vc eq "CVS") && scalar(@revs)) { length($revs[0]) > 0 or $revs[0]="HEAD"; } +if ($vc eq "HG" && scalar(@revs)) { + length($revs[$#revs]) > 0 or $revs[$#revs]="tip"; + length($revs[0]) > 0 or $revs[0]="tip"; +} + +print STDERR "DEBUG LDOptions($#ldoptions): " . join(":",@ldoptions) . "\n" if $debug; # cycle through all files @@ -357,7 +450,11 @@ while ( $infile=$file2=shift @files ) { system("mkdir -p $dirname") unless ( -e $dirname ); # Remaining options are passed to latexdiff - $options = join(" ",@ldoptions); + if (scalar(@ldoptions) > 0 ) { + $options = "\'" . join("\' \'",@ldoptions) . "\'"; + } else { + $options = ""; + } if ( -e $diff && ! $force ) { print STDERR "OK to overwrite existing file $diff (y/n)? "; @@ -367,13 +464,13 @@ while ( $infile=$file2=shift @files ) { die "Abort ... " ; } } - print "Running: $latexdiff $options \"$file1\" \"$file2\" > \"$diff\"\n"; - unless ( system("$latexdiff $options \"$file1\" \"$file2\" > \"$diff\"") == 0 ) { - print STDERR "Something went wrong in $latexdiff. Deleting $diff and abort\n" ; unlink $diff ; exit(5) + print STDERR "Running: $CFG{LATEXDIFF} $options \"$file1\" \"$file2\" > \"$diff\"\n"; + unless ( system("$CFG{LATEXDIFF} $options \"$file1\" \"$file2\" > \"$diff\"") == 0 ) { + print STDERR "Something went wrong in $CFG{LATEXDIFF}. Deleting $diff and abort\n" ; unlink $diff ; exit(5) }; print "Generated difference file $diff\n"; - if ( ( $postscript or $pdf ) and !( scalar(@revs) && greptex( qr/\\document(?:class|style)/ , $diff ) ) ) { + if ( $run and !( scalar(@revs) && greptex( qr/\\document(?:class|style)/ , $diff ) ) ) { # save filename for later processing if postscript or pdf conversion is requested and either two-file mode was used (scalar(@revs)==0) or the diff file contains documentclass statement (ie. is a root document) push @difffiles, $diff ; } @@ -393,46 +490,53 @@ foreach $diff ( @difffiles ) { # adapt magically changebar styles to [pdftex] display driver if pdf output was selected if ( $pdf ) { system("sed \"s/Package\\[dvips\\]/Package[pdftex]/\" \"$diff\" > \"$diff.tmp$$\" ; \\mv \"$diff.tmp$$\" \"$diff\""); - $latexcmd = "pdflatex"; - } elsif ( $postscript ) { - $latexcmd = "latex"; } - if ( $pdf | $postscript ) { + if ( $run ) { print STDERR "PDF: $pdf Postscript: $postscript cwd $cwd\n" if $debug; if ( system("grep -q \'^[^%]*\\\\bibliography{\' \"$diff\"") == 0 ) { - system("$latexcmd --interaction=batchmode \"$diff\"; bibtex \"$diffbase\";"); + system("$CFG{LATEX} --interaction=batchmode \"$diff\"; $CFG{BIBTEX} \"$diffbase\";"); push @ptmpfiles, "$diffbase.bbl","$diffbase.bbl" ; } # if special needs, as CHANGEBAR if ( $extracomp ) { # print "Extracomp\n"; - system("$latexcmd --interaction=batchmode \"$diff\";"); + system("$CFG{LATEX} --interaction=batchmode \"$diff\";"); } # final compilation - system("$latexcmd --interaction=batchmode \"$diff\";"); # needed if cross-refs - system("$latexcmd \"$diff\";"); # final, with possible error messages + system("$CFG{LATEX} --interaction=batchmode \"$diff\";"); # needed if cross-refs + system("$CFG{LATEX} \"$diff\";"); # final, with possible error messages - if ( $postscript ) { + if ( $rundvi2 ) { my $dvi="$diffbase.dvi"; - my $ps="$diffbase.ps"; + #my $ps="$diffbase.ps"; my $ppoption=""; if ( $onlychanges ) { $ppoption="-pp ".join(",",findchangedpages("$diffbase.aux")); } - system("dvips $ppoption -o $ps $dvi"); + #system("$CFG{DVI2} $ppoption -o $ps $dvi"); + system("$CFG{DVI2} $ppoption $dvi"); push @ptmpfiles, "$diffbase.aux","$diffbase.log",$dvi ; - print "Generated postscript file $ps\n"; - } elsif ( $pdf ) { + #print STDERR "Generated postscript file $ps\n"; + } elsif ( $run ) { if ( $onlychanges ) { my @pages=findchangedpages("$diffbase.aux"); ### print ("Running pdftk \"$diffbase.pdf\" cat " . join(" ",@pages) . " output \"$diffbase-changedpage.pdf\"\n") or - system ("pdftk \"$diffbase.pdf\" cat " . join(" ",@pages) . " output \"$diffbase-changedpage.pdf\"")==0 or - die ("Could not execute . Return code: $?"); + my $qpdf = `which qpdf`; + $qpdf =~ s/^\s+|\s+$//g; + my $pdftk = `which pdftk`; + $pdftk =~ s/^\s+|\s+$//g; + if (-x $qpdf) { + system("qpdf --linearize \"$diffbase.pdf\" --pages \"$diffbase.pdf\" " . join(",", @pages) . " -- \"$diffbase-changedpage.pdf\" ") == 0 + or die("could not execute qpdf to strip pages. Return code: $?"); + } elsif (-x $pdftk) { + system ("pdftk \"$diffbase.pdf\" cat " . join(" ",@pages) . " output \"$diffbase-changedpage.pdf\"")==0 or + die ("Could not execute . Return code: $?"); + } move("$diffbase-changedpage.pdf","$diffbase.pdf"); } push @ptmpfiles, "$diffbase.aux","$diffbase.log"; @@ -591,6 +695,51 @@ The generic usage of this function is : C, the intermediate revision snapshots are kept in the current directory (Default is to store them in a temporary directory and delete them after generating the diff file.) +=item B<--config var1=val1,var2=val2,...> or B<-c var1=val1,..> + + + +=item B<--only-changes> + +Post-process the output such that only pages with changes on them are displayed. This requires the use of subtype ZLABEL +in latexdiff, which will be set automatically, but any manually set -s option will be overruled (also requires zref package to +be installed). (note that this option must be combined with --ps or --pdf to make sense) + +=item B<--force> + +Overwrite existing diff files without asking for confirmation. Default +behaviour is to ask for confirmation before overwriting an existing difference +file. + +=item B<--run> + +run latex command on diff file after generation of diff file. + +=item B<--dvi> + +run latex and dvixxx commands after generation of diff file. + +=item B<-c configfile> + +Set configuration variables for latexdiff and latexdiff-vc. The option can be repeated to set different +variables (as an alternative to the comma-separated list). +Available variables for latexdiff-vc: + +=over 8 + +=item C latexdiff command (e.g. latexdiff-fast, latexdiff-so). This command should support the option C<--interaction=batchmode> + +=item C latex command (e.g. pdflatex, lualatex) + +=item C Command for conversion of dvi file (e.g. dvips, dvipdf) + +=item C Command replacing bibtex + +=back + +All other config variables are passed to latexdiff. Explicity set configuration changes always override implicit +changes by the following shortcut options B<--fast>, B<--so>, B<--ps> and B<--pdf>. + =item B<--fast> or B<--so> Use C or C, respectively (instead of C). @@ -609,21 +758,15 @@ bibtex; latex; latex; dvips>. Generate pdf output from difference file using C. This will run the sequence C on the difference file, or C for files requiring bibtex. +Note that this is not just a shortcut for setting configuration variable but also triggers +some special behaviour. -=item B<--only-changes> - -Post-process the output such that only pages with changes on them are displayed. This requires the use of subtype ZLABEL -in latexdiff, which will be set automatically, but any manually set -s option will be overruled (also requires zref package to -be installsed). (note that this option must be combined with --ps or --pdf to make sense) -=item B<--force> +=item B<--show-config> -Overwrite existing diff files without asking for confirmation. Default -behaviour is to ask for confirmation before overwriting an existing difference -file. +Show values of configuration variables. -=item B<--help> or -B<-h> +=item B<--help> or B<-h> Show help text @@ -654,8 +797,8 @@ or send them to I. Include the version number =head1 AUTHOR -Version 1.2.0 -Copyright (C) 2005-2016 Frederik Tilmann +Version 1.2.1 +Copyright (C) 2005-2017 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 3 diff --git a/Build/source/texk/texlive/linked_scripts/latexdiff/latexdiff.pl b/Build/source/texk/texlive/linked_scripts/latexdiff/latexdiff.pl index f29ae9bd4fd..3fd8ea469c7 100755 --- a/Build/source/texk/texlive/linked_scripts/latexdiff/latexdiff.pl +++ b/Build/source/texk/texlive/linked_scripts/latexdiff/latexdiff.pl @@ -24,6 +24,28 @@ # # ToDo: # +# Version 1.2.1 (22 June 2017) +# - add "DeclareOldFontCommand" to styles using \bf or \sf old style font commands (fixies issue #92 ) +# - improved markup: process lstinline commands in listings package correctly +# for styles using colour, \verb and \lstinline arguments are marked up with colour (blue for added, red for deleted) +# - bug fix: protecting inline math expressions for mbox did not work as intended (see stack exchange question: http://tex.stackexchange.com/questions/359412/compiling-the-latexdiff-when-adding-a-subscript-before-a-pmatrix-environment-cau) +# - bug fix: when deleted \item commands are followed immediately by unsafe commands, they were not restored properly +# (thanks to J. Protze for pull request) (pull request #89) +# - treat lstlisting and comment as equivalent to verbatim environment +# make environments that are treated like verbatim environments configurable (config variable VERBATIMENV) +# treat lstinlne as equivalent to verb command +# partially addresses issue #38 +# - refactoring: set default configuration variables in a hash, and those that correspond to lists +# - feature: option --add-to-config used to amend configuration variables, which are regex pattern lists +# - bug fix: deleted figures when endfloat package is activated +# - bug fix: alignat environment now always processed correctly (fix issues #65) +# - bug fix: avoid processing of commands as potential files in routine init_regex_arr (fix issue #70 ) +# - minimal feature enhancement: treat '@' as allowed character in commands (strictly speaking requires prior \makeatletter statement, but always assuming it to be +# @ a letter if it is part of a command name will usually lead to the correct behaviour (see http://tex.stackexchange.com/questions/346651/latexdiff-and-let) +# - new feature/bug fix: --flatten option \endinput in included files now respected but only if \endinput stands right at the beginning of the line (issue #77) +# - bug fix: flatten would incorrectly attempt to process commented out \include commands (from discussion in issue #77 ) +# - introduce an invisible space (\hspace{0pt} after \mbox{..} auxiliary commands (not in math mode), to allow line breaks between added and deleted citations (change should not cause adverse behaviour otherwise) +# # Version 1.2.0: # - highlight new and deleted figures # - bug fix in title mark-up. Previously deleted commands in title (such as \title, \author or \date) were marked up erroneously @@ -597,34 +619,76 @@ my ($algodiffversion)=split(/ /,$Algorithm::Diff::VERSION); my ($versionstring)=< 3, # minimum number of tokens to form an independent block + # shorter identical blocks will be merged to the previous word + SCALEDELGRAPHICS => 0.5, # factor with which deleted figures will be scaled down (i.e. 0.5 implies they are shown at half linear size) + # this is only used for --graphics-markup=BOTH option + FLOATENV => undef , # Environments in which FL variants of defined commands are used + PICTUREENV => undef , # Environments in which all change markup is removed + MATHENV => undef , # Environments turning on display math mode (code also knows about \[ and \]) + MATHREPL => 'displaymath', # Environment introducing deleted maths blocks + MATHARRENV => undef , # Environments turning on eqnarray math mode + MATHARRREPL => 'eqnarray*', # Environment introducing deleted maths blocks + ARRENV => undef , # Environments 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 + COUNTERCMD => undef, + # COUNTERCMD 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 + LISTENV => undef , # list making environments - they will generally be kept + VERBATIMENV => undef, # Environments whose content should be treated as verbatim text + ITEMCMD => 'item' # command marking item in a list environment +); +# Configuration variables: these have to be visible from the subroutines +my ($ARRENV, + $COUNTERCMD, + $FLOATENV, + $ITEMCMD, + $LISTENV, + $MATHARRENV, + $MATHARRREPL, + $MATHENV, + $MATHREPL, + $MINWORDSBLOCK, + $PICTUREENV, + $SCALEDELGRAPHICS, + $VERBATIMENV +); + +# my $MINWORDSBLOCK=3; # minimum number of tokens to form an independent block +# # shorter identical blocks will be merged to the previous word +# my $SCALEDELGRAPHICS=0.5; # factor with which deleted figures will be scaled down (i.e. 0.5 implies they are shown at half linear size) +# # this is only used for --graphics-markup=BOTH option +# my $FLOATENV='(?:figure|table|plate)[\w\d*@]*' ; # Environments in which FL variants of defined commands are used +# my $PICTUREENV='(?:picture|tikzpicture|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|alignat|gather|multline|flalign)[*]?' ; # Environments turning on eqnarray math mode +# my $MATHARRREPL='eqnarray*'; # Environment introducing deleted maths blocks +# my $ARRENV='(?:aligned|array|[pbvBV]?matrix|smallmatrix|cases|split)'; # Environments 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|chapter|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 +# my $LISTENV='(?:itemize|description|enumerate)'; # list making environments - they will generally be kept +# my $ITEMCMD='item'; # command marking item in a list environment + + + my $LABELCMD='(?:label)'; # matching commands are disabled within deleted blocks - mostly useful for maths mode, as otherwise it would be fine to just not add those to SAFECMDLIST my @UNSAFEMATHCMD=('qedhere'); # Commands which are definitely unsafe for marking up in math mode (amsmath qedhere only tested to not work with UNDERLINE markup) (only affects WHOLE and COARSE math markup modes). Note that unlike text mode (or FINE math mode0 deleted unsafe commands are not deleted but simply taken outside \DIFdel my $MBOXINLINEMATH=0; # if set to 1 then surround marked-up inline maths expression with \mbox ( to get around compatibility # problems between some maths packages and ulem package -my $LISTENV='(?:itemize|description|enumerate)'; # list making environments - they will generally be kept -my $ITEMCMD='item'; # Markup strings # If at all possible, do not change these as parts of the program @@ -674,7 +738,8 @@ my @MBOXCMDEXCL=(); # all the patterns in MBOXCMDLIST will be appended my ($i,$j,$l); my ($old,$new); -my ($line); +my ($line,$key); +my (@dumlist); my ($newpreamble,$oldpreamble); my (@newpreamble,@oldpreamble,@diffpreamble,@diffbody); my ($latexdiffpreamble); @@ -709,7 +774,7 @@ use constant { my ($mboxcmd); -my (@configlist,@labels, +my (@configlist,@addtoconfiglist,@labels, @appendsafelist,@excludesafelist, @appendmboxsafelist,@excludemboxsafelist, @appendtextlist,@excludetextlist, @@ -751,6 +816,7 @@ GetOptions('type|t=s' => \$type, 'subtype|s=s' => \$subtype, 'floattype|f=s' => \$floattype, 'config|c=s' => \@configlist, + 'add-to-config|c=s' => \@addtoconfiglist, 'preamble|p=s' => \$preamblefile, 'encoding|e=s' => \$encoding, 'label|L=s' => \@labels, @@ -917,73 +983,81 @@ foreach $config ( @configlist ) { } 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 "ITEMCMD" ) { $ITEMCMD = $2 ; } - elsif ( $1 eq "LISTENV" ) { $LISTENV = $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 ; } - elsif ( $1 eq "SCALEDELGRAPHICS" ) { $SCALEDELGRAPHICS = $2 ; } - else { die "Unknown variable $1 in assignment.";} + exists $CONFIG{$1} or die "Unknown configuration variable $1."; + $CONFIG{$1}=$2; } -if ( $mathmarkup == COARSE || $mathmarkup == WHOLE ) { - push(@MATHTEXTCMDLIST,qr/^MATHBLOCK(?:$MATHENV|$MATHARRENV|SQUAREBRACKET)$/); +my @addtoconfig=(); +foreach $config ( @addtoconfiglist ) { + if (-f $config || lc $config eq '/dev/null' ) { + open(FILE,$config) or die ("Couldn't open addd-to-config file $config: $!"); + while () { + chomp; + next if /^\s*#/ || /^\s*%/ || /^\s*$/ ; + push (@addtoconfig,$_); + } + close(FILE); + } + else { +# foreach ( split(",",$config) ) { +# push @addtoconfig,$_; +# } + push @addtoconfig,split(",",$config) + } } - - -foreach $pkg ( @packagelist ) { - map { $packages{$_}="" } split(/,/,$pkg) ; +# initialise default lists from DATA +# for those configuration variables, which have not been set explicitly, initiate from list in document +foreach $key ( keys(%CONFIG) ) { + if (!defined $CONFIG{$key}) { + @dumlist=(); + init_regex_arr_data(\@dumlist,"$key CONFIG"); + $CONFIG{$key}=join(";",@dumlist) + } } - -if ($showpreamble) { - print "\nPreamble commands:\n"; - print $latexdiffpreamble ; +foreach $assign ( @addtoconfig ) { + $assign=~ m/\s*(\w*)\s*=\s*(\S*)\s*$/ or die "Illegal assignment $assign in configuration list (must be variable=value)"; + exists $CONFIG{$1} or die "Unknown configuration variable $1."; + $CONFIG{$1}.=";$2"; } -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); +# Map from hash to variables (we do this to have more concise code later, change from comma-separeted list) +foreach ( keys(%CONFIG) ) { + if ( $_ eq "MINWORDSBLOCK" ) { $MINWORDSBLOCK = $CONFIG{$_}; } + elsif ( $_ eq "FLOATENV" ) { $FLOATENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "ITEMCMD" ) { $ITEMCMD = $CONFIG{$_} ; } + elsif ( $_ eq "LISTENV" ) { $LISTENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "PICTUREENV" ) { $PICTUREENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "MATHENV" ) { $MATHENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "MATHREPL" ) { $MATHREPL = $CONFIG{$_} ; } + elsif ( $_ eq "MATHARRENV" ) { $MATHARRENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "MATHARRREPL" ) { $MATHARRREPL = $CONFIG{$_} ; } + elsif ( $_ eq "ARRENV" ) { $ARRENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "VERBATIMENV" ) { $VERBATIMENV = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "COUNTERCMD" ) { $COUNTERCMD = liststringtoregex($CONFIG{$_}) ; } + elsif ( $_ eq "SCALEDELGRAPHICS" ) { $SCALEDELGRAPHICS = $CONFIG{$_} ; } + else { die "Unknown configuration variable $_.";} } -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 and 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 ( $mathmarkup == COARSE || $mathmarkup == WHOLE ) { + push(@MATHTEXTCMDLIST,qr/^MATHBLOCK(?:$MATHENV|$MATHARRENV|SQUAREBRACKET)$/); } -if ($showconfig) { - print "Configuration variables:\n"; - print "ARRENV=$ARRENV\n"; - print "COUNTERCMD=$COUNTERCMD\n"; - print "FLOATENV=$FLOATENV\n"; - print "ITEMCMD=$ITEMCMD\n"; - print "LISTENV=$LISTENV\n"; - print "MATHARRENV=$MATHARRENV\n"; - print "MATHARRREPL=$MATHARRREPL\n"; - print "MATHENV=$MATHENV\n"; - print "MATHREPL=$MATHREPL\n"; - print "MINWORDSBLOCK=$MINWORDSBLOCK\n"; - print "PICTUREENV=$PICTUREENV\n"; + +foreach $pkg ( @packagelist ) { + map { $packages{$_}="" } split(/,/,$pkg) ; } + + + if ($showconfig || $showtext || $showsafe || $showpreamble) { - exit 0; } + show_configuration(); + exit 0; +} + if ( @ARGV != 2 ) { print STDERR "2 and only 2 non-option arguments required. Write latexdiff -h to get help\n"; exit(2); @@ -1033,7 +1107,7 @@ push(@SAFECMDLIST, qr/^QLEFTBRACE$/, qr/^QRIGHTBRACE$/); my $word_ja='\p{Han}+|\p{InHiragana}+|\p{InKatakana}+'; my $word='(?:' . $word_ja . '|(?:(?:[-\w\d*]|\\\\[\"\'\`~^][A-Za-z\*])(?!(?:' . $word_ja . ')))+)'; my $cmdleftright='\\\\(?:left|right|[Bb]igg?[lrm]?|middle)\s*(?:[<>()\[\]|\.]|\\\\(?:[|{}]|\w+))'; - my $cmdoptseq='\\\\[\w\d\*]+'.$extraspace.'(?:(?:<'.$abrat0.'>|\['.$brat0.'\]|\{'. $pat_n . '\}|\(' . $coords .'\))'.$extraspace.')*'; + my $cmdoptseq='\\\\[\w\d@\*]+'.$extraspace.'(?:(?:<'.$abrat0.'>|\['.$brat0.'\]|\{'. $pat_n . '\}|\(' . $coords .'\))'.$extraspace.')*'; my $backslashnl='\\\\\n'; my $oneletcmd='\\\\.\*?(?:\['.$brat0.'\]|\{'. $pat_n . '\})*'; my $math='\$(?:[^$]|\\\$)*?\$|\\\\[(](?:.|\n)*?\\\\[)]'; @@ -1056,18 +1130,30 @@ if ( ! -e $newfile ) { # set the labels to be included into the file +# first find out which file name is longer for correct alignment +my ($diff,$oldlabel_n_spaces,$newlabel_n_spaces); +$oldlabel_n_spaces = 0; +$newlabel_n_spaces = 0; +$diff = length($newfile) - length($oldfile); +if ($diff > 0) { + $oldlabel_n_spaces = $diff; +} +if ($diff < 0) { + $newlabel_n_spaces = abs($diff); +} + my ($oldtime,$newtime,$oldlabel,$newlabel); if (defined($labels[0])) { $oldlabel=$labels[0] ; } else { $oldtime=localtime((stat($oldfile))[9]); - $oldlabel="$oldfile " . " "x(length($newfile)-length($oldfile)) . $oldtime; + $oldlabel="$oldfile " . " "x($oldlabel_n_spaces) . $oldtime; } if (defined($labels[1])) { $newlabel=$labels[1] ; } else { $newtime=localtime((stat($newfile))[9]); - $newlabel="$newfile " . " "x(length($oldfile)-length($newfile)) . $newtime; + $newlabel="$newfile " . " "x($newlabel_n_spaces) . $newtime; } $encoding=guess_encoding($newfile) unless defined($encoding); @@ -1088,7 +1174,6 @@ $new=read_file_with_encoding($newfile,$encoding); exetime(1); ($oldpreamble,$oldbody,$oldpost)=splitdoc($old,'\\\\begin\{document\}','\\\\end\{document\}'); - ($newpreamble,$newbody,$newpost)=splitdoc($new,'\\\\begin\{document\}','\\\\end\{document\}'); @@ -1167,22 +1252,28 @@ if ($graphicsmarkup != NONE ) { # is not compatible with this. # (A more elegant solution would be to suppress the redefinitions of the \DIFaddbeginFL etc commands, but for this narrow use case # I currently don't see this as an efficient use of time) - foreach my $cmd ( "DIFaddbegin","DIFaddend","DIFdelbegin","DIFdelend" ) { - @matches=( $latexdiffpreamble =~ m/command{\\DIFaddbeginFL}{($pat_n)}/sg ) ; - # we look at the last one of the list to take into account possible redefinition but almost always matches should have exactly one elemen - if ( $matches[$#matches] =~ m/\\DIFaddbegin/ ) { - die "Cannot combine graphics markup with float styles defining \\DIFaddbeginFL in terms of \\DIFaddbegin. Use --graphics-markup=none option or choose a different float style."; - exit 10; - } + ### The foreach loop does not make sense here. I don't know why I put this in - (F Tilmann) + ###foreach my $cmd ( "DIFaddbegin","DIFaddend","DIFdelbegin","DIFdelend" ) { + @matches=( $latexdiffpreamble =~ m/command\{\\DIFaddbeginFL}\{($pat_n)}/sg ) ; + # we look at the last one of the list to take into account possible redefinition but almost always matches should have exactly one element + if ( $matches[$#matches] =~ m/\\DIFaddbegin/ ) { + die "Cannot combine graphics markup with float styles defining \\DIFaddbeginFL in terms of \\DIFaddbegin. Use --graphics-markup=none option or choose a different float style."; + exit 10; } + ###} $latexdiffpreamble .= join "\n",("\\newcommand{\\DIFscaledelfig}{$SCALEDELGRAPHICS}",extrapream("HIGHLIGHTGRAPHICS"),""); # only change required for highlighting both is to declare \includegraphics safe, as preamble already contains commands for deleted environment if ( $graphicsmarkup == BOTH ) { - init_regex_arr_ext(\@SAFECMDLIST,'includegraphics'); + init_regex_arr_list(\@SAFECMDLIST,'includegraphics'); } } +# If listings is being used and latexdiffpreamble uses color markup +if (defined($packages{"listings"} and $latexdiffpreamble =~ /\\RequirePackage(?:\[$brat0\])?\{color\}/)) { + $latexdiffpreamble .= join "\n",(extrapream("LISTINGS"),""); +} + # adding begin and end marker lines to preamble $latexdiffpreamble = "%DIF PREAMBLE EXTENSION ADDED BY LATEXDIFF\n" . $ latexdiffpreamble . "%DIF END PREAMBLE EXTENSION ADDED BY LATEXDIFF\n"; @@ -1244,11 +1335,11 @@ if (defined $packages{"siunitx"} ) { # this is done to get around an incompatibility between the ulem and siunitx package print STDERR "siunitx package detected.\n" if $verbose ; my $mboxcmds='SI,ang,numlist,numrange,SIlist,SIrange'; - init_regex_arr_ext(\@SAFECMDLIST,'num,si'); + init_regex_arr_list(\@SAFECMDLIST,'num,si'); if ( $enablecitmark || ( $ulem && ! $disablecitmark )) { - init_regex_arr_ext(\@MBOXCMDLIST,$mboxcmds); + init_regex_arr_list(\@MBOXCMDLIST,$mboxcmds); } else { - init_regex_arr_ext(\@SAFECMDLIST,$mboxcmds); + init_regex_arr_list(\@SAFECMDLIST,$mboxcmds); } } @@ -1258,9 +1349,9 @@ if (defined $packages{"cleveref"} ) { print STDERR "cleveref package detected.\n" if $verbose ; my $mboxcmds='[Cc]ref(?:range)?\*?,labelcref,(?:lc)?name[cC]refs?' ; if ( $enablecitmark || ( $ulem && ! $disablecitmark )) { - init_regex_arr_ext(\@MBOXCMDLIST,$mboxcmds); + init_regex_arr_list(\@MBOXCMDLIST,$mboxcmds); } else { - init_regex_arr_ext(\@SAFECMDLIST,$mboxcmds); + init_regex_arr_list(\@SAFECMDLIST,$mboxcmds); } } @@ -1269,17 +1360,17 @@ if (defined $packages{"glossaries"} ) { # this is done to get around an incompatibility between ulem and glossaries package print STDERR "glossaries package detected.\n" if $verbose ; my $mboxcmds='[gG][lL][sS](?:|pl|disp|link|first|firstplural|desc|user[iv][iv]?[iv]?),[aA][cC][rR](?:long|longpl|full|fullpl),[aA][cC][lfp]?[lfp]?'; - init_regex_arr_ext(\@SAFECMDLIST,'[gG][lL][sS](?:(?:entry)?(?:text|plural|name|symbol)|displaynumberlist|entryfirst|entryfirstplural|entrydesc|entrydescplural|entrysymbolplural|entryuser[iv][iv]?[iv]?|entrynumberlist|entrydisplaynumberlist|entrylong|entrylongpl|entryshort|entryshortpl|entryfull|entryfullpl),[gG]lossentry(?:name|desc|symbol),[aA][cC][rR](?:short|shortpl),[aA]csp?'); + init_regex_arr_list(\@SAFECMDLIST,'[gG][lL][sS](?:(?:entry)?(?:text|plural|name|symbol)|displaynumberlist|entryfirst|entryfirstplural|entrydesc|entrydescplural|entrysymbolplural|entryuser[iv][iv]?[iv]?|entrynumberlist|entrydisplaynumberlist|entrylong|entrylongpl|entryshort|entryshortpl|entryfull|entryfullpl),[gG]lossentry(?:name|desc|symbol),[aA][cC][rR](?:short|shortpl),[aA]csp?'); if ( $enablecitmark || ( $ulem && ! $disablecitmark )) { - init_regex_arr_ext(\@MBOXCMDLIST,$mboxcmds); + init_regex_arr_list(\@MBOXCMDLIST,$mboxcmds); } else { - init_regex_arr_ext(\@SAFECMDLIST,$mboxcmds); + init_regex_arr_list(\@SAFECMDLIST,$mboxcmds); } } if (defined $packages{"chemformula"} or defined $packages{"chemmacros"} ) { print STDERR "chemformula package detected.\n" if $verbose ; - init_regex_arr_ext(\@SAFECMDLIST,'ch'); + init_regex_arr_list(\@SAFECMDLIST,'ch'); push(@UNSAFEMATHCMD,'ch'); # The next command would be needed to allow highlighting the interior of \ch commands in math environments # but the redefinitions in chemformula are too deep to make this viable @@ -1288,7 +1379,7 @@ if (defined $packages{"chemformula"} or defined $packages{"chemmacros"} ) { if (defined $packages{"mhchem"} ) { print STDERR "mhchem package detected.\n" if $verbose ; - init_regex_arr_ext(\@SAFECMDLIST,'ce'); + init_regex_arr_list(\@SAFECMDLIST,'ce'); push(@UNSAFEMATHCMD,'ce','cee'); # The next command would be needed to allow highlighting the interior of \cee commands in math environments # but the redefinitions in chemformula are too deep to make this viable @@ -1330,7 +1421,7 @@ if (defined $packages{"amsmath"} or defined $packages{"amsart"} or defined $pac # add commands in MBOXCMDLIST to SAFECMDLIST foreach $mboxcmd ( @MBOXCMDLIST ) { - init_regex_arr_ext(\@SAFECMDLIST, $mboxcmd); + init_regex_arr_list(\@SAFECMDLIST, $mboxcmd); } # check if \label is in SAFECMDLIST, and if yes replace "label" in $LABELCMD by something that never matches (we hope!) @@ -1376,6 +1467,70 @@ print $diffall; print STDERR "(",exetime()," s)\n","Done.\n" if $verbose; +# liststringtoregex(liststring) +# expands string with semi-colon separated list into a regular expression corresponding +# matching any of the elements +sub liststringtoregex { + my ($liststring)=@_; + my @elements=grep /\S/,split(";",$liststring); + if ( @elements) { + return('(?:(?:' . join(')|(?:',@elements) .'))'); + } else { + return ""; + } +} + +# show_configuration +# note that this is not encapsulated but uses variables from the main program +# It is provided for convenience because in the future it is planned to allow output +# to be modified based on what packages are read etc - this works only if the input files are actually red +# whether or not additional files are provided +sub show_configuration { + if ($showpreamble) { + print "\nPreamble commands:\n"; + print $latexdiffpreamble ; + } + + if ($showsafe) { + print "\nsafecmd: Commands safe within scope of $ADDOPEN $ADDCLOSE and $DELOPEN $DELCLOSE (unless excluded):\n"; + print_regex_arr(@SAFECMDLIST); + print "\nsafecmd-exlude: Commands not safe within scope of $ADDOPEN $ADDCLOSE and $DELOPEN $DELCLOSE :\n"; + print_regex_arr(@SAFECMDEXCL); + print "\nmboxsafecmd: Commands safe only if they are surrounded by \\mbox command:\n"; + print_regex_arr(@MBOXCMDLIST); + print "\nnmboxsafecmd: Commands not safe:\n"; + print_regex_arr(@MBOXCMDEXCL); + } + + 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 and 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 "ARRENV=$ARRENV\n"; + print "COUNTERCMD=$COUNTERCMD\n"; + print "FLOATENV=$FLOATENV\n"; + print "ITEMCMD=$ITEMCMD\n"; + print "LISTENV=$LISTENV\n"; + print "MATHARRENV=$MATHARRENV\n"; + print "MATHARRREPL=$MATHARRREPL\n"; + print "MATHENV=$MATHENV\n"; + print "MATHREPL=$MATHREPL\n"; + print "MINWORDSBLOCK=$MINWORDSBLOCK\n"; + print "PICTUREENV=$PICTUREENV\n"; + } +} + + ## guess_encoding(filename) ## reads the first 20 lines of filename and looks for call of inputenc package @@ -1480,52 +1635,63 @@ sub list_packages { # and adds the created commands which are clearly safe to @SAFECMDLIST sub add_safe_commands { my ($preamble)=@_; - my $added_command = 1; + # get rid of comments $preamble=~s/(?catfile($dirname,$fname), $encoding) or die "Couldn't find file ",File::Spec->catfile($dirname,$fname),": $!"; $replacement=flatten(read_file_with_encoding(File::Spec->catfile($dirname,$fname), $encoding), $preamble,$filename,$encoding) or die "Couldn't find file ",File::Spec->catfile($dirname,$fname),": $!"; + $replacement = remove_endinput($replacement); # \include always starts a new page; use explicit \newpage command to simulate this $newpage=(defined($3)? " \\newpage " : "") ; "$begline$newpage$replacement$newpage"; @@ -1593,6 +1760,7 @@ sub flatten { $subfile=read_file_with_encoding(File::Spec->catfile($dirname,$fname), $encoding) or die "Couldn't find file ",File::Spec->catfile($dirname,$fname),": $!"; ($subpreamble,$subbody,$subpost)=splitdoc($subfile,'\\\\begin\{document\}','\\\\end\{document\}'); $replacement=flatten($subbody, $preamble,$filename,$encoding); + $replacement = remove_endinput($replacement); "$begline$replacement"; }/exgm; @@ -2236,7 +2404,7 @@ sub marktags { if ( $word =~ /^\\(?!\()(\\|[`'^"~=.]|[\w*@]+)(.*?)(\s*)$/s && iscmd($1,\@MBOXCMDLIST,\@MBOXCMDEXCL)) { # $word is a safe command in MBOXCMDLIST ###print STDERR "DEBUG Mboxsafecmd detected:$word:\n" if $debug ; - push(@$retval,"\\mbox{$AUXCMD\n\\" . $1 . $2 . $3 ."}$AUXCMD\n" ); + push(@$retval,"\\mbox{$AUXCMD\n\\" . $1 . $2 . $3 ."}\\hspace{0pt}$AUXCMD\n" ); } else { # $word is a normal word or a safe command (not in MBOXCMDLIST) push (@$retval,$word); @@ -2253,47 +2421,49 @@ sub marktags { #used in preprocess sub take_comments_and_enter_from_frac() { - ###*************take the \n and % between frac and {}*********** - ###notice all of the substitution are made none global - while( m/\\begin{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\frac(([\s]*%[^\n]*?)*[\r\n|\r|\n])+\{(.*?)\\end{\1}/s ) { - ### if there isn't any % or \n in the pattern $2 then there should be an \\end{...} in $2 - if( $2 !~ m/\\end{$1}/s ) { - ### take out % and \n from the next match only (none global) - s/\\begin{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\frac(([\s]*%[^\n]*?)*[\r\n|\r|\n])+\{(.*?)\\end{\1}/\\begin{$1}$2\\frac{$5\\end{$1}/s; + #*************take the \n and % between frac and {}*********** + #notice all of the substitution are made none global + while( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\frac(([\s]*%[^\n]*?)*[\r\n|\r|\n])+\{(.*?)\\end\{\1}/s ) { + # if there isn't any % or \n in the pattern $2 then there should be an \\end{...} in $2 + ### print STDERR "Match the following in take_comments and_enter_from_frac(1):\n****$&****\n" if $debug; + if( $2 !~ m/\\end\{$1}/s ) { + # take out % and \n from the next match only (none global) + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\frac(([\s]*%[^\n]*?)*[\r\n|\r|\n])+\{(.*?)\\end\{\1}/\\begin{$1}$2\\frac{$5\\end{$1}/s; } else{ - ###there are no more % and \n in $2, we want to find the next one so we clear the begin-end from the pattern - s/\\begin{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\end{\1}/MATHBLOCK$1$2MATHBLOCKEND/s; + #there are no more % and \n in $2, we want to find the next one so we clear the begin-end from the pattern + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\end\{\1}/MATHBLOCK$1\{$2\}MATHBLOCKEND/s; } } ###cleaning up - while( s/MATHBLOCK($MATHENV|$MATHARRENV|SQUAREBRACKET)(.*?)MATHBLOCKEND/\\begin{$1}$2\\end{$1}/s ){} + while( s/MATHBLOCK($MATHENV|$MATHARRENV|SQUAREBRACKET)\{(.*?)\}MATHBLOCKEND/\\begin{$1}$2\\end{$1}/s ){} ###*************take the \n and % between frac and {}*********** ###**********take the \n and % between {} and {} of the frac*************** while( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)\\frac\{(.*?)\\end\{\1\}/s ) { - ### if there isn't any more //frac before the first //end in the pattern $2 then there should be an \\end{...} in $2 + # if there isn't any more //frac before the first //end in the pattern $2 then there should be an \\end{...} in $2 + ###print STDERR "Match the following in take_comments and_enter_from_frac(2):\n****$&****\n" if $debug; if( $2 !~ m/\\end\{$1\}/s ) { - ### from now on CURRFRAC is the frac we are looking at + # from now on CURRFRAC is the frac we are looking at s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)\\frac\{(.*?)\\end\{\1\}/\\begin\{$1\}$2CURRFRAC\{$3\\end\{$1\}/s; while( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)CURRFRAC\{(.*?)\\end\{\1\}/s ) { - if( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)CURRFRAC\{($pat_n)\}([\s]*(%[^\n]*?)*[\r\n|\r|\n])+[\s]*\{(.*?)\\end{\1}/s ) { + if( m/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)CURRFRAC\{($pat_n)\}([\s]*(%[^\n]*?)*[\r\n|\r|\n])+[\s]*\{(.*?)\\end\{\1}/s ) { s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)\}(.*?)CURRFRAC\{($pat_n)\}([\s]*(%[^\n]*?)*[\r\n|\r|\n])+[\s]*\{(.*?)\\end\{\1\}/\\begin\{$1\}$2CURRFRAC\{$3\}\{$6\\end\{$1\}/s; } else { # there is no comment or \n between the two brackets {}{} - ### change CURRFRAC to FRACSTART so we can change them all back to //frac{ when we finish - s/\\begin{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)CURRFRAC\{(.*?)\\end{\1}/\\begin{$1}$2FRACSTART\{$3\\end{$1}/s; + # change CURRFRAC to FRACSTART so we can change them all back to //frac{ when we finish + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)CURRFRAC\{(.*?)\\end\{\1}/\\begin{$1}$2FRACSTART\{$3\\end{$1}/s; } } } else{ ###there are no more frac in $2, we want to find the next one so we clear the begin-end from the pattern - s/\\begin{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\end{\1}/MATHBLOCK$1$2MATHBLOCKEND/s; + s/\\begin\{($MATHENV|$MATHARRENV|SQUAREBRACKET)}(.*?)\\end\{\1}/MATHBLOCK$1\{$2\}MATHBLOCKEND/s; } } ###cleaning up - while( s/MATHBLOCK($MATHENV|$MATHARRENV|SQUAREBRACKET)(.*?)MATHBLOCKEND/\\begin{$1}$2\\end{$1}/s ){} + while( s/MATHBLOCK($MATHENV|$MATHARRENV|SQUAREBRACKET)\{(.*?)\}MATHBLOCKEND/\\begin{$1}$2\\end{$1}/s ){} s/FRACSTART/\\frac/g; ###***************take the \n and % between {} and {} of the frac********************* } @@ -2306,8 +2476,8 @@ sub take_comments_and_enter_from_frac() { # 2. mark all first empty line (in block of several) with \PAR tokens # 3. Convert all '\%' into '\PERCENTAGE ' and all '\$' into \DOLLAR 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} +# into \verb{hash} (also lstinline) +# 5. Convert \begin{verbatim} some verbatim text \end{verbatim} into \verbatim{hash} (not only verbatim, all patterns matching VERBATIMENV) # 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} @@ -2323,6 +2493,17 @@ sub take_comments_and_enter_from_frac() { # Returns: leading white space removed in step 1 sub preprocess { for (@_) { + + + # change in \verb and similar commands - note that I introduce an extra space here so that the + # already hashed variants do not trigger again + # transform \lstinline{...} +# s/\\lstinline(\[$brat0\])?(\{(?:.*?)\})/"\\DIFlstinline". $1 ."{". tohash(\%verbhash,"$2") ."}"/esg; +# s/\\lstinline(\[$brat0\])?((\S).*?\2)/"\\DIFlstinline". $1 ."{". tohash(\%verbhash,"$2") ."}"/esg; + s/\\lstinline((?:\[$brat0\])?)(\{(?:.*?)\})/"\\DIFlstinline". $1 ."{". tohash(\%verbhash,"$2") ."}"/esg; + s/\\lstinline((?:\[$brat0\])?)((\S).*?\3)/"\\DIFlstinline". $1 ."{". tohash(\%verbhash,"$2") ."}"/esg; + s/\\(verb\*?|lstinline)(\S)(.*?)\2/"\\DIF${1}{". tohash(\%verbhash,"${2}${3}${2}") ."}"/esg; + # Change \{ to \QLEFTBRACE, \} to \QRIGHTBRACE, and \& to \AMPERSAND s/(?>>\n" if $debug; + + # make the array modification in added blocks while ( m/\\DIFaddbegin.*?\\DIFaddend/sg ) { $cnt=0; @@ -2594,7 +2790,7 @@ sub postprocess { $len2=length($&); $begin2=pos($addblock) - $len2; $mathblock="%\n\\mbox{$AUXCMD\n$1\n}$AUXCMD\n"; - next unless $mathblock =~ m/\{$ARRENV\}/ ; + next unless ( $mathblock =~ /ARRAYBLOCK/ or $mathblock =~ m/\{$ARRENV\}/) ; substr($addblock,$begin2,$len2)=$mathblock; pos($addblock) = $begin2 + length($mathblock); } @@ -2603,6 +2799,11 @@ sub postprocess { ##$addblock=~s/($math)/\\mbox{$AUXCMD\n$1\n}$AUXCMD\n/sg; $addblock=~s/($math)(?:[\s\n]*)?/\\mbox{$AUXCMD\n$1\n}$AUXCMD\n/sg; } + ###if ( defined($packages{"listings"} and $latexdiffpreamble =~ /\\RequirePackage(?:\[$brat0\])?\{color\}/)) { + # mark added verbatim commands + $addblock =~ s/\\DIFverb/\\DIFDIFaddverb/g; + $addblock =~ s/\\DIFlstinline/\\DIFDIFaddlstinline/g; + ###} # splice in modified addblock substr($_,$begin,$len)=$addblock; pos = $begin + length($addblock); @@ -2646,15 +2847,15 @@ sub postprocess { # 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) if ( $mathmarkup == FINE ) { - 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; + 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\}((?:(.(?!(?)?)((?:\[$brat0\])?)\s*?(?:\n|$DELCMDCLOSE))/ + $string =~ s/(\%DIFDELCMD < \s*(\\$ITEMCMD)((?:<$abrat0>)?)((?:\[$brat0\])?)\s*((?:${cmdoptseq}\s*?)*)(?:\n|$DELCMDCLOSE))/ # if \item has an []argument, then mark up the argument as deleted) if (length($4)>0) { # use substr to exclude square brackets at end points @@ -2773,7 +2990,8 @@ sub restore_item_commands { } else { $itemarg=""; } - "$1$2$3$itemarg$AUXCMD\n"/sge; + "$1$2$3$itemarg$AUXCMD\n"; ###.((length($5)>0) ? "%DIFDELCMD $5 $DELCMDCLOSE\n" : "") + /sge; return($string); } @@ -2956,16 +3174,32 @@ sub linediff { # 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) +# This is used for command lists and configuration variables, but the processing is slightly +# different: +# For lists, the regular expression is extended to include beginning (^) and end ($) markers, to require full-string matching +# For configuration variables (and all others), simply an unadorned list is copied sub init_regex_arr_data { my ($arr,$token)=@_; - my ($copy); + my $copy=0; + my ($mode); + if ($token =~ m/COMMANDS/ ) { + $mode=0; + } else { + $mode=1; + } + while () { if ( m/^%%BEGIN $token\s*$/ ) { - $copy=1; } - elsif ( m/^%%END $token\s*/ ) { + $copy=1; + next; + } elsif ( m/^%%END $token\s*$/ ) { last; } chomp; - push (@$arr,qr/^$_$/) if ( $copy && !/^%/ ) ; + if ( $mode==0 ) { + push (@$arr,qr/^$_$/) if ( $copy && !/^%/ ) ; + } elsif ($mode==1) { + push (@$arr,"$_") if ( $copy && !/^%/ ) ; + } } seek DATA,0,0; # rewind DATA handle to file begin } @@ -2978,25 +3212,42 @@ sub init_regex_arr_data { # 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 () { - chomp; - next if /^\s*#/ || /^\s*%/ || /^\s*$/ ; - push (@$arr,qr/^$_$/); - } - close(FILE); + if ( -f $arg ) { + init_regex_arr_file($arr,$arg); + } else { + init_regex_arr_list($arr,$arg); } - else { - # assume it is a comma-separated list of reg-ex - foreach $regex (split(qr/(?) { + chomp; + next if /^\s*#/ || /^\s*%/ || /^\s*$/ ; + push (@$arr,qr/^$_$/); + } + close(FILE); +} + +# init_regex_arr_list(\@array,$arg) +# appends array with regular expressions. +# read from comma separated list of regular expressions ($arg) +sub init_regex_arr_list { + my ($arr,$arg)=@_; + my $regex; + ### print STDERR "DEBUG init_regex_arr_list arg >$arg<\n" if $debug; + foreach $regex (split(qr/(? 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 +context to work, e.g. C<\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. @@ -3568,6 +3828,7 @@ argument is shown as deleted text. =item B<--replace-context2cmd=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 @@ -3580,7 +3841,7 @@ their arguments. =item B<--append-mboxsafecmd=append-file> or B<--append-mboxsafecmd="cmd1,cmd2,..."> Define safe commands, which additionally need to be protected by encapsulating -in an \\mbox{..}. This is sometimes needed to get around incompatibilities +in an C<\mbox{..}>. This is sometimes needed to get around incompatibilities between external packages and the ulem package, which is used for highlighting in the default style UNDERLINE as well as CULINECHBAR CFONTSTRIKE @@ -3620,6 +3881,16 @@ C (RegEx) C (Float) +=item B<--add-to-config varenv1=pattern1,varenv2=pattern2,...> + +For configuration variables, which are a regular expression (essentially those ending +in ENV, and COUNTERCMD, see list above) this provides an alternative way to modify the configuration +variables. Instead of setting the complete pattern, with this option it is possible to add an +alternative pattern. C must be one of the variables listed above that take a regular +expression as argument, and pattern is any regular expression (which might need to be +protected from the shell by quotation). Several patterns can be added at once by using semi-colons +to separate them, e.g. C<--add-to-config "LISTENV=myitemize;myenumerate,COUNTERCMD=endnote"> + =item B<--show-safecmd> Print list of RegEx matching and excluding safe commands. @@ -3680,15 +3951,16 @@ expected, e.g. correction of typos. =item B<--graphics-markup=level> Change highlight style for graphics embedded with C<\includegraphics> commands. - Possible values for level: - C or C<0>: no highlighting for figures +Possible values for level: - C or C<1>: surround newly added or changed figures with a blue frame [Default if graphicx package loaded] +C, C or C<0>: no highlighting for figures - C or C<2>: highlight new figures with a blue frame and show deleted figures at reduced - scale, and crossed out with a red diagonal cross. Use configuration - variable SCALEDELGRAPHICS to set size of deleted figures. +C or C<1>: surround newly added or changed figures with a blue frame [Default if graphicx package loaded] + +C or C<2>: highlight new figures with a blue frame and show deleted figures at reduced +scale, and crossed out with a red diagonal cross. Use configuration +variable SCALEDELGRAPHICS to set size of deleted figures. Note that changes to the optional parameters will make the figure appear as changed to latexdiff, and this figure will thus be highlighted @@ -3745,7 +4017,7 @@ labels like this C<-L labelold -L labelnew>. Suppress inclusion of old and new file names as comment in output file -=item B<--visble-label> +=item B<--visible-label> Include old and new filenames (or labels set with C<--label> option) as visible output. @@ -4054,8 +4326,8 @@ I requires the I command to be present. =head1 AUTHOR -Version 1.2.0 -Copyright (C) 2004-2015 Frederik Tilmann +Version 1.2.1 +Copyright (C) 2004-2017 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 3 @@ -4337,8 +4609,67 @@ date institute %%END CONTEXT2 COMMANDS +%% CONFIGURATION variabe defaults +%%BEGIN LISTENV CONFIG +itemize +description +enumerate +%%END LISTENV CONFIG + +%%BEGIN FLOATENV CONFIG +figure[\w\d*@]* +table[\w\d*@]* +plate[\w\d*@]* +%%END FLOATENV CONFIG + +%%BEGIN PICTUREENV CONFIG +picture[\w\d*@]* +tikzpicture[\w\d*@]* +DIFnomarkup +%%END PICTUREENV CONFIG + +%%BEGIN MATHENV CONFIG +equation[*]? +displaymath +DOLLARDOLLAR +%%END MATHENV CONFIG + +%%BEGIN MATHARRENV CONFIG +eqnarray[*]? +align[*]? +alignat[*]? +gather[*]? +multline[*]? +flalign[*]? +%%END MATHARRENV CONFIG + +%%BEGIN ARRENV CONFIG +aligned +array +[pbvBV]?matrix +smallmatrix +cases +split +%%END ARRENV CONFIG + +%%BEGIN COUNTERCMD CONFIG +footnote +part +chapter +section +subsection +subsubsection +paragraph +subparagraph +%%END COUNTERCMD CONFIG + +%%BEGIN VERBATIMENV CONFIG +verbatim[*]? +lstlisting +comment +%%END VERBATIMENV CONFIG -%% TYPES (Commands for highlighting changed blocks) +%%% TYPES (Commands for highlighting changed blocks) %DIF UNDERLINE PREAMBLE \RequirePackage[normalem]{ulem} @@ -4350,24 +4681,28 @@ institute %DIF CTRADITIONAL PREAMBLE \RequirePackage{color}\definecolor{RED}{rgb}{1,0,0}\definecolor{BLUE}{rgb}{0,0,1} \RequirePackage[stable]{footmisc} +\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf} \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} +\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf} \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} +\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf} \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} +\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf} \providecommand{\DIFadd}[1]{{\sf #1}} \providecommand{\DIFdel}[1]{{\footnotesize \sout{#1}}} %DIF END FONTSTRIKE PREAMBLE @@ -4406,6 +4741,7 @@ institute %DIF END INVISIBLE PREAMBLE %DIF BOLD PREAMBLE +\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf} \providecommand{\DIFadd}[1]{{\bf #1}} \providecommand{\DIFdel}[1]{} %DIF END BOLD PREAMBLE @@ -4430,7 +4766,7 @@ institute \providecommand{\DIFaddend}{\protect\marginpar{]}} \providecommand{\DIFdelbegin}{\protect\marginpar{d[}} \providecommand{\DIFdelend}{\protect\marginpar{]}} -%DIF END BRACKET PREAMBLE +%DIF END MARGIN PREAMBLE %DIF DVIPSCOL PREAMBLE %Note: only works with dvips converter @@ -4623,4 +4959,18 @@ institute %DIF HYPERREF PREAMBLE \providecommand{\DIFadd}[1]{\texorpdfstring{\DIFaddtex{#1}}{#1}} \providecommand{\DIFdel}[1]{\texorpdfstring{\DIFdeltex{#1}}{}} -%DIF END HYPERREF PACKAGE +%DIF END HYPERREF PREAMBLE + +%DIF LISTINGS PREAMBLE +\lstdefinelanguage{codediff}{ + moredelim=**[is][\color{red}]{*!----}{----!*}, + moredelim=**[is][\color{blue}]{*!++++}{++++!*} +} +\lstdefinestyle{codediff}{ + belowcaptionskip=.25\baselineskip, + language=codediff, + basicstyle=\ttfamily, + columns=fullflexible, + keepspaces=true, +} +%DIF END LISTINGS PREAMBLE -- cgit v1.2.3