From: g0dil Date: Fri, 9 Feb 2007 14:15:40 +0000 (+0000) Subject: Finished libSocket and libUtils documentation X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=553781d9e9bce316dca24ac4f0c42e5613e849e0;p=senf.git Finished libSocket and libUtils documentation git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@196 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Packets/Mainpage.dox b/Packets/Mainpage.dox index 2b25b40..bfb1120 100644 --- a/Packets/Mainpage.dox +++ b/Packets/Mainpage.dox @@ -72,6 +72,34 @@ ethernet header). Together with it's support classes (especially senf::PacketRegistryMixin) this class greatly simplifies implementing the needed table lookups. + + \todo The Packet Libarary really needs a refactoring of the public + interfaface ... + + \idea Add the Handle-Body idiom to the mix with a PacketRef (or + HeaderRef or InterpreterRef or whatever class). This would + have members for all the API defined in Packet now. \c + operator-> would return a parser object to interpret the + data. This would make awayy with the inheritance relationship + ... + + \idea Templating the parsers on the iterator type does not + introduce additional coupling (because of the inlining) but + looking at it after the fact it looks like severe overdesign + and it does introduce some problems (e.g. rebind and all this + entails). If we just implement all parsers for + Packet::byte_iterator they are no tmplates any more which + should simplify things a log. + + \idea we need some better and automatic checking on data access + especially after data has changed. Idea 1: give the parser the + end iterator as additional member. Enforce, that all parsers + must ultimately be based on ParseInt and have ParseInt check + against end() at construction time. Idea 2: add a dirty flag + to the interpreters. Set this flag whenever the packet is + changed and recall check() in operator-> of the PacketRef + object if the packet is dirty. Maybe we need both and make + them tunable. */ diff --git a/Scheduler/Doxyfile b/Scheduler/Doxyfile index 38eada5..7ea57be 100644 --- a/Scheduler/Doxyfile +++ b/Scheduler/Doxyfile @@ -3,3 +3,4 @@ PROJECT_NAME = libScheduler TAGFILES = "$(TOPDIR)/Utils/doc/Utils.tag" GENERATE_TAGFILE = doc/Scheduler.tag +ALPHABETICAL_INDEX = NO diff --git a/Scheduler/Mainpage.dox b/Scheduler/Mainpage.dox new file mode 100644 index 0000000..6084482 --- /dev/null +++ b/Scheduler/Mainpage.dox @@ -0,0 +1,33 @@ +namespace senf { + +/** \mainpage The SENF Scheduler Library + + The Scheduler library provides a simple yet flexible abstraction + of the standard asynchronous UNIX mainloop utilizing \c select or + \c poll. The Scheduler library is based on the highly efficient + (but linux specific) \c epoll() system call. + + The library provides + \li a central \ref Scheduler singleton and + \li \ref ReadHelper and \ref WriteHelper templates to simplify + common tasks. + + In it's current incarnation, the library only supports network + file handles (including pipes etc) and simple timers (especially + it does not support asynchronous notification for on-disc file + transfers etc). Additional features will be added: + \li UNIX signal support + \li async IO support for local (disc) file handles + \li multi threading support + \li IPC support for multithreaded applications + */ + +} + + +// Local Variables: +// mode: c++ +// mode: flyspell +// mode: auto-fill +// ispell-local-dictionary: "american" +// End: diff --git a/Scheduler/ReadHelper.cci b/Scheduler/ReadHelper.cci index 3d1a0aa..c85bb25 100644 --- a/Scheduler/ReadHelper.cci +++ b/Scheduler/ReadHelper.cci @@ -1,19 +1,36 @@ // $Id$ // +// Copyright (C) 2006 Stefan Bund +// +// 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. // Copyright (C) 2006 -// Definition of inline non-template functions +/** \file + \brief ReadHelper inline non-template implementation */ // Custom includes #define prefix_ inline ///////////////////////////////cci.p/////////////////////////////////////// -prefix_ senf::ReadUntil::ReadUntil(std::string target_) +prefix_ senf::ReadUntil::ReadUntil(std::string const & target_) : target(target_) {} -prefix_ std::string::size_type senf::ReadUntil::operator()(std::string data) +prefix_ std::string::size_type senf::ReadUntil::operator()(std::string const & data) { return data.find(target); } diff --git a/Scheduler/ReadHelper.ct b/Scheduler/ReadHelper.ct index 6a5d0b7..83a557a 100644 --- a/Scheduler/ReadHelper.ct +++ b/Scheduler/ReadHelper.ct @@ -1,8 +1,25 @@ // $Id$ // +// Copyright (C) 2006 Stefan Bund +// +// 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. // Copyright (C) 2006 -// Definition of non-inline template functions +/** \file + \brief ReadHelper non-inline template implementation */ #include "ReadHelper.ih" @@ -52,6 +69,7 @@ template prefix_ void senf::ReadHelper::process(Handle handle, senf::Scheduler::EventId event) { + /** \fixme Move the done() calls to outside the try/catch block */ try { if (event != senf::Scheduler::EV_READ) throw SystemException(EPIPE); diff --git a/Scheduler/ReadHelper.cti b/Scheduler/ReadHelper.cti index 2e9a533..819b0b6 100644 --- a/Scheduler/ReadHelper.cti +++ b/Scheduler/ReadHelper.cti @@ -1,8 +1,25 @@ // $Id$ // +// Copyright (C) 2006 Stefan Bund +// +// 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. // Copyright (C) 2006 -// Definition of inline template functions +/** \file + \brief ReadHelper inline template implementation */ #include "ReadHelper.ih" @@ -24,7 +41,7 @@ template template prefix_ typename senf::ReadHelper::ptr senf::ReadHelper::dispatch(Handle handle, std::string::size_type maxSize, - Predicate predicate, Callback callback) + Predicate const & predicate, Callback callback) { return ptr(new ReadHelper(handle, maxSize, new typename InternalPredicate::template Dispatcher(predicate), diff --git a/Scheduler/ReadHelper.hh b/Scheduler/ReadHelper.hh index 39ea480..23d1157 100644 --- a/Scheduler/ReadHelper.hh +++ b/Scheduler/ReadHelper.hh @@ -1,7 +1,25 @@ // $Id$ // +// Copyright (C) 2006 Stefan Bund +// +// 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. // Copyright (C) 2006 +/** \file + \brief ReadHelper public header */ #ifndef HH_ReadHelper_ #define HH_ReadHelper_ 1 @@ -21,9 +39,31 @@ namespace senf { - /** \brief + /** \brief Asyncronous reading helper + + This class provides a simple asynchronous reading facility. This helper will register with + the Scheduler and read incoming data. It will collect the data until a specific numbner of + bytes has been read or some Predicate evaluated on the data read thus far signals end of + data. + + The ReadHelper accepts the same flexible file handle interfaces as the Scheduler. + + The callback must take a ReadHelper::ptr argument. Using this ReadHelper instance, the + callback can access the data read or retrieve state information. + + The ReadHelper separates the data into two parts: data() will return the matched data, + tail() will return any surplus data already read. If you don't specify a predicate, tail() + will always be empty (there may however some data be left in the socket input buffer after + the ReadHelper returns). + + The predicate is any class instance with an operator(std::string const &). This + operator is called, whenever some data has been read. If the data is not yet complete, the + predicate must return \c std::string::npos. If the ReadHelper should stop readeing more + data, the predicate must return the number of bytes which are to be considered 'matched'. \todo Move all not Handle dependent members to a ReadHandleBase class + \todo Add an optional std::string const & tail argument to the constructors which + takes the tail() of a previous ReadHelper instance. */ template class ReadHelper @@ -33,34 +73,56 @@ namespace senf { /////////////////////////////////////////////////////////////////////////// // Types - typedef boost::intrusive_ptr ptr; - typedef boost::function Callback; + typedef boost::intrusive_ptr ptr; ///< Smart pointer type for this class + typedef boost::function Callback; ///< Callback type /////////////////////////////////////////////////////////////////////////// ///\name Structors and default members ///@{ static ptr dispatch(Handle handle, std::string::size_type maxSize, - Callback callback); + Callback callback); ///< Register new ReadHandler instance + /**< The registered Callback will be called after \a maxSize + bytes have been read or EOF or some error is + encountered. + \post The returned ReadHelper instance is registered + with the Scheduler to handle read events. + \param[in] handle file descriptor or handle providing + the Handle interface defined above. + \param[in] maxSize maximum number of bytes to read + \param[in] cb callback + \returns Smart pointer to new ReadHelper instance */ template - static ptr dispatch(Handle handle, std::string::size_type maxSize, Predicate predicate, - Callback callback); + static ptr dispatch(Handle handle, std::string::size_type maxSize, Predicate const & predicate, + Callback callback); ///< Register new ReadHelper instance + /**< The registered Callback will be called after the \a + predicate returns a Value other than \c + std::string::npos, \a maxSize bytes have been read, or + EOF or some error condition is encountered. + \post The returned ReadHelper instance is registered + with the Scheduler to handle read events +. \param[in] handle file descriptor or handle providing + the Handle interface defined above. + \param[in] maxSize maximum number of bytes to read + \param[in] predicate predicate to check + \param[in] cb callback + \returns smart pointer to new ReadHelper instance */ ///@} /////////////////////////////////////////////////////////////////////////// - Handle handle() const; - unsigned maxSize() const; + Handle handle() const; ///< Access the handle object + unsigned maxSize() const; ///< Return maximum number of bytes to be read - std::string const & data() const; - std::string const & tail() const; + std::string const & data() const; ///< return data read + std::string const & tail() const; ///< return data read but not matched by the predicate - bool complete() const; - bool error() const; - void throw_error() const; + bool complete() const; ///< Check wether the read has completed successfully + bool error() const; ///< Check for error condition + void throw_error() const; ///< If an error occured, throw it - void revoke(); + void revoke(); ///< Remove the ReadHelper from the scheduler protected: @@ -84,10 +146,18 @@ namespace senf { bool complete_; }; + /** \brief ReadHelper predicate matching an arbitrary string + + This predicate will terminate the read when the data read matches a given fixed string. All + data up to and including the string matched is considered to be part of the data() portion, + everything after the matched string is placed into the tail(). + + \see ReadHelper + */ struct ReadUntil { - ReadUntil(std::string target); - std::string::size_type operator()(std::string data); + ReadUntil(std::string const & target); + std::string::size_type operator()(std::string const & data); std::string target; }; @@ -103,4 +173,6 @@ namespace senf { // Local Variables: // mode: c++ +// c-file-style: "senf" +// fill-column: 100 // End: diff --git a/Scheduler/ReadHelper.ih b/Scheduler/ReadHelper.ih index bca58bc..5df6164 100644 --- a/Scheduler/ReadHelper.ih +++ b/Scheduler/ReadHelper.ih @@ -1,7 +1,26 @@ // $Id$ // +// Copyright (C) 2006 Stefan Bund +// +// 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. // Copyright (C) 2006 +/** \file + \brief ReadHelper internal header */ + #ifndef IH_ReadHelper_ #define IH_ReadHelper_ 1 @@ -12,10 +31,24 @@ namespace senf { + /** \brief Abstract predicate interface + \internal + */ template struct ReadHelper::InternalPredicate { virtual ~InternalPredicate() {} + + /** \brief template to runtime polymorphic barrier for the predicate interface + \internal + + \implementation This class will provide a polymorphic + wrapper around the non-polymorphic ReadHelper + predicate. This is used, so the predicate can be + specified as an arbitrary callable object (even a + boost::function or a Boost.Lambda expression) without + imposing any inheritance relationship on the predicate + */ template struct Dispatcher : public ReadHelper::InternalPredicate diff --git a/Scheduler/Scheduler.cc b/Scheduler/Scheduler.cc index fbc917d..f7509bc 100644 --- a/Scheduler/Scheduler.cc +++ b/Scheduler/Scheduler.cc @@ -21,6 +21,7 @@ // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /** \file + \brief Scheduler non-inline non-template implementation \idea Implement signal handling (See source for more discussion about this) @@ -28,7 +29,9 @@ \idea Multithreading support: To support multithreading, the static member Scheduler::instance() must return a thread-local value (that is Scheduler::instance() must allocate one Scheduler - instance per thread) + instance per thread). Another possibility would be to distribute + the async load unto several threads (one scheduler for multiple + threads) */ // Here a basic concept of how to add signal support to the scheduler: @@ -69,8 +72,6 @@ // with the scheduler must be blocked as soon as it is registered with // the scheduler. -// Definition of non-inline non-template functions - #include "Scheduler.hh" //#include "Scheduler.ih" @@ -211,10 +212,10 @@ prefix_ void senf::Scheduler::process() if (spec.cb_hup) spec.cb_hup(EV_HUP); else if (ev.events & EPOLLERR) { - /// \fixme This is stupid, if cb_write and cb_read are - /// the same. The same below. We really have to - /// exactly define sane semantics of what to do on - /// EPOLLHUP and EPOLLERR. + /** \fixme This is stupid, if cb_write and cb_read are + the same. The same below. We really have to + exactly define sane semantics of what to do on + EPOLLHUP and EPOLLERR. */ if (spec.cb_write) spec.cb_write(EV_HUP); if (spec.cb_read) spec.cb_read(EV_HUP); } diff --git a/Scheduler/Scheduler.cci b/Scheduler/Scheduler.cci index f752d3e..028b372 100644 --- a/Scheduler/Scheduler.cci +++ b/Scheduler/Scheduler.cci @@ -20,7 +20,9 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// Definition of inline non-template functions +/** \file + \brief Scheduler inline non-template implementation + */ //#include "Scheduler.ih" diff --git a/Scheduler/Scheduler.cti b/Scheduler/Scheduler.cti index 62bfe38..ec12226 100644 --- a/Scheduler/Scheduler.cti +++ b/Scheduler/Scheduler.cti @@ -20,7 +20,9 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// Definition of inline template functions +/** \file + \brief Scheduler inline template implementation + */ //#include "Scheduler.ih" diff --git a/Scheduler/Scheduler.hh b/Scheduler/Scheduler.hh index 819d70e..a8dc456 100644 --- a/Scheduler/Scheduler.hh +++ b/Scheduler/Scheduler.hh @@ -20,8 +20,8 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -/** \mainpage The SENF Scheduler library - +/** \file + \brief Scheduler public header */ #ifndef HH_Scheduler_ @@ -44,14 +44,28 @@ namespace senf { /** \brief Singleton class to manage the event loop - This class manages a single select() type event loop. A - customer of this class may register any number of file - descriptiors with this class and pass callback functions to be - called on input, output or error. This functions are specified - using boost::function objects - - \todo Fix EventId parameter (probably to int) to allow |-ing - without casting ... + This class manages a single select() type event loop. A customer of this class may register + any number of file descriptiors with this class and pass callback functions to be called on + input, output or error. This functions are specified using boost::function objects (See Boost.Function) + + The Scheduler is based on a generic handle representation. The only information needed from + a handle, is the intrinsic file descriptor. Any object for which the statement + \code + int fd = retrieve_filehandle(object); + \endcode + is valid and places the relevent file descriptor into fd can be used as a Handle type. There + is an implementation of retrieve_filehandle(int) within the library to handle explicit file + descrptors. The Socket library provides an + implementation of retrive_filehandle(FileHandle handle). If you want to support + some other handle type, just define an apropriate \c retrieve_filehandle function in + that types namespace. + + It is important to note, that for every combination of file descriptor and event, only a \e + single handler may be installed. Installing more handlers does not make sense. If you need + to distribute data to serveral interested parties, you must take care of this yourself. + + \todo Fix EventId parameter (probably to int) to allow |-ing without casting ... */ class Scheduler : boost::noncopyable @@ -60,16 +74,24 @@ namespace senf { /////////////////////////////////////////////////////////////////////////// // Types + /// \brief Types of file descriptor events */ enum EventId { EV_NONE=0, EV_READ=1, EV_PRIO=2, EV_WRITE=4, EV_HUP=8, EV_ERR=16, EV_ALL=31 }; + /** \brief Template typedef for Callback type + + This is a template typedef (which does not exist in C++) that is, a template class whose + sole member is a typedef symbol defining the callback type given the handle type. + + The Callback is any callable object taking a \c Handle and an \c EventId as argument. + */ template struct GenericCallback { typedef boost::function::param_type, EventId) > Callback; }; - typedef boost::function SimpleCallback; + /** \brief Callback type for timer events */ typedef boost::function TimerCallback; /////////////////////////////////////////////////////////////////////////// @@ -82,6 +104,17 @@ namespace senf { // default destructor // no conversion constructors + /** \brief Return Scheduler instance + + This static member is used to access the singleton instance. This member is save to + return a correctly initialized Scheduler instance even if called at global construction + time + + \implementation This static member just defines the Scheduler as a static method + variable. The C++ standard then provides above guaratee. The instance will be + initialized the first time, the code flow passes the variable declaration found in + the instance() body. + */ static Scheduler & instance(); ///@} @@ -90,23 +123,58 @@ namespace senf { template void add(Handle const & handle, typename GenericCallback::Callback const & cb, - int eventMask = EV_ALL); + int eventMask = EV_ALL); ///< Add file handle event callback + /**< add() will add a callback to the Scheduler. The + callbeck will be called for the given type of event on + the given arbitrary file-descriptor or + handle-like object. If there already is a Callback + register ed for one of the events requested, the new + handler will replace the old one. + \param[in] handle file descriptor or handle providing + the Handle interface defined above. + \param[in] cb callback + \param[in] eventMask arbitrary combination via '|' + operator of EventId designators. */ template - void remove(Handle const & handle, int eventMask = EV_ALL); - - void timeout(unsigned long timeout, TimerCallback const & cb); - - void process(); - void terminate(); + void remove(Handle const & handle, int eventMask = EV_ALL); ///< Remove event callback + /**< remove() will remove any callback registered for any of + the given events on the given file descriptor or handle + like object. + \param[in] handle file descriptor or handle providing + the Handle interface defined above. + \param[in] eventMask arbitrary combination via '|' + operator of EventId designators. */ + + void timeout(unsigned long timeout, TimerCallback const & cb); ///< Add timeout event + /**< \param[in] timeout timeout in milliseconds + \param[in] cb callback to call after \a timeout + milliseconds + \todo Return some kind of handle/pointer and add + support to update or revoke a timeout */ + + void process(); ///< Event handler main loop + /**< This member must be called at some time to enter the + event handler main loop. Only while this function is + running any events are handled. The call will return + only, if any callback calls terminate(). */ + void terminate(); ///< Called by callbacks to terminate the main loop + /**< This member may be called by any callback to tell the + main loop to terminate. The main loop will return to + it's caller after the currently running callback + returns. */ protected: private: + typedef boost::function SimpleCallback; + Scheduler(); void do_add(int fd, SimpleCallback const & cb, int eventMask = EV_ALL); void do_remove(int fd, int eventMask = EV_ALL); + /** \brief Descriptor event specification + \internal */ struct EventSpec { SimpleCallback cb_read; @@ -118,6 +186,8 @@ namespace senf { int epollMask() const; }; + /** \brief Timer event specification + \internal */ struct TimerSpec { TimerSpec() : timeout(), cb() {} @@ -140,13 +210,20 @@ namespace senf { bool terminate_; }; + /** \brief Default file descriptor accessor + + retrieve_filehandle() provides the Scheduler with support for explicit file descriptors as + file handle argument. + + \relates Scheduler + */ int retrieve_filehandle(int fd); } ///////////////////////////////hh.e//////////////////////////////////////// #include "Scheduler.cci" -#include "Scheduler.ct" +//#include "Scheduler.ct" #include "Scheduler.cti" #endif @@ -154,4 +231,5 @@ namespace senf { // Local Variables: // mode: c++ // c-file-style: "senf" +// fill-column: 100 // End: diff --git a/Scheduler/WriteHelper.ct b/Scheduler/WriteHelper.ct index 04a4f1c..28bf5b6 100644 --- a/Scheduler/WriteHelper.ct +++ b/Scheduler/WriteHelper.ct @@ -1,8 +1,25 @@ // $Id$ // +// Copyright (C) 2006 Stefan Bund +// +// 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. // Copyright (C) 2006 -// Definition of non-inline template functions +/** \file + \brief WriteHelper non-inline template implementation */ //#include "WriteHelper.ih" @@ -58,6 +75,7 @@ template prefix_ void senf::WriteHelper::process(Handle handle, senf::Scheduler::EventId event) { + /** \fixme Move the done() calls to outside the try/catch block */ try { if (event != senf::Scheduler::EV_WRITE) throw senf::SystemException(EPIPE); diff --git a/Scheduler/WriteHelper.cti b/Scheduler/WriteHelper.cti index 14437e9..4ab3a89 100644 --- a/Scheduler/WriteHelper.cti +++ b/Scheduler/WriteHelper.cti @@ -1,8 +1,25 @@ // $Id$ // +// Copyright (C) 2006 Stefan Bund +// +// 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. // Copyright (C) 2006 -// Definition of inline template functions +/** \file + \brief WriteHelper inline template implementation */ //#include "WriteHelper.ih" diff --git a/Scheduler/WriteHelper.hh b/Scheduler/WriteHelper.hh index f22cbfe..d2c68c1 100644 --- a/Scheduler/WriteHelper.hh +++ b/Scheduler/WriteHelper.hh @@ -1,7 +1,26 @@ // $Id$ // +// Copyright (C) 2006 Stefan Bund +// +// 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. // Copyright (C) 2006 +/** \file + \brief WriteHelper public header */ + #ifndef HH_WriteHelper_ #define HH_WriteHelper_ 1 @@ -17,7 +36,20 @@ namespace senf { + /** \brief Asyncronous writing helper + + This class provides a simple asyncronous writing facility. This helper will register with + the Scheduler to write the requested data. It will stay registered until the data has benen + completely sent or some error condition is encountered. As soon as the WriteHelper is done, + the callback will be called. + + The WriteHelper accepts the same flexible file handle interfaces as the Scheduler. + + The callback must take a WriteHelper::ptr argument. Using this WriteHelper instance, the + callback can access the state information and check the termination status. + \todo Add additional interface to better access the intermediate status (data sent so far) + */ template class WriteHelper : public senf::intrusive_refcount @@ -26,27 +58,39 @@ namespace senf { /////////////////////////////////////////////////////////////////////////// // Types - typedef boost::intrusive_ptr ptr; - typedef boost::function Callback; + typedef boost::intrusive_ptr ptr; ///< Smart pointer type for this class + typedef boost::function Callback; ///< Callback type /////////////////////////////////////////////////////////////////////////// ///\name Structors and default members ///@{ static ptr dispatch(Handle handle, std::string data, Callback callback); + ///< Register new WriteHelper instance + /**< The registered callback will be called after all \a + data has been sent or when some error condition is + encountered. + \param[in] handle file descriptor or handle providing + the Handle interface defined above. + \param[in] data data to send + \param[in] cb callback + \returns smart pointer to new WriteHelper instance */ ///@} /////////////////////////////////////////////////////////////////////////// Handle handle() const; - std::string const & data() const; + std::string const & data() const; ///< Return the data + /**< After all data has been sent, this member will return + an empty string. Until then, the complete string will + be returned. */ - bool complete() const; - bool error() const; - void throw_error() const; + bool complete() const; ///< Check wether the write has completed successfully + bool error() const; ///< Check for error condition + void throw_error() const; ///< If an error occured, throw it - void revoke(); + void revoke(); ///< Remove the WriteHelper from the scheduler protected: @@ -78,4 +122,6 @@ namespace senf { // Local Variables: // mode: c++ +// c-file-style: "senf" +// fill-column: 100 // End: diff --git a/Utils/DaemonTools.cc b/Utils/DaemonTools.cc index 44165b6..62bba29 100644 --- a/Utils/DaemonTools.cc +++ b/Utils/DaemonTools.cc @@ -1,8 +1,24 @@ // $Id$ // -// Copyright (C) 2006 +// Copyright (C) 2006 Stefan Bund +// +// 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. -// Definition of non-inline non-template functions +/** \file + \brief DaemonTools non-inline non-template implementation */ #include "DaemonTools.hh" //#include "DaemonTools.ih" diff --git a/Utils/DaemonTools.hh b/Utils/DaemonTools.hh index 7fab851..5f20682 100644 --- a/Utils/DaemonTools.hh +++ b/Utils/DaemonTools.hh @@ -1,6 +1,46 @@ // $Id$ // -// Copyright (C) 2006 +// Copyright (C) 2006 Stefan Bund +// +// 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 DaemonTools public header */ + +/** \defgroup process Process Management + + This collection of utilities provides help in managing daemon processes. + + \idea Add communication between parent and child process to daemonize() and add things like + init_done(), failure() etc which allow the daemon process to tell the frontend of successful + startup or failure. This proabably means moving all the methods into a DaemonTools class (as + statics or via a singleton). This would also allow for automatic pid file creation and + removal (remove in global destructor). + + \idea Add a DaemonProcess baseclass whith init() and main() abstract members which wraps the + startup process. DaeminProcess::run() would fork, call init(), create a pid file and then + call main(). Exceptions during init()'s execution would be passed to the parent + process. This is based on the above API. + + \idea A closeall()/closemost() function which is useful when starting child processes. We'll use + getrlimit to now the biggest filehandle and close all of em. closemost() takes a number of + file handles as arg and will keep those open. + + \idea We might want to add other oft used utitlities: chroot(), setreuid(), pipes() / IPC ... + */ #ifndef HH_DaemonTools_ #define HH_DaemonTools_ 1 @@ -13,10 +53,18 @@ namespace senf { + /// \addtogroup process + /// @{ - void daemonize(); - void redirect_stdio(std::string const & path = "/dev/null"); + void daemonize(); ///< Make the current process a daemon process + /**< daemonize() will fork, detach from the controlling + terminal and start a new process group. */ + void redirect_stdio(std::string const & path = "/dev/null"); ///< Redirect STDIN, STDOUT and STDERR + /**< All standard file-descriptors will be redirected to the + given path defaulting to /dev/null + \param[in] path path to redirect to */ + /// @} } ///////////////////////////////hh.e//////////////////////////////////////// @@ -29,4 +77,6 @@ namespace senf { // Local Variables: // mode: c++ +// c-file-style: "senf" +// fill-column: 100 // End: diff --git a/Utils/Doxyfile b/Utils/Doxyfile index b193e90..50cf427 100644 --- a/Utils/Doxyfile +++ b/Utils/Doxyfile @@ -2,3 +2,4 @@ PROJECT_NAME = libUtils GENERATE_TAGFILE = doc/Utils.tag +ALPHABETICAL_INDEX = NO diff --git a/Utils/Exception.cc b/Utils/Exception.cc index 2798216..cc87f61 100644 --- a/Utils/Exception.cc +++ b/Utils/Exception.cc @@ -20,7 +20,8 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// Definition of non-inline non-template functions +/** \file + \brief Exception non-inline non-template implementation */ #include "Exception.hh" //#include "Exception.ih" @@ -34,6 +35,9 @@ prefix_ void senf::SystemException::init() { + // We normallyl don't want to consume memory in an exception, + // however all other solutions to format the message are terribly + // ugly (since thay must use a static and shared buffer ...) std::stringstream s; if (where) s << where << ": "; diff --git a/Scheduler/Scheduler.ct b/Utils/Exception.cci similarity index 59% rename from Scheduler/Scheduler.ct rename to Utils/Exception.cci index 1df75e3..565ed2e 100644 --- a/Scheduler/Scheduler.ct +++ b/Utils/Exception.cci @@ -1,9 +1,6 @@ // $Id$ // -// Copyright (C) 2006 -// Fraunhofer Institut fuer offene Kommunikationssysteme (FOKUS) -// Kompetenzzentrum fuer Satelitenkommunikation (SatCom) -// Stefan Bund +// Copyright (C) 2006 Stefan Bund // // 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 @@ -20,20 +17,34 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// Definition of non-inline template functions - -//#include "Scheduler.ih" +/** \file + \brief Exception inline non-template implementation */ // Custom includes -#define prefix_ -///////////////////////////////ct.p//////////////////////////////////////// +#define prefix_ inline +///////////////////////////////cci.p/////////////////////////////////////// + +prefix_ senf::SystemException::SystemException(int err_) + : where(0), err(err_) +{ + init(); +} + +prefix_ senf::SystemException::SystemException(char const * where_, int err_) + : where(where_), err(err_) +{ + init(); +} + +prefix_ senf::SystemException::~SystemException() + throw() +{} -///////////////////////////////ct.e//////////////////////////////////////// +///////////////////////////////cci.e/////////////////////////////////////// #undef prefix_ // Local Variables: // mode: c++ -// c-file-style: "senf" // End: diff --git a/Utils/Exception.hh b/Utils/Exception.hh index 7d5dbf1..9efa133 100644 --- a/Utils/Exception.hh +++ b/Utils/Exception.hh @@ -20,6 +20,9 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/** \file + \brief Exception public header */ + #ifndef HH_Exception_ #define HH_Exception_ 1 @@ -32,18 +35,37 @@ namespace senf { + /** \brief Exception handling standard UNIX errors (errno) + + This exception is thrown to signal generic errno failuers. + + \todo make where and err accessors and make the member vars private - struct SystemException : public std::exception + \idea Add a template class derived from SystemException which + takes the error number as a numeric argument. This allows + catching specific errno conditions: ErrnoException etc. + + \idea Add a generic error thrower which takes the origin + string and errno value as an argument and will throw a + corresponding template class instance. This would just be a + big switch statement containing all possible errno values, + probably created using some makro metaprogramming. + */ + class SystemException : public std::exception { - explicit SystemException(int err_) : where(0), err(err_) { init(); } - SystemException(char const * where_, int err_) : where(where_), err(err_) { init(); } + public: + explicit SystemException(int err); ///< SystemException without error lokus info + /**< \param[in] err errror number (the errno value) */ + SystemException(char const * where, int err); ///< SystemException with error lokus info + /**< \param[in] where description of error origin + \param[in] err error number (the errno value) */ - virtual char const * what() const throw(); + virtual char const * what() const throw(); ///< Return verbose error description - char const * where; - int err; + char const * where; ///< Error origin + int err; ///< Error number - virtual ~SystemException() throw() {} + virtual ~SystemException() throw(); private: void init(); std::string buffer_; @@ -52,7 +74,7 @@ namespace senf { } ///////////////////////////////hh.e//////////////////////////////////////// -//#include "Exception.cci" +#include "Exception.cci" //#include "Exception.ct" //#include "Exception.cti" #endif diff --git a/Utils/Mainpage.dox b/Utils/Mainpage.dox new file mode 100644 index 0000000..de801f3 --- /dev/null +++ b/Utils/Mainpage.dox @@ -0,0 +1,43 @@ +namespace senf { + +/** \mainpage The SENF Utilities Library + + The Utilities Library is a collection of independent + utilities. We have +
+ +
SystemException
standard exception for system errors + (errno)
+ +
\ref time
Very rudimentary microsecond time + support
+ +
\ref process
Some simple process management and daemon + helpers + +
\ref membind
a simple Boost.Bind + extension
+ +
intrusive_refcount
mixin to simplify writing classes for + use with boost::intrusive_ptr
+ +
SafeBool
a mixin class to provide a really safe + replacement for operator bool
+ +
prettyName()
an interface to the C++ demangler of g++ + to get formated type names from typeinfo objects
+ +
+ */ + +} + + +// Local Variables: +// mode: c++ +// mode: flyspell +// mode: auto-fill +// ispell-local-dictionary: "american" +// End: diff --git a/Utils/MicroTime.cc b/Utils/MicroTime.cc index 5006c50..23d2a99 100644 --- a/Utils/MicroTime.cc +++ b/Utils/MicroTime.cc @@ -1,8 +1,24 @@ // $Id$ // -// Copyright (C) 2006 +// Copyright (C) 2006 Stefan Bund +// +// 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. -// Definition of non-inline non-template functions +/** \file + \brief MicroTime non-inline non-template implementation */ #include "MicroTime.hh" //#include "MicroTime.ih" diff --git a/Utils/MicroTime.hh b/Utils/MicroTime.hh index d4495e1..0d04900 100644 --- a/Utils/MicroTime.hh +++ b/Utils/MicroTime.hh @@ -1,6 +1,39 @@ // $Id$ // -// Copyright (C) 2006 +// Copyright (C) 2006 Stefan Bund +// +// 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 MicroTime public header */ + +/** \defgroup time Microseconds Time + + MicroTime provides extremely rudimentary support for a time data + type precise to 10-6 seconds. Up to now, the only + supported members are the datatype (which just uses a 64 bit + integer) and the now() function to get the current UTC time in + microsecods since the Epoch. + + \idea This thing only exists as a quick hack. We can probably make + use of Boost.Time or some such thing so it probably does not + make sense to extend this further. We should however check the + performance of Boost.Time since this is used heavily in the + Scheduler. + */ #ifndef HH_MicroTime_ #define HH_MicroTime_ 1 @@ -12,11 +45,15 @@ ///////////////////////////////hh.p//////////////////////////////////////// namespace senf { + + /// \addtogroup time + /// @{ + typedef boost::uint64_t MicroTime; ///< Microsecond accurent time datatype - typedef boost::uint64_t MicroTime; + MicroTime now(); ///< Get current UTC time with microsecond accuracy - MicroTime now(); + /// @} } diff --git a/Utils/SafeBool.hh b/Utils/SafeBool.hh index 43e317b..68b0ffc 100644 --- a/Utils/SafeBool.hh +++ b/Utils/SafeBool.hh @@ -12,44 +12,73 @@ namespace senf { - - // This is a direct copy of a safe bool solution by Bjorn Karlsson - // from http://www.artima.com/cppsource/safebool.html - // - // Usage: - // class TestableWithVirtual - // : public safe_bool<> - // { - // protected: - // bool boolean_test() const - // { - // // Perform Boolean logic here - // } - // }; - // - // class TestableWithoutVirtual - // : public safe_bool - // { - // public: - // bool boolean_test() const - // { - // // Perform Boolean logic here - // } - // }; - + /** \brief internal SafeBool base class + \internal + */ class SafeBoolBase { protected: typedef void (SafeBoolBase::*bool_type)() const; void this_type_does_not_support_comparisons() const; + // Just here to make them protected ... + SafeBoolBase(); SafeBoolBase(const SafeBoolBase&); SafeBoolBase& operator=(const SafeBoolBase&); ~SafeBoolBase(); }; - template + /** \brief Mixin class for safe boolean conversion support + + This is a direct yet simplified copy of a safe bool solution + by Bjorn Karlsson from + http://www.artima.com/cppsource/safebool.html + + This mixin provides the client class with safe boolean + testing. It is a safe replacement for operator + bool. operator bool is problematic since \c bool + is an integer type. This conversion operator makes the class + usable in any numeric context, which can be quite + dangerous. The operator void * solution is much + better in this respect but still allows two instances of any + class having such a void * conversion to be compared + for equality. This again will produce absolutely unexpected + results since it will not check wethere the objects are + identical, it will only check, that both return the same + boolean state. + + This solutions solves all these problems by returning a + pointer-to-member which cannot be converted to any other + type. By providing explicit implementations of \c operator== + and \c operator!= which fail in an obvious way at compile + time, this hazard is removed. + + To make a class boolean testable, just inherit from the mixin + and implement \c boolean_test: + + \code + class Testable + : public SafeBool + { + public: + bool boolean_test() const + { + // Perform Boolean logic here + } + }; + + Testable t = ...; + + if (t) { + ... + } + \endcode + + \todo Either rename intrusive_refcount to IntrusiveRefcount or + SafeBool to safe_bool (I tend to the latter ...) + */ + template class SafeBool : public SafeBoolBase { diff --git a/Utils/TypeInfo.cc b/Utils/TypeInfo.cc index 9f0bd6a..296f22b 100644 --- a/Utils/TypeInfo.cc +++ b/Utils/TypeInfo.cc @@ -20,7 +20,8 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// Definition of non-inline non-template functions +/** \file + \brief TypeInfo non-inline non-template implementation */ #include "TypeInfo.hh" //#include "TypeInfo.ih" diff --git a/Utils/TypeInfo.hh b/Utils/TypeInfo.hh index 98c3b38..8f8ca97 100644 --- a/Utils/TypeInfo.hh +++ b/Utils/TypeInfo.hh @@ -20,6 +20,9 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/** \file + \brief TypeInfo public header */ + #ifndef HH_TypeInfo_ #define HH_TypeInfo_ 1 @@ -32,7 +35,21 @@ namespace senf { + /** \brief Try to return readable type for given type_info + + This function will try to return a demangled type name for the + given type_info object. If the demangling fails, the possibly + mangled name (type->name()) will be returned. + + This function depends on the liberty library provided by the + linux binutils or binutils-dev packages. It also depends on an + internal header file. If the API should change, this header + file (which resides in impl/demangle.h) must be updated from + the binutils sources. + \param[in] type type_info object + \returns type name, possibly demangled + */ std::string prettyName(std::type_info const & type); } diff --git a/Utils/intrusive_refcount.cci b/Utils/intrusive_refcount.cci index 3318a4d..39f8776 100644 --- a/Utils/intrusive_refcount.cci +++ b/Utils/intrusive_refcount.cci @@ -20,7 +20,8 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -// Definition of inline non-template functions +/** \file + \brief intrusive_refcount inline non-template implementation */ //#include "intrusive_refcount.ih" diff --git a/Utils/intrusive_refcount.hh b/Utils/intrusive_refcount.hh index 754686d..36a7564 100644 --- a/Utils/intrusive_refcount.hh +++ b/Utils/intrusive_refcount.hh @@ -20,6 +20,9 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/** \file + \brief intrusive_refcount public header */ + #ifndef HH_intrusive_refcount_ #define HH_intrusive_refcount_ 1 @@ -31,19 +34,28 @@ namespace senf { + /** \brief Reference count mixin for intrusive_ptr + + This class provides a simple internally managed refcount and supplies the boost::intrusive_ptr + required interface. To make a class compatible with \c boost::intrusive_ptr, just derive + publicly from intrusive_refcount. - /** \brief + Two additional benifits of using intrusive_refcount are + \li The object can access it's own refcount + \li It is valid and safe to convert a plain object pointer to an intrusive_ptr at any time + (not only after new) */ class intrusive_refcount : public boost::noncopyable { public: - typedef unsigned refcount_t; + typedef unsigned refcount_t; ///< reference count type virtual ~intrusive_refcount(); - refcount_t refcount(); - bool is_shared(); + refcount_t refcount(); ///< current refcount + bool is_shared(); ///< return \c true if refcount() > 1 protected: intrusive_refcount(); @@ -72,4 +84,5 @@ namespace senf { // Local Variables: // mode: c++ // c-file-style: "senf" +// fill-column: 100 // End: diff --git a/Utils/membind.hh b/Utils/membind.hh index bc9b30d..b876890 100644 --- a/Utils/membind.hh +++ b/Utils/membind.hh @@ -20,6 +20,32 @@ // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/** \file + \brief membind public header */ + +/** \defgroup membind Bound Member Functions + + The membind() family of function templates simplifies the creation + of simple bound member function pointers: + + \code + struct Foo { + int test(int x); + }; + + Foo * foo = ...; + boost::function f = senf::membind(&Foo::test,foo); + int rv = f(1); // Calls foo->test(1) + \endcode + + \idea Make the \a ob argument type an additional P template + parameter (using call_traits for the exact arg type? Probably + we'll get deduction problems then) . The only operation this + object musst suppoprt is ob->*fn. This would allow the use of + smart pointers. We should keep the T & version to still support + ob.*fn use. + */ + #ifndef HH_membind_ #define HH_membind_ 1 @@ -31,7 +57,6 @@ namespace senf { - #define scOBTYPE T * #include "Utils/impl/membind.hh" #undef scOBTYPE @@ -40,6 +65,28 @@ namespace senf { #include "Utils/impl/membind.hh" #undef scOBTYPE +#ifdef DOXYGEN + + /// \addtogroup membind + /// @{ + + /** \brief Build bound member function object + + membind() supports up to 9 function parameters (represented as + \a Args here). The \a ob argument can be either a pointer or a + reference to \a T + \param[in] fn member function pointer + \param[in] ob object instance to bind this pointer to + \returns Boost.Function object representing a bound call of \a + fn on \a ob + */ + template + boost::function membind(R (T::* fn)( Args ), T * ob); + + /// @} + +#endif + } ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/doclib/filter.pl b/doclib/filter.pl index 0fd65d3..eb9214d 100755 --- a/doclib/filter.pl +++ b/doclib/filter.pl @@ -1,8 +1,11 @@ #!/usr/bin/perl -n -if (/^\s*\\code\s*$/ .. /\\endcode/) { - $i=length((/^(\s*)/)[0]) if /^\s*\\code\s*$/; - print substr($_,$i); +s/\s*$//; +while (s/\t/' 'x(8-length($`)%8)/e) {} + +if (/^\s*\\code$/ .. /\\endcode/ && !/^$/) { + $i=length($1) if /^(\s*)\\code$/; + print substr($_,$i),"\n"; } else { - print; + print $_,"\n"; } diff --git a/doclib/senf.css b/doclib/senf.css index 740d06b..270f997 100644 --- a/doclib/senf.css +++ b/doclib/senf.css @@ -188,7 +188,7 @@ dl.bug, dl.fixme, dl.todo, dl.idea { dl.xref-bug, dl.xref-fix, dl.xref-todo, dl.xref-idea { border: 1px solid #CC8888; padding: 2px 3px; - margin: 4px 0; + margin: 4px 8px 4px 2px; background-color: #FFEEEE; color: #666666; font-size: 9px;