% \iffalse meta-comment % % Written in 2009, 2010 by Manuel Pégourié-Gonnard and Élie Roux. % % % % 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 % test-modutils-plain.tex test-modutils-latex.tex test-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 % %<*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-modutils.sty}{\from{luatexbase-modutils.dtx}{texpackage}}% } \generate{% \usedir{doc/luatex/luatexbase}% \file{test-modutils-plain.tex}{\from{luatexbase-modutils.dtx}{testplain}}% \file{test-modutils-latex.tex}{\from{luatexbase-modutils.dtx}{testlatex}}% } \def\MetaPrefix{-- } \def\luapostamble{% \MetaPrefix^^J% \MetaPrefix\space End of File `\outFileName'.% } \def\currentpostamble{\luapostamble}% \generate{% \usedir{tex/luatex/luatexbase}% \file{luatexbase.modutils.lua}{\from{luatexbase-modutils.dtx}{luamodule}}% \usedir{doc/luatex/luatexbase}% \file{test-modutils.lua}{\from{luatexbase-modutils.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-modutils.sty luatexbase.modutils.lua} \Msg{*} \Msg{* Happy TeXing!} \Msg{*} \Msg{************************************************************************} \endbatchfile % %<*ignore> \fi % %<*driver> \documentclass{ltxdoc} \input{lltxb-dtxstyle} \begin{document} \DocInput{luatexbase-modutils.dtx}% \end{document} % % \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} % % 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@modutils@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-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. Aborting.} \lltxb@modutils@AtEnd \expandafter\endinput \fi % \end{macrocode} % % Load the package loader. % % \begin{macrocode} \begingroup\expandafter\expandafter\expandafter\endgroup \expandafter\ifx\csname RequirePackage\endcsname\relax \input luatexbase-loader.sty \else \RequirePackage{luatexbase-loader} \fi % \end{macrocode} % % \subsubsection{Main code} % % 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{\directlua{luatextra.use_module([[#1]])}} \expandafter\ifx\csname ProvidesPackage\endcsname\relax \def\luatexRequireModule#1#2{\directlua{% 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]{\directlua{luatextra.require_module([[#2]], [[#1]])}} \fi % \end{macrocode} % % \begin{macrocode} \directlua{dofile(assert(kpse.find_file('luatexbase.modutils.lua', 'tex')))} % \end{macrocode} % % \begin{macrocode} \lltxb@modutils@AtEnd % % \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 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 require(name) 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 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} % % \end{macrocode} % % \section{Test files} % % A dummy lua file for tests. % % \begin{macrocode} %<*testdummy> luatextra.provides_module { name = 'test-modutils', date = '2000/01/01', version = 1, description = 'dummy test package', } % % \end{macrocode} % % We just check that the package loads properly, under both LaTeX and Plain % TeX. Anyway, the test files of other modules using this one already are a % test\dots % % \begin{macrocode} %\input luatexbase-modutils.sty %\RequirePackage{luatexbase-modutils} %<*testplain,testlatex> \luatexRequireModule %[1970/01/01] {test-modutils} %{1970/01/01} % %\bye %\stop % \end{macrocode} % % \Finale \endinput