#! /usr/bin/perl # feynmf.pl -- FeynMF driver for UNIX systems # Copyright (C) 1996 by Thorsten.Ohl@Physik.TH-Darmstadt.de # $Id: feynmf.pl,v 1.5 1996/12/02 01:38:45 ohl Exp $ # # Feynmf 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, or (at your option) # any later version. # # Feynmf 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. # ######################################################################## =head1 NAME B - Process B files using B =head1 SYNOPSIS B [B<-hvqncfT>] [B<-t> I [B<-t> I ...]] [B<-m> I] I [I ...] B [B<--help>] [B<--version>] [B<--quiet>] [B<--noexec>] [B<--clean>] [B<--force>] [B<--notfm>] [B<--tfm> I [B<--tfm> I ...]] [B<--mode> I] I [I ...] =head1 DESCRIPTION The most complicated part of using the B style appears to be the proper invocation of B. The B script provides a convenient front end and will automagically invoke B with the proper mode and magnifincation. It will also avoid cluttering system font directories and offers an option to clean them. =head1 OPTIONS =over 4 =item B<-h>, B<--help> Print a short help text. =item B<-v>, B<--version> Print the version of B. =item B<-q>, B<--quiet> Don't echo the commands being executed. =item B<-n>, B<--noexec> Don't execute B or B. =item B<-c>, B<--clean> Offer to delete font files that have accidentally been placed in a system directory by the B and B scripts (these scripts are run by B (and B) in the background). This option has only been tested with recent versions of UNIX TeX. =item B<-f>, B<--force> Don't ask any questions. =item B<-T>, B<--notfm> Don't try to prepare fake C<.tfm> files for the first run. =item B<-t>, B<--tfm> I Don't try guess the names of the C<.tfm> files to fake for the first run and use the given name(s) instead. This option can be useful if our incomplete parsing of the LaTeX input files fails. =item -B I, B<--mode> I Select the METAFONT mode I. The default is guessed or C if the guess fails. =item I Main B input files. =item I ... Other LaTeX input files that are included by the main file. =back =head1 AUTHOR Thorsten Ohl =head1 BUGS The preparation of C<.tfm> files is not foolproof yet, because we can parse B files only superficially. This script has only been tested for recent B distributions of UNIX B, though it will probably work with other versions of UNIX B. The author will be grateful for portability suggestions, even concerning B operating systems, for the benefit of those users that are forced to live with DOS or Windows. =cut ######################################################################## require 5.000; # use strict; use File::Find; use Getopt::Long; ######################################################################## # # Run a program, optionally echoing to standard output. # ######################################################################## sub maybe_run { my ($cmd) = @_; print "feynmf: $cmd\n" unless $opt_quiet; system $cmd unless $opt_noexec; } sub run_latex { my ($tex) = @_; maybe_run "$latex_prog $tex"; } ######################################################################## # # Search for auxiliary programs, some of which are required for running # this script. Bail out if these don't exist. # ######################################################################## sub find_program_on_path { my ($p, $flag) = @_; my (@path, $d); @path = grep {s/^$/./; 1} (split /:/, $ENV{"PATH"}); foreach $d (@path) { return "$d/$p" if -x "$d/$p"; } if ($flag eq 'REQUIRED') { die "feynmf: fatal: Can't find $p on \$PATH\n"; } if ($flag eq 'RECOMMENDED') { warn "feynmf: warning: Can't find $p on \$PATH\n"; return; } if (defined $flag) { die "feynmf: fatal: illegal flag in find_program_on_path: $flag\n"; } return; } sub find_programs { $kpsexpand_prog = find_program_on_path 'kpsexpand', 'RECOMMENDED'; $kpsepath_prog = find_program_on_path 'kpsepath', 'RECOMMENDED'; $kpsetool_prog = find_program_on_path 'kpsetool', 'RECOMMENDED'; $gftopk_prog = find_program_on_path 'gftopk', 'RECOMMENDED'; $pltotf_prog = find_program_on_path 'pltotf', 'RECOMMENDED'; $latex_prog = find_program_on_path 'latex', 'REQUIRED'; $mf_prog = find_program_on_path 'mf', 'REQUIRED'; $dvitype_prog = find_program_on_path 'dvitype', 'REQUIRED'; } ######################################################################## # # System dependent stuff: guess search paths and Metafont mode. # ######################################################################## sub uniq { my $last = ''; my (@result, $d); foreach $d (sort @_) { push @result, $d if $d ne $last; $last = $d; } return @result; } sub path_to_list { my ($p) = @_; grep { s=^~=$1$ENV{HOME}=; s=^!!==; s=//.*$=/=; !m=^\.?$= } split (/:+/, $p); } %TEXENV = ( tex => 'TEXINPUTS', mf => 'MFINPUTS', tfm => 'TFMFONTS', pk => 'PKFONTS', gf => 'GFFONTS' ); sub guess_path { my ($type) = @_; if ($kpsetool_prog) { return path_to_list (`$kpsetool_prog -p $type` or die "feynmf: can't run $kpsetool_prog: $!\n"); } elsif ($kpsepath_prog) { return path_to_list (`$kpsepath_prog $type` or die "feynmf: can't run $kpsepath_prog: $!\n"); } elsif ($TEXENV{$type}) { if ($kpsexpand_prog) { return path_to_list (`$kpsexpand_prog '\$$TEXENV{$type}'` or die "feynmf: can't run $kpsexpand_prog: $!\n"); } else { return path_to_list ($ENV{$TEXENV{$type}}); } } else { return; } } sub guess_mode { my ($TEXMF, $maketex_site, $mode); $mode = "localfont"; if ($kpsexpand_prog) { chomp ($TEXMF = `$kpsexpand_prog '\$TEXMF'`); $maketex_site = "$TEXMF/maketex/maketex.site"; chomp ($mode = `. $maketex_site; echo \$MT_DEF_MODE`) if -r $maketex_site; } return $mode; } ######################################################################## # # Prepare empty TFM files. # ######################################################################## sub guess_tfms { my @tex = @_; my @tfm = (); my $tex; foreach $tex (@tex) { open (TEX, "$tex") or die "feynmf: can't open $tex: $!\n"; while () { if (/^[^%]*\\begin\s*\{fmffile\}\s*\{([^}]+)\}/) { push @tfm, $1; } } close (TEX); } return @tfm; } sub fake_tfms { my @tfm = @_; # Prepare a fake temporary PL file # (/dev/null won't do, because the font must not be empty): my ($pl) = "/tmp/feynmf$$.pl"; my ($tfm); $pltotf_prog or die "feynmf: fatal: pltopf programm required unless -notfm\n"; open (PL, ">$pl") or die "feynmf: can't open temporary file $pl: $!\n"; push @temporay_files, $pl; print PL <<__END_PL__; (FAMILY FEYNMF) (DESIGNSIZE R 10.0) (CHARACTER D 1 (CHARWD R 10.0) (CHARHT R 10.0)) __END_PL__ close (PL); foreach $tfm (@tfm) { maybe_run "$pltotf_prog $pl $tfm.tfm" unless -r "$tfm.tfm"; } } ######################################################################## # # Scan the current directory for Metafont sources and offer to delete # TFM, PK and GF files in the system directories that are in the way. # If the option `-force' is in effect, no questions are asked and all # suspicous files are deleted. # ######################################################################## sub clean_fonts { my (@mfs, $mfre, @junk, $j, $nqya); opendir (DIR, '.') or die "Can't open current directory: $!\n"; @mfs = grep { s/\.mf$// } readdir (DIR); closedir (DIR); if (@mfs) { @junk = (); $mfre = '^(' . join ('|', @mfs) . ')(\.|$)'; find (sub { push @junk, $File::Find::name if /$mfre/ }, grep { -r } uniq (guess_path ('tfm'), guess_path ('pk'), guess_path ('gf'))); grep { print "feynmf: $_ suspicious!\n" } @junk; $nqya = 'n'; $nqya = 'a' if $opt_force; junk: foreach $j (@junk) { if ($nqya ne 'a') { print "delete $j ? [N/q/y/a] "; chop ($nqya = ); $nqya =~ tr/A-Z/a-z/; last junk if $nqya eq 'q' } if ($nqya eq 'y' || $nqya eq 'a') { print "rm $j\n" unless $opt_quiet; if (!unlink $j) { if (-o $j) { warn "feynmf: couldn't unlink your file $j!\n"; } else { warn "feynmf: couldn't unlink foreign file $j!\n"; } } } } } } ######################################################################## # # Run Metafont on the files referenced in the DVI file $dvi: # ######################################################################## sub mf_names { my ($dvi) = @_; my (@mf, $name, $mag); open (DVI, "echo 0 | $dvitype_prog $dvi 2>/dev/null |") or die "feynmf: can't dvitype $dvi: $!\n"; while () { if (/^Font\s*\d+:\s*(\S*)\s*(scaled\s*(\d+))?\s*---\s*loaded/) { $name = $1; $mag = $3 ? $3/1000 : 1; push @mf, [$name, $mag]; } } close (DVI); return @mf; } sub run_mf { my (@mf) = @_; my ($mf, $name, $mag, $gf); for $mf (@mf) { $name = $$mf[0]; $mag = $$mf[1]; if (-r "$name.mf") { maybe_run "$mf_prog '\\mode:=$mode; mag:=$mag; input $name'"; if (!$opt_noexec) { # Check the log file for the name of the generated font # and run gftopk on it: open (LOG, "$name.log") or die "feynmf: can't open $name.log: $!\n"; while () { if (/Output written on\s+(\S+)\s+/) { $gf = $1; maybe_run "$gftopk_prog $gf" if $gftopk_prog; } } close (LOG); } } } } ######################################################################## # # Options: # ######################################################################## sub tex_names { my @in = @_; my (@out, $in); foreach $in (@in) { if ($in !~ /\.tex$/ and -r "$in.tex") { push @out, "$in.tex"; } else { push @out, $in; } } return @out; } @options = ('help|h', 'version|v', 'debug|D', 'quiet|q', 'noexec|n', 'clean|c', 'force|f', 'notfm|T', 'tfm|t=s@', 'mode|m=s'); $usage = <<__USAGE__; usage: feynmf [-hvqncfT] [-t tfm [-t tfm ...]] [-m mode] file [file ...] or: feynmf [--help] [--version] [--quiet] [--noexec] [--clean] [--force] [--notfm] [--tfm tfm [--tfm tfm ...]] [--mode mode] file [file ...] __USAGE__ $help = <<__HELP__; usage: feynmf [OPTIONS] FILE [FILE ...] OPTIONS: -h, --help: this message -v, --version: print version to standard output and exit -q, --quiet: shut up -n, --noexec: don't run LaTeX or Metafont -c, --clean: offer to delete suspicious font files -f, --force: just do it (don't ask questions) -T, --notfm: do not initialize TFM files -t tfm, --tfm name: override the guessing of TFM file names -m mode, --mode mode: override the guessing of the Metafont mode FILE: primary LaTeX file FILES: included LaTeX files __HELP__ die "$usage" unless &GetOptions(@options); die 'This is feynmf: $Id: feynmf.pl,v 1.5 1996/12/02 01:38:45 ohl Exp $' . "\n" if $opt_version; die $help if $opt_help; die "$usage" unless $ARGV[0]; ######################################################################## # # Finally, the main program: # ######################################################################## @temporay_files = (); sub END { unlink @temporay_files; } @tex = tex_names @ARGV; $tex_main = $tex[0]; ($dvi = $tex_main) =~ s/\.[^.]+$/.dvi/; find_programs; $mode = $opt_mode ? $opt_mode : guess_mode; fake_tfms (@opt_tfm ? @opt_tfm : guess_tfms @tex) unless $opt_notfm; run_latex $tex_main; clean_fonts if $opt_clean; run_mf mf_names $dvi; run_latex $tex_main; ########################################################################