% \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{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 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.2 2010-05-12} % \author{% % Manuel P\'egouri\'e-Gonnard \\ \email{mpg@elzevir.fr} \and % \'Elie Roux \\ \email{elie.roux@telecom-bretagne.eu}} % % \maketitle % % \begin{abstract} % This package provides functions similar to \latex's |\usepackage| and % |\ProvidesPackage| macros,\footnote{and their variants or synonyms such as % \cs{documentclass} and \cs{RequirePackage} or \cs{ProvidesClass} and % \cs{ProvidesFiles}} or more precisely the part of these macros that deals % with identification and version checking (no attempt is done at implementing % an option mechanism). Functions for error reporting are provided too. % % It also loads \pk{luatexbase-loader}. % \end{abstract} % % \tableofcontents % % \section{Documentation} % % Lua's standard function |require()| is similar to \tex's |\input| primitive % but is somehow more evolved in that it makes a few checks to avoid loading % the same module twice. In the \tex world, this needs to be taken care of by % macro packages; in the \latex world this is done by |\usepackage|. % % But |\usepackage| also takes care of many other things. Most notably, it % implements a complex option system, and does some identification and version % checking. The present package doesn't try to provide anything for options, % but implements a system for identification and version checking similar to % \latex's system. Both \tex macros and Lua functions are provided. % % This package also provides Lua functions for reporting errors, warnings, % etc. % % It is important to notice that Lua's standard function |module()| is % completely unrelated to the present package. It has nothing to do with % identification and deals only with namespaces.\footnote{More precisely, it % modifies the current environment.} So, you should continue to % use it normally, unlike the |require()| function which can be replaced with % this package's |luatexbase.require_module()|. % % \subsection{\tex macros} % % The two macros |\luatexUseModule| and |\luatexRequireModule| are very % similar and are interfaces to the Lua functions |use_module| and % |require_module|. The only difference between those macros is the number of % arguments (just as the underlying Lua functions): |\luatexUseModule| only % take one argument: the module name\footnote{without extension} while % |\luatexRequireModule| takes another argument for specifying a minimal % version (see below). With \latex, this argument is the first and is % optional. Otherwise, it's the second one and it's mandatory. % % \subsection{Lua functions} % % The main functions are |luatexbase.require_module| and % |luatexbase.use_module| which may be used as a replacement to |require()|. % The only difference between these functions is, |require_module| accepts a % second, optional argument in order to specify a minimal version. They do the % same as |require()| but also make sure the module loaded correctly % identifies itself with the name given, and its version is greater than the % minimal version required. The version can be given either as a (floating % point) number or as a date in YYYY/MM/DD format. % % Modules identify themselves using |luatexbase.provides_module|, whose only % argument is a table with some information about the module. The mandatory % fields are |name| (a string), |version| (a number), |date| (a string) and % |description| (a string). Other fields are optional and ignored, and usually % include |copyright|, |author| and |license|. % % \bigskip % % Functions for reporting are provided; similarly to \latex's |\PackageError| % etc. they take the module name as their first argument and include it in the % printed message in an appropriate way. The remaining arguments are passed to % |string.format()| before being printed. % % The functions provided (all found in the |luatexbase| table) are % |module_error|, |module_warning|, |module_info| (writes to terminal and % log), |module_log| (writes only to the log file) and |module_term| (writes % only to the terminal). % % \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/05/12 v0.2 Module utilities 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 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 \pk{luatexbase-loader} (hence \pk{luatexbase-compat}) and require % supporting Lua module. % % \begin{macrocode} \begingroup\expandafter\expandafter\expandafter\endgroup \expandafter\ifx\csname RequirePackage\endcsname\relax \input luatexbase-loader.sty \else \RequirePackage{luatexbase-loader} \fi \luatexbase@directlua{require('luatexbase.modutils')} % \end{macrocode} % % Make sure the primitives we need are available. % % \begin{macrocode} \luatexbase@ensure@primitive{luaescapestring} % \end{macrocode} % % \subsubsection{User macros} % % Interface to |use_module()|. % % \begin{macrocode} \def\luatexUseModule#1{\luatexbase@directlua{% luatexbase.use_module("\luatexluescapestring{#1}")}} % \end{macrocode} % % Interface to |require_module()| with syntax depending on the format. % % \begin{macrocode} \begingroup\expandafter\expandafter\expandafter\endgroup \expandafter\ifx\csname newcommand\endcsname\relax \def\luatexRequireModule#1#2{% \luatexbase@directlua{luatexbase.require_module( "\luatexluaescapestring{#1}", "\luatexluaescapestring{#2}")}} \else \newcommand\luatexRequireModule[2][0]{% \luatexbase@directlua{luatexbase.require_module( "\luatexluaescapestring{#2}", "\luatexluaescapestring{#1}")}} \fi % \end{macrocode} % % \begin{macrocode} \lltxb@modutils@AtEnd % % \end{macrocode} % % \subsection{Lua module} % % \begin{macrocode} %<*luamodule> module("luatexbase", package.seeall) % \end{macrocode} % % \subsection{Internal functions and data} % % Tables holding informations about the modules loaded and the versions % required. % % \begin{macrocode} local modules = modules or {} local requiredversions = {} % \end{macrocode} % % Convert a date in YYYY/MM/DD format into a number % % \begin{macrocode} local function datetonumber(date) numbers = string.gsub(date, "(%d+)/(%d+)/(%d+)", "%1%2%3") return tonumber(numbers) end % \end{macrocode} % % Say if a string is a date in YYYY//MM/DD format. % % \begin{macrocode} local function isdate(date) for _, _ in string.gmatch(date, "%d+/%d+/%d+") do return true end return false end % \end{macrocode} % % Parse a version into a table indicating a type (date or number), a % numeric version and the original version string. % % \begin{macrocode} local date, number = 1, 2 local function parse_version(version) if isdate(version) then return {type = date, version = datetonumber(version), orig = version} else return {type = number, version = tonumber(version), orig = version} end end % \end{macrocode} % % \subsubsection{Error, warning and info function for modules} % % Here are the reporting functions for the modules. For errors, Lua's % |error()| is used. For now, the error reports look less good than with % \tex's |\errmessage|, but hopefully it will be improved in future % versions of \luatex. We could invoke |\errmessage| using |tex.sprint()|, % but it may cause problems on the \tex end, and moreover |error()| will % still be used by Lua for other errors, so it makes messages more % consistent. % % \begin{macrocode} local function module_error_int(mod, ...) error('Module '..mod..' error: '..string.format(...), 3) end function module_error(mod, ...) module_error_int(mod, ...) end function module_warning(mod, ...) texio.write_nl("Module "..mod.." warning: "..string.format(...)) end function module_info(mod, ...) texio.write_nl(mod..": "..string.format(...)) end function module_log(mod, ...) texio.write_nl('log', mod..": "..string.format(...)) end function module_term(mod, ...) texio.write_nl('term', mod..": "..string.format(...)) end % \end{macrocode} % % For our own convenience, local functions for warning and errors in the % present module. % % \begin{macrocode} local function err(...) module_error_int('luatexbase.modutils', ...) end local function warn(...) module_warning('luatexbase.modutils', ...) end % \end{macrocode} % % \subsubsection{module loading and providing functions} % % Load a module without version checking. % % \begin{macrocode} function use_module(name) require(name) if not modules[name] then warn("Module didn't properly identified itself: %s", name) end end % \end{macrocode} % % Load a module with optional version checking. % % \begin{macrocode} function require_module(name, version) if not version then return use_module(name) end luaversion = parse_version(version) if modules[name] then if luaversion.type == date then if datetonumber(modules[name].date) < luaversion.version then err("found module `%s' loaded in version %s, " .."but version %s was required", name, modules[name].date, version) end else if modules[name].version < luaversion.version then err("found module `%s' loaded in version %.02f, " .."but version %s was required", name, modules[name].version, version) end end else requiredversions[name] = luaversion use_module(name) end end % \end{macrocode} % % Provide identification information for a module. % % \begin{macrocode} function provides_module(mod) if not mod then err('cannot provide nil module') return end if not mod.version or not mod.name or not mod.date or not mod.description then err("invalid module registered: " .."fields name, version, date and description are mandatory") return end requiredversion = requiredversions[mod.name] if requiredversion then if requiredversion.type == date and requiredversion.version > datetonumber(mod.date) then err("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 err("loading module %s in version %.02f, " .."but version %s was required", mod.name, mod.version, requiredversion.orig) end end modules[mod.name] = module texio.write_nl('log', string.format("Lua module: %s %s v%.02f %s\n", mod.name, mod.date, mod.version, mod.description)) end % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \section{Test files} % % A dummy lua file for tests. % % \begin{macrocode} %<*testdummy> luatexbase.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