% \iffalse meta-comment
%
%% File: l3luatex.dtx
%
% Copyright (C) 2010-2019 The LaTeX3 Project
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file
%
%    https://www.latex-project.org/lppl.txt
%
% This file is part of the "l3kernel bundle" (The Work in LPPL)
% and all files in that bundle must be distributed together.
%
% -----------------------------------------------------------------------
%
% The development version of the bundle can be found at
%
%    https://github.com/latex3/latex3
%
% for those people who are interested.
%
%<*driver>
\documentclass[full,kernel]{l3doc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{l3luatex} package: \LuaTeX-specific functions^^A
% }
%
% \author{^^A
%  The \LaTeX3 Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Released 2019-10-14}
%
% \maketitle
%
% \begin{documentation}
%
% The \LuaTeX{} engine provides access to the \Lua{} programming language,
% and with it access to the \enquote{internals} of \TeX{}. In order to use
% this within the framework provided here, a family of functions is
% available. When used with \pdfTeX{}, \pTeX{}, \upTeX{} or \XeTeX{} these raise an
% error: use \cs{sys_if_engine_luatex:T} to avoid this. Details on using
% \Lua{} with the \LuaTeX{} engine are given in the \LuaTeX{} manual.
%
% \section{Breaking out to \Lua{}}
%
% \begin{function}[EXP, added = 2018-06-18]{\lua_now:n, \lua_now:e}
%   \begin{syntax}
%     \cs{lua_now:n} \Arg{token list}
%   \end{syntax}
%   The \meta{token list} is first tokenized by \TeX{}, which includes
%   converting line ends to spaces in the usual \TeX{} manner and which
%   respects currently-applicable \TeX{} category codes. The resulting
%   \meta{\Lua{} input} is passed to the \Lua{} interpreter for processing.
%   Each \cs{lua_now:n} block is treated by \Lua{} as a separate chunk.
%   The \Lua{} interpreter executes the \meta{\Lua{} input} immediately,
%   and in an expandable manner.
%   \begin{texnote}
%     \cs{lua_now:e} is a macro wrapper around \tn{directlua}: when
%     \LuaTeX{} is in use two expansions are required to yield the
%     result of the \Lua{} code.
%   \end{texnote}
% \end{function}
%
% \begin{function}[added = 2018-06-18]{\lua_shipout_e:n, \lua_shipout:n}
%   \begin{syntax}
%     \cs{lua_shipout:n} \Arg{token list}
%   \end{syntax}
%   The \meta{token list} is first tokenized by \TeX{}, which includes
%   converting line ends to spaces in the usual \TeX{} manner and which
%   respects currently-applicable \TeX{} category codes.  The resulting
%   \meta{\Lua{} input} is passed to the \Lua{} interpreter when the
%   current page is finalised (\emph{i.e.}~at shipout).  Each
%   \cs{lua_shipout:n} block is treated by \Lua{} as a separate chunk.
%   The \Lua{} interpreter will execute the \meta{\Lua{} input} during the
%   page-building routine: no \TeX{} expansion of the \meta{\Lua{} input}
%   will occur at this stage.
%
%   In the case of the \cs{lua_shipout_e:n} version the input is fully
%   expanded by \TeX{} in an \texttt{e}-type manner during the shipout
%   operation.
%   \begin{texnote}
%     At a \TeX{} level, the \meta{\Lua{} input} is stored as a
%     \enquote{whatsit}.
%   \end{texnote}
% \end{function}
%
% \begin{function}[EXP, added = 2015-06-29]{\lua_escape:n, \lua_escape:e}
%   \begin{syntax}
%     \cs{lua_escape:n} \Arg{token list}
%   \end{syntax}
%   Converts the \meta{token list} such that it can safely be passed to
%   \Lua{}: embedded backslashes, double and single quotes, and newlines
%   and carriage returns are escaped. This is done by prepending an extra
%   token consisting of a backslash with category code~$12$, and for the line
%   endings, converting them to |\n| and |\r|, respectively.
%   \begin{texnote}
%     \cs{lua_escape:e} is a macro wrapper around \tn{luaescapestring}:
%     when \LuaTeX{} is in use two expansions are required to yield the
%     result of the \Lua{} code.
%   \end{texnote}
% \end{function}
%
% \section{Lua interfaces}
%
% As well as interfaces for \TeX{}, there are a small number of Lua functions
% provided here.
%
% \begin{function}{l3kernel}
%   All public interfaces provided by the module are stored within the
%   |l3kernel| table.
% \end{function}
%
% \begin{function}{l3kernel.charcat}
%   \begin{syntax}
%     |l3kernel.charcat(|\meta{charcode}, \meta{catcode}|)|
%   \end{syntax}
%   Constructs a character of \meta{charcode} and \meta{catcode} and returns
%   the result to \TeX{}.
% \end{function}
%
% \begin{function}{l3kernel.elapsedtime}
%   \begin{syntax}
%     |l3kernel.elapsedtime()|
%   \end{syntax}
%   Returns the CPU time in \meta{scaled seconds} since the start of the
%   \TeX{} run or since |l3kernel.resettimer| was issued.  This only
%   measures the time used by the CPU, not the real time, e.g., waiting
%   for user input.
% \end{function}
%
% \begin{function}{l3kernel.filemdfivesum}
%   \begin{syntax}
%     |l3kernel.filemdfivesum(|\meta{file}|)|
%   \end{syntax}
%   Returns the of the MD5 sum of the file contents read as bytes; note that
%   the result will depend on the nature of the line endings used in the file,
%   in contrast to normal \TeX{} behaviour. If the \meta{file} is not found,
%   nothing is returned with \emph{no error raised}.
% \end{function}
%
% \begin{function}{l3kernel.filemoddate}
%   \begin{syntax}
%     |l3kernel.filemoddate(|\meta{file}|)|
%   \end{syntax}
%   Returns the of the date/time of last modification of the \meta{file} in the
%   format
%   \begin{quote}
%     |D:|\meta{year}\meta{month}\meta{day}\meta{hour}\meta{minute}^^A
%     \meta{second}\meta{offset}
%   \end{quote}
%   where the latter may be |Z| (UTC) or
%   \meta{plus-minus}\meta{hours}|'|\meta{minutes}|'|. If the \meta{file} is
%   not found, nothing is returned with \emph{no error raised}.
% \end{function}
%
% \begin{function}{l3kernel.filesize}
%   \begin{syntax}
%     |l3kernel.filesize(|\meta{file}|)|
%   \end{syntax}
%   Returns the size of the \meta{file} in bytes. If the \meta{file} is not
%   found, nothing is returned with \emph{no error raised}.
% \end{function}
%
% \begin{function}{l3kernel.resettimer}
%   \begin{syntax}
%     |l3kernel.resettimer()|
%   \end{syntax}
%  Resets the timer used by |l3kernel.elapsetime|.
% \end{function}
%
% \begin{function}{l3kernel.strcmp}
%   \begin{syntax}
%     |l3kernel.strcmp(|\meta{str one}, \meta{str two}|)|
%   \end{syntax}
%   Compares the two strings and returns |0| to \TeX{}
%   if the two are identical.
% \end{function}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3luatex} implementation}
%
%    \begin{macrocode}
%<*initex|package>
%    \end{macrocode}
%
% \subsection{Breaking out to \Lua{}}
%
%    \begin{macrocode}
%<*tex>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=lua>
%    \end{macrocode}
%
% \begin{macro}[EXP]{\@@_escape:n, \@@_now:n, \@@_shipout:n}
%   Copies of primitives.
%    \begin{macrocode}
\cs_new_eq:NN \@@_escape:n  \tex_luaescapestring:D
\cs_new_eq:NN \@@_now:n     \tex_directlua:D
\cs_new_eq:NN \@@_shipout:n \tex_latelua:D
%    \end{macrocode}
% \end{macro}
%
% These functions are set up in \pkg{l3str} for bootstrapping: we want to
% replace them with a \enquote{proper} version at this stage, so clean up.
%    \begin{macrocode}
\cs_undefine:N \lua_escape:e
\cs_undefine:N \lua_now:e
%    \end{macrocode}
%
% \begin{macro}[EXP]{\lua_now:n, \lua_now:e}
% \begin{macro}{\lua_shipout_e:n, \lua_shipout:n}
% \begin{macro}[EXP]{\lua_escape:n, \lua_escape:e}
%   Wrappers around the primitives. As with engines other than \LuaTeX{}
%   these have to be macros, we give them the same status in all cases.
%   When \LuaTeX{} is not in use, simply give an error message/
%    \begin{macrocode}
\cs_new:Npn \lua_now:e #1 { \@@_now:n {#1} }
\cs_new:Npn \lua_now:n #1 { \lua_now:e { \exp_not:n {#1} } }
\cs_new_protected:Npn \lua_shipout_e:n #1 { \@@_shipout:n {#1} }
\cs_new_protected:Npn \lua_shipout:n #1
  { \lua_shipout_e:n { \exp_not:n {#1} } }
\cs_new:Npn \lua_escape:e #1 { \@@_escape:n {#1} }
\cs_new:Npn \lua_escape:n #1 { \lua_escape:e { \exp_not:n {#1} } }
\sys_if_engine_luatex:F
  {
    \clist_map_inline:nn
      {
        \lua_escape:n , \lua_escape:e ,
        \lua_now:n , \lua_now:e
      }
      {
        \cs_set:Npn #1 ##1
          {
            \__kernel_msg_expandable_error:nnn
              { kernel } { luatex-required } { #1 }
          }
      }
    \clist_map_inline:nn
      { \lua_shipout_e:n , \lua_shipout:n }
      {
        \cs_set_protected:Npn #1 ##1
          {
            \__kernel_msg_error:nnn
              { kernel } { luatex-required } { #1 }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Messages}
%
%    \begin{macrocode}
\__kernel_msg_new:nnnn { kernel } { luatex-required }
  { LuaTeX~engine~not~in~use!~Ignoring~#1. }
  {
    The~feature~you~are~using~is~only~available~
    with~the~LuaTeX~engine.~LaTeX3~ignored~'#1'.
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%</tex>
%    \end{macrocode}
%
% \subsection{\Lua{} functions for internal use}
%
%    \begin{macrocode}
%<*lua>
%    \end{macrocode}
%
% Most of the emulation of \pdfTeX{} here is based heavily on Heiko Oberdiek's
% \pkg{pdftexcmds} package.
%
% \begin{macro}{l3kernel}
%   Create a table for the kernel's own use.
%    \begin{macrocode}
l3kernel = l3kernel or { }
%    \end{macrocode}
% \end{macro}
%
%   Local copies of global tables.
%    \begin{macrocode}
local io      = io
local kpse    = kpse
local lfs     = lfs
local math    = math
local md5     = md5
local os      = os
local string  = string
local tex     = tex
local unicode = unicode
%    \end{macrocode}
%
%   Local copies of standard functions.
%    \begin{macrocode}
local abs        = math.abs
local byte       = string.byte
local floor      = math.floor
local format     = string.format
local gsub       = string.gsub
local lfs_attr   = lfs.attributes
local md5_sum    = md5.sum
local open       = io.open
local os_clock   = os.clock
local os_date    = os.date
local setcatcode = tex.setcatcode
local sprint     = tex.sprint
local cprint     = tex.cprint
local write      = tex.write
%    \end{macrocode}
%
%   Newer Con\TeX{}t releases replace the |unicode| library by |utf|.
%    \begin{macrocode}
local utf8_char = (utf and utf.char) or unicode.utf8.char
%    \end{macrocode}
%
%   Deal with Con\TeX{}t: doesn't use |kpse| library.
%    \begin{macrocode}
local kpse_find = (resolvers and resolvers.findfile) or kpse.find_file
%    \end{macrocode}
%
% \begin{macro}[int]{escapehex}
%   An internal auxiliary to convert a string to the matching hex escape.
%   This works on a byte basis: extension to handled UTF-8 input is
%   covered in \pkg{pdftexcmds} but is not currently required here.
%    \begin{macrocode}
local function escapehex(str)
  write((gsub(str, ".",
    function (ch) return format("%02X", byte(ch)) end)))
end
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{l3kernel.charcat}
%   Creating arbitrary chars using |tex.cprint|.
%    \begin{macrocode}
local charcat
function charcat(charcode, catcode)
  cprint(catcode, utf8_char(charcode))
end
l3kernel.charcat = charcat
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{l3kernel.elapsedtime, l3kernel.resettimer}
%   Simple timing set up: give the result from the system clock in scaled
%   seconds.
%    \begin{macrocode}
local base_time = 0
local function elapsedtime()
  local val = (os_clock() - base_time) * 65536 + 0.5
  if val > 2147483647 then
    val = 2147483647
  end
  write(format("%d",floor(val)))
end
l3kernel.elapsedtime = elapsedtime
local function resettimer()
  base_time = os_clock()
end
l3kernel.resettimer = resettimer
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{l3kernel.filemdfivesum}
%   Read an entire file and hash it: the hash function itself is a built-in.
%   As Lua is byte-based there is no work needed here in terms of UTF-8
%   (see \pkg{pdftexcmds} and how it handles strings that have passed through
%   \LuaTeX{}). The file is read in binary mode so that no line ending
%   normalisation occurs.
%    \begin{macrocode}
local function filemdfivesum(name)
  local file =  kpse_find(name, "tex", true)
  if file then
    local f = open(file, "rb")
    if f then
      local data = f:read("*a")
      escapehex(md5_sum(data))
      f:close()
    end
  end
end
l3kernel.filemdfivesum = filemdfivesum
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{l3kernel.filemoddate}
%   See procedure \texttt{makepdftime} in \texttt{utils.c} of
%   \pdfTeX{}.
%    \begin{macrocode}
local function filemoddate(name)
  local file =  kpse_find(name, "tex", true)
  if file then
    local date = lfs_attr(file, "modification")
    if date then
      local d = os_date("*t", date)
      if d.sec >= 60 then
        d.sec = 59
      end
      local u = os_date("!*t", date)
      local off = 60 * (d.hour - u.hour) + d.min - u.min
      if d.year ~= u.year then
        if d.year > u.year then
          off = off + 1440
        else
          off = off - 1440
        end
      elseif d.yday ~= u.yday then
        if d.yday > u.yday then
          off = off + 1440
        else
          off = off - 1440
        end
      end
      local timezone
      if off == 0 then
        timezone = "Z"
      else
        local hours = floor(off / 60)
        local mins  = abs(off - hours * 60)
        timezone = format("%+03d", hours)
          .. "'" .. format("%02d", mins) .. "'"
      end
      write("D:"
        .. format("%04d", d.year)
        .. format("%02d", d.month)
        .. format("%02d", d.day)
        .. format("%02d", d.hour)
        .. format("%02d", d.min)
        .. format("%02d", d.sec)
        .. timezone)
    end
  end
end
l3kernel.filemoddate = filemoddate
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{l3kernel.filesize}
%   A simple disk lookup.
%    \begin{macrocode}
local function filesize(name)
  local file =  kpse_find(name, "tex", true)
  if file then
    local size = lfs_attr(file, "size")
    if size then
      write(size)
    end
  end
end
l3kernel.filesize = filesize
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{l3kernel.strcmp}
%   String comparison which gives the same results as \pdfTeX{}'s
%   \tn{pdfstrcmp}, although the ordering should likely not be relied upon!
%    \begin{macrocode}
local function strcmp(A, B)
  if A == B then
    write("0")
  elseif A < B then
    write("-1")
  else
    write("1")
  end
end
l3kernel.strcmp = strcmp
%    \end{macrocode}
% \end{macro}
%
% \subsection{Generic \Lua{} and font support}
%
%    \begin{macrocode}
%<*initex>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=alloc>
%    \end{macrocode}
%
% A small amount of generic code is used by almost all \LuaTeX{} material so
% needs to be loaded by the format.
%    \begin{macrocode}
attribute_count_name = "g_@@_attribute_int"
bytecode_count_name  = "g_@@_bytecode_int"
chunkname_count_name = "g_@@_chunkname_int"
whatsit_count_name   = "g_@@_whatsit_int"
require("ltluatex")
%    \end{macrocode}
%
% With the above available the font loader code used by plain \TeX{} and
% \LaTeXe{} when used with \LuaTeX{} can be loaded here. This is thus being
% treated more-or-less as part of the engine itself.
%    \begin{macrocode}
require("luaotfload-main")
local _void = luaotfload.main()
%    \end{macrocode}
%    \begin{macrocode}
%</initex>
%    \end{macrocode}
%
%    \begin{macrocode}
%</lua>
%    \end{macrocode}
%
%    \begin{macrocode}
%</initex|package>
%    \end{macrocode}
%
%\end{implementation}
%
%\PrintIndex
