summaryrefslogtreecommitdiff
path: root/support/latexmk/latexmk.pl
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/latexmk/latexmk.pl
Initial commit
Diffstat (limited to 'support/latexmk/latexmk.pl')
-rwxr-xr-xsupport/latexmk/latexmk.pl9968
1 files changed, 9968 insertions, 0 deletions
diff --git a/support/latexmk/latexmk.pl b/support/latexmk/latexmk.pl
new file mode 100755
index 0000000000..7dd5dc35e3
--- /dev/null
+++ b/support/latexmk/latexmk.pl
@@ -0,0 +1,9968 @@
+#!/usr/bin/env perl
+
+#!!!!!!!!!!!!!!!!!!!????????? CLEAN UP INITIALIZATION
+#!!! See all FIX items.
+
+# Perhaps add subroutine rdb_change_active( (rules to deactivate), (rules to activate))
+
+# ?? Still need to fix bcf error issue.
+# Don't keep looping after error
+# pvc: Only re-run on USER FILE CHANGE.
+# See # ??????? BCF
+
+
+#!!!!!!!!??? Check @pwd_log
+
+
+# !!!!!!!!!! Don't forget to document $silence_logfile_warnings.!!!
+
+# N.B. !!!!!!!!!!! See 17 July 2012 comments !!!!!!!!!!!!!!!!!!
+
+# On a UNIX-like system, the above enables latexmk to run independently
+# of the location of the perl executable. This line relies on the
+# existence of the program /usr/bin/env
+# If there is a problem for any reason, you can replace the first line of
+# this file by:
+
+#!/usr/bin/perl -w
+
+# with the path of the perl executable adjusted for your system.
+
+use warnings;
+
+# Delete #??!! when working
+
+# See ?? <===============================
+
+## ?? Issues with clean-up
+## List of aux files deleted is those read, not those generated.
+## Other files are generated by (pdf)latex; should they be deleted?
+## (I have hooks for this).
+
+
+
+#=======================================
+
+#?? Force mode doesn't appear to do force (if error in latex file)
+#??? Get banner back in.
+#?? CORRECT DIAGNOSTICS ON CHANGED FILES IF THEY DIDN'T EXIST BEFORE
+#?? Further corrections to deal with disappeared source files for custom dependencies.
+# Message repeatedly appears about remake when source file of cusdep doesn't exist.
+#?? logfile w/o fdb file: don't set changed file, perhaps for generated exts.
+# Reconsider
+#?? Do proper run-stuff for bibtex, makeindex, cus-deps. OK I think
+# Parse and correctly find ist files
+
+
+# ATTEMPT TO ALLOW FILENAMES WITH SPACES:
+# (as of 1 Apr 2006, and then 14 Sep. 2007)
+
+# Problems:
+# A. Quoting filenames will not always work.
+# a. Under UNIX, quotes are legal in filenames, so when PERL
+# directly runs a binary, a quoted filename will be treated as
+# as a filename containing a quote character. But when it calls
+# a shell, the quotes are handled by the shell as quotes.
+# b. Under MSWin32, quotes are illegal filename characters, and tend
+# to be handled correctly.
+# c. But under cygwin, results are not so clear (there are many
+# combinations: native v. cygwin perl, native v cygwin programs
+# NT v. unix scripts, which shell is called.
+# B. TeX doesn't always handle filenames with spaces gracefully.
+# a. UNIX/LINUX: The version on gluon2 Mar 31, 2006 to Sep. 2007)
+# doesn't handle them at all. (TeX treats space as separator.)
+# b. At least some later versions actually do (Brad Miller e-mail,
+# Sep. 2007).
+# c. fptex [[e-TeXk, Version 3.141592-2.1 (Web2c 7.5.2)] does, on
+# my MSWin at home. In \input the filename must be in quotes.
+# d. Bibtex [BibTeX (Web2c 7.5.2) 0.99c on my MSWin system at home,
+# Sep. 2007] does not allow names of bibfiles to have spaces.
+# C. =====> Using the shell for command lines is not safe, since special
+# characters can cause lots of mayhem.
+# It will therefore be a good idea to sanitize filenames.
+#
+# I've sanitized all calls out:
+# a. system and exec use a single argument, which forces
+# use of shell, under all circumstances
+# Thus I can safely use quotes on filenames: They will be handled by
+# the shell under UNIX, and simply passed on to the program under MSWin32.
+# b. I reorganized Run, Run_Detached to use single command line
+# c. All calls to Run and Run_Detached have quoted filenames.
+# d. So if a space-free filename with wildcards is given on latexmk's
+# command line, and it globs to space-containing filename(s), that
+# works (fptex on home computer, native NT tex)
+# e. ====> But globbing fails: the glob function takes space as filename
+# separator. ====================
+
+#================= TO DO ================
+#
+# 1. See ?? ESPECIALLY $MSWin_fudge_break
+# 2. Check fudged conditions in looping and make_files
+# 3. Should not completely abort after a run that ends in failure from latex
+# Missing input files (including via custom dependency) should be checked for
+# a change in status
+# If sources for missing files from custom dependency
+# are available, then do a rerun
+# If sources of any kind become available rerun (esp. for pvc)
+# rerun
+# Must parse log_file after unsuccessful run of latex: it may give
+# information about missing files.
+# 4. Check file of bug reports and requests
+# 5. Rationalize bibtex warnings and errors. Two almost identical routines.
+# Should 1. Use single routine
+# 2. Convert errors to failure only in calling routine
+# 3. Save first warning/error.
+
+# ?? Use of generated_exts arrays and hashes needs rationalization
+
+# To do:
+# Rationalize again handling of include files.
+# Now I use kpsewhich to do searches, if file not found
+# (How do I avoid getting slowed down too much?)
+# Document the assumptions at each stage of processing algorithm.
+# Option to restart previewer automatically, if it dies under -pvc
+# Test for already running previewer gets wrong answer if another
+# process has the viewed file in its command line
+
+## Copyright John Collins 1998-2019
+## (username jcc8 at node psu.edu)
+## (and thanks to David Coppit (username david at node coppit.org)
+## for suggestions)
+## Copyright Evan McLean
+## (modifications up to version 2)
+## Copyright 1992 by David J. Musliner and The University of Michigan.
+## (original version)
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+##
+##
+##
+## NEW FEATURES, since v. 2.0:
+## 1. Correct algorithm for deciding how many times to run latex:
+## based on whether source file(s) change between runs
+## 2. Continuous preview works, and can be of ps file or dvi file
+## 3. pdf creation by pdflatex possible
+## 4. Defaults for commands are OS dependent.
+## 5. Parsing of log file instead of source file is used to
+## obtain dependencies, by default.
+##
+## Modification log from 9 Dec 2011 onwards in detail
+##
+## 12 Jan 2012 STILL NEED TO DOCUMENT some items below
+##
+## 28 Jun 2019 John Collins Try to deal with log file parsing problems
+## V. 4.65
+## 21 May 2019 John Collins Fix incorrect listings by -rules and by -deps
+## 21 May 2019 John Collins V. 4.64a.
+## 20,21 May 2019 John Collins Fix problem with not always running dvipdf,
+## dvips, xdvpdf because of missing information
+## on source files. Do this by adding a
+## source_rule hash to each rule and adding a
+## pass-based criterion for out-of-dateness
+## 20 May 2019 John Collins V. 4.64.
+## 17 Mar 2019 John Collins V. 4.63c.
+## 18 Mar 2019 John Collins Update copyright date
+## 17 Mar 2019 John Collins Further test for problems when linking rules
+## 17 Mar 2019 John Collins V. 4.63b.
+## 17 Mar 2019 John Collins Fix problem revealed by bibtopic package
+## 15 Mar 2019 John Collins Corrections
+## 14 Mar 2019 John Collins Version to 4.63a
+## 12-14 Mar 2019 John Collins Jobname has placeholder
+## 8-11 Mar 2019 John Collins Correct aux-dir bugs
+## 8 Mar 2019 John Collins Version to 4.63
+## 1 Jan 2019 John Collins Correct bug in output switching code
+## 31 Dec 2018 John Collins Correct bug that with $cleanup_includes_generated
+## -c mode deletes final output files (e.g., .dvi, .pdf).
+## 13 Dec 2018 John Collins Fixes, cleanup
+## 12 Dec 2018 John Collins Fix rdb_read
+## 11 Dec 2018 John Collins For output-switch: Reorganize initialization
+## Remove possibility of more than one primary.
+## 1 Dec 2018 John Collins Clean up some code.
+## 30 Nov 2018 John Collins Allow any characters in directory part of tex
+## filename when cd mode on. (Still standard
+## set of disallowed characters applies basename.)
+## 21 Nov 2018 John Collins Infrastructure finished, I think, for active flag
+## Start on output switch
+## 16 Nov 2018 John Collins Remove in non-diagnostics mode a warning about
+## rule not in use in this session that is given
+## when reading an fdb_latexmk file.
+## 2 Nov 2018 John Collins Start implementing active flag for rules.
+## 1 Nov 2018 John Collins Fix finding of missing files that are relative to aux_dir
+## 29-31 Oct 2018 John Collins Some cleanup.
+## 27,28 Oct 2018 John Collins Fix bug that in -pvc mode, after an error in
+## certain rules, new rounds of compilation
+## are triggered because of changes in generated
+## files, instead of only by later user-generated
+## changes.
+## Improve linking of rules.
+## 25 Oct 2018 John Collins V. 4.62
+## Clean up can remove (empty) directories
+## 25 Oct 2018 John Collins Fix definition of clean up substitution for %R
+## so that something with intermediate %R works,
+## as in 'pythontex-files-%R/*'.
+##
+## 1998-2018, John Collins. Many improvements and fixes.
+## See CHANGE-log.txt for full list, and CHANGES for summary
+##
+## Modified by Evan McLean (no longer available for support)
+## Original script (RCS version 2.3) called "go" written by David J. Musliner
+##
+##-----------------------------------------------------------------------
+
+
+## Explicit exit codes:
+## 10 = bad command line arguments
+## 11 = file specified on command line not found
+## or other file not found
+## 12 = failure in some part of making files
+## 13 = error in initialization file
+## 20 = probable bug
+## or retcode from called program.
+
+$my_name = 'latexmk';
+$My_name = 'Latexmk';
+$version_num = '4.65';
+$version_details = "$My_name, John Collins, 18 June 2019";
+
+use Config;
+use File::Basename;
+use File::Copy;
+use File::Spec;
+
+# If possible, use better glob, which does not use space as item separator.
+# It's either File::Glob::bsd_glob or File::Glob::glob
+# The first does not exist in old versions of Perl, while the second
+# is deprecated in more recent versions and will be removed
+$have_bsd_glob = 0;
+sub my_glob {
+ if ($have_bsd_glob) { return bsd_glob( $_[0] ); }
+ else { return glob( $_[0] ); }
+}
+use File::Glob;
+if ( eval{ File::Glob->import('bsd_glob'); 1; } ) {
+ # Success in importing bsd_glob
+ $have_bsd_glob = 1;
+}
+elsif ( eval{ File::Glob->import('glob'); 1; } ) {
+ warn "$My_name: I could not import File::Glob:bsd_glob, probably because your\n",
+ " Perl is too old. I have arranged to use the deprecated File::Glob:glob\n",
+ " instead.\n",
+ " WARNING: It may malfunction on clean up operation on filenames containing\n",
+ " spaces.\n";
+ $have_bsd_glob = 0;
+}
+else {
+ die "Could not import 'File::Glob:bsd_glob' or 'File::Glob:glob'\n";
+}
+
+use File::Path 2.08 qw( make_path );
+use FileHandle;
+use File::Find;
+use List::Util qw( max );
+use Cwd; # To be able to change cwd
+use Cwd "chdir"; # Ensure $ENV{PWD} tracks cwd
+use Digest::MD5;
+
+#use strict;
+
+# The following variables are assigned once and then used in symbolic
+# references, so we need to avoid warnings 'name used only once':
+use vars qw( $dvi_update_command $ps_update_command $pdf_update_command );
+
+# Translation of signal names to numbers and vv:
+%signo = ();
+@signame = ();
+if ( defined $Config{sig_name} ) {
+ $i = 0;
+ foreach $name (split('\s+', $Config{sig_name})) {
+ $signo{$name} = $i;
+ $signame[$i] = $name;
+ $i++;
+ }
+}
+else {
+ warn "Something wrong with the perl configuration: No signals?\n";
+}
+
+
+# Line length in log file that indicates wrapping.
+# This number EXCLUDES line-end characters, and is one-based.
+# It is the parameter max_print_line in the TeX program. (tex.web)
+$log_wrap = 79;
+
+#########################################################################
+## Default parsing and file-handling settings
+
+## Array of reg-exps for patterns in log-file for file-not-found
+## Each item is the string in a regexp, without the enclosing slashes.
+## First parenthesized part is the filename.
+## Note the need to quote slashes and single right quotes to make them
+## appear in the regexp.
+## Add items by push, e.g.,
+## push @file_not_found, '^No data file found `([^\\\']*)\\\'';
+## will give match to line starting "No data file found `filename'"
+@file_not_found = (
+ '^No file\\s*(.*)\\.$',
+ '^\\! LaTeX Error: File `([^\\\']*)\\\' not found\\.',
+ '.*?:\\d*: LaTeX Error: File `([^\\\']*)\\\' not found\\.',
+ '^LaTeX Warning: File `([^\\\']*)\\\' not found',
+ '^Package .* [fF]ile `([^\\\']*)\\\' not found',
+ '^Package .* No file `([^\\\']*)\\\'',
+ 'Error: pdflatex \(file ([^\)]*)\): cannot find image file',
+ ': File (.*) not found:\s*$',
+ '! Unable to load picture or PDF file \\\'([^\\\']+)\\\'.',
+);
+
+# Characters that we won't allow in the name of a TeX file.
+# Notes: Some are disallowed by TeX itself.
+# '\' results in TeX macro expansion
+# '$' results in possible variable substitution by kpsewhich called from tex.
+# '"' gets special treatment.
+# See subroutine test_fix_texnames and its call for their use.
+$illegal_in_texname = "\x00\t\f\n\r\$%\\~\x7F";
+
+
+## Hash mapping file extension (w/o period, e.g., 'eps') to a single regexp,
+# whose matching by a line in a file with that extension indicates that the
+# line is to be ignored in the calculation of the hash number (md5 checksum)
+# for the file. Typically used for ignoring datestamps in testing whether
+# a file has changed.
+# Add items e.g., by
+# $hash_calc_ignore_pattern{'eps'} = '^%%CreationDate: ';
+# This makes the hash calculation for an eps file ignore lines starting with
+# '%%CreationDate: '
+# ?? Note that a file will be considered changed if
+# (a) its size changes
+# or (b) its hash changes
+# So it is useful to ignore lines in the hash calculation only if they
+# are of a fixed size (as with a date/time stamp).
+%hash_calc_ignore_pattern =();
+
+
+# Specification of templates for extra rules.
+# See subroutine rdb_initialize_rules for examples of rule templates, and
+# how they get used to construct rules.
+# (Documentation obviously needs to be improved!)
+%extra_rule_spec = ();
+
+
+# Hooks for customized extra processing on aux files. The following
+# variable is an array of references to function. Each function is
+# invoked in turn when a line of an aux file is processed (if none
+# of the built-in actions have been done). On entry to the function,
+# the following variables are set:
+# $_ = current line of aux file
+# $rule = name of rule during the invocation of which, the aux file
+# was supposed to have been generated.
+@aux_hooks = ();
+
+#########################################################################
+## Default document processing programs, and related settings,
+## These are mostly the same on all systems.
+## Most of these variables represents the external command needed to
+## perform a certain action. Some represent switches.
+
+
+## Which TeX distribution is being used
+## E.g., "MiKTeX 2.9", "TeX Live 2018"
+## "" means not determined. Obtain from first line of .log file.
+$tex_distribution = '';
+
+&std_tex_cmds;
+
+# Possible code to execute by *latex before inputting source file.
+# Not used by default.
+$pre_tex_code = '';
+
+## Default switches:
+$latex_default_switches = '';
+$pdflatex_default_switches = '';
+$lualatex_default_switches = '';
+ # Note that xelatex is used to give xdv file, not pdf file, hence
+ # we need the -no-pdf option.
+$xelatex_default_switches = '-no-pdf';
+
+## Switch(es) to make them silent:
+$latex_silent_switch = '-interaction=batchmode';
+$pdflatex_silent_switch = '-interaction=batchmode';
+$lualatex_silent_switch = '-interaction=batchmode';
+$xelatex_silent_switch = '-interaction=batchmode';
+
+%input_extensions = ();
+# %input_extensions maps primary_rule_name to pointer to hash of file extensions
+# used for extensionless files specified in the source file by constructs
+# like \input{file} \includegraphics{file}
+set_input_ext( 'latex', 'tex', 'eps' );
+set_input_ext( 'pdflatex', 'tex', 'jpg', 'pdf', 'png' );
+$input_extensions{lualatex} = $input_extensions{pdflatex};
+$input_extensions{xelatex} = $input_extensions{pdflatex};
+# Save these values as standards to be used when switching output,
+# i.e., when actual primary rule differs from standard.
+%standard_input_extensions = %input_extensions;
+
+%allowed_output_ext = ( ".dvi" => 1, ".xdv" => 1, ".pdf" => 1 );
+# Extensions of files preserved when clean up is by -c rather than -C:
+%small_cleanup_preserved_exts = ();
+foreach ( 'dvi', 'dviF', 'ps', 'psF', 'pdf', 'synctex.gz', 'xdv' ) {
+ $small_cleanup_preserved_exts{$_} = 1;
+}
+
+
+# Information about options to latex and pdflatex that latexmk will simply
+# pass through to (pdf)latex
+# Option without arg. maps to itself.
+# Option with arg. maps the option part to the full specification
+# e.g., -kpathsea-debug => -kpathsea-debug=NUMBER
+%allowed_latex_options = ();
+%allowed_latex_options_with_arg = ();
+foreach (
+ #####
+ # TeXLive options
+ "-draftmode switch on draft mode (generates no output PDF)",
+ "-enc enable encTeX extensions such as \\mubyte",
+ "-etex enable e-TeX extensions",
+ "-file-line-error enable file:line:error style messages",
+ "-no-file-line-error disable file:line:error style messages",
+ "-fmt=FMTNAME use FMTNAME instead of program name or a %& line",
+ "-halt-on-error stop processing at the first error",
+ "-interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/\n".
+ " scrollmode/errorstopmode)",
+ "-ipc send DVI output to a socket as well as the usual\n".
+ " output file",
+ "-ipc-start as -ipc, and also start the server at the other end",
+ "-kpathsea-debug=NUMBER set path searching debugging flags according to\n".
+ " the bits of NUMBER",
+ "-mktex=FMT enable mktexFMT generation (FMT=tex/tfm/pk)",
+ "-no-mktex=FMT disable mktexFMT generation (FMT=tex/tfm/pk)",
+ "-mltex enable MLTeX extensions such as \charsubdef",
+ "-output-comment=STRING use STRING for DVI file comment instead of date\n".
+ " (no effect for PDF)",
+ "-output-format=FORMAT use FORMAT for job output; FORMAT is `dvi\" or `pdf\"",
+ "-parse-first-line enable parsing of first line of input file",
+ "-no-parse-first-line disable parsing of first line of input file",
+ "-progname=STRING set program (and fmt) name to STRING",
+ "-shell-escape enable \\write18{SHELL COMMAND}",
+ "-no-shell-escape disable \\write18{SHELL COMMAND}",
+ "-shell-restricted enable restricted \\write18",
+ "-src-specials insert source specials into the DVI file",
+ "-src-specials=WHERE insert source specials in certain places of\n".
+ " the DVI file. WHERE is a comma-separated value\n".
+ " list: cr display hbox math par parend vbox",
+ "-synctex=NUMBER generate SyncTeX data for previewers if nonzero",
+ "-translate-file=TCXNAME use the TCX file TCXNAME",
+ "-8bit make all characters printable by default",
+
+ #####
+ # MikTeX options not in TeXLive
+ "-alias=app pretend to be app",
+ "-buf-size=n maximum number of characters simultaneously present\n".
+ " in current lines",
+ "-c-style-errors C-style error messages",
+ "-disable-installer disable automatic installation of missing packages",
+ "-disable-pipes disable input (output) from (to) child processes",
+ "-disable-write18 disable the \\write18{command} construct",
+ "-dont-parse-first-line disable checking whether the first line of the main\n".
+ " input file starts with %&",
+ "-enable-enctex enable encTeX extensions such as \\mubyte",
+ "-enable-installer enable automatic installation of missing packages",
+ "-enable-mltex enable MLTeX extensions such as \charsubdef",
+ "-enable-pipes enable input (output) from (to) child processes",
+ "-enable-write18 fully enable the \\write18{command} construct",
+ "-error-line=n set the width of context lines on terminal error\n".
+ " messages",
+ "-extra-mem-bot=n set the extra size (in memory words) for large data\n".
+ " structures",
+ "-extra-mem-top=n set the extra size (in memory words) for chars,\n".
+ " tokens, et al",
+ "-font-max=n set the maximum internal font number",
+ "-font-mem-size=n set the size, in TeX memory words, of the font memory",
+ "-half-error-line=n set the width of first lines of contexts in terminal\n".
+ " error messages",
+ "-hash-extra=n set the extra space for the hash table of control\n".
+ " sequences",
+ "-job-time=file set the time-stamp of all output files equal to\n".
+ " file's time-stamp",
+ "-main-memory=n change the total size (in memory words) of the main\n".
+ " memory array",
+ "-max-in-open=n set the maximum number of input files and error\n".
+ " insertions that can be going on simultaneously",
+ "-max-print-line=n set the width of longest text lines output",
+ "-max-strings=n set the maximum number of strings",
+ "-nest-size=n set the maximum number of semantic levels\n".
+ " simultaneously active",
+ "-no-c-style-errors standard error messages",
+ "-param-size=n set the the maximum number of simultaneous macro\n".
+ " parameters",
+ "-pool-size=n set the maximum number of characters in strings",
+ "-record-package-usages=file record all package usages and write them into\n".
+ " file",
+ "-restrict-write18 partially enable the \\write18{command} construct",
+ "-save-size=n set the the amount of space for saving values\n".
+ " outside of current group",
+ "-stack-size=n set the maximum number of simultaneous input sources",
+ "-string-vacancies=n set the minimum number of characters that should be\n".
+ " available for the user's control sequences and font\n".
+ " names",
+ "-tcx=name process the TCX table name",
+ "-time-statistics show processing time statistics",
+ "-trace enable trace messages",
+ "-trace=tracestreams enable trace messages. The tracestreams argument is\n".
+ " a comma-separated list of trace stream names",
+ "-trie-size=n set the amount of space for hyphenation patterns",
+ "-undump=name use name as the name of the format to be used,\n".
+ " instead of the name by which the program was\n".
+ " called or a %& line.",
+
+ #####
+ # Options passed to (pdf)latex that have special processing by latexmk,
+ # so they are commented out here.
+ #-jobname=STRING set the job name to STRING
+ #-aux-directory=dir Set the directory dir to which auxiliary files are written
+ #-output-directory=DIR use existing DIR as the directory to write files in
+ #-quiet
+ #-recorder enable filename recorder
+ #
+ # Options with different processing by latexmk than (pdf)latex
+ #-help
+ #-version
+ #
+ # Options NOT used by latexmk
+ #-includedirectory=dir prefix dir to the search path
+ #-initialize become the INI variant of the compiler
+ #-ini be pdfinitex, for dumping formats; this is implicitly
+ # true if the program name is `pdfinitex'
+) {
+ if ( /^([^\s=]+)=/ ) {
+ $allowed_latex_options_with_arg{$1} = $_;
+ }
+ elsif ( /^([^\s=]+)\s/ ) {
+ $allowed_latex_options{$1} = $_;
+ }
+ else {
+ $allowed_latex_options{$_} = $_;
+ }
+}
+
+# Arrays of options that will be added to latex and pdflatex.
+# These need to be stored until after the command line parsing is finished,
+# in case the values of $latex and/or $pdflatex change after an option
+# is added.
+@extra_latex_options = ();
+@extra_pdflatex_options = ();
+@extra_lualatex_options = ();
+@extra_xelatex_options = ();
+
+
+## Command to invoke biber & bibtex
+$biber = 'biber %O %B';
+$bibtex = 'bibtex %O %B';
+# Switch(es) to make biber & bibtex silent:
+$bibtex_fudge = 1; # Use hack to get bibtex working in old versions that
+ # don't handle output-directory.
+$biber_silent_switch = '--onlylog';
+$bibtex_silent_switch = '-terse';
+$bibtex_use = 1; # Whether to actually run bibtex to update bbl files.
+ # This variable is also used in deciding whether to
+ # delete bbl files in clean up operations.
+ # 0: Never run bibtex.
+ # Do NOT delete bbl files on clean up.
+ # 1: Run bibtex only if the bibfiles exists
+ # according to kpsewhich, and the bbl files
+ # appear to be out-of-date.
+ # Do NOT delete bbl files on clean up.
+ # 1.5: Run bibtex only if the bibfiles exists
+ # according to kpsewhich, and the bbl files
+ # appear to be out-of-date.
+ # Only delete bbl files on clean up if bibfiles exist.
+ # 2: Run bibtex when the bbl files are out-of-date
+ # Delete bbl files on clean up.
+ #
+ # In any event bibtex is only run if the log file
+ # indicates that the document uses bbl files.
+
+## Command to invoke makeindex
+$makeindex = 'makeindex %O -o %D %S';
+# Switch(es) to make makeinex silent:
+$makeindex_silent_switch = '-q';
+
+## Command to convert dvi file to pdf file directly:
+$dvipdf = 'dvipdf %O %S %D';
+# N.B. Standard dvipdf runs dvips and gs with their silent switch, so for
+# standard dvipdf $dvipdf_silent_switch is unneeded, but innocuous.
+# But dvipdfmx can be used instead, and it has a silent switch (-q).
+# So implementing $dvipdf_silent_switch is useful.
+
+$dvipdf_silent_switch = '-q';
+
+## Command to convert dvi file to ps file:
+$dvips = 'dvips %O -o %D %S';
+## Command to convert dvi file to ps file in landscape format:
+$dvips_landscape = 'dvips -tlandscape %O -o %D %S';
+# Switch(es) to get dvips to make ps file suitable for conversion to good pdf:
+# (If this is not used, ps file and hence pdf file contains bitmap fonts
+# (type 3), which look horrible under acroread. An appropriate switch
+# ensures type 1 fonts are generated. You can put this switch in the
+# dvips command if you prefer.)
+$dvips_pdf_switch = '-P pdf';
+# Switch(es) to make dvips silent:
+$dvips_silent_switch = '-q';
+
+## Command to convert ps file to pdf file:
+$ps2pdf = 'ps2pdf %O %S %D';
+
+## Command to convert xdv file to pdf file
+$xdvipdfmx = 'xdvipdfmx -o %D %O %S';
+$xdvipdfmx_silent_switch = '-q';
+
+
+## Command to search for tex-related files
+$kpsewhich = 'kpsewhich %S';
+
+## Command to run make:
+$make = 'make';
+
+##Printing:
+$print_type = 'auto'; # When printing, print the postscript file.
+ # Possible values: 'dvi', 'ps', 'pdf', 'auto', 'none'
+ # 'auto' ==> set print type according to the printable
+ # file(s) being made: priority 'ps', 'pdf', 'dvi'
+
+## Which treatment of default extensions and filenames with
+## multiple extensions is used, for given filename on
+## tex/latex's command line? See sub find_basename for the
+## possibilities.
+## Current tex's treat extensions like UNIX teTeX:
+$extension_treatment = 'unix';
+
+# Viewers. These are system dependent, so default to none:
+$pdf_previewer = $ps_previewer = $ps_previewer_landscape = $dvi_previewer = $dvi_previewer_landscape = "NONE";
+
+$dvi_update_signal = undef;
+$ps_update_signal = undef;
+$pdf_update_signal = undef;
+
+$dvi_update_command = undef;
+$ps_update_command = undef;
+$pdf_update_command = undef;
+
+$allow_subdir_creation = 1;
+
+$new_viewer_always = 0; # If 1, always open a new viewer in pvc mode.
+ # If 0, only open a new viewer if no previous
+ # viewer for the same file is detected.
+
+# Commands for use in pvc mode for compiling, success, warnings, and failure;
+# they default to empty, i.e., not to use:
+$compiling_cmd = $success_cmd = $warning_cmd = $failure_cmd = "";
+
+# Commands for printing are highly system dependent, so default to NONE:
+$lpr = 'NONE $lpr variable is not configured to allow printing of ps files';
+$lpr_dvi = 'NONE $lpr_dvi variable is not configured to allow printing of dvi files';
+$lpr_pdf = 'NONE $lpr_pdf variable is not configured to allow printing of pdf files';
+
+
+# The $pscmd below holds a **system-dependent** command to list running
+# processes. It is used to find the process ID of the viewer looking at
+# the current output file. The output of the command must include the
+# process number and the command line of the processes, since the
+# relevant process is identified by the name of file to be viewed.
+# Its use is not essential.
+$pscmd = 'NONE $pscmd variable is not configured to detect running processes';
+$pid_position = -1; # offset of PID in output of pscmd.
+ # Negative means I cannot use ps
+
+
+$quote_filenames = 1; # Quote filenames in external commands
+
+$del_dir = ''; # Directory into which cleaned up files are to be put.
+ # If $del_dir is '', just delete the files
+
+@rc_system_files = ();
+
+#########################################################################
+
+################################################################
+## Special variables for system-dependent fudges, etc.
+$log_file_binary = 0; # Whether to treat log file as binary
+ # Normally not, since the log file SHOULD be pure text.
+ # But Miktex 2.7 sometimes puts binary characters
+ # in it. (Typically in construct \OML ... after
+ # overfull box with mathmode.)
+ # Sometimes there is ctrl/Z, which is not only non-text,
+ # but is end-of-file marker for MS-Win in text mode.
+
+$MSWin_fudge_break = 1; # Give special treatment to ctrl/C and ctrl/break
+ # in -pvc mode under MSWin
+ # Under MSWin32 (at least with perl 5.8 and WinXP)
+ # when latexmk is running another program, and the
+ # user gives ctrl/C or ctrl/break, to stop the
+ # daughter program, not only does it reach
+ # the daughter, but also latexmk/perl, so
+ # latexmk is stopped also. In -pvc mode,
+ # this is not normally desired. So when the
+ # $MSWin_fudge_break variable is set,
+ # latexmk arranges to ignore ctrl/C and
+ # ctrl/break during processing of files;
+ # only the daughter programs receive them.
+ # This fudge is not applied in other
+ # situations, since then having latexmk also
+ # stopping because of the ctrl/C or
+ # ctrl/break signal is desirable.
+ # The fudge is not needed under UNIX (at least
+ # with Perl 5.005 on Solaris 8). Only the
+ # daughter programs receive the signal. In
+ # fact the inverse would be useful: In
+ # normal processing, as opposed to -pvc, if
+ # force mode (-f) is set, a ctrl/C is
+ # received by a daughter program does not
+ # also stop latexmk. Under tcsh, we get
+ # back to a command prompt, while latexmk
+ # keeps running in the background!
+
+## Substitute backslashes in file and directory names for
+## MSWin command line
+$MSWin_back_slash = 1;
+
+## Separator of elements in search_path. Default is unix value
+$search_path_separator = ':';
+
+
+# Directory for temporary files. Default to current directory.
+$tmpdir = ".";
+
+
+# When the aux_dir is on a network share (or the like), its system
+# time may differ from the system time on which latexmk is running.
+# This complicates the tests of whether particular files have been
+# made in a current run of a program or are left over from a previous
+# run. One test, which is needed under some situations, is that a
+# file was made on a previous run when the files modification time is
+# less than the system time when the program is started. (See
+# subroutine test_gen_file; this is only needed in a couple of
+# situations.) The comparison between file and system times must be
+# corrected if there is an offset between system times on the computer
+# running latexmk and the computer hosting the file system containing
+# aux_dir. The offset is measured in subroutine get_filetime_offset
+# by writing a temporary file; the test only needs to be done once.
+#
+# The following variables are used. Since the system-independent
+# values of system and file time are only accurate to a second (or 2
+# seconds for FAT file systems), the offset is also accurate only to a
+# second or two. So thresholds are needed below which differences
+# are insignificant.
+#
+# Note that the making or not making of a file is controlled by the
+# state of the document being compiled and by latexmk's configuration.
+# So a file that is left over from a previous run and not overwritten
+# on the current run will have a file time at least many seconds less
+# than the current time, corresponding to the time scale for a human
+# run-edit-run cycle.
+#
+$filetime_offset_measured = 0; # Measurement not yet done.
+$filetime_offset = 0; # Filetime relative to system time.
+$filetime_causality_threshold = 5; # Threshold for detection of left-over file.
+ # Should be non-negative always, and should
+ # be bigger than 2 secs if a remote
+ # filesystem or network share is used.
+$filetime_offset_report_threshold = 30; # Threshold beyond which filetime offsets
+ # are reported; large offsets indicate
+ # incorrect system time on at least one system.
+
+
+################################################################
+
+
+# System-dependent overrides:
+# Currently, the cases I have tests for are: MSWin32, cygwin, linux and
+# darwin, msys, with the main complications being for MSWin32 and cygwin.
+# Further special treatment may also be useful for MSYS (for which $^O reports
+# "msys"). This is another *nix-emulation/system for MSWindows. At
+# present it is treated as unix-like, but the environment variables
+# are those of Windows. (The test for USERNAME as well as USER was
+# to make latexmk work under MSYS's perl.)
+#
+if ( $^O eq "MSWin32" ) {
+# Pure MSWindows configuration
+ ## Configuration parameters:
+
+ ## Use first existing case for $tmpdir:
+ $tmpdir = $ENV{TMPDIR} || $ENV{TEMP} || '.';
+ $log_file_binary = 1; # Protect against ctrl/Z in log file from
+ # Miktex 2.7.
+
+ ## List of possibilities for the system-wide initialization file.
+ ## The first one found (if any) is used.
+ @rc_system_files = ( "C:/latexmk/LatexMk", "C:/latexmk/latexmkrc" );
+
+ $search_path_separator = ';'; # Separator of elements in search_path
+
+ # For a pdf-file, "start x.pdf" starts the pdf viewer associated with
+ # pdf files, so no program name is needed:
+ $pdf_previewer = 'start %O %S';
+ $ps_previewer = 'start %O %S';
+ $ps_previewer_landscape = $ps_previewer;
+ $dvi_previewer = 'start %O %S';
+ $dvi_previewer_landscape = "$dvi_previewer";
+ # Viewer update methods:
+ # 0 => auto update: viewer watches file (e.g., gv)
+ # 1 => manual update: user must do something: e.g., click on window.
+ # (e.g., ghostview, MSWIN previewers, acroread under UNIX)
+ # 2 => send signal. Number of signal in $dvi_update_signal,
+ # $ps_update_signal, $pdf_update_signal
+ # 3 => viewer can't update, because it locks the file and the file
+ # cannot be updated. (acroread under MSWIN)
+ # 4 => run a command to force the update. The commands are
+ # specified by the variables $dvi_update_command,
+ # $ps_update_command, $pdf_update_command
+ $dvi_update_method = 1;
+ $ps_update_method = 1;
+ $pdf_update_method = 3; # acroread locks the pdf file
+}
+elsif ( $^O eq "cygwin" ) {
+ # The problem is a mixed MSWin32 and UNIX environment.
+ # Perl decides the OS is cygwin in two situations:
+ # 1. When latexmk is run from a cygwin shell under a cygwin
+ # environment. Perl behaves in a UNIX way. This is OK, since
+ # the user is presumably expecting UNIXy behavior.
+ # 2. When CYGWIN exectuables are in the path, but latexmk is run
+ # from a native NT shell. Presumably the user is expecting NT
+ # behavior. But perl behaves more UNIXy. This causes some
+ # clashes.
+ # The issues to handle are:
+ # 1. Perl sees both MSWin32 and cygwin filenames. This is
+ # normally only an advantage.
+ # 2. Perl uses a UNIX shell in the system command
+ # This is a nasty problem: under native NT, there is a
+ # start command that knows about NT file associations, so that
+ # we can do, e.g., (under native NT) system("start file.pdf");
+ # But this won't work when perl has decided the OS is cygwin,
+ # even if it is invoked from a native NT command line. An
+ # NT command processor must be used to deal with this.
+ # 3. External executables can be native NT (which only know
+ # NT-style file names) or cygwin executables (which normally
+ # know both cygwin UNIX-style file names and NT file names,
+ # but not always; some do not know about drive names, for
+ # example).
+ # Cygwin executables for tex and latex may only know cygwin
+ # filenames.
+ # 4. The BIBINPUTS environment variables may be
+ # UNIX-style or MSWin-style depending on whether native NT or
+ # cygwin executables are used. They are therefore parsed
+ # differently. Here is the clash:
+ # a. If a user is running under an NT shell, is using a
+ # native NT installation of tex (e.g., fptex or miktex),
+ # but has the cygwin executables in the path, then perl
+ # detects the OS as cygwin, but the user needs NT
+ # behavior from latexmk.
+ # b. If a user is running under an UNIX shell in a cygwin
+ # environment, and is using the cygwin installation of
+ # tex, then perl detects the OS as cygwin, and the user
+ # needs UNIX behavior from latexmk.
+ # Latexmk has no way of detecting the difference. The two
+ # situations may even arise for the same user on the same
+ # computer simply by changing the order of directories in the
+ # path environment variable
+
+
+ ## Configuration parameters: We'll assume native NT executables.
+ ## The user should override if they are not.
+
+ # This may fail: perl converts MSWin temp directory name to cygwin
+ # format. Names containing this string cannot be handled by native
+ # NT executables.
+ $tmpdir = $ENV{TMPDIR} || $ENV{TEMP} || '.';
+
+ ## List of possibilities for the system-wide initialization file.
+ ## The first one found (if any) is used.
+ ## We could stay with MSWin files here, since cygwin perl understands them
+ ## @rc_system_files = ( 'C:/latexmk/LatexMk', 'C:/latexmk/latexmkrc' );
+ ## But they are deprecated in v. 1.7. So use the UNIX version, prefixed
+ ## with a cygwin equivalent of the MSWin location
+ ## In addition, we need to add the same set of possible locations as with
+ ## unix, so that the user use a unix-style setup.
+ @rc_system_files = ();
+ foreach ( 'LatexMk', 'latexmkrc' ) {
+ push @rc_system_files,
+ ( "/cygdrive/c/latexmk/$_",
+ "/opt/local/share/latexmk/$_",
+ "/usr/local/share/latexmk/$_",
+ "/usr/local/lib/latexmk/$_" );
+ }
+ $search_path_separator = ';'; # Separator of elements in search_path
+ # This is tricky. The search_path_separator depends on the kind
+ # of executable: native NT v. cygwin.
+ # So the user will have to override this.
+
+ # We will assume that files can be viewed by native NT programs.
+ # Then we must fix the start command/directive, so that the
+ # NT-native start command of a cmd.exe is used.
+ # For a pdf-file, "start x.pdf" starts the pdf viewer associated with
+ # pdf files, so no program name is needed:
+ $start_NT = "cmd /c start \"\"";
+ $pdf_previewer = "$start_NT %O %S";
+ $ps_previewer = "$start_NT %O %S";
+ $ps_previewer_landscape = $ps_previewer;
+ $dvi_previewer = "$start_NT %O %S";
+ $dvi_previewer_landscape = $dvi_previewer;
+ # Viewer update methods:
+ # 0 => auto update: viewer watches file (e.g., gv)
+ # 1 => manual update: user must do something: e.g., click on window.
+ # (e.g., ghostview, MSWIN previewers, acroread under UNIX)
+ # 2 => send signal. Number of signal in $dvi_update_signal,
+ # $ps_update_signal, $pdf_update_signal
+ # 3 => viewer can't update, because it locks the file and the file
+ # cannot be updated. (acroread under MSWIN)
+ $dvi_update_method = 1;
+ $ps_update_method = 1;
+ $pdf_update_method = 3; # acroread locks the pdf file
+}
+elsif ( $^O eq "msys" ) {
+ $search_path_separator = ';'; # Separator of elements in search_path
+ # I think MS-Win value is OK, since
+ # msys is running under MS-Win
+ $pdf_previewer = q[sh -c 'start %S'];
+ $ps_previewer = q[sh -c 'start %S'];
+ $dvi_previewer = q[sh -c 'start %S'];
+ $ps_previewer_landscape = $ps_previewer;
+ $dvi_previewer_landscape = "$dvi_previewer";
+}
+else {
+ # Assume anything else is UNIX or clone
+ # Do special cases (e.g., linux, darwin (i.e., OS-X)) inside this block.
+
+ ## Use first existing case for $tmpdir:
+ $tmpdir = $ENV{TMPDIR} || '/tmp';
+
+ ## List of possibilities for the system-wide initialization file.
+ ## The first one found (if any) is used.
+ ## Normally on a UNIX it will be in a subdirectory of /opt/local/share or
+ ## /usr/local/share, depending on the local conventions.
+ ## But /usr/local/lib/latexmk is put in the list for
+ ## compatibility with older versions of latexmk.
+ @rc_system_files = ();
+ foreach ( 'LatexMk', 'latexmkrc' ) {
+ push @rc_system_files,
+ ( "/opt/local/share/latexmk/$_",
+ "/usr/local/share/latexmk/$_",
+ "/usr/local/lib/latexmk/$_" );
+ }
+ $search_path_separator = ':'; # Separator of elements in search_path
+
+ $dvi_update_signal = $signo{USR1}
+ if ( defined $signo{USR1} ); # Suitable for xdvi
+ $ps_update_signal = $signo{HUP}
+ if ( defined $signo{HUP} ); # Suitable for gv
+ $pdf_update_signal = $signo{HUP}
+ if ( defined $signo{HUP} ); # Suitable for gv
+ ## default document processing programs.
+ # Viewer update methods:
+ # 0 => auto update: viewer watches file (e.g., gv)
+ # 1 => manual update: user must do something: e.g., click on window.
+ # (e.g., ghostview, MSWIN previewers, acroread under UNIX)
+ # 2 => send signal. Number of signal in $dvi_update_signal,
+ # $ps_update_signal, $pdf_update_signal
+ # 3 => viewer can't update, because it locks the file and the file
+ # cannot be updated. (acroread under MSWIN)
+ # 4 => Run command to update. Command in $dvi_update_command,
+ # $ps_update_command, $pdf_update_command.
+ $dvi_previewer = 'start xdvi %O %S';
+ $dvi_previewer_landscape = 'start xdvi -paper usr %O %S';
+ if ( defined $dvi_update_signal ) {
+ $dvi_update_method = 2; # xdvi responds to signal to update
+ } else {
+ $dvi_update_method = 1;
+ }
+# if ( defined $ps_update_signal ) {
+# $ps_update_method = 2; # gv responds to signal to update
+# $ps_previewer = 'start gv -nowatch';
+# $ps_previewer_landscape = 'start gv -swap -nowatch';
+# } else {
+# $ps_update_method = 0; # gv -watch watches the ps file
+# $ps_previewer = 'start gv -watch';
+# $ps_previewer_landscape = 'start gv -swap -watch';
+# }
+ # Turn off the fancy options for gv. Regular gv likes -watch etc
+ # GNU gv likes --watch etc. User must configure
+ $ps_update_method = 0; # gv -watch watches the ps file
+ $ps_previewer = 'start gv %O %S';
+ $ps_previewer_landscape = 'start gv -swap %O %S';
+ $pdf_previewer = 'start acroread %O %S';
+ $pdf_update_method = 1; # acroread under unix needs manual update
+ $lpr = 'lpr %O %S'; # Assume lpr command prints postscript files correctly
+ $lpr_dvi =
+ 'NONE $lpr_dvi variable is not configured to allow printing of dvi files';
+ $lpr_pdf =
+ 'NONE $lpr_pdf variable is not configured to allow printing of pdf files';
+ # The $pscmd below holds a command to list running processes. It
+ # is used to find the process ID of the viewer looking at the
+ # current output file. The output of the command must include the
+ # process number and the command line of the processes, since the
+ # relevant process is identified by the name of file to be viewed.
+ # Uses:
+ # 1. In preview_continuous mode, to save running a previewer
+ # when one is already running on the relevant file.
+ # 2. With xdvi in preview_continuous mode, xdvi must be
+ # signalled to make it read a new dvi file.
+ #
+ # The following works on Solaris, LINUX, HP-UX, IRIX
+ # Use -f to get full listing, including command line arguments.
+ # Use -u $ENV{USER} to get all processes started by current user (not just
+ # those associated with current terminal), but none of other users'
+ # processes.
+ # However, the USER environment variable may not exist. Windows uses
+ # USERNAME instead. (And this propagates to a situation of
+ # unix-emulation software running under Windows.)
+ if ( exists $ENV{USER} ) {
+ $pscmd = "ps -f -u $ENV{USER}";
+ }
+ elsif ( exists $ENV{USERNAME} ) {
+ $pscmd = "ps -f -u $ENV{USERNAME}";
+ }
+ else {
+ $pscmd = "ps -f";
+ }
+ $pid_position = 1; # offset of PID in output of pscmd; first item is 0.
+ if ( $^O eq "linux" ) {
+ # Ps on Redhat (at least v. 7.2) appears to truncate its output
+ # at 80 cols, so that a long command string is truncated.
+ # Fix this with the --width option. This option works under
+ # other versions of linux even if not necessary (at least
+ # for SUSE 7.2).
+ # However the option is not available under other UNIX-type
+ # systems, e.g., Solaris 8.
+ # But (19 Aug 2010), the truncation doesn't happen on RHEL4 and 5,
+ # unless the output is written to a terminal. So the --width
+ # option is now unnecessary
+ # $pscmd = "ps --width 200 -f -u $ENV{USER}";
+ }
+ elsif ( $^O eq "darwin" ) {
+ # OS-X on Macintosh
+ # open starts command associated with a file.
+ # For pdf, this is set by default to OS-X's preview, which is suitable.
+ # Manual update is simply by clicking on window etc, which is OK.
+ # For ps, this is set also to preview. This works, but since it
+ # converts the file to pdf and views the pdf file, it doesn't
+ # see updates, and a refresh cannot be done. This is far from
+ # optimal.
+ # For a full installation of MacTeX, which is probably the most common
+ # on OS-X, an association is created between dvi files and TeXShop.
+ # This also converts the file to pdf, so again while it works, it
+ # does not deal with changed dvi files, as far as I can see.
+ $pdf_previewer = 'open %S';
+ $pdf_update_method = 1; # manual
+ $dvi_previewer = $dvi_previewer_landscape = 'NONE';
+ $ps_previewer = $ps_previewer_landscape = 'NONE';
+ # Others
+ $lpr_pdf = 'lpr %O %S';
+ $pscmd = "ps -ww -u $ENV{USER}";
+ }
+}
+
+## default parameters
+$auto_rc_use = 1; # Whether to read rc files automatically
+$max_repeat = 5; # Maximum times I repeat latex. Normally
+ # 3 would be sufficient: 1st run generates aux file,
+ # 2nd run picks up aux file, and maybe toc, lof which
+ # contain out-of-date information, e.g., wrong page
+ # references in toc, lof and index, and unresolved
+ # references in the middle of lines. But the
+ # formatting is more-or-less correct. On the 3rd
+ # run, the page refs etc in toc, lof, etc are about
+ # correct, but some slight formatting changes may
+ # occur, which mess up page numbers in the toc and lof,
+ # Hence a 4th run is conceivably necessary.
+ # At least one document class (JHEP.cls) works
+ # in such a way that a 4th run is needed.
+ # We allow an extra run for safety for a
+ # maximum of 5. Needing further runs is
+ # usually an indication of a problem; further
+ # runs may not resolve the problem, and
+ # instead could cause an infinite loop.
+$clean_ext = ""; # space separated extensions of files that are
+ # to be deleted when doing cleanup, beyond
+ # standard set
+$clean_full_ext = ""; # space separated extensions of files that are
+ # to be deleted when doing cleanup_full, beyond
+ # standard set and those in $clean_ext
+@cus_dep_list = (); # Custom dependency list
+@default_files = ( '*.tex' ); # Array of LaTeX files to process when
+ # no files are specified on the command line.
+ # Wildcards allowed
+ # Best used for project specific files.
+@default_excluded_files = ( );
+ # Array of LaTeX files to exclude when using
+ # @default_files, i.e., when no files are specified
+ # on the command line.
+ # Wildcards allowed
+ # Best used for project specific files.
+$texfile_search = ""; # Specification for extra files to search for
+ # when no files are specified on the command line
+ # and the @default_files variable is empty.
+ # Space separated, and wildcards allowed.
+ # These files are IN ADDITION to *.tex in current
+ # directory.
+ # This variable is obsolete, and only in here for
+ # backward compatibility.
+
+$fdb_ext = 'fdb_latexmk'; # Extension for the file for latexmk's
+ # file-database
+ # Make it long to avoid possible collisions.
+$fdb_ver = 3; # Version number for kind of fdb_file.
+
+$jobname = ''; # Jobname: as with current tex, etc indicates
+ # basename of generated files. Defined so
+ # that --jobname=STRING on latexmk's command
+ # line has same effect as with current tex,
+ # etc, with the exception listed below. (If
+ # $jobname is non-empty, then the
+ # --jobname=... option is used on tex.)
+ # Extension: $jobname is allowed to contain
+ # placeholder(s) (currently only %A),
+ # which allows construction of jobnames
+ # dependent on name of main TeX file; this is
+ # useful when a jobname is used and latexmk is
+ # invoked on multiple files.
+$out_dir = ''; # Directory for output files.
+ # Cf. --output-directory of current (pdf)latex
+$aux_dir = ''; # Directory for aux files (log, aux, etc).
+ # Cf. --aux-directory of current (pdf)latex in MiKTeX.
+
+
+## default flag settings.
+$recorder = 1; # Whether to use recorder option on latex/pdflatex
+$silent = 0; # Silence latex's messages?
+$warnings_as_errors = 0;# Treat warnings as errors and exit with non-zero exit code
+$silence_logfile_warnings = 0; # Do list warnings in log file
+$kpsewhich_show = 0; # Show calls to and results from kpsewhich
+$landscape_mode = 0; # default to portrait mode
+$analyze_input_log_always = 1; # Always analyze .log for input files in the
+ # <...> and (...) constructions. Otherwise, only
+ # do the analysis when fls file doesn't exist or is
+ # out of date.
+ # Under normal circumstances, the data in the fls file
+ # is reliable, and the test of the log file gets lots
+ # of false positives; usually $analyze_input_log_always
+ # is best set to zero. But the test of the log file
+ # is needed at least in the following situation:
+ # When a user needs to persuade latexmk that a certain
+ # file is a source file, and latexmk doesn't otherwise
+ # find it. User code causes line with (...) to be
+ # written to log file. One important case is for
+ # lualatex, which doesn't always generate lines in the
+ # .fls file for input lua files. (The situation with
+ # lualatex is HIGHLY version dependent, e.g., between
+ # 2016 and 2017.)
+ # To keep backward compatibility with older versions
+ # of latexmk, the default is to set
+ # $analyze_input_log_always to 1.
+
+# The following two arrays contain lists of extensions (without
+# period) for files that are read in during a (pdf)LaTeX run but that
+# are generated automatically from the previous run, as opposed to
+# being user generated files (directly or indirectly from a custom
+# dependency). These files get two kinds of special treatment:
+# 1. In clean up, where depending on the kind of clean up, some
+# or all of these generated files are deleted.
+# (Note that special treatment is given to aux files.)
+# 2. In analyzing the results of a run of (pdf)LaTeX, to
+# determine if another run is needed. With an error free run,
+# a rerun should be provoked by a change in any source file,
+# whether a user file or a generated file. But with a run
+# that ends in an error, only a change in a user file during
+# the run (which might correct the error) should provoke a
+# rerun, but a change in a generated file should not.
+# These arrays can be user-configured.
+
+@generated_exts = ( 'aux', 'bcf', 'fls', 'idx', 'ind', 'lof', 'lot',
+ 'out', 'toc' );
+ # N.B. 'out' is generated by hyperref package
+
+# Which kinds of file do I have requests to make?
+# If no requests at all are made, then I will make dvi file
+# If particular requests are made then other files may also have to be
+# made. E.g., ps file requires a dvi file
+$dvi_mode = 0; # No dvi file requested
+$postscript_mode = 0; # No postscript file requested
+$pdf_mode = 0; # No pdf file requested to be made by pdflatex
+ # Possible values:
+ # 0 don't create pdf file
+ # 1 to create pdf file by pdflatex
+ # 2 to create pdf file by ps2pdf
+ # 3 to create pdf file by dvipdf
+ # 4 to create pdf file by lualatex
+ # 5 to create pdf file by xelatex + xdvipdfmx
+$view = 'default'; # Default preview is of highest of dvi, ps, pdf
+$sleep_time = 2; # time to sleep b/w checks for file changes in -pvc mode
+$banner = 0; # Non-zero if we have a banner to insert
+$banner_scale = 220; # Original default scale
+$banner_intensity = 0.95; # Darkness of the banner message
+$banner_message = 'DRAFT'; # Original default message
+$do_cd = 0; # Do not do cd to directory of source file.
+ # Thus behave like latex.
+$dependents_list = 0; # Whether to display list(s) of dependencies
+$dependents_phony = 0; # Whether list(s) of dependencies includes phony targets
+ # (as with 'gcc -MP').
+$deps_file = '-'; # File for dependency list output. Default stdout.
+$rules_list = 0; # Whether to display list(s) of dependencies
+@dir_stack = (); # Stack of pushed directories, each of form of
+ # pointer to array [ cwd, good_cwd ], where
+ # good_cwd differs from cwd by being converted
+ # to native MSWin path when cygwin is used.
+$cleanup_mode = 0; # No cleanup of nonessential LaTex-related files.
+ # $cleanup_mode = 0: no cleanup
+ # $cleanup_mode = 1: full cleanup
+ # $cleanup_mode = 2: cleanup except for dvi,
+ # dviF, pdf, ps, psF & xdv
+$cleanup_fdb = 0; # No removal of file for latexmk's file-database
+$cleanup_only = 0; # When doing cleanup, do not go on to making files
+$cleanup_includes_generated = 0;
+ # Determines whether cleanup deletes files generated by
+ # (pdf)latex (found from \openout lines in log file).
+ # It's more than that. BUG
+$cleanup_includes_cusdep_generated = 0;
+ # Determines whether cleanup deletes files generated by
+ # custom dependencies
+$diagnostics = 0;
+$dvi_filter = ''; # DVI filter command
+$ps_filter = ''; # Postscript filter command
+
+$force_mode = 0; # =1 to force processing past errors
+$go_mode = 0; # =1 to force processing regardless of time-stamps
+ # =2 full clean-up first
+$preview_mode = 0;
+$preview_continuous_mode = 0;
+$printout_mode = 0; # Don't print the file
+
+## Control pvc inactivity timeout:
+$pvc_timeout = 0;
+$pvc_timeout_mins = 30;
+
+$show_time = 0;
+@timings = ();
+$processing_time1 = processing_time();
+
+$use_make_for_missing_files = 0; # Whether to use make to try to make missing files.
+
+# Do we make view file in temporary then move to final destination?
+# (To avoid premature updating by viewer).
+$always_view_file_via_temporary = 0; # Set to 1 if viewed file is always
+ # made through a temporary.
+$pvc_view_file_via_temporary = 1; # Set to 1 if only in -pvc mode is viewed
+ # file made through a temporary.
+
+# State variables initialized here:
+
+$updated = 0; # Flags when something has been remade
+ # Used to allow convenient user message in -pvc mode
+$waiting = 0; # Flags whether we are in loop waiting for an event
+ # Used to avoid unnecessary repeated o/p in wait loop
+
+# The following are used for some results of parsing log file
+# Global variables, so results can be reported in main program.
+$reference_changed = 0;
+$mult_defined = 0;
+$bad_reference = 0;
+$bad_character = 0;
+$bad_citation = 0;
+@primary_warning_summary = ();
+
+# Cache of expensive-to-compute state variables, e.g., cwd in form
+# fixed to deal with cygwin issues.
+%cache = ();
+&cache_good_cwd;
+
+# Set search paths for includes.
+# Set them early so that they can be overridden
+$BIBINPUTS = $ENV{'BIBINPUTS'};
+if (!$BIBINPUTS) { $BIBINPUTS = '.'; }
+
+# Convert search paths to arrays:
+# If any of the paths end in '//' then recursively search the
+# directory. After these operations, @BIBINPUTS should
+# have all the directories that need to be searched
+
+@BIBINPUTS = find_dirs1( $BIBINPUTS );
+
+
+######################################################################
+######################################################################
+#
+# ??? UPDATE THE FOLLOWING!!
+#
+# We will need to determine whether source files for runs of various
+# programs are out of date. In a normal situation, this is done by
+# asking whether the times of the source files are later than the
+# destination files. But this won't work for us, since a common
+# situation is that a file is written on one run of latex, for
+# example, and read back in on the next run (e.g., an .aux file).
+# Some situations of this kind are standard in latex generally; others
+# occur with particular macro packages or with particular
+# postprocessors.
+#
+# The correct criterion for whether a source is out-of-date is
+# therefore NOT that its modification time is later than the
+# destination file, but whether the contents of the source file have
+# changed since the last successful run. This also handles the case
+# that the user undoes some changes to a source file by replacing the
+# source file by reverting to an earlier version, which may well have
+# an older time stamp. Since a direct comparison of old and new files
+# would involve storage and access of a large number of backup files,
+# we instead use the md5 signature of the files. (Previous versions
+# of latexmk used the backup file method, but restricted to the case
+# of .aux and .idx files, sufficient for most, but not all,
+# situations.)
+#
+# We will have a database of (time, size, md5) for the relevant
+# files. If the time and size of a file haven't changed, then the file
+# is assumed not to have changed; this saves us from having to
+# determine its md5 signature, which would involve reading the whole
+# file, which is naturally time-consuming, especially if network file
+# access to a server is needed, and many files are involved, when most
+# of them don't change. It is of course possible to change a file
+# without changing its size, but then to adjust its timestamp
+# to what it was previously; this requires a certain amount of
+# perversity. We can safely assume that if the user edits a file or
+# changes its contents, then the file's timestamp changes. The
+# interesting case is that the timestamp does change, because the file
+# has actually been written to, but that the contents do not change;
+# it is for this that we use the md5 signature. However, since
+# computing the md5 signature involves reading the whole file, which
+# may be large, we should avoid computing it more than necessary.
+#
+# So we get the following structure:
+#
+# 1. For each relevant run (latex, pdflatex, each instance of a
+# custom dependency) we have a database of the state of the
+# source files that were last used by the run.
+# 2. On an initial startup, the database for a primary tex file
+# is read that was created by a previous run of latex or
+# pdflatex, if this exists.
+# 3. If the file doesn't exist, then the criterion for
+# out-of-dateness for an initial run is that it goes by file
+# timestamps, as in previous versions of latexmk, with due
+# (dis)regard to those files that are known to be generated by
+# latex and re-read on the next run.
+# 4. Immediately before a run, the database is updated to
+# represent the current conditions of the run's source files.
+# 5. After the run, it is determined whether any of the source
+# files have changed. This covers both files written by the
+# run, which are therefore in a dependency loop, and files that
+# the user may have updated during the run. (The last often
+# happens when latex takes a long time, for a big document,
+# and the user makes edits before latex has finished. This is
+# particularly prevalent when latexmk is used with
+# preview-continuous mode.)
+# 6. In the case of latex or pdflatex, the custom dependencies
+# must also be checked and redone if out-of-date.
+# 7. If any source files have changed, the run is redone,
+# starting at step 1.
+# 8. There is naturally a limit on the number of reruns, to avoid
+# infinite loops from bugs and from pathological or unforeseen
+# conditions.
+# 9. After the run is done, the run's file database is updated.
+# (By hypothesis, the sizes and md5s are correct, if the run
+# is successful.)
+# 10. To allow reuse of data from previous runs, the file database
+# is written to a file after every complete set of passes
+# through latex or pdflatex. (Note that there is separate
+# information for latex and pdflatex; the necessary
+# information won't coincide: Out-of-dateness for the files
+# for each program concerns the properties of the files when
+# the other program was run, and the set of source files could
+# be different, e.g., for graphics files.)
+#
+# We therefore maintain the following data structures.:
+#
+# a. For each run (latex, pdflatex, each custom dependency) a
+# database is maintained. This is a hash from filenames to a
+# reference to an array: [time, size, md5]. The semantics of
+# the database is that it represents the state of the source
+# files used in the run. During a run it represents the state
+# immediately before the run; after a run, with all reruns, it
+# represents the state of the files used, modified by having
+# the latest timestamps for generated files.
+# b. There is a global database for all files, which represents
+# the current state. This saves having to recompute the md5
+# signatures of a changed file used in more than one run
+# (e.g., latex and pdflatex).
+# c. Each of latex and pdflatex has a list of the relevant custom
+# dependencies.
+#
+# In all the following a fdb-hash is a hash of the form:
+# filename -> [time, size, md5]
+# If a file is found to disappear, its entry is removed from the hash.
+# In returns from fdb access routines, a size entry of -1 indicates a
+# non-existent file.
+
+
+# List of known rules. Rule types: primary,
+# external (calls program), internal (calls routine), cusdep.
+
+%possible_primaries = ( 'latex' => 'primary', 'pdflatex' => 'primary',
+ 'lualatex' => 'primary', 'xelatex' => 'primary' );
+
+# Hashes, whose keys give names of particular kinds of rule, and targets.
+# We use hashes for ease of lookup.
+%possible_one_time = ( 'view' => 1, 'print' => 1, 'update_view' => 1, );
+%target_files = (); # Hash for target files.
+ # The keys are the filenames and the value is
+ # currently irrelevant.
+%target_rules = (); # Hash for target rules beyond those corresponding to files.
+ # The keys are the rule names and the value is
+ # currently irrelevant.
+# The target **files** can only be set inside the FILE loop.
+$current_primary = 'latex'; # Rule to compile .tex file.
+ # Subject to document-dependent override if .tex document
+ # uses metcommands andobeying them is enabled.
+$pdf_method = ''; # How to make pdf file. '' if not requested,
+ # else 'ps2pdf', 'dvipdf', 'pdflatex', 'lualatex' or 'xelatex'
+ # Subject to document-dependent override if .tex document
+ #uses \pdfoutput or c.
+%requested_filetypes = (); # Hash of requested file types (dvi, dviF, etc)
+%one_time = (); # Hash for requested one-time-only rules, currently
+ # possible values 'print' and 'view'.
+
+%actives = (); # Hash of active rules
+
+$allow_switch = 1; # Allow switch of rule structure to accommodate
+ # changed output file name of primary. Even if
+ # this flag is set on, the switch may be
+ # prohibited by other issues.
+
+%rule_db = (); # Database of all rules:
+ # Hash: rulename -> [array of rule data]
+ # Rule data:
+ # 0: [ cmd_type, ext_cmd, int_cmd, test_kind,
+ # source, dest, base,
+ # out_of_date, out_of_date_user,
+ # time_of_last_run, time_of_last_file_check,
+ # changed
+ # last_result, last_message,
+ # default_extra_generated,
+ # ]
+ # where
+ # cmd_type is 'primary', 'external', or 'cusdep'
+ # ext_cmd is string for associated external command
+ # with substitutions (%D for destination, %S
+ # for source, %B for base of current rule,
+ # %R for base of primary tex file, %T for
+ # texfile name, %O for options,
+ # %Y for $aux_dir1, and %Z for $out_dir1
+ # int_cmd specifies any internal command to be
+ # used to implement the application of the
+ # rule. If this is present, it overrides
+ # the external command, and it is the
+ # responsibility of the perl subroutine
+ # specified in intcmd to execute the
+ # external command if this is appropriate.
+ # This variable intcmd is a reference to an array,
+ # $$intcmd[0] = internal routine
+ # $$intcmd[1...] = its arguments (if any)
+ # test_kind specifies method of determining
+ # whether a file is out-of-date:
+ # 0 for never
+ # 1 for usual: whether there is a source
+ # file change
+ # 2 for dest earlier than source
+ # 3 for method 2 at first run, 1 thereafter
+ # (used when don't have file data from
+ # previous run).
+ # source = name of primary source file, if any
+ # dest = name of primary destination file,
+ # if any
+ # base = base name, if any, of files for
+ # this rule
+ # out_of_date = 1 if it has been detected that
+ # this rule needs to be run
+ # (typically because a source
+ # file has changed).
+ # Other values may be used for special cases.
+ # 0 otherwise
+ # out_of_date_user is like out_of_date, except
+ # that the detection of out-of-dateness
+ # has been made from a change of a
+ # putative user file, i.e., one that is
+ # not a generated file (e.g., aux). This
+ # kind of out-of-dateness should provoke a
+ # rerun whether or not there was an error
+ # during a run of (pdf)LaTeX. Normally,
+ # if there is an error, one should wait
+ # for the user to correct the error. But
+ # it is possible the error condition is
+ # already corrected during the run, e.g.,
+ # by the user changing a source file in
+ # response to an error message.
+ # time_of_last_run = time that this rule was
+ # last applied. (In standard units
+ # from perl, to be directly compared
+ # with file modification times.)
+ # time_of_last_file_check = last time that a check
+ # was made for changes in source files.
+ # changed flags whether special changes have been made
+ # that require file-existence status to be ignored
+ # last_result is
+ # -1 if no run has been made,
+ # 0 if the last run was successful
+ # 1 if last run was successful, but
+ # failed to create an output file
+ # 2 if last run failed
+ # 200 if last run gave a warning that is
+ # important enough to be reported with
+ # the error summary. The warning
+ # message is stored in last_message.
+ # last_message is error message for last run
+ # default_extra_generated is a reference to an array
+ # of specifications of extra generated files (beyond
+ # the main dest file. Standard place holders are used.
+ # 1: {Hash sourcefile -> [source-file data] }
+ # Source-file data array:
+ # 0: time
+ # 1: size
+ # 2: md5
+ # 3: name of rule to make this file
+ # 4: whether the file is of the kind made by epstopdf.sty
+ # during a primary run. It will have been read during
+ # the run, so that even though the file changes during
+ # a primary run, there is no need to trigger another
+ # run because of this.
+ # Size and md5 correspond to the values at the last run.
+ # But time may be updated to correspond to the time
+ # for the file, if the file is otherwise unchanged.
+ # This saves excessive md5 calculations, which would
+ # otherwise be done everytime the file is checked,
+ # in the following situation:
+ # When the file has been rewritten after a run
+ # has started (commonly aux, bbl files etc),
+ # but the actual file contents haven't
+ # changed. Then because the filetime has
+ # changed, on every file-change check latexmk
+ # would normally redo the md5 calculation to
+ # test for actual changes. Once one such
+ # check is done, and the contents are
+ # unchanged, later checks are superfluous, and
+ # can be avoided by changing the file's time
+ # in the source-file list.
+ # 2: {Hash generated_file -> 1 }
+ # This lists all generated files; the values
+ # are currently unused, only the keys
+ # 3: {Hash source_rule -> last_pass }
+ # This lists rules that are to be considered source
+ # rules for the current rule, separately from the
+ # from_rules of the source files. It maps the name
+ # of each of these rules to the pass number of the
+ # current rule when it was last run. The current rule
+ # is out-of-date if the last_pass for a source rule
+ # is less than the pass number for the rule.
+ # The purpose of this is when the source file list
+ # is expected to be inaccurate, because the source
+ # files are hard to determine. Typical needs are
+ # for rules applied to dvi and xdv files, when
+ # graphics files are involved. Their names are coded
+ # inside the dvi/xdv file, but not the contents.
+ # It would need parsing of the contents of the file
+ # to determine the actual source files.
+ # An implication of using a source_rule is that this
+ # rule passes files to the current rule and that the
+ # current rule can be considered out-of-date whenever
+ # the source_rule has been run. Effectively these
+ # files are unconditionallyconsidered changed when
+ # the source_rule runs.
+
+%fdb_current = (); # Hash of information for all files used.
+ # It maps filename to a reference to an array
+ # (time, size, md5_checksum).
+@nofile = (0,-1,0); # What we use for initializing a new entry in fdb
+ # or flagging non-existent file.
+
+# The following provide information about the structure of the network of rules
+# and files. They are set by the routine rdb_set_rule_net
+%from_rules = (); # Used to optimize test for whether a file is generated.
+ # This maps files to rules that generate them. (Files
+ # anywhere in %$PHdest.)
+%from_rules_main = (); # Used to optimize test for whether a file is generated.
+ # This maps files to rules that generate them as the
+ # MAIN output. (I.e., file $$Pdest only.)
+# Classification of rules, for determining order of application
+%current_primaries = (); # Keys are primary rules (latex, etc) that are currently in use.
+@pre_primary = (); # Array of rules that are thought of as pre-primary
+@post_primary = (); # Array of rules that are thought of as post-primary
+@unusual_one_time = (); # Array of rules that are special cases of one-time rules.
+ # Currently not used.
+
+
+# User's home directory
+$HOME = '';
+if (exists $ENV{'HOME'} ) {
+ $HOME = $ENV{'HOME'};
+}
+elsif (exists $ENV{'USERPROFILE'} ) {
+ $HOME = $ENV{'USERPROFILE'};
+}
+# XDG configuration home
+$XDG_CONFIG_HOME = '';
+if (exists $ENV{'XDG_CONFIG_HOME'} ) {
+ $XDG_CONFIG_HOME = $ENV{'XDG_CONFIG_HOME'};
+}
+elsif ($HOME ne '') {
+ if ( -d "$HOME/.config") {
+ $XDG_CONFIG_HOME = "$HOME/.config";
+ }
+}
+
+
+#==================================================
+
+# Options that are to be obeyed before rc files are read:
+
+foreach $_ ( @ARGV )
+{
+ if (/^-{1,2}norc$/ ) {
+ $auto_rc_use = 0;
+ }
+}
+
+#==================================================
+## Read rc files with this subroutine
+
+sub read_first_rc_file_in_list {
+ foreach my $rc_file ( @_ ) {
+ #print "===Testing for rc file \"$rc_file\" ...\n";
+ if ( -d $rc_file ) {
+ warn "$My_name: I have found a DIRECTORY named \"$rc_file\".\n",
+ " Have you perhaps misunderstood latexmk's documentation?\n",
+ " This name is normally used for a latexmk configuration (rc) file,\n",
+ " and in that case it should be a regular text file, not a directory.\n";
+ }
+ elsif ( -e $rc_file ) {
+ #print "===Reading rc file \"$rc_file\" ...\n";
+ process_rc_file( $rc_file );
+ return;
+ }
+ }
+}
+
+# Note that each rc file may unset $auto_rc_use to
+# prevent lower-level rc files from being read.
+# So test on $auto_rc_use in each case.
+if ( $auto_rc_use ) {
+ # System rc file:
+ if (exists $ENV{LATEXMKRCSYS} ) {
+ push @rc_system_files, $ENV{LATEXMKRCSYS};
+ if ( !-e $ENV{LATEXMKRCSYS} ) {
+ warn "$My_name: you've specified a system rc file `$ENV{LATEXMKRCSYS}`\n",
+ " in environment variable LATEXMKRCSYS, but the file doesn't exist.\n",
+ " I won't read any system rc file.\n";
+ }
+ else {
+ process_rc_file( $ENV{LATEXMKRCSYS} );
+ }
+ }
+ else {
+ read_first_rc_file_in_list( @rc_system_files );
+ }
+}
+if ( $auto_rc_use && ($HOME ne "" ) ) {
+ # User rc file:
+ @user_rc = ();
+ if ( $XDG_CONFIG_HOME ) {
+ push @user_rc, "$XDG_CONFIG_HOME/latexmk/latexmkrc";
+ }
+ # N.B. $HOME equals "" if latexmk couldn't determine a home directory.
+ # In that case, we shouldn't look for an rc file there.
+ if ( $HOME ) {
+ push @user_rc, "$HOME/.latexmkrc";
+ }
+ read_first_rc_file_in_list( @user_rc );
+}
+if ( $auto_rc_use ) {
+ # Rc file in current directory:
+ read_first_rc_file_in_list( "latexmkrc", ".latexmkrc" );
+}
+
+## Process command line args.
+@command_line_file_list = ();
+$bad_options = 0;
+
+while ($_ = $ARGV[0])
+{
+ # Make -- and - equivalent at beginning of option,
+ # but save original for possible use in (pdf)latex command line
+ $original = $_;
+ s/^--/-/;
+ shift;
+ if ( /^-aux-directory=(.*)$/ || /^-auxdir=(.*)$/ ) {
+ $aux_dir = $1;
+ }
+ elsif (/^-bibtex$/) { $bibtex_use = 2; }
+ elsif (/^-bibtex-$/) { $bibtex_use = 0; }
+ elsif (/^-nobibtex$/) { $bibtex_use = 0; }
+ elsif (/^-bibtex-cond$/) { $bibtex_use = 1; }
+ elsif (/^-bibtex-cond1$/) { $bibtex_use = 1.5; }
+ elsif (/^-c$/) { $cleanup_mode = 2; $cleanup_fdb = 1; $cleanup_only = 1; }
+ elsif (/^-C$/ || /^-CA$/ ) { $cleanup_mode = 1; $cleanup_fdb = 1; $cleanup_only = 1; }
+ elsif (/^-CF$/) { $cleanup_fdb = 1; }
+ elsif (/^-cd$/) { $do_cd = 1; }
+ elsif (/^-cd-$/) { $do_cd = 0; }
+ elsif (/^-commands$/) { &print_commands; exit; }
+ elsif (/^-d$/) { $banner = 1; }
+ elsif (/^-dependents$/ || /^-deps$/ || /^-M$/ ) { $dependents_list = 1; }
+ elsif (/^-nodependents$/ || /^-dependents-$/ || /^-deps-$/) { $dependents_list = 0; }
+ elsif (/^-deps-out=(.*)$/) {
+ $deps_file = $1;
+ $dependents_list = 1;
+ }
+ elsif (/^-diagnostics/) { $diagnostics = 1; }
+ elsif (/^-dvi$/) { $dvi_mode = 1; }
+ elsif (/^-dvi-$/) { $dvi_mode = 0; }
+ elsif (/^-f$/) { $force_mode = 1; }
+ elsif (/^-f-$/) { $force_mode = 0; }
+ elsif (/^-g$/) { $go_mode = 1; }
+ elsif (/^-g-$/) { $go_mode = 0; }
+ elsif (/^-gg$/) {
+ $go_mode = 2; $cleanup_mode = 1; $cleanup_fdb = 1; $cleanup_only = 0;
+ }
+ elsif ( /^-h$/ || /^-help$/ ) { &print_help; exit;}
+ elsif (/^-jobname=(.*)$/) {
+ $jobname = $1;
+ }
+ elsif (/^-l$/) { $landscape_mode = 1; }
+ elsif (/^-l-$/) { $landscape_mode = 0; }
+ elsif (/^-latex=(.*)$/) {
+ $latex = $1;
+ }
+ elsif (/^-latexoption=(.*)$/) {
+ push @extra_latex_options, $1;
+ push @extra_pdflatex_options, $1;
+ push @extra_lualatex_options, $1;
+ push @extra_xelatex_options, $1;
+ }
+ elsif ( /^-logfilewarninglist$/ || /^-logfilewarnings$/ )
+ { $silence_logfile_warnings = 0; }
+ elsif ( /^-logfilewarninglist-$/ || /^-logfilewarnings-$/ )
+ { $silence_logfile_warnings = 1; }
+# See above for -M
+ elsif (/^-MF$/) {
+ if ( $ARGV[0] eq '' ) {
+ &exit_help( "No file name specified after -MF switch");
+ }
+ $deps_file = $ARGV[0];
+ shift;
+ }
+ elsif ( /^-MP$/ ) { $dependents_phony = 1; }
+ elsif (/^-new-viewer$/) {
+ $new_viewer_always = 1;
+ }
+ elsif (/^-new-viewer-$/) {
+ $new_viewer_always = 0;
+ }
+ elsif (/^-norc$/ ) {
+ $auto_rc_use = 0;
+ # N.B. This has already been obeyed.
+ }
+ elsif ( /^-output-directory=(.*)$/ ||/^-outdir=(.*)$/ ) {
+ $out_dir = $1;
+ }
+ elsif (/^-p$/) { $printout_mode = 1;
+ $preview_continuous_mode = 0; # to avoid conflicts
+ $preview_mode = 0;
+ }
+ elsif (/^-p-$/) { $printout_mode = 0; }
+ elsif (/^-pdf$/) { $pdf_mode = 1; }
+ elsif (/^-pdf-$/) { $pdf_mode = 0; }
+ elsif (/^-pdfdvi$/){ $pdf_mode = 3; }
+ elsif (/^-pdflua$/){ $pdf_mode = 4; }
+ elsif (/^-pdfps$/) { $pdf_mode = 2; }
+ elsif (/^-pdfxe$/) { $pdf_mode = 5; }
+# elsif (/^-pdflatex$/) {
+# $pdflatex = "pdflatex %O %S";
+# $pdf_mode = 1;
+# $dvi_mode = $postscript_mode = 0;
+# }
+ elsif (/^-pdflatex=(.*)$/) {
+ $pdflatex = $1;
+ }
+ elsif (/^-pdflualatex=(.*)$/) {
+ $lualatex = $1;
+ }
+ elsif (/^-pdfxelatex=(.*)$/) {
+ $xelatex = $1;
+ }
+ elsif (/^-pretex=(.*)$/) {
+ $pre_tex_code = $1;
+ }
+ elsif (/^-print=(.*)$/) {
+ $value = $1;
+ if ( $value =~ /^dvi$|^ps$|^pdf$|^auto$/ ) {
+ $print_type = $value;
+ $printout_mode = 1;
+ }
+ else {
+ &exit_help("$My_name: unknown print type '$value' in option '$_'");
+ }
+ }
+ elsif (/^-ps$/) { $postscript_mode = 1; }
+ elsif (/^-ps-$/) { $postscript_mode = 0; }
+ elsif (/^-pv$/) { $preview_mode = 1;
+ $preview_continuous_mode = 0; # to avoid conflicts
+ $printout_mode = 0;
+ }
+ elsif (/^-pv-$/) { $preview_mode = 0; }
+ elsif (/^-pvc$/) { $preview_continuous_mode = 1;
+ $force_mode = 0; # So that errors do not cause loops
+ $preview_mode = 0; # to avoid conflicts
+ $printout_mode = 0;
+ }
+ elsif (/^-pvc-$/) { $preview_continuous_mode = 0; }
+ elsif (/^-pvctimeout$/) { $pvc_timeout = 1; }
+ elsif (/^-pvctimeout-$/) { $pvc_timeout = 0; }
+ elsif (/^-pvctimeoutmins=(.*)$/) { $pvc_timeout_mins = $1; }
+ elsif (/^-recorder$/ ){ $recorder = 1; }
+ elsif (/^-recorder-$/ ){ $recorder = 0; }
+ elsif (/^-rules$/ ) { $rules_list = 1; }
+ elsif (/^-norules$/ || /^-rules-$/ ) { $rules_list = 0; }
+ elsif (/^-showextraoptions$/) {
+ print "List of extra latex and pdflatex options recognized by $my_name.\n",
+ "These are passed as is to (pdf)latex. They may not be recognized by\n",
+ "particular versions of (pdf)latex. This list is a combination of those\n",
+ "for TeXLive and MikTeX.\n",
+ "\n",
+ "Note that in addition to the options in this list, there are several\n",
+ "options known to the (pdf)latex programs that are also recognized by\n",
+ "latexmk and trigger special behavior by latexmk. Since these options\n",
+ "appear in the main list given by running 'latexmk --help', they do not\n",
+ "appear in the following list\n",
+ "NOTE ALSO: Not all of these options are supported by all versions of (pdf)latex.\n",
+ "\n";
+ foreach $option ( sort( keys %allowed_latex_options, keys %allowed_latex_options_with_arg ) ) {
+ if (exists $allowed_latex_options{$option} ) { print " $allowed_latex_options{$option}\n"; }
+ if (exists $allowed_latex_options_with_arg{$option} ) { print " $allowed_latex_options_with_arg{$option}\n"; }
+ }
+ exit;
+ }
+ elsif (/^-silent$/ || /^-quiet$/ ){ $silent = 1; }
+ elsif (/^-stdtexcmds$/) { &std_tex_cmds; }
+ elsif (/^-time$/) { $show_time = 1;}
+ elsif (/^-time-$/) { $show_time = 0;}
+ elsif (/^-use-make$/) { $use_make_for_missing_files = 1; }
+ elsif (/^-use-make-$/) { $use_make_for_missing_files = 0; }
+ elsif (/^-usepretex$/) { &alt_tex_cmds; }
+ elsif (/^-usepretex=(.*)$/) {
+ &alt_tex_cmds;
+ $pre_tex_code = $1;
+ }
+ elsif (/^-v$/ || /^-version$/) {
+ print "\n$version_details. Version $version_num\n";
+ exit;
+ }
+ elsif (/^-verbose$/) { $silent = 0; }
+ elsif (/^-view=default$/) { $view = "default";}
+ elsif (/^-view=dvi$/) { $view = "dvi";}
+ elsif (/^-view=none$/) { $view = "none";}
+ elsif (/^-view=ps$/) { $view = "ps";}
+ elsif (/^-view=pdf$/) { $view = "pdf"; }
+ elsif (/^-Werror$/){ $warnings_as_errors = 1; }
+ elsif (/^-lualatex$/) {
+ $pdf_mode = 4;
+ $dvi_mode = $postscript_mode = 0;
+ }
+ elsif (/^-xelatex$/) {
+ $pdf_mode = 5;
+ $dvi_mode = $postscript_mode = 0;
+ }
+ elsif (/^-e$/) {
+ if ( $#ARGV < 0 ) {
+ &exit_help( "No code to execute specified after -e switch");
+ }
+ execute_code_string( $ARGV[0] );
+ shift;
+ }
+ elsif (/^-r$/) {
+ if ( $ARGV[0] eq '' ) {
+ &exit_help( "No RC file specified after -r switch");
+ }
+ if ( -e $ARGV[0] ) {
+ process_rc_file( $ARGV[0] );
+ }
+ else {
+ die "$My_name: RC file [$ARGV[0]] does not exist\n";
+ }
+ shift;
+ }
+ elsif (/^-bm$/) {
+ if ( $ARGV[0] eq '' ) {
+ &exit_help( "No message specified after -bm switch");
+ }
+ $banner = 1; $banner_message = $ARGV[0];
+ shift;
+ }
+ elsif (/^-bi$/) {
+ if ( $ARGV[0] eq '' ) {
+ &exit_help( "No intensity specified after -bi switch");
+ }
+ $banner_intensity = $ARGV[0];
+ shift;
+ }
+ elsif (/^-bs$/) {
+ if ( $ARGV[0] eq '' ) {
+ &exit_help( "No scale specified after -bs switch");
+ }
+ $banner_scale = $ARGV[0];
+ shift;
+ }
+ elsif (/^-dF$/) {
+ if ( $ARGV[0] eq '' ) {
+ &exit_help( "No dvi filter specified after -dF switch");
+ }
+ $dvi_filter = $ARGV[0];
+ shift;
+ }
+ elsif (/^-pF$/) {
+ if ( $ARGV[0] eq '' ) {
+ &exit_help( "No ps filter specified after -pF switch");
+ }
+ $ps_filter = $ARGV[0];
+ shift;
+ }
+ elsif ( ( exists( $allowed_latex_options{$_} ) )
+ || ( /^(-.+)=/ && exists( $allowed_latex_options_with_arg{$1} ) )
+ )
+ {
+ push @extra_latex_options, $original;
+ push @extra_pdflatex_options, $original;
+ push @extra_lualatex_options, $original;
+ push @extra_xelatex_options, $original;
+ }
+ elsif (/^-/) {
+ warn "$My_name: $_ bad option\n";
+ $bad_options++;
+ }
+ else {
+ push @command_line_file_list, $_ ;
+ }
+}
+
+if ( $bad_options > 0 ) {
+ &exit_help( "Bad options specified" );
+}
+
+warn "$My_name: This is $version_details, version: $version_num.\n",
+ unless $silent;
+
+
+if ( ($out_dir ne '') && ($aux_dir eq '') ){
+ $aux_dir = $out_dir;
+}
+
+# Save original values for use in diagnositics.
+# We may change $aux_dir and $out_dir after a detection
+# of results of misconfiguration.
+$aux_dir_requested = $aux_dir;
+$out_dir_requested = $out_dir;
+# The following reports results of diagnostics on location of .log file
+# after the first run of a latex engine, when actually used aux_dir
+# may not be the expected one, due to a configuration error.
+# Values: -1 uninitialized (before first run)
+# 0 log file not found;
+# 1 log file in aux_dir;
+# 2 log file **not** in aux_dir but in out_dir;
+# 3 log file **not** in aux_dir or out_dir, but in cwd.
+$where_log = -1;
+
+&set_dirs_etc;
+
+if ($bibtex_use > 1) {
+ push @generated_exts, 'bbl';
+}
+
+# For backward compatibility, convert $texfile_search to @default_files
+# Since $texfile_search is initialized to "", a nonzero value indicates
+# that an initialization file has set it.
+if ( $texfile_search ne "" ) {
+ @default_files = split /\s+/, "*.tex $texfile_search";
+}
+
+#Glob the filenames command line if the script was not invoked under a
+# UNIX-like environment.
+# Cases: (1) MS/MSwin native Glob
+# (OS detected as MSWin32)
+# (2) MS/MSwin cygwin Glob [because we do not know whether
+# the cmd interpreter is UNIXy (and does glob) or is
+# native MS-Win (and does not glob).]
+# (OS detected as cygwin)
+# (3) UNIX Don't glob (cmd interpreter does it)
+# (Currently, I assume this is everything else)
+if ( ($^O eq "MSWin32") || ($^O eq "cygwin") ) {
+ # Preserve ordering of files
+ @file_list = glob_list1(@command_line_file_list);
+#print "A1:File list:\n";
+#for ($i = 0; $i <= $#file_list; $i++ ) { print "$i: '$file_list[$i]'\n"; }
+}
+else {
+ @file_list = @command_line_file_list;
+}
+@file_list = uniq1( @file_list );
+
+
+# Check we haven't selected mutually exclusive modes.
+# Note that -c overrides all other options, but doesn't cause
+# an error if they are selected.
+if (($printout_mode && ( $preview_mode || $preview_continuous_mode ))
+ || ( $preview_mode && $preview_continuous_mode ))
+{
+ # Each of the options -p, -pv, -pvc turns the other off.
+ # So the only reason to arrive here is an incorrect inititalization
+ # file, or a bug.
+ &exit_help( "Conflicting options (print, preview, preview_continuous) selected");
+}
+
+if ( @command_line_file_list ) {
+ # At least one file specified on command line (before possible globbing).
+ if ( !@file_list ) {
+ &exit_help( "Wildcards in file names didn't match any files");
+ }
+}
+else {
+ # No files specified on command line, try and find some
+ # Evaluate in order specified. The user may have some special
+ # for wanting processing in a particular order, especially
+ # if there are no wild cards.
+ # Preserve ordering of files
+ my @file_list1 = uniq1( glob_list1(@default_files) );
+ my @excluded_file_list = uniq1( glob_list1(@default_excluded_files) );
+ # Make hash of excluded files, for easy checking:
+ my %excl = ();
+ foreach my $file (@excluded_file_list) {
+ $excl{$file} = '';
+ }
+ foreach my $file (@file_list1) {
+ push( @file_list, $file) unless ( exists $excl{$file} );
+ }
+ if ( !@file_list ) {
+ &exit_help( "No file name specified, and I couldn't find any");
+ }
+}
+
+$num_files = $#file_list + 1;
+$num_specified = $#command_line_file_list + 1;
+
+#print "Command line file list:\n";
+#for ($i = 0; $i <= $#command_line_file_list; $i++ ) { print "$i: '$command_line_file_list[$i]'\n"; }
+#print "File list:\n";
+#for ($i = 0; $i <= $#file_list; $i++ ) { print "$i: '$file_list[$i]'\n"; }
+
+
+# If selected a preview-continuous mode, make sure exactly one filename was specified
+if ($preview_continuous_mode && ($num_files != 1) ) {
+ if ($num_specified > 1) {
+ &exit_help(
+ "Need to specify exactly one filename for ".
+ "preview-continuous mode\n".
+ " but $num_specified were specified"
+ );
+ }
+ elsif ($num_specified == 1) {
+ &exit_help(
+ "Need to specify exactly one filename for ".
+ "preview-continuous mode\n".
+ " but wildcarding produced $num_files files"
+ );
+ }
+ else {
+ &exit_help(
+ "Need to specify exactly one filename for ".
+ "preview-continuous mode.\n".
+ " Since none were specified on the command line, I looked for \n".
+ " files in '@default_files'.\n".
+ " But I found $num_files files, not 1."
+ );
+ }
+}
+
+# If selected jobname, can only apply that to one file:
+if ( ($jobname ne '') && ($jobname !~ /%A/) && ($num_files > 1) ) {
+ &exit_help(
+ "Need to specify at most one filename if ".
+ "jobname specified without a %A, \n".
+ " but $num_files were found (after defaults and wildcarding)."
+ );
+}
+if ( $jobname =~ /%[^A]/ ) {
+ &exit_help(
+ "Jobname '$jobname' contains placeholder other than %A."
+ );
+}
+
+# Normalize the commands, to have place-holders for source, dest etc:
+&fix_cmds;
+
+# Add common options
+add_option( $latex_default_switches, \$latex );
+add_option( $pdflatex_default_switches, \$pdflatex );
+add_option( $lualatex_default_switches, \$lualatex );
+add_option( $xelatex_default_switches, \$xelatex );
+
+foreach (@extra_latex_options) { add_option( $_, \$latex ); }
+foreach (@extra_pdflatex_options) { add_option( $_, \$pdflatex ); }
+foreach (@extra_lualatex_options) { add_option( $_, \$lualatex ); }
+foreach (@extra_xelatex_options) { add_option( $_, \$xelatex ); }
+
+
+# If landscape mode, change dvips processor, and the previewers:
+if ( $landscape_mode )
+{
+ $dvips = $dvips_landscape;
+ $dvi_previewer = $dvi_previewer_landscape;
+ $ps_previewer = $ps_previewer_landscape;
+}
+
+if ( $silent ) {
+ add_option( "$latex_silent_switch", \$latex );
+ add_option( "$pdflatex_silent_switch", \$pdflatex );
+ add_option( "$lualatex_silent_switch", \$lualatex );
+ add_option( "$xelatex_silent_switch", \$xelatex );
+ add_option( "$biber_silent_switch", \$biber );
+ add_option( "$bibtex_silent_switch", \$bibtex );
+ add_option( "$makeindex_silent_switch", \$makeindex );
+ add_option( "$dvipdf_silent_switch", \$dvipdf );
+ add_option( "$dvips_silent_switch", \$dvips );
+ add_option( "$xdvipdfmx_silent_switch", \$xdvipdfmx );
+}
+
+if ( $recorder ) {
+ add_option( "-recorder", \$latex, \$pdflatex, \$lualatex, \$xelatex );
+}
+
+# If the output and/or aux directories are specified, fix the (pdf)latex
+# commands to use them.
+# N.B. We'll ensure that the directories actually exist only after a
+# possible cd to the document directory, since the directories can be
+# relative to the document.
+
+if ( $out_dir ) {
+ add_option( "-output-directory=\"$out_dir\"",
+ \$latex, \$pdflatex, \$lualatex, \$xelatex );
+}
+if ( $aux_dir && ($aux_dir ne $out_dir) ) {
+ # N.B. If $aux_dir and $out_dir are the same, then the -output-directory
+ # option is sufficient, especially because the -aux-directory exists
+ # only in MiKTeX, not in TeXLive.
+ add_option( "-aux-directory=\"$aux_dir\"",
+ \$latex, \$pdflatex, \$lualatex, \$xelatex );
+}
+
+if ( $jobname ne '' ) {
+ # Since $jobname may include placeholder(s), put %R placeholder
+ # in option, and let %R be substituted by actual jobname at runtime.
+ add_option( "--jobname=%R", \$latex, \$lualatex, \$pdflatex, \$xelatex );
+}
+
+# Which kind of file do we preview?
+if ( $view eq "default" ) {
+ # If default viewer requested, use "highest" of dvi, ps and pdf
+ # that was requested by user.
+ # No explicit request means view dvi.
+ $view = "dvi";
+ if ( $postscript_mode ) { $view = "ps"; }
+ if ( $pdf_mode ) { $view = "pdf"; }
+}
+
+# Make sure we make the kind of file we want to view:
+if ($view eq 'dvi') { $dvi_mode = 1; }
+if ($view eq 'ps') { $postscript_mode = 1; }
+if ( ($view eq 'pdf') && ($pdf_mode == 0) ) {
+ $pdf_mode = 1;
+}
+
+# Make sure that we make something if all requests are turned off
+if ( ! ( $dvi_mode || $pdf_mode || $postscript_mode || $printout_mode) ) {
+ print "No specific requests made, so default to dvi by latex\n";
+ $dvi_mode = 1;
+}
+
+# Determine requests.
+if ( $banner ) { $postscript_mode = 1; }
+if ( $dvi_mode ) {
+ $current_primary = 'latex';
+ $requested_filetypes{'dvi'} = 1;
+ if ( length($dvi_filter) != 0 ) { $requested_filetypes{'dviF'} = 1; }
+}
+if ( $postscript_mode ) {
+ $current_primary = 'latex';
+ $requested_filetypes{'ps'} = 1;
+ if ( length($ps_filter) != 0 ) { $requested_filetypes{'psF'} = 1; }
+}
+
+if ($pdf_mode > 5) {
+ warn "$My_name: Non-allowed value of \$pdf_mode = $pdf_mode,",
+ " replaced by 1.\n";
+ $pdf_mode = 1;
+}
+if ( ($dvi_mode || $postscript_mode) && $pdf_mode ) {
+ my %disallowed = ();
+ foreach (1,4,5) { $disallowed{$_} = 1; }
+ if ($disallowed{$pdf_mode}) {
+ warn "$My_name: \$pdf_mode = $pdf_mode is incompatible with dvi and postscript modes\n",
+ " which are required by other requests.\n";
+ if ($postscript_mode) {$pdf_mode = 2;}
+ else { $pdf_mode = 3; }
+ warn " I replaced it by $pdf_mode.\n";
+ }
+}
+if ( $pdf_mode == 0 ) {
+ $pdf_method = '';
+}
+elsif ( $pdf_mode == 1 ) {
+ $requested_filetypes{'pdf'} = 1;
+ $pdf_method = 'pdflatex';
+}
+elsif ( $pdf_mode == 2 ) {
+ $requested_filetypes{'pdf'} = 1;
+ $pdf_method = 'ps2pdf';
+}
+elsif ( $pdf_mode == 3 ) {
+ $requested_filetypes{'pdf'} = 1;
+ $pdf_method = 'dvipdf';
+}
+elsif ( $pdf_mode == 4 ) {
+ $requested_filetypes{'pdf'} = 1;
+ $pdf_method = 'lualatex';
+}
+elsif ( $pdf_mode == 5 ) {
+ $requested_filetypes{'pdf'} = 1;
+ $pdf_method = 'xelatex';
+}
+
+if ($print_type eq 'auto') {
+ if ( $postscript_mode ) { $print_type = 'ps'; }
+ elsif ( $pdf_mode ) { $print_type = 'pdf'; }
+ elsif ( $dvi_mode ) { $print_type = 'dvi'; }
+ else { $print_type = 'none'; }
+}
+if ( $printout_mode ) {
+ $one_time{'print'} = 1;
+ if ($print_type eq 'none'){
+ warn "$My_name: You have requested printout, but \$print_type is set to 'none'\n";
+ }
+}
+if ( $preview_continuous_mode || $preview_mode ) { $one_time{'view'} = 1; }
+
+$can_switch = $allow_switch;
+if ( $dvi_mode || $postscript_mode
+ || ( $printout_mode && ($print_type eq 'ps') || ($print_type eq 'dvi') )
+ || ( ($preview_mode || $preview_continuous_mode) && ( ($view eq 'ps') || ($view eq 'dvi') ) )
+ ) {
+ # Automatic switching (e.g., pdf<->dvi o/p) requires pdf files to be
+ # the only destinations. So if ps or dvi files needed, we cannot
+ # allow switching. (There will then be an error condition if a TeX
+ # engine fails to produce the correct type of output file.)
+ warn "$My_name: Disallowing switch of output file as incompatible\n",
+ " with file requests.\n";
+ $can_switch = 0;
+}
+
+
+if ( $pdf_mode == 2 ) {
+ # We generate pdf from ps. Make sure we have the correct kind of ps.
+ add_option( "$dvips_pdf_switch", \$dvips );
+}
+
+# Restrict variables to allowed values:
+
+if ($filetime_causality_threshold < 0) {
+ warn "$My_name: Correcting negative value of \$filetime_causality_threshold to zero.\n";
+ $filetime_causality_threshold = 0;
+}
+
+# Note sleep has granularity of 1 second.
+# Sleep periods 0 < $sleep_time < 1 give zero delay,
+# which is probably not what the user intended.
+# Sleep periods less than zero give infinite delay
+if ( $sleep_time < 0 ) {
+ warn "$My_name: Correcting negative sleep_time to 1 sec.\n";
+ $sleep_time = 1;
+}
+elsif ( ($sleep_time < 1) && ( $sleep_time != 0 ) ) {
+ warn "$My_name: Correcting nonzero sleep_time of less than 1 sec to 1 sec.\n";
+ $sleep_time = 1;
+}
+elsif ( $sleep_time == 0 ) {
+ warn "$My_name: sleep_time was configured to zero.\n",
+ " Do you really want to do this? It will give 100% CPU usage.\n";
+}
+
+# Make convenient forms for lookup.
+# Extensions always have period.
+
+# Convert @generated_exts to a hash for ease of look up and deletion
+# Keep extension without period!
+%generated_exts_all = ();
+foreach (@generated_exts ) {
+ $generated_exts_all{$_} = 1;
+}
+
+$quell_uptodate_msgs = $silent;
+ # Whether to quell informational messages when files are uptodate
+ # Will turn off in -pvc mode
+
+$failure_count = 0;
+@failed_primaries = ();
+
+if ($deps_file eq '' ) {
+ # Standardize name used for stdout
+ $deps_file = '-';
+}
+
+# Since deps_file is global (common to all processed files), we must
+# delete it here when doing a clean up, and not in the FILE loop, where
+# per-file processing (including clean-up) is done
+if ( ($cleanup_mode > 0) && $dependents_list && ( $deps_file ne '-' ) ) {
+ unlink_or_move( $deps_file );
+}
+
+# In non-pvc mode, the dependency list is global to all processed TeX files,
+# so we open a single file here, and add items to it after processing
+# each file. But in -pvc mode, the dependency list should be written
+# after round of processing the single TeX file (as if each round were
+# a separate run of latexmk).
+# If we are cleaning up ($cleanup_mode != 0) AND NOT continuing to
+# make files (--gg option and $go_mode == 2), deps_file should not be
+# created.
+# I will use definedness of $deps_handle as flag for global deps file having
+# been opened and therefore being available to be written to after
+# compiling a file.
+$deps_handle = undef;
+if ( $dependents_list
+ && ! $preview_continuous_mode
+ && ( ($cleanup_mode == 0) || ($go_mode == 2) )
+ ) {
+ $deps_handle = new FileHandle "> $deps_file";
+ if (! $deps_handle ) {
+ die "Cannot open '$deps_file' for output of dependency information\n";
+ }
+}
+
+# Remove leading and trailing space in the following space-separated lists,
+# and collapse multiple spaces to one,
+# to avoid getting incorrect blank items when they are split.
+foreach ($clean_ext, $clean_full_ext) { s/^\s+//; s/\s+$//; s/\s+/ /g; }
+
+# Deal with illegal and problematic characters in filename:
+test_fix_texnames( @file_list );
+
+FILE:
+foreach $filename ( @file_list )
+{
+ # Global variables for making of current file:
+ $updated = 0;
+ $failure = 0; # Set nonzero to indicate failure at some point of
+ # a make. Use value as exit code if I exit.
+ $failure_msg = ''; # Indicate reason for failure
+
+ if ( $do_cd ) {
+ ($filename, $path) = fileparse( $filename );
+ warn "$My_name: Changing directory to '$path'\n"
+ if !$silent;
+ pushd( $path );
+ }
+ else {
+ $path = '';
+ }
+
+ # Ensure the output/auxiliary directories exist, if need be
+ if ( $out_dir ) {
+ if ( ! -e $out_dir ) {
+ warn "$My_name: making output directory '$out_dir'\n"
+ if ! $silent;
+ make_path $out_dir;
+ }
+ elsif ( ! -d $out_dir ) {
+ warn "$My_name: you requested output directory '$out_dir',\n",
+ " but an ordinary file of the same name exists, which will\n",
+ " probably give an error later\n";
+ }
+ }
+
+ if ( $aux_dir && ($aux_dir ne $out_dir) ) {
+ # N.B. If $aux_dir and $out_dir are the same, then the -output-directory
+ # option is sufficient, especially because the -aux-directory exists
+ # only in MiKTeX, not in TeXLive.
+ if ( ! -e $aux_dir ) {
+ warn "$My_name: making auxiliary directory '$aux_dir'\n"
+ if ! $silent;
+ make_path $aux_dir;
+ }
+ elsif ( ! -d $aux_dir ) {
+ warn "$My_name: you requested aux directory '$aux_dir',\n",
+ " but an ordinary file of the same name exists, which will\n",
+ " probably give an error later\n";
+ }
+ }
+
+ &set_names;
+ # For use under error conditions:
+ @default_includes = ($texfile_name, $aux_main);
+ # N.B. Do **not** apply local %rule_db here. It might appear appropriate,
+ # but %rule_db is needed in the continue block, which is not in the
+ # scope of a local declaration here.
+ &rdb_initialize_rules;
+
+ if ( $cleanup_mode > 0 ) {
+# ?? MAY NEED TO FIX THE FOLLOWING IF $aux_dir or $out_dir IS SET.
+ my %other_generated = ();
+ my @index_bibtex_generated = ();
+ my @aux_files = ();
+ my @missing_bib_files = ();
+ my $bibs_all_exist = 0;
+ my %final_output_files = ();
+ foreach (keys %small_cleanup_preserved_exts) {
+ $final_output_files{"$out_dir1$root_filename.$_"} = 1;
+ }
+ $have_fdb = 0;
+ if ( -e $fdb_name ) {
+ print "$My_name: Examining fdb file '$fdb_name' for rules ...\n"
+ if $diagnostics;
+ $have_fdb = ( 0 == rdb_read( $fdb_name ) );
+ }
+ if ( $have_fdb ) {
+ rdb_for_actives(
+ sub { # Find generated files at rule level
+ my ($base, $path, $ext) = fileparseA( $$Psource );
+ $base = $path.$base;
+ if ( $rule =~ /^makeindex/ ) {
+ push @index_bibtex_generated, $$Psource, $$Pdest, "$base.ilg";
+ }
+ elsif ( $rule =~ /^(bibtex|biber)/ ) {
+ push @index_bibtex_generated, $$Pdest, "$base.blg";
+ push @aux_files, $$Psource;
+ if ( $bibtex_use == 1.5) {
+ foreach ( keys %$PHsource ) {
+ if ( ( /\.bib$/ ) && (! -e $_) ) {
+ push @missing_bib_files, $_;
+ }
+ }
+ }
+ }
+ elsif ( exists $other_generated{$$Psource} ) {
+# print "=========== CHECKING: source file of rule '$rule', '$$Psource'\n",
+# " is a generated file.\n";
+ ## OLD with apparent bug:
+ #$other_generated{$$Pdest};
+ }
+ foreach my $key (keys %$PHdest) {
+ $other_generated{$key} = 1;
+ }
+ },
+ sub { # Find generated files at source file level
+ if ( $file =~ /\.aux$/ ) { push @aux_files, $file; }
+ }
+ );
+ if ($#missing_bib_files == -1) { $bibs_all_exist = 1; }
+ }
+ elsif ( -e $log_name ) {
+ # No fdb file, but log file exists, so do inferior job by parse_log
+ print "$My_name: Examining log file '$log_name' for generated files...\n"
+ if $diagnostics;
+ # Variables set by parse_log. Can I remove them?
+ local %generated_log = ();
+ local %dependents = (); # Maps files to status. Not used here.
+ local @bbl_files = (); # Not used here.
+ local %idx_files = (); # Maps idx_file to (ind_file, base). Not used here.
+ local %conversions = (); # (pdf)latex-performed conversions. Not used here.
+ # Maps output file created and read by (pdf)latex
+ # to source file of conversion.
+ local $primary_out = ''; # Actual output file (dvi or pdf). Not used here.
+ local $fls_file_analyzed = 0;
+ &parse_log;
+ %other_generated = %generated_log;
+ }
+ else {
+ print "$My_name: No fdb or log file, so clean up default set of files ...\n"
+ if $diagnostics;
+ }
+
+ if ( ($go_mode == 2) && !$silent ) {
+ warn "$My_name: Removing all generated files\n" unless $silent;
+ }
+ my $keep_bbl = 1;
+ if ( ($bibtex_use > 1.6)
+ ||
+ ( ($bibtex_use == 1.5) && ($bibs_all_exist) )
+ ) {
+ $keep_bbl = 0;
+ }
+ if ($keep_bbl) {
+ delete $generated_exts_all{'bbl'};
+ }
+ # Convert two arrays to hashes:
+ my %index_bibtex_generated = ();
+ my %aux_files = ();
+ my %aux_files_to_save = ();
+ foreach (@index_bibtex_generated) {
+ $index_bibtex_generated{$_} = 1
+ unless ( /\.bbl$/ && ($keep_bbl) );
+ delete( $other_generated{$_} );
+ }
+ foreach (@aux_files) {
+ if (exists $other_generated{$_} ) {
+ $aux_files{$_} = 1;
+ }
+ else {
+ $aux_files_to_save{$_} = 1;
+ }
+ }
+
+ foreach (keys %final_output_files) { delete $other_generated{$_}; }
+
+ if ($diagnostics) {
+ show_array( "For deletion, the following were determined from fdb file or log file:\n"
+ ." Generated (from makeindex and bibtex):",
+ keys %index_bibtex_generated );
+ show_array( " Aux files:", keys %aux_files );
+ show_array( " Other generated files:\n"
+ ." (only deleted if \$cleanup_includes_generated is set): ",
+ keys %other_generated );
+ show_array( " Yet other generated files are specified by patterns:\n".
+ " Explicit pattern with %R or root-filename.extension:",
+ keys %generated_exts_all );
+ show_array( " Aux files to SAVE and not delete:", keys %aux_files_to_save );
+ }
+
+ my @clean_args = ( 'blg', 'ilg', 'log', 'aux.bak', 'idx.bak',
+ split('\s+',$clean_ext), keys %generated_exts_all );
+ cleanup1( $aux_dir1, @clean_args );
+ if ( $out_dir1 ne $aux_dir1 ) { cleanup1( $out_dir1, @clean_args ); }
+ if ( $cleanup_mode == 1 ) {
+ my @clean_args = ( keys %small_cleanup_preserved_exts, split('\s+', $clean_full_ext) );
+ cleanup1( $aux_dir1, @clean_args );
+ if ( $out_dir1 ne $aux_dir1 ) { cleanup1( $out_dir1, @clean_args ); }
+ }
+ unlink_or_move( 'texput.log', "texput.aux", "missfont.log",
+ keys %index_bibtex_generated,
+ keys %aux_files );
+ if ($cleanup_includes_generated) {
+ unlink_or_move( keys %other_generated );
+ }
+ if ( $cleanup_includes_cusdep_generated) {
+ &cleanup_cusdep_generated;
+ }
+ }
+ if ($cleanup_fdb) {
+ unlink_or_move( $fdb_name );
+ # If the fdb file exists, it will have been read, and therefore changed
+ # rule database. But deleting the fdb file implies we also want
+ # a virgin rule database, so we must reset it:
+ &rdb_initialize_rules;
+ }
+ if ($cleanup_only) { next FILE; }
+
+ if ( $diagnostics ) {
+ print "$My_name: Rules after start up for '$texfile_name'\n";
+ rdb_show();
+ }
+
+ $have_fdb = 0;
+ if (! -e $aux_main ) {
+ # No aux file => set up trivial aux file
+ # and corresponding fdb_file. Arrange them to provoke one run
+ # as minimum, but no more if actual aux file is trivial.
+ # (Useful on big files without cross references.)
+ # If aux file doesn't exist, then any fdb file is surely
+ # wrong.
+ # Previously, I had condition for this as being both aux and
+ # fdb files failing to exist. But it's not obvious what to
+ # do if aux exists and fdb doesn't. So I won't do anything.
+ &set_trivial_aux_fdb;
+ }
+
+ if ( -e $fdb_name ) {
+ $rdb_errors = rdb_read( $fdb_name );
+ $have_fdb = ($rdb_errors == 0);
+ }
+ if (!$have_fdb) {
+ # We didn't get a valid set of data on files used in
+ # previous run. So use filetime criterion for make
+ # instead of change from previous run, until we have
+ # done our own make.
+ rdb_recurse( [keys %possible_primaries],
+ sub{ if ( $$Ptest_kind == 1 ) { $$Ptest_kind = 3;} }
+ );
+ if ( -e $log_name ) {
+ rdb_for_some( [keys %possible_primaries], \&rdb_set_latex_deps );
+ }
+ }
+ foreach $rule ( &rdb_accessible ) {
+ # For all source files of all accessible rules,
+ # if the file data are not already set (e.g., from fdb_latexmk
+ # file, set them from disk.
+ rdb_one_rule ($rule, undef,
+ sub{ if ( $$Ptime == 0) { &rdb_update1; } }
+ );
+ }
+
+ if ($go_mode) {
+ # Force everything to be remade.
+ rdb_recurse( [ &rdb_target_array], sub{$$Pout_of_date=1;} );
+ }
+
+
+ if ( $diagnostics ) {
+ print "$My_name: Rules after initialization\n";
+ rdb_show();
+ }
+
+ #************************************************************
+
+ if ( $preview_continuous_mode ) {
+ &make_preview_continuous;
+ next FILE;
+ }
+
+
+## Handling of failures:
+## Variable $failure is set to indicate a failure, with information
+## put in $failure_msg.
+## These variables should be set to 0 and '' at any point at which it
+## should be assumed that no failures have occurred.
+## When after a routine is called it is found that $failure is set, then
+## processing should normally be aborted, e.g., by return.
+## Then there is a cascade of returns back to the outermost level whose
+## responsibility is to handle the error.
+## Exception: An outer level routine may reset $failure and $failure_msg
+## after initial processing, when the error condition may get
+## ameliorated later.
+ #Initialize failure flags now.
+ $failure = 0;
+ $failure_msg = '';
+ $failure = &rdb_make;
+ if ( ( $failure <= 0 ) || $force_mode ) {
+ rdb_for_some( [keys %one_time], \&rdb_run1 );
+ }
+ if ($#primary_warning_summary > -1) {
+ # N.B. $mult_defined, $bad_reference, $bad_character, $bad_citation also available here.
+ if ($warnings_as_errors) {
+ $failure = 1;
+ $failure_msg = "Warning(s) from latex (or c.) for '$filename'; treated as error";
+ }
+ }
+ if ($failure > 0) { next FILE; }
+} # end FILE
+continue {
+ if ($deps_handle) { deps_list($deps_handle); }
+ # If requested, print the list of rules. But don't do this in -pvc
+ # mode, since the rules list has already been printed.
+ if ($rules_list && ! $preview_continuous_mode) { rdb_list(); }
+ # Handle any errors
+ $error_message_count = rdb_show_rule_errors();
+ if ( ($error_message_count == 0) || ($failure > 0) ) {
+ if ( $failure_msg ) {
+ #Remove trailing space
+ $failure_msg =~ s/\s*$//;
+ warn "----------------------\n";
+ warn "This message may duplicate earlier message.\n";
+ warn "$My_name: Failure in processing file '$filename':\n",
+ " $failure_msg\n";
+ warn "----------------------\n";
+ $failure = 1;
+ }
+ }
+ if ( ($failure > 0) || ($error_message_count > 0) ) {
+ $failure_count ++;
+ push @failed_primaries, $filename;
+ }
+ &ifcd_popd;
+}
+close($deps_handle) if ( $deps_handle );
+
+if ($show_time) { show_timing();}
+
+sub show_timing {
+ my $processing_time = processing_time() - $processing_time1;
+ print @timings, "Accumulated processing time = $processing_time\n";
+ @timings = ();
+ $processing_time1 = processing_time();
+}
+
+# If we get here without going through the continue section:
+if ( $do_cd && ($#dir_stack > -1) ) {
+ # Just in case we did an abnormal exit from the loop
+ warn "$My_name: Potential bug: dir_stack not yet unwound, undoing all directory changes now\n";
+ &finish_dir_stack;
+}
+
+if ($failure_count > 0) {
+ if ( $#file_list > 0 ) {
+ # Error occured, but multiple files were processed, so
+ # user may not have seen all the error messages
+ warn "\n------------\n";
+ show_array(
+ "$My_name: Some operations failed, for the following tex file(s)",
+ @failed_primaries);
+ }
+ if ( !$force_mode ) {
+ warn "$My_name: Use the -f option to force complete processing,\n",
+ " unless error was exceeding maximum runs, or warnings treated as errors.\n";
+ }
+ exit 12;
+}
+
+if ( $where_log == 2 ) {
+ warn "$My_name: You requested aux_dir '$aux_dir_requested',\n".
+ " but '$aux_dir' was used by the (pdf)latex engine.\n".
+ " That indicates a configuration error.\n";
+ if ( ($tex_distribution !~ /^MiKTeX/i) && ($aux_dir_requested ne $out_dir_requested) ) {
+ warn " Probably you set different aux and out directories,\n".
+ " but that is not supported by your TeX distribution.\n".
+ " The only current distribution supporting this is MiKTeX.\n";
+ }
+}
+
+
+
+# end MAIN PROGRAM
+#############################################################
+#############################################################
+
+sub set_tex_cmds {
+ # Usage, e.g., set_tex_cmds( '%O %S' )
+ my $args = $_[0];
+ foreach my $cmd ('latex', 'lualatex', 'pdflatex', 'xelatex' ) {
+ ${$cmd} = "$cmd $args";
+ }
+ # N.B. See setting of $latex_default_switches, ...,
+ # $xelatex_default_switches, etc, for any special options needed.
+}
+
+sub std_tex_cmds { set_tex_cmds( '%O %S' ); }
+
+sub alt_tex_cmds { set_tex_cmds( '%O %P' ); }
+
+#========================
+
+sub test_fix_texnames {
+ my $illegal_char = 0;
+ my $unbalanced_quote = 0;
+ my $balanced_quote = 0;
+ foreach (@_) {
+ if ( $^O eq "MSWin32" ) {
+ # On MS-Win, change directory separator '\' to '/', as needed
+ # by the TeX engines, for which '\' introduces a macro name.
+ # Remember that '/' is a valid directory separator in MS-Win.
+ s[\\][/]g;
+ }
+ if ($do_cd) {
+ my ($filename, $path) = fileparse( $_ );
+ if ($filename =~ /[\Q$illegal_in_texname\E]/ ) {
+ $illegal_char++;
+ warn "$My_name: Filename '$filename' contains character not allowed for TeX file.\n";
+ }
+ if ($filename =~ /^&/) {
+ $illegal_char++;
+ warn "$My_name: Filename '$filename' contains initial '&', which is\n",
+ " not allowed for TeX file.\n";
+ }
+ }
+ else {
+ if ( /[\Q$illegal_in_texname\E]/ ) {
+ $illegal_char++;
+ warn "$My_name: Filename '$_' contains character not allowed for TeX file.\n";
+ }
+ if (/^&/ ) {
+ $illegal_char++;
+ warn "$My_name: Filename '$_' contains initial '&', which is not allowed\n",
+ " for TeX file.\n";
+ }
+ }
+ my $count_q = ($_ =~ tr/\"//);
+ if ( ($count_q % 2) != 0 ) {
+ warn "$My_name: Filename '$_' contains unbalanced quotes, not allowed.\n";
+ $unbalanced_quote++;
+ }
+ elsif ( $count_q > 0 ) {
+ warn "$My_name: Removed (balanced quotes) from filename '$_',\n";
+ s/\"//g;
+ warn " and obtained '$_'.\n";
+ $balanced_quote++;
+ }
+ }
+ if ($illegal_char || $unbalanced_quote) {
+ die "$My_name: Stopping because of bad filename(s).\n";
+ }
+}
+
+#############################################################
+
+sub ensure_path {
+ # Usage: ensure_path( $var, values ...)
+ # $ENV{$var} is an environment variable (e.g. $ENV{TEXINPUTS}.
+ # Ensure the values are in it, prepending them if not, and
+ # creating the environment variable if it doesn't already exist.
+ my $var = shift;
+ my %cmpts = ();
+ if ( exists $ENV{$var} ) {
+ foreach ( split $search_path_separator, $ENV{$var} ) {
+ if ($_ ne '') { $cmpts{$_} = 1; }
+ }
+ }
+ foreach (@_) {
+ next if ( ($_ eq '') || (exists $cmpts{$_}) );
+ if (exists $ENV{$var}) {
+ $ENV{$var} = $_ . $search_path_separator . $ENV{$var};
+ }
+ else {
+ $ENV{$var} = $_ . $search_path_separator;
+ }
+ }
+}
+
+#############################################################
+
+sub set_dirs_etc {
+ # Normalize versions terminating in directory/path separator
+ # and versions referring to current directory
+ # These actions in a subroutine so they can be used elsewhere.
+ $out_dir1 = $out_dir;
+ $aux_dir1 = $aux_dir;
+ foreach ( $aux_dir1, $out_dir1 ) {
+ if ( ($_ ne '') && ! m([\\/\:]$) ) {
+ $_ .= '/';
+ }
+ while ( s[^\.\/][] ) {}
+ }
+ if ($aux_dir) {
+ # Ensure $aux_dir is in BIBINPUTS and TEXINPUTS search paths.
+ # TEXINPUTS is used by dvips for files generated by mpost.
+ # For BIBINPUTS,
+ # at least one widely package (revtex4-1) generates a bib file
+ # (which is used in revtex4-1 for putting footnotes in the reference
+ # list), and bibtex must be run to use it. But latexmk needs to
+ # determine the existence of the bib file by use of kpsewhich, otherwise
+ # there is an error. So cope with this situation (and any analogous
+ # cases by adding the aux_dir to the relevant path search environment
+ # variables. BIBINPUTS seems to be the only one currently affected.
+ foreach ( 'BIBINPUTS', 'TEXINPUTS' ) {
+ ensure_path( $_, $aux_dir );
+ }
+ }
+}
+
+#############################################################
+
+sub fix_cmds {
+ # If commands do not have placeholders for %S etc, put them in
+ foreach ($latex, $lualatex, $pdflatex, $xelatex, $lpr, $lpr_dvi, $lpr_pdf,
+ $pdf_previewer, $ps_previewer, $ps_previewer_landscape,
+ $dvi_previewer, $dvi_previewer_landscape,
+ $kpsewhich
+ ) {
+ # Source only
+ if ( $_ && ! /%/ ) { $_ .= " %O %S"; }
+ }
+ foreach ($pdf_previewer, $ps_previewer, $ps_previewer_landscape,
+ $dvi_previewer, $dvi_previewer_landscape,
+ ) {
+ # Run previewers detached
+ if ( $_ && ! /^(nostart|NONE|internal) / ) {
+ $_ = "start $_";
+ }
+ }
+ foreach ($biber, $bibtex) {
+ # Base only
+ if ( $_ && ! /%/ ) { $_ .= " %O %B"; }
+ }
+ foreach ($dvipdf, $ps2pdf) {
+ # Source and dest without flag for destination
+ if ( $_ && ! /%/ ) { $_ .= " %O %S %D"; }
+ }
+ foreach ($dvips, $makeindex) {
+ # Source and dest with -o dest before source
+ if ( $_ && ! /%/ ) { $_ .= " %O -o %D %S"; }
+ }
+ foreach ($dvi_filter, $ps_filter) {
+ # Source and dest, but as filters
+ if ( $_ && ! /%/ ) { $_ .= " %O <%S >%D"; }
+ }
+} #END fix_cmds
+
+#############################################################
+
+sub add_option {
+ # Call add_option( $opt, \$cmd ... )
+ # Add option to one or more commands
+ my $option = shift;
+ while (@_) {
+ if ( ${$_[0]} !~ /%/ ) { &fix_cmds; }
+ ${$_[0]} =~ s/%O/$option %O/;
+ shift;
+ }
+} #END add_option
+
+#############################################################
+
+sub rdb_initialize_rules {
+ # Initialize rule database.
+ # (The rule database may get overridden/extended after the fdb_latexmk
+ # file is read, and after running commands to adjust to dependencies
+ # determined from document.
+ %rule_db = ();
+ %target_rules = ();
+ %target_files = ();
+
+ local %rule_list = ();
+ &rdb_set_rule_templates;
+
+ my %rule_template = %rule_list;
+ while ( my ($key, $value) = each %extra_rule_spec ) {
+ $rule_template{$key} = $value;
+ }
+ foreach my $rule ( keys %rule_template ) {
+ my ( $cmd_type, $ext_cmd, $int_cmd, $source, $dest, $base, $test_kind, $PA_extra_gen ) = @{$rule_template{$rule}};
+ if ( ! $PA_extra_gen ) { $PA_extra_gen = []; }
+ my $needs_making = 0;
+ # Substitute in the filename variables, since we will use
+ # those for determining filenames. But delay expanding $cmd
+ # until run time, in case of changes.
+ foreach ($base, $source, $dest, @$PA_extra_gen ) {
+ s/%R/$root_filename/g;
+ s/%Y/$aux_dir1/;
+ s/%Z/$out_dir1/;
+ }
+ foreach ($source, $dest ) {
+ s/%B/$base/;
+ s/%T/$texfile_name/;
+ }
+ rdb_create_rule( $rule, $cmd_type, $ext_cmd, $int_cmd, $test_kind,
+ $source, $dest, $base,
+ $needs_making, undef, undef, 1, $PA_extra_gen );
+ } # End rule iteration
+
+ # Ensure we only have one way to make pdf file, and that it is appropriate. Remove other incompatibilities
+ if ($pdf_mode == 1) { rdb_deactivate( 'dvipdf', 'ps2pdf', 'latex', 'lualatex', 'xdvipdfmx', 'xelatex' ); }
+ elsif ($pdf_mode == 2) { rdb_deactivate( 'dvipdf', 'pdflatex', 'lualatex', 'xdvipdfmx', 'xelatex' ); }
+ elsif ($pdf_mode == 3) { rdb_deactivate( 'pdflatex', 'ps2pdf', 'lualatex', 'xdvipdfmx', 'xelatex' ); }
+ elsif ($pdf_mode == 4) { rdb_deactivate( 'pdflatex', 'ps2pdf', 'dvipdf', 'xdvipdfmx', 'xelatex' ); }
+ elsif ($pdf_mode == 5) { rdb_deactivate( 'pdflatex', 'ps2pdf', 'dvipdf', 'lualatex' ); }
+ else { rdb_deactivate( 'dvipdf', 'pdflatex', 'ps2pdf', 'lualatex', 'xdvipdfmx', 'xelatex' ); }
+
+ if ($dvi_mode == 1) {
+ rdb_activate( 'latex' );
+ $target_files{$dvi_final} = 1;
+ }
+ if ($postscript_mode == 1) {
+ rdb_activate( 'latex' );
+ $target_files{$ps_final} = 1;
+ }
+ if ($pdf_mode) { $target_files{$pdf_final} = 1; }
+ &rdb_set_rule_net;
+} # END rdb_initialize_rules
+
+#************************************************************
+
+sub rdb_set_rule_templates {
+# Set up specifications for standard rules, adjusted to current conditions
+# Substitutions: %S = source, %D = dest, %B = this rule's base
+# %T = texfile, %R = root = base for latex.
+# %Y for $aux_dir1, %Z for $out_dir1
+
+
+ my $print_file = '';
+ my $print_cmd = 'NONE';
+ if ( $print_type eq 'dvi' ) {
+ $print_file = $dvi_final;
+ $print_cmd = $lpr_dvi;
+ }
+ elsif ( $print_type eq 'pdf' ) {
+ $print_file = $pdf_final;
+ $print_cmd = $lpr_pdf;
+ }
+ elsif ( $print_type eq 'ps' ) {
+ $print_file = $ps_final;
+ $print_cmd = $lpr;
+ }
+ elsif ( $print_type eq 'none' ) {
+ $print_cmd = 'NONE echo NO PRINTING CONFIGURED';
+ }
+
+ my $view_file = '';
+ my $viewer = '';
+ my $viewer_update_method = 0;
+ my $viewer_update_signal = undef;
+ my $viewer_update_command = undef;
+
+ if ( ($view eq 'dvi') || ($view eq 'pdf') || ($view eq 'ps') ) {
+ $view_file = ${$view.'_final'};
+ $viewer = ${$view.'_previewer'};
+ $viewer_update_method = ${$view.'_update_method'};
+ $viewer_update_signal = ${$view.'_update_signal'};
+ if (defined ${$view.'_update_command'}) {
+ $viewer_update_command = ${$view.'_update_command'};
+ }
+ }
+ # Specification of internal command for viewer update:
+ my $PA_update = ['do_update_view', $viewer_update_method, $viewer_update_signal, 0, 1];
+
+# For test_kind: Use file contents for latex and friends, but file time for the others.
+# This is because, especially for dvi file, the contents of the file may contain
+# a pointer to a file to be included, not the contents of the file!
+ %rule_list = (
+ 'latex' => [ 'primary', "$latex", '', "%T", $dvi_name, "%R", 1, [$log_name] ],
+ 'pdflatex' => [ 'primary', "$pdflatex", '', "%T", $pdf_name, "%R", 1, [$log_name] ],
+ 'lualatex' => [ 'primary', "$lualatex", '', "%T", $pdf_name, "%R", 1, [$log_name] ],
+ 'xelatex' => [ 'primary', "$xelatex", '', "%T", $xdv_name, "%R", 1, [$log_name] ],
+ 'dvipdf' => [ 'external', "$dvipdf", 'do_viewfile', $dvi_final, $pdf_name, "%Z%R", 2 ],
+ 'xdvipdfmx' => [ 'external', "$xdvipdfmx", 'do_viewfile', $xdv_final, $pdf_name, "%Z%R", 2 ],
+ 'dvips' => [ 'external', "$dvips", 'do_viewfile', $dvi_final, $ps_name, "%Z%R", 2 ],
+ 'dvifilter' => [ 'external', $dvi_filter, 'do_viewfile', $dvi_name, $dviF_name, "%Z%R", 2 ],
+ 'ps2pdf' => [ 'external', "$ps2pdf", 'do_viewfile', $ps_final, $pdf_name, "%Z%R", 2 ],
+ 'psfilter' => [ 'external', $ps_filter, 'do_viewfile', $ps_name, $psF_name, "%Z%R", 2 ],
+ 'print' => [ 'external', "$print_cmd", 'if_source', $print_file, "", "", 2 ],
+ 'update_view' => [ 'external', $viewer_update_command, $PA_update,
+ $view_file, "", "", 2 ],
+ 'view' => [ 'external', "$viewer", 'if_source', $view_file, "", "", 2 ],
+ );
+} # END rdb_set_rule_templates
+
+#************************************************************
+
+sub rdb_set_rule_net {
+ # Set network of rules, including links
+ &rdb_make_links;
+ &rdb_classify_rules;
+}
+
+#************************************************************
+
+sub rdb_make_links {
+# ?? Problem if there are multiple rules for getting a file. Notably pdf.
+# Which one to choose?
+# ?? Problem: what if a rule is inactive,
+# e.g., bibtex because biber is in use,
+# or xelatex when pdflatex is in use
+# or bibtex when $bibtex_use is 0.
+# What if both latex and pdflatex are being used?
+# That has been allowed. But .aux file (also
+# .log file) are made by both.
+
+# Other case: package (like bibtopic) creates bbl or other file when
+# it doesn't exist. Later a rule is created by latexmk to make that
+# file. Then the rule's main destination file should have priority
+# over non-main generated files from other rules.
+ local %from_rules_old = %from_rules;
+ &rdb_cache_generated;
+ rdb_for_actives(
+ 0,
+ sub{
+ if ( exists $from_rules_main{$file} ) {
+ $$Pfrom_rule = $from_rules_main{$file};
+ }
+ elsif ( exists $from_rules{$file} ) {
+ $$Pfrom_rule = $from_rules{$file};
+ }
+ if ( $$Pfrom_rule && (! rdb_rule_exists( $$Pfrom_rule ) ) ) {
+ $$Pfrom_rule = '';
+ }
+ }
+ );
+ rdb_for_actives( \&rdb_set_source_rules );
+# &rdb_show;
+} # END rdb_make_links
+
+#************************************************************
+
+sub rdb_set_source_rules {
+ # This applies to rules whose source file is a dvi or xdv file
+ # Uses rule context
+ my ($base, $path, $ext) = fileparseA( $$Psource );
+ if ( ($ext eq '.dvi') || ($ext eq '.dviF') || ($ext eq '.xdv') ) {
+ my $old_rule = $from_rules_old{$$Psource};
+ my $new_rule = $from_rules{$$Psource};
+ if ( defined $old_rule
+ && defined $new_rule
+ && ($old_rule eq $new_rule)
+ && defined $$PHsource_rules{$new_rule}
+ )
+ { # Nothing to do: source rule is correct.
+ }
+ else {
+ if ( defined $old_rule ) { delete $$PHsource_rules{$old_rule}; }
+ if ( defined $new_rule ) { $$PHsource_rules{$new_rule} = 0; }
+ }
+ }
+}
+
+#************************************************************
+
+sub rdb_cache_generated {
+ # Update %from_rules
+ %from_rules = ();
+ %from_rules_main = ();
+ rdb_for_actives( \&one_from_main_rule_cache );
+ rdb_for_actives( \&one_from_rule_cache );
+} # END rdb_cache_generated
+
+#------------
+
+sub one_from_main_rule_cache {
+ # Rule context assumed.
+ # Set from_rules_main items for one rule
+ if (! $$Pdest) { return; }
+# Error message trigger: if ( $$Pdest =~ /pdf$/) { $from_rules_main{$$Pdest} = 'pdflatexA'; }
+ if ( exists $from_rules_main{$$Pdest} ) {
+ my $old_rule = $from_rules_main{$$Pdest};
+ if ( $old_rule eq $rule ) {
+ # OK
+ }
+ else {
+ warn "$My_name: Possible bug:\n",
+ " In linking rules I already set from_rules_main{$$Pdest} to '$old_rule'\n",
+ " But now I want to set it to '$rule'\n";
+#???? traceback();
+ }
+ }
+ $from_rules_main{$$Pdest} = $rule;
+} # END one_from_main_rule_cache
+
+#------------
+
+sub one_from_rule_cache {
+ # Rule context assumed.
+ # Set from_rules items for one rule
+ foreach ( @$PA_extra_gen, keys %$PHdest ) {
+ # Error message trigger: if ( $_ =~ /aux$/) { $from_rules{$_} = 'pdflatexA'; }
+ if ( exists $from_rules{$_} ) {
+ my $old_rule = $from_rules{$_};
+ if ( $old_rule eq $rule ) {
+ # OK
+ }
+ elsif ( exists $from_rules_main{$_} && ( $from_rules_main{$_} ne $rule ) ) {
+ warn "$My_name: Possible problem:\n",
+ " In linking rules, I already set from_rules_main{$_}\n".
+ " to '$from_rules_main{$_}'\n",
+ " But now I also have a different rule '$rule' that also made the file.\n";
+ }
+ elsif ( exists($possible_primaries{$old_rule}) && exists($possible_primaries{$rule}) ) {
+ # This could be problematic. But we'll let it go,
+ # because it is a common case for .aux and .log files
+ # (etc), and these cases do not appear to mess up
+ # anything (by experience).
+ # Once we allow an active flag for rules and only
+ # examine active rules, the only case of this that
+ # will appear (in the absence of other problems) will
+ # be where two primary rules are active, notably a
+ # latex rule to make dvi and a pdflatex (or other
+ # rule) to make pdf.
+ }
+ else {
+ warn "$My_name: Possible bug:\n",
+ " In linking rules I already set from_rules{$_} to '$old_rule'\n",
+ " But now I want to set it to '$rule'\n";
+#???? traceback();
+ }
+ }
+ $from_rules{$_} = $rule;
+ }
+} #END from_rule_cache
+
+#************************************************************
+
+sub set_trivial_aux_fdb {
+ # 1. Write aux file EXACTLY as would be written if the tex file
+ # had no cross references, etc. I.e., a minimal .aux file.
+ # 2. Write a corresponding fdb file
+ # 3. Provoke a run of (pdf)latex (actually of all primaries).
+
+ local *aux_file;
+ open( aux_file, '>', $aux_main )
+ or die "Cannot write file '$aux_main'\n";
+ print aux_file "\\relax \n";
+ close(aux_file);
+
+ foreach my $rule (keys %possible_primaries ) {
+ rdb_ensure_file( $rule, $texfile_name );
+ rdb_ensure_file( $rule, $aux_main );
+ rdb_one_rule( $rule,
+ sub{ $$Pout_of_date = 1; }
+ );
+ }
+ &rdb_write( $fdb_name );
+} #END set_trivial_aux_fdb
+
+#************************************************************
+#### Particular actions
+#************************************************************
+#************************************************************
+
+sub do_cusdep {
+ # Unconditional application of custom-dependency
+ # except that rule is not applied if the source file source
+ # does not exist, and an error is returned if the dest is not made.
+ #
+ # Assumes rule context for the custom-dependency, and that my first
+ # argument is the name of the subroutine to apply
+ my $func_name = $_[0];
+ my $return = 0;
+ if ( !-e $$Psource ) {
+ # Source does not exist. Users of this rule will need to turn
+ # it off when custom dependencies are reset
+ if ( !$silent ) {
+## ??? Was commented out. 1 Sep. 2008 restored, for cusdep no-file-exists issue
+ warn "$My_name: In trying to apply custom-dependency rule\n",
+ " to make '$$Pdest' from '$$Psource'\n",
+ " the source file has disappeared since the last run\n";
+ }
+ # Treat as successful
+ }
+ elsif ( !$func_name ) {
+ warn "$My_name: Possible misconfiguration or bug:\n",
+ " In trying to apply custom-dependency rule\n",
+ " to make '$$Pdest' from '$$Psource'\n",
+ " the function name is blank.\n";
+ }
+ elsif ( ! defined &$func_name ) {
+ warn "$My_name: Misconfiguration or bug,",
+ " in trying to apply custom-dependency rule\n",
+ " to make '$$Pdest' from '$$Psource'\n",
+ " function name '$func_name' does not exists.\n";
+ }
+ else {
+ my $cusdep_ret = &$func_name( $$Pbase );
+ if ( defined $cusdep_ret && ($cusdep_ret != 0) ) {
+ $return = $cusdep_ret;
+ if ($return) {
+ warn "Rule '$rule', function '$func_name'\n",
+ " failed with return code = $return\n";
+ }
+ }
+ elsif ( !-e $$Pdest ) {
+ # Destination non-existent, but routine failed to give an error
+ warn "$My_name: In running custom-dependency rule\n",
+ " to make '$$Pdest' from '$$Psource'\n",
+ " function '$func_name' did not make the destination.\n";
+ $return = -1;
+ }
+ }
+ return $return;
+} # END do_cusdep
+
+#************************************************************
+
+sub do_viewfile {
+ # Unconditionally make file for viewing, going through temporary file if
+ # Assumes rule context
+
+ my $return = 0;
+ my ($base, $path, $ext) = fileparseA( $$Pdest );
+ if ( &view_file_via_temporary ) {
+ if ( $$Pext_cmd =~ /%D/ ) {
+ my $tmpfile = tempfile1( "${root_filename}_tmp", $ext );
+ warn "$My_name: Making '$$Pdest' via temporary '$tmpfile'...\n";
+ $return = &Run_subst( undef, undef, undef, undef, $tmpfile );
+ move( $tmpfile, $$Pdest );
+ }
+ else {
+ warn "$My_name is configured to make '$$Pdest' via a temporary file\n",
+ " but the command template '$$Pext_cmd' does not have a slot\n",
+ " to set the destination file, so I won't use a temporary file\n";
+ $return = &Run_subst();
+ }
+ }
+ else {
+ $return = &Run_subst();
+ }
+ return $return;
+} #END do_viewfile
+
+#************************************************************
+
+sub do_update_view {
+ # Update viewer
+ # Assumes rule context
+ # Arguments: (method, signal, viewer_process)
+
+ my $return = 0;
+
+ # Although the process is passed as an argument, we'll need to update it.
+ # So (FUDGE??) bypass the standard interface for the process.
+ # We might as well do this for all the arguments.
+ my $viewer_update_method = ${$PAint_cmd}[1];
+ my $viewer_update_signal = ${$PAint_cmd}[2];
+ my $Pviewer_process = \${$PAint_cmd}[3];
+ my $Pneed_to_get_viewer_process = \${$PAint_cmd}[4];
+
+ if ($viewer_update_method == 2) {
+ if ($$Pneed_to_get_viewer_process) {
+ $$Pviewer_process = &find_process_id( $$Psource );
+ if ($$Pviewer_process != 0) {
+ $$Pneed_to_get_viewer_process = 0;
+ }
+ }
+ if ($$Pviewer_process == 0) {
+ print "$My_name: need to signal viewer for file '$$Psource', but didn't get \n",
+ " process ID for some reason, e.g., no viewer, bad configuration, bug\n"
+ if $diagnostics ;
+ }
+ elsif ( defined $viewer_update_signal) {
+ print "$My_name: signalling viewer, process ID $$Pviewer_process ",
+ "with signal $viewer_update_signal\n"
+ if $diagnostics ;
+ kill $viewer_update_signal, $$Pviewer_process;
+ }
+ else {
+ warn "$My_name: viewer is supposed to be sent a signal\n",
+ " but no signal is defined. Misconfiguration or bug?\n";
+ $return = 1;
+ }
+ }
+ elsif ($viewer_update_method == 4) {
+ if (defined $$Pext_cmd) {
+ $return = &Run_subst();
+ }
+ else {
+ warn "$My_name: viewer is supposed to be updated by running a command,\n",
+ " but no command is defined. Misconfiguration or bug?\n";
+ }
+ }
+ return $return;
+} #END do_update_view
+
+#************************************************************
+
+sub if_source {
+ # Unconditionally apply rule if source file exists.
+ # Assumes rule context
+ if ( -e $$Psource ) {
+ return &Run_subst();
+ }
+ else {
+ warn "Needed source file '$$Psource' does not exist.\n";
+ return -1;
+ }
+} #END if_source
+
+#************************************************************
+#### Subroutines
+#************************************************************
+#************************************************************
+
+sub find_basename {
+ # Finds the basename of the root file
+ # Arguments:
+ # 1 - Filename to breakdown
+ # 2 - Where to place base file
+ # 3 - Where to place tex file
+ # Returns non-zero if tex file does not exist
+ #
+ # The rules for determining this depend on the implementation of TeX.
+ # The variable $extension_treatment determines which rules are used.
+
+ # !!!!!!!! I still need to implement use of kpsewhich to match behavior
+ # of (pdf)latex correctly.
+
+ local($given_name, $base_name, $ext, $path, $tex_name);
+ $given_name = $_[0];
+ if ( "$extension_treatment" eq "miktex_old" ) {
+ # Miktex v. 1.20d:
+ # 1. If the filename has an extension, then use it.
+ # 2. Else append ".tex".
+ # 3. The basename is obtained from the filename by
+ # removing the path component, and the extension, if it
+ # exists. If a filename has a multiple extension, then
+ # all parts of the extension are removed.
+ # 4. The names of generated files (log, aux) are obtained by
+ # appending .log, .aux, etc to the basename. Note that
+ # these are all in the CURRENT directory, and the drive/path
+ # part of the originally given filename is ignored.
+ #
+ # Thus when the given filename is "\tmp\a.b.c", the tex
+ # filename is the same, and the basename is "a".
+
+ ($base_name, $path, $ext) = fileparse( $given_name, '\..*' );
+ if ( "$ext" eq "") { $tex_name = "$given_name.tex"; }
+ else { $tex_name = $given_name; }
+ $_[1] = $base_name;
+ $_[2] = $tex_name;
+ }
+ elsif ( "$extension_treatment" eq "unix" ) {
+ # unix (at least TeXLive 2016) =>
+ # A. Finding of tex file:
+ # 1. If filename.tex exists, use it,
+ # 2. else if kpsewhich finds filename.tex, use it
+ # 3. else if filename exists, use it,
+ # 4. else if kpsewhich finds filename, use it.
+ # (Probably can unify the above by
+ # 1'. If kpsewhich finds filename.tex, use result.
+ # 2'. else if kpsewhich finds filename, use result.
+ # 3'. else report file not found.
+ # B. The base filename is obtained by deleting the path
+ # component and, if an extension exists, the last
+ # component of the extension, even if the extension is
+ # null. (A name ending in "." has a null extension.)
+ # C. The names of generated files (log, aux) are obtained by
+ # appending .log, .aux, etc to the basename. Note that
+ # these are all in the CURRENT directory, and the drive/path
+ # part of the originally given filename is ignored.
+ #
+ # Thus when the given filename is "/tmp/a.b.c", there are two
+ # cases:
+ # a. /tmp/a.b.c.tex exists. Then this is the tex file,
+ # and the basename is "a.b.c".
+ # b. /tmp/a.b.c.tex does not exist. Then the tex file is
+ # "/tmp/a.b.c", and the basename is "a.b".
+ # But there are also modifications of this when a file can be
+ # found by kpsewhich.
+
+ if ( -f "$given_name.tex" ) {
+ $tex_name = "$given_name.tex";
+ }
+ else {
+ $tex_name = "$given_name";
+ }
+ ($base_name, $path, $ext) = fileparse( $tex_name, '\.[^\.]*' );
+ $_[1] = $base_name;
+ $_[2] = $tex_name;
+ }
+ else {
+ die "$My_name: Incorrect configuration gives \$extension_treatment=",
+ "'$extension_treatment'\n";
+ }
+ if ($diagnostics) {
+ print "Given='$given_name', tex='$tex_name', base='$base_name'\n";
+ }
+ return ! -e $tex_name;
+} #END find_basename
+
+#************************************************************
+
+sub make_preview_continuous {
+ local @changed = ();
+ local %changed_rules = ();
+ local @changed_user = ();
+ local @disappeared = ();
+ local @no_dest = (); # Non-existent destination files
+ local @rules_never_run = ();
+ local @rules_to_apply = ();
+
+ local $failure = 0;
+ local %rules_applied = ();
+ local $updated = 0;
+
+ print "======= Need to update make_preview_continuous for target files\n";
+
+ $quell_uptodate_msgs = 1;
+
+ local $view_file = '';
+ rdb_one_rule( 'view', sub{ $view_file = $$Psource; } );
+
+ if ( ($view eq 'dvi') || ($view eq 'pdf') || ($view eq 'ps') ) {
+ warn "Viewing $view\n";
+ }
+ elsif ( $view eq 'none' ) {
+ warn "Not using a previewer\n";
+ $view_file = '';
+ }
+ else {
+ warn "$My_name: BUG: Invalid preview method '$view'\n";
+ exit 20;
+ }
+
+ my $viewer_running = 0; # No viewer known to be running yet
+ # Get information from update_view rule
+ local $viewer_update_method = 0;
+ # Pointers so we can update the following:
+ local $Pviewer_process = undef;
+ local $Pneed_to_get_viewer_process = undef;
+ rdb_one_rule( 'update_view',
+ sub{ $viewer_update_method = $$PAint_cmd[1];
+ $Pviewer_process = \$$PAint_cmd[3];
+ $Pneed_to_get_viewer_process = \$$PAint_cmd[4];
+ }
+ );
+ # Note that we don't get the previewer process number from the program
+ # that starts it; that might only be a script to get things set up and the
+ # actual previewer could be (and sometimes IS) another process.
+
+ if ( ($view_file ne '') && (-e $view_file) && !$new_viewer_always ) {
+ # Is a viewer already running?
+ # (We'll save starting up another viewer.)
+ $$Pviewer_process = &find_process_id( $view_file );
+ if ( $$Pviewer_process ) {
+ warn "$My_name: Previewer is already running\n"
+ if !$silent;
+ $viewer_running = 1;
+ $$Pneed_to_get_viewer_process = 0;
+ }
+ }
+
+ # Loop forever, rebuilding .dvi and .ps as necessary.
+ # Set $first_time to flag first run (to save unnecessary diagnostics)
+ my $last_action_time = time();
+ my $timed_out = 0;
+CHANGE:
+ for (my $first_time = 1; 1; $first_time = 0 ) {
+
+ my %rules_to_watch = array_to_hash( &rdb_accessible );
+
+ $updated = 0;
+ $failure = 0;
+ $failure_msg = '';
+ if ( $MSWin_fudge_break && ($^O eq "MSWin32") ) {
+ # Fudge under MSWin32 ONLY, to stop perl/latexmk from
+ # catching ctrl/C and ctrl/break, and let it only reach
+ # downstream programs. See comments at first definition of
+ # $MSWin_fudge_break.
+ $SIG{BREAK} = $SIG{INT} = 'IGNORE';
+ }
+ if ($compiling_cmd) {
+ Run_subst( $compiling_cmd );
+ }
+ $failure = &rdb_make;
+
+## warn "=========Viewer PID = $$Pviewer_process; updated=$updated\n";
+
+ if ( $MSWin_fudge_break && ($^O eq "MSWin32") ) {
+ $SIG{BREAK} = $SIG{INT} = 'DEFAULT';
+ }
+ # Start viewer if needed.
+ if ( ($failure > 0) && (! $force_mode) ) {
+ # No viewer yet
+ }
+ elsif ( ($view_file ne '') && (-e $view_file) && $updated && $viewer_running ) {
+ # A viewer is running. Explicitly get it to update screen if we have to do it:
+ rdb_one_rule( 'update_view', \&rdb_run1 );
+ }
+ elsif ( ($view_file ne '') && (-e $view_file) && !$viewer_running ) {
+ # Start the viewer
+ if ( !$silent ) {
+ if ($new_viewer_always) {
+ warn "$My_name: starting previewer for '$view_file'\n",
+ "------------\n";
+ }
+ else {
+ warn "$My_name: I have not found a previewer that ",
+ "is already running. \n",
+ " So I will start it for '$view_file'\n",
+ "------------\n";
+ }
+ }
+ local $retcode = 0;
+ rdb_one_rule( 'view', sub { $retcode = &rdb_run1;} );
+ if ( $retcode != 0 ) {
+ if ($force_mode) {
+ warn "$My_name: I could not run previewer\n";
+ }
+ else {
+ &exit_msg1( "I could not run previewer", $retcode);
+ }
+ }
+ else {
+ $viewer_running = 1;
+ $$Pneed_to_get_viewer_process = 1;
+ } # end analyze result of trying to run viewer
+ } # end start viewer
+ if ( $failure > 0 ) {
+ if ( !$failure_msg ) {
+ $failure_msg = 'Failure to make the files correctly';
+ }
+ &rdb_set_rule_net;
+ %rules_to_watch = array_to_hash( &rdb_accessible );
+
+ # There will be files changed during the run that are irrelevant.
+ # We need to wait for the user to change the files.
+
+ # So set the GENERATED files from (pdf)latex as up-to-date:
+ rdb_for_some( [keys %current_primaries], \&rdb_update_gen_files );
+
+ # And don't watch for changes for post_primary rules (ps and pdf
+ # from dvi, etc haven't been run after an error in (pdf)latex, so
+ # are out-of-date by filetime criterion, but they should not be run
+ # until after another (pdf)latex run:
+ foreach (@post_primary) { delete $rules_to_watch{$_}; }
+
+ $failure_msg =~ s/\s*$//; #Remove trailing space
+ warn "$My_name: $failure_msg\n",
+ " ==> You will need to change a source file before I do another run <==\n";
+ if ($failure_cmd) {
+ Run_subst( $failure_cmd );
+ }
+ }
+ else {
+ if ( ($#primary_warning_summary > -1) && $warning_cmd ) {
+ Run_subst( $warning_cmd );
+ }
+ elsif ( ($#primary_warning_summary > -1) && $warnings_as_errors && $failure_cmd ) {
+ Run_subst( $failure_cmd );
+ }
+ elsif ($success_cmd) {
+ Run_subst( $success_cmd );
+ }
+ }
+ rdb_show_rule_errors();
+ if ($rules_list) { rdb_list(); }
+ if ($show_time && ! $first_time) { show_timing(); }
+ if ( $dependents_list && ($updated || $failure) ) {
+ my $deps_handle = new FileHandle "> $deps_file";
+ if ( defined $deps_handle ) {
+ deps_list($deps_handle);
+ close($deps_handle);
+ }
+ else {
+ warn "Cannot open '$deps_file' for output of dependency information\n";
+ }
+ }
+
+ # Now wait for a file to change...
+ &rdb_cache_generated;
+ # During waiting for file changes, handle ctrl/C and ctrl/break here,
+ # rather than letting system handle them by terminating script (and
+ # code in the following command line to work: any script that calls
+ # it). This allows, for example, the command cleanup in the following
+ # command line to work:
+ # latexmk -pvc foo; cleanup;
+ &catch_break;
+ $have_break = 0;
+ $last_action_time = time();
+ $waiting = 1;
+ print "\n=== Watching for updated files. Use ctrl/C to stop ...\n";
+ WAIT: while (1) {
+ sleep( $sleep_time );
+ if ($have_break) { last WAIT; }
+ if ( rdb_user_changes(keys %rules_to_watch) ) {
+ if (!$silent) {
+ warn "$My_name: Need to remake files.\n";
+ &rdb_diagnose_changes( ' ' );
+ }
+ last WAIT;
+ }
+ # Don't count waiting time in processing:
+ $processing_time1 = processing_time();
+ # Does this do this job????
+ local $new_files = 0;
+ rdb_for_some( [keys %current_primaries], sub{ $new_files += &rdb_find_new_files } );
+ if ($new_files > 0) {
+ warn "$My_name: New file(s) found.\n";
+ last WAIT;
+ }
+ if ($have_break) { last WAIT; }
+ if ($pvc_timeout && ( time() > $last_action_time+60*$pvc_timeout_mins ) ) {
+ $timed_out = 1;
+ last WAIT;
+ }
+ } # end WAIT:
+ &default_break;
+ if ($have_break) {
+ print "$My_name: User typed ctrl/C or ctrl/break. I'll finish.\n";
+ return;
+ }
+ if ($timed_out) {
+ print "$My_name: More than $pvc_timeout_mins mins of inactivity. I'll finish.\n";
+ return;
+ }
+ $waiting = 0; if ($diagnostics) { warn "NOT WAITING\n"; }
+ } #end infinite_loop CHANGE:
+} #END sub make_preview_continuous
+
+#************************************************************
+
+sub process_rc_file {
+ # Usage process_rc_file( filename )
+ # NEW VERSION
+ # Run rc_file whose name is given in first argument
+ # Exit with code 0 on success
+ # Exit with code 1 if file cannot be read or does not exist.
+ # Stop if there is a syntax error or other problem.
+ # PREVIOUSLY:
+ # Exit with code 2 if is a syntax error or other problem.
+ my $rc_file = $_[0];
+ my $ret_code = 0;
+ warn "$My_name: Executing Perl code in file '$rc_file'...\n"
+ if $diagnostics;
+ # I could use the do command of perl, but the preceeding -r test
+ # to get good diagnostics gets the wrong result under cygwin
+ # (e.g., on /cygdrive/c/latexmk/LatexMk)
+ my $RCH = new FileHandle;
+ if ( !-e $rc_file ) {
+ warn "$My_name: The rc-file '$rc_file' does not exist\n";
+ return 1;
+ }
+ elsif ( -d $rc_file ) {
+ warn "$My_name: The supposed rc-file '$rc_file' is a directory; but it\n",
+ " should be a normal text file\n";
+ return 1;
+ }
+ elsif ( open $RCH, "<$rc_file" ) {
+ { local $/; eval <$RCH>; }
+ close $RCH;
+ }
+ else {
+ warn "$My_name: I cannot read the rc-file '$rc_file'\n";
+ return 1;
+ }
+ # PREVIOUS VERSION
+# if ( ! -r $rc_file ) {
+# warn "$My_name: I cannot read the rc-file '$rc_file'\n",
+# " or at least that's what Perl (for $^O) reports\n";
+# return 1;
+# }
+# do( $rc_file );
+ if ( $@ ) {
+ # Indent each line of possibly multiline message:
+ my $message = prefix( $@, " " );
+ warn "$My_name: Initialization file '$rc_file' gave an error:\n",
+ "$message\n";
+ die "$My_name: Stopping because of problem with rc file\n";
+ # Use the following if want non-fatal error.
+ return 2;
+ }
+ return 0;
+} #END process_rc_file
+
+#************************************************************
+
+sub execute_code_string {
+ # Usage execute_code_string( string_of_code )
+ # Run the perl code contained in first argument
+ # Halt if there is a syntax error or other problem.
+ # ???Should I leave the exiting to the caller (perhaps as an option)?
+ # But I can always catch it with an eval if necessary.
+ # That confuses ctrl/C and ctrl/break handling.
+ my $code = $_[0];
+ warn "$My_name: Executing initialization code specified by -e:\n",
+ " '$code'...\n"
+ if $diagnostics;
+ eval $code;
+ # The return value from the eval is not useful, since it is the value of
+ # the last expression evaluated, which could be anything.
+ # The correct test of errors is on the value of $@.
+
+ if ( $@ ) {
+ # Indent each line of possibly multiline message:
+ my $message = prefix( $@, " " );
+ die "$My_name: ",
+ "Stopping because executing following code from command line\n",
+ " $code\n",
+ "gave an error:\n",
+ "$message\n";
+ }
+} #END execute_code_string
+
+#************************************************************
+
+sub cleanup1 {
+ # Usage: cleanup1( directory, exts_without_period, ... )
+ #
+ # The directory and the root file name are fixed names, so I must escape
+ # any glob metacharacters in them:
+ my $dir = fix_pattern( shift );
+ my $root_fixed = fix_pattern( $root_filename );
+ foreach (@_) {
+ my $name = /%R/ ? $_ : "%R.$_";
+ $name =~ s/%R/${root_fixed}/;
+ $name = $dir.$name;
+ unlink_or_move( my_glob( "$name" ) );
+ }
+} #END cleanup1
+
+#************************************************************
+
+sub cleanup_cusdep_generated {
+ # Remove files generated by custom dependencies
+ rdb_for_actives( \&cleanup_one_cusdep_generated );
+} #END cleanup_cusdep_generated
+
+#************************************************************
+
+sub cleanup_one_cusdep_generated {
+ # Remove destination file generated by one custom dependency
+ # Assume rule context, but not that the rule is a custom dependency.
+ # Only delete destination file if source file exists (so destination
+ # file can be recreated)
+ if ( $$Pcmd_type ne 'cusdep' ) {
+ # NOT cusdep
+ return;
+ }
+ if ( ! -e $$Psource ) {
+ warn "$My_name: For custom dependency '$rule',\n",
+ " I won't delete destination file '$$Pdest'\n",
+ " and any other generated files,\n",
+ " because the source file '$$Psource' doesn't exist,\n",
+ " so the destination file may not be able to be recreated\n";
+ return;
+ }
+ unlink_or_move( $$Pdest, keys %$PHdest );
+} #END cleanup_one_cusdep_generated
+
+#************************************************************
+#************************************************************
+#************************************************************
+
+# Error handling routines, warning routines, help
+
+#************************************************************
+
+sub die_trace {
+ # Call: die_trace( message );
+ &traceback; # argument(s) passed unchanged
+ die "\n";
+} #END die_trace
+
+#************************************************************
+
+sub traceback {
+ # Call: &traceback
+ # or traceback( message, )
+ my $msg = shift;
+ if ($msg) { warn "$msg\n"; }
+ warn "Traceback:\n";
+ my $i=0; # Start with immediate caller
+ while ( my ($pack, $file, $line, $func) = caller($i++) ) {
+ if ($func eq 'die_trace') { next; }
+ warn " $func called from line $line\n";
+ }
+} #END traceback
+
+#************************************************************
+
+sub exit_msg1
+{
+ # exit_msg1( error_message, retcode [, action])
+ # 1. display error message
+ # 2. if action set, then restore aux file
+ # 3. exit with retcode
+ warn "\n------------\n";
+ warn "$My_name: $_[0].\n";
+ warn "-- Use the -f option to force complete processing.\n";
+
+ my $retcode = $_[1];
+ if ($retcode >= 256) {
+ # Retcode is the kind returned by system from an external command
+ # which is 256 * command's_retcode
+ $retcode /= 256;
+ }
+ exit $retcode;
+} #END exit_msg1
+
+#************************************************************
+
+sub warn_running {
+ # Message about running program:
+ if ( $silent ) {
+ warn "$My_name: @_\n";
+ }
+ else {
+ warn "------------\n@_\n------------\n";
+ }
+} #END warn_running
+
+#************************************************************
+
+sub exit_help
+# Exit giving diagnostic from arguments and how to get help.
+{
+ warn "\n$My_name: @_\n",
+ "Use\n",
+ " $my_name -help\nto get usage information\n";
+ exit 10;
+} #END exit_help
+
+
+#************************************************************
+
+sub print_help
+{
+ print
+ "$My_name $version_num: Automatic LaTeX document generation routine\n\n",
+ "Usage: $my_name [latexmk_options] [filename ...]\n\n",
+ " Latexmk_options:\n",
+ " -aux-directory=dir or -auxdir=dir \n",
+ " - set name of directory for auxiliary files (aux, log)\n",
+ " - Currently this only works with MiKTeX\n",
+ " -bibtex - use bibtex when needed (default)\n",
+ " -bibtex- - never use bibtex\n",
+ " -bibtex-cond - use bibtex when needed, but only if the bib file exists\n",
+ " -bibtex-cond1 - use bibtex when needed, but only if the bib file exists;\n",
+ " on cleanup delete bbl file only if bib file exists\n",
+ " -bm <message> - Print message across the page when converting to postscript\n",
+ " -bi <intensity> - Set contrast or intensity of banner\n",
+ " -bs <scale> - Set scale for banner\n",
+ " -commands - list commands used by $my_name for processing files\n",
+ " -c - clean up (remove) all nonessential files, except\n",
+ " dvi, ps and pdf files.\n",
+ " This and the other clean-ups are instead of a regular make.\n",
+ " -C - clean up (remove) all nonessential files\n",
+ " including aux, dep, dvi, postscript and pdf files\n",
+ " and file of database of file information\n",
+ " -CA - clean up (remove) all nonessential files.\n",
+ " Equivalent to -C option.\n",
+ " -CF - Remove file of database of file information before doing \n",
+ " other actions\n",
+ " -cd - Change to directory of source file when processing it\n",
+ " -cd- - Do NOT change to directory of source file when processing it\n",
+ " -dependents or -deps - Show list of dependent files after processing\n",
+ " -dependents- or -deps- - Do not show list of dependent files\n",
+ " -deps-out=file - Set name of output file for dependency list,\n",
+ " and turn on showing of dependency list\n",
+ " -dF <filter> - Filter to apply to dvi file\n",
+ " -dvi - generate dvi\n",
+ " -dvi- - turn off required dvi\n",
+ " -e <code> - Execute specified Perl code (as part of latexmk start-up\n",
+ " code)\n",
+ " -f - force continued processing past errors\n",
+ " -f- - turn off forced continuing processing past errors\n",
+ " -gg - Super go mode: clean out generated files (-CA), and then\n",
+ " process files regardless of file timestamps\n",
+ " -g - process regardless of file timestamps\n",
+ " -g- - Turn off -g\n",
+ " -h - print help\n",
+ " -help - print help\n",
+ " -jobname=STRING - set basename of output file(s) to STRING.\n",
+ " (Like --jobname=STRING on command line for many current\n",
+ " implementations of latex/pdflatex.)\n",
+ " -l - force landscape mode\n",
+ " -l- - turn off -l\n",
+ " -latex=<program> - set program used for latex.\n",
+ " (replace '<program>' by the program name)\n",
+ " -latexoption=<option> - add the given option to the (pdf)latex command\n",
+ " -logfilewarninglist or -logfilewarnings \n",
+ " give list of warnings after run of (pdf)latex\n",
+ " -logfilewarninglist- or -logfilewarnings- \n",
+ " do not give list of warnings after run of (pdf)latex\n",
+ " -lualatex - use lualatex for processing files to pdf\n",
+ " and turn dvi/ps modes off\n",
+ " -M - Show list of dependent files after processing\n",
+ " -MF file - Specifies name of file to receives list dependent files\n",
+ " -MP - List of dependent files includes phony target for each source file.\n",
+ " -new-viewer - in -pvc mode, always start a new viewer\n",
+ " -new-viewer- - in -pvc mode, start a new viewer only if needed\n",
+ " -nobibtex - never use bibtex\n",
+ " -nodependents - Do not show list of dependent files after processing\n",
+ " -norc - omit automatic reading of system, user and project rc files\n",
+ " -output-directory=dir or -outdir=dir\n",
+ " - set name of directory for output files\n",
+ " -pdf - generate pdf by pdflatex\n",
+ " -pdfdvi - generate pdf by dvipdf\n",
+ " -pdflatex=<program> - set program used for pdflatex.\n",
+ " (replace '<program>' by the program name)\n",
+ " -pdflualatex=<program> - set program used for lualatex.\n",
+ " (replace '<program>' by the program name)\n",
+ " -pdfps - generate pdf by ps2pdf\n",
+ " -pdflua - generate pdf by lualatex\n",
+ " -pdfxe - generate pdf by xelatex\n",
+ " -pdfxelatex=<program> - set program used for xelatex.\n",
+ " (replace '<program>' by the program name)\n",
+ " -pdf- - turn off pdf\n",
+ " -ps - generate postscript\n",
+ " -ps- - turn off postscript\n",
+ " -pF <filter> - Filter to apply to postscript file\n",
+ " -p - print document after generating postscript.\n",
+ " (Can also .dvi or .pdf files -- see documentation)\n",
+ " -pretex=<TeX code> - Sets TeX code to be executed before inputting source\n",
+ " file, if commands suitable configured\n",
+ " -print=dvi - when file is to be printed, print the dvi file\n",
+ " -print=ps - when file is to be printed, print the ps file (default)\n",
+ " -print=pdf - when file is to be printed, print the pdf file\n",
+ " -pv - preview document. (Side effect turn off continuous preview)\n",
+ " -pv- - turn off preview mode\n",
+ " -pvc - preview document and continuously update. (This also turns\n",
+ " on force mode, so errors do not cause $my_name to stop.)\n",
+ " (Side effect: turn off ordinary preview mode.)\n",
+ " -pvc- - turn off -pvc\n",
+ " -pvctimeout - timeout in pvc mode after period of inactivity\n",
+ " -pvctimeout- - don't timeout in pvc mode after inactivity\n",
+ " -pvctimeoutmins=<time> - set period of inactivity (minutes) for pvc timeout\n",
+ " -quiet - silence progress messages from called programs\n",
+ " -r <file> - Read custom RC file\n",
+ " (N.B. This file could override options specified earlier\n",
+ " on the command line.)\n",
+ " -recorder - Use -recorder option for (pdf)latex\n",
+ " (to give list of input and output files)\n",
+ " -recorder- - Do not use -recorder option for (pdf)latex\n",
+ " -rules - Show list of rules after processing\n",
+ " -rules- - Do not show list of rules after processing\n",
+ " -showextraoptions - Show other allowed options that are simply passed\n",
+ " as is to latex and pdflatex\n",
+ " -silent - silence progress messages from called programs\n",
+ " -stdtexcmds - Sets standard commands for *latex\n",
+ " -time - show CPU time used\n",
+ " -time- - don't show CPU time used\n",
+ " -use-make - use the make program to try to make missing files\n",
+ " -use-make- - don't use the make program to try to make missing files\n",
+ " -usepretex - Sets commands for *latex to use extra code before inputting\n",
+ " source file\n",
+ " -usepretex=<TeX code> - Equivalent to -pretex=<TeX code> -usepretex\n",
+ " -v - display program version\n",
+ " -verbose - display usual progress messages from called programs\n",
+ " -version - display program version\n",
+ " -view=default - viewer is default (dvi, ps, pdf)\n",
+ " -view=dvi - viewer is for dvi\n",
+ " -view=none - no viewer is used\n",
+ " -view=ps - viewer is for ps\n",
+ " -view=pdf - viewer is for pdf\n",
+ " -Werror - treat warnings from called programs as errors\n",
+ " -xelatex - use xelatex for processing files to pdf\n",
+ " and turn dvi/ps modes off\n",
+ "\n",
+ " filename = the root filename of LaTeX document\n",
+ "\n",
+ "-p, -pv and -pvc are mutually exclusive\n",
+ "-h, -c and -C override all other options.\n",
+ "-pv and -pvc require one and only one filename specified\n",
+ "All options can be introduced by '-' or '--'. (E.g., --help or -help.)\n",
+ " \n",
+ "In addition, latexmk recognizes many other options that are passed to\n",
+ "latex and/or pdflatex without interpretation by latexmk. Run latexmk\n",
+ "with the option -showextraoptions to see a list of these\n",
+ "\n",
+ "Report bugs etc to John Collins <jcc8 at psu.edu>.\n";
+
+} #END print_help
+
+#************************************************************
+
+sub print_commands {
+ warn "Commands used by $my_name:\n",
+ " To run latex, I use \"$latex\"\n",
+ " To run pdflatex, I use \"$pdflatex\"\n",
+ " To run lualatex, I use \"$lualatex\"\n",
+ " To run xelatex, I use \"$xelatex\"\n",
+ " To run biber, I use \"$biber\"\n",
+ " To run bibtex, I use \"$bibtex\"\n",
+ " To run makeindex, I use \"$makeindex\"\n",
+ " To make a ps file from a dvi file, I use \"$dvips\"\n",
+ " To make a ps file from a dvi file with landscape format, ",
+ "I use \"$dvips_landscape\"\n",
+ " To make a pdf file from a dvi file, I use \"$dvipdf\"\n",
+ " To make a pdf file from a ps file, I use \"$ps2pdf\"\n",
+ " To make a pdf file from an xdv file, I use \"$xdvipdfmx\"\n",
+ " To view a pdf file, I use \"$pdf_previewer\"\n",
+ " To view a ps file, I use \"$ps_previewer\"\n",
+ " To view a ps file in landscape format, ",
+ "I use \"$ps_previewer_landscape\"\n",
+ " To view a dvi file, I use \"$dvi_previewer\"\n",
+ " To view a dvi file in landscape format, ",
+ "I use \"$dvi_previewer_landscape\"\n",
+ " To print a ps file, I use \"$lpr\"\n",
+ " To print a dvi file, I use \"$lpr_dvi\"\n",
+ " To print a pdf file, I use \"$lpr_pdf\"\n",
+ " To find running processes, I use \"$pscmd\", \n",
+ " and the process number is at position $pid_position\n";
+ warn "Notes:\n",
+ " Command starting with \"start\" is run detached\n",
+ " Command that is just \"start\" without any other command, is\n",
+ " used under MS-Windows to run the command the operating system\n",
+ " has associated with the relevant file.\n",
+ " Command starting with \"NONE\" is not used at all\n";
+} #END print_commands
+
+#************************************************************
+
+sub view_file_via_temporary {
+ return $always_view_file_via_temporary
+ || ($pvc_view_file_via_temporary && $preview_continuous_mode);
+} #END view_file_via_temporary
+
+#************************************************************
+#### Tex-related utilities
+
+#**************************************************
+
+sub check_biber_log {
+ # Check for biber warnings:
+ # Usage: check_biber_log( base_of_biber_run, \@biber_source )
+ # return 0: OK;
+ # 1: biber warnings;
+ # 2: biber errors;
+ # 3: could not open .blg file;
+ # 4: failed to find one or more source files, except for bibfile;
+ # 5: failed to find bib file;
+ # 6: missing file, one of which is control file
+ # 10: only error is missing \citation commands.
+ # 11: Malformed bcf file (normally due to error in pdflatex run)
+ # Side effect: add source files @biber_source
+ my $base = $_[0];
+ my $Pbiber_source = $_[1];
+ my $blg_name = "$base.blg";
+ my $blg_file = new FileHandle;
+ open( $blg_file, "<$blg_name" )
+ or return 3;
+ my $have_warning = 0;
+ my $have_error = 0;
+ my $missing_citations = 0;
+ my $no_citations = 0;
+ my $error_count = 0; # From my counting of error messages
+ my $warning_count = 0; # From my counting of warning messages
+ # The next two occur only from biber
+ my $bibers_error_count = 0; # From biber's counting of errors
+ my $bibers_warning_count = 0; # From biber's counting of warnings
+ my $not_found_count = 0;
+ my $control_file_missing = 0;
+ my $control_file_malformed = 0;
+ my %remote = (); # List of extensions of remote files
+ while (<$blg_file>) {
+ if (/> WARN /) {
+ print "Biber warning: $_";
+ $have_warning = 1;
+ $warning_count ++;
+ }
+ elsif (/> (FATAL|ERROR) /) {
+ print "Biber error: $_";
+ if ( /> (FATAL|ERROR) - Cannot find file '([^']+)'/ #'
+ || /> (FATAL|ERROR) - Cannot find '([^']+)'/ ) { #'
+ $not_found_count++;
+ push @$Pbiber_source, $2;
+ }
+ elsif ( /> (FATAL|ERROR) - Cannot find control file '([^']+)'/ ) { #'
+ $not_found_count++;
+ $control_file_missing = 1;
+ push @$Pbiber_source, $2;
+ }
+ elsif ( /> ERROR - .*\.bcf is malformed/ ) {
+ # Special treatment: Malformed .bcf file commonly results from error
+ # in (pdf)latex run. This error must be ignored.
+ $control_file_malformed = 1;
+ }
+ else {
+ $have_error = 1;
+ $error_count ++;
+ if ( /> (FATAL|ERROR) - The file '[^']+' does not contain any citations!/ ) { #'
+ $no_citations++;
+ }
+ }
+ }
+ elsif ( /> INFO - Data source '([^']*)' is a remote BibTeX data source - fetching/
+ ){
+ my $spec = $1;
+ my ( $base, $path, $ext ) = fileparseA( $spec );
+ $remote{$ext} = 1;
+ }
+ elsif ( /> INFO - Found .* '([^']+)'\s*$/
+ || /> INFO - Found '([^']+)'\s*$/
+ || /> INFO - Reading '([^']+)'\s*$/
+ || /> INFO - Processing .* file '([^']+)' .*$/
+ ) {
+ my $file = $1;
+ my ( $base, $path, $ext ) = fileparseA( $file );
+ if ($remote{$ext} && ( $base =~ /^biber_remote_data_source/ ) && 1) {
+ # Ignore the file, which appears to be a temporary local copy
+ # of a remote file. Treating the file as a source file will
+ # be misleading, since it will normally have been deleted by
+ # biber itself.
+ }
+ elsif ( (defined $Pbiber_source) && (-e $file) ) {
+ # Note that biber log file gives full path to file. (No search is
+ # needed to find it.) The file must have existed when biber was
+ # run. If it doesn't exist now, a few moments later, it must
+ # have gotten deleted, probably by biber (e.g., because it is a
+ # copy of a remote file).
+ # So I have included a condition above that the file must
+ # exist to be included in the source-file list.
+ push @$Pbiber_source, $file;
+ }
+ }
+ elsif ( /> INFO - WARNINGS: ([\d]+)\s*$/ ) {
+ $bibers_warning_count = $1;
+ }
+ elsif ( /> INFO - ERRORS: ([\d]+)\s*$/ ) {
+ $bibers_error_count = $1;
+ }
+ }
+ close $blg_file;
+ if ($control_file_malformed){return 11;}
+
+ my @not_found = &find_file_list1( $Pbiber_source, $Pbiber_source,
+ '', \@BIBINPUTS );
+ @$Pbiber_source = uniqs( @$Pbiber_source );
+
+ if ( ($#not_found < 0) && ($#$Pbiber_source >= 0) ) {
+ warn "$My_name: Found biber source file(s) [@$Pbiber_source]\n"
+ unless $silent;
+ }
+ elsif ( ($#not_found == 0) && ($not_found[0] =~ /\.bib$/) ) {
+ # Special treatment if sole missing file is bib file
+ # I don't want to treat that as an error
+ warn "$My_name: Biber did't find bib file [$not_found[0]]\n";
+ return 5;
+ }
+ else {
+ warn "$My_name: Failed to find one or more biber source files:\n";
+ foreach (@not_found) { warn " '$_'\n"; }
+ if ($force_mode) {
+ warn "==== Force_mode is on, so I will continue. ",
+ "But there may be problems ===\n";
+ }
+ if ($control_file_missing) {
+ return 6;
+ }
+ return 4;
+ }
+# print "$My_name: #Biber errors = $error_count, warning messages = $warning_count,\n ",
+# "missing citation messages = $missing_citations, no_citations = $no_citations\n";
+ if ( ! $have_error && $no_citations ) {
+ # If the only errors are missing citations, or lack of citations, that should
+ # count as a warning.
+ # HOWEVER: biber doesn't generate a new bbl. So it is an error condition.
+ return 10;
+ }
+ if ($have_error) {return 2;}
+ if ($have_warning) {return 1;}
+ return 0;
+} #END check_biber_log
+
+#**************************************************
+
+sub run_bibtex {
+ my $return = 999;
+ # Prevent changes we make to environment becoming global:
+ local %ENV = %ENV;
+ my ( $base, $path, $ext ) = fileparseA( $$Psource );
+ if ( $path && $bibtex_fudge ) {
+ # Since (e.g.,) 'bibtex output/main.aux' doesn't find subsidiary .aux
+ # files, as from \@include{chap.aux}, we change directory to the
+ # directory of the top-level .aux file to run bibtex. But we have to
+ # fix search paths for .bib and .bst, since they may be specified
+ # relative to the document directory.
+ my $cwd = good_cwd();
+ foreach ( 'BIBINPUTS', 'BSTINPUTS' ) {
+ if ( exists $ENV{$_} ) {
+ $ENV{$_} = $cwd.$search_path_separator.$ENV{$_};
+ }
+ else {
+ $ENV{$_} = $cwd.$search_path_separator;
+ }
+ }
+ pushd( $path );
+ if (!$silent) {
+ print "$My_name: changed directory to '$path'\n",
+ "Set BIBINPUTS='$ENV{BIBINPUTS}'\n",
+ "Set BSTINPUTS='$ENV{BSTINPUTS}'\n";
+ }
+ $return = &Run_subst( undef, undef, '', $base.$ext, '', $base );
+ popd();
+ if (!$silent) {
+ print "$My_name: changed directory back to '", cwd(), "'\n";
+ }
+ }
+ else {
+ $return = Run_subst();
+ }
+ return $return;
+}
+
+
+#**************************************************
+
+sub check_bibtex_log {
+ # Check for bibtex warnings:
+ # Usage: check_bibtex_log( base_of_bibtex_run )
+ # return 0: OK, 1: bibtex warnings, 2: bibtex errors,
+ # 3: could not open .blg file.
+ # 10: only error is missing \citation commands or a missing aux file
+ # (which would normally be corrected after a later run of
+ # (pdf)latex).
+
+ my $base = $_[0];
+ my $blg_name = "$base.blg";
+ my $blg_file = new FileHandle;
+ open( $blg_file, "<$blg_name" )
+ or return 3;
+ my $have_warning = 0;
+ my $have_error = 0;
+ my $missing_citations = 0;
+ my @missing_aux = ();
+ my $error_count = 0;
+ while (<$blg_file>) {
+ if (/^Warning--/) {
+ #print "Bibtex warning: $_";
+ $have_warning = 1;
+ }
+ elsif ( /^I couldn\'t open auxiliary file (.*\.aux)/ ) {
+ push @missing_aux, $1;
+ }
+ elsif ( /^I found no \\citation commands---while reading file/ ) {
+ $missing_citations++;
+ }
+ elsif (/There (were|was) (\d+) error message/) {
+ $error_count = $2;
+ #print "Bibtex error: count=$error_count $_";
+ $have_error = 1;
+ }
+ }
+ close $blg_file;
+ my $missing = $missing_citations + $#missing_aux + 1;
+
+ if ( $#missing_aux > -1 ) {
+ # Need to make the missing files.
+ warn "$My_name: One or more aux files is missing for bibtex. I'll try\n",
+ " to get (pdf)latex to remake them.\n";
+ rdb_for_some( [keys %current_primaries], sub{ $$Pout_of_date = 1; } );
+ }
+ #print "Bibtex errors = $error_count, missing aux files and citations = $missing\n";
+ if ($have_error && ($error_count <= $missing )
+ && ($missing > 0) ) {
+ # If the only error is a missing citation line, that should only
+ # count as a warning.
+ # Also a missing aux file should be innocuous; it will be created on
+ # next run of (pdf)latex. ?? HAVE I HANDLED THAT CORRECTLY?
+ # But have to deal with the problem that bibtex gives a non-zero
+ # exit code. So leave things as they are so that the user gets
+ # a better diagnostic ??????????????????????????
+# $have_error = 0;
+# $have_warning = 1;
+ return 10;
+ }
+ if ($have_error) {return 2;}
+ if ($have_warning) {return 1;}
+ return 0;
+} #END check_bibtex_log
+
+#**************************************************
+
+sub normalize_force_directory {
+ # Usage, normalize_force_directory( dir, filename )
+ # Perform the following operations:
+ # Clean filename
+ # If filename contains no path component, insert dir in front
+ # Normalize filename
+ # Return result
+ my $default_dir = $_[0];
+ my $filename = clean_filename( $_[1] );
+ my ($base_name, $path ) = fileparse( $filename );
+ if ( $base_name eq $filename ) {
+ $filename = "$default_dir$filename";
+ }
+ return normalize_filename( $filename );
+} #END normalize force_directory
+
+#**************************************************
+
+sub set_names {
+ # Set names of standard files. These are global variables.
+
+ ## Remove extension from filename if was given.
+ if ( find_basename($filename, $root_filename, $texfile_name) ) {
+ if ( $force_mode ) {
+ warn "$My_name: Could not find file '$texfile_name'\n";
+ }
+ else {
+ &ifcd_popd;
+ &exit_msg1( "Could not find file '$texfile_name'",
+ 11);
+ }
+ }
+ $tex_basename = $root_filename; # Base name of TeX file itself
+ if ($jobname ne '' ) {
+ $root_filename = $jobname;
+ $root_filename =~ s/%A/$tex_basename/g;
+ }
+
+ $aux_main = "%Y%R.aux";
+ $log_name = "%Y%R.log";
+ $fdb_name = "%Y%R.$fdb_ext";
+ # Note: Only MiKTeX allows out_dir ne aux_dir. It puts
+ # .fls file in out_dir, not aux_dir, which seems
+ # not natural.
+ $fls_name = "%Z%R.fls";
+
+ $dvi_name = "%Z%R.dvi";
+ $dviF_name = "%Z%R.dviF";
+ $ps_name = "%Z%R.ps";
+ $psF_name = "%Z%R.psF";
+ $pdf_name = "%Z%R.pdf";
+ ## It would be logical for a .xdv file to be put in the out_dir,
+ ## just like a .dvi file. But the only program, MiKTeX, that
+ ## currently implements aux_dir, and hence allows aux_dir ne out_dir,
+ ## puts .xdv file in aux_dir. So we must use %Y not %Z:
+ $xdv_name = "%Y%R.xdv";
+
+ foreach ( $aux_main, $log_name, $fdb_name, $fls_name,
+ $dvi_name, $ps_name, $pdf_name, $xdv_name, $dviF_name, $psF_name ) {
+ s/%R/$root_filename/g;
+ s/%Y/$aux_dir1/;
+ s/%Z/$out_dir1/;
+ }
+
+ $dvi_final = $dvi_name;
+ $ps_final = $ps_name;
+ $pdf_final = $pdf_name;
+ $xdv_final = $xdv_name;
+
+ if ( length($dvi_filter) > 0) {
+ $dvi_final = $dviF_name;
+ }
+ if ( length($ps_filter) > 0) {
+ $ps_final = $psF_name;
+ }
+}
+
+#**************************************************
+
+sub parse_log {
+# Scan log file for: dependent files
+# reference_changed, bad_reference, bad_citation
+# Return value: 1 if success, 0 if no log file.
+# Put results in UPDATES of global variables (which are normally declared
+# local in calling routine, to be suitably scoped):
+# %dependents: maps definite dependents to code:
+# 0 = from missing-file line
+# May have no extension
+# May be missing path
+# 1 = from 'File: ... Graphic file (type ...)' line
+# no path. Should exist, but may need a search, by kpsewhich.
+# 2 = from regular '(...' coding for input file,
+# Has NO path, which it would do if LaTeX file
+# Highly likely to be mis-parsed line
+# 3 = ditto, but has a path character ('/').
+# Should be LaTeX file that exists.
+# If it doesn't exist, we have probably a mis-parsed line.
+# There's no need to do a search.
+# 4 = definitive, which in this subroutine is only done:
+# for default dependents,
+# and for files that exist and are source of conversion
+# reported by epstopdf et al.
+# 5 = Had a missing file line. Now the file exists.
+# 6 = File was written during run. (Overrides 5)
+# 7 = File was created during run to be read in. (Overrides 5 and 6)
+# (e.g., by epstopdf)
+# Treat the following specially, since they have special rules
+# @bbl_files to list of .bbl files.
+# %idx_files to map from .idx files to .ind files.
+# %generated_log: keys give set of files written by (pdf)latex (e.g., aux, idx)
+# as determined by \openout = ... lines in log file.
+# @missing_subdirs = list of needed subdirectories of aux_dir
+# These are needed for writing aux_files when an included file is in
+# a subdirectory relative to the directory of the main TeX file.
+# This variable is only set when the needed subdirectories don't exist,
+# and the aux_dir is non-trivial, which results in an error message in
+# the log file
+# %conversions Internally made conversions from one file to another
+#
+# These may have earlier found information in them, so they should NOT
+# be initialized.
+#
+# Also SET
+# $reference_changed, $bad_reference, $bad_citation
+# $pwd_latex
+#
+# Put in trivial or default values if log file does not exist/cannot be opened
+#
+# Input globals: $primary_out, $fls_file_analyzed
+#
+
+
+# Give a quick way of looking up custom-dependency extensions
+ my %cusdep_from = ();
+ my %cusdep_to = ();
+ foreach ( @cus_dep_list ) {
+ my ($fromext, $toext) = split;
+ $cusdep_from{$fromext} = $cusdep_from{".$fromext"} = $_;
+ $cusdep_to{$toext} = $cusdep_to{".$toext"} = $_;
+ }
+# print "==== Cusdep from-exts:"; foreach (keys %cusdep_from) {print " '$_'";} print "\n";
+# print "==== Cusdep to-exts:"; foreach (keys %cusdep_to) {print " '$_'";} print "\n";
+
+
+ # Filenames given in log file may be preceded by a pathname
+ # denoting current directory. In MiKTeX, this is an absolute
+ # pathname; in TeXLive, it is './'. Either way, we'll want to
+ # remove this pathname string --- see the comments in sub
+ # rdb_set_latex_deps. In order of reliability for use in
+ # normalizing filenames from the log file, the following forms
+ # of the cwd are used:
+ # (a) internally deduced pwd from log file from sequence of lines
+ # **file
+ # (dir/file
+ # if possible. NO THAT'S WRONG if kpsearch is done.
+ # (b) from PWD line in fls file (if available), passed as $pwd_latex
+ # (c) system-given cwd as interpreted by sub good_cwd.
+ # We'll put the first two in @pwd_log
+ my @pwd_log = ();
+ if ($pwd_latex) { push @pwd_log, $pwd_latex; }
+
+ # $primary_out is actual output file (dvi or pdf)
+ # It is initialized before the call to this routine, to ensure
+ # a sensible default in case of misparsing
+
+ $reference_changed = 0;
+ $mult_defined = 0;
+ $bad_reference = 0;
+ $bad_character = 0;
+ $bad_citation = 0;
+
+ my $log_file = new FileHandle;
+ if ( ! open( $log_file, "<$log_name" ) ) {
+ return 0;
+ }
+ if ($log_file_binary) { binmode $log_file; }
+# Collect lines of log file
+ my @lines = ();
+ my $line = 0;
+ my $engine = 'pdfTeX'; # Simple default in case of problems
+ while(<$log_file>) {
+ $line++;
+ # Could use chomp here, but that fails if there is a mismatch
+ # between the end-of-line sequence used by latex and that
+ # used by perl. (Notably a problem with MSWin latex and
+ # cygwin perl!)
+ s/[\n\r]*$//;
+ # Handle wrapped lines:
+ # They are lines brutally broken at exactly $log_wrap chars
+ # excluding line-end. Sometimes a line $log_wrap chars
+ # long is an ordinary line, sometimes it is part of a line
+ # that was wrapped. To handle all cases, I keep both
+ # options open by putting the line into @lines before
+ # and after appending the next line:
+ my $len = length($_);
+ if ($line == 1) {
+ if ( /^This is ([^,]+), / ) {
+ $engine = $1;
+ print "=== TeX engine is '$engine'\n"
+ if (!$silent);
+ if ( /^This is ([^,]+), [^\(]*\(([^\)]+)\)/ ) {
+ $tex_distribution = $2;
+ print "=== TeX distribution is '$tex_distribution'\n"
+ if ($diagnostics);
+ }
+ }
+ else {
+ warn "$My_name: First line of .log file '$log_name' is not in standard format.\n";
+ }
+ }
+ else {
+ # LuaTeX sometimes wraps at 80 instead of 79, so work around this
+ while ( ( ($len == $log_wrap) || ( ($engine eq 'LuaTeX') && ($len == $log_wrap+1) ) )
+ && !eof($log_file) ) {
+ push @lines, $_;
+ my $extra = <$log_file>;
+ $extra =~ s/[\n\r]*$//;
+ $len = length($extra);
+ $_ .= $extra;
+ }
+ }
+ push @lines, $_;
+ }
+ close $log_file;
+
+ push @lines, ""; # Blank line to terminate. So multiline blocks
+ # are always terminated by non-block line, rather than eof.
+
+ $line = 0;
+ my $state = 0; # 0 => before ** line,
+ # 1 => after **filename line, before next line (first file-reading line)
+ # 2 => pwd_log determined.
+ # For parsing multiple line blocks of info
+ my $current_pkg = ""; # non-empty string for package name, if in
+ # middle of parsing multi-line block of form:
+ # Package name ....
+ # (name) ...
+ # ...
+ my $block_type = ""; # Specify information in such a block
+ my $delegated_source = ""; # If it is a file conversion, specify source
+ my $delegated_output = ""; # and output file. (Don't put in
+ # data structure until block is ended.)
+ my %new_conversions = ();
+ my @retries = ();
+ my $log_silent = ($silent || $silence_logfile_warnings);
+ my @warning_list = ();
+LINE:
+ while( ($line <= $#lines) || ($#retries > -1) ) {
+ if ($#retries > -1) {
+ $_ = pop @retries;
+ }
+ else {
+ $_ = $lines[$line];
+ $line ++;
+ }
+ if ( /^! pdfTeX warning/ || /^pdfTeX warning/ ) {
+ # This kind of warning is produced by some versions of pdftex
+ # or produced by my reparse of warnings from other
+ # versions.
+ next;
+ }
+ elsif ( /^(.+)(pdfTeX warning.*)$/ ) {
+ # Line contains a pdfTeX warnings that may have been
+ # inserted directly after other material without an
+ # intervening new line. I think pdfTeX always inserts a
+ # newline after the warning. (From examination of source
+ # code.)
+ push @retries, $1;
+ # But continue parsing the original line, in case it was a
+ # misparse, e.g., of a filename ending in 'pdfTeX';
+ }
+ if ( $line == 1 ){
+ if ( /^This is / ) {
+ # First line OK
+ next LINE;
+ } else {
+ warn "$My_name: Error on first line of '$log_name'.\n".
+ "This is apparently not a TeX log file. ",
+ "The first line is:\n$_\n";
+ $failure = 1;
+ $failure_msg = "Log file '$log_name' appears to have wrong format.";
+ return 0;
+ }
+ }
+
+ if ( ($state == 0) && /^\*\*(.*)$/ ) {
+ # Line containing first line specified to tex
+ # It's either a filename or a command starting with \
+ my $first = $1;
+ $state = 1;
+ if ( ! /^\\/ ) {
+ $source_log = $first;
+ if ( -e "$source_log.tex" ) { $source_log .= '.tex'; }
+ }
+ else {
+ $state = 2;
+ }
+ next LINE;
+ }
+ elsif ( $state == 1 ) {
+ $state = 2;
+ if (-e $source_log) {
+ # then the string preceeding $source_log on the line after the
+ # ** line is probably the PWD as it appears in filenames in the
+ # log file, except if the file appears in two locations.
+ if ( m{^\("([^"]*)[/\\]\Q$source_log\E"} ) {
+ unshift @pwd_log, $1;
+ }
+ elsif ( m{^\((.*)[/\\]\Q$source_log\E} ) {
+ unshift @pwd_log, $1;
+ }
+ }
+ }
+
+ if ( $block_type ) {
+ # In middle of parsing block
+ if ( /^\($current_pkg\)/ ) {
+ # Block continues
+ if ( ($block_type eq 'conversion')
+ && /^\($current_pkg\)\s+Output file: <([^>]+)>/ )
+ {
+ $delegated_output = normalize_clean_filename($1, @pwd_log);
+ }
+ next LINE;
+ }
+ # Block has ended.
+ if ($block_type eq 'conversion') {
+#print "=== $delegated_source -> $delegated_output\n";
+ $new_conversions{$delegated_source} = $delegated_output;
+ }
+ $current_pkg = $block_type
+ = $delegated_source = $delegated_output = "";
+ # Then process current line
+ }
+
+ # Check for changed references, bad references and bad citations:
+ if (/Rerun to get/) {
+ warn "$My_name: References changed.\n" if ! $log_silent;
+ $reference_changed = 1;
+ }
+ if (/^LaTeX Warning: (Reference[^\001]*undefined on input line .*)\./) {
+ push @warning_list, $1;
+ $bad_reference++;
+ }
+ elsif (/^LaTeX Warning: (Label [^\001]* multiply defined.*)\./) {
+ push @warning_list, $1;
+ $mult_defined++;
+ }
+ elsif (/^LaTeX Warning: (Citation[^\001]*undefined on input line .*)\./) {
+ push @warning_list, $1;
+ $bad_citation++;
+ }
+ elsif (/^Package natbib Warning: (Citation[^\001]*undefined on input line .*)\./) {
+ push @warning_list, $1;
+ $bad_citation++;
+ }
+ elsif ( /^Missing character: There is no /
+ || /^! Package inputenc Error: Unicode character /
+ || /^! Bad character code /
+ ) {
+ $bad_character++;
+ }
+ elsif ( /^Document Class: / ) {
+ # Class sign-on line
+ next LINE;
+ }
+ elsif ( /^\(Font\)/ ) {
+ # Font info line
+ next LINE;
+ }
+ elsif (/^No pages of output\./) {
+ $primary_out = '';
+ warn "$My_name: Log file says no output from latex\n";
+ next LINE;
+ }
+ elsif ( /^Output written on\s+(.*)\s+\(\d+\s+page/ ) {
+ $primary_out = normalize_clean_filename($1, @pwd_log);
+ warn "$My_name: Log file says output to '$primary_out'\n"
+ unless $silent;
+ next LINE;
+ }
+ elsif ( /^Overfull /
+ || /^Underfull /
+ || /^or enter new name\. \(Default extension: .*\)/
+ || /^\*\*\* \(cannot \\read from terminal in nonstop modes\)/
+ ) {
+ # Latex error/warning, etc.
+ next LINE;
+ }
+ elsif ( /^\\openout\d+\s*=\s*(.*)\s*$/ ) {
+ # \openout followed by filename followed by line end.
+ # pdflatex and xelatex quote it and wrap,
+ # lualatex leaves filename as is, and doesn't wrap.
+ my $cand = $1;
+ if ( $cand =~ /\`\"([^\'\"]+)\"\'\.$/ ) {
+ # One form of quoting by pdflatex, xelatex: `"..."'.
+ $cand = $1;
+ }
+ elsif ( $cand =~ /\`([^\']+)\'\.$/ ) {
+ # Another form of quoting by pdflatex, xelatex: `...'.
+ $cand = $1;
+ }
+ if ( $cand =~ /[\`\'\"]/){
+ # Bad quotes: e.g., incomplete wrapped line
+ next LINE;
+ }
+ $generated_log{normalize_force_directory( $aux_dir1, $cand )} = 1;
+ next LINE;
+ }
+ # Test for conversion produced by package:
+ elsif ( /^Package (\S+) Info: Source file: <([^>]+)>/ ) {
+ # Info. produced by epstopdf (and possibly others)
+ # about file conversion
+ $current_pkg = normalize_clean_filename($1, @pwd_log);
+ $delegated_source = normalize_clean_filename($2, @pwd_log);
+ $block_type = 'conversion';
+ next LINE;
+ }
+# Test for writing of index file. The precise format of the message
+# depends on which package (makeidx.sty , multind.sty or index.sty) and
+# which version writes the message.
+ elsif ( /Writing index file (.*)$/ ) {
+ my $idx_file = '';
+ if ( /^Writing index file (.*)$/ ) {
+ # From makeidx.sty or multind.sty
+ $idx_file = $1;
+ }
+ elsif ( /^index\.sty> Writing index file (.*)$/ ) {
+ # From old versions of index.sty
+ $idx_file = $1;
+ }
+ elsif ( /^Package \S* Info: Writing index file (.*) on input line/ ) {
+ # From new versions of index.sty
+ $idx_file = $1;
+ }
+ else {
+ warn "$My_name: Message indicates index file was written\n",
+ " ==> but I do not know how to understand it: <==\n",
+ " '$_'\n";
+ next LINE;
+ }
+ # Typically, there is trailing space, not part of filename:
+ $idx_file =~ s/\s*$//;
+ # When (pdf)latex is run with an -output-directory
+ # or an -aux_directory, the file name does not contain
+ # the output path; fix this, after removing quotes:
+ $idx_file = normalize_force_directory( $aux_dir1, $idx_file );
+ my ($idx_base, $idx_path, $idx_ext) = fileparseA( $idx_file );
+ $idx_base = $idx_path.$idx_base;
+ $idx_file = $idx_base.$idx_ext;
+ if ( $idx_ext eq '.idx' ) {
+ warn "$My_name: Index file '$idx_file' was written\n"
+ unless $silent;
+ $idx_files{$idx_file} = [ "$idx_base.ind", $idx_base ];
+ }
+ elsif ( exists $cusdep_from{$idx_ext} ) {
+ if ( !$silent ) {
+ warn "$My_name: Index file '$idx_file' was written\n";
+ warn " Cusdep '$cusdep_from{$idx_ext}' should be used\n";
+ }
+ # No action needed here
+ }
+ else {
+ warn "$My_name: Index file '$idx_file' written\n",
+ " ==> but it has an extension I do not know how to handle <==\n";
+ }
+
+ next LINE;
+ }
+ elsif ( /^No file (.*?\.bbl)./ ) {
+ # When (pdf)latex is run with an -output-directory
+ # or an -aux_directory, the file name does not contain
+ # the output path; fix this, after removing quotes:
+ my $bbl_file = normalize_force_directory( $aux_dir1, $1 );
+ warn "$My_name: Non-existent bbl file '$bbl_file'\n $_\n";
+ $dependents{$bbl_file} = 0;
+ push @bbl_files, $bbl_file;
+ next LINE;
+ }
+ foreach my $pattern (@file_not_found) {
+ if ( /$pattern/ ) {
+ my $file = clean_filename($1);
+ warn "$My_name: Missing input file: '$file' from line\n '$_'\n"
+ unless $silent;
+ $dependents{normalize_filename($file, @pwd_log)} = 0;
+ my $file1 = $file;
+ if ( $aux_dir ) {
+ # Allow for the possibility that latex generated
+ # a file in $aux_dir, from which the missing file can
+ # be created by a cusdep (or other) rule that puts
+ # the result in $out_dir. If the announced missing file
+ # has no path, then it would be effectively a missing
+ # file in $aux_dir, with a path. So give this alternate
+ # location.
+ # It is also possible to have a file that is in a directory
+ # relative to the aux_dir, so allow for that as well
+ my $file1 = normalize_force_directory( $aux_dir1, $file );
+ $dependents{$file1} = 0;
+ $dependents{$aux_dir1.$file} = 0;
+
+ }
+ next LINE;
+ }
+ }
+ if ( (! $fls_file_analyzed)
+ && /^File: (.+) Graphic file \(type / ) {
+ # First line of message from includegraphics/x
+ # But this does NOT include full path information
+ # (if exact match is not found and a non-trivial
+ # kpsearch was done by (pdf)latex).
+ # But the source-file information is in the fls file,
+ # if we are using it.
+ $dependents{normalize_clean_filename($1, @pwd_log)} = 1;
+ next LINE;
+ }
+ # Now test for generic lines to ignore, only after special cases!
+ if ( /^File: / ) {
+ # Package sign-on line. Includegraphics/x also produces a line
+ # with this signature, but I've already handled it.
+ next LINE;
+ }
+ if ( /^Package: / ) {
+ # Package sign-on line
+ next LINE;
+ }
+ if (/^\! LaTeX Error: / ) {
+ next LINE;
+ }
+ if ( m[^! I can't write on file `(.*)/([^/']*)'.\s*$] ) {
+ my $dir = $1;
+ my $file = $2;
+ my $full_dir = $aux_dir1.$dir;
+ if ( ($aux_dir ne '') && (! -e $full_dir) && ( $file =~ /\.aux$/) ) {
+ warn "$My_name: === There were problems writing to '$file' in '$full_dir'\n",
+ " I'll try to make the subdirectory later.\n"
+ if $diagnostics;
+ push @missing_subdirs, $full_dir;
+ }
+ else {
+ warn "$My_name: ====== There were problems writing to",
+ "----- '$file' in '$full_dir'.\n",
+ "----- But this is not the standard situation of\n",
+ "----- aux file to subdir of output directory, with\n",
+ "----- non-existent subdir\n",
+ }
+ }
+
+ if ( ($fls_file_analyzed) && (! $analyze_input_log_always) ) {
+ # Skip the last part, which is all about finding input
+ # file names which should all appear more reliably in the
+ # fls file.
+ next LINE;
+ }
+
+ my @new_includes = ();
+
+ GRAPHICS_INCLUDE_CANDIDATE:
+ while ( /<([^>]+)(>|$)/g ) {
+ if ( -f $1 ) { push @new_includes, $1; }
+ } # GRAPHICS_INCLUDE_CANDIDATE:
+
+ INCLUDE_CANDIDATE:
+ while ( /\((.*$)/ ) {
+ # Filename found by
+ # '(', then filename, then terminator.
+ # Terminators: obvious candidates: ')': end of reading file
+ # '(': beginning of next file
+ # ' ': space is an obvious separator
+ # ' [': start of page: latex
+ # and pdflatex put a
+ # space before the '['
+ # '[': start of config file
+ # in pdflatex, after
+ # basefilename.
+ # '{': some kind of grouping
+ # Problem:
+ # All or almost all special characters are allowed in
+ # filenames under some OS, notably UNIX. Luckily most cases
+ # are rare, if only because the special characters need
+ # escaping. BUT 2 important cases are characters that are
+ # natural punctuation
+ # Under MSWin, spaces are common (e.g., "C:\Program Files")
+ # Under VAX/VMS, '[' delimits directory names. This is
+ # tricky to handle. But I think few users use this OS
+ # anymore.
+ #
+ # Solution: use ' [', but not '[' as first try at delimiter.
+ # Then if candidate filename is of form 'name1[name2]', then
+ # try splitting it. If 'name1' and/or 'name2' exists, put
+ # it/them in list, else just put 'name1[name2]' in list.
+ # So form of filename is now:
+ # '(',
+ # then any number of characters that are NOT ')', '(', or '{'
+ # (these form the filename);
+ # then ' [', or ' (', or ')', or end-of-string.
+ # That fails for pdflatex
+ # In log file:
+ # '(' => start of reading of file, followed by filename
+ # ')' => end of reading of file
+ # '[' => start of page (normally preceeded by space)
+ # Remember:
+ # filename (on VAX/VMS) may include '[' and ']' (directory
+ # separators)
+ # filenames (on MS-Win) commonly include space.
+ # filenames on UNIX can included space.
+ # Miktex quotes filenames
+ # But web2c doesn't. Then
+ # (string message
+ # is ambiguous: is the filename "string" or "string message".
+ # Allow both as candidates, since user filenames with spaces
+ # are rare. System filenames with spaces are common, but
+ # they are normally followed by a newline rather than messages.
+
+ # First step: replace $_ by whole of line after the '('
+ # Thus $_ is putative filename followed by other stuff.
+ $_ = $1;
+ # Array of new candidate include files; sometimes more than one.
+ my $quoted = 0;
+ if ( /^\"([^\"]+)\"/ ) {
+ # Quoted file name, as from MikTeX
+ $quoted = 1;
+ }
+ elsif ( /^\"/ ) {
+ # Incomplete quoted file, as in wrapped line before appending
+ # next line
+ next LINE;
+ }
+ elsif ( /^([^\(^\)]*?)\s+[\[\{\<]/ ) {
+ # Terminator: space then '[' or '{' or '<'
+ # Use *? in condition: to pick up first ' [' (etc)
+ # as terminator
+ }
+ elsif ( /^([^\(^\)]*)\s+(?=\()/ ) {
+ # Terminator is ' (', but '(' isn't in matched string,
+ # so we keep the '(' ready for the next match
+ }
+ elsif ( /^([^\(^\)]*)(\))/ ) {
+ # Terminator is ')'
+ }
+ else {
+ #Terminator is end-of-string
+ }
+ $_ = $'; # Put $_ equal to the unmatched tail of string '
+ my $include_candidate = $1;
+ $include_candidate =~ s/\s*$//; # Remove trailing space.
+ if ($quoted) {
+ # Remove quotes around filename.
+ $include_candidate =~ s/^\"(.*)\"$/$1/;
+ }
+ elsif ( !$quoted && ($include_candidate =~ /(\S+)\s/ ) ){
+ # Non-space-containing filename-candidate
+ # followed by space followed by message
+ # (Common)
+ push @new_includes, $1;
+ }
+ if ($include_candidate =~ /[\"\'\`]/) {
+ # Quote inside filename. Probably misparse.
+ next INCLUDE_CANDIDATE;
+ }
+ if ( $include_candidate eq "[]" ) {
+ # Part of overfull hbox message
+ next INCLUDE_CANDIDATE;
+ }
+ if ( $include_candidate =~ /^\\/ ) {
+ # Part of font message
+ next INCLUDE_CANDIDATE;
+ }
+
+ push @new_includes, $include_candidate;
+ if ( $include_candidate =~ /^(.+)\[([^\]]+)\]$/ ) {
+ # Construct of form 'file1[file2]', as produced by pdflatex
+ if ( -e $1 ) {
+ # If the first component exists, we probably have the
+ # pdflatex form
+ push @new_includes, $1, $2;
+ }
+ else {
+ # We have something else.
+ # So leave the original candidate in the list
+ }
+ }
+ } # INCLUDE_CANDIDATE
+
+ INCLUDE_NAME:
+ foreach my $include_name (@new_includes) {
+ if ($include_name =~ /[\"\'\`]/) {
+ # Quote inside filename. Probably misparse.
+ next INCLUDE_NAME;
+ }
+ $include_name = normalize_filename( $include_name, @pwd_log );
+ my ($base, $path, $ext) = fileparseB( $include_name );
+ if ( ($path eq './') || ($path eq '.\\') ) {
+ $include_name = $base.$ext;
+ }
+ if ( $include_name !~ m'[/|\\]' ) {
+ # Filename does not include a path character
+ # High potential for misparsed line
+ $dependents{$include_name} = 2;
+ } else {
+ $dependents{$include_name} = 3;
+ }
+ if ( $ext eq '.bbl' ) {
+ warn "$My_name: Found input bbl file '$include_name'\n"
+ unless $silent;
+ push @bbl_files, $include_name;
+ }
+ } # INCLUDE_NAME
+ } # LINE
+
+ # Default includes are always definitive:
+ foreach (@default_includes) { $dependents{$_} = 4; }
+
+ ###print "New parse: \n";
+ ###foreach (sort keys %dependents) { print " '$_': $dependents{$_}\n"; }
+
+ my @misparsed = ();
+ my @missing = ();
+ my @not_found = ();
+
+ my %kpsearch_candidates = ();
+CANDIDATE:
+ foreach my $candidate (keys %dependents) {
+ my $code = $dependents{$candidate};
+ if ( -d $candidate ) {
+ # If $candidate is directory, it was presumably found from a
+ # mis-parse, so remove it from the list. (Misparse can
+ # arise, for example from a mismatch of latexmk's $log_wrap
+ # value and texmf.cnf value of max_print_line.)
+ delete $dependents{$candidate};
+ }
+ elsif ( -e $candidate ) {
+ if ( exists $generated_log{$candidate} ){
+ $dependents{$candidate} = 6;
+ }
+ elsif ($code == 0) {
+ $dependents{$candidate} = 5;
+ }
+ else {
+ $dependents{$candidate} = 4;
+ }
+ }
+ elsif ($code == 1) {
+ # Graphics file that is supposed to have been read.
+ # Candidate name is as given in source file, not as path
+ # to actual file.
+ # We have already tested that file doesn't exist, as given.
+ # so use kpsewhich.
+ # If the file still is not found, assume non-existent;
+ $kpsearch_candidates{$candidate} = 1;
+ delete $dependents{$candidate};
+ }
+ elsif ($code == 2) {
+ # Candidate is from '(...' construct in log file, for input file
+ # which should include pathname if valid input file.
+ # Name does not have pathname-characteristic character (hence
+ # $code==2.
+ # We get here if candidate file does not exist with given name
+ # Almost surely result of a misparsed line in log file.
+ delete $dependents{$candidate};
+ push @misparse, $candidate;
+ }
+ elsif ($code == 3) {
+ # Candidate is from '(...' construct in log file, for input file
+ # which should include pathname if valid input file.
+ # Name does have pathname-characteristic character (hence
+ # $code==3.
+ # But we get here only if candidate file does not exist with
+ # given name.
+ # Almost surely result of a misparsed line in log file.
+ # But with lower probability than $code == 2
+ delete $dependents{$candidate};
+ push @misparse, $candidate;
+ }
+ elsif ($code == 0) {
+ my ($base, $path, $ext) = fileparseA($candidate);
+ $ext =~ s/^\.//;
+ if ( ($ext eq '') && (-e "$path$base.tex") ) {
+ # I don't think the old version was correct.
+ # If the missing-file report was of a bare
+ # extensionless file, and a corresponding .tex file
+ # exists, then the missing file does not correspond
+ # to the missing file, unless the .tex file was
+ # created during the run.
+ # OLD $dependents{"$path$base.tex"} = 4;
+ # OLD delete $dependents{$candidate};
+ # NEW:
+ $dependents{"$path$base.tex"} = 4;
+ }
+ push @missing, $candidate;
+ }
+ }
+
+ my @kpsearch_candidates = keys %kpsearch_candidates;
+ if (@kpsearch_candidates) {
+ foreach my $result ( kpsewhich( @kpsearch_candidates ) ) {
+ $dependents{$result} = 4;
+ }
+ }
+
+CANDIDATE_PAIR:
+ foreach my $delegated_source (keys %new_conversions) {
+ my $delegated_output = $new_conversions{$delegated_source};
+ my $rule = "Delegated $delegated_source, $delegated_output";
+ # N.B. $delegated_source eq '' means the output file
+ # was created without a named input file.
+ foreach my $candidate ($delegated_source, $delegated_output) {
+ if (! -e $candidate ) {
+ # The file might be somewhere that can be found
+ # in the search path of kpathsea:
+ my @kpse_result = kpsewhich( $candidate,);
+ if ($#kpse_result > -1) {
+ $candidate = $kpse_result[0];
+ }
+ }
+ }
+ if ( ( (-e $delegated_source) || ($delegated_source eq '') )
+ && (-e $delegated_output) )
+ {
+ $conversions{$delegated_output} = $delegated_source;
+ $dependents{$delegated_output} = 7;
+ if ($delegated_source) {
+ $dependents{$delegated_source} = 4;
+ }
+ }
+ elsif (!$silent) {
+ print "Logfile claimed conversion from '$delegated_source' ",
+ "to '$delegated_output'. But:\n";
+ if (! -e $delegated_output) {
+ print " Output file does not exist\n";
+ }
+ if ( ($delegated_source ne '') && (! -e $delegated_source) ) {
+ print " Input file does not exist\n";
+ }
+ }
+ }
+
+ if ( ($#warning_list >= 0) && !$log_silent ) {
+ @warning_list = uniqs( @warning_list );
+ show_array( "$My_name: List of undefined refs and citations:",
+ @warning_list );
+ }
+
+ if ( $diagnostics ) {
+ @misparse = uniqs( @misparse );
+ @missing = uniqs( @missing );
+ @not_found = uniqs( @not_found );
+ my @dependents = sort( keys %dependents );
+
+ my $dependents = $#dependents + 1;
+ my $misparse = $#misparse + 1;
+ my $missing = $#missing + 1;
+ my $not_found = $#not_found + 1;
+ my $exist = $dependents - $not_found - $missing;
+ my $bbl = $#bbl_files + 1;
+
+ print "$dependents dependent files detected, of which ",
+ "$exist exist, $not_found were not found,\n",
+ " and $missing appear not to exist.\n";
+ print "Dependents:\n";
+ foreach (@dependents) {
+ print " '$_' ";
+ if ( $dependents{$_} == 6 ) { print " written by (pdf)latex";}
+ if ( $dependents{$_} == 7 ) { print " converted by (pdf)latex";}
+ print "\n";
+ }
+ if ($not_found > 0) {
+ print "Not found:\n";
+ foreach (@not_found) { print " $_\n"; }
+ }
+ if ($missing > 0) {
+ print "Not existent:\n";
+ foreach (@missing) { print " $_\n"; }
+ }
+ if ( $bbl > 0 ) {
+ print "Input bbl files:\n";
+ foreach (@bbl_files) { print " $_\n"; }
+ }
+
+ if ( $misparse > 0 ) {
+ print "Possible input files, perhaps from misunderstood lines in .log file:\n";
+ foreach ( @misparse ) { print " $_\n"; }
+ }
+ }
+ return 1;
+} #END parse_log
+
+#************************************************************
+
+sub find_set_log {
+ # Locate the log file, if possible. This allows for possible configuration
+ # errors, e.g., because the command for (*)latex was such that it did not
+ # do the setting of -output-directory or -aux-directory that the user intended,
+ # or because the version used did not support one or other of these options.
+ # Put result in $where_log (see its initial declaration/definition for details).
+ # Change $aux_dir and/or $out_dir as appropriate, and make consequent changes.
+ #
+ # Probably further attention to location of output file (.dvi, .pdf, or .xdv)
+ # could be done, to get $out_dir and $$Pdest more accurately set.
+ #
+ # Typical configuration errors that lead to the need for this subroutine:
+ # %O not used in command definition, so directory options don't getpassed
+ # to (*)latex.
+ # Use of $aux_dir different to $out_dir, when (*)latex doesn't support
+ # the -aux-directory option (notably with TeXLive distribution).
+ if ($where_log >= 0) {
+ # .log file was found on previous run. No need to repeat search, since
+ # if the location were to change from run to run, we'd have other
+ # serious difficulties that are to hard to deal with.
+ return;
+ }
+ if ( test_gen_file( "$aux_dir1$root_filename.log" ) ) {
+ # .log file is in expected place.
+ $where_log = 1;
+ }
+ elsif ( test_gen_file( "$out_dir1$root_filename.log" ) ) {
+ # .log file is in out_dir not in aux_dir.
+ # Presumably there is a configuration error
+ # that prevents aux_dir from being used by latex.
+ # So change $aux_dir to the actually used value.
+ $where_log = 2;
+ $aux_dir = $out_dir;
+ }
+ elsif ( test_gen_file( "$root_filename.log" ) ) {
+ # .log file is not in out_dir nor in aux_dir, but is in cwd.
+ # Presumably there is a configuration error
+ # that prevents the directories from being used by latex.
+ # So change $aux_dir to the actually used value.
+ $where_log = 3;
+ $aux_dir = "";
+ }
+ else {
+ # No .log file found
+ $failure = 1;
+ $$Plast_result = 2;
+ $where_log = 0;
+ $failure_msg
+ = "(Pdf)LaTeX didn't generate the expected log file '$log_name'\n";
+ }
+ if ($where_log > 1) {
+ warn "$My_name: Changed aux_dir from '$aux_dir_requested' to '$aux_dir'\n".
+ " to allow for probable configuration error\n";
+ # Allow for the changes associated with change of $aux_dir:
+ &set_dirs_etc;
+ &set_names;
+ warn "$My_name: Actual .log file is\n",
+ " '$log_name'\n",
+ " instead of the value\n",
+ " '$aux_dir_requested/$root_filename.log'\n",
+ " that seemed to be intended.\n";
+ }
+}
+
+#************************************************************
+
+sub parse_fls {
+ my ($fls_name, $Pinputs, $Poutputs, $Pfirst_read_after_write, $Ppwd_latex ) = @_;
+ %$Pinputs = %$Poutputs = %$Pfirst_read_after_write = ();
+ my $fls_file = new FileHandle;
+ # Make a note of current working directory
+ # I'll update it from the fls file later
+ # Currently I don't use this, but it would be useful to use
+ # this when testing prefix for cwd in a filename, by
+ # giving (pdf)latex's best view of the cwd. Note that the
+ # value given by the cwd() function may be mangled, e.g., by cygwin
+ # compared with native MSWin32.
+ #
+ # Two relevant forms of cwd exist: The system one, which we can find, and
+ # the one reported by (pdf)latex in the fls file. It will be
+ # useful to remove leading part of cwd in filenames --- see the
+ # comments in sub rdb_set_latex_deps. Given the possible multiplicity
+ # of representations of cwd, the one reported in the fls file should
+ # be definitive in the fls file.
+
+ my $cwd = good_cwd();
+ if ( ! open ($fls_file, "<$fls_name") ) {
+ return 1;
+ }
+ foreach $_ ( <$fls_file> ) {
+ # Remove trailing CR and LF. Thus we get correct behavior when an fls file
+ # is produced by MS-Windows program (e.g., in MiKTeX) with CRLF line ends,
+ # but is read by Unix Perl (which treats LF as line end, and preserves CRLF
+ # in read-in lines):
+ $_ =~ s/[\n\r]*$//;
+ if (/^\s*PWD\s+(.*)$/) {
+ $cwd = $1;
+ $$Ppwd_latex = $cwd;
+ if ( $cwd =~ /\"/ ) {
+ warn "$My_name: The working directory has a '\"' character in its name:\n",
+ " '$cwd'\n This can cause me trouble. Beware!\n";
+ }
+ }
+ elsif (/^\s*INPUT\s+(.*)$/) {
+ # Take precautions against aliasing of foo, ./foo and other possibilities for cwd.
+ my $file = $1;
+ # Remove exactly pwd reported in this file, and following separator.
+ # MiKTeX reports absolute pathnames, and this way of removing PWD insulates
+ # us from coding issues if the PWD contains non-ASCII characters. What
+ # coding scheme (UTF-8, code page, etc) is used depends on OS, TeX
+ # implementation, ...
+ if ( defined $$Ppwd_latex ) {
+ $file =~ s(^\Q$$Ppwd_latex\E[\\/])();
+ }
+ $file = normalize_filename( $file );
+ if ( (exists $$Poutputs{$file}) && (! exists $$Pinputs{$file}) ) {
+ $$Pfirst_read_after_write{$file} = 1;
+ }
+ $$Pinputs{$file} = 1;
+ }
+ elsif (/^\s*OUTPUT\s+(.*)$/) {
+ # Take precautions against aliasing of foo, ./foo and other possibilities for cwd.
+ my $file = $1;
+ $file =~ s(^\Q$$Ppwd_latex\E[\\/])();
+ $file = normalize_filename( $file );
+ $$Poutputs{$file} = 1;
+ }
+ }
+ close( $fls_file );
+ return 0;
+} #END parse_fls
+
+#************************************************************
+
+sub clean_filename {
+ # Convert quoted filename as found in log file to filename without quotes
+ # Allows arbitrarily embedded double-quoted substrings, includes the
+ # cases
+ # 1. `"string".ext', which arises e.g., from \jobname.bbl:
+ # when the base filename contains spaces, \jobname has quotes.
+ # and from \includegraphics with basename specified.
+ # Also deals with filenames written by asymptote.sty
+ # 2. Or "string.ext" from \includegraphcs with basename and ext specified.
+ # and from MiKTeX logfile for input files with spaces.
+ # Doubled quotes (e.g., A""B) don't get converted.
+ # Neither do unmatched quotes.
+ my $filename = $_[0];
+ while ( $filename =~ s/^([^\"]*)\"([^\"]+)\"(.*)$/$1$2$3/ ) {}
+ return $filename;
+}
+
+# ------------------------------
+
+sub normalize_filename {
+ # Usage: normalize_filename( filename [, extra forms of name of cwd] )
+ # Returns filename with removal of various forms for cwd, and
+ # with conversion of directory separator to '/' only
+ #
+ my ( $file, @dirs ) = @_;
+ my $file1 = $file; # Saved original value
+ my $cwd = good_cwd();
+ # Normalize files to use / to separate directory components:
+ # (Note both / and \ are allowed under MSWin.)
+ foreach ($cwd, $file, @dirs) {
+ s(\\)(/)g;
+ }
+ # Remove initial component equal to current working directory.
+ # Use \Q and \E round directory name in regex to avoid interpretation
+ # of metacharacters in directory name:
+ foreach my $dir ( @dirs, '.', $cwd ) {
+ if ( $file =~ s(^\Q$dir\E/)() ) {
+ last;
+ }
+ }
+ return $file;
+}
+
+# ------------------------------
+
+sub normalize_clean_filename {
+ # Usage: normalize_clean_filename( filename [, extra forms of name of cwd] )
+ # Same as normalize_filename, but first remove any double quotes, as
+ # done by clean_filename, which is appropriate for filenames from log file.
+ my ($file, @dirs) = @_;
+ return normalize_filename( clean_filename( $file ) , @dirs );
+}
+
+#************************************************************
+
+sub fix_pattern {
+ # Escape the characters [ and {, to give a pattern for use in glob
+ # with these characters taken literally.
+ my $pattern = shift;
+ $pattern =~ s/\[/\\\[/g;
+ $pattern =~ s/\{/\\\{/g;
+ return $pattern;
+}
+
+#************************************************************
+
+sub parse_aux {
+ #Usage: parse_aux( $aux_file, \@new_bib_files, \@new_aux_files, \@new_bst_files )
+ # Parse aux_file (recursively) for bib files, and bst files.
+ # If can't open aux file, then
+ # Return 0 and leave @new_bib_files empty
+ # Else set @new_bib_files from information in the aux files
+ # And:
+ # Return 1 if no problems
+ # Return 2 with @new_bib_files empty if there are no \bibdata
+ # lines.
+ # Return 3 if I couldn't locate all the bib_files
+ # Set @new_aux_files to aux files parsed
+
+ my $aux_file = $_[0];
+ local $Pbib_files = $_[1];
+ local $Paux_files = $_[2];
+ local $Pbst_files = $_[3];
+
+ @$Pbib_files = ();
+ @$Pbst_files = ();
+ @$Paux_files = ();
+
+ parse_aux1( $aux_file );
+ if ($#{$Paux_files} < 0) {
+ return 0;
+ }
+ @$Pbib_files = uniqs( @$Pbib_files );
+ @$Pbst_files = uniqs( @$Pbst_files );
+
+ if ( $#{$Pbib_files} == -1 ) {
+ warn "$My_name: No .bib files listed in .aux file '$aux_file' \n",
+ return 2;
+ }
+ my @not_found = &find_file_list1( $Pbib_files, $Pbib_files,
+ '.bib', \@BIBINPUTS );
+ @$Pbib_files = uniqs( @$Pbib_files );
+ &find_file_list1( $Pbst_files, $Pbst_files, '.bst' );
+ @$Pbst_files = uniqs( @$Pbst_files );
+ my @bad_bib = ();
+ foreach ( @$Pbib_files ) {
+ if ( /\s/ ) { push @bad_bib, $_; }
+ }
+ if ($#bad_bib >= 0) {
+ warn "$My_name: White space in an argument list for \\bibliography.\n",
+ " which is not allowed by bibtex. Bad arguments:\n";
+ foreach (@bad_bib ) { warn " '$_'\n"; }
+ return 3;
+ }
+ if ( $#not_found < 0) {
+ warn "$My_name: Found bibliography file(s) [@$Pbib_files]\n"
+ unless $silent;
+ }
+ else {
+ warn "$My_name: Failed to find one or more bibliography files:\n";
+ foreach (@not_found) { warn " '$_'\n"; }
+ if ($force_mode) {
+ warn "==== Force_mode is on, so I will continue. ",
+ "But there may be problems ===\n";
+ }
+ return 3;
+ }
+ return 1;
+} #END parse_aux
+
+#************************************************************
+
+sub parse_aux1
+# Parse single aux file for bib files.
+# Usage: &parse_aux1( aux_file_name )
+# Append newly found bib_filenames in @$Pbib_files, already
+# initialized/in use.
+# Append aux_file_name to @$Paux_files if aux file opened
+# Recursively check \@input aux files
+# Return 1 if success in opening $aux_file_name and parsing it
+# Return 0 if fail to open it
+{
+ my $aux_file = $_[0];
+ my $aux_fh = new FileHandle;
+ if (! open($aux_fh, $aux_file) ) {
+ warn "$My_name: Couldn't find aux file '$aux_file'\n";
+ return 0;
+ }
+ push @$Paux_files, $aux_file;
+AUX_LINE:
+ while (<$aux_fh>) {
+ if ( /^\\bibdata\{(.*)\}/ ) {
+ # \\bibdata{comma_separated_list_of_bib_file_names}
+ # These are normally without the '.bib' extension.
+ push @$Pbib_files, split /,/, $1;
+ }
+ elsif ( /^\\bibstyle\{(.*)\}/ ) {
+ # \\bibstyle{bst_file_name}
+ # Normally without the '.bst' extension.
+ push @$Pbst_files, split /,/, $1;
+ }
+ elsif ( /^\\\@input\{(.*)\}/ ) {
+ # \\@input{next_aux_file_name}
+ &parse_aux1( $aux_dir1.$1 );
+ }
+ else {
+ foreach my $Psub (@aux_hooks) {
+ &$Psub;
+ }
+ }
+ }
+ close($aux_fh);
+ return 1;
+} #END parse_aux1
+
+#************************************************************
+
+#************************************************************
+#************************************************************
+#************************************************************
+
+# Manipulations of main file database:
+
+#************************************************************
+
+sub fdb_get {
+ # Call: fdb_get(filename [, check_time])
+ # Returns an array (time, size, md5) for the current state of the
+ # named file.
+ # The optional argument check_time is either the run_time of some command
+ # that may have changed the file or the last time the file was checked
+ # for changes --- see below.
+ # For non-existent file, deletes its entry in fdb_current,
+ # and returns (0,-1,0) (whatever is in @nofile).
+ # As an optimization, the md5 value is taken from the cache in
+ # fdb_current, if the time and size stamp indicate that the
+ # file has not changed.
+ # The md5 value is recalculated if
+ # the current filetime differs from the cached value:
+ # file has been written
+ # the current filesize differs from the cached value:
+ # file has definitely changed
+ # But the file can also be rewritten without change in filetime when
+ # file processing happens within the 1-second granularity of the
+ # timestamp (notably for aux files from latex on a short source file).
+ # The only case that concerns us is when the file is an input to a program
+ # at some runtime t, the file is rewritten later by the same or another
+ # program, with timestamp t, and when the initial file also has
+ # timestamp t.
+ # A test is applied for this situation if the check_time argument is
+ # supplied and is nonzero.
+
+ my ($file, $check_time) = @_;
+ if ( ! defined $check_time ) { $check_time = 0;}
+ my ($new_time, $new_size) = get_time_size($file);
+ if ( $new_size < 0 ) {
+ delete $fdb_current{$file};
+ return @nofile;
+ }
+ my $recalculate_md5 = 0;
+ if ( ! exists $fdb_current{$file} ) {
+ # Ensure we have a record.
+ $fdb_current{$file} = [@nofile];
+ $recalculate_md5 = 1;
+ }
+ my $file_data = $fdb_current{$file};
+ my ( $time, $size, $md5 ) = @$file_data;
+
+ if ( ($new_time != $time) || ($new_size != $size)
+ || ( $check_time && ($check_time == $time ) )
+ ) {
+ # Only force recalculation of md5 if time or size changed.
+ # However, the physical file time may have changed without
+ # affecting the value of the time coded in $time, because
+ # times are computed with a 1-second granularity.
+ # The only case to treat specially is where the file was created,
+ # then used by the current rule, and then rewritten, all within
+ # the granularity size, otherwise the value of the reported file
+ # time changed, and we've handled it. But we may have already
+ # checked this at an earlier time than the current check. So the
+ # only dangerous case is where the file time equals a check_time,
+ # which is either the run_time of the command or the time of a
+ # previous check.
+ # Else we assume file is really unchanged.
+ $recalculate_md5 = 1;
+ }
+ if ($recalculate_md5) {
+ @$file_data = ( $new_time, $new_size, get_checksum_md5( $file ) );
+ }
+ return @$file_data;;
+} #END fdb_get
+
+#************************************************************
+
+sub fdb_set {
+ # Call: fdb_set(filename, $time, $size, $md5 )
+ # Set data in file data cache, i.e., %fdb_current
+ my ($file, $time, $size, $md5 ) = @_;
+ if ( ! exists $fdb_current{$file} ) {
+ $fdb_current{$file} = [@nofile];
+ }
+ @{$fdb_current{$file}} = ( $time, $size, $md5 );
+} #END fdb_set
+
+#************************************************************
+
+sub fdb_show {
+ # Displays contents of fdb
+ foreach my $file ( sort keys %fdb_current ) {
+ print "'$file': @{$fdb_current{$file}}\n";
+ }
+} #END fdb_show
+
+#************************************************************
+#************************************************************
+#************************************************************
+
+# Routines for manipulating rule database
+
+#************************************************************
+
+sub rdb_read {
+ # Call: rdb_read( $in_name )
+ # Sets rule database from saved file, in format written by rdb_write.
+ # Returns -1 if file could not be read else number of errors.
+ # Thus return value on success is 0
+ # Requires: Rule database initialized with standard rules, with
+ # conditions corresponding to requests determined by
+ # initialization and command line options.
+ # Asssumption: Normally the fdb_latexmk file contains state of
+ # rules and files corresponding to end of last
+ # compilation, and the rules in the file were
+ # active on that run.
+ # Complications arise when that state does not correspond to current
+ # rule set:
+ # (a) Configuration etc may have changed: e.g., different out_dir,
+ # different target rules and files, including different tex engine.
+ # (b) Output extension of primary rule may be different from current
+ # initialized one, because of document properties (use of
+ # \pdfoutput etc).
+ # (c) The same may arise because of misconfigured rules, a situation
+ # that may or may not have changed in current run.
+ # (d) The primary engine requested may not be the one used in
+ # the previous run, possibly because (i) request has
+ # changed, or (ii) document metacommand was obeyed to change
+ # engine. (The last is not currently implemented, but
+ # may/should be in the future.)
+ # (e) Something else, e.g., copying/editing of fdb_latexmk file.
+ #
+ local $in_name = $_[0];
+ my $in_handle = new FileHandle;
+ $in_handle->open( $in_name, '<' )
+ or return ();
+ my $errors = 0;
+ my $state = -1; # Values: -1: before start; 0: outside rule;
+ # 1: in source section;
+ # 2: in generated file section;
+ # 10: ignored rule.
+ my $rule = '';
+ local $run_time = 0;
+ local $source = '';
+ local $dest = '';
+ my $base = '';
+ my %old_actives = (); # Hash: keys are rules in fdb_latexmk file
+ local %new_sources = (); # Hash: rule => { file=>[ time, size, md5, fromrule ] }
+ my $new_source = undef; # Reference to hash of sources for current rule
+LINE:
+ while ( <$in_handle> ) {
+ # Remove leading and trailing white space.
+ s/^\s*//;
+ s/\s*$//;
+ if ($state == -1) {
+ if ( ! /^# Fdb version ([\d]+)$/ ) {
+ warn "$My_name: File-database '$in_name' is not of correct format\n";
+ return 1;
+ }
+ if ( $1 > $fdb_ver) {
+ warn "$My_name: File-database '$in_name' is of too new version, $1 > $fdb_ver\n";
+ return 1;
+ }
+ $state = 0;
+ }
+ # Ignore blank lines and comments
+ if ( /^$/ || /^#/ || /^%/ ) { next LINE;}
+ if ( /^\[\"([^\"]+)\"\]/ ) {
+ # Start of section
+ $rule = $1;
+ my $tail = $'; #' Single quote in comment tricks the parser in
+ # emacs from misparsing an isolated single quote
+ $run_time = $check_time = 0;
+ $source = $dest = $base = '';
+ $old_actives{$rule} = 1;
+ if ( $tail =~ /^\s*(\S+)\s*$/ ) {
+ $run_time = $1;
+ }
+ elsif ( $tail =~ /^\s*(\S+)\s+\"([^\"]*)\"\s+\"([^\"]*)\"\s+\"([^\"]*)\"\s*$/ ) {
+ $run_time = $1;
+ $source = $2;
+ $dest = $3;
+ $base = $4;
+ }
+ elsif ( $tail =~ /^\s*(\S+)\s+\"([^\"]*)\"\s+\"([^\"]*)\"\s+\"([^\"]*)\"\s+(\S+)\s*$/ ) {
+ $run_time = $1;
+ $source = $2;
+ $dest = $3;
+ $base = $4;
+ $check_time = $5;
+ }
+ if ( rdb_rule_exists( $rule ) ) {
+ rdb_one_rule( $rule, \&rdb_read_set_rule );
+ }
+ elsif ($rule =~ /^cusdep\s+(\S+)\s+(\S+)\s+(.+)$/ ) {
+ # Create custom dependency
+ my $fromext = $1;
+ my $toext = $2;
+ my $base = $3;
+ $source = "$base.$fromext";
+# $dest = "$base.$toext";
+ my $func_name = '';
+ foreach my $dep ( @cus_dep_list ) {
+ my ($tryfromext,$trytoext,$must,$try_func_name) = split('\s+',$dep);
+ if ( ($tryfromext eq $fromext) && ($trytoext eq $toext) ) {
+ $func_name = $try_func_name;
+ }
+ }
+ if ($func_name) {
+ my $PAnew_cmd = ['do_cusdep', $func_name];
+ # Set source file as non-existent.
+ # If it existed on last run, it will be in later
+ # lines of the fdb file
+ rdb_create_rule( $rule, 'cusdep', '', $PAnew_cmd, 1,
+ $source, $dest, $base, 0, $run_time, $check_time, 1 );
+ }
+ else {
+ warn "$My_name: In file-database '$in_name', the custom-dependency rule\n",
+ " '$rule' is not available in this session.\n",
+ " Presumably it's no longer in your configuration for latexmk.\n";
+ $state = 10;
+ next LINE;
+ }
+ }
+ elsif ( $rule =~ /^(makeindex|bibtex|biber)\s*(.*)$/ ) {
+ my $PA_extra_gen = [];
+ my $rule_generic = $1;
+ my $int_cmd = '';
+ if ( ! $source ) {
+ # If fdb_file was old-style (v. 1)
+ $source = $2;
+ my $path = '';
+ my $ext = '';
+ ($base, $path, $ext) = fileparseA( $source );
+ $base = $path.$base;
+ if ($rule_generic eq 'makeindex') {
+ $dest = "$base.ind";
+ }
+ elsif ($rule_generic eq 'bibtex') {
+ $dest = "$base.bbl";
+ $source = "$base.aux";
+ }
+ elsif ($rule_generic eq 'biber') {
+ $dest = "$base.bbl";
+ $source = "$base.bcf";
+ }
+ }
+ if ($rule =~ /^makeindex/) { $PA_extra_gen = [ "$base.ilg" ]; }
+ if ($rule =~ /^(bibtex|biber)/) { $PA_extra_gen = [ "$base.blg" ]; }
+ if ($rule =~ /^bibtex/) { $int_cmd = "run_bibtex"; }
+ warn "$My_name: File-database '$in_name': setting rule '$rule'\n"
+ if $diagnostics;
+ my $cmd_type = 'external';
+ my $ext_cmd = ${$rule_generic};
+ warn " Rule kind = '$rule_generic'; ext_cmd = '$ext_cmd';\n",
+ " int_cmd = '$int_cmd';\n",
+ " source = '$source'; dest = '$dest'; base = '$base';\n"
+ if $diagnostics;
+ # Set source file as non-existent.
+ # If it existed on last run, it will be in later
+ # lines of the fdb file
+ rdb_create_rule( $rule, $cmd_type, $ext_cmd, $int_cmd, 1,
+ $source, $dest, $base, 0, $run_time, $check_time, 1, $PA_extra_gen );
+ }
+ else {
+ if ($diagnostics) {
+ warn "$My_name: In file-database '$in_name' rule '$rule'\n",
+ " is not in use in this session\n";
+ }
+ $new_source = undef;
+ $state = 10;
+ next LINE;
+ }
+ $new_source = $new_sources{$rule} = {};
+ $state = 1; #Reading a section, source part
+ }
+ elsif ( ($state <=0) || ($state >= 3) ) {
+ next LINE;
+ }
+ elsif ( /^\(source\)/ ) { $state = 1; next LINE; }
+ elsif ( /^\(generated\)/ ) { $state = 2; next LINE; }
+ elsif ( ($state == 1) && /^\"([^\"]*)\"\s+(\S+)\s+(\S+)\s+(\S+)\s+\"([^\"]*)\"/ ) {
+ # Source file line
+ my $file = $1;
+ my $time = $2;
+ my $size = $3;
+ my $md5 = $4;
+ my $from_rule = $5;
+#?? print " --- File '$file'\n";
+ if ($state != 1) {
+ warn "$My_name: In file-database '$in_name' ",
+ "line $. is outside a section:\n '$_'\n";
+ $errors++;
+ next LINE;
+ }
+ # Set file in database. But ensure we don't do an unnecessary
+ # fdb_get, which can trigger a new MD5 calculation, which is
+ # lengthy for a big file. Ininitially flagging the file
+ # as non-existent solves the problem:
+ rdb_ensure_file( $rule, $file, undef, 1 );
+ rdb_set_file1( $rule, $file, $time, $size, $md5 );
+ fdb_set( $file, $time, $size, $md5 );
+ # Save the rest of the data, especially the from_rule until we know all
+ # the rules, otherwise the from_rule may not exist.
+ # Also we'll have a better chance of looping through files.
+ ${$new_source}{$file} = [ $time, $size, $md5, $from_rule ];
+ }
+ elsif ( ($state == 2) && /^\"([^\"]*)\"/ ) {
+ my $file = $1;
+ rdb_one_rule( $rule, sub{ rdb_add_generated($file); } );
+ }
+ else {
+ warn "$My_name: In file-database '$in_name' ",
+ "line $. is of wrong format:\n '$_'\n";
+ $errors++;
+ next LINE;
+ }
+ }
+ undef $in_handle;
+ # Set cus dependencies.
+ &rdb_set_dependents( keys %rule_db );
+ &rdb_set_rule_net;
+
+ return $errors;
+} # END rdb_read
+
+#************************************************************
+
+sub rdb_read_set_rule {
+ # Rule context assumed. Implicit passing of $dest, $run_time, $check_time,
+ # $in_name used as local variables in calling routine rdb_read;
+ if ($$Ptest_kind == 3) { $$Ptest_kind = 1; }
+ $$Prun_time = $run_time;
+ $$Pcheck_time = $check_time;
+ # Deal with possibility that destination in fdb_latexmk has different name
+ # than the default one. The only case that concerns us is where
+ # the extension is changed (by \pdfoutput, e.g., in tex file). But
+ # it is possible out and aux directories have been chosen differently,
+ # and the user choice there MUST OVERRIDE the value in the fdb_latexmk file.
+ if ($dest ne $$Pdest) {
+ if (! $possible_primaries{$rule} ) {
+ warn "$My_name: In reading rule '$rule' in '$in_name',\n",
+ " name of destination file is not current one; I'll flag rule as out of date.\n";
+ $$Pout_of_date = 10;
+ }
+ elsif ( ! rdb_is_active($rule) ) {
+ warn "$My_name: In reading rule '$rule' in '$in_name',\n",
+ " rule is not currently active.\n";
+ # No fixup now. Causes to be analyzed:
+ # Change of requested files: no action needed.
+ # Obeying of metacommand: not implemented yet.
+ }
+ else {
+ # Get here if rule is active and primary and destination is different
+ warn "$My_name: In reading rule '$rule' in '$in_name',\n",
+ " destination has different name than configured...\n";
+ my ($oldbase, $oldpath, $oldext) = fileparseA( $dest );
+ my ($newbase, $newpath, $newext) = fileparseA( $$Pdest );
+ if ($oldext ne $newext) {
+ if ( ! exists $allowed_output_ext{$oldext} ) {
+ warn " Old extension '$oldext' not allowed.\n";
+ $$Pout_of_date = 10;
+ }
+ else {
+ warn " ===== CHANGING output type from '$newext' to '$oldext' in '$rule'\n";
+ my $switch_error = switch_output( $oldext, $newext );
+ if ($switch_error) {
+ warn " I could not accommodate the changed output extension\n",
+ " (either because the configuration does not allow it\n",
+ " or because there is a conflict with requested filetypes).\n",
+ "===> There may be subsequent warnings, which may or may not be ignorable.\n",
+ "===> If necessary, clean out generated files and try again\n";
+ }
+ }
+ }
+ if ( ($oldbase ne $newbase) || ($oldpath ne $newpath) ) {
+ # There are further issues (e.g., change of out_dir).
+ # Need rerun to correct:
+ $$Pout_of_date = 10;
+ }
+ }
+ }
+} #END rdb_read_set_rule
+
+#************************************************************
+
+sub rdb_write {
+ # Call: rdb_write( $out_name )
+ # Writes to the given file name the database of file and rule data
+ # for all rules needed to make final output
+ # Returns 1 on success, 0 if file couldn't be opened.
+ local $out_name = $_[0];
+ local $out_handle = new FileHandle;
+ if ( ($out_name eq "") || ($out_name eq "-") ) {
+ # Open STDOUT
+ $out_handle->open( '>-' );
+ }
+ else {
+ $out_handle->open( $out_name, '>' );
+ }
+ if (!$out_handle) { return 0; }
+
+ &rdb_set_rule_net;
+
+ print $out_handle "# Fdb version $fdb_ver\n";
+ my @rules = sort &rdb_accessible;
+ rdb_for_some(
+ \@rules,
+ sub {
+ # Omit data on a unused and never-run primary rule:
+ if ( ($$Prun_time == 0)
+ && exists( $possible_primaries{$rule} )
+ && ! exists( $current_primaries{$rule} )
+ )
+ {
+ return;
+ }
+ print $out_handle "[\"$rule\"] $$Prun_time \"$$Psource\" \"$$Pdest\" \"$$Pbase\" $$Pcheck_time\n";
+ rdb_do_files(
+ sub { print $out_handle " \"$file\" $$Ptime $$Psize $$Pmd5 \"$$Pfrom_rule\"\n"; }
+ );
+ print $out_handle " (generated)\n";
+ foreach (keys %$PHdest) {
+ print $out_handle " \"$_\"\n";
+ }
+ }
+ );
+ undef $out_handle;
+ return 1;
+} #END rdb_write
+
+#************************************************************
+
+sub rdb_set_latex_deps {
+ # Assume rule context.
+ # This is intended to be applied only for a primary (LaTeX-like) rule.
+ # Set its dependents etc, using information from log, aux, and fls files.
+ # Use fls file only if $recorder is set, and the fls file was generated
+ # on this run.
+
+ # N.B. A complication which we try and handle in determining
+ # dependent files is that there may be aliasing of file names,
+ # especially when characters are used in file and directory
+ # names that are not pure 7-bit-ASCII. Here is a list of some
+ # of the difficulties that do arise, between, on the one hand,
+ # the filenames specified on latexmk's and the cwd found by
+ # latexmk from the system, and, on the other hand, the filenames
+ # and their components reported by (pdf)latex in the fls and log
+ # files:
+ # 1. Whether the separator of path components is / or \ in
+ # MSWin.
+ # 2. Whether the LFN or the SFN is provided.
+ # 3. Whether the filenames include the cwd or whether they
+ # are relative to the current directory.
+ # 4. Under cygwin, whether the absolute filenames are
+ # specified by UNIX or native MSWin conventions.
+ # (With cygin, the programs used, including the Perl that
+ # executes latexmk, can be any combination of native MSWin
+ # programs and cygwin programs with their UNIX-like
+ # behavior.)
+ # 5. Whether UTF-8 or some other coding is used, and under
+ # which circumstances: e.g., in calls to the OS to access
+ # files, in files output by programs, on latexmk's command
+ # line, on other programs' command lines, by the command
+ # interpreterS.
+ # 6. If UTF-8 is used, what kind of canonicalization is used,
+ # if any. (This is a particular bugbear when files are
+ # transferred between different OSes.)
+ # 7. Whether the name of a file in the current directory is
+ # reported as the simple filename or whether it is
+ # preceeded by ".\" or "./".
+ # 8. How is it determined whether a pathname is absolute or
+ # relative? An absolute pathname in MSWin may start with
+ # a drive letter and a colon, but under UNIX-type systems,
+ # the colon is an ordinary character.
+ # 9. Whether a filename reported in an fls or log file can be
+ # used as is by perl to access a file, or on the command
+ # line to invoke another program, and whether the use on a
+ # command line depends on whether the command line is
+ # executed by a CLI, and by which CLI. (E.g., cmd.exe,
+ # v. sh v. tcsh, etc.)
+ # 10. Whether such a filename for the filename on (pdf)latex's
+ # file agrees with the one on the command line.
+ # The above questions have arisen from actual experiences and
+ # tests.
+ #
+ # In any case, when determining dependent files, we will try to
+ # remove an initial directory string from filenames found in the
+ # fls and log files, whenever it denotes the current
+ # directory. The directory string may be an absolute pathname,
+ # such as MiKTeX writes in both fls and log files, or it may be
+ # simply "./" as given by TeXLive in its log file. There are
+ # several reasons for removing a directory string when possible:
+ #
+ # 1. To avoid having multiple names referring to the same
+ # file in the list of dependents.
+ # 2. Because the name may be in a different coding. Thus
+ # under MSWin 7, cmd.exe and perl (by default) work in an
+ # "ANSI" coding with some code page, but the filenames
+ # written by MiKTeX are UTF-8 coded (and if they are non-ASCII
+ # can't be used for file-processing by Perl without some
+ # trouble). This is a particular problem if the pathname
+ # contains non-ASCII characters; the directory names may not
+ # even be under the user's control, unlike typical filenames.
+ # 3. When it comes to filenames that are then used in calls to
+ # bibtex and makeindex, it is bad to use absolute pathnames
+ # instead of clearly relative pathnames, because the default
+ # security settings are to prohibit writing files to the
+ # corresponding directories, which makes the calls to these
+ # programs unnecessarily fail.
+ #
+ # In removing unnecessary directory-specifying strings, to
+ # convert a filename to a simple specification relative to the
+ # current directory, it will be important to preferentially use
+ # a determination of the current directory from the file being
+ # processed. In the fls file, there is normally a PWD line. In
+ # the log file, if (pdf)latex is started with a filename instead
+ # of a command-executing first line, then this can be determined
+ # from the first few lines of the log file -- see parse_log.
+ # This gives a more reliable determination of the relevant path
+ # string; this is especially important in cases where there is a
+ # mismatch of coding of the current directory, particularly
+ # notable in the above-mentioned case of non-ASCII characters
+ # under MSWin. Other inconsistencies happen when there is a
+ # mixure of cygwin and native MSWin software. There can also be
+ # inconsistencies between whether the separator of pathname
+ # components is "/" or "\". So we will allow for this. The
+ # necessary normalizations of filenames are handled by the
+ # subroutines normalize_filename and normalize_clean_filename.
+ #
+ # I have not tried to handle the (currently rare) cases that the
+ # OS is neither UNIX-like nor MSWin-like.
+ #
+ # Assumption: the list of generated files in %PHdest was already initialized earlier.
+ # In principle, I should do it here, but useful possibilities (e.g.,
+ # see pythontex-latexmk) for subroutine called to process a .tex to add items to
+ # %PHdest. So initializing here is too late.
+
+ # Rules should only be primary
+ if ( $$Pcmd_type ne 'primary' ) {
+ warn "\n$My_name: ==========$My_name: Probable BUG======= \n ",
+ " rdb_set_latex_deps called to set files ",
+ "for non-primary rule '$rule'\n\n";
+ return;
+ }
+
+#?? # We'll prune this by all files determined to be needed for source files.
+#?? my %unneeded_source = %$PHsource;
+
+ # Parse fls and log files to find relevant filenames
+ # Result in the following variables:
+ local %dependents = (); # Maps files to status
+ local @bbl_files = ();
+ local %idx_files = (); # Maps idx_file to (ind_file, base)
+ local %generated_log = (); # Lists generated files found in log file
+ local %generated_fls = (); # Lists generated files found in fls file
+ local %source_fls = (); # Lists source files found in fls file
+ local %first_read_after_write = (); # Lists source files that are only read
+ # after being written (so are not true
+ # source files.
+ local $primary_out = $$Pdest; # output file (dvi or pdf)
+ local %conversions = (); # (pdf)latex-performed conversions.
+ # Maps output file created and read by (pdf)latex
+ # to source file of conversion.
+ local @missing_subdirs = (); # Missing subdirectories in aux_dir
+
+ local $pwd_latex = undef; # Cwd as reported in fls file by (pdf)latex
+
+ local %created_rules = (); # Maps files to rules existing or created to
+ # make them. Use to avoid misunderstood
+ # dependencies when a package creates a
+ # missing file during *latex compliation
+ # instead of just allowing to be made later
+ # by another rule.
+
+ # The following are also returned by parsing routines, but are global,
+ # to be used by caller:
+ # $reference_changed, $bad_reference, $bad_character, $bad_citation, $mult_defined
+
+ # Do I have my own eps-to-pdf conversion?
+ my $epspdf_cusdep = 0;
+ foreach (@cus_dep_list) {
+ if ( /^eps pdf / ) { $epspdf_cusdep = 1; last; }
+ }
+
+ # Analyze fls file first. It tells us the working directory as seen by (pdf)latex
+ # But we'll use the results later, so that they take priority over the findings
+ # from the log file.
+ local $fls_file_analyzed = 0;
+ if ($recorder && test_gen_file($fls_name) ) {
+ $fls_file_analyzed =
+ (0== parse_fls( $fls_name, \%source_fls, \%generated_fls, \%first_read_after_write, \$pwd_latex ));
+ if (! $fls_file_analyzed ) {
+ warn "$My_name: fls file '$fls_name' appears to have been made but it couldn't be opened.\n";
+ }
+ }
+
+ &parse_log;
+ $missing_dirs = 'none'; # Status of missing directories
+ if (@missing_subdirs) {
+ $missing_dirs = 'success';
+ if ($allow_subdir_creation) {
+ foreach my $dir ( uniqs( @missing_subdirs ) ) {
+ if ( -d $dir ) {
+ $missing_dirs = 'failure';
+ warn "$My_name: ==== Directory '$dir' is said to be missing\n",
+ " But it exists!\n";
+ }
+ elsif ( (-e $dir) && (!-d $dir) ) {
+ $missing_dirs = 'failure';
+ warn "$My_name: ==== Directory '$dir' is said to be missing\n",
+ " But a non-directory file of this name exists!\n";
+ }
+ else {
+ if (mkdir $dir) {
+ warn "$My_name: Directory '$dir' created\n";
+ }
+ else {
+ $missing_dirs = 'failure';
+ warn "$My_name: Couldn't create directory '$dir'.\n",
+ " System error: '$!'\n";
+ }
+ }
+ }
+ }
+ else {
+ $missing_dirs = 'not allowed';
+ warn "$My_name: There are missing subdirectories, but their creation\n",
+ " is not allowed. The subdirectories are:\n";
+ foreach my $dir ( uniqs( @missing_subdirs ) ) {
+ warn " '$dir'\n";
+ }
+ }
+ }
+ # Use results from fls file. (N.B. The hashes will be empty if the fls file
+ # wasn't used/analyzed, so we don't need a test as to whether the fls file was
+ # used.
+ foreach (keys %source_fls) {
+ if (! -e ) {
+ # File is listed in .fls file as read, but doesn't exist now.
+ # Therefore it is not a true source file, surely.
+ # Sometimes this is caused by a bug (e.g., lualatex in TeXLive 2016,
+ # 2017) when there is an incorrect line in .fls file. (This
+ # would deserve a warning.)
+ # But sometimes (e.g., with minted package), the file could be
+ # created during a run, read, and then deleted.
+ next;
+ }
+ $dependents{$_} = 4;
+ if ( /\.bbl$/ ) { push @bbl_files, $_; }
+ }
+ foreach (keys %generated_fls) {
+ if (! -e ) {
+ # File is listed in .fls file as written, but doesn't exist now.
+ # Therefore it is not a true externally visible generated file.
+ # (Typically, e.g., with the minted package, it is a temporary
+ # file created during a run and then deleted during the run.)
+ next;
+ }
+ rdb_add_generated( $_ );
+ if ( exists($dependents{$_}) ) {
+ $dependents{$_} = 6;
+ }
+ }
+
+ for my $conv (sort keys %conversions) {
+ my $conv_source = $conversions{$conv};
+ if ( $conv =~ /^(.*)-eps-converted-to\.pdf$/ ) {
+ # Check all the conditions for pdflatex's conversion eps to pdf
+ # are valid; if they are, treat the converted file as not a
+ # source file.
+ my $base = $1;
+ if ( (-e $conv_source) && (-e $conv) && ( $conv_source eq "$base.eps" ) ) {
+ # $conv isn't a real source of (pdf)latex
+ rdb_remove_files( $rule, $conv );
+ delete $dependents{$conv};
+ if ($epspdf_cusdep) {
+ $dependents{"$base.pdf"} = ((-e "$base.pdf") ? 4 : 0 );
+ }
+ }
+ }
+ }
+
+
+
+# ?? !! Should also deal with .run.xml file
+
+ # Handle result on output file:
+ # 1. Non-existent output file, which is because of no content.
+ # This could either be because the source file has genuinely
+ # no content, or because of a missing input file. Since a
+ # missing input file might be correctable by a run of some
+ # other program whose running is provoked AFTER a run of
+ # (pdf)latex, we'll set a diagnostic and leave it to the
+ # rdb_make to handle after all circular dependencies are
+ # resolved.
+ # 2. The output file might be of a different kind than expected
+ # (i.e., dvi instead of pdf, or vv). This could
+ # legitimately occur when the source file (or an invoked
+ # package or class) sets \pdfoutput.
+ $missing_dvi_pdf = '';
+ if ($primary_out eq '') {
+ warn "$My_name: For rule '$rule', no output was made\n";
+ $missing_dvi_pdf = $$Pdest;
+ }
+ elsif ($primary_out ne normalize_filename($$Pdest) ) {
+ my ($actual_base, $actual_path, $actual_ext) = fileparseA( $primary_out );
+ my ($intended_base, $intended_path, $intended_ext) = fileparseA( $$Pdest );
+ if ( $actual_ext ne $intended_ext ) {
+ warn "$My_name: ===For rule '$rule', the extensions differ between the\n",
+ " actual output file '$primary_out',\n",
+ " and the expected output '$$Pdest'.\n";
+ if ( ! exists $allowed_output_ext{$actual_ext} ) {
+ warn " Actual output file has an extension '$actual_ext' that\n",
+ " is not one I know about. I cannot handle this\n";
+ }
+ else {
+ my $switch_error = switch_output( $actual_ext, $intended_ext );
+ if ( $switch_error ) {
+ warn " I could not accommodate the changed output extension\n",
+ " (either because the configuration does not allow it\n",
+ " or because there is a conflict with requested filetypes).\n";
+ $failure = 1;
+ $failure_msg = 'Could not handle change of output extension';
+ }
+ else {
+ warn " Rule structure will be changed suitably.\n";
+ }
+ }
+ }
+ }
+
+ IDX_FILE:
+ foreach my $idx_file ( keys %idx_files ) {
+ my ($ind_file, $ind_base) = @{$idx_files{$idx_file}};
+ my $from_rule = "makeindex $idx_file";
+ if ( ! rdb_rule_exists( $from_rule ) ){
+ print "!!!===Creating rule '$from_rule': '$ind_file' from '$idx_file'\n"
+ if ($diagnostics);
+ rdb_create_rule( $from_rule, 'external', $makeindex, '', 1,
+ $idx_file, $ind_file, $ind_base, 1, 0, 0, 1, [ "$ind_base.ilg" ] );
+ print " ===Source file '$ind_file' for '$rule'\n"
+ if ($diagnostics);
+ rdb_ensure_file( $rule, $ind_file, $from_rule );
+ }
+ # Make sure the .ind file is treated as a detected source file;
+ # otherwise if the log file has it under a different name (as
+ # with MiKTeX which gives full directory information), there
+ # will be problems with the clean-up of the rule concerning
+ # no-longer-in-use source files:
+ $dependents{$ind_file} = 4;
+ if ( ! -e $ind_file ) {
+ # Failure was non-existence of makable file
+ # Leave failure issue to other rules.
+ $failure = 0;
+ }
+ $created_rules{$ind_file} = $from_rule;
+ }
+
+ local %processed_aux_files = ();
+ BBL_FILE:
+ foreach my $bbl_file ( uniqs( @bbl_files ) ) {
+ my ($bbl_base, $bbl_path, $bbl_ext) = fileparseA( $bbl_file );
+ $bbl_base = $bbl_path.$bbl_base;
+ my @new_bib_files = ();
+ my @new_aux_files = ();
+ my @new_bst_files = ();
+ my @biber_source = ( "$bbl_base.bcf" );
+ my $bib_program = 'bibtex';
+ if ( test_gen_file( "$bbl_base.bcf" ) ) {
+ $bib_program = 'biber';
+ }
+ my $from_rule = "$bib_program $bbl_base";
+ print "======= Dealing with '$from_rule'\n" if ($diagnostics);
+ # Don't change to use activation and deactivation here, rather than
+ # creation and removal of rules. This is because rules are to be
+ # created on the fly here with details corresponding to current. So
+ # activating a previously inactive rule, which is out-of-date, may
+ # cause trouble.
+ if ($bib_program eq 'biber') {
+ check_biber_log( $bbl_base, \@biber_source );
+ # Remove OPPOSITE kind of bbl generation:
+ rdb_remove_rule( "bibtex $bbl_base" );
+ }
+ else {
+ parse_aux( "$bbl_base.aux", \@new_bib_files, \@new_aux_files, \@new_bst_files );
+ # Remove OPPOSITE kind of bbl generation:
+ rdb_remove_rule( "biber $bbl_base" );
+ }
+ if ( ! rdb_rule_exists( $from_rule ) ){
+ print " ===Creating rule '$from_rule'\n" if ($diagnostics);
+ if ( $bib_program eq 'biber' ) {
+ rdb_create_rule( $from_rule, 'external', $biber, '', 1,
+ "$bbl_base.bcf", $bbl_file, $bbl_base, 1, 0, 0, 1, [ "$bbl_base.blg" ] );
+ }
+ else {
+ rdb_create_rule( $from_rule, 'external', $bibtex, 'run_bibtex', 1,
+ "$bbl_base.aux", $bbl_file, $bbl_base, 1, 0, 0, 1, [ "$bbl_base.blg" ] );
+ }
+ }
+ $created_rules{$bbl_file} = $from_rule;
+ local %old_sources = ();
+ rdb_one_rule( $from_rule, sub { %old_sources = %$PHsource; } );
+ my @new_sources = ( @new_bib_files, @new_aux_files, @new_bst_files );
+ if ( $bib_program eq 'biber' ) {
+ push @new_sources, @biber_source;
+ }
+ foreach my $source ( @new_sources ) {
+ print " ===Source file '$source' for '$from_rule'\n"
+ if ($diagnostics);
+ rdb_ensure_file( $from_rule, $source );
+ delete $old_sources{$source};
+ }
+ foreach my $source ( @new_aux_files ) {
+ $processed_aux_files{$source} = 1;
+ }
+ if ($diagnostics) {
+ foreach ( keys %old_sources ) {
+ print "Removing no-longer-needed dependent '$_' from rule '$from_rule'\n";
+ }
+ }
+ rdb_remove_files( $from_rule, keys %old_sources );
+ print " ===Source file '$bbl_file' for '$rule'\n"
+ if ($diagnostics);
+ rdb_ensure_file( $rule, $bbl_file, $from_rule );
+ if ( ! -e $bbl_file ) {
+ # Failure was non-existence of makable file
+ # Leave failure issue to other rules.
+ $failure = 0;
+ }
+ }
+
+ if ( ($#aux_hooks > -1) && ! exists $processed_aux_files{$aux_main} ) {
+ my @new_bib_files = ();
+ my @new_aux_files = ();
+ my @new_bst_files = ();
+ parse_aux( $aux_main, \@new_bib_files, \@new_aux_files, \@new_bst_files );
+ foreach my $source ( @new_aux_files ) {
+ $processed_aux_files{$source} = 1;
+ }
+ }
+
+NEW_SOURCE:
+ foreach my $new_source (keys %dependents) {
+ print " ===Source file for rule '$rule': '$new_source'\n"
+ if ($diagnostics);
+ if ( exists $first_read_after_write{$new_source} ) {
+ if ( dep_at_start($new_source) ) {
+ #warn "--- READ ONLY AFTER WRITE OF '$new_source'\n";
+ $dependents{$new_source} = 7;
+ }
+ else {
+ #warn "--- READ ONLY AFTER CREATE OF '$new_source'\n";
+ $dependents{$new_source} = 6;
+ }
+ }
+ if ( ($dependents{$new_source} == 5)
+ || ($dependents{$new_source} == 6)
+ ) {
+ # (a) File was detected in "No file..." line in log file.
+ # Typically file was searched for early in run of
+ # latex/pdflatex, was not found, and then was written
+ # later in run.
+ # or (b) File was written during run.
+ # In both cases, if file doesn't already exist in database, we
+ # don't know its previous status. Therefore we tell
+ # rdb_ensure_file that if it needs to add the file to its
+ # database, then the previous version of the file should be
+ # treated as non-existent, to ensure another run is forced.
+ rdb_ensure_file( $rule, $new_source, undef, 1 );
+ }
+ elsif ( $dependents{$new_source} == 7 ) {
+ # File was result of conversion by (pdf)latex.
+ my $cnv_source = $conversions{$new_source};
+ rdb_ensure_file( $rule, $new_source );
+# if ($cnv_source && ($cnv_source !~ /\"/ ) ) {
+ if ($cnv_source ) {
+ # Conversion from $cnv_source to $new_source
+ # implies that effectively $cnv_source is a source
+ # of the (pdf)latex run.
+ rdb_ensure_file( $rule, $cnv_source );
+ }
+ # Flag that changes of the generated file during a run
+ # do not require a rerun:
+ rdb_one_file( $new_source, sub{ $$Pcorrect_after_primary = 1; } );
+ }
+ else {
+ # But we don't need special precautions for ordinary user files
+ # (or for files that are generated outside of latex/pdflatex).
+ rdb_ensure_file( $rule, $new_source );
+ }
+ if ( ($dependents{$new_source} == 6)
+ || ($dependents{$new_source} == 7)
+ ) {
+ rdb_add_generated($new_source);
+ }
+ }
+
+ # Some packages (e.g., bibtopic) generate a dummy error-message-providing
+ # bbl file when a bbl file does not exist. Then the fls and log files
+ # show the bbl file as created by the primary run and hence as a
+ # generated file. Since we now have a rule to create a real bbl file,
+ # the information in the fls and log files no longer represents a
+ # correct dependency, so the bbl file is to be removed from the
+ # generated files.
+ foreach (keys %created_rules) { rdb_remove_generated( $_ ); }
+
+ my @more_sources = &rdb_set_dependents( $rule );
+ my $num_new = $#more_sources + 1;
+ foreach (@more_sources) {
+ $dependents{$_} = 4;
+ if ( ! -e $_ ) {
+ # Failure was non-existence of makable file
+ # Leave failure issue to other rules.
+ $failure = 0;
+ $$Pchanged = 1; # New files can be made. Ignore error.
+ }
+ }
+ if ($diagnostics) {
+ if ($num_new > 0 ) {
+ print "$num_new new source files for rule '$rule':\n";
+ foreach (@more_sources) { print " '$_'\n"; }
+ }
+ else {
+ print "No new source files for rule '$rule':\n";
+ }
+ my @first_read_after_write = sort keys %first_read_after_write;
+ if ($#first_read_after_write >= 0) {
+ print "The following files were only read after being written:\n";
+ foreach (@first_read_after_write) {
+ print " '$_'\n";
+ }
+ }
+ }
+ my @files_not_needed = ();
+ foreach (keys %$PHsource) {
+ if ( ! exists $dependents{$_} ) {
+ print "Removing no-longer-needed dependent '$_' from rule '$rule'\n"
+ if $diagnostics;
+ push @files_not_needed, $_;
+ }
+ }
+ rdb_remove_files( $rule, @files_not_needed );
+
+} # END rdb_set_latex_deps
+
+#************************************************************
+
+sub switch_output {
+ # Assume rule context for primary rule
+ # Return 0 on success, non-zero error code on failure
+
+ my ( $actual_ext, $intended_ext ) = @_;
+ if ( $actual_ext eq $intended_ext) { return 0; }
+ if ( ! $can_switch ) { return 1; }
+
+ if ( $intended_ext eq '.dvi' ) {
+ rdb_deactivate_derequest( 'dvipdf', 'pspdf' );
+ }
+ elsif ( $intended_ext eq '.xdv' ) {
+ rdb_deactivate_derequest( 'xdvipdfmx' );
+ }
+ else { }
+
+ if ( $actual_ext eq '.dvi' ) {
+ rdb_activate_request( 'dvipdf' );
+ $input_extensions{$rule} = $standard_input_extensions{latex};
+ }
+ elsif ( $actual_ext eq '.xdv' ) {
+ rdb_activate_request( 'xdvipdfmx' );
+ $input_extensions{$rule} = $standard_input_extensions{xelatex};
+ }
+ else {
+ $input_extensions{$rule} = $standard_input_extensions{pdflatex};
+ }
+
+ my $old_dest = $$Pdest;
+ my $new_dest = $$Pdest;
+ $new_dest =~ s/$intended_ext$/$actual_ext/;
+ # Compensate for MiKTeX's behavior: dvi and pdf are put in out_dir, but xdv is put in aux_dir:
+ if ( ($actual_ext eq '.xdv') && ($out_dir ne $aux_dir) ){ $new_dest =~ s/^$out_dir1/$aux_dir1/; }
+ if ( ($intended_ext eq '.xdv') && ($out_dir ne $aux_dir) ){ $new_dest =~ s/^$aux_dir1/$out_dir1/; }
+
+ rdb_change_dest( $new_dest );
+
+ # Some fixes to avoid spurious error conditions:
+ $switched_primary_output = 1;
+ if (-e $$Pdest) {
+ $missing_dvi_pdf = '';
+ if ($$Plast_result == 1 ) { $$Plast_result = 0; }
+ }
+ else { $missing_dvi_pdf = $$Pdest; }
+
+ return 0;
+} #END switch_output
+
+#************************************************************
+
+sub test_gen_file {
+ # Usage: test_gen_file( filename )
+ # Tests whether the file was generated during a run of (pdf)latex.
+ # Assumes context for primary rule.
+ # Two kinds of test are used:
+ # a. From %generated_log, which works after the log file has been parsed,
+ # but only for certain files and for those TeX engines (not MiKTeX)
+ # that put \openout lines in log file.
+ # b. By the file existing and being at least as new as the system
+ # time at the start of the run. But we allow for a measured
+ # offset between filetime and system time, which could be
+ # nonzero if the file is on a different, remote system than the
+ # one running latexmk. We must also allow a threshold in the
+ # comparisons of filetimes to allow for the inaccuracy of the
+ # offset measurement.
+ my $file = shift;
+ return exists $generated_log{$file}
+ || ( -e $file && ( get_mtime( $file ) >= $$Prun_time + $filetime_offset - $filetime_causality_threshold));
+}
+
+#************************************************************
+
+sub dep_at_start {
+ # Usage: dep_at_start( filename )
+ # Tests whether the file was source file and existed at start of run.
+ # Assumes context for primary rule.
+ my $time = undef;
+ rdb_one_file( shift, sub{ $time = $$Ptime; } );
+ return (defined $time) && ($time != 0);
+}
+
+#************************************************************
+
+sub rdb_find_new_files {
+ # Call: rdb_find_new_files
+ # Assumes rule context for primary rule.
+ # Deal with files which were missing and for which a method
+ # of finding them has become available:
+ # (a) A newly available source file for a custom dependency.
+ # (b) When there was no extension, a file with appropriate
+ # extension
+ # (c) When there was no extension, and a newly available source
+ # file for a custom dependency can make it.
+
+ my %new_includes = ();
+
+MISSING_FILE:
+ foreach my $missing ( keys %$PHsource ) {
+ next if ( $$PHsource{$missing} != 0 );
+ my ($base, $path, $ext) = fileparseA( $missing );
+ $ext =~ s/^\.//;
+ if ( -e "$missing.tex" ) {
+ $new_includes{"$missing.tex"} = 1;
+ }
+ elsif ( -e $missing ) {
+ $new_includes{$missing} = 1;
+ }
+ elsif ( $ext ne "" ) {
+ foreach my $dep (@cus_dep_list){
+ my ($fromext,$toext) = split('\s+',$dep);
+ if ( ( "$ext" eq "$toext" )
+ && ( -e "$path$base.$fromext" )
+ ) {
+ # Source file for the missing file exists
+ # So we have a real include file, and it will be made
+ # next time by rdb_set_dependents
+ $new_includes{$missing} = 1;
+ }
+ else {
+ # no point testing the $toext if the file doesn't exist.
+ }
+ next MISSING_FILE;
+ }
+ }
+ else {
+ # $_ doesn't exist, $_.tex doesn't exist,
+ # and $_ doesn't have an extension
+ foreach my $dep (@cus_dep_list){
+ my ($fromext,$toext) = split('\s+',$dep);
+ if ( -e "$path$base.$fromext" ) {
+ # Source file for the missing file exists
+ # So we have a real include file, and it will be made
+ # next time by &rdb__dependents
+ $new_includes{"$path$base.$toext"} = 1;
+# next MISSING_FILE;
+ }
+ if ( -e "$path$base.$toext" ) {
+ # We've found the extension for the missing file,
+ # and the file exists
+ $new_includes{"$path$base.$toext"} = 1;
+# next MISSING_FILE;
+ }
+ }
+ }
+ } # end MISSING_FILES
+
+ # Sometimes bad line-breaks in log file (etc) create the
+ # impression of a missing file e.g., ./file, but with an incorrect
+ # extension. The above tests find the file with an extension,
+ # e.g., ./file.tex, but it is already in the list. So now I will
+ # remove files in the new_include list that are already in the
+ # include list. Also handle aliasing of file.tex and ./file.tex.
+ # For example, I once found:
+# (./qcdbook.aux (./to-do.aux) (./ideas.aux) (./intro.aux) (./why.aux) (./basics
+#.aux) (./classics.aux)
+
+ my $found = 0;
+ foreach my $file (keys %new_includes) {
+# if ( $file =~ /\"/ ) {next; }
+ my $stripped = $file;
+ $stripped =~ s{^\./}{};
+ if ( exists $PHsource{$file} ) {
+ delete $new_includes{$file};
+ }
+ else {
+ $found ++;
+ rdb_ensure_file( $rule, $file );
+ }
+ }
+
+ if ( $diagnostics && ( $found > 0 ) ) {
+ warn "$My_name: Detected previously missing files:\n";
+ foreach ( sort keys %new_includes ) {
+ warn " '$_'\n";
+ }
+ }
+ return $found;
+} # END rdb_find_new_files
+
+#************************************************************
+
+sub rdb_set_dependents {
+ # Call rdb_set_dependents( rules ...)
+ # Returns array (sorted), of new source files.
+ local @new_sources = ();
+ local @deletions = ();
+
+# Shouldn't recurse. The definite rules to be examined are given.
+ rdb_for_some( [@_], 0, \&rdb_one_dep );
+# OLD rdb_recurse( [@_], 0, \&rdb_one_dep );
+ foreach (@deletions) {
+ my ($rule, $file) = @$_;
+ rdb_remove_files( $rule, $file );
+ }
+ &rdb_make_links;
+ return uniqs( @new_sources );
+} #END rdb_set_dependents
+
+#************************************************************
+
+sub rdb_find_source_file {
+ # Helper for searching dependencies in all paths inside the TEXINPUTS
+ # environment variable.
+ my $test = "$_[0].$_[1]";
+ if ( -e $test ) {
+ return $_[0];
+ }
+ if ( exists $ENV{TEXINPUTS} ) {
+ foreach my $searchpath (split $search_path_separator, $ENV{TEXINPUTS}) {
+ my $file = File::Spec->catfile($searchpath,$_[0]);
+ my $test = "$file.$_[1]";
+ if ( -e $test ) {
+ return $file;
+ }
+ }
+ }
+ return "$_[0]";
+}
+
+#************************************************************
+
+sub rdb_one_dep {
+ # Helper for finding dependencies. One case, $rule and $file given
+ # Assume file (and rule) context for DESTINATION file.
+
+ # Only look for dependency if $rule is primary rule (i.e., latex
+ # or pdflatex) or is a custom dependency:
+ if ( (! exists $possible_primaries{$rule}) && ($rule !~ /^cusdep/) ) {
+ return;
+ }
+#print "=============ONE_DEP: '$rule' '$file'\n";
+ local $new_dest = $file;
+ my ($base_name, $path, $toext) = fileparseA( $new_dest );
+ $base_name = $path.$base_name;
+ $toext =~ s/^\.//;
+ my $Pinput_extensions = $input_extensions{$rule};
+DEP:
+ foreach my $dep ( @cus_dep_list ) {
+ my ($fromext,$proptoext,$must,$func_name) = split('\s+',$dep);
+ if ( $toext eq $proptoext ) {
+ $base_name = rdb_find_source_file($base_name, $fromext);
+ my $source = "$base_name.$fromext";
+ # Found match of rule
+ if ($diagnostics) {
+ print "Found cusdep: $source to make $rule:$new_dest ====\n";
+ }
+ if ( -e $source ) {
+ $$Pfrom_rule = "cusdep $fromext $toext $base_name";
+ my $new_new_dest = "$base_name.$toext";
+ if ($new_new_dest ne $new_dest) {
+ rdb_ensure_file( $rule, $new_new_dest );
+ $new_dest = $new_new_dest;
+ }
+ local @PAnew_cmd = ( 'do_cusdep', $func_name );
+ if ( !-e $new_dest ) {
+ push @new_sources, $new_dest;
+ }
+ if (! rdb_rule_exists( $$Pfrom_rule ) ) {
+ print "$My_name: === Creating rule '$$Pfrom_rule'\n" if $diagnostics;
+ rdb_create_rule( $$Pfrom_rule, 'cusdep', '', \@PAnew_cmd, 3,
+ $source, $new_dest, $base_name, 0 );
+ }
+ return;
+ }
+ else {
+ # Source file does not exist
+ if ( !$force_mode && ( $must != 0 ) ) {
+ # But it is required that the source exist ($must !=0)
+ $failure = 1;
+ $failure_msg = "File '$base_name.$fromext' does not exist ".
+ "to build '$base_name.$toext'";
+ return;
+ }
+ elsif ( $$Pfrom_rule =~ /^cusdep $fromext $toext / ) {
+ # Source file does not exist, destination has the rule set.
+ # So turn the from_rule off
+ $$Pfrom_rule = '';
+ }
+ else {
+ }
+ }
+ }
+ elsif ( ($toext eq '')
+ && (! -e $file )
+ && (! -e "$base_name.$proptoext" )
+ && exists $$Pinput_extensions{$proptoext}
+ ) {
+ # Empty extension and non-existent destination
+ # This normally results from \includegraphics{A}
+ # without graphics extension for file, when file does
+ # not exist. So we will try to find something to make it.
+ $base_name = rdb_find_source_file($base_name, $fromext);
+ my $source = "$base_name.$fromext";
+ if ( -e $source ) {
+ $new_dest = "$base_name.$proptoext";
+ my $from_rule = "cusdep $fromext $proptoext $base_name";
+ push @new_sources, $new_dest;
+ print "Ensuring rule for '$from_rule', to make '$new_dest'\n"
+ if $diagnostics > -1;
+ local @PAnew_cmd = ( 'do_cusdep', $func_name );
+ if (! rdb_rule_exists( $from_rule ) ) {
+ print "$My_name: === Creating rule '$$Pfrom_rule'\n" if $diagnostics;
+ rdb_create_rule( $from_rule, 'cusdep', '', \@PAnew_cmd, 3,
+ $source, $new_dest, $base_name, 0 );
+ }
+ rdb_ensure_file( $rule, $new_dest, $from_rule );
+ # We've now got a spurious file in our rule. But don't mess
+ # with deleting an item we are in the middle of!
+ push @deletions, [$rule, $file];
+ return;
+ }
+ } # End of Rule found
+ } # End DEP
+ if ( (! -e $file) && $use_make_for_missing_files ) {
+ # Try to make the missing file
+ #Set character to surround filenames in commands:
+ my $q = $quote_filenames ? '"' : '';
+ if ( $toext ne '' ) {
+ print "$My_name: '$rule': source file '$file' doesn't exist. I'll try making it...\n";
+ &Run_subst( "$make $q$file$q" );
+ if ( -e $file ) {
+ return;
+ }
+ }
+ else {
+ print "$My_name: '$rule': source '$file' doesn't exist.\n",
+ " I'll try making it with allowed extensions \n";
+ foreach my $try_ext ( keys %$Pinput_extensions ) {
+ my $new_dest = "$file.$try_ext";
+ &Run_subst( "$make $q$new_dest$q" );
+ if ( -e $new_dest ) {
+ print "SUCCESS in making '$new_dest'\n";
+ # Put file in rule, without a from_rule, but
+ # set its state as non-existent, to correspond
+ # to file's state before the file was made
+ # This ensures a rerun of (pdf)latex is provoked.
+ rdb_ensure_file( $rule, $new_dest, undef, 1 );
+ push @new_sources, $new_dest;
+ push @deletions, [$rule, $file];
+ # Flag need for a new run of (pdf)latex despite
+ # the error due to a missing file.
+ $$Pout_of_date_user = 1;
+ return;
+ }
+ }
+ }
+ }
+} #END rdb_one_dep
+
+#************************************************************
+
+sub rdb_list {
+ # Call: rdb_list()
+ # List rules and their source files
+ print "===Rules:\n";
+ local $count_rules = 0;
+ my @accessible_all = &rdb_accessible;
+ rdb_for_some(
+ \@accessible_all,
+ sub{ $count_rules++;
+ print "Rule '$rule' depends on:\n";
+ },
+ sub{ print " '$file'\n"; },
+ sub{ print " and generates:\n";
+ foreach (keys %$PHdest) { print " '$_'\n"; }
+# print " default_extra_generated:\n";
+# foreach (@$PA_extra_gen) { print " '$_'\n"; }
+ },
+ );
+ if ($count_rules <= 0) {
+ print " ---No rules defined\n";
+ }
+} #END rdb_list
+
+#************************************************************
+
+sub deps_list {
+ # Call: deps_list(fh)
+ # List dependent files to file open on fh
+ my $fh = $_[0];
+ print $fh "#===Dependents, and related info, for $filename:\n";
+ my @dest_exts = ();
+ if ($pdf_mode) {push @dest_exts, '.pdf';}
+ if ($dvi_mode) {push @dest_exts, '.dvi';}
+ if ($postscript_mode) {push @dest_exts, '.ps';}
+ my %source = ( $texfile_name => 1 );
+ my @generated = ();
+ my @accessible_all = &rdb_accessible;
+ rdb_for_some(
+ \@accessible_all,
+ sub{ push @generated, keys %$PHdest; },
+ sub{ $source{$file} = 1; }
+ );
+ foreach (keys %generated_exts_all) {
+ (my $name = /%R/ ? $_ : "%R.$_") =~ s/%R/${aux_dir1}${root_filename}/;
+ push @generated, $name;
+ }
+ show_array( "Generated:", @generated ) if $diagnostics;
+ foreach (@generated) {
+ delete $source{$_};
+ }
+ show_array( "Sources:", keys %source ) if $diagnostics;
+ foreach my $ext (@dest_exts) {
+ # Don't insert name of deps file in targets.
+ # The previous behavior of inserting the name of the deps file
+ # matched the method recommended by GNU make for automatically
+ # generated prerequisites -- see Sec. "Generating Prerequisites
+ # Automatically" of GNU make manual (v. 4.2). But this can
+ # cause problems in complicated cases, and as far as I can see,
+ # it doesn't actually help, despite the reasoning given.
+ # The only purpose of the deps file is to to determine source
+ # files for a particular rule. The files whose changes make the
+ # deps file out-of-date are the same as those that make the real
+ # target file (e.g., .pdf) out-of-date. So the GNU method seems
+ # completely unnecessary.
+ print $fh "${out_dir1}${root_filename}${ext} :";
+ foreach (sort keys %source) {
+ print $fh "\\\n $_";
+ }
+ print $fh "\n";
+ }
+ print $fh "#===End dependents for $filename:\n";
+ if ($dependents_phony) {
+ print $fh "\n#===Phony rules for $filename:\n\n";
+ foreach (sort keys %source) {
+ print $fh "$_ :\n\n";
+ }
+ print $fh "#===End phony rules for $filename:\n";
+ }
+} #END deps_list
+
+#************************************************************
+
+sub rdb_show {
+ # Call: rdb_show()
+ # Displays contents of rule data base.
+ # Side effect: Exercises access routines!
+ print "===Rules:\n";
+ local $count_rules = 0;
+ rdb_for_actives(
+ sub{ $count_rules++;
+ my @int_cmd = @$PAint_cmd;
+ foreach (@int_cmd) {
+ if ( !defined($_) ) { $_='undef';}
+ }
+ print " [$rule]: '$$Pcmd_type' '$$Pext_cmd' '@int_cmd' $$Ptest_kind ",
+ "'$$Psource' '$$Pdest' '$$Pbase' $$Pout_of_date $$Pout_of_date_user\n"; },
+ sub{ print " '$file': $$Ptime $$Psize $$Pmd5 '$$Pfrom_rule'\n"; }
+ );
+ if ($count_rules <= 0) {
+ print " ---No rules defined\n";
+ }
+} #END rdb_show
+
+#************************************************************
+
+sub rdb_target_array {
+ # Returns array of all rules implicated by %target_rules and %target_files
+ my %rules = &rdb_target_hash;
+ return keys %rules;
+} # End rdb_target_array
+
+#************************************************************
+
+sub rdb_target_hash {
+ # Returns hash mapping to 1 all rules implicated by %target_rules and %target_files
+ my %rules = %target_rules;
+ foreach (keys %target_files) {
+ if (exists $from_rules{$_}) { $rules{$from_rules{$_}} = 1; }
+ }
+ return %rules;
+} # End rdb_target_hash
+
+#************************************************************
+
+sub rdb_accessible {
+ # Call: &rdb_accessible
+ # Returns array of rules accessible from target rules and rules to make target files
+
+ local %accessible_rules = &rdb_target_hash;
+ rdb_recurse( [keys %accessible_rules], sub{ $accessible_rules{$rule} = 1; } );
+ return keys %accessible_rules;
+} #END rdb_accessible
+
+#************************************************************
+#************************************************************
+#************************************************************
+
+sub rdb_make {
+ # Call: &rdb_make
+ # Makes the targets and prerequisites.
+ # Leaves one-time rules to last.
+ # Does appropriate repeated makes to resolve dependency loops
+
+ # Returns 0 on success, nonzero on failure.
+
+ # General method: Find all accessible rules, then repeatedly make
+ # them until all accessible rules are up-to-date and the source
+ # files are unchanged between runs. On termination, all
+ # accessible rules have stable source files.
+ #
+ # One-time rules are view and print rules that should not be
+ # repeated in an algorithm that repeats rules until the source
+ # files are stable. It is the calling routine's responsibility to
+ # arrange to call them, or to use them here with caution.
+ #
+ # Note that an update-viewer rule need not be considered
+ # one-time. It can be legitimately applied everytime the viewed
+ # file changes.
+ #
+ # Note also that the criterion of stability is to be applied to
+ # source files, not to output files. Repeated application of a
+ # rule to IDENTICALLY CONSTANT source files may produce different
+ # output files. This may be for a trivial reason (e.g., the
+ # output file contains a time stamp, as in the header comments for
+ # a typical postscript file), or for a non-trivial reason (e.g., a
+ # stochastic algorithm, as in abcm2ps).
+ #
+ # This caused me some actual trouble. In general, circular
+ # dependencies produce non-termination, and the the following
+ # situation is an example of a generic situation where certain
+ # rules must be obeyed in order to obtain proper results:
+ # 1. A/the latex source file contains specifications for
+ # certain postprocessing operations. Standard (pdf)latex
+ # already has this, for indexing and bibliography.
+ # 2. In the case in point that caused me trouble, the
+ # specification was for musical tunes that were contained
+ # in external source files not directly input to
+ # (pdf)latex. But in the original version, there was a
+ # style file (abc.sty) that caused latex itself to call
+ # abcm2ps to make .eps files for each tune that were to be
+ # read in on the next run of latex.
+ # 3. Thus the specification can cause a non-terminating loop
+ # for latexmk, because the output files of abcm2ps changed
+ # even with identical input.
+ # 4. The solution was to
+ # a. Use a style file abc_get.sty that simply wrote the
+ # specification on the tunes to the .aux file in a
+ # completely deterministic fashion.
+ # b. Instead of latex, use a script abclatex.pl that runs
+ # latex and then extracts the abc contents for each tune
+ # from the source abc file. This is also
+ # deterministic.
+ # c. Use a cusdep rule in latexmk to convert the tune abc
+ # files to eps. This is non-deterministic, but only
+ # gets called when the (deterministic) source file
+ # changes.
+ # This solves the problem. Latexmk works. Also, it is no
+ # longer necessary to enable write18 in latex, and multiple
+ # unnecessary runs of abcm2ps are no longer used.
+ #
+ # The order of testing and applying rules is chosen by the
+ # following heuristics:
+ # 1. Both latex and pdflatex may be used, but the resulting
+ # aux files etc may not be completely identical. Define
+ # latex and pdflatex as primary rules. Apply the general
+ # method of repeated circulating through all rules until
+ # the source files are stable for each primary rule
+ # separately. Naturally the rules are all accessible
+ # rules, but excluding primary rules except for the current
+ # primary.
+ # 2. Assume that the primary rules are relatively
+ # time-consuming, so that unnecessary passes through them
+ # to check stability of the source files should be avoided.
+ # 3. Assume that although circular dependencies exist, the
+ # rules can nevertheless be thought of as basically
+ # non-circular, and that many rules are strictly or
+ # normally non-circular. In particular cusdep rules are
+ # typically non-circular (e.g., fig2eps), as are normal
+ # output processing rules like dvi2ps.
+ # 4. The order for the non-circular approximation is
+ # determined by applying the assumption that an output file
+ # from one rule that is read in for an earlier stage is
+ # unchanged.
+ # HOWEVER, at a first attempt, the ordering is not needed. It
+ # only gives an optimization
+ # 5. (Note that these assumptions could be violated, e.g., if
+ # $dvips is arranged not only to do the basic dvips
+ # command, but also to extract information from the ps file
+ # and feed it back to an input file for (pdf)latex.)
+ # 6. Nevertheless, the overall algorithm should allow
+ # circularities. Then the general criterion of stability
+ # of source files covers the general case, and also
+ # robustly handles the case that the USER changes source
+ # files during a run. This is particularly important in
+ # -pvc mode, given that a full make on a large document can
+ # be quite lengthy in time, and moreover that a user
+ # naturally wishes to make corrections in response to
+ # errors, particularly latex errors, and have them apply
+ # right away.
+ # This leads to the following approach:
+ # 1. Classify accessible rules as: primary, pre-primary
+ # (typically cusdep, bibtex, makeindex, etc), post-primary
+ # (typically dvips, etc), and one-time
+ # 2. Then stratify the rules into an order of application that
+ # corresponds to the basic feedforward structure, with the
+ # exclusion of one-time rules.
+ # 3. Always require that one-time rules are among the
+ # explicitly requested rules, i.e., the last to be applied,
+ # were we to apply them. Anything else would not match the
+ # idea of a one-time rule.
+ # 4. Then work as follows:
+ # a. Loop over primaries
+ # b. For each primary, examine each pre-primary rule and
+ # apply if needed, then the primary rule and then each
+ # post-primary rule. The ordering of the pre-primary
+ # and post-primary rules was found in step 2.
+ # BUT applying the ordering is not essential
+ # c. Any time that a pre-primary or primary rule is
+ # applied, loop back to the beginning of step b. This
+ # ensures that bibtex etc are applied before rerunning
+ # (pdf)latex, and also covers changing source files, and
+ # gives priority to quick pre-primary rules for changing
+ # source files against slow reruns of latex.
+ # d. Then apply post-primary rules in order, but not
+ # looping back after each rule. This non-looping back
+ # is because the rules are normally feed-forward only.
+ # BUT applying the ordering is not essential
+ # e. But after completing post-primary rules do loop back
+ # to b if any rules were applied. This covers exotic
+ # circular dependence (and as a byproduct, changing
+ # source files).
+ # f. On each case of looping back to b, re-evaluate the
+ # dependence setup to allow for the effect of changing
+ # source files.
+ #
+
+
+ &rdb_set_rule_net;
+
+ local %pass = ();
+ local $failure = 0; # General accumulated error flag
+ local $missing_dvi_pdf = ''; # Did primary run fail to make its output file?
+ local $runs = 0;
+ local $too_many_passes = 0;
+ local %rules_applied = ();
+ local $switched_primary_output = 0;
+ my $retry_msg = 0; # Did I earlier say I was going to attempt
+ # another pass after a failure?
+ rdb_for_some ( [keys %rule_db],
+ sub{ $pass{$rule} = 0;
+ foreach (keys %$PHsource_rules) {
+ $$PHsource_rules{$_} = 0;
+ }
+ }
+ );
+ PASS:
+ while (1==1) {
+ # Exit condition at end of body of loop.
+ $runs = 0;
+ $switched_primary_output = 0;
+ my $previous_failure = $failure;
+ $failure = 0;
+ local $newrule_nofile = 0; # Flags whether rule created for
+ # making currently non-existent file, which
+ # could become a needed source file for a run
+ # and therefore undo an error condition
+ foreach my $rule (keys %rule_db) {
+ # Update %pass in case new rules have been created
+ if (! exists $pass{$rule} ) { $pass{$rule} = 0; }
+ }
+ if ($diagnostics) {
+ print "Make: doing pre_primary and primary...\n";
+ }
+ # Do the primary run if it is needed. On return $runs == 0
+ # signals that nothing was run (and hence no output
+ # files changed), either because no input files
+ # changed and no run was needed, or because the
+ # number of passes through the rule exceeded the
+ # limit. In the second case $too_many_runs is set.
+ rdb_for_some( [@pre_primary, $current_primary], \&rdb_make1 );
+ if ($switched_primary_output) {
+ print "=========SWITCH OF OUTPUT WAS DONE.\n";
+ next PASS;
+ }
+ if ( ($runs > 0) && ! $too_many_passes ) {
+ $retry_msg = 0;
+ if ( $force_mode || (! $failure) || $switched_primary_output ) {
+ next PASS;
+ }
+ # Get here on failure, without being in force_mode
+ if ( $newrule_nofile ) {
+ $retry_msg = 1;
+ print "$My_name: Error on run, but found possibility to ",
+ "make new source files\n";
+ next PASS;
+ }
+ else { last PASS; }
+ }
+ if ($runs == 0) {
+ # $failure not set on this pass, so use value from previous pass:
+ $failure = $previous_failure;
+ if ($retry_msg) {
+ print "But in fact no new files made\n";
+ }
+ if ($failure && !$force_mode ) { last PASS; }
+ }
+ if ( $missing_dvi_pdf ) {
+ # No output from primary, after completing circular dependence
+ warn "Failure to make '$missing_dvi_pdf'\n";
+ $failure = 1;
+ last PASS;
+ }
+ if ($diagnostics) {
+ print "Make: doing post_primary...\n";
+ }
+ rdb_for_some( [@post_primary], \&rdb_make1 );
+ if ( ($runs == 0) || $too_many_passes ) {
+ # If $too_many_passes is set, it should also be that
+ # $runs == 0; but for safety, I also checked
+ # $too_many_passes.
+ last PASS;
+ }
+ }
+ continue {
+ # Re-evaluate rule classification and accessibility,
+ # but do not change primaries.
+ # Problem is that %current_primaries gets altered
+ &rdb_set_rule_net;
+ } #End PASS
+
+ rdb_for_some( [@unusual_one_time], \&rdb_make1 );
+ rdb_write( $fdb_name );
+
+ if ($#primary_warning_summary > -1) {
+ # N.B. $mult_defined, $bad_reference, $bad_character, $bad_citation also available here.
+ show_array( "$My_name: Summary of warnings from last run of (pdf)latex:",
+ @primary_warning_summary );
+ }
+ if (! $silent) {
+ if ($failure && $force_mode) {
+ print "$My_name: Errors, in force_mode: so I tried finishing targets\n";
+ }
+ elsif ($failure) {
+ print "$My_name: Errors, so I did not complete making targets\n";
+ }
+ else {
+ local @dests = ();
+ rdb_for_some( [@_], sub{ push @dests, $$Pdest if ($$Pdest); } );
+ print "$My_name: All targets (@dests) are up-to-date\n";
+ }
+ }
+ rdb_for_some ( [keys %rule_db],
+ sub{
+ foreach my $s_rule (keys %$PHsource_rules) {
+ $$PHsource_rules{$s_rule} = $pass{$s_rule};
+ }
+ }
+ );
+ return $failure;
+} #END rdb_make
+
+#-------------------
+
+sub rdb_show_rule_errors {
+ local @errors = ();
+ local @warnings = ();
+ rdb_for_actives(
+ sub{
+ if ($$Plast_message ne '') {
+ if ($$Plast_result == 200) {
+ push @warnings, "$rule: $$Plast_message";
+ }
+ else {
+ push @errors, "$rule: $$Plast_message";
+ }
+ }
+ elsif ($$Plast_result == 1) {
+ push @errors, "$rule: failed to create output file";
+ }
+ elsif ($$Plast_result == 2) {
+ push @errors, "$rule: gave an error";
+ }
+ elsif ($$Prun_time == 0) {
+ # This can have innocuous causes. So don't report
+ }
+ }
+ );
+ if ($#warnings > -1) {
+ warn "Collected warning summary (may duplicate other messages):\n";
+ foreach (@warnings){
+ warn " $_\n";
+ }
+ }
+ if ($#errors > -1) {
+ warn "Collected error summary (may duplicate other messages):\n";
+ foreach (@errors){
+ warn " $_\n";
+ }
+ }
+ return $#errors+1;
+}
+
+#-------------------
+
+sub rdb_make1 {
+ # Call: rdb_make1
+ # Helper routine for rdb_make.
+ # Carries out make at level of given rule (all data available).
+ # Assumes contexts for recursion, make, and rule, and
+ # assumes that source files for the rule are to be considered
+ # up-to-date.
+ if ($diagnostics) { print " Make1 $rule\n"; }
+ if ($failure & ! $force_mode) {return;}
+ if ( ! defined $pass{$rule} ) {$pass{$rule} = 0; }
+
+ # Special fix up for bibtex:
+ my $bibtex_not_run = -1; # Flags status as to whether this is a
+ # bibtex rule and if it is, whether out-of-date condition is to
+ # be ignored.
+ # -1 => not a bibtex rule
+ # 0 => no special treatment
+ # 1 => don't run bibtex because of non-existent bibfiles
+ # (and setting to do this test)
+ # 2 => don't run bibtex because of setting
+ my @missing_bib_files = ();
+ if ( $rule =~ /^(bibtex|biber)/ ) {
+ $bibtex_not_run = 0;
+ if ($bibtex_use == 0) {
+ $bibtex_not_run = 2;
+ }
+ elsif ( ($bibtex_use == 1) || ($bibtex_use == 1.5) ) {
+ foreach ( keys %$PHsource ) {
+ if ( ( /\.bib$/ ) && (! -e $_) ) {
+ push @missing_bib_files, $_;
+ $bibtex_not_run = 1;
+ }
+ }
+ }
+ }
+
+ if ( ($$Prun_time == 0) && exists($possible_primaries{$rule}) ) {
+ push @rules_never_run, $rule;
+ $$Pout_of_date = 1;
+ $$Plast_result = -1;
+ }
+ else {
+ if ( $$Pdest && (! -e $$Pdest) ) {
+ # With a non-existent destination, if we haven't made any passes
+ # through a rule, rerunning the rule is good, because the file
+ # may fail to exist because of being deleted by the user (for ex.)
+ # rather than because of a failure on a previous run.
+ # (We could do better with a flag in fdb file.)
+ # But after the first pass, the situation is different.
+ # For a primary rule (pdf)latex, the lack of a destination file
+ # could result from there being zero content due to a missing
+ # essential input file. The input file could be generated
+ # by a program to be run later (e.g., a cusdep or bibtex),
+ # so we should wait until all passes are completed before
+ # deciding a non-existent destination file is an error.
+ # For a custom dependency, the rule may be obsolete, and
+ # if the source file does not exist also, we should simply
+ # not run the rule, but not set an error condition.
+ # Any error will arise at the (pdf)latex level due to a
+ # missing source file at that level.
+ if ( $$Psource && (! -e $$Psource)
+ && ( ( $$Pcmd_type ne 'primary') )
+ ) {
+ # Main source file doesn't exist, and rule is NOT primary.
+ # No action, since a run is pointless. Primary is different:
+ # file might be found elsewhere (by kpsearch from (pdf)latex),
+ # while non-existence of main source file is a clear error.
+ }
+ elsif ( $$Pcmd_type eq 'delegated' ) {
+ # Delegate to destination rule
+ }
+ elsif ( $pass{$rule}==0) {
+ push @no_dest, $$Pdest;
+ $$Pout_of_date = 1;
+ }
+ if ( $$Pcmd_type eq 'primary' ) {
+ $missing_dvi_pdf = $$Pdest;
+ }
+ }
+ }
+
+ &rdb_clear_change_record;
+ &rdb_flag_changes_here(0);
+
+ if (!$$Pout_of_date) {
+ return;
+ }
+ if ($diagnostics) { print " remake\n"; }
+ if (!$silent) {
+ print "$My_name: applying rule '$rule'...\n";
+ &rdb_diagnose_changes( "Rule '$rule': " );
+ }
+
+ # We are applying the rule, so its source file state for when it
+ # was last made is as of now:
+ # ??IS IT CORRECT TO DO NOTHING IN CURRENT VERSION?
+
+ # The actual run
+ my $return = 0; # Return code from called routine
+ # Rule may have been created since last run:
+ if ( ! defined $pass{$rule} ) {$pass{$rule} = 0; }
+ if ( $pass{$rule} >= $max_repeat ) {
+ # Avoid infinite loop by having a maximum repeat count
+ # Getting here represents some kind of weird error.
+ warn "$My_name: Maximum runs of $rule reached ",
+ "without getting stable files\n";
+ $too_many_passes = 1;
+ # Treat rule as completed, else in -pvc mode get infinite reruns:
+ $$Pout_of_date = 0;
+ $failure = 1;
+ $failure_msg = "'$rule' needed too many passes";
+ return;
+ }
+
+ $rules_applied{$rule} = 1;
+ $runs++;
+
+ $pass{$rule}++;
+ if ($bibtex_not_run > 0) {
+ if ($bibtex_not_run == 1 ) {
+ show_array ("$My_name: I WON'T RUN '$rule' because I don't find the following files:",
+ @missing_bib_files);
+ }
+ elsif ($bibtex_not_run == 2 ) {
+ warn "$My_name: I AM CONFIGURED/INVOKED NOT TO RUN '$rule'\n";
+ }
+ $return = &rdb_dummy_run1;
+ }
+ else {
+ warn_running( "Run number $pass{$rule} of rule '$rule'" );
+ if ($$Pcmd_type eq 'primary' ) {
+ $return = &rdb_primary_run;
+ }
+ else { $return = &rdb_run1; }
+ }
+ if ($$Pchanged) {
+ $newrule_nofile = 1;
+ $return = 0;
+ }
+ elsif ( $$Pdest && ( !-e $$Pdest ) && (! $failure) ){
+ # If there is a destination to make, but for some reason
+ # it did not get made, and no other error was reported,
+ # then a priori there appears to be an error condition:
+ # the run failed. But there are some important cases in
+ # which this is a wrong diagnosis.
+ if ( ( $$Pcmd_type eq 'cusdep') && $$Psource && (! -e $$Psource) ) {
+ # However, if the rule is a custom dependency, this is not by
+ # itself an error, if also the source file does not exist. In
+ # that case, we may have the situation that (1) the dest file is no
+ # longer needed by the tex file, and (2) therefore the user
+ # has deleted the source and dest files. After the next
+ # latex run and the consequent analysis of the log file, the
+ # cusdep rule will no longer be needed, and will be removed.
+
+ # So in this case, do NOT report an error
+ $$Pout_of_date = 0;
+ }
+ elsif ($$Pcmd_type eq 'primary' ) {
+ # For a primary rule, i.e., (pdf)latex, not to produce the
+ # expected output file may not be an error condition.
+ # Diagnostics were handled in parsing the log file.
+ # Special action in main loop in rdb_make
+ $missing_dvi_pdf = $$Pdest;
+ }
+ elsif ($return == -2) {
+ # Missing output file was reported to be NOT an error
+ $$Pout_of_date = 0;
+ }
+ elsif ( ($bibtex_use <= 1.5) && ($bibtex_not_run > 0) ) {
+ # Lack of destination file is not to be treated as an error
+ # for a bibtex rule when latexmk is configured not to treat
+ # this as an error, and the lack of a destination file is the
+ # only error.
+ $$Pout_of_date = 0;
+ }
+ else {
+ $failure = 1;
+ }
+ }
+ if ( ($return != 0) && ($return != -2) ) {
+ $failure = 1;
+ $$Plast_result = 2;
+ if ( !$$Plast_message ) {
+ $$Plast_message = "Run of rule '$rule' gave a non-zero error code";
+ }
+# !!?? $failure_msg = $$Plast_message;
+
+ }
+ foreach ( keys %$PHsource_rules ) {
+ $$PHsource_rules{$_} = $pass{$_};
+ }
+} #END rdb_make1
+
+#************************************************************
+
+sub rdb_classify_rules {
+ # Usage: &rdb_classify_rules
+ # Assume the following variables are available (global or local):
+ # Input:
+ # %target_rules # Set to target rules
+ # %target_files # Set to target files
+ # %possible_primaries
+
+ # Output:
+ # %current_primaries # Keys are actual primaries
+ # @pre_primary # Array of rules
+ # @post_primary # Array of rules
+ # @unusual_one_time # Array of rules
+ # @pre_primary and @post_primary are in natural order of application.
+
+ local @requested_targets = &rdb_target_array;
+ local $state = 0; # Post-primary
+ local @classify_stack = ();
+
+ %current_primaries = ();
+ @pre_primary = ();
+ @post_primary = ();
+ @unusual_one_time = ();
+
+ rdb_recurse( \@requested_targets, \&rdb_classify1, 0,0, \&rdb_classify2 );
+
+ # Reverse, as tendency is to find last rules first.
+ @pre_primary = reverse @pre_primary;
+ @post_primary = reverse @post_primary;
+
+ my @current_primaries = keys %current_primaries;
+ if ($#current_primaries < 0) {
+ die "$My_name: No active primary rules found. I have to stop.\n";
+ }
+ elsif ($#current_primaries > 0) {
+ die "$My_name: More than one active primary rule found. I have to stop.\n",
+ " Primary rules: @current_primaries\n";
+ }
+ $current_primary = $current_primaries[0];
+
+ if ($diagnostics) {
+ print "Rule classification: \n";
+ if ($#requested_targets < 0) {
+ print " No requested rules\n";
+ }
+ else {
+ print " Requested rules:\n";
+ foreach ( @requested_targets ) { print " $_\n"; }
+ }
+ if ($#pre_primary < 0) {
+ print " No pre-primaries\n";
+ }
+ else {
+ print " Pre-primaries:\n";
+ foreach (@pre_primary) { print " $_\n"; }
+ }
+ print " Primaries:\n";
+ foreach (keys %current_primaries) { print " $_\n"; }
+ if ($#post_primary < 0) {
+ print " No post-primaries\n";
+ }
+ else {
+ print " Post-primaries:\n";
+ foreach (@post_primary) { print " $_\n"; }
+ }
+ if ($#unusual_one_time < 0) {
+ print " No inner-level one_time rules, as expected\n";
+ }
+ else {
+ print " Inner-level one_time rules:\n";
+ foreach ( @unusual_one_time ) { print " $_\n"; }
+ }
+ my @normal_one_time = keys %one_time;
+ if ($#normal_one_time < 0) {
+ print " No outer-level one_time rules\n";
+ }
+ else {
+ print " Outer-level one_time rules:\n";
+ foreach ( @normal_one_time ) { print " $_\n"; }
+ }
+ } #end diagnostics
+
+} #END rdb_classify_rules
+
+#-------------------
+
+sub rdb_classify1 {
+ # Helper routine for rdb_classify_rules
+ # Applied as rule_act1 in recursion over rules
+ # Assumes rule context, and local variables from rdb_classify_rules
+ push @classify_stack, [$state];
+ if ( exists $possible_one_time{$rule} ) {
+ # Normally, we will have already extracted the one_time rules,
+ # and they will never be accessed here. But just in case of
+ # problems or generalizations, we will cover all possibilities:
+ if ($depth > 1) {
+ warn "ONE TIME rule not at outer level '$rule'\n";
+ }
+ push @unusual_one_time, $rule;
+ }
+ elsif ($state == 0) {
+ if ( exists $possible_primaries{$rule} ) {
+ $state = 1; # In primary rule
+ $current_primaries{ $rule } = 1;
+ }
+ else {
+ push @post_primary, $rule;
+ }
+ }
+ else {
+ $state = 2; # in post-primary rule
+ push @pre_primary, $rule;
+ }
+} #END rdb_classify1
+
+#-------------------
+
+sub rdb_classify2 {
+ # Helper routine for rdb_classify_rules
+ # Applied as rule_act2 in recursion over rules
+ # Assumes rule context
+ ($state) = @{ pop @classify_stack };
+} #END rdb_classify2
+
+#************************************************************
+
+
+sub rdb_run1 {
+ # Assumes contexts for: rule.
+ # Unconditionally apply the rule
+ # Returns return code from applying the rule.
+ # Otherwise: 0 on other kind of success,
+ # -1 on error,
+ # -2 when missing dest_file is to be ignored
+
+ # Source file data, by definition, correspond to the file state just
+ # before the latest run, and the run_time to the time just before the run:
+ &rdb_update_files;
+ $$Prun_time = time();
+ $$Pchanged = 0; # No special changes in files
+ $$Plast_result = 0;
+ $$Plast_message = '';
+
+ # Return values for external command:
+ my $return = 0;
+
+ # Find any internal command
+ my @int_args = @$PAint_cmd;
+ my $int_cmd = shift @int_args;
+ my @int_args_for_printing = @int_args;
+ foreach (@int_args_for_printing) {
+ if ( ! defined $_ ) { $_ = 'undef'; }
+ }
+ if ($int_cmd) {
+ print "For rule '$rule', running '\&$int_cmd( @int_args_for_printing )' ...\n";
+ $return = &$int_cmd( @int_args );
+ }
+ elsif ($$Pext_cmd) {
+ $return = &Run_subst() / 256;
+ }
+ else {
+ warn "$My_name: Either a bug OR a configuration error:\n",
+ " No command provided for '$rule'\n";
+ &traceback();
+ $return = -1;
+ $$Plast_result = 2;
+ $$Plast_message = "Bug or configuration error; incorrect command type";
+ }
+ if ( $rule =~ /^biber/ ) {
+ my @biber_source = ( );
+ my $retcode = check_biber_log( $$Pbase, \@biber_source );
+ foreach my $source ( @biber_source ) {
+# if ( $source =~ /\"/ ) {next; }
+ print " ===Source file '$source' for '$rule'\n"
+ if ($diagnostics);
+ rdb_ensure_file( $rule, $source );
+ }
+ if ($retcode == 5) {
+ # Special treatment if sole missing file is bib file
+ # I don't want to treat that as an error
+ $return = 0;
+ $$Plast_result = 200;
+ $$Plast_message = "Could not find bib file for '$$Pbase'";
+ push @warnings, "Bib file not found for '$$Pbase'";
+ }
+ elsif ($retcode == 6) {
+ # Missing control file. Need to remake it (if possible)
+ # Don't treat missing bbl file as error.
+ warn "$My_name: bibtex control file missing. Since that can\n",
+ " be recreated, I'll try to do so.\n";
+ $return = -2;
+ rdb_for_some( [keys %current_primaries], sub{ $$Pout_of_date = 1; } );
+ }
+ elsif ($retcode == 4) {
+ $$Plast_result = 2;
+ $$Plast_message = "Could not find all biber source files for '$$Pbase'";
+ push @warnings, "Not all biber source files found for '$$Pbase'";
+ }
+ elsif ($retcode == 3) {
+ $$Plast_result = 2;
+ $$Plast_message = "Could not open biber log file for '$$Pbase'";
+ push @warnings, $$Plast_message;
+ }
+ elsif ($retcode == 2) {
+ $$Plast_message = "Biber errors: See file '$$Pbase.blg'";
+ push @warnings, $$Plast_message;
+ }
+ elsif ($retcode == 1) {
+ push @warnings, "Biber warnings for '$$Pbase'";
+ }
+ elsif ($retcode == 10) {
+ push @warnings, "Biber found no citations for '$$Pbase'";
+ # Biber doesn't generate a bbl file in this situation.
+ $return = -2;
+ }
+ elsif ($retcode == 11) {
+ push @warnings, "Biber: malformed bcf file for '$$Pbase'. IGNORE";
+ if (!$silent) {
+ warn "$My_name: biber found malformed bcf file for '$$Pbase'.\n",
+ " I'll ignore error, and delete any bbl file.\n";
+ }
+ # Malformed bcf file is a downstream consequence, normally,
+ # of an error in (pdf)latex run. So this is not an error
+ # condition in biber itself.
+ # Current version of biber deletes bbl file.
+ # Older versions (pre-2016) made an incorrect bbl file, which
+ # tended to cause latex errors, and give a self-perpetuating error.
+ # To be safe, ensure the bbl file doesn't exist.
+ unlink $$Pdest;
+ # The missing bbl file is now not an error:
+ $return = -2;
+# ??????? BCF
+# Following is intended to work, but creates infinite loop
+# in malformed bcf file situation under -pvc.
+# since on each check for change in ANY file, pvc finds changed file
+# Need to restrict pvc reruns to case of changed USER files
+# # To give good properties for (pdf)latex rule, it is best
+# # to have a valid bbl file that exists:
+# create_empty_file( $$Pdest );
+# $return = 0;
+
+ }
+ }
+ if ( $rule =~ /^bibtex/ ) {
+ my $retcode = check_bibtex_log($$Pbase);
+ if ( ! -e $$Psource ) {
+ $retcode = 10;
+ rdb_for_some( [keys %current_primaries], sub{ $$Pout_of_date = 1; } );
+ }
+ if ($retcode == 3) {
+ $$Plast_result = 2;
+ $$Plast_message = "Could not open bibtex log file for '$$Pbase'";
+ push @warnings, $$Plast_message;
+ }
+ elsif ($retcode == 2) {
+ $$Plast_message = "Bibtex errors: See file '$$Pbase.blg'";
+ $failure = 1;
+ push @warnings, $$Plast_message;
+ }
+ elsif ($retcode == 1) {
+ push @warnings, "Bibtex warnings for '$$Pbase'";
+ }
+ elsif ($retcode == 10) {
+ push @warnings, "Bibtex found no citations for '$$Pbase',\n",
+ " or bibtex found a missing aux file\n";
+ if (! -e $$Pdest ) {
+ warn "$My_name: Bibtex did not produce '$$Pdest'. But that\n",
+ " was because of missing files, so I will continue.\n";
+ $return = -2;
+ }
+ else {
+ $return = 0;
+ }
+ }
+ }
+
+ $updated = 1;
+ if ($$Ptest_kind == 3) {
+ # We are time-criterion first time only. Now switch to
+ # file-change criterion
+ $$Ptest_kind = 1;
+ }
+ $$Pout_of_date = $$Pout_of_date_user = 0;
+
+ if ( ($$Plast_result == 0) && ($return != 0) && ($return != -2) ) {
+ $$Plast_result = 2;
+ if ($$Plast_message eq '') {
+ $$Plast_message = "Command for '$rule' gave return code $return";
+ if ($rule =~ /^(pdf|lua|xe|)latex/) {
+ $$Plast_message .= "\n Refer to '$log_name' for details";
+ }
+ elsif ($rule =~ /^makeindex/) {
+ $$Plast_message .= "\n Refer to '${aux_dir1}${root_filename}.ilg' for details";
+ }
+ }
+ }
+ elsif ( $$Pdest && (! -e $$Pdest) && ($return != -2) ) {
+ $$Plast_result = 1;
+ }
+ return $return;
+} # END rdb_run1
+
+#-----------------
+
+sub rdb_dummy_run1 {
+ # Assumes contexts for: rule.
+ # Update rule state as if the rule ran successfully,
+ # but don't run the rule.
+ # Returns 0 (success code)
+
+ # Source file data, by definition, correspond to the file state just before
+ # the latest run, and the run_time to the time just before the run:
+ &rdb_update_files;
+ $$Prun_time = time();
+ $$Pchanged = 0; # No special changes in files
+ $$Plast_result = 0;
+ $$Plast_message = '';
+
+ if ($$Ptest_kind == 3) {
+ # We are time-criterion first time only. Now switch to
+ # file-change criterion
+ $$Ptest_kind = 1;
+ }
+ $$Pout_of_date = $$Pout_of_date_user = 0;
+
+ return 0;
+} # END rdb_dummy_run1
+
+#-----------------
+
+sub Run_subst {
+ # Call: Run_subst( cmd, msg, options, source, dest, base )
+ # Runs command with substitutions.
+ # If an argument is omitted or undefined, it is replaced by a default:
+ # cmd is the command to execute
+ # msg is whether to print a message:
+ # 0 for not, 1 according to $silent setting, 2 always
+ # options, source, dest, base: correspond to placeholders.
+ # Substitutions:
+ # %S=source, %D=dest, %B=base, %R=root=base for latex, %O=options,
+ # %T=texfile, %Y=$aux_dir1, %Z=$out_dir1
+ # This is a globally usable subroutine, and works in a rule context,
+ # and outside.
+ # Defaults:
+ # cmd: $PPext_cmd if defined, else '';
+ # msg: 1
+ # options: ''
+ # source: $$Psource if defined, else $texfile_name;
+ # dest: $$Pdest if defined, else $view_file, else '';
+ # base: $$Pbase if defined, else $root_filename;
+
+ my ($ext_cmd, $msg, $options, $source, $dest, $base ) = @_;
+
+ $ext_cmd ||= ( $Pext_cmd ? $$Pext_cmd : '' );
+ $msg = ( defined $msg ? $msg : 1 );
+ $options ||= '';
+ $source ||= ( $Psource ? $$Psource : $texfile_name );
+ $dest ||= ( $Pdest ? $$Pdest : ( $view_file || '' ) );
+ $base ||= ( $Pbase ? $$Pbase : $root_filename );
+
+ if ( $ext_cmd eq '' ) {
+ return 0;
+ }
+
+ #Set character to surround filenames:
+ my $q = $quote_filenames ? '"' : '';
+
+ my %subst = (
+ '%A' => $q.$tex_basename.$q,
+ '%B' => $q.$base.$q,
+ '%D' => $q.$dest.$q,
+ '%O' => $options,
+ '%S' => $q.$source.$q,
+ '%R' => $q.$root_filename.$q,
+ '%S' => $q.$source.$q,
+ '%T' => $q.$texfile_name.$q,
+ '%Y' => $q.$aux_dir1.$q,
+ '%Z' => $q.$out_dir1.$q,
+ '%%' => '%' # To allow literal %B, %R, etc, by %%B.
+ );
+ if ($pre_tex_code) {
+ $subst{'%U'} = $q.$pre_tex_code.$q;
+ $subst{'%P'} = "$q$pre_tex_code\\input{$source}$q";
+ }
+ else {
+ $subst{'%U'} = '';
+ $subst{'%P'} = $subst{'%S'};
+ }
+ if ( ($^O eq "MSWin32" ) && $MSWin_back_slash ) {
+ foreach ( '%R', '%B', '%T', '%S', '%D', '%Y', '%Z' ) {
+ $subst{$_} =~ s(/)(\\)g;
+ }
+ }
+
+ my @tokens = split /(%.)/, $ext_cmd;
+ foreach (@tokens) {
+ if (exists($subst{$_})) { $_ = $subst{$_}; }
+ }
+ $ext_cmd = join '', @tokens;
+
+ my ($pid, $return) =
+ ( ($msg == 0) || ( ($msg == 1) && $silent ) )
+ ? &Run($ext_cmd)
+ : &Run_msg($ext_cmd);
+ return $return;
+} #END Run_subst
+
+#-----------------
+
+sub rdb_primary_run {
+#?? See multipass_run in previous version Aug 2007 for issues
+ # Call: rdb_primary_run
+ # Assumes contexts for: recursion, make, & rule.
+ # Assumes (a) the rule is a primary,
+ # (b) a run has to be made,
+ # (c) source files have been made.
+ # This routine carries out the run of the rule unconditionally,
+ # and then parses log file etc.
+ my $return = 0;
+
+ if ( ! $filetime_offset_measured ) {
+ $filetime_offset = get_filetime_offset( $aux_dir1."tmp" );
+ if ( (abs($filetime_offset) > $filetime_offset_report_threshold)
+ && ($diagnostics || ! $silent) )
+ {
+ warn "$My_name: I am working around an offset relative to my system time by\n",
+ " $filetime_offset secs for file times in directory '$aux_dir1'.\n";
+ }
+ $filetime_offset_measured = 1;
+ }
+
+ # Need to reset the list of generated files. If we don't it can contain
+ # out-of-date items from previous run. (Think bibtopic, which writes
+ # bbl files!)
+ &rdb_initialize_generated;
+ my $return_latex = &rdb_run1;
+
+ # Need to worry about changed directory, changed output extension
+ # Where else is $missing_dvi_pdf set? Was it initialized?
+ if (-e $$Pdest) { $missing_dvi_pdf = '';}
+
+ # Handle case that log file is caused to be in an unexpected place,
+ # from a configuration error:
+ &find_set_log;
+
+ if ($recorder) {
+ # Handle problem that some version of (pdf)latex give fls files
+ # of name latex.fls or pdflatex.fls instead of $root_filename.fls.
+ # Also that setting of -output-directory -aux-directory is not
+ # respected by (pdf)latex, at least in some versions.
+ my $std_fls_file = $fls_name;
+ my @other_fls_names = ( );
+ if ( $rule =~ /^pdflatex/ ) {
+ push @other_fls_names, "pdflatex.fls";
+ }
+ else {
+ push @other_fls_names, "latex.fls";
+ }
+ if ( $aux_dir1 ne '' ) {
+ push @other_fls_names, "$root_filename.fls";
+ # MiKTeX uses out_dir for .fls. However, it seems logical to
+ # me for .fls to be in aux_dir. So I'll allow for this
+ # Possibility, although I don't know if it has been used.
+ push @other_fls_names, "$aux_dir1$root_filename.fls";
+ }
+ # Find the first non-standard fls file and copy it to the standard
+ # place. But only do this if the file time is compatible with being
+ # generated in the current run, and if the standard fls file hasn't
+ # been made in the current run, as tested by the use of
+ # test_gen_file; that avoids problems with fls files leftover from
+ # earlier runs with other versions of latex.
+ if ( ! test_gen_file( $std_fls_file ) ) {
+ foreach my $cand (@other_fls_names) {
+ if ( test_gen_file( $cand ) ) {
+ copy $cand, $std_fls_file;
+ last;
+ }
+ }
+ }
+ if ( ! test_gen_file( $std_fls_file ) ) {
+ warn "$My_name: fls file doesn't appear to have been made.\n";
+ }
+ }
+
+ # Find current set of source files:
+ &rdb_set_latex_deps;
+
+ # For each file of the kind made by epstopdf.sty during a run,
+ # if the file has changed during a run, then the new version of
+ # the file will have been read during the run. Unlike the usual
+ # case, we will NOT need to redo the primary run because of the
+ # change of this file during the run. Therefore set the file as
+ # up-to-date:
+ rdb_do_files( sub { if ($$Pcorrect_after_primary) {&rdb_update1;} } );
+
+ $updated = 1; # Flag that some dependent file has been remade
+
+#?? # Fix the state of the files as of now: this will solve the
+#?? # problem of latex and pdflatex interfering with each other,
+#?? # at the expense of some non-optimality
+#?? #?? Check this is correct:
+#?? &rdb_update_files;
+
+ if ( $diagnostics ) {
+ print "$My_name: Rules after run: \n";
+ rdb_show();
+ }
+
+ $return = $return_latex;
+
+# ???? Is the following needed?
+ if ($return_latex && $$Pout_of_date_user) {
+ print "Error in (pdf)LaTeX, but change of user file(s), ",
+ "so ignore error & provoke rerun\n"
+ if (! $silent);
+ $return = 0;
+ }
+ # Summarize issues that may have escaped notice:
+ @primary_warning_summary = ();
+ if ($bad_reference) {
+ push @primary_warning_summary,
+ "Latex failed to resolve $bad_reference reference(s)";
+ }
+ if ($mult_defined) {
+ push @primary_warning_summary,
+ "Latex found $mult_defined multiply defined reference(s)";
+ }
+ if ($bad_character) {
+ push @primary_warning_summary,
+ "=====Latex reported missing or unavailable character(s).\n".
+ "=====See log file for details.";
+ }
+ if ($bad_citation) {
+ push @primary_warning_summary,
+ "Latex failed to resolve $bad_citation citation(s)";
+ }
+ if ( $diagnostics && ($#primary_warning_summary > -1) ) {
+ show_array( "$My_name: Summary of warnings:", @primary_warning_summary );
+ }
+ return $return;
+} #END rdb_primary_run
+
+#************************************************************
+
+sub rdb_clear_change_record {
+ # Initialize diagnostics for reasons for running rule.
+ @changed = ();
+ %changed_rules = ();
+ @changed_user = ();
+ @disappeared = ();
+ @no_dest = (); # We are not now using this
+ @rules_never_run = ();
+ @rules_to_apply = (); # This is used in recursive application
+ # of rdb_flag_changes_here, to list
+ # rules that were out-of-date for some reason.
+} #END rdb_clear_change_record
+
+#************************************************************
+
+sub rdb_flag_changes_here {
+ # Flag changes in current rule.
+ # Assumes rule context.
+ # Usage: rdb_flag_changes_here( ignore_run_time )
+ # Argument: if true then fdb_get shouldn't do runtime test
+ # for recalculation of md5
+ local $ignore_run_time = $_[0];
+ if ( ! defined $ignore_run_time ) { $ignore_run_time = 0; }
+
+ $$Pcheck_time = time();
+
+ local $dest_mtime = 0;
+ $dest_mtime = get_mtime($$Pdest) if ($$Pdest);
+ rdb_do_files( \&rdb_file_change1);
+ while ( my ($s_rule, $l_pass) = each %$PHsource_rules ) {
+ if ( defined $pass{$s_rule}
+ && ($pass{$s_rule} > $l_pass)
+ )
+ {
+ $changed_rules{$s_rule} = $rule;
+ $$Pout_of_date = 1;
+ }
+ }
+ if ($$Pout_of_date) {
+ push @rules_to_apply, $rule;
+ }
+#?? print "======== flag: $rule $$Pout_of_date ==========\n";
+} #END rdb_flag_changes_here
+
+#************************************************************
+
+sub rdb_file_change1 {
+ # Call: &rdb_file_change1
+ # Assumes rule and file context. Assumes $dest_mtime set.
+ # Flag whether $file in $rule has changed or disappeared.
+ # Set rule's make flag if there's a change.
+
+ my $check_time_argument = 0;
+ if (! $ignore_run_time ) {
+ $check_time_argument = max( $$Pcheck_time, $$Prun_time );
+ }
+ my ($new_time, $new_size, $new_md5) = fdb_get($file, $check_time_argument );
+ my $ext_no_period = ext_no_period( $file );
+ my $generated = exists $from_rules{$file};
+ if ( ($new_size < 0) && ($$Psize >= 0) ) {
+ # print "Disappeared '$file' in '$rule'\n";
+ push @disappeared, $file;
+ # No reaction is good.
+ #$$Pout_of_date = 1;
+ # ??? 1 Sep. 2008: I do NOT think so, for cusdep no-file-exists issue
+ # ??? 30 Sep 2008: I think I have this fixed. There were other changes
+ # needed. No-change-flagged is correct. The array @disappeared flags
+ # files that have disappeared, if I need to know. But having a source
+ # file disappear is not a reason for a remake unless I know how to
+ # make the file. If the file is a destination of a rule, that rule
+ # will be rerun. It may be that the user is changing another source
+ # in such a way that the disappeared file won't be needed. Before the
+ # change is applied we get a superfluous infinite loop.
+ return;
+ }
+ if ( ($new_size < 0) && ($$Psize < 0) ) {
+ return;
+ }
+ # Primarily use md5 signature to determine whether file contents have
+ # changed.
+ # Backup by file size change, but only in the case where there is
+ # no pattern of lines to ignore in testing for a change
+ if ( ($new_md5 ne $$Pmd5)
+ || (
+ (! exists $hash_calc_ignore_pattern{$ext_no_period})
+ && ($new_size != $$Psize)
+ )
+ ) {
+ push @changed, $file;
+ $$Pout_of_date = 1;
+ if ( ! $generated ) {
+ push @changed_user, $file;
+ $$Pout_of_date_user = 1;
+ }
+ }
+ elsif ( $new_time != $$Ptime ) {
+ $$Ptime = $new_time;
+ }
+ if ( ( ($$Ptest_kind == 2) || ($$Ptest_kind == 3) )
+ && (! $generated)
+ && ( $new_time > $dest_mtime )
+ ) {
+ push @changed, $file;
+ $$Pout_of_date = $$Pout_of_date_user = 1;
+ }
+} #END rdb_file_change1
+
+#************************************************************
+
+sub rdb_user_changes {
+ # Argument(s): a set of top level rules.
+ # Aim: Find any changed user files for those rules and all ancestors.
+ # Assumption: List of from_rules for files is up-to-date.
+ &rdb_clear_change_record;
+ rdb_recurse( [@_], sub{ &rdb_flag_changes_here(1); } );
+ if ($#changed_user >=0) { show_array( 'User changed files', @changed_user ); }
+ return ($#changed_user >= 0);
+} #END rdb_user_changes
+
+#************************************************************
+
+sub rdb_diagnose_changes {
+ # Call: rdb_diagnose_changes or rdb_diagnose_changes( heading )
+ # List changes on STDERR
+ # Precede the message by the optional heading, else by "$My_name: "
+ my $heading = defined($_[0]) ? $_[0] : "$My_name: ";
+
+ if ($#rules_never_run >= 0) {
+ warn "${heading}Rules & subrules not known to be previously run:\n";
+ foreach (@rules_never_run) { warn " $_\n"; }
+ }
+ if ( ($#changed >= 0) || (keys %changed_rules > 0) || ($#disappeared >= 0) || ($#no_dest >= 0) ) {
+ warn "${heading}File changes, etc:\n";
+ if ( $#changed >= 0 ) {
+ warn " Changed files, or newly in use since previous run(s):\n";
+ foreach (uniqs(@changed)) { warn " '$_'\n"; }
+ }
+ if ( $#disappeared >= 0 ) {
+ warn " No-longer-existing files:\n";
+ foreach (uniqs(@disappeared)) { warn " '$_'\n"; }
+ }
+ if ( $#no_dest >= 0 ) {
+ warn " Non-existent destination files:\n";
+ foreach (uniqs(@no_dest)) { warn " '$_'\n"; }
+ }
+ if ( keys %changed_rules > 0 ) {
+ warn " Rule(s) that have been run and require run of dependent rule:\n";
+ while (my ($s_rule, $d_rule) = each %changed_rules) {
+ warn " '$s_rule' which requires run of '$d_rule'\n";
+ }
+ }
+ }
+ elsif ($#rules_to_apply >=0) {
+ warn "${heading}The following rules & subrules became out-of-date:\n";
+ foreach (@rules_to_apply) { warn " '$_'\n"; }
+ }
+ else {
+ warn "${heading}No file changes\n";
+ }
+} #END rdb_diagnose_changes
+
+
+#************************************************************
+#************************************************************
+#************************************************************
+#************************************************************
+
+#************************************************************
+#************************************************************
+#************************************************************
+#************************************************************
+
+# Routines for convenient looping and recursion through rule database
+# ================= NEW VERSION ================
+
+# There are several places where we need to loop through or recurse
+# through rules and files. This tends to involve repeated, tedious
+# and error-prone coding of much book-keeping detail. In particular,
+# working on files and rules needs access to the variables involved,
+# which either involves direct access to the elements of the database,
+# and consequent fragility against changes and upgrades in the
+# database structure, or involves lots of routines for reading and
+# writing data in the database, then with lots of repetitious
+# house-keeping code.
+#
+# The routines below provide a solution. Looping and recursion
+# through the database are provided by a set of basic routines where
+# each necessary kind of looping and iteration is coded once. The
+# actual actions are provided as references to action subroutines.
+# (These can be either actual references, as in \&routine, or
+# anonymous subroutines, as in sub{...}, or aas a zero value 0 or an
+# omitted argument, to indicate that no action is to be performed.)
+#
+# When the action subroutine(s) are actually called, a context for the
+# rule and/or file (as appropriate) is given by setting named
+## NEW ??
+# variables to REFERENCES to the relevant data values. These can be
+# used to retrieve and set the data values. As a convention,
+# references to scalars are given by variables named start with "$P",
+# as in "$Pdest", while references to arrays start with "$PA", as in
+# "$PAint_cmd", and references to hashes with "$PH", as in "$PHsource".
+# After the action subroutine has finished, checks for data
+# consistency may be made.
+## ??? OLD
+# variables to the relevant data values. After the action subroutine
+# has finished, the database is updated with the values of these named
+# variables, with any necessary consistency checks. Thus the action
+# subroutines can act on sensibly named variables without needed to
+# know the database structure.
+#
+# The only routines that actually use the database structure and need
+# to be changed if that is changed are: (a) the routines rdb_one_rule
+# and rdb_one_file that implement the calling of the action subroutines,
+# (b) routines for creation of single rules and file items, and (c) to
+# a lesser extent, the routine for destroying a file item.
+#
+# Note that no routine is provided for destroying a rule. During a
+# run, a rule, with its source files, may become inaccessible or
+# unused. This happens dynamically, depending on the dependencies
+# caused by changes in the source file or by error conditions that
+# cause the computation of dependencies, particular of latex files, to
+# become wrong. In that situation the files certainly come and go in
+# the database, but subsidiary rules, with their content information
+# on their source files, need to be retained so that their use can be
+# reinstated later depending on dynamic changes in other files.
+#
+# However, there is a potential memory leak unless some pruning is
+# done in what is written to the fdb file. (Probably only accessible
+# rules and those for which source files exist. Other cases have no
+# relevant information that needs to be preserved between runs.)
+
+#
+#
+
+
+#************************************************************
+
+# First the top level routines for recursion and iteration
+
+#************************************************************
+
+sub rdb_recurse {
+ # Call: rdb_recurse( rule | [ rules],
+ # \&rule_act1, \&file_act1, \&file_act2,
+ # \&rule_act2 )
+ # The actions are pointers to subroutines, and may be null (0, or
+ # undefined) to indicate no action to be applied.
+ # Recursively acts on the given rules and all ancestors:
+ # foreach rule found:
+ # apply rule_act1
+ # loop through its files:
+ # apply file_act1
+ # act on its ancestor rule, if any
+ # apply file_act2
+ # apply rule_act2
+ # Guards against loops.
+ # Access to the rule and file data by local variables, only
+ # for getting and setting.
+
+ # This routine sets a context for anything recursive, with @heads,
+ # %visited and $depth being set as local variables.
+
+ local @heads = ();
+ my $rules = shift;
+
+ # Distinguish between single rule (a string) and a reference to an
+ # array of rules:
+ if ( ref $rules eq 'ARRAY' ) { @heads = @$rules; }
+ else { @heads = ( $rules ); }
+
+ # Keep a list of visited rules, used to block loops in recursion:
+ local %visited = ();
+ local $depth = 0;
+
+ foreach $rule ( @heads ) {
+ if ( rdb_is_active($rule) ) { rdb_recurse_rule( $rule, @_ ); }
+ }
+
+} #END rdb_recurse
+
+#************************************************************
+
+sub rdb_for_actives {
+ # Call: rdb_for_actives( \&rule_act1, \&file_act, \&rule_act2 )
+ # Loops through all rules and their source files, using the
+ # specified set of actions, which are pointers to subroutines.
+ # Sorts rules alphabetically.
+ # See rdb_for_some for details.
+# rdb_for_some( [ sort keys %rule_db ], @_);
+ rdb_for_some( [ sort &rdb_actives ], @_);
+} #END rdb_for_actives
+
+#************************************************************
+
+sub rdb_for_some {
+ # Call: rdb_for_some( rule | [ rules],
+ # \&rule_act1, \&file_act, \&rule_act2)
+ # Actions can be zero, and rules at tail of argument list can be
+ # omitted. E.g. rdb_for_some( rule, 0, \&file_act ).
+ # Anonymous subroutines can be used, e.g., rdb_for_some( rule, sub{...} ).
+ #
+ # Loops through rules and their source files, using the
+ # specified set of rules:
+ # foreach rule:
+ # apply rule_act1
+ # loop through its files:
+ # apply file_act
+ # apply rule_act2
+ #
+ # Rule data and file data are made available in local variables
+ # for access by the subroutines.
+
+ local @heads = ();
+ my $rules = shift;
+ # Distinguish between single rule (a string) and a reference to an
+ # array of rules:
+ if ( ref $rules eq 'ARRAY' ) { @heads = @$rules; }
+ else { @heads = ( $rules ); }
+
+ foreach $rule ( @heads ) {
+ # $rule is implicitly local
+ &rdb_one_rule( $rule, @_ );
+ }
+} #END rdb_for_some
+
+#************************************************************
+
+sub rdb_for_one_file {
+ my $rule = shift;
+ # Avoid name collisions with general recursion and iteraction routines:
+ local $file1 = shift;
+ local $action1 = shift;
+ rdb_for_some( $rule, sub{rdb_one_file($file1,$action1)} );
+} #END rdb_for_one_file
+
+
+#************************************************************
+
+# Routines for inner part of recursion and iterations
+
+#************************************************************
+
+sub rdb_recurse_rule {
+ # Call: rdb_recurse_rule($rule, \&rule_act1, \&file_act1, \&file_act2,
+ # \&rule_act2 )
+ # to do the work for one rule, recurisvely called from_rules for
+ # the sources of the rules.
+ # Assumes recursion context, i.e. that %visited, @heads, $depth.
+ # We are overriding actions:
+ my ($rule, $rule_act1, $new_file_act1, $new_file_act2, $rule_act2)
+ = @_;
+ if (! rdb_is_active($rule)) { return; }
+ # and must propagate the file actions:
+ local $file_act1 = $new_file_act1;
+ local $file_act2 = $new_file_act2;
+ # Prevent loops:
+ if ( (! $rule) || exists $visited{$rule} ) { return; }
+ $visited{$rule} = 1;
+ # Recursion depth
+ $depth++;
+ # We may need to repeat actions on dependent rules, without being
+ # blocked by the test on visited files. So save %visited:
+ # NOT CURRENTLY USED!! local %visited_at_rule_start = %visited;
+ # At end, the last value set for %visited wins.
+ rdb_one_rule( $rule, $rule_act1, \&rdb_recurse_file, $rule_act2 );
+ $depth--;
+ } #END rdb_recurse_rule
+
+#************************************************************
+
+sub rdb_recurse_file {
+ # Call: rdb_recurse_file to do the work for one file.
+ # This has no arguments, since it is used as an action subroutine,
+ # passed as a reference in calls in higher-level subroutine.
+ # Assumes contexts set for: Recursion, rule, and file
+ &$file_act1 if $file_act1;
+ rdb_recurse_rule( $$Pfrom_rule, $rule_act1, $file_act1, $file_act2,
+ $rule_act2 )
+ if $$Pfrom_rule;
+ &$file_act2 if $file_act2;
+} #END rdb_recurse_file
+
+#************************************************************
+
+sub rdb_do_files {
+ # Assumes rule context, including $PHsource.
+ # Applies an action to all the source files of the rule.
+ local $file_act = shift;
+ my @file_list = sort keys %$PHsource;
+ foreach my $file ( @file_list ){
+ rdb_one_file( $file, $file_act );
+ }
+} #END rdb_do_files
+
+#************************************************************
+
+# Routines for action on one rule and one file. These are the main
+# places (in addition to creation and destruction routines for rules
+# and files) where the database structure is accessed.
+
+#************************************************************
+
+sub rdb_one_rule {
+ # Call: rdb_one_rule( $rule, $rule_act1, $file_act, $rule_act2 )
+ # Sets context for rule and carries out the actions.
+#===== Accesses rule part of database structure =======
+
+ local ( $rule, $rule_act1, $file_act, $rule_act2 ) = @_;
+#?? &R1;
+ if ( (! $rule) || ! rdb_rule_exists($rule) ) { return; }
+
+ local ( $PArule_data, $PHsource, $PHdest, $PHsource_rules ) = @{$rule_db{$rule}};
+ local ($Pcmd_type, $Pext_cmd, $PAint_cmd, $Ptest_kind,
+ $Psource, $Pdest, $Pbase,
+ $Pout_of_date, $Pout_of_date_user, $Prun_time, $Pcheck_time,
+ $Pchanged,
+ $Plast_result, $Plast_message, $PA_extra_gen )
+ = Parray( $PArule_data );
+
+ &$rule_act1 if $rule_act1;
+ &rdb_do_files( $file_act ) if $file_act;
+ &$rule_act2 if $rule_act2;
+#?? &R2;
+} #END rdb_one_rule
+
+#************************************************************
+
+sub rdb_activate {
+ # Usage rdb_activate( rule_names )
+ # Turns on active flag for the rules
+ foreach ( @_ ) {
+ if ( rdb_rule_exists($_) ) { $actives{$_} = 1; }
+ }
+}
+
+#--------------------------------------------------
+
+sub rdb_deactivate {
+ # Usage rdb_deactivate( rule_names )
+ # Turns off active flag for the rules
+ foreach ( @_ ) { delete $actives{$_}; }
+}
+
+#--------------------------------------------------
+
+sub rdb_activate_request {
+ # Usage rdb_activate_request( rule_names )
+ # Turns on active flag for the rules.
+ # Adds rules to target_rules list
+ foreach ( @_ ) {
+ if ( rdb_rule_exists($_) ) { $actives{$_} = 1; $target_rules{$_} = 1; }
+ }
+}
+
+#--------------------------------------------------
+
+sub rdb_deactivate_derequest {
+ # Usage rdb_deactivate_derequest( rule_names )
+ # Turns off active flag for the rules
+ # Removes rules from target_rules list
+ foreach ( @_ ) { delete $actives{$_}; delete $target_rules{$_}; }
+}
+
+#--------------------------------------------------
+sub rdb_is_active {
+ # Usage rdb_is_active( rule_name )
+ if ( (exists $actives{$_[0]}) && rdb_rule_exists($_[0]) ) { return 1; }
+ else { return 0; }
+}
+
+#--------------------------------------------------
+
+sub rdb_actives {
+ # Returns array of active rules
+ return keys %actives;
+}
+
+#************************************************************
+
+sub rdb_one_file {
+ # Call: rdb_one_file($file, $file_act)
+ # Sets context for file and carries out the action.
+ # Assumes $rule context set.
+#===== Accesses file part of database structure =======
+ local ($file, $file_act) = @_;
+#?? &F1;
+ if ( (!$file) ||(!exists ${$PHsource}{$file}) ) { return; }
+ local $PAfile_data = ${$PHsource}{$file};
+ local ($Ptime, $Psize, $Pmd5, $Pfrom_rule, $Pcorrect_after_primary )
+ = Parray( $PAfile_data );
+ &$file_act() if $file_act;
+ if ( ! rdb_rule_exists( $$Pfrom_rule ) ) {
+ $$Pfrom_rule = '';
+ }
+#?? &F2;
+} #END rdb_one_file
+
+#************************************************************
+
+# Routines for creation of rules and file items, and for removing file
+# items.
+
+#************************************************************
+
+sub rdb_remove_rule {
+ # rdb_remove_rule( rule, ... )
+ foreach my $key (@_) {
+ delete $rule_db{$key};
+ delete $actives{$key};
+ }
+}
+
+#************************************************************
+
+sub rdb_create_rule {
+ # rdb_create_rule( rule, command_type, ext_cmd, int_cmd, test_kind,
+ # source, dest, base,
+ # needs_making, run_time, check_time, set_file_not_exists,
+ # ref_to_array_of_specs_of_extra_generated_files )
+ # int_cmd is either a string naming a perl subroutine or it is a
+ # reference to an array containing the subroutine name and its
+ # arguments.
+ # Makes rule. Update rule if it already exists.
+ # Omitted arguments: replaced by 0 or '' as needed.
+ # Rule is made active
+# ==== Sets rule data ====
+ my ( $rule, $cmd_type, $ext_cmd, $PAint_cmd, $test_kind,
+ $source, $dest, $base,
+ $needs_making, $run_time, $check_time, $set_file_not_exists, $extra_gen ) = @_;
+ my $active = 1;
+ my $changed = 0;
+
+ # Set defaults, and normalize parameters:
+ foreach ( $cmd_type, $ext_cmd, $PAint_cmd, $source, $dest, $base,
+ $set_file_not_exists ) {
+ if (! defined $_) { $_ = ''; }
+ }
+ if ( ($source =~ /\"/) || ($dest =~ /\"/) || ($base =~ /\"/) ) {
+ die "$My_name: Error. In rdb_create_rule to create rule\n",
+ " '$rule',\n",
+ " there is a double quote in one of source, destination or base parameters:\n",
+ " '$source'\n",
+ " '$dest'\n",
+ " '$base'\n",
+ " I cannot handle this. Cause is probably a latexmk bug. Please report it.\n";
+ }
+ foreach ( $needs_making, $run_time, $check_time, $test_kind ) {
+ if (! defined $_) { $_ = 0; }
+ }
+ if (!defined $test_kind) {
+ # Default to test on file change
+ $test_kind = 1;
+ }
+ if ( ref( $PAint_cmd ) eq '' ) {
+ # It is a single command. Convert to array reference:
+ $PAint_cmd = [ $PAint_cmd ];
+ }
+ else {
+ # COPY the referenced array:
+ $PAint_cmd = [ @$PAint_cmd ];
+ }
+ my $PA_extra_gen = [];
+ if ($extra_gen) {
+ @$PA_extra_gen = @$extra_gen;
+ }
+ $rule_db{$rule} =
+ [ [$cmd_type, $ext_cmd, $PAint_cmd, $test_kind,
+ $source, $dest, $base,
+ $needs_making, 0, $run_time, $check_time, $changed,
+ -1, '', $PA_extra_gen ],
+ {},
+ {},
+ {}
+ ];
+ if ($source) {
+ rdb_ensure_file( $rule, $source, undef, $set_file_not_exists );
+ }
+ rdb_one_rule( $rule, \&rdb_initialize_generated );
+ if ($active) { rdb_activate($rule); }
+ else { rdb_deactivate($rule); }
+} #END rdb_create_rule
+
+#************************************************************
+
+sub rdb_initialize_generated {
+# Assume rule context.
+# Initialize hash of generated files
+ %$PHdest = ();
+ if ($$Pdest) { rdb_add_generated($$Pdest); }
+ rdb_add_generated(@$PA_extra_gen);
+} #END rdb_initialize_generated
+
+#************************************************************
+
+sub rdb_add_generated {
+# Assume rule context.
+# Add arguments to hash of generated files, and to global cache
+ foreach (@_) {
+ $$PHdest{$_} = 1;
+ $from_rules{$_} = $rule;
+ }
+} #END rdb_add_generated
+
+#************************************************************
+
+sub rdb_remove_generated {
+# Assume rule context.
+# Remove arguments from hash of generated files
+ foreach (@_) { delete $$PHdest{$_}; }
+} #END rdb_remove_generated
+
+#************************************************************
+
+sub rdb_ensure_file {
+ # rdb_ensure_file( rule, file[, fromrule[, set_not_exists]] )
+ # Ensures the source file item exists in the given rule.
+ # Then if the fromrule is specified, set it for the file item.
+ # If the item is created, then:
+ # (a) by default initialize it to current file state.
+ # (b) but if the fourth argument, set_not_exists, is true,
+ # initialize the item as if the file does not exist.
+ # This case is typically used when the log file for a run
+ # of latex/pdflatex claims that the file was non-existent
+ # at the beginning of a run.
+#============ rule and file data set here ======================================
+ my $rule = shift;
+ local ( $new_file, $new_from_rule, $set_not_exists ) = @_;
+ if ( ! rdb_rule_exists( $rule ) ) {
+ die_trace( "$My_name: BUG in call to rdb_ensure_file: non-existent rule '$rule'" );
+ }
+ if ( ! defined $new_file ) {
+ die_trace( "$My_name: BUG in call to rdb_ensure_file: undefined file for '$rule'" );
+ }
+ if ( $new_file =~ /\"/ ) {
+ warn "$My_name: in rdb_ensure_file for rule '$rule', there is a double quote in\n",
+ " the filename: '$new_file'.\n",
+ " I cannot handle this, will ignore this file.\n";
+ return;
+ }
+ if ( ! defined $set_not_exists ) { $set_not_exists = 0; }
+ rdb_one_rule( $rule,
+ sub{
+ if (! exists ${$PHsource}{$new_file} ) {
+ if ( $set_not_exists ) {
+ ${$PHsource}{$new_file} = [@nofile, '', 0];
+ }
+ else {
+ ${$PHsource}{$new_file}
+ = [fdb_get($new_file, $$Prun_time), '', 0];
+ }
+ }
+ }
+ );
+ if (defined $new_from_rule ) {
+ rdb_for_one_file( $rule, $new_file, sub{ $$Pfrom_rule = $new_from_rule; });
+ }
+} #END rdb_ensure_file
+
+#************************************************************
+
+sub rdb_remove_files {
+ # rdb_remove_file( rule, file, ... )
+ # Removes file(s) for the rule.
+ my $rule = shift;
+ if (!$rule) { return; }
+ local @files = @_;
+ rdb_one_rule( $rule,
+ sub{ foreach (@files) { delete ${$PHsource}{$_}; } }
+ );
+} #END rdb_remove_files
+
+#************************************************************
+
+sub rdb_list_source {
+ # rdb_list_source( rule )
+ # Return array of source files for rule.
+ my $rule = shift;
+ my @files = ();
+ rdb_one_rule( $rule,
+ sub{ @files = keys %$PHsource; }
+ );
+ return @files;
+} #END rdb_list_source
+
+#************************************************************
+
+sub rdb_set_source {
+ # rdb_set_source( rule, file, ... )
+ my $rule = shift;
+ if (!$rule) { return; }
+ my %files = ();
+ foreach (@_) {
+# if ( /\"/ ) {next; }
+ rdb_ensure_file( $rule, $_ );
+ $files{$_} = 1;
+ }
+ foreach ( rdb_list_source($rule) ) {
+ if ( ! exists $files{$_} ) { rdb_remove_files( $rule, $_ ); }
+ }
+ return;
+} #END rdb_list_source
+
+#************************************************************
+
+sub rdb_change_dest {
+ # Assumes rule context.
+ # Usage change_dest( new_dest [, flag] )
+ # Changes destination for this rule. Fixes from_rule links.
+ # If flag set, make the new_dest a source file in any rule
+ # for which the old destination is already set.
+ # No action if there's no change of destination.
+ local ($new_dest, $flag) = @_;
+ local $old_dest = $$Pdest;
+ if ($old_dest eq $new_dest) { return; }
+# if ( $new_dest =~ /\"/ ) { return; }
+ rdb_remove_generated( $old_dest );
+ rdb_add_generated( $new_dest );
+ if ($flag) {
+ print "rdb_change_dest: fixing dependencies\n";
+ rdb_for_actives( sub{ if ( rdb_file_exists( $rule, $old_dest ) ) {
+ rdb_ensure_file( $rule, $new_dest );
+ rdb_remove_files( $rule, $old_dest );
+ }
+ }
+ );
+ }
+ $$Pdest = $new_dest;
+ # ??? Do I need to fix from_rule setting?
+ #&rdb_make_links;
+ return;
+} #END rdb_change_dest
+
+#************************************************************
+
+sub rdb_rule_exists {
+ # Call rdb_rule_exists($rule): Returns whether rule exists.
+ my $rule = shift;
+ if (! $rule ) { return 0; }
+ return exists $rule_db{$rule};
+} #END rdb_rule_exists
+
+#************************************************************
+
+sub rdb_file_exists {
+ # Call rdb_file_exists($rule, $file):
+ # Returns whether source file item in rule exists.
+ local ( $rule, $file ) = @_;
+ local $exists = 0;
+ rdb_one_rule( $rule,
+ sub{ $exists = exists( ${$PHsource}{$file} ) ? 1:0; }
+ );
+ return $exists;
+} #END rdb_file_exists
+
+#************************************************************
+
+sub rdb_update_gen_files {
+ # Assumes rule context. Update source files of rule to current state.
+ rdb_do_files(
+ sub{
+ if ( exists $generated_exts_all{ ext_no_period($file) }
+ || exists $$PHdest{$file}
+ ) {
+ &rdb_update1;
+ }
+ }
+ );
+} #END rdb_update_gen_files
+
+#************************************************************
+
+sub rdb_update_files {
+ # Call: rdb_update_files
+ # Assumes rule context. Update source files of rule to current state.
+ rdb_do_files( \&rdb_update1 );
+}
+
+#************************************************************
+
+sub rdb_update1 {
+ # Call: rdb_update1.
+ # Assumes file context. Updates file data to correspond to
+ # current file state on disk
+ ($$Ptime, $$Psize, $$Pmd5) = fdb_get($file);
+}
+
+#************************************************************
+
+sub rdb_set_file1 {
+ # Call: fdb_file1(rule, file, new_time, new_size, new_md5)
+ # Sets file time, size and md5.
+ my $rule = shift;
+ my $file = shift;
+ local @new_file_data = @_;
+ rdb_for_one_file( $rule, $file, sub{ ($$Ptime,$$Psize,$$Pmd5)=@new_file_data; } );
+}
+
+#************************************************************
+
+sub rdb_dummy_file {
+ # Returns file data for non-existent file
+# ==== Uses rule_db structure ====
+ return (0, -1, 0, '');
+}
+
+#************************************************************
+#************************************************************
+
+# Predefined subroutines for custom dependency
+
+sub cus_dep_delete_dest {
+ # This subroutine is used for situations like epstopdf.sty, when
+ # the destination (target) of the custom dependency invoking
+ # this subroutine will be made by the primary run provided the
+ # file (destination of the custom dependency, source of the
+ # primary run) doesn't exist.
+ # It is assumed that the resulting file will be read by the
+ # primary run.
+
+ # Remove the destination file, to indicate it needs to be remade:
+ unlink_or_move( $$Pdest );
+ # Arrange that the non-existent destination file is not treated as
+ # an error. The variable changed here is a bit misnamed.
+ $$Pchanged = 1;
+ # Ensure a primary run is done
+ &cus_dep_require_primary_run;
+ # Return success:
+ return 0;
+}
+
+#************************************************************
+
+sub cus_dep_require_primary_run {
+ # This subroutine is used for situations like epstopdf.sty, when
+ # the destination (target) of the custom dependency invoking
+ # this subroutine will be made by the primary run provided the
+ # file (destination of the custom dependency, source of the
+ # primary run) doesn't exist.
+ # It is assumed that the resulting file will be read by the
+ # primary run.
+
+ local $cus_dep_target = $$Pdest;
+ # Loop over all rules and source files:
+ rdb_for_actives( 0,
+ sub { if ($file eq $cus_dep_target) {
+ $$Pout_of_date = 1;
+ $$Pcorrect_after_primary = 1;
+ }
+ }
+ );
+ # Return success:
+ return 0;
+}
+
+
+#************************************************************
+#************************************************************
+#************************************************************
+#
+# UTILITIES:
+#
+
+#************************************************************
+# Miscellaneous
+
+sub show_array {
+# For use in diagnostics and debugging.
+# On stderr, print line with $_[0] = label.
+# Then print rest of @_, one item per line preceeded by some space
+ warn "$_[0]\n";
+ shift;
+ if ($#_ >= 0) { foreach (@_){ warn " $_\n";} }
+ else { warn " NONE\n"; }
+}
+
+
+#************************************************************
+
+sub array_to_hash {
+ # Call: array_to_hash( items )
+ # Returns: hash mapping items to 1
+ my %hash = ();
+ foreach (@_) {$hash{$_} = 1; }
+ return %hash;
+}
+
+#************************************************************
+
+sub Parray {
+ # Call: Parray( \@A )
+ # Returns array of references to the elements of @A
+ # But if an element of @A is already a reference, the
+ # reference will be returned in the output array, not a
+ # reference to the reference.
+ my $PA = shift;
+ my @P = (undef) x (1+$#$PA);
+ foreach my $i (0..$#$PA) {
+ $P[$i] = (ref $$PA[$i]) ? ($$PA[$i]) : (\$$PA[$i]);
+ }
+ return @P;
+}
+
+#************************************************************
+
+sub glob_list1 {
+ # Glob a collection of filenames.
+ # But no sorting or elimination of duplicates
+ # Usage: e.g., @globbed = glob_list1(string, ...);
+ # Since perl's glob appears to use space as separator, I'll do a special check
+ # for existence of non-globbed file (assumed to be tex like)
+
+ my @globbed = ();
+ foreach my $file_spec (@_) {
+ # Problem, when the PATTERN contains spaces, the space(s) are
+ # treated as pattern separaters.
+ # Solution: I now the glob from use File::Glob.
+ # The following hack avoids issues with glob in the case that a file exists
+ # with the specified name (possibly with extension .tex):
+ if ( -e $file_spec || -e "$file_spec.tex" ) {
+ # Non-globbed file exists, return the file_spec.
+ # Return $file_spec only because this is not a file-finding subroutine, but
+ # only a globber
+ push @globbed, $file_spec;
+ }
+ else {
+ push @globbed, my_glob( "$file_spec" );
+ }
+ }
+ return @globbed;
+} #END glob_list1
+
+#************************************************************
+# Miscellaneous
+
+sub prefix {
+ #Usage: prefix( string, prefix );
+ #Return string with prefix inserted at the front of each line
+ my @line = split( /\n/, $_[0] );
+ my $prefix = $_[1];
+ for (my $i = 0; $i <= $#line; $i++ ) {
+ $line[$i] = $prefix.$line[$i]."\n";
+ }
+ return join( "", @line );
+} #END prefix
+
+
+#===============================
+
+sub parse_quotes {
+ # Split string into words.
+ # Words are delimited by space, except that strings
+ # quoted all stay inside a word. E.g.,
+ # 'asdf B" df "d "jkl"'
+ # is split to ( 'asdf', 'B df d', 'jkl').
+ # An array is returned.
+ my @results = ();
+ my $item = '';
+ local $_ = shift;
+ pos($_) = 0;
+ ITEM:
+ while() {
+ /\G\s*/gc;
+ if ( /\G$/ ) {
+ last ITEM;
+ }
+ # Now pos (and \G) is at start of item:
+ PART:
+ while () {
+ if (/\G([^\s\"]*)/gc) {
+ $item .= $1;
+ }
+ if ( /\G\"([^\"]*)\"/gc ) {
+ # Match balanced quotes
+ $item .= $1;
+ next PART;
+ }
+ elsif ( /\G\"(.*)$/gc ) {
+ # Match unbalanced quote
+ $item .= $1;
+ warn "====Non-matching quotes in\n '$_'\n";
+ }
+ push @results, $item;
+ $item = '';
+ last PART;
+ }
+ }
+ return @results;
+} #END parse_quotes
+
+#************************************************************
+#************************************************************
+# File handling utilities:
+
+
+#************************************************************
+
+sub get_latest_mtime
+# - arguments: each is a filename.
+# - returns most recent modify time.
+{
+ my $return_mtime = 0;
+ foreach my $include (@_)
+ {
+ my $include_mtime = &get_mtime($include);
+ # The file $include may not exist. If so ignore it, otherwise
+ # we'll get an undefined variable warning.
+ if ( ($include_mtime) && ($include_mtime > $return_mtime) )
+ {
+ $return_mtime = $include_mtime;
+ }
+ }
+ return $return_mtime;
+}
+
+#************************************************************
+
+sub get_mtime_raw
+{
+ my $mtime = (stat($_[0]))[9];
+ return $mtime;
+}
+
+#************************************************************
+
+sub get_mtime {
+ return get_mtime0($_[0]);
+}
+
+#************************************************************
+
+sub get_mtime0 {
+ # Return time of file named in argument
+ # If file does not exist, return 0;
+ if ( -e $_[0] ) {
+ return get_mtime_raw($_[0]);
+ }
+ else {
+ return 0;
+ }
+}
+
+#************************************************************
+
+sub get_size {
+ # Return time of file named in argument
+ # If file does not exist, return 0;
+ if ( -e $_[0] ) {
+ return get_size_raw($_[0]);
+ }
+ else {
+ return 0;
+ }
+}
+
+#************************************************************
+
+sub get_size_raw
+{
+ my $size = (stat($_[0]))[7];
+ return $size;
+}
+
+#************************************************************
+
+sub get_time_size {
+ # Return time and size of file named in argument
+ # If file does not exist, return (0,-1);
+ if ( -e $_[0] ) {
+ return get_time_size_raw($_[0]);
+ }
+ else {
+ return (0,-1);
+ }
+}
+
+#************************************************************
+
+sub get_time_size_raw
+{
+ my $mtime = (stat($_[0]))[9];
+ my $size = (stat($_[0]))[7];
+ return ($mtime, $size);
+}
+
+#************************************************************
+
+sub processing_time
+{ my ($user, $system, $cuser, $csystem) = times();
+ return $user + $system + $cuser + $csystem;
+}
+
+#************************************************************
+
+sub get_checksum_md5 {
+ my $source = shift;
+ my $input = new FileHandle;
+ my $md5 = Digest::MD5->new;
+ my $ignore_pattern = undef;
+
+#&traceback;
+#warn "======= GETTING MD5: $source\n";
+ if ( -d $source ) {
+ # We won't use checksum for directory
+ return 0;
+ }
+ else {
+ open( $input, '<:bytes', $source )
+ or return 0;
+ my ($base, $path, $ext) = fileparseA( $source );
+ $ext =~ s/^\.//;
+ if ( exists $hash_calc_ignore_pattern{$ext} ) {
+ $ignore_pattern = $hash_calc_ignore_pattern{$ext};
+ }
+ }
+ if ( defined $ignore_pattern ) {
+ while (<$input>) {
+ if ( ! /$ignore_pattern/ ){
+ $md5->add($_);
+ }
+ }
+ }
+ else {
+ $md5->addfile($input);
+ }
+ close $input;
+ return $md5->hexdigest();
+}
+
+#************************************************************
+#************************************************************
+
+sub create_empty_file {
+ my $name = shift;
+ my $h = new FileHandle ">$name"
+ or return 1;
+ close ($h);
+ return 0;
+}
+
+#************************************************************
+#************************************************************
+
+sub find_file1 {
+#?? Need to use kpsewhich, if possible
+
+ # Usage: find_file1(name, ref_to_array_search_path)
+ # Modified find_file, which doesn't die.
+ # Given filename and path, return array of:
+ # full name
+ # retcode
+ # On success: full_name = full name with path, retcode = 0
+ # On failure: full_name = given name, retcode = 1
+
+ my $name = $_[0];
+ # Make local copy of path, since we may rewrite it!
+ my @path = ();
+ if ($_[1]) {
+ @path = @{$_[1]};
+ }
+ if ( $name =~ /^\// ) {
+ # Absolute path (if under UNIX)
+ # This needs fixing, in general
+ if (-e $name) { return( $name, 0 );}
+ else { return( $name, 1 );}
+ }
+ foreach my $dir ( @path ) {
+ #??print "-------------dir='$dir', ";
+ # Make $dir concatenatable, and empty for current dir:
+ if ( $dir eq '.' ) {
+ $dir = '';
+ }
+ elsif ( $dir =~ /[\/\\:]$/ ) {
+ #OK if dir ends in / or \ or :
+ }
+ elsif ( $dir ne '' ) {
+ #Append directory separator only to non-empty dir
+ $dir = "$dir/";
+ }
+ #?? print " newdir='$dir'\n";
+ if (-e "$dir$name") {
+ return("$dir$name", 0);
+ }
+ }
+ my @kpse_result = kpsewhich( $name );
+ if ($#kpse_result > -1) {
+ return( $kpse_result[0], 0);
+ }
+ return("$name" , 1);
+} #END find_file1
+
+#************************************************************
+
+sub find_file_list1 {
+ # Modified version of find_file_list that doesn't die.
+ # Given output and input arrays of filenames, a file suffix, and a path,
+ # fill the output array with full filenames
+ # Return array of not-found files.
+ # Usage: find_file_list1( ref_to_output_file_array,
+ # ref_to_input_file_array,
+ # suffix,
+ # ref_to_array_search_path
+ # )
+ # SPECIAL TREATMENT TO .bib extension, because of behavior of bibtex
+ # OTHER SPECIAL TREATMENT IF EXTENSION IS GIVEN.
+
+ my $ref_output = $_[0];
+ my $ref_input = $_[1];
+ my $suffix = $_[2];
+ my $ref_search = $_[3];
+ my @not_found = ();
+
+#?? show_array( "=====find_file_list1. Suffix: '$suffix'\n Source:", @$ref_input );
+#?? show_array( " Bibinputs:", @$ref_search );
+
+ my @return_list = (); # Generate list in local array, since input
+ # and output arrays may be same
+ my $retcode = 0;
+ foreach my $file1 (@$ref_input) {
+ my $file = $file1;
+ if ($suffix eq '.bib') { $file =~ s/\.bib$//; }
+ my ($tmp_file, $find_retcode) = &find_file1( "$file$suffix", $ref_search );
+ if ($tmp_file) {
+ push @return_list, $tmp_file;
+ }
+ if ( $find_retcode != 0 ) {
+ push @not_found, $file.$suffix;
+ }
+ }
+ @$ref_output = @return_list;
+#?? show_array( " Output", @$ref_output );
+#?? foreach (@$ref_output) { if ( /\/\// ) { print " ====== double slash in '$_'\n"; } }
+ return @not_found;
+} #END find_file_list1
+
+#************************************************************
+
+sub unlink_or_move {
+ if ( $del_dir eq '' ) {
+ foreach (@_) {
+ if (!-e) {next;}
+ if (-d) {
+ if (!rmdir) {
+ warn "$My_name: Cannot remove directory '$_'\n";
+ }
+ }
+ else {
+ if (!unlink) {
+ warn "$My_name: Cannot remove file '$_'\n";
+ }
+ }
+ }
+ }
+ else {
+ foreach (@_) {
+ if (-e $_ && ! move $_, "$del_dir/$_" ) {
+ warn "$My_name: Cannot move '$_' to '$del_dir/$_'\n";
+ }
+ }
+ }
+}
+
+#************************************************************
+
+sub kpsewhich {
+# Usage: kpsewhich( filespec, ...)
+# Returns array of files with paths as found by kpsewhich
+# kpsewhich( 'try.sty', 'jcc.bib' );
+# With standard use of kpsewhich (i.e., without -all option), the array
+# has either 0 or 1 element.
+# Can also do, e.g.,
+# kpsewhich( '-format=bib', 'trial.bib', 'file with spaces');
+ my $cmd = $kpsewhich;
+ my @args = @_;
+ if ( ($cmd eq '') || ( $cmd =~ /^NONE($| )/ ) ) {
+ # Kpsewhich not set up.
+ warn "$My_name: Kpsewhich command needed but not set up\n";
+ return ();
+ }
+ foreach (@args) {
+ if ( ! /^-/ ) {
+ $_ = "\"$_\"";
+ }
+ }
+ $cmd =~ s/%[RBTDO]//g;
+ $cmd =~ s/%S/@args/g;
+ my @found = ();
+ local $fh;
+ if ( $kpsewhich_show || $diagnostics ) {
+ print "$My_name.kpsewhich: Running '$cmd'...\n";
+ }
+ open $fh, "$cmd|"
+ or die "Cannot open pipe for \"$cmd\"\n";
+ while ( <$fh> ) {
+ s/[\r\n]*$//;
+ push @found, $_;
+ }
+ close $fh;
+ if ( $kpsewhich_show || $diagnostics ) {
+ show_array( "$My_name.kpsewhich: '$cmd' ==>", @found );
+ }
+ return @found;
+}
+
+####################################################
+
+sub add_cus_dep {
+ # Usage: add_cus_dep( from_ext, to_ext, flag, sub_name )
+ # Add cus_dep after removing old versions
+ my ($from_ext, $to_ext, $must, $sub_name) = @_;
+ remove_cus_dep( $from_ext, $to_ext );
+ push @cus_dep_list, "$from_ext $to_ext $must $sub_name";
+}
+
+####################################################
+
+sub remove_cus_dep {
+ # Usage: remove_cus_dep( from_ext, to_ext )
+ my ($from_ext, $to_ext) = @_;
+ my $i = 0;
+ while ($i <= $#cus_dep_list) {
+ # Use \Q and \E round directory name in regex to avoid interpretation
+ # of metacharacters in directory name:
+ if ( $cus_dep_list[$i] =~ /^\Q$from_ext $to_ext \E/ ) {
+ splice @cus_dep_list, $i, 1;
+ }
+ else {
+ $i++;
+ }
+ }
+}
+
+####################################################
+
+sub show_cus_dep {
+ show_array( "Custom dependency list:", @cus_dep_list );
+}
+
+####################################################
+
+sub add_aux_hook {
+ # Usage: add_aux_hook( sub_name )
+ # Add the name subroutine to the array of hooks for
+ # processing lines of aux files.
+ # The argument is either a string naming the subroutine, e.g.
+ # add_aux_hook( 'subname' );
+ # or a Perl reference to the subroutine, e.g.,
+ # add_aux_hook( \&subname );
+ # It is also possible to use an anonymous subroutine, e.g.,
+ # add_aux_hook( sub{ code of subroutine... } );
+ my ($sub_name) = @_;
+ push @aux_hooks, $sub_name;
+}
+
+####################################################
+
+sub set_input_ext {
+ # Usage: set_input_ext( rule, ext, ... )
+ # Set list of extension(s) (specified without a leading period)
+ # for the given rule ('latex', 'pdflatex', etc).
+ # These extensions are used when an input
+ # file without an extension is found by (pdf)latex, as in
+ # \input{file} or \includegraphics{figure}. When latexmk searches
+ # custom dependencies to make the missing file, it will assume that
+ # the file has one of the specified extensions.
+ my $rule = shift;
+ $input_extensions{$rule} = {};
+ my $Prule = $input_extensions{$rule};
+ foreach (@_) { $$Prule{$_} = 1; }
+}
+
+####################################################
+
+sub show_input_ext {
+ # Usage: show_input_ext( rule )
+ my $rule = shift;
+ show_array ("Input extensions for rule '$rule': ",
+ keys %{$input_extensions{$rule}} );
+}
+
+####################################################
+
+sub find_dirs1 {
+ # Same as find_dirs, but argument is single string with directories
+ # separated by $search_path_separator
+ find_dirs( &split_search_path( $search_path_separator, ".", $_[0] ) );
+}
+
+
+#************************************************************
+
+sub find_dirs {
+# @_ is list of directories
+# return: same list of directories, except that for each directory
+# name ending in //, a list of all subdirectories (recursive)
+# is added to the list.
+# Non-existent directories and non-directories are removed from the list
+# Trailing "/"s and "\"s are removed
+ local @result = ();
+ my $find_action
+ = sub
+ { ## Subroutine for use in File::find
+ ## Check to see if we have a directory
+ if (-d) { push @result, $File::Find::name; }
+ };
+ foreach my $directory (@_) {
+ my $recurse = ( $directory =~ m[//$] );
+ # Remove all trailing /s, since directory name with trailing /
+ # is not always allowed:
+ $directory =~ s[/+$][];
+ # Similarly for MSWin reverse slash
+ $directory =~ s[\\+$][];
+ if ( ! -e $directory ){
+ next;
+ }
+ elsif ( $recurse ){
+ # Recursively search directory
+ find( $find_action, $directory );
+ }
+ else {
+ push @result, $directory;
+ }
+ }
+ return @result;
+}
+
+#************************************************************
+
+sub uniq
+# Read arguments, delete neighboring items that are identical,
+# return array of results
+{
+ my @sort = ();
+ my ($current, $prev);
+ my $first = 1;
+ while (@_)
+ {
+ $current = shift;
+ if ($first || ($current ne $prev) )
+ {
+ push @sort, $current;
+ $prev = $current;
+ $first = 0;
+ }
+ }
+ return @sort;
+}
+
+#==================================================
+
+sub uniq1 {
+ # Usage: uniq1( strings )
+ # Returns array of strings with duplicates later in list than
+ # first occurence deleted. Otherwise preserves order.
+
+ my @strings = ();
+ my %string_hash = ();
+
+ foreach my $string (@_) {
+ if (!exists( $string_hash{$string} )) {
+ $string_hash{$string} = 1;
+ push @strings, $string;
+ }
+ }
+ return @strings;
+}
+
+#************************************************************
+
+sub uniqs {
+ # Usage: uniq2( strings )
+ # Returns array of strings sorted and with duplicates deleted
+ return uniq( sort @_ );
+}
+
+#************************************************************
+
+sub ext {
+ # Return extension of filename. Extension includes the period
+ my $file_name = $_[0];
+ my ($base_name, $path, $ext) = fileparseA( $file_name );
+ return $ext;
+ }
+
+#************************************************************
+
+sub ext_no_period {
+ # Return extension of filename. Extension excludes the period
+ my $file_name = $_[0];
+ my ($base_name, $path, $ext) = fileparseA( $file_name );
+ $ext =~ s/^\.//;
+ return $ext;
+ }
+
+#************************************************************
+
+sub fileparseA {
+ # Like fileparse but replace $path for current dir ('./' or '.\') by ''
+ # Also default second argument to get normal extension.
+ my $given = $_[0];
+ my $pattern = '\.[^\.]*';
+ if ($#_ > 0 ) { $pattern = $_[1]; }
+ my ($base_name, $path, $ext) = fileparse( $given, $pattern );
+ if ( ($path eq './') || ($path eq '.\\') ) {
+ $path = '';
+ }
+ return ($base_name, $path, $ext);
+}
+
+#************************************************************
+
+sub fileparseB {
+ # Like fileparse but with default second argument for normal extension
+ my $given = $_[0];
+ my $pattern = '\.[^\.]*';
+ if ($#_ > 0 ) { $pattern = $_[1]; }
+ my ($base_name, $path, $ext) = fileparse( $given, $pattern );
+ return ($base_name, $path, $ext);
+}
+
+#************************************************************
+
+sub split_search_path
+{
+# Usage: &split_search_path( separator, default, string )
+# Splits string by separator and returns array of the elements
+# Allow empty last component.
+# Replace empty terms by the default.
+ my $separator = $_[0];
+ my $default = $_[1];
+ my $search_path = $_[2];
+ my @list = split( /$separator/, $search_path);
+ if ( $search_path =~ /$separator$/ ) {
+ # If search path ends in a blank item, the split subroutine
+ # won't have picked it up.
+ # So add it to the list by hand:
+ push @list, "";
+ }
+ # Replace each blank argument (default) by current directory:
+ for ($i = 0; $i <= $#list ; $i++ ) {
+ if ($list[$i] eq "") {$list[$i] = $default;}
+ }
+ return @list;
+}
+
+#################################
+
+sub get_filetime_offset {
+ # Usage: get_filetime_offset( prefix, [suffix] )
+ # Measures offset between filetime in a directory and system time
+ # Makes a temporary file of a unique name, and deletes in.
+ # Filename is of form concatenation of prefix, an integer, suffix.
+ # Prefix is normally of form dir/ or dir/tmp.
+ # Default default suffix ".tmp".
+ my $prefix = $_[0];
+ my $suffix = $_[1] || '.tmp';
+ my $tmp_file_count = 0;
+ while (1==1) {
+ # Find a new temporary file, and make it.
+ $tmp_file_count++;
+ my $tmp_file = "${prefix}${tmp_file_count}${suffix}";
+ if ( ! -e $tmp_file ) {
+ open( TMP, ">$tmp_file" )
+ or die "$My_name.get_filetime_offset: In measuring filetime offset, couldn't write to\n",
+ " temporary file '$tmp_file'\n";
+ my $time = time();
+ close(TMP);
+ my $offset = get_mtime($tmp_file) - $time;
+ unlink $tmp_file;
+ return $offset;
+ }
+ }
+ die "$My_name.get_filetime_offset: BUG TO ARRIVE HERE\n";
+}
+
+#################################
+
+sub tempfile1 {
+ # Makes a temporary file of a unique name. I could use file::temp,
+ # but it is not present in all versions of perl.
+ # Filename is of form $tmpdir/$_[0]nnn$suffix, where nnn is an integer
+ my $tmp_file_count = 0;
+ my $prefix = $_[0];
+ my $suffix = $_[1];
+ while (1==1) {
+ # Find a new temporary file, and make it.
+ $tmp_file_count++;
+ my $tmp_file = "${tmpdir}/${prefix}${tmp_file_count}${suffix}";
+ if ( ! -e $tmp_file ) {
+ open( TMP, ">$tmp_file" )
+ or next;
+ close(TMP);
+ return $tmp_file;
+ }
+ }
+ die "$My_name.tempfile1: BUG TO ARRIVE HERE\n";
+}
+
+#################################
+
+#************************************************************
+#************************************************************
+# Process/subprocess routines
+
+sub Run_msg {
+ # Same as Run, but give message about my running
+ warn_running( "Running '$_[0]'" );
+ return Run($_[0]);
+} #END Run_msg
+
+#==================
+
+sub Run {
+ # This is wrapper around Run_no_time to capture timing information
+ my $time1 = processing_time();
+ my ($pid, $return) = Run_no_time($_[0]);
+ my $time = processing_time() - $time1;
+ push @timings, "'$_[0]': time = $time\n";
+ return ($pid, $return);
+} #END Run_msg
+
+#==================
+
+sub Run_no_time {
+# Usage: Run_no_time ("command string");
+# or Run_no_time ("one-or-more keywords command string");
+# Possible keywords: internal, NONE, start, nostart.
+#
+# A command string not started by keywords just gives a call to system with
+# the specified string, I return after that has finished executing.
+# Exceptions to this behavior are triggered by keywords.
+# The general form of the string is
+# Zero or more occurences of the start keyword,
+# followed by at most one of the other key words (internal, nostart, NONE),
+# followed by (a) a command string to be executed by the systerm
+# or (b) if the command string is specified to be internal, then
+# it is of the form
+#
+# routine arguments
+#
+# which implies invocation of the named Perl subroutine
+# with the given arguments, which are obtained by splitting
+# the string into words, delimited by spaces, but with
+# allowance for double quotes.
+#
+# The meaning of the keywords is:
+#
+# start: The command line is to be running detached, as appropriate for
+# a previewer. The method is appropriate for the operating system
+# (and the keyword is inspired by the action of the start command
+# that implements in under MSWin).
+# HOWEVER: the start keyword is countermanded by the nostart,
+# internal, and NONE keywords. This allows rules that do
+# previewing to insert a start keyword to create a presumption
+# of detached running unless otherwise.
+# nostart: Countermands a previous start keyword; the following command
+# string is then to be obeyed by the system, and any necessary
+# detaching (as of a previewer) is done by the executed command(s).
+# internal: The following command string, of the form 'routine arguments'
+# specifies a call to the named Perl subroutine.
+# NONE: This does not run anything, but causes an error message to be
+# printed. This is provided to allow program names defined in the
+# configuration to flag themselves as unimplemented.
+# Note that if the word "start" is duplicated at the beginning, that is
+# equivalent to a single "start".
+#
+# Return value is a list (pid, exitcode):
+# If a process is spawned sucessfully, and I know the PID,
+# return (pid, 0),
+# else if process is spawned sucessfully, but I do not know the PID,
+# return (0, 0),
+# else if process is run,
+# return (0, exitcode of process)
+# else if I fail to run the requested process
+# return (0, suitable return code)
+# where return code is 1 if cmdline is null or begins with "NONE" (for
+# an unimplemented command)
+# or the return value of the Perl subroutine.
+ my $cmd_line = $_[0];
+ if ( $cmd_line eq '' ) {
+ traceback( "$My_name: Bug OR configuration error\n".
+ " In run of '$rule', attempt to run a null program" );
+ return (0, 1);
+ }
+ # Deal with latexmk-defined pseudocommands 'start' and 'NONE'
+ # at front of command line:
+ my $detach = 0;
+ while ( $cmd_line =~ s/^start +// ) {
+ # But first remove extra starts (which may have been inserted
+ # to force a command to be run detached, when the command
+ # already contained a "start").
+ $detach = 1;
+ }
+ if ( $cmd_line =~ s/^nostart +// ) {
+ $detach = 0;
+ }
+ if ( $cmd_line =~ /^internal\s+([a-zA-Z_]\w*)\s+(.*)$/ ) {
+ my $routine = $1;
+ my @args = parse_quotes( $2 );
+ warn "$My_name: calling $routine( $2 )\n"
+ if (! $silent);
+ return ( 0, &$routine( @args ) );
+ }
+ elsif ( $cmd_line =~ /^internal\s+([a-zA-Z_]\w*)\s*$/ ) {
+ my $routine = $1;
+ warn "$My_name: calling $routine()\n"
+ if (! $silent);
+ return ( 0, &$routine() );
+ }
+ elsif ( $cmd_line =~ /^NONE/ ) {
+ warn "$My_name: ",
+ "Program not implemented for this version. Command line:\n";
+ warn " '$cmd_line'\n";
+ return (0, 1);
+ }
+ elsif ($detach) {
+ # Run detached. How to do this depends on the OS
+ return &Run_Detached( $cmd_line );
+ }
+ else {
+ # The command is given to system as a single argument, to force shell
+ # metacharacters to be interpreted:
+ return( 0, system( $cmd_line ) );
+ }
+} #END Run
+
+#************************************************************
+
+sub Run_Detached {
+# Usage: Run_Detached ("program arguments ");
+# Runs program detached. Returns 0 on success, 1 on failure.
+# Under UNIX use a trick to avoid the program being killed when the
+# parent process, i.e., me, gets a ctrl/C, which is undesirable for pvc
+# mode. (The simplest method, system("program arguments &"), makes the
+# child process respond to the ctrl/C.)
+# Return value is a list (pid, exitcode):
+# If process is spawned sucessfully, and I know the PID,
+# return (pid, 0),
+# else if process is spawned sucessfully, but I do not know the PID,
+# return (0, 0),
+# else if I fail to spawn a process
+# return (0, 1)
+
+ my $cmd_line = $_[0];
+
+## warn "Running '$cmd_line' detached...\n";
+ if ( $cmd_line =~ /^NONE / ) {
+ warn "$My_name: ",
+ "Program not implemented for this version. Command line:\n";
+ warn " '$cmd_line'\n";
+ return (0, 1);
+ }
+
+ if ( "$^O" eq "MSWin32" ){
+ # Win95, WinNT, etc: Use MS's start command:
+ # Need extra double quotes to deal with quoted filenames:
+ # MSWin start takes first quoted argument to be a Window title.
+ return( 0, system( "start \"\" $cmd_line" ) );
+ } else {
+ # Assume anything else is UNIX or clone
+ # For this purpose cygwin behaves like UNIX.
+ ## warn "Run_Detached.UNIX: A\n";
+ my $pid = fork();
+ ## warn "Run_Detached.UNIX: B pid=$pid\n";
+ if ( ! defined $pid ) {
+ ## warn "Run_Detached.UNIX: C\n";
+ warn "$My_name: Could not fork to run the following command:\n";
+ warn " '$cmd_line'\n";
+ return (0, 1);
+ }
+ elsif( $pid == 0 ){
+ ## warn "Run_Detached.UNIX: D\n";
+ # Forked child process arrives here
+ # Insulate child process from interruption by ctrl/C to kill parent:
+ # setpgrp(0,0);
+ # Perhaps this works if setpgrp doesn't exist
+ # (and therefore gives fatal error):
+ eval{ setpgrp(0,0);};
+ exec( $cmd_line );
+ # Exec never returns; it replaces current process by new process
+ die "$My_name forked process: could not run the command\n",
+ " '$cmd_line'\n";
+ }
+ ##warn "Run_Detached.UNIX: E\n";
+ # Original process arrives here
+ return ($pid, 0);
+ }
+ # NEVER GET HERE.
+ ##warn "Run_Detached.UNIX: F\n";
+} #END Run_Detached
+
+#************************************************************
+
+sub find_process_id {
+# find_process_id(string) finds id of process containing string and
+# being run by the present user. Typically the string will be the
+# name of the process or part of its command line.
+# On success, this subroutine returns the process ID.
+# On failure, it returns 0.
+# This subroutine only works on UNIX systems at the moment.
+
+ if ( $pid_position < 0 ) {
+ # I cannot do a ps on this system
+ return (0);
+ }
+
+ my $looking_for = $_[0];
+ my @ps_output = `$pscmd`;
+ my @ps_lines = ();
+
+# There may be multiple processes. Find only latest,
+# almost surely the one with the highest process number
+# This will deal with cases like xdvi where a script is used to
+# run the viewer and both the script and the actual viewer binary
+# have running processes.
+ my @found = ();
+
+ shift(@ps_output); # Discard the header line from ps
+ foreach (@ps_output) {
+ next unless ( /$looking_for/ ) ;
+ s/^\s*//;
+ my @ps_line = split ('\s+');
+ push @found, $ps_line[$pid_position];
+ push @ps_lines, $_;
+ }
+
+ if ($#found < 0) {
+ # No luck in finding the specified process.
+ return(0);
+ }
+ @found = reverse sort @found;
+ if ($diagnostics) {
+ print "Found the following processes concerning '$looking_for'\n",
+ " @found\n",
+ " I will use $found[0]\n";
+ print " The relevant lines from '$pscmd' were:\n";
+ foreach (@ps_lines) { print " $_"; }
+ }
+ return $found[0];
+}
+
+#************************************************************
+#************************************************************
+#************************************************************
+
+#============================================
+
+sub cache_good_cwd {
+ # Set cached value of cwd to current cwd.
+ # Under cygwin, the cached value is converted to a native MSWin path so
+ # that the result can be used for input to MSWin programs as well
+ # as cygwin programs.
+ # Similarly for msys.
+ my $cwd = cwd();
+ if ( $^O eq "cygwin" ) {
+ my $cmd = "cygpath -w \"$cwd\"";
+ my $Win_cwd = `$cmd`;
+ chomp $Win_cwd;
+ if ( $Win_cwd ) {
+ $cwd = $Win_cwd;
+ }
+ else {
+ warn "$My_name: Could not correctly run command\n",
+ " '$cmd'\n",
+ " to get MSWin version of cygwin path\n",
+ " '$cwd'\n",
+ " The result was\n",
+ " '$Win_cwd'\n";
+ }
+ }
+ elsif ( $^O eq "msys" ) {
+ $cwd =~ s[^/([a-z])/][\u$1:/];
+ }
+ $cache{cwd} = $cwd;
+} # END cache_good_cwd
+
+#============================================
+
+sub good_cwd {
+ # Return cwd, but under cygwin (or ...), convert to MSWin path.
+ # Use cached result, to save a possible expensive computation (running
+ # of extenal program under cygwin).
+ return $cache{cwd};
+} # END good_cwd
+
+#============================================
+
+# Directory stack routines
+
+sub pushd {
+ push @dir_stack, [cwd(), $cache{cwd}];
+ if ( $#_ > -1) {
+ chdir $_[0];
+ &cache_good_cwd;
+ }
+}
+
+#************************************************************
+
+sub popd {
+ if ($#dir_stack > -1 ) {
+ my $Parr = pop @dir_stack;
+ chdir $$Parr[0];
+ $cache{cwd} = $$Parr[1];
+ }
+}
+
+#************************************************************
+
+sub ifcd_popd {
+ if ( $do_cd ) {
+ warn "$My_name: Undoing directory change\n"
+ if !$silent;
+ &popd;
+ }
+}
+
+#************************************************************
+
+sub finish_dir_stack {
+ while ($#dir_stack > -1 ) { &popd; }
+}
+
+#************************************************************
+#************************************************************
+# Break handling routines (for wait-loop in preview continuous)
+
+sub end_wait {
+ # Handler for break: Set global variable $have_break to 1.
+ # Some systems (e.g., MSWin reset) appear to reset the handler.
+ # So I'll re-enable it
+ &catch_break;
+ $have_break = 1;
+}
+
+#========================
+
+sub catch_break {
+# Capture ctrl/C and ctrl/break.
+# $SIG{INT} corresponds to ctrl/C on LINUX/?UNIX and MSWin
+# $SIG{BREAK} corresponds to ctrl/break on MSWin, doesn't exist on LINUX
+ $SIG{INT} = \&end_wait;
+ if ( exists $SIG{BREAK} ) {
+ $SIG{BREAK} = \&end_wait;
+ }
+}
+
+#========================
+
+sub default_break {
+# Arrange for ctrl/C and ctrl/break to give default behavior
+ $SIG{INT} = 'DEFAULT';
+ if ( exists $SIG{BREAK} ) {
+ $SIG{BREAK} = 'DEFAULT';
+ }
+}
+
+#************************************************************
+#************************************************************