summaryrefslogtreecommitdiff
path: root/support/texfot
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/texfot
Initial commit
Diffstat (limited to 'support/texfot')
-rw-r--r--support/texfot/README16
-rw-r--r--support/texfot/texfot.pdfbin0 -> 30442 bytes
-rwxr-xr-xsupport/texfot/texfot.pl419
3 files changed, 435 insertions, 0 deletions
diff --git a/support/texfot/README b/support/texfot/README
new file mode 100644
index 0000000000..05c1b5b329
--- /dev/null
+++ b/support/texfot/README
@@ -0,0 +1,16 @@
+texfot is a Perl script to filter the online output from a TeX run,
+attempting to show only those messages which (probably) deserve some
+attention in the source. The TeX invocation itself need not change.
+
+The PDF file is made from the POD documentation in the Perl source:
+ pod2man texfot.pl | groff -man -t -rS11 | ps2pdf - texfot.pdf
+
+The -t preprocesses with (g)tbl, which pod2man output might need.
+The -rS11 switches to 11pt font size.
+Supposedly it is possible to add options to either groff or ps2pdf
+(aka ghostscript) to force A4 page output, but nothing worked for me.
+
+Originally written in 2014. Released to the public domain.
+
+Author and maintainer: Karl Berry <karl@freefriends.org>
+Home page: https://ctan.org/pkg/texfot
diff --git a/support/texfot/texfot.pdf b/support/texfot/texfot.pdf
new file mode 100644
index 0000000000..be9a8b62d1
--- /dev/null
+++ b/support/texfot/texfot.pdf
Binary files differ
diff --git a/support/texfot/texfot.pl b/support/texfot/texfot.pl
new file mode 100755
index 0000000000..3915539d48
--- /dev/null
+++ b/support/texfot/texfot.pl
@@ -0,0 +1,419 @@
+#!/usr/bin/env perl
+# $Id: texfot,v 1.38 2019/06/30 22:59:39 karl Exp $
+# Invoke a TeX command, filtering all but interesting terminal output;
+# do not look at the log or check any output files.
+# Exit status is that of the subprogram.
+# Tee the complete (unfiltered) standard output and standard error to
+# (by default) /tmp/fot.
+#
+# Public domain. Originally written 2014 by Karl Berry.
+
+my $ident = '$Id: texfot,v 1.38 2019/06/30 22:59:39 karl Exp $';
+(my $prg = $0) =~ s,^.*/,,;
+select STDERR; $| = 1; # no buffering
+select STDOUT; $| = 1;
+
+use IPC::Open3; # control what happens with stderr from the child.
+use IO::File; # use new_tmpfile for that stderr.
+
+# require_order because we don't want getopt to permute anything;
+# arguments to the tex invocation must remain in order, not handled by us.
+use Getopt::Long qw(:config require_order);
+use Pod::Usage;
+
+my $opt_debug = 0;
+my @opt_ignore = ();
+my $opt_interactive = 0;
+my $opt_quiet = 0;
+my $opt_stderr = 1;
+my $opt_tee = ($ENV{"TMPDIR"} || "/tmp") . "/fot";
+my $opt_version = 0;
+my $opt_help = 0;
+
+exit (&main ());
+
+
+#
+sub main {
+ my $ret = GetOptions (
+ "debug!" => \$opt_debug,
+ "ignore=s" => \@opt_ignore,
+ "interactive!" => \$opt_interactive,
+ "quiet!" => \$opt_quiet,
+ "stderr!" => \$opt_stderr,
+ "tee=s" => \$opt_tee,
+ "version" => \$opt_version,
+ "help|?" => \$opt_help) || pod2usage (2);
+
+ # --help, --version
+ pod2usage ("-exitstatus" => 0, "-verbose" => 2)
+ if $opt_help;
+ #
+ if ($opt_version) {
+ print "$ident\n";
+ return 0;
+ }
+
+ die "$prg: missing TeX command, i.e., a non-option argument\n"
+ . "Try --help if you need it."
+ if ! @ARGV;
+
+ # guess we're going to run something. typically no interaction.
+ close (STDIN) unless $opt_interactive;
+
+ local *FOTTMP;
+ $FOTTMP = ">$opt_tee";
+ open (FOTTMP) || die "$prg: aborting, open($FOTTMP) failed: $!";
+
+ # We need to separate stderr from stdout. Otherwise they are randomly
+ # merged, not always at line breaks, hence we can lose useful messages.
+ print "$0: invoking: @ARGV\n" unless $opt_quiet;
+
+ # In order to avoid deadlock when there is lots of stuff on stderr,
+ # we must write it to a temporary file
+ # http://perldoc.perl.org/perlfaq8.html#How-can-I-capture-STDERR-from-an-external-command
+ local *TEXERR = IO::File->new_tmpfile
+ || die "IO::File->new_tmpfile failed: $!";
+
+ # But we can process stdout as it comes.
+ local *TEXOUT;
+ my $pid = open3 (undef, \*TEXOUT, ">&TEXERR", @ARGV)
+ || die "$prg: fork(TeX) failed: $! [cmd=@ARGV]\n";
+ &debug ("open3() returned pid $pid [cmd=@ARGV]");
+
+ # It's not ideal to read all of stdout and then all of stderr; it would
+ # be better to intermix them in the original order of child output.
+ # this is simpler than other ways of avoiding possible deadlock (such
+ # as select, sysread, etc.).
+ &debug ("processing stdout from child");
+ &process_output (\*TEXOUT, "");
+
+ # Be sure everything is drained.
+ &debug ("starting waitpid() for $pid") ;
+ waitpid ($pid, 0) || die "$prog: waitpid($pid) failed: $!\n";
+ my $child_exit_status = $? >> 8;
+ &debug ("child exit status = $exit_status\n");
+
+ &debug ("processing stderr from child");
+ seek (TEXERR, 0, 0) || warn "seek(stderr) failed: $!";
+ &process_output (\*TEXERR, "[stderr] ");
+ close (TEXERR) || warn "close(stderr tmpfile) failed: $!";
+
+ return $child_exit_status;
+}
+
+
+
+# Read filehandle $FH; print lines that we want to stdout, prefixed by
+# $PREFIX. If $PREFIX is null, omit lines by default; if $PREFIX is
+# non-null, print lines by default.
+#
+sub process_output {
+ my ($fh,$prefix) = @_;
+
+ my $print_next = 0;
+ LINE: while (<$fh>) {
+ my $line = $_;
+ print FOTTMP $line; # tee everything
+
+ warn "\n" if $opt_debug; # get blank line without texfot: prefix
+ &debug ("looking at line: $_");
+ &debug ("checking if have print_next (is $print_next)\n");
+ if ($print_next) {
+ &debug (" printing next ($print_next)\n");
+ print $prefix;
+ print $line;
+ $print_next = 0;
+ next;
+ }
+
+ &debug ("checking ignores\n");
+ next if /^(
+ LaTeX\ Warning:\ You\ have\ requested\ package
+ |LaTeX\ Font\ Warning:\ Some\ font\ shapes
+ |LaTeX\ Font\ Warning:\ Size\ substitutions
+ |Package\ caption\ Warning:\ Unsupported\ document\ class
+ |Package\ fixltx2e\ Warning:\ fixltx2e\ is\ not\ required
+ |Package\ frenchb?\.ldf\ Warning:\ (Figures|The\ definition)
+ |\*\*\*\ Reloading\ Xunicode\ for\ encoding # spurious ***
+ |This\ is\ `?(epsf\.tex|.*\.sty|TAP) # so what
+ |pdfTeX\ warning:.*inclusion:\ fou #nd PDF version ...
+ |pdfTeX\ warning:.*inclusion:\ mul #tiple pdfs with page group
+ |libpng\ warning:\ iCCP:\ Not\ recognizing
+ |!\ $
+ )/x;
+
+ # don't anchor user ignores, leave it up to them.
+ for my $user_ignore (@opt_ignore) {
+ &debug ("checking user ignore '$user_ignore'\n");
+ next LINE if /${user_ignore}/;
+ }
+
+ &debug ("checking for print_next\n");
+ if (/^(
+ .*?:[0-9]+: # usual file:lineno: form
+ |! # usual ! form
+ |>\ [^<] # from \show..., but not "> <img.whatever"
+ |.*pdfTeX\ warning # pdftex complaints often cross lines
+ |LaTeX\ Font\ Warning:\ Font\ shape
+ |Package\ hyperref\ Warning:\ Token\ not\ allowed
+ |removed\ on\ input\ line # hyperref
+ |Runaway\ argument
+ )/x) {
+ &debug (" found print_next ($1)\n");
+ print $prefix;
+ print $line;
+ $print_next = 1;
+ next;
+ }
+
+ &debug ("checking for showing\n");
+ if (/^(
+ This\ is
+ |Output\ written
+ |No\ pages\ of\ output
+ |\(.*end\ occurred\ inside\ a\ group
+ |(Und|Ov)erfull
+ |(LaTeX|Package|Class).*(Error|Warning)
+ |.*Citation.*undefined
+ |.*\ Error # as in \Url Error ->...
+ |Missing\ character: # good to show (need \tracinglostchars=1)
+ |\\endL.*problem # XeTeX?
+ |\*\*\*\s # *** from some packages or subprograms
+ |l\.[0-9]+\ # line number marking
+ |all\ text\ was\ ignored\ after\ line
+ |.*Fatal\ error
+ |.*for\ symbol.*on\ input\ line
+ )/x) {
+ &debug (" matched for showing ($1)\n");
+ print $prefix;
+ print $line;
+ next;
+ }
+
+ &debug ("done with all checks\n");
+
+ if ($prefix && $opt_stderr) {
+ &debug ("prefix (stderr), showing line by default: $_");
+ print $prefix;
+ print $line;
+ } else {
+ &debug ("no prefix (stdout) or no stderr, ignoring line by default: $_");
+ }
+ }
+}
+
+
+sub debug { warn ("$prg: ", @_) if $opt_debug; }
+
+
+__END__
+
+=head1 NAME
+
+texfot - run TeX, filtering online transcript for interesting messages
+
+=head1 SYNOPSIS
+
+texfot [I<option>]... I<texcmd> [I<texarg>...]
+
+=head1 DESCRIPTION
+
+C<texfot> invokes I<texcmd> with the given I<texarg> arguments,
+filtering the online output for ``interesting'' messages. Its exit
+value is that of I<texcmd>. Examples:
+
+ # Sample basic invocation:
+ texfot pdflatex file.tex
+
+ # Ordinarily all output is copied to /tmp/fot before filtering,
+ # but that can be omitted:
+ texfot --tee=/dev/null lualatex file.tex
+
+ # Example of more complex engine invocation:
+ texfot xelatex --recorder '\nonstopmode\input file'
+
+Aside from its own options, described below, C<texfot> just runs the
+given command with the given arguments (same approach to command line
+syntax as C<env>, C<nice>, C<time>, C<timeout>, etc.). Thus, C<texfot>
+works with any engine and any command line options.
+
+C<texfot> does not look at the log file or any other possible output
+file(s); it only looks at the standard output and standard error from
+the command. stdout is processed first, then stderr. Lines from stderr
+have an identifying prefix. C<texfot> writes all accepted lines to its
+stdout.
+
+The messages shown are intended to be those which likely need action by
+the author: error messages, overfull and underfull boxes, undefined
+citations, missing characters from fonts, etc.
+
+=head1 FLOW OF OPERATION
+
+Here is the order in which lines of output are checked:
+
+=over 4
+
+=item 1.
+
+If the ``next line'' needs to be printed (see below), print it.
+
+=item 2.
+
+Otherwise, if the line matches the built-in list of regexps to ignore,
+or any user-supplied list of regexps to ignore (given with C<--ignore>,
+see below), in that order, ignore it.
+
+=item 3.
+
+Otherwise, if the line matches the list of regexps for which the next
+line (two lines in all) should be shown, show this line and set the
+``next line'' flag for the next time around the loop. Examples are the
+common C<!> and C<filename:lineno:> error messages, which are generally
+followed by a line with specific detail about the error.
+
+=item 4.
+
+Otherwise, if the line matches the list of regexps to show, show it.
+
+=item 5.
+
+Otherwise, the default: if the line came from stdout, ignore it; if the
+line came from stderr, print it (to stdout). This distinction is made
+because TeX engines write relatively few messages to stderr, and it's
+likely that any such should be considered.
+
+It would be easy to add more options to allow for user additions to the
+various regex lists, if that ever seems useful. Or email me (see end).
+
+=back
+
+Once a particular check matches, the program moves on to process the
+next line.
+
+Don't hesitate to peruse the source to the script, which is essentially
+a straightforward loop matching against the different lists as above.
+You can see the exact regexps being matched in the different categories
+in the source.
+
+Incidentally, although nothing in this basic operation is specific to
+TeX engines, all the regular expressions included in the program are
+specific to TeX. So in practice the program isn't useful except with
+TeX engines, although it would be easy enough to adapt it (if there was
+anything else as verbose as TeX to make that useful).
+
+=head1 OPTIONS
+
+The following are the options to C<texfot> itself (not the TeX engine
+being invoked; consult the TeX documentation or the engine's C<--help>
+output for that).
+
+The first non-option terminates C<texfot>'s option parsing, and the
+remainder of the command line is invoked as the TeX command, without
+further parsing. For example, C<texfot --debug tex
+--debug> will output debugging information from both C<texfot> and
+C<tex>.
+
+Options may start with either - or --, and may be unambiguously
+abbreviated. It is best to use the full option name in scripts, though,
+to avoid possible collisions with new options in the future.
+
+=over 4
+
+=item C<--debug>
+
+=item C<--no-debug>
+
+Output (or not) what is being done on standard error. Off by default.
+
+=item C<--ignore> I<regexp>
+
+Ignore lines in the TeX output matching (Perl) I<regexp>. Can be
+repeated. Adds to the default set of ignore regexps rather than
+replacing. These regexps are not automatically anchored (or otherwise
+altered), simply used as-is.
+
+=item C<--interactive>
+
+=item C<--no-interactive>
+
+By default, standard input to the TeX process is closed so that TeX's
+interactive mode (waiting for input upon error, the C<*> prompt, etc.)
+is never entered. Giving C<--interactive> allows interaction to happen.
+
+=item C<--quiet>
+
+=item C<--no-quiet>
+
+By default, the TeX command being invoked is reported on standard output.
+C<--quiet> omits that reporting. To get a completely silent run,
+redirect standard output: S<C<texfot ... E<gt>/dev/null>>. (The only
+messages to standard error should be errors from C<texfot> itself, so it
+shouldn't be necessary to redirect that, but of course that can be done
+as well.)
+
+=item C<--stderr>
+
+=item C<--no-stderr>
+
+The default is for C<texfot> to report everything written to stderr by
+the TeX command (on stdout). C<--no-stderr> omits that reporting.
+(Some programs, C<dvisvgm> is one, can be rather verbose on stderr.)
+
+=item C<--tee> I<file>
+
+By default, the output being filtered is C<tee>-ed, before filtering, to
+C<$TMPDIR/fot> (C</tmp/fot> if C<TMPDIR> is not set), to make it easy to
+check the full output when the filtering seems suspect. This option
+allows specifying a different file. Use S<C<--tee /dev/null>> if you don't
+want the original output at all.
+
+=item C<--version>
+
+Output version information and exit successfully.
+
+=item C<--help>
+
+Display this help and exit successfully.
+
+=back
+
+=head1 RATIONALE
+
+I wrote this because, in my work as a TUGboat editor
+(L<http://tug.org/TUGboat>, journal submissions always welcome!), I run
+and rerun many documents, many times each. It was too easy to lose
+warnings I needed to see in the mass of unvarying and uninteresting
+output from TeX, such as style files being read and fonts being used. I
+wanted to see all and only those messages which needed some action by
+me.
+
+I found some other programs of a similar nature, the LaTeX package
+C<silence>, and plenty of other (La)TeX wrappers, but it seemed none of
+them did what I wanted. Either they read the log file (I wanted the
+online output only), or they output more or less than I wanted, or they
+required invoking TeX differently (I wanted to keep my build process
+exactly the same, most critically the TeX invocation, which can get
+complicated). Hence I wrote this.
+
+Here are some keywords if you want to explore other options:
+texloganalyser, pydflatex, logfilter, latexmk, rubber, arara, and
+searching for C<log> at L<https://ctan.org/search>.
+
+C<texfot> is written in Perl, and runs on Unix, and does not work on
+Windows.
+
+The name comes from the C<trip.fot> and C<trap.fot> files that are part
+of Knuth's trip and trap torture tests, which record the online output
+from the programs. I am not sure what "fot" stands for in trip and
+trap, but I can pretend that it stands for "filter online transcript" in
+the present S<case :).>
+
+=head1 AUTHORS AND COPYRIGHT
+
+This script and its documentation were written by Karl Berry and both
+are released to the public domain. Email C<karl@freefriends.org> with
+bug reports. It has no home page beyond the package on CTAN:
+L<https://ctan.org/pkg/texfot>.
+
+=cut