% \iffalse meta-comment % %% File: l3sys.dtx % % Copyright (C) 2015-2022 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} % % \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 2022-05-30} % % \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 % inserts the \texttt{true code} if the shell is available and no quote is % detected, and the \texttt{false code} 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:} % \begin{syntax} % \cs{sys_load_debug:} % \end{syntax} % Load the additional configuration file for debugging support. % \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} % \begin{macrocode} \bool_new:N \g_@@_debug_bool % \end{macrocode} % \end{variable} % % \begin{macro}{\sys_load_debug:} % 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 } % \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} % %<*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") % % \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 } % % \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 using a |late_lua| whatsit. % Creating a |late_lua| whatsit works a bit different if we are running under Con\TeX{}t. % \begin{macrocode} %<*lua> local new_latelua = nodes and nodes.nuts and nodes.nuts.pool and nodes.nuts.pool.latelua or (function() 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 return function(f) local n = node_new(whatsit_id, latelua_sub) setfield(n, 'data', f) return n end end)() local node_write = node.direct.write luacmd("@@_shell_shipout:e", function() local cmd = scan_string() node_write(new_latelua(function() shellescape(cmd) end)) end, "global", "protected") end % % \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} % %<*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') % %<*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} % % % \end{macrocode} % %\end{implementation} % %\PrintIndex