From 35474b9cf148bba81870c1361bfac31cf8c7c7bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= <mpg@elzevir.fr>
Date: Sat, 27 Mar 2010 22:13:18 +0100
Subject: Initial version of modutils.

---
 luatexbase-modutils.dtx | 513 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 513 insertions(+)
 create mode 100644 luatexbase-modutils.dtx

(limited to 'luatexbase-modutils.dtx')

diff --git a/luatexbase-modutils.dtx b/luatexbase-modutils.dtx
new file mode 100644
index 0000000..1593a3b
--- /dev/null
+++ b/luatexbase-modutils.dtx
@@ -0,0 +1,513 @@
+% \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-modutils.dtx
+% and the derived files
+%    luatexbase-modutils.sty modutils.lua ...
+%
+% Unpacking:
+%    tex luatexbase-modutils.dtx
+% Documentation:
+%    pdflatex luatexbase-modutils.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
+This is a generated file.
+
+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-modutils.dtx
+and the derived files
+   luatexbase-modutils.sty modutils.lua ...
+
+\endpreamble
+
+\let\MetaPrefix\DoubleperCent
+
+\generate{%
+  \usedir{tex/luatex/luatexbase}%
+  \file{luatexbase-modutils.sty}{\from{luatexbase-modutils.dtx}{texpackage}}%
+}
+
+\def\MetaPrefix{-- }
+
+\def\luapostamble{%
+  \MetaPrefix^^J%
+  \MetaPrefix\space End of File `\outFileName'.%
+}
+
+\def\currentpostamble{\luapostamble}%
+
+\generate{%
+  \usedir{tex/luatex/luatexbase}%
+  \file{modutils.lua}{\from{luatexbase-modutils.dtx}{luamodule}}%
+}
+
+\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-modutils.sty modutils.lua ...}
+\Msg{*}
+\Msg{* Happy TeXing!}
+\Msg{*}
+\Msg{************************************************************************}
+
+\endbatchfile
+%</install>
+%<*ignore>
+\fi
+%</ignore>
+%<*driver>
+\documentclass{ltxdoc}
+\input{lltxb-dtxstyle}
+\begin{document}
+  \DocInput{luatexbase-modutils.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         \~}
+%
+% \title{The \pk{luatexbase-modutils} package}
+% \date{v0.1 2010-03-27}
+% \author{%
+%  Manuel P\'egouri\'e-Gonnard \\ \texttt{mpg@elzevir.fr} \and
+%   \'Elie Roux \\ \texttt{elie.roux@telecom-bretagne.eu}}
+%
+% \maketitle
+%
+% \begin{abstract}
+% \end{abstract}
+%
+% \section{Documentation}
+%
+% Lua has some embedded module management, with the functions \texttt{module}
+% and \texttt{require}. With this package we try get more control on the
+% module system, by implementing something close to the \LaTeX 's
+% \texttt{\string\usepackage} and \texttt{\string\RequirePackage} macros: the
+% \texttt{\string\luatexUseModule} and \texttt{\string\luatexRequireModule}
+% that act like them, but for lua files. The functions \texttt{module} and
+% \texttt{require} should not be used, in profit of the lua functions
+% \texttt{luatextra.provides\_module} and \texttt{luatextra.use\_module} or
+% \texttt{luatextra.require\_module}.
+%
+%    \section{Implementation}
+%
+%    \subsection{\tex package}
+%
+%    \begin{macrocode}
+%<*texpackage>
+%    \end{macrocode}
+%
+%    \subsubsection{Preliminaries}
+%
+%    Reload protection, especially for \plaintex.
+%
+%    \begin{macrocode}
+                \csname lltxb@modutils@loaded\endcsname
+\expandafter\let\csname lltxb@modutils@loaded\endcsname\endinput
+%    \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-modutils}[2010/03/26 v0.1  Module utilities for LuaTeX  (mpg)]
+%    \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 PackageWarningNoLine\endcsname\relax
+      \def\x#1#2{\begingroup\newlinechar10
+        \immediate\write16{Package #1 warning: #2}\endgroup}
+    \else
+      \let\x\PackageWarningNoLine
+    \fi
+  \expandafter\endgroup
+  \x{luatexbase-modutils}{LuaTeX is required for this package.^^J
+    Aborting package loading.}
+  \expandafter\endinput
+\fi
+%    \end{macrocode}
+%
+%    Make sure the catcode of @ is correct, especially for \plaintex.
+%
+%    \begin{macrocode}
+\expandafter\edef\csname lltxb@modutils@AtEnd\endcsname{%
+  \catcode64 \the\catcode64\relax}
+\catcode64 11
+%    \end{macrocode}
+%
+%    \subsubsection{Module handling}
+%
+%    The \texttt{\string\luatexModuleError} macro is called by the lua function
+%    \texttt{luatextra.module\_error}. It is necessary because we can't call
+%    directly \texttt{\string\errmessage} in lua.
+%
+%    \begin{macrocode}
+
+\def\luatexModuleError#1#2{%
+  \errorcontextlines=0\relax
+  \immediate\write16{}%
+  \errmessage{Module #1 error: #2^^J^^J%
+See the module #1 documentation for explanation.^^J ...^^J}%
+}
+
+%    \end{macrocode}
+%
+%    Then we define
+%    \texttt{\string\luatexUseModule} that simply calls
+%    \texttt{luatextra.use\_module}.
+%    If the package is loaded with Plain, we define
+%    \texttt{\string\luaRequireModule} with two mandatory arguments.
+%
+%    \begin{macrocode}
+\def\luatexUseModule#1{\luadirect{luatextra.use_module([[#1]])}}
+
+\expandafter\ifx\csname ProvidesPackage\endcsname\relax
+  \def\luatexRequireModule#1#2{\luadirect{%
+      luatextra.require_module([[#1]], [[#2]])}}
+\else
+%    \end{macrocode}
+%
+%    If the package is loaded with \LaTeX , we define 
+%    \texttt{\string\luaRequireModule} with one mandatory
+%    argument (the name of the package) and one optional (the version or the
+%    date).
+%
+%    \begin{macrocode}
+  \newcommand\luatexRequireModule[2][0]{\luadirect{luatextra.require_module([[#2]], [[#1]])}}
+\fi
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+\lltxb@modutils@AtEnd
+%</texpackage>
+%    \end{macrocode}
+%
+%    \subsection{Lua module}
+%
+%    \begin{macrocode}
+%<*luamodule>
+%    \end{macrocode}
+%
+%    Initialisation borrowed from luatextra
+%
+%    \begin{macrocode}
+module("luatextra", package.seeall)
+
+local format = string.format
+
+luatextra.modules = luatextra.modules or {}
+%    \end{macrocode}
+%
+%    Here we define the warning and error functions specific to
+%    \texttt{luatextra}.
+%
+%    \begin{macrocode}
+
+luatextra.internal_warning_spaces = "                   "
+
+function luatextra.internal_warning(msg)
+    if not msg then return end
+    texio.write_nl(format("\nLuaTeXtra Warning: %s\n\n", msg))
+end
+
+luatextra.internal_error_spaces = "                 "
+
+function luatextra.internal_error(msg)
+    if not msg then return end
+    tex.sprint(format("\\immediate\\write16{}\\errmessage{LuaTeXtra error: %s^^J^^J}", msg))
+end
+
+%    \end{macrocode}
+%
+%    \subsubsection{Error, warning and info function for modules}
+%
+%    Some module printing functions are provided, they have the same
+%    philosophy as the \LaTeX 's \texttt{\string\PackageError} and
+%    \texttt{\string\PackageWarning} macros: their first argument is the name
+%    of the module, and the second is the message. These functions are meant
+%    to be used by lua module writers.
+%
+%    \begin{macrocode}
+
+function luatextra.module_error(package, msg, helpmsg)
+    if not package or not msg then
+        return
+    end
+    if helpmsg then
+        tex.sprint(format("\\errhelp{%s}", helpmsg))
+    end
+    tex.sprint(format("\\luatexModuleError{%s}{%s}", package, msg))
+end
+
+function luatextra.module_warning(modulename, msg)
+    if not modulename or not msg then
+        return
+    end
+    texio.write_nl(format("\nModule %s Warning: %s\n\n", modulename, msg))
+end
+
+function luatextra.module_log(modulename, msg)
+    if not modulename or not msg then
+        return
+    end
+    texio.write_nl('log', format("%s: %s", modulename, msg))
+end
+
+function luatextra.module_term(modulename, msg)
+    if not modulename or not msg then
+        return
+    end
+    texio.write_nl('term', format("%s: %s", modulename, msg))
+end
+
+function luatextra.module_info(modulename, msg)
+    if not modulename or not msg then
+        return
+    end
+    texio.write_nl(format("%s: %s\n", modulename, msg))
+end
+
+%    \end{macrocode}
+%
+%    \subsubsection{module loading and providing functions}
+%
+%    A small function to find a lua module file according to its name, with or
+%    without the \texttt{.lua} at the end of the filename.
+%
+%    \begin{macrocode}
+
+function luatextra.find_module_file(name)
+    if string.sub(name, -4) ~= '.lua' then
+        name = name..'.lua'
+    end
+    path = kpse.find_file(name, 'tex')
+    if not path then
+      path = kpse.find_file(name, 'texmfscripts')
+    end
+    return path, name
+end
+
+%    \end{macrocode}
+%
+%    A small patch, for the \texttt{module} function to work in this file. I
+%    can't understand why it doens't otherwise.
+%
+%    \begin{macrocode}
+
+luatextra.module = module
+
+%    \end{macrocode}
+%
+%    \begin{macro}{luatextra.use module}
+%
+%    This macro is the one used to simply load a lua module file. It does not
+%    reload it if it's already loaded, and prints the filename in the terminal
+%    and the log. A lua module must call the macro
+%    \texttt{luatextra.provides\_module}.
+%
+%    \begin{macrocode}
+
+
+function luatextra.use_module(name)
+    if not name or luatextra.modules[name] then
+        return
+    end
+    local path, filename = luatextra.find_module_file(name)
+    if not path then
+        luatextra.internal_error(format("unable to find lua module %s", name))
+    else
+        if path:sub(1,2) == "./" then
+            path = path:sub(3)
+        end
+        texio.write_nl('('..path)
+        dofile(path)
+        if not luatextra.modules[name] then
+            luatextra.internal_warning(format("You have requested module `%s',\n%s but the file %s does not provide it.", name, luatextra.internal_warning_spaces, filename))
+        end
+        if not package.loaded[name] then
+            luatextra.module(name, package.seeall)
+        end
+        texio.write(')')
+    end
+end
+
+%    \end{macrocode}
+%
+%    \end{macro}
+%
+%    Some internal functions to convert a date into a number, and to determine
+%    if a string is a date. It is useful for
+%    \texttt{luatextra.require\_package} to understand if a user asks a
+%    version with a date or a version number.
+%
+%    \begin{macrocode}
+
+function luatextra.datetonumber(date)
+    numbers = string.gsub(date, "(%d+)/(%d+)/(%d+)", "%1%2%3")
+    return tonumber(numbers)
+end
+
+function luatextra.isdate(date)
+    for _, _ in string.gmatch(date, "%d+/%d+/%d+") do
+        return true
+    end
+    return false
+end
+
+local date, number = 1, 2
+
+function luatextra.versiontonumber(version)
+    if luatextra.isdate(version) then
+        return {type = date, version = luatextra.datetonumber(version), orig = version}
+    else
+        return {type = number, version = tonumber(version), orig = version}
+    end
+end
+
+luatextra.requiredversions = {}
+
+%    \end{macrocode}
+%
+%    \begin{macro}{luatextra.require module}
+%
+%    This function is like the \texttt{luatextra.use\_module} function, but
+%    can accept a second argument that checks for the version of the module.
+%    The version can be a number or a date (format yyyy/mm/dd).
+%
+%    \begin{macrocode}
+
+function luatextra.require_module(name, version)
+    if not name then
+        return
+    end
+    if not version then
+        return luatextra.use_module(name)
+    end
+    luaversion = luatextra.versiontonumber(version)
+    if luatextra.modules[name] then
+        if luaversion.type == date then
+            if luatextra.datetonumber(luatextra.modules[name].date) < luaversion.version then
+                luatextra.internal_error(format("found module `%s' loaded in version %s, but version %s was required", name, luatextra.modules[name].date, version))
+            end
+        else
+            if luatextra.modules[name].version < luaversion.version then
+                luatextra.internal_error(format("found module `%s' loaded in version %.02f, but version %s was required", name, luatextra.modules[name].version, version))
+            end
+        end
+    else
+        luatextra.requiredversions[name] = luaversion
+        luatextra.use_module(name)
+    end
+end
+
+%    \end{macrocode}
+%
+%    \end{macro}
+%
+%    \begin{macro}{luatextra.provides module}
+%
+%    This macro is the one that must be called in the module files. It takes a
+%    table as argument. You can put any information you want in this table,
+%    but the mandatory ones are \texttt{name} (a string), \texttt{version} (a
+%    number), \texttt{date} (a string) and \texttt{description} (a string).
+%    Other fields are usually \texttt{copyright}, \texttt{author} and
+%    \texttt{license}.
+%
+%    This function logs informations about the module the same way \LaTeX\
+%    does for informations about packages.
+%
+%    \begin{macrocode}
+
+function luatextra.provides_module(mod)
+    if not mod then
+        luatextra.internal_error('cannot provide nil module')
+        return
+    end
+    if not mod.version or not mod.name or not mod.date or not mod.description then
+        luatextra.internal_error('invalid module registered, fields name, version, date and description are mandatory')
+        return
+    end
+    requiredversion = luatextra.requiredversions[mod.name]
+    if requiredversion then
+        if requiredversion.type == date and requiredversion.version > luatextra.datetonumber(mod.date) then
+            luatextra.internal_error(format("loading module %s in version %s, but version %s was required", mod.name, mod.date, requiredversion.orig))
+        elseif requiredversion.type == number and requiredversion.version > mod.version then
+            luatextra.internal_error(format("loading module %s in version %.02f, but version %s was required", mod.name, mod.version, requiredversion.orig))
+        end
+    end
+    luatextra.modules[mod.name] = module
+    texio.write_nl('log', format("Lua module: %s %s v%.02f %s\n", mod.name, mod.date, mod.version, mod.description))
+end
+
+%    \end{macrocode}
+%
+%    \end{macro}
+%
+%    \begin{macrocode}
+%</luamodule>
+%    \end{macrocode}
+%
+% \Finale
+\endinput
-- 
cgit v1.2.3

