% \iffalse meta-comment
%
%% File: l3str.dtx Copyright (C) 2011-2014 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
%%
%%    http://www.latex-project.org/lppl.txt
%%
%% This file is part of the "l3experimental bundle" (The Work in LPPL)
%% and all files in that bundle must be distributed together.
%%
%% The released version of this bundle is available from CTAN.
%%
%% -----------------------------------------------------------------------
%%
%% The development version of the bundle can be found at
%%
%%    http://www.latex-project.org/svnroot/experimental/trunk/
%%
%% for those people who are interested.
%%
%%%%%%%%%%%
%% NOTE: %%
%%%%%%%%%%%
%%
%%   Snapshots taken from the repository represent work in progress and may
%%   not work or may contain conflicting material!  We therefore ask
%%   people _not_ to put them into distributions, archives, etc. without
%%   prior consultation with the LaTeX3 Project.
%%
%% -----------------------------------------------------------------------
%
%<*driver|package>
% The version of expl3 required is tested as early as possible, as
% some really old versions do not define \ProvidesExplPackage.
\RequirePackage{expl3}[2014/11/25]
%<package>\@ifpackagelater{expl3}{2014/11/25}
%<package>  {}
%<package>  {%
%<package>    \PackageError{l3str}{Support package l3kernel too old}
%<package>      {%
%<package>        Please install an up to date version of l3kernel\MessageBreak
%<package>        using your TeX package manager or from CTAN.\MessageBreak
%<package>        \MessageBreak
%<package>        Loading l3str will abort!%
%<package>      }%
%<package>    \endinput
%<package>  }
\GetIdInfo$Id: l3str-expl.dtx 5471 2014-11-25 20:11:29Z joseph $
  {L3 Experimental strings}
