#! /usr/local/bin/perl -- # -*-Perl-*- ######################################################################## # TeXit: a Perl script for running TeX. # # Copyright (C) 1993 by Norman Walsh. # # 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__log" routine gets called # format-name : What is the '&' 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__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_" # If the file . 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 - 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 \").\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 (); $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 (); chop; $doit = 0 if !/^y/i; } if ($doit) { if ($PRINTQUERY{$queue} ne "") { print $PRINTQUERY{$queue}; $_ = scalar (); $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 (); 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_". 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 (); 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(); 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 () { 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 () { 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 () { 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 () { 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 () { 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 () { 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*", ); 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 " = value of Perl variable \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 () { $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 texfile[.tex] Where may be: - 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; }