% \iffalse meta-comment
%
% Written in 2009, 2010 by Manuel Pégourié-Gonnard and Élie Roux.
%     <mpg@elzevir.fr>
%     <elie.roux@telecom-bretagne.eu>
%
% This work is under the CC0 license.
%
% This work consists of the main source file luatexbase-loader.dtx
% and the derived files
%    luatexbase-loader.sty luatexbase.loader.lua
%    test-loader-plain.tex test-loader-latex.tex
%
% Unpacking:
%    tex luatexbase-loader.dtx
% Documentation:
%    pdflatex luatexbase-loader.dtx
%
%<*ignore>
\begingroup
  \def\x{LaTeX2e}%
\expandafter\endgroup
\ifcase 0\ifx\install y1\fi\expandafter
         \ifx\csname processbatchFile\endcsname\relax\else1\fi
         \ifx\fmtname\x\else 1\fi\relax
\else\csname fi\endcsname
%</ignore>
%<*install>
\input docstrip.tex

\keepsilent
\askforoverwritefalse

\let\MetaPrefix\relax

\preamble

Written in 2009, 2010 by Manuel Pegourie-Gonnard and Elie Roux.

This work is under the CC0 license.
See source file '\inFileName' for details.

\endpreamble

\let\MetaPrefix\DoubleperCent

\generate{%
  \usedir{tex/luatex/luatexbase}%
  \file{luatexbase-loader.sty}{\from{luatexbase-loader.dtx}{texpackage}}%
}

\generate{%
  \usedir{doc/luatex/luatexbase}%
  \file{test-loader-plain.tex}{\from{luatexbase-loader.dtx}{testplain}}%
  \file{test-loader-latex.tex}{\from{luatexbase-loader.dtx}{testlatex}}%
}

\def\MetaPrefix{-- }

