% \iffalse meta-comment
%
% Copyright (C) 2009-2013 by Élie Roux <elie.roux@telecom-bretagne.eu>
% Copyright (C) 2010, 2011 by Manuel Pégourié-Gonnard <mpg@elzevir.fr>
% Copyright (C) 2015 David Carlisle and Joseph Wright
%
% 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
%
%<emu>\ifx\BeginCatcodeRegime\undefined\else\expandafter\endinput\fi
%<tex,sty>
%<tex,sty>\ifx
%<sty>  \ProvidesPackage\undefined\begingroup\def\ProvidesPackage
%<tex>  \ProvidesFile\undefined\begingroup\def\ProvidesFile
%<tex,sty>  #1#2[#3]{\endgroup\immediate\write-1{Package: #1 #3}}
%<tex,sty>\fi
%<emu>\ProvidesPackage{luatexbase}
%<emu-cmp>\ProvidesPackage{luatexbase-compat}
%<emu-mod>\ProvidesPackage{luatexbase-modutils}
%<emu-loa>\ProvidesPackage{luatexbase-loader}
%<emu-reg>\ProvidesPackage{luatexbase-regs}
%<emu-att>\ProvidesPackage{luatexbase-attr}
%<emu-cct>\ProvidesPackage{luatexbase-cctb}
%<emu-mcb>\ProvidesPackage{luatexbase-mcb}
%<*driver>
\ProvidesFile{luatexbase.dtx}
%</driver>
%<*tex,sty>
[2015/10/04 v1.3
%</tex,sty>
%<emu>  luatexbase interface to LuaTeX
%<emu-cmp>  luatexbase interface to LuaTeX (legacy subpackage)
%<emu-mod>  luatexbase interface to LuaTeX (legacy subpackage)
%<emu-loa>  luatexbase interface to LuaTeX (legacy subpackage)
%<emu-reg>  luatexbase interface to LuaTeX (legacy subpackage)
%<emu-att>  luatexbase interface to LuaTeX (legacy subpackage)
%<emu-cct>  luatexbase interface to LuaTeX (legacy subpackage)
%<emu-mcb>  luatexbase interface to LuaTeX (legacy subpackage)
%<*tex,sty>
]
%</tex,sty>
%<*driver>
\documentclass{ltxdoc}
\GetFileInfo{luatexbase.dtx}
\begin{document}
\title{\filename\\(Lua\TeX{}-specific support, luatexbase interface)}
\author{David Carlisle and Joseph Wright\footnote{Significant portions
  of the code here are adapted/simplified from the packages \textsf{luatex} and
  \textsf{luatexbase} written by Heiko Oberdiek, \'{E}lie Roux,
  Manuel P\'{e}gouri\'{e}-Gonnar and Philipp Gesang.}}
\date{\filedate}
\maketitle
\setcounter{tocdepth}{2}
\tableofcontents
\DocInput{\filename}
\end{document}
%</driver>
% \fi
%
% \section{Overview}
%
% Lua\TeX{} adds a number of engine-specific functions to \TeX{}. Support
% for those is now available for this area in the \LaTeX{} kernel and as
% an equivalent stand-alone file |ltluatex.tex| for plain users. The
% functionality there is derived from the earlier \textsf{luatex}
% and \textsf{luatexbase} packages by Heiko Oberdiek, \'{E}lie Roux,
% Manuel P\'{e}gouri\'{e}-Gonnar and Philipp Gesang. However, the
% interfaces are not all identical.
%  
% The interfaces defined in this package are closely modelled on the original
% \textsf{luatexbase} package, and provide a compatibility layer between
% the new kernel-level support and existing code using \textsf{luatexbase}.
%
% \section{The \textsf{luatexbase} package interface}
%
% \subsection
% [Catcode tables]
% {Catcode tables\footnote{This
% interface was previously defined in the \textsf{luatexbase-cctbl}
% sub-package.}}
%
% \subsubsection{\TeX}
% 
%
% \noindent
% \DescribeMacro{\CatcodeTableIniTeX}
% \DescribeMacro{\CatcodeTableString}
% \DescribeMacro{\CatcodeTableLaTeX}
% \DescribeMacro{\CatcodeTableLaTeXAtLetter}
% \DescribeMacro{\CatcodeTableOther}
% \DescribeMacro{\CatcodeTableExpl}
% \TeX\ access to predefined catcode tables.
%
% The first four tables are aliases giving alternative names for some
% catcodetables that are defined in the \textsf{ltluatex} core.
%
% |\CatcodeTableOther| is like |\CatcodeTableString| except that
% the catcode of space is $12$ (other).
%
% |\CatcodeTableExpl| is similar to the environment set by the
% \textsf{expl3} command |\ExplSyntaxOn| note that this only affects
% catcode settings, not for example |\endlinechar|.
%
% One difference between this implementation and the tables defined
% in the earlier \textsf{luatexbase} package is that these tables are
% defined to match the settings used by \LaTeX\ over the full Unicode
% range (as set in the file \textsf{unicode-letters.def}).
%
% \noindent
% \DescribeMacro{\SetCatcodeRange}
% An alias for |\@setrangecatcode| which is defined in the
% \textsf{ctablestack} package imported into this version of
% \textsf{luatexbase}. (The order of arguments is the
% same despite the variation in the naming). This is useful for
% setting up a new catcode table and assigns a given catcode to a
% range of characters.
% 
% \noindent
% \DescribeMacro{\BeginCatcodeRegime}
% \DescribeMacro{\EndCatcodeRegime}
% A simple wrapper around |\@pushcatcodetable| providing a slightly
% different interface. The usage is:\\%
% \verb|\BeginCatcodeRegime|\meta{catcode table}\\
% \verb|   |\meta{code}\\
% \verb|\EndCatcodeRegime|
%
% \noindent
% \DescribeMacro{\PushCatcodeTableNumStack}
% \DescribeMacro{\PopCatcodeTableNumStack}
% These are defined to be aliases for |\@pushcatcodetable| and
% |\@popcatcodetable| although the actual implementation is quite different
% to the older packages, the use of the commands should match.
%
% \noindent
% \DescribeMacro{\newluatexcatcodetable}
% \DescribeMacro{\setluatexcatcodetable}
% Aliases for the \textsf{ltluatex} functions
% dropping |luatex|  from the name to match the convention of not
% using  |luatex|-prefixed names for the Lua\TeX\ primitives.
%
% \subsubsection{Lua}
%
% The standard way to access catcode table numbers from Lua in
% \textsf{ltluatex} is the |registernumber| function.  This
% package provides a |catcodetables| table with a metatable
% that accesses this function and is extended with aliases for the
% predefined tables so you can use |catcodetables.expl| as an
% alternative to |catcodetables.CatcodeTableExpl|, both being
% equivalent to\\
% |registernumber('CatcodeTableExpl')|.
%
%
% \subsection
% [Lua Callbacks]
% {Lua Callbacks\footnote{This
% interface was previously defined in the \textsf{luatexbase-mcb}
% sub-package.}}
%
% The |luatexbase| table is extended with some additional Lua
% functions to provide the interfaces provided by the previous
% implementation.
%
%
% \noindent
% \DescribeMacro{priority\_in\_callback}\meta{name}\meta{description}\\%
% As in the earlier interfaces the function
% is provided to return a number indicating the position of a
% specified function in a callback list. However it is usually used
% just as a boolean test that the function is registered with the
% callback. Kernel-level support does not directly expose the priority numbers,
% however the function here is defined to return the number of the specified
% function in the list returned by |luatexbase.callback_descriptions|.
%
%
% \noindent
% \DescribeMacro{is\_active\_callback}\meta{name}\meta{description}\\%
% This boolean function was defined in the development sources
% of the previous  implementation. Here it is defined as an alias for
% the function |in_callback| provided by \textsf{ltluatex}.
% Given a callback and a description string, it returns true if a
% callback function with that description is currently registered.
%
% \noindent
% \DescribeMacro{reset\_callback}\meta{name}\meta{make\_false}\\
% This function unregisters all functions registered for the callback
% \meta{name}. If \meta{make\_false} is true, the callback is then set
% to false (rather than nil). Unlike the earlier implementation
% This version does call |remove_from_callback| on each function in
% the callback list for \meta{name}, and each removal will be recorded
% in the log.
%
% \noindent
% \DescribeMacro{remove\_from\_callback}\meta{name}\meta{description}\\%
% This function is unchanged from the kernel-level implementation.
% It is backward compatible with the previous \textsf{luatexbase}
% package but enhanced as it returns the removed callback and its
% description.
% Together with the |callback_descriptions| function this allows much
% finer control over the order of functions in a callback list as the
% functions can be removed then re-added to the list in any desired order.
%
% \noindent
% \DescribeMacro{add\_to\_callback}\meta{name}\meta{function}\meta{description}\meta{priority}\\%
% This function is defined as a wrapper around the kernel-level
% implementation, which does not have the fourth \meta{priority}
% argument. 
%
% If multiple callbacks are registered to a callback of type
% \textsf{exclusive} then \textsf{ltluatex} raises an error, but
% here it is allowed if \texttt{priority} is $1$, in which case the
% \verb|reset_callback| is first called to remove the existing
% callback.
%
% In general the \texttt{priority} argument is implemented by 
% temporarily removing some callbacks from the list and replacing
% them after having added the new callback.
%
% \noindent
% \DescribeMacro{create\_callback}\meta{name}\meta{type}\meta{default}\\%
% This function is unchanged from kernel-level
% implementation, the only change is a change of terminology for the
% types of callback, the type |first| is now classified as |exclusive|
% and the kernel code raises an error if multiple callback functions
% are registered. The previous \textsf{luatexbase} implementation
% allowed multiple functions to be registered, but only activated the
% first in the list.
%
% \subsection
% [Module declaration]
% {Module declaration\footnote{This
% interface was previously defined in the \textsf{luatexbase-modutils}
% sub-package.}}
%
% \subsubsection{\TeX}
%
% \noindent
% \DescribeMacro{\RequireLuaModule}\meta{file}\oarg{info}\\
% This command is provided as a wrapper around
% |\directlua{require(|\meta{file}|}|, and executes the Lua code in
% the specified file.
% The optional argument is accepted but ignored.
% 
% Current versions of Lua\TeX\ all use the |kpse| \TeX\ path searching
% library with the |require| function, so the more complicated
% definition used in earlier implementations is no
% longer needed.
%
% \subsubsection{Lua}
% 
% \noindent
% \DescribeMacro{provides\_module}\meta{info}\\
% The \textsf{luatexbase} version of |provides_module| returns a list of log
% and error functions so that it is usually called as:\\
% |local err, warning, info, log = luatexbase.provides_module({name=..|
%
% The returned functions are all instances of the functions provided by
% the kernel: |module_error|,
% |module_warning| and |module_info|, They all use their first argument
% as a format string fo rany later arguments.
%
% \DescribeMacro{errwarinf}\meta{name}\\
% Returns four error and warning functions associated with \meta{name}
% mostly a helper function for \verb|provides\_module|, but can be called
% separately.  
%
% \subsection
% [Lua Attributes]
% {Lua Attributes and Whatsits\footnote{This
% interface was previously defined in the \textsf{luatexbase-attr}
% sub-package.}}
%
%
% \subsubsection{\TeX}
%
% \noindent
% \DescribeMacro{\newluatexattribute}
% \DescribeMacro{\setluatexattribute}
% \DescribeMacro{\unsetluatexattribute}
% As for catcode tables, aliases for the attribute allocation functions are
% provided with |luatex| in the names.
%
%
% \subsubsection{Lua}
% The lua code in this section is concerned with an experimental
% whatsit handling suite of functions in the original package.
% This is not fully documented here and is guraded by the
% \textsf{docstrip} guard \verb|whatsit| so it may optionally be
% included or excluded from the sources when the package is built.
%
% \subsection{Prefixed names for lua\TeX\ primitives}
% \noindent
% \DescribeMacro{\luatexattributedef}
% \DescribeMacro{\luatexcatcodetable}
% \DescribeMacro{\luatexluaescapestring}
% \DescribeMacro{\luatexlatelua}
% \DescribeMacro{\luatexoutputbox}
% \DescribeMacro{\luatexscantextokens}
% Aliases for commonly ued lua\TeX\ primitives that existing packages
% using \textsf{luatexbase} use with prefixed names.
%
% If additional primtives are required it is recommended that the
% code is updated to use unprefixed names. To ensure that the code
% works with the original \textsf{luatexbase} package on older formats
% you may use the lua function \texttt{tex.enableprimitives} to enable
% some or all primitives to be available with unprefixed names.
%
% \StopEventually{}
%
% \section{Implementation}
%
% \subsection{\textsf{luatexbase} interface}
%
%    \begin{macrocode}
%<*emu>
\edef\emuatcatcode{\the\catcode`\@}
\catcode`\@=11
%    \end{macrocode}
%
% Load |ctablestack|.
%    \begin{macrocode}
\ifx\@setrangecatcode\@undefined
  \ifx\RequirePackage\@undefined
    \input{ctablestack.sty}%
  \else
    \RequirePackage{ctablestack}
  \fi
\fi
%    \end{macrocode}
%
% Simple require wrapper as we now assume |require| implicitly uses the
% |kpathsea| search library.
%    \begin{macrocode}
\def\RequireLuaModule#1{\directlua{require("#1")}\@gobbleoptarg}
%    \end{macrocode}
%
% In \LaTeX\ (or plain macro package that has defined |\@ifnextchar|)
% use  |\@ifnextchar| otherwise use a simple alternative, in practice this
% will never be followed by a brace group, so full version of |\@ifnextchar|
% not needed.
%    \begin{macrocode}
\ifdefined\@ifnextchar
\def\@gobbleoptarg{\@ifnextchar[\@gobble@optarg{}}%
\else
\long\def\@gobbleoptarg#1{\ifx[#1\expandafter\@gobble@optarg\fi#1}%
\fi
%    \end{macrocode}
%
%    \begin{macrocode}
\def\@gobble@optarg[#1]{}
%    \end{macrocode}
%
% Extended catcode table support.  Use the names from the previous
% \textsf{luatexbase} and \textsf{luatex} packages.
%    \begin{macrocode}
\let\CatcodeTableIniTeX\catcodetable@initex
\let\CatcodeTableString\catcodetable@string
\let\CatcodeTableLaTeX\catcodetable@latex
\let\CatcodeTableLaTeXAtLetter\catcodetable@atletter
%    \end{macrocode}
%
% Additional tables declared in the previous interface.
%    \begin{macrocode}
\newcatcodetable\CatcodeTableOther
\@setcatcodetable\CatcodeTableOther{%
  \catcodetable\CatcodeTableString
  \catcode32 12 }
%    \end{macrocode}
%
%    \begin{macrocode}
\newcatcodetable\CatcodeTableExpl
\@setcatcodetable\CatcodeTableExpl{%
  \catcodetable\CatcodeTableLaTeX
  \catcode126 10 % tilde is a space char
  \catcode32  9  % space is ignored
  \catcode9   9  % tab also ignored
  \catcode95  11 % underscore letter
  \catcode58  11 % colon letter
}
%    \end{macrocode}
%
% Top level access to catcodetable stack.
%    \begin{macrocode}
\def\BeginCatcodeRegime#1{%
  \@pushcatcodetable
  \catcodetable#1\relax}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\EndCatcodeRegime{%
  \@popcatcodetable}
%    \end{macrocode}
%
% The implementation of the stack is completely
% different, but usage should match.
%    \begin{macrocode}
\let\PushCatcodeTableNumStack\@pushcatcodetable
\let\PopCatcodeTableNumStack\@popcatcodetable
%    \end{macrocode}
%
% A simple copy.
%    \begin{macrocode}
\let\SetCatcodeRange\@setrangecatcode
%    \end{macrocode}
%
% Another copy.
%    \begin{macrocode}
\let\setcatcodetable\@setcatcodetable
%    \end{macrocode}
%
% \subsubsection{Additional lua code}
%    \begin{macrocode}
\directlua{
%    \end{macrocode}
%
% Remove all registered callbacks, then disable.
% Set to false if optional second argument is |true|.
%    \begin{macrocode}
function luatexbase.reset_callback(name,make_false)
  for _,v in pairs(luatexbase.callback_descriptions(name))
  do
    luatexbase.remove_from_callback(name,v)
  end
  if make_false == true then
    luatexbase.disable_callback(name)
  end
end
%    \end{macrocode}
%
% Allow exclusive callbacks to be over-written if priority argument is
% 1 to match the ``first'' semantics of the original package.
% \changes{v1.1}{2015/10/02}{Fully handle priority argument}
%
% First save the kernel function.
%    \begin{macrocode}
luatexbase.base_add_to_callback=luatexbase.add_to_callback
%    \end{macrocode}
%
% Implement the priority argument by taking off existing callbacks
% that have higher priority than the new one, adding the new one, 
% Then putting the saved callbacks back.
%    \begin{macrocode}
function luatexbase.add_to_callback(name,fun,description,priority)
%    \end{macrocode}
%^^A  texio.write_nl('\string\n HERE: adding ' .. 
%^^A                  description .. 
%^^A                  ' to ' ..
%^^A                  name .. 
%^^A                  ' with priority ' ..
%^^A                  (priority or '@@@'))
%^^A    texio.write_nl('Original list')
%^^A    for k,v in pairs(luatexbase.callback_descriptions(name)) do
%^^A      texio.write_nl('    ' .. k .. ': ' .. v)
%^^A    end
%    \begin{macrocode}
  local priority= priority
  if priority==nil then
   priority=\string#luatexbase.callback_descriptions(name)+1
  end
  if(luatexbase.callbacktypes[name] == 3 and
     priority == 1 and
     \string#luatexbase.callback_descriptions(name)==1) then
    luatexbase.module_warning("luatexbase",
                              "resetting exclusive callback: " .. name)
    luatexbase.reset_callback(name)
  end
  local saved_callback={},ff,dd
  for k,v in pairs(luatexbase.callback_descriptions(name)) do
    if k >= priority then
      ff,dd= luatexbase.remove_from_callback(name, v)
      saved_callback[k]={ff,dd}
    end
  end
  luatexbase.base_add_to_callback(name,fun,description)
  for k,v in pairs(saved_callback) do
    luatexbase.base_add_to_callback(name,v[1],v[2])
  end
%    \end{macrocode}
%^^A  texio.write_nl('New list')
%^^A  for k,v in pairs(luatexbase.callback_descriptions(name)) do
%^^A    texio.write_nl('    ' .. k .. ': ' .. v)
%^^A  end
%    \begin{macrocode}
  return
end
%    \end{macrocode}
%
% Emulate the catcodetables table.
% Explicitly fill the table rather than rely on the metatable call to
% |registernumber| as that is unreliable on old Lua\TeX{}.
%    \begin{macrocode}
luatexbase.catcodetables=setmetatable(
 {['latex-package'] = \number\CatcodeTableLaTeXAtLetter,
  ini    = \number\CatcodeTableIniTeX,
  string = \number\CatcodeTableString,
  other  = \number\CatcodeTableOther,
  latex  = \number\CatcodeTableLaTeX,
  expl   = \number\CatcodeTableExpl,
  expl3  = \number\CatcodeTableExpl},
 { __index = function(t,key)
    return luatexbase.registernumber(key) or nil
  end}
)}
%    \end{macrocode}
%
% On old Lua\TeX{} workaround hashtable issues.
% Allocate in \TeX{}, and also directly add to |luatexbase.catcodetables|.
%    \begin{macrocode}
\ifnum\luatexversion<80 %
\def\newcatcodetable#1{%
  \e@alloc\catcodetable\chardef
    \e@alloc@ccodetable@count\m@ne{"8000}#1%
  \initcatcodetable\allocationnumber
  {\escapechar=\m@ne
  \directlua{luatexbase.catcodetables['\string#1']=%
    \the\allocationnumber}}%
}
\fi
%    \end{macrocode}
%
%    \begin{macrocode}
\directlua{
%    \end{macrocode}
%
% |priority_in_callback| returns position in the callback list.
% Not provided by default by the kernel as usually it is just used 
% as a boolean test, for which |in_callback| is provided.
%    \begin{macrocode}
function luatexbase.priority_in_callback (name,description)
  for i,v in ipairs(luatexbase.callback_descriptions(name))
  do
    if v == description then
      return i
    end
  end
  return false
end
%    \end{macrocode}
% 
% The (unreleased) version~0.7 of \textsf{luatexbase} provided this
% boolean test under a different name, so we provide an alias here.
%    \begin{macrocode}
luatexbase.is_active_callback = luatexbase.in_callback
%    \end{macrocode}
%
% \textsf{ltluatex} implementation of |provides_module| does not return
% print functions so define modified version here.
% \changes{v1.3}{2015/10/03}{Use the first argument as a format string for
%   later arguments}
%    \begin{macrocode}
luatexbase.base_provides_module=luatexbase.provides_module
function luatexbase.errwarinf(name)
    return
    function(s,...) return luatexbase.module_error(name, s:format(...)) end,
    function(s,...) return luatexbase.module_warning(name, s:format(...)) end,
    function(s,...) return luatexbase.module_info(name, s:format(...)) end,
    function(s,...) return luatexbase.module_info(name, s:format(...)) end
end
function luatexbase.provides_module(info)
  luatexbase.base_provides_module(info)
  return luatexbase.errwarinf(info.name)
end
}
%    \end{macrocode}
%
% Same for attribute table as catcode tables. In old Lua\TeX{}, add to the
% |luatexbase.attributes| table directly.
%    \begin{macrocode}
\ifnum\luatexversion<80 %
\def\newattribute#1{%
  \e@alloc\attribute\attributedef
    \e@alloc@attribute@count\m@ne\e@alloc@top#1%
  {\escapechar=\m@ne
  \directlua{luatexbase.attributes['\string#1']=%
    \the\allocationnumber}}%
}
\fi
%    \end{macrocode}
%
% Define a safe percent command for plain \TeX.
%    \begin{macrocode}
\ifx\@percentchar\@undefined
  {\catcode`\%=12 \gdef\@percentchar{%}}
\fi
%    \end{macrocode}
% \changes{v1.2a}{2015/10/03}{Add missing local definitions for whatsit code}
%
%    \begin{macrocode}
%<*whatsit>
\directlua{%
%    \end{macrocode}
%
%    \begin{macrocode}
local copynode          = node.copy
local newnode           = node.new
local nodesubtype       = node.subtype
local nodetype          = node.id
local stringformat      = string.format
local tableunpack       = unpack or table.unpack
local texiowrite_nl     = texio.write_nl
local texiowrite        = texio.write
local whatsit_t         = nodetype"whatsit"
local user_defined_t    = nodesubtype"user_defined"
local unassociated      = "__unassociated"
local user_whatsits       = {  __unassociated = { } }
local whatsit_ids         = { }
local anonymous_whatsits  = 0
local anonymous_prefix    = "anon"
%    \end{macrocode}
%
%    User whatsit allocation is split into two functions:
%    \verb|new_user_whatsit_id| registers a new id (an integer)
%    and returns it. This is a wrapper around \verb|new_whatsit|
%    but with the extra \texttt{package} argument, and recording
%    the mapping in lua tables
%
%    If no name given, generate a name from a counter.
%
%    \begin{macrocode}
local new_user_whatsit_id = function (name, package)
    if name then
        if not package then
            package = unassociated
        end
    else % anonymous
        anonymous_whatsits = anonymous_whatsits + 1
        warning("defining anonymous user whatsit no. \@percentchar 
                  d", anonymous_whatsits)
        package = unassociated
        name    = anonymous_prefix .. tostring(anonymous_whatsits)
    end

    local whatsitdata = user_whatsits[package]
    if not whatsitdata then
        whatsitdata             = { }
        user_whatsits[package]  = whatsitdata
    end

    local id = whatsitdata[name]
    if id then %- warning
        warning("replacing whatsit \@percentchar s:\@percentchar 
                  s (\@percentchar d)", package, name, id)
    else %- new id
        id=luatexbase.new_whatsit(name)
        whatsitdata[name]   = id
        whatsit_ids[id]     = { name, package }
    end
    return id
end
luatexbase.new_user_whatsit_id = new_user_whatsit_id
%    \end{macrocode}
%
%    \verb|new_user_whatsit| first registers a new id and then also
%    creates the corresponding whatsit node of subtype “user-defined”.
%    Return a nullary function that delivers copies of the whatsit.
%
%    Alternatively, the first argument can be a whatsit node that
%    will then be used as prototype.
%
%    \begin{macrocode}
local new_user_whatsit = function (req, package)
    local id, whatsit
    if type(req) == "string" then
        id              = new_user_whatsit_id(req, package)
        whatsit         = newnode(whatsit_t, user_defined_t)
        whatsit.user_id = id
    elseif req.id == whatsit_t and req.subtype == user_defined_t then
        id      = req.user_id
        whatsit = copynode(req)
        if not whatsit_ids[id] then
            warning("whatsit id \@percentchar d unregistered; "
                    .. "inconsistencies may arise", id)
        end
    end
    return function () return copynode(whatsit) end, id
end
luatexbase.new_user_whatsit         = new_user_whatsit
%    \end{macrocode}
%
%    If one knows the name of a user whatsit, its corresponding id
%    can be retrieved by means of \verb|get_user_whatsit_id|.
%
%    \begin{macrocode}
local get_user_whatsit_id = function (name, package)
    if not package then
        package = unassociated
    end
    return user_whatsits[package][name]
end
luatexbase.get_user_whatsit_id = get_user_whatsit_id
%    \end{macrocode}
%
%    The inverse lookup is also possible via \verb|get_user_whatsit_name|.
%    \begin{macrocode}
local get_user_whatsit_name = function (asked)
    local id
    if type(asked) == "number" then
        id = asked
    elseif type(asked) == "function" then
        %- node generator
        local n = asked()
        id = n.user_id
    else %- node
        id = asked.user_id
    end
    local metadata = whatsit_ids[id]
    if not metadata then % unknown
        warning("whatsit id \@percentchar d unregistered;
                   inconsistencies may arise", id)
        return "", ""
    end
    return tableunpack(metadata)
end
luatexbase.get_user_whatsit_name = get_user_whatsit_name
%    \end{macrocode}
%  A function that outputs the
%    current allocation status to the terminal.
%
%    \begin{macrocode}
local dump_registered_whatsits = function (asked_package)
    local whatsit_list = { }
    if asked_package then
        local whatsitdata = user_whatsits[asked_package]
        if not whatsitdata then
            error("(no user whatsits registered for package
                      \@percentchar s)", asked_package)
            return
        end
        texiowrite_nl("(user whatsit allocation stats for " ..
                          asked_package)
        for name, id in next, whatsitdata do
            whatsit_list[\string#whatsit_list+1] =
                stringformat("(\@percentchar s:\@percentchar 
                     s \@percentchar d)", asked_package, name, id)
        end
    else
        texiowrite_nl("(user whatsit allocation stats")
        texiowrite_nl(stringformat(" ((total \@percentchar d)\string\n
                         (anonymous \@percentchar d))",
            current_whatsit, anonymous_whatsits))
        for package, whatsitdata in next, user_whatsits do
            for name, id in next, whatsitdata do
                whatsit_list[\string#whatsit_list+1] =
                    stringformat("(\@percentchar s:\@percentchar
                        s \@percentchar d)", package, name, id)
            end
        end
    end
    texiowrite_nl" ("
    local first = true
    for i=1, \string#whatsit_list do
        if first then
            first = false
        else % indent
            texiowrite_nl"  "
        end
        texiowrite(whatsit_list[i])
    end
    texiowrite"))\string\n"
end
luatexbase.dump_registered_whatsits = dump_registered_whatsits
%    \end{macrocode}
%    Lastly, we define a couple synonyms for convenience.
%    \begin{macrocode}
luatexbase.newattribute            = new_attribute
luatexbase.newuserwhatsit          = new_user_whatsit
luatexbase.newuserwhatsitid        = new_user_whatsit_id
luatexbase.getuserwhatsitid        = get_user_whatsit_id
luatexbase.getuserwhatsitname      = get_user_whatsit_name
luatexbase.dumpregisteredwhatsits  = dump_registered_whatsits
%    \end{macrocode}
%
%    \begin{macrocode}
}
%</whatsit>
%    \end{macrocode}
%
% Resolve name clashes and prefixed name issues.
%
% Top level \textsf{luatexbase} macros
%    \begin{macrocode}
\let\newluatexattribute\newattribute
\let\setluatexattribute\setattribute
\let\unsetluatexattribute\unsetattribute
\let\newluatexcatcodetable\newcatcodetable
\let\setluatexcatcodetable\setcatcodetable
%    \end{macrocode}
%
% Internal \textsf{luatexbase} macros
%    \begin{macrocode}
\let\luatexbase@directlua\directlua
\let\luatexbase@ensure@primitive\@gobble
%    \end{macrocode}
%
% Lua\TeX\ primitives
%    \begin{macrocode}
\let\luatexattribute\attribute
\let\luatexattributedef\attributedef
\let\luatexcatcodetable\catcodetable
\let\luatexluaescapestring\luaescapestring
\let\luatexlatelua\latelua
\let\luatexoutputbox\outputbox
\let\luatexscantextokens\scantextokens
%    \end{macrocode}
%
% Reset catcode of |@|.
%    \begin{macrocode}
\catcode`\@=\emuatcatcode\relax
%    \end{macrocode}
%
%    \begin{macrocode}
%</emu>
%    \end{macrocode}
%
% \subsection{Legacy \textsf{luatexbase} sub-packages}
%
% The original \textsf{luatexbase} was comprised of seven sub packages
% that could in principle be loaded separately. Here we define them all
% with the same code that just loads the main package, they are
% distinguished just by the |\ProvidesPackage| specified above at the start
% of the file.
%    \begin{macrocode}
%<*emu-cmp,emu-mod,emu-loa,emu-reg,emu-att,emu-cct,emu-mcb>
%    \end{macrocode}
%
%    \begin{macrocode}
\ifx\RequirePackage\undefined
  \input{luatexbase.sty}%
\else
  \RequirePackage{luatexbase}
\fi
%    \end{macrocode}
%
%    \begin{macrocode}
%</emu-cmp,emu-mod,emu-loa,emu-reg,emu-att,emu-cct,emu-mcb>
%    \end{macrocode}
%
% \subsection{Legacy Lua code}
%
% \changes{v1.2}{2015/10/03}{Provide \texttt{luatexbase.loader.lua}}
%
% The original \textsf{luatexbase} included a file |luatexbase.loader.lua|
% that could be loaded independently of the rest of the package. This really
% doesn't need to do anything!
%    \begin{macrocode}
%<*emu-lua>
%    \end{macrocode}
%
%    \begin{macrocode}
luatexbase = luatexbase or { }
%    \end{macrocode}
%
%    \begin{macrocode}
%</emu-lua>
%    \end{macrocode}
%
% \Finale