\def\ExplFileName{l3str}
%</driver|package>
%<*driver>
\documentclass[full]{l3doc}
\usepackage{amsmath}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
%
% \title{^^A
%   The \textsf{l3str} package: manipulating strings of characters^^A
%   \thanks{This file describes v\ExplFileVersion,
%     last revised \ExplFileDate.}^^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 \ExplFileDate}
%
% \maketitle
%
% \tableofcontents
%
% \begin{documentation}
%
% \LaTeX3 provides a set of functions to manipulate token lists
% as strings of characters, ignoring the category codes of those
% characters.
%
% String variables are simply specialised token lists, but by convention
% should be named with the suffix \texttt{\ldots{}str}.  Such variables
% should contain characters with category code $12$ (other), except
% spaces, which have category code $10$ (blank space).  All the
% functions in this module which accept a token list argument first
% convert it to a string using \cs{tl_to_str:n} for internal processing,
% and will not treat a token list or the corresponding string
% representation differently.
%
% Most expandable functions in this module come in three flavours:
% \begin{itemize}
%   \item \cs{str_...:N}, which expect a token list or string
%     variable as their argument;
%   \item \cs{str_...:n}, taking any token list (or string) as an
%     argument;
%   \item \cs{str_..._ignore_spaces:n}, which ignores any space
%     encountered during the operation: these functions are typically
%     faster than those which take care of escaping spaces
%     appropriately.
% \end{itemize}
%
% \section{Building strings}
%
% \begin{variable}
%   {
%     \c_backslash_str,
%     \c_left_brace_str,
%     \c_right_brace_str,
%     \c_hash_str,
%     \c_tilde_str,
%     \c_percent_str
%   }
%   Constant strings, containing a single character token, with category
%   code $12$.  Any character can be accessed as \cs{iow_char:N}
%   |\|\meta{character}.
% \end{variable}
%
% \begin{function}{\str_new:N, \str_new:c}
%   \begin{syntax}
%     \cs{str_new:N} \meta{str~var}
%   \end{syntax}
%   Creates a new \meta{str~var} or raises an error if the name is
%   already taken.  The declaration is global.  The \meta{str~var} will
%   initially be empty.
% \end{function}
%
% \begin{function}{\str_const:Nn, \str_const:Nx, \str_const:cn, \str_const:cx}
%   \begin{syntax}
%     \cs{str_const:Nn} \meta{str~var} \Arg{token list}
%   \end{syntax}
%   Creates a new constant \meta{str~var} or raises an error if the name
%   is already taken.  The value of the \meta{str~var} will be set
%   globally to the \meta{token list}, converted to a string.
% \end{function}
%
% \begin{function}
%   {
%     \str_set:Nn,  \str_set:Nx,  \str_set:cn,  \str_set:cx,
%     \str_gset:Nn, \str_gset:Nx, \str_gset:cn, \str_gset:cx
%   }
%   \begin{syntax}
%     \cs{str_set:Nn} \meta{str var} \Arg{token list}
%   \end{syntax}
%   Converts the \meta{token list} to a \meta{string}, and stores the
%   result in \meta{str var}.
% \end{function}
%
% \begin{function}
%   {
%     \str_put_left:Nn,  \str_put_left:Nx,
%     \str_put_left:cn,  \str_put_left:cx,
%     \str_gput_left:Nn, \str_gput_left:Nx,
%     \str_gput_left:cn, \str_gput_left:cx
%   }
%   \begin{syntax}
%     \cs{str_put_left:Nn} \meta{str var} \Arg{token list}
%   \end{syntax}
%   Converts the \meta{token list} to a \meta{string}, and prepends the
%   result to \meta{str var}.  The current contents of the \meta{str
%     var} are not automatically converted to a string.
% \end{function}
%
% \begin{function}
%   {
%     \str_put_right:Nn,  \str_put_right:Nx,
%     \str_put_right:cn,  \str_put_right:cx,
%     \str_gput_right:Nn, \str_gput_right:Nx,
%     \str_gput_right:cn, \str_gput_right:cx
%   }
%   \begin{syntax}
%     \cs{str_put_right:Nn} \meta{str var} \Arg{token list}
%   \end{syntax}
%   Converts the \meta{token list} to a \meta{string}, and appends the
%   result to \meta{str var}.  The current contents of the \meta{str
%     var} are not automatically converted to a string.
% \end{function}
%
% \section{Accessing the contents of a string}
%
% \begin{function}[EXP]
%   {\str_count:N, \str_count:n, \str_count_ignore_spaces:n}
%   \begin{syntax}
%     \cs{str_count:n} \Arg{token list}
%   \end{syntax}
%   Leaves in the input stream the number of characters in the string
%   representation of \meta{token list}, as an integer denotation.  The
%   functions differ in their treatment of spaces.  In the case of
%   \cs{str_count:N} and \cs{str_count:n}, all characters including
%   spaces are counted.  The \cs{str_count_ignore_spaces:n} function
%   leaves the number of non-space characters in the input stream.
% \end{function}
%
% \begin{function}[EXP]{\str_count_spaces:N, \str_count_spaces:n}
%   \begin{syntax}
%     \cs{str_count_spaces:n} \Arg{token list}
%   \end{syntax}
%   Leaves in the input stream the number of space characters in the
%   string representation of \meta{token list}, as an integer
%   denotation. Of course, this function has no \texttt{_ignore_spaces}
%   variant.
% \end{function}
%
% \begin{function}[EXP]{\str_head:N, \str_head:n, \str_head_ignore_spaces:n}
%   \begin{syntax}
%     \cs{str_head:n} \Arg{token list}
%   \end{syntax}
%   Converts the \meta{token list} into a \meta{string}.  The first
%   character in the \meta{string} is then left in the input stream,
%   with category code \enquote{other}.  The functions differ if the
%   first character is a space: \cs{str_head:N} and \cs{str_head:n}
%   return a space token with category code~$10$ (blank space), while
%   the \cs{str_head_ignore_spaces:n} function ignores this space
%   character and leaves the first non-space character in the input
%   stream.  If the \meta{string} is empty (or only contains spaces in
%   the case of the \texttt{_ignore_spaces} function), then nothing is
%   left on the input stream.
% \end{function}
%
% \begin{function}[EXP]{\str_tail:N, \str_tail:n, \str_tail_ignore_spaces:n}
%   \begin{syntax}
%     \cs{str_tail:n} \Arg{token list}
%   \end{syntax}
%   Converts the \meta{token list} to a \meta{string}, removes the first
%   character, and leaves the remaining characters (if any) in the input
%   stream, with category codes $12$ and $10$ (for spaces).  The
%   functions differ in the case where the first character is a space:
%   \cs{str_tail:N} and \cs{str_tail:n} will trim only that space, while
%   \cs{str_tail_ignore_spaces:n} removes the first non-space character
%   and any space before it.  If the \meta{token list} is empty (or
%   blank in the case of the \texttt{_ignore_spaces} variant), then
%   nothing is left on the input stream.
% \end{function}
%
% \begin{function}[EXP]{\str_item:Nn, \str_item:nn, \str_item_ignore_spaces:nn}
%   \begin{syntax}
%     \cs{str_item:nn} \Arg{token list} \Arg{integer expression}
%   \end{syntax}
%   Converts the \meta{token list} to a \meta{string}, and leaves in the
%   input stream the character in position \meta{integer expression} of
%   the \meta{string}, starting at $1$ for the first (left-most)
%   character.  In the case of \cs{str_item:Nn} and \cs{str_item:nn},
%   all characters including spaces are taken into account.  The
%   \cs{str_item_ignore_spaces:nn} function skips spaces when counting
%   characters.  If the \meta{integer expression} is negative,
%   characters are counted from the end of the \meta{string}. Hence,
%   $-1$ is the right-most character, \emph{etc.}
% \end{function}
%
% \begin{function}[EXP]
%   {\str_range:Nnn, \str_range:nnn, \str_range_ignore_spaces:nnn}
%   \begin{syntax}
%     \cs{str_range:nnn} \Arg{token list} \Arg{start index} \Arg{end index}
%   \end{syntax}
%   Converts the \meta{token list} to a \meta{string}, and leaves in the
%   input stream the characters from the \meta{start index} to the
%   \meta{end index} inclusive.  Positive \meta{indices} are counted
%   from the start of the string, $1$~being the first character, and
%   negative \meta{indices} are counted from the end of the string,
%   $-1$~being the last character.  If either of \meta{start index} or
%   \meta{end index} is~$0$, the result is empty.  For instance,
%   \begin{verbatim}
%     \iow_term:x { \str_range:nnn { abcdef } { 2 } { 5 } }
%     \iow_term:x { \str_range:nnn { abcdef } { -4 } { -1 } }
%     \iow_term:x { \str_range:nnn { abcdef } { -2 } { -1 } }
%     \iow_term:x { \str_range:nnn { abcdef } { 0 } { -1 } }
%   \end{verbatim}
%   will print \texttt{bcd}, \texttt{cdef}, \texttt{ef}, and an empty
%   line to the terminal.
% \end{function}
%
% \section{Viewing strings}
%
% \begin{function}{\str_show:N, \str_show:c, \str_show:n}
%   \begin{syntax}
%     \cs{str_show:N} \meta{tl~var}
%   \end{syntax}
%   Displays the content of the \meta{str~var} on the terminal.
% \end{function}
%
% \section{Scratch strings}
%
% \begin{variable}{\l_tmpa_str, \l_tmpb_str}
%   Scratch strings for local assignment. These are never used by
%   the kernel code, and so are safe for use with any \LaTeX3-defined
%   function. However, they may be overwritten by other non-kernel
%   code and so should only be used for short-term storage.
% \end{variable}
%
% \begin{variable}{\g_tmpa_str, \g_tmpb_str}
%   Scratch strings for global assignment. These are never used by
%   the kernel code, and so are safe for use with any \LaTeX3-defined
%   function. However, they may be overwritten by other non-kernel
%   code and so should only be used for short-term storage.
% \end{variable}
%
% \section{Internal \pkg{l3str} functions}
%
% \begin{function}[EXP]{\__str_to_other:n}
%   \begin{syntax}
%     \cs{__str_to_other:n} \Arg{token list}
%   \end{syntax}
%   Converts the \meta{token list} to a \meta{other string}, where
%   spaces have category code \enquote{other}.  This function can be
%   \texttt{f}-expanded without fear of losing a leading space, since
%   spaces do not have category code $10$ in its result.  It takes a
%   time quadratic in the character count of the string, but there exist
%   non-expandable ways to reach linear time.
% \end{function}
%
% \begin{function}[EXP]{\__str_count_unsafe:n}
%   \begin{syntax}
%     \cs{__str_count_unsafe:n} \Arg{other string}
%   \end{syntax}
%   This function expects an argument that is entirely made of
%   characters with category \enquote{other}, as produced by
%   \cs{__str_to_other:n}.  It leaves in the input stream the number of
%   character tokens in the \meta{other string}, faster than the
%   analogous \cs{str_count:n} function.
% \end{function}
%
% \begin{function}[EXP]{\__str_range_unsafe:nnn}
%   \begin{syntax}
%     \cs{__str_range_unsafe:nnn} \Arg{other string} \Arg{start index} \Arg{end index}
%   \end{syntax}
%   Identical to \cs{str_range:nnn} except that the first argument is
%   expected to be entirely made of characters with category
%   \enquote{other}, as produced by \cs{__str_to_other:n}, and the
%   result is also an \meta{other string}.
% \end{function}
%
% \section{Possible additions to \pkg{l3str}}
%
% Semantically correct copies of some \texttt{tl} functions.
% \begin{itemize}
%   \item \cs{c_space_str}
%   \item \cs{str_clear:N}, \cs{str_gclear:N}, \cs{str_clear_new:N},
%     \cs{str_gclear_new:N}.
%   \item \cs{str_concat:NNN}, \cs{str_gconcat:NNN}
%   \item \cs{str_set_eq:NN}, \cs{str_gset_eq:NN}
%   \item \cs{str_if_empty:NTF}, \cs{str_if_empty_p:N}
%   \item \cs{str_if_exist:NTF}, \cs{str_if_exist_p:N}
%   \item \cs{str_use:N}
% \end{itemize}
%
% Some functions that are not copies of \texttt{tl} functions.
% \begin{itemize}
%   \item \cs{str_if_blank:NTF}, \cs{str_if_blank_p:N}.
%   \item \cs{str_map_inline:Nn}, \cs{str_map_function:NN},
%     \cs{str_map_variable:NNn}, and \texttt{:n} analogs.
%   \item Expandable \cs{str_if_in:nnTF}?
%   \item \cs{str_if_head_eq:nNTF}, \cs{str_if_head_eq_p:nN}
%   \item \cs{str_if_numeric/decimal/integer:n}, perhaps in \pkg{l3fp}?
% \end{itemize}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3str} implementation}
%
%    \begin{macrocode}
%<*initex|package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=str>
%    \end{macrocode}
%
%    \begin{macrocode}
\ProvidesExplPackage
  {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
%    \end{macrocode}
%
% The following string-related functions are currently defined in
% \pkg{l3kernel}.
% \begin{itemize}
%   \item \cs{str_if_eq:nn}[pTF] and variants,
%   \item \cs{str_if_eq_x_return:on}, \cs{str_if_eq_x_return:nn}
%   \item \cs{tl_to_str:n}, \cs{tl_to_str:N}, \cs{tl_to_str:c},
%   \item \cs{token_to_str:N}, \cs{cs_to_str:N}
%   \item \cs{str_head:n}, \cs{__str_head:w}, (copied here)
%   \item \cs{str_tail:n}, \cs{__str_tail:w}, (copied here)
%   \item \cs{__str_count_ignore_spaces} (unchanged)
%   \item \cs{__str_count_loop:NNNNNNNNN} (unchanged)
% \end{itemize}
%
% \subsection{String assignments}
%
% \begin{macro}{\str_new:N, \str_new:c}
%   A string is simply a token list.
%    \begin{macrocode}
\cs_new_eq:NN \str_new:N \tl_new:N
\cs_generate_variant:Nn \str_new:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \str_set:Nn, \str_set:Nx,
%     \str_set:cn, \str_set:cx,
%     \str_gset:Nn, \str_gset:Nx,
%     \str_gset:cn, \str_gset:cx,
%     \str_const:Nn, \str_const:Nx,
%     \str_const:cn, \str_const:cx,
%     \str_put_left:Nn, \str_put_left:Nx,
%     \str_put_left:cn, \str_put_left:cx,
%     \str_gput_left:Nn, \str_gput_left:Nx,
%     \str_gput_left:cn, \str_gput_left:cx,
%     \str_put_right:Nn, \str_put_right:Nx,
%     \str_put_right:cn, \str_put_right:cx,
%     \str_gput_right:Nn, \str_gput_right:Nx,
%     \str_gput_right:cn, \str_gput_right:cx,
%   }
%   Simply convert the token list inputs to \meta{strings}.
%    \begin{macrocode}
\tl_map_inline:nn
  {
    { set }
    { gset }
    { const }
    { put_left }
    { gput_left }
    { put_right }
    { gput_right }
  }
  {
    \cs_new_protected:cpx { str_ #1 :Nn } ##1##2
      { \exp_not:c { tl_ #1 :Nx } ##1 { \exp_not:N \tl_to_str:n {##2} } }
    \exp_args:Nc \cs_generate_variant:Nn { str_ #1 :Nn } { Nx , cn , cx }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{String variables and constants}
%
% \begin{variable}
%   {
%     \c_backslash_str,
%     \c_left_brace_str,
%     \c_right_brace_str,
%     \c_hash_str,
%     \c_tilde_str,
%     \c_percent_str
%   }
%   For all of those strings, use \cs{cs_to_str:N} to get characters with
%   the correct category code.
%    \begin{macrocode}
\str_const:Nx \c_backslash_str { \cs_to_str:N \\ }
\str_const:Nx \c_left_brace_str  { \cs_to_str:N \{ }
\str_const:Nx \c_right_brace_str { \cs_to_str:N \} }
\str_const:Nx \c_hash_str      { \cs_to_str:N \# }
\str_const:Nx \c_tilde_str     { \cs_to_str:N \~ }
\str_const:Nx \c_percent_str   { \cs_to_str:N \% }
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_tmpa_str, \l_tmpb_str, \g_tmpa_str, \g_tmpb_str}
%   Scratch strings.
%    \begin{macrocode}
\str_new:N \l_tmpa_str
\str_new:N \l_tmpb_str
\str_new:N \g_tmpa_str
\str_new:N \g_tmpb_str
%    \end{macrocode}
% \end{variable}
%
% \subsection{Counting characters}
%
% \begin{macro}[EXP]{\str_count_spaces:N, \str_count_spaces:n}
% \begin{macro}[EXP, aux]{\@@_count_spaces_loop:wwwwwwwww}
%   To speed up this function, we grab and discard $9$ space-delimited
%   arguments in each iteration of the loop.  The loop stops when the
%   last argument is one of the trailing |X|\meta{number}, and that
%   \meta{number} is added to the sum of $9$ that precedes, to adjust
%   the result.
%    \begin{macrocode}
\cs_new_nopar:Npn \str_count_spaces:N
  { \exp_args:No \str_count_spaces:n }
\cs_new:Npn \str_count_spaces:n #1
  {
    \int_eval:n
      {
        \exp_after:wN \@@_count_spaces_loop:wwwwwwwww
        \tl_to_str:n {#1} ~
        X 7 ~ X 6 ~ X 5 ~ X 4 ~ X 3 ~ X 2 ~ X 1 ~ X 0 ~ X -1 ~
        \q_stop
      }
  }
\cs_new:Npn \@@_count_spaces_loop:wwwwwwwww #1~#2~#3~#4~#5~#6~#7~#8~#9~
  {
    \if_meaning:w X #9
      \use_i_delimit_by_q_stop:nw
    \fi:
    \c_nine + \@@_count_spaces_loop:wwwwwwwww
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\str_count:N, \str_count:n, \str_count_ignore_spaces:n}
% \begin{macro}[EXP, int]{\@@_count_unsafe:n}
% \begin{macro}[EXP, aux]{\@@_count:n, \@@_count_loop:NNNNNNNNN}
%   To count characters in a string we could first escape all spaces
%   using \cs{@@_to_other:n}, then pass the result to \cs{tl_count:n}.
%   However, the escaping step would be quadratic in the number of
%   characters in the string, and we can do better.  Namely, sum the
%   number of spaces (\cs{str_count_spaces:n}) and the result of
%   \cs{tl_count:n}, which ignores spaces.  Since strings tend to be
%   longer than token lists, we use specialized functions to count
%   characters ignoring spaces.  Namely, loop, grabbing $9$ non-space
%   characters at each step, and end as soon as we reach one of the $9$
%   trailing items.  The \texttt{_unsafe} variant expects a token list
%   already converted to category code $12$ characters, and is used by
%   \cs{str_item:nn} and \cs{str_range:nnn}.
%    \begin{macrocode}
\cs_new_nopar:Npn \str_count:N { \exp_args:No \str_count:n }
\cs_new:Npn \str_count:n #1
  {
    \@@_count:n
      {
        \str_count_spaces:n {#1}
        + \exp_after:wN \@@_count_loop:NNNNNNNNN \tl_to_str:n {#1}
      }
  }
\cs_new:Npn \@@_count_unsafe:n #1
  {
    \@@_count:n
      { \@@_count_loop:NNNNNNNNN #1 }
  }
\cs_new:Npn \str_count_ignore_spaces:n #1
  {
    \@@_count:n
      { \exp_after:wN \@@_count_loop:NNNNNNNNN \tl_to_str:n {#1} }
  }
\cs_new:Npn \@@_count:n #1
  {
    \int_eval:n
      {
        #1
        { X \c_eight } { X \c_seven } { X \c_six   }
        { X \c_five  } { X \c_four  } { X \c_three }
        { X \c_two   } { X \c_one   } { X \c_zero  }
        \q_stop
      }
  }
\cs_set:Npn \@@_count_loop:NNNNNNNNN #1#2#3#4#5#6#7#8#9
  {
    \if_meaning:w X #9
      \exp_after:wN \use_none_delimit_by_q_stop:w
    \fi:
    \c_nine + \@@_count_loop:NNNNNNNNN
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Head and tail of a string}
%
% \begin{macro}[EXP]{\str_head:N, \str_head:n, \str_head_ignore_spaces:n}
% \begin{macro}[EXP, aux]{\@@_head:w}
%   The \texttt{_ignore_spaces} variant is almost identical to
%   \cs{tl_head:n}.  As usual, \cs{str_head:N} expands its argument and
%   hands it to \cs{str_head:n}.  To circumvent the fact that \TeX{}
%   skips spaces when grabbing undelimited macro parameters,
%   \cs{@@_head:w} takes an argument delimited by a space. If |#1|
%   starts with a non-space character, \cs{use_i_delimit_by_q_stop:nw}
%   leaves that in the input stream. On the other hand, if |#1| starts
%   with a space, the \cs{@@_head:w} takes an empty argument, and the
%   single (initially braced) space in the definition of \cs{@@_head:w}
%   makes its way to the output. Finally, for an empty argument, the
%   (braced) empty brace group in the definition of \cs{str_head:n}
%   gives an empty result after passing through
%   \cs{use_i_delimit_by_q_stop:nw}.
%    \begin{macrocode}
\cs_new_nopar:Npn \str_head:N { \exp_args:No \str_head:n }
\cs_set:Npn \str_head:n #1
  {
    \exp_after:wN \@@_head:w
    \tl_to_str:n {#1}
    { { } } ~ \q_stop
  }
\cs_set:Npn \@@_head:w #1 ~ %
  { \use_i_delimit_by_q_stop:nw #1 { ~ } }
\cs_new:Npn \str_head_ignore_spaces:n #1
  {
    \exp_after:wN \use_i_delimit_by_q_stop:nw
    \tl_to_str:n {#1} { } \q_stop
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\str_tail:N, \str_tail:n, \str_tail_ignore_spaces:n}
% \begin{macro}[EXP, aux]{\@@_tail_auxi:w, \@@_tail_auxii:w}
%   Getting the tail is a little bit more convoluted than the head of a
%   string.  We hit the front of the string with \cs{reverse_if:N}
%   \cs{if_charcode:w} \cs{scan_stop:}.  This removes the first
%   character, and necessarily makes the test true, since the character
%   cannot match \cs{scan_stop:}. The auxiliary function then inserts
%   the required \cs{fi:} to close the conditional, and leaves the tail
%   of the string in the input stream.  The details are such that an
%   empty string has an empty tail (this requires in particular that the
%   end-marker |X| be unexpandable and not a control sequence).  The
%   \texttt{_ignore_spaces} is rather simpler: after converting the
%   input to a string, \cs{@@_tail_auxii:w} removes one undelimited
%   argument and leaves everything else until an end-marker \cs{q_mark}.
%   One can check that an empty (or blank) string yields an empty
%   tail.
%    \begin{macrocode}
\cs_new_nopar:Npn \str_tail:N { \exp_args:No \str_tail:n }
\cs_set:Npn \str_tail:n #1
  {
    \exp_after:wN \@@_tail_auxi:w
    \reverse_if:N \if_charcode:w
        \scan_stop: \tl_to_str:n {#1} X X \q_stop
  }
\cs_set:Npn \@@_tail_auxi:w #1 X #2 \q_stop { \fi: #1 }
\cs_new:Npn \str_tail_ignore_spaces:n #1
  {
    \exp_after:wN \@@_tail_auxii:w
    \tl_to_str:n {#1} \q_mark \q_mark \q_stop
  }
\cs_new:Npn \@@_tail_auxii:w #1 #2 \q_mark #3 \q_stop { #2 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Accessing specific characters in a string}
%
% \begin{macro}[EXP, int]{\@@_to_other:n}
% \begin{macro}[EXP, aux]{\@@_to_other_loop:w, \@@_to_other_end:w}
%   First apply \cs{tl_to_str:n}, then replace all spaces by
%   \enquote{other} spaces, $8$ at a time, storing the converted part of
%   the string between the \cs{q_mark} and \cs{q_stop} markers.  The end
%   is detected when \cs{@@_to_other_loop:w} finds one of the trailing
%   |A|, distinguished from any contents of the initial token list by
%   their category.  Then \cs{@@_to_other_end:w} is called, and finds
%   the result between \cs{q_mark} and the first |A| (well, there is
%   also the need to remove a space).
%    \begin{macrocode}
\group_begin:
\char_set_lccode:nn { `\* } { `\  }
\char_set_lccode:nn { `\A } { `\A }
\tl_to_lowercase:n
  {
    \group_end:
    \cs_new:Npn \@@_to_other:n #1
      {
        \exp_after:wN \@@_to_other_loop:w \tl_to_str:n {#1} ~ %
        A ~ A ~ A ~ A ~ A ~ A ~ A ~ A ~ \q_mark \q_stop
      }
    \cs_new:Npn \@@_to_other_loop:w
      #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 ~ #7 ~ #8 ~ #9 \q_stop
      {
        \if_meaning:w A #8
          \@@_to_other_end:w
        \fi:
        \@@_to_other_loop:w
        #9 #1 * #2 * #3 * #4 * #5 * #6 * #7 * #8 * \q_stop
      }
    \cs_new:Npn \@@_to_other_end:w \fi: #1 \q_mark #2 * A #3 \q_stop
      { \fi: #2 }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP, aux]{\@@_skip_c_zero:w}
% \begin{macro}[EXP, aux]
%   {\@@_skip_loop:wNNNNNNNN, \@@_skip_end:w, \@@_skip_end:NNNNNNNN}
%   Removes |max(#1,0)| characters from the input stream, and then
%   leaves \cs{c_zero}.  This should be expanded using
%   \cs{tex_romannumeral:D}.  We remove characters $8$ at a time until
%   there are at most $8$ to remove.  Then we do a dirty trick: the
%   \cs{if_case:w} construction leaves between $0$ and $8$ times the
%   \cs{or:} control sequence, and those \cs{or:} become arguments of
%   \cs{@@_skip_end:NNNNNNNN}.  If the number of characters to remove
%   is $6$, say, then there are two \cs{or:} left, and the $8$ arguments
%   of \cs{@@_skip_end:NNNNNNNN} are the two \cs{or:}, and $6$
%   characters from the input stream, exactly what we wanted to
%   remove. Then close the \cs{if_case:w} conditional with \cs{fi:}, and
%   stop the initial expansion with \cs{c_zero} (see places where
%   \cs{@@_skip_c_zero:w} is called).
%    \begin{macrocode}
\cs_new:Npn \@@_skip_c_zero:w #1;
  {
    \if_int_compare:w #1 > \c_eight
      \exp_after:wN \@@_skip_loop:wNNNNNNNN
    \else:
      \exp_after:wN \@@_skip_end:w
      \int_use:N \__int_eval:w
    \fi:
    #1 ;
  }
\cs_new:Npn \@@_skip_loop:wNNNNNNNN #1; #2#3#4#5#6#7#8#9
  { \exp_after:wN \@@_skip_c_zero:w \int_use:N \__int_eval:w #1 - \c_eight ; }
\cs_new:Npn \@@_skip_end:w #1 ;
  {
    \exp_after:wN \@@_skip_end:NNNNNNNN
    \if_case:w #1 \exp_stop_f: \or: \or: \or: \or: \or: \or: \or: \or:
  }
\cs_new:Npn \@@_skip_end:NNNNNNNN #1#2#3#4#5#6#7#8 { \fi: \c_zero }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP, aux]{\@@_collect_delimit_by_q_stop:w}
% \begin{macro}[EXP, aux]
%   {
%     \@@_collect_loop:wn, \@@_collect_loop:wnNNNNNNN,
%     \@@_collect_end:wn, \@@_collect_end:nnnnnnnnw
%   }
%   Collects |max(#1,0)| characters, and removes everything else until
%   \cs{q_stop}. This is somewhat similar to \cs{@@_skip_c_zero:w}, but
%   accepts integer expression arguments.  This time we can only grab
%   $7$ characters at a time.  At the end, we use an \cs{if_case:w}
%   trick again, so that the $8$ first arguments of
%   \cs{@@_collect_end:nnnnnnnnw} are some \cs{or:}, followed by an
%   \cs{fi:}, followed by |#1| characters from the input stream. Simply
%   leaving this in the input stream will close the conditional properly
%   and the \cs{or:} disappear.
%    \begin{macrocode}
\cs_new:Npn \@@_collect_delimit_by_q_stop:w #1;
  { \@@_collect_loop:wn #1 ; { } }
\cs_new:Npn \@@_collect_loop:wn #1 ;
  {
    \if_int_compare:w #1 > \c_seven
      \exp_after:wN \@@_collect_loop:wnNNNNNNN
    \else:
      \exp_after:wN \@@_collect_end:wn
    \fi:
    #1 ;
  }
\cs_new:Npn \@@_collect_loop:wnNNNNNNN #1; #2 #3#4#5#6#7#8#9
  {
    \exp_after:wN \@@_collect_loop:wn
    \int_use:N \__int_eval:w #1 - \c_seven ;
    { #2 #3#4#5#6#7#8#9 }
  }
\cs_new:Npn \@@_collect_end:wn #1 ;
  {
    \exp_after:wN \@@_collect_end:nnnnnnnnw
    \if_case:w \if_int_compare:w #1 > \c_zero #1 \else: 0 \fi: \exp_stop_f:
    \or: \or: \or: \or: \or: \or: \fi:
  }
\cs_new:Npn \@@_collect_end:nnnnnnnnw #1#2#3#4#5#6#7#8 #9 \q_stop
  { #1#2#3#4#5#6#7#8 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\str_item:Nn, \str_item:nn, \str_item_ignore_spaces:nn}
% \begin{macro}[EXP, aux]{\@@_item_unsafe:nn, \@@_item:ww}
%   The \cs{str_item:nn} hands its argument with spaces escaped to
%   \cs{@@_item_unsafe:nn}, and makes sure to turn the result back into
%   a proper string (with category code~$10$ spaces) eventually.  The
%   \cs{str_item_ignore_spaces:nn} function cheats a little bit in that
%   it doesn't hand to \cs{@@_item_unsafe:nn} an \enquote{other string}.
%   This is safe, as everything else is done with undelimited arguments.
%   Then evaluate the \meta{index} argument~|#2| and count characters in
%   the string, passing those two numbers to \cs{@@_item:ww} for further
%   analysis.  If the \meta{index} is negative, shift by |#2|, and
%   remove that number of characters before returning the next item in
%   the input stream (and if |#1| is smaller than $-|#2|$, nothing is
%   returned).  If the \meta{index} is positive, ignore that number
%   (minus one) of characters before returning the next one.  The shift
%   by one is obtained by inserting an empty brace group before the
%   string in that case: that brace group also covers the case where the
%   \meta{index} is zero.
%    \begin{macrocode}
\cs_new_nopar:Npn \str_item:Nn { \exp_args:No \str_item:nn }
\cs_new:Npn \str_item:nn #1#2
  {
    \exp_args:Nf \tl_to_str:n
      {
        \exp_args:Nf \@@_item_unsafe:nn
          { \@@_to_other:n {#1} } {#2}
      }
  }
\cs_new:Npn \str_item_ignore_spaces:nn #1
  { \exp_args:No \@@_item_unsafe:nn { \tl_to_str:n {#1} } }
\cs_new:Npn \@@_item_unsafe:nn #1#2
  {
    \exp_after:wN \@@_item:ww
    \int_use:N \__int_eval:w #2 \exp_after:wN ;
    \__int_value:w \@@_count_unsafe:n {#1} ;
    #1 \q_stop
  }
\cs_new:Npn \@@_item:ww #1; #2;
  {
    \int_compare:nNnTF {#1} < \c_zero
      {
        \int_compare:nNnTF {#1} < {-#2}
          { \use_none_delimit_by_q_stop:w }
          {
            \exp_after:wN \use_i_delimit_by_q_stop:nw
            \tex_romannumeral:D \exp_after:wN \@@_skip_c_zero:w
              \int_use:N \__int_eval:w #1 + #2 ;
          }
      }
      {
        \int_compare:nNnTF {#1} > {#2}
          { \use_none_delimit_by_q_stop:w }
          {
            \exp_after:wN \use_i_delimit_by_q_stop:nw
            \tex_romannumeral:D \@@_skip_c_zero:w #1 ; { }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP, aux]{\@@_range_normalize:nn}
%   This function converts an \meta{index} argument into an explicit
%   position in the string (a result of $0$ denoting \enquote{out of
%     bounds}).  Expects two explicit integer arguments: the
%   \meta{index} |#1| and the string count~|#2|.  If |#1| is negative,
%   replace it by $|#1| + |#2| + 1$, then limit to the range $[0,
%   |#2|]$.
%    \begin{macrocode}
\cs_new:Npn \@@_range_normalize:nn #1#2
  {
    \int_eval:n
      {
        \if_int_compare:w #1 < \c_zero
          \if_int_compare:w #1 < -#2 \exp_stop_f:
            \c_zero
          \else:
            #1 + #2 + \c_one
          \fi:
        \else:
          \if_int_compare:w #1 < #2 \exp_stop_f:
            #1
          \else:
            #2
          \fi:
        \fi:
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]
%   {\str_range:Nnn, \str_range:nnn, \str_range_ignore_spaces:nnn}
% \begin{macro}[EXP, int]{\@@_range_unsafe:nnn}
% \begin{macro}[EXP, aux]{\@@_range:www, \@@_range:nnw}
%   Sanitize the string.  Then evaluate the arguments.  At this stage we
%   also decrement the \meta{start index}, since our goal is to know how
%   many characters should be removed.  Then limit the range to be
%   non-negative and at most the length of the string (this avoids
%   needing to check for the end of the string when grabbing
%   characters), shifting negative numbers by the appropriate amount.
%   Afterwards, skip characters, then keep some more, and finally drop
%   the end of the string.
%    \begin{macrocode}
\cs_new_nopar:Npn \str_range:Nnn { \exp_args:No \str_range:nnn }
\cs_new:Npn \str_range:nnn #1#2#3
  {
    \exp_args:Nf \tl_to_str:n
      {
        \exp_args:Nf \@@_range_unsafe:nnn
          { \@@_to_other:n {#1} } {#2} {#3}
      }
  }
\cs_new:Npn \str_range_ignore_spaces:nnn #1
  { \exp_args:No \@@_range_unsafe:nnn { \tl_to_str:n {#1} } }
\cs_new:Npn \@@_range_unsafe:nnn #1#2#3
  {
    \exp_after:wN \@@_range:www
    \__int_value:w \@@_count_unsafe:n {#1} \exp_after:wN ;
    \int_use:N \__int_eval:w #2 - \c_one \exp_after:wN ;
    \int_use:N \__int_eval:w #3 ;
    #1 \q_stop
  }
\cs_new:Npn \@@_range:www #1; #2; #3;
  {
    \exp_args:Nf \@@_range:nnw
      { \@@_range_normalize:nn {#2} {#1} }
      { \@@_range_normalize:nn {#3} {#1} }
  }
\cs_new:Npn \@@_range:nnw #1#2
  {
    \exp_after:wN \@@_collect_delimit_by_q_stop:w
    \int_use:N \__int_eval:w #2 - #1 \exp_after:wN ;
    \tex_romannumeral:D \@@_skip_c_zero:w #1 ;
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{String conditionals}
%
% \begin{macro}[EXP, pTF]{\str_if_eq:NN}
% \begin{macro}[EXP, pTF]{\str_if_eq:nn, \str_if_eq_x:nn}
%   Note that \cs{str_if_eq:NN} is different from
%   \cs{tl_if_eq:NN} because it needs to ignore category codes.
%    \begin{macrocode}
\prg_new_conditional:Npnn \str_if_eq:NN #1#2 { p , TF , T , F }
  {
    \if_int_compare:w \__str_if_eq_x:nn { \tl_to_str:N #1 } { \tl_to_str:N #2 }
      = \c_zero \prg_return_true: \else: \prg_return_false: \fi:
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP, TF]{\str_case:nn, \str_case:on, \str_case_x:nn}
%   Defined in \pkg{l3basics} at present.
% \end{macro}
%
% \subsection{Viewing strings}
%
% \begin{macro}{\str_show:n, \str_show:N, \str_show:c}
%   Displays a string on the terminal.
%    \begin{macrocode}
\cs_new_eq:NN \str_show:n \tl_show:n
\cs_new_eq:NN \str_show:N \tl_show:N
\cs_generate_variant:Nn \str_show:N { c }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Deprecated string functions}
%
% Deprecated 2013-01-20 for removal by 2013-04-30
% \begin{macro}[EXP]
%   {\str_substr:Nnn, \str_substr:nnn, \str_substr_ignore_spaces:nnn}
% \begin{macro}[EXP, aux]{\@@_substr:nnn}
%   These functions used to allow for an empty argument to denote the
%   start/end of the string.  We reimplement them here by first checking
%   for an empty argument, then only calling the appropriate version of
%   the \cs{str_range:nnn} function.
%    \begin{macrocode}
\cs_new:Npn \str_substr:Nnn #1 { \@@_substr:nnn { \str_range:Nnn #1 } }
\cs_new:Npn \str_substr:nnn #1 { \@@_substr:nnn { \str_range:nnn {#1} } }
\cs_new:Npn \str_substr_ignore_spaces:nnn #1
  { \@@_substr:nnn { \str_range_ignore_spaces:nnn {#1} } }
\cs_new:Npn \@@_substr:nnn #1#2#3
  {
    \tl_if_empty:nTF {#2}
      { \tl_if_empty:nTF {#3} { #1 { 1} { -1 } } { #1 { 1} {#3} } }
      { \tl_if_empty:nTF {#3} { #1 {#2} { -1 } } { #1 {#2} {#3} } }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% Deprecated 2013-01-20 for removal by 2013-04-30
% \begin{variable}{\c_lbrace_str, \c_rbrace_str}
%    \begin{macrocode}
\cs_new_eq:NN \c_lbrace_str \c_left_brace_str
\cs_new_eq:NN \c_rbrace_str \c_right_brace_str
%    \end{macrocode}
% \end{variable}
%
%    \begin{macrocode}
%</initex|package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
