File Coverage

blib/lib/TeX/AutoTeX/Process.pm
Criterion Covered Total %
statement 15 214 7.0
branch 0 92 0.0
condition 0 33 0.0
subroutine 5 20 25.0
pod 14 14 100.0
total 34 373 9.1


line stmt bran cond sub pod time code
1             package TeX::AutoTeX::Process;
2              
3             #
4             # $Id: Process.pm,v 1.14.2.7 2011/02/03 03:57:38 thorstens Exp $
5             # $Revision: 1.14.2.7 $
6             # $Source: /cvsroot/arxivlib/arXivLib/lib/TeX/AutoTeX/Process.pm,v $
7             #
8             # $Date: 2011/02/03 03:57:38 $
9             # $Author: thorstens $
10             #
11              
12 2     2   11 use strict;
  2         3  
  2         59  
13             ### use warnings;
14 2     2   9 use Carp;
  2         4  
  2         200  
15              
16             our ($VERSION) = '$Revision: 1.14.2.7 $' =~ m{ \$Revision: \s+ (\S+) }x;
17              
18 2     2   13 use TeX::AutoTeX::Config qw($AUTOTEX_TIMEOUT $DIRECTIVE_FILE);
  2         6  
  2         246  
19 2     2   11 use TeX::AutoTeX::Fileset;
  2         4  
  2         34  
20 2     2   10 use TeX::AutoTeX::File;
  2         4  
  2         5252  
21              
22             sub new {
23 0     0 1   my $class = shift;
24 0           my $self = {
25             log => undef,
26             fileset => undef,
27             stampref => [],
28             use_stamp => 1,
29             hlink_stamp => 1,
30             temp_dir => undef,
31             branch => undef,
32             dvi_flags => [],
33             made_pdf => [],
34             nohypertex => undef,
35             warnings => {},
36             decryption_key => undef,
37             tex_env_path => undef,
38             @_
39             };
40              
41             # Sanity checks...
42 0 0         croak 'No log configuration supplied.' unless defined $self->{log};
43 0 0 0       croak 'Missing site configuration.'
      0        
44             unless (defined $self->{temp_dir}
45             and defined $self->{branch}
46             and defined $self->{tex_env_path});
47 0 0         if (!$self->{fileset}) {
48 0           $self->{fileset} = TeX::AutoTeX::Fileset->new(
49             log => $self->{log},
50             dir => $self->{temp_dir},
51             );
52             }
53 0           bless $self, $class;
54 0           return $self;
55             }
56              
57             sub go {
58 0     0 1   my $self = shift;
59              
60             eval {
61 0           delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; #make %ENV safer, see camel
62             local $SIG{ALRM} = sub {
63 0     0     croak "AutoTeX process timed out after ${AUTOTEX_TIMEOUT}s (" . localtime(), q{)};
64 0           };
65 0           alarm $AUTOTEX_TIMEOUT;
66 0           umask oct 2;
67 0           setpgrp;
68 0           $self->process_files();
69 0           alarm 0; # Cancel the alarm if process_files() returns before $AUTOTEX_TIMEOUT
70 0           1;
71 0 0         } or do {
72 0           local $SIG{TERM} = 'IGNORE';
73 0           kill TERM => -$$;
74             };
75 0           alarm 0; # Cancel the alarm if process_files() died.
76 0           return $@; # true on eval error
77             }
78              
79             sub process_files {
80 0     0 1   my $self = shift;
81              
82 0           $self->clean_times();
83 0           $self->parse_readme();
84 0           $self->process_of_type('TYPE_ENCRYPTED');
85 0           my $log = $self->{log};
86 0           my $tmpdir = $self->{temp_dir};
87             # list files, and build a hash of tex files and assign processing priorities
88 0           my $highest_pri = 0;
89 0           my %priority_value; # stores priority values of all tex-type files
90 0           my @unknowns = (); # names of files with unknown types
91 0           foreach my $fileobj ($self->file_list()) {
92 0           my $filename = $fileobj->{filename};
93 0           my $filetype = $fileobj->filetype_name();
94 0 0         if ( $filetype =~ /(compressed|TAR\sarchive|MULTI_PART_MIME|UUencoded)/i ) {
95 0           $log->error("Package contains '$filename' which is of type '$filetype'. This file should be removed or marked ignore.");
96 0           exit 121;
97             }
98 0 0 0       next if ( -d "$tmpdir/$filename" #TS there shouldn't be any directories in self->file_list()
      0        
99             || $filename eq q{.} # shouldn't happen
100             || $filename eq q{..} # shouldn't happen
101             );
102 0 0         unless ($filename =~ /auto_gen(?:_ps)?\.log/) {
103 0           $log->verbose(" <$filename>\t is of type '$filetype'.");
104 0 0         if ($filetype eq 'unknown'){
105 0           push @unknowns, $filename;
106             }
107             }
108 0           $priority_value{$filename} = $fileobj->assign_tex_priority();
109 0 0         if ($priority_value{$filename} > $highest_pri) {
110 0           $highest_pri = $priority_value{$filename};
111             }
112             }
113              
114 0           my %process_fail_time = (); # times of attempted processing of failures
115 0 0         unless ($highest_pri) {
116 0           $log->verbose('No tex files present, going to hope we can process as a postscript or dvi only package.');
117             } else {
118             # We go through the list of files sorted by priorities in reverse and
119             # lexicographic order (as of perl 5.6 sort is stable, so two succeeding
120             # sorts retain order) attempting to process them until successful.
121 0           my @fileobjects =
122 0           sort {$priority_value{$b->{filename}} <=> $priority_value{$a->{filename}}}
123 0           sort {$a->{filename} cmp $b->{filename}}
124 0           grep {$priority_value{$_->{filename}} > 0}
125             $self->file_list(); #TS: note that this re-scans cwd
126 0           my $successful = 0;
127 0           my $start = time;
128 0           foreach my $fileobj (@fileobjects) {
129 0           my $filename = $fileobj->{filename};
130 0 0         if (! -e "$tmpdir/$filename") {
131 0           $log->verbose("'$filename' already used and removed, no longer in the processing queue.");
132 0           next;
133             }
134 0 0 0       if (!$successful || (stat "$tmpdir/$filename")[8] < $start) { #atime
135 0           $log->verbose(" ~~~~~~~~~~~ Processing file '$filename'");
136 0 0         if ($fileobj->process($self)){ # returns true if unsuccessful
137 0           $process_fail_time{$filename} = time;
138             } else { # processing was successful
139 0 0 0       if ($self->{branch} eq '3' || $self->{branch} =~ m{texlive/}) { # check for generated PDF
140 0           (my $pdffile = $filename) =~ s/(?:\.[^.]*)?$/.pdf/; #TS: note pathologic case "^.something$"
141 0 0 0       if (-s "$tmpdir/$pdffile" && (stat(_))[9] >= $start) { # mtime
142 0           push @{$self->{made_pdf}}, $pdffile;
  0            
143 0 0         if (my $stampref = $self->get_stamp()) {
144 0           $log->verbose("now stamping pdf file '$pdffile' with stamp '$stampref->[0]'");
145 0           require TeX::AutoTeX::StampPDF;
146 0           TeX::AutoTeX::StampPDF::stamp_pdf("$tmpdir/$pdffile", $stampref);
147 0           $log->verbose('stamped pdf file');
148             }
149             }
150             }
151 0           $successful++;
152             }
153             }
154              
155 0 0         if ( -e "$tmpdir/missfont.log" ){
156 0           $log->error('missfont.log present.');
157 0           exit 123;
158             }
159             }
160 0 0         if (!$successful) {
161 0           $log->error('Unable to sucessfully process tex files.');
162 0           exit 125;
163             } else {
164 0           my $junk_warning = q{};
165 0           foreach my $unknown (@unknowns) {
166 0           my $atime = (stat "$tmpdir/$unknown")[8]; # undef if no longer present
167 0 0 0       if ($atime and $atime < $start){ # file hasn't been read since start of process
168 0 0         $junk_warning .= "<$unknown> unrecognized by FileGuess:\n"
169             . ( -T "$tmpdir/$unknown" ? `head "$tmpdir/$unknown"` : 'not an ASCII text file' )
170             . "\n--------------------------------------\n";
171             }
172             }
173 0           foreach my $filename (keys %process_fail_time){
174 0           my $atime = (stat "$tmpdir/$filename")[8];
175 0 0         if ($atime <= $process_fail_time{$filename}){
176             # file hasn't been read since failed attempt at processing it
177             # TS: note that atime = process_fail_time does not conclusively indicate anything
178             # would have to re-set atime after process failure before continuing,
179             # because second granularity is not fine enough.
180 0 0         $junk_warning .= "<$filename> appears to be tex-type, but was neither included nor processable:\n"
181             . ( -T "$tmpdir/$filename" ? `head "$tmpdir/$filename"` : 'not an ASCII text file' )
182             . "\n--------------------------------------\n";
183             }
184             }
185 0 0         if ($junk_warning) {
186 0           $log->verbose("Junk file warnings!\n\n--------------------------------------\n$junk_warning");
187 0           $self->{warnings}{junk_warning} = $junk_warning;
188             }
189             }
190             }
191 0           $self->process_of_type('TYPE_DVI');
192 0 0         if ( -e "$tmpdir/missfont.log" ){
193 0           $log->error('missfont.log present.');
194 0           exit 127;
195             }
196 0 0         if (@{$self->{made_pdf}} > 1) {
  0            
197 0           my $combinedfile = 'xxxpdfpages';
198 0 0 0       if (-e "$tmpdir/$combinedfile.tex" || -e "$tmpdir/$combinedfile.pdf") {
199 0           $combinedfile = "xxxpdfpages_$$";
200             }
201 0   0       open my $PDFPAGES, '>', "$tmpdir/$combinedfile.tex" ||
202             $log->error("couldn't open $combinedfile.tex for writing: $!");
203 0           print {$PDFPAGES} "\\pdfoutput=1\n\\documentclass{article}\n\\usepackage{pdfpages}\n\\begin{document}\n",
  0            
204 0           join(q{}, map { "\\includepdf[pages=-]{$_}\n" } @{$self->{made_pdf}}),
  0            
205             "\\end{document}\n";
206 0 0         close $PDFPAGES || $log->verbose("error closing $combinedfile.tex: $!");
207 0           $log->verbose('creating a combined PDF file out of multiple PDF documents');
208 0           $self->{made_pdf} = [];
209 0 0         if ($self->{fileset}->new_File("$combinedfile.tex")->process($self)) {
210 0           $log->error('failed to create combined PDF file.');
211             } else {
212 0           $self->{made_pdf}->[0] = "$combinedfile.pdf";
213             }
214             }
215 0           return;
216             }
217              
218             sub clean_times {
219 0     0 1   my $self = shift;
220              
221 0 0         opendir(my $DIR, $self->{temp_dir})
222             || $self->{log}->error("Could not read directory '$self->{temp_dir}'.");
223 0           my $now = time;
224 0 0         my $count = map {utime($now, $now, "$self->{temp_dir}/$_")
  0            
225             || $self->{log}->verbose("Couldn't touch $_: $!");}
226 0 0         map {m/^(.*)$/}
227 0           grep {$_ ne q{.} && $_ ne q{..}}
228             readdir $DIR;
229             ### $self->{log}->verbose("Touched $count files and directories.");
230 0           closedir $DIR;
231 0           return 0;
232             }
233              
234             sub process_of_type {
235 0     0 1   my $self = shift;
236 0           my @types = @_;
237              
238 0           my @fileobjects = $self->file_list();
239              
240 0           foreach my $fileobj (@fileobjects) {
241 0           my $t = $fileobj->type();
242 0           foreach my $type (@types) {
243 0 0         if ($t eq $type) {
244 0 0         if ($t eq 'TYPE_DVI') {
245 0           $fileobj->set_dvi_flags($self->dvi_flags_tostring());
246             }
247 0           $fileobj->process($self);
248             }
249             }
250             }
251 0           return 0;
252             }
253              
254             sub file_list {
255 0     0 1   my $self = shift;
256 0 0         opendir(my $DIR, $self->{temp_dir})
257             || $self->{log}->error('Could not read directory.');
258 0           my @fileobjects = map {$self->{fileset}->new_File($_)}
  0            
259 0           grep {-f "$self->{temp_dir}/$_"} # no (sub-)directories, symlinks, etc
260             readdir $DIR;
261 0           closedir $DIR;
262 0           return @fileobjects;
263             }
264              
265             sub parse_readme {
266 0     0 1   my $self = shift;
267 0 0         return unless -e "$self->{temp_dir}/$DIRECTIVE_FILE";
268 0 0         open(my $README, '<', "$self->{temp_dir}/$DIRECTIVE_FILE")
269             || $self->{log}->error("Couldn't open $DIRECTIVE_FILE");
270 0           while (my $line = <$README>) {
271 0 0         next if $line =~ /^[#%]/; # allow shell-style and tex-style comments
272 0           chomp $line;
273 0           $line =~ s/\s+$//;
274 0           $line =~ s/^\s+//;
275 0           my ($token, $type) = split /\s+/, $line, 2;
276 0 0         if (!defined $token) {
277 0           $self->{log}->verbose("$DIRECTIVE_FILE PARSER: I don't get this: $line");
278 0           next;
279             }
280 0 0         if ($token eq 'nohypertex') {
281 0           $self->{nohypertex} = 1;
282 0           $self->{log}->verbose('nohypertex: switching off hyperlinks');
283 0           next;
284             }
285 0 0         if ($token eq 'nostamp') {
286 0           $self->{use_stamp} = 0;
287 0           $self->{log}->verbose('nostamp: will not stamp PostScript or PDF');
288 0           next;
289             }
290 0 0         if (!defined $type) {
291 0           $self->{log}->verbose("$DIRECTIVE_FILE PARSER: I don't get this: '$line'");
292 0           next;
293             }
294              
295 0           my $file = $self->{fileset}->new_File($token);
296              
297 0           my %flagtypes = (
298             landscape => "landscape: will use landscape mode for '$token'\n",
299             keepcomments => "dvips flag '-K0' will be used for '$token'\n",
300             toplevelfile => "toplevelfile: will use '$token' as parent\n" ,
301             );
302              
303              
304 0 0         if (my $message = $flagtypes{$type}) {
305 0           $file->set_flag($type);
306 0           $self->{log}->verbose($message);
307 0           next;
308             }
309              
310 0 0         if ($type eq 'fontmap') {
311 0 0         if ($token =~ /^\+?([a-z]+\.map)$/i) {
312 0           $self->add_dvi_flag("-u +./$1");
313 0           $self->{log}->verbose("dvips flag '-u +./$1' will be used");
314             }
315 0           $file->type_override('include');
316 0           next;
317             }
318             # if type didn't trigger any other action
319 0           $file->type_override($type);
320             }
321 0 0         close($README) || $self->{log}->verbose("warning: couldn't close directive file: $!");
322 0           return;
323             }
324              
325             sub set_dvi_flags {
326 0     0 1   my $self = shift;
327 0           @{$self->{dvi_flags}} = @_;
  0            
328             }
329              
330             sub dvi_flags_tostring {
331 0     0 1   my $self = shift;
332 0           return join q{ }, @{$self->{dvi_flags}};
  0            
333             }
334              
335             sub add_dvi_flag {
336 0     0 1   my $self = shift;
337 0   0       my $argstring = shift || carp 'no arguments provided to add_dvi_flag()';
338 0           push @{$self->{dvi_flags}}, $argstring;
  0            
339             }
340              
341             sub set_use_stamp {
342 0     0 1   my $self = shift;
343 0           $self->{use_stamp} = shift;
344             }
345              
346             sub toggle_hlink_stamp {
347 0     0 1   my $self = shift;
348 0           $self->{hlink_stamp} = 1^$self->{hlink_stamp};
349             }
350              
351             sub get_stamp {
352 0     0 1   my $self = shift;
353 0 0         if ($self->{use_stamp}) {
354 0           return $self->{stampref};
355             }
356 0           return;
357             }
358              
359             sub get_warning {
360 0     0 1   my ($self, $type) = @_;
361 0           return($self->{warnings}{$type});
362             }
363              
364             1;
365              
366             __END__