summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3kernel/l3candidates.dtx
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/l3kernel/l3candidates.dtx
Initial commit
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3candidates.dtx')
-rw-r--r--macros/latex/contrib/l3kernel/l3candidates.dtx4480
1 files changed, 4480 insertions, 0 deletions
diff --git a/macros/latex/contrib/l3kernel/l3candidates.dtx b/macros/latex/contrib/l3kernel/l3candidates.dtx
new file mode 100644
index 0000000000..a92db3ebed
--- /dev/null
+++ b/macros/latex/contrib/l3kernel/l3candidates.dtx
@@ -0,0 +1,4480 @@
+% \iffalse meta-comment
+%
+%% File: l3candidates.dtx
+%
+% Copyright (C) 2012-2019 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}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \textsf{l3candidates} package\\ Experimental additions to
+% \pkg{l3kernel}^^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 2019-08-25}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \section{Important notice}
+%
+% This module provides a space in which functions can be added to
+% \pkg{l3kernel} (\pkg{expl3}) while still being experimental.
+% \begin{quote}
+% \bfseries
+% As such, the
+% functions here may not remain in their current form, or indeed at all,
+% in \pkg{l3kernel} in the future.
+% \end{quote}
+% In contrast to the material in
+% \pkg{l3experimental}, the functions here are all \emph{small} additions to
+% the kernel. We encourage programmers to test them out and report back on
+% the \texttt{LaTeX-L} mailing list.
+%
+% \medskip
+%
+% Thus, if you intend to use any of these functions from the candidate module in a public package
+% offered to others for productive use (e.g., being placed on CTAN) please consider the following points carefully:
+% \begin{itemize}
+% \item Be prepared that your public packages might require updating when such functions
+% are being finalized.
+% \item Consider informing us that you use a particular function in your public package, e.g., by
+% discussing this on the \texttt{LaTeX-L}
+% mailing list. This way it becomes easier to coordinate any updates necessary without issues
+% for the users of your package.
+% \item Discussing and understanding use cases for a particular addition or concept also helps to
+% ensure that we provide the right interfaces in the final version so please give us feedback
+% if you consider a certain candidate function useful (or not).
+% \end{itemize}
+% We only add functions in this space if we consider them being serious candidates for a final inclusion
+% into the kernel. However, real use sometimes leads to better ideas, so functions from this module are
+% \textbf{not necessarily stable} and we may have to adjust them!
+%
+% \section{Additions to \pkg{l3basics}}
+%
+% \begin{function}[added = 2017-07-16, updated = 2017-08-02]{\debug_on:n, \debug_off:n}
+% \begin{syntax}
+% \cs{debug_on:n} |{| \meta{comma-separated list} |}|
+% \cs{debug_off:n} |{| \meta{comma-separated list} |}|
+% \end{syntax}
+% Turn on and off within a group various debugging code, some of which
+% is also available as \pkg{expl3} load-time options. The items that
+% can be used in the \meta{list} are
+% \begin{itemize}
+% \item \texttt{check-declarations} that checks all \pkg{expl3}
+% variables used were previously declared and that local/global
+% variables (based on their name or on their first assignment) are
+% only locally/globally assigned;
+% \item \texttt{check-expressions} that checks integer, dimension,
+% skip, and muskip expressions are not terminated prematurely;
+% \item \texttt{deprecation} that makes soon-to-be-deprecated commands produce errors;
+% \item \texttt{log-functions} that logs function definitions;
+% \item \texttt{all} that does all of the above.
+% \end{itemize}
+% Providing these as switches rather than options allows testing code
+% even if it relies on other packages: load all other packages, call
+% \cs{debug_on:n}, and load the code that one is interested in
+% testing. These functions can only be used in \LaTeXe{} package mode
+% loaded with \texttt{enable-debug} or another option implying it.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28]{\debug_suspend:, \debug_resume:}
+% \begin{syntax}
+% \cs{debug_suspend:} \ldots{} \cs{debug_resume:}
+% \end{syntax}
+% Suppress (locally) errors and logging from \texttt{debug} commands,
+% except for the \texttt{deprecation} errors or warnings. These pairs
+% of commands can be nested. This can be used around pieces of code
+% that are known to fail checks, if such failures should be ignored.
+% See for instance \pkg{l3coffins}.
+% \end{function}
+%
+% \begin{function}[added = 2017-07-04]{\mode_leave_vertical:}
+% \begin{syntax}
+% \cs{mode_leave_vertical:}
+% \end{syntax}
+% Ensures that \TeX{} is not in vertical (inter-paragraph) mode. In
+% horizontal or math mode this command has no effect, in vertical mode it
+% switches to horizontal mode, and inserts a box of width
+% \tn{parindent}, followed by the \tn{everypar} token list.
+% \begin{texnote}
+% This results in the contents of the \tn{everypar} token register being
+% inserted, after \cs{mode_leave_vertical:} is complete. Notice that in
+% contrast to the \LaTeXe{} \tn{leavevmode} approach, no box is used
+% by the method implemented here.
+% \end{texnote}
+% \end{function}
+%
+% \section{Additions to \pkg{l3box}}
+%
+% \subsection{Viewing part of a box}
+%
+% \begin{function}[updated = 2019-01-23]
+% {\box_clip:N, \box_clip:c, \box_gclip:N, \box_gclip:c}
+% \begin{syntax}
+% \cs{box_clip:N} \meta{box}
+% \end{syntax}
+% Clips the \meta{box} in the output so that only material inside the
+% bounding box is displayed in the output. The updated \meta{box} is an
+% hbox, irrespective of the nature of the \meta{box} before the clipping is
+% applied.
+%
+% \textbf{These functions require the \LaTeX3 native drivers: they do
+% not work with the \LaTeXe{} \pkg{graphics} drivers!}
+%
+% \begin{texnote}
+% Clipping is implemented by the driver, and as such the full content of
+% the box is placed in the output file. Thus clipping does not remove
+% any information from the raw output, and hidden material can therefore
+% be viewed by direct examination of the file.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2019-01-23]
+% {
+% \box_set_trim:Nnnnn, \box_set_trim:cnnnn,
+% \box_gset_trim:Nnnnn, \box_gset_trim:cnnnn
+% }
+% \begin{syntax}
+% \cs{box_set_trim:Nnnnn} \meta{box} \Arg{left} \Arg{bottom} \Arg{right} \Arg{top}
+% \end{syntax}
+% Adjusts the bounding box of the \meta{box} \meta{left} is removed from
+% the left-hand edge of the bounding box, \meta{right} from the right-hand
+% edge and so fourth. All adjustments are \meta{dimension expressions}.
+% Material outside of the bounding box is still displayed in the output
+% unless \cs{box_clip:N} is subsequently applied.
+% The updated \meta{box} is an
+% hbox, irrespective of the nature of the \meta{box} before the trim
+% operation is applied.
+% The behavior of the operation where the trims requested is
+% greater than the size of the box is undefined.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-23]
+% {
+% \box_set_viewport:Nnnnn, \box_set_viewport:cnnnn,
+% \box_gset_viewport:Nnnnn, \box_gset_viewport:cnnnn
+% }
+% \begin{syntax}
+% \cs{box_set_viewport:Nnnnn} \meta{box} \Arg{llx} \Arg{lly} \Arg{urx} \Arg{ury}
+% \end{syntax}
+% Adjusts the bounding box of the \meta{box} such that it has lower-left
+% co-ordinates (\meta{llx}, \meta{lly}) and upper-right co-ordinates
+% (\meta{urx}, \meta{ury}). All four co-ordinate positions are
+% \meta{dimension expressions}. Material outside of the bounding box is
+% still displayed in the output unless \cs{box_clip:N} is
+% subsequently applied.
+% The updated \meta{box} is an
+% hbox, irrespective of the nature of the \meta{box} before the viewport
+% operation is applied.
+% \end{function}
+%
+% \section{Additions to \pkg{l3expan}}
+%
+% \begin{function}[added = 2018-04-04, updated = 2019-02-08]
+% {\exp_args_generate:n}
+% \begin{syntax}
+% \cs{exp_args_generate:n} \Arg{variant argument specifiers}
+% \end{syntax}
+% Defines \cs[no-index]{exp_args:N\meta{variant}} functions for each
+% \meta{variant} given in the comma list \Arg{variant argument
+% specifiers}. Each \meta{variant} should consist of the letters |N|,
+% |c|, |n|, |V|, |v|, |o|, |f|, |e|, |x|, |p| and the resulting function is
+% protected if the letter |x| appears in the \meta{variant}. This is
+% only useful for cases where \cs{cs_generate_variant:Nn} is not
+% applicable.
+% \end{function}
+%
+% \section{Additions to \pkg{l3fp}}
+%
+% \begin{function}[EXP, added = 2018-11-03]{logb}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |logb(| \meta{fpexpr} |)| \}
+% \end{syntax}
+% Determines the exponent of the \meta{fpexpr}, namely the floor of
+% the base-$10$ logarithm of its absolute value. \enquote{Division by
+% zero} occurs when evaluating $\operatorname{logb}(\pm 0) = -\infty$.
+% Other special values are $\operatorname{logb}(\pm\infty)=+\infty$
+% and $\operatorname{logb}(\nan{})=\nan{}$. If the operand is a tuple
+% or is \nan{}, then \enquote{invalid operation} occurs and the result
+% is \nan{}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-11-03]{\fp_sign:n}
+% \begin{syntax}
+% \cs{fp_sign:n} \Arg{fpexpr}
+% \end{syntax}
+% Evaluates the \meta{fpexpr} and leaves its sign in the input stream
+% using \cs{fp_eval:n} |{sign(|\meta{result}|)}|: $+1$ for positive
+% numbers and for $+\infty$, $-1$ for negative numbers and for
+% $-\infty$, $\pm 0$ for $\pm 0$. If the operand is a tuple or is
+% \nan{}, then \enquote{invalid operation} occurs and the result
+% is~$0$.
+% \end{function}
+%
+% \begin{function}[pTF, added = 2019-08-25]{\fp_if_nan:n}
+% \begin{syntax}
+% \cs{fp_if_nan:n} \Arg{fpexpr}
+% \end{syntax}
+% Evaluates the \meta{fpexpr} and tests whether the result is exactly
+% \nan{}. The test returns \texttt{false} for any other result, even
+% a tuple containing \nan{}.
+% \end{function}
+%
+% \section{Additions to \pkg{l3fparray}}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_new:Nn}
+% \begin{syntax}
+% \cs{fparray_new:Nn} \meta{fparray~var} \Arg{size}
+% \end{syntax}
+% Evaluates the integer expression \meta{size} and allocates an
+% \meta{floating point array variable} with that number of (zero)
+% entries. The variable name should start with |\g_| because
+% assignments are always global.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-05-05]{\fparray_count:N}
+% \begin{syntax}
+% \cs{fparray_count:N} \meta{fparray~var}
+% \end{syntax}
+% Expands to the number of entries in the \meta{floating point array
+% variable}. This is performed in constant time.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_gset:Nnn}
+% \begin{syntax}
+% \cs{fparray_gset:Nnn} \meta{fparray~var} \Arg{position} \Arg{value}
+% \end{syntax}
+% Stores the result of evaluating the floating point expression
+% \meta{value} into the \meta{floating point array variable} at the
+% (integer expression) \meta{position}. If the \meta{position} is not
+% between $1$ and the \cs{fparray_count:N}, an error occurs.
+% Assignments are always global.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-05]{\fparray_gzero:N}
+% \begin{syntax}
+% \cs{fparray_gzero:N} \meta{fparray~var}
+% \end{syntax}
+% Sets all entries of the \meta{floating point array variable} to
+% $+0$. Assignments are always global.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2018-05-05]
+% {\fparray_item:Nn, \fparray_item_to_tl:Nn}
+% \begin{syntax}
+% \cs{fparray_item:Nn} \meta{fparray~var} \Arg{position}
+% \end{syntax}
+% Applies \cs{fp_use:N} or \cs{fp_to_tl:N} (respectively) to the
+% floating point entry stored at the (integer expression)
+% \meta{position} in the \meta{floating point array variable}. If the
+% \meta{position} is not between $1$ and the \cs{fparray_count:N}, an
+% error occurs.
+% \end{function}
+%
+% \section{Additions to \pkg{l3file}}
+%
+% \begin{function}[added = 2019-05-13, TF]{\file_compare_timestamp:nNn}
+% \begin{syntax}
+% \cs{file_compare_timestamp:nNn} \Arg{file-1} \meta{comparator} \Arg{file-2} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Compares the file stamps on the two \meta{files} as indicated by
+% the \meta{comparator}, and inserts either the \meta{true code}
+% or \meta{false case} as required. A file which is not found
+% is treated as older than any file which is found.This allows for
+% example the construct
+% \begin{verbatim}
+% \file_compare_timestamp:nNnT { source-file } > { derived-file }
+% {
+% % Code to regenerate derived file
+% }
+% \end{verbatim}
+% to work when the derived file is entirely absent. The timestamp
+% of two absent files is regarded as different.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-13]{\ior_map_variable:NNn}
+% \begin{syntax}
+% \cs{ior_map_variable:NNn} \meta{stream} \meta{tl~var} \Arg{code}
+% \end{syntax}
+% For each set of \meta{lines} obtained by calling \cs{ior_get:NN}
+% until reaching the end of the file, stores the \meta{lines} in the
+% \meta{tl~var} then applies the \meta{code}. The \meta{code} will
+% usually make use of the \meta{variable}, but this is not enforced.
+% The assignments to the \meta{variable} are local.
+% Its value after the loop is the last set of \meta{lines}, or its
+% original value if the \meta{stream} is empty. \TeX{} ignores
+% any trailing new-line marker from the file it reads.
+% This function is typically faster than \cs{ior_map_inline:Nn}.
+% \end{function}
+%
+% \begin{function}[added = 2019-01-13]{\ior_str_map_variable:NNn}
+% \begin{syntax}
+% \cs{ior_str_map_variable:NNn} \meta{stream} \meta{variable} \Arg{code}
+% \end{syntax}
+% For each \meta{line} in the \meta{stream}, stores the \meta{line} in
+% the \meta{variable} then applies the \meta{code}. The material is
+% read from the \meta{stream} as a series of tokens with category code
+% $12$ (other), with the exception of space characters which are given
+% category code $10$ (space). The \meta{code} will usually make use
+% of the \meta{variable}, but this is not enforced. The assignments
+% to the \meta{variable} are local. Its value after the loop is the
+% last \meta{line}, or its original value if the \meta{stream} is
+% empty. Note that \TeX{} removes trailing
+% space and tab characters (character codes 32 and 9) from every line
+% upon input. \TeX{} also ignores any trailing new-line marker from
+% the file it reads.
+% This function is typically faster than \cs{ior_str_map_inline:Nn}.
+% \end{function}
+%
+% \begin{function}[added = 2018-12-29]{\iow_allow_break:}
+% \begin{syntax}
+% \cs{iow_allow_break:}
+% \end{syntax}
+% In the first argument of \cs{iow_wrap:nnnN} (for instance in
+% messages), inserts a break-point that allows a line break.
+% In other words this is a zero-width breaking space.
+% \end{function}
+%
+% \begin{function}[added = 2019-03-23]{\ior_get_term:nN, \ior_str_get_term:nN}
+% \begin{syntax}
+% \cs{ior_get_term:nN} \meta{prompt} \meta{token list variable}
+% \end{syntax}
+% Function that reads one or more lines (until an equal number of left
+% and right braces are found) from the terminal and stores
+% the result locally in the \meta{token list} variable. Tokenization
+% occurs as described for \cs{ior_get:NN} or \cs{ior_str_get:NN}, respectively.
+% When the \meta{prompt}
+% is empty, \TeX{} will wait for input without any other indication:
+% typically the programmer will have provided a suitable text using
+% e.g.~\cs{iow_term:n}. Where the \meta{prompt} is given, it will appear
+% in the terminal followed by an |=|, e.g.
+% \begin{verbatim}
+% prompt=
+% \end{verbatim}
+% \end{function}
+%
+% \begin{function}[added = 2019-05-08]{\ior_shell_open:Nn}
+% \begin{syntax}
+% \cs{ior_shell_open:nN} \meta{stream} \Arg{shell~command}
+% \end{syntax}
+% Opens the \emph{pseudo}-file created by the output of the
+% \meta{shell command} for reading using \meta{stream} as the
+% control sequence for access. If the \meta{stream} was already
+% open it is closed before the new operation begins. The
+% \meta{stream} is available for access immediately and will remain
+% allocated to \meta{shell command} until a \cs{ior_close:N} instruction
+% is given or the \TeX{} run ends.
+% If piped system calls are disabled an error is raised.
+%
+% For details of handling of the \meta{shell command}, see
+% \cs{sys_shell_get:nnN(TF)}.
+% \end{function}
+%
+% \section{Additions to \pkg{l3flag}}
+%
+% \begin{function}[EXP, added = 2018-04-02]{\flag_raise_if_clear:n}
+% \begin{syntax}
+% \cs{flag_raise_if_clear:n} \Arg{flag name}
+% \end{syntax}
+% Ensures the \meta{flag} is raised by making its height at least~$1$,
+% locally.
+% \end{function}
+%
+% \section{Additions to \pkg{l3int}}
+%
+% \begin{function}[EXP, added = 2018-11-03]{\int_sign:n}
+% \begin{syntax}
+% \cs{int_sign:n} \Arg{intexpr}
+% \end{syntax}
+% Evaluates the \meta{integer expression} then leaves $1$ or $0$ or
+% $-1$ in the input stream according to the sign of the result.
+% \end{function}
+%
+% \section{Additions to \pkg{l3intarray}}
+%
+% \begin{function}[added = 2018-05-05]{\intarray_gset_rand:Nnn, \intarray_gset_rand:Nn}
+% \begin{syntax}
+% \cs{intarray_gset_rand:Nnn} \meta{intarray~var} \Arg{minimum} \Arg{maximum}
+% \cs{intarray_gset_rand:Nn} \meta{intarray~var} \Arg{maximum}
+% \end{syntax}
+% Evaluates the integer expressions \meta{minimum} and \meta{maximum}
+% then sets each entry (independently) of the \meta{integer array
+% variable} to a pseudo-random number between the two (with bounds
+% included). If the absolute value of either bound is bigger than
+% $2^{30}-1$, an error occurs. Entries are generated in the same way
+% as repeated calls to \cs{int_rand:nn} or \cs{int_rand:n}
+% respectively, in particular for the second function the
+% \meta{minimum} is $1$.
+% Assignments are always global.
+% This is not available in older versions of \XeTeX{}.
+% \end{function}
+%
+% \subsection{Working with contents of integer arrays}
+%
+% \begin{function}[added = 2018-05-04, rEXP]{\intarray_const_from_clist:Nn}
+% \begin{syntax}
+% \cs{intarray_const_from_clist:Nn} \meta{intarray~var} \meta{intexpr clist}
+% \end{syntax}
+% Creates a new constant \meta{integer array variable} or raises an
+% error if the name is already taken. The \meta{integer array
+% variable} is set (globally) to contain as its items the results of
+% evaluating each \meta{integer expression} in the \meta{comma list}.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-04, rEXP]{\intarray_to_clist:N}
+% \begin{syntax}
+% \cs{intarray_to_clist:N} \meta{intarray~var}
+% \end{syntax}
+% Converts the \meta{intarray} to integer denotations separated by
+% commas. All tokens have category code other. If the
+% \meta{intarray} has no entry the result is empty; otherwise the
+% result has one fewer comma than the number of items.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-04]{\intarray_show:N, \intarray_log:N}
+% \begin{syntax}
+% \cs{intarray_show:N} \meta{intarray~var}
+% \cs{intarray_log:N} \meta{intarray~var}
+% \end{syntax}
+% Displays the items in the \meta{integer array variable} in the
+% terminal or writes them in the log file.
+% \end{function}
+%
+% \section{Additions to \pkg{l3msg}}
+%
+% In very rare cases it may be necessary to produce errors in an
+% expansion-only context. The functions in this section should only be
+% used if there is no alternative approach using \cs{msg_error:nnnnnn}
+% or other non-expandable commands from the previous section. Despite
+% having a similar interface as non-expandable messages, expandable
+% errors must be handled internally very differently from normal error
+% messages, as none of the tools to print to the terminal or the log
+% file are expandable. As a result, short-hands such as |\{| or |\\| do
+% not work, and messages must be very short (with default settings,
+% they are truncated after approximately 50 characters). It is
+% advisable to ensure that the message is understandable even when
+% truncated, by putting the most important information up front.
+% Another particularity of expandable messages is that they
+% cannot be redirected or turned off by the user.
+%
+% \begin{function}[EXP, added = 2015-08-06, updated = 2019-02-28]
+% {
+% \msg_expandable_error:nnnnnn ,
+% \msg_expandable_error:nnnnn ,
+% \msg_expandable_error:nnnn ,
+% \msg_expandable_error:nnn ,
+% \msg_expandable_error:nn ,
+% \msg_expandable_error:nnffff ,
+% \msg_expandable_error:nnfff ,
+% \msg_expandable_error:nnff ,
+% \msg_expandable_error:nnf ,
+% }
+% \begin{syntax}
+% \cs{msg_expandable_error:nnnnnn} \Arg{module} \Arg{message} \Arg{arg one} \Arg{arg two} \Arg{arg three} \Arg{arg four}
+% \end{syntax}
+% Issues an \enquote{Undefined error} message from \TeX{} itself
+% using the undefined control sequence \cs{::error} then prints
+% \enquote{! \meta{module}: }\meta{error message}, which should be
+% short. With default settings, anything beyond approximately $60$
+% characters long (or bytes in some engines) is cropped. A leading
+% space might be removed as well.
+% \end{function}
+%
+% \begin{function}[added = 2017-12-04]{\msg_show_eval:Nn, \msg_log_eval:Nn}
+% \begin{syntax}
+% \cs{msg_show_eval:Nn} \meta{function} \Arg{expression}
+% \end{syntax}
+% Shows or logs the \meta{expression} (turned into a string), an equal
+% sign, and the result of applying the \meta{function} to the
+% \Arg{expression} (with \texttt{f}-expansion). For instance, if the
+% \meta{function} is \cs{int_eval:n} and the \meta{expression} is
+% |1+2| then this logs |> 1+2=3.|
+% \end{function}
+%
+% \begin{function}[added = 2017-12-04]
+% {
+% \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{syntax}
+% \cs{msg_show: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 shown on the terminal and the \TeX{} run is
+% interrupted in a manner similar to \cs{tl_show:n}. This is used in
+% conjunction with \cs{msg_show_item:n} and similar functions to print
+% complex variable contents completely. If the formatted text does
+% not contain |>~| at the start of a line, an additional line |>~.|
+% will be put at the end. In addition, a final period is added if not
+% present.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2017-12-04]
+% {\msg_show_item:n, \msg_show_item_unbraced:n, \msg_show_item:nn, \msg_show_item_unbraced:nn}
+% \begin{syntax}
+% \cs{seq_map_function:NN} \meta{seq} \cs{msg_show_item:n}
+% \cs{prop_map_function:NN} \meta{prop} \cs{msg_show_item:nn}
+% \end{syntax}
+% Used in the text of messages for \cs{msg_show:nnxxxx} to show or log
+% a list of items or key--value pairs. The one-argument functions are
+% used for sequences, clist or token lists and the others for property
+% lists. These functions turn their arguments to strings.
+% \end{function}
+%
+% \section{Additions to \pkg{l3prg}}
+%
+% \begin{function}[added = 2017-11-28]{\bool_const:Nn, \bool_const:cn}
+% \begin{syntax}
+% \cs{bool_const:Nn} \meta{boolean} \Arg{boolexpr}
+% \end{syntax}
+% Creates a new constant \meta{boolean} or raises an error if the name
+% is already taken. The value of the \meta{boolean} is set globally to
+% the result of evaluating the \meta{boolexpr}.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-10]
+% {
+% \bool_set_inverse:N , \bool_set_inverse:c ,
+% \bool_gset_inverse:N, \bool_gset_inverse:c
+% }
+% \begin{syntax}
+% \cs{bool_set_inverse:N} \meta{boolean}
+% \end{syntax}
+% Toggles the \meta{boolean} from \texttt{true} to \texttt{false} and
+% conversely: sets it to the inverse of its current value.
+% \end{function}
+%
+% \section{Additions to \pkg{l3prop}}
+%
+% \begin{function}[rEXP]
+% {\prop_map_tokens:Nn, \prop_map_tokens:cn}
+% \begin{syntax}
+% \cs{prop_map_tokens:Nn} \meta{property list} \Arg{code}
+% \end{syntax}
+% Analogue of \cs{prop_map_function:NN} which maps several tokens
+% instead of a single function. The \meta{code} receives each
+% key--value pair in the \meta{property list} as two trailing brace
+% groups. For instance,
+% \begin{verbatim}
+% \prop_map_tokens:Nn \l_my_prop { \str_if_eq:nnT { mykey } }
+% \end{verbatim}
+% expands to the value corresponding to \texttt{mykey}: for each
+% pair in |\l_my_prop| the function \cs{str_if_eq:nnT} receives
+% \texttt{mykey}, the \meta{key} and the \meta{value} as its three
+% arguments. For that specific task, \cs{prop_item:Nn} is faster.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2016-12-06]
+% {\prop_rand_key_value:N, \prop_rand_key_value:c}
+% \begin{syntax}
+% \cs{prop_rand_key_value:N} \meta{prop~var}
+% \end{syntax}
+% Selects a pseudo-random key--value pair from the \meta{property list}
+% and returns \Arg{key} and \Arg{value}. If the \meta{property list} is
+% empty the result is empty.
+% This is not available in older versions of \XeTeX{}.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{value}
+% does not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28, updated = 2019-08-25]
+% {
+% \prop_set_from_keyval:Nn, \prop_set_from_keyval:cn,
+% \prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn,
+% }
+% \begin{syntax}
+% \cs{prop_set_from_keyval:Nn} \meta{prop~var}
+% \{
+% \meta{key1} |=| \meta{value1} |,|
+% \meta{key2} |=| \meta{value2} |,| \ldots{}
+% \}
+% \end{syntax}
+% Sets \meta{prop~var} to contain key--value pairs given in the second
+% argument. If duplicate keys appear only one of the values is kept.
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28, updated = 2019-08-25]
+% {\prop_const_from_keyval:Nn, \prop_const_from_keyval:cn}
+% \begin{syntax}
+% \cs{prop_const_from_keyval:Nn} \meta{prop~var}
+% \{
+% \meta{key1} |=| \meta{value1} |,|
+% \meta{key2} |=| \meta{value2} |,| \ldots{}
+% \}
+% \end{syntax}
+% Creates a new constant \meta{prop~var} or raises an error if the
+% name is already taken. The \meta{prop~var} is set globally to
+% contain key--value pairs given in the second argument.
+% If duplicate keys appear only one of the values is kept.
+% \end{function}
+%
+% \section{Additions to \pkg{l3seq}}
+%
+% \begin{function}[rEXP]
+% {
+% \seq_mapthread_function:NNN, \seq_mapthread_function:NcN,
+% \seq_mapthread_function:cNN, \seq_mapthread_function:ccN
+% }
+% \begin{syntax}
+% \cs{seq_mapthread_function:NNN} \meta{seq_1} \meta{seq_2} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every pair of items
+% \meta{seq_1-item}--\meta{seq_2-item} from the two sequences, returning
+% items from both sequences from left to right. The \meta{function}
+% receives two \texttt{n}-type arguments for each iteration. The mapping
+% terminates when
+% the end of either sequence is reached (\emph{i.e.}~whichever sequence has
+% fewer items determines how many iterations
+% occur).
+% \end{function}
+%
+% \begin{function}{\seq_set_filter:NNn, \seq_gset_filter:NNn}
+% \begin{syntax}
+% \cs{seq_set_filter:NNn} \meta{sequence_1} \meta{sequence_2} \Arg{inline boolexpr}
+% \end{syntax}
+% Evaluates the \meta{inline boolexpr} for every \meta{item} stored
+% within the \meta{sequence_2}. The \meta{inline boolexpr}
+% receives the \meta{item} as |#1|. The sequence of all \meta{items}
+% for which the \meta{inline boolexpr} evaluated to \texttt{true}
+% is assigned to \meta{sequence_1}.
+% \begin{texnote}
+% Contrarily to other mapping functions, \cs{seq_map_break:} cannot
+% be used in this function, and would lead to low-level \TeX{} errors.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2011-12-22]
+% {\seq_set_map:NNn, \seq_gset_map:NNn}
+% \begin{syntax}
+% \cs{seq_set_map:NNn} \meta{sequence_1} \meta{sequence_2} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{sequence_2}. The \meta{inline function} should
+% consist of code which will receive the \meta{item} as |#1|.
+% The sequence resulting from \texttt{x}-expanding
+% \meta{inline function} applied to each \meta{item}
+% is assigned to \meta{sequence_1}. As such, the code
+% in \meta{inline function} should be expandable.
+% \begin{texnote}
+% Contrarily to other mapping functions, \cs{seq_map_break:} cannot
+% be used in this function, and would lead to low-level \TeX{} errors.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2017-11-28]
+% {\seq_const_from_clist:Nn, \seq_const_from_clist:cn}
+% \begin{syntax}
+% \cs{seq_const_from_clist:Nn} \meta{seq~var} \Arg{comma-list}
+% \end{syntax}
+% Creates a new constant \meta{seq~var} or raises an error if the name
+% is already taken. The \meta{seq~var} is set globally to contain the
+% items in the \meta{comma list}.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-06]
+% {\seq_set_from_function:NnN, \seq_gset_from_function:NnN}
+% \begin{syntax}
+% \cs{seq_set_from_function:NnN} \meta{seq~var} \Arg{loop~code} \meta{function}
+% \end{syntax}
+% Sets the \meta{seq~var} equal to a sequence whose items are obtained
+% by \texttt{x}-expanding \meta{loop~code} \meta{function}. This
+% expansion must result in successive calls to the \meta{function}
+% with no nonexpandable tokens in between. More precisely the
+% \meta{function} is replaced by a wrapper function that inserts the
+% appropriate separators between items in the sequence. The
+% \meta{loop~code} must be expandable; it can be for example
+% \cs{tl_map_function:NN} \meta{tl~var} or \cs{clist_map_function:nN}
+% \Arg{clist} or \cs{int_step_function:nnnN} \Arg{initial value}
+% \Arg{step} \Arg{final value}.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-06]
+% {\seq_set_from_inline_x:Nnn, \seq_gset_from_inline_x:Nnn}
+% \begin{syntax}
+% \cs{seq_set_from_inline_x:Nnn} \meta{seq~var} \Arg{loop~code} \Arg{inline~code}
+% \end{syntax}
+% Sets the \meta{seq~var} equal to a sequence whose items are obtained
+% by \texttt{x}-expanding \meta{loop~code} applied to a
+% \meta{function} derived from the \meta{inline~code}. A
+% \meta{function} is defined, that takes one argument,
+% \texttt{x}-expands the \meta{inline~code} with that argument
+% as~|#1|, then adds appropriate separators to turn the result into an
+% item of the sequence. The \texttt{x}-expansion of \meta{loop~code}
+% \meta{function} must result in successive calls to the
+% \meta{function} with no nonexpandable tokens in between. The
+% \meta{loop~code} must be expandable; it can be for example
+% \cs{tl_map_function:NN} \meta{tl~var} or \cs{clist_map_function:nN}
+% \Arg{clist} or \cs{int_step_function:nnnN} \Arg{initial value}
+% \Arg{step} \Arg{final value}, but not the analogous \enquote{inline}
+% mappings.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-29]{\seq_shuffle:N, \seq_gshuffle:N}
+% \begin{syntax}
+% \cs{seq_shuffle:N} \meta{seq~var}
+% \end{syntax}
+% Sets the \meta{seq~var} to the result of placing the items of the
+% \meta{seq~var} in a random order. Each item is (roughly) as likely
+% to end up in any given position.
+% \begin{texnote}
+% For sequences with more than $13$ items or so, only a small
+% proportion of all possible permutations can be reached, because
+% the random seed \cs{sys_rand_seed:} only has $28$-bits. The use
+% of \tn{toks} internally means that sequences with more than
+% $32767$ or $65535$ items (depending on the engine) cannot be
+% shuffled.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2018-05-03]{\seq_indexed_map_function:NN}
+% \begin{syntax}
+% \cs{seq_indexed_map_function:NN} \meta{seq~var} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every entry in the \meta{sequence
+% variable}. The \meta{function} should have signature |:nn|. It
+% receives two arguments for each iteration: the \meta{index} (namely
+% |1| for the first entry, then |2| and so on) and the \meta{item}.
+% \end{function}
+%
+% \begin{function}[added = 2018-05-03]{\seq_indexed_map_inline:Nn}
+% \begin{syntax}
+% \cs{seq_indexed_map_inline:Nn} \meta{seq~var} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every entry in the \meta{sequence
+% variable}. The \meta{inline function} should consist of code which
+% receives the \meta{index} (namely |1| for the first entry, then |2|
+% and so on) as~|#1| and the \meta{item} as~|#2|.
+% \end{function}
+%
+% \section{Additions to \pkg{l3skip}}
+%
+% \begin{function}[EXP, added = 2018-11-03]{\dim_sign:n}
+% \begin{syntax}
+% \cs{dim_sign:n} \Arg{dimexpr}
+% \end{syntax}
+% Evaluates the \meta{dimexpr} then leaves $1$ or $0$ or $-1$ in the
+% input stream according to the sign of the result.
+% \end{function}
+%
+% \section{Additions to \pkg{l3sys}}
+%
+% \begin{function}[noTF, added = 2019-01-16, updated = 2019-02-16]
+% {\sys_shell_get:nnN}
+% \begin{syntax}
+% \cs{sys_shell_get:nnN} \Arg{shell~command} \Arg{setup} \meta{tl~var}
+% \cs{sys_shell_get:nnNTF} \Arg{shell~command} \Arg{setup} \meta{tl~var} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Defines \meta{tl} to the text returned by the \meta{shell command}.
+% The \meta{shell command} is converted to a string using
+% \cs{tl_to_str:n}. Category codes may need to be set appropriately
+% via the \meta{setup} argument, which is run just before running the
+% \meta{shell command} (in a group).
+% If shell escape is disabled, the \meta{tl~var} will be set to
+% \cs{q_no_value} in the non-branching version.
+% Note that quote characters (|"|) \emph{cannot} be used inside the
+% \meta{shell command}. The \cs{sys_shell_get:nnNTF} conditional
+% returns \texttt{true} if the shell is available and no quote is
+% detected, and \texttt{false} otherwise.
+% \end{function}
+%
+% \begin{variable}[added = 2018-05-02]{\c_sys_engine_version_str}
+% The version string of the current engine, in the same form as
+% given in the banner issued when running a job. For \pdfTeX{}
+% and \LuaTeX{} this is of the form
+% \begin{quote}
+% \meta{major}.\meta{minor}.\meta{revision}
+% \end{quote}
+% For \XeTeX{}, the form is
+% \begin{quote}
+% \meta{major}.\meta{minor}
+% \end{quote}
+% For \pTeX{} and \upTeX{}, only releases since \TeX{} Live 2018
+% make the data available, and the form is more complex, as it comprises
+% the \pTeX{} version, the \upTeX{} version and the e-\pTeX{} version.
+% \begin{quote}
+% p\meta{major}.\meta{minor}.\meta{revision}-u\meta{major}.\meta{minor}^^A
+% -\meta{epTeX}
+% \end{quote}
+% where the |u| part is only present for \upTeX{}.
+% \end{variable}
+%
+% \begin{function}[added = 2017-05-27, EXP, pTF]{\sys_if_rand_exist:}
+% \begin{syntax}
+% \cs{sys_if_rand_exist_p:}
+% \cs{sys_if_rand_exist:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the engine has a pseudo-random number generator. Currently
+% this is the case in \pdfTeX{}, \LuaTeX{}, \pTeX{} and \upTeX{}.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27, EXP]{\sys_rand_seed:}
+% \begin{syntax}
+% \cs{sys_rand_seed:}
+% \end{syntax}
+% Expands to the current value of the engine's random seed, a
+% non-negative integer. In engines without random number support this
+% expands to $0$.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27]{\sys_gset_rand_seed:n}
+% \begin{syntax}
+% \cs{sys_gset_rand_seed:n} \Arg{intexpr}
+% \end{syntax}
+% Globally sets the seed for the engine's pseudo-random number
+% generator to the \meta{integer expression}. This random seed
+% affects all \cs[no-index]{\ldots{}_rand} functions (such as
+% \cs{int_rand:nn} or \cs{clist_rand_item:n}) as well as other
+% packages relying on the engine's random number generator. In
+% engines without random number support this produces an error.
+% \begin{texnote}
+% While a $32$-bit (signed) integer can be given as a seed, only the
+% absolute value is used and any number beyond $2^{28}$ is divided
+% by an appropriate power of~$2$. We recommend using an integer in
+% $[0,2^{28}-1]$.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2018-07-27, EXP, pTF]
+% {
+% \sys_if_platform_unix:,
+% \sys_if_platform_windows:
+% }
+% \begin{syntax}
+% \cs{sys_if_platform_unix:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Conditionals which allow platform-specific code to be used. The names
+% follow the \Lua{} |os.type()| function, \emph{i.e.}~all Unix-like systems
+% are |unix| (including Linux and MacOS).
+% \end{function}
+%
+% \begin{variable}[added = 2018-07-27]{\c_sys_platform_str}
+% The current platform given as a lower case string: one of
+% |unix|, |windows| or |unknown|.
+% \end{variable}
+%
+% \begin{variable}[added = 2017-05-27]{\c_sys_shell_escape_int}
+% This variable exposes the internal triple of the shell escape
+% status. The possible values are
+% \begin{description}
+% \item[0] Shell escape is disabled
+% \item[1] Unrestricted shell escape is enabled
+% \item[2] Restricted shell escape is enabled
+% \end{description}
+% \end{variable}
+%
+% \begin{function}[added = 2017-05-27, EXP, pTF]{\sys_if_shell:}
+% \begin{syntax}
+% \cs{sys_if_shell_p:}
+% \cs{sys_if_shell:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Performs a check for whether shell escape is enabled. This
+% returns true if either of restricted or unrestricted shell escape
+% is enabled.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27, EXP, pTF]{\sys_if_shell_unrestricted:}
+% \begin{syntax}
+% \cs{sys_if_shell_unrestricted_p:}
+% \cs{sys_if_shell_unrestricted:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Performs a check for whether \emph{unrestricted} shell escape is
+% enabled.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27, EXP, pTF]{\sys_if_shell_restricted:}
+% \begin{syntax}
+% \cs{sys_if_shell_restricted_p:}
+% \cs{sys_if_shell_restricted:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Performs a check for whether \emph{restricted} shell escape is
+% enabled. This returns false if unrestricted shell escape is
+% enabled. Unrestricted shell escape is not considered a superset
+% of restricted shell escape in this case. To find whether any
+% shell escape is enabled use \cs{sys_if_shell:}.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27]{\sys_shell_now:n, \sys_shell_now:x}
+% \begin{syntax}
+% \cs{sys_shell_now:n} \Arg{tokens}
+% \end{syntax}
+% Execute \meta{tokens} through shell escape immediately.
+% \end{function}
+%
+% \begin{function}[added = 2017-05-27]{\sys_shell_shipout:n, \sys_shell_shipout:x}
+% \begin{syntax}
+% \cs{sys_shell_shipout:n} \Arg{tokens}
+% \end{syntax}
+% Execute \meta{tokens} through shell escape at shipout.
+% \end{function}
+%
+% \section{Additions to \pkg{l3tl}}
+%
+% \begin{function}[EXP, added = 2014-06-30, updated = 2016-01-12]
+% {
+% \tl_lower_case:n, \tl_upper_case:n, \tl_mixed_case:n,
+% \tl_lower_case:nn, \tl_upper_case:nn, \tl_mixed_case:nn
+% }
+% \begin{syntax}
+% \cs{tl_upper_case:n} \Arg{tokens}
+% \cs{tl_upper_case:nn} \Arg{language} \Arg{tokens}
+% \end{syntax}
+% These functions are intended to be applied to input which may be
+% regarded broadly as \enquote{text}. They traverse the \meta{tokens} and
+% change the case of characters as discussed below. The character code of
+% the characters replaced may be arbitrary: the replacement characters
+% have standard document-level category codes ($11$ for letters, $12$ for
+% letter-like characters which can also be case-changed). Begin-group and
+% end-group characters in the \meta{tokens} are normalized and become |{|
+% and |}|, respectively.
+%
+% Importantly, notice that these functions are intended for working with
+% user text for typesetting. For case changing programmatic data see the
+% \pkg{l3str} module and discussion there of \cs{str_lower_case:n},
+% \cs{str_upper_case:n} and \cs{str_fold_case:n}.
+% \end{function}
+%
+% The functions perform expansion on the input in most cases. In particular,
+% input in the form of token lists or expandable functions is expanded
+% \emph{unless} it falls within one of the special handling classes described
+% below. This expansion approach means that in general the result of case
+% changing matches the \enquote{natural} outcome expected from a
+% \enquote{functional} approach to case modification. For example
+% \begin{verbatim}
+% \tl_set:Nn \l_tmpa_tl { hello }
+% \tl_upper_case:n { \l_tmpa_tl \c_space_tl world }
+% \end{verbatim}
+% produces
+% \begin{verbatim}
+% HELLO WORLD
+% \end{verbatim}
+% The expansion approach taken means that in package mode any \LaTeXe{}
+% \enquote{robust} commands which may appear in the input should be converted
+% to engine-protected versions using for example the \tn{robustify} command
+% from the \pkg{etoolbox} package.
+%
+% \begin{variable}{\l_tl_case_change_math_tl}
+% Case changing does not take place within math mode material so for example
+% \begin{verbatim}
+% \tl_upper_case:n { Some~text~$y = mx + c$~with~{Braces} }
+% \end{verbatim}
+% becomes
+% \begin{verbatim}
+% SOME TEXT $y = mx + c$ WITH {BRACES}
+% \end{verbatim}
+% Material inside math mode is left entirely unchanged: in particular, no
+% expansion is undertaken.
+%
+% Detection of math mode is controlled by the list of tokens in
+% \cs{l_tl_case_change_math_tl}, which should be in open--close pairs. In
+% package mode the standard settings is
+% \begin{verbatim}
+% $ $ \( \)
+% \end{verbatim}
+%
+% Note that while expansion occurs when searching the text it does not
+% apply to math mode material (which should be unaffected by case changing).
+% As such, whilst the opening token for math mode may be \enquote{hidden}
+% inside a command/macro, the closing one cannot be as this is being
+% searched for in math mode. Typically, in the types of \enquote{text}
+% the case changing functions are intended to apply to this should not be
+% an issue.
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_exclude_tl}
+% Case changing can be prevented by using any command on the list
+% \cs{l_tl_case_change_exclude_tl}. Each entry should be a function
+% to be followed by one argument: the latter will be preserved as-is
+% with no expansion. Thus for example following
+% \begin{verbatim}
+% \tl_put_right:Nn \l_tl_case_change_exclude_tl { \NoChangeCase }
+% \end{verbatim}
+% the input
+% \begin{verbatim}
+% \tl_upper_case:n
+% { Some~text~$y = mx + c$~with~\NoChangeCase {Protection} }
+% \end{verbatim}
+% will result in
+% \begin{verbatim}
+% SOME TEXT $y = mx + c$ WITH \NoChangeCase {Protection}
+% \end{verbatim}
+% Notice that the case changing mapping preserves the inclusion of
+% the escape functions: it is left to other code to provide suitable
+% definitions (typically equivalent to \cs{use:n}). In particular, the
+% result of case changing is returned protected by \cs{exp_not:n}.
+%
+% When used with \LaTeXe{} the commands |\cite|, |\ensuremath|, |\label|
+% and |\ref| are automatically included in the list for exclusion from
+% case changing.
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_accents_tl}
+% This list specifies accent commands which should be left unexpanded
+% in the output. This allows for example
+% \begin{verbatim}
+% \tl_upper_case:n { \" { a } }
+% \end{verbatim}
+% to yield
+% \begin{verbatim}
+% \" { A }
+% \end{verbatim}
+% irrespective of the expandability of |\"|.
+%
+% The standard contents of this variable is |\"|, |\'|, |\.|, |\^|, |\`|,
+% |\~|, |\c|, |\H|, |\k|, |\r|, |\t|, |\u| and |\v|.
+% \end{variable}
+%
+% \enquote{Mixed} case conversion may be regarded informally as converting the
+% first character of the \meta{tokens} to upper case and the rest to lower
+% case. However, the process is more complex than this as there are some
+% situations where a single lower case character maps to a special form, for
+% example \texttt{ij} in Dutch which becomes \texttt{IJ}. As such,
+% \cs[index=tl_mixed_case:n]{tl_mixed_case:n(n)}
+% implement a more sophisticated mapping which accounts
+% for this and for modifying accents on the first letter. Spaces at the start
+% of the \meta{tokens} are ignored when finding the first \enquote{letter} for
+% conversion.
+% \begin{verbatim}
+% \tl_mixed_case:n { hello~WORLD } % => "Hello world"
+% \tl_mixed_case:n { ~hello~WORLD } % => " Hello world"
+% \tl_mixed_case:n { {hello}~WORLD } % => "{Hello} world"
+% \end{verbatim}
+% When finding the first \enquote{letter} for this process, any content in
+% math mode or covered by \cs{l_tl_case_change_exclude_tl} is ignored.
+%
+% (Note that the Unicode Consortium describe this as \enquote{title case}, but
+% that in English title case applies on a word-by-word basis. The
+% \enquote{mixed} case implemented here is a lower level concept needed for
+% both \enquote{title} and \enquote{sentence} casing of text.)
+%
+% \begin{variable}{\l_tl_mixed_case_ignore_tl}
+% The list of characters to ignore when searching for the first
+% \enquote{letter} in mixed-casing is determined by
+% \cs{l_tl_mixed_change_ignore_tl}. This has the standard setting
+% \begin{verbatim}
+% ( [ { ` -
+% \end{verbatim}
+% where comparisons are made on a character basis.
+% \end{variable}
+%
+% As is generally true for \pkg{expl3}, these functions are designed to
+% work with Unicode input only. As such, UTF-8 input is assumed for
+% \emph{all} engines. When used with \XeTeX{} or \LuaTeX{} a full range of
+% Unicode transformations are enabled. Specifically, the standard mappings
+% here follow those defined by the \href{http://www.unicode.org}^^A
+% {Unicode Consortium} in \texttt{UnicodeData.txt} and
+% \texttt{SpecialCasing.txt}. In the case of $8$-bit engines, mappings
+% are provided for characters which can be represented in output typeset
+% using the |T1| font encoding. Thus for example |ä| can be case-changed
+% using \pdfTeX{}. For \pTeX{} only the ASCII range is covered as the
+% engine treats input outside of this range as east Asian.
+%
+% Context-sensitive mappings are enabled: language-dependent cases are
+% discussed below. Context detection expands input but treats any
+% unexpandable control sequences as \enquote{failures} to match a context.
+%
+% Language-sensitive conversions are enabled using the \meta{language}
+% argument, and follow Unicode Consortium guidelines. Currently, the
+% languages recognised for special handling are as follows.
+% \begin{itemize}
+% \item Azeri and Turkish (\texttt{az} and \texttt{tr}).
+% The case pairs I/i-dotless and I-dot/i are activated for these
+% languages. The combining dot mark is removed when lower
+% casing I-dot and introduced when upper casing i-dotless.
+% \item German (\texttt{de-alt}).
+% An alternative mapping for German in which the lower case
+% \emph{Eszett} maps to a \emph{gro\ss{}es Eszett}.
+% \item Lithuanian (\texttt{lt}).
+% The lower case letters i and j should retain a dot above when the
+% accents grave, acute or tilde are present. This is implemented for
+% lower casing of the relevant upper case letters both when input as
+% single Unicode codepoints and when using combining accents. The
+% combining dot is removed when upper casing in these cases. Note that
+% \emph{only} the accents used in Lithuanian are covered: the behaviour
+% of other accents are not modified.
+% \item Dutch (\texttt{nl}).
+% Capitalisation of \texttt{ij} at the beginning of mixed cased
+% input produces \texttt{IJ} rather than \texttt{Ij}. The output
+% retains two separate letters, thus this transformation \emph{is}
+% available using \pdfTeX{}.
+% \end{itemize}
+%
+% Creating additional context-sensitive mappings requires knowledge
+% of the underlying mapping implementation used here. The team are happy
+% to add these to the kernel where they are well-documented
+% (\emph{e.g.}~in Unicode Consortium or relevant government publications).
+%
+% \begin{function}[EXP, added = 2017-07-15]
+% {
+% \tl_range_braced:Nnn, \tl_range_braced:cnn, \tl_range_braced:nnn,
+% \tl_range_unbraced:Nnn, \tl_range_unbraced:cnn, \tl_range_unbraced:nnn
+% }
+% \begin{syntax}
+% \cs{tl_range_braced:Nnn} \meta{tl~var} \Arg{start index} \Arg{end index}
+% \cs{tl_range_braced:nnn} \Arg{token list} \Arg{start index} \Arg{end index}
+% \cs{tl_range_unbraced:Nnn} \meta{tl~var} \Arg{start index} \Arg{end index}
+% \cs{tl_range_unbraced:nnn} \Arg{token list} \Arg{start index} \Arg{end index}
+% \end{syntax}
+% Leaves in the input stream the items from the \meta{start index} to
+% the \meta{end index} inclusive, using the same indexing as
+% \cs{tl_range:nnn}. Spaces are ignored. Regardless of whether items
+% appear with or without braces in the \meta{token list}, the
+% \cs{tl_range_braced:nnn} function wraps each item in braces, while
+% \cs{tl_range_unbraced:nnn} does not (overall it removes an outer set
+% of braces). For instance,
+% \begin{verbatim}
+% \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { 2 } { 5 } }
+% \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { -4 } { -1 } }
+% \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { -2 } { -1 } }
+% \iow_term:x { \tl_range_braced:nnn { abcd~{e{}}f } { 0 } { -1 } }
+% \end{verbatim}
+% prints \verb*|{b}{c}{d}{e{}}|, \verb*|{c}{d}{e{}}{f}|, \verb*|{e{}}{f}|, and an empty
+% line to the terminal, while
+% \begin{verbatim}
+% \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { 2 } { 5 } }
+% \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { -4 } { -1 } }
+% \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { -2 } { -1 } }
+% \iow_term:x { \tl_range_unbraced:nnn { abcd~{e{}}f } { 0 } { -1 } }
+% \end{verbatim}
+% prints \verb*|bcde{}|, \verb*|cde{}f|, \verb*|e{}f|, and an empty
+% line to the terminal. Because braces are removed, the result of
+% \cs{tl_range_unbraced:nnn} may have a different number of items as
+% for \cs{tl_range:nnn} or \cs{tl_range_braced:nnn}. In cases where
+% preserving spaces is important, consider the slower function
+% \cs{tl_range:nnn}.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% does not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2018-04-01]{\tl_build_begin:N, \tl_build_gbegin:N}
+% \begin{syntax}
+% \cs{tl_build_begin:N} \meta{tl~var}
+% \end{syntax}
+% Clears the \meta{tl~var} and sets it up to support other
+% \cs[no-index]{tl_build_\ldots{}} functions, which allow accumulating
+% large numbers of tokens piece by piece much more efficiently than
+% standard \pkg{l3tl} functions. Until \cs{tl_build_end:N}
+% \meta{tl~var} is called, applying any function from \pkg{l3tl} other
+% than \cs[no-index]{tl_build_\ldots{}} will lead to incorrect
+% results. The |begin| and |gbegin| functions must be used for local
+% and global \meta{tl~var} respectively.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-01]{\tl_build_clear:N, \tl_build_gclear:N}
+% \begin{syntax}
+% \cs{tl_build_clear:N} \meta{tl~var}
+% \end{syntax}
+% Clears the \meta{tl~var} and sets it up to support other
+% \cs[no-index]{tl_build_\ldots{}} functions. The |clear| and
+% |gclear| functions must be used for local and global \meta{tl~var}
+% respectively.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-01]
+% {
+% \tl_build_put_left:Nn, \tl_build_put_left:Nx,
+% \tl_build_gput_left:Nn, \tl_build_gput_left:Nx,
+% \tl_build_put_right:Nn, \tl_build_put_right:Nx,
+% \tl_build_gput_right:Nn, \tl_build_gput_right:Nx,
+% }
+% \begin{syntax}
+% \cs{tl_build_put_left:Nn} \meta{tl~var} \Arg{tokens}
+% \cs{tl_build_put_right:Nn} \meta{tl~var} \Arg{tokens}
+% \end{syntax}
+% Adds \meta{tokens} to the left or right side of the current contents
+% of \meta{tl~var}. The \meta{tl~var} must have been set up with
+% \cs{tl_build_begin:N} or \cs{tl_build_gbegin:N}. The |put| and
+% |gput| functions must be used for local and global \meta{tl~var}
+% respectively. The |right| functions are about twice faster than the
+% |left| functions.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-01]{\tl_build_get:NN}
+% \begin{syntax}
+% \cs{tl_build_get:N} \meta{tl~var_1} \meta{tl~var_2}
+% \end{syntax}
+% Stores the contents of the \meta{tl~var_1} in the \meta{tl~var_2}.
+% The \meta{tl~var_1} must have been set up with \cs{tl_build_begin:N}
+% or \cs{tl_build_gbegin:N}. The \meta{tl~var_2} is a
+% \enquote{normal} token list variable, assigned locally using
+% \cs{tl_set:Nn}.
+% \end{function}
+%
+% \begin{function}[added = 2018-04-01]{\tl_build_end:N, \tl_build_gend:N}
+% \begin{syntax}
+% \cs{tl_build_end:N} \meta{tl~var}
+% \end{syntax}
+% Gets the contents of \meta{tl~var} and stores that into the
+% \meta{tl~var} using \cs{tl_set:Nn}. The \meta{tl~var} must have
+% been set up with \cs{tl_build_begin:N} or \cs{tl_build_gbegin:N}.
+% The |end| and |gend| functions must be used for local and global
+% \meta{tl~var} respectively. These functions completely remove the
+% setup code that enabled \meta{tl~var} to be used for other
+% \cs[no-index]{tl_build_\ldots{}} functions.
+% \end{function}
+%
+% \section{Additions to \pkg{l3token}}
+%
+% \begin{variable}[added = 2017-08-07]{\c_catcode_active_space_tl}
+% Token list containing one character with category code $13$,
+% (\enquote{active}), and character code $32$ (space).
+% \end{variable}
+%
+% \begin{function}[added = 2018-04-06, updated = 2019-05-03, EXP]
+% {
+% \char_lower_case:N, \char_upper_case:N,
+% \char_mixed_case:N, \char_fold_case:N,
+% \char_str_lower_case:N, \char_str_upper_case:N,
+% \char_str_mixed_case:N, \char_str_fold_case:N,
+% }
+% \begin{syntax}
+% \cs{char_lower_case:N} \meta{char}
+% \end{syntax}
+% Converts the \meta{char} to the equivalent case-changed character
+% as detailed by the function name (see \cs{str_fold_case:n}
+% and \cs{tl_mixed_case:n} for details of these terms). The case mapping
+% is carried out with no context-dependence (\emph{cf.}~\cs{tl_upper_case:n},
+% \emph{etc.}) The \texttt{str} versions always generate \enquote{other}
+% (category code $12$) characters, whilst the standard versions generate
+% characters with the currently-active category code (\emph{i.e.}~as if
+% the character had been read directly here).
+% \end{function}
+%
+% \begin{function}[added = 2018-06-01, EXP]{\char_codepoint_to_bytes:n}
+% \begin{syntax}
+% \cs{char_codepoint_to_bytes:n} \Arg{codepoint}
+% \end{syntax}
+% Converts the (Unicode) \meta{codepoint} to UTF-8 bytes. The expansion
+% of this function comprises four brace groups, each of which will contain
+% a hexadecimal value: the appropriate byte. As UTF-8 is a variable-length,
+% one or more of the grouos may be empty: the bytes read in the logical order,
+% such that a two-byte codepoint will have groups |#1| and |#2| filled
+% and |#3| and |#4| empty.
+% \end{function}
+%
+% \begin{function}[added = 2018-09-23]
+% {
+% \peek_catcode_collect_inline:Nn,
+% \peek_charcode_collect_inline:Nn,
+% \peek_meaning_collect_inline:Nn
+% }
+% \begin{syntax}
+% \cs{peek_catcode_collect_inline:Nn} \meta{test token} \Arg{inline code}
+% \cs{peek_charcode_collect_inline:Nn} \meta{test token} \Arg{inline code}
+% \cs{peek_meaning_collect_inline:Nn} \meta{test token} \Arg{inline code}
+% \end{syntax}
+% Collects and removes tokens from the input stream until finding a
+% token that does not match the \meta{test token} (as defined by the
+% test \cs{token_if_eq_catcode:NNTF} or \cs{token_if_eq_charcode:NNTF}
+% or \cs{token_if_eq_meaning:NNTF}). The collected tokens are passed
+% to the \meta{inline code} as~|#1|. When begin-group or end-group
+% tokens (usually |{| or~|}|) are collected they are replaced by
+% implicit \cs{c_group_begin_token} and \cs{c_group_end_token}, and
+% when spaces (including \cs{c_space_token}) are collected they are
+% replaced by explicit spaces.
+%
+% For example the following code prints ``Hello'' to the terminal and
+% leave ``, world!'' in the input stream.
+% \begin{verbatim}
+% \peek_catcode_collect_inline:Nn A { \iow_term:n {#1} } Hello,~world!
+% \end{verbatim}
+% Another example is that the following code tests if the next token is |*|, ignoring intervening spaces, but putting them back using |#1| if there is no~|*|.
+% \begin{verbatim}
+% \peek_meaning_collect_inline:Nn \c_space_token
+% { \peek_charcode:NTF * { star } { no~star #1 } }
+% \end{verbatim}
+% \end{function}
+%
+% \begin{function}[added = 2018-10-01]{\peek_remove_spaces:n}
+% \begin{syntax}
+% \cs{peek_remove_spaces:n} \Arg{code}
+% \end{syntax}
+% Removes explicit and implicit space tokens (category code~$10$ and
+% character code~$32$) from the input stream, then inserts \meta{code}.
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3candidates} Implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \subsection{Additions to \pkg{l3basics}}
+%
+% \begin{macro}{\mode_leave_vertical:}
+% The approach here is different to that used by \LaTeXe{} or plain \TeX{},
+% which unbox a void box to force horizontal mode. That inserts the
+% \tn{everypar} tokens \emph{before} the re-inserted unboxing tokens. The
+% approach here uses either the \tn{quitvmode} primitive or the equivalent
+% protected macro. In vertical mode, the \tn{indent} primitive is inserted:
+% this will switch to horizontal mode and insert \tn{everypar} tokens and
+% nothing else. Unlike the \LaTeXe{} version, the availability of \eTeX{}
+% means using a mode test can be done at for example the start of an
+% \tn{halign}.
+% \begin{macrocode}
+\cs_new_protected:Npn \mode_leave_vertical:
+ {
+ \if_mode_vertical:
+ \exp_after:wN \tex_indent:D
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3box}}
+%
+% \begin{macrocode}
+%<@@=box>
+% \end{macrocode}
+%
+% \subsubsection{Viewing part of a box}
+%
+% \begin{macro}{\box_clip:N, \box_clip:c, \box_gclip:N, \box_gclip:c}
+% A wrapper around the driver-dependent code.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_clip:N #1
+ { \hbox_set:Nn #1 { \@@_backend_clip:N #1 } }
+\cs_generate_variant:Nn \box_clip:N { c }
+\cs_new_protected:Npn \box_gclip:N #1
+ { \hbox_gset:Nn #1 { \@@_backend_clip:N #1 } }
+\cs_generate_variant:Nn \box_gclip:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \box_set_trim:Nnnnn, \box_set_trim:cnnnn,
+% \box_gset_trim:Nnnnn, \box_gset_trim:cnnnn
+% }
+% \begin{macro}{\@@_set_trim:NnnnnN}
+% Trimming from the left- and right-hand edges of the box is easy: kern the
+% appropriate parts off each side.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_trim:Nnnnn #1#2#3#4#5
+ { \@@_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN }
+\cs_generate_variant:Nn \box_set_trim:Nnnnn { c }
+\cs_new_protected:Npn \box_gset_trim:Nnnnn #1#2#3#4#5
+ { \@@_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN }
+\cs_generate_variant:Nn \box_gset_trim:Nnnnn { c }
+\cs_new_protected:Npn \@@_set_trim:NnnnnN #1#2#3#4#5#6
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \tex_kern:D - \@@_dim_eval:n {#2}
+ \box_use:N #1
+ \tex_kern:D - \@@_dim_eval:n {#4}
+ }
+% \end{macrocode}
+% For the height and depth, there is a need to watch the baseline is
+% respected. Material always has to stay on the correct side, so trimming
+% has to check that there is enough material to trim. First, the bottom
+% edge. If there is enough depth, simply set the depth, or if not move
+% down so the result is zero depth. \cs{box_move_down:nn} is used in both
+% cases so the resulting box always contains a \tn{lower} primitive.
+% The internal box is used here as it allows safe use of \cs{box_set_dp:Nn}.
+% \begin{macrocode}
+ \dim_compare:nNnTF { \box_dp:N #1 } > {#3}
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_down:nn \c_zero_dim
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_dp:Nn \l_@@_internal_box { \box_dp:N #1 - (#3) }
+ }
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_down:nn { (#3) - \box_dp:N #1 }
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_dp:Nn \l_@@_internal_box \c_zero_dim
+ }
+% \end{macrocode}
+% Same thing, this time from the top of the box.
+% \begin{macrocode}
+ \dim_compare:nNnTF { \box_ht:N \l_@@_internal_box } > {#5}
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_up:nn \c_zero_dim
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_ht:Nn \l_@@_internal_box
+ { \box_ht:N \l_@@_internal_box - (#5) }
+ }
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_up:nn { (#5) - \box_ht:N \l_@@_internal_box }
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_ht:Nn \l_@@_internal_box \c_zero_dim
+ }
+ #6 #1 \l_@@_internal_box
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \box_set_viewport:Nnnnn, \box_set_viewport:cnnnn,
+% \box_gset_viewport:Nnnnn, \box_gset_viewport:cnnnn
+% }
+% \begin{macro}{\@@_viewport:NnnnnN}
+% The same general logic as for the trim operation, but with absolute
+% dimensions. As a result, there are some things to watch out for in the
+% vertical direction.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_set_viewport:Nnnnn #1#2#3#4#5
+ { \@@_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN }
+\cs_generate_variant:Nn \box_set_viewport:Nnnnn { c }
+\cs_new_protected:Npn \box_gset_viewport:Nnnnn #1#2#3#4#5
+ { \@@_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN }
+\cs_generate_variant:Nn \box_gset_viewport:Nnnnn { c }
+\cs_new_protected:Npn \@@_set_viewport:NnnnnN #1#2#3#4#5#6
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \tex_kern:D - \@@_dim_eval:n {#2}
+ \box_use:N #1
+ \tex_kern:D \@@_dim_eval:n { #4 - \box_wd:N #1 }
+ }
+ \dim_compare:nNnTF {#3} < \c_zero_dim
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_down:nn \c_zero_dim
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_dp:Nn \l_@@_internal_box { - \@@_dim_eval:n {#3} }
+ }
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ { \box_move_down:nn {#3} { \box_use_drop:N \l_@@_internal_box } }
+ \box_set_dp:Nn \l_@@_internal_box \c_zero_dim
+ }
+ \dim_compare:nNnTF {#5} > \c_zero_dim
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_up:nn \c_zero_dim
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_ht:Nn \l_@@_internal_box
+ {
+ (#5)
+ \dim_compare:nNnT {#3} > \c_zero_dim
+ { - (#3) }
+ }
+ }
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \box_move_up:nn { - \@@_dim_eval:n {#5} }
+ { \box_use_drop:N \l_@@_internal_box }
+ }
+ \box_set_ht:Nn \l_@@_internal_box \c_zero_dim
+ }
+ #6 #1 \l_@@_internal_box
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3fp-convert}}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \begin{macro}{\fp_sign:n}
+% Trivial but useful. See the implementation of \cs{fp_add:Nn} for an
+% explanation of why to use \cs{@@_parse:n}, namely, for better error
+% reporting.
+% \begin{macrocode}
+\cs_new:Npn \fp_sign:n #1
+ { \fp_to_decimal:n { sign \@@_parse:n {#1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3flag}}
+%
+% \begin{macrocode}
+%<@@=flag>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\flag_raise_if_clear:n}
+% It might be faster to just call the \enquote{trap} function in all
+% cases but conceptually the function name suggests we should only run
+% it if the flag is zero in case the \enquote{trap} made customizable
+% in the future.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \@@_chk_exist:n {#1} } { }
+\cs_new:Npn \flag_raise_if_clear:n #1
+ {
+ \if_cs_exist:w flag~#1~0 \cs_end:
+ \else:
+ \cs:w flag~#1 \cs_end: 0 ;
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3int}}
+%
+% \begin{macrocode}
+%<@@=int>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\int_sign:n, \@@_sign:Nw}
+% See \cs{int_abs:n}. Evaluate the expression once (and when
+% debugging is enabled, check that the expression is well-formed),
+% then test the first character to determine the sign. This is
+% wrapped in \cs{int_value:w} \ldots{} \cs{exp_stop_f:} to ensure a
+% fixed number of expansions and to avoid dealing with closing the
+% conditionals.
+% \begin{macrocode}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \int_sign:n } }
+\cs_new:Npn \int_sign:n #1
+ {
+ \int_value:w \exp_after:wN \@@_sign:Nw
+ \int_value:w \@@_eval:w #1 \@@_eval_end: ;
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_sign:Nw #1#2 ;
+ {
+ \if_meaning:w 0 #1
+ 0
+ \else:
+ \if_meaning:w - #1 - \fi: 1
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3msg}}
+%
+% \begin{macrocode}
+%<@@=msg>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]
+% {
+% \msg_expandable_error:nnnnnn ,
+% \msg_expandable_error:nnnnn ,
+% \msg_expandable_error:nnnn ,
+% \msg_expandable_error:nnn ,
+% \msg_expandable_error:nn ,
+% \msg_expandable_error:nnffff ,
+% \msg_expandable_error:nnfff ,
+% \msg_expandable_error:nnff ,
+% \msg_expandable_error:nnf
+% }
+% \begin{macro}{\@@_expandable_error_module:nn}
+% Pass to an auxiliary the message to display and the module name
+% \begin{macrocode}
+\cs_new:Npn \msg_expandable_error:nnnnnn #1#2#3#4#5#6
+ {
+ \exp_args:Ne \@@_expandable_error_module:nn
+ {
+ \exp_args:Nc \exp_args:Noooo
+ { \c_@@_text_prefix_tl #1 / #2 }
+ { \tl_to_str:n {#3} }
+ { \tl_to_str:n {#4} }
+ { \tl_to_str:n {#5} }
+ { \tl_to_str:n {#6} }
+ }
+ {#1}
+ }
+\cs_new:Npn \msg_expandable_error:nnnnn #1#2#3#4#5
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} {#4} {#5} { } }
+\cs_new:Npn \msg_expandable_error:nnnn #1#2#3#4
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} {#4} { } { } }
+\cs_new:Npn \msg_expandable_error:nnn #1#2#3
+ { \msg_expandable_error:nnnnnn {#1} {#2} {#3} { } { } { } }
+\cs_new:Npn \msg_expandable_error:nn #1#2
+ { \msg_expandable_error:nnnnnn {#1} {#2} { } { } { } { } }
+\cs_generate_variant:Nn \msg_expandable_error:nnnnnn { nnffff }
+\cs_generate_variant:Nn \msg_expandable_error:nnnnn { nnfff }
+\cs_generate_variant:Nn \msg_expandable_error:nnnn { nnff }
+\cs_generate_variant:Nn \msg_expandable_error:nnn { nnf }
+\cs_new:Npn \@@_expandable_error_module:nn #1#2
+ {
+ \exp_after:wN \exp_after:wN
+ \exp_after:wN \use_none_delimit_by_q_stop:w
+ \use:n { \::error ! ~ #2 : ~ #1 } \q_stop
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\msg_show_eval:Nn, \msg_log_eval:Nn, \@@_show_eval:nnN}
+% A short-hand used for \cs{int_show:n} and similar functions that
+% passes to \cs{tl_show:n} the result of applying |#1| (a
+% function such as \cs{int_eval:n}) to the expression |#2|. The use of
+% \texttt{f}-expansion ensures that |#1| is expanded in the scope in which the
+% show command is called, rather than in the group created by
+% \cs{iow_wrap:nnnN}. This is only important for expressions
+% involving the \tn{currentgrouplevel} or \tn{currentgrouptype}.
+% On the other hand we want the expression to be converted to a string
+% with the usual escape character, hence within the wrapping code.
+% \begin{macrocode}
+\cs_new_protected:Npn \msg_show_eval:Nn #1#2
+ { \exp_args:Nf \@@_show_eval:nnN { #1 {#2} } {#2} \tl_show:n }
+\cs_new_protected:Npn \msg_log_eval:Nn #1#2
+ { \exp_args:Nf \@@_show_eval:nnN { #1 {#2} } {#2} \tl_log:n }
+\cs_new_protected:Npn \@@_show_eval:nnN #1#2#3 { #3 { #2 = #1 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\msg_show_item:n}
+% \begin{macro}[EXP]{\msg_show_item_unbraced:n}
+% \begin{macro}[EXP]{\msg_show_item:nn}
+% \begin{macro}[EXP]{\msg_show_item_unbraced:nn}
+% Each item in the variable is formatted using one of the following
+% functions. We cannot use |\\| and so on because these short-hands
+% cannot be used inside the arguments of messages, only when defining
+% the messages.
+% \begin{macrocode}
+\cs_new:Npx \msg_show_item:n #1
+ { \iow_newline: > ~ \c_space_tl \exp_not:N \tl_to_str:n { {#1} } }
+\cs_new:Npx \msg_show_item_unbraced:n #1
+ { \iow_newline: > ~ \c_space_tl \exp_not:N \tl_to_str:n {#1} }
+\cs_new:Npx \msg_show_item:nn #1#2
+ {
+ \iow_newline: > \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n { {#1} }
+ \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n { {#2} }
+ }
+\cs_new:Npx \msg_show_item_unbraced:nn #1#2
+ {
+ \iow_newline: > \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n {#1}
+ \use:nn { ~ } { ~ } => \use:nn { ~ } { ~ }
+ \exp_not:N \tl_to_str:n {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3prg}}
+%
+% \begin{macro}[added = 2017-11-28]{\bool_const:Nn, \bool_const:cn}
+% A merger between \cs{tl_const:Nn} and \cs{bool_set:Nn}.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_scope:NN c #1 } { }
+\cs_new_protected:Npn \bool_const:Nn #1#2
+ {
+ \__kernel_chk_if_free_cs:N #1
+ \tex_global:D \tex_chardef:D #1 = \bool_if_p:n {#2}
+ }
+\cs_generate_variant:Nn \bool_const:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[added = 2018-05-10]
+% {\bool_set_inverse:N, \bool_set_inverse:c, \bool_gset_inverse:N, \bool_gset_inverse:c}
+% Set to \texttt{false} or \texttt{true} locally or globally.
+% \begin{macrocode}
+\cs_new_protected:Npn \bool_set_inverse:N #1
+ { \bool_if:NTF #1 { \bool_set_false:N } { \bool_set_true:N } #1 }
+\cs_generate_variant:Nn \bool_set_inverse:N { c }
+\cs_new_protected:Npn \bool_gset_inverse:N #1
+ { \bool_if:NTF #1 { \bool_gset_false:N } { \bool_gset_true:N } #1 }
+\cs_generate_variant:Nn \bool_gset_inverse:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3prop}}
+%
+% \begin{macrocode}
+%<@@=prop>
+% \end{macrocode}
+%
+% \begin{macro}[rEXP]{\prop_map_tokens:Nn, \prop_map_tokens:cn}
+% \begin{macro}{\@@_map_tokens:nwwn}
+% The mapping is very similar to \cs{prop_map_function:NN}. The
+% \cs{use_i:nn} removes the leading \cs{s_@@}. The odd construction
+% |\use:n {#1}| allows |#1| to contain any token without interfering
+% with \cs{prop_map_break:}. The loop stops when the argument
+% delimited by \cs{@@_pair:wn} is \cs{prg_break:} instead of being
+% empty.
+% \begin{macrocode}
+\cs_new:Npn \prop_map_tokens:Nn #1#2
+ {
+ \exp_last_unbraced:Nno
+ \use_i:nn { \@@_map_tokens:nwwn {#2} } #1
+ \prg_break: \@@_pair:wn \s_@@ { } \prg_break_point:
+ \prg_break_point:Nn \prop_map_break: { }
+ }
+\cs_new:Npn \@@_map_tokens:nwwn #1#2 \@@_pair:wn #3 \s_@@ #4
+ {
+ #2
+ \use:n {#1} {#3} {#4}
+ \@@_map_tokens:nwwn {#1}
+ }
+\cs_generate_variant:Nn \prop_map_tokens:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {\prop_rand_key_value:N, \prop_rand_key_value:c}
+% \begin{macro}[EXP]{\@@_rand_item:w}
+% Contrarily to |clist|, |seq| and |tl|, there is no function to get
+% an item of a |prop| given an integer between $1$ and the number of
+% items, so we write the appropriate code. There is no bounds
+% checking because \cs{int_rand:nn} is always within bounds. The
+% initial \cs{int_value:w} is stopped by the first \cs{s_@@} in~|#1|.
+% \begin{macrocode}
+\cs_new:Npn \prop_rand_key_value:N #1
+ {
+ \prop_if_empty:NF #1
+ {
+ \exp_after:wN \@@_rand_item:w
+ \int_value:w \int_rand:nn { 1 } { \prop_count:N #1 }
+ #1 \q_stop
+ }
+ }
+\cs_generate_variant:Nn \prop_rand_key_value:N { c }
+\cs_new:Npn \@@_rand_item:w #1 \s_@@ \@@_pair:wn #2 \s_@@ #3
+ {
+ \int_compare:nNnF {#1} > 1
+ { \use_i_delimit_by_q_stop:nw { \exp_not:n { {#2} {#3} } } }
+ \exp_after:wN \@@_rand_item:w
+ \int_value:w \int_eval:n { #1 - 1 } \s_@@
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3seq}}
+%
+% \begin{macrocode}
+%<@@=seq>
+% \end{macrocode}
+%
+% \begin{macro}
+% {
+% \seq_mapthread_function:NNN, \seq_mapthread_function:NcN,
+% \seq_mapthread_function:cNN, \seq_mapthread_function:ccN
+% }
+% \begin{macro}
+% {
+% \@@_mapthread_function:wNN, \@@_mapthread_function:wNw,
+% \@@_mapthread_function:Nnnwnn
+% }
+% The idea is to first expand both sequences, adding the
+% usual |{ ? \prg_break: } { }| to the end of each one. This is
+% most conveniently done in two steps using an auxiliary function.
+% The mapping then throws away the first tokens of |#2| and |#5|,
+% which for items in both sequences are \cs{s_@@}
+% \cs{@@_item:n}. The function to be mapped are then be applied to
+% the two entries. When the code hits the end of one of the
+% sequences, the break material stops the entire loop and tidy up.
+% This avoids needing to find the count of the two sequences, or
+% worrying about which is longer.
+% \begin{macrocode}
+\cs_new:Npn \seq_mapthread_function:NNN #1#2#3
+ { \exp_after:wN \@@_mapthread_function:wNN #2 \q_stop #1 #3 }
+\cs_new:Npn \@@_mapthread_function:wNN \s_@@ #1 \q_stop #2#3
+ {
+ \exp_after:wN \@@_mapthread_function:wNw #2 \q_stop #3
+ #1 { ? \prg_break: } { }
+ \prg_break_point:
+ }
+\cs_new:Npn \@@_mapthread_function:wNw \s_@@ #1 \q_stop #2
+ {
+ \@@_mapthread_function:Nnnwnn #2
+ #1 { ? \prg_break: } { }
+ \q_stop
+ }
+\cs_new:Npn \@@_mapthread_function:Nnnwnn #1#2#3#4 \q_stop #5#6
+ {
+ \use_none:n #2
+ \use_none:n #5
+ #1 {#3} {#6}
+ \@@_mapthread_function:Nnnwnn #1 #4 \q_stop
+ }
+\cs_generate_variant:Nn \seq_mapthread_function:NNN { Nc , c , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_filter:NNn, \seq_gset_filter:NNn}
+% \begin{macro}{\@@_set_filter:NNNn}
+% Similar to \cs{seq_map_inline:Nn}, without a
+% \cs{prg_break_point:} because the user's code
+% is performed within the evaluation of a boolean expression,
+% and skipping out of that would break horribly.
+% The \cs{@@_wrap_item:n} function inserts the relevant
+% \cs{@@_item:n} without expansion in the input stream,
+% hence in the \texttt{x}-expanding assignment.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_filter:NNn
+ { \@@_set_filter:NNNn \tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_filter:NNn
+ { \@@_set_filter:NNNn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_filter:NNNn #1#2#3#4
+ {
+ \@@_push_item_def:n { \bool_if:nT {#4} { \@@_wrap_item:n {##1} } }
+ #1 #2 { #3 }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_map:NNn, \seq_gset_map:NNn}
+% \begin{macro}{\@@_set_map:NNNn}
+% Very similar to \cs{seq_set_filter:NNn}. We could actually
+% merge the two within a single function, but it would have weird
+% semantics.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_map:NNn
+ { \@@_set_map:NNNn \tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_map:NNn
+ { \@@_set_map:NNNn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_map:NNNn #1#2#3#4
+ {
+ \@@_push_item_def:n { \exp_not:N \@@_item:n {#4} }
+ #1 #2 { #3 }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_from_inline_x:Nnn, \seq_gset_from_inline_x:Nnn}
+% \begin{macro}{\@@_set_from_inline_x:NNnn}
+% Set \cs{@@_item:n} then map it using the loop code.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_from_inline_x:Nnn
+ { \@@_set_from_inline_x:NNnn \tl_set:Nx }
+\cs_new_protected:Npn \seq_gset_from_inline_x:Nnn
+ { \@@_set_from_inline_x:NNnn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_from_inline_x:NNnn #1#2#3#4
+ {
+ \@@_push_item_def:n { \exp_not:N \@@_item:n {#4} }
+ #1 #2 { \s_@@ #3 \@@_item:n }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_from_function:NnN, \seq_gset_from_function:NnN}
+% Reuse \cs{seq_set_from_inline_x:Nnn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_from_function:NnN #1#2#3
+ { \seq_set_from_inline_x:Nnn #1 {#2} { #3 {##1} } }
+\cs_new_protected:Npn \seq_gset_from_function:NnN #1#2#3
+ { \seq_gset_from_inline_x:Nnn #1 {#2} { #3 {##1} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\seq_const_from_clist:Nn, \seq_const_from_clist:cn}
+% Almost identical to \cs{seq_set_from_clist:Nn}.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_const_from_clist:Nn #1#2
+ {
+ \tl_const:Nx #1
+ { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n }
+ }
+\cs_generate_variant:Nn \seq_const_from_clist:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\seq_shuffle:N, \seq_gshuffle:N, \@@_shuffle:NN, \@@_shuffle_item:n}
+% \begin{variable}{\g_@@_internal_seq, \l_@@_internal_a_int, \l_@@_internal_b_int}
+% We apply the Fisher–Yates shuffle, storing items in \tn{toks}
+% registers. We use the primitive \cs{tex_uniformdeviate:D} for
+% speed reasons. Its non-uniformity is of order its argument divided
+% by $2^{28}$, not too bad for small lists. For sequences with more
+% than $13$ elements there are more possible permutations than
+% possible seeds ($13!>2^{28}$) so the question of uniformity is
+% somewhat moot.
+% \begin{macrocode}
+\cs_if_exist:NTF \tex_uniformdeviate:D
+ {
+ \int_new:N \l_@@_internal_a_int
+ \int_new:N \l_@@_internal_b_int
+ \seq_new:N \g_@@_internal_seq
+ \cs_new_protected:Npn \seq_shuffle:N { \@@_shuffle:NN \seq_set_eq:NN }
+ \cs_new_protected:Npn \seq_gshuffle:N { \@@_shuffle:NN \seq_gset_eq:NN }
+ \cs_new_protected:Npn \@@_shuffle:NN #1#2
+ {
+ \int_compare:nNnTF { \seq_count:N #2 } > \c_max_register_int
+ {
+ \__kernel_msg_error:nnx { kernel } { shuffle-too-large }
+ { \token_to_str:N #2 }
+ }
+ {
+ \group_begin:
+ \cs_set_eq:NN \@@_item:n \@@_shuffle_item:n
+ \int_zero:N \l_@@_internal_a_int
+ #2
+ \seq_gset_from_inline_x:Nnn \g_@@_internal_seq
+ { \int_step_function:nN { \l_@@_internal_a_int } }
+ { \tex_the:D \tex_toks:D ##1 }
+ \group_end:
+ #1 #2 \g_@@_internal_seq
+ \seq_gclear:N \g_@@_internal_seq
+ }
+ }
+ \cs_new_protected:Npn \@@_shuffle_item:n
+ {
+ \int_incr:N \l_@@_internal_a_int
+ \int_set:Nn \l_@@_internal_b_int
+ { 1 + \tex_uniformdeviate:D \l_@@_internal_a_int }
+ \tex_toks:D \l_@@_internal_a_int
+ = \tex_toks:D \l_@@_internal_b_int
+ \tex_toks:D \l_@@_internal_b_int
+ }
+ \__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.
+ }
+ }
+ {
+ \cs_new_protected:Npn \seq_shuffle:N #1
+ {
+ \__kernel_msg_error:nnn { kernel } { fp-no-random }
+ { \seq_shuffle:N #1 }
+ }
+ \cs_new_eq:NN \seq_gshuffle:N \seq_shuffle:N
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_indexed_map_function:NN, \seq_indexed_map_inline:Nn,
+% \@@_indexed_map:nNN, \@@_indexed_map:Nw
+% }
+% Similar to \cs{seq_map_function:NN} but we keep track of the item
+% index as a |;|-delimited argument of \cs{@@_indexed_map:Nw}.
+% \begin{macrocode}
+\cs_new:Npn \seq_indexed_map_function:NN #1#2
+ {
+ \@@_indexed_map:NN #1#2
+ \prg_break_point:Nn \seq_map_break: { }
+ }
+\cs_new_protected:Npn \seq_indexed_map_inline:Nn #1#2
+ {
+ \int_gincr:N \g__kernel_prg_map_int
+ \cs_gset_protected:cpn
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1##2 {#2}
+ \exp_args:NNc \@@_indexed_map:NN #1
+ { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
+ \prg_break_point:Nn \seq_map_break:
+ { \int_gdecr:N \g__kernel_prg_map_int }
+ }
+\cs_new:Npn \@@_indexed_map:NN #1#2
+ {
+ \exp_after:wN \@@_indexed_map:Nw
+ \exp_after:wN #2
+ \int_value:w 1
+ \exp_after:wN \use_i:nn
+ \exp_after:wN ;
+ #1
+ \prg_break: \@@_item:n { } \prg_break_point:
+ }
+\cs_new:Npn \@@_indexed_map:Nw #1#2 ; #3 \@@_item:n #4
+ {
+ #3
+ #1 {#2} {#4}
+ \exp_after:wN \@@_indexed_map:Nw
+ \exp_after:wN #1
+ \int_value:w \int_eval:w 1 + #2 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3skip}}
+%
+% \begin{macrocode}
+%<@@=dim>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\dim_sign:n, \@@_sign:Nw}
+% See \cs{dim_abs:n}. Contrarily to \cs{int_sign:n} the case of a
+% zero dimension cannot be distinguished from a positive dimension by
+% looking only at the first character, since |0.2pt| and |0pt| start
+% the same way. We need explicit comparisons. We start by
+% distinguishing the most common case of a positive dimension.
+% \begin{macrocode}
+\__kernel_patch_args:nNNpn
+ { { \__kernel_chk_expr:nNnN {#1} \@@_eval:w { } \dim_sign:n } }
+\cs_new:Npn \dim_sign:n #1
+ {
+ \int_value:w \exp_after:wN \@@_sign:Nw
+ \dim_use:N \@@_eval:w #1 \@@_eval_end: ;
+ \exp_stop_f:
+ }
+\cs_new:Npn \@@_sign:Nw #1#2 ;
+ {
+ \if_dim:w #1#2 > \c_zero_dim
+ 1
+ \else:
+ \if_meaning:w - #1
+ -1
+ \else:
+ 0
+ \fi:
+ \fi:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3sys}}
+%
+% \begin{macrocode}
+%<@@=sys>
+% \end{macrocode}
+%
+% \begin{variable}{\c_sys_engine_version_str}
+% Various different engines, various different ways to extract the
+% data!
+% \begin{macrocode}
+\str_const:Nx \c_sys_engine_version_str
+ {
+ \str_case:on \c_sys_engine_str
+ {
+ { pdftex }
+ {
+ \fp_eval:n { round(\int_use:N \tex_pdftexversion:D / 100 , 2) }
+ .
+ \tex_pdftexrevision:D
+ }
+ { ptex }
+ {
+ \cs_if_exist:NT \tex_ptexversion:D
+ {
+ p
+ \int_use:N \tex_ptexversion:D
+ \int_use:N \tex_ptexminorversion:D
+ \tex_ptexrevision:D
+ -
+ \int_use:N \tex_epTeXversion:D
+ }
+ }
+ { luatex }
+ {
+ \fp_eval:n { round(\int_use:N \tex_luatexversion:D / 100, 2) }
+ .
+ \tex_luatexrevision:D
+ }
+ { uptex }
+ {
+ \cs_if_exist:NT \tex_ptexversion:D
+ {
+ p
+ \int_use:N \tex_ptexversion:D
+ \int_use:N \tex_ptexminorversion:D
+ \tex_ptexrevision:D
+ -
+ u
+ \int_use:N \tex_uptexversion:D
+ \tex_uptexrevision:D
+ -
+ \int_use:N \tex_epTeXversion:D
+ }
+ }
+ { xetex }
+ {
+ \int_use:N \tex_XeTeXversion:D
+ \tex_XeTeXrevision:D
+ }
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\sys_rand_seed:}
+% Unpack the primitive. When random numbers are not available, we
+% return zero after an error (and incidentally make sure the number of
+% expansions needed is the same as with random numbers available).
+% \begin{macrocode}
+\sys_if_rand_exist:TF
+ { \cs_new:Npn \sys_rand_seed: { \tex_the:D \tex_randomseed:D } }
+ {
+ \cs_new:Npn \sys_rand_seed:
+ {
+ \int_value:w
+ \__kernel_msg_expandable_error:nnn { kernel } { fp-no-random }
+ { \sys_rand_seed: }
+ \c_zero_int
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\sys_gset_rand_seed:n}
+% The primitive always assigns the seed globally.
+% \begin{macrocode}
+\sys_if_rand_exist:TF
+ {
+ \cs_new_protected:Npn \sys_gset_rand_seed:n #1
+ { \tex_setrandomseed:D \int_eval:n {#1} \exp_stop_f: }
+ }
+ {
+ \cs_new_protected:Npn \sys_gset_rand_seed:n #1
+ {
+ \__kernel_msg_error:nnn { kernel } { fp-no-random }
+ { \sys_gset_rand_seed:n {#1} }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\c_sys_shell_escape_int}
+% Expose the engine's shell escape status to the user.
+% \begin{macrocode}
+\int_const:Nn \c_sys_shell_escape_int
+ {
+ \sys_if_engine_luatex:TF
+ {
+ \tex_directlua:D
+ { tex.sprint(status.shell_escape~or~os.execute()) }
+ }
+ {
+ \tex_shellescape:D
+ }
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_internal_tl}
+% \begin{macrocode}
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\c_@@_marker_tl}
+% The same idea as the marker for rescanning token lists.
+% \begin{macrocode}
+\tl_const:Nx \c_@@_marker_tl { : \token_to_str:N : }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\sys_shell_get:nnN}
+% \begin{macro}{\sys_shell_get:nnN,\@@_get:nnN,\@@_get_do:Nw}
+% Setting using a shell is at this level just a slightly specialised file
+% operation, with an additional check for quotes, as these are not supported.
+% \begin{macrocode}
+\cs_new_protected:Npn \sys_shell_get:nnN #1#2#3
+ {
+ \sys_shell_get:nnNF {#1} {#2} #3
+ { \tl_set:Nn #3 { \q_no_value } }
+ }
+\prg_new_protected_conditional:Npnn \sys_shell_get:nnN #1#2#3 { T , F , TF }
+ {
+ \sys_if_shell:TF
+ { \exp_args:No \@@_get:nnN { \tl_to_str:n {#1} } {#2} #3 }
+ { \prg_return_false: }
+ }
+\cs_new_protected:Npn \@@_get:nnN #1#2#3
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \__kernel_msg_error:nnx
+ { kernel } { quote-in-shell } {#1}
+ \prg_return_false:
+ }
+ {
+ \group_begin:
+ \if_false: { \fi:
+ \int_set_eq:NN \tex_tracingnesting:D \c_zero_int
+ \exp_args:No \tex_everyeof:D { \c_@@_marker_tl }
+ #2 \scan_stop:
+ \exp_after:wN \@@_get_do:Nw
+ \exp_after:wN #3
+ \exp_after:wN \prg_do_nothing:
+ \tex_input:D | "#1" \scan_stop:
+ \if_false: } \fi:
+ \prg_return_true:
+ }
+ }
+\exp_args:Nno \use:nn
+ { \cs_new_protected:Npn \@@_get_do:Nw #1#2 }
+ { \c_@@_marker_tl }
+ {
+ \group_end:
+ \tl_set:No #1 {#2}
+ }
+\__kernel_msg_new:nnnn { kernel } { quote-in-shell }
+ { Quotes~in~shell~command~'#1'. }
+ { Shell~commands~cannot~contain~quotes~("). }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_sys_platform_str}
+% Detecting the platform on \LuaTeX{} is easy: for other engines, we use
+% the fact that the two common cases have special null files. It is possible
+% to probe further (see package \pkg{platform}), but that requires shell
+% escape and seems unlikely to be useful.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \str_const:Nx \c_sys_platform_str
+ { \lua_now:n { tex.print(os.type) } }
+ }
+ {
+ \file_if_exist:nTF { nul: }
+ {
+ \file_if_exist:nF { /dev/null }
+ { \str_const:Nn \c_sys_platform_str { windows } }
+ }
+ {
+ \file_if_exist:nT { /dev/null }
+ { \str_const:Nn \c_sys_platform_str { unix } }
+ }
+ }
+\cs_if_exist:NF \c_sys_platform_str
+ { \str_const:Nn \c_sys_platform_str { unknown } }
+% \end{macrocode}
+% \end{variable}
+% \begin{macro}[pTF]{\sys_if_platform_unix:}
+% \begin{macro}[pTF]{\sys_if_platform_windows:}
+% We can now set up the tests.
+% \begin{macrocode}
+\clist_map_inline:nn { unix , windows }
+ {
+ \@@_const:nn { sys_if_platform_ #1 }
+ { \str_if_eq_p:Vn \c_sys_platform_str { #1 } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\sys_if_shell:, \sys_if_shell_unrestricted:, \sys_if_shell_restricted:}
+% Performs a check for whether shell escape is enabled. The first set
+% of functions returns true if either of restricted or unrestricted
+% shell escape is enabled, while the other two sets of functions
+% return true in only one of these two cases.
+% \begin{macrocode}
+\@@_const:nn { sys_if_shell }
+ { \int_compare_p:nNn \c_sys_shell_escape_int > 0 }
+\@@_const:nn { sys_if_shell_unrestricted }
+ { \int_compare_p:nNn \c_sys_shell_escape_int = 1 }
+\@@_const:nn { sys_if_shell_restricted }
+ { \int_compare_p:nNn \c_sys_shell_escape_int = 2 }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_shell_stream_int}
+% This is not needed for \LuaTeX{}: shell escape there isn't done using
+% a \TeX{} interface.
+% \begin{macrocode}
+\sys_if_engine_luatex:F
+ { \int_const:Nn \c_@@_shell_stream_int { 18 } }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\sys_shell_now:n}
+% Execute commands through shell escape immediately.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \sys_shell_now:n #1
+ {
+ \lua_now:e
+ { os.execute(" \lua_escape:e { \tl_to_str:n {#1} } ") }
+ }
+ }
+ {
+ \cs_new_protected:Npn \sys_shell_now:n #1
+ { \iow_now:Nn \c_@@_shell_stream_int {#1} }
+ }
+\cs_generate_variant:Nn \sys_shell_now:n { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\sys_shell_shipout:n}
+% Execute commands through shell escape at shipout.
+% \begin{macrocode}
+\sys_if_engine_luatex:TF
+ {
+ \cs_new_protected:Npn \sys_shell_shipout:n #1
+ {
+ \lua_shipout_e:n
+ { os.execute(" \lua_escape:e { \tl_to_str:n {#1} } ") }
+ }
+ }
+ {
+ \cs_new_protected:Npn \sys_shell_shipout:n #1
+ { \iow_shipout:Nn \c_@@_shell_stream_int {#1} }
+ }
+\cs_generate_variant:Nn \sys_shell_shipout:n { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3file}}
+%
+% \begin{macrocode}
+%<@@=file>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\@@_str_cmp:nn}
+% \begin{macro}[EXP]{\@@_str_escape:n}
+% As we are doing a fixed-length \enquote{big} integer comparison, it
+% is easiest to use the low-level behavior of string comparisons.
+% \begin{macrocode}
+\cs_new:Npn \@@_str_cmp:nn #1#2 { \tex_strcmp:D {#1} {#2} }
+\sys_if_engine_luatex:T
+ {
+ \cs_set:Npn \@@_str_cmp:nn #1#2
+ {
+ \lua_now:e
+ {
+ l3kernel.strcmp
+ (
+ " \@@_str_escape:n {#1}",
+ " \@@_str_escape:n {#2}"
+ )
+ }
+ }
+ \cs_new:Npn \@@_str_escape:n #1
+ {
+ \lua_escape:e
+ { \__kernel_tl_to_str:w \use:e { {#1} } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_first_tl, \l_@@_second_tl}
+% \begin{macrocode}
+\tl_new:N \l_@@_first_tl
+\tl_new:N \l_@@_second_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[TF]{\file_compare_timestamp:nNn}
+% \begin{macro}[EXP]{\@@_timestamp:n}
+% Comparison of file date can be done by using the low-level nature of the
+% string comparison functions. This could be expandable but that would
+% skip any file look-up: to stay consistent we retain that at the cost of
+% expansion.
+% \begin{macrocode}
+\prg_new_protected_conditional:Npnn \file_compare_timestamp:nNn #1#2#3
+ { T , F , TF }
+ {
+ \group_begin:
+ \file_get_full_name:nNTF {#1} \l_@@_first_tl
+ {
+ \file_get_full_name:nNTF {#3} \l_@@_second_tl
+ {
+ \if_int_compare:w
+ \@@_str_cmp:nn
+ { \@@_timestamp:n \exp_after:wN { \l_@@_first_tl } }
+ { \@@_timestamp:n \exp_after:wN { \l_@@_second_tl } }
+ #2 0 \exp_stop_f:
+ \group_end:
+ \prg_return_true:
+ \else:
+ \group_end:
+ \prg_return_false:
+ \fi:
+ }
+ {
+ \group_end:
+ \if_charcode:w #2 >
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+ {
+ \group_end:
+ \if_charcode:w #2 <
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+ }
+\sys_if_engine_luatex:TF
+ {
+ \cs_new:Npn \@@_timestamp:n #1
+ {
+ \lua_now:e
+ { l3kernel.filemoddate ( " \lua_escape:e {#1} " ) }
+ }
+ }
+ { \cs_new_eq:NN \@@_timestamp:n \tex_filemoddate:D }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=ior>
+% \end{macrocode}
+%
+% \begin{macro}{\ior_shell_open:Nn}
+% \begin{macro}{\@@_shell_open:nN}
+% Actually much easier than either the standard open or input versions!
+% \begin{macrocode}
+\cs_new_protected:Npn \ior_shell_open:Nn #1#2
+ {
+ \sys_if_shell:TF
+ { \exp_args:No \@@_shell_open:nN { \tl_to_str:n {#2} } #1 }
+ { \__kernel_msg_error:nn { kernel } { pipe-failed } }
+ }
+\cs_new_protected:Npn \@@_shell_open:nN #1#2
+ {
+ \tl_if_in:nnTF {#1} { " }
+ {
+ \__kernel_msg_error:nnx
+ { kernel } { quote-in-shell } {#1}
+ }
+ { \__kernel_ior_open:Nn #2 { "|#1" } }
+ }
+\__kernel_msg_new:nnnn { kernel } { pipe-failed }
+ { Cannot~run~piped~system~commands. }
+ {
+ LaTeX~tried~to~call~a~system~process~but~this~was~not~possible.\\
+ Try~the~"--shell-escape"~(or~"--enable-pipes")~option.
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3tl}}
+%
+% \subsubsection{Unicode case changing}
+%
+% The mechanisms needed for case changing are somewhat involved, particularly
+% to allow for all of the special cases. These functions also require the
+% appropriate data extracted from the Unicode documentation (either manually
+% or automatically).
+%
+% First, some code which \enquote{belongs} in \pkg{l3tokens} but has to come
+% here.
+% \begin{macrocode}
+%<@@=char>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]
+% {
+% \char_lower_case:N, \char_upper_case:N,
+% \char_mixed_case:N, \char_fold_case:N
+% }
+% \begin{macro}[EXP]{\@@_change_case:nNN}
+% \begin{macro}[EXP]{\@@_change_case:nN}
+% \begin{macro}[EXP]{\@@_change_case_multi:n, \@@_change_case_multi:v}
+% \begin{macro}[EXP]{\@@_change_case_multi:NNNw}
+% \begin{macro}[EXP]{\@@_change_case:NN}
+% \begin{macro}[EXP]{\@@_change_case:NNN}
+% \begin{macro}[EXP]{\@@_change_case:N}
+% \begin{macro}[EXP]
+% {
+% \char_str_lower_case:N, \char_str_upper_case:N,
+% \char_str_mixed_case:N, \char_str_fold_case:N
+% }
+% \begin{macro}[EXP]{\@@_str_change_case:nNN}
+% \begin{macro}[EXP]{\@@_str_change_case:nN}
+% Expandable character generation is done using a two-part approach.
+% First, see if the current character has a special mapping for the current
+% transformation. If it does, insert that. Otherwise, use the \TeX{} data
+% to look up the one-to-one mapping, and generate the appropriate character
+% with the appropriate category code. Mixed case needs an extra step as it
+% may be special-cased or might be a special upper case outcome. The internal
+% when using non-Unicode engines has to be set up to only do anything
+% with ASCII characters.
+%
+% To ensure that the category codes produced are predictable, every character
+% is re-generated even if it is otherwise unchanged. This makes life a little
+% interesting when we might have multiple output characters: we have to
+% grab each of them and case change them in reverse order to maintain
+% \texttt{f}-type expandability.
+% \begin{macrocode}
+\cs_new:Npn \char_lower_case:N #1
+ { \@@_change_case:nNN { lower } \char_value_lccode:n #1 }
+\cs_new:Npn \char_upper_case:N #1
+ { \@@_change_case:nNN { upper } \char_value_uccode:n #1 }
+\cs_new:Npn \char_mixed_case:N #1
+ {
+ \tl_if_exist:cTF { c_@@_mixed_case_ \token_to_str:N #1 _tl }
+ {
+ \@@_change_case_multi:v
+ { c_@@_mixed_case_ \token_to_str:N #1 _tl }
+ }
+ { \char_upper_case:N #1 }
+ }
+\cs_new:Npn \char_fold_case:N #1
+ { \@@_change_case:nNN { fold } \char_value_lccode:n #1 }
+\cs_new:Npn \@@_change_case:nNN #1#2#3
+ {
+ \tl_if_exist:cTF { c_@@_ #1 _case_ \token_to_str:N #3 _tl }
+ {
+ \@@_change_case_multi:v
+ { c_@@_ #1 _case_ \token_to_str:N #3 _tl }
+ }
+ { \exp_args:Nf \@@_change_case:nN { #2 { `#3 } } #3 }
+ }
+\cs_new:Npn \@@_change_case:nN #1#2
+ {
+ \int_compare:nNnTF {#1} = 0
+ { \@@_change_case:N #2 }
+ { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
+ }
+\cs_new:Npn \@@_change_case_multi:n #1
+ { \@@_change_case_multi:NNNw #1 \q_no_value \q_no_value \q_stop }
+\cs_new:Npn \@@_change_case_multi:NNNw #1#2#3#4 \q_stop
+ {
+ \quark_if_no_value:NTF #3
+ {
+ \quark_if_no_value:NTF #2
+ { \@@_change_case:N #1 }
+ { \@@_change_case:NN #1#2 }
+ }
+ { \@@_change_case:NNN #1#2#3 }
+ }
+\cs_generate_variant:Nn \@@_change_case_multi:n { v }
+\cs_new:Npn \@@_change_case:NN #1#2
+ {
+ \exp_args:Nnf \use:nn
+ { \@@_change_case:N #1 }
+ { \@@_change_case:N #2 }
+ }
+\cs_new:Npn \@@_change_case:NNN #1#2#3
+ {
+ \exp_args:Nnff \use:nnn
+ { \@@_change_case:N #1 }
+ { \@@_change_case:N #2 }
+ { \@@_change_case:N #3 }
+ }
+\cs_new:Npn \@@_change_case:N #1
+ { \char_generate:nn { `#1 } { \char_value_catcode:n { `#1 } } }
+\bool_lazy_or:nnF { \sys_if_engine_luatex_p: } { \sys_if_engine_xetex_p: }
+ {
+ \cs_set_eq:NN \@@_change_case:nN \use_ii:nn
+ }
+% \end{macrocode}
+% Same story for the string version, except category code is easier
+% to follow. This of course makes this version significantly faster.
+% \begin{macrocode}
+\cs_new:Npn \char_str_lower_case:N #1
+ { \@@_str_change_case:nNN { lower } \char_value_lccode:n #1 }
+\cs_new:Npn \char_str_upper_case:N #1
+ { \@@_str_change_case:nNN { upper } \char_value_uccode:n #1 }
+\cs_new:Npn \char_str_mixed_case:N #1
+ {
+ \tl_if_exist:cTF { c_@@_mixed_case_ \token_to_str:N #1 _tl }
+ { \tl_to_str:c { c_@@_mixed_case_ \token_to_str:N #1 _tl } }
+ { \char_str_upper_case:N #1 }
+ }
+\cs_new:Npn \char_str_fold_case:N #1
+ { \@@_str_change_case:nNN { fold } \char_value_lccode:n #1 }
+\cs_new:Npn \@@_str_change_case:nNN #1#2#3
+ {
+ \tl_if_exist:cTF { c_@@_ #1 _case_ \token_to_str:N #3 _tl }
+ { \tl_to_str:c { c_@@_ #1 _case_ \token_to_str:N #3 _tl } }
+ { \exp_args:Nf \@@_str_change_case:nN { #2 { `#3 } } #3 }
+ }
+\cs_new:Npn \@@_str_change_case:nN #1#2
+ {
+ \int_compare:nNnTF {#1} = 0
+ { \tl_to_str:n {#2} }
+ { \char_generate:nn {#1} { 12 } }
+ }
+\bool_lazy_or:nnF { \sys_if_engine_luatex_p: } { \sys_if_engine_xetex_p: }
+ {
+ \cs_set:Npn \@@_str_change_case:nN #1#2
+ { \tl_to_str:n {#2} }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\char_codepoint_to_bytes:n}
+% \begin{macro}[EXP]{\@@_codepoint_to_bytes_auxi:n}
+% \begin{macro}[EXP]{\@@_codepoint_to_bytes_auxii:Nnn}
+% \begin{macro}[EXP]{\@@_codepoint_to_bytes_auxiii:n}
+% \begin{macro}[EXP]
+% {
+% \@@_codepoint_to_bytes_outputi:nw ,
+% \@@_codepoint_to_bytes_outputii:nw ,
+% \@@_codepoint_to_bytes_outputiii:nw ,
+% \@@_codepoint_to_bytes_outputiv:nw
+% }
+% \begin{macro}[EXP]
+% {\@@_codepoint_to_bytes_output:nnn, \@@_codepoint_to_bytes_output:fnn}
+% \begin{macro}[EXP]{\@@_codepoint_to_bytes_end:}
+% This code converts a codepoint into the correct UTF-8 representation.
+% In terms of the algorithm itself, see
+% \url{https://en.wikipedia.org/wiki/UTF-8} for the octet pattern.
+% \begin{macrocode}
+\cs_new:Npn \char_codepoint_to_bytes:n #1
+ {
+ \exp_args:Nf \@@_codepoint_to_bytes_auxi:n
+ { \int_eval:n {#1} }
+ }
+\cs_new:Npn \@@_codepoint_to_bytes_auxi:n #1
+ {
+ \if_int_compare:w #1 > "80 \exp_stop_f:
+ \if_int_compare:w #1 < "800 \exp_stop_f:
+ \@@_codepoint_to_bytes_outputi:nw
+ { \@@_codepoint_to_bytes_auxii:Nnn C {#1} { 64 } }
+ \@@_codepoint_to_bytes_outputii:nw
+ { \@@_codepoint_to_bytes_auxiii:n {#1} }
+ \else:
+ \if_int_compare:w #1 < "10000 \exp_stop_f:
+ \@@_codepoint_to_bytes_outputi:nw
+ { \@@_codepoint_to_bytes_auxii:Nnn E {#1} { 64 * 64 } }
+ \@@_codepoint_to_bytes_outputii:nw
+ {
+ \@@_codepoint_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 } }
+ }
+ \@@_codepoint_to_bytes_outputiii:nw
+ { \@@_codepoint_to_bytes_auxiii:n {#1} }
+ \else:
+ \@@_codepoint_to_bytes_outputi:nw
+ {
+ \@@_codepoint_to_bytes_auxii:Nnn F
+ {#1} { 64 * 64 * 64 }
+ }
+ \@@_codepoint_to_bytes_outputii:nw
+ {
+ \@@_codepoint_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 * 64 } }
+ }
+ \@@_codepoint_to_bytes_outputiii:nw
+ {
+ \@@_codepoint_to_bytes_auxiii:n
+ { \int_div_truncate:nn {#1} { 64 } }
+ }
+ \@@_codepoint_to_bytes_outputiv:nw
+ { \@@_codepoint_to_bytes_auxiii:n {#1} }
+ \fi:
+ \fi:
+ \else:
+ \@@_codepoint_to_bytes_outputi:nw {#1}
+ \fi:
+ \@@_codepoint_to_bytes_end: { } { } { } { }
+ }
+\cs_new:Npn \@@_codepoint_to_bytes_auxii:Nnn #1#2#3
+ { "#10 + \int_div_truncate:nn {#2} {#3} }
+\cs_new:Npn \@@_codepoint_to_bytes_auxiii:n #1
+ { \int_mod:nn {#1} { 64 } + 128 }
+\cs_new:Npn \@@_codepoint_to_bytes_outputi:nw
+ #1 #2 \@@_codepoint_to_bytes_end: #3
+ { \@@_codepoint_to_bytes_output:fnn { \int_eval:n {#1} } { } {#2} }
+\cs_new:Npn \@@_codepoint_to_bytes_outputii:nw
+ #1 #2 \@@_codepoint_to_bytes_end: #3#4
+ { \@@_codepoint_to_bytes_output:fnn { \int_eval:n {#1} } { {#3} } {#2} }
+\cs_new:Npn \@@_codepoint_to_bytes_outputiii:nw
+ #1 #2 \@@_codepoint_to_bytes_end: #3#4#5
+ {
+ \@@_codepoint_to_bytes_output:fnn
+ { \int_eval:n {#1} } { {#3} {#4} } {#2}
+ }
+\cs_new:Npn \@@_codepoint_to_bytes_outputiv:nw
+ #1 #2 \@@_codepoint_to_bytes_end: #3#4#5#6
+ {
+ \@@_codepoint_to_bytes_output:fnn
+ { \int_eval:n {#1} } { {#3} {#4} {#5} } {#2}
+ }
+\cs_new:Npn \@@_codepoint_to_bytes_output:nnn #1#2#3
+ {
+ #3
+ \@@_codepoint_to_bytes_end: #2 {#1}
+ }
+\cs_generate_variant:Nn \@@_codepoint_to_bytes_output:nnn { f }
+\cs_new:Npn \@@_codepoint_to_bytes_end: { }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=tl>
+% \end{macrocode}
+%
+% \begin{macro}[EXP, documented-as=\tl_if_head_eq_catcode:nNTF]
+% {\tl_if_head_eq_catcode:oNTF}
+% Extra variants.
+% \begin{macrocode}
+\cs_generate_variant:Nn \tl_if_head_eq_catcode:nNTF { o }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\tl_lower_case:n, \tl_upper_case:n, \tl_mixed_case:n}
+% \begin{macro}[EXP]{\tl_lower_case:nn, \tl_upper_case:nn, \tl_mixed_case:nn}
+% The user level functions here are all wrappers around the internal
+% functions for case changing.
+% \begin{macrocode}
+\cs_new:Npn \tl_lower_case:n { \@@_change_case:nnn { lower } { } }
+\cs_new:Npn \tl_upper_case:n { \@@_change_case:nnn { upper } { } }
+\cs_new:Npn \tl_mixed_case:n { \@@_change_case:nnn { mixed } { } }
+\cs_new:Npn \tl_lower_case:nn { \@@_change_case:nnn { lower } }
+\cs_new:Npn \tl_upper_case:nn { \@@_change_case:nnn { upper } }
+\cs_new:Npn \tl_mixed_case:nn { \@@_change_case:nnn { mixed } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case:nnn}
+% \begin{macro}[EXP]{\@@_change_case_aux:nnn}
+% \begin{macro}[EXP]{\@@_change_case_loop:wnn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_output:nwn ,
+% \@@_change_case_output:Vwn ,
+% \@@_change_case_output:own ,
+% \@@_change_case_output:vwn ,
+% \@@_change_case_output:fwn ,
+% }
+% \begin{macro}[EXP]{\@@_change_case_end:wn}
+% \begin{macro}[EXP]{\@@_change_case_group:nwnn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_group_lower:nnnn ,
+% \@@_change_case_group_upper:nnnn ,
+% \@@_change_case_group_mixed:nnnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_space:wnn}
+% \begin{macro}[EXP]{\@@_change_case_N_type:Nwnn}
+% \begin{macro}[EXP]{\@@_change_case_N_type:NNNnnn}
+% \begin{macro}[EXP]{\@@_change_case_math:NNNnnn}
+% \begin{macro}[EXP]{\@@_change_case_math_loop:wNNnn}
+% \begin{macro}[EXP]{\@@_change_case_math:NwNNnn}
+% \begin{macro}[EXP]{\@@_change_case_math_group:nwNNnn}
+% \begin{macro}[EXP]{\@@_change_case_math_space:wNNnn}
+% \begin{macro}[EXP]{\@@_change_case_N_type:Nnnn}
+% \begin{macro}[EXP]
+% {
+% \@@_change_case_char_lower:Nnn ,
+% \@@_change_case_char_upper:Nnn ,
+% \@@_change_case_char_mixed:Nnn
+% }
+% \begin{macro}[EXP]{\@@_change_case_char:nN}
+% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNN}
+% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNNN}
+% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNNNN}
+% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nn}
+% \begin{macro}[EXP]{\@@_change_case_cs_letterlike:Nn}
+% \begin{macro}[EXP]{\@@_change_case_cs_letterlike:NnN}
+% \begin{macro}[EXP]{\@@_change_case_cs_accents:NN}
+% \begin{macro}[EXP]{\@@_change_case_cs:N}
+% \begin{macro}[EXP]{\@@_change_case_cs:NN}
+% \begin{macro}[EXP]{\@@_change_case_cs:NNn}
+% \begin{macro}[EXP]{\@@_change_case_protect:wNN}
+% \begin{macro}[EXP]{\@@_change_case_if_expandable:NTF}
+% \begin{macro}[EXP]{\@@_change_case_cs_expand:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_cs_expand:NN}
+% \begin{macro}[EXP]{\@@_change_case_mixed_skip:N}
+% \begin{macro}[EXP]{\@@_change_case_mixed_skip:NN}
+% \begin{macro}[EXP]{\@@_change_case_mixed_skip_tidy:Nwn}
+% \begin{macro}[EXP]{\@@_change_case_mixed_switch:w}
+% The mechanism for the core conversion of case is based on the idea that
+% we can use a loop to grab the entire token list plus a quark: the latter is
+% used as an end marker and to avoid any brace stripping. Depending on the
+% nature of the first item in the grabbed argument, it can either processed
+% as a single token, treated as a group or treated as a space. These
+% different cases all work by re-reading |#1| in the appropriate way, hence
+% the repetition of |#1 \q_recursion_stop|.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case:nnn #1#2#3
+ {
+ \__kernel_exp_not:w \exp_after:wN
+ {
+ \exp:w
+ \@@_change_case_aux:nnn {#1} {#2} {#3}
+ }
+ }
+\cs_new:Npn \@@_change_case_aux:nnn #1#2#3
+ {
+ \group_align_safe_begin:
+ \@@_change_case_loop:wnn
+ #3 \q_recursion_tail \q_recursion_stop {#1} {#2}
+ \@@_change_case_result:n { }
+ }
+\cs_new:Npn \@@_change_case_loop:wnn #1 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_change_case_N_type:Nwnn }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_change_case_group:nwnn }
+ { \@@_change_case_space:wnn }
+ }
+ #1 \q_recursion_stop
+ }
+% \end{macrocode}
+% Earlier versions of the code where only \texttt{x}-type expandable rather
+% than \texttt{f}-type: this causes issues with nesting and so the slight
+% performance hit is taken for a better outcome in usability terms. Setting
+% up for \texttt{f}-type expandability has two requirements: a marker
+% token after the main loop (see above) and a mechanism to \enquote{load}
+% and finalise the result. That is handled in the code below, which includes
+% the necessary material to end the \cs{exp:w} expansion.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_output:nwn #1#2 \@@_change_case_result:n #3
+ { #2 \@@_change_case_result:n { #3 #1 } }
+\cs_generate_variant:Nn \@@_change_case_output:nwn { V , o , v , f }
+\cs_new:Npn \@@_change_case_end:wn #1 \@@_change_case_result:n #2
+ {
+ \group_align_safe_end:
+ \exp_end:
+ #2
+ }
+% \end{macrocode}
+% Handling for the cases where the current argument is a brace group or
+% a space is relatively easy. For the brace case, the routine works
+% recursively, using the expandability of the mechanism to ensure that the
+% result is finalised before storage. For the space case it is simply a
+% question of removing the space in the input and storing it in the output.
+% In both cases, and indeed for the \texttt{N}-type grabber, after removing
+% the current item from the input \cs{@@_change_case_loop:wnn} is inserted
+% in front of the remaining tokens.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_group:nwnn #1#2 \q_recursion_stop #3#4
+ {
+ \use:c { @@_change_case_group_ #3 : nnnn } {#1} {#2} {#3} {#4}
+ }
+\cs_new:Npn \@@_change_case_group_lower:nnnn #1#2#3#4
+ {
+ \@@_change_case_output:own
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \@@_change_case_aux:nnn {#3} {#4} {#1}
+ }
+ }
+ \@@_change_case_loop:wnn #2 \q_recursion_stop {#3} {#4}
+ }
+\cs_new_eq:NN \@@_change_case_group_upper:nnnn
+ \@@_change_case_group_lower:nnnn
+% \end{macrocode}
+% For the \enquote{mixed} case, a group is taken as forcing a switch to lower
+% casing. That means we need a separate auxiliary. (Tracking whether we have
+% found a first character inside a group and transferring the information out
+% looks pretty horrible.)
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_group_mixed:nnnn #1#2#3#4
+ {
+ \@@_change_case_output:own
+ {
+ \exp_after:wN
+ {
+ \exp:w
+ \@@_change_case_aux:nnn {#3} {#4} {#1}
+ }
+ }
+ \@@_change_case_loop:wnn #2 \q_recursion_stop { lower } {#4}
+ }
+\exp_last_unbraced:NNo \cs_new:Npn \@@_change_case_space:wnn \c_space_tl
+ {
+ \@@_change_case_output:nwn { ~ }
+ \@@_change_case_loop:wnn
+ }
+% \end{macrocode}
+% For \texttt{N}-type arguments there are several stages to the approach.
+% First, a simply check for the end-of-input marker, which if found triggers
+% the final clean up and output step. Assuming that is not the case, the
+% first check is for math-mode escaping: this test can encompass control
+% sequences or other \texttt{N}-type tokens so is handled up front.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_N_type:Nwnn #1#2 \q_recursion_stop
+ {
+ \quark_if_recursion_tail_stop_do:Nn #1
+ { \@@_change_case_end:wn }
+ \exp_after:wN \@@_change_case_N_type:NNNnnn
+ \exp_after:wN #1 \l_tl_case_change_math_tl
+ \q_recursion_tail ? \q_recursion_stop {#2}
+ }
+% \end{macrocode}
+% Looking for math mode escape first requires a loop over the possible
+% token pairs to see if the current input (|#1|) matches an open-math case
+% (|#2|). If if does then this test loop is ended and a new input-gathering
+% one is begun. The latter simply transfers material from the input to the
+% output without any expansion, testing each \texttt{N}-type token to see
+% if it matches the close-math case required. If that is the situation then
+% the \enquote{math loop} stops and resumes the main loop: as that might
+% be either the standard case-changing one or the mixed-case alternative,
+% it is not hard-coded into the math loop but is rather passed as argument
+% |#3| to \cs{@@_change_case_math:NNNnnn}. If no close-math token is found
+% then the final clean-up is forced (\emph{i.e.}~there is no assumption
+% of \enquote{well-behaved} input in terms of math mode).
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_N_type:NNNnnn #1#2#3
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ { \@@_change_case_N_type:Nnnn #1 }
+ \token_if_eq_meaning:NNTF #1 #2
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_change_case_math:NNNnnn
+ #1 #3 \@@_change_case_loop:wnn
+ }
+ }
+ { \@@_change_case_N_type:NNNnnn #1 }
+ }
+\cs_new:Npn \@@_change_case_math:NNNnnn #1#2#3#4
+ {
+ \@@_change_case_output:nwn {#1}
+ \@@_change_case_math_loop:wNNnn #4 \q_recursion_stop #2 #3
+ }
+\cs_new:Npn \@@_change_case_math_loop:wNNnn #1 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_change_case_math:NwNNnn }
+ {
+ \tl_if_head_is_group:nTF {#1}
+ { \@@_change_case_math_group:nwNNnn }
+ { \@@_change_case_math_space:wNNnn }
+ }
+ #1 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_math:NwNNnn #1#2 \q_recursion_stop #3#4
+ {
+ \token_if_eq_meaning:NNTF \q_recursion_tail #1
+ { \@@_change_case_end:wn }
+ {
+ \@@_change_case_output:nwn {#1}
+ \token_if_eq_meaning:NNTF #1 #3
+ { #4 #2 \q_recursion_stop }
+ { \@@_change_case_math_loop:wNNnn #2 \q_recursion_stop #3#4 }
+ }
+ }
+\cs_new:Npn \@@_change_case_math_group:nwNNnn #1#2 \q_recursion_stop
+ {
+ \@@_change_case_output:nwn { {#1} }
+ \@@_change_case_math_loop:wNNnn #2 \q_recursion_stop
+ }
+\exp_last_unbraced:NNo
+ \cs_new:Npn \@@_change_case_math_space:wNNnn \c_space_tl
+ {
+ \@@_change_case_output:nwn { ~ }
+ \@@_change_case_math_loop:wNNnn
+ }
+% \end{macrocode}
+% Once potential math-mode cases are filtered out the next stage is to
+% test if the token grabbed is a control sequence: they cannot be used in
+% the lookup table and also may require expansion. At this stage the loop
+% code starting \cs{@@_change_case_loop:wnn} is inserted: all subsequent
+% steps in the code which need a look-ahead are coded to rely on this and
+% thus have \texttt{w}-type arguments if they may do a look-ahead.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_N_type:Nnnn #1#2#3#4
+ {
+ \token_if_cs:NTF #1
+ { \@@_change_case_cs_letterlike:Nn #1 {#3} }
+ { \use:c { @@_change_case_char_ #3 :Nnn } #1 {#3} {#4} }
+ \@@_change_case_loop:wnn #2 \q_recursion_stop {#3} {#4}
+ }
+% \end{macrocode}
+% For character tokens there are some special cases to deal with then
+% the majority of changes are covered by using the \TeX{} data as a lookup
+% along with expandable character generation. This avoids needing a very
+% large number of macros or (as seen in earlier versions) a somewhat tricky
+% split of the characters into various blocks. Notice that the special case
+% code may do a look-ahead so requires a final \texttt{w}-type argument
+% whereas the core lookup table does not and also guarantees an output so
+% \texttt{f}-type expansion may be used to obtain the case-changed result.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_char_lower:Nnn #1#2#3
+ {
+ \cs_if_exist_use:cF { @@_change_case_ #2 _ #3 :Nnw }
+ { \use_ii:nn }
+ #1
+ {
+ \use:c { @@_change_case_ #2 _ sigma:Nnw } #1
+ { \@@_change_case_char:nN {#2} #1 }
+ }
+ }
+\cs_new_eq:NN \@@_change_case_char_upper:Nnn
+ \@@_change_case_char_lower:Nnn
+% \end{macrocode}
+% For mixed case, the code is somewhat different: there is a need to
+% look up both mixed and upper case chars and we have to cover the
+% situation where there is a character to skip over.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_char_mixed:Nnn #1#2#3
+ {
+ \@@_change_case_mixed_switch:w
+ \cs_if_exist_use:cF { @@_change_case_mixed_ #3 :Nnw }
+ {
+ \cs_if_exist_use:cF { @@_change_case_upper_ #3 :Nnw }
+ { \use_ii:nn }
+ }
+ #1
+ { \@@_change_case_mixed_skip:N #1 }
+ }
+% \end{macrocode}
+% For Unicode engines we can handle all characters directly. However, for
+% the $8$-bit engines the aim is to deal with (a subset of) Unicode (UTF-8)
+% input. They deal with that by making the upper half of the range active,
+% so we look for that and if found work out how many UTF-8 octets there
+% are to deal with. Those can then be grabbed to reconstruct the full
+% Unicode character, which is then used in a lookup. (As will become
+% obvious below, there is no intention here of covering all of Unicode.)
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_change_case_char:nN #1#2
+ {
+ \@@_change_case_output:fwn
+ { \use:c { char_ #1 _case:N } #2 }
+ }
+ }
+ {
+ \cs_new:Npn \@@_change_case_char:nN #1#2
+ {
+ \int_compare:nNnTF { `#2 } > { "80 }
+ {
+ \int_compare:nNnTF { `#2 } < { "E0 }
+ { \@@_change_case_char_UTFviii:nNNN {#1} #2 }
+ {
+ \int_compare:nNnTF { `#2 } < { "F0 }
+ { \@@_change_case_char_UTFviii:nNNNN {#1} #2 }
+ { \@@_change_case_char_UTFviii:nNNNNN {#1} #2 }
+ }
+ }
+ {
+ \@@_change_case_output:fwn
+ { \use:c { char_ #1 _case:N } #2 }
+ }
+ }
+ }
+% \end{macrocode}
+% To allow for the special case of mixed case, we insert here a
+% action-dependent auxiliary.
+% \begin{macrocode}
+\bool_lazy_or:nnF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_change_case_char_UTFviii:nNNN #1#2#3#4
+ { \@@_change_case_char_UTFviii:nnN {#1} {#2#4} #3 }
+ \cs_new:Npn \@@_change_case_char_UTFviii:nNNNN #1#2#3#4#5
+ { \@@_change_case_char_UTFviii:nnN {#1} {#2#4#5} #3 }
+ \cs_new:Npn \@@_change_case_char_UTFviii:nNNNNN #1#2#3#4#5#6
+ { \@@_change_case_char_UTFviii:nnN {#1} {#2#4#5#6} #3 }
+ \cs_new:Npn \@@_change_case_char_UTFviii:nnN #1#2#3
+ {
+ \cs_if_exist:cTF { c_@@_ #1 _case_ \tl_to_str:n {#2} _tl }
+ {
+ \@@_change_case_output:vwn
+ { c_@@_ #1 _case_ \tl_to_str:n {#2} _tl }
+ }
+ { \@@_change_case_output:nwn {#2} }
+ #3
+ }
+ }
+% \end{macrocode}
+% Before dealing with general control sequences there are the special
+% ones to deal with. Letter-like control sequences are a simple look-up,
+% while for accents the loop is much as done elsewhere. Notice that
+% we have a no-op test to make sure there is no unexpected expansion of
+% letter-like input. The split into two parts here allows us to insert
+% the \enquote{switch} code for mixed casing.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_cs_letterlike:Nn #1#2
+ {
+ \str_if_eq:nnTF {#2} { mixed }
+ {
+ \@@_change_case_cs_letterlike:NnN #1 { upper }
+ \@@_change_case_mixed_switch:w
+ }
+ { \@@_change_case_cs_letterlike:NnN #1 {#2} \prg_do_nothing: }
+ }
+\cs_new:Npn \@@_change_case_cs_letterlike:NnN #1#2#3
+ {
+ \cs_if_exist:cTF { c_@@_change_case_ #2 _ \token_to_str:N #1 _tl }
+ {
+ \@@_change_case_output:vwn
+ { c_@@_change_case_ #2 _ \token_to_str:N #1 _tl }
+ #3
+ }
+ {
+ \cs_if_exist:cTF
+ {
+ c_@@_change_case_
+ \str_if_eq:nnTF {#2} { lower } { upper } { lower }
+ _ \token_to_str:N #1 _tl
+ }
+ {
+ \@@_change_case_output:nwn {#1}
+ #3
+ }
+ {
+ \exp_after:wN \@@_change_case_cs_accents:NN
+ \exp_after:wN #1 \l_tl_case_change_accents_tl
+ \q_recursion_tail \q_recursion_stop
+ }
+ }
+ }
+\cs_new:Npn \@@_change_case_cs_accents:NN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ { \@@_change_case_cs:N #1 }
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ { \@@_change_case_output:nwn {#1} }
+ }
+ { \@@_change_case_cs_accents:NN #1 }
+ }
+% \end{macrocode}
+% To deal with a control sequence there is first a need to test if it is
+% on the list which indicate that case changing should be skipped. That's
+% done using a loop as for the other special cases. If a hit is found then
+% the argument is grabbed: that comes \emph{after} the loop function which
+% is therefore rearranged. In a \LaTeXe{} context, \tn{protect} needs
+% to be treated specially, to prevent expansion of the next token but
+% output it without braces.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_cs:N #1
+ {
+%<*package>
+ \str_if_eq:nnTF {#1} { \protect } { \@@_change_case_protect:wNN }
+%</package>
+ \exp_after:wN \@@_change_case_cs:NN
+ \exp_after:wN #1 \l_tl_case_change_exclude_tl
+ \q_recursion_tail \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_cs:NN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:Nn #2
+ {
+ \@@_change_case_cs_expand:Nnw #1
+ { \@@_change_case_output:nwn {#1} }
+ }
+ \str_if_eq:nnTF {#1} {#2}
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ { \@@_change_case_cs:NNn #1 }
+ }
+ { \@@_change_case_cs:NN #1 }
+ }
+\cs_new:Npn \@@_change_case_cs:NNn #1#2#3
+ {
+ \@@_change_case_output:nwn { #1 {#3} }
+ #2
+ }
+%<*package>
+\cs_new:Npn \@@_change_case_protect:wNN #1 \q_recursion_stop #2 #3
+ { \@@_change_case_output:nwn { \protect #3 } #2 }
+%</package>
+% \end{macrocode}
+% When a control sequence is not on the exclude list the other test if
+% to see if it is expandable. Once again, if there is a hit then the loop
+% function is grabbed as part of the clean-up and reinserted before the
+% now expanded material. The test for expandability has to check for
+% end-of-recursion as it is needed by the look-ahead code which might hit
+% the end of the input. The test is done in two parts as \cs{bool_if:nTF}
+% would choke if |#1| was |(|!
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_if_expandable:NTF #1
+ {
+ \token_if_expandable:NTF #1
+ {
+ \bool_lazy_any:nTF
+ {
+ { \token_if_eq_meaning_p:NN \q_recursion_tail #1 }
+ { \token_if_protected_macro_p:N #1 }
+ { \token_if_protected_long_macro_p:N #1 }
+ }
+ { \use_ii:nn }
+ { \use_i:nn }
+ }
+ { \use_ii:nn }
+ }
+\cs_new:Npn \@@_change_case_cs_expand:Nnw #1#2
+ {
+ \@@_change_case_if_expandable:NTF #1
+ { \@@_change_case_cs_expand:NN #1 }
+ { #2 }
+ }
+\cs_new:Npn \@@_change_case_cs_expand:NN #1#2
+ { \exp_after:wN #2 #1 }
+% \end{macrocode}
+% For mixed case, there is an additional list of exceptions to deal with:
+% once that is sorted, we can move on back to the main loop.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_mixed_skip:N #1
+ {
+ \exp_after:wN \@@_change_case_mixed_skip:NN
+ \exp_after:wN #1 \l_tl_mixed_case_ignore_tl
+ \q_recursion_tail \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_mixed_skip:NN #1#2
+ {
+ \quark_if_recursion_tail_stop_do:nn {#2}
+ { \@@_change_case_char:nN { mixed } #1 }
+ \int_compare:nNnT { `#1 } = { `#2 }
+ {
+ \use_i_delimit_by_q_recursion_stop:nw
+ {
+ \@@_change_case_output:nwn {#1}
+ \@@_change_case_mixed_skip_tidy:Nwn
+ }
+ }
+ \@@_change_case_mixed_skip:NN #1
+ }
+\cs_new:Npn \@@_change_case_mixed_skip_tidy:Nwn #1#2 \q_recursion_stop #3
+ {
+ \@@_change_case_loop:wnn #2 \q_recursion_stop { mixed }
+ }
+% \end{macrocode}
+% Needed to switch from mixed to lower casing when we have found a
+% first character in the former mode.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_mixed_switch:w
+ #1 \@@_change_case_loop:wnn #2 \q_recursion_stop #3
+ {
+ #1
+ \@@_change_case_loop:wnn #2 \q_recursion_stop { lower }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:w}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:Nw}
+% \begin{macro}[EXP]{\@@_change_case_upper_sigma:Nnw}
+% If the current char is an upper case sigma, the a check is made on the next
+% item in the input. If it is \texttt{N}-type and not a control sequence
+% then there is a look-ahead phase.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_sigma:Nnw #1#2#3#4 \q_recursion_stop
+ {
+ \int_compare:nNnTF { `#1 } = { "03A3 }
+ {
+ \@@_change_case_output:fwn
+ { \@@_change_case_lower_sigma:w #4 \q_recursion_stop }
+ }
+ {#2}
+ #3 #4 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_lower_sigma:w #1 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \@@_change_case_lower_sigma:Nw #1 \q_recursion_stop }
+ { \c_@@_final_sigma_tl }
+ }
+\cs_new:Npn \@@_change_case_lower_sigma:Nw #1#2 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #1
+ {
+ \exp_after:wN \@@_change_case_lower_sigma:w #1
+ #2 \q_recursion_stop
+ }
+ {
+ \token_if_letter:NTF #1
+ { \c_@@_std_sigma_tl }
+ { \c_@@_final_sigma_tl }
+ }
+ }
+% \end{macrocode}
+% Simply skip to the final step for upper casing.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_change_case_upper_sigma:Nnw \use_ii:nn
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr_auxi:Nw}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr_auxii:Nw}
+% \begin{macro}[EXP]{\@@_change_case_upper_tr:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_az:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_az:Nnw}
+% The Turkic languages need special treatment for dotted-i and dotless-i.
+% The lower casing rule can be expressed in terms of searching first for
+% either a dotless-I or a dotted-I. In the latter case the mapping is
+% easy, but in the former there is a second stage search.
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_new:Npn \@@_change_case_lower_tr:Nnw #1#2
+ {
+ \int_compare:nNnTF { `#1 } = { "0049 }
+ { \@@_change_case_lower_tr_auxi:Nw }
+ {
+ \int_compare:nNnTF { `#1 } = { "0130 }
+ { \@@_change_case_output:nwn { i } }
+ {#2}
+ }
+ }
+% \end{macrocode}
+% After a dotless-I there may be a dot-above character. If there is then
+% a dotted-i should be produced, otherwise output a dotless-i. When the
+% combination is found both the dotless-I and the dot-above char have to
+% be removed from the input, which is done by the \cs{use_i:nn}
+% (it grabs \cs{@@_change_case_loop:wn} and the dot-above char and
+% discards the latter).
+% \begin{macrocode}
+ \cs_new:Npn \@@_change_case_lower_tr_auxi:Nw #1#2 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nTF {#2}
+ { \@@_change_case_lower_tr_auxii:Nw #2 \q_recursion_stop }
+ { \@@_change_case_output:Vwn \c_@@_dotless_i_tl }
+ #1 #2 \q_recursion_stop
+ }
+ \cs_new:Npn \@@_change_case_lower_tr_auxii:Nw #1#2 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #1
+ {
+ \exp_after:wN \@@_change_case_lower_tr_auxi:Nw #1
+ #2 \q_recursion_stop
+ }
+ {
+ \bool_lazy_or:nnTF
+ { \token_if_cs_p:N #1 }
+ { ! \int_compare_p:nNn { `#1 } = { "0307 } }
+ { \@@_change_case_output:Vwn \c_@@_dotless_i_tl }
+ {
+ \@@_change_case_output:nwn { i }
+ \use_i:nn
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% For $8$-bit engines, dot-above is not available so there is a simple
+% test for an upper-case I. Then we can look for the UTF-8 representation of
+% an upper case dotted-I without the combining char. If it's not there,
+% preserve the UTF-8 sequence as-is.
+% \begin{macrocode}
+ {
+ \cs_new:Npn \@@_change_case_lower_tr:Nnw #1#2
+ {
+ \int_compare:nNnTF { `#1 } = { "0049 }
+ { \@@_change_case_output:Vwn \c_@@_dotless_i_tl }
+ {
+ \int_compare:nNnTF { `#1 } = { 196 }
+ { \@@_change_case_lower_tr_auxi:Nw #1 {#2} }
+ {#2}
+ }
+ }
+ \cs_new:Npn \@@_change_case_lower_tr_auxi:Nw #1#2#3#4
+ {
+ \int_compare:nNnTF { `#4 } = { 176 }
+ {
+ \@@_change_case_output:nwn { i }
+ #3
+ }
+ {
+ #2
+ #3 #4
+ }
+ }
+ }
+% \end{macrocode}
+% Upper casing is easier: just one exception with no context.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_tr:Nnw #1#2
+ {
+ \int_compare:nNnTF { `#1 } = { "0069 }
+ { \@@_change_case_output:Vwn \c_@@_dotted_I_tl }
+ {#2}
+ }
+% \end{macrocode}
+% Straight copies.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_change_case_lower_az:Nnw \@@_change_case_lower_tr:Nnw
+\cs_new_eq:NN \@@_change_case_upper_az:Nnw \@@_change_case_upper_tr:Nnw
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:nNnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:nnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:Nw}
+% \begin{macro}[EXP]{\@@_change_case_lower_lt:NNw}
+% \begin{macro}[EXP]{\@@_change_case_upper_lt:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_lt:nnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_lt:Nw}
+% \begin{macro}[EXP]{\@@_change_case_upper_lt:NNw}
+% For Lithuanian, the issue to be dealt with is dots over lower case
+% letters: these should be present if there is another accent. That means
+% that there is some work to do when lower casing I and J. The first step
+% is a simple match attempt: \cs{c_@@_accents_lt_tl} contains
+% accented upper case letters which should gain a dot-above char in their
+% lower case form. This is done using \texttt{f}-type expansion so only one
+% pass is needed to find if it works or not. If there was no hit, the second
+% stage is to check for I, J and I-ogonek, and if the current char is a
+% match to look for a following accent.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_lt:Nnw #1
+ {
+ \exp_args:Nf \@@_change_case_lower_lt:nNnw
+ { \str_case:nVF #1 \c_@@_accents_lt_tl \exp_stop_f: }
+ #1
+ }
+\cs_new:Npn \@@_change_case_lower_lt:nNnw #1#2
+ {
+ \tl_if_blank:nTF {#1}
+ {
+ \exp_args:Nf \@@_change_case_lower_lt:nnw
+ {
+ \int_case:nnF {`#2}
+ {
+ { "0049 } i
+ { "004A } j
+ { "012E } \c_@@_i_ogonek_tl
+ }
+ \exp_stop_f:
+ }
+ }
+ {
+ \@@_change_case_output:nwn {#1}
+ \use_none:n
+ }
+ }
+\cs_new:Npn \@@_change_case_lower_lt:nnw #1#2
+ {
+ \tl_if_blank:nTF {#1}
+ {#2}
+ {
+ \@@_change_case_output:nwn {#1}
+ \@@_change_case_lower_lt:Nw
+ }
+ }
+% \end{macrocode}
+% Grab the next char and see if it is one of the accents used in Lithuanian:
+% if it is, add the dot-above char into the output.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_lt:Nw #1#2 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nT {#2}
+ { \@@_change_case_lower_lt:NNw }
+ #1 #2 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_lower_lt:NNw #1#2#3 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #2
+ {
+ \exp_after:wN \@@_change_case_lower_lt:Nw \exp_after:wN #1 #2
+ #3 \q_recursion_stop
+ }
+ {
+ \bool_lazy_and:nnT
+ { ! \token_if_cs_p:N #2 }
+ {
+ \bool_lazy_any_p:n
+ {
+ { \int_compare_p:nNn { `#2 } = { "0300 } }
+ { \int_compare_p:nNn { `#2 } = { "0301 } }
+ { \int_compare_p:nNn { `#2 } = { "0303 } }
+ }
+ }
+ { \@@_change_case_output:Vwn \c_@@_dot_above_tl }
+ #1 #2#3 \q_recursion_stop
+ }
+ }
+% \end{macrocode}
+% For upper casing, the test required is for a dot-above char after an I,
+% J or I-ogonek. First a test for the appropriate letter, and if found a
+% look-ahead and potentially one token dropped.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_lt:Nnw #1
+ {
+ \exp_args:Nf \@@_change_case_upper_lt:nnw
+ {
+ \int_case:nnF {`#1}
+ {
+ { "0069 } I
+ { "006A } J
+ { "012F } \c_@@_I_ogonek_tl
+ }
+ \exp_stop_f:
+ }
+ }
+\cs_new:Npn \@@_change_case_upper_lt:nnw #1#2
+ {
+ \tl_if_blank:nTF {#1}
+ {#2}
+ {
+ \@@_change_case_output:nwn {#1}
+ \@@_change_case_upper_lt:Nw
+ }
+ }
+\cs_new:Npn \@@_change_case_upper_lt:Nw #1#2 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nT {#2}
+ { \@@_change_case_upper_lt:NNw }
+ #1 #2 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_upper_lt:NNw #1#2#3 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #2
+ {
+ \exp_after:wN \@@_change_case_upper_lt:Nw \exp_after:wN #1 #2
+ #3 \q_recursion_stop
+ }
+ {
+ \bool_lazy_and:nnTF
+ { ! \token_if_cs_p:N #2 }
+ { \int_compare_p:nNn { `#2 } = { "0307 } }
+ { #1 }
+ { #1 #2 }
+ #3 \q_recursion_stop
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_change_case_upper_de-alt:Nnw}
+% A simple alternative version for German.
+% \begin{macrocode}
+\cs_new:cpn { @@_change_case_upper_de-alt:Nnw } #1#2
+ {
+ \int_compare:nNnTF { `#1 } = { 223 }
+ { \@@_change_case_output:Vwn \c_@@_upper_Eszett_tl }
+ {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_@@_std_sigma_tl ,
+% \c_@@_final_sigma_tl ,
+% \c_@@_accents_lt_tl ,
+% \c_@@_dot_above_tl ,
+% \c_@@_upper_Eszett_tl
+% }
+% The above needs various special token lists containing pre-formed characters.
+% This set are only available in Unicode engines, with no-op definitions
+% for $8$-bit use.
+% \begin{macrocode}
+\bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \group_begin:
+ \cs_set:Npn \@@_tmp:n #1
+ { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
+ \tl_const:Nx \c_@@_std_sigma_tl { \@@_tmp:n { "03C3 } }
+ \tl_const:Nx \c_@@_final_sigma_tl { \@@_tmp:n { "03C2 } }
+ \tl_const:Nx \c_@@_accents_lt_tl
+ {
+ \@@_tmp:n { "00CC }
+ {
+ \@@_tmp:n { "0069 }
+ \@@_tmp:n { "0307 }
+ \@@_tmp:n { "0300 }
+ }
+ \@@_tmp:n { "00CD }
+ {
+ \@@_tmp:n { "0069 }
+ \@@_tmp:n { "0307 }
+ \@@_tmp:n { "0301 }
+ }
+ \@@_tmp:n { "0128 }
+ {
+ \@@_tmp:n { "0069 }
+ \@@_tmp:n { "0307 }
+ \@@_tmp:n { "0303 }
+ }
+ }
+ \tl_const:Nx \c_@@_dot_above_tl { \@@_tmp:n { "0307 } }
+ \tl_const:Nx \c_@@_upper_Eszett_tl { \@@_tmp:n { "1E9E } }
+ \group_end:
+ }
+ {
+ \tl_const:Nn \c_@@_std_sigma_tl { }
+ \tl_const:Nn \c_@@_final_sigma_tl { }
+ \tl_const:Nn \c_@@_accents_lt_tl { }
+ \tl_const:Nn \c_@@_dot_above_tl { }
+ \tl_const:Nn \c_@@_upper_Eszett_tl { }
+ }
+% \end{macrocode}
+% \end{variable}
+% \begin{variable}
+% {
+% \c_@@_dotless_i_tl ,
+% \c_@@_dotted_I_tl ,
+% \c_@@_i_ogonek_tl ,
+% \c_@@_I_ogonek_tl ,
+% }
+% For cases where there is an $8$-bit option in the |T1| font set up,
+% a variant is provided in both cases.
+% \begin{macrocode}
+\group_begin:
+ \bool_lazy_or:nnTF
+ { \sys_if_engine_luatex_p: }
+ { \sys_if_engine_xetex_p: }
+ {
+ \cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \tl_const:Nx #1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn
+ {"#2} { \char_value_catcode:n {"#2} }
+ }
+ }
+ }
+ {
+ \cs_set_protected:Npn \@@_tmp:w #1#2
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4
+ {
+ \tl_const:Nx #1
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {##1} { 13 }
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {##2} { 13 }
+ }
+ }
+ \tl_set:Nx \l_@@_internal_a_tl
+ { \char_codepoint_to_bytes:n {"#2} }
+ \exp_after:wN \@@_tmp:w \l_@@_internal_a_tl
+ \group_end:
+ }
+ }
+ \@@_tmp:w \c_@@_dotless_i_tl { 0131 }
+ \@@_tmp:w \c_@@_dotted_I_tl { 0130 }
+ \@@_tmp:w \c_@@_i_ogonek_tl { 012F }
+ \@@_tmp:w \c_@@_I_ogonek_tl { 012E }
+\group_end:
+% \end{macrocode}
+% \end{variable}
+%
+% For $8$-bit engines we now need to define the case-change data for
+% the multi-octet mappings. These need a list of what code points are
+% doable in |T1| so the list is hard coded (there's no saving in loading
+% the mappings dynamically). All of the straight-forward ones have two
+% octets, so that is taken as read.
+% \begin{macrocode}
+\group_begin:
+ \bool_lazy_or:nnT
+ { \sys_if_engine_pdftex_p: }
+ { \sys_if_engine_uptex_p: }
+ {
+ \cs_set_protected:Npn \@@_loop:nn #1#2
+ {
+ \quark_if_recursion_tail_stop:n {#1}
+ \tl_set:Nx \l_@@_internal_a_tl
+ {
+ \char_codepoint_to_bytes:n {"#1}
+ \char_codepoint_to_bytes:n {"#2}
+ }
+ \exp_after:wN \@@_tmp:w \l_@@_internal_a_tl
+ \@@_loop:nn
+ }
+ \cs_set_protected:Npn \@@_tmp:nnnn #1#2#3#4#5
+ {
+ \tl_const:cx
+ {
+ c_@@_ #1 _case_
+ \char_generate:nn {#2} { 12 }
+ \char_generate:nn {#3} { 12 }
+ _tl
+ }
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {#4} { 13 }
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {#5} { 13 }
+ }
+ }
+ \cs_set_protected:Npn \@@_tmp:w #1#2#3#4#5#6#7#8
+ {
+ \tl_const:cx
+ {
+ c_@@_lower_case_
+ \char_generate:nn {#1} { 12 }
+ \char_generate:nn {#2} { 12 }
+ _tl
+ }
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {#5} { 13 }
+ \exp_after:wN \exp_after:wN \exp_after:wN
+ \exp_not:N \char_generate:nn {#6} { 13 }
+ }
+ \@@_tmp:nnnn { upper } {#5} {#6} {#1} {#2}
+ \@@_tmp:nnnn { mixed } {#5} {#6} {#1} {#2}
+ }
+ \@@_loop:nn
+ { 00C0 } { 00E0 }
+ { 00C2 } { 00E2 }
+ { 00C3 } { 00E3 }
+ { 00C4 } { 00E4 }
+ { 00C5 } { 00E5 }
+ { 00C6 } { 00E6 }
+ { 00C7 } { 00E7 }
+ { 00C8 } { 00E8 }
+ { 00C9 } { 00E9 }
+ { 00CA } { 00EA }
+ { 00CB } { 00EB }
+ { 00CC } { 00EC }
+ { 00CD } { 00ED }
+ { 00CE } { 00EE }
+ { 00CF } { 00EF }
+ { 00D0 } { 00F0 }
+ { 00D1 } { 00F1 }
+ { 00D2 } { 00F2 }
+ { 00D3 } { 00F3 }
+ { 00D4 } { 00F4 }
+ { 00D5 } { 00F5 }
+ { 00D6 } { 00F6 }
+ { 00D8 } { 00F8 }
+ { 00D9 } { 00F9 }
+ { 00DA } { 00FA }
+ { 00DB } { 00FB }
+ { 00DC } { 00FC }
+ { 00DD } { 00FD }
+ { 00DE } { 00FE }
+ { 0100 } { 0101 }
+ { 0102 } { 0103 }
+ { 0104 } { 0105 }
+ { 0106 } { 0107 }
+ { 0108 } { 0109 }
+ { 010A } { 010B }
+ { 010C } { 010D }
+ { 010E } { 010F }
+ { 0110 } { 0111 }
+ { 0112 } { 0113 }
+ { 0114 } { 0115 }
+ { 0116 } { 0117 }
+ { 0118 } { 0119 }
+ { 011A } { 011B }
+ { 011C } { 011D }
+ { 011E } { 011F }
+ { 0120 } { 0121 }
+ { 0122 } { 0123 }
+ { 0124 } { 0125 }
+ { 0128 } { 0129 }
+ { 012A } { 012B }
+ { 012C } { 012D }
+ { 012E } { 012F }
+ { 0132 } { 0133 }
+ { 0134 } { 0135 }
+ { 0136 } { 0137 }
+ { 0139 } { 013A }
+ { 013B } { 013C }
+ { 013E } { 013F }
+ { 0141 } { 0142 }
+ { 0143 } { 0144 }
+ { 0145 } { 0146 }
+ { 0147 } { 0148 }
+ { 014A } { 014B }
+ { 014C } { 014D }
+ { 014E } { 014F }
+ { 0150 } { 0151 }
+ { 0152 } { 0153 }
+ { 0154 } { 0155 }
+ { 0156 } { 0157 }
+ { 0158 } { 0159 }
+ { 015A } { 015B }
+ { 015C } { 015D }
+ { 015E } { 015F }
+ { 0160 } { 0161 }
+ { 0162 } { 0163 }
+ { 0164 } { 0165 }
+ { 0168 } { 0169 }
+ { 016A } { 016B }
+ { 016C } { 016D }
+ { 016E } { 016F }
+ { 0170 } { 0171 }
+ { 0172 } { 0173 }
+ { 0174 } { 0175 }
+ { 0176 } { 0177 }
+ { 0178 } { 00FF }
+ { 0179 } { 017A }
+ { 017B } { 017C }
+ { 017D } { 017E }
+ { 01CD } { 01CE }
+ { 01CF } { 01D0 }
+ { 01D1 } { 01D2 }
+ { 01D3 } { 01D4 }
+ { 01E2 } { 01E3 }
+ { 01E6 } { 01E7 }
+ { 01E8 } { 01E9 }
+ { 01EA } { 01EB }
+ { 01F4 } { 01F5 }
+ { 0218 } { 0219 }
+ { 021A } { 021B }
+ \q_recursion_tail ?
+ \q_recursion_stop
+ \cs_set_protected:Npn \@@_tmp:w #1#2#3
+ {
+ \group_begin:
+ \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4
+ {
+ \tl_const:cx
+ {
+ c_@@_ #3 _case_
+ \char_generate:nn {##1} { 12 }
+ \char_generate:nn {##2} { 12 }
+ _tl
+ }
+ {#2}
+ }
+ \tl_set:Nx \l_@@_internal_a_tl
+ { \char_codepoint_to_bytes:n { "#1 } }
+ \exp_after:wN \@@_tmp:w \l_@@_internal_a_tl
+ \group_end:
+ }
+ \@@_tmp:w { 00DF } { SS } { upper }
+ \@@_tmp:w { 00DF } { Ss } { mixed }
+ \@@_tmp:w { 0131 } { I } { upper }
+ }
+ \group_end:
+% \end{macrocode}
+%
+% The (fixed) look-up mappings for letter-like control sequences.
+% \begin{macrocode}
+\group_begin:
+ \cs_set_protected:Npn \@@_change_case_setup:NN #1#2
+ {
+ \quark_if_recursion_tail_stop:N #1
+ \tl_const:cn { c_@@_change_case_lower_ \token_to_str:N #1 _tl }
+ { #2 }
+ \tl_const:cn { c_@@_change_case_upper_ \token_to_str:N #2 _tl }
+ { #1 }
+ \@@_change_case_setup:NN
+ }
+ \@@_change_case_setup:NN
+ \AA \aa
+ \AE \ae
+ \DH \dh
+ \DJ \dj
+ \IJ \ij
+ \L \l
+ \NG \ng
+ \O \o
+ \OE \oe
+ \SS \ss
+ \TH \th
+ \q_recursion_tail ?
+ \q_recursion_stop
+ \tl_const:cn { c_@@_change_case_upper_ \token_to_str:N \i _tl } { I }
+ \tl_const:cn { c_@@_change_case_upper_ \token_to_str:N \j _tl } { J }
+\group_end:
+% \end{macrocode}
+%
+% \begin{variable}{\l_tl_case_change_accents_tl}
+% A list of accents to leave alone.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_accents_tl
+\tl_set:Nn \l_tl_case_change_accents_tl
+ { \" \' \. \^ \` \~ \c \H \k \r \t \u \v }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_change_case_mixed_nl:Nnw}
+% \begin{macro}[EXP]{\@@_change_case_mixed_nl:Nw}
+% \begin{macro}[EXP]{\@@_change_case_mixed_nl:NNw}
+% For Dutch, there is a single look-ahead test for \texttt{ij} when
+% title casing. If the appropriate letters are found, produce \texttt{IJ}
+% and gobble the \texttt{j}/\texttt{J}.
+% \begin{macrocode}
+\cs_new:Npn \@@_change_case_mixed_nl:Nnw #1
+ {
+ \bool_lazy_or:nnTF
+ { \int_compare_p:nNn { `#1 } = { `i } }
+ { \int_compare_p:nNn { `#1 } = { `I } }
+ {
+ \@@_change_case_output:nwn { I }
+ \@@_change_case_mixed_nl:Nw
+ }
+ }
+\cs_new:Npn \@@_change_case_mixed_nl:Nw #1#2 \q_recursion_stop
+ {
+ \tl_if_head_is_N_type:nT {#2}
+ { \@@_change_case_mixed_nl:NNw }
+ #1 #2 \q_recursion_stop
+ }
+\cs_new:Npn \@@_change_case_mixed_nl:NNw #1#2#3 \q_recursion_stop
+ {
+ \@@_change_case_if_expandable:NTF #2
+ {
+ \exp_after:wN \@@_change_case_mixed_nl:Nw \exp_after:wN #1 #2
+ #3 \q_recursion_stop
+ }
+ {
+ \bool_lazy_and:nnTF
+ { ! ( \token_if_cs_p:N #2 ) }
+ {
+ \bool_lazy_or_p:nn
+ { \int_compare_p:nNn { `#2 } = { `j } }
+ { \int_compare_p:nNn { `#2 } = { `J } }
+ }
+ {
+ \@@_change_case_output:nwn { J }
+ #1
+ }
+ { #1 #2 }
+ #3 \q_recursion_stop
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_tl_case_change_math_tl}
+% The list of token pairs which are treated as math mode and so
+% not case changed.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_math_tl
+%<*package>
+\tl_set:Nn \l_tl_case_change_math_tl
+ { $ $ \( \) }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tl_case_change_exclude_tl}
+% The list of commands for which an argument is not case changed.
+% \begin{macrocode}
+\tl_new:N \l_tl_case_change_exclude_tl
+%<*package>
+\tl_set:Nn \l_tl_case_change_exclude_tl
+ { \cite \ensuremath \label \ref }
+%</package>
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_tl_mixed_case_ignore_tl}
+% Characters to skip over when finding the first letter in a word to be
+% mixed cased.
+% \begin{macrocode}
+\tl_new:N \l_tl_mixed_case_ignore_tl
+\tl_set:Nx \l_tl_mixed_case_ignore_tl
+ {
+ ( % )
+ [ % ]
+ \cs_to_str:N \{ % \}
+ `
+ -
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsubsection{Building a token list}
+%
+% Between \cs{tl_build_begin:N} \meta{tl~var} and \cs{tl_build_end:N}
+% \meta{tl~var}, the \meta{tl~var} has the structure
+% \begin{quote}
+% \cs{exp_end:} \ldots{} \cs{exp_end:} \cs{@@_build_last:NNn}
+% \meta{assignment} \meta{next~tl} \Arg{left} \meta{right}
+% \end{quote}
+% where \meta{right} is not braced. The \enquote{data} it represents is
+% \meta{left} followed by the \enquote{data} of \meta{next~tl} followed
+% by \meta{right}. The \meta{next~tl} is a token list variable whose
+% name is that of \meta{tl~var} followed by~|'|. There are between $0$
+% and $4$ \cs{exp_end:} to keep track of when \meta{left} and
+% \meta{right} should be put into the \meta{next~tl}. The
+% \meta{assignment} is \cs{cs_set_nopar:Npx} if the variable is local,
+% and \cs{cs_gset_nopar:Npx} if it is global.
+%
+% \begin{macro}{\tl_build_begin:N, \tl_build_gbegin:N}
+% \begin{macro}{\@@_build_begin:NN, \@@_build_begin:NNN}
+% First construct the \meta{next~tl}: using a prime here conflicts
+% with the usual \pkg{expl3} convention but we need a name that can be
+% derived from |#1| without any external data such as a counter.
+% Empty that \meta{next~tl} and setup the structure. The local and
+% global versions only differ by a single function
+% \cs[no-index]{cs_(g)set_nopar:Npx} used for all assignments: this is
+% important because only that function is stored in the \meta{tl~var}
+% and \meta{next~tl} for subsequent assignments. In principle
+% \cs{@@_build_begin:NNN} could use \cs[no-index]{tl_(g)clear_new:N}
+% to empty |#1| and make sure it is defined, but logging the
+% definition does not seem useful so we just do |#3| |#1| |{}| to
+% clear it locally or globally as appropriate.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_build_begin:N #1
+ { \@@_build_begin:NN \cs_set_nopar:Npx #1 }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_build_gbegin:N #1
+ { \@@_build_begin:NN \cs_gset_nopar:Npx #1 }
+\cs_new_protected:Npn \@@_build_begin:NN #1#2
+ { \exp_args:Nc \@@_build_begin:NNN { \cs_to_str:N #2 ' } #2 #1 }
+\cs_new_protected:Npn \@@_build_begin:NNN #1#2#3
+ {
+ #3 #1 { }
+ #3 #2
+ {
+ \exp_not:n { \exp_end: \exp_end: \exp_end: \exp_end: }
+ \exp_not:n { \@@_build_last:NNn #3 #1 { } }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_clear:N, \tl_build_gclear:N}
+% The |begin| and |gbegin| functions already clear enough to make the
+% token list variable effectively empty. Eventually the |begin| and
+% |gbegin| functions should check that |#1'| is empty or undefined,
+% while the |clear| and |gclear| functions ought to empty |#1'|,
+% |#1''| and so on, similar to \cs{tl_build_end:N}. This only affects
+% memory usage.
+% \begin{macrocode}
+\cs_new_eq:NN \tl_build_clear:N \tl_build_begin:N
+\cs_new_eq:NN \tl_build_gclear:N \tl_build_gbegin:N
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_build_put_right:Nn, \tl_build_put_right:Nx,
+% \tl_build_gput_right:Nn, \tl_build_gput_right:Nx,
+% \@@_build_last:NNn, \@@_build_put:nn, \@@_build_put:nw
+% }
+% Similar to \cs{tl_put_right:Nn}, but apply \cs{exp:w} to |#1|. Most
+% of the time this just removes one \cs{exp_end:}. When there are
+% none left, \cs{@@_build_last:NNn} is expanded instead. It resets
+% the definition of the \meta{tl~var} by ending the \cs{exp_not:n} and
+% the definition early. Then it makes sure the \meta{next~tl} (its
+% argument |#1|) is set-up and starts a new definition. Then
+% \cs{@@_build_put:nn} and \cs{@@_build_put:nw} place the \meta{left}
+% part of the original \meta{tl~var} as appropriate for the definition
+% of the \meta{next~tl} (the \meta{right} part is left in the right
+% place without ever becoming a macro argument). We use
+% \cs{exp_after:wN} rather than some \cs{exp_args:No} to avoid reading
+% arguments that are likely very long token lists. We use
+% \cs[no-index]{cs_(g)set_nopar:Npx} rather than
+% \cs[no-index]{tl_(g)set:Nx} partly for the same reason and partly
+% because the assignments are interrupted by brace tricks, which
+% implies that the assignment does not simply set the token list to an
+% |x|-expansion of the second argument.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_build_put_right:Nn #1#2
+ {
+ \cs_set_nopar:Npx #1
+ { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 #2 } }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_build_put_right:Nx #1#2
+ {
+ \cs_set_nopar:Npx #1
+ { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 } #2 }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_build_gput_right:Nn #1#2
+ {
+ \cs_gset_nopar:Npx #1
+ { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 #2 } }
+ }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_build_gput_right:Nx #1#2
+ {
+ \cs_gset_nopar:Npx #1
+ { \exp_after:wN \exp_not:n \exp_after:wN { \exp:w #1 } #2 }
+ }
+\cs_new_protected:Npn \@@_build_last:NNn #1#2
+ {
+ \if_false: { { \fi:
+ \exp_end: \exp_end: \exp_end: \exp_end: \exp_end:
+ \@@_build_last:NNn #1 #2 { }
+ }
+ }
+ \if_meaning:w \c_empty_tl #2
+ \@@_build_begin:NN #1 #2
+ \fi:
+ #1 #2
+ {
+ \exp_after:wN \exp_not:n \exp_after:wN
+ {
+ \exp:w \if_false: } } \fi:
+ \exp_after:wN \@@_build_put:nn \exp_after:wN {#2}
+ }
+\cs_new_protected:Npn \@@_build_put:nn #1#2 { \@@_build_put:nw {#2} #1 }
+\cs_new_protected:Npn \@@_build_put:nw #1#2 \@@_build_last:NNn #3#4#5
+ { #2 \@@_build_last:NNn #3 #4 { #1 #5 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \tl_build_put_left:Nn, \tl_build_put_left:Nx,
+% \tl_build_gput_left:Nn, \tl_build_gput_left:Nx, \@@_build_put_left:NNn
+% }
+% See \cs{tl_build_put_right:Nn} for all the machinery. We could
+% easily provide \cs[no-index]{tl_build_put_left_right:Nnn}, by just
+% add the \meta{right} material after the \Arg{left} in the
+% |x|-expanding assignment.
+% \begin{macrocode}
+\__kernel_patch:nnNNpn { \__kernel_chk_var_local:N #1 } { }
+\cs_new_protected:Npn \tl_build_put_left:Nn #1
+ { \@@_build_put_left:NNn \cs_set_nopar:Npx #1 }
+\cs_generate_variant:Nn \tl_build_put_left:Nn { Nx }
+\__kernel_patch:nnNNpn { \__kernel_chk_var_global:N #1 } { }
+\cs_new_protected:Npn \tl_build_gput_left:Nn #1
+ { \@@_build_put_left:NNn \cs_gset_nopar:Npx #1 }
+\cs_generate_variant:Nn \tl_build_gput_left:Nn { Nx }
+\cs_new_protected:Npn \@@_build_put_left:NNn #1#2#3
+ {
+ #1 #2
+ {
+ \exp_after:wN \exp_not:n \exp_after:wN
+ {
+ \exp:w \exp_after:wN \@@_build_put:nn
+ \exp_after:wN {#2} {#3}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_get:NN}
+% \begin{macro}{\@@_build_get:NNN, \@@_build_get:w, \@@_build_get_end:w}
+% The idea is to expand the \meta{tl~var} then the \meta{next~tl} and
+% so on, all within an |x|-expanding assignment, and wrap as
+% appropriate in \cs{exp_not:n}. The various \meta{left} parts are
+% left in the assignment as we go, which enables us to expand the
+% \meta{next~tl} at the right place. The various \meta{right} parts
+% are eventually picked up in one last \cs{exp_not:n}, with a brace
+% trick to wrap all the \meta{right} parts together.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_get:NN
+ { \@@_build_get:NNN \tl_set:Nx }
+\cs_new_protected:Npn \@@_build_get:NNN #1#2#3
+ { #1 #3 { \if_false: { \fi: \exp_after:wN \@@_build_get:w #2 } } }
+\cs_new:Npn \@@_build_get:w #1 \@@_build_last:NNn #2#3#4
+ {
+ \exp_not:n {#4}
+ \if_meaning:w \c_empty_tl #3
+ \exp_after:wN \@@_build_get_end:w
+ \fi:
+ \exp_after:wN \@@_build_get:w #3
+ }
+\cs_new:Npn \@@_build_get_end:w #1#2#3
+ { \exp_after:wN \exp_not:n \exp_after:wN { \if_false: } \fi: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_build_end:N, \tl_build_gend:N, \@@_build_end_loop:NN}
+% Get the data then clear the \meta{next~tl} recursively until finding
+% an empty one. It is perhaps wasteful to repeatedly use
+% \cs{cs_to_sr:N}. The local/global scope is checked by
+% \cs{tl_set:Nx} or \cs{tl_gset:Nx}.
+% \begin{macrocode}
+\cs_new_protected:Npn \tl_build_end:N #1
+ {
+ \@@_build_get:NNN \tl_set:Nx #1 #1
+ \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } \tl_clear:N
+ }
+\cs_new_protected:Npn \tl_build_gend:N #1
+ {
+ \@@_build_get:NNN \tl_gset:Nx #1 #1
+ \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } \tl_gclear:N
+ }
+\cs_new_protected:Npn \@@_build_end_loop:NN #1#2
+ {
+ \if_meaning:w \c_empty_tl #1
+ \exp_after:wN \use_none:nnnnnn
+ \fi:
+ #2 #1
+ \exp_args:Nc \@@_build_end_loop:NN { \cs_to_str:N #1 ' } #2
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsubsection{Other additions to \pkg{l3tl}}
+%
+% \begin{macro}{\tl_range_braced:Nnn, \tl_range_braced:cnn, \tl_range_braced:nnn}
+% \begin{macro}
+% {\tl_range_unbraced:Nnn, \tl_range_unbraced:cnn, \tl_range_unbraced:nnn}
+% \begin{macro}
+% {
+% \@@_range_braced:w, \@@_range_collect_braced:w,
+% \@@_range_unbraced:w, \@@_range_collect_unbraced:w,
+% }
+% For the braced version \cs{@@_range_braced:w} sets up
+% \cs{@@_range_collect_braced:w} which stores items one by one in an
+% argument after the semicolon. The unbraced version is almost
+% identical. The version preserving braces and spaces starts by
+% deleting spaces before the argument to avoid collecting them, and
+% sets up \cs{@@_range_collect:nn} with a first argument of the form
+% |{| \Arg{collected} \meta{tokens} |}|, whose head is the collected
+% tokens and whose tail is what remains of the original token list.
+% This form makes it easier to move tokens to the \meta{collected}
+% tokens.
+% \begin{macrocode}
+\cs_new:Npn \tl_range_braced:Nnn { \exp_args:No \tl_range_braced:nnn }
+\cs_generate_variant:Nn \tl_range_braced:Nnn { c }
+\cs_new:Npn \tl_range_braced:nnn { \@@_range:Nnnn \@@_range_braced:w }
+\cs_new:Npn \tl_range_unbraced:Nnn
+ { \exp_args:No \tl_range_unbraced:nnn }
+\cs_generate_variant:Nn \tl_range_unbraced:Nnn { c }
+\cs_new:Npn \tl_range_unbraced:nnn
+ { \@@_range:Nnnn \@@_range_unbraced:w }
+\cs_new:Npn \@@_range_braced:w #1 ; #2
+ { \@@_range_collect_braced:w #1 ; { } #2 }
+\cs_new:Npn \@@_range_unbraced:w #1 ; #2
+ { \@@_range_collect_unbraced:w #1 ; { } #2 }
+\cs_new:Npn \@@_range_collect_braced:w #1 ; #2#3
+ {
+ \if_int_compare:w #1 > 1 \exp_stop_f:
+ \exp_after:wN \@@_range_collect_braced:w
+ \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ;
+ \fi:
+ { #2 {#3} }
+ }
+\cs_new:Npn \@@_range_collect_unbraced:w #1 ; #2#3
+ {
+ \if_int_compare:w #1 > 1 \exp_stop_f:
+ \exp_after:wN \@@_range_collect_unbraced:w
+ \int_value:w \int_eval:n { #1 - 1 } \exp_after:wN ;
+ \fi:
+ { #2 #3 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3token}}
+%
+% \begin{variable}{\c_catcode_active_space_tl}
+% While \cs{char_generate:nn} can produce active characters in some
+% engines it cannot in general. It would be possible to simply change
+% the catcode of space but then the code would need to avoid all
+% spaces, making it quite unreadable. Instead we use the primitive
+% \cs{tex_lowercase:D} trick.
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:N *
+ \char_set_lccode:nn { `* } { `\ }
+ \tex_lowercase:D { \tl_const:Nn \c_catcode_active_space_tl { * } }
+\group_end:
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macrocode}
+%<@@=peek>
+% \end{macrocode}
+%
+% \begin{variable}{\l_@@_collect_tl}
+% \begin{macrocode}
+\tl_new:N \l_@@_collect_tl
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}
+% {
+% \peek_catcode_collect_inline:Nn,
+% \peek_charcode_collect_inline:Nn,
+% \peek_meaning_collect_inline:Nn
+% }
+% \begin{macro}
+% {
+% \@@_collect:NNn, \@@_collect_true:w,
+% \@@_collect_remove:nw, \@@_collect:N
+% }
+% Most of the work is done by \cs{@@_execute_branches_\ldots{}:},
+% which calls either \cs{@@_true:w} or \cs{@@_false:w} according to
+% whether the next token \cs{l_peek_token} matches the search token
+% (stored in \cs{l_@@_search_token} and \cs{l_@@_search_tl}).
+% Here, in the \texttt{true} case we run \cs{@@_collect_true:w},
+% which generally calls \cs{@@_collect:N} to store the peeked token
+% into \cs{l_@@_collect_tl}, except in special non-\texttt{N}-type
+% cases (begin-group, end-group, or space), where a frozen token is
+% stored. The \texttt{true} branch calls
+% \cs{@@_execute_branches_\ldots{}:} to fetch more matching tokens.
+% Once there are no more, \cs{@@_false_aux:n} closes the safe-align
+% group and runs the user's inline code.
+% \begin{macrocode}
+\cs_new_protected:Npn \peek_catcode_collect_inline:Nn
+ { \@@_collect:NNn \@@_execute_branches_catcode: }
+\cs_new_protected:Npn \peek_charcode_collect_inline:Nn
+ { \@@_collect:NNn \@@_execute_branches_charcode: }
+\cs_new_protected:Npn \peek_meaning_collect_inline:Nn
+ { \@@_collect:NNn \@@_execute_branches_meaning: }
+\cs_new_protected:Npn \@@_collect:NNn #1#2#3
+ {
+ \group_align_safe_begin:
+ \cs_set_eq:NN \l_@@_search_token #2
+ \tl_set:Nn \l_@@_search_tl {#2}
+ \tl_clear:N \l_@@_collect_tl
+ \cs_set:Npn \@@_false:w
+ { \exp_args:No \@@_false_aux:n \l_@@_collect_tl }
+ \cs_set:Npn \@@_false_aux:n ##1
+ {
+ \group_align_safe_end:
+ #3
+ }
+ \cs_set_eq:NN \@@_true:w \@@_collect_true:w
+ \cs_set:Npn \@@_true_aux:w { \peek_after:Nw #1 }
+ \@@_true_aux:w
+ }
+\cs_new_protected:Npn \@@_collect_true:w
+ {
+ \if_case:w
+ \if_catcode:w \exp_not:N \l_peek_token { 1 \exp_stop_f: \fi:
+ \if_catcode:w \exp_not:N \l_peek_token } 2 \exp_stop_f: \fi:
+ \if_meaning:w \l_peek_token \c_space_token 3 \exp_stop_f: \fi:
+ 0 \exp_stop_f:
+ \exp_after:wN \@@_collect:N
+ \or: \@@_collect_remove:nw { \c_group_begin_token }
+ \or: \@@_collect_remove:nw { \c_group_end_token }
+ \or: \@@_collect_remove:nw { ~ }
+ \fi:
+ }
+\cs_new_protected:Npn \@@_collect:N #1
+ {
+ \tl_put_right:Nn \l_@@_collect_tl {#1}
+ \@@_true_aux:w
+ }
+\cs_new_protected:Npn \@@_collect_remove:nw #1
+ {
+ \tl_put_right:Nn \l_@@_collect_tl {#1}
+ \exp_after:wN \@@_true_remove:w
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex