% \iffalse meta-comment % %% File: l3msg.dtx % % Copyright (C) 2009-2020 The LaTeX3 Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % This file is part of the "l3kernel bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver> \documentclass[full,kernel]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3msg} package\\ Messages^^A % } % % \author{^^A % The \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2020-04-06} % % \maketitle % % \begin{documentation} % % Messages need to be passed to the user by modules, either when errors % occur or to indicate how the code is proceeding. The \pkg{l3msg} % module provides a consistent method for doing this (as opposed to % writing directly to the terminal or log). % % The system used by \pkg{l3msg} to create messages divides the process % into two distinct parts. Named messages are created in the first part % of the process; at this stage, no decision is made about the type of % output that the message will produce. The second part of the process % is actually producing a message. At this stage a choice of message % \emph{class} has to be made, for example \texttt{error}, % \texttt{warning} or \texttt{info}. % % By separating out the creation and use of messages, several benefits % are available. First, the messages can be altered later without % needing details of where they are used in the code. This makes it % possible to alter the language used, the detail level and so on. % Secondly, the output which results from a given message can be % altered. This can be done on a message class, module or message name % basis. In this way, message behaviour can be altered and messages can % be entirely suppressed. % % \section{Creating new messages} % % All messages have to be created before they can be used. % The text of messages is automatically wrapped to the length % available in the console. As a result, formatting is only needed % where it helps to show meaning. In particular, |\\| may be % used to force a new line and \verb*|\ | forces an explicit space. % Additionally, |\{|, |\#|, |\}|, |\%| and |\~| can be used to produce % the corresponding character. % % Messages may be subdivided \emph{by one level} using the~|/| % character. This is used within the message filtering system to allow % for example the \LaTeX{} kernel messages to belong to the module % \texttt{LaTeX} while still being filterable at a more granular level. % Thus for example % \begin{verbatim} % \msg_new:nnnn { mymodule } { submodule / message } ... % \end{verbatim} % will allow to filter out specifically messages from the \texttt{submodule}. % % \begin{function}[updated = 2011-08-16]{\msg_new:nnnn, \msg_new:nnn} % \begin{syntax} % \cs{msg_new:nnnn} \Arg{module} \Arg{message} \Arg{text} \Arg{more text} % \end{syntax} % Creates a \meta{message} for a given \meta{module}. % The message is defined to first give \meta{text} and then % \meta{more text} if the user requests it. If no \meta{more text} is % available then a standard text is given instead. Within \meta{text} % and \meta{more text} four parameters (|#1| to |#4|) can be used: % these will be supplied at the time the message is % used. An error is raised if the \meta{message} already exists. % \end{function} % % \begin{function}{\msg_set:nnnn, \msg_set:nnn, \msg_gset:nnnn, \msg_gset:nnn} % \begin{syntax} % \cs{msg_set:nnnn} \Arg{module} \Arg{message} \Arg{text} \Arg{more text} % \end{syntax} % Sets up the text for a \meta{message} for a given \meta{module}. % The message is defined to first give \meta{text} and then % \meta{more text} if the user requests it. If no \meta{more text} is % available then a standard text is given instead. Within \meta{text} % and \meta{more text} four parameters (|#1| to |#4|) can be used: % these will be supplied at the time the message is used. % \end{function} % % \begin{function}[EXP, pTF, added = 2012-03-03]{\msg_if_exist:nn} % \begin{syntax} % \cs{msg_if_exist_p:nn} \Arg{module} \Arg{message} % \cs{msg_if_exist:nnTF} \Arg{module} \Arg{message} \Arg{true code} \Arg{false code} % \end{syntax} % Tests whether the \meta{message} for the \meta{module} is currently % defined. % \end{function} % % \section{Contextual information for messages} % % \begin{function}[rEXP]{\msg_line_context:} % \begin{syntax} % \cs{msg_line_context:} % \end{syntax} % Prints the current line number when a message is given, and % thus suitable for giving context to messages. The number itself % is proceeded by the text |on line|. % \end{function} % % \begin{function}[EXP]{\msg_line_number:} % \begin{syntax} % \cs{msg_line_number:} % \end{syntax} % Prints the current line number when a message is given. % \end{function} % % \begin{function}[EXP]{\msg_fatal_text:n} % \begin{syntax} % \cs{msg_fatal_text:n} \Arg{module} % \end{syntax} % Produces the standard text % \begin{quote} % \ttfamily % Fatal Package \meta{module} Error % \end{quote} % This function can be redefined to alter the language in which the % message is given, using |#1| as the name of the \meta{module} to % be included. % \end{function} % % \begin{function}[EXP]{\msg_critical_text:n} % \begin{syntax} % \cs{msg_critical_text:n} \Arg{module} % \end{syntax} % Produces the standard text % \begin{quote} % \ttfamily % Critical Package \meta{module} Error % \end{quote} % This function can be redefined to alter the language in which the % message is given, using |#1| as the name of the \meta{module} to % be included. % \end{function} % % \begin{function}[EXP]{\msg_error_text:n} % \begin{syntax} % \cs{msg_error_text:n} \Arg{module} % \end{syntax} % Produces the standard text % \begin{quote} % \ttfamily % Package \meta{module} Error % \end{quote} % This function can be redefined to alter the language in which the % message is given, using |#1| as the name of the \meta{module} to % be included. % \end{function} % % \begin{function}[EXP]{\msg_warning_text:n} % \begin{syntax} % \cs{msg_warning_text:n} \Arg{module} % \end{syntax} % Produces the standard text % \begin{quote} % \ttfamily % Package \meta{module} Warning % \end{quote} % This function can be redefined to alter the language in which the % message is given, using |#1| as the name of the \meta{module} to % be included. The \meta{type} of \meta{module} may be adjusted: % |Package| is the standard outcome: see \cs{msg_module_type:n}. % \end{function} % % \begin{function}[EXP]{\msg_info_text:n} % \begin{syntax} % \cs{msg_info_text:n} \Arg{module} % \end{syntax} % Produces the standard text: % \begin{quote} % \ttfamily % Package \meta{module} Info % \end{quote} % This function can be redefined to alter the language in which the % message is given, using |#1| as the name of the \meta{module} to % be included. The \meta{type} of \meta{module} may be adjusted: % |Package| is the standard outcome: see \cs{msg_module_type:n}. % \end{function} % % \begin{function}[EXP, added = 2018-10-10]{\msg_module_name:n} % \begin{syntax} % \cs{msg_module_name:n} \Arg{module} % \end{syntax} % Expands to the public name of the \meta{module} as defined by % \cs{g_msg_module_name_prop} (or otherwise leaves the \meta{module} % unchanged). % \end{function} % % \begin{function}[EXP, added = 2018-10-10]{\msg_module_type:n} % \begin{syntax} % \cs{msg_module_type:n} \Arg{module} % \end{syntax} % Expands to the description which applies to the \meta{module}, % for example a |Package| or |Class|. The information here is defined % in \cs{g_msg_module_type_prop}, and will default to |Package| if an % entry is not present. % \end{function} % % \begin{function}[EXP,updated = 2018-09-30]{\msg_see_documentation_text:n} % \begin{syntax} % \cs{msg_see_documentation_text:n} \Arg{module} % \end{syntax} % Produces the standard text % \begin{quote} % \ttfamily % % See the \meta{module} documentation for further information. % \end{quote} % This function can be redefined to alter the language in which the % message is given, using |#1| as the name of the \meta{module} to % be included. The name of the \meta{module} may be altered by use of % \cs{g_msg_module_documentation_prop} % \end{function} % % \begin{variable}[added = 2018-10-10]{\g_msg_module_name_prop} % Provides a mapping between the module name used for messages, and that % for documentation. For example, \LaTeX{}3 core messages are stored in % the reserved |LaTeX| tree, but are printed as |LaTeX3|. % \end{variable} % % \begin{variable}[added = 2018-10-10]{\g_msg_module_type_prop} % Provides a mapping between the module name used for messages, and that % type of module. For example, for \LaTeX{}3 core messages, an empty entry % is set here meaning that they are not described using the standard % |Package| text. % \end{variable} % % \section{Issuing messages} % % Messages behave differently depending on the message class. In all cases, % the message may be issued supplying~$0$ to~$4$ arguments. If the number of % arguments supplied here does not match the number in the definition of the % message, extra arguments are ignored, or empty arguments added (of % course the sense of the message may be impaired). The four arguments are % converted to strings before being added to the message text: the % \texttt{x}-type variants should be used to expand material. % % \begin{function}[updated = 2012-08-11] % { % \msg_fatal:nnnnnn , % \msg_fatal:nnnnn , % \msg_fatal:nnnn , % \msg_fatal:nnn , % \msg_fatal:nn , % \msg_fatal:nnxxxx , % \msg_fatal:nnxxx , % \msg_fatal:nnxx , % \msg_fatal:nnx % } % \begin{syntax} % \cs{msg_fatal:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four} % \end{syntax} % Issues \meta{module} error \meta{message}, passing \meta{arg one} to % \meta{arg four} to the text-creating functions. After issuing a % fatal error the \TeX{} run halts. No PDF file will be produced in % this case (DVI mode runs may produce a truncated DVI file). % \end{function} % % \begin{function}[updated = 2012-08-11] % { % \msg_critical:nnnnnn , % \msg_critical:nnnnn , % \msg_critical:nnnn , % \msg_critical:nnn , % \msg_critical:nn , % \msg_critical:nnxxxx , % \msg_critical:nnxxx , % \msg_critical:nnxx , % \msg_critical:nnx % } % \begin{syntax} % \cs{msg_critical:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four} % \end{syntax} % Issues \meta{module} error \meta{message}, passing \meta{arg one} to % \meta{arg four} to the text-creating functions. After issuing a % critical error, \TeX{} stops reading the current input file. % This may halt the \TeX{} run (if the current file is the main file) % or may abort reading a sub-file. % \begin{texnote} % The \TeX{} \tn{endinput} primitive is used to exit the file. In % particular, the rest of the current line remains in the input % stream. % \end{texnote} % \end{function} % % \begin{function}[updated = 2012-08-11] % { % \msg_error:nnnnnn , % \msg_error:nnnnn , % \msg_error:nnnn , % \msg_error:nnn , % \msg_error:nn , % \msg_error:nnxxxx , % \msg_error:nnxxx , % \msg_error:nnxx , % \msg_error:nnx % } % \begin{syntax} % \cs{msg_error:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four} % \end{syntax} % Issues \meta{module} error \meta{message}, passing \meta{arg one} to % \meta{arg four} to the text-creating functions. The error % interrupts processing and issues the text at the terminal. After user % input, the run continues. % \end{function} % % \begin{function}[updated = 2012-08-11] % { % \msg_warning:nnnnnn , % \msg_warning:nnnnn , % \msg_warning:nnnn , % \msg_warning:nnn , % \msg_warning:nn , % \msg_warning:nnxxxx , % \msg_warning:nnxxx , % \msg_warning:nnxx , % \msg_warning:nnx % } % \begin{syntax} % \cs{msg_warning:nnxxxx} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four} % \end{syntax} % Issues \meta{module} warning \meta{message}, passing \meta{arg one} % to \meta{arg four} to the text-creating functions. The warning text % is added to the log file and the terminal, but the \TeX{} run % is not interrupted. % \end{function} % % \begin{function}[updated = 2012-08-11] % { % \msg_info:nnnnnn , % \msg_info:nnnnn , % \msg_info:nnnn , % \msg_info:nnn , % \msg_info:nn , % \msg_info:nnxxxx , % \msg_info:nnxxx , % \msg_info:nnxx , % \msg_info:nnx % } % \begin{syntax} % \cs{msg_info:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four} % \end{syntax} % Issues \meta{module} information \meta{message}, passing % \meta{arg one} to \meta{arg four} to the text-creating functions. % The information text is added to the log file. % \end{function} % % \begin{function}[updated = 2012-08-11] % { % \msg_log:nnnnnn , % \msg_log:nnnnn , % \msg_log:nnnn , % \msg_log:nnn , % \msg_log:nn , % \msg_log:nnxxxx , % \msg_log:nnxxx , % \msg_log:nnxx , % \msg_log:nnx % } % \begin{syntax} % \cs{msg_log:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four} % \end{syntax} % Issues \meta{module} information \meta{message}, passing % \meta{arg one} to \meta{arg four} to the text-creating functions. % The information text is added to the log file: the output % is briefer than \cs{msg_info:nnnnnn}. % \end{function} % % \begin{function}[updated = 2012-08-11] % { % \msg_none:nnnnnn , % \msg_none:nnnnn , % \msg_none:nnnn , % \msg_none:nnn , % \msg_none:nn , % \msg_none:nnxxxx , % \msg_none:nnxxx , % \msg_none:nnxx , % \msg_none:nnx % } % \begin{syntax} % \cs{msg_none:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four} % \end{syntax} % Does nothing: used as a message class to prevent any output at % all (see the discussion of message redirection). % \end{function} % % \section{Redirecting messages} % % Each message has a \enquote{name}, which can be used to alter the behaviour % of the message when it is given. Thus we might have % \begin{verbatim} % \msg_new:nnnn { module } { my-message } { Some~text } { Some~more~text } % \end{verbatim} % to define a message, with % \begin{verbatim} % \msg_error:nn { module } { my-message } % \end{verbatim} % when it is used. With no filtering, this raises an error. However, we % could alter the behaviour with % \begin{verbatim} % \msg_redirect_class:nn { error } { warning } % \end{verbatim} % to turn all errors into warnings, or with % \begin{verbatim} % \msg_redirect_module:nnn { module } { error } { warning } % \end{verbatim} % to alter only messages from that module, or even % \begin{verbatim} % \msg_redirect_name:nnn { module } { my-message } { warning } % \end{verbatim} % to target just one message. Redirection applies first to individual messages, % then to messages from one module and finally to messages of one class. Thus % it is possible to select out an individual message for special treatment % even if the entire class is already redirected. % % Multiple redirections are possible. Redirections can be cancelled by % providing an empty argument for the target class. Redirection to a % missing class raises an error immediately. Infinite loops are % prevented by eliminating the redirection starting from the target of % the redirection that caused the loop to appear. Namely, if % redirections are requested as $A \to B$, $B \to C$ and $C \to A$ in % this order, then the $A \to B$ redirection is cancelled. % % \begin{function}[updated = 2012-04-27]{\msg_redirect_class:nn} % \begin{syntax} % \cs{msg_redirect_class:nn} \Arg{class one} \Arg{class two} % \end{syntax} % Changes the behaviour of messages of \meta{class one} so that they % are processed using the code for those of \meta{class two}. % \end{function} % % \begin{function}[updated = 2012-04-27]{\msg_redirect_module:nnn} % \begin{syntax} % \cs{msg_redirect_module:nnn} \Arg{module} \Arg{class one} \Arg{class two} % \end{syntax} % Redirects message of \meta{class one} for \meta{module} to act as % though they were from \meta{class two}. Messages of \meta{class one} % from sources other than \meta{module} are not affected by this % redirection. This function can be used to make some messages % \enquote{silent} by default. For example, all of the % \texttt{warning} messages of \meta{module} could be turned off with: % \begin{verbatim} % \msg_redirect_module:nnn { module } { warning } { none } % \end{verbatim} % \end{function} % % \begin{function}[updated = 2012-04-27]{\msg_redirect_name:nnn} % \begin{syntax} % \cs{msg_redirect_name:nnn} \Arg{module} \Arg{message} \Arg{class} % \end{syntax} % Redirects a specific \meta{message} from a specific \meta{module} to % act as a member of \meta{class} of messages. No further redirection % is performed. This function can be used to make a selected message % \enquote{silent} without changing global parameters: % \begin{verbatim} % \msg_redirect_name:nnn { module } { annoying-message } { none } % \end{verbatim} % \end{function} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3msg} implementation} % % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \begin{macrocode} %<@@=msg> % \end{macrocode} % % \begin{variable}{\l_@@_internal_tl} % A general scratch for the module. % \begin{macrocode} \tl_new:N \l_@@_internal_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_name_str, \l_@@_text_str} % Used to save module info when creating messages. % \begin{macrocode} \str_new:N \l_@@_name_str \str_new:N \l_@@_text_str % \end{macrocode} % \end{variable} % % \subsection{Creating messages} % % Messages are created and used separately, so there two parts to % the code here. First, a mechanism for creating message text. % This is pretty simple, as there is not actually a lot to do. % % \begin{variable}{\c_@@_text_prefix_tl, \c_@@_more_text_prefix_tl} % Locations for the text of messages. % \begin{macrocode} \tl_const:Nn \c_@@_text_prefix_tl { msg~text~>~ } \tl_const:Nn \c_@@_more_text_prefix_tl { msg~extra~text~>~ } % \end{macrocode} % \end{variable} % % \begin{macro}[EXP,pTF]{\msg_if_exist:nn} % Test whether the control sequence containing the message text exists % or not. % \begin{macrocode} \prg_new_conditional:Npnn \msg_if_exist:nn #1#2 { p , T , F , TF } { \cs_if_exist:cTF { \c_@@_text_prefix_tl #1 / #2 } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_chk_if_free:nn} % This auxiliary is similar to \cs{__kernel_chk_if_free_cs:N}, and is used when % defining messages with \cs{msg_new:nnnn}. % \begin{macrocode} \cs_new_protected:Npn \@@_chk_free:nn #1#2 { \msg_if_exist:nnT {#1} {#2} { \__kernel_msg_error:nnxx { kernel } { message-already-defined } {#1} {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\msg_new:nnnn, \msg_new:nnn} % \begin{macro}{\msg_gset:nnnn, \msg_gset:nnn} % \begin{macro}{\msg_set:nnnn, \msg_set:nnn} % Setting a message simply means saving the appropriate text % into two functions. A sanity check first. % \begin{macrocode} \cs_new_protected:Npn \msg_new:nnnn #1#2 { \@@_chk_free:nn {#1} {#2} \msg_gset:nnnn {#1} {#2} } \cs_new_protected:Npn \msg_new:nnn #1#2#3 { \msg_new:nnnn {#1} {#2} {#3} { } } \cs_new_protected:Npn \msg_set:nnnn #1#2#3#4 { \cs_set:cpn { \c_@@_text_prefix_tl #1 / #2 } ##1##2##3##4 {#3} \cs_set:cpn { \c_@@_more_text_prefix_tl #1 / #2 } ##1##2##3##4 {#4} } \cs_new_protected:Npn \msg_set:nnn #1#2#3 { \msg_set:nnnn {#1} {#2} {#3} { } } \cs_new_protected:Npn \msg_gset:nnnn #1#2#3#4 { \cs_gset:cpn { \c_@@_text_prefix_tl #1 / #2 } ##1##2##3##4 {#3} \cs_gset:cpn { \c_@@_more_text_prefix_tl #1 / #2 } ##1##2##3##4 {#4} } \cs_new_protected:Npn \msg_gset:nnn #1#2#3 { \msg_gset:nnnn {#1} {#2} {#3} { } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Messages: support functions and text} % % \begin{variable} % { % \c_@@_coding_error_text_tl , % \c_@@_continue_text_tl , % \c_@@_critical_text_tl , % \c_@@_fatal_text_tl , % \c_@@_help_text_tl , % \c_@@_no_info_text_tl , % \c_@@_on_line_text_tl , % \c_@@_return_text_tl , % \c_@@_trouble_text_tl % } % Simple pieces of text for messages. % \begin{macrocode} \tl_const:Nn \c_@@_coding_error_text_tl { This~is~a~coding~error. \\ \\ } \tl_const:Nn \c_@@_continue_text_tl { Type~~to~continue } \tl_const:Nn \c_@@_critical_text_tl { Reading~the~current~file~'\g_file_curr_name_str'~will~stop. } \tl_const:Nn \c_@@_fatal_text_tl { This~is~a~fatal~error:~LaTeX~will~abort. } \tl_const:Nn \c_@@_help_text_tl { For~immediate~help~type~H~ } \tl_const:Nn \c_@@_no_info_text_tl { LaTeX~does~not~know~anything~more~about~this~error,~sorry. \c_@@_return_text_tl } \tl_const:Nn \c_@@_on_line_text_tl { on~line } \tl_const:Nn \c_@@_return_text_tl { \\ \\ Try~typing~~to~proceed. \\ If~that~doesn't~work,~type~X~~to~quit. } \tl_const:Nn \c_@@_trouble_text_tl { \\ \\ More~errors~will~almost~certainly~follow: \\ the~LaTeX~run~should~be~aborted. } % \end{macrocode} % \end{variable} % % \begin{macro}{\msg_line_number:, \msg_line_context:} % For writing the line number nicely. \cs{msg_line_context:} was set up % earlier, so this is not \texttt{new}. % \begin{macrocode} \cs_new:Npn \msg_line_number: { \int_use:N \tex_inputlineno:D } \cs_gset:Npn \msg_line_context: { \c_@@_on_line_text_tl \c_space_tl \msg_line_number: } % \end{macrocode} % \end{macro} % % \subsection{Showing messages: low level mechanism} % % \begin{macro}{\@@_interrupt:Nnnn} % \begin{macro}{\@@_no_more_text:nnnn} % The low-level interruption macro is rather opaque, unfortunately. % Depending on the availability of more information there is a choice % of how to set up the further help. We feed the extra help text and % the message itself to a wrapping auxiliary, in this order because we % must first setup \TeX{}'s \tn{errhelp} register before issuing an % \tn{errmessage}. To deal with the various cases of critical or fatal % errors with and without help text, there is a bit of argument-passing % to do. % \begin{macrocode} \cs_new_protected:Npn \@@_interrupt:NnnnN #1#2#3#4#5 { \str_set:Nx \l_@@_text_str { #1 {#2} } \str_set:Nx \l_@@_name_str { \msg_module_name:n {#2} } \cs_if_eq:cNTF { \c_@@_more_text_prefix_tl #2 / #3 } \@@_no_more_text:nnnn { \@@_interrupt_wrap:nnn { \use:c { \c_@@_text_prefix_tl #2 / #3 } #4 } { \c_@@_continue_text_tl } { \c_@@_no_info_text_tl \tl_if_empty:NF #5 { \\ \\ #5 } } } { \@@_interrupt_wrap:nnn { \use:c { \c_@@_text_prefix_tl #2 / #3 } #4 } { \c_@@_help_text_tl } { \use:c { \c_@@_more_text_prefix_tl #2 / #3 } #4 \tl_if_empty:NF #5 { \\ \\ #5 } } } } \cs_new:Npn \@@_no_more_text:nnnn #1#2#3#4 { } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_interrupt_wrap:nnn} % \begin{macro}{\@@_interrupt_text:n, \@@_interrupt_more_text:n} % First setup \TeX{}'s \tn{errhelp} register with the extra help |#1|, % then build a nice-looking error message with |#2|. Everything is % done using \texttt{x}-type expansion as the new line markers are % different for the two type of text and need to be correctly set up. % The auxiliary \cs{@@_interrupt_more_text:n} receives its argument % as a line-wrapped string, which is thus unaffected by expansion. % We ave to split the main text into two parts as only the \enquote{message} % itself is wrapped with a leader: the generic help is wrapped at full % width. We also have to allow for the two characters used by \tn{errmessage} % itself. % \begin{macrocode} \cs_new_protected:Npn \@@_interrupt_wrap:nnn #1#2#3 { \iow_wrap:nnnN { \\ #3 } { } { } \@@_interrupt_more_text:n \group_begin: \int_sub:Nn \l_iow_line_count_int { 2 } \iow_wrap:nxnN { \l_@@_text_str : ~ #1 } { ( \l_@@_name_str ) \prg_replicate:nn { \str_count:N \l_@@_text_str - \str_count:N \l_@@_name_str + 2 } { ~ } } { } \@@_interrupt_text:n \iow_wrap:nnnN { \l_@@_internal_tl \\ \\ #2 } { } { } \@@_interrupt:n } \cs_new_protected:Npn \@@_interrupt_text:n #1 { \group_end: \tl_set:Nn \l_@@_internal_tl {#1} } \cs_new_protected:Npn \@@_interrupt_more_text:n #1 { \exp_args:Nx \tex_errhelp:D { #1 \iow_newline: } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_interrupt:n} % The business end of the process starts by producing some visual % separation of the message from the main part of the log. The error % message needs to be printed with everything made % \enquote{invisible}: \TeX{}'s own information involves the macro in % which \tn{errmessage} is called, and the end of the argument of the % \tn{errmessage}, including the closing brace. We use an active |!| % to call the \tn{errmessage} primitive, and end its argument with % \cs{use_none:n} \Arg{spaces} which fills the output with spaces. Two % trailing closing braces are turned into spaces to hide them as well. % The group in which we alter the definition of the active |!| is % closed before producing the message: this ensures that tokens % inserted by typing |I| in the command-line are inserted after % the message is entirely cleaned up. % % The \cs{__kernel_iow_with:Nnn} auxiliary, defined in \pkg{l3file}, expects % an \meta{integer variable}, an integer \meta{value}, and some % \meta{code}. It runs the \meta{code} after ensuring that the % \meta{integer variable} takes the given \meta{value}, then restores % the former value of the \meta{integer variable} if needed. We use % it to ensure that the \tn{newlinechar} is $10$, as needed for % \cs{iow_newline:} to work, and that \tn{errorcontextlines} is $-1$, % to avoid showing irrelevant context. Note that restoring the former % value of these integers requires inserting tokens after the % \tn{errmessage}, which go in the way of tokens which could be % inserted by the user. This is unavoidable. % \begin{macrocode} \group_begin: \char_set_lccode:nn { 38 } { 32 } % & \char_set_lccode:nn { 46 } { 32 } % . \char_set_lccode:nn { 123 } { 32 } % { \char_set_lccode:nn { 125 } { 32 } % } \char_set_catcode_active:N \& \tex_lowercase:D { \group_end: \cs_new_protected:Npn \@@_interrupt:n #1 { \iow_term:n { } \__kernel_iow_with:Nnn \tex_newlinechar:D { `\^^J } { \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 } { \group_begin: \cs_set_protected:Npn & { \tex_errmessage:D { #1 \use_none:n { ............................................ } } } \exp_after:wN \group_end: & } } } } % \end{macrocode} % \end{macro} % % \subsection{Displaying messages} % % \LaTeX{} is handling error messages and so the \TeX{} ones are disabled. % This is already done by the \LaTeXe{} kernel, so to avoid messing up any % deliberate change by a user this is only set in format mode. % \begin{macrocode} %<*initex> \int_gset:Nn \tex_errorcontextlines:D { -1 } % % \end{macrocode} % % \begin{macro}[EXP] % { % \msg_fatal_text:n , % \msg_critical_text:n , % \msg_error_text:n , % \msg_warning_text:n , % \msg_info_text:n % } % \begin{macro}[EXP]{\@@_text:nn} % \begin{macro}[EXP]{\@@_text:n} % A function for issuing messages: both the text and order could % in principle vary. The module name may be empty for kernel messages, % hence the slightly contorted code path for a space. % \begin{macrocode} \cs_new:Npn \msg_fatal_text:n #1 { Fatal ~ \msg_error_text:n {#1} } \cs_new:Npn \msg_critical_text:n #1 { Critical ~ \msg_error_text:n {#1} } \cs_new:Npn \msg_error_text:n #1 { \@@_text:nn {#1} { Error } } \cs_new:Npn \msg_warning_text:n #1 { \@@_text:nn {#1} { Warning } } \cs_new:Npn \msg_info_text:n #1 { \@@_text:nn {#1} { Info } } \cs_new:Npn \@@_text:nn #1#2 { \exp_args:Nf \@@_text:n { \msg_module_type:n {#1} } \msg_module_name:n {#1} ~ #2 } \cs_new:Npn \@@_text:n #1 { \tl_if_blank:nF {#1} { #1 ~ } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{variable}{\g_msg_module_name_prop, \g_msg_module_type_prop} % For storing public module information: the kernel data is set up % in advance. % \begin{macrocode} \prop_new:N \g_msg_module_name_prop \prop_gput:Nnn \g_msg_module_name_prop { LaTeX } { LaTeX3 } \prop_new:N \g_msg_module_type_prop \prop_gput:Nnn \g_msg_module_type_prop { LaTeX } { } % \end{macrocode} % \end{variable} % % \begin{macro}[EXP]{\msg_module_type:n} % Contextual footer information, with the potential to give modules an % alternative name. % \begin{macrocode} \cs_new:Npn \msg_module_type:n #1 { \prop_if_in:NnTF \g_msg_module_type_prop {#1} { \prop_item:Nn \g_msg_module_type_prop {#1} } %<*initex> { Module } % %<*package> { Package } % } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\msg_module_name:n, \msg_see_documentation_text:n} % Contextual footer information, with the potential to give modules an % alternative name. % \begin{macrocode} \cs_new:Npn \msg_module_name:n #1 { \prop_if_in:NnTF \g_msg_module_name_prop {#1} { \prop_item:Nn \g_msg_module_name_prop {#1} } {#1} } \cs_new:Npn \msg_see_documentation_text:n #1 { See~the~ \msg_module_name:n {#1} ~ documentation~for~further~information. } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_class_new:nn} % \begin{macrocode} \group_begin: \cs_set_protected:Npn \@@_class_new:nn #1#2 { \prop_new:c { l_@@_redirect_ #1 _prop } \cs_new_protected:cpn { @@_ #1 _code:nnnnnn } ##1##2##3##4##5##6 {#2} \cs_new_protected:cpn { msg_ #1 :nnnnnn } ##1##2##3##4##5##6 { \use:x { \exp_not:n { \@@_use:nnnnnnn {#1} {##1} {##2} } { \tl_to_str:n {##3} } { \tl_to_str:n {##4} } { \tl_to_str:n {##5} } { \tl_to_str:n {##6} } } } \cs_new_protected:cpx { msg_ #1 :nnnnn } ##1##2##3##4##5 { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} {##4} {##5} { } } \cs_new_protected:cpx { msg_ #1 :nnnn } ##1##2##3##4 { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} {##4} { } { } } \cs_new_protected:cpx { msg_ #1 :nnn } ##1##2##3 { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} {##3} { } { } { } } \cs_new_protected:cpx { msg_ #1 :nn } ##1##2 { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} { } { } { } { } } \cs_new_protected:cpx { msg_ #1 :nnxxxx } ##1##2##3##4##5##6 { \use:x { \exp_not:N \exp_not:n { \exp_not:c { msg_ #1 :nnnnnn } {##1} {##2} } {##3} {##4} {##5} {##6} } } \cs_new_protected:cpx { msg_ #1 :nnxxx } ##1##2##3##4##5 { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} {##4} {##5} { } } \cs_new_protected:cpx { msg_ #1 :nnxx } ##1##2##3##4 { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} {##4} { } { } } \cs_new_protected:cpx { msg_ #1 :nnx } ##1##2##3 { \exp_not:c { msg_ #1 :nnxxxx } {##1} {##2} {##3} { } { } { } } } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \msg_fatal:nnnnnn , % \msg_fatal:nnnnn , % \msg_fatal:nnnn , % \msg_fatal:nnn , % \msg_fatal:nn , % \msg_fatal:nnxxxx , % \msg_fatal:nnxxx , % \msg_fatal:nnxx , % \msg_fatal:nnx % } % \begin{macro}{\@@_fatal_exit:} % For fatal errors, after the error message \TeX{} bails out. We force % a bail out rather than using \tn{end} as this means it does not % matter if we are in a context where normally the run cannot end. % \begin{macrocode} \@@_class_new:nn { fatal } { \@@_interrupt:NnnnN \msg_fatal_text:n {#1} {#2} { {#3} {#4} {#5} {#6} } \c_@@_fatal_text_tl \@@_fatal_exit: } \cs_new_protected:Npn \@@_fatal_exit: { \tex_batchmode:D \tex_read:D -1 to \l_@@_internal_tl } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % { % \msg_critical:nnnnnn , % \msg_critical:nnnnn , % \msg_critical:nnnn , % \msg_critical:nnn , % \msg_critical:nn , % \msg_critical:nnxxxx , % \msg_critical:nnxxx , % \msg_critical:nnxx , % \msg_critical:nnx % } % Not quite so bad: just end the current file. % \begin{macrocode} \@@_class_new:nn { critical } { \@@_interrupt:NnnnN \msg_critical_text:n {#1} {#2} { {#3} {#4} {#5} {#6} } \c_@@_critical_text_tl \tex_endinput:D } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \msg_error:nnnnnn , % \msg_error:nnnnn , % \msg_error:nnnn , % \msg_error:nnn , % \msg_error:nn , % \msg_error:nnxxxx , % \msg_error:nnxxx , % \msg_error:nnxx , % \msg_error:nnx % } % For an error, the interrupt routine is called. We check if there is % a \enquote{more text} by comparing that control sequence with a % permanently empty text. % \begin{macrocode} \@@_class_new:nn { error } { \@@_interrupt:NnnnN \msg_error_text:n {#1} {#2} { {#3} {#4} {#5} {#6} } \c_empty_tl } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \msg_warning:nnnnnn , % \msg_warning:nnnnn , % \msg_warning:nnnn , % \msg_warning:nnn , % \msg_warning:nn , % \msg_warning:nnxxxx , % \msg_warning:nnxxx , % \msg_warning:nnxx , % \msg_warning:nnx % } % Warnings are printed to the terminal. % \begin{macrocode} \@@_class_new:nn { warning } { \str_set:Nx \l_@@_text_str { \msg_warning_text:n {#1} } \str_set:Nx \l_@@_name_str { \msg_module_name:n {#1} } \iow_term:n { } \iow_wrap:nxnN { \l_@@_text_str : ~ \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} } { ( \l_@@_name_str ) \prg_replicate:nn { \str_count:N \l_@@_text_str - \str_count:N \l_@@_name_str } { ~ } } { } \iow_term:n \iow_term:n { } } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \msg_info:nnnnnn , % \msg_info:nnnnn , % \msg_info:nnnn , % \msg_info:nnn , % \msg_info:nn , % \msg_info:nnxxxx , % \msg_info:nnxxx , % \msg_info:nnxx , % \msg_info:nnx % } % Information only goes into the log. % \begin{macrocode} \@@_class_new:nn { info } { \str_set:Nx \l_@@_text_str { \msg_info_text:n {#1} } \str_set:Nx \l_@@_name_str { \msg_module_name:n {#1} } \iow_log:n { } \iow_wrap:nxnN { \l_@@_text_str : ~ \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} } { ( \l_@@_name_str ) \prg_replicate:nn { \str_count:N \l_@@_text_str - \str_count:N \l_@@_name_str } { ~ } } { } \iow_log:n \iow_log:n { } } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \msg_log:nnnnnn , % \msg_log:nnnnn , % \msg_log:nnnn , % \msg_log:nnn , % \msg_log:nn , % \msg_log:nnxxxx , % \msg_log:nnxxx , % \msg_log:nnxx , % \msg_log:nnx % } % \enquote{Log} data is very similar to information, but with no extras % added. % \begin{macrocode} \@@_class_new:nn { log } { \iow_wrap:nnnN { \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} } { } { } \iow_log:n } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \msg_none:nnnnnn , % \msg_none:nnnnn , % \msg_none:nnnn , % \msg_none:nnn , % \msg_none:nn , % \msg_none:nnxxxx , % \msg_none:nnxxx , % \msg_none:nnxx , % \msg_none:nnx % } % The \texttt{none} message type is needed so that input can be gobbled. % \begin{macrocode} \@@_class_new:nn { none } { } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \msg_show:nnnnnn , % \msg_show:nnnnn , % \msg_show:nnnn , % \msg_show:nnn , % \msg_show:nn , % \msg_show:nnxxxx , % \msg_show:nnxxx , % \msg_show:nnxx , % \msg_show:nnx % } % \begin{macro}{\@@_show:n, \@@_show:w, \@@_show_dot:w, \@@_show:nn} % The \texttt{show} message type is used for \cs{seq_show:N} and % similar complicated data structures. Wrap the given text with a % trailing dot (important later) then pass it to \cs{@@_show:n}. If % there is |\\>~| (or if the whole thing starts with |>~|) we split % there, print the first part and show the second part using % \tn{showtokens} (the \cs{exp_after:wN} ensure a nice display). Note % that this primitive adds a leading |>~| and trailing dot. That is % why we included a trailing dot before wrapping and removed it % afterwards. If there is no |\\>~| do the same but with an empty % second part which adds a spurious but inevitable |>~.| % \begin{macrocode} \@@_class_new:nn { show } { \iow_wrap:nnnN { \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} {#4} {#5} {#6} } { } { } \@@_show:n } \cs_new_protected:Npn \@@_show:n #1 { \tl_if_in:nnTF { ^^J #1 } { ^^J > ~ } { \tl_if_in:nnTF { #1 \q_mark } { . \q_mark } { \@@_show_dot:w } { \@@_show:w } ^^J #1 \q_stop } { \@@_show:nn { ? #1 } { } } } \cs_new:Npn \@@_show_dot:w #1 ^^J > ~ #2 . \q_stop { \@@_show:nn {#1} {#2} } \cs_new:Npn \@@_show:w #1 ^^J > ~ #2 \q_stop { \@@_show:nn {#1} {#2} } \cs_new_protected:Npn \@@_show:nn #1#2 { \tl_if_empty:nF {#1} { \exp_args:No \iow_term:n { \use_none:n #1 } } \tl_set:Nn \l_@@_internal_tl {#2} \__kernel_iow_with:Nnn \tex_newlinechar:D { 10 } { \__kernel_iow_with:Nnn \tex_errorcontextlines:D { -1 } { \tex_showtokens:D \exp_after:wN \exp_after:wN \exp_after:wN { \exp_after:wN \l_@@_internal_tl } } } } % \end{macrocode} % \end{macro} % \end{macro} % % End the group to eliminate \cs{@@_class_new:nn}. % \begin{macrocode} \group_end: % \end{macrocode} % % \begin{macro}{\@@_class_chk_exist:nT} % Checking that a message class exists. We build this from % \cs{cs_if_free:cTF} rather than \cs{cs_if_exist:cTF} because that % avoids reading the second argument earlier than necessary. % \begin{macrocode} \cs_new:Npn \@@_class_chk_exist:nT #1 { \cs_if_free:cTF { @@_ #1 _code:nnnnnn } { \__kernel_msg_error:nnx { kernel } { message-class-unknown } {#1} } } % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_class_tl, \l_@@_current_class_tl} % Support variables needed for the redirection system. % \begin{macrocode} \tl_new:N \l_@@_class_tl \tl_new:N \l_@@_current_class_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_redirect_prop} % For redirection of individually-named messages % \begin{macrocode} \prop_new:N \l_@@_redirect_prop % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_hierarchy_seq} % During redirection, split the message name into a sequence: % |{/module/submodule}|, |{/module}|, and |{}|. % \begin{macrocode} \seq_new:N \l_@@_hierarchy_seq % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_class_loop_seq} % Classes encountered when following redirections to check for loops. % \begin{macrocode} \seq_new:N \l_@@_class_loop_seq % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_use:nnnnnnn} % \begin{macro} % { % \@@_use_redirect_name:n , \@@_use_hierarchy:nwwN , % \@@_use_redirect_module:n, \@@_use_code: % } % Actually using a message is a multi-step process. First, some % safety checks on the message and class requested. The code and % arguments are then stored to avoid passing them around. The % assignment to \cs{@@_use_code:} is similar to \cs{tl_set:Nn}. % The message is eventually produced with whatever \cs{l_@@_class_tl} % is when \cs{@@_use_code:} is called. % Here is also a good place to suppress tracing output if the % \pkg{trace} package is loaded since all (non-expandable) messages go % through this auxiliary. % \begin{macrocode} \cs_new_protected:Npn \@@_use:nnnnnnn #1#2#3#4#5#6#7 { % \cs_if_exist_use:N \conditionally@traceoff \msg_if_exist:nnTF {#2} {#3} { \@@_class_chk_exist:nT {#1} { \tl_set:Nn \l_@@_current_class_tl {#1} \cs_set_protected:Npx \@@_use_code: { \exp_not:n { \use:c { @@_ \l_@@_class_tl _code:nnnnnn } {#2} {#3} {#4} {#5} {#6} {#7} } } \@@_use_redirect_name:n { #2 / #3 } } } { \__kernel_msg_error:nnxx { kernel } { message-unknown } {#2} {#3} } % \cs_if_exist_use:N \conditionally@traceon } \cs_new_protected:Npn \@@_use_code: { } % \end{macrocode} % The first check is for a individual message redirection. If this % applies then no further redirection is attempted. Otherwise, split % the message name into \meta{module}, \meta{submodule} and \meta{message} % (with an % arbitrary number of slashes), and store |{/module/submodule}|, % |{/module}| and |{}| into \cs{l_@@_hierarchy_seq}. We then % map through this sequence, applying the most specific redirection. % \begin{macrocode} \cs_new_protected:Npn \@@_use_redirect_name:n #1 { \prop_get:NnNTF \l_@@_redirect_prop { / #1 } \l_@@_class_tl { \@@_use_code: } { \seq_clear:N \l_@@_hierarchy_seq \@@_use_hierarchy:nwwN { } #1 \q_mark \@@_use_hierarchy:nwwN / \q_mark \use_none_delimit_by_q_stop:w \q_stop \@@_use_redirect_module:n { } } } \cs_new_protected:Npn \@@_use_hierarchy:nwwN #1#2 / #3 \q_mark #4 { \seq_put_left:Nn \l_@@_hierarchy_seq {#1} #4 { #1 / #2 } #3 \q_mark #4 } % \end{macrocode} % At this point, the items of \cs{l_@@_hierarchy_seq} are the % various levels at which we should look for a redirection. % Redirections which are less specific than the argument of % \cs{@@_use_redirect_module:n} are not attempted. This argument is % empty for a class redirection, \texttt{/module} for a module % redirection, \emph{etc.} Loop through the sequence to find the most % specific redirection, with module |##1|. The loop is interrupted % after testing for a redirection for |##1| equal to the argument |#1| % (least specific redirection allowed). When a redirection is found, % break the mapping, then if the redirection targets the same class, % output the code with that class, and otherwise set the target as the % new current class, and search for further redirections. Those % redirections should be at least as specific as |##1|. % \begin{macrocode} \cs_new_protected:Npn \@@_use_redirect_module:n #1 { \seq_map_inline:Nn \l_@@_hierarchy_seq { \prop_get:cnNTF { l_@@_redirect_ \l_@@_current_class_tl _prop } {##1} \l_@@_class_tl { \seq_map_break:n { \tl_if_eq:NNTF \l_@@_current_class_tl \l_@@_class_tl { \@@_use_code: } { \tl_set_eq:NN \l_@@_current_class_tl \l_@@_class_tl \@@_use_redirect_module:n {##1} } } } { \str_if_eq:nnT {##1} {#1} { \tl_set_eq:NN \l_@@_class_tl \l_@@_current_class_tl \seq_map_break:n { \@@_use_code: } } } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\msg_redirect_name:nnn} % Named message always use the given class even if that class is % redirected further. An empty target class cancels any existing % redirection for that message. % \begin{macrocode} \cs_new_protected:Npn \msg_redirect_name:nnn #1#2#3 { \tl_if_empty:nTF {#3} { \prop_remove:Nn \l_@@_redirect_prop { / #1 / #2 } } { \@@_class_chk_exist:nT {#3} { \prop_put:Nnn \l_@@_redirect_prop { / #1 / #2 } {#3} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\msg_redirect_class:nn, \msg_redirect_module:nnn} % \begin{macro}{\@@_redirect:nnn, \@@_redirect_loop_chk:nnn} % \begin{macro}{\@@_redirect_loop_list:n} % If the target class is empty, eliminate the corresponding % redirection. Otherwise, add the redirection. We must then check % for a loop: as an initialization, we start by storing the initial % class in \cs{l_@@_current_class_tl}. % \begin{macrocode} \cs_new_protected:Npn \msg_redirect_class:nn { \@@_redirect:nnn { } } \cs_new_protected:Npn \msg_redirect_module:nnn #1 { \@@_redirect:nnn { / #1 } } \cs_new_protected:Npn \@@_redirect:nnn #1#2#3 { \@@_class_chk_exist:nT {#2} { \tl_if_empty:nTF {#3} { \prop_remove:cn { l_@@_redirect_ #2 _prop } {#1} } { \@@_class_chk_exist:nT {#3} { \prop_put:cnn { l_@@_redirect_ #2 _prop } {#1} {#3} \tl_set:Nn \l_@@_current_class_tl {#2} \seq_clear:N \l_@@_class_loop_seq \@@_redirect_loop_chk:nnn {#2} {#3} {#1} } } } } % \end{macrocode} % Since multiple redirections can only happen with increasing % specificity, a loop requires that all steps are of the same % specificity. The new redirection can thus only create a loop with % other redirections for the exact same module, |#1|, and not % submodules. After some initialization above, follow redirections % with \cs{l_@@_class_tl}, and keep track in % \cs{l_@@_class_loop_seq} of the various classes encountered. A % redirection from a class to itself, or the absence of redirection % both mean that there is no loop. A redirection to the initial class % marks a loop. To break it, we must decide which redirection to % cancel. The user most likely wants the newly added redirection to % hold with no further redirection. We thus remove the redirection % starting from |#2|, target of the new redirection. Note that no % message is emitted by any of the underlying functions: otherwise we % may get an infinite loop because of a message from the message % system itself. % \begin{macrocode} \cs_new_protected:Npn \@@_redirect_loop_chk:nnn #1#2#3 { \seq_put_right:Nn \l_@@_class_loop_seq {#1} \prop_get:cnNT { l_@@_redirect_ #1 _prop } {#3} \l_@@_class_tl { \str_if_eq:VnF \l_@@_class_tl {#1} { \tl_if_eq:NNTF \l_@@_class_tl \l_@@_current_class_tl { \prop_put:cnn { l_@@_redirect_ #2 _prop } {#3} {#2} \__kernel_msg_warning:nnxxxx { kernel } { message-redirect-loop } { \seq_item:Nn \l_@@_class_loop_seq { 1 } } { \seq_item:Nn \l_@@_class_loop_seq { 2 } } {#3} { \seq_map_function:NN \l_@@_class_loop_seq \@@_redirect_loop_list:n { \seq_item:Nn \l_@@_class_loop_seq { 1 } } } } { \@@_redirect_loop_chk:onn \l_@@_class_tl {#2} {#3} } } } } \cs_generate_variant:Nn \@@_redirect_loop_chk:nnn { o } \cs_new:Npn \@@_redirect_loop_list:n #1 { {#1} ~ => ~ } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Kernel-specific functions} % % \begin{macro}{\__kernel_msg_new:nnnn, \__kernel_msg_new:nnn} % \begin{macro}{\__kernel_msg_set:nnnn, \__kernel_msg_set:nnn} % The kernel needs some messages of its own. These are created using % pre-built functions. Two functions are provided: one more general % and one which only has the short text part. % \begin{macrocode} \cs_new_protected:Npn \__kernel_msg_new:nnnn #1#2 { \msg_new:nnnn { LaTeX } { #1 / #2 } } \cs_new_protected:Npn \__kernel_msg_new:nnn #1#2 { \msg_new:nnn { LaTeX } { #1 / #2 } } \cs_new_protected:Npn \__kernel_msg_set:nnnn #1#2 { \msg_set:nnnn { LaTeX } { #1 / #2 } } \cs_new_protected:Npn \__kernel_msg_set:nnn #1#2 { \msg_set:nnn { LaTeX } { #1 / #2 } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_kernel_class_new:nN} % \begin{macro}{\@@_kernel_class_new_aux:nN} % All the functions for kernel messages come in variants ranging % from~$0$ to~$4$ arguments. Those with less than~$4$ arguments are % defined in terms of the $4$-argument variant, in a way very similar % to \cs{@@_class_new:nn}. This auxiliary is destroyed at the end of % the group. % \begin{macrocode} \group_begin: \cs_set_protected:Npn \@@_kernel_class_new:nN #1 { \@@_kernel_class_new_aux:nN { __kernel_msg_ #1 } } \cs_set_protected:Npn \@@_kernel_class_new_aux:nN #1#2 { \cs_new_protected:cpn { #1 :nnnnnn } ##1##2##3##4##5##6 { \use:x { \exp_not:n { #2 { LaTeX } { ##1 / ##2 } } { \tl_to_str:n {##3} } { \tl_to_str:n {##4} } { \tl_to_str:n {##5} } { \tl_to_str:n {##6} } } } \cs_new_protected:cpx { #1 :nnnnn } ##1##2##3##4##5 { \exp_not:c { #1 :nnnnnn } {##1} {##2} {##3} {##4} {##5} { } } \cs_new_protected:cpx { #1 :nnnn } ##1##2##3##4 { \exp_not:c { #1 :nnnnnn } {##1} {##2} {##3} {##4} { } { } } \cs_new_protected:cpx { #1 :nnn } ##1##2##3 { \exp_not:c { #1 :nnnnnn } {##1} {##2} {##3} { } { } { } } \cs_new_protected:cpx { #1 :nn } ##1##2 { \exp_not:c { #1 :nnnnnn } {##1} {##2} { } { } { } { } } \cs_new_protected:cpx { #1 :nnxxxx } ##1##2##3##4##5##6 { \use:x { \exp_not:N \exp_not:n { \exp_not:c { #1 :nnnnnn } {##1} {##2} } {##3} {##4} {##5} {##6} } } \cs_new_protected:cpx { #1 :nnxxx } ##1##2##3##4##5 { \exp_not:c { #1 :nnxxxx } {##1} {##2} {##3} {##4} {##5} { } } \cs_new_protected:cpx { #1 :nnxx } ##1##2##3##4 { \exp_not:c { #1 :nnxxxx } {##1} {##2} {##3} {##4} { } { } } \cs_new_protected:cpx { #1 :nnx } ##1##2##3 { \exp_not:c { #1 :nnxxxx } {##1} {##2} {##3} { } { } { } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % { % \__kernel_msg_fatal:nnnnnn , % \__kernel_msg_fatal:nnnnn , % \__kernel_msg_fatal:nnnn , % \__kernel_msg_fatal:nnn , % \__kernel_msg_fatal:nn , % \__kernel_msg_fatal:nnxxxx , % \__kernel_msg_fatal:nnxxx , % \__kernel_msg_fatal:nnxx , % \__kernel_msg_fatal:nnx , % \__kernel_msg_error:nnnnnn , % \__kernel_msg_error:nnnnn , % \__kernel_msg_error:nnnn , % \__kernel_msg_error:nnn , % \__kernel_msg_error:nn , % \__kernel_msg_error:nnxxxx , % \__kernel_msg_error:nnxxx , % \__kernel_msg_error:nnxx , % \__kernel_msg_error:nnx % } % Neither fatal kernel errors nor kernel errors can be redirected. We % directly use the code for (non-kernel) fatal errors and errors, % adding the \enquote{\LaTeX{}} module name. Three functions are % already defined by \pkg{l3basics}; we need to undefine them to avoid % errors. % \begin{macrocode} \@@_kernel_class_new:nN { fatal } \@@_fatal_code:nnnnnn \cs_undefine:N \__kernel_msg_error:nnxx \cs_undefine:N \__kernel_msg_error:nnx \cs_undefine:N \__kernel_msg_error:nn \@@_kernel_class_new:nN { error } \@@_error_code:nnnnnn % \end{macrocode} % \end{macro} % % \begin{macro} % { % \__kernel_msg_warning:nnnnnn , % \__kernel_msg_warning:nnnnn , % \__kernel_msg_warning:nnnn , % \__kernel_msg_warning:nnn , % \__kernel_msg_warning:nn , % \__kernel_msg_warning:nnxxxx , % \__kernel_msg_warning:nnxxx , % \__kernel_msg_warning:nnxx , % \__kernel_msg_warning:nnx , % \__kernel_msg_info:nnnnnn , % \__kernel_msg_info:nnnnn , % \__kernel_msg_info:nnnn , % \__kernel_msg_info:nnn , % \__kernel_msg_info:nn , % \__kernel_msg_info:nnxxxx , % \__kernel_msg_info:nnxxx , % \__kernel_msg_info:nnxx , % \__kernel_msg_info:nnx % } % Kernel messages which can be redirected simply use the machinery for % normal messages, with the module name \enquote{\LaTeX{}}. % \begin{macrocode} \@@_kernel_class_new:nN { warning } \msg_warning:nnxxxx \@@_kernel_class_new:nN { info } \msg_info:nnxxxx % \end{macrocode} % \end{macro} % % End the group to eliminate \cs{@@_kernel_class_new:nN}. % \begin{macrocode} \group_end: % \end{macrocode} % % Error messages needed to actually implement the message system % itself. % \begin{macrocode} \__kernel_msg_new:nnnn { kernel } { message-already-defined } { Message~'#2'~for~module~'#1'~already~defined. } { \c_@@_coding_error_text_tl LaTeX~was~asked~to~define~a~new~message~called~'#2'\\ by~the~module~'#1':~this~message~already~exists. \c_@@_return_text_tl } \__kernel_msg_new:nnnn { kernel } { message-unknown } { Unknown~message~'#2'~for~module~'#1'. } { \c_@@_coding_error_text_tl LaTeX~was~asked~to~display~a~message~called~'#2'\\ by~the~module~'#1':~this~message~does~not~exist. \c_@@_return_text_tl } \__kernel_msg_new:nnnn { kernel } { message-class-unknown } { Unknown~message~class~'#1'. } { LaTeX~has~been~asked~to~redirect~messages~to~a~class~'#1':\\ this~was~never~defined. \c_@@_return_text_tl } \__kernel_msg_new:nnnn { kernel } { message-redirect-loop } { Message~redirection~loop~caused~by~ {#1} ~=>~ {#2} \tl_if_empty:nF {#3} { ~for~module~' \use_none:n #3 ' } . } { Adding~the~message~redirection~ {#1} ~=>~ {#2} \tl_if_empty:nF {#3} { ~for~the~module~' \use_none:n #3 ' } ~ created~an~infinite~loop\\\\ \iow_indent:n { #4 \\\\ } } % \end{macrocode} % % Messages for earlier kernel modules plus a few for \pkg{l3keys} which % cover coding errors. % \begin{macrocode} \__kernel_msg_new:nnnn { kernel } { bad-number-of-arguments } { Function~'#1'~cannot~be~defined~with~#2~arguments. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~define~a~function~'#1'~with~ #2~arguments.~ TeX~allows~between~0~and~9~arguments~for~a~single~function. } \__kernel_msg_new:nnn { kernel } { char-active } { Cannot~generate~active~chars. } \__kernel_msg_new:nnn { kernel } { char-invalid-catcode } { Invalid~catcode~for~char~generation. } \__kernel_msg_new:nnn { kernel } { char-null-space } { Cannot~generate~null~char~as~a~space. } \__kernel_msg_new:nnn { kernel } { char-out-of-range } { Charcode~requested~out~of~engine~range. } \__kernel_msg_new:nnn { kernel } { char-space } { Cannot~generate~space~chars. } \__kernel_msg_new:nnnn { kernel } { command-already-defined } { Control~sequence~#1~already~defined. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~create~a~new~control~sequence~'#1'~ but~this~name~has~already~been~used~elsewhere. \\ \\ The~current~meaning~is:\\ \ \ #2 } \__kernel_msg_new:nnnn { kernel } { command-not-defined } { Control~sequence~#1~undefined. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~use~a~control~sequence~'#1':\\ this~has~not~been~defined~yet. } \__kernel_msg_new:nnnn { kernel } { empty-search-pattern } { Empty~search~pattern. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~replace~an~empty~pattern~by~'#1':~that~ would~lead~to~an~infinite~loop! } \__kernel_msg_new:nnnn { kernel } { out-of-registers } { No~room~for~a~new~#1. } { TeX~only~supports~\int_use:N \c_max_register_int \ % of~each~type.~All~the~#1~registers~have~been~used.~ This~run~will~be~aborted~now. } \__kernel_msg_new:nnnn { kernel } { non-base-function } { Function~'#1'~is~not~a~base~function } { \c_@@_coding_error_text_tl Functions~defined~through~\iow_char:N\\cs_new:Nn~must~have~ a~signature~consisting~of~only~normal~arguments~'N'~and~'n'.~ To~define~variants~use~\iow_char:N\\cs_generate_variant:Nn~ and~to~define~other~functions~use~\iow_char:N\\cs_new:Npn. } \__kernel_msg_new:nnnn { kernel } { missing-colon } { Function~'#1'~contains~no~':'. } { \c_@@_coding_error_text_tl Code-level~functions~must~contain~':'~to~separate~the~ argument~specification~from~the~function~name.~This~is~ needed~when~defining~conditionals~or~variants,~or~when~building~a~ parameter~text~from~the~number~of~arguments~of~the~function. } \__kernel_msg_new:nnnn { kernel } { overflow } { Integers~larger~than~2^{30}-1~cannot~be~stored~in~arrays. } { An~attempt~was~made~to~store~#3~ \tl_if_empty:nF {#2} { at~position~#2~ } in~the~array~'#1'.~ The~largest~allowed~value~#4~will~be~used~instead. } \__kernel_msg_new:nnnn { kernel } { out-of-bounds } { Access~to~an~entry~beyond~an~array's~bounds. } { An~attempt~was~made~to~access~or~store~data~at~position~#2~of~the~ array~'#1',~but~this~array~has~entries~at~positions~from~1~to~#3. } \__kernel_msg_new:nnnn { kernel } { protected-predicate } { Predicate~'#1'~must~be~expandable. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~define~'#1'~as~a~protected~predicate.~ Only~expandable~tests~can~have~a~predicate~version. } \__kernel_msg_new:nnn { kernel } { randint-backward-range } { Bounds~ordered~backwards~in~\iow_char:N\\int_rand:nn~{#1}~{#2}. } \__kernel_msg_new:nnnn { kernel } { conditional-form-unknown } { Conditional~form~'#1'~for~function~'#2'~unknown. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~define~the~conditional~form~'#1'~of~ the~function~'#2',~but~only~'TF',~'T',~'F',~and~'p'~forms~exist. } \__kernel_msg_new:nnnn { kernel } { key-no-property } { No~property~given~in~definition~of~key~'#1'. } { \c_@@_coding_error_text_tl Inside~\keys_define:nn each~key~name~ needs~a~property: \\ \\ \iow_indent:n { #1 . } \\ \\ LaTeX~did~not~find~a~'.'~to~indicate~the~start~of~a~property. } \__kernel_msg_new:nnnn { kernel } { key-property-boolean-values-only } { The~property~'#1'~accepts~boolean~values~only. } { \c_@@_coding_error_text_tl The~property~'#1'~only~accepts~the~values~'true'~and~'false'. } \__kernel_msg_new:nnnn { kernel } { key-property-requires-value } { The~property~'#1'~requires~a~value. } { \c_@@_coding_error_text_tl LaTeX~was~asked~to~set~property~'#1'~for~key~'#2'.\\ No~value~was~given~for~the~property,~and~one~is~required. } \__kernel_msg_new:nnnn { kernel } { key-property-unknown } { The~key~property~'#1'~is~unknown. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~set~the~property~'#1'~for~key~'#2':~ this~property~is~not~defined. } \__kernel_msg_new:nnnn { kernel } { quote-in-shell } { Quotes~in~shell~command~'#1'. } { Shell~commands~cannot~contain~quotes~("). } \__kernel_msg_new:nnnn { kernel } { scanmark-already-defined } { Scan~mark~#1~already~defined. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~create~a~new~scan~mark~'#1'~ but~this~name~has~already~been~used~for~a~scan~mark. } \__kernel_msg_new:nnnn { kernel } { shuffle-too-large } { The~sequence~#1~is~too~long~to~be~shuffled~by~TeX. } { TeX~has~ \int_eval:n { \c_max_register_int + 1 } ~ toks~registers:~this~only~allows~to~shuffle~up~to~ \int_use:N \c_max_register_int \ items.~ The~list~will~not~be~shuffled. } \__kernel_msg_new:nnnn { kernel } { variable-not-defined } { Variable~#1~undefined. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~show~a~variable~#1,~but~this~has~not~ been~defined~yet. } \__kernel_msg_new:nnnn { kernel } { variant-too-long } { Variant~form~'#1'~longer~than~base~signature~of~'#2'. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~create~a~variant~of~the~function~'#2'~ with~a~signature~starting~with~'#1',~but~that~is~longer~than~ the~signature~(part~after~the~colon)~of~'#2'. } \__kernel_msg_new:nnnn { kernel } { invalid-variant } { Variant~form~'#1'~invalid~for~base~form~'#2'. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~create~a~variant~of~the~function~'#2'~ with~a~signature~starting~with~'#1',~but~cannot~change~an~argument~ from~type~'#3'~to~type~'#4'. } \__kernel_msg_new:nnnn { kernel } { invalid-exp-args } { Invalid~variant~specifier~'#1'~in~'#2'. } { \c_@@_coding_error_text_tl LaTeX~has~been~asked~to~create~an~\iow_char:N\\exp_args:N...~ function~with~signature~'N#2'~but~'#1'~is~not~a~valid~argument~ specifier. } \__kernel_msg_new:nnn { kernel } { deprecated-variant } { Variant~form~'#1'~deprecated~for~base~form~'#2'.~ One~should~not~change~an~argument~from~type~'#3'~to~type~'#4' \str_case:nnF {#3} { { n } { :~use~a~'\token_if_eq_charcode:NNTF #4 c v V'~variant? } { N } { :~base~form~only~accepts~a~single~token~argument. } {#4} { :~base~form~is~already~a~variant. } } { . } } % \end{macrocode} % % Some errors are only needed in package mode if debugging is enabled by % one of the options \texttt{enable-debug}, \texttt{check-declarations}, % \texttt{log-functions}, or on the contrary if debugging is turned off. % In format mode the error is somewhat different. % \begin{macrocode} %<*package> \__kernel_msg_new:nnnn { kernel } { enable-debug } { To~use~'#1'~load~expl3~with~the~'enable-debug'~option. } { The~function~'#1'~will~be~ignored~because~it~can~only~work~if~ some~internal~functions~in~expl3~have~been~appropriately~ defined.~This~only~happens~if~one~of~the~options~ 'enable-debug',~'check-declarations'~or~'log-functions'~was~ given~when~loading~expl3. } % %<*initex> \__kernel_msg_new:nnnn { kernel } { enable-debug } { '#1'~cannot~be~used~in~format~mode. } { The~function~'#1'~will~be~ignored~because~it~can~only~work~if~ some~internal~functions~in~expl3~have~been~appropriately~ defined.~This~only~happens~in~package~mode~(and~only~if~one~of~ the~options~'enable-debug',~'check-declarations'~or~'log-functions'~ was~given~when~loading~expl3. } % % \end{macrocode} % % Some errors only appear in expandable settings, % hence don't need a \enquote{more-text} argument. % \begin{macrocode} \__kernel_msg_new:nnn { kernel } { bad-exp-end-f } { Misused~\exp_end_continue_f:w or~:nw } \__kernel_msg_new:nnn { kernel } { bad-variable } { Erroneous~variable~#1 used! } \__kernel_msg_new:nnn { kernel } { misused-sequence } { A~sequence~was~misused. } \__kernel_msg_new:nnn { kernel } { misused-prop } { A~property~list~was~misused. } \__kernel_msg_new:nnn { kernel } { negative-replication } { Negative~argument~for~\iow_char:N\\prg_replicate:nn. } \__kernel_msg_new:nnn { kernel } { prop-keyval } { Missing/extra~'='~in~'#1'~(in~'..._keyval:Nn') } \__kernel_msg_new:nnn { kernel } { unknown-comparison } { Relation~'#1'~unknown:~use~=,~<,~>,~==,~!=,~<=,~>=. } \__kernel_msg_new:nnn { kernel } { zero-step } { Zero~step~size~for~step~function~#1. } \cs_if_exist:NF \tex_expanded:D { \__kernel_msg_new:nnn { kernel } { e-type } { #1 ~ in~e-type~argument } } % \end{macrocode} % % Messages used by the \enquote{\texttt{show}} functions. % \begin{macrocode} \__kernel_msg_new:nnn { kernel } { show-clist } { The~comma~list~ \tl_if_empty:nF {#1} { #1 ~ } \tl_if_empty:nTF {#2} { is~empty \\>~ . } { contains~the~items~(without~outer~braces): #2 . } } \__kernel_msg_new:nnn { kernel } { show-intarray } { The~integer~array~#1~contains~#2~items: \\ #3 . } \__kernel_msg_new:nnn { kernel } { show-prop } { The~property~list~#1~ \tl_if_empty:nTF {#2} { is~empty \\>~ . } { contains~the~pairs~(without~outer~braces): #2 . } } \__kernel_msg_new:nnn { kernel } { show-seq } { The~sequence~#1~ \tl_if_empty:nTF {#2} { is~empty \\>~ . } { contains~the~items~(without~outer~braces): #2 . } } \__kernel_msg_new:nnn { kernel } { show-streams } { \tl_if_empty:nTF {#2} { No~ } { The~following~ } \str_case:nn {#1} { { ior } { input ~ } { iow } { output ~ } } streams~are~ \tl_if_empty:nTF {#2} { open } { in~use: #2 . } } % \end{macrocode} % % System layer messages % \begin{macrocode} \__kernel_msg_new:nnnn { sys } { backend-set } { Backend~configuration~already~set. } { Run-time~backend~selection~may~only~be~carried~out~once~during~a~run.~ This~second~attempt~to~set~them~will~be~ignored. } \__kernel_msg_new:nnnn { sys } { wrong-backend } { Backend~request~inconsistent~with~engine:~using~'#2'~backend. } { You~have~requested~backend~'#1',~but~this~is~not~suitable~for~use~with~the~ active~engine.~LaTeX3~will~use~the~'#2'~backend~instead. } % \end{macrocode} % % \subsection{Expandable errors} % % \begin{macro}{\@@_expandable_error:n} % \begin{macro}{\@@_expandable_error:w} % In expansion only context, we cannot use the normal means of % reporting errors. Instead, we feed \TeX{} an undefined control % sequence, \cs{LaTeX3 error:}. It is thus interrupted, and shows % the context, which thanks to the odd-looking \cs{use:n} is % \begin{verbatim} % \LaTeX3 error: % The error message. % \end{verbatim} % In other words, \TeX{} is processing the argument of \cs{use:n}, % which is \cs{LaTeX3 error:} \meta{error message}. % Then \cs{@@_expandable_error:w} cleans up. In fact, there % is an extra subtlety: if the user inserts tokens for error recovery, % they should be kept. Thus we also use an odd space character % (with category code $7$) and keep tokens until that space character, % dropping everything else until \cs{q_stop}. The \cs{exp_end:} prevents % losing braces around the user-inserted text if any, and stops the % expansion of \cs{exp:w}. The group is used to prevent % |\LaTeX3~error:| from being globally equal to \cs{scan_stop:}. % \begin{macrocode} \group_begin: \cs_set_protected:Npn \@@_tmp:w #1#2 { \cs_new:Npn \@@_expandable_error:n ##1 { \exp:w \exp_after:wN \exp_after:wN \exp_after:wN \@@_expandable_error:w \exp_after:wN \exp_after:wN \exp_after:wN \exp_end: \use:n { #1 #2 ##1 } #2 } \cs_new:Npn \@@_expandable_error:w ##1 #2 ##2 #2 {##1} } \exp_args:Ncx \@@_tmp:w { LaTeX3~error: } { \char_generate:nn { `\ } { 7 } } \group_end: % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro} % { % \__kernel_msg_expandable_error:nnnnnn, % \__kernel_msg_expandable_error:nnnnn, % \__kernel_msg_expandable_error:nnnn, % \__kernel_msg_expandable_error:nnn, % \__kernel_msg_expandable_error:nn, % \__kernel_msg_expandable_error:nnffff, % \__kernel_msg_expandable_error:nnfff, % \__kernel_msg_expandable_error:nnff, % \__kernel_msg_expandable_error:nnf, % } % The command built from the csname % \cs{c_@@_text_prefix_tl} |LaTeX / #1 / #2| % takes four arguments and builds the error text, which is fed to % \cs{@@_expandable_error:n} with appropriate expansion: just as for % usual messages the arguments are first turned to strings, then the % message is fully expanded. % \begin{macrocode} \exp_args_generate:n { oooo } \cs_new:Npn \__kernel_msg_expandable_error:nnnnnn #1#2#3#4#5#6 { \exp_args:Ne \@@_expandable_error:n { \exp_args:Nc \exp_args:Noooo { \c_@@_text_prefix_tl LaTeX / #1 / #2 } { \tl_to_str:n {#3} } { \tl_to_str:n {#4} } { \tl_to_str:n {#5} } { \tl_to_str:n {#6} } } } \cs_new:Npn \__kernel_msg_expandable_error:nnnnn #1#2#3#4#5 { \__kernel_msg_expandable_error:nnnnnn {#1} {#2} {#3} {#4} {#5} { } } \cs_new:Npn \__kernel_msg_expandable_error:nnnn #1#2#3#4 { \__kernel_msg_expandable_error:nnnnnn {#1} {#2} {#3} {#4} { } { } } \cs_new:Npn \__kernel_msg_expandable_error:nnn #1#2#3 { \__kernel_msg_expandable_error:nnnnnn {#1} {#2} {#3} { } { } { } } \cs_new:Npn \__kernel_msg_expandable_error:nn #1#2 { \__kernel_msg_expandable_error:nnnnnn {#1} {#2} { } { } { } { } } \cs_generate_variant:Nn \__kernel_msg_expandable_error:nnnnnn { nnffff } \cs_generate_variant:Nn \__kernel_msg_expandable_error:nnnnn { nnfff } \cs_generate_variant:Nn \__kernel_msg_expandable_error:nnnn { nnff } \cs_generate_variant:Nn \__kernel_msg_expandable_error:nnn { nnf } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex