summaryrefslogtreecommitdiff
path: root/support/tex-it/texit.pl
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/tex-it/texit.pl
Initial commit
Diffstat (limited to 'support/tex-it/texit.pl')
-rw-r--r--support/tex-it/texit.pl2686
1 files changed, 2686 insertions, 0 deletions
diff --git a/support/tex-it/texit.pl b/support/tex-it/texit.pl
new file mode 100644
index 0000000000..8c4c428e8a
--- /dev/null
+++ b/support/tex-it/texit.pl
@@ -0,0 +1,2686 @@
+#! /usr/local/bin/perl -- # -*-Perl-*-
+
+########################################################################
+# TeXit: a Perl script for running TeX.
+#
+# Copyright (C) 1993 by Norman Walsh. <norm@ora.com>
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Comments, questions, suggestions, and even flames welcome.
+#
+# See "display_usage" at the bottom of this file for information
+# about how to use TeXit.
+########################################################################
+
+($VERSION = '$Revision: 1.36 $ ') =~ s/.*: (\d+.\d+).*/$1/;
+print "This is TeXit v$VERSION. ";
+print "Copyright (C) 1993 by Norman Walsh.\n";
+
+($revision, $patchlevel) = &perl_version_info();
+
+if ($revision lt "4.0.1.7" ||
+ ($revision eq "4.0.1.7" && $patchlevel < 35)) {
+ print "***********************************",
+ "***********************************\n";
+ print " WARNING: Your Perl is old. TeXit may or may not work.\n";
+ print " In particular, options may not be interpreted correctly.\n";
+ print "***********************************",
+ "***********************************\n";
+}
+
+########################################################################
+# Load standard library routines for handling abbreviations. I use
+# this for the command line options.
+#
+require "abbrev.pl";
+
+########################################################################
+# Initialize associative arrays of format information.
+#
+# &setup_format (common-name, printable-name, parser-name, format-name);
+#
+# Where:
+#
+# common-name : The (unique) internal (to TeXit) name of the format
+# printable-name : What we tell the user this format is called
+# parser-name : Which "parse_<parser-name>_log" routine gets called
+# format-name : What is the '&<format-name>' filename of this format
+#
+&setup_format ("amslatex", "AMSLaTeX", "latex", "lplain", "" );
+&setup_format ("amstex", "AMSTeX", "default", "amstex", "" );
+&setup_format ("foiltex", "FoilTeX", "latex", "fltplain", "" );
+&setup_format ("lamstex", "LAMSTeX", "default", "lamstex", "" );
+&setup_format ("latex", "LaTeX", "latex", "lplain", "" );
+&setup_format ("lollipop", "Lollipop", "default", "lollipop", "" );
+&setup_format ("nfss2ltx", "LaTeX (NFSS2)", "latex", "nfss2ltx", "" );
+&setup_format ("plain", "Plain TeX", "default", "plain", "" );
+&setup_format ("slitex", "SliTeX", "latex", "splain", "" );
+&setup_format ("texinfo", "Texinfo", "texinfo", "plain", "" );
+&setup_format ("latex2e", "LaTeX2e", "latex", "latex2e", "2E" );
+
+########################################################################
+# Global defaults. These should be set to reasonable values when the
+# script is installed. The values below are reasonable for many Unix
+# installations.
+#
+# To override these defaults, when TeXit begins, it loads each of the
+# following files, if they exist:
+#
+# A system-wide init file (if configured to do so)
+# ~/.texitrc (.texitrc in the users home directory)
+# /path/.texitrc (/path is the path of the tex file being processed)
+# foo (for each "-initfile foo" specified on the command line)
+#
+# They are loaded in that order and each can over-ride the values of
+# the preceding. Or any of the defaults shown below:
+#
+$TEXITRC = ".texitrc"; # Filename for init files ~/.texitrc ...
+
+$DEFAULTFORMAT = "plain"; # Default format to use...
+$DEFAULTLATEXFORMAT = "latex"; # Default LaTeX format to use...
+$VERBOSE = 0; # Lots of messages...
+$TEXITFASTEXIT = 1; # Empty line at prompt == quit
+$ONCE = 0; # Run once, no prompting? (-once)
+$GOTOMENU = 0; # Go right to menu? (-menu)
+$CONFIRMPRINT = 1; # Ask before printing?
+$CLEANONEXIT = 0; # Cleanup automatically on exit?
+$CLEANPROMPT = 1; # Should we ask first? (-clean)
+$SETXTITLE = 0; # Should we alter the X11 title/icon name?
+
+%DEPENDSON = (); # what does the DVI file depend on?
+
+%WARNINGS = (); # are there any warnings parse_<format>_log
+ # didn't recognize?
+%DEFAULTCMD = (); # no default commands
+%AUTOCMD = (); # no automatic commands
+$AUTOMAX = 1; # how many automatic commands can occur
+ # in a row?
+
+$TEXERROR = 1; # Return code from TEX that indicates an
+ # an error. Under emTeX, make this 2.
+ # emTeX returns 1 for overfull boxes, etc.
+ # which arent really errors.
+$ERRORLINE = 0; # What line did the TeX error occur on...
+$ERRORFILE = ""; # What file was being processed...
+%Parens = ();
+$ParenLevel = 0;
+
+$ENV_TEXINPUTS = "TEXINPUTS"; # Name of TEXINPUTS environment variable
+$ENV_BIBINPUTS = "BIBINPUTS"; # Name of BIBINPUTS environment variable
+$ENV_TEXFMTS = "TEXFORMATS";# Name of TEXFORMATS environment variable
+
+$TEXRETRIES = 4; # How many retries before we give up?
+
+$TEXHEADERSIZE = 20; # number of lines at the top of each
+ # document that TeXit searches for
+ # formatting clues.
+
+$QuitReply = "e[x]it"; # To leave TeXit
+$TeXReply = "[t]ex"; # To run TeX
+$CleanupReply = "[c]leanup"; # To cleanup files
+$BibTeXReply = "[b]ibtex"; # To run BibTeX
+$GhostviewReply= "[g]hostview";# To run Ghostview
+$PrintReply = "[p]rint"; # To print
+$ViewReply = "[v]iew"; # To view
+$StatusReply = "[?]status"; # To get a status report from TeXit
+
+$TEX = "virtex"; # TeX, the program
+$BIBTEX = "bibtex"; # BibTeX
+$MAKEINDEX = "makeindex"; # MakeIndex
+$VIEW = "xdvi"; # A previewer
+$GSVIEW = "ghostview"; # Ghostview, another previewer
+
+# The following commands are used to run each of the above programs.
+# See the 'run' subroutine for a description of the %-variable
+# substitution that is performed. Also, note that Perl $VARIABLE
+# substitution is performed too.
+#
+# The $TEXCMDs are stored in an associative array because not all
+# format files can be run with the same command line.
+#
+$TEXCMD{"default"} = '$TEX "%F %o \\input{%t}"';
+$TEXCMD{"plain"} = '$TEX "%F %o \\input %t"';
+$TEXCMD{"texinfo"} = '$TEX "%F %o \\input %t"';
+$BIBTEXCMD = '$BIBTEX %o %s';
+$MAKEINDEXCMD = '$MAKEINDEX %o %s';
+$VIEWCMD = '$VIEW %o %d';
+$GSVIEWCMD = '$GSVIEW %o %s.ps';
+
+# Default options for each of the above commands. These options
+# are _always_ used (where %o occurs in the command) if they are
+# set, so choose wisely. They come before any additional options
+# specified at the TeXit prompt.
+#
+$TEXOPTS{"default"} = "";
+$BIBTEXOPTS = "";
+$MAKEINDEXOPTS = "";
+$VIEWOPTS = "";
+$GSVIEWOPTS = "";
+
+########################################################################
+# Printer initialization. It works like this: the user selects a
+# printer queue "Q". Then $PRQUEUE{"Q"} determines the printer _type_.
+# Ordinarily, the user never knows about the type, but it is used
+# to lookup the DVI driver and printer commands. In particular:
+#
+# $PRQUEUE{"Q"} = "TYPE"
+#
+# $DVICMD{"TYPE"} = DVI driver command (with substitutions)
+# $DVIOPT{"TYPE"} = Default options for driver
+# $DVIQUERY{"TYPE"} = Ask for additional DVI options?
+# If $DVIQUERY{"TYPE"} is defined, TeXit will ask
+# the user for additional dvi driver options (using
+# the value of $DVIQUERY{"TYPE"} as the prompt).
+# These options are added to any options entered
+# at the prompt.
+#
+# $PRINTCMD{"Q"} = How to print resulting file (with substitution)
+# $PRINTOPT{"Q"} = Default options for driver
+# $PRINTQUERY{"Q"} = Ask for additional printer options? See above.
+# $PRINTCONFIRM{"Q"}= Yes or No? Overrides $CONFIRMPRINT...
+#
+# Ordinarily, all of the above variables should be set for each queue
+# and it is an error for them to be unset. However, the following
+# exception is made: if the $PRINTCMD{"Q"} isn't set, then the file
+# is run through a DVI driver but is not printed.
+#
+# Note: Ghostview is implemented this way, but it is also hard coded
+# later on as an option in it's own right because we can be a little
+# more intelligent about it.
+#
+# It would clearly have been possible to do configuration in other
+# ways, in particular by using a single print command with different
+# printer options ("lpr -P%p" for example). But that has disadvantages
+# too and it doesn't save much time or space.
+#
+$PRQUEUE{"Local"} = "ps"; # A 'local' printer
+$PRQUEUE{"File"} = "ps"; # Just save the PS file
+$PRQUEUE{"ghostview"} = "ps"; # Ghostview
+
+$PRINTCMD{"Local"} = 'lpr %o %s.ps';
+$PRINTOPT{"Local"} = "";
+
+$PRINTCMD{"File"} = "";
+$PRINTOPT{"File"} = "";
+
+$PRINTCMD{"ghostview"} = '$GSVIEW %o %s.ps';
+$PRINTOPT{"ghostview"} = "";
+
+$DVICMD{"ps"} = 'dvips %o %d';
+$DVIOPT{"ps"} = "";
+
+########################################################################
+# The $CleanupReply option looks for a list of files to delete in the
+# list "@cleanup_files". These are usually set by perl commands in the
+# document since there aren't any default _files_ to delete.
+#
+# It also looks for a list of extensions in the list "@cleanup_<format>"
+# If the file <texbasefile>.<ext> exists, it is deleted during cleanup
+#
+@cleanup_files = ();
+@cleanup_plain = ('log');
+@cleanup_texinfo = ('log', 'aux', 'cp','fn','ky','pg','toc','tp','vr');
+@cleanup_latex = ('log', 'aux', 'blg', 'ilg', 'lot', 'lof', 'toc');
+@cleanup_nfss2ltx = ('log', 'aux', 'blg', 'ilg', 'lot', 'lof', 'toc');
+@cleanup_latex2e = ('log', 'aux', 'blg', 'ilg', 'lot', 'lof', 'toc');
+
+########################################################################
+# The system default, user default, and directory default initfiles
+# have to be kept around until we're ready to load them. That's what
+# the @TEXITINIT list is for. Ordinarily this is initialized empty.
+# But if you always want to load a system-wide init file, perhaps
+# "/usr/local/lib/texitrc" then you can initialize TEXITINIT to
+# include that file.
+#
+@TEXITINIT = ();
+
+# Unfortunately, we want to run the system default files before the
+# files specified with -initfile on the command line, but we don't
+# find out what the name of the directory default is until after we've
+# parsed the command line. To circumvent this problem, we store
+# initfiles from the command line in a seperate array.
+#
+@TEXITCMDLINE = (); # leave this empty
+
+########################################################################
+# Locate the user-default init file and store it on the TEXITINIT list.
+# Some installations might not have a HOME environment variable to
+# contain the home directory, so we let them specify a TEXIT env. var.
+# instead.
+#
+$_ = $ENV{"TEXIT"};
+$_ = $ENV{"HOME"} if $_ eq "";
+$_ .= "/" if $_ && $_ !~ /.*\/$/;
+$texitrc = $_ . $TEXITRC;
+print "Looking for user-init file: $texitrc\n" if $VERBOSE;
+unshift(@TEXITINIT, $texitrc) if -r $texitrc;
+
+########################################################################
+# make STDOUT unbuffered so we can print messages w/o newlines in them
+#
+select(STDOUT); $| = 1;
+
+########################################################################
+# Get ready to parse the arguments...
+#
+# The associative array "cmdlineopts" is built to hold all of the
+# possible, unambiguous command line options. It will later be used
+# to get back the full name (i.e. $cmdlineopts{"ini"} = "initfile").
+#
+{
+ local (@formatlist) = keys (%format);
+ %cmdlineopts = ();
+ &abbrev (*cmdlineopts,
+ 'initfile', 'verbose', 'once', 'menu', 'clean',
+ 'bibtex', 'makeindex', 'tex',
+ @formatlist);
+}
+
+# Build the case statement for parsing the options. This code builds
+# an associative array and 'eval's it to get the cases. It's a little
+# obscure, but it works.
+#
+# If a valid option is not found in the case statement, it is assumed
+# to be the name of a format file. This saves a whole bunch of cases.
+# Note: the lines inbetween &case and ENDCASE _are not_ evaluated
+# by the Perl compiler at compile time...they are 'eval'ed at runtime.
+#
+&case (*casefoo,<<'ENDCASE');
+ initfile $texitrc = $ARGV[0] || "no file"; shift;
+ verbose $VERBOSE = 1;
+ once $ONCE = 1;
+ menu $GOTOMENU = 1;
+ clean $CLEANPROMPT = 0;
+ tex $runtexfirst = 1;
+ bibtex $runbibtexfirst = 1;
+ makeindex $runmakeindexfirst = 1;
+ENDCASE
+
+# make sure a few things are undefined
+undef ($TEXFORMAT);
+undef ($texitrc);
+undef ($runtexfirst);
+undef ($runbibtexfirst);
+undef ($runmakeindexfirst);
+
+# Now look at the arguments, keep scanning through them as long as
+# they begin with a hyphen. Anything after that has to be a filename
+#
+while ($ARGV[0] =~ /^-(.*)/ && shift) {
+ ($option = $1) =~ tr/A-Z/a-z/;
+ $option = $cmdlineopts{$option}; # find long opt
+ die "Unrecognized or ambiguous option: -$1.\n"
+ if $option eq "";
+
+ if (defined($casefoo{$option})) {
+ # Ok, it's one of the arms of the case statement
+ eval $casefoo{$option};
+ if (defined($texitrc)) {
+ # The only arm that isn't self-contained is the -initfile arm.
+ # For that option, we have to check for the initfile and save
+ # it for later.
+ die "You forgot the file name after \"-$1\".\n"
+ if $texitrc eq "no file";
+
+ die "Initfile does not exist: $texitrc\n"
+ if ! -r $texitrc;
+
+ print "Looking for -initfile: $texitrc\n" if $VERBOSE;
+ unshift(@TEXITCMDLINE, $texitrc);
+ undef ($texitrc);
+ }
+ } else {
+ # It's not one of the arms of the case, so it must be a format.
+ $TEXFORMAT = $option;
+ }
+}
+
+########################################################################
+# What's left on the command line must be the name of the file that
+# contains the document to process. Anything else must be options to
+# TeX. Make sure they begin with an escape (you can put \relax
+# in front if you need to). Note: this verifies that the user
+# escaped the shell command line correctly.
+#
+$texfile = $ARGV[0] || &display_usage(); # display_usage doesn't return
+shift;
+
+if (defined($ARGV[0])) {
+ local ($texescape, $printescape);
+ $texescape = "\\\\";
+ $texescape = "@" if $TEXFORMAT eq "texinfo";
+ eval "\$printescape = \"$texescape\"";
+ if ($ARGV[0] !~ /^$texescape.*/) {
+ print "TeX options must begin with a \"$printescape\". ";
+ die "Use ${printescape}relax if necessary.\n";
+ }
+
+ $CMDLINEOPTS = join (" ", @ARGV);
+}
+
+########################################################################
+# Locate the requested TeX file. It's either in the current (or
+# specified) directory or no path was specified and it's on the
+# TEXINPUTS path.
+#
+$qualifiedtexfile = &cleanup_texfilename($texfile);
+if (! -e $qualifiedtexfile) {
+ if ($texfile !~ /[\/\\]/) { # no path...
+ $qualifiedtexfile = &find_on_path("$ENV{$ENV_TEXINPUTS}",
+ "$texfile");
+ $qualifiedtexfile = &find_on_path("$ENV{$ENV_TEXINPUTS}",
+ "$texfile" . ".tex")
+ if $qualifiedtexfile eq "";
+
+ die "Cannot find \"$texfile[.tex]\" on $ENV_TEXINPUTS path.\n"
+ if $qualifiedtexfile eq "";
+
+ $texfile = $qualifiedtexfile;
+ } else {
+ die "Cannot find \"$texfile[.tex]\".\n";
+ }
+} else {
+ $texfile = $qualifiedtexfile;
+}
+
+########################################################################
+# Maybe the file has an emacs-style "local variables" definition that
+# describes the file. Here, we only care about the local variables
+# "TeX-master" and "mode". These are the same variables AUC-TeX uses.
+# The TeX-master file means that _this_ file is some sub-part of a
+# larger document. The larger document is pointed to by TeX-master.
+# The "mode" is what format this is in (Plain TeX, LaTeX, ... etc.)
+#
+# Alternatively, if you don't use Emacs, you can get the same logic
+# out of TeXit by putting:
+#
+# % Master: file
+# % Mode: format % or Format: format
+#
+# somewhere near the top (within the first $TEXHEADERSIZE lines) of
+# your document. In this case, the line must begin with a percent
+# sign and there must be nothing else on the line. The Master: and
+# Mode: commands must each appear on a line of their own. If you
+# specify both a Master: and a TeX-master in the local variables, the
+# TeX-master will win (simply because it comes later in the file).
+# The same goes for mode:.
+#
+# Other than that, TeXit tries to be smart about what format to use,
+# looking for things that are clues to the format. Unfortunately,
+# TeXit is easy to fool, so you may need a -<format> on the command
+# line sometimes.
+#
+&get_local_vars ($texfile);
+
+# If this file has a master, load the local vars from the master file
+# as well...these override variables from the initial file, but that's
+# probably for the best anyway...
+#
+# TeX-master = "t" is special, it means _this_ file is the master file
+# (i.e. it's `true' that this is the master file).
+#
+# If there isn't a master file, then $texfile is the master.
+#
+$texmaster = $texvars{"TeX-master"};
+if (defined($texmaster) && $texmaster ne "t") {
+ $texmaster = &cleanup_texfilename($texmaster);
+ print "Processing $texfile ... superceded by master file.\n";
+ die "Cannot read master file: $texmaster" if (! -r $texmaster);
+ &get_local_vars ($texmaster);
+} else {
+ $texmaster = $texfile;
+}
+
+if ($texvars{"mode"} eq "") {
+ if ($TEXFORMAT eq "") {
+ print "\nI can't figure out what format this file uses, ",
+ "and you didn't say, so \nI'm going to guess that ",
+ "it's a \"$formatname{$DEFAULTFORMAT}\" file. Use ",
+ "the \"-format\" \noption if this is a bad guess.\n\n";
+
+ $texvars{"mode"} = $DEFAULTFORMAT;
+ } else {
+ $texvars{"mode"} = $TEXFORMAT;
+ }
+}
+
+########################################################################
+# Split the master filename up into a path, name, and extension
+#
+($TEXFILEPATH,$TEXFILENAME,$TEXFILEEXT) = &splitfn($texmaster,".tex");
+$DEPENDSON{$texmaster} = 1;
+
+########################################################################
+# Build up the default array of commands.
+#
+# Prompt with "key" if $CMDPROMPT{key} exists and the test $CMDTEST{key}
+# succeeds. A null test succeeds by default. The string enclosed in
+# square brackets in the key is what the user has to enter to select
+# that option.
+#
+# TeXit `eval's $CMDPROMPT{key} if the user selects the option "key".
+#
+# Use single quotes when building the command to be evaluated _and_
+# the test so that $VAR substitution isn't performed prematurely!
+#
+# The commands to run tex, cleanup the directory, and quit are used
+# elsewhere. Change the definitions of $xxxReply to make them different.
+# But don't change what they _mean_!!!
+#
+
+$RETEX = "last ASKLOOP";
+$REASK = "next ASKLOOP";
+
+$CMDPROMPT{$TeXReply} = '$RETEX';
+$CMDPROMPT{$CleanupReply} = '&cleanup("$opts"); $REASK';
+$CMDPROMPT{$QuitReply} = 'print "Thank you for using TeXit!\n\n";exit(0)';
+$CMDPROMPT{$StatusReply} = '&showstatus("$opts"); $REASK';
+
+$CMDTEST{$TeXReply} = '';
+$CMDTEST{$CleanupReply} = '&files_to_cleanup()';
+$CMDTEST{$QuitReply} = '';
+$CMDTEST{$StatusReply} = '';
+
+# It would be ok to change these...
+#
+$CMDPROMPT{$BibTeXReply} = '&bibtexcmd("$opts"); $RETEX';
+$CMDPROMPT{$GhostviewReply} = '&ghostviewcmd("$opts"); $REASK';
+$CMDPROMPT{$PrintReply} = '&printcmd("$opts"); $REASK';
+$CMDPROMPT{$ViewReply} = '&viewcmd("$opts"); $REASK';
+
+$CMDTEST{$BibTeXReply} = '"$BIBTEX" && -r &auxfile("aux")';
+$CMDTEST{$GhostviewReply} = '"$GSVIEW" && -r &auxfile("dvi")';
+$CMDTEST{$PrintReply} = '"%PRQUEUE" && -r &auxfile("dvi")';
+$CMDTEST{$ViewReply} = '"$VIEW" && -r &auxfile("dvi")';
+
+########################################################################
+# Look for a directory-local init file. Then load all the init files
+# that we've found. Load the global one, then the user-global one,
+# then the directory-local one then all the user specified ones, in
+# order.
+#
+# Note: init files are just bits of Perl code so they can really do
+# anything you want. But they have to be legal Perl. Usually they
+# just set some global vars.
+#
+# Yes, "require_hack" is ugly. But read the comments in that routine
+# and you'll see why it was necessary.
+#
+$texitrc = $TEXFILEPATH . $TEXITRC;
+print "Looking for local init file: $texitrc\n" if $VERBOSE;
+unshift (@TEXITINIT, $texitrc) if -r $texitrc;
+
+while (@TEXITINIT) {
+ $texitrc = pop(@TEXITINIT);
+ if (-r $texitrc) {
+ &require_hack ($texitrc);
+ } else {
+ warn "Can't read initfile: $texitrc\n";
+ }
+}
+
+while (@TEXITCMDLINE) {
+ $texitrc = pop(@TEXITCMDLINE);
+ if (-r $texitrc) {
+ &require_hack ($texitrc);
+ } else {
+ warn "Can't read initfile: $texitrc\n";
+ }
+}
+
+undef($texitrc);
+
+########################################################################
+# Now that all the initialization code has been run, build up an
+# abbreviation list for the printer queues.
+#
+{
+ local ($queue,@prlist);
+
+ foreach $queue (keys %PRQUEUE) {
+ unshift(@prlist, $queue);
+ }
+
+ %pr_queue_names = ();
+ &abbrev (*pr_queue_names, @prlist);
+}
+
+########################################################################
+# Pick a format. Tell the user if we don't agree about the format
+# they demanded. Shift the format to lowercase for using as a key in
+# the %format associative array.
+#
+# Bail out if we can't find the format in the %format.
+#
+print "Processing $TEXFILEPATH$TEXFILENAME.$TEXFILEEXT ... ";
+if ($TEXFORMAT ne "" && $TEXFORMAT ne $texvars{"mode"}) {
+ print "it looks like a $formatname{$texvars{mode}} file to me,\n";
+ print "but you said it was a $formatname{$TEXFORMAT} file. ";
+ print "Suit yourself.\n";
+} elsif ($TEXFORMAT eq "") {
+ print "it looks like a $formatname{$texvars{mode}} file to me.\n";
+ if ($texvars{"mode"} eq "latex" && $DEFAULTLATEXFORMAT) {
+ $TEXFORMAT = $DEFAULTLATEXFORMAT;
+ print "(Using $formatname{$TEXFORMAT} variant of LaTeX.)\n";
+ }
+} else {
+ print "using $formatname{$TEXFORMAT}, like you said.\n";
+}
+
+$TEXFORMAT = $texvars{"mode"} if $TEXFORMAT eq "";
+$TEXFORMAT =~ tr/A-Z/a-z/;
+
+die "Don't know what format file to use for \"$TEXFORMAT\".\n"
+ if $format{$TEXFORMAT} eq "";
+
+########################################################################
+# Let's make sure we can process this format...
+#
+{
+ local ($path) = $ENV{$ENV_TEXFMTS} || "/usr/local/lib/tex/formats";
+ local ($formatfile) = $format{$TEXFORMAT} . ".fmt";
+ local ($formatpath) = &find_on_path($path, $formatfile);
+
+ if ($formatpath eq "") {
+ print "\nCan't find the format file for ";
+ print "$formatname{$TEXFORMAT} on the $ENV_TEXFMTS path.\n";
+ die "Can't continue without \"$formatfile\".\n";
+ }
+}
+
+# If the user has specified an alternate inputs environment for this
+# format, temporarily set the environment for TeX...
+if ($altinputenv{$TEXFORMAT}
+ && $ENV{"$ENV_TEXINPUTS$altinputenv{$TEXFORMAT}"}) {
+ $ENV{"$ENV_TEXINPUTS"} = $ENV{"$ENV_TEXINPUTS$altinputenv{$TEXFORMAT}"};
+ print "Adjusting $ENV_TEXINPUTS environment variable for this format.\n";
+}
+
+########################################################################
+# Evaluate the PLC commands for this document.
+#
+# PLC commands are bits of Perl that usually look something like this:
+#
+# % &makeindex('-o driver.ind driver.idx')
+# % if &missing('driver.ind') || &changed('driver.idx');
+# % &bibtex('driver') if &newer('jigsaw.bib','driver.bbl');
+#
+# This says run MakeIndex if "driver.ind" is missing or "driver.idx"
+# changes over the course of a TeX run. And run BibTeX if the
+# bibliography file is newer than the BBL file (it'll also get run
+# automatically if any citations are unresolved).
+#
+# $first_time is true now so that &missing, &dependson, &changed, and
+# &newer all return false regardless of the condition tested. This
+# way we can initialize some globals before we run TeX for the first
+# time (to see if files change on the first run, for example).
+#
+# VERBOSE is forced off temporarily so that no stupid messages are
+# printed, even if the user wants them. Note the dynamic scoping
+# nature of Perl.
+#
+
+{
+ local ($VERBOSE) = 0;
+ local ($first_time) = 1;
+ local ($line, @the_rules);
+
+ @the_rules = @default_rules;
+ foreach $line (@the_rules) {
+ eval $line;
+ die "Invalid rule in \@default_rules: $line\n" if $@;
+ }
+
+ eval "\@the_rules = \@${TEXFORMAT}_rules";
+ foreach $line (@the_rules) {
+ eval $line;
+ die "Invalid rule in \@${TEXFORMAT}_rules: $line\n" if $@;
+ }
+
+ foreach $line (@PLCRULES) {
+ eval $line;
+ die "Invalid PLC rule: $line\n" if $@;
+ }
+}
+
+{
+ # Now print out the rules, if the user wants to see them...
+
+ local ($line, @the_rules);
+
+ if ($VERBOSE && @default_rules) {
+ print "Default rules:\n";
+ foreach $line (@default_rules) {
+ print " $line\n";
+ }
+ print "\n";
+ }
+
+ eval "\@the_rules = \@${TEXFORMAT}_rules";
+ if ($VERBOSE && @the_rules) {
+ print "$TEXFORMAT rules:\n";
+ foreach $line (@the_rules) {
+ print " $line\n";
+ }
+ print "\n";
+ }
+
+ if ($VERBOSE && @PLCRULES) {
+ print "Your document defined the following rules:\n";
+ foreach $line (@PLCRULES) {
+ print " $line\n";
+ }
+ print "\n";
+ }
+}
+
+########################################################################
+# If the user asked us to run MakeIndex or BibTeX first, run them now.
+#
+if ($runbibtexfirst) {
+ if (-r &auxfile("aux")) {
+ &bibtexcmd("");
+ } else {
+ print "There's no AUX file to run BibTeX against.\n";
+ }
+}
+undef($runbibtexfirst);
+
+if ($runmakeindexfirst) {
+ if (-r &auxfile("idx")) {
+ &makeindexcmd("");
+ } else {
+ print "There's no IDX file to run MakeIndex against.\n";
+ }
+}
+undef($runmakeindexfirst);
+
+########################################################################
+# Ok, now it's time to run TeX. Run TeX repeatedly until an error
+# occurs, the dependencies don't change or we've tried more than four
+# times. If it doesn't work in four tries, it never will.
+#
+$opts = ""; # user specified options after the prompt
+$changed = 0; # did the file change on this run?
+$runcount = 0; # how many times have we run TeX in a row?
+
+# Think about BibTeX: every time we run it, it changes the BBL file and
+# potentially causes us to decide to run TeX again. But if the list of
+# missing citations doesn't change after the first run, running again
+# won't help. So we keep track of the missing citations between runs
+# and only run BibTeX when it changes.
+#
+%missingcites = (); # last missing cite list
+
+# If not -menu, assume we'll TeX first...
+#
+$changed = !$GOTOMENU;
+
+# If the tex file and the dvi file and the log file all exist, maybe
+# we don't actually have to run TeX first...maybe the user just wants
+# to print or preview or something and they're used to typing "texit".
+#
+if (!$GOTOMENU && !$runtexfirst) { # if the user doesn't insist...
+ $changed = &dependencies_changed ()
+ if (-r $texmaster && -r &auxfile("dvi") && -r &auxfile("log"));
+ %missingcites = (); # the results from an old log don't count
+}
+undef($runtexfirst);
+
+$autocount = 0; # No automatic commands yet...
+$lastcmd = $TeXReply; # assume we ran tex...
+TEXLOOP: for(;;) { # don't stop till the user says quit.
+ if ($changed) {
+ $ERRORLINE = 0; # No errors occurred...
+ $ERRORFILE = ""; #
+ $rc = &texcmd($opts); # run TeX
+ # If the return code is less than TEXERROR, set it to zero.
+ # This canonicalizes the test for success when running a program
+ $rc = 0 if $rc < $TEXERROR;
+
+ # 9 Sep 93: the former test at this location failed to handle
+ # a document that _required_ four tries to process correctly.
+ $changed = 0;
+ $changed = 1 if $rc == 0 && &dependencies_changed ();
+ next TEXLOOP if $changed && $runcount < $TEXRETRIES;
+ } else {
+ $rc = 0;
+ }
+
+ if ($rc != 0) {
+ warn "TeX failed. All bets are off.\n\n";
+ ($ERRORFILE, $ERRORLINE) = &find_error_line();
+ }
+
+ if ($runcount >= $TEXRETRIES && $changed) {
+ print "\nI've tried $TEXRETRIES times and it still ";
+ print "isn't right...I give up.\n\n";
+ exit 1 if $ONCE;
+ } else {
+ if ($ONCE) {
+ &cleanup("") if $CLEANONEXIT && !$CLEANPROMPT && !$rc;
+ exit $rc;
+ }
+ }
+
+ $changed = 1; # assume it'll change before we get back to top...
+
+ ASKLOOP: for (;;) {
+ # User can change anything while we're sitting at the prompt, so
+ # reset runcount and citation list.
+ $runcount = 0;
+ %missingcites = ();
+
+ &set_x_titles("TeXit");
+
+ if ($rc == 0
+ && $AUTOCMD{$lastcmd} ne ""
+ && $CMDPROMPT{$AUTOCMD{$lastcmd}} ne ""
+ && $autocount < $AUTOMAX) {
+ $cmd = $AUTOCMD{$lastcmd};
+ $opts = "";
+ $autocount++;
+ } else {
+ $rc = 0;
+ $autocount = 0;
+ ($cmd, $opts) = &get_command();
+ }
+
+ if ($cmd eq "") {
+ $cmd = $QuitReply;
+ $cmd = $DEFAULTCMD{$lastcmd} if $DEFAULTCMD{$lastcmd} ne "";
+ $cmd = "redo" if !$TEXITFASTEXIT && $DEFAULTCMD{$lastcmd} eq "";
+
+ # Unless the last thing we did was cleanup, re-running TeX
+ # supercedes other commands if the DVI file is gone or any
+ # dependent files are newer...
+ if ($lastcmd ne $CleanupReply) {
+ if (! -r &auxfile("dvi")) {
+ print "\n", &auxfile("dvi"), " disappeared.\n";
+ $cmd = $TeXReply;
+ } elsif (&dependent_files_are_newer()) {
+ print "\nA file that the document ",
+ "depends on has changed.\n";
+ $cmd = $TeXReply;
+ }
+ }
+ }
+
+ next if $cmd eq "redo";
+
+ # Special case for $QuitReply if CLEANONEXIT is true:
+ #
+ if ($cmd eq $QuitReply && $CLEANONEXIT) {
+ $AUTOCMD{$CleanupReply} = $QuitReply; # exit w/o asking next
+ $CLEANONEXIT = 0; # Prevent reexecution of this code...
+ $cmd = $CleanupReply;
+ $opts = "";
+ }
+
+ if (($cmd eq $QuitReply) && $SETXTITLE) {
+ local ($host, $cwd);
+ chop($host = `hostname`);
+ chop($cwd = `pwd`);
+ &set_xterm_title("$host: $cwd");
+ &set_xicon_title("$host");
+ }
+
+ $lastcmd = $cmd;
+
+ # Ok, now it's time to 'eval' the command.
+ #
+ # First, copy it and protect any quotations in it from premature
+ # replacement. Note: the user has to protect backslashes by
+ # escaping them (i.e. in order to get "\" use "\\"). This is
+ # necessary so that the user can use a backslash to protect
+ # dollar signs for variable assignment.
+ #
+ # Then eval it once to do $-var substitution.
+ #
+ # Then eval it again to really do the work...
+ #
+ $thecmd = $CMDPROMPT{$cmd};
+ $thecmd =~ s/\"/\\"/g; # protect double quotes
+ eval "\$thecmd = \"$thecmd\"";
+ eval ( $thecmd );
+ die "Invalid command defined for $cmd: $CMDPROMPT{$cmd}\n" if $@;
+ }
+}
+
+########################################################################
+# These subroutines execute the expected commands. They are here, with
+# nice names, so that the PLC commands can use them and be easy to read.
+# Internally, different routines are used so that %-var substitution is
+# preformed. Note that it _isn't_ preformed here.
+#
+# &tex("foo") ==> $TEX foo
+#
+sub tex {
+ local ($opts) = @_;
+ local ($exopts);
+
+ $exopts = join(" ",$TEXOPTS{$TEXFORMAT},$CMDLINEOPTS,$opts);
+
+ &run($TEX . $exopts, "");
+}
+
+sub bibtex {
+ local ($opts) = @_;
+
+ &run($BIBTEX . " " . $BIBTEXOPTS . " " . $opts, "");
+}
+
+sub makeindex {
+ local ($opts) = @_;
+
+ &run($MAKEINDEX . " " . $MAKEINDEXOPTS . " " . $opts, "");
+}
+
+########################################################################
+# These are the subroutines used internally where all we want to pass
+# in are the _additional_ options.
+#
+# &texcmd("foo") => $TEXCMD with $TEXOPTS and "foo" inserted as options
+# and all other %-var substitution performed.
+#
+sub texcmd {
+ local ($opts) = @_;
+ local ($rc, $extex, $exopt);
+
+ $extex = $TEXCMD{"default"};
+ $extex = $TEXCMD{$TEXFORMAT} if $TEXCMD{$TEXFORMAT} ne "";
+ $exopt = join (" ", $TEXOPTS{$TEXFORMAT}, $CMDLINEOPTS, $opts);
+
+ $rc = &run ($extex, $exopt);
+ $runcount++;
+ $rc;
+}
+
+sub bibtexcmd {
+ local ($opts) = @_;
+ local ($rc);
+
+ if (! -r &auxfile("aux")) {
+ print "Cannot run BibTeX, no AUX file." if $VERBOSE;
+ $rc = -1;
+ } else {
+ $rc = &run ($BIBTEXCMD, $BIBTEXOPTS . " " . $opts);
+ }
+
+ $rc;
+}
+
+sub makeindexcmd {
+ local ($opts) = @_;
+
+ &run($MAKEINDEXCMD, $MAKEINDEXOPTS . " " . $opts);
+}
+
+sub viewcmd {
+ local ($opts) = @_;
+
+ &run ($VIEWCMD, $VIEWOPTS . " " . $opts);
+}
+
+sub printcmd {
+ local ($opts) = @_;
+ local ($queueopt, $dviuseropts, $prtuseropts);
+ local ($rc, $type);
+ local ($dvicmd, $dviopt, $prtcmd, $prtopt);
+ local ($doit,$queue);
+ local ($confirm) = $CONFIRMPRINT;
+
+ $queueopt = "";
+
+ # parse the user-specified options for a printer queue and options
+ if ($opts =~ /^\s*(\S+)\s*(.*)$/) {
+ $queueopt = $1;
+ $opts = $2;
+ }
+
+ # if the queue name starts with "-" or "/", assume that it's an option,
+ # not a queue name...
+ if ($queueopt =~ /^(-|\/)/) {
+ $opts = "$queueopt $opts";
+ $queueopt = "";
+ }
+
+ # if no queue is specified, use the default printer queue
+ $queueopt = $DEFAULT_PRINTER if $queueopt eq "";
+
+ if ($queueopt eq "") {
+ print "You must specify a printer queue (use \"p <printer>\").\n";
+ return(0);
+ }
+
+ # Special case, if the queue is "?", list the queues we know about.
+ if ($queueopt eq "?") {
+ &print_long_list("\nThe following queues are available: ",
+ sort(keys(%PRQUEUE)));
+ print "\n";
+ return (0);
+ }
+
+ $queue = $pr_queue_names{$queueopt};
+
+ if (!defined ($PRQUEUE{$queue})) {
+ warn "There is no printer queue named \"$queueopt\".\n";
+ return 0;
+ }
+
+ ($dviuseropts, $prtuseropts) = split (/\|/, $opts);
+
+ $type = $PRQUEUE{$queue};
+
+ if (!defined ($DVICMD{$type})) {
+ warn "There is no DVI driver for \"$type\" printers.\n";
+ return 0;
+ }
+
+ $dvicmd = $DVICMD{$type};
+ $dviopt = $DVIOPT{$type};
+
+ if ($DVIQUERY{$type} ne "") {
+ print $DVIQUERY{$type};
+ $_ = scalar (<STDIN>);
+ $dviuseropts .= " " . $_;
+ }
+
+ $prtcmd = $PRINTCMD{$queue};
+ $prtopt = $PRINTOPT{$queue};
+
+ $rc = &run ($dvicmd, $dviopt . " " . $dviuseropts);
+
+ warn "DVI driver failed.\n" if $rc;
+
+ $doit = ($prtcmd ne ""); # really print it...
+
+ if (defined($PRINTCONFIRM{$queue})) {
+ undef $confirm;
+ $confirm = 0 if "0" =~ /^$PRINTCONFIRM{$queue}/i;
+ $confirm = 0 if "no" =~ /^$PRINTCONFIRM{$queue}/i;
+ $confirm = 0 if "false" =~ /^$PRINTCONFIRM{$queue}/i;
+ $confirm = 1 if "1" =~ /^$PRINTCONFIRM{$queue}/i;
+ $confirm = 1 if "yes" =~ /^$PRINTCONFIRM{$queue}/i;
+ $confirm = 1 if "true" =~ /^$PRINTCONFIRM{$queue}/i;
+ if (! defined($confirm)) {
+ print "\nI don't know what `$PRINTCONFIRM{$queue}' means, ";
+ print "I'll assume that it means ";
+ print "`yes'.\n" if $CONFIRMPRINT;
+ print "`no'.\n" if !$CONFIRMPRINT;
+ $confirm = $CONFIRMPRINT;
+ }
+ }
+
+ # Wait! If the driver failed or there's a queue other than
+ # Ghostview selected, ask and make sure.
+ #
+ if ($rc || ($doit && ($queue ne "ghostview") && $confirm)) {
+ print "Really print to $queue? ";
+ $_ = scalar (<STDIN>); chop;
+ $doit = 0 if !/^y/i;
+ }
+
+ if ($doit) {
+ if ($PRINTQUERY{$queue} ne "") {
+ print $PRINTQUERY{$queue};
+ $_ = scalar (<STDIN>);
+ $prtuseropts .= " " . $_;
+ }
+ $rc = &run ($prtcmd, $prtopt . " " . $prtuseropts);
+ } else {
+ print "Print command aborted.\n" if ($prtcmd ne "");
+ $rc = -1;
+ }
+
+ $rc;
+}
+
+########################################################################
+# Ghostview is a little special. The print commands always have to
+# run the DVI driver because they don't know what kind of file might
+# be produced, but with Ghostview, we know that it'll be the master
+# document name with '.ps' on the end. So, if that exists and is
+# newer than the DVI file, don't bother running the DVI driver again.
+#
+# Note also that Ghostview knows what type it is, so the type doesn't
+# have to be in the printer queue. This means you can remove the
+# Ghostview printer from the queue list even if you don't have any
+# other PS printers.
+#
+# Oh, also, we always run the DVI driver again if the user entered
+# DVI driver options at the prompt...
+#
+sub ghostviewcmd {
+ local ($opts) = @_;
+ local ($queue, $dviuseropts, $prtuseropts);
+ local ($rc, $type);
+ local ($dvicmd, $dviopt, $prtcmd, $prtopt);
+ local ($doit);
+ local ($psfile) = &auxfile("ps");
+
+ ($dviuseropts, $prtuseropts) = split (/\|/, $opts);
+
+ $type = "ps";
+
+ if (!defined ($DVICMD{$type})) {
+ warn "There is no DVI driver for \"$type\" output.\n";
+ return 0;
+ }
+
+ $dvicmd = $DVICMD{$type};
+ $dviopt = $DVIOPT{$type};
+
+ if ((! -r $psfile || (&mtime($psfile) < &mtime(&auxfile("dvi"))))
+ || $dviuseropts ne "") {
+ $rc = &run ($dvicmd, $dviopt . " " . $dviuseropts);
+ } else {
+ print "\nPostScript file already exists.\n";
+ $rc = 0;
+ }
+
+ $doit = 1;
+ if ($rc) {
+ warn "DVI driver failed.\n";
+ print "Preview anyway? ";
+ $_ = scalar (<STDIN>); chop;
+ $doit = 1 if /^y/i;
+ }
+
+ if ($doit) {
+ $rc = &run ($GSVIEWCMD, $GSVIEWOPTS . " " . $prtuseropts);
+ }
+
+ $rc;
+}
+
+########################################################################
+# Add new files to the @cleanup_files array. This call makes sure that
+# duplicate filenames are not added. It simplifies the TeXit rules
+# for extending the list of files to clean up. Simply saying:
+#
+# push(@cleanup_files, 'one', 'two');
+#
+# is a bad idea because it'll add "one" and "two" each time the document
+# is processed. On the other hand, expecting users to type the rule:
+#
+# push(@cleanup_files, 'one', 'two') if $first_time;
+#
+# is unrealistic. So now they can just call cleanup_files...
+#
+sub cleanup_files {
+ local (@newfiles) = @_;
+ local ($file, $files, $newfile, $add);
+
+ $add = 1;
+ foreach $newfile (@newfiles) {
+ foreach $file (@cleanup_files) {
+ $add = 0;
+ last if $file eq $newfile;
+ $add = 1;
+ }
+ push (@cleanup_files, $newfile) if ($add);
+ }
+}
+
+########################################################################
+# Go out there and find all the files that have to be cleaned up.
+# Return them in a list.
+#
+sub files_to_cleanup {
+ local ($opts) = @_;
+ local (%thefiles);
+ local ($file, @filelist, $ext, @extlist);
+
+ @filelist = ();
+
+ # throw them all in an associative array first to
+ # avoid duplicates.
+ foreach $file (@cleanup_files) {
+ $file =~ s/\\/\//; # \ -> /
+ $file = $TEXFILEPATH . $file if $file !~ /\//;
+ $thefiles{$file} = 1;
+ }
+
+ # find the list of extensions in "@cleanup_<format>".
+ eval "\@extlist = \@cleanup_$TEXFORMAT";
+ if (@extlist) {
+ local ($file);
+ foreach $ext (@extlist) {
+ $file = &auxfile($ext);
+ $thefiles{$file} = 1;
+ }
+ }
+
+ # now put them in a regular array
+ foreach $file (keys %thefiles) {
+ if (-e $file) {
+ unshift(@filelist, $file);
+ }
+ }
+
+ # return that array
+
+ @filelist;
+}
+
+########################################################################
+# Delete any files that have to be cleaned up...
+#
+sub cleanup {
+ local ($opts) = @_;
+ local (@filelist, $_);
+
+ @filelist = &files_to_cleanup();
+
+ # if there are any files to be deleted, ask the user to confirm
+ # and then blow 'em away...
+ if (@filelist) {
+ &print_long_list ("\nDelete: ", sort @filelist);
+ print "\n";
+ if ($CLEANPROMPT) {
+ print "Really delete these files? ";
+ $_ = scalar (<STDIN>); chop;
+ } else {
+ $_ = "yes";
+ }
+
+ if (/^y/i) {
+ print "\n", unlink(@filelist), " files deleted.\n";
+ } else {
+ print "\nCleanup aborted.\n";
+ }
+
+ print "\n";
+ }
+}
+
+########################################################################
+# The workhorse. This routine takes a command string and an options
+# string. The command string is subjected to the following
+# interpolation:
+#
+# IN ORDER TO HANDLE SOME TEX WEIRDNESS WITH FILENAMES, THIS MACRO ASSUMES
+# THAT YOU PASSED '%t' TO THE TEX PROGRAM, IF YOU PASS '%s', THIS CODE
+# WILL MAKE INCORRECT SUBSTITUTIONS FOR SOME VARIABLES!
+#
+# 1) It is "eval"ed so that Perl variables in the
+# string are replaced with their values, i.e.
+# "$TEX foo" -> "virtex foo" if $TEX="virtex".
+#
+# 2) %-substitution is performed. The following
+# substitutions are currently supported:
+#
+# %F = format name (&lplain)
+# %s = base document name (myfile)
+# [ this used to mean /path/file w/o an extension, it now
+# means the name of the DVI file w/o the .dvi extension ]
+# %t = tex document name (/path/file.tex)
+# %d = dvi document name (myfile.dvi)
+# %e = error line, if there was one, otherwise 0
+# %E = file containing error, if there was one
+# %o = the options passed into run
+#
+# %S, %T, and %D are the same as %s, %t, and %d except that
+# pathname components are seperated by backslashes. This is a
+# hack for MS-DOS and OS/2.
+#
+# TeX has some funny rules about path names:
+#
+# - The DVI file always goes in the current directory,
+# the path is ignored.
+#
+# - To form the dvi filename, TeX strips off the last extension that
+# *you* provide and appends .dvi. For example:
+#
+# You say: You mean: Dvi file:
+# ------------- ------------ ------------
+# test.foo test.foo.tex test.dvi
+# test.foo test.foo test.dvi
+# test.foo.tex test.foo.tex test.foo.dvi
+# test.foo.bar test.foo.bar test.foo.dvi
+#
+sub run {
+ local ($cmd,$opts) = @_;
+ local ($format) = "&" . $format{$TEXFORMAT};
+ local ($texbase, $dvifile, $texfile);
+ local ($Texbase, $Dvifile, $Texfile);
+ local ($rc,$title);
+
+ $texfile = $TEXFILEPATH . $TEXFILENAME . "." . $TEXFILEEXT;
+ $texbase = $TEXFILENAME;
+ $dvifile = &auxfile ("dvi");
+
+ ($Texbase = $texbase) =~ s#/#\\#g;
+ ($Texfile = $texfile) =~ s#/#\\#g;
+ ($Dvifile = $dvifile) =~ s#/#\\#g;
+
+ # $cmd may be of the form "$TEX ..." and we want to
+ # expand the definition of $TEX, etc.
+ $cmd =~ s/\\/\\\\/g; # protect \'s
+ $cmd =~ s/\"/\\\"/g; # protect "'s
+ eval "\$cmd = \"$cmd\"";
+
+ # Now substitute for `%' options...
+ $cmd =~ s/\%F/$format/g;
+ $cmd =~ s/\%s/$texbase/g;
+ $cmd =~ s/\%t/$texfile/g;
+ $cmd =~ s/\%d/$dvifile/g;
+ $cmd =~ s/\%S/$Texbase/g;
+ $cmd =~ s/\%T/$Texfile/g;
+ $cmd =~ s/\%D/$Dvifile/g;
+ $cmd =~ s/\%e/$ERRORLINE/g if $ERRORLINE;
+ $cmd =~ s/\%E/$ERRORFILE/g if $ERRORLINE;
+ $cmd =~ s/\%o/$opts/g;
+
+ $cmd =~ s/\s+/ /g; # remove extranous whitespace, (display hack).
+
+ $title = (split(/\s/, $cmd))[0];
+ &set_xterm_title("TeXit: $title");
+ &set_xicon_title("$title");
+
+ print "\n$cmd\n";
+ $rc = system "$cmd";
+
+ if ($rc > 0) {
+ local (@parms);
+ @parms = split (/\s+/, $cmd);
+ print "\nNote: $parms[0] ended with return code $rc.\n";
+ }
+
+ $rc;
+}
+
+########################################################################
+# Gets a command from the user by building a prompt up from the
+# %CMDPROMPT and %CMDTEST arrays. A particular entry is only allowed
+# if an eval of it's %CMDTEST returns true.
+#
+# &get_command() returns a two element list. The first element is
+# index into the %CMDPROMPT array, the second element is any additional
+# arguments supplied by the user.
+#
+sub get_command {
+ local (%commands, $_, $options);
+ local ($promptstring, $promptprefix);
+ local ($line, $thetest, $result);
+
+ %commands = ();
+ $promptstring = "";
+ $promptprefix = "Again? ";
+ $line = $promptprefix;
+
+ # look for a stupid configuration error where a CMDTEST has
+ # gotten misspelled.
+ foreach $cmdname (keys %CMDTEST) {
+ die "$cmdname is defined as a CMDTEST but not as a CMDPROMPT.\n"
+ if !defined($CMDPROMPT{$cmdname});
+ }
+
+ foreach $cmdname (sort keys %CMDPROMPT) {
+ $thetest = $CMDTEST{$cmdname};
+ $result = 1;
+ if ($thetest ne "") {
+ eval "\$result = $thetest";
+ die "Bad CMDTEST: $thetest\n" if $@;
+ }
+
+ if ($result) {
+ local ($cmdkey) = $cmdname;
+ $cmdkey =~ s/^.*\[(.*)\].*$/$1/;
+ die "No command string in square brackets: $cmdname\n"
+ if $cmdkey eq "";
+ die "Duplicate command: $cmdname = $commands{$cmdkey}\n"
+ if defined($commands{$cmdkey});
+ $commands{$cmdkey} = $cmdname;
+ if ($cmdname !~ /^!.*/) {
+ if (length($line . $cmdname) < 60) {
+ $line .= $cmdname . ", ";
+ } else {
+ $promptstring .= $line . "\n";
+ $line = " " x length($promptprefix) . $cmdname . ", ";
+ }
+ }
+ }
+ }
+
+ $line =~ s/(.*),\s*$/$1/; # remove the last ", "...
+ $promptstring .= $line . ": ";
+
+ for (;;) {
+ ($cmdkey, $options) = ("", "");
+ print "\nProcessing: $TEXFILEPATH$TEXFILENAME.$TEXFILEEXT\n",
+ $promptstring;
+ $_ = scalar(<STDIN>); chop;
+ ($cmdkey, $options) = ($1, $2) if /\s*(\S+)\s*(.*)/;
+ last if $commands{$cmdkey} ne "" || $_ eq "";
+ print "\nI don't understand the command: $cmdkey\n\n";
+ }
+
+ ($commands{$cmdkey}, $options);
+}
+
+########################################################################
+# Parse the TeX file looking for a local variables section. Build
+# the associative array "%texvars" to hold the variables.
+#
+sub get_local_vars {
+ local($texfile) = @_;
+ local(@parms, $in_locals, $near_top, $in_plc, $plc_line);
+
+ open (TEXFILE, $texfile) || die "Can't read $texfile.\n";
+
+ $in_locals = 0; # have we found the start of the local vars?
+ $in_plc = 0; # are we in the PLC commands?
+ $near_top = $TEXHEADERSIZE; # are we in the "header" area?
+
+ @PLCRULES = (); # throw away any rules we've already found
+
+ while (<TEXFILE>) {
+ chop;
+
+ if ($near_top) {
+ $texvars{"TeX-master"} = $1 if /\%+\s*master:\s*(\S+)\s*$/i;
+ $texvars{"mode"} = $1 if /\%+\s*format:\s*(\S+)\s*$/i;
+ $texvars{"mode"} = $1 if /\%+\s*mode:\s*(\S+)\s*$/i;
+
+ if (!defined($texvars{"mode"})) {
+ $texvars{"mode"} = "latex" if (/^\\documentstyle/);
+ $texvars{"mode"} = "latex2e" if (/^\\documentclass/);
+ $texvars{"mode"} = "slitex" if (/^\\documentstyle.*\{slides\}/);
+ $texvars{"mode"} = "foiltex" if (/^\\documentstyle.*\{foils\}/);
+ $texvars{"mode"} = "amstex" if (/^\\document\s*$/);
+ $texvars{"mode"} = "lollipop" if (/^\\Define\w+:\w+/);
+ $texvars{"mode"} = "texinfo" if (/^\@node/);
+ $texvars{"mode"} = "texinfo" if (/^\@ifinfo/);
+ $texvars{"mode"} = "texinfo" if (/input texinfo/);
+ }
+
+ $near_top--;
+ }
+
+ $in_locals = 0 if $in_locals && /End:\s*$/;
+
+ if ($in_locals) {
+ local($_) = $_;
+ s/^\W*//; # remove leading whitespace
+ @parms = split(/[: \t\"\']+/);
+ $var = $parms[0];
+ shift(@parms);
+ $val = join(" ",@parms);
+ $texvars{$var} = $val if !defined($texvars{$var});
+ }
+
+ # the [V] makes sure Emacs doesn't get confused when editing
+ # texit.pl
+ $in_locals = 1 if /Local [V]ariables:\s*$/;
+
+ if ($in_plc && /End\s+TeXit\s+rules:\s*$/) {
+ push (@PLCRULES, $plc_line) if $plc_line ne "";
+ undef($plc_line);
+ $in_plc = 0;
+ }
+
+ if ($in_plc) {
+ local ($line) = $_;
+ local ($lastline) = /.*;\s*$/; # ends with a semicolon?
+ $line =~ s/\s*\%*\s*(.*)/$1/; # trim leading whitespace
+ $line =~ s/(.*)([^\\])(\#)(.*)$/$1$2/; # trim trailing comments
+ $line = "" if $line =~ /^\#/; # _only_ a comment?
+ $plc_line .= $line . " " if $line ne "";
+ if ($lastline) {
+ push (@PLCRULES, $plc_line);
+ $plc_line = "";
+ }
+ }
+
+ $in_plc = 1 if /Start\s+TeXit\s+rules:\s*$/;
+ }
+
+ warn "Unended local variables section?\n" if $in_locals;
+ die "Unended TeXit rules section!\n" if $in_plc;
+
+ close (TEXFILE);
+}
+
+########################################################################
+# Cleanup the TeX filename. Add the extension ".tex" if it doesn't
+# already have an extension.
+#
+sub cleanup_texfilename {
+ local($texfile) = @_;
+ local($path,$base,$ext) = &splitfn($texfile,".tex");
+
+ $ext = "tex" if ($ext eq "");
+
+ $path . $base . "." . $ext;
+}
+
+########################################################################
+# Break a filename into it's path, basename, and extension components.
+# The path returned always ends with a slash. "./" is returned if the
+# file has no path. If the filename passed in does not exist, the
+# default extension passed in is tried (actually, is assumed to be
+# correct).
+#
+sub splitfn {
+ local ($filename, $defext) = @_;
+ local ($path, $basename, $ext) = ("", "", "");
+
+ $filename =~ tr/\\/\//; # translate \ into /
+
+ $filename = $filename . $defext if ! -r $filename;
+
+ if ($filename =~ /\//) {
+ ($path = $filename) =~ s/\/[^\/]*$//;
+ ($basename = $filename) =~ s/.*\///;
+ }
+ else {
+ $path = ".";
+ $basename = $filename;
+ }
+
+ if ($basename =~ /\./) {
+ ($ext = $basename) =~ s/.*\.//;
+ $basename =~ s/\.[^.]*$//;
+ }
+
+ ($path . "/",$basename,$ext);
+}
+
+########################################################################
+# Print a %done message when scanning LOGFILE
+#
+sub percent_done {
+ local ($logfile) = @_;
+
+ if ($logfile) {
+ $LOGSIZE = -s $logfile;
+ $LOGPERC = 0;
+ $LOGPREV = 0;
+ } else {
+ $LOGPERC = int (tell(LOGFILE) / $LOGSIZE * 100.0);
+ printf "Parsing log file...%d%% done\r", $LOGPERC
+ if $LOGPERC > $LOGPREV;
+ $LOGPREV = $LOGPERC;
+ }
+}
+
+########################################################################
+# Search the log file looking for the (first) line that contains an
+# error.
+#
+sub find_error_line {
+ local ($logfile) = &auxfile("log");
+ local ($prevtext, $curfile, $paren, $rest, $temp);
+ local ($line) = 0;
+
+ &init_parse_log_filenames();
+
+ $curfile = "";
+
+ if (-r $logfile) {
+ &percent_done($logfile);
+ open (LOGFILE, $logfile);
+ while (<LOGFILE>) {
+ chop;
+ &percent_done();
+
+ ($paren, $rest, $temp) = &parse_log_filenames($_);
+ while ($paren eq "(" || $paren eq ")") {
+ $curfile = $temp;
+ ($paren, $rest, $temp) = &parse_log_filenames($rest);
+ }
+
+ if ($prevtext =~ /^! .*$/
+ && /^l\.([0-9]+) /) {
+ $line = $1;
+ }
+
+ last if $line;
+
+ $prevtext = $_;
+ }
+ close (LOGFILE);
+ }
+
+ ($curfile, $line);
+}
+
+########################################################################
+# Look through the log file for warnings.
+#
+sub parse_default_log {
+ # only looks for new dependency files...
+ local ($paren, $rest, $tempfile, $curfile);
+ local ($logfile) = &auxfile("log");
+
+ &init_parse_log_filenames();
+
+ if (-r $logfile) {
+ &percent_done ($logfile);
+ open (LOGFILE, $logfile);
+ while (<LOGFILE>) {
+ chop;
+ &percent_done ();
+
+ ($paren, $rest, $tempfile) = &parse_log_filenames($_);
+ while ($paren eq "(" || $paren eq ")") {
+ $curfile = $tempfile;
+ printf("Adding dependency for `%s'.\n", $curfile)
+ if !defined($DEPENDSON{$curfile}) && $VERBOSE;
+ $DEPENDSON{$curfile} = 1;
+ ($paren, $rest, $tempfile) = &parse_log_filenames($rest);
+ }
+ }
+ }
+ close (LOGFILE);
+}
+
+########################################################################
+# Look through the log file for the LaTeX warnings.
+#
+sub parse_latex_log {
+ local ($newbib, $misbib, $newref, $badref, $badfont) = (0, 0, 0, 0, 0);
+ local ($hastoc, $haslof, $haslot) = (0, 0, 0);
+ local ($font, $reference, $citation);
+ local ($inputfile, $warning);
+ local ($logfile) = &auxfile("log");
+ local ($auxfile) = &auxfile("aux");
+ local ($paren, $rest, $curfile, $tempfile);
+
+ # These are global values.
+ undef (%citelist); # undefined citations
+ undef (%reflist); # undefined references
+ undef (%fontlist); # NFSS substituted fonts
+ undef (%refmultlist); # multiply defined references
+ undef (%WARNINGS); # Unrecognized warning messages
+
+ &init_parse_log_filenames();
+
+ if (-r $logfile) {
+ &percent_done ($logfile);
+ open (LOGFILE, $logfile);
+ while (<LOGFILE>) {
+ chop;
+ &percent_done ();
+
+ $warning = 0;
+
+ ($paren, $rest, $tempfile) = &parse_log_filenames($_);
+ while ($paren eq "(" || $paren eq ")") {
+ $curfile = $tempfile;
+ printf("Adding dependency for `%s'.\n", $curfile)
+ if !defined($DEPENDSON{$curfile}) && $VERBOSE;
+ $DEPENDSON{$curfile} = 1;
+ ($paren, $rest, $tempfile) = &parse_log_filenames($rest);
+ }
+
+ if (/LaTeX Warning: Citation/) {
+ $newbib = 1;
+ ($citation = $_) =~ s/.*`(.*)'.*/$1/; # '`
+ $citelist{$citation} = 1;
+ $warning = 1;
+ }
+
+ if (/LaTeX Warning: Reference/) {
+ ($reference = $_) =~ s/.*\`(.*)\'.*/$1/;
+ $reflist{$reference} = 1;
+ $badref = 1;
+ $warning = 1;
+ }
+
+ if (/LaTeX Warning: Label `(.*)' multiply defined/) { # '`
+ $refmultlist{$1} = 1;
+ $badref = 1;
+ $warning = 1;
+ }
+
+ if (/Warning: Font\/shape.*undefined/) {
+ ($font = $_) =~ s/.*\`(\S+)\s*\'.*/$1/;
+ $fontlist{$font} = 1;
+ $badfont = 1;
+ $warning = 1;
+ }
+
+ if (/Warning: Font\/shape.*not available/) {
+ ($font = $_) =~ s/.*\`(\S+)\s*\'.*/$1/;
+ $fontlist{$font} = 1;
+ $badfont = 1;
+ $warning = 1;
+ }
+
+ if (/LaTeX Warning: Label\(s\) may have changed/) {
+ $newref = 1;
+ $warning = 1;
+ }
+
+ # LaTeX2e
+ if (/LaTeX Warning: There are unresolved references./) {
+ $warning = 1;
+ }
+
+ if (/LaTeX Warning: Marginpar on page .* moved./
+ || /Warning: Using `.*' instead on input line [0-9]+./ # '`
+ || /Warning: Using external font `.*' instead on input/ # '`
+ || /Warning: Substituting `.*' instead on input/) { # '`
+ $warning = 1; # don't bother to show these warnings.
+ }
+
+ if ((/Warning:/ && !$warning)
+ || /NFSS Info: \*\*\*/) {
+ $WARNINGS{$_} = $curfile;
+ }
+
+ if (/No file .*\.bbl\./) {
+ $misbib = 1;
+ }
+
+ # see if the file should have TOC, LOF, and LOT files...
+ $hastoc = 1 if /tf\@toc/;
+ $haslof = 1 if /tf\@lof/;
+ $haslot = 1 if /tf\@lot/;
+ }
+ close (LOGFILE);
+ }
+
+ if (-r $auxfile) {
+ open (AUXFILE, $auxfile);
+ while (<AUXFILE>) {
+ chop;
+
+ if (/\\bibstyle\{(.+)\}/) {
+ local ($name, $fullname);
+ $name = $1 . ".bst";
+ $fullname = &find_on_path($ENV{"$ENV_TEXINPUTS"}, $name);
+ if ($fullname ne ""
+ && -r $fullname
+ && !defined($DEPENDSON{$fullname})) {
+ printf("Adding dependency for `%s'.\n", $fullname)
+ if !defined($DEPENDSON{$fullname}) && $VERBOSE;
+ $DEPENDSON{$fullname} = 1;
+ }
+ }
+
+ if (/\\bibdata\{(.+)\}/) {
+ local (@filelist, $fullname, $name);
+
+ @filelist = split(/,/, $1);
+ while (($name = shift @filelist)) {
+ $name = $name . ".bib";
+ $fullname = &find_on_path($ENV{"$ENV_BIBINPUTS"}, $name);
+ if ($fullname ne ""
+ && -r $fullname
+ && !defined($DEPENDSON{$fullname})) {
+ printf("Adding dependency for `%s'.\n", $fullname)
+ if !defined($DEPENDSON{$fullname}) && $VERBOSE;
+ $DEPENDSON{$fullname} = 1;
+ }
+ }
+ }
+ }
+ close (AUXFILE);
+ }
+
+ ($newbib, $misbib, $newref, $badref, $badfont, $hastoc, $haslof, $haslot);
+}
+
+########################################################################
+# Look through the log file for the Texinfo warnings.
+#
+sub parse_texinfo_log {
+ local ($newref, $badref) = (0, 0);
+ local ($logfile) = &auxfile("log");
+ local ($ref);
+ local ($paren, $rest, $tempfile, $curfile);
+
+ undef (%reflist); # undefined references
+
+ &init_parse_log_filenames();
+
+ if (-r $logfile) {
+ &percent_done ($logfile);
+ open (LOGFILE, $logfile);
+ while (<LOGFILE>) {
+ chop;
+ &percent_done ();
+
+ ($paren, $rest, $tempfile) = &parse_log_filenames($_);
+ while ($paren eq "(" || $paren eq ")") {
+ $curfile = $tempfile;
+ printf("Adding dependency for `%s'.\n", $curfile)
+ if !defined($DEPENDSON{$curfile}) && $VERBOSE;
+ $DEPENDSON{$curfile} = 1;
+ ($paren, $rest, $tempfile) = &parse_log_filenames($rest);
+ }
+
+ if (/Undefined cross reference \`(.*)\'./) {
+ $badref = 1;
+ $ref = $1;
+ $ref = $1 if $ref =~ /(.*)-[^\-]*/;
+ $reflist{$ref} = 1;
+ }
+
+ $newref = 1
+ if /Cross reference values unknown; you must run TeX again./;
+ }
+ close (LOGFILE);
+ }
+
+ ($newref, $badref);
+}
+
+########################################################################
+# This routine aids in the parsing of log files be locating filenames
+# within the log. Filenames included in a document always appear as
+# '(filename...' in the log.
+#
+# NOTE: if unbalanced parenthesis occur in the log file (because someone
+# printed them in a \typeout command or some such), all bets are
+# off!
+#
+# FURTHER NOTE: this routine returns the top of the filename stack
+# whenever a close paren is seen, so you will get the
+# same files more than once...
+#
+# This routine uses the global variables %ParenLevel and %Parens which
+# it initializes appropriately...
+#
+# **********************************************************************
+# THIS ROUTINE IS NOT RE-ENTRANT! IT REQUIRES STATE SAVED ACROSS CALLS!
+# **********************************************************************
+#
+sub init_parse_log_filenames {
+ %Parens = ();
+ $ParenLevel = 0;
+}
+
+sub parse_log_filenames {
+ local ($rest) = @_;
+ local ($paren, $curfile);
+ local (@result) = ();
+
+ undef ($curfile);
+
+ while ($rest =~ /^[^()]*(\(|\))(.*)/) {
+ $paren = $1;
+ $rest = $2;
+
+ ($curfile = $rest) =~ s/\s*(\S+)/$1/; # leading whitesp
+ $curfile =~ s/^([^() ]+).*/$1/; # trailing ( and )
+
+ if ($paren eq "(") {
+ $ParenLevel++;
+ if (-r $curfile) {
+ $Parens{$ParenLevel} = $curfile;
+ } else {
+ $Parens{$ParenLevel} = '*';
+ }
+ } else {
+ $ParenLevel--;
+ $curfile = $Parens{$ParenLevel};
+ }
+
+ last if -r $curfile;
+
+ undef ($curfile);
+ }
+
+ if ($curfile) {
+ @result = ("$paren", "$rest", "$curfile");
+ }
+
+ @result;
+}
+
+########################################################################
+# This routine is called after each run of TeX to see if TeX needs to
+# be run again. TeX needs to be run twice when documents contain
+# cross references, lists of tables, or other things which cannot be
+# completely determined in a single pass. Sometimes TeX needs to be
+# run three times.
+#
+# Since different formats print different messages in the log file,
+# there is no universal way to determine if TeX needs to be run again.
+#
+# This routine calls "&parse_FORMAT_log()" to extract information from
+# the log file and "&interpret_FORMAT_log()" to decide what to do with
+# the information. "&parse_FORMAT_log()" should return a list, this
+# list is passed to "&interpret_FORMAT_log()".
+#
+# FORMAT = $parsename{$TEXFORMAT}.
+#
+# Currently only "default" and "latex" FORMATs exist. The default
+# format just looks to see if the DVI file still exists or if the
+# master file is newer than the DVI file.
+#
+# The latex format does a lot more.
+#
+# If you write new &parse_FORMAT_log()/&interpret_FORMAT_log() routines,
+# remember to do the same checking that &interpret_default_log() does
+# and remember to run the users PLC commands at an appropriate place.
+# Copying and modifying the latex versions is probably the best way
+# to start.
+#
+sub dependencies_changed {
+ local (@parselist, $sub);
+
+ $changed = 0;
+
+ eval "\@parselist = &parse_$parsename{$TEXFORMAT}_log();";
+ eval "\$changed = &interpret_$parsename{$TEXFORMAT}_log(\@parselist);";
+
+ $changed;
+}
+
+########################################################################
+# Called by the interpret_FORMAT_routines, this function returns
+# true if the dvi file is missing or the master document is more
+# recent than the dvi file...
+#
+sub document_is_newer {
+ local ($changed) = 0;
+
+ if (! -r &auxfile("dvi")) {
+ print &auxfile("dvi"), " is missing.\n" if $VERBOSE;
+ $changed = 1;
+ } elsif (&mtime(&auxfile("dvi")) < &mtime($texmaster)) {
+ print "$texmaster is newer than ", &auxfile("dvi"), ".\n"
+ if $VERBOSE;
+ $changed = 1;
+ }
+
+ $changed;
+}
+
+########################################################################
+# Called by the interpret_FORMAT_routines, this function runs the
+# @default_rules, @FORMAT_rules, and user rules...
+# Input: $changed
+# Output: new value of $changed
+#
+sub run_rules {
+ local ($changed) = @_;
+ local ($line, @the_rules);
+
+ @the_rules = @default_rules;
+ foreach $line (@the_rules) {
+ print "Rule: $line\n" if $VERBOSE;
+ eval $line;
+ die "Invalid rule: $line\n" if $@;
+ }
+
+ eval "\@the_rules = \@${TEXFORMAT}_rules";
+ foreach $line (@the_rules) {
+ print "Rule: $line\n" if $VERBOSE;
+ eval $line;
+ die "Invalid rule: $line\n" if $@;
+ }
+
+ foreach $line (@PLCRULES) {
+ print "Rule: $line\n" if $VERBOSE;
+ eval $line;
+ die "Invalid rule: $line\n" if $@;
+ }
+
+ $changed;
+}
+
+########################################################################
+# Input: the list of values returned by parse_default_log().
+# Output: prints informative messages.
+# Returns: 1 if TeX should be run again...
+#
+sub interpret_default_log {
+ local ($changed) = 0;
+
+ $changed = &document_is_newer();
+ $changed = &run_rules($changed);
+ $changed = 1 if &dependent_files_are_newer();
+
+ print "\nAs far as I can tell, the document is up to date.\n"
+ if !$changed;
+
+ $changed;
+}
+
+########################################################################
+# Input: the list of values returned by parse_latex_log().
+# Output: prints informative messages.
+# Returns: 1 if TeX should be run again...
+#
+sub interpret_latex_log {
+ local ($newbib, $misbib, $newref, $badref, $badfont,
+ $hastoc, $haslof, $haslot) = @_;
+ local ($changed) = 0;
+ local ($warning);
+
+ $changed = &document_is_newer();
+
+ # If the document should have TOC, LOF, or LOT files but they
+ # don't actually exist, then the document has to be TeX'd
+ # again.
+ if (($hastoc && ! -r &auxfile("toc"))
+ || ($haslof && ! -r &auxfile("lof"))
+ || ($haslot && ! -r &auxfile("lot"))) {
+ $changed = 1;
+ print "Table of contents disappeared.\n"
+ if $hastoc && ! -r &auxfile("toc");
+ print "List of figures disappeared.\n"
+ if $haslof && ! -r &auxfile("lof");
+ print "List of tables disappeared.\n"
+ if $haslot && ! -r &auxfile("lot");
+ }
+
+ # If there are unknown citations...
+ if ($newbib) {
+ local ($wasdiff) = 0;
+
+ &print_long_list ("Missing citations: ", keys(%citelist));
+
+ # calculate the intersection between the currently missing
+ # citations and the ones that were missing last time. If they
+ # are different, run BibTeX. Since missingcites begins empty,
+ # this will force BibTeX to be run at least once if there are
+ # any missing citations.
+ {
+ local (%mark, @diff, $_);
+ grep($mark{$_}++, keys(%missingcites));
+ @diff = grep(!$mark{$_}, keys(%citelist));
+ if (@diff) {
+ $rc = &bibtexcmd ("");
+ print "\n";
+ $changed = 1;
+ $wasdiff = 1;
+ }
+ }
+
+ if (%missingcites) {
+ print "Bibliography database(s) may be incomplete.\n";
+ if (! $wasdiff) {
+ print "The same citations are missing. ",
+ "Running BibTeX again won't help.\n";
+ }
+ }
+ print "\n";
+
+ %missingcites = %citelist;
+ }
+
+ if ($misbib && ! $newbib) {
+ $rc = &bibtexcmd("");
+ print "\n";
+ }
+
+ # If there are unknown references...
+ if ($badref) {
+ if (%reflist) {
+ &print_long_list ("Missing references: ",
+ keys(%reflist));
+ print "\n";
+ }
+ if (%refmultlist) {
+ &print_long_list ("Multiply defined references: ",
+ keys(%refmultlist));
+ print "\n";
+ # Retry once if the error is found on the first pass
+ # because the error persists in the AUX file through
+ # one run after the user fixes the problem.
+ $newref = 0 if $runcount > 1;
+ }
+ }
+
+ # Just FYI about substituted fonts...
+ if ($badfont) {
+ &print_long_list ("FYI: substituted fonts: ",
+ keys(%fontlist));
+ print "\n";
+ }
+
+ # If there are Warnings that TeXit doesn't understand...
+ if (%WARNINGS) {
+ print "\nThe following (unrecognized) warnings appear ",
+ "in the log file:\n";
+ foreach $warning (keys %WARNINGS) {
+ if ($warning =~ /input line ([0-9]+)./) {
+ printf "%s: %d: %s\n", $WARNINGS{$warning}, $1, $warning;
+ } else {
+ print $WARNINGS{$warning}, ": ", $warning, "\n";
+ }
+ }
+ }
+
+ # The file has changed if there are new references...
+ $changed = $changed || $newref;
+
+ $changed = &run_rules($changed);
+ $changed = 1 if &dependent_files_are_newer();
+
+ if (!$changed) {
+ if ($newbib || $badref) {
+ print "\nThe document is still incorrect, ";
+ print "but TeXing again won't help.\n";
+ print "Some citations cannot be found. "
+ if $newbib;
+ print "Some references cannot be found or cannot be resolved."
+ if $badref;
+ print "\n\n";
+ } else {
+ print "\nAs far as I can tell, the document is up to date.\n";
+ }
+ } else {
+ print "\n";
+ }
+
+ $changed;
+}
+
+########################################################################
+# Input: the list of values returned by parse_texinfo_log().
+# Output: prints informative messages.
+# Returns: 1 if TeX should be run again...
+#
+sub interpret_texinfo_log {
+ local ($newref, $badref) = @_;
+ local ($changed) = 0;
+
+ # If there are unknown references...
+ if ($badref) {
+ if (%reflist) {
+ print "\n";
+ &print_long_list ("Missing references: ",
+ keys(%reflist));
+ print "\n";
+ }
+ }
+
+ $changed = &document_is_newer() || $newref;
+ $changed = &run_rules($changed);
+ $changed = 1 if &dependent_files_are_newer();
+
+ if (!$changed) {
+ if ($badref) {
+ print "The document is still incorrect, ";
+ print "but TeXing again won't help.\n";
+ print "Some references cannot be found or cannot be resolved."
+ if $badref;
+ print "\n\n";
+ } else {
+ print "As far as I can tell, the document is up to date.\n";
+ }
+ }
+
+ $changed;
+}
+
+########################################################################
+# Input: an extension, `ext'
+# Output: the filename $TEXFILEPATH . $TEXFILENAME . `.ext'
+# Note: auxilliary files are always placed in the current directory,
+# the path is ignored. This is a change from v1.27 to be more
+# compatible with TeX.
+#
+sub auxfile {
+ local($ext) = @_;
+ local($dot);
+
+ $dot = "";
+ $dot = "." if $ext ne "" && $ext !~ /^\./;
+
+ $TEXFILENAME . $dot . $ext;
+}
+
+########################################################################
+# Input: none
+# Output: true if any file in %DEPENDSON is newer than DVI file
+#
+sub dependent_files_are_newer {
+ local($file, $rerun);
+
+ $rerun = 0;
+ if (-r &auxfile("dvi")) {
+ foreach $file (keys %DEPENDSON) {
+ if (-r $file) {
+ if (&mtime(&auxfile("dvi")) < &mtime($file)) {
+ print "$file newer than DVI file? Yes.\n"
+ if $VERBOSE;
+ $rerun = 1;
+ } else {
+ print "$file newer than DVI file? No.\n"
+ if $VERBOSE;
+ }
+ } else {
+ print "$file newer than DVI file? No (doesn't exist).\n"
+ if $VERBOSE;
+ }
+ }
+ }
+
+ $rerun;
+}
+
+########################################################################
+# Input: list of files
+# Output: None. Sets the %DEPENDSON list...
+#
+sub dependson {
+ local(@filelist) = @_;
+ local($file);
+
+ foreach $file (@filelist) {
+ $DEPENDSON{$file} = 1;
+ }
+}
+
+########################################################################
+# Input: list of files
+# Output: true if any file is missing
+#
+sub missing {
+ local(@filelist) = @_;
+ local($missing, $file);
+
+ $missing = 0;
+
+ foreach $file (@filelist) {
+ if (defined($ifmissing{$file})) {
+ if (! -r $file) {
+ print "Missing: $file? Yes.\n" if $VERBOSE;
+ $missing = 1;
+ } else {
+ print "Missing: $file? No.\n" if $VERBOSE;
+ }
+ } else {
+ $ifmissing{$file} = 1;
+ }
+ }
+
+ $missing && (!$first_time);
+}
+
+########################################################################
+# Input: list of files
+# Output: waits until they all exist (and are readable)
+#
+sub waitfor {
+ local(@filelist) = @_;
+ local(@missing, $file);
+
+ @missing = ();
+
+ foreach $file (@filelist) {
+ push (@missing, $file) if (! -r $file);
+ }
+
+ if ($VERBOSE && @missing) {
+ &print_long_list ("\nWaiting for: ",@filelist);
+ print "\n";
+ }
+
+ foreach $file (@missing) {
+ while (! -r $file) {
+ sleep 1;
+ }
+ }
+}
+
+########################################################################
+# Input: list of files
+# Output: true if any file has changed. A file has changed if its
+# checksum is different from the preceding call. Files that
+# are missing both times are currently the same.
+#
+sub changed {
+ local(@filelist) = @_;
+ local($changed, $savecheck, $file);
+
+ $changed = 0;
+
+ foreach $file (@filelist) {
+ if (defined($ifchanged{$file})) {
+ $savecheck = &checksum($file);
+ if ($ifchanged{$file} != $savecheck) {
+ print "Changed: $file? Yes.\n" if $VERBOSE;
+ $ifchanged{$file} = $savecheck;
+ $changed = 1;
+ } else {
+ print "Changed: $file? No.\n" if $VERBOSE;
+ }
+ } else {
+ $ifchanged{$file} = &checksum($file);
+ }
+ }
+
+ $changed && (!$first_time);
+}
+
+########################################################################
+# Calculate a checksum for a file.
+#
+sub checksum {
+ local($file) = @_;
+ local($/, $checksum);
+
+ undef $/;
+
+ if (-r $file) {
+ open (CHECKSUM, $file);
+ $checksum = unpack("%32C*", <CHECKSUM>);
+ close (CHECKSUM);
+ } else {
+ $checksum = "none";
+ }
+
+ $checksum;
+}
+
+########################################################################
+# Input: two lists of files. The lists must be quoted strings so that
+# they can be seperated easily. The two strings are split on
+# "/, /" so they _can_ include multiple files.
+# Output: true if _any_ of the first list of files is newer than _any_
+# of the second list of files _or_ any of the second list of
+# files is missing.
+#
+sub newer {
+ local ($newfile, $oldfile) = @_;
+ local (@newfiles,@oldfiles);
+ local ($file, $missing, $newer);
+
+ @newfiles = split(/[, ]/, $newfile);
+ @oldfiles = split(/[, ]/, $oldfile);
+
+ die "Bad call to &newer(). No files in `new' list.\n"
+ if !@newfiles;
+ die "Bad call to &newer(). No files in `old' list.\n"
+ if !@oldfiles;
+
+ $newer = 0;
+
+ # Find the oldest old file. All must exist.
+ undef $oldfile;
+ foreach $file (@oldfiles) {
+ $newer = 1, $missing = $file, last if ! -r $file;
+ $oldfile = $file
+ if (! $oldfile || (&mtime($file) < &mtime($oldfile)));
+ }
+
+ # Find the newest new file.
+ undef $newfile;
+ foreach $file (@newfiles) {
+ $newfile = $file
+ if (-r $file
+ && (! $newfile || (&mtime($file) > &mtime($newfile))));
+ }
+
+ warn "None of the `new' files exist: @newfiles\n"
+ if ! $newfile;
+
+ $newer = $newer
+ || ($newfile && (&mtime($newfile) > &mtime($oldfile)));
+
+ if ($newer) {
+ if ($oldfile) {
+ print "$newfile newer than $oldfile? Yes.\n"
+ if $VERBOSE;
+ } else {
+ print "$missing doesn't exist, so $newfile is newer.\n"
+ if $VERBOSE;
+ }
+ } else {
+ print "$newfile newer than $oldfile? No.\n"
+ if $VERBOSE;
+ }
+
+ $newer && (!$first_time);
+}
+
+########################################################################
+# This routine prints a message followed by a potentially long list of
+# items, seperated by spaces. It will never allow "word wrap" to occur
+# in the middle of a word. There has to be a better way, using Perl's
+# report generation to do this, but I haven't looked yet.
+#
+sub print_long_list {
+ local ($message,@thelist) = @_;
+ local ($line) = $message;
+ local ($item, $displaystring) = ("", "");
+
+ foreach $item (@thelist) {
+ if (length($line . $item) < 73) {
+ $line .= $item . ", ";
+ } else {
+ $displaystring .= $line . "\n";
+ $line = $item . ", ";
+ }
+ }
+
+ $line =~ s/(.*),\s*$/$1/; # remove the last ", "...
+ $displaystring .= $line . "\n";
+
+ print $displaystring;
+}
+
+########################################################################
+# Shows the current status of TeXit. This is kindof a debugging option
+# and kindof a 'satisfy my curiousity' option. It isn't really very
+# complete yet.
+sub showstatus {
+ local($opts) = @_;
+
+ print "\n";
+
+ if ($opts eq "?" || $opts eq "") {
+ print "Status options:\n";
+ print " rules = rules\n";
+ print " dep = document dependencies\n";
+ print " <var> = value of Perl variable <var>\n";
+ } elsif ($opts eq "rules") {
+ local ($line, @the_rules);
+
+ @the_rules = @default_rules;
+ foreach $line (@the_rules) {
+ print "Default rule: $line\n";
+ }
+
+ eval "\@the_rules = \@${TEXFORMAT}_rules";
+ foreach $line (@the_rules) {
+ print "$TEXFORMAT rule: $line\n";
+ }
+
+ foreach $line (@PLCRULES) {
+ print "Document rule: $line\n";
+ }
+ } elsif ($opts eq "dep") {
+ local ($line);
+
+ print "This document depends on the following files:\n";
+ foreach $line (keys %DEPENDSON) {
+ print "\t$line\n";
+ }
+ } else {
+ local ($arg, $svalue, %pvalue, @avalue, $line, $isdef);
+
+ $isdef = 0;
+
+ eval "\$arg = defined(\$$opts);";
+ if ($arg) {
+ eval "\$svalue = \$$opts;";
+ print "\$$opts = $svalue\n";
+ $isdef = 1;
+ }
+
+ eval "\$arg = defined(\%$opts);";
+ if ($arg) {
+ print "\%$opts:\n";
+ eval "\%pvalue = \%$opts;";
+ foreach $line (keys %pvalue) {
+ print "$line = $pvalue{$line}\n";
+ }
+ $isdef = 1;
+ }
+
+ eval "\$arg = defined(\@$opts);";
+ if ($arg) {
+ print "\@$opts:\n";
+ eval "\@avalue = \@$opts;";
+ foreach $line (@avalue) {
+ print "$line\n";
+ }
+ $isdef = 1;
+ }
+
+ if (! $isdef) {
+ print "$opts is undefined.\n";
+ }
+ }
+}
+
+########################################################################
+# Setup the $format{}, $formatname{}, and $parsename{} associative
+# arrays for finding the format file name, printable name, and parser
+# name for each format.
+sub setup_format {
+ local ($common_name, $printable_name,
+ $parser_name, $format_name, $alt_inputenv) = @_;
+
+ $format{$common_name} = $format_name;
+ $formatname{$common_name} = $printable_name;
+ $parsename{$common_name} = $parser_name;
+ $altinputenv{$common_name} = $alt_inputenv;
+}
+
+########################################################################
+# Builds an associative array for a pascal-like case statement. This
+# is a bit obscure and relies on Perl's ability to evaluate strings
+# that look like code at runtime.
+sub case {
+ local(*assoc,$_) = @_;
+ for (split(/\n/)) {
+ /^\s*(\S+)\s+(.*)/;
+ for (eval $1) {
+ $assoc{$_} = $2;
+ }
+ }
+}
+
+########################################################################
+# Return Perl version info...
+#
+sub perl_version_info {
+ local($savestar) = $*;
+
+ $* = 1;
+
+ $] =~ /^.*Revision: ([\d.]+) .*\nPatch level: (\d+).*$/;
+ $revision = $1;
+ $patchlevel = $2;
+
+ $* = $savestar;
+
+ ($revision, $patchlevel);
+}
+
+########################################################################
+# This hack gets around problems with MS-DOS and OS/2 ports of Perl.
+# Some ports get confused when presented with "require d:/path/file"
+# because they think "d:/path/file" should appear somewhere in @INC
+# because it doesn't begin with a slash. Sigh. This code isn't as
+# nice as "require" but it works...
+#
+sub require_hack {
+ local($file) = @_;
+ local($line);
+
+ print "Loading init file: $file\n" if $VERBOSE;
+
+ $line = "";
+ open (INIT, $file);
+ while (<INIT>) {
+ $line = $line . $_;
+ if (/;\s*$/) {
+ eval $line;
+ die "Error in init file: $file at line $..\n" if $@;
+ $line = "";
+ }
+ }
+ close (INIT);
+
+ die "Missing \";\" at end of init file: $file.\n"
+ if ($line !~ /^\s*$/);
+}
+
+########################################################################
+# This hack gets around problems with (at least) the OS/2 port of Perl.
+# The OS/2 port (maybe the MS-DOS port?) have one _hell_ of an annoying
+# bug. The test "-M file" returns a large positive number instead of
+# a negative number for files that are newer than the start-time of the
+# script (someone probably did an unsigned computation where a signed
+# computation was required...) The result is that -M file1 > -M file2
+# if file2 existed when the script began and file1 did not. Really
+# bad.
+#
+# Caveat, on a working system:
+#
+# -M file1 > -M file2 ==> &mtime(file1) < &mtime(file2)
+#
+sub mtime {
+ local ($file) = @_;
+ local ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
+ $atime, $mtime, $ctime, $blksize, $blocks);
+
+ if (-r $file) {
+ ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
+ $atime, $mtime, $ctime, $blksize, $blocks) = stat($file);
+ } else {
+ $mtime = -1;
+ }
+
+ $mtime;
+}
+
+########################################################################
+# This helpful little routine locates a file on a path. The path can
+# be ":" or ";" delimited. If the file is found, it's fully qualified
+# name is returned, otherwise the null string is returned. If the
+# input path contains "/" or "\" then either it is returned (if the file
+# specified exists) or the empty string is returned, the path _is not_
+# searched.
+#
+sub find_on_path {
+ local($path, $file) = @_;
+ local($dir, $filename);
+
+ $filename = "";
+
+ if ($file =~ /\/|\\/) {
+ $filename = $file if -e $file;
+ } else {
+ foreach $dir (split(/;|:/,$path)) {
+ $dir =~ s/\\/\//g;
+ $filename = $dir . "/" . $file;
+ last if -e $filename;
+ $filename = "";
+ }
+ }
+ $filename;
+}
+
+########################################################################
+# Sometimes it is helpful to have a .texitrc file (especially in a
+# working directory) update the paths used by TeX. These two routines
+# are a big help...
+#
+sub del_from_path {
+ local($path,$dir) = @_;
+ local(@paths,$outpath,$curdir,$place);
+
+ @paths = split(/$PATHSEP/,$path);
+
+ $outpath = "";
+ for ($place = 0; $place <= $#paths; $place++) {
+ $curdir = @paths[$place];
+ $outpath = sprintf("%s$PATHSEP%s", $outpath, $curdir)
+ if $curdir && $curdir ne $dir;
+ }
+
+ $outpath = $1 if $outpath =~ /^$PATHSEP+([^$PATHSEP].*)$/;
+ $outpath = $1 if $outpath =~ /^(.*[^$PATHSEP])$PATHSEP+$/;
+
+ $outpath;
+}
+
+sub add_to_path {
+ local($path,$dir,$front) = @_;
+ local(@paths,$outpath,$curdir,$place);
+
+ $path = &del_from_path($path,$dir); # remove it if it's already there
+
+ if ($front) {
+ $path = $dir . $PATHSEP . $path;
+ } else {
+ $path = $path . $PATHSEP . $dir;
+ }
+
+ $path;
+}
+
+########################################################################
+# For texit running under X11, this routine changes the title to reflect
+# what's going on now...works for the iconized xterm too!
+#
+sub set_xterm_title {
+ local ($title) = @_;
+
+ print STDOUT "]2;$title" if $SETXTITLE;
+}
+
+sub set_xicon_title {
+ local ($title) = @_;
+
+ print STDOUT "]1;$title" if $SETXTITLE;
+}
+
+sub set_x_titles {
+ local ($title) = @_;
+ &set_xterm_title ($title);
+ &set_xicon_title ($title);
+}
+
+########################################################################
+# I'm sure this could be improved...but real docs should be written
+# anyway so there's no need to go overboard here. That's my excuse,
+# in any event.
+#
+sub display_usage {
+ local (@formatlist, $item);
+
+ print "#" x 72, "\n";
+ print <<'EOF';
+Usage: texit <options> texfile[.tex]
+Where <options> may be:
+ -<format> process `texfile' using the named format.
+ -once process document once and exit. Returns 0
+ if the document seems ok after processing.
+ -clean suppress prompt message when cleaning up files.
+ -menu skip the initial run of TeX and go straight to the menu.
+ -verbose display additional status messages.
+ -initfile init load the file "init" during initialization.
+ -tex force an initial run of TeX, even if it doesn't
+ seem necessary.
+ -bibtex start by running BibTeX (instead of TeX).
+ -makeindex start by running MakeIndex.
+
+TeXit tries to figure out the format of your document, so you don't
+have to specify it. Of course, you have to specify it if TeXit guesses
+wrong. Better yet, stick in an Emacs-style local var section to define
+the format or a "% Format: format" line at the top of your document.
+
+You only have to specify enough of the option string to make it
+unambiguous (e.g. "-v" is good enough for -verbose).
+
+EOF
+
+ foreach $item (keys %format) {
+ unshift(@formatlist, $item) if !defined($casefoo{$item});
+ }
+
+ &print_long_list("TeXit recognizes the following format names: ",
+ sort(@formatlist));
+ print "#" x 72, "\n";
+ exit 2;
+}
+