From ef4a791cfef99df6f7b9043051d3ba25255db354 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Thu, 10 Feb 2022 21:20:19 +0000 Subject: texlogsieve (10feb22) git-svn-id: svn://tug.org/texlive/trunk@61974 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/man/man1/texlogsieve.1 | 29 +- .../texmf-dist/doc/man/man1/texlogsieve.man1.pdf | Bin 40946 -> 41446 bytes .../doc/support/texlogsieve/texlogsieve.pdf | Bin 52118 -> 56978 bytes .../doc/support/texlogsieve/texlogsieve.tex | 127 ++- Master/texmf-dist/scripts/texlogsieve/texlogsieve | 909 ++++++++++++++++----- 5 files changed, 846 insertions(+), 219 deletions(-) (limited to 'Master') diff --git a/Master/texmf-dist/doc/man/man1/texlogsieve.1 b/Master/texmf-dist/doc/man/man1/texlogsieve.1 index a6656bcc0db..bd5d7b38fc5 100644 --- a/Master/texmf-dist/doc/man/man1/texlogsieve.1 +++ b/Master/texmf-dist/doc/man/man1/texlogsieve.1 @@ -1,4 +1,4 @@ -.TH TEXLOGSIEVE "1" "February 2022" "texlogsieve 1.0.0-beta-3" "User Commands" +.TH TEXLOGSIEVE "1" "February 2022" "texlogsieve 1.0.0" "User Commands" .SH NAME @@ -65,6 +65,11 @@ No messages, show only the final summary (default disabled). Enable/disable reporting shipouts (default disabled with page-delay, enabled with no-page-delay). +.TP +\fB\-\-file\-banner\fR, \fB\-\-no\-file\-banner\fR +Show/don't show the \[lq]From file...\[rq] banner messages (default +enabled, except with level DEBUG as that would be redundant and confusing). + .TP \fB\-\-repetitions\fR, \fB\-\-no\-repetitions\fR Allow/prevent repeated messages (default disabled, i.e., repeated messages @@ -103,6 +108,11 @@ Toggle \-\-box\-detail, \-\-ref\-detail, and \-\-cite\-detail at once. \fB\-\-heartbeat\fR, \fB\-\-no\-heartbeat\fR Enable/disable progress gauge in page-delay mode (default enabled). +.TP +\fB\-\-color\fR, \fB\-\-no\-color\fR +Enable/disable colored output. On Windows, this will only work with +an up-to-date Windows 10 or later (default disabled). + .TP \fB\-l\fR \fI\,LEVEL\/\fR, \fB\-\-minlevel\fR=\fI\,LEVEL\/\fR Filter out messages with severity level lower than LEVEL. Valid levels @@ -115,13 +125,13 @@ Do not filter messages and do not output the summary, only unwrap long, wrapped lines. The output should be very similar (but not equal) to the input file, but with wrapped lines reconstructed. This activates \-l debug, \-\-no\-summary, \-\-no\-page\-delay, \-\-repetitions, \-\-be\-redundant, -and \-\-shipouts, and also supresses the verbose \[lq]open/close file\[rq] -and \[lq]shipout\[rq] messages, simulating instead the TeX format, with parens -and square brackets. This is useful if you prefer the reports generated -by some other tool but want to benefit from texlogsieve's line unwrapping -algorithm; the output generated by this option should be parseable by other -tools (but you probably need to coerce the other tool not to try to unwrap -lines). +\-\-shipouts, and \-\-no\-file\-banner, and also supresses the verbose +\[lq]open/close file\[rq] and \[lq]shipout\[rq] messages, simulating +instead the TeX format, with parens and square brackets. This is useful if +you prefer the reports generated by some other tool but want to benefit +from texlogsieve's line unwrapping algorithm; the output generated by this +option should be parseable by other tools (but you probably need to coerce +the other tool not to try to unwrap lines). .TP \fB\-\-silence\-package\fR=\fI\,PKGNAME\/\fR @@ -260,7 +270,8 @@ TeX. Still, adding support to them should not be too difficult. .SH SEE ALSO -The pdf documentation (in TeXLive, try \fI\,texdoc texlogsieve\/\fR). +The pdf documentation (in TeXLive, try \fI\,texdoc texlogsieve\/\fR) +includes a \fBTIPS\fR section you may find useful. If you want to know more about the TeX log file and the workings of the program, check the initial comments in the code. diff --git a/Master/texmf-dist/doc/man/man1/texlogsieve.man1.pdf b/Master/texmf-dist/doc/man/man1/texlogsieve.man1.pdf index 0f7b7e77472..5a30d5da6f2 100644 Binary files a/Master/texmf-dist/doc/man/man1/texlogsieve.man1.pdf and b/Master/texmf-dist/doc/man/man1/texlogsieve.man1.pdf differ diff --git a/Master/texmf-dist/doc/support/texlogsieve/texlogsieve.pdf b/Master/texmf-dist/doc/support/texlogsieve/texlogsieve.pdf index bf2f2df5f9f..1301f70b662 100644 Binary files a/Master/texmf-dist/doc/support/texlogsieve/texlogsieve.pdf and b/Master/texmf-dist/doc/support/texlogsieve/texlogsieve.pdf differ diff --git a/Master/texmf-dist/doc/support/texlogsieve/texlogsieve.tex b/Master/texmf-dist/doc/support/texlogsieve/texlogsieve.tex index eb1ad528700..17d1919575e 100644 --- a/Master/texmf-dist/doc/support/texlogsieve/texlogsieve.tex +++ b/Master/texmf-dist/doc/support/texlogsieve/texlogsieve.tex @@ -39,34 +39,40 @@ \RecordChanges -\changes{v1.0.0-beta-1}{2021/12/16}{First public prerelease} -\changes{v1.0.0-beta-2}{2022/01/04}{Automatically read \texttt{texlogsieverc} +\changes{1.0.0-beta-1}{2021/12/16}{First public prerelease} +\changes{1.0.0-beta-2}{2022/01/04}{Automatically read \texttt{texlogsieverc} if it exists} -\changes{v1.0.0-beta-2}{2022/01/04}{Add options \texttt{-\/-be-redundant} +\changes{1.0.0-beta-2}{2022/01/04}{Add options \texttt{-\/-be-redundant} and \texttt{-\/-box-detail}} -\changes{v1.0.0-beta-2}{2022/01/04}{Add options +\changes{1.0.0-beta-2}{2022/01/04}{Add options \texttt{-\/-set-to-level-[levelname]}} -\changes{v1.0.0-beta-2}{2022/01/04}{Include silenced messages in summaries} -\changes{v1.0.0-beta-2}{2022/01/04}{Substitute empty citation/label keys +\changes{1.0.0-beta-2}{2022/01/04}{Include silenced messages in summaries} +\changes{1.0.0-beta-2}{2022/01/04}{Substitute empty citation/label keys for ``???''} -\changes{v1.0.0-beta-2}{2022/01/04}{Fix bug that prevented +\changes{1.0.0-beta-2}{2022/01/04}{Fix bug that prevented \texttt{-\/-add-[info\textbar warning]-message} from working} -\changes{v1.0.0-beta-2}{2022/01/04}{Misc small bugfixes} -\changes{v1.0.0-beta-3}{2022/02/02}{Add options +\changes{1.0.0-beta-2}{2022/01/04}{Misc small bugfixes} +\changes{1.0.0-beta-3}{2022/02/02}{Add options \texttt{-\/-summary-detail}, \texttt{-\/-ref-detail}, \texttt{-\/-cite-detail}} -\changes{v1.0.0-beta-3}{2022/02/02}{Abort on invalid command-line options} -\changes{v1.0.0-beta-3}{2022/02/02}{Detect ``please rerun`` messages and +\changes{1.0.0-beta-3}{2022/02/02}{Abort on invalid command-line options} +\changes{1.0.0-beta-3}{2022/02/02}{Detect ``please rerun`` messages and add them to the summary} -\changes{v1.0.0-beta-3}{2022/02/02}{Fix line unwrapping with \XeTeX} +\changes{1.0.0-beta-3}{2022/02/02}{Fix line unwrapping with \XeTeX} +\changes{1.0.0-final={1.0.0}}{2022/02/09}{Better compatibility with MiK\TeX\ for + Windows} +\changes{1.0.0-final={1.0.0}}{2022/02/09}{If possible, use the \texttt{.fls} file} +\changes{1.0.0-final={1.0.0}}{2022/02/09}{Add options \texttt{-\/-file-banner} + and \texttt{-\/-color}} +\changes{1.0.0-final={1.0.0}}{2022/02/09}{Changed the effect of filters on the summary} \begin{document} \title{\textsf{texlogsieve}:\thanks{This document -corresponds to \textsf{texlogsieve}~1.0.0-beta-3, -dated~2022-02-02.}\\[.3\baselineskip] +corresponds to \textsf{texlogsieve}~1.0.0, +dated~2022-02-09.}\\[.3\baselineskip] {\normalsize(yet another program to)\\[-.6\baselineskip]} {\large filter and summarize \LaTeX\ log files} } @@ -203,10 +209,11 @@ starting with a ``\#'' sign are comments. An example configuration file: \begin{quote} \begin{verbatim} +no-summary-detail no-page-delay # no-page-delay enables shipouts, but we do not want that no-shipouts -silence-string = Hyperreferences in rotated content will be misplaced +set-to-level-info=Hyperreferences in rotated content will be misplaced # no need to escape the "\" (or any other) character silence-string = Using \overbracket and \underbracket from `mathtools' # silence a string using lua pattern matching @@ -240,6 +247,12 @@ Enable/disable reporting shipouts (default disabled with page-delay, enabled with no-page-delay). \end{description} +\begin{description} +\item[\texttt{-\/-file-banner}, \texttt{-\/-no-file-banner}]~\\ +Show/don't show the ``From file\dots'' banner messages (default enabled, +except with level \texttt{DEBUG} as that would be redundant and confusing). +\end{description} + \begin{description} \item[\texttt{-\/-repetitions}, \texttt{-\/-no-repetitions}]~\\ Allow/prevent repeated messages (default disabled, i.e., repeated messages @@ -287,6 +300,12 @@ Toggle \texttt{-\/-box-detail}, \texttt{-\/-ref-detail}, and Enable/disable progress gauge in page-delay mode (default enabled). \end{description} +\begin{description} +\item[\texttt{-\/-color}, \texttt{-\/-no-color}]~\\ +Enable/disable colored output. On Windows, this will only work with +an up-to-date Windows 10 or later (default disabled). +\end{description} + \begin{description} \item[\texttt{-l LEVEL}, \texttt{-\/-minlevel=LEVEL}]~\\ Filter out messages with severity level lower than \texttt{LEVEL}. Valid @@ -300,9 +319,9 @@ Do not filter messages and do not output the summary, only unwrap long, wrapped lines. The output should be very similar (but not equal) to the input file, but with wrapped lines reconstructed. This activates \texttt{-l debug}, \texttt{-\/-no-summary}, \texttt{-\/-no-page-delay}, -\texttt{-\/-repetitions}, \texttt{-\/-be-redundant}, and -\texttt{-\/-shipouts}, and also supresses the verbose ``open/close file'' -and ``shipout'' messages, simulating instead the \TeX{} format, with +\texttt{-\/-repetitions}, \texttt{-\/-be-redundant}, \texttt{-\/-shipouts}, +and \texttt{-\/-no-file-banner}, and also supresses the verbose ``open/close +file'' and ``shipout'' messages, simulating instead the \TeX{} format, with parens and square brackets. This is useful if you prefer the reports generated by some other tool but want to benefit from texlogsieve's line unwrapping algorithm; the output generated by this option should be @@ -397,6 +416,78 @@ Show concise options description. Print program version. \end{description} +\section{Tips} + +\begin{itemize} + \item If the program output is still too verbose to your liking, resist + the urge to use \texttt{-l critical} or \texttt{-\/-only-summary}. Instead, + create a configuration file with \texttt{no-summary-detail} and appropriate + \texttt{silence-[something]} and \texttt{set-to-level-[something]} options: + You will get reasonably quiet output but still be notified of problems. Since + you probably always use essentially the same set of packages and classes, + the file will be useable in other \LaTeX\ projects too (you may even put + it somewhere in your \TeX\ path, like \texttt{\$HOME/texmf/tex/latex} on + Unix-like systems). + + \item \texttt{texlogsieve} has no smarts to deal with error messages, but + you may use options \texttt{-l unknown -\/-no-page-delay -\/-no-summary + -\/-no-shipouts -\/-no-file-banner} to get almost nothing but errors. + + \item With \texttt{latexmk}, it is enough to put something like this in + \texttt{latexmkrc}: + + \bgroup\small + \begin{verbatim} +set_tex_cmds("-halt-on-error -interaction nonstopmode %O %S|texlogsieve");\end{verbatim} + \egroup + + However, this means you get to see the repeated output of all iterations + needed to produce the document. To only see the output of the last iteration, + you may do something like this: + + \bgroup\small + \begin{verbatim} +set_tex_cmds("-halt-on-error %O %S"); + +$silent = 1; # This adds "-interaction batchmode" +$silence_logfile_warnings = 1; + +END { + if (-s "$root_filename.blg" + and open my $bibfile, '<', "$root_filename.blg") { + + print("**********************\n"); + print("bibtex/biber messages:\n"); + while(my $line = <$bibfile>) { + if ($line =~ /You.ve used/) { + last; + } else { + print($line); + }; + }; + close($bibfile); + }; + + if (-s "$root_filename.ilg" + and open my $indfile, '<', "$root_filename.ilg") { + + print("*************************\n"); + print("makeindex/xindy messages:\n"); + while(my $line = <$indfile>) { + print($line); + }; + close($indfile); + }; + + if (-s "$root_filename.log") { + print("***************\n"); + print("LaTeX messages:\n"); + Run_subst("texlogsieve %R.log"); + }; +};\end{verbatim} + \egroup +\end{itemize} + \section{License} Copyright © 2021, 2022 Nelson Lago \textless lago@ime.usp.br\textgreater\\ diff --git a/Master/texmf-dist/scripts/texlogsieve/texlogsieve b/Master/texmf-dist/scripts/texlogsieve/texlogsieve index 14674e60aeb..7e3d0b8cb89 100755 --- a/Master/texmf-dist/scripts/texlogsieve/texlogsieve +++ b/Master/texmf-dist/scripts/texlogsieve/texlogsieve @@ -331,7 +331,7 @@ A simple handler: ----------------- exampleHandler = {} - exampleHandler.pattern = '^%s*L3 programming layer %b<> xparse %b<>' + exampleHandler.pattern = '^%s*L3 programming layer %b<>' function exampleHandler:init() end @@ -532,6 +532,13 @@ on the same line. CRITICAL = 3 UNKNOWN = 4 + RED = '\x1B[31m' + YELLOW = '\x1B[33m' + GREEN = '\x1B[32m' + BRIGHT = '\x1B[37;1m' + BGREEN = '\x1B[32;1m' +RESET_COLOR = '\x1B[0m' + function main(arg) initializeKpse() processCommandLine(arg) @@ -695,6 +702,11 @@ function initializeGlobals() -- detectEngine() may set one of these to true LUATEX = false XETEX = false + + -- When we print a message that is the first from a given filename, + -- we announce the filename first. This is used to detect the change + -- in file - used by showFileBanner() + lastFileBanner = "" end function initializeKpse() @@ -722,7 +734,8 @@ function registerHandlers() table.insert(beginningOfLineHandlers, labelHandler) table.insert(beginningOfLineHandlers, genericLatexHandler) table.insert(beginningOfLineHandlers, latex23MessageHandler) - table.insert(beginningOfLineHandlers, genericLatexVariantHandler) + table.insert(beginningOfLineHandlers, genericLatexVariantIHandler) + table.insert(beginningOfLineHandlers, genericLatexVariantIIHandler) table.insert(beginningOfLineHandlers, providesHandler) table.insert(beginningOfLineHandlers, geometryDetailsHandler) table.insert(beginningOfLineHandlers, epilogueHandler) @@ -737,6 +750,7 @@ function registerHandlers() table.insert(anywhereHandlers, anywhereInfoStringsHandler) table.insert(anywhereHandlers, anywhereWarningStringsHandler) table.insert(anywhereHandlers, anywhereCriticalStringsHandler) + table.insert(anywhereHandlers, fpHandler) -- before open/closeParensHandler! table.insert(anywhereHandlers, openParensHandler) table.insert(anywhereHandlers, closeParensHandler) table.insert(anywhereHandlers, openSquareBracketHandler) @@ -818,18 +832,16 @@ end function detectEngine() local line = logfile:read("*line") - local first = string.find(string.lower(line), '^this is lua') - if first ~= nil then + if line == nil then return end + + if string.find(string.lower(line), '^this is lua') then LUATEX = true - else - first = string.find(string.lower(line), '^this is xe') - if first ~= nil then XETEX = true end + elseif string.find(string.lower(line), '^this is xe') then + XETEX = true end - local msg = Message:new() - msg.content = line - msg.severity = DEBUG - dispatch(msg) + -- leave the line for normal processing + Lines:append(line) end function processCommandLine(args) @@ -842,10 +854,13 @@ function processCommandLine(args) SILENCE_REPETITIONS = true MINLEVEL = WARNING BE_REDUNDANT = false + FILE_BANNER = true DETAILED_UNDEROVER_SUMMARY = true DETAILED_REFERENCE_SUMMARY = true DETAILED_CITATION_SUMMARY = true + COLOR = false + SILENCE_STRINGS = {} SILENCE_PKGS = {} -- just the package names SEMISILENCE_FILES = {} -- filenames (without leading path), file globs work @@ -879,6 +894,7 @@ Options: --summary, --no-summary enable/disable final summary --only-summary no filtering, only final summary --shipouts, --no-shipouts enable/disable reporting shipouts + --file-banner, --no-file-banner Show/suppress "From file ..." banners --repetitions, --no-repetitions allow/prevent repeated messages --be-redundant, --no-be-redundant present/suppress ordinary messages that will also appear in the summary @@ -891,6 +907,7 @@ Options: --summary-detail, --no-summary-detail toggle box-detail, ref-detail, and cite-detail at once --heartbeat, --no-heartbeat enable/disable progress gauge + --color, --no-color enable/disable colored output -l LEVEL, --minlevel=LEVEL filter out messages with severity level lower than [LEVEL]. Valid levels are DEBUG, INFO, WARNING, @@ -933,7 +950,7 @@ Options: --version if vars.version then - print("texlogsieve 1.0.0-beta-3") + print("texlogsieve 1.0.0") print("Copyright (C) 2021, 2022 Nelson Lago ") print("License GPLv3+: GNU GPL version 3 or later " .. ".") @@ -978,6 +995,7 @@ Options: SILENCE_REPETITIONS = false BE_REDUNDANT = true MINLEVEL = DEBUG + FILE_BANNER = false end vars.u = nil @@ -1050,10 +1068,28 @@ Options: end end + -- When severity is debug, we already output open/close + -- file messages; adding these would be confusing. + if MINLEVEL == DEBUG then FILE_BANNER = false end + vars.l = nil vars.minlevel = nil + --no-file-banner + --file-banner + --file-banner=true/false + if vars['no-file-banner'] or vars['file-banner'] ~= nil + and not vars['file-banner'] then + + FILE_BANNER = false + end + if vars['file-banner'] then FILE_BANNER = true end + + vars['file-banner'] = nil + vars['no-file-banner'] = nil + + --no-repetitions --repetitions --repetitions=true/false @@ -1161,10 +1197,23 @@ Options: vars['no-heartbeat'] = nil + --no-color + --color + --color=true/false + if vars['no-color'] or vars.color ~= nil and not vars.color then + COLOR = false + end + if vars.color then COLOR = true end + + vars.color = nil + vars['no-color'] = nil + + if vars.filename == nil then logfile = io.stdin else logfile = assert(io.open(vars.filename, "r")) + readFls(vars.filename) end vars.filename = nil @@ -1271,9 +1320,7 @@ function processConfigFile(configFile, currentVars) if line == nil then break end line = trim(line) - local first = string.find(line, '^#') - - if first == nil and line ~= "" then + if not string.find(line, '^#') and line ~= "" then local equals = string.find(line, '=', 1, true) if equals ~= nil then optname = string.sub(line, 1, equals -1) @@ -1337,6 +1384,7 @@ function adjustSeverity(msg) "Tab has been converted to Blank", "The morewrites package is unnecessary", 'Unused \\captionsetup%b[]', + "Unknown feature `' in font %b`'", -- empty feature, not a problem } DEFAULT_FORCED_CRITICAL = { @@ -1358,9 +1406,7 @@ function adjustSeverity(msg) -- defaults (note that there is no "return" in the first two blocks) if msg.severity ~= INFO then for _, val in ipairs(DEFAULT_FORCED_INFO) do - local first = string.find(formatted, val) - local other = string.find(msg.content, val) - if first ~= nil or other ~= nil then + if string.find(formatted, val) or string.find(msg.content, val) then msg.severity = INFO end end @@ -1368,9 +1414,7 @@ function adjustSeverity(msg) if msg.severity ~= CRITICAL then for _, val in ipairs(DEFAULT_FORCED_CRITICAL) do - local first = string.find(formatted, val) - local other = string.find(msg.content, val) - if first ~= nil or other ~= nil then + if string.find(formatted, val) or string.find(msg.content, val) then msg.severity = CRITICAL end end @@ -1378,9 +1422,7 @@ function adjustSeverity(msg) if msg.severity ~= DEBUG then for _, val in ipairs(FORCED_DEBUG) do - local first = string.find(formatted, val) - local other = string.find(msg.content, val) - if first ~= nil or other ~= nil then + if string.find(formatted, val) or string.find(msg.content, val) then msg.severity = DEBUG return end @@ -1389,9 +1431,7 @@ function adjustSeverity(msg) if msg.severity ~= INFO then for _, val in ipairs(FORCED_INFO) do - local first = string.find(formatted, val) - local other = string.find(msg.content, val) - if first ~= nil or other ~= nil then + if string.find(formatted, val) or string.find(msg.content, val) then msg.severity = INFO return end @@ -1400,9 +1440,7 @@ function adjustSeverity(msg) if msg.severity ~= WARNING then for _, val in ipairs(FORCED_WARNING) do - local first = string.find(formatted, val) - local other = string.find(msg.content, val) - if first ~= nil or other ~= nil then + if string.find(formatted, val) or string.find(msg.content, val) then msg.severity = WARNING return end @@ -1411,9 +1449,7 @@ function adjustSeverity(msg) if msg.severity ~= CRITICAL then for _, val in ipairs(FORCED_CRITICAL) do - local first = string.find(formatted, val) - local other = string.find(msg.content, val) - if first ~= nil or other ~= nil then + if string.find(formatted, val) or string.find(msg.content, val) then msg.severity = CRITICAL return end @@ -1421,20 +1457,9 @@ function adjustSeverity(msg) end end -function checkRerun(msg) - if msg:checkMatch(msg.rerunMessages) then - -- Rerun messages should be silenced when - -- "--no-be-redundant" is in effect - if not BE_REDUNDANT then msg.redundant = true end - return true - end - - return false -end function processMessage(msg) - -- can't use short-circuit eval here, we need checkRerun() to always execute - SHOULD_RERUN_LATEX = checkRerun(msg) or SHOULD_RERUN_LATEX + SHOULD_RERUN_LATEX = SHOULD_RERUN_LATEX or msg:checkMatch(msg.rerunMessages) adjustSeverity(msg) @@ -1474,6 +1499,26 @@ function finishProcessingMessages() if SHOW_SUMMARY then showSummary() end end +function showFileBanner(msg) + if not FILE_BANNER then return end + + if msg.filename ~= nil + and msg.filename ~= "" + and msg.filename ~= lastFileBanner + -- TODO: "DUMMY" should never happen, but + -- what should we do if it does? + then + lastFileBanner = msg.filename + local txt = "From file " .. msg.filename .. ":" + if COLOR then + txt = yellow(txt) + else + print(string.rep('-', string.len(txt))) + end + print(txt) + end +end + function showMessage(msg) local formatted = msg:toString() if trim(formatted) ~= "" then @@ -1481,7 +1526,8 @@ function showMessage(msg) local spaces = "" if not RAW and msg.physicalPage ~= nil then pageinfo = 'pg ' .. msg.physicalPage .. ': ' - spaces = string.rep(" ", string.len(pageinfo)) + spaces = string.rep(" ", string.len(pageinfo)) -- before color + if COLOR then pageinfo = bright(pageinfo) end end -- A message is a repetition if it has @@ -1497,8 +1543,15 @@ function showMessage(msg) end if not SILENCE_REPETITIONS or not alreadySeen then + showFileBanner(msg) + for _, line in ipairs(linesToTable(formatted)) do + if COLOR and msg.severity >= CRITICAL then + line = red(line) + end + print(pageinfo .. line) + pageinfo = spaces end end @@ -1523,9 +1576,11 @@ function showRemainingMessages() end if thereIsSomething then - print("") - print("After last page:") - print("") + print() + local txt = "After last page:" + if COLOR then txt = bgreen(txt) end + print(txt) + print() end -- we always call this, even if there is nothing to show, @@ -1549,7 +1604,13 @@ function showSummary() if not thereIsSomething then return end - if not ONLY_SUMMARY then for i = 1, 3 do print("") end end + if not ONLY_SUMMARY then + print("") + local txt = "==== Summary: ====" + if COLOR then txt = bgreen(txt) end + print(txt) + print("") + end for _, summary in ipairs(summaries) do local formatted = summary:toString() @@ -1564,7 +1625,9 @@ function showSummary() end if SHOULD_RERUN_LATEX then - print("** LaTeX says you should rerun **") + local txt = "** LaTeX says you should rerun **" + if COLOR then txt = red(txt) end + print(txt) print() end end @@ -1605,9 +1668,15 @@ end -- datepat and filepat will come in handy later on. -- --- Note that, in some cases, it may be useful to start filepat with '^[%.]?/'. +-- Absolute paths may be in the form "/...blah.ext" and "C:\...blah.ext"; +-- Relative paths may be in the form "./blah.ext" or "blah.ext". This last +-- form makes ordinary words almost indistinguishable from paths. Since it +-- is also possible for a path to include dots and spaces, this pattern has +-- to be *very* permissible, which means it may easily match something that +-- is not really a filename. Don't blindly trust it! guessFilename() uses +-- this but with added sanity checks. -- --- filepat will fail: +-- filepat will also fail: -- -- 1. If the path or filename includes weird characters, such as ":" or "|" -- 2. If the file has no extension or the extension has only one character @@ -1615,20 +1684,22 @@ end -- 4. If filepat should match the end of the message and the matching line is -- wrapped in the middle of the file extension, for example "myfile.pd\nf" -- (but we have a hack in unwrapUntilPatternMatches() to work around that) --- --- More importantly, filepat allows for spaces and multiple dots in filenames, --- but this means it may match something that is not really a filename. Don't --- blindly trust it! We do not use this to detect open/close file; for that, --- check guessFilename(). datepat = '%d%d%d%d[/%-%.]%d%d[/%-%.]%d%d' -filepat = '%a?[:]?' - .. '[^%%:;,%=%*%?%|%&%$%#%!%@"%`\'%<%>%[%]%{%}]+' - .. '%.' - .. '[^/ %-%_%.%%:;,%=%*%?%|%&%$%#%!%@"%`\'%<%>%[%]%{%}]' - .. '[^/ %-%_%.%%:;,%=%*%?%|%&%$%#%!%@"%`\'%<%>%[%]%{%}]+' +-- These charaters should never be part of a path +unreasonable = '%%:;,%=%*%?%|%&%$%#%!%@"%`\'%<%>%[%]%{%}%(%)' + +filepat = '[^%s%-' .. unreasonable .. ']' -- the first char + .. ':?' -- If the first char is a drive letter + .. '[^' .. unreasonable .. ']*' -- other (optional) chars + .. '%.' -- the extension must exist and be at least two chars long + .. '[^/\\ %-%_%.' .. unreasonable .. ']' + .. '[^/\\ %-%_%.' .. unreasonable .. ']+' +-- This is even more fragile than filepat, it matches almost anything. +dirpat = '[^%s%-' .. unreasonable .. ']' + .. '[^' .. unreasonable .. ']*' ------------------------------------------------------------------------------- -- HandlerPrototype @@ -1715,8 +1786,7 @@ end -- We need to "manually" unwrap the file list because some -- lines are wrapped at lengths different from max_print_line function epilogueHandler:handleOtherLines() - local first = string.find(Lines.current, '^Output written') - if first ~= nil then + if string.find(Lines.current, '^Output written') then self.processingFilelist = false self.message.content = '\n' .. self.message.content end @@ -1728,8 +1798,9 @@ function epilogueHandler:handleOtherLines() self.message.content = self.message.content .. '\n' .. Lines.current end - first = string.find(Lines.current, '^[%<%{]') - if first ~= nil then self.processingFilelist = true end + if string.find(Lines.current, '^[%<%{]') then + self.processingFilelist = true + end Lines:handledChars() nextHandler = self @@ -1743,6 +1814,132 @@ function epilogueHandler:flush() end +------------------------------------------------------------------------------- +-- fpHandler +-- +-- Handles the messages output by the "fp" (fixed point) package, which +-- look like ( FP-UPN ( FP-MUL ) ( FP-ROUND ) ) etc. +-- +-- They are \message's, so they may appear anywhere on a line. Usually, +-- several of such messages appear together, so line wrapping is common. +-- We handle the parens with a stack independent from the openFiles stack. +------------------------------------------------------------------------------- + +fpHandler = HandlerPrototype:new() + +function fpHandler:init() + self.stack = Stack:new() +end + +-- The space between the open parens char and "FP" is optional +-- because it is ommited in case of line wrapping. +fpHandler.loosePattern = '%( ?FP%-[^%s%)]+' +fpHandler.strictPattern = '^%s*' .. fpHandler.loosePattern +fpHandler.pattern = fpHandler.strictPattern + + +function fpHandler:canDoit(position) + if position == nil then position = 0 end + local line = Lines:get(position) + if line == nil then return false, {} end + + -- When we are looking into the future, let's just lie: since this + -- handler deals with several similar short messages in sequence, + -- preventing unwrapping is a very bad idea, because it will affect the + -- processing of the current message. As for other messages looking into + -- the future, openParensHandler:canDoit() return value will work fine. + if position > 0 then return false, {} end + + while true do + local first = string.find(line, self.pattern) + if first ~= nil then return true, {first = first} end + + if not Lines:seemsWrapped(position) then return false, {} end + + line = line .. Lines:get(position +1) + position = position +1 + end +end + +function fpHandler:lookahead() + self.pattern = self.loosePattern + local match, data = self:canDoit() + self.pattern = self.strictPattern + + return match, data +end + +function fpHandler:startProcessing() + local myTurn, data = self:canDoit() + if not myTurn then return false end + + self.message = self:newMessage() + self.message.severity = DEBUG + self.message.content = "" + self.doit = self.process + nextHandler = self + return true +end + +fpHandler.doit = fpHandler.startProcessing + +function fpHandler:process() + while true do + local _, last = string.find(Lines.current, self.pattern) + if last ~= nil then return self:processOpen(last) end + + _, last = string.find(Lines.current, '%s*%)') + if last ~= nil then return self:processClose(last) end + + if Lines:seemsWrapped() then + Lines:unwrapOneLine() + else + -- This should never happen, but if it does + -- we will probably end up in an endless loop + io.stderr:write(" texlogsieve: parsing error in " + .. "fpHandler:process()\n") + + dispatch(self.message) + self.doit = self.startProcessing + self.stack = Stack:new() + return true + end + end +end + +function fpHandler:processOpen(last) + self.message.content = self.message.content + .. string.sub(Lines.current, 1, last) + + Lines:handledChars(last) + self.stack:push("DUMMY") + nextHandler = self + + return true +end + +function fpHandler:processClose(last) + self.message.content = self.message.content + .. string.sub(Lines.current, 1, last) + + Lines:handledChars(last) + + if self.stack:pop() == nil then + io.stderr:write(" texlogsieve: parsing error in " + .. "fpHandler:processClose()\n") + end + + if self.stack:empty() then + dispatch(self.message) + self.doit = self.startProcessing + else + nextHandler = self + end + + return true +end + + ------------------------------------------------------------------------------- -- underOverFullBoxHandler -- @@ -1783,8 +1980,9 @@ function underOverFullBoxHandler:handleFirstLine() self.message.verthoriz = data.verthoriz self.message.amount = data.amount self.message.severity = WARNING - local first = string.find(data.amount, 'badness 10000') - if first ~= nil then self.message.severity = CRITICAL end + if string.find(data.amount, 'badness 10000') then + self.message.severity = CRITICAL + end Lines:handledChars(data.last) self.doit = self.handleClosing @@ -1803,7 +2001,9 @@ function underOverFullBoxHandler:handleFirstLine() end if last == nil then - io.stderr:write(" texlogsieve: parsing error\n") + io.stderr:write(" texlogsieve: parsing error in " + .. "underOverFullBoxHandler:handleFirstLine()\n") + self.doit = self.handleFirstLine dispatch(self.message) return true @@ -1902,6 +2102,8 @@ end stringsHandler = HandlerPrototype:new() +stringsHandler.IHandleAnywhere = false + function stringsHandler:canDoit(position) for _, pattern in ipairs(self.patterns) do local success, data = self:canDoitRecursive(pattern, position, 0, 0) @@ -1922,10 +2124,15 @@ function stringsHandler:handleFirstLine() self.message = self:newMessage() self.message.severity = self.severity - -- ignore leading spaces in the first line (in the others, - -- they may be indentation or somehow relevant) - local _, last = string.find(Lines.current, '^%s+') - if last ~= nil then Lines:handledChars(last) end + -- If we know the pattern must match at the beginning of a line, + -- the pattern may or may not include spaces at the start. If that + -- is not the case, however, there may be added spaces before the + -- message, so it is better to remove them from the first line (in + -- the others, they may be indentation or somehow relevant) + if self.IHandleAnywhere then + local _, last = string.find(Lines.current, '^%s+') + if last ~= nil then Lines:handledChars(last) end + end self.captures = {} -- just in case we want to use captures self.patternLines = data.pattern -- the table with the pattern lines @@ -1955,7 +2162,9 @@ function stringsHandler:handleLines() for _, val in ipairs(tmp) do table.insert(self.captures, val) end if last == nil then - io.stderr:write(" texlogsieve: parsing error\n") + io.stderr:write(" texlogsieve: parsing error in " + .. "stringsHandler:handleLines()\n") + dispatch(self.message) self.doit = self.handleFirstLine return true @@ -2024,9 +2233,9 @@ function stringsHandler:canDoitRecursive(patternLines, while true do local first, last = string.find(line, patternLine) - local tmp = string.find(nextline, patternLine) -- see comment below - if first ~= nil and tmp == nil then + -- see comment below about "nextline, patternLine" + if first ~= nil and not string.find(nextline, patternLine) then -- Found it! if depth > 2 -- 3 lines matched, that is enough or #patternLines == 1 -- no more pattern lines @@ -2217,8 +2426,28 @@ beginningOfLineDebugStringsHandler.patterns = { '^`inconsolata%-zi4\' v%S-, ' .. datepat .. ' Text macros for Inconsolata %(msharpe%)', - '^Requested font ".-" at [%d%.]+pt\n %-> ' .. filepat, - '^Requested font ".-" scaled %d+\n %-> ' .. filepat, + '^Requested font ".-" at [%d%.]+pt\n ?%-> ' .. filepat, + '^Requested font ".-" scaled %d+\n ?%-> ' .. filepat, + '^Requested font ".-" at [%d%.]+pt', + '^Requested font ".-" scaled %d+', + + -- Usually these are warnings, but for font "nil", why bother? + '^luaotfload | aux : font no %d+ %(nil%) does not define feature ' + .. '.- for script .- with language %S+', + + '^luaotfload | aux : font no %d+ %(nil%) defines no feature for script %S+', + + -- From IEEEtran.cls + '^%-%- This is a[n]? %d+ point document%.', + '^%-%- Lines per column: %S+ %(%S+%)%.', + + '^%-%- See the "IEEEtran%_HOWTO" manual for usage information%.\n' + .. '%-%- http://www%.michaelshell%.org/tex/ieeetran/', + + '^%-%- Using %S+ x %S+ %b() paper%.', + '^%-%- Using %S+ output%.', + '^%-%- Verifying Times compatible math font%.', + '^%-%- %S+ loaded, OK%.', } @@ -2226,9 +2455,11 @@ beginningOfLineDebugStringsHandler.patterns = { -- Always start these patterns with "^%s*", see lookahead(). -- Order matters! The first match wins, so the longer ones should come first. anywhereDebugStringsHandler = stringsHandler:new() +anywhereDebugStringsHandler.IHandleAnywhere = true anywhereDebugStringsHandler.severity = DEBUG anywhereDebugStringsHandler.patterns = { - '^%s*L3 programming layer %b<> xparse %b<>', + '^%s*L3 programming layer %b<>', + '^%s*xparse %b<>', '^%s*%{.*pdftex%.map%}', '^%s*ABD: EverySelectfont initializing macros', @@ -2244,6 +2475,14 @@ anywhereDebugStringsHandler.patterns = { '^%s*%<' .. filepat .. '%>', -- "^%s*`Fixed Point Package', .- %(C%) Michael Mehlich", + + "^%s*`newtxmath' v%S+, " .. datepat + .. " Math macros based originally on txfonts %(msharpe%)", + + "^%s*`newtxtt' v%S+, " .. datepat + .. " Typewriter text macros based on txfonts %(msharpe%)", + + '^%s*%* soulpos %- computing points %- it may take a few seconds %*', } @@ -2267,12 +2506,66 @@ beginningOfLineInfoStringsHandler.patterns = { "^No file .-%.ind%.", "^No file .-%.bbl%.", "^No file .-%.gls%.", + "^reledmac reminder:%s*\n" - .. "%s*The number of the footnotes in this section " - .. "has changed since the last run.\n" - .. "%s*You will need to run LaTeX two more times " - .. "before the footnote placement\n" - .. "%s*and line numbering in this section are correct%.", + .. "%s*The number of the footnotes in this section " + .. "has changed since the last run.\n" + .. "%s*You will need to run LaTeX two more times " + .. "before the footnote placement\n" + .. "%s*and line numbering in this section are correct%.", + + "^ ?LaTeX document class for Lecture Notes in Computer Science", + + "^%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*\n" + .. "%* Local config file " .. filepat .. " used\n" + .. "%*", + + "^=== Package selnolig, Version %S+, Date " .. datepat .. " ===", + + -- Package snapshot + '^Dependency list written on .-%.dep%.', + + -- These come from IEEEtran.cls + '^%*%* Times compatible math font not found, forcing%.', + '^%-%- Found %S+, loading%.', + '^%-%- Using IEEE %S+ Society mode%.', + + '^%*%* Conference Paper %*%*\n' + .. 'Before submitting the final camera ready copy, remember to:\n' + .. '1%. Manually equalize the lengths of two columns on the last page\n' + .. 'of your paper%;\n' + .. '2%. Ensure that any PostScript and/or PDF output post%-processing\n' + .. 'uses only Type 1 fonts and that every step in the generation\n' + .. 'process uses the appropriate paper size%.', + + '^%*%* ATTENTION: Overriding %S+ to %S+ via %S+%.', + + '^%*%* ATTENTION: Overriding inner side margin to %S+ and ' + .. 'outer side margin to %S+ via %S+%.', + + '^%*%* ATTENTION: Overriding top text margin to %S+ and ' + .. 'bottom text margin to %S+ via %S+%.', + + '^%*%* ATTENTION: \\IEEEPARstart is disabled in draft mode %(line %S+%)%.', + '^%*%* ATTENTION: Overriding command lockouts %(line %S+%)%.', + + "^%*%* ATTENTION: Single column mode is not typically used " + .. "with IEEE publications%.", + + '^%*%* ATTENTION: Technotes are normally 9pt documents%.', + + -- MiKTeX auto-updates + '^======================================================================\n' + .. 'starting package maintenance%.%.%.\n' + .. 'installation directory: ' .. dirpat .. '\n' + .. 'package repository: http.+\n' + .. 'package repository digest: [%dabcdef]+\n' + .. 'going to download %S+ .*bytes\n' + .. 'going to install %d+ file%(s%) %(%d package%(s%)%)\n' + .. 'downloading http.-%.%.%.\n' + .. '%S+, %S+ Mbit/s\n' + .. 'extracting files from ' .. filepat .. '%.%.%.\n' + .. '======================================================================', } @@ -2280,6 +2573,7 @@ beginningOfLineInfoStringsHandler.patterns = { -- Always start these patterns with "^%s*", see lookahead(). -- Order matters! The first match wins, so the longer ones should come first. anywhereInfoStringsHandler = stringsHandler:new() +anywhereInfoStringsHandler.IHandleAnywhere = true anywhereInfoStringsHandler.severity = INFO anywhereInfoStringsHandler.patterns = { -- TODO: there are other "... patterns for blah blah" @@ -2322,9 +2616,30 @@ beginningOfLineWarningStringsHandler.patterns = { "^Couldn't patch \\%S+", "^Invalid UTF%-8 byte or sequence at line %d+ replaced by U%+FFFD%.", - '^Requested font ".-" at [%d%.]+pt\n' - .. "Unknown feature %b`' in font %b`'%.\n" + "^Unknown feature %b`' in font %b`'%.\n" .. ' %-> ' .. filepat, + + -- From IEEEtran.cls + "^%*%* WARNING: %S+ mode specifiers after the first in %b`' " + .. "ignored %(line %S+%)%.", + + "^%*%* WARNING: IEEEeqnarraybox position specifiers after " + .. "the first in %b`' ignored %(line %S+%)%.", + + "^%*%* WARNING: IEEEeqnarray predefined inter%-column glue type " + .. "specifiers after the first in %b`' ignored %(line %S+%)%.", + + "^%*%* WARNING: \\and is valid only when in conference or peerreviewca\n" + .. "modes %(line %S+%)%.", + + '^%*%* WARNING: Ignoring useless \\section in Appendix %(line %S+%)%.', + + '^%*%* WARNING: IEEEPARstart drop letter has zero height%! %(line %S+%)\n' + .. ' Forcing the drop letter font size to 10pt%.', + + '^%*%* WARNING: \\IEEEPARstart is locked out for technotes %(line %S+%)%.', + '^%*%* WARNING: %S+ is locked out when in conference mode %(line %S+%)%.', + '^%*%* ATTENTION: %S+ is deprecated %(line %S+%)%. Use %S+ instead%.', } @@ -2332,6 +2647,7 @@ beginningOfLineWarningStringsHandler.patterns = { -- Always start these patterns with "^%s*", see lookahead(). -- Order matters! The first match wins, so the longer ones should come first. anywhereWarningStringsHandler = stringsHandler:new() +anywhereWarningStringsHandler.IHandleAnywhere = true anywhereWarningStringsHandler.severity = WARNING anywhereWarningStringsHandler.patterns = { } @@ -2343,11 +2659,25 @@ anywhereWarningStringsHandler.patterns = { beginningOfLineCriticalStringsHandler = stringsHandler:new() beginningOfLineCriticalStringsHandler.severity = CRITICAL beginningOfLineCriticalStringsHandler.patterns = { - "^The control sequence at the end of the top line\n" - .. "of your error message was never \\def'ed%. If you have\n" - .. "misspelled it %(e%.g%., `\\hobx'%), type `I' and the correct\n" - .. "spelling %(e%.g%., `I\\hbox'%)%. Otherwise just continue,\n" - .. "and I'll forget about whatever was undefined%.", + "^The control sequence at the end of the top line\n" + .. "of your error message was never \\def'ed%. If you have\n" + .. "misspelled it %(e%.g%., `\\hobx'%), type `I' and the correct\n" + .. "spelling %(e%.g%., `I\\hbox'%)%. Otherwise just continue,\n" + .. "and I'll forget about whatever was undefined%.", + + "^ ======================================= \n" + .. " WARNING WARNING WARNING \n" + .. " %-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%- \n" + .. " The ligature suppression macros of the \n" + .. " selnolig package %*require%* LuaLaTeX%. \n" + .. " Because you're NOT running this package \n" + .. " under LuaLaTeX, ligature suppression \n" + .. " %*can not%* be performed%. \n" + .. "=========================================", + + -- From IEEEtran.cls + "^%*%* No Times compatible math font package found%. " + .. "newtxmath is required%.", } @@ -2355,6 +2685,7 @@ beginningOfLineCriticalStringsHandler.patterns = { -- Always start these patterns with "^%s*", see lookahead(). -- Order matters! The first match wins, so the longer ones should come first. anywhereCriticalStringsHandler = stringsHandler:new() +anywhereCriticalStringsHandler.IHandleAnywhere = true anywhereCriticalStringsHandler.severity = CRITICAL anywhereCriticalStringsHandler.patterns = { } @@ -2536,13 +2867,17 @@ function genericLatexHandler:parseSeverity(severity) severity = string.lower(severity) -- tocbibind uses "Note" + -- floatflt uses "Message" if severity == 'info' or severity == 'notification' or severity == 'note' + or severity == 'message' then return INFO - else + elseif severity == 'warning' then return WARNING + else + return UNKNOWN end end @@ -2553,13 +2888,11 @@ function genericLatexHandler:unwrapLines() -- not know how to handle the next line, but we still need to -- check another possibility: the next line might be a "normal" -- continuation line - local first = string.find(Lines:get(1), '^' .. self.prefix) - if first ~= nil then break end + if string.find(Lines:get(1), '^' .. self.prefix) then break end -- Ok, this is almost certainly a wrapped line, but it does -- not hurt to also check this just in case - first = string.find(Lines.current, 'on input line %d+%.$') - if first ~= nil then break end + if string.find(Lines.current, 'on input line %d+%.$') then break end Lines:unwrapOneLine() end @@ -2568,7 +2901,7 @@ end ------------------------------------------------------------------------------- -- latex23MessageHandler --- genericLatexVariantHandler +-- genericLatexVariantIHandler -- (from genericLatexHandler) -- -- They differ from the prototype by the set of patterns to search for and by @@ -2601,13 +2934,13 @@ function latex23MessageHandler:unpackData(data) self.message.content = Lines.current end -genericLatexVariantHandler = genericLatexHandler:new() +genericLatexVariantIHandler = genericLatexHandler:new() -genericLatexVariantHandler.patterns = { +genericLatexVariantIHandler.patterns = { "^(Package) (%S+) (%S+) on input line (%S+): ", } -function genericLatexVariantHandler:unpackData(data) +function genericLatexVariantIHandler:unpackData(data) local last = data[1] local what = data[2] local name = data[3] @@ -2624,6 +2957,28 @@ function genericLatexVariantHandler:unpackData(data) self.message.content = Lines.current end +-- Only ever saw "Library (tcolorbox):" +genericLatexVariantIIHandler = genericLatexHandler:new() + +genericLatexVariantIIHandler.patterns = { + "^(Library) (%(%S+%)): ", +} + +function genericLatexVariantIIHandler:unpackData(data) + local last = data[1] + local what = data[2] + local name = data[3] + self.message.what = what + self.message.name = name + self.message.severity = INFO + + self:findPrefix(last, name, what) + self.message.prefix = self.prefix + + self:unwrapLines() + self.message.content = Lines.current +end + ------------------------------------------------------------------------------- -- citationHandler @@ -2731,8 +3086,7 @@ function providesHandler:unpackData(data) self.message.content = Lines.current if not Lines:empty() then - local first = string.find(Lines:get(1), 'with kernel methods') - if first ~= nil then + if string.find(Lines:get(1), 'with kernel methods') then self.message.content = self.message.content .. ' ' .. Lines:get(1) Lines:gotoNextLine() end @@ -2837,8 +3191,7 @@ function openParensHandler:canDoit(position) -- (because of the unwrapping) or we will handle them using the -- "DUMMY" entry in the stack as usual. if filename == nil and position > 0 then - local first = string.find(line, '%)') - if first ~= nil then return false, {} end + if string.find(line, '%)') then return false, {} end end return true, {first = first, filename = filename} -- might be nil @@ -2858,7 +3211,9 @@ function openParensHandler:doit() flushUnrecognizedMessages() local last = unwrapUntilStringMatches(data.filename) if last == nil then - io.stderr:write(" texlogsieve: parsing error\n") + io.stderr:write(" texlogsieve: parsing error in " + .. "openParensHandler:doit()\n") + else Lines:handledChars(last) end @@ -3038,8 +3393,7 @@ function openSquareBracketHandler:canDoit(position) -- See the comment "HACK ALERT" in openParensHandler:canDoit() if latexPage == nil and position > 0 then - local first = string.find(line, '%]') - if first ~= nil then return false, {} end + if string.find(line, '%]') then return false, {} end end return true, {first = first, latexPage = latexPage} -- may be nil @@ -3059,7 +3413,9 @@ function openSquareBracketHandler:doit() flushUnrecognizedMessages() local last = unwrapUntilStringMatches(data.latexPage) if last == nil then - io.stderr:write(" texlogsieve: parsing error\n") + io.stderr:write(" texlogsieve: parsing error in " + .. "openSquareBracketHandler:doit()\n") + else Lines:handledChars(last) end @@ -3252,10 +3608,9 @@ end utf8FontMapHandler.doit = utf8FontMapHandler.handleFirstLine function utf8FontMapHandler:handleSecondLine() - local first = string.find(Lines.current, - "^%.%.%. no UTF%-8 mapping file for font encoding") + if string.find(Lines.current, + "^%.%.%. no UTF%-8 mapping file for font encoding") then - if first ~= nil then self.message.content = self.message.content .. '\n' .. Lines.current Lines:handledChars() dispatch(self.message) @@ -3263,10 +3618,9 @@ function utf8FontMapHandler:handleSecondLine() return true end - first = string.find(Lines.current, - "^%.%.%. processing UTF%-8 mapping file for font encoding") + if string.find(Lines.current, + "^%.%.%. processing UTF%-8 mapping file for font encoding") then - if first ~= nil then self.message.content = self.message.content .. '\n' .. Lines.current Lines:handledChars() self.doit = self.handleOtherLines @@ -3278,7 +3632,9 @@ function utf8FontMapHandler:handleSecondLine() -- The second line was neither "no UTF-8 mapping..." nor -- "processing UTF-8 mapping" - this should never happen dispatch(self.message) - io.stderr:write(" texlogsieve: parsing error\n") + io.stderr:write(" texlogsieve: parsing error in " + .. "utf8FontMapHandler:handleSecondLine()\n") + Lines:handledChars() self.doit = self.handleFirstLine return true @@ -3289,9 +3645,7 @@ end -- Therefore, we try to find the first "...defining Unicode char" -- message for the following 4 lines before giving up. function utf8FontMapHandler:handleOtherLines() - local first = string.find(Lines.current, "^%s*defining Unicode char") - - if first ~= nil then + if string.find(Lines.current, "^%s*defining Unicode char") then flushUnrecognizedMessages() self.foundOtherLines = true self.message.content = self.message.content .. '\n' .. Lines.current @@ -3303,14 +3657,14 @@ function utf8FontMapHandler:handleOtherLines() -- this line does not match; why? First possibility: there are no -- "...defining Unicode char" lines to be found; instead, there is -- another encoding being defined. This obviously should not happen - first = string.find(Lines.current, - "^Now handling font encoding (%S+) %.%.%.") - if first ~=nil then + if string.find(Lines.current, + "^Now handling font encoding (%S+) %.%.%.") then -- give up and start processing the new message (should not happen) dispatch(self.message) flushUnrecognizedMessages() - io.stderr:write(" texlogsieve: parsing error\n") + io.stderr:write(" texlogsieve: parsing error in " + .. "utf8FontMapHandler:handleOtherLines()\n") self.numTries = 0 self.foundOtherLines = false @@ -3363,38 +3717,42 @@ end Message.severity = UNKNOWN -function Message:toString(noSilence) - -- noSilence may be useful when generating summaries - if noSilence then - local formatted = self:realToString() - if trim(formatted) == "" then return "" else return formatted end - end +function Message:toString(bypassMostFilters) - -- If we've already been here, just output the previous result - if self.formatted ~= nil then return self.formatted end + -- We do not want to exclude these in the summaries + if not bypassMostFilters then - if self.mute then self.formatted = "" return "" end + -- If we've already been here, just output the previous result + if self.formatted ~= nil then return self.formatted end - if self.severity < MINLEVEL then self.formatted = "" return "" end + if self.mute then self.formatted = "" return "" end - if self:ignoreAsRedundant() then self.formatted = "" return "" end + if self.severity < MINLEVEL then self.formatted = "" return "" end - self.formatted = self:realToString() - if trim(self.formatted) == "" then self.formatted = "" return "" end + if self:ignoreAsRedundant() then self.formatted = "" return "" end + + if self.name ~= nil then + for _, val in ipairs(SILENCE_PKGS) do + if self.name == val then self.formatted = "" return "" end + end + end - for _, val in ipairs(SILENCE_STRINGS) do - local first = string.find(self.formatted, val) - local other = string.find(self.content, val) - if first ~= nil or other ~= nil then self.formatted = "" return "" end end - if self.name ~= nil then - for _, val in ipairs(SILENCE_PKGS) do - if self.name == val then self.formatted = "" return "" end + local formatted = self:realToString() + if trim(formatted) == "" then self.formatted = "" return "" end + + for _, val in ipairs(SILENCE_STRINGS) do + if string.find(formatted, val) or string.find(self.content, val) + then + self.formatted = "" + return "" end end - return self.formatted + if not bypassMostFilters then self.formatted = formatted end + + return formatted end function Message:realToString() @@ -3432,6 +3790,11 @@ Message.rerunMessages = { 'longtable', 'Table widths have changed%. Rerun LaTeX%.' }, + { + WARNING, + 'longtable', + 'Column widths have changed\nin table' + }, { WARNING, 'rerunfilecheck', @@ -3442,6 +3805,16 @@ Message.rerunMessages = { 'biblatex', 'Please rerun LaTeX%.' }, + { + WARNING, + 'atenddvi', + 'Rerun LaTeX, last page not yet found%.' + }, + { + WARNING, + 'hyperref', + 'Rerun to get /PageLabels entry%.' + }, } function Message:checkMatch(patlist) @@ -3462,8 +3835,10 @@ function Message:checkMatch(patlist) if name == nil then name = self.what end if name ~= pkgname then break end - local first = string.find(self:realToString(), text) - if first ~= nil then return true end + if string.find(self:realToString(), text) + or string.find(self.content, text) + then return true end + until true end @@ -3473,9 +3848,11 @@ end function Message:ignoreAsRedundant() if BE_REDUNDANT then return false end - -- this may also be set by checkRerun() if self.redundant == nil then - if self:checkMatch(self.redundantMessages) then + if self:checkMatch(self.redundantMessages) + or self:checkMatch(self.rerunMessages) -- these are redundant too + + then self.redundant = true else self.redundant = false @@ -3492,8 +3869,7 @@ function Message:toSummary() -- In the rare event that one of these is sent out as -- an unrecognizedMessage with no other text, allow for -- repetitions - local first = string.find(trim(formatted), '^[%(%)%[%]]$') - if first ~= nil then return end + if string.find(trim(formatted), '^[%(%)%[%]]$') then return end repetitionsSummary:add(self) end @@ -3506,8 +3882,7 @@ function currentFileIsSilenced() local _, last = string.find(filename, '^.*/') -- get just the basename if last ~= nil then filename = string.sub(filename, last +1) end for _, pattern in ipairs(SEMISILENCE_FILES) do - local first = string.find(filename, pattern) - if first ~= nil then return true end + if string.find(filename, pattern) then return true end end -- This is O(n*m) and gets executed for every message, @@ -3519,8 +3894,7 @@ function currentFileIsSilenced() if last ~= nil then basename = string.sub(basename, last +1) end for _, pattern in ipairs(SILENCE_FILES_RECURSIVE) do - _, last = string.find(basename, pattern) - if last ~= nil then return true end + if string.find(basename, pattern) then return true end end end @@ -3551,6 +3925,8 @@ function endShipoutMessage:realToString() .. ', LaTeX page counter [' .. latexPages[self.physicalPage] .. ']' + if COLOR then msg = green(msg) end + return msg end @@ -3664,6 +4040,10 @@ end -- specific undefined citation). SummaryPrototype = {} +-- Should filtered out messages be included in the summary? For repetitions +-- this should be false, for most others true is probably better. +SummaryPrototype.bypassMostFilters = false + function SummaryPrototype:new() local o = {} setmetatable(o, self) @@ -3674,10 +4054,10 @@ function SummaryPrototype:new() end function SummaryPrototype:add(msg) - -- group messages by message content - local formatted = msg:toString() + local formatted = msg:toString(self.bypassMostFilters) if trim(formatted) == "" then return end + -- group messages by message content if self.messages[formatted] == nil then self.messages[formatted] = {} end @@ -3705,6 +4085,7 @@ function SummaryPrototype:toString() if text == "" then return "" end -- happens with repetitionsSummary if self.header ~= "" then + if COLOR then self.header = green(self.header) end if self:showDetails() then self.header = self.header .. '\n' else @@ -3784,6 +4165,7 @@ end repetitionsSummary = SummaryPrototype:new() +repetitionsSummary.bypassMostFilters = false repetitionsSummary.header = 'Repeated messages:' function repetitionsSummary:toString() @@ -3820,6 +4202,7 @@ end missingCharSummary = SummaryPrototype:new() +missingCharSummary.bypassMostFilters = true missingCharSummary.header = 'Missing characters:' function missingCharSummary:processSingleMessageList(messages) @@ -3836,6 +4219,7 @@ end citationsSummary = SummaryPrototype:new() +citationsSummary.bypassMostFilters = true citationsSummary.header = 'Undefined citations:' function citationsSummary:showDetails() @@ -3843,6 +4227,10 @@ function citationsSummary:showDetails() end function citationsSummary:add(msg) + -- Filter out stuff explicitly excluded by the user + local tmp = msg:toString(self.bypassMostFilters) + if trim(tmp) == "" then return end + -- group messages by problem key. We do not use msg:toString() -- here because some messages may include the page number, making -- messages that are otherwise the same appear to be different. @@ -3902,8 +4290,13 @@ end -- under/overfull boxes in pages X, Y, and Z. So we store messages -- directly in self.messages instead of using sublists. underOverSummary = SummaryPrototype:new() +underOverSummary.bypassMostFilters = true function underOverSummary:add(msg) + -- Filter out stuff explicitly excluded by the user + local tmp = msg:toString(self.bypassMostFilters) + if trim(tmp) == "" then return end + table.insert(self.messages, msg) end @@ -3912,18 +4305,28 @@ function underOverSummary:toString() local pages, files = self:pageAndFileList(self.messages) + local output + if DETAILED_UNDEROVER_SUMMARY then - local output = "Under/overfull boxes:" + output = "Under/overfull boxes:" + if COLOR then output = green(output) end for _, msg in ipairs(self.messages) do - output = output .. '\npage ' .. msg.physicalPage - .. ' (file ' .. msg.filename .. '):\n' - output = output .. msg:toString(true) + local pageinfo = 'page ' .. msg.physicalPage + local fileinfo = 'file ' .. msg.filename + if COLOR then pageinfo = bright(pageinfo) end + + output = output .. '\n' .. pageinfo .. ' (' .. fileinfo .. '):\n' + .. msg:toString(true) end - return output else - return "Under/overfull boxes in pages " - .. pages .. " (files " .. files .. ")" + output = "Under/overfull boxes" + if COLOR then output = green(output) end + output = output .. " in pages " + .. pages .. " (files " .. files .. ")" + end + + return output end @@ -3966,9 +4369,8 @@ function trim(s) return (string.gsub(s, '^%s*(.-)%s*$', '%1')) end function trimRight(s) return (string.gsub(s, '^(.-)%s*$', '%1')) end function stringToPattern(s) - local first, _ = string.find(s, '^////') local pat - if first ~= nil then + if string.find(s, '^////') then pat = string.sub(s, 5) else pat = protect_metachars(s) @@ -4027,6 +4429,26 @@ function listToCommaSeparatedString(list) return tmp end +function green(s) + return GREEN .. s .. RESET_COLOR +end + +function bgreen(s) + return BGREEN .. s .. RESET_COLOR +end + +function red(s) + return RED .. s .. RESET_COLOR +end + +function yellow(s) + return YELLOW .. s .. RESET_COLOR +end + +function bright(s) + return BRIGHT .. s .. RESET_COLOR +end + --[[ ##### STACK ##### ]]-- @@ -4540,7 +4962,10 @@ function unwrapUntilPatternMatches(pat) if Lines:seemsWrapped() then Lines:unwrapOneLine() else - io.stderr:write(" texlogsieve: parsing error\n") + io.stderr:write(" texlogsieve: parsing error in " + .. "unwrapUntilPatternMatches()\n") + + break end end @@ -4682,29 +5107,25 @@ end function guessQuotedFilename(line, position) -- luatex puts quotes around filenames with spaces, which makes things easier while true do - local _, last = string.find(line, '^"') - if line ~= "" and last == nil then return false end -- no quotes - last = string.find(line, '^"[%.]?/') -- relative path or unix-style path - if last == nil then - last = string.find(line, '^"%a:/') -- windows-style path - end - -- there are quotes, but what follows is not a filename - if string.len(line) >= 4 and last == nil then return true, nil end + -- no quotes + if line ~= "" and not string.find(line, '^"') then return false end - _, last = string.find(line, '^"%a?[:]?[%.]?/[^"%(%)%[%]]+"') + local _, last = string.find(line, '^"' .. filepat .. '"') if last ~= nil then - local filename = string.sub(line, 2, last -1) -- remove quotes - if kpse.find_file(filename, 'other text files') == nil then - return true, nil -- there are quotes, but no filename + local filename = string.sub(line, 1, last) + if checkIfFileExists(filename) then + return true, filename else - return true, '"' .. filename .. '"' + return true, nil -- there are quotes, but no filename end end -- no closing quote or the line is too short; can we unwrap this line? if not Lines:seemsWrapped(position) then - io.stderr:write(" texlogsieve: parsing error\n") + io.stderr:write(" texlogsieve: parsing error in " + .. "guessQuotedFilename()\n") + return true, nil end @@ -4724,24 +5145,27 @@ function guessUnquotedFilename(line, position) local filename while true do - local first = string.find(line, '^[%.]?/') -- relative or unix-style path - if first == nil then - first = string.find(line, '^%a:/') -- windows-style path - end - -- this does not look like a filename - if string.len(line) >= 3 and first == nil then return nil end local longest = string.len(line) + local notWrapped = false -- if there is a ")", "(", "[", or "]" char, stop before that - first = string.find(line, "[%)%(%[%]]") - if first ~= nil then longest = first -1 end + local first = string.find(line, "[%)%(%[%]]") + if first ~= nil then + notWrapped = true -- this line is obviously not wrapped + longest = first -1 + end -- From longest to shortest, to avoid problems if there is a -- substring in the filename that matches some other filename. for i = longest, alreadyCheckedIdx +1, -1 do local candidate = string.sub(line, 1, i) - if kpse.find_file(candidate, 'other text files') ~= nil then + + -- We are gradually removing chars from the end of the + -- string; if we reach a slash, only the directories remain + if string.sub(candidate, #candidate) == "/" then break end + + if checkIfFileExists(candidate) then filename = candidate break end @@ -4753,9 +5177,9 @@ function guessUnquotedFilename(line, position) -- 2. If we did find the filename, we might find a different -- (longer) filename, in which case we should stick with -- it (yes, I have seen it happen!) - if not Lines:seemsWrapped(position) then return filename end - first = string.find(line, "[%)%(%[%]]") - if first ~= nil then return filename end + if notWrapped or not Lines:seemsWrapped(position) then + return filename + end alreadyCheckedIdx = longest line = line .. Lines:get(position +1) @@ -4763,6 +5187,107 @@ function guessUnquotedFilename(line, position) end end +function readFls(logfilename) + local flsfilename = string.gsub(logfilename, '%.log$', '.fls') + + -- Let's be reasonably sure that we are not dealing + -- with a stale .fls file from a previous run + local logdate = lfs.attributes(logfilename, 'modification') + local flsdate = lfs.attributes(flsfilename, 'modification') or 0 + local timediff = math.abs(logdate - flsdate) -- seconds + + -- It is ok for this to fail, so no "assert" + local flsfile = io.open(flsfilename, 'r') + + if flsfile ~= nil and timediff <= 5 then + USE_FLS_FILE = true + + filelist = {} + while true do + local line = flsfile:read("*line") + if line == nil then + io.close(flsfile) + return + end + + local _, last = string.find(line, '^[IO][NU]T?PUT ') + if last ~= nil then line = string.sub(line, last +1) end + + -- I don't think this ever happens + if string.find(line, '^".*"$') then line = string.sub(line, 2, -2) end + + -- No idea what is this, but it happens with the MiKTeX version + -- of luatex for Windows. It apparently only appears with font + -- files, which are irrelevant here, but let's be safe + if string.find(line, '^\\\\%?\\') then line = string.sub(line, 5) end + + line = string.gsub(line, '\\', '/') + + _, last = string.find(line, '^%./') + if last ~= nil then line = string.sub(line, last +1) end + + -- Save as a Set to eliminate duplicates + if not string.find(line, '^PWD') then filelist[line] = true end + end + end +end + +function checkIfFileExistsWithFls(filename) + if string.find(filename, '^%./') then filename = string.sub(filename, 3) end + + for tmp, _ in pairs(filelist) do -- not ipairs! + if tmp == filename then return true end + end + + return false +end + +function checkIfFileExistsWithKpse(filename) + -- Is this something like "blah.ext"? If so, make it "./blah.ext", + -- otherwise kpse.find_file may find some file in the TeX path that + -- has nothing to do with us. + local onlyName = true + + -- "C:/" + if string.find(filename, '^%a%:/') then onlyName = false end + + -- "./" or just "/" + if string.find(filename, '^[%.]?/') then onlyName = false end + + if onlyName then filename = './' .. filename end + + if kpse.find_file(filename, 'other text files') ~= nil then + return true + else + return false + end +end + +function checkIfFileExists(filename) + -- If there are quotes, remove them + if string.find(filename, '^".*"$') then + filename = string.sub(filename, 2, -2) + end + + -- MiKTeX for windows does not necessarily use the same type of + -- slashes in paths inside the log file and the .fls file. I also + -- do not know if kpse.find_file works ok with backslashes. Let's + -- just turn everything to forward slashes and be done with it. + filename = string.gsub(filename, '\\', '/') + + -- If we are reading a logfile (not stdin) and there is a + -- corresponding .fls file, use it. Maybe this is slightly more + -- reliable, but the real advantage is that we can process a + -- logfile even if we are not on the same environment that + -- created it, which is nice for testing, reporting bugs, etc. + if USE_FLS_FILE then + return checkIfFileExistsWithFls(filename) + else + return checkIfFileExistsWithKpse(filename) + end +end + + --[[ ###################################################################### ]]-- --[[ ######################## BEGINNING OF SCRIPT ######################### ]]-- --[[ ###################################################################### ]]-- -- cgit v1.2.3