% \iffalse meta-comment
%
%% File: l3sys.dtx
%
% Copyright (C) 2015-2021 The LaTeX 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 \pkg{l3sys} package: System/runtime functions^^A
% }
%
% \author{^^A
%  The \LaTeX{} Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Released 2021-08-27}
%
% \maketitle
%
% \begin{documentation}
%
% \section{The name of the job}
%
% \begin{variable}[added = 2015-09-19, updated = 2019-10-27]{\c_sys_jobname_str}
%   Constant that gets the \enquote{job name} assigned when \TeX{} starts.
%   \begin{texnote}
%     This copies the contents of the primitive \tn{jobname}. For technical
%     reasons, the string here is not of the same internal form as other,
%     but may be manipulated using normal string functions.
%   \end{texnote}
% \end{variable}
%
% \section{Date and time}
%
% \begin{variable}[added = 2015-09-22]
%   {
%     \c_sys_minute_int,
%     \c_sys_hour_int,
%     \c_sys_day_int,
%     \c_sys_month_int,
%     \c_sys_year_int,
%   }
%   The date and time at which the current job was started: these are
%   all reported as integers.
%   \begin{texnote}
%     Whilst the underlying primitives can be altered by the user, this
%     interface to the time and date is intended to be the \enquote{real}
%     values.
%   \end{texnote}
% \end{variable}
%
% \section{Engine}
%
% \begin{function}[added = 2015-09-07, EXP, pTF]
%   {
%     \sys_if_engine_luatex:,
%     \sys_if_engine_pdftex:,
%     \sys_if_engine_ptex:  ,
%     \sys_if_engine_uptex: ,
%     \sys_if_engine_xetex:
%   }
%   \begin{syntax}
%     \cs{sys_if_engine_pdftex:TF} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Conditionals which allow engine-specific code to be used. The names
%   follow naturally from those of the engine binaries: note that the
%   |(u)ptex| tests are for \epTeX{} and \eupTeX{} as \pkg{expl3} requires
%   the \eTeX{} extensions. Each conditional is true for
%   \emph{exactly one} supported engine. In particular,
%   |\sys_if_engine_ptex_p:| is true for \epTeX{} but false for \eupTeX{}.
% \end{function}
%
% \begin{variable}[added = 2015-09-19]{\c_sys_engine_str}
%   The current engine given as a lower case string: one of
%   |luatex|, |pdftex|, |ptex|, |uptex| or |xetex|.
% \end{variable}
%
% \begin{variable}[added = 2020-08-20]{\c_sys_engine_exec_str}
%   The name of the standard executable for the current \TeX{} engine given
%   as a lower case string: one of  |luatex|,
%   |luahbtex|, |pdftex|, |eptex|, |euptex| or |xetex|.
% \end{variable}
%
% \begin{variable}[added = 2020-08-20]{\c_sys_engine_format_str}
%   The name of the preloaded format for the current \TeX{} run given
%   as a lower case string: one of
%   |lualatex| (or |dvilualatex|),
%   |pdflatex| (or |latex|), |platex|, |uplatex| or |xelatex| for \LaTeX{},
%   similar names for plain \TeX{} (except \pdfTeX{} in DVI mode yields
%   |etex|), and |cont-en| for Con\TeX{}t (i.e.~the
%   \tn{fmtname}).
% \end{variable}
%
% \begin{function}[added = 2020-09-24, EXP]{\sys_timer:}
%   \begin{syntax}
%     \cs{sys_timer:}
%   \end{syntax}
%   Expands to the current value of the engine's timer clock, a
%   non-negative integer.  This function is only defined for engines with
%   timer support.  This command measures not just CPU time but
%   real time (including time waiting for user input).  The unit are
%   scaled seconds ($2^{-16}$ seconds).
% \end{function}
%
% \section{Output format}
%
% \begin{function}[added = 2015-09-19, EXP, pTF]
%   {
%     \sys_if_output_dvi:,
%     \sys_if_output_pdf:
%   }
%   \begin{syntax}
%     \cs{sys_if_output_dvi:TF} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Conditionals which give the current output mode the \TeX{} run is
%   operating in. This is always one of two outcomes, DVI mode or
%   PDF mode. The two sets of conditionals are thus complementary and
%   are both provided to allow the programmer to emphasise the most
%   appropriate case.
% \end{function}
%
% \begin{variable}[added = 2015-09-19]{\c_sys_output_str}
%   The current output mode given as a lower case string: one of
%   |dvi| or |pdf|.
% \end{variable}
%
% \section{Platform}
%
% \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}
%
% \section{Random numbers}
%
% \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}
%
% \section{Access to the shell}
%
% \begin{function}[noTF, added = 2019-09-20]
%   {\sys_get_shell:nnN}
%   \begin{syntax}
%     \cs{sys_get_shell:nnN} \Arg{shell~command} \Arg{setup} \meta{tl~var}
%     \cs{sys_get_shell:nnNTF} \Arg{shell~command} \Arg{setup} \meta{tl~var} \Arg{true code} \Arg{false code}
%   \end{syntax}
%   Defines \meta{tl~var} 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_get_shell:nnNTF} conditional
%   returns \texttt{true} if the shell is available and no quote is
%   detected, and \texttt{false} otherwise.
% \end{function}
%
% \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{Loading configuration data}
%
% \begin{function}[added = 2019-09-12]{\sys_load_backend:n}
%   \begin{syntax}
%     \cs{sys_load_backend:n} \Arg{backend}
%   \end{syntax}
%   Loads the additional configuration file needed for backend support.
%   If the \meta{backend} is empty, the standard backend for the engine in
%   use will be loaded. This command may only be used once.
% \end{function}
%
% \begin{variable}{\c_sys_backend_str}
%   Set to the name of the backend in use by \cs{sys_load_backend:n} when
%   issued.
% \end{variable}
%
% \begin{function}[added = 2019-09-12]{\sys_load_debug:, \sys_load_deprecation:}
%   \begin{syntax}
%     \cs{sys_load_debug:}
%     \cs{sys_load_deprecation:}
%   \end{syntax}
%   Load the additional configuration files for debugging support and rolling
%   back deprecations, respectively.
% \end{function}
%
% \subsection{Final settings}
%
% \begin{function}[added = 2019-10-06]{\sys_finalise:}
%   \begin{syntax}
%     \cs{sys_finalise:}
%   \end{syntax}
%   Finalises all system-dependent functionality: required before loading
%   a backend.
% \end{function}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3sys} implementation}
%
%    \begin{macrocode}
%<@@=sys>
%    \end{macrocode}
%
% \subsection{Kernel code}
%
%    \begin{macrocode}
%<*package>
%<*tex>
%    \end{macrocode}
%
% \subsubsection{Detecting the engine}
%
% \begin{macro}{\@@_const:nn}
%   Set the |T|, |F|, |TF|, |p| forms of |#1| to be constants equal to
%   the result of evaluating the boolean expression~|#2|.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_const:nn #1#2
  {
    \bool_if:nTF {#2}
      {
        \cs_new_eq:cN { #1 :T }  \use:n
        \cs_new_eq:cN { #1 :F }  \use_none:n
        \cs_new_eq:cN { #1 :TF } \use_i:nn
        \cs_new_eq:cN { #1 _p: } \c_true_bool
      }
      {
        \cs_new_eq:cN { #1 :T }  \use_none:n
        \cs_new_eq:cN { #1 :F }  \use:n
        \cs_new_eq:cN { #1 :TF } \use_ii:nn
        \cs_new_eq:cN { #1 _p: } \c_false_bool
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF, EXP]
%   {
%     \sys_if_engine_luatex:,
%     \sys_if_engine_pdftex:,
%     \sys_if_engine_ptex:,
%     \sys_if_engine_uptex:,
%     \sys_if_engine_xetex:
%   }
% \begin{variable}{\c_sys_engine_str}
%   Set up the engine tests on the basis exactly one test should be true.
%   Mainly a case of looking for the appropriate marker primitive.
%    \begin{macrocode}
\str_const:Nx \c_sys_engine_str
  {
    \cs_if_exist:NT \tex_luatexversion:D { luatex }
    \cs_if_exist:NT \tex_pdftexversion:D { pdftex }
    \cs_if_exist:NT \tex_kanjiskip:D
      {
        \cs_if_exist:NTF \tex_enablecjktoken:D
          { uptex }
          { ptex }
      }
    \cs_if_exist:NT \tex_XeTeXversion:D { xetex }
  }
\tl_map_inline:nn { { luatex } { pdftex } { ptex } { uptex } { xetex } }
  {
    \@@_const:nn { sys_if_engine_ #1 }
      { \str_if_eq_p:Vn \c_sys_engine_str {#1} }
  }
%    \end{macrocode}
% \end{variable}
% \end{macro}
%
% \begin{variable}{\c_sys_engine_exec_str,\c_sys_engine_format_str}
%   Take the functions defined above, and set up the engine and format
%   names.  \cs{c_sys_engine_exec_str} differs from \cs{c_sys_engine_str}
%   as it is the \emph{actual} engine name, not a \enquote{filtered}
%   version.  It differs for |ptex| and |uptex|, which have a leading
%   |e|, and for |luatex|, because \LaTeX{} uses the \Lua HB\TeX{}
%   engine.
%
%   \cs{c_sys_engine_format_str} is quite similar to
%   \cs{c_sys_engine_str}, except that it differentiates |pdflatex| from
%   |latex| (which is \pdfTeX{} in DVI mode).  This differentiation,
%   however, is reliable only if the user doesn't change
%   \cs{tex_pdfoutput:D} before loading this code.
%    \begin{macrocode}
\group_begin:
  \cs_set_eq:NN \lua_now:e    \tex_directlua:D
  \str_const:Nx \c_sys_engine_exec_str
    {
      \sys_if_engine_pdftex:T { pdf }
      \sys_if_engine_xetex:T  { xe  }
      \sys_if_engine_ptex:T   { ep  }
      \sys_if_engine_uptex:T  { eup }
      \sys_if_engine_luatex:T
        {
          lua \lua_now:e
            {
              if (pcall(require, 'luaharfbuzz')) then ~
                tex.print("hb") ~
              end
            }
        }
      tex
    }
\group_end:
\str_const:Nx \c_sys_engine_format_str
  {
    \cs_if_exist:NTF \fmtname
      {
        \bool_lazy_or:nnTF
          { \str_if_eq_p:Vn \fmtname { plain } }
          { \str_if_eq_p:Vn \fmtname { LaTeX2e } }
          {
            \sys_if_engine_pdftex:T
              { \int_compare:nNnT { \tex_pdfoutput:D } = { 1 } { pdf } }
            \sys_if_engine_xetex:T  { xe }
            \sys_if_engine_ptex:T   { p  }
            \sys_if_engine_uptex:T  { up }
            \sys_if_engine_luatex:T
              {
                \int_compare:nNnT { \tex_pdfoutput:D } = { 0 } { dvi }
                lua
              }
            \str_if_eq:VnTF \fmtname { LaTeX2e }
              { latex }
              {
                \bool_lazy_and:nnT
                  { \sys_if_engine_pdftex_p: }
                  { \int_compare_p:nNn { \tex_pdfoutput:D } = { 0 } }
                    { e }
                tex
              }
          }
          { \fmtname }
      }
      { unknown }
  }
%    \end{macrocode}
% \end{variable}
%
% \subsubsection{Randomness}
%
% This candidate function is placed there because
% \cs{sys_if_rand_exist:TF} is used in \pkg{l3fp-rand}.
%
% \begin{macro}[EXP, pTF]{\sys_if_rand_exist:}
%   Currently, randomness exists under \pdfTeX{}, \LuaTeX{}, \pTeX{} and \upTeX{}.
%    \begin{macrocode}
\@@_const:nn { sys_if_rand_exist }
  { \cs_if_exist_p:N \tex_uniformdeviate:D }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Platform}
%
% \begin{macro}[pTF]{\sys_if_platform_unix:, \sys_if_platform_windows:}
% \begin{variable}{\c_sys_platform_str}
%   Setting these up requires the file module (file lookup), so is actually
%   implemented there.
% \end{variable}
% \end{macro}
%
% \subsubsection{Configurations}
%
% \begin{macro}{\sys_load_backend:n}
% \begin{macro}{\@@_load_backend_check:N}
% \begin{variable}{\c_sys_backend_str}
%   Loading the backend code is pretty simply: check that the backend is valid,
%   then load it up.
%    \begin{macrocode}
\cs_new_protected:Npn \sys_load_backend:n #1
  {
    \sys_finalise:
    \str_if_exist:NTF \c_sys_backend_str
      {
        \str_if_eq:VnF \c_sys_backend_str {#1}
          { \msg_error:nn { sys } { backend-set } }
      }
      {
        \tl_if_blank:nF {#1}
          { \tl_gset:Nn \g_@@_backend_tl {#1} }
        \@@_load_backend_check:N \g_@@_backend_tl
        \str_const:Nx \c_sys_backend_str { \g_@@_backend_tl }
        \__kernel_sys_configuration_load:n
          { l3backend- \c_sys_backend_str }
      }
  }
\cs_new_protected:Npn \@@_load_backend_check:N #1
  {
    \sys_if_engine_xetex:TF
      {
        \str_case:VnF #1
          {
            { dvisvgm }   { }
            { xdvipdfmx } { \tl_gset:Nn #1 { xetex } }
            { xetex }     { }
          }
          {
            \msg_error:nnxx { sys } { wrong-backend }
              #1 { xetex }
            \tl_gset:Nn #1 { xetex }
          }
      }
      {
        \sys_if_output_pdf:TF
          {
            \str_if_eq:VnTF #1 { pdfmode }
              {
                \sys_if_engine_luatex:TF
                  { \tl_gset:Nn #1 { luatex } }
                  { \tl_gset:Nn #1 { pdftex } }
              }
              {
                \bool_lazy_or:nnF
                  { \str_if_eq_p:Vn #1 { luatex } }
                  { \str_if_eq_p:Vn #1 { pdftex } }
                  {
                    \msg_error:nnxx { sys } { wrong-backend }
                      #1 { \sys_if_engine_luatex:TF { luatex } { pdftex } }
                    \sys_if_engine_luatex:TF
                      { \tl_gset:Nn #1 { luatex } }
                      { \tl_gset:Nn #1 { pdftex } }
                  }
              }
          }
          {
            \str_case:VnF #1
              {
                { dvipdfmx } { }
                { dvips }    { }
                { dvisvgm }  { }
              }
              {
                \msg_error:nnxx { sys } { wrong-backend }
                  #1 { dvips }
                \tl_gset:Nn #1 { dvips }
              }
          }
      }
  }
%    \end{macrocode}
% \end{variable}
% \end{macro}
% \end{macro}
%
% \begin{variable}{\g_@@_debug_bool, \g_@@_deprecation_bool}
%    \begin{macrocode}
\bool_new:N \g_@@_debug_bool
\bool_new:N \g_@@_deprecation_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\sys_load_debug:, \sys_load_deprecation:}
%   Simple.
%    \begin{macrocode}
\cs_new_protected:Npn \sys_load_debug:
  {
    \bool_if:NF \g_@@_debug_bool
      { \__kernel_sys_configuration_load:n { l3debug } }
    \bool_gset_true:N \g_@@_debug_bool
  }
\cs_new_protected:Npn \sys_load_deprecation:
  {
    \bool_if:NF \g_@@_deprecation_bool
      { \__kernel_sys_configuration_load:n { l3deprecation } }
    \bool_gset_true:N \g_@@_deprecation_bool
  }
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Access to the shell}
%
% \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_get_shell:nnN}
% \begin{macro}{\sys_get_shell: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_get_shell:nnN #1#2#3
  {
    \sys_get_shell:nnNF {#1} {#2} #3
      { \tl_set:Nn #3 { \q_no_value } }
  }
\prg_new_protected_conditional:Npnn \sys_get_shell: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} { " }
      {
        \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}
  }
%    \end{macrocode}
% \end{macro}
% \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}
% \begin{macro}{\@@_shell_now:e}
%   Execute commands through shell escape immediately.
%
%   For \LuaTeX{}, we use a pseudo-primitive to do the actual work.
%    \begin{macrocode}
%</tex>
%<*lua>
do
  local os_exec = os.execute

  local function shellescape(cmd)
    local status,msg = os_exec(cmd)
    if status == nil then
      write_nl("log","runsystem(" .. cmd .. ")...(" .. msg .. ")\n")
    elseif status == 0 then
      write_nl("log","runsystem(" .. cmd .. ")...executed\n")
    else
      write_nl("log","runsystem(" .. cmd .. ")...failed " .. (msg or "") .. "\n")
    end
  end
  luacmd("@@_shell_now:e", function()
    shellescape(scan_string())
  end, "global", "protected")
%</lua>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*tex>
\sys_if_engine_luatex:TF
  {
    \cs_new_protected:Npn \sys_shell_now:n #1
      { \@@_shell_now:e { \exp_not: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 }
%</tex>
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\sys_shell_shipout:n}
% \begin{macro}{\@@_shell_shipout:e}
%   Execute commands through shell escape at shipout.
%
%   For \LuaTeX, we use the same helper as above but delayed to using a late_lua whatsit.
%    \begin{macrocode}
%<*lua>
  local whatsit_id = node.id'whatsit'
  local latelua_sub = node.subtype'late_lua'
  local node_new = node.direct.new
  local setfield = node.direct.setwhatsitfield or node.direct.setfield
  local node_write = node.direct.write

  luacmd("@@_shell_shipout:e", function()
    local cmd = scan_string()
    local n = node_new(whatsit_id, latelua_sub)
    setfield(n, 'data', function() shellescape(cmd) end)
    node_write(n)
  end, "global", "protected")
end
%</lua>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*tex>
\sys_if_engine_luatex:TF
  {
    \cs_new_protected:Npn \sys_shell_shipout:n #1
    { \@@_shell_shipout:e { \exp_not: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}
% \end{macro}
%
% \subsection{Dynamic (every job) code}
%
% \begin{macro}{\sys_everyjob:}
% \begin{macro}{\@@_everyjob:n}
% \begin{variable}{\g_@@_everyjob_tl}
%    \begin{macrocode}
\cs_new_protected:Npn \sys_everyjob:
  {
    \tl_use:N \g_@@_everyjob_tl
    \tl_gclear:N \g_@@_everyjob_tl
  }
\cs_new_protected:Npn \@@_everyjob:n #1
  { \tl_gput_right:Nn \g_@@_everyjob_tl {#1} }
\tl_new:N \g_@@_everyjob_tl
%    \end{macrocode}
% \end{variable}
% \end{macro}
% \end{macro}
%
% \subsubsection{The name of the job}
%
% \begin{variable}{\c_sys_jobname_str}
%   Inherited from the \LaTeX3 name for the primitive. This \emph{has} to be
%   the primitive as it's set in \tn{everyjob}. If the user does
%   \begin{verbatim}
%     pdflatex \input some-file-name
%   \end{verbatim}
%   then \tn{everyjob} is inserted \emph{before} \tn{jobname} is changed form
%   |texput|, and thus we would have the wrong result.
%    \begin{macrocode}
\@@_everyjob:n
  { \cs_new_eq:NN \c_sys_jobname_str \tex_jobname:D }
%    \end{macrocode}
% \end{variable}
%
% \subsubsection{Time and date}
%
% \begin{variable}
%   {
%     \c_sys_minute_int,
%     \c_sys_hour_int,
%     \c_sys_day_int,
%     \c_sys_month_int,
%     \c_sys_year_int,
%   }
%   Copies of the information provided by \TeX{}. There is a lot of defensive
%   code in package mode: someone may have moved the primitives, and they can
%   only be recovered if we have \tn{primitive} and it is working correctly.
%   For Ini\TeX{} of course that is all redundant but does no harm.
%    \begin{macrocode}
\@@_everyjob:n
  {
    \group_begin:
      \cs_set:Npn \@@_tmp:w #1
        {
          \str_if_eq:eeTF { \cs_meaning:N #1 } { \token_to_str:N #1 }
            { #1 }
            {
              \cs_if_exist:NTF \tex_primitive:D
                {
                  \bool_lazy_and:nnTF
                    { \sys_if_engine_xetex_p: }
                    {
                      \int_compare_p:nNn
                        { \exp_after:wN \use_none:n \tex_XeTeXrevision:D }
                          < { 99999 }
                    }
                    { 0 }
                    { \tex_primitive:D #1 }
                }
                { 0 }
            }
        }
      \int_const:Nn \c_sys_minute_int
        { \int_mod:nn { \@@_tmp:w \time } { 60 } }
      \int_const:Nn \c_sys_hour_int
        { \int_div_truncate:nn { \@@_tmp:w \time } { 60 } }
      \int_const:Nn \c_sys_day_int   { \@@_tmp:w \day }
      \int_const:Nn \c_sys_month_int { \@@_tmp:w \month }
      \int_const:Nn \c_sys_year_int  { \@@_tmp:w \year }
    \group_end:
  }
%    \end{macrocode}
% \end{variable}
%
% \subsubsection{Random numbers}
%
% \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}
\@@_everyjob:n
  {
    \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
            \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}
\@@_everyjob:n
  {
    \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
          {
            \msg_error:nnn { kernel } { fp-no-random }
              { \sys_gset_rand_seed:n {#1} }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\sys_timer:, \@@_elapsedtime:}
% \begin{macro}[EXP, pTF]{\sys_if_timer_exist:}
%   In \LuaTeX{}, create a pseudo-primitve, otherwise try to
%   locate the real primitive.  The elapsed time will be
%   available if this succeeds.
%    \begin{macrocode}
%</tex>
%<*lua>
  local gettimeofday = os.gettimeofday
  local epoch = gettimeofday() - os.clock()
  local write = tex.write
  local tointeger = math.tointeger
  luacmd('@@_elapsedtime:', function()
    write(tointeger((gettimeofday() - epoch)*65536 // 1))
  end, 'global')
%</lua>
%<*tex>
\sys_if_engine_luatex:TF
  {
    \cs_new:Npn \sys_timer:
      { \@@_elapsedtime: }
  }
  {
    \cs_if_exist:NTF \tex_elapsedtime:D
      {
        \cs_new:Npn \sys_timer:
          { \int_value:w \tex_elapsedtime:D }
      }
      {
        \msg_new:nnnn { kernel } { no-elapsed-time }
          { No~clock~detected~for~#1. }
          { The~current~engine~provides~no~way~to~access~the~system~time. }
        \cs_new:Npn \sys_timer:
          {
            \int_value:w
            \msg_expandable_error:nnn { kernel } { no-elapsed-time }
              { \sys_timer: }
            \c_zero_int
          }
      }
  }
\@@_const:nn { sys_if_timer_exist }
  { \cs_if_exist_p:N \tex_elapsedtime:D || \cs_if_exist_p:N \@@_elapsedtime: }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsubsection{Access to the shell}
%
% \begin{variable}{\c_sys_shell_escape_int}
%   Expose the engine's shell escape status to the user.
%    \begin{macrocode}
\@@_everyjob:n
  {
    \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{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}
\@@_everyjob:n
  {
    \@@_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}
%
% \subsubsection{Held over from \pkg{l3file}}
%
% \begin{variable}{\g_file_curr_name_str}
%   See comments about \cs{c_sys_jobname_str}: here, as soon as there is
%   file input/output, things get \enquote{tided up}.
%    \begin{macrocode}
\@@_everyjob:n
  { \cs_gset_eq:NN \g_file_curr_name_str \tex_jobname:D }
%    \end{macrocode}
% \end{variable}
%
% \subsection{Last-minute code}
%
% \begin{macro}{\sys_finalise:}
% \begin{macro}{\@@_finalise:n}
% \begin{variable}{\g_@@_finalise_tl}
%   A simple hook to finalise the system-dependent layer. This is forced by
%   the backend loader, which is forced by the main loader, so we do not need
%   to include that here.
%    \begin{macrocode}
\cs_new_protected:Npn \sys_finalise:
  {
    \sys_everyjob:
    \tl_use:N \g_@@_finalise_tl
    \tl_gclear:N \g_@@_finalise_tl
  }
\cs_new_protected:Npn \@@_finalise:n #1
  { \tl_gput_right:Nn \g_@@_finalise_tl {#1} }
\tl_new:N \g_@@_finalise_tl
%    \end{macrocode}
% \end{variable}
% \end{macro}
% \end{macro}
%
% \subsubsection{Detecting the output}
%
% \begin{macro}[pTF, EXP]
%   {
%     \sys_if_output_dvi:,
%     \sys_if_output_pdf:
%   }
% \begin{variable}{\c_sys_output_str}
%   This is a simple enough concept: the two views here are complementary.
%    \begin{macrocode}
\@@_finalise:n
  {
    \str_const:Nx \c_sys_output_str
      {
        \int_compare:nNnTF
          { \cs_if_exist_use:NF \tex_pdfoutput:D { 0 } } > { 0 }
          { pdf }
          { dvi }
      }
    \@@_const:nn { sys_if_output_dvi }
      { \str_if_eq_p:Vn \c_sys_output_str { dvi } }
    \@@_const:nn { sys_if_output_pdf }
      { \str_if_eq_p:Vn \c_sys_output_str { pdf } }
  }
%    \end{macrocode}
% \end{variable}
% \end{macro}
%
% \subsubsection{Configurations}
%
% \begin{variable}{\g_@@_backend_tl}
%   As the backend has to be checked and possibly adjusted, the approach here
%   is to create a variable and use that in a one-shot to set a constant.
%    \begin{macrocode}
\tl_new:N \g_@@_backend_tl
\@@_finalise:n
  {
    \__kernel_tl_gset:Nx \g_@@_backend_tl
      {
        \sys_if_engine_xetex:TF
          { xetex }
          {
             \sys_if_output_pdf:TF
              {
                \sys_if_engine_pdftex:TF
                  { pdftex }
                  { luatex }
              }
              { dvips }
           }
      }
  }
%    \end{macrocode}
%   If there is a class option set, and recognised, we pick it up: these
%   will over-ride anything set automatically but will themselves be
%   over-written if there is a package option.
%    \begin{macrocode}
\@@_finalise:n
  {
    \cs_if_exist:NT \@classoptionslist
      {
        \cs_if_eq:NNF \@classoptionslist \scan_stop:
          {
            \clist_map_inline:Nn \@classoptionslist
              {
                \str_case:nnT {#1}
                  {
                    { dvipdfmx }
                      { \tl_gset:Nn \g_@@_backend_tl { dvipdfmx } }
                    { dvips }
                      { \tl_gset:Nn \g_@@_backend_tl { dvips } }
                    { dvisvgm }
                      { \tl_gset:Nn \g_@@_backend_tl { dvisvgm } }
                    { pdftex }
                      { \tl_gset:Nn \g_@@_backend_tl { pdfmode } }
                    { xetex }
                      { \tl_gset:Nn \g_@@_backend_tl { xdvipdfmx } }
                  }
                  { \clist_remove_all:Nn \@unusedoptionlist {#1} }
              }
          }
      }
  }
%    \end{macrocode}
% \end{variable}
%
%    \begin{macrocode}
%</tex>
%</package>
%    \end{macrocode}
%
%\end{implementation}
%
%\PrintIndex
