% \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-mcb.dtx
% and the derived files
%    luatexbase-mcb.sty, mcb.lua, luatexbase-mcb.pdf,
%    test-mcb-plain.tex test-mcb-latex.tex
%
% Unpacking:
%    tex luatexbase-mcb.dtx
% Documentation:
%    pdflatex luatexbase-mcb.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

Copyright (C) 2009 by Elie Roux <elie.roux@telecom-bretagne.eu>

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

\endpreamble

\let\MetaPrefix\DoubleperCent

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

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

\def\MetaPrefix{-- }

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

\def\currentpostamble{\luapostamble}%

\generate{%
  \usedir{tex/luatex/luatexbase}%
  \file{mcb.lua}{\from{luatexbase-mcb.dtx}{lua}}%
}

\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-mcb.sty mcb.lua}
\Msg{*}
\Msg{* Happy TeXing!}
\Msg{*}
\Msg{************************************************************************}

\endbatchfile
%</install>
%<*ignore>
\fi
%</ignore>
%<*driver>
\documentclass{ltxdoc}
\input{lltxb-dtxstyle}
\begin{document}
  \DocInput{luatexbase-mcb.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 \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}}
%
% \maketitle
%
% \begin{abstract}
% 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 explicitely.
% \end{abstract}
%
% \tableofcontents
%
% \section{Documentation}
%
% \subsection{Managing functions in callbacks}
%
% Lua\TeX\ 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
% enables to register a function in a callback.
%
% 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 signature of the functions that can be registered in it:
% \begin{description}
% \item[list] functions taking a list of nodes and returning a boolean and
% possibly a new head: (TODO);
% \item[data] functions taking datas and returning it modified: the functions
% are called in order and passed the return value of the previous function as
% an argument, the return value is that of the last function;
% \item[simple] functions that don't return anything: they are called in
% order, all with the same argument;
% \item[first] 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 postive 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)
% \end{verbatim}
%
% 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 privides a way to create and call new callbacks, in
% addition to the default Lua\TeX\ 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 teh 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 splitted callbacks) if it appears there is an actual need for it.
%
%    \section{Implementation}
%
%    \subsection{\tex package}
%
%    \begin{macrocode}
%<*texpackage>
%    \end{macrocode}
%
%    \subsubsection{Preliminaries}
%
%    Reload protection, especially for \plaintex.
%
%    \begin{macrocode}
                \csname lltxb@mcb@loaded\endcsname
\expandafter\let\csname lltxb@mcb@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@mcb@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-mcb}[2010/05/27 v0.2a Callback management 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-mcb}{LuaTeX is required for this package. Aborting.}
  \lltxb@mcb@AtEnd
  \expandafter\endinput
\fi
%    \end{macrocode}
%
%    \subsubsection{Load supporting Lua module}
%
%    First load \pk{luatexbase-loader} (hence \pk{luatexbase-compat}), then
%    the supporting Lua module.
%
%    \begin{macrocode}
\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname RequirePackage\endcsname\relax
  \input luatexbase-modutils.sty
\else
  \RequirePackage{luatexbase-modutils}
