| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package TeX::AutoTeX::File; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # $Id: File.pm,v 1.36.2.5 2011/01/22 04:53:23 thorstens Exp $ | 
| 5 |  |  |  |  |  |  | # $Revision: 1.36.2.5 $ | 
| 6 |  |  |  |  |  |  | # $Source: /cvsroot/arxivlib/arXivLib/lib/TeX/AutoTeX/File.pm,v $ | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | # $Date: 2011/01/22 04:53:23 $ | 
| 9 |  |  |  |  |  |  | # $Author: thorstens $ | 
| 10 |  |  |  |  |  |  | # | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 2 |  |  | 2 |  | 11 | use strict; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 700 |  | 
| 13 |  |  |  |  |  |  | ### use warnings; | 
| 14 | 2 |  |  | 2 |  | 13 | use Carp; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 203 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | our ($VERSION) = '$Revision: 1.36.2.5 $' =~ m{ \$Revision: \s+ (\S+) }x; | 
| 17 |  |  |  |  |  |  |  | 
| 18 | 2 |  |  | 2 |  | 12 | use Scalar::Util qw(weaken); | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 156 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 2 |  |  | 2 |  | 1193 | use TeX::AutoTeX::Exception; | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 17 |  | 
| 21 | 2 |  |  | 2 |  | 1586 | use TeX::AutoTeX::HyperTeX; | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 80 |  | 
| 22 | 2 |  |  | 2 |  | 1287 | use TeX::AutoTeX::PostScript; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 71 |  | 
| 23 | 2 |  |  |  |  | 355 | use TeX::AutoTeX::Config qw( | 
| 24 |  |  |  |  |  |  | $CRYPT | 
| 25 |  |  |  |  |  |  | $DVIPS | 
| 26 |  |  |  |  |  |  | $TEXCHR | 
| 27 |  |  |  |  |  |  | $DIRECTIVE_FILE | 
| 28 |  |  |  |  |  |  | %TEX_BINARIES | 
| 29 |  |  |  |  |  |  | $TEX_PATH | 
| 30 | 2 |  |  | 2 |  | 20 | ); | 
|  | 2 |  |  |  |  | 5 |  | 
| 31 |  |  |  |  |  |  |  | 
| 32 | 2 |  |  | 2 |  | 1643 | use arXiv::FileGuess qw(guess_file_type is_tex_type type_name); | 
|  | 2 |  |  |  |  | 5514 |  | 
|  | 2 |  |  |  |  | 13261 |  | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | sub new { | 
| 35 | 0 |  |  | 0 | 1 |  | my ($class, $fileset, $filename) = @_; | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 0 |  |  |  |  |  | my $self = { | 
| 38 |  |  |  |  |  |  | flags     => {}, | 
| 39 |  |  |  |  |  |  | dvi_flags => q{}, | 
| 40 |  |  |  |  |  |  | fileset   => $fileset, | 
| 41 |  |  |  |  |  |  | binaries  => {%TEX_BINARIES}, | 
| 42 |  |  |  |  |  |  | untaint_regexp => qr/[^&\s;]+/, | 
| 43 |  |  |  |  |  |  | }; | 
| 44 | 0 | 0 | 0 |  |  |  | if ($filename =~ /^($self->{untaint_regexp})$/ && $filename !~ /^\./) { | 
| 45 | 0 |  |  |  |  |  | $self->{filename} = $1; # untaint | 
| 46 |  |  |  |  |  |  | } else { | 
| 47 | 0 |  |  |  |  |  | throw TeX::AutoTeX::InvNameException("Invalid filename: '$filename'."); | 
| 48 |  |  |  |  |  |  | } | 
| 49 | 0 |  |  |  |  |  | weaken $self->{fileset}; | 
| 50 | 0 |  |  |  |  |  | bless $self, $class; | 
| 51 |  |  |  |  |  |  | } | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | sub filename { | 
| 54 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 55 | 0 |  |  |  |  |  | return $self->{filename}; | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | sub type { | 
| 59 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 60 |  |  |  |  |  |  | # we cache the value | 
| 61 | 0 | 0 |  |  |  |  | $self->{type} = $self->determine_type() if !defined $self->{type}; | 
| 62 | 0 |  |  |  |  |  | return $self->{type}; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | sub determine_type { | 
| 66 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 67 |  |  |  |  |  |  |  | 
| 68 | 0 | 0 |  |  |  |  | return 'TYPE_README' if $self->{filename} eq $DIRECTIVE_FILE; | 
| 69 | 0 |  |  |  |  |  | my $fullname = "$self->{fileset}->{dir}/$self->{filename}"; | 
| 70 | 0 | 0 |  |  |  |  | return 'TYPE_DIRECTORY' if -d $fullname; | 
| 71 | 0 |  |  |  |  |  | my ($type, $texformat, $error) = guess_file_type($fullname); | 
| 72 | 0 | 0 |  |  |  |  | $self->{fileset}->{log}->error($error) if $error; | 
| 73 | 0 | 0 |  |  |  |  | $self->{tex_format} = lc $texformat if defined $texformat; | 
| 74 | 0 |  |  |  |  |  | return $type; | 
| 75 |  |  |  |  |  |  | } | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | sub filetype_name { | 
| 78 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 79 | 0 |  |  |  |  |  | return arXiv::FileGuess::type_name($self->type()); | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | sub type_is_tex { | 
| 83 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 84 | 0 |  |  |  |  |  | return arXiv::FileGuess::is_tex_type($self->type()); | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | sub assign_tex_priority { | 
| 88 |  |  |  |  |  |  | # TS: 12/2010 | 
| 89 |  |  |  |  |  |  | # trying to solve the dependency tree in general is equivalent to the halting problem | 
| 90 |  |  |  |  |  |  | # these are heuristics that work in practice on the material arXiv deals with, | 
| 91 |  |  |  |  |  |  | # but they are also easily fooled | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 94 | 0 | 0 |  |  |  |  | if ($self->{flags}->{toplevelfile}){ | 
| 95 | 0 |  |  |  |  |  | return 4; | 
| 96 |  |  |  |  |  |  | } | 
| 97 |  |  |  |  |  |  | # FileGuess incorrectly identifies some files (especially style files) | 
| 98 |  |  |  |  |  |  | # as tex files that aren't. | 
| 99 | 0 | 0 | 0 |  |  |  | if (!$self->type_is_tex() | 
|  |  |  | 0 |  |  |  |  | 
| 100 |  |  |  |  |  |  | || $self->{filename} eq 'auto_gen_ps.log' | 
| 101 |  |  |  |  |  |  | || $self->{filename} =~ /\.(?:sty|st|cls)$/i){ | 
| 102 | 0 |  |  |  |  |  | return 0; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  | # take advantage of logic in arXiv::FileGuess | 
| 105 | 0 | 0 |  |  |  |  | if (0 <= index $self->{type}, 'priority2') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 106 | 0 |  |  |  |  |  | return 1; | 
| 107 |  |  |  |  |  |  | } elsif (0 <= index $self->{type}, 'priority') { | 
| 108 | 0 |  |  |  |  |  | return 2; | 
| 109 |  |  |  |  |  |  | } elsif ('_MAC' eq substr $self->{type}, -4) { | 
| 110 | 0 |  |  |  |  |  | return 3; | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  | # rate based on filename and contents | 
| 113 | 0 | 0 |  |  |  |  | open(my $CURRENTFILE, '<', "$self->{fileset}->{dir}/$self->{filename}") | 
| 114 |  |  |  |  |  |  | || $self->{fileset}->{log}->error("Could not open '$self->{filename}': $!"); | 
| 115 | 0 |  |  |  |  |  | my $docstycls = 0; | 
| 116 |  |  |  |  |  |  | # grant higher priority to files that end with tex-related extension | 
| 117 | 0 | 0 |  |  |  |  | $docstycls++ if $self->{filename} =~ /\.(?:la)?tex$/i; | 
| 118 | 0 |  |  |  |  |  | while (<$CURRENTFILE>) { | 
| 119 | 0 | 0 |  |  |  |  | if (/^\s*\\document(?:style|class)/){ | 
| 120 | 0 |  |  |  |  |  | $docstycls++; | 
| 121 | 0 |  |  |  |  |  | last; | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  | } | 
| 124 | 0 |  |  |  |  |  | while (<$CURRENTFILE>) { | 
| 125 | 0 | 0 |  |  |  |  | if (/^\s*\\begin\s*\{\s*document\s*\}/){ | 
| 126 | 0 |  |  |  |  |  | $docstycls++; | 
| 127 | 0 |  |  |  |  |  | last; | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  | } | 
| 130 | 0 | 0 |  |  |  |  | close $CURRENTFILE or $self->{fileset}->{log}->verbose("couldn't close file: $!"); | 
| 131 | 0 |  |  |  |  |  | return $docstycls; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | sub type_override { | 
| 135 | 0 |  |  | 0 | 1 |  | my ($self, $type) = @_; | 
| 136 | 0 | 0 |  |  |  |  | if (my $override = $self->{fileset}->override($type)) { | 
| 137 | 0 |  |  |  |  |  | $self->{type} = $override; | 
| 138 | 0 |  |  |  |  |  | $self->{fileset}->{log}->verbose(<<"EOM"); | 
| 139 |  |  |  |  |  |  | Directive file has overriden $self->{filename}'s type to be '$type' ($override). | 
| 140 |  |  |  |  |  |  | EOM | 
| 141 | 0 |  |  |  |  |  | return; | 
| 142 |  |  |  |  |  |  | } | 
| 143 | 0 |  |  |  |  |  | $self->{fileset}->{log}->verbose(<<"EOW"); | 
| 144 |  |  |  |  |  |  | Directive file wanted to override $self->{filename}'s type as '$type', | 
| 145 |  |  |  |  |  |  | but this type has no override. | 
| 146 |  |  |  |  |  |  | EOW | 
| 147 | 0 |  |  |  |  |  | return 1; | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | sub set_flag { | 
| 151 | 0 |  |  | 0 | 1 |  | my ($self, $flag) = @_; | 
| 152 | 0 |  |  |  |  |  | return $self->{flags}->{$flag} = 1; | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | sub check_flag { | 
| 156 | 0 |  |  | 0 | 1 |  | my ($self, $flag) = @_; | 
| 157 | 0 |  |  |  |  |  | return exists $self->{flags}->{$flag}; | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | sub set_dvi_flags { | 
| 161 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 162 | 0 |  |  |  |  |  | return $self->{dvi_flags} = shift; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | sub get_dvi_flags { | 
| 166 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 167 | 0 |  |  |  |  |  | return $self->{dvi_flags}; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | sub process { | 
| 171 | 0 |  |  | 0 | 1 |  | my ($self, $process) = @_; | 
| 172 |  |  |  |  |  |  |  | 
| 173 | 0 |  |  |  |  |  | my $result; | 
| 174 | 0 | 0 |  |  |  |  | if ($self->type_is_tex()) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 175 | 0 |  |  |  |  |  | $result = $self->process_tex($process); | 
| 176 |  |  |  |  |  |  | } elsif ($self->type() eq 'TYPE_ENCRYPTED') { | 
| 177 | 0 |  |  |  |  |  | $result = $self->process_encrypted($process); | 
| 178 |  |  |  |  |  |  | } elsif ($self->type() eq 'TYPE_DVI') { | 
| 179 | 0 |  |  |  |  |  | $result = $self->process_dvitype($process); | 
| 180 |  |  |  |  |  |  | } | 
| 181 | 0 |  |  |  |  |  | return $result; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub process_tex { | 
| 185 | 0 |  |  | 0 | 1 |  | my ($self, $process) = @_; | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 0 |  |  |  |  |  | my $log = $self->{fileset}->{log}; | 
| 188 | 0 |  |  |  |  |  | my $dir = $self->{fileset}->{dir}; | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 0 |  |  |  |  |  | my $tex_passes = 4; | 
| 191 | 0 |  |  |  |  |  | my ($new_type, $dont_hyper, $try_amslplain) = | 
| 192 |  |  |  |  |  |  | TeX::AutoTeX::HyperTeX::copy_source_from_hyper( | 
| 193 |  |  |  |  |  |  | $self->type(), | 
| 194 |  |  |  |  |  |  | $self->{filename}, | 
| 195 |  |  |  |  |  |  | $dir, | 
| 196 |  |  |  |  |  |  | $log, | 
| 197 |  |  |  |  |  |  | $self->{fileset}->{local_hyper_transform} | 
| 198 |  |  |  |  |  |  | ); | 
| 199 | 0 | 0 |  |  |  |  | if ($self->{type} ne $new_type) { | 
| 200 | 0 |  |  |  |  |  | $log->verbose(<<"EOM"); | 
| 201 |  |  |  |  |  |  | Changing type from '$self->{type}' to '$new_type' on recommendation of | 
| 202 |  |  |  |  |  |  | TeX::AutoTeX::HyperTeX::copy_source_from_hyper. | 
| 203 |  |  |  |  |  |  | EOM | 
| 204 | 0 |  |  |  |  |  | $self->{type} = $new_type; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 0 | 0 |  |  |  |  | my $try_hyper = $dont_hyper ? 0 : !$process->{nohypertex}; | 
| 208 | 0 | 0 |  |  |  |  | $log->verbose('Will not attempt to use hypertex.') unless $try_hyper; | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | # initalize @to_try with the different formats in the order we want to try them | 
| 211 | 0 |  |  |  |  |  | my @to_try; | 
| 212 | 0 | 0 | 0 |  |  |  | if ($process->{branch} eq '3' || $process->{branch} =~ m{texlive/}) { | 
|  |  | 0 |  |  |  |  |  | 
| 213 | 0 | 0 | 0 |  |  |  | if ($self->type() eq 'TYPE_LATEX' || $self->type() eq 'TYPE_LATEX2e') { | 
|  |  | 0 |  |  |  |  |  | 
| 214 | 0 | 0 |  |  |  |  | @to_try = map {$try_hyper ? ("h$_", $_) : $_ } qw(latex pdflatex tex); | 
|  | 0 |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | } elsif ($self->type() eq 'TYPE_PDFLATEX') { | 
| 216 | 0 | 0 |  |  |  |  | @to_try = map {$try_hyper ? ("h$_", $_) : $_ } qw(pdflatex tex); | 
|  | 0 |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | } else { | 
| 218 |  |  |  |  |  |  | # we always try tex, in case of amslplain or such | 
| 219 | 0 | 0 |  |  |  |  | @to_try = $try_hyper ? qw(htex tex) : 'tex'; | 
| 220 |  |  |  |  |  |  | # we always try latex, not just tex, because sometimes latex papers are misidentified as tex | 
| 221 | 0 | 0 |  |  |  |  | push @to_try, $try_hyper ? qw(hlatex latex) : 'latex'; | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  | } elsif ($process->{branch} eq '2') { | 
| 224 |  |  |  |  |  |  | # deal with legacy stuff | 
| 225 | 0 | 0 |  |  |  |  | if (exists $self->{tex_format}) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 226 | 0 | 0 |  |  |  |  | if ($self->{tex_format} eq 'bigtex') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 227 | 0 |  |  |  |  |  | $log->verbose('Using bigtex'); | 
| 228 | 0 |  |  |  |  |  | $self->{binaries}->{HTEX} = $self->{binaries}->{TEX} = $self->{binaries}->{BIGTEX}; | 
| 229 |  |  |  |  |  |  | } elsif ($self->{tex_format} eq 'biglatex') { | 
| 230 | 0 |  |  |  |  |  | $log->verbose('Using biglatex'); | 
| 231 | 0 |  |  |  |  |  | $self->{binaries}->{HLATEX2E} = $self->{binaries}->{LATEX2E} = $self->{binaries}->{BIGLATEX}; | 
| 232 |  |  |  |  |  |  | } elsif ($self->{tex_format} eq 'latex209') { | 
| 233 | 0 | 0 |  |  |  |  | @to_try = $try_hyper? qw(hlatex209 latex209) : 'latex209'; | 
| 234 |  |  |  |  |  |  | } elsif ($self->{tex_format} eq 'latex') { | 
| 235 | 0 | 0 |  |  |  |  | @to_try = $try_hyper ? qw(hlatex2e hlatex209 latex2e latex209) : qw(latex2e latex209); | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  | } elsif ($self->type() eq 'TYPE_LATEX') { | 
| 238 | 0 | 0 |  |  |  |  | @to_try = $try_hyper ? qw(hlatex2e hlatex209 latex2e latex209) : qw(latex2e latex209); | 
| 239 |  |  |  |  |  |  | } elsif ($self->type() eq 'TYPE_LATEX2e') { | 
| 240 | 0 | 0 |  |  |  |  | @to_try = $try_hyper ? qw(hlatex2e latex2e) : 'latex2e'; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  | # under some circumstances, what looked like latex files are actually | 
| 243 |  |  |  |  |  |  | # tex files with amsart. | 
| 244 | 0 | 0 |  |  |  |  | if ($try_amslplain) { | 
| 245 | 0 | 0 |  |  |  |  | push @to_try, $try_hyper ? qw(hamslplain amslplain) : 'amslplain'; | 
| 246 |  |  |  |  |  |  | } | 
| 247 | 0 | 0 |  |  |  |  | if (@to_try == 0) { | 
| 248 |  |  |  |  |  |  | # we always try latex, not just tex, because sometimes latex papers are misidentified as tex | 
| 249 | 0 | 0 |  |  |  |  | @to_try = map { $try_hyper ? ("h$_", $_) : $_ } qw(tex latex2e); | 
|  | 0 |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | } else { | 
| 251 | 0 | 0 |  |  |  |  | push @to_try, $try_hyper ? qw(htex tex) : 'tex'; | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  | } else { | 
| 254 | 0 |  |  |  |  |  | $log->verbose('unknown TeX branch'); | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  |  | 
| 257 | 0 |  |  |  |  |  | my $failed = 0; | 
| 258 | 0 |  |  |  |  |  | my %written = (); | 
| 259 | 0 |  |  |  |  |  | foreach my $tex_type (@to_try) { | 
| 260 | 0 | 0 |  |  |  |  | if ('h' eq substr $tex_type, 0, 1) { | 
| 261 | 0 |  |  |  |  |  | $self->swap_source('hyper'); | 
| 262 |  |  |  |  |  |  | } else { | 
| 263 | 0 |  |  |  |  |  | $self->swap_source('nohyper'); | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  |  | 
| 266 | 0 |  |  |  |  |  | my ($stime, $program, $old_format); | 
| 267 |  |  |  |  |  |  | # get the appropriate program name from the variables set in TeX::AutoTeX::Config | 
| 268 | 0 | 0 |  |  |  |  | if ($tex_type =~ /amslplain/) { | 
| 269 | 0 |  |  |  |  |  | $old_format = $self->{tex_format}; | 
| 270 | 0 |  |  |  |  |  | $self->{tex_format} = 'amslplain'; | 
| 271 | 0 | 0 |  |  |  |  | if ('h' eq substr $tex_type, 0, 1) { | 
| 272 | 0 |  |  |  |  |  | $program = $self->{binaries}->{HTEX}; | 
| 273 |  |  |  |  |  |  | } else { | 
| 274 | 0 |  |  |  |  |  | $program = $self->{binaries}->{TEX}; | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  | } else { | 
| 277 | 0 |  |  |  |  |  | $program = $self->{binaries}->{uc($tex_type)}; | 
| 278 |  |  |  |  |  |  | } | 
| 279 | 0 | 0 |  |  |  |  | if (! $self->{fileset}->{utime}) {# set mtime and atime on all (non-dot-)files in CWD back 10 seconds | 
| 280 | 0 |  |  |  |  |  | my $setbacktime = time() - 10; | 
| 281 |  |  |  |  |  |  | ###	 {#TS: extensive logging | 
| 282 |  |  |  |  |  |  | ###	       my @allfiles = glob("*"); | 
| 283 |  |  |  |  |  |  | ###	       local $" = "]\n\t["; | 
| 284 |  |  |  |  |  |  | ###	       $log->verbose("current file contents:\n\t[@allfiles]"); | 
| 285 |  |  |  |  |  |  | ###	 } | 
| 286 | 0 | 0 |  |  |  |  | opendir my $CDIR, $dir or $log->verbose("opening directory '$dir' for reading failed: $!"); | 
| 287 | 0 |  |  |  |  |  | my $numfiles = utime $setbacktime, $setbacktime, | 
| 288 | 0 |  |  |  |  |  | map { "$dir/$_" } | 
| 289 | 0 | 0 |  |  |  |  | map { /^($self->{untaint_regexp})$/o } | 
| 290 | 0 |  |  |  |  |  | grep { !/^\./ && -f "$dir/$_" } | 
| 291 |  |  |  |  |  |  | readdir $CDIR; | 
| 292 | 0 | 0 |  |  |  |  | closedir $CDIR or $log->verbose("closing directory $dir failed: $!"); | 
| 293 |  |  |  |  |  |  | #TS Note: | 
| 294 |  |  |  |  |  |  | # if we don't reset (a|m)time each time the loop processes a new file | 
| 295 |  |  |  |  |  |  | # the .with_hyper and .without_hyper files will not have proper stat | 
| 296 |  |  |  |  |  |  | # values and will be removed. if we globally reset (a|m)time, things go | 
| 297 |  |  |  |  |  |  | # awry with inclusion checking. Therefore make sure to reset utime in | 
| 298 |  |  |  |  |  |  | # swap_source for hyper files in HyperTeX.pm. | 
| 299 | 0 |  |  |  |  |  | $self->{fileset}->{utime}++; | 
| 300 |  |  |  |  |  |  | } | 
| 301 | 0 |  |  |  |  |  | $stime = time; | 
| 302 | 0 |  |  |  |  |  | $failed = $self->run_tex_attempt($program, $tex_passes, $process, $stime, \%written, $tex_type); | 
| 303 | 0 | 0 |  |  |  |  | if ($tex_type =~ /amslplain/) { | 
| 304 | 0 |  |  |  |  |  | $self->{tex_format} = $old_format; | 
| 305 |  |  |  |  |  |  | } | 
| 306 | 0 |  |  |  |  |  | $self->clean_aux_files($stime); | 
| 307 | 0 | 0 |  |  |  |  | if (!$failed) { | 
| 308 | 0 |  |  |  |  |  | my $logfile = $self->basename() . '.log'; | 
| 309 | 0 | 0 |  |  |  |  | unlink "$dir/$logfile" or | 
| 310 |  |  |  |  |  |  | $log->error("Could not remove file '$logfile'."); | 
| 311 | 0 |  |  |  |  |  | last; | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  | } | 
| 314 | 0 | 0 |  |  |  |  | if ($failed) { | 
| 315 | 0 |  |  |  |  |  | $log->verbose("We failed utterly to process the TeX file '$self->{filename}'"); | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  | # ensure that no copies of the tex file are left behind | 
| 318 | 0 |  |  |  |  |  | unlink map {"$dir/$self->{filename}.$_"} qw(with_hyper without_hyper); | 
|  | 0 |  |  |  |  |  |  | 
| 319 | 0 |  |  |  |  |  | return $failed; | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | sub process_encrypted { | 
| 323 | 0 |  |  | 0 | 1 |  | my ($self, $process) = @_; | 
| 324 |  |  |  |  |  |  |  | 
| 325 | 0 |  |  |  |  |  | my $log = $self->{fileset}->{log}; | 
| 326 | 0 |  |  |  |  |  | my $dir = $self->{fileset}->{dir}; | 
| 327 | 0 |  |  |  |  |  | my $file = $self->{filename}; | 
| 328 |  |  |  |  |  |  |  | 
| 329 | 0 |  |  |  |  |  | $log->verbose( "Decrypting file '$file'"); | 
| 330 | 0 |  |  |  |  |  | my $newfile = $file; | 
| 331 | 0 | 0 |  |  |  |  | if ('.cry' ne lc substr $newfile, -4, 4, q{}) { | 
| 332 |  |  |  |  |  |  | # throw exception | 
| 333 | 0 |  |  |  |  |  | $log->error("filename '$file' does not end in '.cry'."); | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | # arXiv specific | 
| 337 | 0 |  |  |  |  |  | my $key = $process->{decryption_key}; | 
| 338 | 0 |  |  |  |  |  | $log->verbose("running: '$CRYPT $newfile $key'"); | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | # ensure proper path so that correct programs decry and cipher are found | 
| 341 | 0 | 0 |  |  |  |  | if (index($ENV{PATH}, $TEX_PATH) != 0) { | 
| 342 | 0 |  |  |  |  |  | local $ENV{PATH} = "$TEX_PATH/bin:" . $ENV{PATH}; | 
| 343 |  |  |  |  |  |  | } | 
| 344 | 0 |  |  |  |  |  | my $fullname = "$dir/$newfile"; | 
| 345 |  |  |  |  |  |  | # The following two lines are necessary to get decry to work | 
| 346 |  |  |  |  |  |  | { | 
| 347 | 0 |  |  |  |  |  | open my $OUTFH, '>', $fullname; close $OUTFH; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 348 | 0 |  |  |  |  |  | chmod oct(666), $fullname; | 
| 349 |  |  |  |  |  |  | } | 
| 350 | 0 |  |  |  |  |  | $log->verbose("path is: '$ENV{PATH}'"); | 
| 351 | 0 |  | 0 |  |  |  | my $response = `$CRYPT $fullname $key` || "[no response, exit code $?]"; | 
| 352 | 0 | 0 |  |  |  |  | if ($?) { | 
| 353 | 0 |  |  |  |  |  | $log->error("$CRYPT error response: '$response'"); | 
| 354 |  |  |  |  |  |  | } | 
| 355 | 0 | 0 | 0 |  |  |  | if (! -T $fullname || -z _) { | 
| 356 | 0 |  |  |  |  |  | $log->error("'$file' didn't decrypt to a text file."); | 
| 357 |  |  |  |  |  |  | } | 
| 358 | 0 | 0 |  |  |  |  | unlink "$dir/$file" or $log->error("unable to remove '$file': $!"); | 
| 359 | 0 |  |  |  |  |  | return; | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | sub process_dvitype { | 
| 363 | 0 |  |  | 0 | 1 |  | my ($self, $process) = @_; | 
| 364 |  |  |  |  |  |  |  | 
| 365 | 0 |  |  |  |  |  | my $log = $self->{fileset}->{log}; | 
| 366 | 0 |  |  |  |  |  | my $dir = $self->{fileset}->{dir}; | 
| 367 | 0 |  |  |  |  |  | my $file = $self->{filename}; | 
| 368 |  |  |  |  |  |  |  | 
| 369 | 0 | 0 |  |  |  |  | my $dvi_flags = $process->{branch} =~ m{texlive/}? '-R2' : '-R'; | 
| 370 | 0 | 0 |  |  |  |  | $dvi_flags .= " $self->{dvi_flags}" if $self->{dvi_flags}; | 
| 371 | 0 | 0 |  |  |  |  | $dvi_flags .= ' -t landscape' if $self->{flags}->{landscape}; | 
| 372 | 0 | 0 |  |  |  |  | $dvi_flags .= $self->{flags}->{keepcomments}? ' -K0' : ' -K1'; | 
| 373 |  |  |  |  |  |  |  | 
| 374 | 0 |  |  |  |  |  | my $response; | 
| 375 | 0 | 0 |  |  |  |  | my $setenv = qq{export HOME=/tmp @{[$ENV{TEXMFCNF}? "TEXMFCNF=$ENV{TEXMFCNF}": q{}]} PATH=$process->{tex_env_path}}; | 
|  | 0 |  |  |  |  |  |  | 
| 376 | 0 |  |  |  |  |  | my $crdir = substr $dir, length $TEX_PATH; | 
| 377 | 0 |  |  |  |  |  | while (1) { | 
| 378 | 0 |  |  |  |  |  | $log->verbose(" ~~~~~~~~~~~ Processing file '$file'"); | 
| 379 | 0 |  |  |  |  |  | my $dvipscommand = qq#$TEXCHR $TEX_PATH "($setenv; cd $crdir && $DVIPS $dvi_flags -z '$file' -o )" 2>&1#; | 
| 380 | 0 |  |  |  |  |  | $log->verbose('Running: ' . substr $dvipscommand, length "$TEXCHR $TEX_PATH "); | 
| 381 | 0 |  |  |  |  |  | my $dvipstime = time; | 
| 382 | 0 |  |  |  |  |  | $response = `$dvipscommand`; | 
| 383 | 0 | 0 |  |  |  |  | last if !$?; | 
| 384 | 0 |  |  |  |  |  | $log->verbose("$DVIPS $dvi_flags -z produced an error: $?\nResponse was $response\nRetrying without '-z'"); | 
| 385 | 0 | 0 | 0 |  |  |  | if (-e "$dir/head.tmp" && -e "$dir/body.tmp") { | 
| 386 | 0 |  |  |  |  |  | unlink "$dir/head.tmp", "$dir/body.tmp"; | 
| 387 | 0 |  |  |  |  |  | $log->verbose('removed dvips leftover head.tmp and body.tmp'); | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  | # dvips -z may have core-dumped. remove only newly generated core file(s) | 
| 390 | 0 | 0 |  |  |  |  | if (my @corefiles = glob "$dir/core\.[0-9]*") { | 
| 391 | 0 |  |  |  |  |  | @corefiles = | 
| 392 | 0 | 0 |  |  |  |  | map { m/(.*)/ } | 
| 393 | 0 |  |  |  |  |  | grep { m{^$dir/core\.\d+$} && (stat "$dir/$_")[9] >= $dvipstime } | 
| 394 |  |  |  |  |  |  | @corefiles; | 
| 395 | 0 | 0 |  |  |  |  | if (@corefiles) { | 
| 396 | 0 |  |  |  |  |  | unlink @corefiles; | 
| 397 | 0 |  |  |  |  |  | $log->verbose("removed one or more core files: @corefiles"); | 
| 398 |  |  |  |  |  |  | } | 
| 399 |  |  |  |  |  |  | } | 
| 400 | 0 |  |  |  |  |  | $dvipscommand = qq#$TEXCHR $TEX_PATH "($setenv; cd $crdir && $DVIPS $dvi_flags '$file' -o )" 2>&1#; | 
| 401 | 0 |  |  |  |  |  | $log->verbose('Running: ' . substr $dvipscommand, length "$TEXCHR $TEX_PATH "); | 
| 402 | 0 |  |  |  |  |  | $response = `$dvipscommand`; | 
| 403 | 0 | 0 |  |  |  |  | last if !$?; | 
| 404 | 0 |  |  |  |  |  | $log->verbose("$DVIPS $dvi_flags produced an error: $?\nResponse was $response."); | 
| 405 | 0 |  |  |  |  |  | $log->error('Failed to produce postscript from dvi.'); | 
| 406 |  |  |  |  |  |  | } | 
| 407 | 0 |  |  |  |  |  | $log->verbose("dvi(h)ps said ...\n$response."); | 
| 408 |  |  |  |  |  |  |  | 
| 409 | 0 |  |  |  |  |  | my %commondvipsheaders; | 
| 410 | 0 |  |  |  |  |  | @commondvipsheaders{qw( | 
| 411 |  |  |  |  |  |  | tex.pro | 
| 412 |  |  |  |  |  |  | texc.pro | 
| 413 |  |  |  |  |  |  | texps.pro | 
| 414 |  |  |  |  |  |  | hps.pro | 
| 415 |  |  |  |  |  |  | special.pro | 
| 416 |  |  |  |  |  |  | color.pro | 
| 417 |  |  |  |  |  |  | finclude.pro | 
| 418 |  |  |  |  |  |  | alt-rule.pro | 
| 419 |  |  |  |  |  |  | head.tmp | 
| 420 |  |  |  |  |  |  | body.tmp | 
| 421 |  |  |  |  |  |  | 8r.enc | 
| 422 |  |  |  |  |  |  | texnansi.enc | 
| 423 |  |  |  |  |  |  | )} = (); | 
| 424 |  |  |  |  |  |  |  | 
| 425 | 0 |  |  |  |  |  | while ($response =~ m{<(?:\.//?)*([^><\n]+)>}g) { | 
| 426 | 0 |  |  |  |  |  | my $included = $1; | 
| 427 | 0 | 0 |  |  |  |  | if ($included !~ m{^/}){ | 
| 428 | 0 | 0 | 0 |  |  |  | if (-e "$dir/$included") { | 
|  |  | 0 |  |  |  |  |  | 
| 429 | 0 |  |  |  |  |  | $self->{fileset}->new_File($included)->set_flag('used_by_dvips'); | 
| 430 | 0 |  |  |  |  |  | $log->verbose("'$included' no longer required ... it's in the postscript file."); | 
| 431 |  |  |  |  |  |  | } elsif (!(exists $commondvipsheaders{$included} || '.pfb' eq substr $included, -4)) { | 
| 432 | 0 |  |  |  |  |  | $log->verbose("'$included' was apparently included, but cannot be deleted, because it cannot be found in cwd."); | 
| 433 |  |  |  |  |  |  | } | 
| 434 |  |  |  |  |  |  | } | 
| 435 |  |  |  |  |  |  | } | 
| 436 | 0 |  |  |  |  |  | my $psfile = $file; | 
| 437 | 0 |  |  |  |  |  | substr $psfile, -3, 3, 'ps'; | 
| 438 |  |  |  |  |  |  | # Change %%Title if wanted | 
| 439 | 0 | 0 |  |  |  |  | if (my $stampref = $process->get_stamp()) { | 
| 440 | 0 |  |  |  |  |  | TeX::AutoTeX::PostScript::fix_ps_title( | 
| 441 |  |  |  |  |  |  | $psfile, | 
| 442 |  |  |  |  |  |  | $dir, | 
| 443 |  |  |  |  |  |  | $stampref->[0], | 
| 444 |  |  |  |  |  |  | $log | 
| 445 |  |  |  |  |  |  | ); | 
| 446 | 0 |  |  |  |  |  | TeX::AutoTeX::PostScript::stamp_postscript( | 
| 447 |  |  |  |  |  |  | $psfile, | 
| 448 |  |  |  |  |  |  | $dir, | 
| 449 |  |  |  |  |  |  | $stampref, | 
| 450 |  |  |  |  |  |  | $log | 
| 451 |  |  |  |  |  |  | ); | 
| 452 |  |  |  |  |  |  | } | 
| 453 | 0 |  |  |  |  |  | $self->{fileset}->new_File($psfile)->set_flag('main_postscript'); | 
| 454 |  |  |  |  |  |  |  | 
| 455 | 0 |  |  |  |  |  | return; | 
| 456 |  |  |  |  |  |  | } | 
| 457 |  |  |  |  |  |  |  | 
| 458 |  |  |  |  |  |  | sub slurp_log { | 
| 459 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 460 | 0 |  |  |  |  |  | my $log = $self->basename() . '.log'; | 
| 461 |  |  |  |  |  |  |  | 
| 462 | 0 | 0 |  |  |  |  | open(my $LOG, '<', "$self->{fileset}->{dir}/$log") | 
| 463 |  |  |  |  |  |  | || $self->{fileset}->{log}->error("Could not open log file '$log' produced by (la)tex."); | 
| 464 | 0 |  |  |  |  |  | local $/ = undef; | 
| 465 | 0 |  |  |  |  |  | my $log_contents = <$LOG>; | 
| 466 | 0 |  |  |  |  |  | close $LOG; | 
| 467 | 0 |  |  |  |  |  | return \$log_contents; | 
| 468 |  |  |  |  |  |  | } | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | sub clean_aux_files { | 
| 471 | 0 |  |  | 0 | 1 |  | my ($self, $stime) = @_; | 
| 472 |  |  |  |  |  |  |  | 
| 473 | 0 | 0 |  |  |  |  | opendir(my $WORKING_DIR, $self->{fileset}->{dir}) | 
| 474 |  |  |  |  |  |  | || $self->{fileset}->{log}->error("Can't open processing directory: $!"); | 
| 475 | 0 | 0 | 0 |  |  |  | if (my @auxfiles = | 
|  | 0 | 0 |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | grep { /\.aux$/ && -f "$self->{fileset}->{dir}/$_" && (stat(_))[9] >= $stime } | 
| 477 |  |  |  |  |  |  | readdir $WORKING_DIR) { | 
| 478 |  |  |  |  |  |  | my $numauxfiles = unlink( | 
| 479 |  |  |  |  |  |  | map { "$self->{fileset}->{dir}/$_" } | 
| 480 | 0 |  | 0 |  |  |  | map { m/(.*)/ } | 
| 481 |  |  |  |  |  |  | @auxfiles | 
| 482 |  |  |  |  |  |  | ) | 
| 483 |  |  |  |  |  |  | || $self->{fileset}->{log}->error("Could not remove one of the auxfiles: @auxfiles."); | 
| 484 | 0 |  |  |  |  |  | $self->{fileset}->{log}->verbose("unlinked $numauxfiles '.aux' files"); | 
| 485 |  |  |  |  |  |  | } | 
| 486 | 0 |  |  |  |  |  | closedir($WORKING_DIR); | 
| 487 | 0 |  |  |  |  |  | return; | 
| 488 |  |  |  |  |  |  | } | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | sub swap_source { | 
| 491 | 0 |  |  | 0 | 1 |  | my ($self, $hyper) = @_; | 
| 492 |  |  |  |  |  |  |  | 
| 493 | 0 |  |  |  |  |  | my $file  = $self->{filename}; | 
| 494 | 0 |  |  |  |  |  | my $dir = $self->{fileset}->{dir}; | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | #  remove aux files if any | 
| 497 | 0 |  |  |  |  |  | my $basename = $self->basename(); | 
| 498 | 0 |  |  |  |  |  | foreach my $auxfile (grep {-e "$dir/$_"} map {"$basename.$_"} qw(aux lot lof toc)) { | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 499 | 0 | 0 |  |  |  |  | unlink "$dir/$auxfile" or $self->{fileset}->{log}->error("failed to remove '$auxfile'."); | 
| 500 | 0 |  |  |  |  |  | $self->{fileset}->{log}->verbose("removed aux file '$auxfile'"); | 
| 501 |  |  |  |  |  |  | } | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | # remove the existing file to be processed, then link the | 
| 504 |  |  |  |  |  |  | # with/without hyper version to it | 
| 505 | 0 | 0 |  |  |  |  | unlink "$dir/$file" or $self->{fileset}->{log}->error("failed to remove '$file'."); | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | # note that (hard-)linking does not change atime or mtime | 
| 508 | 0 | 0 |  |  |  |  | if ($hyper eq 'hyper') { | 
| 509 | 0 | 0 |  |  |  |  | link "$dir/$file.with_hyper", "$dir/$file" | 
| 510 |  |  |  |  |  |  | or $self->{fileset}->{log}->verbose("failed to rename '$file'"); | 
| 511 |  |  |  |  |  |  | } else { | 
| 512 | 0 | 0 |  |  |  |  | link "$dir/$file.without_hyper", "$dir/$file" | 
| 513 |  |  |  |  |  |  | or $self->{fileset}->{log}->verbose("failed to rename '$file'"); | 
| 514 |  |  |  |  |  |  | } | 
| 515 | 0 |  |  |  |  |  | return; | 
| 516 |  |  |  |  |  |  | } | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | sub run_tex_attempt { | 
| 519 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 520 | 0 |  |  |  |  |  | my ($program, $tex_passes, $process, $stime, $written, $tex_type) = @_; | 
| 521 |  |  |  |  |  |  |  | 
| 522 | 0 |  |  |  |  |  | my $log = $self->{fileset}->{log}; | 
| 523 |  |  |  |  |  |  |  | 
| 524 | 0 | 0 |  |  |  |  | if (!defined $TEXCHR) { | 
| 525 | 0 |  |  |  |  |  | throw TeX::AutoTeX::TexChrException('TEXCHR not set.'); | 
| 526 |  |  |  |  |  |  | } | 
| 527 | 0 | 0 | 0 |  |  |  | if ($process->{branch} eq '2' && !defined $ENV{TEXMFCNF}) { | 
| 528 | 0 |  |  |  |  |  | throw TeX::AutoTeX::TexMFCnfException('TEXMFCNF not set for /2 branch.'); | 
| 529 |  |  |  |  |  |  | } | 
| 530 |  |  |  |  |  |  | #TS: find the source of STDIN input to be used. this was historically used for | 
| 531 |  |  |  |  |  |  | # macros which required user input -- e.g. big or little (b/l) | 
| 532 | 0 |  |  |  |  |  | my $latex_input = $self->basename() . '.inp'; | 
| 533 | 0 | 0 |  |  |  |  | my $feeder = -e "$self->{fileset}->{dir}/$latex_input" ? qq{'$latex_input'} : '/dev/null'; | 
| 534 |  |  |  |  |  |  |  | 
| 535 | 0 |  | 0 |  |  |  | my $tex_format = $self->{tex_format} || q{}; | 
| 536 | 0 |  |  |  |  |  | my $escaped_tex_format = q{}; | 
| 537 |  |  |  |  |  |  |  | 
| 538 | 0 | 0 | 0 |  |  |  | if (!$tex_format && $tex_type eq 'htex') { | 
| 539 | 0 |  |  |  |  |  | $tex_format = 'htex'; | 
| 540 |  |  |  |  |  |  | } | 
| 541 | 0 | 0 | 0 |  |  |  | if ($tex_format !~ /209$/ &&  $tex_type =~ /209/) { | 
| 542 | 0 |  |  |  |  |  | $tex_format .= '209'; | 
| 543 |  |  |  |  |  |  | } | 
| 544 | 0 | 0 | 0 |  |  |  | if ($tex_format =~ /^latex209/ && $program =~ /^h/) { | 
| 545 | 0 |  |  |  |  |  | $tex_format = 'hlatex209'; | 
| 546 |  |  |  |  |  |  | } | 
| 547 | 0 | 0 |  |  |  |  | if ($tex_format){ | 
| 548 |  |  |  |  |  |  | ## TS: FIXME | 
| 549 |  |  |  |  |  |  | ## static lookup table instead of (convoluted) regexp. should go into | 
| 550 |  |  |  |  |  |  | ## TeX::AutoTeX::Config?  here is the list of all formats available in | 
| 551 |  |  |  |  |  |  | ## arXiv's tex installation. For texlive 2009 and newer arXiv doesn't | 
| 552 |  |  |  |  |  |  | ## build custom formats any longer. | 
| 553 | 0 |  |  |  |  |  | my %known_formats; | 
| 554 | 0 |  |  |  |  |  | @{$known_formats{'3'}}{qw( | 
|  | 0 |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | amstex | 
| 556 |  |  |  |  |  |  | htex | 
| 557 |  |  |  |  |  |  | tex | 
| 558 |  |  |  |  |  |  | latex | 
| 559 |  |  |  |  |  |  | biglatex | 
| 560 |  |  |  |  |  |  | pdfamstex | 
| 561 |  |  |  |  |  |  | pdflatex | 
| 562 |  |  |  |  |  |  | pdftex | 
| 563 |  |  |  |  |  |  | )} = (); | 
| 564 |  |  |  |  |  |  |  | 
| 565 | 0 |  |  |  |  |  | @{$known_formats{'2'}}{qw( | 
|  | 0 |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | amslatex1.1 | 
| 567 |  |  |  |  |  |  | amslplain | 
| 568 |  |  |  |  |  |  | amstex | 
| 569 |  |  |  |  |  |  | biglatex | 
| 570 |  |  |  |  |  |  | bigtex | 
| 571 |  |  |  |  |  |  | cp-aa | 
| 572 |  |  |  |  |  |  | hlatex209 | 
| 573 |  |  |  |  |  |  | hlatex2e | 
| 574 |  |  |  |  |  |  | hlatex | 
| 575 |  |  |  |  |  |  | hlplain | 
| 576 |  |  |  |  |  |  | hplain | 
| 577 |  |  |  |  |  |  | htex | 
| 578 |  |  |  |  |  |  | latex209 | 
| 579 |  |  |  |  |  |  | latex2e | 
| 580 |  |  |  |  |  |  | latex | 
| 581 |  |  |  |  |  |  | lplain | 
| 582 |  |  |  |  |  |  | plain | 
| 583 |  |  |  |  |  |  | tex | 
| 584 |  |  |  |  |  |  | texsis | 
| 585 |  |  |  |  |  |  | )} = (); | 
| 586 |  |  |  |  |  |  |  | 
| 587 | 0 | 0 |  |  |  |  | if (exists $known_formats{$process->{branch}}{$tex_format}) { | 
| 588 | 0 |  |  |  |  |  | $log->verbose("Using format file $tex_format"); | 
| 589 | 0 |  |  |  |  |  | $escaped_tex_format = q{&} . $tex_format; | 
| 590 |  |  |  |  |  |  | # escape (&) in $tex_format and remove double quotes | 
| 591 |  |  |  |  |  |  | # TS: where would those come from after the lookup table replaced older code? | 
| 592 | 0 |  |  |  |  |  | $escaped_tex_format =~ s/&/\\\&/g; | 
| 593 | 0 |  |  |  |  |  | $escaped_tex_format =~ s/"//g; # " | 
| 594 |  |  |  |  |  |  | } else { | 
| 595 | 0 |  |  |  |  |  | $log->verbose("'$tex_format' is not a valid TeX format; will ignore."); | 
| 596 |  |  |  |  |  |  | } | 
| 597 |  |  |  |  |  |  | } | 
| 598 |  |  |  |  |  |  |  | 
| 599 | 0 | 0 |  |  |  |  | my $setenv = qq{export HOME=/tmp @{[$ENV{TEXMFCNF}? "TEXMFCNF=$ENV{TEXMFCNF}": q{}]} PATH=$process->{tex_env_path}}; | 
|  | 0 |  |  |  |  |  |  | 
| 600 | 0 | 0 |  |  |  |  | $log->verbose(qq{TEXMFCNF is @{[$ENV{TEXMFCNF}? "set to: '$ENV{TEXMFCNF}'": 'unset.']}}); | 
|  | 0 |  |  |  |  |  |  | 
| 601 | 0 |  |  |  |  |  | my $crdir = substr $self->{fileset}->{dir}, length $TEX_PATH; | 
| 602 | 0 |  |  |  |  |  | my $runtexcommand = qq#$TEXCHR $TEX_PATH "($setenv; cd $crdir && $program $escaped_tex_format '$self->{filename}' < $feeder)" 2>&1#; | 
| 603 |  |  |  |  |  |  |  | 
| 604 | 0 |  |  |  |  |  | my $passes = 0; | 
| 605 | 0 |  |  |  |  |  | my $rerun = 0; | 
| 606 | 0 |  |  |  |  |  | my $extra_pass = 0; | 
| 607 | 0 |  |  |  |  |  | my $xfontcreate = 0; | 
| 608 | 0 |  |  |  |  |  | my $lastlog_ref; | 
| 609 |  |  |  |  |  |  | my $failed; | 
| 610 |  |  |  |  |  |  |  | 
| 611 | 0 |  |  |  |  |  | my @ORDER = qw(first second third fourth fifth sixth seventh); | 
| 612 |  |  |  |  |  |  |  | 
| 613 |  |  |  |  |  |  | PASSES: | 
| 614 | 0 |  |  |  |  |  | while ($passes < $tex_passes) { | 
| 615 | 0 |  |  |  |  |  | $log->verbose(" ~~~~~~~~~~~ Running $tex_type for the $ORDER[$passes] time ~~~~~~~~"); | 
| 616 | 0 |  |  |  |  |  | $log->verbose('Running: ' . substr $runtexcommand, length "$TEXCHR $TEX_PATH "); | 
| 617 | 0 |  |  |  |  |  | my $out = `$runtexcommand`; | 
| 618 | 0 |  |  |  |  |  | $log->verbose($out); | 
| 619 | 0 |  |  |  |  |  | $lastlog_ref = $self->slurp_log(); | 
| 620 |  |  |  |  |  |  |  | 
| 621 |  |  |  |  |  |  | # TS: This is due to peculiarities of feynmf and similar dynamical font | 
| 622 |  |  |  |  |  |  | # creation. If we get an error exit status from latex then we need to | 
| 623 |  |  |  |  |  |  | # check for new font files and possibly rerun. Only do this once, in | 
| 624 |  |  |  |  |  |  | # case the non-zero exit status is due to some problem other than | 
| 625 |  |  |  |  |  |  | # font-creation and persistent, otherwise this could loop indefinitely. | 
| 626 | 0 | 0 |  |  |  |  | if ($?) { | 
| 627 | 0 | 0 | 0 |  |  |  | if (!$xfontcreate && $self->extra_fontcreation_pass($stime)) { | 
| 628 | 0 |  |  |  |  |  | $xfontcreate++; | 
| 629 | 0 |  |  |  |  |  | redo PASSES; | 
| 630 |  |  |  |  |  |  | } else { | 
| 631 |  |  |  |  |  |  | #the message below is slightly misleading because $program for | 
| 632 |  |  |  |  |  |  | #latex2e hyper/nohyper is the same. | 
| 633 | 0 |  |  |  |  |  | $log->verbose("$program '$self->{filename}' failed."); | 
| 634 | 0 |  |  |  |  |  | $self->trash_tex_aux_files($stime, $written); | 
| 635 | 0 |  |  |  |  |  | my $dvi = $self->basename() . '.dvi'; | 
| 636 | 0 | 0 |  |  |  |  | if (-e "$self->{fileset}->{dir}/$dvi") { | 
| 637 | 0 |  |  |  |  |  | $log->verbose("removing leftover dvi file '$dvi'"); | 
| 638 | 0 | 0 |  |  |  |  | unlink "$self->{fileset}->{dir}/$dvi" or | 
| 639 |  |  |  |  |  |  | $log->verbose("Could not remove file '$dvi'."); | 
| 640 |  |  |  |  |  |  | } | 
| 641 | 0 |  |  |  |  |  | $failed = 1; | 
| 642 | 0 |  |  |  |  |  | last; | 
| 643 |  |  |  |  |  |  | } | 
| 644 |  |  |  |  |  |  | } | 
| 645 |  |  |  |  |  |  |  | 
| 646 | 0 | 0 | 0 |  |  |  | if ($passes == 0 &&	($tex_type eq 'hlatex2e' || | 
|  |  |  | 0 |  |  |  |  | 
| 647 |  |  |  |  |  |  | $tex_type eq 'latex2e' || | 
| 648 |  |  |  |  |  |  | $tex_type =~ /h?pdflatex/o)) { | 
| 649 | 0 | 0 |  |  |  |  | if (0 <= index ${$lastlog_ref}, 'LaTeX Warning: Writing file `') { | 
|  | 0 |  |  |  |  |  |  | 
| 650 | 0 |  |  |  |  |  | while(${$lastlog_ref} =~ /LaTeX Warning: Writing file \`([^']*)\'\./g) { # ')){ | 
|  | 0 |  |  |  |  |  |  | 
| 651 | 0 |  |  |  |  |  | $written->{$1}++; | 
| 652 |  |  |  |  |  |  | } | 
| 653 |  |  |  |  |  |  | } | 
| 654 |  |  |  |  |  |  | } | 
| 655 |  |  |  |  |  |  |  | 
| 656 |  |  |  |  |  |  | # TS: added $tex_format b/c otherwise &amslplain will only be processed twice | 
| 657 | 0 | 0 | 0 |  |  |  | if ($tex_type =~ /latex/i || $tex_format) { | 
| 658 | 0 | 0 | 0 |  |  |  | if (0 <= index(${$lastlog_ref}, q{Label(s) may have changed. Rerun}) || | 
|  | 0 |  | 0 |  |  |  |  | 
|  | 0 |  | 0 |  |  |  |  | 
| 659 | 0 |  |  |  |  |  | 0 <= index(${$lastlog_ref}, q{Warning: Citation(s) may have changed.}) || | 
| 660 | 0 |  |  |  |  |  | 0 <= index(${$lastlog_ref}, q{Table widths have changed. Rerun LaTeX.}) || | 
| 661 |  |  |  |  |  |  | 0 <= index(${$lastlog_ref}, q{Rerun to get citations correct.}) | 
| 662 |  |  |  |  |  |  | ) { | 
| 663 | 0 |  |  |  |  |  | $rerun = 1; | 
| 664 |  |  |  |  |  |  | } else { | 
| 665 | 0 |  |  |  |  |  | $rerun = $self->extra_pass($stime); | 
| 666 |  |  |  |  |  |  | } | 
| 667 |  |  |  |  |  |  | } else { | 
| 668 | 0 |  |  |  |  |  | $rerun = 0; | 
| 669 |  |  |  |  |  |  | } | 
| 670 |  |  |  |  |  |  | # TS: this seems to be contraproductive to the $tex_passes=4 for &amslplain | 
| 671 |  |  |  |  |  |  | # and possibly others | 
| 672 | 0 | 0 | 0 |  |  |  | last unless $rerun || $passes == 0; | 
| 673 | 0 |  |  |  |  |  | $passes++; | 
| 674 |  |  |  |  |  |  | } # End of while ($passes < $tex_passes) | 
| 675 |  |  |  |  |  |  |  | 
| 676 | 0 | 0 | 0 |  |  |  | if ($passes == $tex_passes && $rerun) { | 
| 677 | 0 |  |  |  |  |  | $log->verbose("WARNING: Reached max number of passes, possibly failed to get CROSS-REFERENCES right."); | 
| 678 |  |  |  |  |  |  | } | 
| 679 | 0 |  |  |  |  |  | $self->trash_tex_aux_files($stime, $written); | 
| 680 | 0 |  |  |  |  |  | return $failed; | 
| 681 |  |  |  |  |  |  | } | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | sub extra_pass { | 
| 684 | 0 |  |  | 0 | 1 |  | my ($self, $younger_than) = @_; | 
| 685 |  |  |  |  |  |  |  | 
| 686 | 0 |  |  |  |  |  | opendir my $CDIR, $self->{fileset}->{dir}; | 
| 687 | 0 | 0 | 0 |  |  |  | my $tocloflot = | 
| 688 | 0 |  |  |  |  |  | grep {/\.(?:toc|lof|lot)$/ && -f "$self->{fileset}->{dir}/$_" && ((stat(_))[9] >= $younger_than)} | 
| 689 |  |  |  |  |  |  | readdir $CDIR; | 
| 690 | 0 |  |  |  |  |  | closedir $CDIR; | 
| 691 | 0 | 0 |  |  |  |  | if ($tocloflot) { | 
| 692 |  |  |  |  |  |  | #TS:  here a toc/lof/lot will always lead to max number of runs | 
| 693 |  |  |  |  |  |  | #     this is more robust than attempting to keep it to minimum | 
| 694 |  |  |  |  |  |  | #     in particular since a long toc etc. can lead to a shift between | 
| 695 |  |  |  |  |  |  | #     3rd and 4th pass without any indication of such in the log | 
| 696 |  |  |  |  |  |  |  | 
| 697 | 0 |  |  |  |  |  | $self->{fileset}->{log}->verbose('LaTeX wrote a .toc, .lof, or .lot file - running extra passes'); | 
| 698 | 0 |  |  |  |  |  | return 1; | 
| 699 |  |  |  |  |  |  | } | 
| 700 | 0 |  |  |  |  |  | return 0; | 
| 701 |  |  |  |  |  |  | } | 
| 702 |  |  |  |  |  |  |  | 
| 703 |  |  |  |  |  |  | sub extra_fontcreation_pass { | 
| 704 | 0 |  |  | 0 | 1 |  | my ($self, $younger_than) = @_; | 
| 705 |  |  |  |  |  |  |  | 
| 706 | 0 |  |  |  |  |  | opendir my $CDIR, $self->{fileset}->{dir}; | 
| 707 | 0 | 0 | 0 |  |  |  | my $mftfm = | 
| 708 | 0 |  |  |  |  |  | grep {/\.(?:mf|tfm)$/ && -f "$self->{fileset}->{dir}/$_" && ((stat(_))[9] >= $younger_than)} | 
| 709 |  |  |  |  |  |  | readdir $CDIR; | 
| 710 | 0 |  |  |  |  |  | closedir $CDIR; | 
| 711 | 0 | 0 |  |  |  |  | if ($mftfm) { | 
| 712 | 0 |  |  |  |  |  | $self->{fileset}->{log}->verbose(<<"EOM"); | 
| 713 |  |  |  |  |  |  | LaTeX wrote a .tfm or .mf file -- this indicates feynmf or similar dynamic font generation. | 
| 714 |  |  |  |  |  |  | Ignoring non-zero exit status and starting over retaining the new font files! | 
| 715 |  |  |  |  |  |  | EOM | 
| 716 | 0 |  |  |  |  |  | return 1; | 
| 717 |  |  |  |  |  |  | } | 
| 718 | 0 |  |  |  |  |  | return 0; | 
| 719 |  |  |  |  |  |  | } | 
| 720 |  |  |  |  |  |  |  | 
| 721 |  |  |  |  |  |  | sub trash_tex_aux_files { | 
| 722 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 723 | 0 |  |  |  |  |  | my ($younger_than, $written) = @_; | 
| 724 |  |  |  |  |  |  |  | 
| 725 | 0 |  |  |  |  |  | my $dir = $self->{fileset}->{dir}; | 
| 726 | 0 |  |  |  |  |  | opendir(my $TEMPDIR, $dir); | 
| 727 | 0 |  |  |  |  |  | my @files = | 
| 728 | 0 | 0 | 0 |  |  |  | map {/^(.*)$/}                      # untaint | 
|  |  |  | 0 |  |  |  |  | 
| 729 | 0 |  |  |  |  |  | grep {!/^\./                      # no dot files | 
| 730 |  |  |  |  |  |  | && !/\.(mf|log)$/         # need metafont files and associated log for labels | 
| 731 |  |  |  |  |  |  | && -f "$dir/$_"         # recently modified files only | 
| 732 |  |  |  |  |  |  | && (stat(_))[9] >= $younger_than} | 
| 733 |  |  |  |  |  |  | readdir $TEMPDIR; | 
| 734 | 0 |  |  |  |  |  | closedir $TEMPDIR; | 
| 735 |  |  |  |  |  |  |  | 
| 736 | 0 |  |  |  |  |  | foreach my $file (@files) { | 
| 737 |  |  |  |  |  |  | # Warn about files written by latex but still delete them. | 
| 738 |  |  |  |  |  |  | # We do not want people to include figures by dumping them out | 
| 739 |  |  |  |  |  |  | # from the tex file using the LaTeX2e filecontents environment. | 
| 740 |  |  |  |  |  |  | # Simeon-21Jul2000 | 
| 741 | 0 | 0 |  |  |  |  | if ($written->{$file}) { | 
| 742 | 0 |  |  |  |  |  | $self->{fileset}->{log}->verbose("TeX wrote out '$file', going to delete it as we don't permit filecontents inclusion of figures."); | 
| 743 | 0 |  |  |  |  |  | delete $written->{$file}; | 
| 744 |  |  |  |  |  |  | } | 
| 745 | 0 |  |  |  |  |  | my $fmt = $self->{fileset}->new_File($file)->type(); | 
| 746 | 0 | 0 |  |  |  |  | next if grep {$fmt eq 'TYPE_' . $_} qw(DVI POSTSCRIPT PDF); | 
|  | 0 |  |  |  |  |  |  | 
| 747 |  |  |  |  |  |  |  | 
| 748 | 0 |  |  |  |  |  | my $age = (stat("$dir/$file"))[9]; #TS FIXME: expensive stat for logging | 
| 749 | 0 |  |  |  |  |  | $self->{fileset}->{log}->verbose("Removing (La)TeX AUX file called '$file' ($age >= $younger_than)"); | 
| 750 | 0 | 0 |  |  |  |  | unlink "$dir/$file" | 
| 751 |  |  |  |  |  |  | or $self->{fileset}->{log}->error("failed to remove '$file': $!"); | 
| 752 |  |  |  |  |  |  | } | 
| 753 | 0 |  |  |  |  |  | return 0; | 
| 754 |  |  |  |  |  |  | } | 
| 755 |  |  |  |  |  |  |  | 
| 756 |  |  |  |  |  |  | ####################################################################### | 
| 757 |  |  |  |  |  |  | # basename() | 
| 758 |  |  |  |  |  |  | # takes filename from AutoTeX::File object, removes its extension if it has | 
| 759 |  |  |  |  |  |  | # one, caches and returns result. | 
| 760 |  |  |  |  |  |  | # a special case is a filename ending in '.'. in this | 
| 761 |  |  |  |  |  |  | # context, it should also be removed. | 
| 762 |  |  |  |  |  |  | # substr/rindex is 3x faster than regexp s/\.[^.]*$//; | 
| 763 |  |  |  |  |  |  |  | 
| 764 |  |  |  |  |  |  | sub basename { | 
| 765 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 766 | 0 | 0 |  |  |  |  | if (!defined $self->{basename}) { | 
| 767 | 0 | 0 |  |  |  |  | if (0 < index $self->{filename}, q{.}) { | 
| 768 | 0 |  |  |  |  |  | $self->{basename} = substr $self->{filename}, 0, rindex($self->{filename}, q{.}); | 
| 769 |  |  |  |  |  |  | } else { | 
| 770 | 0 |  |  |  |  |  | $self->{basename} = $self->{filename}; | 
| 771 |  |  |  |  |  |  | } | 
| 772 |  |  |  |  |  |  | } | 
| 773 | 0 |  |  |  |  |  | return $self->{basename}; | 
| 774 |  |  |  |  |  |  | } | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | 1; | 
| 777 |  |  |  |  |  |  |  | 
| 778 |  |  |  |  |  |  | __END__ |