% \iffalse meta-comment
% ******************************************************************************
% ******************************************************************************
% **                                                                          **
% ** silence v.1.4 by Paul Isambert                                           **
% ** See the documentation for a comment on the implementation.               **
% **                                                                          **
% ** This set of macros is published under the LaTeX Project Public License.  **
% **                                                                          **
% ** Comments, suggestions and bugs:                                          **
% **                                                                          **
% ** zappathustra@free.fr                                                     **
% **                                                                          **
% ** http://paulisambert.free.fr/                                             **
% **                                                                          **
% ** Enjoy!                                                                   **
% **                                                                          **
% ******************************************************************************
% ******************************************************************************
% \fi

% \iffalse
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\ProvidesPackage{silence}[2011/12/06 v1.4 Selective filtering of warnings and error messages]
%<*driver>
\documentclass[a4paper]{ltxdoc}
\usepackage[T1]{fontenc}
\usepackage{silence}
\usepackage[left=5cm,textwidth=14cm,textheight=23cm]{geometry}
\begin{document}
\DocInput{silence-doc.dtx}
\end{document}
%</driver>
% \fi
% 
% \def\vbskip{\vskip.5em\noindent}
% \WarningFilter{latex}{Marginpar on page}
% \MakeShortVerb{"}
% \def\vbopt#1{\noindent\llap{\textbf{#1}\enspace}\ignorespaces} 
% \GetFileInfo{silence.sty}
% \title{Filtering messages with "silence"\\\Large\fileversion}
% \date{\filedate}
% \author{Paul Isambert\\"zappathustra@free.fr"}
%
% \maketitle
% \vskip1em
% \bgroup
% \raggedleft
% \small
% \textit{`Errare humanum est, perseverare diabolicum.'}\\ Proverb (attributed to Seneca)
%
% \textit{`Marginpar on page 3 moved.'}\\\LaTeX
%
% \egroup
% \vskip3em
%
%\begin{abstract}
%This package is designed to filter out unwanted warnings
%and error messages. Entire packages (including \LaTeX)
%can be censored, but very specific messages can be
%targeted too. \TeX's messages are left untouched.
%\end{abstract}
%
%
% \tableofcontents\clearpage
%
%\setcounter{section}{-1}
%
%\section{Changes}
%\def\Change#1#2#3{\par\noindent\llap{\textbullet\ }\textbf{#1} (#2)\leaders\hbox{... }\hfill#3\par}
%
%\Change{v1.1}{2009/03/20}{Initial version}
%\Change{v1.2}{2009/04/02}{\texttt{LaTeX Font Warning}s can be filtered}
%\Change{v1.3}{2010/02/28}{Compatibility with Lua\TeX}
%\Change{v1.4}{2011/12/06}{Fixed the \texttt{\string\@gobbletwo} error}
%\Change{v1.4}{2012/01/26}{Fixed the \texttt{\string\@gobble} error (same as before, spotted with biblatex)}
%
%\section{Introduction}
%When working with \LaTeX, messages are utterly important.
%
%Ok, this was the well-behaved \LaTeX\ user's offering to
%the gods. Now, the fact is, when processing long
%documents, repeated messages can really get boring,
%especially when there's nothing you can do about them.
%For instance, everybody knows the following:
%\vbskip
%\hfil\emph{Marginpar on page \emph{x} moved.}
%\vbskip
%When you encounter that message, it means that you
%have to keep in mind for the next compilations that
%`there is a warning that I can ignore,' which sounds
%pretty paradoxical. When your
%document is 5 pages long, it doesn't matter, but when
%you're typesetting a book-length text, it's different, because
%you can have tens of such messages. And if you're
%hacking around, error messages might also appear
%that you don't want to consider. This package is
%meant to filter out those unwanted messages.
%
%You probably know all there is to know about
%warnings and errors, in which case you may want
%to skip this paragraph. If you don't, here's some
%information. When \LaTeX\ processes your document,
%it produces error messages and warnings, and packages
%and the class you're using do too. Errors generally
%refer to wrong operations such that the desired result
%won't be attained, while warnings usually inform you
%that there might be unexpected but benign modifications.
%Those messages are sent to the "log" file along with
%other bits of information. You're probably using
%a \LaTeX-oriented text editor to create your document
%and at the end of a compilation, it certainly gives you
%the number of errors and warnings encountered. 
%Hopefully, your editor also has a facility to browse
%errors and warnings. With "silence", unwanted messages
%won't appear anymore.
%
%Those messages always have one of the following structures:
%
%\vbskip
%"Package" \meta{Name of the package} "Error:" \meta{message text}
%\vbskip
%or
%\vbskip
%"Class" \meta{Name of the class} "Error:" \meta{message text}
%\vbskip
%or
%\vbskip
%"LaTeX Error:" \meta{message text}
%\vbskip
%Replace \emph{Error} with \emph{Warning} and you have
%warnings. The important part for "silence" is \meta{Name of the
%package} or \meta{Name of the class}, because the information
%is needed when filtering messages. In case of a \LaTeX\ message,
%\meta{Name of the package} is supposed to be "latex". In case
%of a `\texttt{LaTeX Font}' message, \meta{Name of the package}
%is "latexfont" by convention.
%For filters, \meta{message text} will be crucial too.
%
%If a message doesn't begin as above, but displays its text
%immediately, then it has been sent by \TeX; "silence" cannot
%filter it out, and anyway it would probably be a very bad
%idea. For instance, there's nothing you can do about
%something like:
%
%\vbskip
%"Missing number, treated as zero."
%\vbskip
%and the only thing you want to do is correct your mistake.
%
%Another very bad idea is to forget the following:
%\emph{"silence" does not fix anything}.
%
%
%
%\section{Usage}
%\subsection{Calling the package}
%
%The package may be called as any other
%package with:
%
%\vbskip
%"\usepackage"\oarg{options}"{silence}"
%\vbskip
%and as any other package it will be effective only
%after it has been called, which means that messages
%issued before will not be filtered. So put it at the
%very beginning of your preamble. However, if you also
%want to catch a message issued by the class when it
%is specified with "\documentclass", you can't say
%
%\vbskip
%"\usepackage"\oarg{options}"{silence}"\\
%\meta{filters}\\
%"\documentclass{article}"
%\vbskip
%but instead
%
%\vbskip
%"\RequirePackage"\oarg{options}"{silence}"\\
%\meta{filters}\\
%"\documentclass{article}"
%\vbskip
%because "\usepackage" is not allowed before
%"\documentclass". This way, you can filter for
%instance a warning issued by the "memoir" class
%according to which you don't have "ifxetex", although
%you have it...
%
%
%
%
%\subsection{The brutal way}
%
%\DescribeMacro{\WarningsOff\oarg{list}}\DescribeMacro{\WarningsOff*}
%\DescribeMacro{\ErrorsOff\oarg{list}}\DescribeMacro{\ErrorsOff*}
%There is one very simple and very effective way to
%get rid of messages: "\WarningsOff" and "\ErrorsOff". In their
%starred versions, no warning or error will make it to your log
%file. Without a star, and without argument either, they filter
%out all messages except \LaTeX's. Finally, you can specify
%a list of comma-separated packages whose messages you don't want.
% For that purpose, \LaTeX\ is considered a package whose name is "latex".
%For instance "\WarningsOff[superpack,packex]" will remove all
%warnings issued by the "superpack" and "packex" packages. Messages
%issued by classes are also accessible in the same way, so to
%avoid the warning mentioned above, you can say:
%
%\vbskip
%"\RequirePackage"\oarg{options}"{silence}"\\
%"\WarningsOff[memoir]"\\
%"\documentclass{memoir}"
%\vbskip%
%If you want to filter out \LaTeX's warnings only, you can
%say "\WarningsOff[latex]". Note that issuing "\WarningsOff[mypack]"
%after "\WarningsOff", for instance, is useless, because all
%warnings are already filtered out. But this won't be mentioned to you.
%And "silence" won't check either whether a censored package exists
%or not. So if you say "\WarningsOff[mypak]" and mean "\WarningsOff[mypack]",
%this will go unnoticed.
%
%\DescribeMacro{\WarningsOn\oarg{list}}\DescribeMacro{\ErrorsOn\oarg{list}}
%These commands allow messages to be output again. If there is no \meta{list},
%all packages are affected, otherwise only specified packages are affected.
%So, for instance
%
%\vbskip
%"\WarningsOff"\\
%"Blah blah blah..."\\
%"\WarningsOn[superpack,packex]"\\
%"Blah blah blah..."
%\vbskip
%will have all warnings turned off, except those issued by \LaTeX\ (because
%"\WarningsOff" has no star) and by "superpack" and "packex" in the second
%part of the document.
%
%Note that the command described in this section are independant of the
%filters described in the following one, and vice-versa. That is, 
%"\WarningsOn" has no effect on the filters, and if you design a filter
%to avoid a specific message issued by, say, "superpack", "\WarningsOn[superpack]"
%will not deactivate that filter and that one message will still be
%filtered out.
%
%
%
%
%
%
%\subsection{The exquisite way}
%
%Turning off all messages might be enough in many cases, especially if
%you turn them on again rapidly, but it is a blunt approach and it can lead
%to unwanted results. So "silence" allows you to create filters to target
%specific messages.
%
%\DescribeMacro{\WarningFilter\oarg{family}\\\marg{package}\marg{message}}
%\DescribeMacro{\ErrorFilter\oarg{family}\\\marg{package}\marg{message}}
%These commands will filter out messages issued by \meta{package} and beginning with \meta{message}.
%The optional \meta{family} specification is used to create groups of filters
%that will be (de)activated together. If there is no family, the filter will be immediately
%active and you won't be able to turn if off. So, for instance:
%
%\vbskip
%"\WarningFilter{latex}{Marginpar on page}"
%\vbskip
%will filter out all \LaTeX\ warnings beginning with `"Marginpar on page"'. On the other
%hand,
%
%\vbskip
%"\WarningFilter[myfam]{latex}{Marginpar on page}"
%\vbskip
%will filter out those same warnings if and only if "myfam" filters are active (but
%see the "immediate" package option below).
%
%You can be quite specific with the text of the message. For instance, 
%
%\vbskip
%"\WarningFilter{latex}{Marginpar on page 2 moved}"
%\vbskip
%will filter out marginpar warnings issued on page 2.
%
%In this version (contrary to the starred version below), \meta{message} should
%reproduce the (beginning of) the displayed message. For instance, suppose
%that you have the following error message:
%
%\vbskip
%"Package superpack Error: The command \foo should not be used here."
%\vbskip
%To filter it out, you can simply say:
%\vbskip
%"\ErrorFilter"\oarg{family}"{superpack}{The command \foo should not}"
%\vbskip
%although you might know that "superpack" didn't produce it so easily,
%but instead must have said something like:
%\vbskip
%"\PackageError{superpack}{The command \string\foo\space should not be used here}{}"
%\vbskip
%Here, you don't have to know how the message was produced, but simply what
%it looks like. The starred versions of those commands below work differently.
%
%\DescribeMacro{\ActivateWarningFilters\\\oarg{list}}
%\DescribeMacro{\ActivateErrorFilters\\\oarg{list}}
%These macros activate the filters which belong to the families specified
%in \meta{list}. If there is no such list, all filters are activated. Indeed,
%unless the "immediate" option is turned on (see below), filters are not active
%when created, except those that don't belong to a family. Note that \meta{list}
%contains the name of the family specified in "\WarningFilter"\oarg{family}\marg{package}\marg{message}
%(and similarly for "\ErrorFilter") and not the name of the package (i.e. \meta{package}), 
%although you can freely use the same name for both. So for instance:
%
%\vbskip
%"\WarningFilter[myfam]{packex}{You can't do that}"\\
%"\ActivateWarningFilters[packex]"
%\vbskip
%will not activate the desired filter, but instead will try to activate the filters
%belonging to the "packex" family. If this family doesn't exist, you won't be warned.\footnote{You
%won't be warned either that the \texttt{packex} family will really be active, which
%means that if you create (warning) filters with that family name afterwards, they will
%take effect immediately, even if you're not in \texttt{immediate} mode.}
%So the proper way instead is:
%\vbskip
%"\WarningFilter[myfam]{packex}{You can't do that}"\\
%"\ActivateWarningFilters[myfam]"
%\vbskip
%Finally, if a filter is created while its family is active, it will be active itself
%from its creation.
%
%\DescribeMacro{\DeactivateWarningFilters\\\oarg{list}}
%\DescribeMacro{\DeactivateErrorFilters\\\oarg{list}}
%These are self-explanatory. Once again, if there is no list, all filters are
%deactivated. Note that the "immediate" option implicitly turns on families 
%associated with the filters that are created, so:
%
%\vbskip
%"\WarningFilter[myfam]{packex}{You can't do that}"\\
%"\DeactivateWarningFilters[myfam]"\\
%"\WarningFilter[myfam]{superpack}{This is very bad}"
%\vbskip
%will make all filters belonging to "myfam" active if "immediate" is on. 
%In this example, both filters will be active, although one might have 
%intended only the second one to be.
%
%\DescribeMacro{\ActivateFilters\oarg{list}}
%\DescribeMacro{\DeactivateFilters\oarg{list}}
%I bet you know what I'm going to say. These two macros activate
%or deactivate all filters, or all filters belonging
%to the specified families in case there's an argument. So
%you can create error filters and warning filters with the
%same family name and control them all at once. Isn't it
%amazing? Note that, just like above, "\ActivateFilters[myfam]"
%won't complain if there's no "myfam" family, or if there's
%just a warning family and no error family, and so on and
%so forth.
%
%\DescribeMacro{\WarningFilter*\oarg{family}\\\marg{name}\marg{message}}
%\DescribeMacro{\ErrorFilter*\oarg{family}\\\marg{name}\marg{message}}
%These are the same as the starless versions above, except that they
%target the message not as it appears in the log file but as it was produced.
%For instance, suppose you have an undefined citation, that is you
%wrote "\cite{Spinoza1677}" and \LaTeX\ complains as follows:
%
%
%\vbskip
%\bgroup
%\hfuzz=1cm
%"LaTeX Warning: Citation `Spinoza1677' on page 4 undefined on input line 320."
%
%\egroup
%\vbskip
%You know you have to bring your bibliography up to date, but right now you're
%working on a terrific idea and you don't have time to waste. So you say:
%
%\vbskip
%"\WarningFilter{latex}{Citation `Spinoza1677'}"
%\vbskip
%and everything's ok (since you've turned on the "save" option---see below---you
%will not forget to fix that reference). So you go on but then, as you're
%trying to link string theory with german philology, you stumble on that
%paper with bold new ideas and tens of fascinating references that you can't
%read but definitely have to cite. As expected, \LaTeX\ will start whining, 
%because it doesn't like undefined citations. How are you going to shut it up?
%You might try
%
%\vbskip
%"\WarningFilter{latex}{Citation}"
%\vbskip
%but that's dangerous because all warnings beginning with "Citation" will
%be filtered out, and maybe there are other types of messages which begin with 
%"Citation" and that you don't want to avoid. So the solution is "\WarningFilter*".
%Indeed, you can say:
%
%\vbskip
%"\WarningFilter*{latex}{Citation `\@citeb' on page \thepage \space undefined}"
%\vbskip
%That is, you target the way the message was produced instead of the way it appears.
%Of course, you have to know how the message was produced, but that's easy to figure
%out. In case of a \LaTeX\ message, just check the source (available on the web). In
%case the message was issued by a package or a class, just give a look at the
%corresponding files.
%
%As a rule of thumb, remember that a command that appears verbatim in the message
%was probably prefixed with "\protect", "\noexpand" or "\string", and you can try
%them all. Commands that are expanded are likely to be followed by "\space", to avoid unwanted gobbling.
%So if a message says `"You can't use \foo here"', it is likely to be produced
%with `"You can't use \protect\foo here"'. 
%
%Here's a comparison of starred and starless filters:
%
%\vbskip
%"\WarningFilter{latex}{Marginpar on page 3 moved}" will filter off marginpar
%warnings concerning page 3.
%\vbskip
%"\WarningFilter{latex}{Marginpar on page \thepage\space moved}" will be inefficient
%because it will search messages that actually look like the specified text.
%\vbskip
%"\WarningFilter*{latex}{Marginpar on page \thepage\space moved}" will filter off 
%all marginpar warnings.
%\vbskip
%"\WarningFilter*{latex}{Marginpar on page 3 moved}" will miss everything because when
%the the warning is produced the page number is not specified.
%
%\DescribeMacro{\SafeMode}\DescribeMacro{\BoldMode}
%As you might have guessed, evaluating messages as they appear means expanding them.
%Normally, this should be harmless, but one never knows. So these two commands allow
%you to turn that process on and off. When you say "\SafeMode", messages are not
%expanded. In that case, starless filters might miss their goal if the message
%contains expanded material. Starred filters are unaffected. So if you encounter
%an avalanche of unexplained error messages, just try some "\SafeMode". "\BoldMode"
%is used to switch back to the default mode. 
%
%This expansion process concerns messages, not filters. That is, there is no need
%to protect your filter definitions with "\SafeMode". Instead, use this command when
%you suspect that a message is being sent and "silence" gets everything wrong. Use
%"\BoldMode" to switch back to normal when messages are not troublesome anymore.
%Here's an example:
%
%\vbskip
%"\WarningFilter{latex}{Marginpar on page 3 moved}"\\
%"\WarningFilter*{latex}{Citation `\@citeb' undefined}"\\
%"..."\\
%"\SafeMode"\\
%"..."
%\bgroup\ttfamily\itshape\obeylines\parindent0pt%
%Here a strange message is being sent by the \emph{strangex} package.
%If \upshape"\SafeMode"\itshape\ was not active, we would be in big trouble.
%The \emph{Marginpar} warnings all go through, because our starless filter
%is too specific. Fortunately, the \emph{Citation} warnings are correctly
%avoided.
%"..."
%\egroup
%\noindent"\BoldMode"\\
%"..."\\
%\texttt{\emph{Everything is back to normal.}}
%\vbskip
%
%
%\section{Package options}
%Here are the options that may be loaded with the package:
%
%\vbskip
%\vbopt{debrief} At the end of the document, "silence" will output a
%warning saying how many messages were issued during the compilation
%and how many were filtered out. This warning will not appear if
%no message was output or if none were filtered out.
%
%\vbopt{immediate} Makes filters active as soon as they are created.
%This does not affect filters created without a family, since they
%always behave so. If a filter is created with family "myfam", and
%if that family has been previously deactivated, it will be reactivated.
%
%\vbopt{safe} Makes safe mode the default mode. "\BoldMode" and "\SafeMode"
%are still available.
%
%\vbopt{save} Messages that were filtered out are written to an 
%external file called "jobname.sil".
%
%\vbopt{saveall} All messages, including those that were left untouched,
%are written to "jobname.sil".
%
%\vbopt{showwarnings} Warnings are left untouched. That is, "silence"'s
%commands become harmless. Note that warnings are not written to
%"jobname.sil", even if the "saveall" option is loaded. This command
%is useful if you want to recompile your document as usual.
%
%\vbopt{showerrors} Same as "showwarnings" for errors.
%
%
%
%\section{It doesn't work!}
%Messages can be tricky. This package was originally designed to take
%care of marginpar warnings, and I wanted to do something like:
%
%\vbskip
%"\WarningsOff*"\\
%"\marginpar{A marginpar that will move}"\\
%"\WarningsOn"
%\vbskip
%Unfortunately, this doesn't work. Indeed, marginpar warnings are not
%issued when the "\marginpar" command is used but at the output routine,
%that is when \LaTeX\ builds the page, which happens at some interesting
%breakpoint that you're not supposed to know. That's the reason why
%those messages must be filtered out with warnings that are always
%active. Of course, this means that you can't filter out just one
%particular marginpar warning, unless it's the only one on its page.
%
%
%Now, messages aren't always what they seem to be. More precisely,
%two attributes do not really belong to them: the final full stop
%and the line number (only for some warnings). For instance, the following
%message \emph{does not} containt a full stop:
%\vbskip
%"Package packex Error: You can't do that."
%\vbskip
%The full stop is added by \LaTeX. So
%\vbskip
%"\ErrorFilter{packex}{You can't do that.}"
%\vbskip
%won't do. You have to remove the stop. This goes the same with the
%phrase `\texttt{on input line} \meta{number}.' (including the stop once
%again). That is, the message
%\vbskip
%"Package superpack Warning: Something is wrong on input line 352."
%\vbskip
%was actually produced with
%\vbskip
%"\PackageWarning{superpack}{Something is wrong}"
%\vbskip
%The end of it was added by \LaTeX. You know what you have to do.
%Unfortunately, this means that warnings can't be filtered according
%to the line they refer to.
%
%Another difficulty concerns line breaking in messages. If a new line
%begins in a message, it was either explicitely created or it's a single
%line wrapped by your text editor. When properly written, messages use
%\LaTeX's "\MessageBreak" command to create new lines, which \LaTeX\
%formats as a nicely indented line with the name of the package at the
%beginning, between parentheses. So if you encounter such a display,
%you know that there's something more behind the scene. You have two
%solutions: either you make the text of your filter shorter than
%the first line, which in most cases will be accurate enough, or
%you use a starred filter and explicitely write "\MessageBreak".
%Unfortunately, you can't use "\MessageBreak" in a starless filter.
%Note that some stupid people (including the author of this package)
%sometimes use "^^J" instead of "\MessageBreak", which is a \TeX\
%construct to create a new line. In that case, the line break
%will be indistinguishable from a single line wrapped by your text
%editor (although no wrapping occur in the log file).
%
%The most efficient filters are the starred ones (unless
%you're aiming at a specific value for a variable) whose text has simply
%been pasted from the source. E.g., if "superpack" tells you:
%
%\vbskip
%"Package superpack Error: You can't use \@terrific@command in"\\
%"(superpack)              a \@fantastic@environment, because"\\
%"(superpack)              unbelievable@parameter is off".
%\vbskip
%this was probably produced with:
%\vbskip
%"\PackageError{superpack}{"\\
%"  You can't use \protect\@terrific@command in\MessageBreak"\\
%"  a \protect\@fantastic@environment, because\MessageBreak"\\
%"  \superparameter\space is off}"\\
%"  {}"
%\vbskip
%with "\superparameter" "\def"ined to "unbelievable@parameter"
%beforehand. So the simplest way to filter out such a message
%is to open "superpack.sty", look for it, and copy it as is
%in a starred filter.
%
%
%There remains one problematic case, if a primitive
%control sequence appears in the message to be avoided.
%Imagine for instance that a package sends the warning
%`"You can't use \def here"'. It will not be reachable
%with a starless filter, because the package may have
%said "\def" without any prefix, since primitive commands
%can be used as such in messages, where they appear verbatim.
%On the other hand, when you create a starless filter with
%a command in it, "silence" considers this command simply as a string
%of characters beginning with a backslash devoid of its
%usual `escapeness'---as are control sequences prefixed
%with "\protect", "\string" and "\noexpand" in messages. So
%"\def" won't be reached. A starred filter might do, but
%in this case you shouldn't prefix "\def" with any of the
%three commands just mentioned. Now if "\def" is the result
%of an expansion, you'll be forced to rely on the previous
%techniques. Fortunately, this is very rare.
%
%Now, in case there's a message you really can't reach, although
%you pasted it in your filter, just let me know: it's an opportunity
%to refine "silence".
%
%
%
%
%
%
% \section{Implementation}
% \subsection{Basic definitions}
% \MacrocodeTopsep.5em
% \def\macskip{\par\vskip-17.7pt\vskip0cm}
% \let\sldescribemacro\DescribeMacro
% \def\DescribeMacro{\noindent\sldescribemacro}
% The options only turn on some conditionals, except "save" and
% "saveall", which set the count "\sl@save", to be used
% in a "\ifcase" statement.
%    \begin{macrocode}
\makeatletter

\newcount\sl@Save
\newif\ifsl@Debrief
\newif\ifsl@ShowWarnings
\newif\ifsl@ShowErrors
\newif\ifsl@Immediate
\newif\ifsl@SafeMode

\DeclareOption{debrief}{\sl@Debrieftrue}
\DeclareOption{immediate}{\sl@Immediatetrue}
\DeclareOption{safe}{\sl@SafeModetrue}
\DeclareOption{save}{\sl@Save1
  \newwrite\sl@Write
  \immediate\openout\sl@Write=\jobname.sil}
\DeclareOption{saveall}{\sl@Save2
  \newwrite\sl@Write
  \immediate\openout\sl@Write=\jobname.sil}
\DeclareOption{showwarnings}{\sl@ShowWarningstrue}
\DeclareOption{showerrors}{\sl@ShowErrorstrue}
\ProcessOptions\relax
%    \end{macrocode}

% Here are the counts, token lists and conditionals, that
% will be used by warnings and errors.
%    \begin{macrocode}
\newcount\sl@StateNumber
\newcount\sl@MessageCount
\newcount\sl@Casualties

\newtoks\sl@Filter
\newtoks\sl@Message
\newtoks\sl@UnexpandedMessage
\newtoks\sl@Mess@ge

\newif\ifsl@Check
\newif\ifsl@Belong
\newif\ifsl@KillMessage
\newif\ifsl@SafeTest
%    \end{macrocode}

% And here are some keywords and further definitions.
% "\sl@PackageName" is used to identify the name of the
% package, but in case "\GenericError" or "\GenericWarning"
% were directly used, it would be undefined (or defined
% with the name of the last package that issued a message),
% which would lead to some trouble, hence its definition here.
%    \begin{macrocode}
\def\sl@end{sl@end}
\def\sl@latex{latex}
\def\sl@Terminator{\sl@Terminator}
\gdef\sl@active{active}
\gdef\sl@safe{safe}
\gdef\sl@PackageName{NoPackage}
\def\SafeMode{\global\sl@SafeModetrue}
\def\BoldMode{\global\sl@SafeModefalse}
\def\sl@Gobble#1sl@end,{}
%    \end{macrocode}

% \subsection{Warnings}
% Now these are the counts, token lists, conditionals and
% keywords specific to warnings. "sl@family" is
% actually the family of those familyless filters. It
% is made active as wanted.
%    \begin{macrocode}
\newcount\sl@WarningCount
\newcount\sl@WarningNumber
\newcount\sl@WarningCasualties

\newtoks\sl@TempBOW
\newtoks\sl@BankOfWarnings

\newif\ifsl@WarningsOff
\newif\ifsl@NoLine

\expandafter\gdef\csname sl@family:WarningState\endcsname{active}
\def\sl@WarningNames{}
\def\sl@UnwantedWarnings{}
\def\sl@ProtectedWarnings{}
%    \end{macrocode}

% \subsubsection{Brutal commands}
% \DescribeMacro{\WarningsOn}
% The basic mechanism behind "\WarningsOn" and "\WarningsOff"
% is a conditional, namely "\ifsl@WarningsOff". When a warning is sent,
% "silence" checks the value of this conditional and acts
% accordingly: if it is set to "true", then the warning is
% filtered out unless it belongs to the "\sl@ProtectedWarnings"
% list; if it is set to "false", the warning is output unless
% it belongs to the "\sl@UnwantedWarnings" list.
% 
% Without argument, "\WarningsOn" empties both lists and sets
% this conditional to "false". (Emptying "\sl@ProtectedWarnings"
% is useless but it keeps the list clean.) If it has an argument,
% its behavior depends on the conditional; if it is set to
% "true", the argument is added to "\sl@ProtectedWarnings";
% otherwise, it is removed from "\sl@UnwantedWarnings".
%
%    \begin{macrocode}
\def\WarningsOn{%
  \@ifnextchar[%
  {\ifsl@WarningsOff
      \def\sl@next{\sl@Add\sl@ProtectedWarnings}%
    \else
      \def\sl@next{\sl@Remove\sl@UnwantedWarnings}%
    \fi\sl@next}%
  {\global\sl@WarningsOfffalse
  \gdef\sl@ProtectedWarnings{}%
  \gdef\sl@UnwantedWarnings{}}}
%    \end{macrocode}

% \DescribeMacro{\WarningsOff}
% "\WarningsOff" does the same as "\WarningsOn", but in
% the other way. If it has no star and no argument,
% "\sl@ProtectedWarnings" is overwritten with only
% "latex" in it.
%
%    \begin{macrocode}
\def\WarningsOff{%
  \@ifstar
  {\global\sl@WarningsOfftrue
    \gdef\sl@UnwantedWarnings{}%
    \gdef\sl@ProtectedWarnings{}}%
  {\@ifnextchar[{%
    \ifsl@WarningsOff
      \def\sl@next{\sl@Remove\sl@ProtectedWarnings}%
    \else
      \def\sl@next{\sl@Add\sl@UnwantedWarnings}%
    \fi\sl@next}%
    {\global\sl@WarningsOfftrue
    \gdef\sl@UnwantedWarnings{}%
    \gdef\sl@ProtectedWarnings{latex,}}}}
%    \end{macrocode}
%
% Note that the "\WarningsOn" and "\WarningsOff"
% don't really take any argument. If an opening bracket is
% present, they launch "\sl@Add" or "\sl@Remove" on the
% adequate list.

% \DescribeMacro{\sl@Add}
% "\sl@Add" is no more than an "\xdef" of the list
% on itself, plus the new item.
%
%    \begin{macrocode}
\def\sl@Add#1[#2]{%
  \xdef#1{#1#2,}}
%    \end{macrocode}

% \DescribeMacro{\sl@Remove}
% "\sl@Remove" is slightly more complicated. It stores
% the items to be removed and then
% launches the recursive "\sl@@Remove" on the expanded list,
% with a terminator to stop it.
% When "\sl@@Remove" has done its job, the list will
% be "\let" to the new one.
%
%    \begin{macrocode}
\def\sl@Remove#1[#2]{%
  \def\sl@Items{#2}%
  \def\sl@TempNewList{}%
  \expandafter\sl@@Remove#1sl@end,%
  \let#1\sl@TempNewList}
%    \end{macrocode}

% \DescribeMacro{\sl@@Remove}
% This macro takes each element of the list to
% be updated, checks it against the items to be
% removed, and builds a new list containing the element
% currently tested if and only if it has not been
% matched with items to be removed.
%
% First, we check the current element of the list,
% and if it's the terminator we end recursion.
%
%    \begin{macrocode}
\def\sl@@Remove#1,{%
  \def\sl@Tempa{#1}%
  \ifx\sl@Tempa\sl@end
    \let\sl@next\relax
%    \end{macrocode}
%
% \noindent Otherwise, we launch "\sl@ListCheck" on the element.
%
%    \begin{macrocode}
  \else
    \sl@Checkfalse
    \expandafter\sl@ListCheck\sl@Items,sl@end,%
%    \end{macrocode}
%
% \noindent If the check is positive, we do nothing. If not,
% we add the element to "\sl@TempNewList", which is
% itself repeated to keep the former elements.
%
%    \begin{macrocode}
    \ifsl@Check
    \else
      \xdef\sl@TempNewList{\sl@TempNewList#1,}%
    \fi
    \let\sl@next\sl@@Remove
  \fi\sl@next}%
%    \end{macrocode}

% \DescribeMacro{\sl@ListCheck}
% Here's the internal checking mechanism. It takes an argument
% (from the expansion of "\sl@Items" above) and compares
% it to the element under scrutiny. In case they match,
% the proper conditional is turned to "true", and we
% launch "\sl@Gobble" to discard remaining items. Otherwise,
% we proceed to the next item.
%
% First we check for the terminator, as usual, and
% the recursion is ended in case we find it. 
%
%    \begin{macrocode}
\def\sl@ListCheck#1,{%
  \def\sl@Tempb{#1}%
  \ifx\sl@Tempb\sl@end
    \let\sl@next\relax
%    \end{macrocode}
%
% \noindent If the item is not the terminator,
% we compare it to the current element. If they
% match, we confirm the test and gobble.
%
%    \begin{macrocode}
  \else
    \ifx\sl@Tempa\sl@Tempb
      \sl@Checktrue
      \let\sl@next\sl@Gobble
%    \end{macrocode}
%
% \noindent Otherwise we repeat.
%
%    \begin{macrocode}
    \else
      \let\sl@next\sl@ListCheck
    \fi
  \fi\sl@next}
%    \end{macrocode}

% \subsubsection{Filters}
% \DescribeMacro{\WarningFilter}
% Let's now turn to filters.
% When created, each warning filter is associated
% with a "\sl@WarningNumber" to retrieve it and to specify its
% status. In case of "\WarningFilter*", this status, referred to
% with "\csname\the\sl@WarningNumber:WarningMode\endcsname" (i.e.
% "\"\meta{number}":WarningMode", where \meta{number} is the unique
% number associated with the filter), is
% set to ``"safe"''. Otherwise, we don't bother to define it,
% because when evaluated the above command will then be
% equal to "\relax" (as are all undefined commands called with
% "\csname... \endcsname").
% This will be checked in due time to know whether
% the expanded or the unexpanded version of the target message
% must be tested.
%
%    \begin{macrocode}
\def\WarningFilter{%
  \global\advance\sl@WarningNumber1
  \@ifstar
    {\expandafter\gdef\csname\the\sl@WarningNumber:WarningMode\endcsname{safe}%
    \sl@WarningFilter}%
    {\sl@WarningFilter}}
%    \end{macrocode}

% \DescribeMacro{\sl@WarningFilter}
% Once the star has been checked, we look for an optional argument.
% In case there is none, we assign the "sl@family" to the filter.
%
%    \begin{macrocode}
\def\sl@WarningFilter{%
  \@ifnextchar[%
  {\sl@@WarningFilter}%
  {\sl@@WarningFilter[sl@family]}}
%    \end{macrocode}

% \DescribeMacro{\sl@@WarningFilter}
% Here comes the big part. First, we update the list of
% filters associated with a package, stored in "\"\meta{package}"@WarningFilter".
% The list itself is a concatenation of comma-separated pairs of
% the form \meta{number}":sl@"\meta{family} referring to filters. 
% When \meta{package} issues a warning, "silence" checks the filters
% contained in the associated list. \meta{number} is
% used to determine the "WarningMode" ("safe" or undefined, as noted above), so
% that the right test will be run, while \meta{family} is used
% to ensure that this family is active.
% 
% First we update the list:
%
%    \begin{macrocode}
\def\sl@@WarningFilter[#1]#2{%
  \expandafter\ifx\csname #2@WarningFilter\endcsname\relax
    \expandafter\xdef\csname #2@WarningFilter\endcsname{\the\sl@WarningNumber:sl@#1}%
  \else
    \expandafter\xdef\csname #2@WarningFilter\endcsname{%
      \csname #2@WarningFilter\endcsname,\the\sl@WarningNumber:sl@#1}%
  \fi
%    \end{macrocode}
%
% \noindent Now, if "\"\meta{family}":WarningState" is undefined, this means
% that this is the first time we encounter that family. So we store
% its name, which  will be useful for "\ActivateWarningFilters" and
% its deactivating counterpart.
%
%    \begin{macrocode}
  \expandafter\ifx\csname #1:WarningState\endcsname\relax
    \sl@Add\sl@WarningNames[#1]%
  \fi
%    \end{macrocode}
%
% \noindent Next, we check "WarningState" again; if it's not active, we change it,
% depending on whether we're in "immediate" mode or not.
%    \begin{macrocode}
  \expandafter\ifx\csname #1:WarningState\endcsname\sl@active
  \else
    \ifsl@Immediate
      \expandafter\gdef\csname #1:WarningState\endcsname{active}%
    \else
      \expandafter\gdef\csname #1:WarningState\endcsname{inactive}%
    \fi
  \fi
%    \end{macrocode}
%
% \noindent Finally, we prepare the storage of the filter's message.
% We open a group, and if the filter is starred, we turn "@" into
% a letter, so that it will be able to form macro names.
% If the filter has no star, we turn the escape character into
% a normal one, so that control sequences won't be formed. Messages
% will then be tested token by token, and if a message contains e.g. "\foo",
% then it has (most likely) been "\string"'ed, so it's really a sequence of characters
% with "\catcode" 12 (including the escape character) that the starless
% filter will match.
%    \begin{macrocode}
  \begingroup
  \expandafter\ifx\csname\the\sl@WarningNumber:WarningMode\endcsname\sl@safe
    \makeatletter
  \else
    \catcode`\\12
  \fi
  \sl@AddToBankOfWarnings}
%    \end{macrocode}

% \DescribeMacro{\sl@AddToBankOfWarnings}
% Now we add the filter to the "\sl@BankOfWarnings" token list,
% delimited by "(:sl@"\meta{number}":)", where \meta{number}
% is the number assigned to it above. Thus, it might be easily
% retrieved. Following a technique explained by Victor Eijkhout
% in \emph{\TeX\ by Topic}, we make use of "\edef" to add an item
% to a token list. First, we store it in the temporary "\sl@TempBOW"
% token list. Then we "\edef" "\sl@act" such that its definition
% will be:
%
% \vbskip
% "\sl@BankOfWarnings{"\meta{previous content}"(:sl@"\meta{number}":)"\meta{filter}"(:sl@"\meta{number}":)}"
% \vbskip
% Thus, the "\sl@BankOfWarnings" token list will add the new filter
% to itself. This works because token lists prefixed with "\the" in an "\edef"
% expand to their content unexpanded, unlike normal macros. The macro
% is made "\long" just in case.
%
%    \begin{macrocode}
\long\def\sl@AddToBankOfWarnings#1{%
  \sl@TempBOW{#1}%
  \edef\sl@act{%
  \global\noexpand\sl@BankOfWarnings{%
    \the\sl@BankOfWarnings
    (:sl@\the\sl@WarningNumber:)\the\sl@TempBOW(:sl@\the\sl@WarningNumber:)}}%
  \sl@act
  \endgroup}
%    \end{macrocode}

% \DescribeMacro{\ActivateWarningFilters}
% \DescribeMacro{\DeactivateWarningFilters}
% This macro launches a recursive redefinition
% of its expanded argument, which is either a list
% defined by the user or the list containing
% all warning families (updated in "\WarningFilter"
% above). We set "\sl@StateNumber" to register
% what the redefinition should be: activate or deactivate "WarningState",
% or activate or deactivate "ErrorState".
%
%    \begin{macrocode}
\def\ActivateWarningFilters{%
  \sl@StateNumber0\relax
  \@ifnextchar[%
  {\sl@ChangeState}%
  {\sl@ChangeState[\sl@WarningNames]}}

\def\DeactivateWarningFilters{%
  \sl@StateNumber1\relax
  \@ifnextchar[%
  {\sl@ChangeState}%
  {\sl@ChangeState[\sl@WarningNames]}}
%    \end{macrocode}

% \DescribeMacro{\sl@ChangeState}
% \DescribeMacro{\sl@@ChangeState}
% "\sl@ChangeState" only calls "\sl@@ChangeState" on
% the expanded argument, adding a terminator.
% "\sl@@ChangeState" checks whether its argument
% is the terminator, in which case it stops, or else
% it sets its state to the appropriate value, depending
% on "\sl@StateNumber"
%
%    \begin{macrocode}
\def\sl@ChangeState[#1]{%
  \expandafter\sl@@ChangeState#1,sl@end,}

\def\sl@@ChangeState#1,{%
  \def\sl@Tempa{#1}%
  \ifx\sl@Tempa\sl@end
    \let\sl@next\relax
  \else
    \ifcase\sl@StateNumber
      \expandafter\gdef\csname #1:WarningState\endcsname{active}%
    \or
      \expandafter\gdef\csname #1:WarningState\endcsname{inactive}%
    \or
      \expandafter\gdef\csname #1:ErrorState\endcsname{active}%
    \or
      \expandafter\gdef\csname #1:ErrorState\endcsname{inactive}%
    \fi
    \let\sl@next\sl@@ChangeState
  \fi\sl@next}
%    \end{macrocode}

% \DescribeMacro{\ActivateFilters}
% \DescribeMacro{\DeactivateFilters}
% This aren't just shorthands, something more
% is needed in case there's an argument (because we
% have to retrieve it to pass it to two macros).
% However, it's rather straightforward, we're just
% using "\sl@StateNumber" to keep track of what
% is needed.
%
% \DescribeMacro{\sl@RetrieveArgument}
% Here we just rely on the value required for
% "WarningState", i.e. 0 or 1, and the value
% for "ErrorState" follows suit.
%
%    \begin{macrocode}
\def\ActivateFilters{%
  \@ifnextchar[%
  {\sl@StateNumber0
  \sl@RetrieveArgument}%
  {\sl@StateNumber0
  \sl@ChangeState[\sl@WarningNames]%
  \sl@StateNumber2
  \sl@ChangeState[\sl@ErrorNames]}}

\def\DeactivateFilters{%
  \@ifnextchar[%
  {\sl@StateNumber1
  \sl@RetrieveArgument}%
  {\sl@StateNumber1
  \sl@ChangeState[\sl@WarningNames]%
  \sl@StateNumber3
  \sl@ChangeState[\sl@ErrorNames]}}

\def\sl@RetrieveArgument[#1]{%
  \def\sl@Argument{#1}%
  \ifcase\sl@StateNumber
    \sl@ChangeState[\sl@Argument]%
    \sl@StateNumber2\relax
    \sl@ChangeState[\sl@Argument]%
  \or
    \sl@ChangeState[\sl@Argument]%
    \sl@StateNumber3\relax
    \sl@ChangeState[\sl@Argument]%
  \fi}
%    \end{macrocode}

%
% 
% \subsubsection{String testing}
% Now, here comes the crux of the biscuit, as some guitarist from
% California once said. Here are the macros to test the messages.
% They will be used in the redefinition of "\GenericWarning".
%
% \DescribeMacro{\sl@GetNumber}
% When packages send a warning, "silence" launches "\sl@GetNumber"
% on the expanded list of \meta{number}":sl@"\meta{family} pairs associated
% with this package (created in "\WarningFilter" above). Remember that \meta{number}
% refers to a filter and \meta{family} to its family.
%
% If \meta{number} is not 0 (which is associated with the terminator
% added when "\sl@GetNumber" is launched),
% we test whether the family is active. If so, "\sl@GetMessage" is
% called; otherwise, we proceed to the next pair.
%
% The command
% \vbskip
% "\csname #2:\ifcase\sl@StateNumber Warning\or Error\fi State\endcsname"
% \vbskip
% reduces to "\"\meta{family}":WarningState" or "\"\meta{family}":ErrorState",
% depending on the value of "\sl@StateNumber", which is turned
% to 0 if we're testing a warning or to 1 if we're testing an error.
%
%    \begin{macrocode}
\def\sl@GetNumber#1:sl@#2,{%
  \ifnum#1>0
    \expandafter
    \ifx\csname #2:\ifcase\sl@StateNumber Warning\or Error\fi State\endcsname\sl@active
      \sl@GetMessage{#1}%
    \else
      \let\sl@next\sl@GetNumber
    \fi
  \else
    \let\sl@next\relax
  \fi\sl@next}
%    \end{macrocode}

% \DescribeMacro{\sl@GetMessage}
% \noindent Now we're going to retrieve the filter from the "\sl@BankOfWarnings"
% token list. This list has the following structure, as you might remember:
% \vbskip
% "(:sl@1:)"\meta{first filter}"(:sl@1:)(:sl@2:)"\meta{second filter}"(:sl@2:)...(:sl@n:)"\meta{\emph{n}th filter}"(:sl@n:)"
% \vbskip
% To do so, "\sl@GetMessage" defines a new macro on the fly, "\sl@@GetMessage",
% which takes three arguments, delimited by "(:sl@"\meta{number}":)",
% where \meta{numbers} depends on the pair we're in. That is, suppose
% "\sl@GetNumber" is examining the pair "15:sl@myfam" and "myfam"
% is active. Then we feed "15" to "\sl@GetMessage", which in turn
% creates "\sl@@GetMessage" with the arguments delimited by "(:sl@15:)".
% The first and the third arguments are discarded, while the second
% one, which is the message of the filter, is stored in the "\sl@Filter"
% token list, along with a terminator.
%
%    \begin{macrocode}
\def\sl@GetMessage#1{%
  \def\sl@@GetMessage##1(:sl@#1:)##2(:sl@#1:)##3(:sl@end:){\sl@Filter={##2\sl@Terminator}}%
%    \end{macrocode}
%
% \noindent Now we launch this command on the bank of warnings (or errors)
% expanded one step, thanks to the same "\edef" trick as above.
%
%    \begin{macrocode}
  \ifcase\sl@StateNumber
    \edef\sl@act{\noexpand\sl@@GetMessage\the\sl@BankOfWarnings(:sl@end:)}%
  \or
    \edef\sl@act{\noexpand\sl@@GetMessage\the\sl@BankOfErrors(:sl@end:)}%
  \fi
  \sl@act
%    \end{macrocode}
%
% \noindent When a warning is sent, its message is stored in two forms,
% expanded and unexpanded. Depending on the mode we're currently in
% (safe or bold), we retrieve the right form; in safe mode, we take
% the unexpanded form; in bold mode, we take it too if the "WarningMode"
% of this particular filter is "safe" (i.e. if it was created with
% "\WarningFilter*"), otherwise we take the expanded version.
%
% "\sl@Message" is the token list containing the expanded version,
% "\sl@UnexpandedMessage" contains the unexpanded version, and "\sl@Mess@ge"
% stores the adequate version for the test to come followed by a terminator.
%
% At the end of this macro, we call the string tester.
%
%    \begin{macrocode}
  \ifsl@SafeMode
    \sl@SafeTesttrue
    \edef\sl@act{\noexpand\sl@Mess@ge{\the\sl@UnexpandedMessage\noexpand\sl@Terminator}}%
  \else
    \expandafter
    \ifx\csname #1:\ifcase\sl@StateNumber Warning\or Error\fi Mode\endcsname\sl@safe%
      \sl@SafeTesttrue
      \edef\sl@act{\noexpand\sl@Mess@ge{\the\sl@UnexpandedMessage\noexpand\sl@Terminator}}%
    \else
      \sl@SafeTestfalse
      \edef\sl@act{\noexpand\sl@Mess@ge{\the\sl@Message\noexpand\sl@Terminator}}%
    \fi
  \fi
  \sl@act
  \sl@TestStrings}
%    \end{macrocode}

% \DescribeMacro{\sl@TestStrings}
% This test is a recursive token by token comparison
% of "\sl@Filter" and "\sl@Message", i.e. it compares 
% two strings.
%
% First we take the first token of each token list
% thanks, once again, to an "\edef". They are stored
% in "\sl@FilterToken" and "\sl@MessageToken", which
% are macros, not token lists, by the way (see below
% the definition of "\sl@Slice").
%
%    \begin{macrocode}
\def\sl@TestStrings{%
  \edef\sl@act{%
  \noexpand\sl@Slice\the\sl@Filter(:sl@mid:)\noexpand\sl@Filter\noexpand\sl@FilterToken
  \noexpand\sl@Slice\the\sl@Mess@ge(:sl@mid:)\noexpand\sl@Mess@ge\noexpand\sl@MessageToken}%
  \sl@act
%    \end{macrocode}
%
% \noindent Then we simply run some conditional.
% If we reach the terminator in the filter, this means
% that it matches the warning (otherwise we wouldn't have
% gone so far), so we turn a very sad conditional to "true",
% stop the recursion of "\sl@TestString" (thanks to "\sl@@next")
% and gobble the remaining \meta{number}":sl@"\meta{family} pairs waiting to be evaluated
% by "\sl@GetNumber" (thanks to "\sl@next"). Our job is done,
% and "silence" grins with cruelty.
%
%    \begin{macrocode}
  \ifx\sl@FilterToken\sl@Terminator
    \sl@KillMessagetrue
    \let\sl@@next\relax
    \let\sl@next\sl@Gobble
%    \end{macrocode}
%
% \noindent On the other hand, if we reach the terminator in the warning text,
% this means that it is shorter than the filter (unless we also
% reach the terminator in the filter, but this was taken care of
% in the previous case), so they don't match. We stop recursion
% (there's nothing left to test) and make "\sl@GetNumber" consider
% the following pair.
%
%    \begin{macrocode}
  \else
    \ifx\sl@MessageToken\sl@Terminator
      \let\sl@@next\relax
      \let\sl@next\sl@GetNumber
%    \end{macrocode}
%
% \noindent Now, if none of the tokens is a terminator, then we
% have to compare them. The test will depend on the value
% of "\ifsl@SafeTest" which was turned to "true" in case
% we're in safe mode or the filter is a starred one. In that
% case we run an "\ifx" test, so that even control sequences
% can be properly compared. Since the message is not expanded,
% this is vital. If the tokens match, we proceed to the next
% ones; otherwise, this means that the filter and the message
% are different, so we stop recursion (there's no reason to
% test further), and we call "\sl@GetNumber" on the next pair.
%
%    \begin{macrocode}
    \else
      \ifsl@SafeTest
        \ifx\sl@FilterToken\sl@MessageToken
          \let\sl@@next\sl@TestStrings
        \else
          \let\sl@@next\relax
          \let\sl@next\sl@GetNumber
        \fi
%    \end{macrocode}
%
% \noindent If we're in bold mode and the filter is starless,
% then we simply compare character codes with "\if". Thus,
% the letters of, say, "\foo", in the filter, will match
% their counterpart in the warning (where the command has
% probably been "\string"'ed), although their category codes
% are different: it's 11 in the filter (no control sequence
% was ever created: "\" was turned to a normal character
% before the filter was stored) and 12 in the message
% (like all "\string"'ed characters).
%
%    \begin{macrocode}
      \else
        \if\sl@FilterToken\sl@MessageToken
          \let\sl@@next\sl@TestStrings
        \else
          \let\sl@@next\relax
          \let\sl@next\sl@GetNumber
        \fi
      \fi
    \fi
  \fi\sl@@next}
%    \end{macrocode}

% \DescribeMacro{\sl@Slice}
% And here's the final cog in this testing. To put it quite
% unintelligibly, "\sl@Slice" defines its fourth argument
% to expand to the first, while the third, which is a
% token list, is set to the second, and you should not
% forget that the first two arguments are just an expansion
% of the third. Copy that?
% 
% Let's get things clear. Remember that "\sl@act" at the 
% beginning of "\sl@TestStrings" above was "\edef"ined to:
% \vbskip
% "\noexpand\sl@Slice\the\sl@Filter(:sl@mid:)\noexpand\sl@Filter\noexpand\sl@FilterToken"
% \vbskip
% and similarly for the message. This means that its definition
% text is:
% \vbskip
% "\sl@Slice"\meta{content of $\backslash$\texttt{\emph{sl@Filter}}}"(:sl@mid:)\sl@Filter\sl@FilterToken"
% \vbskip
% The first argument of "\sl@Slice" is undelimited.
% This means that it will be the first token of 
% \meta{content of $\backslash$\texttt{\emph{sl@Filter}}}.
% Its second argument will be the rest of this content.
% Now, as explained, "\sl@Slice" defines its fourth argument,
% namely "\sl@FilterToken", to expand to its first one, namely
% the first token in the "\sl@Filter" token list, and
% sets this token list to the second argument, i.e. the to
% rest of itself. In short, we're emptying "\sl@Filter" token by
% token and we compare them along the way, as described above.
% 
%    \begin{macrocode}    
\def\sl@Slice#1#2(:sl@mid:)#3#4{\def#4{#1}#3={#2}}
%    \end{macrocode}

% \subsubsection{Redefining warnings}
% \DescribeMacro{\sl@Belong}
% We have two more macros to create before we can redefine
% warnings themselves.
% "\sl@Belong" is used to check whether a package belongs
% to the "\sl@UnwantedWarnings" list or the "\sl@ProtectedWarnings" list
% (correspondingly for errors). Its argument is an item of those lists
% expanded, that is, the name of a package. It is
% compared to "\sl@PackageName", which is defined
% to the name of the package sending the message.
% If they don't match, we relaunch the command on
% the next item. If they do, we turn "\ifsl@Belong"
% to "true" and gobble the following items.
%
%    \begin{macrocode}  
\def\sl@Belong#1,{%
  \def\sl@Tempa{#1}%
  \ifx\sl@Tempa\sl@end
    \let\sl@next\relax
  \else
    \ifx\sl@Tempa\sl@PackageName
      \sl@Belongtrue
      \let\sl@next\sl@Gobble
    \else
      \let\sl@next\sl@Belong
    \fi
  \fi\sl@next}
%    \end{macrocode}

% \DescribeMacro{\sl@StoreMessage}
% Finally, we need a mechanism to store the message
% being sent. In safe mode, we store it unexpanded.
% In bold mode, we also store it unexpanded for
% starred filters, but we also store an expanded version
% where "\protect" and "\noexpand" are "\let" to
% "\string", so that the control sequences they
% prefix will be turned into sequences of characters
% (remember that no control sequence is formed in the
% text of a starless filter, only strings of characters). 
% This expanded version is first stripped of a "\@gobbletwo"
% suffix, if any, thus avoiding error when "\edef"ing.
% (The "\@gobbletwo" occurs in some \LaTeX\ messages for
% some obscure reason.) ... And at least in biblatex "\@gobble"
% was also found, which also ruined everything, so it is removed
% too if found at the end of a message.
% We do this in a group because, well, you know,
% you shouldn't do that...
%
%    \begin{macrocode}
\def\sl@RemoveGobble#1\@gobble\sl@Terminator#2\sl@Terminator{%
  \def\sl@Tempb{#2}%
  \ifx\sl@Tempb\@empty
  \else
    \def\sl@Tempa{#1}%
    \expandafter\@gobble
  \fi
  }

\def\sl@RemoveGobbletwo#1\@gobbletwo\sl@Terminator#2\sl@Terminator{%
  \def\sl@Tempb{#2}%
  \ifx\sl@Tempb\@empty
  \else
    \def\sl@Tempa{#1}%
    \expandafter\@gobble
  \fi
  }

\def\sl@StoreMessage#1{%
  \ifsl@SafeMode
    \sl@UnexpandedMessage{#1}%
  \else
    \sl@UnexpandedMessage{#1}%
    \bgroup
    \let\protect\string
    \let\noexpand\string
    \def\sl@Tempa{#1}%
    \sl@RemoveGobble#1\sl@Terminator\@gobble\sl@Terminator\sl@Terminator
    \sl@RemoveGobbletwo#1\sl@Terminator\@gobbletwo\sl@Terminator\sl@Terminator
    \edef\sl@Tempa{\sl@Tempa}%
    \global\expandafter\sl@Message\expandafter{\sl@Tempa}%
    \egroup
  \fi}
%    \end{macrocode}

% Now we're ready for the big redefinitions.
% First, if the "showwarnings" option is on, we
% simply redefine nothing, otherwise we begin by retrieving the current definitions
% of the commands used to issue warnings, in order
% to patch them. For instance, we "\let" "\sl@PackageWarning"
% to "\PackageWarning", and thus we'll be able to
% write:
% 
% \vbskip
% "\def\PackageWarning#1#2{%"\\
% "  "\meta{additional code}\\
% "  \sl@PackageWarning{#1}{#2}}"
% \vbskip
% and this will launch "\sl@PackageWarning", i.e. the
% original definition of "\PackageWarning", which will
% do the job it was meant to do in the first place.
% 
% For "\GenericWarning",
% this needs some hacking. Indeed "\GenericWarning" is robust,
% which means that it actually does nothing except
% calling "\protect\GenericWarning"\meta{space}, where
% "\GenericWarning"\meta{space} is defined to
% issue the warning as wanted.
% Thus, if we simply 
% "\let" "\sl@GenericWarning" to "\GenericWarning" and
% write:
% 
% \vbskip
% "\DeclareRobustCommand{\GenericWarning}[2]{%"\\
% "  "\meta{additional code}\\
% "  \sl@GenericWarning{#1}{#2}}"
% \vbskip
% then, because of the robust declaration, "\GenericWarning" 
% will be defined to "\protect" "\GenericWarning"\meta{space},
% whose definition is the additional code followed by 
% "\sl@GenericWarning" which, because it was "\let" to
% the older version of "\GenericWarning", expands to
% "\GenericWarning"\meta{space}---that is, we enter an
% infinite loop. The solution is to "\let" "\sl@GenericWarning"
% directly to "\GenericWarning"\meta{space}.
%
%    \begin{macrocode}
\ifsl@ShowWarnings
\else
\expandafter\let\expandafter\sl@GenericWarning\csname GenericWarning \endcsname
\let\sl@PackageWarning\PackageWarning
\let\sl@ClassWarning\ClassWarning
\let\sl@latex@warning\@latex@warning
\let\sl@font@warning\@font@warning
%    \end{macrocode}

% \DescribeMacro{\PackageWarning}
% We redefine "\PackageWarning" so that it stores
% the name of the package calling it, and the message only
% if a certain conditional is true, that is if it
% has not been sent with "\PackageWarningNoLine"
%
%    \begin{macrocode}
\def\PackageWarning#1#2{%
  \def\sl@PackageName{#1}%
  \ifsl@NoLine
    \sl@NoLinefalse
  \else
    \sl@StoreMessage{#2}%
  \fi
  \sl@PackageWarning{#1}{#2}}
%    \end{macrocode}

% \DescribeMacro{\PackageWarningNoLine}
% For "\PackageWarningNoLine", we simply store the message
% and send it to "\PackageWarning" with an additional "\@gobble"
% to discard the `"on input line..."' phrase added by \LaTeX.
% (This "\@gobble" was already in the original definition, there
% is nothing new here.)
%
%    \begin{macrocode}
\def\PackageWarningNoLine#1#2{%
  \sl@StoreMessage{#2}%
  \sl@NoLinetrue
  \PackageWarning{#1}{#2\@gobble}}
%    \end{macrocode}

% \DescribeMacro{\ClassWarning}\DescribeMacro{\ClassWarningNoLine}
% \DescribeMacro{\@latex@warning}\DescribeMacro{\@latex@warning@no@line}
% \DescribeMacro{\@font@warning}
% We do exactly the same for class warnings and \LaTeX\ warnings, except
% that in the latter case we manually set "\sl@PackageName" to "latex".
%
%    \begin{macrocode}
\def\ClassWarning#1#2{%
  \def\sl@PackageName{#1}%
  \ifsl@NoLine
    \sl@NoLinefalse
  \else
    \sl@StoreMessage{#2}%
  \fi
  \sl@ClassWarning{#1}{#2}}

\def\ClassWarningNoLine#1#2{%
  \sl@StoreMessage{#2}%
  \sl@NoLinetrue
  \ClassWarning{#1}{#2\@gobble}}

\def\@latex@warning#1{%
  \def\sl@PackageName{latex}%
  \ifsl@NoLine
    \sl@NoLinefalse
  \else
    \sl@StoreMessage{#1}%
  \fi
  \sl@latex@warning{#1}}

\def\@latex@warning@no@line#1{%
  \sl@StoreMessage{#1}%
  \sl@NoLinetrue
  \@latex@warning{#1\@gobble}}

\def\@font@warning#1{%
  \def\sl@PackageName{latexfont}%
  \sl@StoreMessage{#1}%
  \sl@font@warning{#1}}
%    \end{macrocode}

% \DescribeMacro{\GenericWarning}
% Now we can redefine "\GenericWarning". Originally, a message
% sent with, for instance, "\PackageWarning", is edited, and sent to
% "\GenericWarning", which edits it further and sends it to
% the log file. None of this is modified here, although
% some names have changed. Indeed, "\PackageWarning"
% now stores the name of the package and the message
% but then calls "\sl@PackageWarning" on them, which has
% been "\let" to the previous value of "\PackageWarning".
% So the message is formatted as usual and sent to
% "\GenericWarning" which, if the message is not filtered out,
% will launch "\sl@GenericWarning" on the same arguments
% and thus \LaTeX's original mechanism will pass unaffected---albeit
% somewhat spied upon... The original command is robust, so
% we make it robust too.
%
% First, we increment "\sl@WarningCount", which will be used
% if the "debrief" option is on. We also restore some conditional
% to their default values.
%
%    \begin{macrocode}
\DeclareRobustCommand{\GenericWarning}[2]{%
  \global\advance\sl@WarningCount1
  \sl@KillMessagefalse
  \sl@Belongfalse
%    \end{macrocode}
%
% \noindent Next, we launch "\sl@Belong" on the expanded
% list of protected warnings if warnings are off, or on
% the expanded list of unwanted warnings if they're on. Depending
% on the result (see the comment on "\WarningsOn" and "\WarningsOff"
% above), we might sentence the message to death. (Turn on
% the "save" option, please, so it may live a happy afterlife
% in "jobname.sil".)
%
%    \begin{macrocode}
  \ifsl@WarningsOff
    \expandafter\sl@Belong\sl@ProtectedWarnings sl@end,%
    \ifsl@Belong
    \else
      \sl@KillMessagetrue
    \fi
  \else
    \expandafter\sl@Belong\sl@UnwantedWarnings sl@end,%
    \ifsl@Belong
      \sl@KillMessagetrue
    \fi
  \fi
%    \end{macrocode}
%
% \noindent If the preceding operation is not enough,
% we check whether some filters are associated with the
% package sending the message (in which case "\"\meta{package}"@WarningFilter"
% is defined and contains \meta{number}":sl@"\meta{family} pairs).
% If there are some, we test them with "\sl@GetNumber" as defined above.
%
%    \begin{macrocode}
  \ifsl@KillMessage
  \else
    \expandafter\ifx\csname\sl@PackageName @WarningFilter\endcsname\relax
    \else
      \sl@StateNumber0
      \expandafter\expandafter\expandafter
      \sl@GetNumber\csname\sl@PackageName @WarningFilter\endcsname,0:sl@sl@end,%
    \fi
  \fi
%    \end{macrocode}
%
% \noindent Now the message's fate is sealed. If it has
% been doomed to filtering, we grimly step a sad sad counter.
% Otherwise, we happily sends everything to "\sl@GenericWarning"
% to enlighten the user's log file.
%
%    \begin{macrocode}
  \ifsl@KillMessage
    \global\advance\sl@WarningCasualties1
  \else
    \sl@GenericWarning{#1}{#2}%
  \fi
%    \end{macrocode}
%
% \noindent Finally, we consider saving it. "\sl@Save" is 0
% by default, 1 if the "save" option is on, and 2 if "saveall"
% is on. We act accordingly: case 0 does nothing, case 1 sends
% the message to "jobname.sil" if it has been filtered out,
% and case 2 sends all messages. Instead of writing
% everything out properly, we simply `re-route' "\sl@GenericWarning"
% by "\let"ting "\@unused", \LaTeX's output stream, to "\sl@Write",
% "silence"'s output stream directed to "jobname.sil". We do this
% locally, of course.
%
%    \begin{macrocode}
  \ifcase\sl@Save
  \or
    \ifsl@KillMessage
      \bgroup
      \let\@unused\sl@Write
      \sl@GenericWarning{#1}{#2}%
      \egroup
    \fi
  \or
    \bgroup
    \let\@unused\sl@Write
    \sl@GenericWarning{#1}{#2}%
    \egroup
  \fi
%    \end{macrocode}
%
% \noindent Before closing, we set "\sl@PackageName" to
% "NoPackage", just in case (very unlikely, to be sure)
% a package uses "\GenericWarning" directly. Thus, the
% former value of "\sl@PackageName" won't interfere. The final
% "\fi" matches "\ifsl@ShowWarnings" some hundred lines
% above.
%
%    \begin{macrocode}
  \gdef\sl@PackageName{NoPackage}}%
\fi
%    \end{macrocode}


% \subsection{Errors}
% Errors are implemented just like warnings, so I don't
% comment the code. See you at the end of the package
% for the last macro.
%
%    \begin{macrocode}
\newcount\sl@ErrorCount
\newcount\sl@ErrorNumber
\newcount\sl@ErrorCasualties

\newtoks\sl@TempBOE
\newtoks\sl@BankOfErrors

\newif\ifsl@ErrorsOff

\expandafter\gdef\csname sl@family:ErrorState\endcsname{active}
\expandafter\gdef\csname sl@end:ErrorState\endcsname{active}
\def\sl@ErrorNames{}
\def\sl@UnwantedErrors{}
\def\sl@ProtectedErrors{}
%    \end{macrocode}

% \DescribeMacro{\ErrorsOn}
% \macskip
%    \begin{macrocode}
\def\ErrorsOn{%
  \@ifnextchar[%
  {\ifsl@ErrorsOff
      \def\sl@next{\sl@Add\sl@ProtectedErrors}%
    \else
      \def\sl@next{\sl@Remove\sl@UnwantedErrors}%
    \fi\sl@next}%
  {\global\sl@ErrorsOfffalse
  \gdef\sl@ProtectedErrors{}%
  \gdef\sl@UnwantedErrors{}}}
%    \end{macrocode}

% \DescribeMacro{\ErrorsOff}
% \macskip
%    \begin{macrocode}
\def\ErrorsOff{%
  \@ifstar
  {\global\sl@ErrorsOfftrue
    \gdef\sl@UnwantedErrors{}%
    \gdef\sl@ProtectedErrors{}}%
  {\@ifnextchar[{%
    \ifsl@ErrorsOff
      \def\sl@next{\sl@Remove\sl@ProtectedErrors}%
    \else
      \def\sl@next{\sl@Add\sl@UnwantedErrors}%
    \fi\sl@next}%
    {\global\sl@ErrorsOfftrue
    \gdef\sl@UnwantedErrors{}%
    \gdef\sl@ProtectedErrors{latex,}}}}
%    \end{macrocode}

% \DescribeMacro{\ErrorFilter}
% \DescribeMacro{\sl@ErrorFilter}
% \DescribeMacro{\sl@@ErrorFilter}
% \macskip
%    \begin{macrocode}
\def\ErrorFilter{%
  \global\advance\sl@ErrorNumber1
  \@ifstar
    {\expandafter\gdef\csname\the\sl@ErrorNumber:ErrorMode\endcsname{safe}\sl@ErrorFilter}%
    {\sl@ErrorFilter}}

\def\sl@ErrorFilter{%
  \@ifnextchar[%
  {\sl@@ErrorFilter}%
  {\sl@@ErrorFilter[sl@family]}}

\def\sl@@ErrorFilter[#1]#2{%
  \expandafter\ifx\csname #2@ErrorFilter\endcsname\relax
    \expandafter\xdef\csname #2@ErrorFilter\endcsname{\the\sl@ErrorNumber:sl@#1}%
  \else
    \expandafter\xdef\csname #2@ErrorFilter\endcsname{%
      \csname #2@ErrorFilter\endcsname,\the\sl@ErrorNumber:sl@#1}%
  \fi
  \expandafter\ifx\csname #1:ErrorState\endcsname\relax
    \sl@Add\sl@ErrorNames[#1]%
  \fi
  \expandafter\ifx\csname #1:ErrorState\endcsname\sl@active
  \else
    \ifsl@Immediate
      \expandafter\gdef\csname #1:ErrorState\endcsname{active}%
    \else
      \expandafter\gdef\csname #1:ErrorState\endcsname{inactive}%
    \fi
  \fi
  \begingroup
  \expandafter\ifx\csname\the\sl@ErrorNumber:ErrorMode\endcsname\sl@safe
    \makeatletter
  \else
     \catcode`\\12
  \fi
  \sl@AddToBankOfErrors}
%    \end{macrocode}

% \DescribeMacro{\sl@AddToBankOfErrors}
% \macskip
%    \begin{macrocode}
\long\def\sl@AddToBankOfErrors#1{%
  \sl@TempBOE{#1}%
  \edef\sl@act{%
    \global\noexpand\sl@BankOfErrors{%
    \the\sl@BankOfErrors
    (:sl@\the\sl@ErrorNumber:)\the\sl@TempBOE(:sl@\the\sl@ErrorNumber:)}}%
  \sl@act
  \endgroup}
%    \end{macrocode}

% \DescribeMacro{\ActivateErrorFilters}
% \macskip
%    \begin{macrocode}
\def\ActivateErrorFilters{%
  \sl@StateNumber2
  \@ifnextchar[%
  {\sl@ChangeState}%
  {\sl@ChangeState[\sl@ErrorNames]}}
%    \end{macrocode}

% \DescribeMacro{\DeactivateErrorFilters}
% \macskip
%    \begin{macrocode}
\def\DeactivateErrorFilters{%
  \sl@StateNumber3
  \@ifnextchar[%
  {\sl@ChangeState}%
  {\sl@ChangeState[\sl@ErrorNames]}}

\ifsl@ShowErrors
\else
\expandafter\let\expandafter\sl@GenericError\csname GenericError \endcsname
\let\sl@PackageError\PackageError
\let\sl@ClassError\ClassError
\let\sl@latex@error\@latex@error
%    \end{macrocode}

% \DescribeMacro{\PackageError}
% \DescribeMacro{\ClassError}
% \DescribeMacro{\@latex@error}
% \macskip
%    \begin{macrocode}
\def\PackageError#1#2#3{%
  \def\sl@PackageName{#1}%
  \sl@StoreMessage{#2}%
  \sl@PackageError{#1}{#2}{#3}}

\def\ClassError#1#2#3{%
  \def\sl@PackageName{#1}%
  \sl@StoreMessage{#2}%
  \sl@ClassError{#1}{#2}{#3}}

\def\@latex@error#1#2{%
  \def\sl@PackageName{latex}%
  \sl@StoreMessage{#1}%
  \sl@latex@error{#1}{#2}}
%    \end{macrocode}

% \DescribeMacro{\GenericError}
% \macskip
%    \begin{macrocode}
\DeclareRobustCommand{\GenericError}[4]{%
  \global\advance\sl@ErrorCount1
  \sl@KillMessagefalse
  \sl@Belongfalse
  \ifsl@ErrorsOff
    \expandafter\sl@Belong\sl@ProtectedErrors,sl@end,%
    \ifsl@Belong
    \else
      \sl@KillMessagetrue
    \fi
  \else
    \expandafter\sl@Belong\sl@UnwantedErrors,sl@end,%
    \ifsl@Belong
      \sl@KillMessagetrue
    \fi
  \fi
  \ifsl@KillMessage
  \else
    \expandafter\ifx\csname\sl@PackageName @ErrorFilter\endcsname\relax
    \else
      \sl@StateNumber1
      \expandafter\expandafter\expandafter
      \sl@GetNumber\csname\sl@PackageName @ErrorFilter\endcsname,0:sl@sl@end,%
    \fi
  \fi
  \ifsl@KillMessage
    \global\advance\sl@ErrorCasualties1
  \else
    \sl@GenericError{#1}{#2}{#3}{#4}%
  \fi
  \ifcase\sl@Save
  \or
    \ifsl@KillMessage
      \bgroup
      \let\@unused\sl@Write
      \sl@GenericError{#1}{#2}{#3}{#4}%
      \egroup
    \fi
  \or
    \bgroup
    \let\@unused\sl@Write
    \sl@GenericError{#1}{#2}{#3}{#4}%
    \egroup
  \fi
  \gdef\sl@PackageName{NoPackage}}%
  \fi
%    \end{macrocode}
%
% \subsection{Debrief}
% Finally, at the end of the document, we
% issue a debrief if the user requested it.
%
% A "\clearpage" is needed because we want
% the final output routine to be processed so
% we don't miss the last messages if there are
% some.
% 
%    \begin{macrocode}
\AtEndDocument{%
  \ifsl@Debrief
    \clearpage
%    \end{macrocode}
%
% \noindent Then we do some arithmetics and if messages
% appeared and some of them were filtered out,
% we output the warning.
%
% And we say goodbye.
%
%    \begin{macrocode}
    \sl@MessageCount\sl@WarningCount
    \advance\sl@MessageCount\sl@ErrorCount
    \sl@Casualties\sl@WarningCasualties
    \advance\sl@Casualties\sl@ErrorCasualties
    \ifnum\sl@MessageCount>0
      \ifnum\sl@Casualties>0
        \advance\sl@WarningCount-1
        \PackageWarningNoLine{silence}{%
        There were \the\sl@WarningCount\space warning(s)
        and \the\sl@ErrorCount\space error(s).\MessageBreak
        \ifnum\sl@Casualties=\sl@MessageCount
          None survived. This is a violent world%
        \else
          I've killed \the\sl@WarningCasualties\space warning(s)
          and \the\sl@ErrorCasualties\space error(s)%
        \fi}%
      \fi
    \fi
  \fi}

\makeatother
%    \end{macrocode}