\fi
\luatexbase@directlua{require('luatexbase.mcb')}
%    \end{macrocode}
%
%    That's all folks!
%
%    \begin{macrocode}
\lltxb@mcb@AtEnd
%</texpackage>
%    \end{macrocode}
%
%    \subsection{Lua module}
%
%    \begin{macrocode}
%<*lua>
%    \end{macrocode}
%
%    \subsubsection{Module identification}
%
%    \begin{macrocode}
module('luatexbase', package.seeall)
local err, warning, info = luatexbase.provides_module({
    name          = "luamcallbacks",
    version       = 0.2,
    date          = "2010/05/12",
    description   = "register several functions in a callback",
    author        = "Hans Hagen, Elie Roux and Manuel Pegourie-Gonnard",
    copyright     = "Hans Hagen, Elie Roux and Manuel Pegourie-Gonnard",
    license       = "CC0",
})
%    \end{macrocode}
%
%    \subsubsection{Housekeeping}
%
%    The main table: keys are callback names, and values are the associated
%    lists of functions. Only callbacks with a non-empty list of functions have
%    an entry in this list.
%
%    \begin{macrocode}
local callbacklist = callbacklist or { }
%    \end{macrocode}
%
%    Numerical codes for callback types, and name to value association (the
%    table keys are strings, the values are numbers).
%
%    \begin{macrocode}
local list, data, first, simple = 1, 2, 3, 4
local types = {
  list   = list,
  data   = data,
  first  = first,
  simple = simple,
}
%    \end{macrocode}
%
%    Now, assign a type for each predefined callback.
%
%    \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,
}
%    \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.
%
%    \begin{macrocode}
if luatexbase.luatexversion > 42 then
    callbacktypes["process_output_buffer"] = data
end
%    \end{macrocode}
%
%    All user-defined callbacks have a default function: this property is used
%    to identify them as being user-defined. Those are kept in the following
%    table.
%
%    \begin{macrocode}
local lua_callbacks_defaults = { }
%    \end{macrocode}
%
%    Overwrite |callback.register|, but save it first.
%
%    \begin{macrocode}
local internalregister = internalregister or callback.register
callback.register = function ()
  err("function callback.register has been trapped,\n"
  .."please use luatexbase.add_to_callback instead.")
end
%    \end{macrocode}
%
%    \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.
%
%    Handler for |list| callbacks.
%
%    \begin{macrocode}
-- 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\nin callback '%s'",
                      f.description, name)
                end
            end
            return head, done
        else
            return head, false
        end
    end
end
%    \end{macrocode}
%
%    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
        end
        return data
    end
end
%    \end{macrocode}
%
%    Handler for |first| callbacks.
%
%    \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
    end
end
%    \end{macrocode}
%
%    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
        end
    end
end
%    \end{macrocode}
%
%    Finally, keep a handlers table for indexed access.
%
%    \begin{macrocode}
local handlers = {
  [list]   = listhandler,
  [data]   = datahandler,
  [first]  = firsthandler,
  [simple] = simplehandler,
}
%    \end{macrocode}
%
%    \subsubsection{Public functions for functions management}
%
%    Add a function to a callback. First check arguments.
%
%    \begin{macrocode}
function add_to_callback (name,func,description,priority)
    if type(func) ~= "function" then
        return err("unable to add function:\nno proper function passed")
    end
    if not name or name == "" then
        err("unable to add function:\nno proper callback name passed")
        return
    elseif not callbacktypes[name] then
        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':\nno proper description passed",
          name)
        return
    end
    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
        end
    end
%    \end{macrocode}
%
%    Actually register the function.
%
%    \begin{macrocode}
    local f = {
        func = func,
        description = description,
    }
    priority = tonumber(priority)
    if not priority or priority > #l then
        priority = #l+1
    elseif priority < 1 then
        priority = 1
    end
    table.insert(l,priority,f)
%    \end{macrocode}
%
%    Keep user informed.
%
%    \begin{macrocode}
    if callbacktypes[name] == first and (priority ~= 1 or #l ~= 0) then
        warning("several callbacks registered in callback '%s',\n"
        .."only the first function will be active.", name)
    end
    info("inserting function '%s'\nat position %s in callback list\nfor '%s'",
      description, priority, name)
end
%    \end{macrocode}
%
%    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:\nno proper callback name passed")
        return
    elseif not callbacktypes[name] then
        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':\nno proper description passed",
          name)
        return
    end
    local l = callbacklist[name]
    if not l then
        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
            index = k
            break
        end
    end
    if not index then
        err("unable to remove function '%s'\nfrom '%s'", description, name)
        return
    end
    table.remove(l, index)
    info("removing function '%s'\nfrom '%s'", description, name)
    if table.maxn(l) == 0 then
        callbacklist[name] = nil
        if not lua_callbacks_defaults[name] then
            internalregister(name, nil)
        end
    end
    return
