--- /dev/null
+// Copyright (C) 2007
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
+// Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/** \file
+ \brief Conenctors public header */
+
+/** \defgroup connectors Connector classes
+
+ A connector has two independent properties
+ \li it may be \e active or \e passive
+ \li it may be an \e input or an \e output
+
+ \e Active connectors are activated from within the module, \e passive connectors are signaled by
+ the external framework. \e Input modules receive packets, \e output modules send packets.
+
+ All passive connectors call some onRequest callback whenever I/O needs to be performed. All
+ input modules possess a packet queue.
+
+ We therefore have 4 connector types: senf::ppi::ActiveInput, senf::ppi::ActiveOutput,
+ senf::ppi::PassiveInput and senf::ppi::PassiveOutput.
+ */
+
+#ifndef HH_Conenctors_
+#define HH_Conenctors_ 1
+
+// Custom includes
+#include <boost/utility.hpp>
+
+//#include "Conenctors.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace ppi {
+
+ /** \brief Connector baseclass
+
+ This connector provides access to the generic connector facilities. This includes the
+ connection management (access to the connected peer) and the containment management (access
+ to the containing module)
+ */
+ class Connector
+ : boost::noncopyable
+ {
+ public:
+ Connector & peer(); ///< Get peer connected to this connector
+ Module & module(); ///< Get this connectors containing module
+
+ protected:
+ // here to protect
+ Connector();
+ ~Connector();
+ };
+
+ /** \brief Passive connector baseclass
+
+ A passive connector is a connector which is activated externally whenever an I/O request
+ occurs. Passive connectors are the origin of throttling notifications. Depending on the type
+ of connector (output or input) the respective throttling is called forward or backward
+ throttling.
+
+ Passive connectors always handle two throttling states:
+
+ \li The \e native throttling state is set manually by the module. It is the throttling state
+ originating in the current module
+ \li The \e forwarded throttling state is the state as it is received by throttling
+ notifications
+
+ The accumulative throttling state is generated by combining all sub-states.
+
+ \ingroup connectors
+ */
+ class PassiveConnector
+ : public virtual Connector
+ {
+ public:
+ template <class Handler>
+ void onRequest(Handler handler);///< Register I/O event handler
+ /**< The registered handler will be called, whenever packets
+ arrive or should be generated by the module depending
+ on the connector type (input or output). The \a handler
+ argument is either an arbitrary callable object or it
+ is a pointer-to-member to a member of the class which
+ holds this input. In the second case, the pointer will
+ automatically be bound to the containing instance.
+
+ \param[in] handler Handler to call, whenever an I/O
+ operation is to be performed. */
+
+
+ bool throttled(); ///< Get accumulative throttling state
+ bool nativeThrottled(); ///< Get native throttling state
+
+ void throttle(); ///< Set native throttling
+ void unthrottle(); ///< Revoke native throttling
+
+ void notifyThrottle(); ///< Forward a throttling notification to this connector
+ void notifyUnthrottle(); ///< Forward an unthrottling notification to this connector
+
+ ActiveConnector & peer();
+
+ protected:
+ // here to protect
+ PassiveConnector();
+ ~PassiveConnector();
+ };
+
+ /** \brief Active connector baseclass
+
+ An active connector is a connector which emits I/O requests. Active connectors receive
+ throttling notifications. Depending on the type of connector (input or output) the
+ respective throttling is called forward or backward throttling.
+
+ Active connectors do not handle any throttling state, they just receive the
+ notifications. These notifications should then either be processed by the module or be
+ forwarded to other connectors.
+
+ \ingroup connectors
+ */
+ class ActiveConnector
+ : public virtual Connector
+ {
+ public:
+ template <class Handler>
+ void onThrottle(Handler); ///< Register throttle notification handler
+ /**< The handler register here will be called, whenever a
+ throttle notification comes in. The \a handler argument
+ is either an arbitrary callable object or it is a
+ pointer-to-member to a member of the class which holds
+ this input. In the second case, the pointer will
+ automatically be bound to the containing instance.
+
+ \param[in] handler Handler to call on throttle
+ notifications. */
+
+ template <class Handler>
+ void onUnthrottle(Handler); ///< Register unthrottle notification handler
+ /**< The handler register here will be called, whenever an
+ unthrottle notification comes in. The \a handler
+ argument is either an arbitrary callable object or it
+ is a pointer-to-member to a member of the class which
+ holds this input. In the second case, the pointer will
+ automatically be bound to the containing instance.
+
+ \param[in] handler Handler to call on unthrottle
+ notifications. */
+
+ PassiveConnector & peer();
+
+ protected:
+ // here to protect
+ PassiveConnector();
+ ~PassiveConnector();
+ };
+
+ /** \brief Input connector baseclass
+
+ An input connector receives packets. It may be either an ActiveConnector or a
+ PassiveConnector. An input connector contains a packet queue. This queue enables processing
+ packets in batches or generating multiple output packets from a single input packet. The
+ queues have the potential to greatly simplify the module implementations.
+
+ \ingroup connectors
+ */
+ class InputConnector
+ : public virtual Connector
+ {
+ public:
+ typedef unspecified queue_iterator; ///< Iterator type of the embedded queue
+ typedef unspecified size_type; ///< Unsigned type representing the number of queue elements
+
+ Packet::ptr operator(); ///< Get a packet
+ /**< This member is the primary method to access received
+ data. On passive connectors, this operator will just
+ dequeue a packet from the packet queue. If the
+ connector is active, the connector will request new
+ packets from the connected module. If the packet
+ request cannot be fulfilled, this is considered to be a
+ logic error in the module implementation and an
+ exception is raised. */
+ operator unspecified_boolean_type (); ///< Check packet availability
+ /**< Using any input connector in a boolean context will
+ check, wether an input request can be fulfilled. This
+ is always possible if the queue is non-empty. If the
+ input is active, it also returns when the connected
+ passive output is not throttled so new packets can be
+ requested.
+
+ Calling the operator() member is an error if this test
+ returns \c false
+
+ \returns \c true if operator() can be called, \c false
+ otherwise */
+ operator ! (); ///< Check packet availability
+ /**< Inverse of the boolean conversion operator
+ \returns \c false if operator() can be called, \c true
+ otherwise */
+
+ OutputConnector & peer();
+
+ queue_iterator begin(); ///< Access queue begin (head)
+ queue_iterator end(); ///< Access queue past-the-end (tail)
+ Packet::ptr head(); ///< Return head element from the queue
+
+ size_type queueSize(); ///< Return number of elements in the queue
+ bool empty(); ///< Return queueSize() == 0
+
+ protected:
+ // here to protect
+ PassiveConnector();
+ ~PassiveConnector();
+ };
+
+ /** \brief Output connector baseclass
+
+ An output connector sends out packets. It may be either an ActiveConnector or a
+ PassiveConnector. An output connector does \e not have an built-in queueing, it relies on
+ the queueing of the connected input.
+
+ \ingroup connectors
+ */
+ class OutputConnector
+ : public virtual Connector
+ {
+ public:
+ void operator(Packet::ptr); ///< Send out a packet
+
+ InputConnector & peer();
+
+ protected:
+ // here to protect
+ PassiveConnector();
+ ~PassiveConnector();
+ };
+
+ /** \brief Combination of PassiveConnector and InputConnector
+
+ \ingroup connectors
+ */
+ class PassiveInput
+ : public PassiveConnector, public InputConnector
+ {
+ public:
+ ActiveOutput & peer();
+
+ template <class QDisc>
+ QDisc const & qdisc(QDisc const & disc);
+ };
+
+ /** \brief Combination of PassiveConnector and OutputConnector
+
+ \ingroup connectors
+ */
+ class PassiveOutput
+ : public PassiveConnector, public OutputConnector
+ {
+ public:
+ ActiveInput & peer();
+ };
+
+ /** \brief Combination of ActiveConnector and InputConnector
+
+ \ingroup connectors
+ */
+ class ActiveInput
+ : public ActiveConnector, public InputConnector
+ {
+ public:
+ void request(); ///< request more packets without dequeing any packet
+
+ PassiveOutput & peer();
+ };
+
+ /** \brief Combination of ActiveConnector and OutputConnector
+
+ \ingroup connectors
+ */
+ class ActiveOutput
+ : public ActiveConnector, public OutputConnector
+ {
+ public:
+ ActiveInput & peer();
+ };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "Conenctors.cci"
+//#include "Conenctors.ct"
+//#include "Conenctors.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// End:
PROJECT_NAME = libPPI
GENERATE_TAGFILE = doc/ppi.tag
+
+TAGFILES = "$(TOPDIR)/Packets/doc/Packets.tag" "$(TOPDIR)/Socket/doc/Socket.tag"
The PPI concept is built around some key concepts
- \li The PPI is based on processing \e packets. It does not handle stream oriented channels.
- \li The PPI is built around reusable \e modules. Each module is completely independent.
- \li Each module has an arbitrary number of \e connectors, inputs and outputs.
- \li The modules are connected to each other using flexible \e connections.
- \li Data flow throughout the network is governed via flexible automatic or manual \e throttling.
- \li Modules may register additional external \e events (file descriptor events or timers).
+ \li The PPI is based on processing \ref packets. It does not handle stream oriented channels.
+ \li The PPI is built around reusable \ref modules. Each module is completely independent.
+ \li Each module has an arbitrary number of \ref connectors, inputs and outputs.
+ \li The modules are connected to each other using flexible \ref connections.
+ \li Data flow throughout the network is governed via flexible automatic or manual \ref throttling.
+ \li Modules may register additional external \ref events (file descriptor events or timers).
The PPI thereby builds on the facilities provided by the other components of the SENF
framework.
route(payload, output);
route(stuffing, output);
- registerEvent(&RateStuffer::tick, IntervalTimer(1000u, packetsPerSecond));
+ registerEvent(&RateStuffer::tick,
+ senf::ppi::IntervalTimer(1000u, packetsPerSecond));
}
private:
This module just produces a copy of a given packet whenever output is requested.
- \subsection connectors Connectors
+ \section connectors Connectors
Inputs and Outputs can be active and passive. An \e active I/O is <em>activated by the
module</em> to send data or to poll for available packets. A \e passive I/O is <em>signaled by
iteration. However, reading will only succeed, as long as packets are available from the
connection.
- A module might want to queue incoming packets within a passive input or outgoing packets within
- an active output. This is possible by either not reading any packet even though a new packet has
- been scheduled on the input or by writing to the output while it is still throttled. To
- facilitate this use, the connectors provide accessors to access the attached connection and it's
- queue. This allows to analyze all packets available in the queue and act accordingly.
+ Since a module is free to generate more than a single packet on incoming packet requests, all
+ input connectors incorporate a packet queue. This queue is exposed to the module and allows the
+ module to process packets in batches.
\section connections Connections
To make use of the modules, they have to be instantiated and connections have to be created
between the I/O connectors. It is possible to connect any pair of input/output connectors as
- long as at least one of them is active
-
- Every connection contains an internal packet queue. Under normal operating conditions (without
- throttling) the queue will mostly be empty since packets will be processed directly. If a
- connection is throttled, it can still receive new packets on it's input which will then be
- queued. This is necessary even though the throttling will be propagated backwards (so no new
- packets should arrive) since a module may produce more then one result packet from a single
- incoming packet.
+ long as one of them is active and the other is passive.
+
+ It is possible to connect two active connectors with each other using a special adaptor
+ module. This Module has a passive input and a passive output. It will queue any incoming packets
+ and automatically handle throttling events (see below). This adaptor is automatically added by
+ the connect method if needed.
- To complete our simplified example: Lets say we have a <tt>UdpInput</tt> module and a
- <tt>UdpOutput</tt> module. We can then use our <tt>RateStuffer</tt> module to build an
+ To complete our simplified example: Lets say we have an <tt>ActiveSocketInput</tt> and a
+ <tt>PassiveUdpOutput</tt> module. We can then use our <tt>RateStuffer</tt> module to build an
application which will create a fixed-rate UDP stream:
\code
RateStuffer rateStuffer (10);
- CopyPacketGenerator generator (some_packet_ptr);
+
+ senf::Packet::ptr stuffingPacket = senf::Packet::create<...>(...);
+ CopyPacketGenerator generator (stuffingPacket);
+
senf::UDPv4ClientSocketHandle inputSocket (1111);
- senf::ppi::SocketInput udpInput (inputSocket);
+ senf::ppi::ActiveSocketInput udpInput (inputSocket);
+
senf::UDPv4ClientSocketHandle outputSocket ("2.3.4.5:2222");
- senf::ppi::SocketOutput udpOutput (outputSocket);
+ senf::ppi::PassiveSocketOutput udpOutput (outputSocket);
- senf::ppi::connect(udpInput.output, rateStuffer.payload)
- .bufferHighThresh(10)
- .bufferLowThresh(5);
+ senf::ppi::connect(udpInput.output, rateStuffer.payload,
+ dynamicModule<PassiveQueue>()
+ -> qdisc(ThresholdQueueing(10,5)) );
senf::ppi::connect(generator.output, rateStuffer.stuffing);
senf::ppi::connect(rateStuffer.output, udpOutput.input);
\endcode
First all necessary modules are created. Then the connections between these modules are set
- up. The buffering of the udpInput <-> rateStuffer connection is changed so the queue will begin
- to throttle only if more than 10 packets are in the queue. The connection will be unthrottled as
+ up. The buffering on the udpInput <-> rateStuffer adaptor is changed so the queue will begin to
+ throttle only if more than 10 packets are in the queue. The connection will be unthrottled as
soon as there are no more than 5 packets left in the queue. This application will read
udp-packts coming in on port 1111 and will forward them to port 2222 on host 2.3.4.5 with a
- fixed rate of 10 packets / second.
+ fixed rate of 10 packets / second.
\section throttling Throttling
- If a connection cannot pass packets in it's current state, the connection is \e throttled. In
- simple cases, throttling is handled automatically by
- \li the connection if the queue is exceeds the buffering threshold
- \li I/O modules whenever the external source or sink of the module is not ready
-
- Throttling is handled separately in each direction:
- \li Forward throttling will throttle in the direction of the data flow. Example: No new packets
- are available from an input. This will activate forward throttling until new data arrives
- \li Backward throttling will throttle in the direction to the data source. Example: an output
- device (e.g. serial interface) has no more room for data. This event will activate backward
- throttling so no new data will arrive until the device can send data again
-
- The throttling state is managed within the Connection. Automatic throttling utilizes the routing
- information (provided in the modules constructor) to forward throttling events across
- modules. However, automatic throttling can be disabled for each connector. Modules may also
- register event handlers whenever a throttling event occurs.
-
- Whenever a connection is throttled (in the corresponding direction), passive connectors will \e
- not be called by the framework. This may lead to packets being accumulated in the connection
- queue. These packets will be sent as soon as the connection is unthrottled. The unthrottle event
- will hoewever only be forwarded when the queue is empty (or has reached it's lower buffering
- threshold).
-
- \code
- passiveConnector.autoForwardThrottling(false);
- passiveConnector.autoBackwardThrotttling(true);
- passiveConnector.onForwardThrottle(...);
- passiveConnector.onBackwardUnthrottle(...);
- \endcode
-
- Throttling is <em>not</em> enforced: especially a throttled output may still be called, the
- excessive packets will be queued in the connection queue.
+ If a passive connector cannot handle incoming requests, this connector may be \e
+ throttled. Throttling a request will forward a throttle notification to the module connected
+ to that connector. The module then must handle this throttle notification. If automatic
+ throttling is enabled for the module (which is the default), the notification will automatically
+ be forwarded to all dependent connectors as taken from the flow information. For there it will
+ be forwarded to further modules and so on.
+
+ A throttle notification reaching an I/O module will normally disable the input/output by
+ disabling any external I/O events registered by the module. When the passive connector which
+ originated the notification becomes active again, it creates an unthrottle notification which
+ will be forwarded in the same way. This notification will re-enable any registered I/O events.
+
+ The above discussion shows, that throttle events are always generated on passive connectors and
+ received on active connectors. To differentiate further, the throttling originating from a
+ passive input is called <em>backward throttling</em> since it is forwarded in the direction \e
+ opposite to the data flow. Backward throttling notifications are sent towards the input
+ modules. On the other hand, the throttling originating from a passive output is called
+ <em>forward throttling</em> since it is forwarded along the \e same direction the data
+ is. Forward throttling notifications are therefore sent towards the output modules.
+
+ Since throttling a passive input may not disable all further packet delivery immediately, any
+ passive input contains an input queue. In it's default configuration, the queue will send out
+ throttle notifications when it becomes non-empty and unthrottle notifications when it becomes
+ empty again. This automatic behavior may however be disabled. This allows a module to collect
+ incoming packets in it's input queue before processing a bunch of them in one go.
\section events Events
the PPI framework. Possible event sources are
\li time based events
\li file descriptors.
+
+ Here some example code implementing the ActiveSocketInput Module:
+
+ \code
+ class ActiveSocketInput
+ : public senf::ppi::Module
+ {
+ static PacketParser<senf::DataPacket> defaultParser_;
+
+ public:
+ ActiveOutput output;
+
+ typedef senf::ClientSocketHandle<
+ senf::MakeSocketPolicy< senf::ReadablePolicy,
+ senf::DatagramFramingPolicy > > Socket;
+
+ // I hestitate taking parser by const & since a const & can be bound to
+ // a temporary even though a const & is all we need. The real implementation
+ // will probably make this a template arg. This simplifies the memory management
+ // from the users pov.
+ ActiveSocketInput(Socket socket, DataParser & parser = SocketInput::defaultParser_)
+ : socket_ (socket),
+ parser_ (parser)
+ event_ (registerEvent( &ActiveSocketInput::data,
+ senf::ppi::IOSignaler(socket, senf::ppi::IOSignaler::Read) ))
+ {
+ route(event_, output);
+ }
+
+ private:
+ Socket socket_;
+ DataParser const & parser_;
+ senf::ppi:IOSignaler::EventBinding event_;
+
+ void data()
+ {
+ std::string data;
+ socket_.read(data);
+ output(parser_(data));
+ }
+ };
+ \endcode
+
+ First we declare our own socket handle type which allows us to read packets. The constructor
+ then takes two arguments: A compatible socket and a parser object. This parser object gets
+ passed the packet data as read from the socket (an \c std::string) and returns a
+ senf::Packet::ptr. The \c PacketParser is a simple parser which interprets the data as specified
+ by the template argument.
+
+ We register an IOSignaler event. This event will be signaled whenever the socket is
+ readable. This event is routet to the output. This routing automates throttling for the socket:
+ Whenever the output receives a throttle notifications, the event will be temporarily disabled.
+
+ Processing arriving packets happens in the \c data() member: This member simple reads a packet
+ from the socket. It passes this packet to the \c parser_ and sends the generated packet out.
+
+ \implementation Generation of throttle notifications: Backward throttling notifications are
+ automatically generated (if this is not disabled) whenever the input queue is non-empty \e
+ after the event handler has finished processing. Forward throttling notifications are not
+ generated automatically within the connector. However, the Passive-Passive adaptor will
+ generate Forward-throttling notifications whenever the input queue is empty.
+
+ \note Open Issues
+ \li We need to clearly differentiate between auto-throttling and auto-throttle-forwarding,
+ between a connectors own throttling state and the forwarded state.
+ \li Exception handling
+ \li ActiveInputs also need a queue: This is necessary to allow a PassiveOutput to create more
+ than a single packet from a single 'onRequest' event. This greatly simplifies writing
+ modules which produce multiple output packets for a single input packet.
+ \li We need to clear up the throttled() member semantics: If the connector is throttled, does
+ it return so if there are still packets in the queue? Probably yes. However, it does not
+ forward throttling notifications until instructed by the qdisc. Throttling notifications are
+ also bound to onThrottle/onUnThrottle callbacks. The semantics are then clear: An active
+ connector emitting onThrottle cannot process any further request (for inputs, no data will
+ be available, for outputs the data will be queued in the peer input)
*/
\f
--- /dev/null
+// Copyright (C) 2007
+// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS)
+// Kompetenzzentrum fuer Satelitenkommunikation (SatCom)
+// Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/** \file
+ \brief Module public header */
+
+#ifndef HH_Module_
+#define HH_Module_ 1
+
+// Custom includes
+#include <boost/utility.hpp>
+
+//#include "Module.mpp"
+///////////////////////////////hh.p////////////////////////////////////////
+
+namespace senf {
+namespace ppi {
+
+ /** \brief Module baseclass
+
+ senf::ppi::Module is the baseclass of all PPI modules. It provides the module implementation
+ with interfaces to several PPI facilities:
+
+ \li Connector management
+ \li Flow management
+ \li Event handling
+
+ To provide internal bookkeeping, most access to the PPI infrastructure is managed through
+ this base class.
+
+ Instances of this class may be allocated either statically or dynamically. Dynamic instances
+ are automatically managed using the dynamicModule adaptor.
+ */
+ class Module
+ : boost::noncopyable
+ {
+ protected:
+ Module();
+ ~Module();
+
+ template <class Source, class Target>
+ Route route(Source const & source, Target const & target); ///< Define flow information
+ /**< Using the route() and noroute() members, the
+ information flow within the module is defined. Routing
+ may be specified either between inputs, outputs and
+ events. The routing information is used to perform
+ automatic throttling. The throttling behavior may
+ however be controlled manually.
+
+ Even if no automatic throttling is desired <em>it is
+ vital to define the flow information for all inputs and
+ outputs</em>. Without flow information important
+ internal state of the module cannot be
+ initialized. This includes, explicitly defining
+ terminal inputs and outputs using noroute. Event
+ routing however is optional.
+
+ The return value may be used to alter routing
+ parameters like throttling parameters.
+
+ \param[in] source Data source, object which controlls
+ incoming data
+ \param[in] target Data target, object which controlls
+ outgoing data
+ \returns Route instance describing this route */
+
+ template <class Connector>
+ void noroute(Connector const & connector); ///<Define terminal connectors
+ /**<
+
+ The noroute() member explicitly declares, that a
+ connector is terminal and does not directly
+ receive/forward data from/to some other
+ connector. <em>It is mandatory to define routing
+ information for terminal connectors</em>.
+
+ See the route() documentation for more on routing
+
+ \param[in] connector Terminal connector to declare */
+
+ template <class Target, class Descriptor>
+ typename Descriptor::EventBinding const registerEvent(Target target,
+ Descriptor const & descriptor);
+ ///< Register an external event
+ /**< The \a target argument may be either an arbitrary
+ callable object or it may be a member function pointer
+ pointing to a member function of the Module derived
+ classed. The handler may \e optionally take an Argument
+ of type <tt>typename Descriptor::Event const
+ &</tt>. This object allows to access detailed
+ information on the event delivered.
+
+ The \a descriptor describes the event to signal. This
+ may be a timer event or some type of I/O event on a
+ file descriptor or socket.
+
+ The return value may be used to modify the
+ binding. This allows to temporarily inhibit event
+ delivery or to remove the binding explicitly. Depending
+ on the type of event, other operations may be
+ possible. See the event descriptor documentation.
+
+ \param[in] target The handler to call whenever the event
+ is signaled
+ \param[in] descriptor The type of event to register
+ \returns An event binding instance of the appropriate
+ type. */
+ };
+
+}}
+
+///////////////////////////////hh.e////////////////////////////////////////
+//#include "Module.cci"
+//#include "Module.ct"
+//#include "Module.cti"
+#endif
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// End:
env.Command('search.php', 'html-munge.xsl',
[ writeTemplate,
'xsltproc --nonet --html --stringparam topdir .. -o - $SOURCE $TARGET 2>/dev/null'
- + r'| sed -e "s/\[\[/<?/g" -e "s/\]\]/?>/g" > ${TARGET}.tmp',
+ + "| sed"
+ + r" -e 's/\[\[/<?/g' -e 's/\]\]/?>/g'"
+ + r" -e 's/\$$projectname/Overview/g'"
+ + r" -e 's/\$$title/Search results/g'"
+ + "> ${TARGET}.tmp",
'mv ${TARGET}.tmp ${TARGET}' ],
TEMPLATE = Literal(HEADER
+ OVERVIEW_EXTRA_HEADER