dotfiles

configuration files for shell, text editor, graphical environment, etc.
git clone git://src.adamsgaard.dk/dotfiles
Log | Files | Refs | README | LICENSE Back to index

commit afd19395e5743179525a103cb5dbdbc4c156a0b0
parent 9a0000b89692424af6e261fd080c9317685d1820
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Mon, 29 Jun 2020 11:32:08 +0200

Add latexmk-like functionality to compile script, remove separate latexmk

Diffstat:
M.local/bin/compile | 56++++++++++++++++++++++++++++++++++++++++++++++----------
D.local/bin/latexmk | 9447-------------------------------------------------------------------------------
2 files changed, 46 insertions(+), 9457 deletions(-)

diff --git a/.local/bin/compile b/.local/bin/compile @@ -1,5 +1,4 @@ #!/bin/sh -set -e show_help() { echo "usage: ${0##*/} [OPTIONS] FILE ..." @@ -18,8 +17,38 @@ die() { exit 1 } +regeximatch() { + printf '%s' "$1" | grep -iEq "$2" +} + +extract_tex_include() { + d="$(grep -v ".*%.*\\${2}" "$1" | + sed -nE "/.*\\${2}\[*.*\]*{([^}]*)}.*/{ s//\1/;p; }")" + #tr '\n' ' ')" + [ -n "$d" ] && printf '%s\n' "$d" || : +} + +find_dependencies() { + case "$1" in + *.tex) + printf '%s\n' "$1" + if grep -q -e '\input{' \ + -e '\include[graphics]' \ + -e '\addbibresource{' \ + -e '\bibliography{' \ + "$1"; then + extract_tex_include "$1" 'input' + extract_tex_include "$1" 'include' + extract_tex_include "$1" 'includegraphics' + extract_tex_include "$1" 'addbibresource' + extract_tex_include "$1" 'bibliography' + fi;; + *) + die "cannot find dependencies in unknown file type $1";; + esac +} + run_compile() { - base="${2%.*}" if [ "$continuous" = 1 ]; then printf '%s\n' "$2" | entr -s "$1" else @@ -32,12 +61,22 @@ handle_target() { if [ -e "$1" ]; then case "$1" in *\.tex) - pdflatex "$1" - if [ "$bibtex" = 1 ]; then - bibtex "${1%.tex}.aux" - pdflatex "$1" + deps="$1" + deps="$(find_dependencies "$1")" + for d in $deps; do + if [ "${d##*.}" = "tex" ]; then + deps="$(printf '%s\n%s\n' "${deps}" "$(find_dependencies "$d")")" + fi + done + deps="$(printf '%s\n' "$deps" | sort | uniq)" + printf 'deps=%s\n' "$deps" + if regeximatch "$deps" '\.bib'; then + compile_string="pdflatex '$1'; bibtex '${1%.tex}'; pdflatex '$1'; pdflatex '$1'" + else + compile_string="pdflatex '$1' && pdflatex '$1'" fi - pdflatex "$1" + printf 'compile_string=%s\n' "$compile_string" + run_compile "$compile_string" "$deps" ;; *\.gp) run_compile "gnuplot '$1'" "$1" @@ -73,14 +112,11 @@ handle_target() { fi } -bibtex=0 verbose=0 continuous=0 open=0 while :; do case "$1" in - -b) - bibtex=1;; -h|-\?) show_help exit 0 diff --git a/.local/bin/latexmk b/.local/bin/latexmk @@ -1,9447 +0,0 @@ -#!/usr/bin/env perl - -# ?? 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 - -$my_name = 'latexmk'; -$My_name = 'Latexmk'; -$version_num = '4.61'; -$version_details = "$My_name, John Collins, 25 October 2018"; - -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"; -} - -## Copyright John Collins 1998-2018 -## (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 -## -## 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/*'. -## 24 Oct 2018 John Collins V. 4.61 -## 16 Oct 2018 John Collins Routines for setting all of $latex, etc. -## Variables, options, substitutable parameters -## for executing code in *latex before inputting -## source file. -## 10 Oct 2018 John Collins Fix problem that if biber gets a remote file, -## it would be deleted and latexmk would report -## it as missing, incorrectly. -## 8 Oct 2018 John Collins Report count of warnings about missing characters -## (typically unavailable Unicode characters). -## Messages about this may appear only in the .log -## file and are therefore easily missed by the user. -## V. 4.60a -## 21 Sep 2018 John Collins Fix bug that --gg with --deps-file doesn't -## create deps file. -## 3 Sep 2018 John Collins -pdfxelatex and -pdflualatex options -## 3 Sep 2018 John Collins V. 4.60 -## 7 Aug 2018 John Collins V. 4.59 Released on CTAN -## 1 Aug 2018 John Collins Correct sub rdb_find_source_file. -## 30 Jul 2018 John Collins Change handling of warnings for a difference -## between actual and expected output filenames -## for a run of a primary rule. Only give a -## warning if the extensions differ. -## In all other cases, there were significant -## numbers of false positives and no true positives. -## Improve providing of -no-pdf option to xelatex, to -## ensure it's used even after a user redefinition -## of $xelatex. Works if %O is in definition. -## 25 Jul 2018 John Collins Clean up of code. -## 24 Jul 2018 John Collins Fatal error when filename has initial '&'. This -## is not allowed for TeX file, since TeX et al -## treat the initial '&' as specifying the format. -## 20,21,23 Jul 2018 John Collins Improve quoting in file-not-found messages -## On MS-Win change \ to / on files on command line -## 18,19 Jul 2018 John Collins Complete TEXINPUTS stuff -## Fix reading of file database file when -## custom dependency no longer exists. -## 13,17 Jul 2018 John Collins Deal with double quotes in specified filename, -## and filenames not allowed by TeX. -## Illegal characters and unbalanced quotes give fatal error. -## 10 Jul 2018 John Collins Use TEXINPUTS for finding source file for cus dep. -## Version 4.58. -## 21 Jun 2018 John Collins Version 4.57. -## In get_checksum_md5, open file with mode :bytes, -## to avoid error about malformed utf8 characters -## that can happen if PERL_UNICODE is set. -## 15 Jun 2018 John Collins Configuration to be able to turn off bibtex fudge. -## 24,25 May 2018 John Collins Fix problem of .bib files not found with msys. -## Add use of environment variable LATEXMKRCSYS -## 12 May 2018 John Collins Simplify code in run_bibtex. -## 3,9 May 2018 John Collins Improved diagnostics on mismatch of output filenames -## 28,30 Apr 2018 John Collins Improve error messages for bib files not found -## 26 Apr 2018 John Collins In testing for different expected and actual -## output of primary run, normalize $$Pdest, to -## avoid spurious warnings. -## -## 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. - - -# 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_make_rule_list for examples of rule templates. -# See subroutine rdb_set_rules for 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 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} -# Could write -#%input_extensions = ( 'latex' => { 'tex' => 1, 'eps' => 1 };, -# 'pdflatex' => { 'tex' => 1, 'pdf' => 1, 'jpg' => 1, 'png' => 1 }; ); -# Instead we'll exercise the user-friendly access routines: -add_input_ext( 'latex', 'tex', 'eps' ); -add_input_ext( 'pdflatex', 'tex', 'jpg', 'pdf', 'png' ); -add_input_ext( 'lualatex', 'tex', 'jpg', 'pdf', 'png' ); -add_input_ext( 'xelatex', 'tex', 'jpg', 'pdf', 'png' ); -#show_input_ext( 'latex' ); show_input_ext( 'pdflatex' ); - -%allowed_output_ext = ( ".dvi" => 1, ".xdv" => 1, ".pdf" => 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. (If $jobname is non-empty, then - # the --jobname=... option is used on tex.) -$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' ); -%primaries = (); # Hash of rules for primary part of make. Keys are - # currently 'latex', 'pdflatex' or both; also 'lualatex' - # and 'xelatex'. Value is currently irrelevant. - # Use hash for ease of lookup - # Make remove this later, if use rdb_makeB - -# Hashes, whose keys give names of particular kinds of rule. We use -# hashes for ease of lookup. -%possible_one_time = ( 'view' => 1, 'print' => 1, 'update_view' => 1, ); -%requested_filerules = (); # Hash for rules corresponding to requested files. - # The keys are the rulenames and the value is - # currently irrelevant. -%one_time = (); # Hash for requested one-time-only rules, currently - # possible values 'print' and 'view'. - - -%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). - # 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. - # Example ['%Y%R.log'] for (pdf)latex, and ['%R.blg'] - # for bibtex. (There's no need for '%R.aux', here, - # since such generated files are detected dynamically.) - # 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 - -%fdb_current = (); # Fdb-hash for all files 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 '') && ($num_files > 1) ) { - &exit_help( - "Need to specify at most one filename if ". - "jobname specified, \n". - " but $num_files were found (after defaults and wildcarding)." - ); -} - -# 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 '' ) { - $jobstring = "--jobname=\"$jobname\""; - add_option( "$jobstring", \$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; -} - -# Set new-style requested rules: -if ( $dvi_mode ) { $requested_filerules{'latex'} = 1; } -if ( $pdf_mode == 1 ) { $requested_filerules{'pdflatex'} = 1; } -elsif ( $pdf_mode == 2 ) { - $requested_filerules{'latex'} = 1; - $requested_filerules{'dvips'} = 1; - $requested_filerules{'ps2pdf'} = 1; -} -elsif ( $pdf_mode == 3 ) { - $requested_filerules{'latex'} = 1; - $requested_filerules{'dvipdf'} = 1; -} -elsif ( $pdf_mode == 4 ) { - $requested_filerules{'lualatex'} = 1; -} -elsif ( $pdf_mode == 5 ) { - $requested_filerules{'xelatex'} = 1; - $requested_filerules{'xdvipdfmx'} = 1; -} -if ( $postscript_mode ) { - $requested_filerules{'latex'} = 1; - $requested_filerules{'dvips'} = 1; -} -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; } -if ( length($dvi_filter) != 0 ) { $requested_filerules{'dvi_filter'} = 1; } -if ( length($ps_filter) != 0 ) { $requested_filerules{'ps_filter'} = 1; } -if ( $banner ) { $requested_filerules{'dvips'} = 1; } - - -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"; - 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"; - } - } - - ## 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); - } - } - if ($jobname ne '' ) { - $root_filename = $jobname; - } - - &set_names; - - # Initialize basic dependency information: - - # For use under error conditions: - @default_includes = ($texfile_name, $aux_main); - - # Initialize rule database. - # ?? Should I also initialize file database? - %rule_list = (); - &rdb_make_rule_list; - &rdb_set_rules( \%rule_list, \%extra_rule_spec ); - - 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; - $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_all( - 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; - } - } - - 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 ); - } - - &cleanup1( $aux_dir1, $fdb_ext, 'blg', 'ilg', 'log', 'aux.bak', 'idx.bak', - split('\s+',$clean_ext), - keys %generated_exts_all - ); - 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_mode == 1 ) { - &cleanup1( $out_dir1, 'dvi', 'dviF', 'ps', 'psF', 'pdf', - 'synctex.gz', 'xdv', - split('\s+', $clean_full_ext) - ); - } - } - 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_set_rules( \%rule_list ); - } - if ($cleanup_only) { next FILE; } - - -#??? The following are not needed if use rdb_make. -# ?? They may be set too early? -# Arrays and hashes for picking out accessible rules. -# Distinguish rules for making files and others - @accessible_all = sort ( &rdb_accessible( keys %requested_filerules, keys %one_time )); - %accessible_filerules = (); - foreach (@accessible_all) { - unless ( /view/ || /print/ ) { $accessible_filerules{$_} = 1; } - } - @accessible_filerules = sort keys %accessible_filerules; - -# show_array ( "=======All rules used", @accessible_all ); -# show_array ( "=======Requested file rules", sort keys %requested_filerules ); -# show_array ( "=======Rules for files", @accessible_filerules ); - - if ( $diagnostics ) { - print "$My_name: Rules after start up for '$texfile_name'\n"; - rdb_show(); - } - - %primaries = (); - foreach (@accessible_all) { - if ( ($_ eq 'latex') || ($_ eq 'pdflatex') || ($_ eq 'lualatex') - || ($_ eq 'xelatex') ) - { $primaries{$_} = 1; } - } - - $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( uniq1( keys %requested_filerules ) ) ){ - # 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( [keys %requested_filerules], 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( keys %requested_filerules ); - 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 ( /[\Q$illegal_in_texname\E]/ ) { - $illegal_char++; - warn "$My_name: Filename '$_' contains character not allowed for TeX file.\n"; - } - my ($filename, $path) = fileparse( $_ ); - if ( $do_cd && ($filename =~ /^&/) ) { - $illegal_char++; - warn "$My_name: Filename part of '$_' contains initial '&', which is\n", - " not allowed for TeX file in my -cd mode.\n"; - } - elsif ( /^&/ ) { - $illegal_char++; - warn "$My_name: Filename '$_' contains initial '&', which is not allowed 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_make_rule_list { -# 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 - - # Defaults for dvi, ps, and pdf files - # Use local, not my, so these variables can be referenced - local $dvi_final = "%Z%R.dvi"; - local $ps_final = "%Z%R.ps"; - local $pdf_final = "%Z%R.pdf"; - local $xdv_final = "%Z%R.xdv"; - if ( length($dvi_filter) > 0) { - $dvi_final = "%Z%R.dviF"; - } - if ( length($ps_filter) > 0) { - $ps_final = "%Z%R.psF"; - } - - 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", "%Z%B.dvi", "%R", 1, ["%Y%R.log"] ], - 'pdflatex' => [ 'primary', "$pdflatex", '', "%T", "%Z%B.pdf", "%R", 1, ["%Y%R.log"] ], - 'lualatex' => [ 'primary', "$lualatex", '', "%T", "%Z%B.pdf", "%R", 1, ["%Y%R.log"] ], - 'xelatex' => [ 'primary', "$xelatex", '', "%T", "%Z%B.xdv", "%R", 1, ["%Y%R.log"] ], - 'dvipdf' => [ 'external', "$dvipdf", 'do_viewfile', $dvi_final, "%B.pdf", "%Z%R", 2 ], - 'xdvipdfmx' => [ 'external', "$xdvipdfmx", 'do_viewfile', $xdv_final, "%B.pdf", "%Z%R", 2 ], - 'dvips' => [ 'external', "$dvips", 'do_viewfile', $dvi_final, "%B.ps", "%Z%R", 2 ], - 'dvifilter'=> [ 'external', $dvi_filter, 'do_viewfile', "%B.dvi", "%B.dviF", "%Z%R", 2 ], - 'ps2pdf' => [ 'external', "$ps2pdf", 'do_viewfile', $ps_final, "%B.pdf", "%Z%R", 2 ], - 'psfilter' => [ 'external', $ps_filter, 'do_viewfile', "%B.ps", "%B.psF", "%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 ], - ); - -# Ensure we only have one way to make pdf file, and that it is appropriate: - if ($pdf_mode == 2) { delete $rule_list{'dvipdf'}; delete $rule_list{'pdflatex'}; delete $rule_list{'lualatex'}; delete $rule_list{'xelatex'}; } - elsif ($pdf_mode == 3) { delete $rule_list{'pdflatex'}; delete $rule_list{'ps2pdf'}; delete $rule_list{'lualatex'}; delete $rule_list{'xelatex'}; } - elsif ($pdf_mode == 4) { delete $rule_list{'pdflatex'}; delete $rule_list{'ps2pdf'}; delete $rule_list{'dvipdf'}; delete $rule_list{'xelatex'}; } - elsif ($pdf_mode == 5) { delete $rule_list{'pdflatex'}; delete $rule_list{'ps2pdf'}; delete $rule_list{'dvipdf'}; delete $rule_list{'lualatex'}; } - else { # Default is to leave pdflatex - delete $rule_list{'dvipdf'}; delete $rule_list{'ps2pdf'}; delete $rule_list{'lualatex'}; delete $rule_list{'xelatex'}; - } - -} # END rdb_make_rule_list - -#************************************************************ - -sub rdb_set_rules { - # Call rdb_set_rules( \%rule_list, ...) - # Set up rule database from definitions - - # Map of files to rules that MAKE them: - %rule_db = (); - - foreach my $Prule_list (@_) { - foreach my $rule ( keys %$Prule_list) { - my ( $cmd_type, $ext_cmd, $int_cmd, $source, $dest, $base, $test_kind, $PA_extra_gen ) = @{$$Prule_list{$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/; - s/%Y/$aux_dir1/; - s/%Z/$out_dir1/; - } - foreach ($source, $dest ) { - s/%B/$base/; - s/%T/$texfile_name/; - } - # print "$rule: $cmd_type, EC='$ext_cmd', IC='$int_cmd', $test_kind,\n", - # " S='$source', D='$dest', B='$base' $needs_making\n"; - rdb_create_rule( $rule, $cmd_type, $ext_cmd, $int_cmd, $test_kind, - $source, $dest, $base, - $needs_making, undef, undef, 1, $PA_extra_gen ); -# !! ?? Last line was -# $needs_making, undef, ($test_kind==1) ); - } - } # End arguments of subroutine - &rdb_make_links; -} # END rdb_set_rules - -#************************************************************ - -sub rdb_make_links { -# ?? Problem if there are multiple rules for getting a file. Notably pdf. -# Which one to choose? - # Create $from_rule if there's a suitable rule. - # Map files to rules: - local %from_rules = (); - rdb_for_all( sub{ if($$Pdest){$from_rules{$$Pdest} = $rule;} } ); -#?? foreach (sort keys %from_rules) {print "D='$_' F='$from_rules{$_}\n";} - rdb_for_all( - 0, - sub{ - # Set from_rule, but only if it isn't set or is invalid. - # Don't forget the biber v. bibtex issue - if ( exists $from_rules{$file} - && ( (!$$Pfrom_rule) || (! exists $rule_db{$$Pfrom_rule} ) ) - ) - { $$Pfrom_rule = $from_rules{$file}; - } - } - ); - rdb_for_all( - 0, - sub{ - if ( exists $from_rules{$file} ) { - $$Pfrom_rule = $from_rules{$file}; - } - if ( $$Pfrom_rule && (! rdb_rule_exists( $$Pfrom_rule ) ) ) { - $$Pfrom_rule = ''; - } -#?? print "$rule: $file, $$Pfrom_rule\n"; - } - ); -} # END rdb_make_links - -#************************************************************ - -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 %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 @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; - - # What to make? - my @targets = keys %requested_filerules; - - $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 = %requested_filerules; - $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( @targets ); - -## 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'; - } - @pre_primary = (); # Array of rules - @post_primary = (); # Array of rules - @unusual_one_time = (); # Array of rules - &rdb_classify_rules( \%possible_primaries, keys %requested_filerules ); - # 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"; - } - } - if ( $first_time || $updated || $failure ) { - system("notify-send", "latexmk complete", "--expire-time=1000"); - print "\n=== Watching for updated files. Use ctrl/C to stop ...\n"; - } - $waiting = 1; if ($diagnostics) { warn "WAITING\n"; } -# During waiting for file changes, handle ctrl/C and ctrl/break here, rather than letting -# system handle them by terminating script (and any script that calls it). This allows, -# for example, the clean up code in the following command line to work: -# latexmk -pvc foo; cleanup; - &catch_break; - $have_break = 0; - $last_action_time = time(); - WAIT: while (1) { - sleep( $sleep_time ); - if ($have_break) { last WAIT; } - if ( rdb_new_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_all( \&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 $$Pdest) && (-e $$Psource) ) { - unlink_or_move( $$Pdest ); - } - elsif ( (-e $$Pdest) && (!-e $$Psource) ) { - warn "$My_name: For custom dependency '$rule',\n", - " I won't delete destination file '$$Pdest'\n", - " because the source file '$$Psource' doesn't exist,\n", - " so the destination file may not be able to be recreated\n"; - } -} #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 $log_name = "$base.blg"; - my $log_file = new FileHandle; - open( $log_file, "<$log_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 (<$log_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 $log_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 $log_name = "$base.blg"; - my $log_file = new FileHandle; - open( $log_file, "<$log_name" ) - or return 3; - my $have_warning = 0; - my $have_error = 0; - my $missing_citations = 0; - my @missing_aux = (); - my $error_count = 0; - while (<$log_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 $log_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: - $aux_main = "$aux_dir1$root_filename.aux"; - $log_name = "$aux_dir1$root_filename.log"; - $fdb_name = "$aux_dir1$root_filename.$fdb_ext"; -} - -#************************************************** - -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*\`([^\']+)\'\.$/ ) { - # 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: - $generated_log{normalize_force_directory( $aux_dir1, $1 )} = 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. - my $file1 = normalize_force_directory( $aux_dir1, $file ); - $dependents{$file1} = 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 ( /^([^\(^\)]*?)\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 && ($include_candidate =~ /(\S+)\s/ ) ){ - # Non-space-containing filename-candidate - # followed by space followed by message - # (Common) - push @new_includes, $1; - } - if ( $include_candidate eq "[]" ) { - # Part of overfull hbox message - next INCLUDE_CANDIDATE; - } - if ( $include_candidate =~ /^\\/ ) { - # Part of font message - next INCLUDE_CANDIDATE; - } - # Remove quotes around filename, as for MikTeX. I've already - # treated this as a special case. For safety check here: - $include_candidate =~ s/^\"(.*)\"$/$1/; - - 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) { - $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) - # 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); - my @nofile = (0,-1,0); # What we use for initializing - # a new entry in fdb or flagging - # non-existent 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) { -#warn "--------- RECALC MD5: $rule $file: (N,O,R,C) \n = $new_time, $time, $$Prun_time, $check_time\n"; - @$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} = [0, -1, 0]; - } - @{$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 - my $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 = ''; - my $run_time = 0; - my $source = ''; - my $dest = ''; - my $base = ''; - 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 = ''; - 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, - sub{ - if ($$Ptest_kind == 3) { $$Ptest_kind = 1; } - $$Prun_time = $run_time; - $$Pcheck_time = $check_time; - } - ); - } - 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 { - warn "$My_name: In file-database '$in_name' rule '$rule'\n", - " is not in use in this session\n" - if $diagnostics; - $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_fule 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 ); - -#?? Check from_rules exist. - - return $errors; -} # END rdb_read - -#************************************************************ - -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 - # !!?? Previously was: - # OLD Writes to the given file name the database of file and rule data - # OLD accessible from the primary rules. - # 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; } - - local %current_primaries = (); # Hash whose keys are primary rules - # needed, i.e., known latex-like rules which trigger - # circular dependencies - local @pre_primary = (); # Array of rules - local @post_primary = (); # Array of rules - local @unusual_one_time = (); # Array of rules - &rdb_classify_rules( \%possible_primaries, keys %requested_filerules ); - - print $out_handle "# Fdb version $fdb_ver\n"; -# !!?? Rules or rules accessible from primary -# my @rules = rdb_accessible( uniq1( keys %possible_primaries ) ) ; - my @rules = rdb_accessible( uniq1( keys %possible_primaries, keys %requested_filerules ) ) ; - # Separate call to sort. Otherwise rdb_accessible seems to get wrong argument. - @rules = sort( @rules ); - 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. - - # 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 log file 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 - - # The following are also returned, 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. - my $fls_name = "$aux_dir1$root_filename.fls"; - 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\n"; - } - if ( (($actual_ext eq '.pdf') && ($intended_ext eq '.dvi')) - || (($actual_ext eq '.dvi') && ($intended_ext eq '.pdf')) - ) - { - warn " This could arise from use of \\pdfoutput in the source file,\n", - " or from a configuration error\n"; - } - else { - warn " This indicates a probable configuration error\n"; - } - warn " A future version of $my_name should be able to make dynamically\n", - " adjustments to deal with this problem\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; - } - } - - 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); - 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" ] ); - } - } - 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); - } - } - - 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 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( keys %requested_filerules ); - 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_generated) { 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( keys %requested_filerules ); - rdb_for_some( - \@accessible_all, - sub{ -# foreach (keys %$PHdest) { print "----- $_\n"; } - 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_all( - 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_accessible { - # Call: rdb_accessible( rule, ...) - # Returns array of rules accessible from the given rules - local @accessible = (); - rdb_recurse( [@_], sub{ push @accessible, $rule; } ); - return @accessible; -} #END rdb_accessible - -#************************************************************ -#************************************************************ -#************************************************************ - -sub rdb_make { - # Call: rdb_make( target, ... ) - # 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. - # - - local @requested_targets = @_; - local %current_primaries = (); # Hash whose keys are primary rules - # needed, i.e., known latex-like rules which trigger - # circular dependencies - local @pre_primary = (); # Array of rules - local @post_primary = (); # Array of rules - local @unusual_one_time = (); # Array of rules - - - # For diagnostics on changed files, etc: - local @changed = (); - local @disappeared = (); - local @no_dest = (); # Non-existent destination files - local @rules_never_run = (); - local @rules_to_apply = (); - - &rdb_classify_rules( \%possible_primaries, @requested_targets ); - - 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 = (); - my $retry_msg = 0; # Did I earlier say I was going to attempt - # another pass after a failure? - PRIMARY: - foreach my $primary (keys %current_primaries ) { - foreach my $rule (keys %rule_db) { - $pass{$rule} = 0; - } - PASS: - while (1==1) { - # Exit condition at end of body of loop. - $runs = 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 - 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, $primary], \&rdb_make1 ); - if ( ($runs > 0) && ! $too_many_passes ) { - $retry_msg = 0; - if ( $force_mode || (! $failure) ) { - 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 - my %old_curr_prim = %current_primaries; - &rdb_classify_rules( \%possible_primaries, @requested_targets ); - %current_primaries = %old_curr_prim; - &rdb_make_links; - } - } - 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"; - } - } - return $failure; -} #END rdb_make - -#------------------- - -sub rdb_show_rule_errors { - local @errors = (); - local @warnings = (); - rdb_for_all( - 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; } - &rdb_clear_change_record; - - # 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) -# OLD && ( ( $$Pcmd_type eq 'cusdep') ) -# NEW - && ( ( $$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_flag_changes_here(0); - - if (!$$Pout_of_date) { -#?? if ( ($$Pcmd_type eq 'primary') && (! $silent) ) { -# print "Rule '$rule' up to date\n"; -# } - 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; - - } -} #END rdb_make1 - -#************************************************************ - -#??sub rdb_submake { -#?? # Call: rdb_submake -#?? # Makes all the source files for a given rule. -#?? # Assumes contexts for recursion, for make, and rule. -#?? %visited = %visited_at_rule_start; -#?? local $failure = 0; # Error flag -#?? my @v = keys %visited; -#?? rdb_do_files( sub{ rdb_recurse_rule( $$Pfrom_rule, 0,0,0, \&rdb_make1 ) } ); -#?? return $failure; -#??} #END rdb_submake - -#************************************************************ - -sub rdb_classify_rules { - # Usage: rdb_classify_rules( \%allowed_primaries, requested targets ) - # Assume the following variables are available (global or local): - # Input: - # @requested_targets # Set to target rules - - # 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 $P_allowed_primaries = shift; - local @requested_targets = @_; - 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; - - 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 ${$P_allowed_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 = ( - '%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; - } - - 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 = "$aux_dir1$root_filename.fls"; - 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"; - } - # 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, as tested by the use of - # test_gen_file; that avoids problems with fls files leftover from - # earlier runs with other versions of latex. - 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;} } ); - -#?? # There may be new source files, and the run may have caused -#?? # circular-dependency files to be changed. And the regular -#?? # source files may have been updated during a lengthy run of -#?? # latex. So redo the makes for sources of the current rule: -#?? my $submake_return = &rdb_submake; -#?? &rdb_clear_change_record; -#?? &rdb_flag_changes_here(0); -#?? if ($$Pout_of_date && !$silent) { -#?? &rdb_diagnose_changes( "Rule '$rule': " ); -#?? } - - $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 = (); - @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); - 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 ); - 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) - ) - ) { -#print "========= CHANGED: '$file' from '$$Pfrom_rule'\n"; - push @changed, $file; - $$Pout_of_date = 1; - if ( ! exists $generated_exts_all{$ext_no_period} ) { - $$Pout_of_date_user = 1; - } - } - elsif ( $new_time != $$Ptime ) { - $$Ptime = $new_time; - } - if ( ( ($$Ptest_kind == 2) || ($$Ptest_kind == 3) ) - && (! exists $generated_exts_all{$ext_no_period} ) - && ( $new_time > $dest_mtime ) - ) { - push @changed, $file; - $$Pout_of_date = $$Pout_of_date_user = 1; - } -} #END rdb_file_change1 - -#************************************************************ - -sub rdb_new_changes { - &rdb_clear_change_record; - rdb_recurse( [@_], sub{ &rdb_flag_changes_here(1); } ); - return ($#changed >= 0) || ($#no_dest >= 0) || ($#rules_to_apply >= 0); -} #END rdb_new_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) || ($#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"; } - } - } - 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 ) { rdb_recurse_rule( $rule, @_ ); } - -} #END rdb_recurse - -#************************************************************ - -sub rdb_for_all { - # Call: rdb_for_all( \&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 ], @_); -} #END rdb_for_all - -#************************************************************ - -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) - = @_; - # 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 ) = @{$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_generated ) - = 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_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}; - } -} - -#************************************************************ - -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. Error if it already exists. - # Omitted arguments: replaced by 0 or '' as needed. -# ==== 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 $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 there is a double quote in one of\n", - " source, destination or base parameters:\n", - " '$source', '$dest', '$base'\n", - " I cannot handle this.\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 ); -} #END rdb_create_rule - -#************************************************************ - -sub rdb_initialize_generated { -# Assume rule context. -# Initialize hash of generated files - %$PHdest = (); - if ($$Pdest) { rdb_add_generated($$Pdest); } - foreach (@$PA_extra_generated) { - rdb_add_generated($_); - } -} #END rdb_initialize_generated - -#************************************************************ - -sub rdb_add_generated { -# Assume rule context. -# Add arguments to hash of generated files - foreach (@_) { - $$PHdest{$_} = 1; - } -} #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} = [0, -1, 0, '', 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_all( 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) } ) {&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_all( 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 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 '' ) { - unlink @_; - } - else { - foreach (@_) { - if (-e $_ && ! rename $_, "$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 add_input_ext { - # Usage: add_input_ext( rule, ext, ... ) - # Add extension(s) (specified without a leading period) to the - # list of input extensions for the given rule. The rule should be - # 'latex' or 'pdflatex'. 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; - if ( ! exists $input_extensions{$rule} ) { - $input_extensions{$rule} = {}; - } - my $Prule = $input_extensions{$rule}; - foreach (@_) { $$Prule{$_} = 1; } -} - -#################################################### - -sub remove_input_ext { - # Usage: remove_input_ext( rule, ext, ... ) - # Remove extension(s) (specified without a leading period) to the - # list of input extensions for the given rule. The rule should be - # 'latex' or 'pdflatex'. See sub add_input_ext for the use. - my $rule = shift; - if ( ! exists $input_extensions{$rule} ) { return; } - my $Prule = $input_extensions{$rule}; - foreach (@_) { delete $$Prule{$_}; } -} - -#################################################### - -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"; - return ( 0, &$routine( @args ) ); - } - elsif ( $cmd_line =~ /^internal\s+([a-zA-Z_]\w*)\s*$/ ) { - my $routine = $1; - warn "$My_name: calling $routine()\n"; - 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"; - &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'; - } -} - -#************************************************************ -#************************************************************