end
%    \end{macrocode}
%
%    Remove all the functions registered in a callback. Unregisters the
%    callback handler unless the callback is user-defined.
%
%    \begin{macrocode}
function reset_callback (name)
    if not name or name == "" then
        err("unable to reset:\nno proper callback name passed")
        return
    elseif not callbacktypes[name] then
        err("reset error: '%s'\nis not a valid callback", name)
        return
    end
    if not lua_callbacks_defaults[name] then
        internalregister(name, nil)
    end
    local l = callbacklist[name]
    if l then
        info("resetting callback list '%s'",name)
        callbacklist[name] = nil
    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 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
%    \texttt{default} is the default function to call if no function is
%    registered in this callback.
%
%    The created callback will behave the same way Lua\TeX\ callbacks do, you
%    can add and remove functions in it. The difference is that the callback
%    is not automatically called, the package developer creating a new
%    callback must also call it, see next function.
%
%    \begin{macrocode}
function create_callback(name, ctype, default)
    if not name then
        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':\n"
        .."callbacktype or default function not specified", name)
        return nil
    end
    if callbacktypes[name] then
        err("unable to create callback '%s':\ncallback already exists", name)
        return nil
    end
    ctype = types[ctype]
    if not ctype then
        err("unable to create callback '%s':\ntype '%s' undefined", name, ctype)
        return nil
    end
    info("creating new callback '%s'", name)
    lua_callbacks_defaults[name] = default
    callbacktypes[name] = ctype
end
%    \end{macrocode}
%
%    This function calls a callback. It can only call a callback created by
%    the \texttt{create} function.
%
%    \begin{macrocode}
function call_callback(name, ...)
    if not name then
        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':\nunknown callback", name)
        return nil
    end
    local l = callbacklist[name]
    local f
    if not l then
        f = lua_callbacks_defaults[name]
    else
        f = handlers[callbacktypes[name]]
        if not f then
            err("unknown callback type")
            return
        end
    end
    return f(...)
end
%    \end{macrocode}
%
%    That's all folks!
%
%    \begin{macrocode}
%</lua>
%    \end{macrocode}
%
%    \section{Test files}
%
%    A few basic tests for Plain and LaTeX.
%
%    \begin{macrocode}
%<testplain>\input luatexbase-mcb.sty
%<testlatex>\RequirePackage{luatexbase-mcb}
%<*testplain,testlatex>
\catcode 64 11
\luatexbase@directlua{
  local function sample(head,...)
      return head, true
  end

  local prio = luatexbase.priority_in_callback
  luatexbase.add_to_callback("hpack_filter", sample, "sample function one", 1)
  luatexbase.add_to_callback("hpack_filter", sample, "sample function two", 2)
  luatexbase.add_to_callback("hpack_filter", sample, "sample function three", 1)
  assert(prio("hpack_filter", "sample function three"))
  luatexbase.remove_from_callback("hpack_filter", "sample function three")
  assert(not prio("hpack_filter", "sample function three"))
  luatexbase.reset_callback("hpack_filter")
  assert(not prio("hpack_filter", "sample function one"))

  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

  luatexbase.create_callback("fooback", "data", data_one)
  luatexbase.call_callback("fooback", "default")
  luatexbase.add_to_callback("fooback", data_two, "my sample function two", 2)
  luatexbase.add_to_callback("fooback", data_three, "my sample function three", 1)
  luatexbase.call_callback("fooback", "all")
  luatexbase.remove_from_callback("fooback", "my sample function three")
  luatexbase.call_callback("fooback", "all but three")
  luatexbase.reset_callback("fooback")
  luatexbase.call_callback("fooback", "default")
}
%</testplain,testlatex>
%<testplain>\bye
%<testlatex>\stop
%    \end{macrocode}
%
% \Finale
\endinput