\def\luapostamble{%
  \MetaPrefix^^J%
  \MetaPrefix\space End of File `\outFileName'.%
}

\def\currentpostamble{\luapostamble}%

\generate{%
  \usedir{tex/luatex/luatexbase}%
  \file{luatexbase.loader.lua}{\from{luatexbase-loader.dtx}{luamodule}}%
  \usedir{doc/luatex/luatexbase}%
  \file{test-loader.lua}{\from{luatexbase-loader.dtx}{testdummy}}%
  \file{test-loader.sub.lua}{\from{luatexbase-loader.dtx}{testdummy}}%
}

\obeyspaces
\Msg{************************************************************************}
\Msg{*}
\Msg{* To finish the installation you have to move the following}
\Msg{* files into a directory searched by TeX:}
\Msg{*}
\Msg{*     luatexbase-loader.sty luatex.loader.lua}
\Msg{*}
\Msg{* Happy TeXing!}
\Msg{*}
\Msg{************************************************************************}

\endbatchfile
%</install>
%<*ignore>
\fi
%</ignore>
%<*driver>
\documentclass{ltxdoc}
\input{lltxb-dtxstyle}
\begin{document}
  \DocInput{luatexbase-loader.dtx}%
\end{document}
%</driver>
% \fi
%
% \CheckSum{0}
%
% \CharacterTable
%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%   Digits        \0\1\2\3\4\5\6\7\8\9
%   Exclamation   \!     Double quote  \"     Hash (number) \#
%   Dollar        \$     Percent       \%     Ampersand     \&
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \<
%   Equals        \=     Greater than  \>     Question mark \?
%   Commercial at \@     Left bracket  \[     Backslash     \\
%   Right bracket \]     Circumflex    \^     Underscore    \_
%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%   Right brace   \}     Tilde         \~}
%
% \pkdate{luatexbase-loader}{v0.3 2010-05-27}
%
% \maketitle
%
% \begin{abstract}
% Lua modules are loaded using the |require()| function which, similarly to
% \tex's |\input|, takes care of locating the file and load it, but also makes
% a few supplementary checks, for example to avoid loading the same module
% twice. This package adapts the way the files are searched in order to
% accommodate the TDS as well as usual Lua naming conventions.
%
% For higher-level functions related to Lua modules, see
% \href{file:luatexbase-modutils.pdf}{\pk{luatexbase-modutils}}, which also
% loads the present package.
% \end{abstract}
%
% \section{Documentation}
%
% Starting with \luatex 0.45.0, |require()| uses Kpathsea for file searching
% when the library is initialised (which is always the case in \tex mode,
% unless explicitly disabled by the user). However, it does not respect the
% Lua convention that |require("foo.bar")| should look for |foo/bar.lua|.
% \footnote{Support for that has been added in rev 3558 of \luatex, currently
% unreleased but probably part of \luatex 0.54.} This package implements such
% behaviour. 
%
% More precisely, it implements a new kpse searcher that looks for file
% |foo/bar| using Kpathsea with the format |lua| (that is, search along
% |LUAINPUTS| and try the following extensions:  |.luc|, |.luctex|, |.texluc|,
% |.lua|, |.luatex|, |.texlua|). If this search fails, it falls back to
% |foo.bar| (along the same path with the same extensions).
%
% Also, older versions of \luatex, such as 0.25.4 (\texlive 2008), don't know
% about the |lua| format for kpse searching. So, an emulator for this function
% is provided. The emulator is not perfect, in particular it may find more
% results than the normal |lua| format search.\footnote{An may also fail to
% find the file in particular cases, see comments in the implementation for
% details.} In order to ensure more homogeneous results across versions, this
% emulator is used as a fall-back when the real |lua| format search doesn't
% find any result.
%
% Finally, a combined version of this new kpse searcher and the original
% function at |package.loaders[2]| (using first the new loader, then the old
% one if the new doesn't return any result) is installed as
% |package.loaders[2]|.
%
%    \section{Implementation}
%
%    \subsection{\tex package}
%
%    \begin{macrocode}
%<*texpackage>
%    \end{macrocode}
%
%    \subsubsection{Preliminaries}
%
%    Reload protection, especially for \plaintex.
%
%    \begin{macrocode}
                \csname lltxb@loader@loaded\endcsname
\expandafter\let\csname lltxb@loader@loaded\endcsname\endinput
%    \end{macrocode}
%
%    Catcode defenses.
%
%    \begin{macrocode}
\begingroup
  \catcode123 1 % {
  \catcode125 2 % }
  \catcode 35 6 % #
  \toks0{}%
  \def\x{}%
  \def\y#1 #2 {%
    \toks0\expandafter{\the\toks0 \catcode#1 \the\catcode#1}%
    \edef\x{\x \catcode#1 #2}}%
  \y 123 1  % {
  \y 125 2  % }
  \y  35 6  % #
  \y  10 12 % ^^J
  \y  34 12 % "
  \y  36 3  % $ $
  \y  39 12 % '
  \y  40 12 % (
  \y  41 12 % )
  \y  42 12 % *
  \y  43 12 % +
  \y  44 12 % ,
  \y  45 12 % -
  \y  46 12 % .
  \y  47 12 % /
  \y  60 12 % <
  \y  61 12 % =
  \y  64 11 % @ (letter)
  \y  62 12 % >
  \y  95 12 % _ (other)
  \y  96 12 % `
  \edef\y#1{\endgroup\edef#1{\the\toks0\relax}\x}%
\expandafter\y\csname lltxb@loader@AtEnd\endcsname
%    \end{macrocode}
%
%    Package declaration.
%
%    \begin{macrocode}
\begingroup
  \expandafter\ifx\csname ProvidesPackage\endcsname\relax
    \def\x#1[#2]{\immediate\write16{Package: #1 #2}}
  \else
    \let\x\ProvidesPackage
  \fi
\expandafter\endgroup
\x{luatexbase-loader}[2010/10/10 v0.3 Lua module loader for LuaTeX]
%    \end{macrocode}
%
%    Make sure \luatex is used.
%
%    \begin{macrocode}
\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname RequirePackage\endcsname\relax
  \input ifluatex.sty
\else
  \RequirePackage{ifluatex}
\fi
\ifluatex\else
  \begingroup
    \expandafter\ifx\csname PackageError\endcsname\relax
      \def\x#1#2#3{\begingroup \newlinechar10
        \errhelp{#3}\errmessage{Package #1 error: #2}\endgroup}
    \else
      \let\x\PackageError
    \fi
  \expandafter\endgroup
  \x{luatexbase-attr}{LuaTeX is required for this package. Aborting.}{%
    This package can only be used with the LuaTeX engine^^J%
    (command `lualatex' or `luatex').^^J%
    Package loading has been stopped to prevent additional errors.}
  \lltxb@loader@AtEnd
  \expandafter\endinput
\fi
%    \end{macrocode}
%
%    \subsubsection{Main content}
%
%    First load \pk{luatexbase-compat}.
%
%    \begin{macrocode}
\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname RequirePackage\endcsname\relax
  \input luatexbase-compat.sty
\else
  \RequirePackage{luatexbase-compat}
\fi
%    \end{macrocode}
%
%    Load the supporting Lua module. This one doesn't follow the usual naming
%    conventions, since it won't be loaded with the usual functions for
%    obvious bootstraping reasons.
%
%    \begin{macrocode}
\luatexbase@directlua{%
  local file = "luatexbase.loader.lua"
  local path = assert(kpse.find_file(file, 'tex'),
    "File '"..file.."' no found")
  texio.write_nl("("..path..")")
  dofile(path)}
%    \end{macrocode}
%
%    That's all, folks!
%
%    \begin{macrocode}
\lltxb@loader@AtEnd
%</texpackage>
%    \end{macrocode}
%
%    \subsection{Lua module}
%
%    \begin{macrocode}
%<*luamodule>
module('luatexbase', package.seeall)
%    \end{macrocode}
%
%    Emulate (approximatively) kpse's lua format search. More precisely,
%    combine the search path of |texmfscripts| and |tex| in order to
%    approximate |LUAINPUTS|. But we need to handle suffixes ourselves.
%
%    |lua_suffixes| is taken verbatim from Kpathsea's source
%    (\file{tex-file.c}, constant |LUA_SUFFIXES|),\footnote{Unchanged since
%    2007-07-06, last checked 2010-05-10.}.
%
%    \begin{macrocode}
local lua_suffixes = {
  ".luc", ".luctex", ".texluc", ".lua", ".luatex", ".texlua",
}
%    \end{macrocode}
%
%    Auxiliary function for suffixes: says if |suffix| is a suffix of |name|.
%
%    \begin{macrocode}
local function ends_with(suffix, name)
    return name:sub(-suffix:len()) == suffix
end
%    \end{macrocode}
%
%    The search function first builds the list of filenames to be search. For
%    the lua format, kpse always adds a suffix if no (known) suffix is
%    present, so we do the same.
%
%    \begin{macrocode}
function find_file_lua_emul(name)
  local search_list = {}
  for _, suffix in ipairs(lua_suffixes) do
    if ends_with(suffix, name) then
      search_list = { name }
      break
    else
      table.insert(search_list, name..suffix)
    end
  end
%    \end{macrocode}
%
%    Now look for each file in this list.
%
%    \begin{macrocode}
  for _, search_name in ipairs(search_list) do
    local f = kpse.find_file(search_name, 'texmfscripts')
      or kpse.find_file(search_name, 'tex')
%    \end{macrocode}
%
%    There is a problem with using the |tex| search format: kpse will try to
%    add suffixes from the |TEX_SUFFIXES| list, which may lead to problems
%    if a file like \meta{name}|.lua.tex| exists. We prevent that by checking if
%    the file found ends with the required name. So \meta{name}|.lua| will
%    still be hidden by \meta{name}.|lua.tex| but it seems less bad not to
%    find it than to return an incorrect result.
%
%    \begin{macrocode}
    if f and ends_with(search_name, f) then
      return f
    end
  end
end
%    \end{macrocode}
%
%    If lua search format is available, use it with emulation as a fall-back,
%    or just use emulation.
%
%    \begin{macrocode}
local find_file_lua
if pcall('kpse.find_file', 'dummy', 'lua') then
  find_file_lua = function (name)
    return kpse.find_file(name, 'lua') or find_file_lua_emul(name)
  end
else
  find_file_lua = function (name)
    return find_file_lua_emul(name)
  end
end
%    \end{macrocode}
%
%    Find the full path corresponding to a module name.
%
%    \begin{macrocode}
local function find_module_file(mod)
  return find_file_lua(mod:gsub('%.', '/'), 'lua')
    or find_file_lua(mod, 'lua')
end
%    \end{macrocode}
%
%    Combined searcher, using primarily the new kpse searcher, and the
%    original as a fall-back.
%
%    \begin{macrocode}
local package_loader_two = package.loaders[2]
local function load_module(mod)
  local file = find_module_file(mod)
  if not file then
    local msg = "\n\t[luatexbase.loader] Search failed"
    local ret = package_loader_two(mod)
    if type(ret) == 'string' then
      return msg..ret
    elseif type(ret) == 'nil' then
      return msg
    else
      return ret
    end
  end
  local loader, error = loadfile(file)
  if not loader then
    return "\n\t[luatexbase.loader] Loading error:\n\t"..error
  end
  texio.write_nl("("..file..")")
  return loader
end
%    \end{macrocode}
%
%    Finally install this combined loader as loader 2.
%
%    \begin{macrocode}
package.loaders[2] = load_module
%</luamodule>
%    \end{macrocode}
%
%    \section{Test files}
%
%    A dummy lua file for tests.
%
%    \begin{macrocode}
%<*testdummy>
return true
%</testdummy>
%    \end{macrocode}
%
%    Check that the package loads properly, under both LaTeX and Plain TeX,
%    and load a dummy module in the current diretory.
%
%    \begin{macrocode}
%<testplain>\input luatexbase-loader.sty
%<testlatex>\RequirePackage{luatexbase-loader}
%<*testplain,testlatex>
\catcode64 11
\luatexbase@directlua{require "test-loader"}
\luatexbase@directlua{require "test-loader.sub"}
%</testplain,testlatex>
%<testplain>\bye
%<testlatex>\stop
%    \end{macrocode}
%
% \Finale
\endinput
