From 75a9ad6b597ca31c07f38850761832951b0f6224 Mon Sep 17 00:00:00 2001
From: Karl Berry <karl@freefriends.org>
Date: Thu, 18 Nov 2010 00:35:41 +0000
Subject: luatexbase (17nov10)

git-svn-id: svn://tug.org/texlive/trunk@20476 c570f23f-e606-0410-a88d-b1316a301751
---
 .../source/luatex/luatexbase/luatexbase-mcb.dtx    | 984 +++++++++++++--------
 1 file changed, 628 insertions(+), 356 deletions(-)

(limited to 'Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx')

diff --git a/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx b/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx
index 09c8c1c53dd..b944cd261e4 100644
--- a/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx
+++ b/Master/texmf-dist/source/luatex/luatexbase/luatexbase-mcb.dtx
@@ -69,6 +69,11 @@ See source file '\inFileName' for details.
   \file{mcb.lua}{\from{luatexbase-mcb.dtx}{lua}}%
 }
 
+\generate{%
+  \usedir{doc/luatex/luatexbase}%
+  \file{test-mcb.lua}{\from{luatexbase-mcb.dtx}{testlua}}%
+}
+
 \obeyspaces
 \Msg{************************************************************************}
 \Msg{*}
@@ -113,73 +118,163 @@ See source file '\inFileName' for details.
 %   Grave accent  \`     Left brace    \{     Vertical bar  \|
 %   Right brace   \}     Tilde         \~}
 %
-% \title{The \textsf{luatexbase-mcb} package}
-% \date{2010/05/27 v0.2a}
-% \author{%
-%   Manuel P\'egouri\'e-Gonnard \\ \email{mpg@elzevir.fr} \and
-%   \'Elie Roux \\ \email{elie.roux@telecom-bretagne.eu}}
+% \pkdate{luatexbase-mcb}{2010/10/10 v0.3}
 %
 % \maketitle
 %
 % \begin{abstract}
-% This package manages the callback adding and removing, by providing new
-% functions and overwriting \texttt{callback.register}. It also allows to
-% create and call new callbacks.\par
-% The user part of this documentation is currently very incomplete, and will
-% be expanded later.  Sorry for this and thanks for your patience.\par
-% \textbf{Warning.} Currently assumes that \textsf{luatexbase-modutils}
-% has been previously loaded. (This is a temporary limitation.)
+% The primary feature of this package is to allow many functions to be
+% registered in the same callback. Depending of the type of the callback, the
+% functions will be combined in some way when the callback is called. Functions
+% are provided for addition and removal of individual functions from a
+% callback's list, with a priority system.\par
+% Additionally, you can create new callbacks that will be handled the same way
+% as predefined callbacks, except that they must be called explicitly.
 % \end{abstract}
 %
 % \tableofcontents
 %
 % \section{Documentation}
 %
-% Lua\TeX\ provides an extremely interesting feature, named callbacks. It
-% allows to call some lua functions at some points of the \TeX\ algorithm (a
+% Before we start, let me mention that test files are provided (they should be
+% in the same directory as this PDF file). You can have a look at them,
+% compile them and have a look at the log, if you want examples of how this
+% module works.
+%
+% \subsection{Managing functions in callbacks}
+%
+% \luatex provides an extremely interesting feature, named callbacks. It
+% allows to call some Lua functions at some points of the \TeX\ algorithm (a
 % \emph{callback}), like when \TeX\ breaks likes, puts vertical spaces, etc.
-% The Lua\TeX\ core offers a function called \texttt{callback.register} that
+% The \luatex core offers a function called \texttt{callback.register} that
 % enables to register a function in a callback.
 %
-% The problem with \texttt{callback.register} is that is registers only one
-% function in a callback. For a lot of callbacks it can be common to have
-% several packages registering their function in a callback, and thus it is
-% impossible with them to be compatible with each other.
-%
-% This package solves this problem by adding mainly one new function
-% \verb+luatexbase.add_to_callback+ that adds a function in a callback. With
-% this function it is possible for packages to register their function in a
-% callback without overwriting the functions of the other packages. There are
-% also functions to remove one or all functions from a callback.
-% See comments in the implementation section for details.
-%
-% The functions are called in a certain order, and when a package registers a
-% callback it can assign a priority to its function. Conflicts can still
-% remain even with the priority mechanism, for example in the case where two
-% packages want to have the highest priority. In these cases the packages have
-% to solve the conflicts themselves.
-% See comments in the implementation section for details.
-%
-% This package also privides a way to create and call new callbacks, in
-% addition to the default Lua\TeX\ callbacks.
-% See comments in the implementation section for details.
-%
-% \subsubsection*{Limitations}
-%
-% This package only works for callbacks where it's safe to add multiple
-% functions without changing the functions' signatures. There are callbacks,
-% though, where registering several functions is not possible without changing
-% the function's signatures, like for example the readers callbacks. These
-% callbacks take a filename and give the datas in it. One solution would be to
-% change the functions' signature to open it when the function is the first,
-% and to take the datas and modify them eventually if they are called after
-% the first. But it seems rather fragile and useless, so it's not implemented.
-% With these callbacks, in this package we simply execute the first function
-% in the list.
-%
-% Other callbacks in this case are \texttt{define\_font} and
-% \texttt{open\_read\_file}. There is though a solution for several packages
-% to use these callbacks, see the implementation of \texttt{luatextra}.
+% The problem with |callback.register| is that is registers only one function
+% in a callback. This package solves the problem by disabling
+% |callback.register| and providing a new interface allowing many functions to
+% be registered in a single callback.
+%
+% The way the functions are combined together depends on
+% the type of the callback. There are currently 4 types of callback, depending
+% on the calling convention of the functions the callback can hold:
+% \begin{description}
+%   \item[simple] is for functions that don't return anything: they are called
+%     in order, all with the same argument;
+%   \item[data] is for functions receiving a piece of data of nay type
+%     except node list head (and possibly other arguments) and returning it
+%     (possibly modified): the functions are called in order, and each is
+%     passed the return value of the previous (and the other arguments
+%     untouched, if any). The return value is that of the last function;
+%   \item[list] is a specialized variant of \emph{data} for functions
+%     filtering node lists. Such functions may return either the head of a
+%     modified node list, or the boolean values |true| or |false|. The
+%     functions are chained the same way as for \emph{data} except that for
+%     the following. If
+%     one function returns |false|, then |false| is immediately return and the
+%     following functions are \emph{not} called. If one function returns
+%     |true|, then the same head is passed to the next function. If all
+%     functions return |true|, then |true| is returned, otherwise the return
+%     value of the last function not returning |true| is used.
+%   \item[first] is for functions with more complex signatures; functions in
+%     this type of callback are \emph{not} combined: only the first one
+%     (according to priorities) is executed.
+% \end{description}
+%
+% To add a function to a callback, use:
+% \begin{verbatim}
+% luatexbase.add_to_callback(name, func, description, priority)
+% \end{verbatim}
+% The first argument is the name of the callback, the second is a function,
+% the third one is a string used to identify the function later, and the
+% optional priority is a positive integer, representing the rank of the
+% function in the list of functions to be executing for this callback. So,
+% |1| is the highest priority. If no priority is specified, the function is
+% appended to the list, that is, its priority is the one of the last function
+% plus one.
+%
+% The priority system is intended to help resolving conflicts between packages
+% competing on the same callback, but it cannot solve every possible issue. If
+% two packages request priority |1| on the same callback, then the last one
+% loaded will win.
+%
+% To remove a function from a callback, use:
+% \begin{verbatim}
+% luatexbase.remove_from_callback(name, description)
+% \end{verbatim}
+% The first argument must be the name of the callback, and the second one the
+% description used when adding the function to this callback. You can also
+% remove all functions from a callback at once using
+% \begin{verbatim}
+% luatexbase.reset_callback(name, make_false)
+% \end{verbatim}
+% The |make_false| argument is optional. If it is |true| (repeat: |true|, not
+% |false|) then the value |false| is registered in the callback, which has a
+% special meaning for some callback.
+%
+% Note that |reset_callback| is very invasive since it removes all functions
+% possibly installed by other packages in this callback. So, use it with care
+% if there is any chance that another package wants to share this callback
+% with you.
+%
+% When new functions are added at the beginning of the list, other functions
+% are shifted down the list. To get the current rank of a function in a
+% callback's list, use:
+% \begin{verbatim}
+% priority = luatexbase.priority_in_callback(name, description)
+% \end{verbatim}
+% Again, the description is the string used when adding the function. If the
+% function identified by this string is not in this callback's list, the
+% priority returned is the boolean value |false|.
+%
+% \subsection{Creating new callbacks}
+%
+% This package also provides a way to create and call new callbacks, in
+% addition to the default \luatex callbacks.
+% \begin{verbatim}
+% luatexbase.create_callback(name, type, default)
+% \end{verbatim}
+% The first argument is the callback's name, it must be unique. Then, the type
+% goes as explained above, it is given as a string. Finally all user-defined
+% callbacks have a default function which must\footnote{You can obviously
+% provide a dummy function. If you're doing so often, please tell me, I may
+% want to make this argument optional.} be provided as the third
+% argument. It will be used when no other function is registered for this
+% callback.
+%
+% Functions are added to and removed from user-defined callbacks just the same
+% way as predefined callback, so the previous section still applies. There is
+% one difference, however: user-defined callbacks must be called explicitly at
+% some point in your code, while predefined callbacks are called automatically
+% by \luatex. To do so, use:
+% \begin{verbatim}
+% luatexbase.call_callback(name, arguments...)
+% \end{verbatim}
+% The functions registered for this callback (or the default function) will be
+% called with |arguments...| as arguments.
+%
+% \subsubsection{Limitations}
+%
+% For callbacks of type |first|, our new management system isn't actually
+% better than good old |callback.register|. For some of them, is may be
+% possible to split them into many callbacks, so that these callbacks can
+% accept multiple functions. However, its seems risky and limited in use and
+% is therefore nor implemented.
+%
+% At some point, \pk{luatextra} used to split |open_read_file| that way, but
+% support for this was removed. It may be added back (as well as support for
+% other split callbacks) if it appears there is an actual need for it.
+%
+% \subsection{Compatibility}
+%
+% Some callbacks have a calling convention that varies depending on the
+% version of \luatex used. This package \emph{does not} try to track the type
+% of the callbacks in every possible version of \luatex. The types are based
+% on the last stable beta version (0.60.2 at the time this doc is written).
+%
+% However, for callbacks that have the same calling convention for every
+% version of \luatex, this package should work with the same range of \luatex
+% version as other packages in the \pk{luatexbase} bundle (currently, 0.25.4
+% to 0.60.2).
 %
 %    \section{Implementation}
 %
@@ -245,7 +340,7 @@ See source file '\inFileName' for details.
     \let\x\ProvidesPackage
   \fi
 \expandafter\endgroup
-\x{luatexbase-mcb}[2010/05/27 v0.2a Callback management for LuaTeX]
+\x{luatexbase-mcb}[2010/10/10 v0.3 Callback management for LuaTeX]
 %    \end{macrocode}
 %
 %    Make sure \luatex is used.
@@ -259,14 +354,17 @@ See source file '\inFileName' for details.
 \fi
 \ifluatex\else
   \begingroup
-    \expandafter\ifx\csname PackageWarningNoLine\endcsname\relax
-      \def\x#1#2{\begingroup\newlinechar10
-        \immediate\write16{Package #1 warning: #2}\endgroup}
+    \expandafter\ifx\csname PackageError\endcsname\relax
+      \def\x#1#2#3{\begingroup \newlinechar10
+        \errhelp{#3}\errmessage{Package #1 error: #2}\endgroup}
     \else
-      \let\x\PackageWarningNoLine
+      \let\x\PackageError
     \fi
   \expandafter\endgroup
-  \x{luatexbase-mcb}{LuaTeX is required for this package. Aborting.}
+  \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@mcb@AtEnd
   \expandafter\endinput
 \fi
@@ -304,309 +402,280 @@ See source file '\inFileName' for details.
 %
 %    \begin{macrocode}
 module('luatexbase', package.seeall)
-luatexbase.provides_module({
-    name          = "luamcallbacks",
+local err, warning, info = luatexbase.provides_module({
+    name          = "luatexbase-mcb",
     version       = 0.2,
     date          = "2010/05/12",
     description   = "register several functions in a callback",
-    author        = "Hans Hagen, Elie Roux and Manuel Pégourie-Gonnard",
-    copyright     = "Hans Hagen, Elie Roux and Manuel Pégourie-Gonnard",
+    author        = "Hans Hagen, Elie Roux and Manuel Pegourie-Gonnard",
+    copyright     = "Hans Hagen, Elie Roux and Manuel Pegourie-Gonnard",
     license       = "CC0",
 })
 %    \end{macrocode}
 %
-%    Shortcuts for error functions.
+%    \subsubsection{Housekeeping}
+%
+%    The main table: keys are callback names, and values are the associated
+%    lists of functions. More precisely, the entries in the list are tables
+%    holding the actual function as |func| and the identifying description as
+%    |description|. Only callbacks with a non-empty list of functions have an
+%    entry in this list.
 %
 %    \begin{macrocode}
-local log = log or function(...)
-  luatexbase.module_log('luamcallbacks', string.format(...))
-end
-local info = info or function(...)
-  luatexbase.module_info('luamcallbacks', string.format(...))
-end
-local warning = warning or function(...)
-  luatexbase.module_warning('luamcallbacks', string.format(...))
-end
-local err = err or function(...)
-  luatexbase.module_error('luamcallbacks', string.format(...))
-end
+local callbacklist = callbacklist or { }
 %    \end{macrocode}
 %
-%    \subsubsection{Initialisations}
+%    Numerical codes for callback types, and name to value association (the
+%    table keys are strings, the values are numbers).
 %
-%    \texttt{callbacklist} is the main list, that contains the callbacks as
-%    keys and a table of the registered functions a values.
+%    \begin{macrocode}
+local list, data, first, simple = 1, 2, 3, 4
+local types = {
+    list   = list,
+    data   = data,
+    first  = first,
+    simple = simple,
+}
+%    \end{macrocode}
+%
+%    Now, list all predefined callbacks with their current type, based on the
+%    LuaTeX manual version 0.60.2.
 %
 %    \begin{macrocode}
-local callbacklist = callbacklist or { }
+local callbacktypes = callbacktypes or {
 %    \end{macrocode}
 %
-%    A table with the default functions of the created callbacks. See
-%    \texttt{create} for further informations.
+%    Section 4.1.1: file discovery callbacks.
 %
 %    \begin{macrocode}
-local lua_callbacks_defaults = { }
+    find_read_file     = first,
+    find_write_file    = first,
+    find_font_file     = data,
+    find_output_file   = data,
+    find_format_file   = data,
+    find_vf_file       = data,
+    find_ocp_file      = data,
+    find_map_file      = data,
+    find_enc_file      = data,
+    find_sfd_file      = data,
+    find_pk_file       = data,
+    find_data_file     = data,
+    find_opentype_file = data,
+    find_truetype_file = data,
+    find_type1_file    = data,
+    find_image_file    = data,
 %    \end{macrocode}
 %
-%    There are 4 types of callback:
-%    \begin{itemize}
-%    \item the ones taking a list of nodes and returning a boolean and
-%    eventually a new head (\texttt{list})
-%    \item the ones taking datas and returning the modified ones
-%    (\texttt{data})
-%    \item the ones that can't have multiple functions registered in them
-%    (\texttt{first})
-%    \item the ones for functions that don't return anything (\texttt{simple})
-%    \end{itemize}
+%    Section 4.1.2: file reading callbacks.
 %
 %    \begin{macrocode}
-local list = 1
-local data = 2
-local first = 3
-local simple = 4
+    open_read_file     = first,
+    read_font_file     = first,
+    read_vf_file       = first,
+    read_ocp_file      = first,
+    read_map_file      = first,
+    read_enc_file      = first,
+    read_sfd_file      = first,
+    read_pk_file       = first,
+    read_data_file     = first,
+    read_truetype_file = first,
+    read_type1_file    = first,
+    read_opentype_file = first,
 %    \end{macrocode}
 %
-%    \texttt{callbacktypes} is the list that contains the callbacks as keys
-%    and the type (list or data) as values.
+%    Section 4.1.3: data processing callbacks.
 %
 %    \begin{macrocode}
-local callbacktypes = callbacktypes or {
-  buildpage_filter = simple,
-  token_filter = first,
-  pre_output_filter = list,
-  hpack_filter = list,
-  process_input_buffer = data,
-  mlist_to_hlist = list,
-  vpack_filter = list,
-  define_font = first,
-  open_read_file = first,
-  linebreak_filter = list,
-  post_linebreak_filter = list,
-  pre_linebreak_filter = list,
-  start_page_number = simple,
-  stop_page_number = simple,
-  start_run = simple,
-  show_error_hook = simple,
-  stop_run = simple,
-  hyphenate = simple,
-  ligaturing = simple,
-  kerning = data,
-  find_write_file = first,
-  find_read_file = first,
-  find_vf_file = data,
-  find_map_file = data,
-  find_format_file = data,
-  find_opentype_file = data,
-  find_output_file = data,
-  find_truetype_file = data,
-  find_type1_file = data,
-  find_data_file = data,
-  find_pk_file = data,
-  find_font_file = data,
-  find_image_file = data,
-  find_ocp_file = data,
-  find_sfd_file = data,
-  find_enc_file = data,
-  read_sfd_file = first,
-  read_map_file = first,
-  read_pk_file = first,
-  read_enc_file = first,
-  read_vf_file = first,
-  read_ocp_file = first,
-  read_opentype_file = first,
-  read_truetype_file = first,
-  read_font_file = first,
-  read_type1_file = first,
-  read_data_file = first,
-}
+    process_input_buffer  = data,
+    process_output_buffer = data,
+    token_filter          = first,
 %    \end{macrocode}
 %
-%    In Lua\TeX\ version 0.43, a new callback called |process_output_buffer|
-%    appeared, so we enable it. Test the version using the compat package for,
-%    well, compatibility.
+%    Section 4.1.4: node list processiong callbacks.
 %
 %    \begin{macrocode}
-if luatexbase.luatexversion > 42 then
-    callbacktypes["process_output_buffer"] = data
-end
+    buildpage_filter      = simple,
+    pre_linebreak_filter  = list,
+    linebreak_filter      = list,
+    post_linebreak_filter = list,
+    hpack_filter          = list,
+    vpack_filter          = list,
+    pre_output_filter     = list,
+    hyphenate             = simple,
+    ligaturing            = simple,
+    kerning               = simple,
+    mlist_to_hlist        = list,
 %    \end{macrocode}
 %
-%    As we overwrite \texttt{callback.register}, we save it as
-%    \texttt{internalregister}.
+%    Section 4.1.5: information reporting callbacks.
 %
 %    \begin{macrocode}
-local internalregister = internalregister or callback.register
+    start_run         = simple,
+    stop_run          = simple,
+    start_page_number = simple,
+    stop_page_number  = simple,
+    show_error_hook   = simple,
 %    \end{macrocode}
 %
-%    \subsubsection{Unsorted stuff}
+%    Section 4.1.6: font-related callbacks.
 %
-%    A simple function we'll use later to understand the arguments of the
-%    \texttt{create} function. It takes a string and returns the type
-%    corresponding to the string or nil.
+%    \begin{macrocode}
+    define_font = first,
+}
+%    \end{macrocode}
+%
+%    All user-defined callbacks have a default function. The following table's
+%    keys are the names of the user-defined callback, the associated value is
+%    the default functon for this callback. This table is also used to
+%    identify the user-defined callbacks.
 %
 %    \begin{macrocode}
-local function str_to_type(str)
-    if str == 'list' then
-        return list
-    elseif str == 'data' then
-        return data
-    elseif str == 'first' then
-        return first
-    elseif str == 'simple' then
-        return simple
-    else
-        return nil
-    end
+local lua_callbacks_defaults = { }
+%    \end{macrocode}
+%
+%    Overwrite |callback.register|, but save it first. Also define a wrapper
+%    that automatically raise an error when something goes wrong.
+%
+%    \begin{macrocode}
+local original_register = original_register or callback.register
+callback.register = function ()
+  err("function callback.register has been trapped,\n"
+  .."please use luatexbase.add_to_callback instead.")
+end
+local function register_callback(...)
+    return assert(original_register(...))
 end
 %    \end{macrocode}
 %
-%     This function and the following ones are only internal. This one is the
-%     handler for the first type of callbacks: the ones that take a list head
-%     and return true, false, or a new list head.
+%    \subsubsection{Handlers}
+%
+%    Normal (as opposed to user-defined) callbacks have handlers depending on
+%    their type. The handler function is registered into the callback when the
+%    first function is added to this callback's list. Then, when the callback
+%    is called, then handler takes care of running all functions in the list.
+%    When the last function is removed from the callback's list, the handler
+%    is unregistered.
+%
+%    More precisely, the functions below are used to generate a specialized
+%    function (closure) for a given callback, which is the actual handler.
+%
+%    Handler for |list| callbacks.
 %
 %    \begin{macrocode}
--- local
-function listhandler (name)
+local function listhandler (name)
     return function(head,...)
-        local l = callbacklist[name]
-        if l then
-            local done = true
-            for _, f in ipairs(l) do
-                -- the returned value is either true or a new head plus true
-                rtv1, rtv2 = f.func(head,...)
-                if type(rtv1) == 'boolean' then
-                    done = rtv1
-                elseif type (rtv1) == 'userdata' then
-                    head = rtv1
-                end
-                if type(rtv2) == 'boolean'  then
-                    done = rtv2
-                elseif type(rtv2) == 'userdata' then
-                    head = rtv2
-                end
-                if done == false then
-                    err("function \"%s\" returned false in callback '%s'",
-                      f.description, name)
-                end
+        local ret
+        local alltrue = true
+        for _, f in ipairs(callbacklist[name]) do
+            ret = f.func(head, ...)
+            if ret == false then
+                warn("function '%s' returned false\nin callback '%s'",
+                    f.description, name)
+                break
+            end
+            if ret ~= true then
+                alltrue = false
+                head = ret
             end
-            return head, done
-        else
-            return head, false
         end
+        return alltrue and true or head
     end
 end
 %    \end{macrocode}
 %
-%     The handler for callbacks taking datas and returning modified ones.
+%    Handler for |data| callbacks.
 %
 %    \begin{macrocode}
 local function datahandler (name)
-    return function(data,...)
-        local l = callbacklist[name]
-        if l then
-            for _, f in ipairs(l) do
-                data = f.func(data,...)
-            end
+    return function(data, ...)
+        for _, f in ipairs(callbacklist[name]) do
+            data = f.func(data, ...)
         end
         return data
     end
 end
 %    \end{macrocode}
 %
-%     This function is for the handlers that don't support more than one
-%     functions in them. In this case we only call the first function of the
-%     list.
+%    Handler for |first| callbacks. We can assume |callbacklist[name]| is not
+%    empty: otherwise, the function wouldn't be registered in the callback any
+%    more.
 %
 %    \begin{macrocode}
 local function firsthandler (name)
     return function(...)
-        local l = callbacklist[name]
-        if l then
-            local f = l[1].func
-            return f(...)
-        else
-            return nil, false
-        end
+        return callbacklist[name][1].func(...)
     end
 end
 %    \end{macrocode}
 %
-%     Handler for simple functions that don't return anything.
+%    Handler for |simple| callbacks.
 %
 %    \begin{macrocode}
 local function simplehandler (name)
     return function(...)
-        local l = callbacklist[name]
-        if l then
-            for _, f in ipairs(l) do
-                f.func(...)
-            end
+        for _, f in ipairs(callbacklist[name]) do
+            f.func(...)
         end
     end
 end
 %    \end{macrocode}
 %
-%    \subsubsection{Public functions}
+%    Finally, keep a handlers table for indexed access.
 %
-%    The main function. The signature is \texttt{add (name,
-%    func, description, priority)} with \texttt{name} being the name of the
-%    callback in which the function is added; \texttt{func} is the added
-%    function; \texttt{description} is a small character string describing the
-%    function, and \texttt{priority} an optional argument describing the
-%    priority the function will have.
+%    \begin{macrocode}
+local handlers = {
+  [list]   = listhandler,
+  [data]   = datahandler,
+  [first]  = firsthandler,
+  [simple] = simplehandler,
+}
+%    \end{macrocode}
 %
-%    The functions for a callbacks are added in a list (in
-%    \texttt{callbacklist\\.callbackname}). If they have no
-%    priority or a high priority number, they will be added at the end of the
-%    list, and will be called after the others. If they have a low priority
-%    number, the will be added at the beginning of the list and will be called
-%    before the others.
+%    \subsubsection{Public functions for functions management}
 %
-%    Something that must be made clear, is that there is absolutely no
-%    solution for packages conflicts: if two packages want the top priority on
-%    a certain callback, they will have to decide the priority they will give
-%    to their function themselves. Most of the time, the priority is not needed.
+%    Add a function to a callback. First check arguments.
 %
 %    \begin{macrocode}
 function add_to_callback (name,func,description,priority)
     if type(func) ~= "function" then
-        err("unable to add function, no proper function passed")
-        return
+        return err("unable to add function:\nno proper function passed")
     end
     if not name or name == "" then
-        err("unable to add function, no proper callback name passed")
+        err("unable to add function:\nno proper callback name passed")
         return
     elseif not callbacktypes[name] then
-        err("unable to add function, '%s' is not a valid callback", name)
+        err("unable to add function:\n'%s' is not a valid callback", name)
         return
     end
     if not description or description == "" then
-        err("unable to add function to '%s', no proper description passed",
+        err("unable to add function to '%s':\nno proper description passed",
           name)
         return
     end
-    if priority_in_callback(name, description) ~= 0 then
-        warning("function '%s' already registered in callback '%s'",
+    if priority_in_callback(name, description) then
+        err("function '%s' already registered\nin callback '%s'",
           description, name)
+        return
     end
+%    \end{macrocode}
+%
+%    Then test if this callback is already in use. If not, initialise its list
+%    and register the proper handler.
+%
+%    \begin{macrocode}
     local l = callbacklist[name]
     if not l then
         l = {}
         callbacklist[name] = l
         if not lua_callbacks_defaults[name] then
-            if callbacktypes[name] == list then
-                internalregister(name, listhandler(name))
-            elseif callbacktypes[name] == data then
-                internalregister(name, datahandler(name))
-            elseif callbacktypes[name] == simple then
-                internalregister(name, simplehandler(name))
-            elseif callbacktypes[name] == first then
-                internalregister(name, firsthandler(name))
-            else
-                err("unknown callback type")
-            end
+            register_callback(name, handlers[callbacktypes[name]](name))
         end
     end
+%    \end{macrocode}
+%
+%    Actually register the function.
+%
+%    \begin{macrocode}
     local f = {
         func = func,
         description = description,
@@ -617,33 +686,35 @@ function add_to_callback (name,func,description,priority)
     elseif priority < 1 then
         priority = 1
     end
-    if callbacktypes[name] == first and (priority ~= 1 or #l ~= 0) then
-        warning("several callbacks registered in callback '%s', "
-        .."only the first function will be active.", name)
-    end
     table.insert(l,priority,f)
-    log("inserting function '%s' at position %s in callback list for '%s'",
+%    \end{macrocode}
+%
+%    Keep user informed.
+%
+%    \begin{macrocode}
+    if callbacktypes[name] == first and #l ~= 1 then
+        warning("several functions in '%s',\n"
+        .."only one will be active.", name)
+    end
+    info("inserting '%s'\nat position %s in '%s'",
       description, priority, name)
 end
 %    \end{macrocode}
 %
-%    The function that removes a function from a callback. The signature is
-%    \texttt{remove (name, description)} with \texttt{name} being
-%    the name of callbacks, and description the description passed to
-%    \texttt{add}.
+%    Remove a function from a callback. First check arguments.
 %
 %    \begin{macrocode}
 function remove_from_callback (name, description)
     if not name or name == "" then
-        err("unable to remove function, no proper callback name passed")
+        err("unable to remove function:\nno proper callback name passed")
         return
     elseif not callbacktypes[name] then
-        err("unable to remove function, '%s' is not a valid callback", name)
+        err("unable to remove function:\n'%s' is not a valid callback", name)
         return
     end
     if not description or description == "" then
         err(
-          "unable to remove function from '%s', no proper description passed",
+          "unable to remove function from '%s':\nno proper description passed",
           name)
         return
     end
@@ -652,45 +723,84 @@ function remove_from_callback (name, description)
         err("no callback list for '%s'",name)
         return
     end
+%    \end{macrocode}
+%
+%    Then loop over the callback's function list until we find a matching
+%    entry. Remove it and check if the list gets empty: if so, unregister the
+%    callback handler unless the callback is user-defined.
+%
+%    \begin{macrocode}
+    local index = false
     for k,v in ipairs(l) do
         if v.description == description then
-            table.remove(l,k)
-            log("removing function '%s' from '%s'",description,name)
-            if not next(l) then
-              callbacklist[name] = nil
-              if not lua_callbacks_defaults[name] then
-                internalregister(name, nil)
-              end
-            end
-            return
+            index = k
+            break
+        end
+    end
+    if not index then
+        err("unable to remove '%s'\nfrom '%s'", description, name)
+        return
+    end
+    table.remove(l, index)
+    info("removing '%s'\nfrom '%s'", description, name)
+    if table.maxn(l) == 0 then
+        callbacklist[name] = nil
+        if not lua_callbacks_defaults[name] then
+            register_callback(name, nil)
         end
     end
-    warning("unable to remove function '%s' from '%s'",description,name)
+    return
 end
 %    \end{macrocode}
 %
-%    This function removes all the functions registered in a callback.
+%    Remove all the functions registered in a callback. Unregisters the
+%    callback handler unless the callback is user-defined.
 %
 %    \begin{macrocode}
-function reset_callback (name)
+function reset_callback (name, make_false)
     if not name or name == "" then
-        err("unable to reset, no proper callback name passed")
+        err("unable to reset:\nno proper callback name passed")
         return
     elseif not callbacktypes[name] then
-        err("reset error, '%s' is not a valid callback", name)
+        err("unable to reset '%s':\nis not a valid callback", name)
         return
     end
+    info("resetting callback '%s'", name)
+    callbacklist[name] = nil
     if not lua_callbacks_defaults[name] then
-        internalregister(name, nil)
+        if make_false == true then
+            info("setting '%s' to false", name)
+            register_callback(name, false)
+        else
+            register_callback(name, nil)
+        end
+    end
+end
+%    \end{macrocode}
+%
+%    Get a function's priority in a callback list, or false if the function is
+%    not in the list.
+%
+%    \begin{macrocode}
+function priority_in_callback (name, description)
+    if not name or name == ""
+            or not callbacktypes[name]
+            or not description then
+        return false
     end
     local l = callbacklist[name]
-    if l then
-        log("resetting callback list '%s'",name)
-        callbacklist[name] = nil
+    if not l then return false end
+    for p, f in pairs(l) do
+        if f.description == description then
+            return p
+        end
     end
+    return false
 end
 %    \end{macrocode}
 %
+%    \subsubsection{Public functions for user-defined callbacks}
+%
 %    This first function creates a new callback. The signature is
 %    \texttt{create(name, ctype, default)} where \texttt{name} is the name of
 %    the new callback to create, \texttt{ctype} is the type of callback, and
@@ -705,24 +815,24 @@ end
 %    \begin{macrocode}
 function create_callback(name, ctype, default)
     if not name then
-        err("unable to call callback, no proper name passed", name)
+        err("unable to call callback:\nno proper name passed", name)
         return nil
     end
     if not ctype or not default then
-        err("unable to create callback '%s': "
+        err("unable to create callback '%s':\n"
         .."callbacktype or default function not specified", name)
         return nil
     end
     if callbacktypes[name] then
-        err("unable to create callback '%s', callback already exists", name)
+        err("unable to create callback '%s':\ncallback already exists", name)
         return nil
     end
-    local temp = str_to_type(ctype)
-    if not temp then
-        err("unable to create callback '%s', type '%s' undefined", name, ctype)
+    ctype = types[ctype]
+    if not ctype then
+        err("unable to create callback '%s':\ntype '%s' undefined", name, ctype)
         return nil
     end
-    ctype = temp
+    info("creating '%s' type %s", name, ctype)
     lua_callbacks_defaults[name] = default
     callbacktypes[name] = ctype
 end
@@ -734,11 +844,11 @@ end
 %    \begin{macrocode}
 function call_callback(name, ...)
     if not name then
-        err("unable to call callback, no proper name passed", name)
+        err("unable to call callback:\nno proper name passed", name)
         return nil
     end
     if not lua_callbacks_defaults[name] then
-        err("unable to call lua callback '%s', unknown callback", name)
+        err("unable to call lua callback '%s':\nunknown callback", name)
         return nil
     end
     local l = callbacklist[name]
@@ -746,92 +856,254 @@ function call_callback(name, ...)
     if not l then
         f = lua_callbacks_defaults[name]
     else
-        if callbacktypes[name] == list then
-            f = listhandler(name)
-        elseif callbacktypes[name] == data then
-            f = datahandler(name)
-        elseif callbacktypes[name] == simple then
-            f = simplehandler(name)
-        elseif callbacktypes[name] == first then
-            f = firsthandler(name)
-        else
+        f = handlers[callbacktypes[name]](name)
+        if not f then
             err("unknown callback type")
+            return
         end
     end
     return f(...)
 end
 %    \end{macrocode}
 %
-%    This function tells if a function has already been registered in a
-%    callback, and gives its current priority. The arguments are the name of
-%    the callback and the description of the function. If it has already been
-%    registered, it gives its priority, and if not it returns false.
+%    That's all folks!
 %
 %    \begin{macrocode}
-function priority_in_callback (name, description)
-    if not name or name == ""
-            or not callbacktypes[name]
-            or not description then
-        return 0
-    end
-    local l = callbacklist[name]
-    if not l then return 0 end
-    for p, f in pairs(l) do
-        if f.description == description then
-            return p
-        end
-    end
-    return 0
+%</lua>
+%    \end{macrocode}
+%
+%    \section{Test files}
+%
+%    A few basic tests for Plain and LaTeX. Use a separate Lua file for
+%    convenience, since this package works on the Lua side of the force.
+%
+%    \begin{macrocode}
+%<*testlua>
+local msg = texio.write_nl
+%    \end{macrocode}
+%
+%    Test the management functions with a predefined callback.
+%
+%    \begin{macrocode}
+local function sample(head,...)
+    return head, true
 end
+local prio = luatexbase.priority_in_callback
+msg("\n*********\n* Testing management functions\n*********")
+luatexbase.add_to_callback("hpack_filter", sample, "sample one", 1)
+luatexbase.add_to_callback("hpack_filter", sample, "sample two", 2)
+luatexbase.add_to_callback("hpack_filter", sample, "sample three", 1)
+assert(prio("hpack_filter", "sample three"))
+luatexbase.remove_from_callback("hpack_filter", "sample three")
+assert(not prio("hpack_filter", "sample three"))
+luatexbase.reset_callback("hpack_filter")
+assert(not prio("hpack_filter", "sample one"))
 %    \end{macrocode}
 %
-%    Finally we
-%    overwrite \texttt{callback.register} so that it outputs an error.
+%    Create a callback, and check that the managment functions work with this
+%    callback too.
 %
 %    \begin{macrocode}
-callback.register = function ()
-err("function callback.register has been deleted by luamcallbacks, "
-.."please use luatexbase.add_to_callback instead.")
+local function data_one(s)
+  texio.write_nl("I'm data 1 whith argument: "..s)
+  return s
 end
+local function data_two(s)
+  texio.write_nl("I'm data 2 whith argument: "..s)
+  return s
+end
+local function data_three(s)
+  texio.write_nl("I'm data 3 whith argument: "..s)
+  return s
+end
+msg("\n*********\n* Testing user-defined callbacks\n*********")
+msg("* create one")
+luatexbase.create_callback("fooback", "data", data_one)
+msg("* call it")
+luatexbase.call_callback("fooback", "default")
+msg("* add two functions")
+luatexbase.add_to_callback("fooback", data_two, "function two", 2)
+luatexbase.add_to_callback("fooback", data_three, "function three", 1)
+msg("* call")
+luatexbase.call_callback("fooback", "all")
+msg("* rm one function")
+luatexbase.remove_from_callback("fooback", "function three")
+msg("* call")
+luatexbase.call_callback("fooback", "all but three")
+msg("* reset")
+luatexbase.reset_callback("fooback")
+msg("* call")
+luatexbase.call_callback("fooback", "default")
 %    \end{macrocode}
 %
-%    That's all folks!
+%    Now, we want to make each handler run at least once. So, define dummy
+%    functions and register them in various callbacks. We will make sure the
+%    callbacks are executed on the \tex end. Also, we want to check that
+%    everything works when we unload the functions either one by one, or using
+%    reset.
+%
+%    A |list| callback.
 %
 %    \begin{macrocode}
-%</lua>
+function add_hpack_filter()
+    luatexbase.add_to_callback('hpack_filter', function(head, ...)
+            texio.write_nl("I'm a dummy hpack_filter")
+            return head
+        end,
+        'dummy hpack filter')
+    luatexbase.add_to_callback('hpack_filter', function(head, ...)
+            texio.write_nl("I'm an optimized dummy hpack_filter")
+            return true
+        end,
+        'optimized dummy hpack filter')
+end
+function rm_one_hpack_filter()
+    luatexbase.remove_from_callback('hpack_filter', 'dummy hpack filter')
+end
+function rm_two_hpack_filter()
+    luatexbase.remove_from_callback('hpack_filter',
+        'optimized dummy hpack filter')
+end
 %    \end{macrocode}
 %
-%    \section{Test files}
+%    A |simple| callback.
+%
+%    \begin{macrocode}
+function add_hyphenate()
+    luatexbase.add_to_callback('hyphenate', function(head, tail)
+            texio.write_nl("I'm a dummy hyphenate")
+        end,
+        'dummy hyphenate')
+    luatexbase.add_to_callback('hyphenate', function(head, tail)
+            texio.write_nl("I'm an other dummy hyphenate")
+        end,
+        'other dummy hyphenate')
+end
+function rm_one_hyphenate()
+    luatexbase.remove_from_callback('hyphenate', 'dummy hyphenate')
+end
+function rm_two_hyphenate()
+    luatexbase.remove_from_callback('hyphenate', 'other dummy hyphenate')
+end
+%    \end{macrocode}
+%
+%    A |first| callback.
+%
+%    \begin{macrocode}
+function add_find_write_file()
+    luatexbase.add_to_callback('find_write_file', function(id, name)
+            texio.write_nl("I'm a dummy find_write_file")
+            return "dummy-"..name
+        end,
+        'dummy find_write_file')
+    luatexbase.add_to_callback('find_write_file', function(id, name)
+            texio.write_nl("I'm an other dummy find_write_file")
+            return "dummy-other-"..name
+        end,
+        'other dummy find_write_file')
+end
+function rm_one_find_write_file()
+    luatexbase.remove_from_callback('find_write_file',
+        'dummy find_write_file')
+end
+function rm_two_find_write_file()
+    luatexbase.remove_from_callback('find_write_file',
+        'other dummy find_write_file')
+end
+%    \end{macrocode}
 %
-%    A few basic tests for Plain and LaTeX.
+%    A |data| callback.
+%
+%    \begin{macrocode}
+function add_process_input_buffer()
+    luatexbase.add_to_callback('process_input_buffer', function(buffer)
+            return buffer.."\\msg{dummy}"
+        end,
+        'dummy process_input_buffer')
+    luatexbase.add_to_callback('process_input_buffer', function(buffer)
+            return buffer.."\\msg{otherdummy}"
+        end,
+        'other dummy process_input_buffer')
+end
+function rm_one_process_input_buffer()
+    luatexbase.remove_from_callback('process_input_buffer',
+        'dummy process_input_buffer')
+end
+function rm_two_process_input_buffer()
+    luatexbase.remove_from_callback('process_input_buffer',
+        'other dummy process_input_buffer')
+end
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%</testlua>
+%    \end{macrocode}
 %
 %    \begin{macrocode}
 %<testplain>\input luatexbase-mcb.sty
 %<testlatex>\RequirePackage{luatexbase-mcb}
 %<*testplain,testlatex>
 \catcode 64 11
-\luatexbase@directlua{
-  local function one(head,...)
-      texio.write_nl("I'm number 1")
-      return head, true
-  end
-
-  local function two(head,...)
-      texio.write_nl("I'm number 2")
-      return head, true
-  end
-
-  local function three(head,...)
-      texio.write_nl("I'm number 3")
-      return head, true
-  end
-
-  luatexbase.add_to_callback("hpack_filter",one,"my sample function one",1)
-  luatexbase.add_to_callback("hpack_filter",two,"my sample function two",2)
-  luatexbase.add_to_callback("hpack_filter",three,"my sample function three",1)
-
-  luatexbase.remove_from_callback("hpack_filter","my sample function three")
+\def\msg{\immediate\write16}
+\msg{===== BEGIN =====}
+%    \end{macrocode}
+%
+%    Loading the lua files tests that the management functions can be called
+%    without raising errors.
+%
+%    \begin{macrocode}
+\luatexbase@directlua{dofile('test-mcb.lua')}
+%    \end{macrocode}
+%
+%    We now want to load and unload stuff from the various callbacks have them
+%    called to test the handlers. Here is a helper macro for that.
+%
+%    \begin{macrocode}
+\def\test#1#2{%
+  \msg{^^J*********^^J* Testing #1 (type #2)^^J*********}
+  \msg{* Add two functions}
+  \luatexbase@directlua{add_#1()}
+  \csname test_#1\endcsname
+  \msg{* Remove one}
+  \luatexbase@directlua{rm_one_#1()}
+  \csname test_#1\endcsname
+  \msg{* Remove the second}
+  \luatexbase@directlua{rm_two_#1()}
+  \csname test_#1\endcsname
+  \msg{* Add two functions again}
+  \luatexbase@directlua{add_#1()}
+  \csname test_#1\endcsname
+  \msg{* Remove all functions}
+  \luatexbase@directlua{luatexbase.reset_callback("#1")}
+  \csname test_#1\endcsname
 }
+%    \end{macrocode}
+%
+%    For each callback, we need a specific macro that triggers it. For the
+%    hyphenate test, we need to untrap |\everypar| first, in the \latex case.
+%
+%    \begin{macrocode}
+\catcode`\_ 11
+%<testlatex>\everypar{}
+\def\test_hpack_filter{\setbox0=\hbox{bla}}
+\def\test_hyphenate{\showhyphens{hyphenation}}
+\def\test_find_write_file{\immediate\openout15 test-mcb-out.log}
+\def\test_process_input_buffer{\input test-mcb-aux.tex}
+%    \end{macrocode}
+%
+%    Now actually test them
+%
+%    \begin{macrocode}
+\test{hpack_filter}{list}
+\test{hyphenate}{simple}
+\test{find_write_file}{first}
+\test{process_input_buffer}{data}
+%    \end{macrocode}
+%
+%    Done.
+%
+%    \begin{macrocode}
+\msg{=====  END =====}
 %</testplain,testlatex>
 %<testplain>\bye
 %<testlatex>\stop
-- 
cgit v1.2.3

