Import old svn repository.
This commit is contained in:
		
							parent
							
								
									f81b32b830
								
							
						
					
					
						commit
						432db441a8
					
				| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
/* lw-support/src/docs/Exceptions.txt
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*! \page exceptions Exception Hierarchy
 | 
			
		||||
 | 
			
		||||
LW Support provides a comprehensive exception subsystem. There are two
 | 
			
		||||
main categories of exception: errors (these are derived from the
 | 
			
		||||
lw::Exception class, the most important of which is probably lw::SystemError)
 | 
			
		||||
and program exceptions (which are derived from the lw::ProgramException
 | 
			
		||||
class).
 | 
			
		||||
 | 
			
		||||
Errors will likely require user intervention, or at least some form of
 | 
			
		||||
error handling in higher level code. These exceptions are therefore
 | 
			
		||||
quite comprehensive, containing a human-readable message and also copies
 | 
			
		||||
of data that might be required to solve the error.
 | 
			
		||||
 | 
			
		||||
Program exceptions are exceptional conditions which arise from time to
 | 
			
		||||
time but are seen as normal functioning of the program. Since these do
 | 
			
		||||
not denote errors, they are not intended to be propagated back to users
 | 
			
		||||
or higher level code, and therefore do not contain the same
 | 
			
		||||
comprehensive facilities as error classes.
 | 
			
		||||
 | 
			
		||||
All LW Support exceptions are derived from the \a std::exception class,
 | 
			
		||||
which means they all support the \a what() member function, which
 | 
			
		||||
returns a human-readable string describing the error message. Since LW
 | 
			
		||||
Support uses UCS-4 strings, the message is converted into UTF-8 first.
 | 
			
		||||
The Exception base class also provides functions to explicitly get a
 | 
			
		||||
UTF-8 or UCS-4 string: lw::Exception::toUtf8() and lw::Exception::toString().
 | 
			
		||||
 | 
			
		||||
Error classes, which are all derived from lw::Exception (and may possibly be
 | 
			
		||||
part of a larger hierarchy themselves), also expose additional
 | 
			
		||||
functionality -- you can often request additional information about the
 | 
			
		||||
error.
 | 
			
		||||
 | 
			
		||||
Program exceptions don't have a nice error message (they just use a
 | 
			
		||||
stock one telling you that you shouldn't see the error) and don't
 | 
			
		||||
generally have any further information.
 | 
			
		||||
 | 
			
		||||
A useful feature is building up a chain of code through which the error
 | 
			
		||||
has propagated. This can be done with the lw::Exception::chain() member.
 | 
			
		||||
This feature should allow a user to narrow down the root cause of an
 | 
			
		||||
error. It is mainly of use when an exception has to propagate through
 | 
			
		||||
several layers of indirection and cannot be dealt with automatically.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -1,15 +1,22 @@
 | 
			
		|||
/* lw-support/src/docs/MainPage.dox
 | 
			
		||||
/* lw-support/src/docs/MainPage.txt
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2006, Laurence Withers, <l@lwithers.me.uk>.
 | 
			
		||||
 *  Released under the GNU GPLv2. See file COPYING or
 | 
			
		||||
 *  http://www.gnu.org/copyleft/gpl.html for details.
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*! \mainpage
 | 
			
		||||
/*! \mainpage LW Support
 | 
			
		||||
 | 
			
		||||
\section intro Introduction
 | 
			
		||||
 | 
			
		||||
LW Support is a C++ library intended to provide additional functionality
 | 
			
		||||
to C++. For the most part, it simply exposes C library functionality in
 | 
			
		||||
an object-oriented manner. Some general pages about the various concepts
 | 
			
		||||
LW Support provides are listed here, but the majority of the
 | 
			
		||||
documentation is to be found in the class listings.
 | 
			
		||||
 | 
			
		||||
\section concepts Concepts
 | 
			
		||||
 | 
			
		||||
\li \ref exceptions LW Support's exception hierarchy.
 | 
			
		||||
\li \ref timedate Time and Date classes.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
vim: expandtab:ts=4:sw=4
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
/* lw-support/src/docs/TimeDate.txt
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*! \page timedate Time and Date Classes
 | 
			
		||||
 | 
			
		||||
LW Support has extensive support for time and date representation and
 | 
			
		||||
manipulation. It uses <a href="http://en.wikipedia.org/wiki/ISO_8601">
 | 
			
		||||
ISO8601</a> to portably convert to/from a human-readable string
 | 
			
		||||
representation.
 | 
			
		||||
 | 
			
		||||
The time support classes are split into two: the elapsed time classes
 | 
			
		||||
(which are used to represent a period of time), and the day time classes
 | 
			
		||||
(which are used to represent specific points in time).
 | 
			
		||||
 | 
			
		||||
\section elapsed Elapsed Time
 | 
			
		||||
 | 
			
		||||
The class used to represent an elapsed time period is lw::ElapsedTime.
 | 
			
		||||
It uses a nanosecond resolution, and can only represent positive time
 | 
			
		||||
periods (since negative time periods don't make sense). It is
 | 
			
		||||
supplemented by the stopwatch class, lw::StopWatch.
 | 
			
		||||
 | 
			
		||||
\section calendar Calendar Time
 | 
			
		||||
 | 
			
		||||
LW Support allows you to represent and manipulate calendar times (i.e.
 | 
			
		||||
times in the 24-hour clock and/or Gregorian dates). The 24-hour clock
 | 
			
		||||
is represented by lw::DayTime, with Gregorian dates stored in
 | 
			
		||||
lw::Date. These two classes are combined in the convenience class
 | 
			
		||||
lw::DateTime.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
src/docs/MainPage.txt
 | 
			
		||||
src/docs/Exceptions.txt
 | 
			
		||||
src/docs/TimeDate.txt
 | 
			
		||||
| 
						 | 
				
			
			@ -1,13 +1,18 @@
 | 
			
		|||
/* lw-support/src/liblw-support/BottomHeader.h
 | 
			
		||||
/* lw-support/src/lib/BottomHeader.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2006, Laurence Withers, <l@lwithers.me.uk>.
 | 
			
		||||
 *  Released under the GNU GPLv2. See file COPYING or
 | 
			
		||||
 *  http://www.gnu.org/copyleft/gpl.html for details.
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// deprecated classes go here
 | 
			
		||||
namespace lw {
 | 
			
		||||
#ifndef DOXYGEN
 | 
			
		||||
#define DEPRECATED(className) \
 | 
			
		||||
    typedef class className __attribute__((deprecated)) className
 | 
			
		||||
#undef DEPRECATED
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// end of include guard
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
vim: expandtab:ts=4:sw=4
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
/* lw-support/src/lib/Enums/IOMode.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
/*! \brief I/O open modes.
 | 
			
		||||
 | 
			
		||||
This enumeration represents what a device is opened for; reading,
 | 
			
		||||
writing, both or neither. It also has a member to report that the
 | 
			
		||||
device isn't open at all.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
enum IOMode {
 | 
			
		||||
    /// Device isn't open.
 | 
			
		||||
    IOClosed,
 | 
			
		||||
 | 
			
		||||
    /// Device was opened in non-read, non-write mode.
 | 
			
		||||
    IONone,
 | 
			
		||||
 | 
			
		||||
    /// Device is read only.
 | 
			
		||||
    IORead,
 | 
			
		||||
 | 
			
		||||
    /// Device is write only.
 | 
			
		||||
    IOWrite,
 | 
			
		||||
 | 
			
		||||
    /// Device is read/write.
 | 
			
		||||
    IOReadWrite
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
/* lw-support/src/lib/Enums/IOTimeoutMode.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
/*! \brief I/O timeout modes.
 | 
			
		||||
 | 
			
		||||
There are a few different ways for I/O to time out, depending on
 | 
			
		||||
how you want the program to behave. There can either be a "hard
 | 
			
		||||
timeout", which specifies the absolute maximum waiting time, or a "soft
 | 
			
		||||
timeout", which can be overruled by chunks of incoming data. Similarly,
 | 
			
		||||
it might be acceptable to return with only some data transferred, or it
 | 
			
		||||
might not.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
enum IOTimeoutMode {
 | 
			
		||||
    /*! \brief Hard timeout, partial transfer OK.
 | 
			
		||||
 | 
			
		||||
    This mode has a hard timeout, but partial data transfers are OK. In
 | 
			
		||||
    this mode, the read is guaranteed to return within the timeout with
 | 
			
		||||
    whatever data is available, but it doesn't guarantee to transfer the
 | 
			
		||||
    full amount of data. No data will be lost. An IOTimeout exception
 | 
			
		||||
    will only be thrown if no data is transferred at all during the
 | 
			
		||||
    specified timeout.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    IOTimeoutHardPartial,
 | 
			
		||||
 | 
			
		||||
    /*! \brief Hard timeout, full data only.
 | 
			
		||||
 | 
			
		||||
    This mode has a hard timeout during which all data must be
 | 
			
		||||
    transferred. If only partial data transfer occurs, an IOTimeout
 | 
			
		||||
    exception is thrown at the end of the timeout period.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    IOTimeoutHardFull,
 | 
			
		||||
 | 
			
		||||
    /*! \brief Soft timeout, partial transfer OK.
 | 
			
		||||
 | 
			
		||||
    This mode has a soft timeout, which is to say that the timeout
 | 
			
		||||
    period is restarted every time a partial transfer of data takes
 | 
			
		||||
    place. An IOTimeout exception will only be thrown if no data is
 | 
			
		||||
    transferred at all during the specified timeout.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    IOTimeoutSoftPartial,
 | 
			
		||||
 | 
			
		||||
    /*! \brief Soft timeout, full data only.
 | 
			
		||||
 | 
			
		||||
    This mode has a soft timeout, which is to say that the timeout
 | 
			
		||||
    period is restarted every time a partial transfer of data takes
 | 
			
		||||
    place. However, upon reaching the end of the timeout period, an
 | 
			
		||||
    exception will be thrown if all the data has not been transferred.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    IOTimeoutSoftFull,
 | 
			
		||||
 | 
			
		||||
    /*! \brief One-shot data transfer.
 | 
			
		||||
 | 
			
		||||
    This mode is used when you want to perform all available I/O and
 | 
			
		||||
    then return immediately (for instance, you might be listening for
 | 
			
		||||
    commands on a network connection, but not know the size of each
 | 
			
		||||
    command in advance).
 | 
			
		||||
 | 
			
		||||
    It will always return after it has completed as much data transfer
 | 
			
		||||
    as possible. It will only throw an IOTimeout exception if no data is
 | 
			
		||||
    transferred within the specified time limit.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    IOTimeoutOneShot
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
/* lw-support/src/lib/Enums/ISO8601Format.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Denotes the different formats accepted for ISO8601 dates.
 | 
			
		||||
enum ISO8601Format {
 | 
			
		||||
    ISO8601FormatDetect, ///< Used to autodetect format when reading.
 | 
			
		||||
    ISO8601FormatCalendar, ///< yyyymmdd
 | 
			
		||||
    ISO8601FormatOrdinal, ///< yyyyddd
 | 
			
		||||
    ISO8601FormatWeekDate ///< yyyyWwwd
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Allows you to specify precision when converting to ISO8601 format.
 | 
			
		||||
enum ISO8601Precision {
 | 
			
		||||
    ISO8601PrecisionFull, ///< Converts everything.
 | 
			
		||||
    ISO8601PrecisionYear, ///< Only converts year.
 | 
			
		||||
    ISO8601PrecisionWeek, ///< Converts year and week.
 | 
			
		||||
    ISO8601PrecisionMonth, ///< Converts year and month.
 | 
			
		||||
    ISO8601PrecisionDay, ///< Converts year, month and day.
 | 
			
		||||
    ISO8601PrecisionHour, ///< Converts date and hour.
 | 
			
		||||
    ISO8601PrecisionMinute, ///< Converts date, hour and minute.
 | 
			
		||||
    ISO8601PrecisionSecond ///< Converts date and time.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
/* lw-support/src/lib/Events/CallbackIO.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// I/O event callback object.
 | 
			
		||||
class EventCallbackIO {
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor. Does nothing.
 | 
			
		||||
    virtual ~EventCallbackIO()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /*! \brief I/O ready callback.
 | 
			
		||||
 | 
			
		||||
    \param flags A bitfield of events that have occurred.
 | 
			
		||||
 | 
			
		||||
    This function is called when the device it is associated with becomes ready for I/O. The details
 | 
			
		||||
    will be indicated through the \a flags argument, which is a bitfield of:
 | 
			
		||||
     - IOEventRead
 | 
			
		||||
     - IOEventWrite
 | 
			
		||||
     - IOEventUrgent
 | 
			
		||||
     - IOEventError
 | 
			
		||||
     - IOEventHUP
 | 
			
		||||
 | 
			
		||||
    Note that you cannot call any lw::EventManager functions within this callback, with the
 | 
			
		||||
    exception of modifyDevice().
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void ioReady(uint32_t flags) throw() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
/* lw-support/src/lib/Events/CallbackTimer.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Timer event callback object.
 | 
			
		||||
class EventCallbackTimer {
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor. Does nothing.
 | 
			
		||||
    virtual ~EventCallbackTimer()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Timer event callback.
 | 
			
		||||
 | 
			
		||||
    \param key The key of the timer event.
 | 
			
		||||
 | 
			
		||||
    This function is called whenever a timer event expires. It must not throw any exceptions. The
 | 
			
		||||
    value of \a key is the value returned when you initially registered the timer.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void timer(Key key) throw() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
/* lw-support/src/lib/Events/CallbackUpdate.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EventCallbackUpdate::~EventCallbackUpdate()
 | 
			
		||||
{
 | 
			
		||||
    for(uint i = 0, end = managers.size(); i < end; i++) managers[i]->ignoreUpdate(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
/* lw-support/src/lib/Events/CallbackUpdate.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Update event callback object.
 | 
			
		||||
 | 
			
		||||
Using lw::EventManager's update mechanism, objects can (in the event loop) register themselves as
 | 
			
		||||
requiring an update. After the event loop has completed, all objects needing an update will then be
 | 
			
		||||
processed. During this processing stage, it is safe for objects to delete themselves or other
 | 
			
		||||
objects (which may also be on the update list). The update function will be called at most once
 | 
			
		||||
per event loop, and only if the object has requested that it be added to the update list.
 | 
			
		||||
 | 
			
		||||
It is possible for an object to be registered with more than one lw::EventManager's update list at
 | 
			
		||||
a time; but it will only be called a maximum of once per call to lw::EventManager::processEvents(),
 | 
			
		||||
regardless of how many times it is registered. Deleting an object which is registered with an
 | 
			
		||||
lw::EventManager will cause the object to become unregisterd.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class EventCallbackUpdate {
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor. Unregisters the device from any update lists it may be in.
 | 
			
		||||
    virtual ~EventCallbackUpdate();
 | 
			
		||||
 | 
			
		||||
    /// Update callback. The object can safely delete itself in here.
 | 
			
		||||
    virtual void update(void) throw() = 0;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    friend class EventManager;
 | 
			
		||||
    std::vector<lw::EventManager*> managers;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,385 @@
 | 
			
		|||
/* lw-support/src/lib/Events/EventManager.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const uint32_t IOEventRead = EPOLLIN;
 | 
			
		||||
const uint32_t IOEventWrite = EPOLLOUT;
 | 
			
		||||
const uint32_t IOEventHUP = EPOLLHUP;
 | 
			
		||||
const uint32_t IOEventUrgent = EPOLLPRI;
 | 
			
		||||
const uint32_t IOEventError = EPOLLERR;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct EventManager::pimpl {
 | 
			
		||||
    pimpl()
 | 
			
		||||
        : epollFd(-1), epollEvents(0), numEvents(0), shutdownFlag(false), processing(false),
 | 
			
		||||
        last_ns(now_ns())
 | 
			
		||||
    {
 | 
			
		||||
        pipeFd[0] = -1;
 | 
			
		||||
        pipeFd[1] = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~pimpl()
 | 
			
		||||
    {
 | 
			
		||||
        delete [] epollEvents;
 | 
			
		||||
        close(epollFd);
 | 
			
		||||
        close(pipeFd[0]);
 | 
			
		||||
        close(pipeFd[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // epoll-related structures
 | 
			
		||||
    int epollFd;
 | 
			
		||||
    struct epoll_event* epollEvents;
 | 
			
		||||
    int numEvents;
 | 
			
		||||
 | 
			
		||||
    // pipe for signals/shutdown
 | 
			
		||||
    int pipeFd[2];
 | 
			
		||||
 | 
			
		||||
    // operational status
 | 
			
		||||
    bool shutdownFlag, processing;
 | 
			
		||||
    class ModifiedWhileProcessing : public lw::ProgramException { };
 | 
			
		||||
    void processingCheck(const wchar_t* chain)
 | 
			
		||||
    {
 | 
			
		||||
        if(processing) throw ModifiedWhileProcessing().chain(chain);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // timer events
 | 
			
		||||
    uint64_t last_ns;
 | 
			
		||||
    static uint64_t now_ns()
 | 
			
		||||
    {
 | 
			
		||||
        struct timeval tv;
 | 
			
		||||
        gettimeofday(&tv, 0);
 | 
			
		||||
        uint64_t ret = tv.tv_sec;
 | 
			
		||||
        ret *= tenExp9;
 | 
			
		||||
        ret += tv.tv_usec * tenExp3;
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct TimerEntry {
 | 
			
		||||
        EventCallbackTimer* timerCallback;
 | 
			
		||||
        ElapsedTime remainingPeriod, recurringPeriod;
 | 
			
		||||
        Key key;
 | 
			
		||||
 | 
			
		||||
        TimerEntry(EventCallbackTimer* timerCallback, ElapsedTime initialPeriod,
 | 
			
		||||
                   ElapsedTime recurringPeriod)
 | 
			
		||||
            : timerCallback(timerCallback), remainingPeriod(initialPeriod),
 | 
			
		||||
            recurringPeriod(recurringPeriod), key(Key::get())
 | 
			
		||||
        { }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct TimerCmp {
 | 
			
		||||
        bool operator()(const struct TimerEntry& a, const struct TimerEntry& b)
 | 
			
		||||
        {
 | 
			
		||||
            return a.remainingPeriod < b.remainingPeriod;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::vector<TimerEntry> timers; // note -- this is a sorted list!
 | 
			
		||||
 | 
			
		||||
    enum InternalEvent {
 | 
			
		||||
        InternalEventShutdown
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void dieHorribly()
 | 
			
		||||
    {
 | 
			
		||||
        processing = false;
 | 
			
		||||
        shutdownFlag = true;
 | 
			
		||||
        throw lw::ProgramException()
 | 
			
		||||
            .chain(L"EventManager::pimpl::dieHorribly()")
 | 
			
		||||
            .chain(L"EventManager::processEvents()");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void processInternalEvents()
 | 
			
		||||
    {
 | 
			
		||||
        InternalEvent ev;
 | 
			
		||||
 | 
			
		||||
        while(true) {
 | 
			
		||||
            ssize_t ret = read(pipeFd[pipeRead], (char*)&ev, sizeof(ev));
 | 
			
		||||
            if(ret == -1 && errno == EAGAIN) break;
 | 
			
		||||
            if(ret != sizeof(ev)) dieHorribly();
 | 
			
		||||
 | 
			
		||||
            switch(ev) {
 | 
			
		||||
            case InternalEventShutdown:
 | 
			
		||||
                shutdownFlag = true;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                dieHorribly();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // objects registered for updates (sorted list)
 | 
			
		||||
    std::vector<EventCallbackUpdate*> updateList;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EventManager::EventManager(int numDevicesHint, int numEvents)
 | 
			
		||||
    : p(new pimpl)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        if(numDevicesHint < 0) throw BadArgument(L"numDevicesHint", L">= 0", numDevicesHint);
 | 
			
		||||
        if(numEvents < 1) throw BadArgument(L"numEvents", L">= 1", numEvents);
 | 
			
		||||
 | 
			
		||||
        // create epoll structure
 | 
			
		||||
        p->numEvents = numEvents;
 | 
			
		||||
        p->epollEvents = new struct epoll_event[numEvents];
 | 
			
		||||
        p->epollFd = epoll_create(numDevicesHint + 1); // for the pipe
 | 
			
		||||
        if(p->epollFd < 0) throw SystemError().chain(L"epoll_create()");
 | 
			
		||||
 | 
			
		||||
        // set up pipes (non-blocking mode required)
 | 
			
		||||
        if(pipe(p->pipeFd) == -1) throw SystemError().chain(L"pipe()");
 | 
			
		||||
        int pipeFdFlags;
 | 
			
		||||
        if( (pipeFdFlags = fcntl(p->pipeFd[0], F_GETFL, 0)) == -1 ||
 | 
			
		||||
            fcntl(p->pipeFd[0], F_SETFL, pipeFdFlags | O_NONBLOCK) ||
 | 
			
		||||
            (pipeFdFlags = fcntl(p->pipeFd[1], F_GETFL, 0)) == -1 ||
 | 
			
		||||
            fcntl(p->pipeFd[1], F_SETFL, pipeFdFlags | O_NONBLOCK))
 | 
			
		||||
        {
 | 
			
		||||
            throw SystemError().chain(L"fcntl()");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        struct epoll_event ev;
 | 
			
		||||
        memset(&ev, 0, sizeof(ev));
 | 
			
		||||
        ev.events = EPOLLIN | EPOLLERR | EPOLLET;
 | 
			
		||||
        if(epoll_ctl(p->epollFd, EPOLL_CTL_ADD, p->pipeFd[pipeRead], &ev))
 | 
			
		||||
            throw SystemError().chain(L"epoll_ctl(EPOLL_CTL_ADD)");
 | 
			
		||||
    }
 | 
			
		||||
    catch(lw::Exception& e) {
 | 
			
		||||
        delete p;
 | 
			
		||||
        e.chain(L"EventManager::EventManager()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EventManager::~EventManager()
 | 
			
		||||
{
 | 
			
		||||
    delete p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void EventManager::processEvents(int userTimeout)
 | 
			
		||||
{
 | 
			
		||||
    if(p->shutdownFlag) throw Shutdown();
 | 
			
		||||
 | 
			
		||||
    // find the shortest required timeout
 | 
			
		||||
    int timerTimeout = p->timers.empty() ? -1 : (int)(p->timers[0].remainingPeriod.to_ms());
 | 
			
		||||
    int timeout = std::min(userTimeout, timerTimeout);
 | 
			
		||||
    if(timeout < 0) {
 | 
			
		||||
        if(userTimeout >= 0) timeout = userTimeout;
 | 
			
		||||
        else if(timerTimeout >= 0) timeout = timerTimeout;
 | 
			
		||||
        else timeout = -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // run the event loop
 | 
			
		||||
    int ret = epoll_wait(p->epollFd, p->epollEvents, p->numEvents, timeout);
 | 
			
		||||
 | 
			
		||||
    if(ret == -1) {
 | 
			
		||||
        if(errno == EINTR) return;
 | 
			
		||||
        throw SystemError().chain(L"epoll_wait()").chain(L"EventManager::processEvents()");
 | 
			
		||||
    }
 | 
			
		||||
    if(!ret && timerTimeout == -1) throw Timeout();
 | 
			
		||||
 | 
			
		||||
    // process events
 | 
			
		||||
    for(int i = 0; i < ret; ++i) {
 | 
			
		||||
        EventCallbackIO* cb = (EventCallbackIO*)(p->epollEvents[i].data.ptr);
 | 
			
		||||
        if(cb) cb->ioReady(p->epollEvents[i].events);
 | 
			
		||||
        else p->processInternalEvents();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // evaluate timers
 | 
			
		||||
    uint64_t now_ns = p->now_ns(), ns_elapsed = now_ns - p->last_ns;
 | 
			
		||||
    p->last_ns = now_ns;
 | 
			
		||||
    ElapsedTime elapsedTime = ElapsedTime::from_ns(ns_elapsed);
 | 
			
		||||
 | 
			
		||||
    bool timerFired = false;
 | 
			
		||||
    int timerErased = 0;
 | 
			
		||||
    for(uint i = 0, end = p->timers.size(); i < end; ++i) {
 | 
			
		||||
        if(p->timers[i].remainingPeriod <= elapsedTime) {
 | 
			
		||||
            // fire timer
 | 
			
		||||
            timerFired = true;
 | 
			
		||||
 | 
			
		||||
            if(!p->timers[i].recurringPeriod) {
 | 
			
		||||
                p->timers[i].timerCallback->timer(p->timers[i].key);
 | 
			
		||||
                p->timers[i].remainingPeriod.set_ns(~(uint64_t)0);
 | 
			
		||||
                ++timerErased;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uint64_t remaining = ns_elapsed,
 | 
			
		||||
                oldPeriod = p->timers[i].remainingPeriod.to_ns(),
 | 
			
		||||
                recur = p->timers[i].recurringPeriod.to_ns();
 | 
			
		||||
            while(true) {
 | 
			
		||||
                p->timers[i].timerCallback->timer(p->timers[i].key);
 | 
			
		||||
                oldPeriod += recur;
 | 
			
		||||
                if(remaining < oldPeriod) break;
 | 
			
		||||
                remaining -= recur;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            oldPeriod -= remaining;
 | 
			
		||||
            p->timers[i].remainingPeriod.set_ns(oldPeriod);
 | 
			
		||||
        } else {
 | 
			
		||||
            p->timers[i].remainingPeriod -= elapsedTime;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // resort list if appropriate
 | 
			
		||||
    if(timerFired) {
 | 
			
		||||
        std::stable_sort(p->timers.begin(), p->timers.end(), pimpl::TimerCmp());
 | 
			
		||||
        if(timerErased) p->timers.erase(p->timers.end() - timerErased, p->timers.end());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // deal with all the objects that need to be updated
 | 
			
		||||
    int x;
 | 
			
		||||
    while((x = p->updateList.size())) {
 | 
			
		||||
        --x;
 | 
			
		||||
        p->updateList[x]->update();
 | 
			
		||||
        ignoreUpdate(p->updateList[x]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void EventManager::registerDevice(IODevice* ioDevice, EventCallbackIO* eventCallback,
 | 
			
		||||
                                  uint32_t eventFlags)
 | 
			
		||||
{
 | 
			
		||||
    p->processingCheck(L"EventManager::registerDevice()");
 | 
			
		||||
    NullPointer::check(ioDevice, L"ioDevice", L"EventManager::registerDevice()");
 | 
			
		||||
    NullPointer::check(eventCallback, L"eventCallback", L"EventManager::registerDevice()");
 | 
			
		||||
 | 
			
		||||
    struct epoll_event ev;
 | 
			
		||||
    memset(&ev, 0, sizeof(ev));
 | 
			
		||||
    ev.data.ptr = eventCallback;
 | 
			
		||||
    ev.events = (eventFlags & (EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP)) | EPOLLET;
 | 
			
		||||
    if(epoll_ctl(p->epollFd, EPOLL_CTL_ADD, ioDevice->getListenFd(), &ev)) throw SystemError()
 | 
			
		||||
        .chain(L"epoll_ctl(EPOLL_CTL_ADD)").chain(L"EventManager::registerDevice()");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void EventManager::modifyDevice(IODevice* ioDevice, EventCallbackIO* eventCallback,
 | 
			
		||||
                                uint32_t eventFlags)
 | 
			
		||||
{
 | 
			
		||||
    NullPointer::check(ioDevice, L"ioDevice", L"EventManager::modifyDevice()");
 | 
			
		||||
    NullPointer::check(eventCallback, L"eventCallback", L"EventManager::modifyDevice()");
 | 
			
		||||
 | 
			
		||||
    struct epoll_event ev;
 | 
			
		||||
    memset(&ev, 0, sizeof(ev));
 | 
			
		||||
    ev.data.ptr = eventCallback;
 | 
			
		||||
    ev.events = (eventFlags & (EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLERR | EPOLLHUP)) | EPOLLET;
 | 
			
		||||
    if(epoll_ctl(p->epollFd, EPOLL_CTL_MOD, ioDevice->getListenFd(), &ev)) throw SystemError()
 | 
			
		||||
        .chain(L"epoll_ctl(EPOLL_CTL_ADD)").chain(L"EventManager::modifyDevice()");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void EventManager::ignoreDevice(IODevice* ioDevice)
 | 
			
		||||
{
 | 
			
		||||
    p->processingCheck(L"EventManager::ignoreDevice()");
 | 
			
		||||
    NullPointer::check(ioDevice, L"ioDevice", L"EventManager::modifyDevice()");
 | 
			
		||||
 | 
			
		||||
    if(epoll_ctl(p->epollFd, EPOLL_CTL_DEL, ioDevice->getListenFd(), 0)) throw SystemError()
 | 
			
		||||
        .chain(L"epoll_ctl(EPOLL_CTL_DEL)").chain(L"EventManager::ignoreDevice()");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Key EventManager::registerTimer(EventCallbackTimer* timerCallback, ElapsedTime initialPeriod,
 | 
			
		||||
                                ElapsedTime recurringPeriod)
 | 
			
		||||
{
 | 
			
		||||
    p->processingCheck(L"EventManager::registerTimer()");
 | 
			
		||||
    NullPointer::check(timerCallback, L"timerCallback", L"EventManager::registerTimer()");
 | 
			
		||||
    if(!recurringPeriod) throw BadArgument(L"recurringPeriod", L"non-zero", L"zero");
 | 
			
		||||
 | 
			
		||||
    pimpl::TimerEntry t(timerCallback, initialPeriod, recurringPeriod);
 | 
			
		||||
    p->timers.insert(std::upper_bound(p->timers.begin(), p->timers.end(), t, pimpl::TimerCmp()), t);
 | 
			
		||||
    return t.key;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Key EventManager::registerTimer(EventCallbackTimer* timerCallback, ElapsedTime period)
 | 
			
		||||
{
 | 
			
		||||
    p->processingCheck(L"EventManager::registerTimer()");
 | 
			
		||||
    NullPointer::check(timerCallback, L"timerCallback", L"EventManager::registerTimer()");
 | 
			
		||||
 | 
			
		||||
    pimpl::TimerEntry t(timerCallback, period, lw::ElapsedTime());
 | 
			
		||||
    p->timers.insert(std::upper_bound(p->timers.begin(), p->timers.end(), t, pimpl::TimerCmp()), t);
 | 
			
		||||
    return t.key;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void EventManager::ignoreTimer(Key key)
 | 
			
		||||
{
 | 
			
		||||
    p->processingCheck(L"EventManager::ignoreTimer()");
 | 
			
		||||
    for(uint i = 0, end = p->timers.size(); i < end; ++i) {
 | 
			
		||||
        if(p->timers[i].key == key) {
 | 
			
		||||
            p->timers.erase(p->timers.begin() + i);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void EventManager::registerUpdate(lw::EventCallbackUpdate* updateCallback)
 | 
			
		||||
{
 | 
			
		||||
    // search for it in the list, in case it's already registered
 | 
			
		||||
    std::vector<EventCallbackUpdate*>::iterator iter =
 | 
			
		||||
        std::lower_bound(p->updateList.begin(), p->updateList.end(), updateCallback);
 | 
			
		||||
    if(*iter == updateCallback) return;
 | 
			
		||||
 | 
			
		||||
    // do a sorted insert
 | 
			
		||||
    p->updateList.insert(iter, updateCallback);
 | 
			
		||||
 | 
			
		||||
    // register ourselves with it, in case it's deleted
 | 
			
		||||
    updateCallback->managers.push_back(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void EventManager::ignoreUpdate(lw::EventCallbackUpdate* updateCallback)
 | 
			
		||||
{
 | 
			
		||||
    // search for it in the list, return if not found
 | 
			
		||||
    std::vector<EventCallbackUpdate*>::iterator iter =
 | 
			
		||||
        std::lower_bound(p->updateList.begin(), p->updateList.end(), updateCallback);
 | 
			
		||||
    if(*iter != updateCallback) return;
 | 
			
		||||
 | 
			
		||||
    // remove it, unregister with it
 | 
			
		||||
    std::vector<EventManager*>& evlist = (*iter)->managers;
 | 
			
		||||
    p->updateList.erase(iter);
 | 
			
		||||
 | 
			
		||||
    for(uint i = 0, end = evlist.size(); i < end; ++i) {
 | 
			
		||||
        if(evlist[i] == this) {
 | 
			
		||||
            evlist.erase(evlist.begin() + i);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void EventManager::shutdown()
 | 
			
		||||
{
 | 
			
		||||
    pimpl::InternalEvent ev = pimpl::InternalEventShutdown;
 | 
			
		||||
    write(p->pipeFd[pipeWrite], (char*)&ev, sizeof(ev));
 | 
			
		||||
    // TODO: yes, we ignore error handling here -- but what can we do?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,265 @@
 | 
			
		|||
/* lw-support/src/lib/Events/EventManager.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Flag to denote I/O device has input available.
 | 
			
		||||
extern const uint32_t IOEventRead;
 | 
			
		||||
 | 
			
		||||
/// Flag to denote I/O device is ready for output.
 | 
			
		||||
extern const uint32_t IOEventWrite;
 | 
			
		||||
 | 
			
		||||
/// Flag to denote I/O device has urgent input available.
 | 
			
		||||
extern const uint32_t IOEventUrgent;
 | 
			
		||||
 | 
			
		||||
/// Flag to denote I/O device has developed an error condition.
 | 
			
		||||
extern const uint32_t IOEventError;
 | 
			
		||||
 | 
			
		||||
/// Flag to denote I/O device has signalled HUP.
 | 
			
		||||
extern const uint32_t IOEventHUP;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Event manager.
 | 
			
		||||
 | 
			
		||||
This object is responsible for driving an event-driven application. It collects system events (I/O,
 | 
			
		||||
timers and signals) and dispatches them appropriately. This class uses \c epoll() (which is a
 | 
			
		||||
\c select() replacement) to implement fast and efficient I/O event notification. To use it,
 | 
			
		||||
interested objects must register themselves through the various registration functions.
 | 
			
		||||
 | 
			
		||||
To poll for events, call the processEvents() function. This will process any events in the queue or,
 | 
			
		||||
if the queue is empty, will wait (possibly only for a limited time) for more. Object processing is
 | 
			
		||||
split into two phases within this function: first, all of the I/O, timer and signal callbacks are
 | 
			
		||||
processed, and then the update callbacks.
 | 
			
		||||
 | 
			
		||||
During the first phase of callbacks, it is not legal for objects to delete themselves, delete other
 | 
			
		||||
objects registered with the event manager, or to call any of the event manager functions (other than
 | 
			
		||||
modifyDevice() and registerUpdate()). During the second phase (the update callbacks), these
 | 
			
		||||
restrictions no longer apply. It is not possible to throw exceptions from callback functions.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class EventManager : public Uncopyable {
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor. Sets up event manager.
 | 
			
		||||
 | 
			
		||||
    \param numDevicesHint The maximum likely number of devices that will be polled.
 | 
			
		||||
    \param numEvents The number of events to process in a single loop.
 | 
			
		||||
    \throws SystemError if an error occurs.
 | 
			
		||||
 | 
			
		||||
    The constructor creates and sets up the initial event manager. It expects two parameters: a
 | 
			
		||||
    hint as to how many devices you might be interested in (the kernel uses this to pre-allocate
 | 
			
		||||
    space, but the buffer size is dynamic anyway), and the number of events to process at once. This
 | 
			
		||||
    parameter is actually the size of the buffer the kernel uses to pass events to userspace; a
 | 
			
		||||
    larger buffer means more events to process per system call, but at the expense of memory usage.
 | 
			
		||||
    A reasonable value is to match \a numDevicesHint.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    EventManager(int numDevicesHint, int numEvents);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Cleans up.
 | 
			
		||||
    ~EventManager();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Register interest in a device's I/O readiness.
 | 
			
		||||
 | 
			
		||||
    \param ioDevice The I/O device in which you are interested.
 | 
			
		||||
    \param eventCallback The event sink object.
 | 
			
		||||
    \param eventFlags A bitfield of flags signifying which events you are interested in receiving.
 | 
			
		||||
        You may pass \c ~0 if you are interested in all events.
 | 
			
		||||
    \throws SystemError on error.
 | 
			
		||||
    \throws NullPointer if \a ioDevice or \a eventCallback are null.
 | 
			
		||||
 | 
			
		||||
    This function will register interest in receiving readiness notification for an I/O device. You
 | 
			
		||||
    specify the event sink object, which receives a callback for each readiness change, and the
 | 
			
		||||
    eventFlags object, which is a bitfield comprising the events you are interested in, and may
 | 
			
		||||
    include:
 | 
			
		||||
     - lw::IOEventRead
 | 
			
		||||
     - lw::IOEventWrite
 | 
			
		||||
     - lw::IOEventUrgent
 | 
			
		||||
     - lw::IOEventError
 | 
			
		||||
     - lw::IOEventHUP
 | 
			
		||||
 | 
			
		||||
    Once registered, you may cancel the callback by closing the device (once the kernel detects
 | 
			
		||||
    the file descriptor is closed, it will automatically remove it from the list of devices we are
 | 
			
		||||
    interested in), or explicitly by calling ignoreDevice() with the same parameters. If you do
 | 
			
		||||
    close the device's file descriptor, you should not call ignoreDevice().
 | 
			
		||||
 | 
			
		||||
    Note that it is your responsibility to free the EventCallbackIO object after calling
 | 
			
		||||
    ignoreDevice() or closing the file descriptor.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void registerDevice(lw::IODevice* ioDevice, lw::EventCallbackIO* eventCallback,
 | 
			
		||||
                        uint32_t eventFlags);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Modify device.
 | 
			
		||||
 | 
			
		||||
    \param ioDevice The I/O device in which you are interested.
 | 
			
		||||
    \param eventCallback The event sink object.
 | 
			
		||||
    \param eventFlags A bitfield of flags signifying which events you are interested in receiving.
 | 
			
		||||
        You may pass \c ~0 if you are interested in all events.
 | 
			
		||||
    \throws SystemError on error.
 | 
			
		||||
    \throws NullPointer if \a ioDevice or \a eventCallback are null.
 | 
			
		||||
 | 
			
		||||
    This is like registerDevice(), only it modifies the information for a device you have already
 | 
			
		||||
    added. It can be used to change the callback object or to change the events in which you are
 | 
			
		||||
    interested. It can be called at any time, even from within a callback.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void modifyDevice(lw::IODevice* ioDevice, lw::EventCallbackIO* eventCallback,
 | 
			
		||||
                      uint32_t eventFlags);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Stop listening to a device.
 | 
			
		||||
 | 
			
		||||
    \param ioDevice The I/O device in which you are no longer interested.
 | 
			
		||||
    \throws SystemError on error.
 | 
			
		||||
    \throws NullPointer if \a ioDevice is null.
 | 
			
		||||
 | 
			
		||||
    This stops reporting events for a particular device. It does not delete the event callback
 | 
			
		||||
    object or close the device. If you have already closed the device, you do not need to call this
 | 
			
		||||
    function, since devices are automatically removed from the epoll set when their file descriptors
 | 
			
		||||
    are closed.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void ignoreDevice(lw::IODevice* ioDevice);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Register a timer callback.
 | 
			
		||||
 | 
			
		||||
    \param timerCallback The callback object.
 | 
			
		||||
    \param initialPeriod The initial time to wait before calling the callback.
 | 
			
		||||
    \param recurringPeriod After the initial callback, this specifies the period at which the event
 | 
			
		||||
        will be repeated.
 | 
			
		||||
    \throws NullPointer if \a timerCallback is null.
 | 
			
		||||
    \throws BadArgument if \a initialPeriod or \a recurringPeriod are zero.
 | 
			
		||||
    \returns A unique key.
 | 
			
		||||
 | 
			
		||||
    This function allows you to register a timer object for regular callback events. It returns a
 | 
			
		||||
    unique key that can be used with \a ignoreTimer (and which is also passed to the
 | 
			
		||||
    EventCallbackTimer::timer() callback function).
 | 
			
		||||
 | 
			
		||||
    This system is limited by the timing resolution of the operating system, which is typically on
 | 
			
		||||
    the order of 1-10 milliseconds. However, the average frequency over time will match what you
 | 
			
		||||
    request; the events will simply be bunched up.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    lw::Key registerTimer(lw::EventCallbackTimer* timerCallback, lw::ElapsedTime initialPeriod,
 | 
			
		||||
                          lw::ElapsedTime recurringPeriod);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Register a one-shot timer callback.
 | 
			
		||||
 | 
			
		||||
    \param timerCallback The callback object.
 | 
			
		||||
    \param period The time to wait before calling the callback.
 | 
			
		||||
    \throws NullPointer if \a timerCallback is null.
 | 
			
		||||
    \throws BadArgument if \a period is zero.
 | 
			
		||||
    \returns A unique key.
 | 
			
		||||
 | 
			
		||||
    This function allows you to register a timer object for a single callback event. It returns a
 | 
			
		||||
    unique key that can be used with \a ignoreTimer (and which is also passed to the
 | 
			
		||||
    EventCallbackTimer::timer() callback function).
 | 
			
		||||
 | 
			
		||||
    This system is limited by the timing resolution of the operating system, which is typically on
 | 
			
		||||
    the order of 1-10 milliseconds.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    lw::Key registerTimer(lw::EventCallbackTimer* timerCallback, lw::ElapsedTime period);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Stop a timer event.
 | 
			
		||||
 | 
			
		||||
    \param key The unique key returned from registerTimer().
 | 
			
		||||
 | 
			
		||||
    Calling this function will stop a timer event from occuring. If the value of \a key you
 | 
			
		||||
    specified does not exist, it will be ignored.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void ignoreTimer(lw::Key key);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Register an object for updating.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void registerUpdate(lw::EventCallbackUpdate* updateCallback);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Remove an object from the update list.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void ignoreUpdate(lw::EventCallbackUpdate* updateCallback);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Process events.
 | 
			
		||||
 | 
			
		||||
    \param timeout The timeout, in milliseconds. Passing 0 means the
 | 
			
		||||
        function will time out or return immediately; passing a negative
 | 
			
		||||
        value means it will never time out.
 | 
			
		||||
    \throws SystemError if an error occurred. This could either be an
 | 
			
		||||
        error from the epoll_wait() function used internally, or an
 | 
			
		||||
        asynchronous error from the backing thread. In either case, it
 | 
			
		||||
        is only safe to delete the object and propagate the error.
 | 
			
		||||
    \throws EventManager::Shutdown if the object has been shut down.
 | 
			
		||||
    \throws Timeout if no events are processed during the specified
 | 
			
		||||
        timeout period.
 | 
			
		||||
 | 
			
		||||
    This function will wait until there are some events to process. It
 | 
			
		||||
    will then call the relevant callbacks and return. See the
 | 
			
		||||
    registerDevice() function for more information about callbacks.
 | 
			
		||||
 | 
			
		||||
    This function is not reentrant. It should only ever be called by one
 | 
			
		||||
    thread at once.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void processEvents(int timeout);
 | 
			
		||||
 | 
			
		||||
    /// Thrown when an EventManager times out.
 | 
			
		||||
    class Timeout : public ProgramException { };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Shut down the object.
 | 
			
		||||
 | 
			
		||||
    \throws SystemError on error.
 | 
			
		||||
 | 
			
		||||
    This function marks the object as shut down. Any thread currently
 | 
			
		||||
    waiting in processEvents(), or that calls processEvents() from now
 | 
			
		||||
    on, will be
 | 
			
		||||
 | 
			
		||||
    This function is thread safe and reentrant.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void shutdown();
 | 
			
		||||
 | 
			
		||||
    /// Thrown when an EventManager is shut down.
 | 
			
		||||
    class Shutdown : public ProgramException { };
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    // use pimpl here, since this class will only be created infrequently
 | 
			
		||||
    struct pimpl;
 | 
			
		||||
    pimpl* p;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/BadArgument.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2006, Laurence Withers. Released under the GNU GPL. See file COPYING for more information
 | 
			
		||||
 *  and terms of license, or read http://www.gnu.org/copyleft/gpl.html .
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BadArgument::BadArgument(const std::wstring& argName, const std::wstring& acceptableRange,
 | 
			
		||||
                         const std::wstring& value)
 | 
			
		||||
    : Exception(format(argName, acceptableRange, value)), argName(argName),
 | 
			
		||||
    acceptableRange(acceptableRange), value(value)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring BadArgument::format(const std::wstring& argName, const std::wstring& acceptableRange,
 | 
			
		||||
                                 const std::wstring& value)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream ostr;
 | 
			
		||||
    ostr << "Invalid argument to function."
 | 
			
		||||
        "\n  Argument name   : " << argName <<
 | 
			
		||||
        "\n  Acceptable range: " << acceptableRange <<
 | 
			
		||||
        "\n  Actual value    : " << value;
 | 
			
		||||
    return ostr.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/BadArgument.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2006, Laurence Withers. Released under the GNU GPL. See file COPYING for more information
 | 
			
		||||
 *  and terms of license, or read http://www.gnu.org/copyleft/gpl.html .
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Exception class: bad argument to function.
 | 
			
		||||
 | 
			
		||||
This exception class is used when an invalid argument is passed to a function (for instance, a
 | 
			
		||||
negative size in bytes, etc.). It stores some useful details for tracking down the bad argument.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class BadArgument : public lw::Exception {
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param argName Name of the invalid argument.
 | 
			
		||||
    \param acceptableRange Human-understandable range of valid arguments.
 | 
			
		||||
    \param value Actual value passed to function.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    BadArgument(const std::wstring& argName, const std::wstring& acceptableRange,
 | 
			
		||||
                const std::wstring& value);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param argName Name of the invalid argument.
 | 
			
		||||
    \param acceptableRange Human-understandable range of valid arguments.
 | 
			
		||||
    \param value Actual value passed to function.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    template<class T> BadArgument(const std::wstring& argName, const std::wstring& acceptableRange,
 | 
			
		||||
                                  const T& value)
 | 
			
		||||
        : Exception(format(argName, acceptableRange, mkValue(value))), argName(argName),
 | 
			
		||||
        acceptableRange(acceptableRange), value(mkValue(value))
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Destructor (does nothing).
 | 
			
		||||
    virtual ~BadArgument() throw() { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Name of the invalid argument.
 | 
			
		||||
    const std::wstring argName;
 | 
			
		||||
 | 
			
		||||
    /// Human-understandable range of valid arguments.
 | 
			
		||||
    const std::wstring acceptableRange;
 | 
			
		||||
 | 
			
		||||
    /// Actual value passed to function.
 | 
			
		||||
    const std::wstring value;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    template<class T> std::wstring mkValue(const T& value)
 | 
			
		||||
    {
 | 
			
		||||
        std::wostringstream ostr;
 | 
			
		||||
        ostr << value;
 | 
			
		||||
        return ostr.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::wstring format(const std::wstring& argName, const std::wstring& acceptableRange,
 | 
			
		||||
                        const std::wstring& value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/BadString.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring BadSourceChar::buildDesc(const char* seqStart,
 | 
			
		||||
    const char* seqEnd, int seqOffset, const std::wstring& encodingName,
 | 
			
		||||
    const std::wstring& reason)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
 | 
			
		||||
    o << L"Error parsing a string. Details:\n"
 | 
			
		||||
         L"  Encoding    : " << encodingName << L"\n"
 | 
			
		||||
         L"  Reason      : " << reason << L"\n"
 | 
			
		||||
         L"  Byte offset : " << seqOffset << L"\n"
 | 
			
		||||
         L"  Bad sequence: ";
 | 
			
		||||
    o << std::hex << std::setfill(L'0');
 | 
			
		||||
    for(; seqStart != seqEnd; ++seqStart)
 | 
			
		||||
        o << std::setw(2) << (uint)(uint8_t)(*seqStart);
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BadSourceChar::BadSourceChar(const char* seqStart, const char* seqEnd,
 | 
			
		||||
    int seqOffset, const std::wstring& encodingName, const std::wstring& reason)
 | 
			
		||||
    : BadString(buildDesc(
 | 
			
		||||
          seqStart, seqEnd, seqOffset, encodingName, reason)),
 | 
			
		||||
      byteSeq(seqStart, seqEnd), seqOffset(seqOffset),
 | 
			
		||||
      encoding(encodingName), reason(reason)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring CannotConvertChar::buildDesc(wchar_t srcChar, int seqOffset,
 | 
			
		||||
    const std::wstring& srcEnc, const std::wstring& destEnc)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
 | 
			
		||||
    o << L"Error converting a string. Details:\n"
 | 
			
		||||
         L"  Source encoding     : " << srcEnc << L"\n"
 | 
			
		||||
         L"  Destination encoding: " << destEnc << L"\n"
 | 
			
		||||
         L"  Byte offset         : " << seqOffset << L"\n"
 | 
			
		||||
         L"  Source character    : 0x" << std::hex << uint(srcChar)
 | 
			
		||||
      << L", ``" << srcChar << L"''.";
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CannotConvertChar::CannotConvertChar(wchar_t srcChar, int seqOffset,
 | 
			
		||||
    const std::wstring& srcEnc, const std::wstring& destEnc)
 | 
			
		||||
  : BadString(buildDesc(srcChar, seqOffset, srcEnc, destEnc)),
 | 
			
		||||
    srcChar(srcChar), seqOffset(seqOffset), srcEnc(srcEnc), destEnc(destEnc)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,153 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/BadString.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Base exception class for string-related errors.
 | 
			
		||||
 | 
			
		||||
This simply serves as the base class for string- and character-related
 | 
			
		||||
errors.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class BadString : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param desc A description of the error.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    BadString(const std::wstring& desc)
 | 
			
		||||
        : Exception(desc)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~BadString() throw() { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Cannot represent source character.
 | 
			
		||||
 | 
			
		||||
This exception is thrown when a source character in a given encoding
 | 
			
		||||
cannot be represented in the destination encoding (e.g. ASCII cannot
 | 
			
		||||
represent arbitrary Unicode characters).
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class CannotConvertChar : public BadString {
 | 
			
		||||
private:
 | 
			
		||||
    wchar_t srcChar;
 | 
			
		||||
    int seqOffset;
 | 
			
		||||
    std::wstring srcEnc, destEnc;
 | 
			
		||||
 | 
			
		||||
    static std::wstring buildDesc(wchar_t srcChar, int seqOffset,
 | 
			
		||||
        const std::wstring& srcEnc, const std::wstring& destEnc);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param srcChar The (Unicode) code point that cannot be represented.
 | 
			
		||||
    \param seqOffset How far in the source string the conversion process
 | 
			
		||||
        reached before encountering the code point.
 | 
			
		||||
    \param srcEnc The name of the source encoding, e.g. "UCS-4".
 | 
			
		||||
    \param destEnc The name of the destination encoding, e.g. "ASCII".
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    CannotConvertChar(wchar_t srcChar, int seqOffset,
 | 
			
		||||
        const std::wstring& srcEnc, const std::wstring& destEnc);
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~CannotConvertChar() throw()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Find the source character (Unicode).
 | 
			
		||||
    wchar_t getSourceChar() const
 | 
			
		||||
    { return srcChar; }
 | 
			
		||||
 | 
			
		||||
    /// Find the offset in the source sequence.
 | 
			
		||||
    int getSeqOffset() const
 | 
			
		||||
    { return seqOffset; }
 | 
			
		||||
 | 
			
		||||
    /// Find the source encoding.
 | 
			
		||||
    const std::wstring& getSourceEncoding()
 | 
			
		||||
    { return srcEnc; }
 | 
			
		||||
 | 
			
		||||
    /// Find the destination encoding.
 | 
			
		||||
    const std::wstring& getDestEncoding()
 | 
			
		||||
    { return destEnc; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Bad source character.
 | 
			
		||||
 | 
			
		||||
This exception is thrown when converting a string from one encoding to
 | 
			
		||||
another, and a byte sequence in the source string is not a valid
 | 
			
		||||
character. This can happen with UTF-8 for example.
 | 
			
		||||
 | 
			
		||||
The exception provides some context about where in the conversion
 | 
			
		||||
process the error occurred, potentially allowing a continuation of the
 | 
			
		||||
process, but certainly providing a detailed human-readable description
 | 
			
		||||
of the error.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class BadSourceChar : public BadString {
 | 
			
		||||
private:
 | 
			
		||||
    std::vector<uint8_t> byteSeq; // the invalid byte sequence
 | 
			
		||||
    int seqOffset;
 | 
			
		||||
    std::wstring encoding; // the source encoding name
 | 
			
		||||
    std::wstring reason; // a specific reason (if there is one)
 | 
			
		||||
 | 
			
		||||
    static std::wstring buildDesc(const char* seqStart, const char* seqEnd,
 | 
			
		||||
        int seqOffset, const std::wstring& encodingName,
 | 
			
		||||
        const std::wstring& reason);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Construct from a byte sequence.
 | 
			
		||||
 | 
			
		||||
    \param seqStart Pointer to the start of the invalid byte sequence.
 | 
			
		||||
    \param seqEnd Pointer to one-past-the-end byte of the invalid byte
 | 
			
		||||
        sequence.
 | 
			
		||||
    \param seqOffset Offset (in bytes) of the invalid sequence from the
 | 
			
		||||
        start of the string. This could potentially be used to restart a
 | 
			
		||||
        conversion.
 | 
			
		||||
    \param encodingName Name of the encoding (in UCS-4).
 | 
			
		||||
    \param reason A specific reason for the character being bad, if one
 | 
			
		||||
        can be ascertained.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    BadSourceChar(const char* seqStart, const char* seqEnd,
 | 
			
		||||
        int seqOffset, const std::wstring& encodingName,
 | 
			
		||||
        const std::wstring& reason = std::wstring());
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~BadSourceChar() throw() { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Query the invalid byte sequence.
 | 
			
		||||
    const std::vector<uint8_t>& getByteSeq() const
 | 
			
		||||
    { return byteSeq; }
 | 
			
		||||
 | 
			
		||||
    /// Query the error offset.
 | 
			
		||||
    int getSeqOffset() const
 | 
			
		||||
    { return seqOffset; }
 | 
			
		||||
 | 
			
		||||
    /// Query the encoding.
 | 
			
		||||
    const std::wstring& getEncoding() const
 | 
			
		||||
    { return encoding; }
 | 
			
		||||
 | 
			
		||||
    /// Query the reason.
 | 
			
		||||
    const std::wstring& getReason() const
 | 
			
		||||
    { return reason; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/BadTime.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Base class of all time-related exceptions.
 | 
			
		||||
class BadTime : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    BadTime(const std::wstring& errorDesc)
 | 
			
		||||
        : Exception(errorDesc)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~BadTime() throw()
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Thrown when a time operation overflows.
 | 
			
		||||
 | 
			
		||||
This exception will be thrown when a subtraction operation on an elapsed
 | 
			
		||||
time period would have resulted in a negative time period. Since this
 | 
			
		||||
result does not make sense, the operation cannot proceed.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class BadTimeOverflow : public BadTime {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    BadTimeOverflow() : BadTime(L"Cannot represent negative time periods.")
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Thrown when a time value is bad.
 | 
			
		||||
 | 
			
		||||
This exception is thrown when a time value is bad (for instance, you are
 | 
			
		||||
trying to specify a negative period, or a minute of 61, etc.).
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class BadTimeValue : public BadTime {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    BadTimeValue() : BadTime(L"Bad time value.")
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Thrown when a bad date is specified.
 | 
			
		||||
class BadDate : public BadTime {
 | 
			
		||||
public:
 | 
			
		||||
    BadDate() : BadTime(L"Bad date specified.")
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/DNS.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring DnsError::buildDesc(const std::wstring& reason,
 | 
			
		||||
    const std::string& host)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
    o << L"DNS operation failed.\n"
 | 
			
		||||
         L"  Description: " << reason << L"\n"
 | 
			
		||||
         L"  Host       : " << utf8ToUcs4(host, true);
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DnsError::DnsError(const std::wstring& reason, const std::string& host)
 | 
			
		||||
    : Exception(buildDesc(reason, host)),
 | 
			
		||||
      reason(reason), host(host)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/DNS.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Thrown when a DNS error occurs.
 | 
			
		||||
 | 
			
		||||
This class is thrown when a DNS lookup fails. It contains the name
 | 
			
		||||
of the host being looked up, and a description of what failed.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class DnsError : public Exception {
 | 
			
		||||
private:
 | 
			
		||||
    std::wstring reason;
 | 
			
		||||
    std::string host;
 | 
			
		||||
 | 
			
		||||
    static std::wstring buildDesc(const std::wstring& reason,
 | 
			
		||||
        const std::string& host);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param reason A description of what failed.
 | 
			
		||||
    \param host The host name being manipulated.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    DnsError(const std::wstring& reason,
 | 
			
		||||
        const std::string& host);
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~DnsError() throw()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Get reason for failure.
 | 
			
		||||
    const std::wstring& getReason() const
 | 
			
		||||
    { return reason; }
 | 
			
		||||
 | 
			
		||||
    /// Get host name being looked up.
 | 
			
		||||
    const std::string& getHost() const
 | 
			
		||||
    { return host; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/Exception.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Exception::Exception(const std::wstring& errorDesc)
 | 
			
		||||
    : errorDesc(errorDesc), whatBuffer(0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Exception::~Exception() throw()
 | 
			
		||||
{
 | 
			
		||||
    free(whatBuffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Exception& Exception::chain(const std::wstring& chainDesc)
 | 
			
		||||
{
 | 
			
		||||
    errorDesc.append(L"\n From: ");
 | 
			
		||||
    errorDesc.append(chainDesc);
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const char* Exception::what() const throw()
 | 
			
		||||
{
 | 
			
		||||
    free(whatBuffer);
 | 
			
		||||
    whatBuffer = strdup(toUtf8().c_str());
 | 
			
		||||
    return whatBuffer ?: "Couldn't allocate memory for exception conversion.";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::string Exception::toUtf8() const
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        return ucs4ToUtf8(errorDesc);
 | 
			
		||||
    }
 | 
			
		||||
    catch(...) {
 | 
			
		||||
        return "Couldn't encode exception text into UTF-8.";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wostream& operator<<(std::wostream& ostr, const Exception& e)
 | 
			
		||||
{
 | 
			
		||||
    ostr << e.toString();
 | 
			
		||||
    return ostr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,143 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/Exception.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief LW Support exception base class.
 | 
			
		||||
 | 
			
		||||
This is the base class for all LW Support exceptions. It is derived
 | 
			
		||||
from std::exception, allowing you to treat it as a standard library
 | 
			
		||||
exception, if that is desired. It provides an implementation of the
 | 
			
		||||
what() function that returns a UTF-8 representation of the error; the
 | 
			
		||||
error can also be accessed through the toString() and toUtf8()
 | 
			
		||||
methods.
 | 
			
		||||
 | 
			
		||||
Exceptions chains may be built; these simply track the progress of the
 | 
			
		||||
exception through the library. An example of such a chain is:
 | 
			
		||||
 | 
			
		||||
<pre>void a()
 | 
			
		||||
{
 | 
			
		||||
    throw Exception("errorMessage");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void b()
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        a();
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain("b()");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        b();
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        cout << e.what() << endl;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}</pre>
 | 
			
		||||
 | 
			
		||||
This would give the following output:
 | 
			
		||||
 | 
			
		||||
<pre>errorMessage
 | 
			
		||||
 From: b()</pre>
 | 
			
		||||
 | 
			
		||||
It is guaranteed that no exceptions will be thrown from this class,
 | 
			
		||||
unless the system is totally out of memory and cannot allocate std::string
 | 
			
		||||
objects.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class Exception : public std::exception {
 | 
			
		||||
private:
 | 
			
		||||
    // description of the error itself
 | 
			
		||||
    std::wstring errorDesc;
 | 
			
		||||
 | 
			
		||||
    // buffer for what()
 | 
			
		||||
    mutable char* whatBuffer;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Construct from error message.
 | 
			
		||||
 | 
			
		||||
    \param errorDesc A UCS-4 description of the error.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Exception(const std::wstring& errorDesc);
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~Exception() throw();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Add a link in the error chain.
 | 
			
		||||
 | 
			
		||||
    \param chainDesc A UCS-4 description of the chain.
 | 
			
		||||
    \returns a reference to the exception, so you can chain again.
 | 
			
		||||
 | 
			
		||||
    This allows you to add another link into the exception chain, after
 | 
			
		||||
    the current exception. This is intended to be used when you want to
 | 
			
		||||
    propagate a lower-level error to a higher level, but want to mark
 | 
			
		||||
    where the exception has been on its way through.
 | 
			
		||||
 | 
			
		||||
    \note It is good practice to give a function name here.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Exception& chain(const std::wstring& chainDesc);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Return UTF-8 error report.
 | 
			
		||||
    virtual const char* what() const throw();
 | 
			
		||||
 | 
			
		||||
    /// Return UTF-8 error report.
 | 
			
		||||
    std::string toUtf8() const;
 | 
			
		||||
 | 
			
		||||
    /// Return UCS-4 error report.
 | 
			
		||||
    const std::wstring& toString() const
 | 
			
		||||
    { return errorDesc; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief LW Support program exception base class.
 | 
			
		||||
 | 
			
		||||
Program exceptions are exceptions that are expected to occur, and do
 | 
			
		||||
not necessarily indicate system failure. They do signal that an
 | 
			
		||||
exceptional condition has arisen, but this is something that the program
 | 
			
		||||
should foresee and cope with. This sort of exception probably won't lead
 | 
			
		||||
directly to user intervention, so no description is required.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class ProgramException : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    ProgramException()
 | 
			
		||||
        : Exception(L"Program exception. Should not be seen by users!")
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Numeric overflow class.
 | 
			
		||||
class NumericOverflow : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    NumericOverflow()
 | 
			
		||||
        : Exception(L"Numeric overflow.")
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Stream insertion operator.
 | 
			
		||||
std::wostream& operator<<(std::wostream&, const lw::Exception&);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/FIFO.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "lw-support"
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring FIFOOutOfMemory::getDesc(size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
 | 
			
		||||
    o << L"Ran out of system memory allocating "
 | 
			
		||||
      << amt << L" bytes for FIFO.";
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/FIFO.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// FIFO exception base class.
 | 
			
		||||
class FIFOException : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    FIFOException(const std::wstring& desc)
 | 
			
		||||
        : Exception(desc)
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// FIFO memory limit exceeded.
 | 
			
		||||
class FIFOOverflow : public FIFOException {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    FIFOOverflow()
 | 
			
		||||
        : FIFOException(L"FIFO buffer overflowed (too much data).")
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Ran out of system memory while enlarging FIFO.
 | 
			
		||||
class FIFOOutOfMemory : public FIFOException {
 | 
			
		||||
private:
 | 
			
		||||
    static std::wstring getDesc(size_t amt);
 | 
			
		||||
    size_t amt;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    FIFOOutOfMemory(size_t amt)
 | 
			
		||||
        : FIFOException(getDesc(amt)), amt(amt)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Get amount allocated, in bytes.
 | 
			
		||||
    size_t getAmount() const
 | 
			
		||||
    { return amt; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Not enough data in FIFO.
 | 
			
		||||
class FIFOUnderflow : public FIFOException {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    FIFOUnderflow()
 | 
			
		||||
        : FIFOException(L"FIFO buffer underflowed (not enough data).")
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/HTTP.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring HTTPMalformed::format(const std::wstring& msg)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
    o << L"Error parsing HTTP header fields: " << msg << L".";
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
HTTPMalformed::HTTPMalformed(const std::wstring& msg)
 | 
			
		||||
    : Exception(format(msg))
 | 
			
		||||
{
 | 
			
		||||
    chain(L"while parsing HTTP header fields");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/HTTP.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Thrown when HTTP is malformed.
 | 
			
		||||
class HTTPMalformed : public Exception {
 | 
			
		||||
private:
 | 
			
		||||
    static std::wstring format(const std::wstring& msg);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    HTTPMalformed(const std::wstring& msg);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,136 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/IO.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring IOModeError::getModeDesc(IOMode mode)
 | 
			
		||||
{
 | 
			
		||||
    switch(mode) {
 | 
			
		||||
        case IOClosed:
 | 
			
		||||
            return L"closed";
 | 
			
		||||
        case IONone:
 | 
			
		||||
            return L"not opened for reading or writing";
 | 
			
		||||
        case IORead:
 | 
			
		||||
            return L"opened for reading";
 | 
			
		||||
        case IOWrite:
 | 
			
		||||
            return L"opened for writing";
 | 
			
		||||
        case IOReadWrite:
 | 
			
		||||
            return L"opened for reading and writing";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // shouldn't reach here
 | 
			
		||||
    return L"Bug in IOModeError::getModeDesc()";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring IOModeError::getDesc(IOMode currentMode, IOMode requiredMode,
 | 
			
		||||
    const std::wstring& op)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
 | 
			
		||||
    o << L"Could not perform operation on device in its current mode.\n"
 | 
			
		||||
         L"  Current mode : " << getModeDesc(currentMode) << L"\n"
 | 
			
		||||
         L"  Required mode: " << getModeDesc(requiredMode) << L"\n"
 | 
			
		||||
         L"  Operation    : " << op;
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IOModeError::IOModeError(IOInterface* ioInterface, const std::wstring& op,
 | 
			
		||||
    IOMode requiredMode)
 | 
			
		||||
    : IOException(getDesc(ioInterface->getIOMode(), requiredMode, op),
 | 
			
		||||
        ioInterface),
 | 
			
		||||
      op(op), currentMode(ioInterface->getIOMode()),
 | 
			
		||||
      requiredMode(requiredMode)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring IOTimeout::getDesc(const std::wstring& op, int timeout,
 | 
			
		||||
    IOTimeoutMode timeoutMode, size_t amountTransferred)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
 | 
			
		||||
    o << L"I/O operation timed out.\n"
 | 
			
		||||
         L"  Operation   : " << op << L"\n"
 | 
			
		||||
         L"  Timeout     : " << timeout;
 | 
			
		||||
    if(timeout < 0) o << L" (never -- so why are we here?)";
 | 
			
		||||
    else if(!timeout) o << L" (immediately)";
 | 
			
		||||
    else o << "ms";
 | 
			
		||||
 | 
			
		||||
    o << L"\n  Timeout mode: ";
 | 
			
		||||
    switch(timeoutMode) {
 | 
			
		||||
        case IOTimeoutHardPartial:
 | 
			
		||||
            o << L"hard timeout, partial transfer OK";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case IOTimeoutHardFull:
 | 
			
		||||
            o << L"hard timeout, full transfer only";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case IOTimeoutSoftPartial:
 | 
			
		||||
            o << L"soft timeout, partial transfer OK";
 | 
			
		||||
 | 
			
		||||
        case IOTimeoutSoftFull:
 | 
			
		||||
            o << L"soft timeout, full transfer only";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case IOTimeoutOneShot:
 | 
			
		||||
            o << L"one-shot";
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    o << L"\n  Bytes transferred before timeout: " << amountTransferred;
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IOTimeout::IOTimeout(IOInterface* ioInterface, const std::wstring& op,
 | 
			
		||||
    int timeout, IOTimeoutMode timeoutMode, size_t amountTransferred)
 | 
			
		||||
    : IOException(getDesc(op, timeout, timeoutMode, amountTransferred),
 | 
			
		||||
        ioInterface),
 | 
			
		||||
      op(op), timeout(timeout), timeoutMode(timeoutMode),
 | 
			
		||||
      amountTransferred(amountTransferred)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring FileNotFound::getDesc(const char* fileName)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
 | 
			
		||||
    o << L"File not found.\n"
 | 
			
		||||
         L"  Filename: ";
 | 
			
		||||
    if(fileName) o << utf8ToUcs4(fileName, true);
 | 
			
		||||
    else o << L"(null)";
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FileNotFound::FileNotFound(const char* fileName)
 | 
			
		||||
    : Exception(getDesc(fileName)), fileName(fileName)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FileNotFound::~FileNotFound() throw()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,170 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/IO.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Base class for all I/O device exceptions.
 | 
			
		||||
class IOException : public Exception {
 | 
			
		||||
private:
 | 
			
		||||
    IOInterface* ioInterface;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    IOException(const std::wstring& desc, IOInterface* ioInterface)
 | 
			
		||||
        : Exception(desc),
 | 
			
		||||
          ioInterface(ioInterface)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Does nothing.
 | 
			
		||||
    virtual ~IOException() throw()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Get the underlying device.
 | 
			
		||||
    IOInterface* getIOInterface() const
 | 
			
		||||
    { return ioInterface; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Thrown when trying to read past end-of-file.
 | 
			
		||||
class EndOfFile : public IOException {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    EndOfFile(IOInterface* ioInterface)
 | 
			
		||||
        : IOException(L"End of file reached.", ioInterface)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Does nothing.
 | 
			
		||||
    virtual ~EndOfFile() throw()
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Operation not permitted on device in current mode.
 | 
			
		||||
 | 
			
		||||
This exception is thrown whenever you try to carry out an operation on a
 | 
			
		||||
device, but the device isn't opened in the correct mode (e.g. if you try
 | 
			
		||||
to read from a write-only device, or if you try to write to an unopened
 | 
			
		||||
device).
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class IOModeError : public IOException {
 | 
			
		||||
private:
 | 
			
		||||
    const std::wstring op;
 | 
			
		||||
    const IOMode currentMode, requiredMode;
 | 
			
		||||
 | 
			
		||||
    static std::wstring getModeDesc(IOMode mode);
 | 
			
		||||
    static std::wstring getDesc(IOMode currentMode, IOMode requiredMode,
 | 
			
		||||
        const std::wstring& op);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param ioInterface The interface which was the source of the error.
 | 
			
		||||
    \param requiredMode What the device needed to be opened in for the
 | 
			
		||||
        operation to be legal.
 | 
			
		||||
    \param op A quick description of the operation (probably the
 | 
			
		||||
        function name).
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    IOModeError(IOInterface* ioInterface, const std::wstring& op,
 | 
			
		||||
        IOMode requiredMode);
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Does nothing.
 | 
			
		||||
    virtual ~IOModeError() throw()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Returns the name of the operation that was attempted.
 | 
			
		||||
    const std::wstring& getOp() const
 | 
			
		||||
    { return op; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the device's current mode.
 | 
			
		||||
    IOMode getCurrentMode() const
 | 
			
		||||
    { return currentMode; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the mode required for the operation.
 | 
			
		||||
    IOMode getRequiredMode() const
 | 
			
		||||
    { return requiredMode; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief I/O timed out.
 | 
			
		||||
 | 
			
		||||
This exception is thrown whenever an I/O operation times out.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class IOTimeout : public IOException {
 | 
			
		||||
private:
 | 
			
		||||
    const std::wstring op;
 | 
			
		||||
    const int timeout;
 | 
			
		||||
    const IOTimeoutMode timeoutMode;
 | 
			
		||||
    const size_t amountTransferred;
 | 
			
		||||
 | 
			
		||||
    static std::wstring getDesc(const std::wstring& op, int timeout,
 | 
			
		||||
        IOTimeoutMode timeoutMode, size_t amountTransferred);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param ioInterface The device that timed out.
 | 
			
		||||
    \param op Name of the operation that was being performed (probably
 | 
			
		||||
        the function name).
 | 
			
		||||
    \param timeout The timeout value, in milliseconds.
 | 
			
		||||
    \param timeoutMode The timeout mode.
 | 
			
		||||
    \param amountTransferred The amount actually transferred before
 | 
			
		||||
        timing out.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    IOTimeout(IOInterface* ioInterface, const std::wstring& op, int timeout,
 | 
			
		||||
        IOTimeoutMode timeoutMode, size_t amountTransferred);
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Does nothing.
 | 
			
		||||
    virtual ~IOTimeout() throw()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Returns the name of the operation that was attempted.
 | 
			
		||||
    const std::wstring& getOp() const
 | 
			
		||||
    { return op; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the timeout value.
 | 
			
		||||
    int getTimeout() const
 | 
			
		||||
    { return timeout; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the timeout mode.
 | 
			
		||||
    IOTimeoutMode getTimeoutMode() const
 | 
			
		||||
    { return timeoutMode; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the amount of data actually transferred before timeout.
 | 
			
		||||
    size_t getAmountTransferred() const
 | 
			
		||||
    { return amountTransferred; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// File not found exception.
 | 
			
		||||
class FileNotFound : public Exception {
 | 
			
		||||
private:
 | 
			
		||||
    static std::wstring getDesc(const char* fileName);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    FileNotFound(const char* fileName);
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    ~FileNotFound() throw();
 | 
			
		||||
 | 
			
		||||
    /// The filename.
 | 
			
		||||
    const std::string fileName;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/NullPointer.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Null pointer exception.
 | 
			
		||||
 | 
			
		||||
This exception is used whenever a function that is expecting a valid pointer is passed a null
 | 
			
		||||
pointer.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class NullPointer : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    /// Construct from pointer name.
 | 
			
		||||
    NullPointer(const std::wstring& pointerName)
 | 
			
		||||
        : lw::Exception(L"Null pointer exception: " + pointerName), pointerName(pointerName)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Destructor (does nothing).
 | 
			
		||||
    virtual ~NullPointer() throw()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Name of the pointer causing the problem.
 | 
			
		||||
    const std::wstring pointerName;
 | 
			
		||||
 | 
			
		||||
    /*! \brief Check a pointer.
 | 
			
		||||
 | 
			
		||||
    \param ptr The pointer to check.
 | 
			
		||||
    \param name Descriptive name of the pointer.
 | 
			
		||||
    \param func Name of the function, passed to chain(). May be omitted.
 | 
			
		||||
 | 
			
		||||
    If \a ptr is a null (zero) pointer, this function will throw a NullPointer exception.
 | 
			
		||||
    Otherwise, it does nothing.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline static void check(const void* ptr, const wchar_t* name, const wchar_t* func = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if(!ptr) {
 | 
			
		||||
            if(func) throw NullPointer(name).chain(func);
 | 
			
		||||
            throw NullPointer(name);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/ParseError.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring ParseError::constructDesc(const std::wstring& reason,
 | 
			
		||||
    const std::wstring& src, std::wstring::size_type errorPos)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
    std::wstring::size_type len = src.length();
 | 
			
		||||
 | 
			
		||||
    if(!len) {
 | 
			
		||||
        o << "Error while parsing input.\n"
 | 
			
		||||
            " Reason: " << reason << "\n"
 | 
			
		||||
            " (std::string is empty)";
 | 
			
		||||
        return o.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type start = std::max(errorPos, std::wstring::size_type(32)) - 32,
 | 
			
		||||
        end = std::min(errorPos + 32, len);
 | 
			
		||||
 | 
			
		||||
    o << "Error while parsing input.\n"
 | 
			
		||||
         " Reason      : " << reason << "\n"
 | 
			
		||||
         " Char offset : " << errorPos << "\n"
 | 
			
		||||
         " Nearby      : " << src.substr(start, end) << "\n"
 | 
			
		||||
         " Error at    : " << std::wstring(errorPos - start, L' ') << '^';
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ParseError::ParseError(const std::wstring& reason,
 | 
			
		||||
    const std::wstring& src, std::wstring::size_type errorPos)
 | 
			
		||||
    : Exception(constructDesc(reason, src, errorPos)),
 | 
			
		||||
      reason(reason), src(src), errorPos(errorPos)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/ParseError.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Exception thrown when parsing a std::string fails.
 | 
			
		||||
 | 
			
		||||
This exception should be thrown when the parsing of a std::string fails. It
 | 
			
		||||
is intended for std::strings which are "displayable", since the exception
 | 
			
		||||
message will contain a copy of (part of) the std::string being parsed. The
 | 
			
		||||
exception message is intended for output onto a fixed-width font
 | 
			
		||||
display.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class ParseError : public Exception {
 | 
			
		||||
private:
 | 
			
		||||
    std::wstring reason, src;
 | 
			
		||||
    std::wstring::size_type errorPos;
 | 
			
		||||
 | 
			
		||||
    static std::wstring constructDesc(const std::wstring& reason,
 | 
			
		||||
        const std::wstring& src, std::wstring::size_type errorPos);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param reason A description of why the parsing error occured.
 | 
			
		||||
    \param src The source std::string.
 | 
			
		||||
    \param errorPos The character offset at which the error occured.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ParseError(const std::wstring& reason,
 | 
			
		||||
        const std::wstring& src, std::wstring::size_type errorPos);
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~ParseError() throw()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Gets the reason for the parsing error.
 | 
			
		||||
    const std::wstring& getReason() const
 | 
			
		||||
    { return reason; }
 | 
			
		||||
 | 
			
		||||
    /// Gets the source std::string.
 | 
			
		||||
    const std::wstring& getSource() const
 | 
			
		||||
    { return src; }
 | 
			
		||||
 | 
			
		||||
    /// Gets the position (in characters) at which the error occured.
 | 
			
		||||
    std::wstring::size_type getErrorPos() const
 | 
			
		||||
    { return errorPos; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/SystemError.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring SystemError::errnoToString(int errnum)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
    o << L"System error.\n"
 | 
			
		||||
         L"  Error number: " << errnum << L"\n"
 | 
			
		||||
         L"  Description : ";
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    // this is needed if we're using the non-GNU version of strerror_r()
 | 
			
		||||
    // specified by POSIX, with signature:
 | 
			
		||||
    //    int strerror_r(int, char*, int)
 | 
			
		||||
    //
 | 
			
		||||
    // the GNU version may go away one day, according to the manpage.
 | 
			
		||||
    int bufSize = 500;
 | 
			
		||||
    char* buf = 0;
 | 
			
		||||
    bool loop = true;
 | 
			
		||||
    while(loop) {
 | 
			
		||||
        delete [] buf;
 | 
			
		||||
        bufSize <<= 1;
 | 
			
		||||
        buf = new char[bufSize];
 | 
			
		||||
 | 
			
		||||
        errno = 0;
 | 
			
		||||
        if(strerror_r(errnum, buf, bufSize)) {
 | 
			
		||||
            switch(errno) {
 | 
			
		||||
                case ERANGE:
 | 
			
		||||
                    break; // go through again
 | 
			
		||||
                case EINVAL:
 | 
			
		||||
                    o << L"(unknown -- invalid error code)";
 | 
			
		||||
                    loop = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    o << L"(unknown -- error " << errno
 | 
			
		||||
                      << L" during strerror_r conversion)";
 | 
			
		||||
                    loop = false;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // conversion was OK
 | 
			
		||||
            try {
 | 
			
		||||
                o << utf8ToUcs4(buf);
 | 
			
		||||
            }
 | 
			
		||||
            catch(...) {
 | 
			
		||||
                delete [] buf;
 | 
			
		||||
                o << "(invalid UTF-8 std::string)";
 | 
			
		||||
            }
 | 
			
		||||
            loop = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    delete [] buf;
 | 
			
		||||
#else
 | 
			
		||||
    // this is needed for the GNU version of strerror_r(), with
 | 
			
		||||
    // signature:
 | 
			
		||||
    //     char* strerror_r(int, char*, int);
 | 
			
		||||
    //
 | 
			
		||||
    // see implementation at glibc/sysdeps/generic/_strerror.c
 | 
			
		||||
    char buf[30];
 | 
			
		||||
    o << utf8ToUcs4(strerror_r(errnum, buf, sizeof(buf)), true);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SystemError::SystemError()
 | 
			
		||||
    : Exception(errnoToString(errno))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SystemError::SystemError(int errnum)
 | 
			
		||||
    : Exception(errnoToString(errnum))
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
/* lw-support/src/lib/Exceptions/SystemError.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief System call error.
 | 
			
		||||
 | 
			
		||||
This exception is used whenever a system call results in an error that
 | 
			
		||||
cannot be handled by LW Support (for instance, if an I/O device fails to
 | 
			
		||||
open). Many of the important cases will be handled with their own,
 | 
			
		||||
specific exception classes; those that aren't will be handled with this
 | 
			
		||||
class.
 | 
			
		||||
 | 
			
		||||
This class is intended as a direct replacement for the global variable
 | 
			
		||||
\a errno.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class SystemError : public Exception {
 | 
			
		||||
private:
 | 
			
		||||
    static std::wstring errnoToString(int errnum);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Construct from errno.
 | 
			
		||||
 | 
			
		||||
    This constructor will examine the current value of \a errno and
 | 
			
		||||
    build the exception from there. The exception is always built, even
 | 
			
		||||
    if \a errno is 0.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    SystemError();
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from error number.
 | 
			
		||||
 | 
			
		||||
    \param errnum The error number.
 | 
			
		||||
 | 
			
		||||
    This constructor doesn't use the global \a errno variable, but
 | 
			
		||||
    instead uses the user-supplied value. The exception is always built,
 | 
			
		||||
    even if \a errnum is 0.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    SystemError(int errnum);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
/* lw-support/src/lib/ForwardDeclare.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// This file simply contains forward declarations of all LW Support
 | 
			
		||||
// classes, to facilitate header ordering, etc.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// This namespace holds the LW Support core classes.
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BadDate;
 | 
			
		||||
class BadSourceChar;
 | 
			
		||||
class BadString;
 | 
			
		||||
class BadTime;
 | 
			
		||||
class BadTimeOverflow;
 | 
			
		||||
class BadTimeValue;
 | 
			
		||||
class CannotConvertChar;
 | 
			
		||||
class CompletionCallback;
 | 
			
		||||
class CompletionData;
 | 
			
		||||
class CompletionList;
 | 
			
		||||
class CompletionNotifier;
 | 
			
		||||
class CompletionTask;
 | 
			
		||||
class Date;
 | 
			
		||||
class DateTime;
 | 
			
		||||
class DayTime;
 | 
			
		||||
class DnsError;
 | 
			
		||||
class ElapsedTime;
 | 
			
		||||
class EndOfFile;
 | 
			
		||||
class EndPipe;
 | 
			
		||||
class EventCallbackIO;
 | 
			
		||||
class EventCallbackNetServer;
 | 
			
		||||
class EventCallbackTimer;
 | 
			
		||||
class EventManager;
 | 
			
		||||
class Exception;
 | 
			
		||||
class FIFO;
 | 
			
		||||
class FIFOException;
 | 
			
		||||
class FIFOOutOfMemory;
 | 
			
		||||
class FIFOOverflow;
 | 
			
		||||
class FIFOUnderflow;
 | 
			
		||||
class FileNotFound;
 | 
			
		||||
class HTTPHeaderParser;
 | 
			
		||||
class HTTPMalformed;
 | 
			
		||||
class HTTPRequestParser;
 | 
			
		||||
class HTTPResponseParser;
 | 
			
		||||
class IODevice;
 | 
			
		||||
class IOException;
 | 
			
		||||
class IOFilter;
 | 
			
		||||
class IOInterface;
 | 
			
		||||
class IOModeError;
 | 
			
		||||
class IOPosixDevice;
 | 
			
		||||
class IOSerialPort;
 | 
			
		||||
class IOTimeout;
 | 
			
		||||
class IOWriteTask;
 | 
			
		||||
class Key;
 | 
			
		||||
class Log;
 | 
			
		||||
class LoggingSMTPClient;
 | 
			
		||||
class Mutex;
 | 
			
		||||
class MutexHolder;
 | 
			
		||||
class MutexPadlock;
 | 
			
		||||
class MutexPadlockRecursive;
 | 
			
		||||
class NetAddress;
 | 
			
		||||
class NetAddressIPv4;
 | 
			
		||||
class NetAddressIPv6;
 | 
			
		||||
class NetClient;
 | 
			
		||||
class NetClientTCP;
 | 
			
		||||
class NetServer;
 | 
			
		||||
class NetServerCallback;
 | 
			
		||||
class NetServerTCP;
 | 
			
		||||
class NullPointer;
 | 
			
		||||
class NumericOverflow;
 | 
			
		||||
class ParseError;
 | 
			
		||||
class Pipe;
 | 
			
		||||
class ProgramException;
 | 
			
		||||
class SMTPClient;
 | 
			
		||||
class Semaphore;
 | 
			
		||||
class StopWatch;
 | 
			
		||||
class SystemError;
 | 
			
		||||
class TaskAlreadyCompleted;
 | 
			
		||||
class Thread;
 | 
			
		||||
class Uncopyable;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
/* lw-support/src/lib/IO/Device.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Base I/O device class.
 | 
			
		||||
 | 
			
		||||
This class is the base of the I/O device hierarchy. It specifies the
 | 
			
		||||
common functionality, such as closing, reading and writing. It provides
 | 
			
		||||
for both blocking and non-blocking I/O (which means that an 'open'
 | 
			
		||||
function would have to open its file descriptor in non-blocking mode).
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class IODevice : public IOInterface {
 | 
			
		||||
protected:
 | 
			
		||||
    friend class EventManager;
 | 
			
		||||
 | 
			
		||||
    /// Returns a file descriptor suitable for EventManager.
 | 
			
		||||
    virtual int getListenFd() const = 0;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor. Does nothing.
 | 
			
		||||
    IODevice()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Calls close().
 | 
			
		||||
    virtual ~IODevice()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Closes the device.
 | 
			
		||||
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    This call will close a device.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void close() = 0;
 | 
			
		||||
 | 
			
		||||
    // implemented virtuals
 | 
			
		||||
    virtual IOMode getIOMode() const
 | 
			
		||||
    { return ioMode; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
/* lw-support/src/lib/IO/Filter.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IOFilter::IOFilter(IOInterface* ioDev)
 | 
			
		||||
    : ioDev(ioDev)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOFilter::sync()
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        flush();
 | 
			
		||||
        ioDev->sync();
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"IOFilter::sync()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,94 @@
 | 
			
		|||
/* lw-support/src/lib/IO/Filter.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief I/O filter base class.
 | 
			
		||||
 | 
			
		||||
The I/O filter class sits between the program and an underlying device
 | 
			
		||||
(or another filter, since it is possible to create arbitrary chains of
 | 
			
		||||
such filters). It can perform various transformations on the data. One
 | 
			
		||||
example is a gzip filter, which compresses data before writing it and
 | 
			
		||||
decompresses it when reading.
 | 
			
		||||
 | 
			
		||||
\warning Closing and reopening the underlying device may leave data in
 | 
			
		||||
         the filter's buffers. The solution is to call the reset()
 | 
			
		||||
         method (or delete the filter and create a new one).
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class IOFilter : public IOInterface {
 | 
			
		||||
protected:
 | 
			
		||||
    /// The source device.
 | 
			
		||||
    IOInterface* ioDev;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor. Takes pointer to underlying device.
 | 
			
		||||
    IOFilter(IOInterface* ioDev);
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Flushes write cache.
 | 
			
		||||
    virtual ~IOFilter()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Empty the filter's write buffers.
 | 
			
		||||
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws IOModeError if the file isn't open for writing.
 | 
			
		||||
 | 
			
		||||
    This causes the filter to flush its write buffers to the underlying
 | 
			
		||||
    device. If the underlying device has its own write buffer, this
 | 
			
		||||
    function does not cause the underlying buffer to be flushed; that
 | 
			
		||||
    has to be done explicitly.
 | 
			
		||||
 | 
			
		||||
    \sa sync()
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void flush() = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Reset the filter.
 | 
			
		||||
 | 
			
		||||
    This should only be called if you want to clear all buffer data and
 | 
			
		||||
    state information in the filter (e.g. if you have closed and
 | 
			
		||||
    reopened the underlying device). It's important to note that calling
 | 
			
		||||
    this will simply discard any data waiting to be read/written, so you
 | 
			
		||||
    should call flush() first.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void reset() = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // implemented virtuals
 | 
			
		||||
    virtual IOMode getIOMode() const
 | 
			
		||||
    { return ioDev->getIOMode(); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Ensure data is written to disk.
 | 
			
		||||
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws IOModeError if the file isn't open for writing.
 | 
			
		||||
 | 
			
		||||
    This function will flush any output buffers to the underlying
 | 
			
		||||
    device, and will then propagate down the chain, until it reaches
 | 
			
		||||
    the eventual sink device (e.g. file or network connection), at which
 | 
			
		||||
    point it will call fdatasync() to ensure the data is physically
 | 
			
		||||
    written to disk/network/whatever.
 | 
			
		||||
 | 
			
		||||
    \sa flush()
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void sync();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,183 @@
 | 
			
		|||
/* lw-support/src/lib/IO/Interface.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief I/O interface.
 | 
			
		||||
 | 
			
		||||
This class is the interface that any I/O device or filter will
 | 
			
		||||
implement. It has both blocking and non-blocking functions.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class IOInterface {
 | 
			
		||||
protected:
 | 
			
		||||
    /// The read/write mode of the device.
 | 
			
		||||
    IOMode ioMode;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor. Initialises ioMode.
 | 
			
		||||
    IOInterface()
 | 
			
		||||
        : ioMode(IOClosed)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Destructor -- does nothing.
 | 
			
		||||
    virtual ~IOInterface() { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Gets the mode the device was opened in.
 | 
			
		||||
    virtual IOMode getIOMode() const = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Read some data (non-blocking).
 | 
			
		||||
 | 
			
		||||
    \param buf Buffer to read data into.
 | 
			
		||||
    \param amt Maximum amount of data to read (i.e. size of buffer).
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws EndOfFile if end-of-file has been reached.
 | 
			
		||||
    \throws IOModeError if the file isn't open for reading.
 | 
			
		||||
    \returns Number of bytes actually read (which will be zero if no
 | 
			
		||||
        data is currently available).
 | 
			
		||||
 | 
			
		||||
    Reads some data in a non-blocking manner. This call will return
 | 
			
		||||
    immediately and report the number of bytes read (which may well be
 | 
			
		||||
    less than the number of bytes requested). It will return zero if no
 | 
			
		||||
    data is available.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual size_t read(char* buf, size_t amt) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Write some data (non-blocking).
 | 
			
		||||
 | 
			
		||||
    \param buf Data to write.
 | 
			
		||||
    \param amt Maximum amount of data to write.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws IOModeError if the file isn't open for writing.
 | 
			
		||||
    \returns Number of bytes actually written (will be zero if the
 | 
			
		||||
        device's output buffer is currently full).
 | 
			
		||||
 | 
			
		||||
    Writes some data in a non-blocking manner. This call will return
 | 
			
		||||
    immediately and report the number of bytes written (which may well
 | 
			
		||||
    be less than the number of bytes requested). It will return zero if
 | 
			
		||||
    the device wasn't ready for writing.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual size_t write(const char* buf, size_t amt) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Wait for I/O.
 | 
			
		||||
 | 
			
		||||
    \param read If \a true, returns when the device has input available.
 | 
			
		||||
    \param write If \a true, returns when you can output to the device.
 | 
			
		||||
    \param timeout Timeout period, in milliseconds. 0 means immediate;
 | 
			
		||||
        negative means forever.
 | 
			
		||||
    \throws IOTimeout if a timeout occurs.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \retval true if an exceptional condition occurred.
 | 
			
		||||
    \retval false if no exception condition occurred.
 | 
			
		||||
 | 
			
		||||
    This function waits for the underlying device to become ready for
 | 
			
		||||
    the specified I/O operations, and returns when it is. It will
 | 
			
		||||
    return immediately if there is some exceptional condition, you can
 | 
			
		||||
    check for this by looking at the return value, which will be
 | 
			
		||||
    \a true if there is such a condition. Exceptional conditions
 | 
			
		||||
    include things such as out-of-band data and asynchronous errors.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool wait(bool read, bool write, int timeout) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Read some data (blocking) (full buffer).
 | 
			
		||||
 | 
			
		||||
    \param buf Buffer to read data into.
 | 
			
		||||
    \param amt Maximum amount of data to read (i.e. size of buffer).
 | 
			
		||||
    \param timeout Timeout period, in milliseconds. 0 means immediate;
 | 
			
		||||
        negative means forever.
 | 
			
		||||
    \param timeoutMode The timeout mode.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws IOModeError if the file isn't open for reading.
 | 
			
		||||
    \throws EndOfFile if end-of-file has been reached.
 | 
			
		||||
    \throws IOTimeout if a timeout occurs.
 | 
			
		||||
    \returns Number of bytes actually read (always more than zero).
 | 
			
		||||
 | 
			
		||||
    Reads some data, blocking if none is available. Various timeout
 | 
			
		||||
    options are available. By default, the entire buffer must be filled
 | 
			
		||||
    in the specified time period (but this is set to "forever" by
 | 
			
		||||
    default). Should the end of file be reached before the buffer is
 | 
			
		||||
    completely filled, an EndOfFile exception will be thrown; instead,
 | 
			
		||||
    you could use wait() and read() individually to get around this.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual size_t readBlock(char* buf, size_t amt, int timeout = -1,
 | 
			
		||||
        IOTimeoutMode timeoutMode = IOTimeoutHardFull) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Write some data (blocking).
 | 
			
		||||
 | 
			
		||||
    \param buf Data to write.
 | 
			
		||||
    \param amt Amount of data to write.
 | 
			
		||||
    \param timeout Timeout period, in milliseconds. 0 means immediate;
 | 
			
		||||
        negative means forever.
 | 
			
		||||
    \param timeoutMode The timeout mode.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws IOModeError if the file isn't open for writing.
 | 
			
		||||
    \throws IOTimeout if a timeout occurs.
 | 
			
		||||
    \returns Number of bytes actually written (always more than zero).
 | 
			
		||||
 | 
			
		||||
    Writes some data, blocking until it is sent. Various timeout
 | 
			
		||||
    options are available. By default, the entire buffer must be sent
 | 
			
		||||
    in the specified time period (but this is set to "forever" by
 | 
			
		||||
    default).
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual size_t writeBlock(const char* buf, size_t amt, int timeout
 | 
			
		||||
        = -1, IOTimeoutMode timeoutMode = IOTimeoutHardFull) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Ensure data is written to disk.
 | 
			
		||||
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws IOModeError if the file isn't open for writing.
 | 
			
		||||
 | 
			
		||||
    This function will flush any output buffers, then call fdatasync()
 | 
			
		||||
    on the underlying device, ensuring the data is written to disk (or
 | 
			
		||||
    network, etc.).
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void sync() = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Check for asynchronous errors.
 | 
			
		||||
 | 
			
		||||
    \throws SystemError if there are any asynchronous errors.
 | 
			
		||||
 | 
			
		||||
    This function should be called to check for asynchronous errors.
 | 
			
		||||
    Examples of where you would use it are after a non-blocking
 | 
			
		||||
    connection has completed (socket will be marked as ready for
 | 
			
		||||
    reading/writing, so you need to check what's going on), and if
 | 
			
		||||
    you receive an error signal while waiting for events on the
 | 
			
		||||
    device, etc. Implementations of this function may do nothing if
 | 
			
		||||
    asynchronous errors can't occur (e.g. on standard disk files).
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void checkErrors() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
/* lw-support/src/lib/IO/Pipe.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Pipe::Pipe()
 | 
			
		||||
    : read(0), write(0)
 | 
			
		||||
{
 | 
			
		||||
    int fds[2];
 | 
			
		||||
    if(TEMP_FAILURE_RETRY(::pipe(fds)))
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"Pipe::Pipe()").chain(L"pipe()");
 | 
			
		||||
    try {
 | 
			
		||||
        read = new EndPipe(this, fds[0], true);
 | 
			
		||||
        read->setNonBlocking();
 | 
			
		||||
        write = new EndPipe(this, fds[1], false);
 | 
			
		||||
        write->setNonBlocking();
 | 
			
		||||
    }
 | 
			
		||||
    catch(...) {
 | 
			
		||||
        delete read;
 | 
			
		||||
        delete write;
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Pipe::~Pipe()
 | 
			
		||||
{
 | 
			
		||||
    delete read;
 | 
			
		||||
    delete write;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EndPipe::EndPipe(Pipe* myPipe, int fd, bool read)
 | 
			
		||||
    : myPipe(myPipe)
 | 
			
		||||
{
 | 
			
		||||
    this->fd = fd;
 | 
			
		||||
    this->ioMode = read ? IORead : IOWrite;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EndPipe::~EndPipe()
 | 
			
		||||
{
 | 
			
		||||
    // unregister with the containing Pipe object
 | 
			
		||||
    if(ioMode == IORead) myPipe->read = 0;
 | 
			
		||||
    else myPipe->write = 0;
 | 
			
		||||
 | 
			
		||||
    // closing happens automatically in IOPosixDevice's destructor.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
/* lw-support/src/lib/IO/Pipe.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Pipe class container.
 | 
			
		||||
 | 
			
		||||
This class, when instantiated, creates a POSIX pipe device. This device
 | 
			
		||||
has two ends (of type EndPipe); you can write to one of these ends, and
 | 
			
		||||
read from the other.
 | 
			
		||||
 | 
			
		||||
This class just acts as a container for the two ends of the pipe.
 | 
			
		||||
Deleting it will delete the two end pipes.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class Pipe : public Uncopyable {
 | 
			
		||||
private:
 | 
			
		||||
    friend class EndPipe;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \throws lw::SystemError if an error occurs while creating the pipes.
 | 
			
		||||
 | 
			
		||||
    The constructor sets up both ends of the pipe.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Pipe();
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Closes both ends of the pipe.
 | 
			
		||||
    ~Pipe();
 | 
			
		||||
 | 
			
		||||
    /// Reading end of the pipe.
 | 
			
		||||
    EndPipe* read;
 | 
			
		||||
 | 
			
		||||
    /// Writing end of the pipe.
 | 
			
		||||
    EndPipe* write;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief One end of a pipe connection.
 | 
			
		||||
 | 
			
		||||
See the lw::Pipe class for a better description of POSIX pipes. This
 | 
			
		||||
object has a private constructor, since it can only be created as part
 | 
			
		||||
of an lw::Pipe container object.
 | 
			
		||||
 | 
			
		||||
The destructor will automatically unregister the object with its
 | 
			
		||||
containing lw::Pipe, in order to avoid double deletion when the Pipe
 | 
			
		||||
instance is deleted.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class EndPipe : public IOPosixDevice {
 | 
			
		||||
private:
 | 
			
		||||
    friend class Pipe;
 | 
			
		||||
    Pipe* myPipe;
 | 
			
		||||
    EndPipe(Pipe* myPipe, int fd, bool read);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~EndPipe();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,379 @@
 | 
			
		|||
/* lw-support/src/lib/IO/PosixDevice.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
// This class is a helper class used to keep track of timeouts and
 | 
			
		||||
// implement timeout behaviour for an I/O device.
 | 
			
		||||
class IOCountdown {
 | 
			
		||||
private:
 | 
			
		||||
    StopWatch started;
 | 
			
		||||
    IOInterface* ioInterface;
 | 
			
		||||
    std::wstring op;
 | 
			
		||||
    int timeout;
 | 
			
		||||
    IOTimeoutMode timeoutMode;
 | 
			
		||||
    size_t totalTransferred;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    IOCountdown(IOInterface* ioInterface, const std::wstring& op, int timeout,
 | 
			
		||||
        IOTimeoutMode timeoutMode)
 | 
			
		||||
        : ioInterface(ioInterface), op(op), timeout(timeout),
 | 
			
		||||
          timeoutMode(timeoutMode), totalTransferred(0)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    // Call this when some data is transferred.
 | 
			
		||||
    void transferred(size_t amt)
 | 
			
		||||
    {
 | 
			
		||||
        // record amount transferred
 | 
			
		||||
        totalTransferred += amt;
 | 
			
		||||
 | 
			
		||||
        // reset countdown timer
 | 
			
		||||
        if(amt && (timeoutMode == IOTimeoutSoftFull ||
 | 
			
		||||
            timeoutMode == IOTimeoutSoftPartial))
 | 
			
		||||
        {
 | 
			
		||||
            started.start();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Call this when poll() returns a timeout. Returns no. of bytes
 | 
			
		||||
    // transferred, but throws an exception if specified by behaviour.
 | 
			
		||||
    size_t timedOut()
 | 
			
		||||
    {
 | 
			
		||||
        switch(timeoutMode) {
 | 
			
		||||
            case IOTimeoutHardPartial:
 | 
			
		||||
            case IOTimeoutSoftPartial:
 | 
			
		||||
            case IOTimeoutOneShot:
 | 
			
		||||
                if(totalTransferred) break;
 | 
			
		||||
                // fall through
 | 
			
		||||
            case IOTimeoutHardFull:
 | 
			
		||||
            case IOTimeoutSoftFull:
 | 
			
		||||
                throw IOTimeout(ioInterface, op, timeout,
 | 
			
		||||
                    timeoutMode, totalTransferred);
 | 
			
		||||
        }
 | 
			
		||||
        return totalTransferred;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Call this when you need to update the timeout variable. It will
 | 
			
		||||
    // return false if the timeout has happened.
 | 
			
		||||
    bool update(int& remaining)
 | 
			
		||||
    {
 | 
			
		||||
        /* Special case for one-shot mode: after initial transfer has
 | 
			
		||||
           completed, we want to continue retrying until the I/O is
 | 
			
		||||
           drained; therefore, we set the timeout to zero.
 | 
			
		||||
 | 
			
		||||
           FIXME: a stray signal could cause poll() to fail with EINTR,
 | 
			
		||||
           at which point update() will be called event though no I/O
 | 
			
		||||
           has yet occurred, possibly leading to a premature timeout.
 | 
			
		||||
        */
 | 
			
		||||
        if(timeoutMode == IOTimeoutOneShot) return 0;
 | 
			
		||||
 | 
			
		||||
        if(timeout < 0) {
 | 
			
		||||
            remaining = timeout;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        remaining = timeout - started.elapsed().to_ms();
 | 
			
		||||
        return (remaining >= 0);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IOPosixDevice::~IOPosixDevice()
 | 
			
		||||
{
 | 
			
		||||
    if(fd >= 0) TEMP_FAILURE_RETRY(::close(fd));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOPosixDevice::close()
 | 
			
		||||
{
 | 
			
		||||
    if(fd != -1) {
 | 
			
		||||
        int ret = TEMP_FAILURE_RETRY(::close(fd));
 | 
			
		||||
        fd = -1;
 | 
			
		||||
        ioMode = IOClosed;
 | 
			
		||||
 | 
			
		||||
        if(ret) {
 | 
			
		||||
            throw SystemError()
 | 
			
		||||
                .chain(L"close()")
 | 
			
		||||
                .chain(L"IOPosixDevice::close()");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t IOPosixDevice::read(char* buf, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        checkRead();
 | 
			
		||||
 | 
			
		||||
        ssize_t ret = TEMP_FAILURE_RETRY(::read(fd, buf, amt));
 | 
			
		||||
        if(!ret) throw EndOfFile(this);
 | 
			
		||||
        if(ret == -1) {
 | 
			
		||||
            if(errno == EAGAIN) return 0;
 | 
			
		||||
            throw SystemError().chain(L"read()");
 | 
			
		||||
        }
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"IOPosixDevice::read()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t IOPosixDevice::write(const char* buf, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        checkWrite();
 | 
			
		||||
 | 
			
		||||
        ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, buf, amt));
 | 
			
		||||
        if(ret == -1) {
 | 
			
		||||
            if(errno == EAGAIN) return 0;
 | 
			
		||||
            throw SystemError().chain(L"write()");
 | 
			
		||||
        }
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"IOPosixDevice::write()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool IOPosixDevice::wait(bool read, bool write, int timeout)
 | 
			
		||||
{
 | 
			
		||||
    struct pollfd ufd = { fd, POLLPRI | (read ? POLLIN : 0) | (write ? POLLOUT : 0), 0 };
 | 
			
		||||
    switch(poll(&ufd, 1, timeout)) {
 | 
			
		||||
        case -1:
 | 
			
		||||
            if(errno == EINTR) return false;
 | 
			
		||||
            throw SystemError().chain(L"IOPosixDevice::wait()").chain(L"poll()");
 | 
			
		||||
 | 
			
		||||
        case 0:
 | 
			
		||||
            // timeout
 | 
			
		||||
            throw IOTimeout(this, L"wait()", timeout, IOTimeoutHardFull, 0);
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    return (ufd.revents & (POLLERR | POLLPRI | POLLHUP | POLLNVAL));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t IOPosixDevice::readBlock(char* buf, size_t amt, int timeout,
 | 
			
		||||
    IOTimeoutMode timeoutMode)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        checkRead();
 | 
			
		||||
 | 
			
		||||
        size_t remaining = amt;
 | 
			
		||||
        int timeout_remaining = timeout;
 | 
			
		||||
        struct pollfd ufd = { fd, POLLIN, 0 };
 | 
			
		||||
        IOCountdown countdown(this, L"readBlock()", timeout, timeoutMode);
 | 
			
		||||
 | 
			
		||||
        while(true) {
 | 
			
		||||
            switch(poll(&ufd, 1, timeout_remaining)) {
 | 
			
		||||
                case -1:
 | 
			
		||||
                    if(errno == EINTR) break;
 | 
			
		||||
                    throw SystemError().chain(L"poll()");
 | 
			
		||||
 | 
			
		||||
                case 0:
 | 
			
		||||
                    // timeout
 | 
			
		||||
                    return countdown.timedOut();
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    // perform read
 | 
			
		||||
                    size_t res = read(buf, remaining);
 | 
			
		||||
                    if( !(remaining -= res) ) return amt;
 | 
			
		||||
                    buf += res;
 | 
			
		||||
                    countdown.transferred(res);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // update timeout
 | 
			
		||||
            if(!countdown.update(timeout_remaining))
 | 
			
		||||
                return countdown.timedOut();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"IOPosixDevice::readBlock()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t IOPosixDevice::writeBlock(const char* buf, size_t amt, int timeout,
 | 
			
		||||
    IOTimeoutMode timeoutMode)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        checkWrite();
 | 
			
		||||
 | 
			
		||||
        size_t remaining = amt;
 | 
			
		||||
        int timeout_remaining = timeout;
 | 
			
		||||
        struct pollfd ufd = { fd, POLLOUT, 0 };
 | 
			
		||||
        IOCountdown countdown(this, L"writeBlock()", timeout, timeoutMode);
 | 
			
		||||
 | 
			
		||||
        while(true) {
 | 
			
		||||
            switch(poll(&ufd, 1, timeout_remaining)) {
 | 
			
		||||
                case -1:
 | 
			
		||||
                    if(errno == EINTR) break;
 | 
			
		||||
                    throw SystemError().chain(L"poll()");
 | 
			
		||||
 | 
			
		||||
                case 0:
 | 
			
		||||
                    // timeout
 | 
			
		||||
                    return countdown.timedOut();
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    // perform read
 | 
			
		||||
                    size_t res = write(buf, remaining);
 | 
			
		||||
                    if( !(remaining -= res) ) return amt;
 | 
			
		||||
                    buf += res;
 | 
			
		||||
                    countdown.transferred(res);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // update timeout
 | 
			
		||||
            if(!countdown.update(timeout_remaining))
 | 
			
		||||
                return countdown.timedOut();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"IOPosixDevice::writeBlock()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOPosixDevice::sync()
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        checkWrite();
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"IOPosixDevice::sync()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(TEMP_FAILURE_RETRY(::fdatasync(fd))) {
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"fdatasync()")
 | 
			
		||||
            .chain(L"IOPosixDevice::sync()");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOPosixDevice::setNonBlocking()
 | 
			
		||||
{
 | 
			
		||||
    int flags = fcntl(fd, F_GETFL, 0);
 | 
			
		||||
    if(flags == -1) {
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"fcntl(..., F_GETFL, ...)")
 | 
			
		||||
            .chain(L"IOPosixDevice::setNonBlocking()");
 | 
			
		||||
    }
 | 
			
		||||
    if(flags & O_NONBLOCK) return;
 | 
			
		||||
    flags |= O_NONBLOCK;
 | 
			
		||||
    if(fcntl(fd, F_SETFL, flags)) {
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"fcntl(..., F_SETFL, ...)")
 | 
			
		||||
            .chain(L"IOPosixDevice::setNonBlocking()");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int IOPosixDevice::ioModeFlags(IOMode mode)
 | 
			
		||||
{
 | 
			
		||||
    switch(mode) {
 | 
			
		||||
        case IOClosed:
 | 
			
		||||
            throw IOModeError(this, L"open", IONone);
 | 
			
		||||
 | 
			
		||||
        case IONone:
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        case IORead:
 | 
			
		||||
            return O_RDONLY;
 | 
			
		||||
 | 
			
		||||
        case IOWrite:
 | 
			
		||||
            return O_WRONLY;
 | 
			
		||||
 | 
			
		||||
        case IOReadWrite:
 | 
			
		||||
            return O_RDWR;
 | 
			
		||||
    }
 | 
			
		||||
    throw ProgramException();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOPosixDevice::checkRead() const
 | 
			
		||||
{
 | 
			
		||||
    IOMode mode = getIOMode();
 | 
			
		||||
    if(mode == IORead || mode == IOReadWrite) return;
 | 
			
		||||
    throw IOModeError(const_cast<IOPosixDevice*>(this), L"read", IORead);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOPosixDevice::checkWrite() const
 | 
			
		||||
{
 | 
			
		||||
    IOMode mode = getIOMode();
 | 
			
		||||
    if(mode == IOWrite || mode == IOReadWrite) return;
 | 
			
		||||
    throw IOModeError(const_cast<IOPosixDevice*>(this), L"write", IOWrite);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOPosixDevice::checkOpen(const std::wstring& op) const
 | 
			
		||||
{
 | 
			
		||||
    IOMode mode = getIOMode();
 | 
			
		||||
    if(mode != IOClosed) return;
 | 
			
		||||
    throw IOModeError(const_cast<IOPosixDevice*>(this), op, IOWrite);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOPosixDevice::posixOpen(const char* path, IOMode mode)
 | 
			
		||||
{
 | 
			
		||||
    fd = TEMP_FAILURE_RETRY(::open(path, ioModeFlags(mode)));
 | 
			
		||||
    if(fd == -1) {
 | 
			
		||||
        if(errno == ENOENT) throw FileNotFound(path);
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"open()")
 | 
			
		||||
            .chain(L"IOPosixDevice::posixOpen()");
 | 
			
		||||
    }
 | 
			
		||||
    try {
 | 
			
		||||
        setNonBlocking();
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        TEMP_FAILURE_RETRY(::close(fd));
 | 
			
		||||
        fd = -1;
 | 
			
		||||
        e.chain(L"IOPosixDevice::posixOpen()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
    ioMode = mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOPosixDevice::checkErrors()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
/* lw-support/src/lib/IO/PosixDevice.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief POSIX I/O device class.
 | 
			
		||||
 | 
			
		||||
Many standard devices can be accessed through a POSIX file descriptor.
 | 
			
		||||
This is the base class for all such devices. It provides implementations
 | 
			
		||||
for reading, writing, etc.; but not for actually opening devices.
 | 
			
		||||
 | 
			
		||||
\todo Port readBlock() and writeBlock() over to use wait(), and deal
 | 
			
		||||
    with any exceptional conditions somehow.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class IOPosixDevice : public IODevice, public Uncopyable {
 | 
			
		||||
protected:
 | 
			
		||||
    /// The POSIX file descriptor.
 | 
			
		||||
    int fd;
 | 
			
		||||
 | 
			
		||||
    // implemented virtuals
 | 
			
		||||
    virtual int getListenFd() const
 | 
			
		||||
    { return fd; }
 | 
			
		||||
 | 
			
		||||
    /// Convenience function: sets the fd into non-blocking mode.
 | 
			
		||||
    void setNonBlocking();
 | 
			
		||||
 | 
			
		||||
    /// Convenience function: converts enumeration value into GNU
 | 
			
		||||
    /// \c open() bitfield, throwing an exception if necessary.
 | 
			
		||||
    int ioModeFlags(IOMode mode);
 | 
			
		||||
 | 
			
		||||
    /// Convenience function: POSIX open() wrapper, throws exception.
 | 
			
		||||
    void posixOpen(const char* path, IOMode mode);
 | 
			
		||||
 | 
			
		||||
    /// Convenience function: check that we can read or throw exception.
 | 
			
		||||
    void checkRead() const;
 | 
			
		||||
 | 
			
		||||
    /// Convenience function: check that we can write or throw exception.
 | 
			
		||||
    void checkWrite() const;
 | 
			
		||||
 | 
			
		||||
    /// Convenience function: check device is open or throw exception.
 | 
			
		||||
    void checkOpen(const std::wstring& op) const;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor. Does nothing.
 | 
			
		||||
    IOPosixDevice()
 | 
			
		||||
        : fd(-1)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Closes device.
 | 
			
		||||
    virtual ~IOPosixDevice();
 | 
			
		||||
 | 
			
		||||
    // implemented virtuals
 | 
			
		||||
    virtual void close();
 | 
			
		||||
    virtual size_t read(char* buf, size_t amt);
 | 
			
		||||
    virtual size_t write(const char* buf, size_t amt);
 | 
			
		||||
    virtual bool wait(bool read, bool write, int timeout);
 | 
			
		||||
    virtual size_t readBlock(char* buf, size_t amt, int timeout = -1,
 | 
			
		||||
        IOTimeoutMode timeoutMode = IOTimeoutHardFull);
 | 
			
		||||
    virtual size_t writeBlock(const char* buf, size_t amt, int timeout
 | 
			
		||||
        = -1, IOTimeoutMode timeoutMode = IOTimeoutHardFull);
 | 
			
		||||
    virtual void sync();
 | 
			
		||||
    virtual void checkErrors();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
/* lw-support/src/lib/IO/SerialPort.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOSerialPort::open(IOMode mode, const char* path, int baud, bool twoStopBits)
 | 
			
		||||
{
 | 
			
		||||
    if(fd != -1) close();
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        posixOpen(path, mode);
 | 
			
		||||
        setOptions(baud, twoStopBits);
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"IOSerialPort::open()");
 | 
			
		||||
        close();
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOSerialPort::setOptions(int baud, bool twoStopBits)
 | 
			
		||||
{
 | 
			
		||||
    // read current characteristics
 | 
			
		||||
    struct termios term;
 | 
			
		||||
    if(tcgetattr(fd, &term) < 0) {
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"tcgetattr()")
 | 
			
		||||
            .chain(L"IOSerialPort::setOptions()");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // set 8N1 mode
 | 
			
		||||
    term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR
 | 
			
		||||
        | ICRNL | IXON | IXOFF);
 | 
			
		||||
    term.c_oflag &= ~OPOST;
 | 
			
		||||
    term.c_cflag &= ~(PARENB | CSIZE | CSTOPB);
 | 
			
		||||
    term.c_cflag |= CS8;
 | 
			
		||||
    term.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
 | 
			
		||||
 | 
			
		||||
    // set baud rate and stop bits
 | 
			
		||||
    if(twoStopBits) term.c_cflag |= CSTOPB;
 | 
			
		||||
    if(cfsetspeed(&term, baud)) {
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"cfsetspeed()")
 | 
			
		||||
            .chain(L"IOSerialPort::setOptions()");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // set attributes
 | 
			
		||||
    if(tcsetattr(fd, TCSAFLUSH, &term)) {
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"tcsetattr()")
 | 
			
		||||
            .chain(L"IOSerialPort::setOptions()");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOSerialPort::discardInput()
 | 
			
		||||
{
 | 
			
		||||
    if(TEMP_FAILURE_RETRY(tcflush(fd, TCIFLUSH))) {
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"tcflush()")
 | 
			
		||||
            .chain(L"IOSerialPort::discardInput()");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
/* lw-support/src/lib/IO/SerialPort.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Serial port I/O device.
 | 
			
		||||
 | 
			
		||||
This class allows you to control a serial (RS232) port or equivalent
 | 
			
		||||
device. It provides control over the baud rate and whether you want to
 | 
			
		||||
use one stop bit (the standard) or two (useful for communicating with
 | 
			
		||||
embedded devices when the baud rate generators don't exactly match). It
 | 
			
		||||
does not provide support for parity, flow control or other than 8 data
 | 
			
		||||
bits.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class IOSerialPort : public IOPosixDevice {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor (doesn't open any device).
 | 
			
		||||
    IOSerialPort()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Open or reopen device.
 | 
			
		||||
 | 
			
		||||
    \param mode The mode in which the device should be opened.
 | 
			
		||||
    \param path Path to the device node to open.
 | 
			
		||||
    \param baud The baud rate you want.
 | 
			
		||||
    \param twoStopBits \a true if you want two stop bits; \a false if
 | 
			
		||||
        you don't.
 | 
			
		||||
    \throws IOModeError if you pass lw::IOClosed as the mode.
 | 
			
		||||
    \throws SystemError if the device couldn't be opened or if the
 | 
			
		||||
        options cannot be set.
 | 
			
		||||
 | 
			
		||||
    Opens the device, setting the desired baud rate and number of stop
 | 
			
		||||
    bits. If a device is already open, it is closed first.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void open(IOMode mode, const char* path, int baud,
 | 
			
		||||
        bool twoStopBits = false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Change device options (baud rate etc.).
 | 
			
		||||
 | 
			
		||||
    \param baud The new baud rate.
 | 
			
		||||
    \param twoStopBits \a true if you want two stop bits; \a false if
 | 
			
		||||
        you don't.
 | 
			
		||||
    \throws IOModeError if the device is closed.
 | 
			
		||||
    \throws SystemError if the options cannot be set.
 | 
			
		||||
 | 
			
		||||
    Changes the baud rate and number of stop bits.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void setOptions(int baud, bool twoStopBits = false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Discard any input waiting to be read.
 | 
			
		||||
    void discardInput();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,119 @@
 | 
			
		|||
/* lw-support/src/lib/IO/Util/WriteTask.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IOWriteTask::IOWriteTask(lw::EventManager& eventManager, IODevice* ioDev, int timeoutInactive,
 | 
			
		||||
                         int timeoutOverall, CompletionCallback* completion,
 | 
			
		||||
                         const char* data, size_t amt, bool copy)
 | 
			
		||||
    : CompletionTask(completion), eventManager(eventManager), ioDev(ioDev),
 | 
			
		||||
      timeoutInactive(timeoutInactive), timeoutOverall(timeoutOverall), data(0), wr(0), amt(amt),
 | 
			
		||||
      copy(copy)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        NullPointer::check(data, L"data");
 | 
			
		||||
        NullPointer::check(ioDev, L"ioDev");
 | 
			
		||||
 | 
			
		||||
        eventManager.registerDevice(ioDev, this, lw::IOEventHUP | lw::IOEventWrite
 | 
			
		||||
                                    | lw::IOEventError);
 | 
			
		||||
#if 0
 | 
			
		||||
        timerKey2 = eventManager.registerTimer(this, lw::ElapsedTime(0));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        // copy data if necessary
 | 
			
		||||
        if(copy) {
 | 
			
		||||
            this->data = new char[amt];
 | 
			
		||||
            memcpy(this->data, data, amt);
 | 
			
		||||
        } else {
 | 
			
		||||
            this->data = (char*)data;
 | 
			
		||||
        }
 | 
			
		||||
        wr = this->data;
 | 
			
		||||
 | 
			
		||||
        // set timeouts if necessary
 | 
			
		||||
        if(timeoutInactive >= 0)
 | 
			
		||||
            timerKey0 = eventManager.registerTimer(this, lw::ElapsedTime::from_ms(timeoutInactive));
 | 
			
		||||
        if(timeoutOverall >= 0)
 | 
			
		||||
            timerKey1 = eventManager.registerTimer(this, lw::ElapsedTime::from_ms(timeoutOverall));
 | 
			
		||||
    }
 | 
			
		||||
    catch(lw::Exception& e) {
 | 
			
		||||
        if(copy) delete [] this->data;
 | 
			
		||||
        e.chain(L"IOWriteTask::IOWriteTask()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
IOWriteTask::~IOWriteTask()
 | 
			
		||||
{
 | 
			
		||||
    if(this->timeoutInactive >= 0) eventManager.ignoreTimer(timerKey0);
 | 
			
		||||
    if(this->timeoutOverall >= 0) eventManager.ignoreTimer(timerKey1);
 | 
			
		||||
    delete ioDev;
 | 
			
		||||
    //    if(copy) delete [] data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOWriteTask::taskAbort(const lw::Exception& e)
 | 
			
		||||
{
 | 
			
		||||
    taskAborted(e);
 | 
			
		||||
    delete this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool IOWriteTask::ioReady(uint32_t flags)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        if(flags & lw::IOEventError) ioDev->checkErrors();
 | 
			
		||||
        if(flags & lw::IOEventHUP) throw lw::Exception(L"Remote device closed connection.");
 | 
			
		||||
        if(flags & lw::IOEventWrite) tryWrite();
 | 
			
		||||
    }
 | 
			
		||||
    catch(lw::Exception& e) {
 | 
			
		||||
        taskAbort(e);
 | 
			
		||||
        delete this;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void IOWriteTask::tryWrite()
 | 
			
		||||
{
 | 
			
		||||
    while(amt) {
 | 
			
		||||
        size_t w = ioDev->write(wr, amt);
 | 
			
		||||
        if(!w) return;
 | 
			
		||||
 | 
			
		||||
        wr += w;
 | 
			
		||||
        amt -= w;
 | 
			
		||||
 | 
			
		||||
        if(timeoutInactive >= 0) {
 | 
			
		||||
            eventManager.ignoreTimer(timerKey0);
 | 
			
		||||
            timerKey0 = eventManager.registerTimer(this, lw::ElapsedTime::from_ms(timeoutInactive), 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // done!
 | 
			
		||||
    taskCompleted();
 | 
			
		||||
    delete this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool IOWriteTask::timer(Key)
 | 
			
		||||
{
 | 
			
		||||
    // must be a timeout
 | 
			
		||||
    taskAbort(lw::Exception(L"WriteTask timeout."));
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
/* lw-support/src/lib/IO/Util/WriteTask.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Class which writes some data to an I/O device and then closes it.
 | 
			
		||||
 | 
			
		||||
Instances of this class will write data to an I/O device, using non-blocking I/O, and (on
 | 
			
		||||
destruction) will close the device and delete it. It has options for timeouts and deals correctly
 | 
			
		||||
with errors. On task completion, the object will delete itself.
 | 
			
		||||
 | 
			
		||||
A typical usage example would be to accept a TCP connection, instantiate an IOWriteTask (with a
 | 
			
		||||
suitable message and timeout, pointed at the TCP connection, connected to a CompletionList object),
 | 
			
		||||
register the task with the CompletionList, and then simply process events as usual, forgetting about
 | 
			
		||||
the TCP connection and the IOWriteTask instance.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class IOWriteTask : public CompletionTask,
 | 
			
		||||
    private EventCallbackIO,
 | 
			
		||||
    private EventCallbackTimer
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    lw::EventManager& eventManager;
 | 
			
		||||
    IOInterface* ioDev;
 | 
			
		||||
    int timeoutInactive, timeoutOverall;
 | 
			
		||||
    char* data, * wr;
 | 
			
		||||
    size_t amt;
 | 
			
		||||
    bool copy;
 | 
			
		||||
    Key timerKey0, timerKey1;
 | 
			
		||||
 | 
			
		||||
    void tryWrite();
 | 
			
		||||
 | 
			
		||||
    // from EventCallbackIO
 | 
			
		||||
    virtual bool ioReady(uint32_t flags);
 | 
			
		||||
 | 
			
		||||
    // from EventCallbackTimer
 | 
			
		||||
    virtual bool timer(Key key);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor. Sets up the task.
 | 
			
		||||
 | 
			
		||||
    \param eventManager The event manager object to use.
 | 
			
		||||
    \param ioDev The I/O device in question.
 | 
			
		||||
    \param timeoutInactive An inactivity timeout period, in milliseconds. -1 to disable.
 | 
			
		||||
    \param timeoutOverall An overall timeout period, in milliseconds. -1 to disable.
 | 
			
		||||
    \param completion The object which will be notified about completion. Can be 0.
 | 
			
		||||
    \param data Pointer to the block of data to write.
 | 
			
		||||
    \param amt Number of bytes of data to write.
 | 
			
		||||
    \param copy Pass \c true if you want the data to be copied, or \c false if you guarantee its
 | 
			
		||||
        persistence until the task is completed.
 | 
			
		||||
 | 
			
		||||
    \throws NullPointer if \a ioDev or \a data is 0.
 | 
			
		||||
    \throws SystemError if a system call fails.
 | 
			
		||||
 | 
			
		||||
    Begins the process. Registers the device with the event manager, attempts to write some data
 | 
			
		||||
    (these can result in system errors). On completion, the object will delete itself.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    IOWriteTask(lw::EventManager& eventManager, IODevice* ioDev, int timeoutInactive,
 | 
			
		||||
                int timeoutOverall, CompletionCallback* completion,
 | 
			
		||||
                const char* data, size_t amt, bool copy);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Destructor. Closes and deletes I/O device.
 | 
			
		||||
    virtual ~IOWriteTask();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Aborts task, closing and deleting the I/O device and the instance.
 | 
			
		||||
    virtual void taskAbort(const lw::Exception& e);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Address/Address.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wostream& operator<<(std::wostream& ostr, const lw::NetAddress& addr)
 | 
			
		||||
{
 | 
			
		||||
    ostr << addr.toString();
 | 
			
		||||
    return ostr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Address/Address.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Base class for network addresses.
 | 
			
		||||
 | 
			
		||||
Since network addresses are used in very different ways, this class
 | 
			
		||||
doesn't provide much of an interface. However, it does allow for
 | 
			
		||||
dynamic casting, which is its main purpose.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class NetAddress {
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor. Does nothing.
 | 
			
		||||
    virtual ~NetAddress()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Convert to std::string representation
 | 
			
		||||
    virtual std::wstring toString() const = 0;
 | 
			
		||||
 | 
			
		||||
    /// Clone address.
 | 
			
		||||
    virtual NetAddress* clone() const = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Stream insertion operator.
 | 
			
		||||
std::wostream& operator<<(std::wostream&, const lw::NetAddress&);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,129 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Address/IPv4.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const NetAddressIPv4 NetAddressIPv4::any(0, 0, 0, 0);
 | 
			
		||||
const NetAddressIPv4 NetAddressIPv4::loopback(127, 0, 0, 1);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring NetAddressIPv4::toString() const
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
 | 
			
		||||
    o << (uint)(addr[0]) << L'.'
 | 
			
		||||
      << (uint)(addr[1]) << L'.'
 | 
			
		||||
      << (uint)(addr[2]) << L'.'
 | 
			
		||||
      << (uint)(addr[3]);
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetAddressIPv4 NetAddressIPv4::fromString(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    uint8_t addr[4];
 | 
			
		||||
    int addrPos = 0;
 | 
			
		||||
    uint p = 0;
 | 
			
		||||
    bool seen = false;
 | 
			
		||||
    wchar_t ch;
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type pos = 0, len = str.length();
 | 
			
		||||
 | 
			
		||||
    for(pos = 0; pos < len; ++pos) {
 | 
			
		||||
        ch = str[pos];
 | 
			
		||||
        switch(ch) {
 | 
			
		||||
            case '0' ... '9':
 | 
			
		||||
                seen = true;
 | 
			
		||||
                p *= 10;
 | 
			
		||||
                p += (ch - '0');
 | 
			
		||||
                if(p > 255) {
 | 
			
		||||
                    throw ParseError(L"Octet cannot be > 255.", str, pos)
 | 
			
		||||
                        .chain(L"NetAddressIPv4::fromString()");
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case '.':
 | 
			
		||||
                if(!seen) {
 | 
			
		||||
                    throw ParseError(L"Empty octet.", str, pos)
 | 
			
		||||
                        .chain(L"NetAddressIPv4::fromString()");
 | 
			
		||||
                }
 | 
			
		||||
                if(addrPos == 3) {
 | 
			
		||||
                    throw ParseError(L"Too many octets.", str, pos)
 | 
			
		||||
                        .chain(L"NetAddressIPv4::fromString()");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                addr[addrPos++] = p;
 | 
			
		||||
                p = 0;
 | 
			
		||||
                seen = false;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw ParseError(L"Unexpected symbol.", str, pos)
 | 
			
		||||
                    .chain(L"NetAddressIPv4::fromString()");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(addrPos != 3 || !seen) {
 | 
			
		||||
        throw ParseError(L"Not enough octets.", str, pos)
 | 
			
		||||
            .chain(L"NetAddressIPv4::fromString()");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addr[3] = p;
 | 
			
		||||
    return NetAddressIPv4(addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetAddressIPv4 NetAddressIPv4::fromDNS(const std::string& host)
 | 
			
		||||
{
 | 
			
		||||
    // attempt to convert from numeric form first
 | 
			
		||||
    try {
 | 
			
		||||
        return fromString(lw::asciiToUcs4(host));
 | 
			
		||||
    }
 | 
			
		||||
    catch(...) { }
 | 
			
		||||
 | 
			
		||||
    // now perform blocking DNS lookup
 | 
			
		||||
    struct hostent he, * hep = 0;
 | 
			
		||||
    int err = 0, result = 0;
 | 
			
		||||
    char buffer[4096];
 | 
			
		||||
 | 
			
		||||
    result = gethostbyname2_r(host.c_str(), AF_INET,
 | 
			
		||||
        &he, buffer, sizeof(buffer), &hep, &err);
 | 
			
		||||
    switch(result) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            // success!
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case HOST_NOT_FOUND:
 | 
			
		||||
            throw DnsError(L"Host not found.", host);
 | 
			
		||||
 | 
			
		||||
        case TRY_AGAIN:
 | 
			
		||||
            throw DnsError(L"Temporary failure in name resolution.", host);
 | 
			
		||||
 | 
			
		||||
        case NO_RECOVERY:
 | 
			
		||||
            throw DnsError(L"Unrecoverable failure in name resolution.", host);
 | 
			
		||||
 | 
			
		||||
        case NO_ADDRESS:
 | 
			
		||||
            throw DnsError(L"Host does not have an associated IPv4 address.", host);
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            throw DnsError(L"Unknown DNS error.", host);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return NetAddressIPv4(he.h_addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,175 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Address/IPv4.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief IPv4 network address.
 | 
			
		||||
 | 
			
		||||
This class is used to represent an IPv4 network address. It has a basic
 | 
			
		||||
blocking DNS lookup function.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class NetAddressIPv4 : public NetAddress {
 | 
			
		||||
private:
 | 
			
		||||
    uint8_t addr[4];
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Well-known addresses ////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// 0.0.0.0
 | 
			
		||||
    static const NetAddressIPv4 any;
 | 
			
		||||
 | 
			
		||||
    /// 127.0.0.1
 | 
			
		||||
    static const NetAddressIPv4 loopback;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Constructors ////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Default constructor: address 0.0.0.0.
 | 
			
		||||
    NetAddressIPv4()
 | 
			
		||||
    {
 | 
			
		||||
        memset(addr, 0, 4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Copy constructor.
 | 
			
		||||
    NetAddressIPv4(const NetAddressIPv4& other)
 | 
			
		||||
        : NetAddress()
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, other.addr, 4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Constructor: specify numerically.
 | 
			
		||||
    NetAddressIPv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
 | 
			
		||||
    {
 | 
			
		||||
        addr[0] = a;
 | 
			
		||||
        addr[1] = b;
 | 
			
		||||
        addr[2] = c;
 | 
			
		||||
        addr[3] = d;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Constructor: from array.
 | 
			
		||||
    NetAddressIPv4(const uint8_t* newAddr)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, newAddr, 4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Constructor: from memory.
 | 
			
		||||
    NetAddressIPv4(const char* newAddr)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, newAddr, 4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // implemented virtual
 | 
			
		||||
    virtual NetAddressIPv4* clone() const
 | 
			
		||||
    { return new NetAddressIPv4(*this); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Modifying functions /////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Assignment operator.
 | 
			
		||||
    NetAddressIPv4& operator=(const NetAddressIPv4& other)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, other.addr, 4);
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set numerically.
 | 
			
		||||
    NetAddressIPv4& set(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
 | 
			
		||||
    {
 | 
			
		||||
        addr[0] = a;
 | 
			
		||||
        addr[1] = b;
 | 
			
		||||
        addr[2] = c;
 | 
			
		||||
        addr[3] = d;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set from array.
 | 
			
		||||
    NetAddressIPv4& set(const uint8_t* newAddr)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, newAddr, 4);
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set from memory.
 | 
			
		||||
    NetAddressIPv4& set(const char* newAddr)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, newAddr, 4);
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Query functions /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Copy to array.
 | 
			
		||||
    uint8_t* copy(uint8_t* dest) const
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(dest, addr, 4);
 | 
			
		||||
        return dest;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Copy to memory.
 | 
			
		||||
    char* copy(char* dest) const
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(dest, addr, 4);
 | 
			
		||||
        return dest;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// For interfacing with the C library.
 | 
			
		||||
    struct sockaddr* copy(struct sockaddr_in* s, uint16_t port) const
 | 
			
		||||
    {
 | 
			
		||||
        s->sin_family = AF_INET;
 | 
			
		||||
        s->sin_port = htons(port);
 | 
			
		||||
        memcpy(&(s->sin_addr.s_addr), addr, 4);
 | 
			
		||||
        return (struct sockaddr*)s;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // String functions ////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    // implemented virtual
 | 
			
		||||
    virtual std::wstring toString() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from string.
 | 
			
		||||
 | 
			
		||||
    \param str The string containing an IPv4 address to parse.
 | 
			
		||||
    \throws ParseError if a parse error occurs.
 | 
			
		||||
    \returns A new address object.
 | 
			
		||||
 | 
			
		||||
    This function parses an IPv4 address in dotted quad format, i.e.
 | 
			
		||||
    <tt>a.b.c.d</tt> where a, b, c and d are all 8-bit unsigned values.
 | 
			
		||||
    Any trailing or leading whitespace is ignored.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static NetAddressIPv4 fromString(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Look up via DNS (blocking).
 | 
			
		||||
 | 
			
		||||
    \param host The hostname to look up.
 | 
			
		||||
    \throws DnsError If an error occurs.
 | 
			
		||||
    \returns A new address object.
 | 
			
		||||
 | 
			
		||||
    This function looks up a specific host via DNS, returning its IPv4
 | 
			
		||||
    address. It is a blocking function.
 | 
			
		||||
 | 
			
		||||
    \todo std::wstring-capable version
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static NetAddressIPv4 fromDNS(const std::string& host);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Address/IPv6.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
uint8_t ipv6_any_initialiser[16] = {
 | 
			
		||||
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 | 
			
		||||
};
 | 
			
		||||
uint8_t ipv6_loopback_initialiser[16] = {
 | 
			
		||||
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const NetAddressIPv6 NetAddressIPv6::any(ipv6_any_initialiser);
 | 
			
		||||
const NetAddressIPv6 NetAddressIPv6::loopback(ipv6_loopback_initialiser);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring NetAddressIPv6::toString() const
 | 
			
		||||
{
 | 
			
		||||
    char buffer[50]; // should be enough for a full IPv6 address
 | 
			
		||||
    if(!inet_ntop(AF_INET6, addr, buffer, sizeof(buffer))) {
 | 
			
		||||
        throw lw::ProgramException().chain(L"NetAddressIPv6::toString()");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return lw::strToUcs4(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetAddressIPv6 NetAddressIPv6::fromString(const std::wstring& str)
 | 
			
		||||
{
 | 
			
		||||
    std::string ip;
 | 
			
		||||
    try {
 | 
			
		||||
        ip = ucs4ToAscii(stripWhitespace(str));
 | 
			
		||||
    }
 | 
			
		||||
    catch(lw::Exception& e) {
 | 
			
		||||
        e.chain(L"NetAddressIPv6::fromString()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct in6_addr addr;
 | 
			
		||||
 | 
			
		||||
    if(!inet_pton(AF_INET6, ip.c_str(), &addr))
 | 
			
		||||
        throw ParseError(L"Not an IPv6 address.", str, 0);
 | 
			
		||||
 | 
			
		||||
    return NetAddressIPv6(addr.s6_addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetAddressIPv6 NetAddressIPv6::fromDNS(const std::string& host)
 | 
			
		||||
{
 | 
			
		||||
    // attempt to convert from numeric form first
 | 
			
		||||
    try {
 | 
			
		||||
        return fromString(lw::asciiToUcs4(host));
 | 
			
		||||
    }
 | 
			
		||||
    catch(...) { }
 | 
			
		||||
 | 
			
		||||
    // now perform blocking DNS lookup
 | 
			
		||||
    struct hostent he, * hep = 0;
 | 
			
		||||
    int err = 0, result = 0;
 | 
			
		||||
    char buffer[4096];
 | 
			
		||||
 | 
			
		||||
    result = gethostbyname2_r(host.c_str(), AF_INET6,
 | 
			
		||||
        &he, buffer, sizeof(buffer), &hep, &err);
 | 
			
		||||
    switch(result) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            // success!
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case HOST_NOT_FOUND:
 | 
			
		||||
            throw DnsError(L"Host not found.", host);
 | 
			
		||||
 | 
			
		||||
        case TRY_AGAIN:
 | 
			
		||||
            throw DnsError(L"Temporary failure in name resolution.", host);
 | 
			
		||||
 | 
			
		||||
        case NO_RECOVERY:
 | 
			
		||||
            throw DnsError(L"Unrecoverable failure in name resolution.", host);
 | 
			
		||||
 | 
			
		||||
        case NO_ADDRESS:
 | 
			
		||||
            throw DnsError(L"Host does not have an associated IPv6 address.", host);
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            throw DnsError(L"Unknown DNS error.", host);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return NetAddressIPv6(he.h_addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,160 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Address/IPv6.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief IPv6 network address.
 | 
			
		||||
 | 
			
		||||
This class is used to represent an IPv6 network address. It does not
 | 
			
		||||
have functions for converting names to IP addresses (DNS); this must be
 | 
			
		||||
done with the ... class.
 | 
			
		||||
 | 
			
		||||
\todo mention DNS class
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class NetAddressIPv6 : public NetAddress {
 | 
			
		||||
private:
 | 
			
		||||
    uint8_t addr[16];
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Well-known addresses ////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// The address \c ::
 | 
			
		||||
    static const NetAddressIPv6 any;
 | 
			
		||||
 | 
			
		||||
    /// The address \c ::1
 | 
			
		||||
    static const NetAddressIPv6 loopback;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Constructors ////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Default constructor: address \c ::
 | 
			
		||||
    NetAddressIPv6()
 | 
			
		||||
    {
 | 
			
		||||
        memset(addr, 0, 16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Copy constructor.
 | 
			
		||||
    NetAddressIPv6(const NetAddressIPv6& other)
 | 
			
		||||
        : NetAddress()
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, other.addr, 16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Constructor: from array.
 | 
			
		||||
    NetAddressIPv6(const uint8_t* newAddr)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, newAddr, 16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Constructor: from memory.
 | 
			
		||||
    NetAddressIPv6(const char* newAddr)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, newAddr, 16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // implemented virtual
 | 
			
		||||
    virtual NetAddressIPv6* clone() const
 | 
			
		||||
    { return new NetAddressIPv6(*this); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Modifying functions /////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Assignment operator.
 | 
			
		||||
    NetAddressIPv6& operator=(const NetAddressIPv6& other)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, other.addr, 16);
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set from array.
 | 
			
		||||
    NetAddressIPv6& set(const uint8_t* newAddr)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, newAddr, 16);
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set from memory.
 | 
			
		||||
    NetAddressIPv6& set(const char* newAddr)
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(addr, newAddr, 16);
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Query functions /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Copy to array.
 | 
			
		||||
    uint8_t* copy(uint8_t* dest) const
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(dest, addr, 16);
 | 
			
		||||
        return dest;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Copy to memory.
 | 
			
		||||
    char* copy(char* dest) const
 | 
			
		||||
    {
 | 
			
		||||
        memcpy(dest, addr, 16);
 | 
			
		||||
        return dest;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// For interfacing with the C library.
 | 
			
		||||
    struct sockaddr* copy(struct sockaddr_in6* s, uint16_t port) const
 | 
			
		||||
    {
 | 
			
		||||
        s->sin6_family = AF_INET6;
 | 
			
		||||
        s->sin6_port = htons(port);
 | 
			
		||||
        memcpy(s->sin6_addr.s6_addr, addr, 16);
 | 
			
		||||
        return (struct sockaddr*)s;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // String functions ////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    // implemented virtual
 | 
			
		||||
    virtual std::wstring toString() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from std::string.
 | 
			
		||||
 | 
			
		||||
    \param str The std::string containing an IPv6 address to parse.
 | 
			
		||||
    \throws ParseError if a parse error occurs.
 | 
			
		||||
    \returns A new address object.
 | 
			
		||||
 | 
			
		||||
    This function parses an IPv6 address in colon-delimited format,
 | 
			
		||||
    e.g. \c "0123:4567:89ab:cdef:0123:4567:89ab:cdef". It understands
 | 
			
		||||
    the compact form, e.g. \c "::1". Any traling or leading whitespace
 | 
			
		||||
    is ignored.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static NetAddressIPv6 fromString(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Look up via DNS (blocking).
 | 
			
		||||
 | 
			
		||||
    \param host The hostname to look up.
 | 
			
		||||
    \throws DnsError If an error occurs.
 | 
			
		||||
    \returns A new address object.
 | 
			
		||||
 | 
			
		||||
    This function looks up a specific host via DNS, returning its IPv6
 | 
			
		||||
    address. It is a blocking function.
 | 
			
		||||
 | 
			
		||||
    \todo std::wstring-capable version
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static NetAddressIPv6 fromDNS(const std::string& host);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Client.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetClient::checkErrors()
 | 
			
		||||
{
 | 
			
		||||
    int error;
 | 
			
		||||
    socklen_t socklen = sizeof(error);
 | 
			
		||||
 | 
			
		||||
    if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &socklen) == -1)
 | 
			
		||||
        throw SystemError().chain(L"NetClient::checkSocketErrors()")
 | 
			
		||||
                           .chain(L"getsockopt()");
 | 
			
		||||
 | 
			
		||||
    if(error)
 | 
			
		||||
        throw SystemError(error).chain(L"NetClient::checkSocketErrors()")
 | 
			
		||||
                                .chain(L"(pending error)");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetClient::shutdown(bool read, bool write)
 | 
			
		||||
{
 | 
			
		||||
    if(ioMode == IOClosed)
 | 
			
		||||
        throw IOModeError(this, L"shutdown", IONone);
 | 
			
		||||
 | 
			
		||||
    IOMode newIoMode;
 | 
			
		||||
    int shut;
 | 
			
		||||
 | 
			
		||||
    if(read && write) {
 | 
			
		||||
        newIoMode = IONone;
 | 
			
		||||
        shut = SHUT_RDWR;
 | 
			
		||||
    } else if (read) {
 | 
			
		||||
        if(ioMode == IOWrite || ioMode == IOReadWrite) newIoMode = IOWrite;
 | 
			
		||||
        else newIoMode = IONone;
 | 
			
		||||
        shut = SHUT_RD;
 | 
			
		||||
    } else if (write) {
 | 
			
		||||
        if(ioMode == IORead || ioMode == IOReadWrite) newIoMode = IORead;
 | 
			
		||||
        else newIoMode = IONone;
 | 
			
		||||
        shut = SHUT_WR;
 | 
			
		||||
    } else {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(TEMP_FAILURE_RETRY(::shutdown(fd, shut)))
 | 
			
		||||
        throw SystemError().chain(L"NetClient::shutdown()");
 | 
			
		||||
    ioMode = newIoMode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Client.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Base class for network clients.
 | 
			
		||||
 | 
			
		||||
This class contains some common functionality shared by all network
 | 
			
		||||
socket clients.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class NetClient : public IOPosixDevice {
 | 
			
		||||
public:
 | 
			
		||||
    // implemented virtuals
 | 
			
		||||
    virtual void checkErrors();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Shut down part of a connection.
 | 
			
		||||
 | 
			
		||||
    \param read Set to \a true if you want to shut down reading.
 | 
			
		||||
    \param write Set to \a true if you want to shut down writing.
 | 
			
		||||
    \throws IOModeError if the socket is not connected.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    This allows you to shut down part of a connection; either reading or
 | 
			
		||||
    writing (or possibly both). Any data waiting to be received or
 | 
			
		||||
    transmitted is discarded, and the read or write operation becomes
 | 
			
		||||
    unavailable (the I/O mode is changed to reflect this).
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void shutdown(bool read, bool write);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,372 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Protocols/HTTP.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//BEGIN // HTTPHeaderParser ////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
HTTPHeaderParser::HTTPHeaderParser()
 | 
			
		||||
    : state(StateBegin)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t HTTPHeaderParser::parse(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    size_t parsed = 0;
 | 
			
		||||
    char ch;
 | 
			
		||||
 | 
			
		||||
begin:
 | 
			
		||||
    if(parsed == amt || state == StateDone) return parsed;
 | 
			
		||||
    if(state == StateError) throw HTTPMalformed(L"(previous error)");
 | 
			
		||||
 | 
			
		||||
#define ERROR(p) do { \
 | 
			
		||||
    state = StateError; \
 | 
			
		||||
    throw HTTPMalformed(p); \
 | 
			
		||||
}while(0)
 | 
			
		||||
 | 
			
		||||
    // extract character (ASCII only)
 | 
			
		||||
    ch = data[parsed++];
 | 
			
		||||
    if(ch & ~0x7F) {
 | 
			
		||||
        state = StateError;
 | 
			
		||||
        throw HTTPMalformed(L"Non-ASCII data encountered in HTTP header.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch(state) {
 | 
			
		||||
        case StateBegin:
 | 
			
		||||
            if(ch == '\r') {
 | 
			
		||||
                state = StateNewline2;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if(!isgraph(ch) || ch == ':') ERROR(L"invalid field name");
 | 
			
		||||
            fieldName = tolower(ch);
 | 
			
		||||
            state = StateFieldName;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateNewline:
 | 
			
		||||
            if(ch != '\n') ERROR(L"expecting \\n after \\r");
 | 
			
		||||
            state = StateLine;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateLine:
 | 
			
		||||
            if(ch == '\r') {
 | 
			
		||||
                state = StateNewline2;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if(isblank(ch)) {
 | 
			
		||||
                state = StateFieldValue;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if(!isgraph(ch) || ch == ':') ERROR(L"invalid field name");
 | 
			
		||||
            fieldName = tolower(ch);
 | 
			
		||||
            state = StateFieldName;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateFieldName:
 | 
			
		||||
            if(ch == ':') {
 | 
			
		||||
                state = StateFieldValue;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if(!isgraph(ch)) ERROR(L"invalid field name");
 | 
			
		||||
            fieldName += tolower(ch);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateFieldValue:
 | 
			
		||||
            if(ch == '\r') {
 | 
			
		||||
                state = StateNewline;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if(!isprint(ch) && ch != '\t') ERROR(L"invalid field value");
 | 
			
		||||
            fields[fieldName] += ch;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateNewline2:
 | 
			
		||||
            if(ch != '\n') ERROR(L"expecting \\n after \\r");
 | 
			
		||||
            state = StateDone;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateDone:
 | 
			
		||||
        case StateError:
 | 
			
		||||
            // should never get here!
 | 
			
		||||
            throw lw::ProgramException().chain(L"HTTPHeaderParser::parse() [1]");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    goto begin;
 | 
			
		||||
#undef ERROR
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//END // HTTPHeaderParser //////////////////////////////////////////////
 | 
			
		||||
//BEGIN // HTTPResponseParser //////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const char* HTTPResponseParser::HTTPSeq = "HTTP/";
 | 
			
		||||
 | 
			
		||||
HTTPResponseParser::HTTPResponseParser()
 | 
			
		||||
    : state(StateHTTPSeq), HTTPSeqPos(HTTPSeq),
 | 
			
		||||
      versionMajor(0), versionMinor(0), responseCode(0), fields(hp.fields)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t HTTPResponseParser::parse(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    size_t parsed = 0;
 | 
			
		||||
    char ch;
 | 
			
		||||
 | 
			
		||||
begin:
 | 
			
		||||
    if(parsed == amt || state == StateDone) return parsed;
 | 
			
		||||
    if(state == StateError) throw HTTPMalformed(L"(previous error)");
 | 
			
		||||
    if(state == StateHeader) {
 | 
			
		||||
        try {
 | 
			
		||||
            parsed += hp.parse(data + parsed, amt - parsed);
 | 
			
		||||
            if(hp.completed()) state = StateDone;
 | 
			
		||||
            return parsed;
 | 
			
		||||
        }
 | 
			
		||||
        catch(...) {
 | 
			
		||||
            state = StateError;
 | 
			
		||||
            throw;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#define ERROR(p) do { \
 | 
			
		||||
    state = StateError; \
 | 
			
		||||
    throw HTTPMalformed(p); \
 | 
			
		||||
}while(0)
 | 
			
		||||
 | 
			
		||||
    // extract character (ASCII only)
 | 
			
		||||
    ch = data[parsed++];
 | 
			
		||||
    if(ch & ~0x7F) {
 | 
			
		||||
        state = StateError;
 | 
			
		||||
        throw HTTPMalformed(L"Non-ASCII data encountered in HTTP response line.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch(state) {
 | 
			
		||||
        case StateHTTPSeq:
 | 
			
		||||
            if(ch != *HTTPSeqPos) ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            if(!*(++HTTPSeqPos)) state = StateHTTPMajor1;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMajor1:
 | 
			
		||||
            if(!isdigit(ch)) ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            versionMajor = ch - '0';
 | 
			
		||||
            state = StateHTTPMajor;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMajor:
 | 
			
		||||
            if(isdigit(ch)) {
 | 
			
		||||
                versionMajor *= 10;
 | 
			
		||||
                versionMajor += ch - '0';
 | 
			
		||||
            } else if(ch == '.') {
 | 
			
		||||
                state = StateHTTPMinor1;
 | 
			
		||||
            } else ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMinor1:
 | 
			
		||||
            if(!isdigit(ch)) ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            versionMinor = ch - '0';
 | 
			
		||||
            state = StateHTTPMinor;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMinor:
 | 
			
		||||
            if(isdigit(ch)) {
 | 
			
		||||
                versionMinor *= 10;
 | 
			
		||||
                versionMinor += ch - '0';
 | 
			
		||||
            } else if(ch == ' ') {
 | 
			
		||||
                state = StateHTTPResp1;
 | 
			
		||||
            } else ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPResp1:
 | 
			
		||||
            if(!isdigit(ch)) ERROR(L"invalid response code");
 | 
			
		||||
            responseCode = 100 * (ch - '0');
 | 
			
		||||
            state = StateHTTPResp2;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPResp2:
 | 
			
		||||
            if(!isdigit(ch)) ERROR(L"invalid response code");
 | 
			
		||||
            responseCode += 10 * (ch - '0');
 | 
			
		||||
            state = StateHTTPResp3;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPResp3:
 | 
			
		||||
            if(!isdigit(ch)) ERROR(L"invalid response code");
 | 
			
		||||
            responseCode += (ch - '0');
 | 
			
		||||
            state = StateHTTPResp4;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPResp4:
 | 
			
		||||
            if(ch != ' ') ERROR(L"invalid response code");
 | 
			
		||||
            state = StateHTTPMsg;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMsg:
 | 
			
		||||
            if(ch == '\n') ERROR(L"\\n invalid on its own");
 | 
			
		||||
            else if(ch == '\r') state = StateHTTPNewline;
 | 
			
		||||
            else status += ch;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPNewline:
 | 
			
		||||
            if(ch != '\n') ERROR(L"expecting \\r after \\n");
 | 
			
		||||
            state = StateHeader;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHeader:
 | 
			
		||||
        case StateDone:
 | 
			
		||||
        case StateError:
 | 
			
		||||
            // should never get here!
 | 
			
		||||
            throw lw::ProgramException().chain(L"HTTPResponseParser::parse() [1]");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    goto begin;
 | 
			
		||||
#undef ERROR
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//END // HTTPResponseParser ////////////////////////////////////////////
 | 
			
		||||
//BEGIN // HTTPRequestParser ///////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const char* HTTPRequestParser::HTTPSeq = "HTTP/";
 | 
			
		||||
 | 
			
		||||
HTTPRequestParser::HTTPRequestParser()
 | 
			
		||||
    : state(StateHTTPMethod1), HTTPSeqPos(HTTPSeq), fields(hp.fields)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t HTTPRequestParser::parse(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    size_t parsed = 0;
 | 
			
		||||
    char ch;
 | 
			
		||||
 | 
			
		||||
begin:
 | 
			
		||||
    if(parsed == amt || state == StateDone) return parsed;
 | 
			
		||||
    if(state == StateError) throw HTTPMalformed(L"(previous error)");
 | 
			
		||||
    if(state == StateHeader) {
 | 
			
		||||
        try {
 | 
			
		||||
            parsed += hp.parse(data + parsed, amt - parsed);
 | 
			
		||||
            if(hp.completed()) state = StateDone;
 | 
			
		||||
            return parsed;
 | 
			
		||||
        }
 | 
			
		||||
        catch(...) {
 | 
			
		||||
            state = StateError;
 | 
			
		||||
            throw;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#define ERROR(p) do { \
 | 
			
		||||
    state = StateError; \
 | 
			
		||||
    throw HTTPMalformed(p); \
 | 
			
		||||
}while(0)
 | 
			
		||||
 | 
			
		||||
    // extract character (ASCII only)
 | 
			
		||||
    ch = data[parsed++];
 | 
			
		||||
    if(ch & ~0x7F) {
 | 
			
		||||
        state = StateError;
 | 
			
		||||
        throw HTTPMalformed(L"Non-ASCII data encountered in HTTP request line.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch(state) {
 | 
			
		||||
        case StateHTTPMethod1:
 | 
			
		||||
            if(!isgraph(ch)) ERROR(L"invalid HTTP method");
 | 
			
		||||
            method = ch;
 | 
			
		||||
            state = StateHTTPMethod;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMethod:
 | 
			
		||||
            if(ch == ' ') {
 | 
			
		||||
                state = StateHTTPUrl1;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if(!isgraph(ch)) ERROR(L"invalid HTTP method");
 | 
			
		||||
            method += ch;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPUrl1:
 | 
			
		||||
            if(!isgraph(ch)) ERROR(L"invalid HTTP URL");
 | 
			
		||||
            url = ch;
 | 
			
		||||
            state = StateHTTPUrl;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPUrl:
 | 
			
		||||
            if(ch == ' ') {
 | 
			
		||||
                state = StateHTTPSeq;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if(!isgraph(ch)) ERROR(L"invalid HTTP URL");
 | 
			
		||||
            url += ch;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPSeq:
 | 
			
		||||
            if(ch != *HTTPSeqPos) ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            if(!*(++HTTPSeqPos)) state = StateHTTPMajor1;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMajor1:
 | 
			
		||||
            if(!isdigit(ch)) ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            versionMajor = ch - '0';
 | 
			
		||||
            state = StateHTTPMajor;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMajor:
 | 
			
		||||
            if(isdigit(ch)) {
 | 
			
		||||
                versionMajor *= 10;
 | 
			
		||||
                versionMajor += ch - '0';
 | 
			
		||||
            } else if(ch == '.') {
 | 
			
		||||
                state = StateHTTPMinor1;
 | 
			
		||||
            } else ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMinor1:
 | 
			
		||||
            if(!isdigit(ch)) ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            versionMinor = ch - '0';
 | 
			
		||||
            state = StateHTTPMinor;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPMinor:
 | 
			
		||||
            if(isdigit(ch)) {
 | 
			
		||||
                versionMinor *= 10;
 | 
			
		||||
                versionMinor += ch - '0';
 | 
			
		||||
            } else if(ch == '\r') {
 | 
			
		||||
                state = StateHTTPNewline;
 | 
			
		||||
            } else ERROR(L"expecting 'HTTP/major.minor'");
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHTTPNewline:
 | 
			
		||||
            if(ch != '\n') ERROR(L"\\r must be followed by \\n");
 | 
			
		||||
            state = StateHeader;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case StateHeader:
 | 
			
		||||
        case StateDone:
 | 
			
		||||
        case StateError:
 | 
			
		||||
            // should never get here!
 | 
			
		||||
            throw lw::ProgramException().chain(L"HTTPRequestParser::parse() [1]");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    goto begin;
 | 
			
		||||
#undef ERROR
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//END // HTTPRequestParser /////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,212 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Protocols/HTTP.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parser for HTTP header fields.
 | 
			
		||||
 | 
			
		||||
This class is used to parse the HTTP header fields that occur after the
 | 
			
		||||
HTTP request or response line. It is a finite state machine parser that
 | 
			
		||||
only consumes as many characters as it needs (i.e. it stops at the final
 | 
			
		||||
\c "\r\n\r\n" sequence demanded by the standard). It exposes these
 | 
			
		||||
header fields as part of a map.
 | 
			
		||||
 | 
			
		||||
\warning This isn't 100% RFC-compliant: it won't handle multiple
 | 
			
		||||
    instances of the same field name in an RFC-compatible manner.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class HTTPHeaderParser {
 | 
			
		||||
private:
 | 
			
		||||
    enum State {
 | 
			
		||||
        StateBegin,
 | 
			
		||||
        StateNewline,
 | 
			
		||||
        StateLine,
 | 
			
		||||
        StateFieldName,
 | 
			
		||||
        StateFieldValue,
 | 
			
		||||
        StateNewline2,
 | 
			
		||||
        StateDone,
 | 
			
		||||
        StateError
 | 
			
		||||
    }state;
 | 
			
		||||
 | 
			
		||||
    std::string fieldName;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    HTTPHeaderParser();
 | 
			
		||||
 | 
			
		||||
    /// Map of field name (key) to value.
 | 
			
		||||
    std::map<std::string, std::string> fields;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Parse incoming data.
 | 
			
		||||
 | 
			
		||||
    \param data Pointer to character data.
 | 
			
		||||
    \param amt Number of bytes to read.
 | 
			
		||||
    \throws lw::HTTPMalformed if the HTTP is malformed in any way.
 | 
			
		||||
    \returns Number of bytes parsed. If it returns less than the number
 | 
			
		||||
        of bytes given, it has reached the end.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    size_t parse(const char* data, size_t amt);
 | 
			
		||||
 | 
			
		||||
    /// Returns true if parsing is complete.
 | 
			
		||||
    bool completed() const { return state == StateDone; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if parsing failed.
 | 
			
		||||
    bool failed() const { return state == StateError; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief HTTP response parser.
 | 
			
		||||
 | 
			
		||||
This class parses HTTP responses. It uses an HTTPHeaderParser internally
 | 
			
		||||
to deal with the header fields, and has code for dealing with the first
 | 
			
		||||
line of the HTTP response.
 | 
			
		||||
 | 
			
		||||
\warning ASCII only; see also HTTPHeaderParser.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class HTTPResponseParser {
 | 
			
		||||
private:
 | 
			
		||||
    HTTPHeaderParser hp;
 | 
			
		||||
 | 
			
		||||
    enum State {
 | 
			
		||||
        StateHTTPSeq,
 | 
			
		||||
        StateHTTPMajor1,
 | 
			
		||||
        StateHTTPMajor,
 | 
			
		||||
        StateHTTPMinor1,
 | 
			
		||||
        StateHTTPMinor,
 | 
			
		||||
        StateHTTPResp1,
 | 
			
		||||
        StateHTTPResp2,
 | 
			
		||||
        StateHTTPResp3,
 | 
			
		||||
        StateHTTPResp4,
 | 
			
		||||
        StateHTTPMsg,
 | 
			
		||||
        StateHTTPNewline,
 | 
			
		||||
        StateHeader,
 | 
			
		||||
        StateDone,
 | 
			
		||||
        StateError
 | 
			
		||||
    }state;
 | 
			
		||||
 | 
			
		||||
    static const char* HTTPSeq;
 | 
			
		||||
    const char* HTTPSeqPos;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    HTTPResponseParser();
 | 
			
		||||
 | 
			
		||||
    /// HTTP major version number.
 | 
			
		||||
    int versionMajor;
 | 
			
		||||
 | 
			
		||||
    /// HTTP minor version number.
 | 
			
		||||
    int versionMinor;
 | 
			
		||||
 | 
			
		||||
    /// HTTP 3-digit response code.
 | 
			
		||||
    int responseCode;
 | 
			
		||||
 | 
			
		||||
    /// HTTP status message.
 | 
			
		||||
    std::string status;
 | 
			
		||||
 | 
			
		||||
    /// HTTP header fields.
 | 
			
		||||
    std::map<std::string, std::string>& fields;
 | 
			
		||||
 | 
			
		||||
    /*! \brief Parse incoming data.
 | 
			
		||||
 | 
			
		||||
    \param data Pointer to character data.
 | 
			
		||||
    \param amt Number of bytes to read.
 | 
			
		||||
    \throws lw::HTTPMalformed if the HTTP is malformed in any way.
 | 
			
		||||
    \returns Number of bytes parsed. If it returns less than the number
 | 
			
		||||
        of bytes given, it has reached the end.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    size_t parse(const char* data, size_t amt);
 | 
			
		||||
 | 
			
		||||
    /// Returns true if parsing is complete.
 | 
			
		||||
    bool completed() const { return state == StateDone; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if parsing failed.
 | 
			
		||||
    bool failed() const { return state == StateError; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief HTTP request parser.
 | 
			
		||||
 | 
			
		||||
This class parses HTTP requests. It uses an HTTPHeaderParser internally
 | 
			
		||||
to deal with the header fields, and has code for dealing with the first
 | 
			
		||||
line of the HTTP request.
 | 
			
		||||
 | 
			
		||||
\warning ASCII only; see also HTTPHeaderParser.
 | 
			
		||||
\warning URIs are simply copied verbatim and are not validated in any
 | 
			
		||||
    way whatsoever.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class HTTPRequestParser {
 | 
			
		||||
private:
 | 
			
		||||
    HTTPHeaderParser hp;
 | 
			
		||||
 | 
			
		||||
    enum State {
 | 
			
		||||
        StateHTTPMethod1,
 | 
			
		||||
        StateHTTPMethod,
 | 
			
		||||
        StateHTTPUrl1,
 | 
			
		||||
        StateHTTPUrl,
 | 
			
		||||
        StateHTTPSeq,
 | 
			
		||||
        StateHTTPMajor1,
 | 
			
		||||
        StateHTTPMajor,
 | 
			
		||||
        StateHTTPMinor1,
 | 
			
		||||
        StateHTTPMinor,
 | 
			
		||||
        StateHTTPNewline,
 | 
			
		||||
        StateHeader,
 | 
			
		||||
        StateDone,
 | 
			
		||||
        StateError
 | 
			
		||||
    }state;
 | 
			
		||||
 | 
			
		||||
    static const char* HTTPSeq;
 | 
			
		||||
    const char* HTTPSeqPos;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    HTTPRequestParser();
 | 
			
		||||
 | 
			
		||||
    /// HTTP method (POST, GET, etc.).
 | 
			
		||||
    std::string method;
 | 
			
		||||
 | 
			
		||||
    /// URL
 | 
			
		||||
    std::string url;
 | 
			
		||||
 | 
			
		||||
    /// HTTP major version number.
 | 
			
		||||
    int versionMajor;
 | 
			
		||||
 | 
			
		||||
    /// HTTP minor version number.
 | 
			
		||||
    int versionMinor;
 | 
			
		||||
 | 
			
		||||
    /// HTTP header fields.
 | 
			
		||||
    std::map<std::string, std::string>& fields;
 | 
			
		||||
 | 
			
		||||
    /*! \brief Parse incoming data.
 | 
			
		||||
 | 
			
		||||
    \param data Pointer to character data.
 | 
			
		||||
    \param amt Number of bytes to read.
 | 
			
		||||
    \throws lw::HTTPMalformed if the HTTP is malformed in any way.
 | 
			
		||||
    \returns Number of bytes parsed. If it returns less than the number
 | 
			
		||||
        of bytes given, it has reached the end.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    size_t parse(const char* data, size_t amt);
 | 
			
		||||
 | 
			
		||||
    /// Returns true if parsing is complete.
 | 
			
		||||
    bool completed() const { return state == StateDone; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if parsing failed.
 | 
			
		||||
    bool failed() const { return state == StateError; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,629 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Protocols/SMTP.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const char* const SMTPClient::dayName[8] = {
 | 
			
		||||
    "(?) ", "Mon ", "Tue ", "Wed ", "Thu ", "Fri ", "Sat ", "Sun "
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char* const SMTPClient::monthName[13] = {
 | 
			
		||||
    " (?) ", " Jan ", " Feb ", " Mar ", " Apr ", " May ", " Jun ",
 | 
			
		||||
    " Jul ", " Aug ", " Sep ", " Oct ", " Nov ", " Dec "
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t SMTPClient::parserFeedData(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    for(size_t used = 0; used < amt; ++used) {
 | 
			
		||||
        char ch = data[used];
 | 
			
		||||
 | 
			
		||||
        switch(parserState) {
 | 
			
		||||
            case ParserStateDone:
 | 
			
		||||
                throw std::wstring(L"Program logic error [1]");
 | 
			
		||||
 | 
			
		||||
            case ParserStateNone:
 | 
			
		||||
                if(!isdigit(ch)) throw std::wstring(L"Expecting SMTP reply code.");
 | 
			
		||||
                parserCode = 100 * (ch - '0');
 | 
			
		||||
                parserState = ParserStateCode1;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateCode1:
 | 
			
		||||
                if(!isdigit(ch)) throw std::wstring(L"Expecting SMTP reply code.");
 | 
			
		||||
                parserCode += 10 * (ch - '0');
 | 
			
		||||
                parserState = ParserStateCode2;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateCode2:
 | 
			
		||||
                if(!isdigit(ch)) throw std::wstring(L"Expecting SMTP reply code.");
 | 
			
		||||
                parserCode += (ch - '0');
 | 
			
		||||
                parserState = ParserStateSP;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateSP:
 | 
			
		||||
                if(isspace(ch)) parserState = ParserStateData;
 | 
			
		||||
                else if(ch == '-') parserState = ParserStateContData;
 | 
			
		||||
                else throw std::wstring(L"Expecting ' ' or '-' after SMTP reply code.");
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateData:
 | 
			
		||||
                if(ch == '\n') { // bad server!
 | 
			
		||||
                    parserState = ParserStateDone;
 | 
			
		||||
                    return used + 1;
 | 
			
		||||
                }
 | 
			
		||||
                if(ch == '\r') {
 | 
			
		||||
                    parserState = ParserStateLF;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                parserData.append(&ch, 1);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateLF:
 | 
			
		||||
                if(ch != '\n') throw std::wstring(L"Expecting \\r\\n sequence.");
 | 
			
		||||
                parserState = ParserStateDone;
 | 
			
		||||
                return used + 1;
 | 
			
		||||
 | 
			
		||||
            case ParserStateContData:
 | 
			
		||||
                if(ch == '\n') parserState = ParserStateContCode0;
 | 
			
		||||
                else if(ch == '\r') parserState = ParserStateContLF;
 | 
			
		||||
                else parserData.append(&ch, 1);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateContLF:
 | 
			
		||||
                if(ch != '\n') throw std::wstring(L"Expecting \\r\\n sequence.");
 | 
			
		||||
                parserState = ParserStateContCode0;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateContCode0:
 | 
			
		||||
                if((parserCode / 100) != (ch - '0'))
 | 
			
		||||
                    throw std::wstring(L"Mismatched multi-line SMTP reply code.");
 | 
			
		||||
                parserState = ParserStateContCode1;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateContCode1:
 | 
			
		||||
                if(((parserCode % 100) / 10) != (ch - '0'))
 | 
			
		||||
                    throw std::wstring(L"Mismatched multi-line SMTP reply code.");
 | 
			
		||||
                parserState = ParserStateContCode2;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case ParserStateContCode2:
 | 
			
		||||
                if((parserCode % 10) != (ch - '0'))
 | 
			
		||||
                    throw std::wstring(L"Mismatched multi-line SMTP reply code.");
 | 
			
		||||
                parserState = ParserStateSP;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return amt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::parserReset()
 | 
			
		||||
{
 | 
			
		||||
    parserState = ParserStateNone;
 | 
			
		||||
    parserCode = 0;
 | 
			
		||||
    parserData.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool SMTPClient::ioReady(uint32_t flags)
 | 
			
		||||
{
 | 
			
		||||
    if(flags & (IOEventUrgent | IOEventHUP)) {
 | 
			
		||||
        state = StateDone;
 | 
			
		||||
        logFail(L"Urgent data received / remote hangup");
 | 
			
		||||
        return IOListenResultDelete;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        if(flags & IOEventError) netClient.checkErrors();
 | 
			
		||||
 | 
			
		||||
        while(tryRead() | tryWrite())
 | 
			
		||||
            ;
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        logFail(e);
 | 
			
		||||
        delete this;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    catch(std::wstring& e) {
 | 
			
		||||
        logFail(e);
 | 
			
		||||
        delete this;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(state == StateDone) {
 | 
			
		||||
        delete this;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool SMTPClient::tryRead() // returns true if I/O happens
 | 
			
		||||
{
 | 
			
		||||
    bool ioHappened = false;
 | 
			
		||||
    while(true) {
 | 
			
		||||
        size_t amt = inBuffer.getRemaining();
 | 
			
		||||
        amt = netClient.read(inBuffer.getBuffer(amt), amt);
 | 
			
		||||
        inBuffer.addedData(amt);
 | 
			
		||||
        if(!amt) return ioHappened;
 | 
			
		||||
        ioHappened = true;
 | 
			
		||||
        logRead(inBuffer.getData(), inBuffer.getSize());
 | 
			
		||||
 | 
			
		||||
        while(!inBuffer.isEmpty()) {
 | 
			
		||||
            // parse data
 | 
			
		||||
            inBuffer.getData(parserFeedData(inBuffer.getData(),
 | 
			
		||||
                inBuffer.getSize()));
 | 
			
		||||
            if(parserState != ParserStateDone) break;
 | 
			
		||||
 | 
			
		||||
#define FAIL() do { \
 | 
			
		||||
    logFail(parserCode, parserData); \
 | 
			
		||||
    state = StateDone; \
 | 
			
		||||
    return true; \
 | 
			
		||||
}while(0)
 | 
			
		||||
 | 
			
		||||
#define WRITE(x) do { \
 | 
			
		||||
    writePtr = x.c_str(); \
 | 
			
		||||
    writeAmt = x.size(); \
 | 
			
		||||
}while(0)
 | 
			
		||||
 | 
			
		||||
            switch(state) {
 | 
			
		||||
                case StateDone:
 | 
			
		||||
                    throw std::wstring(L"Program logic error [2].");
 | 
			
		||||
 | 
			
		||||
                case StateNone:
 | 
			
		||||
                    if((parserCode / 10) != 22) FAIL();
 | 
			
		||||
                    else {
 | 
			
		||||
                        state = StateHelo;
 | 
			
		||||
                        outBuffer = "helo ";
 | 
			
		||||
                        outBuffer.append(hostName);
 | 
			
		||||
                        outBuffer.append("\r\n");
 | 
			
		||||
                        WRITE(outBuffer);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case StateHelo:
 | 
			
		||||
                    if((parserCode / 10) != 25) FAIL();
 | 
			
		||||
                    else {
 | 
			
		||||
                        state = StateMailFrom;
 | 
			
		||||
                        outBuffer = "mail from: ";
 | 
			
		||||
                        outBuffer.append(mailFrom);
 | 
			
		||||
                        outBuffer.append("\r\n");
 | 
			
		||||
                        WRITE(outBuffer);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case StateMailFrom:
 | 
			
		||||
                    if((parserCode / 10) != 25) FAIL();
 | 
			
		||||
                    else {
 | 
			
		||||
                        state = StateRcptTo;
 | 
			
		||||
                        outBuffer = "rcpt to: ";
 | 
			
		||||
                        outBuffer.append(rcptTo);
 | 
			
		||||
                        outBuffer.append("\r\n");
 | 
			
		||||
                        WRITE(outBuffer);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case StateRcptTo:
 | 
			
		||||
                    if((parserCode / 10) != 25) FAIL();
 | 
			
		||||
                    else {
 | 
			
		||||
                        state = StateData;
 | 
			
		||||
                        outBuffer = "data\r\n";
 | 
			
		||||
                        WRITE(outBuffer);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case StateData:
 | 
			
		||||
                    if((parserCode / 10) != 35) FAIL();
 | 
			
		||||
                    else {
 | 
			
		||||
                        state = StateQuit;
 | 
			
		||||
                        WRITE(data);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case StateQuit:
 | 
			
		||||
                    if((parserCode / 10) != 25) FAIL();
 | 
			
		||||
                    else {
 | 
			
		||||
                        state = StateFinal;
 | 
			
		||||
                        outBuffer = "quit\r\n";
 | 
			
		||||
                        WRITE(outBuffer);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case StateFinal:
 | 
			
		||||
                    if((parserCode / 10) != 22) FAIL();
 | 
			
		||||
                    else {
 | 
			
		||||
                        state = StateDone;
 | 
			
		||||
                        logDone();
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
#undef WRITE
 | 
			
		||||
#undef FAIL
 | 
			
		||||
 | 
			
		||||
            // reset parser and go again
 | 
			
		||||
            parserReset();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool SMTPClient::tryWrite()
 | 
			
		||||
{
 | 
			
		||||
    bool ioHappened = false;
 | 
			
		||||
    if(!writePtr) return false;
 | 
			
		||||
 | 
			
		||||
    while(writeAmt) {
 | 
			
		||||
        size_t amt = netClient.write(writePtr, writeAmt);
 | 
			
		||||
        if(!amt) return ioHappened;
 | 
			
		||||
        ioHappened = true;
 | 
			
		||||
 | 
			
		||||
        logWrite(writePtr, amt);
 | 
			
		||||
        writeAmt -= amt;
 | 
			
		||||
        writePtr += amt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    writePtr = 0;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool SMTPClient::isWhitespace(char ch)
 | 
			
		||||
{
 | 
			
		||||
    return (ch >= 0x9 && ch <= 0xD) || ch == 0x20;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::string SMTPClient::wsFieldName(const std::string& fieldName)
 | 
			
		||||
{
 | 
			
		||||
    // strip whitespace from beginning and end
 | 
			
		||||
    std::string::size_type pos, len = fieldName.length();
 | 
			
		||||
    for(pos = 0; pos < len; ++pos) if(!isWhitespace(fieldName[pos])) break;
 | 
			
		||||
    if(pos == len) return std::string();
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type endPos = len - 1;
 | 
			
		||||
    while(isWhitespace(fieldName[endPos])) --endPos;
 | 
			
		||||
 | 
			
		||||
    std::string str = fieldName.substr(pos, endPos - pos + 1);
 | 
			
		||||
 | 
			
		||||
    // return empty string if there are any illegal chars, and convert
 | 
			
		||||
    // to lowercase
 | 
			
		||||
    for(pos = 0, len = str.length(); pos < len; ++pos) {
 | 
			
		||||
        if(str[pos] <= 0x20 || str[pos] & ~0x7F) return std::string();
 | 
			
		||||
        str[pos] = tolower(str[pos]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // done
 | 
			
		||||
    return str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::string SMTPClient::wsFieldValue(const std::string& fieldValue)
 | 
			
		||||
{
 | 
			
		||||
    // strip whitespace from beginning and end
 | 
			
		||||
    std::string::size_type pos, len = fieldValue.length();
 | 
			
		||||
    for(pos = 0; pos < len; ++pos) if(!isWhitespace(fieldValue[pos])) break;
 | 
			
		||||
    if(pos == len) return std::string();
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type endPos = len - 1;
 | 
			
		||||
    while(isWhitespace(fieldValue[endPos])) --endPos;
 | 
			
		||||
 | 
			
		||||
    std::string str = fieldValue.substr(pos, endPos - pos + 1);
 | 
			
		||||
 | 
			
		||||
    // convert remaining whitespace
 | 
			
		||||
    for(pos = 0, len = str.length(); pos < len; ++pos)
 | 
			
		||||
        if(str[pos] == '\r' || str[pos] == '\n')
 | 
			
		||||
            str[pos] = ' ';
 | 
			
		||||
 | 
			
		||||
    // done
 | 
			
		||||
    return str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::string SMTPClient::buildRFC2822(const std::string& hostName,
 | 
			
		||||
    const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
    const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
    const std::string& message)
 | 
			
		||||
{
 | 
			
		||||
    (void)hostName; // TODO: add message-id
 | 
			
		||||
    std::ostringstream o;
 | 
			
		||||
 | 
			
		||||
    // first, deal with headers (sanitise as we go)
 | 
			
		||||
    bool seenDate = false, seenFrom = false, seenTo = false;
 | 
			
		||||
 | 
			
		||||
    std::multimap<std::string, std::string>::const_iterator
 | 
			
		||||
        iter = headerLines.begin(), end = headerLines.end();
 | 
			
		||||
    for(; iter != end; ++iter) {
 | 
			
		||||
        std::string fieldName = wsFieldName(iter->first);
 | 
			
		||||
        std::string fieldValue = wsFieldValue(iter->second);
 | 
			
		||||
 | 
			
		||||
        if(fieldName.empty()) continue;
 | 
			
		||||
        if(fieldName == "date") seenDate = true;
 | 
			
		||||
        if(fieldName == "from") seenFrom = true;
 | 
			
		||||
        if(fieldName == "to") seenTo = true;
 | 
			
		||||
        if(fieldValue.empty()) continue;
 | 
			
		||||
 | 
			
		||||
        o << fieldName << ": " << fieldValue << "\r\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(!seenDate) {
 | 
			
		||||
        lw::DateTime date = lw::DateTime::now();
 | 
			
		||||
        o << std::setfill('0')
 | 
			
		||||
          << "Date: "
 | 
			
		||||
          << dayName[date.getDate().getWeekDay()]
 | 
			
		||||
          << date.getDate().getDay()
 | 
			
		||||
          << monthName[date.getDate().getMonth()]
 | 
			
		||||
          << date.getDate().getYear()
 | 
			
		||||
          << ' '
 | 
			
		||||
          << std::setw(2) << date.getTime().h() << ':'
 | 
			
		||||
          << std::setw(2) << date.getTime().m() << ':'
 | 
			
		||||
          << std::setw(2) << date.getTime().s()
 | 
			
		||||
          << " +0000" // FIXME
 | 
			
		||||
          << "\r\n"
 | 
			
		||||
          << std::setfill(' ');
 | 
			
		||||
    }
 | 
			
		||||
    if(!seenFrom) o << "From: " << wsFieldValue(mailFrom) << "\r\n";
 | 
			
		||||
    if(!seenTo) o << "To: " << wsFieldValue(rcptTo) << "\r\n";
 | 
			
		||||
 | 
			
		||||
    o << "\r\n";
 | 
			
		||||
 | 
			
		||||
    // now output the escaped message
 | 
			
		||||
    bool wasNewline = true;
 | 
			
		||||
    for(std::string::size_type pos = 0, len = message.size(); pos < len; ++pos) {
 | 
			
		||||
        char ch = message[pos];
 | 
			
		||||
        switch(ch) {
 | 
			
		||||
            case '\r':
 | 
			
		||||
                ch = ' ';
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case '\n':
 | 
			
		||||
                o << "\r\n";
 | 
			
		||||
                wasNewline = true;
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            case '.':
 | 
			
		||||
                if(wasNewline) o.put('.');
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        o.put(ch);
 | 
			
		||||
        wasNewline = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    o << "\r\n.\r\n";
 | 
			
		||||
 | 
			
		||||
    // done
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SMTPClient::SMTPClient(const NetAddress& netAddress, uint16_t port,
 | 
			
		||||
    EventManager& eventManager, const std::string& hostName,
 | 
			
		||||
    const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
    const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
    const std::string& message, CompletionNotifier* completionNotifier)
 | 
			
		||||
  : completionNotifier(completionNotifier),
 | 
			
		||||
    hostName(wsFieldValue(hostName)),
 | 
			
		||||
    mailFrom(wsFieldValue(mailFrom)),
 | 
			
		||||
    rcptTo(wsFieldValue(rcptTo)),
 | 
			
		||||
    data(buildRFC2822(hostName, mailFrom, rcptTo, headerLines, message)),
 | 
			
		||||
    state(StateNone), inBuffer(1024, 1024), writePtr(0), writeAmt(0),
 | 
			
		||||
    parserState(ParserStateNone), parserCode(0),
 | 
			
		||||
    port(port), netAddress(netAddress.clone())
 | 
			
		||||
{
 | 
			
		||||
    // NOTE: we can't use any of the log* functions in the constructor,
 | 
			
		||||
    // because the virtual functions won't be pointing to the derived
 | 
			
		||||
    // class yet. We'll have to wait until we're in the I/O callback to
 | 
			
		||||
    // log anything.
 | 
			
		||||
 | 
			
		||||
    // set up client
 | 
			
		||||
    netClient.connect(netAddress, port);
 | 
			
		||||
    eventManager.registerDevice(&netClient, this, ~0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SMTPClient::~SMTPClient()
 | 
			
		||||
{
 | 
			
		||||
    if(completionNotifier) completionNotifier->signal();
 | 
			
		||||
    delete netAddress;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::sendMail(const NetAddress& netAddress, uint16_t port,
 | 
			
		||||
    EventManager& eventManager, const std::string& hostName,
 | 
			
		||||
    const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
    const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
    const std::string& message, CompletionNotifier* completionNotifier)
 | 
			
		||||
{
 | 
			
		||||
    new SMTPClient(netAddress, port, eventManager, hostName,
 | 
			
		||||
        mailFrom, rcptTo, headerLines, message, completionNotifier);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::logStart(const std::string&,
 | 
			
		||||
    const std::string&, const std::string&,
 | 
			
		||||
    const std::string&)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::logDone()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::logRead(const char*, size_t)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::logWrite(const char*, size_t)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::logFail(int, const std::string&)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::logFail(Exception&)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void SMTPClient::logFail(const std::wstring&)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int LoggingSMTPClient::instanceCount = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LoggingSMTPClient::LoggingSMTPClient(const NetAddress& netAddress,
 | 
			
		||||
    uint16_t port,
 | 
			
		||||
    EventManager& eventManager, const std::string& hostName,
 | 
			
		||||
    const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
    const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
    const std::string& message, Log& logger, Log& logError,
 | 
			
		||||
    CompletionNotifier* completionNotifier)
 | 
			
		||||
  : SMTPClient(netAddress, port, eventManager, hostName, mailFrom, rcptTo,
 | 
			
		||||
               headerLines, message, completionNotifier),
 | 
			
		||||
    logger(logger), logError(logError)
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
    o << L"SMTP <"
 | 
			
		||||
      << ++instanceCount
 | 
			
		||||
      << L">, "
 | 
			
		||||
      << netAddress.toString()
 | 
			
		||||
      << L" port "
 | 
			
		||||
      << port;
 | 
			
		||||
    logID = o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LoggingSMTPClient::~LoggingSMTPClient()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void LoggingSMTPClient::sendMail(const NetAddress& netAddress,
 | 
			
		||||
    uint16_t port,
 | 
			
		||||
    EventManager& eventManager, const std::string& hostName,
 | 
			
		||||
    const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
    const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
    const std::string& message, Log& logger, Log& logError,
 | 
			
		||||
    CompletionNotifier* completionNotifier)
 | 
			
		||||
{
 | 
			
		||||
    new LoggingSMTPClient(netAddress, port, eventManager, hostName,
 | 
			
		||||
        mailFrom, rcptTo, headerLines, message,
 | 
			
		||||
        logger, logError, completionNotifier);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void LoggingSMTPClient::logStart(const std::string& hostName,
 | 
			
		||||
    const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
    const std::string& data)
 | 
			
		||||
{
 | 
			
		||||
    logger << Log::start(logID)
 | 
			
		||||
           << L"Sending a new email:"
 | 
			
		||||
           << L"\n  Host: " << hostName
 | 
			
		||||
           << L"\n  From: " << mailFrom
 | 
			
		||||
           << L"\n  To  : " << rcptTo
 | 
			
		||||
           << L"\n\n"
 | 
			
		||||
           << Log::asciiDump(data.c_str(), data.size())
 | 
			
		||||
           << Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void LoggingSMTPClient::logDone()
 | 
			
		||||
{
 | 
			
		||||
    logger << Log::start(logID)
 | 
			
		||||
           << L"Email sent successfully."
 | 
			
		||||
           << Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void LoggingSMTPClient::logRead(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    logger << Log::start(logID)
 | 
			
		||||
           << L"Incoming data.\n\n"
 | 
			
		||||
           << Log::asciiDump(data, amt)
 | 
			
		||||
           << Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void LoggingSMTPClient::logWrite(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    logger << Log::start(logID)
 | 
			
		||||
           << L"Wrote data.\n\n"
 | 
			
		||||
           << Log::asciiDump(data, amt)
 | 
			
		||||
           << Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void LoggingSMTPClient::logFail(int smtpCode, const std::string& errorString)
 | 
			
		||||
{
 | 
			
		||||
    logError << Log::start(logID)
 | 
			
		||||
             << L"SMTP error."
 | 
			
		||||
                L"\n  Code  : " << smtpCode
 | 
			
		||||
             << L"\n  Reason: " << errorString
 | 
			
		||||
             << Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void LoggingSMTPClient::logFail(Exception& e)
 | 
			
		||||
{
 | 
			
		||||
    logError << Log::start(logID)
 | 
			
		||||
             << L"System error.\n"
 | 
			
		||||
             << e
 | 
			
		||||
             << Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void LoggingSMTPClient::logFail(const std::wstring& s)
 | 
			
		||||
{
 | 
			
		||||
    logError << Log::start(logID)
 | 
			
		||||
             << L"Communication failed: "
 | 
			
		||||
             << s
 | 
			
		||||
             << Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,287 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Protocols/SMTP.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief An SMTP (email) client class.
 | 
			
		||||
 | 
			
		||||
This class can be used to perform an SMTP transaction. It will send one
 | 
			
		||||
email. It is used in an asynchronous manner, and can (optionally) signal
 | 
			
		||||
completion through a CompletionNotifier object.
 | 
			
		||||
 | 
			
		||||
\warning You should only send ASCII characters through this client, but
 | 
			
		||||
    it does not perform any checking to enforce this.
 | 
			
		||||
 | 
			
		||||
\todo Timeout options.
 | 
			
		||||
\todo Split SMTP response parser into its own class.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class SMTPClient : private EventCallbackIO {
 | 
			
		||||
private:
 | 
			
		||||
    // for performing I/O
 | 
			
		||||
    CompletionNotifier* completionNotifier;
 | 
			
		||||
    NetClientTCP netClient;
 | 
			
		||||
    virtual bool ioReady(uint32_t flags);
 | 
			
		||||
    bool tryRead();
 | 
			
		||||
    bool tryWrite();
 | 
			
		||||
 | 
			
		||||
    // data
 | 
			
		||||
    const std::string hostName, mailFrom, rcptTo, data;
 | 
			
		||||
 | 
			
		||||
    // builds an RFC2822-compliant message
 | 
			
		||||
    static const char* const dayName[8];
 | 
			
		||||
    static const char* const monthName[13];
 | 
			
		||||
    static std::string buildRFC2822(const std::string& hostName,
 | 
			
		||||
        const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
        const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
        const std::string& message);
 | 
			
		||||
 | 
			
		||||
    static bool isWhitespace(char ch);
 | 
			
		||||
    static std::string wsFieldName(const std::string& fieldName);
 | 
			
		||||
    static std::string wsFieldValue(const std::string& fieldValue);
 | 
			
		||||
 | 
			
		||||
    // transaction state
 | 
			
		||||
    enum State {
 | 
			
		||||
        StateNone,
 | 
			
		||||
        StateHelo,
 | 
			
		||||
        StateMailFrom,
 | 
			
		||||
        StateRcptTo,
 | 
			
		||||
        StateData,
 | 
			
		||||
        StateQuit,
 | 
			
		||||
        StateFinal,
 | 
			
		||||
        StateDone
 | 
			
		||||
    }state;
 | 
			
		||||
    FIFO inBuffer;
 | 
			
		||||
    std::string outBuffer;
 | 
			
		||||
    const char* writePtr;
 | 
			
		||||
    size_t writeAmt;
 | 
			
		||||
 | 
			
		||||
    // RFC 2821 parser
 | 
			
		||||
    size_t parserFeedData(const char* data, size_t amt);
 | 
			
		||||
    void parserReset();
 | 
			
		||||
    enum ParserState {
 | 
			
		||||
        ParserStateNone,
 | 
			
		||||
        ParserStateCode1,
 | 
			
		||||
        ParserStateCode2,
 | 
			
		||||
        ParserStateSP,
 | 
			
		||||
        ParserStateData,
 | 
			
		||||
        ParserStateLF,
 | 
			
		||||
        ParserStateContData,
 | 
			
		||||
        ParserStateContLF,
 | 
			
		||||
        ParserStateContCode0,
 | 
			
		||||
        ParserStateContCode1,
 | 
			
		||||
        ParserStateContCode2,
 | 
			
		||||
        ParserStateDone
 | 
			
		||||
    }parserState;
 | 
			
		||||
    int parserCode;
 | 
			
		||||
    std::string parserData;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /// TCP port to connect to.
 | 
			
		||||
    uint16_t port;
 | 
			
		||||
 | 
			
		||||
    /// Host to connect to.
 | 
			
		||||
    NetAddress* netAddress;
 | 
			
		||||
 | 
			
		||||
    /// Protected constructor for "fire-and-forget".
 | 
			
		||||
    SMTPClient(const NetAddress& netAddress, uint16_t port,
 | 
			
		||||
        EventManager& eventManager, const std::string& hostName,
 | 
			
		||||
        const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
        const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
        const std::string& message, CompletionNotifier* completionNotifier);
 | 
			
		||||
 | 
			
		||||
    /// Log the start of a new message.
 | 
			
		||||
    virtual void logStart(const std::string& hostName,
 | 
			
		||||
        const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
        const std::string& data);
 | 
			
		||||
 | 
			
		||||
    /// Log message complete.
 | 
			
		||||
    virtual void logDone();
 | 
			
		||||
 | 
			
		||||
    /// Log a data transfer (input).
 | 
			
		||||
    virtual void logRead(const char* data, size_t amt);
 | 
			
		||||
 | 
			
		||||
    /// Log a data transfer (output).
 | 
			
		||||
    virtual void logWrite(const char* data, size_t amt);
 | 
			
		||||
 | 
			
		||||
    /// Log an SMTP error.
 | 
			
		||||
    virtual void logFail(int smtpCode, const std::string& errorString);
 | 
			
		||||
 | 
			
		||||
    /// Log a connection error.
 | 
			
		||||
    virtual void logFail(Exception& e);
 | 
			
		||||
 | 
			
		||||
    /// Log a communications error.
 | 
			
		||||
    virtual void logFail(const std::wstring& s);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~SMTPClient();
 | 
			
		||||
 | 
			
		||||
    /*! \brief Begins sending email.
 | 
			
		||||
 | 
			
		||||
    \param netAddress The address of the host to connect to.
 | 
			
		||||
    \param port The TCP port number of the SMTP service.
 | 
			
		||||
    \param eventManager The event manager to use.
 | 
			
		||||
    \param hostName The name of the host to present to the SMTP server.
 | 
			
		||||
    \param mailFrom Email address of the (SMTP envelope) sender.
 | 
			
		||||
    \param rcptTo Email address of the (SMTP envelope) receiver.
 | 
			
		||||
    \param headerLines The header lines to use in the email. These will
 | 
			
		||||
        be properly escaped, whitespace stripped, etc.
 | 
			
		||||
    \param message The actual message to send. It will be properly
 | 
			
		||||
        escaped.
 | 
			
		||||
    \param completionNotifier The CompletionNotifier object to use to
 | 
			
		||||
        signal when the process is complete. May be left as 0 in which
 | 
			
		||||
        case no signal will be issued.
 | 
			
		||||
    \throws Exception if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    The constructor will create a properly-formatted email, and begin
 | 
			
		||||
    connecting to the SMTP server.
 | 
			
		||||
 | 
			
		||||
    Several standard header lines will be automatically filled out for
 | 
			
		||||
    you. You may override this by providing values for them in the
 | 
			
		||||
    \a headerLines parameter. These headers are:
 | 
			
		||||
     - \a orig-date (set to current date).
 | 
			
		||||
     - \a from (set to the SMTP envelope \a mailFrom).
 | 
			
		||||
     - \a to (set to the SMTP envelope \a rcptTo).
 | 
			
		||||
 | 
			
		||||
    Blank value strings in the \a headerLines array will cause the
 | 
			
		||||
    corresponding header line \e not to be generated. Blank key strings
 | 
			
		||||
    will be ignored. Whitespace will be stripped from all key strings.
 | 
			
		||||
    Key strings are case insensitive.
 | 
			
		||||
 | 
			
		||||
    Rules for whitespace handling are:
 | 
			
		||||
     - all ASCII CR characters are replaced with an ASCII space;
 | 
			
		||||
     - all ASCII LF characters in header fields are replaced with an
 | 
			
		||||
       ASCII space;
 | 
			
		||||
     - any remaining whitespace in field names causes the field to be
 | 
			
		||||
       skipped;
 | 
			
		||||
     - otherwise, whitespace is left verbatim.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static void sendMail(const NetAddress& netAddress, uint16_t port,
 | 
			
		||||
        EventManager& eventManager, const std::string& hostName,
 | 
			
		||||
        const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
        const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
        const std::string& message, CompletionNotifier* completionNotifier);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief A logging SMTP (email) client class.
 | 
			
		||||
 | 
			
		||||
This class can be used to perform an SMTP transaction. It will send one
 | 
			
		||||
email. It is used in a "fire and forget" manner, and there is no way to
 | 
			
		||||
determine the outcome of sending an email (except through the logging
 | 
			
		||||
functions).
 | 
			
		||||
 | 
			
		||||
This class will log its actions to the specified log objects.
 | 
			
		||||
 | 
			
		||||
\warning You should only send ASCII characters through this client, but
 | 
			
		||||
    it does not perform any checking to enforce this.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class LoggingSMTPClient : public SMTPClient {
 | 
			
		||||
private:
 | 
			
		||||
    static int instanceCount;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /// This email's unique ID, for logging.
 | 
			
		||||
    std::wstring logID;
 | 
			
		||||
 | 
			
		||||
    /// Log object.
 | 
			
		||||
    Log& logger, & logError;
 | 
			
		||||
 | 
			
		||||
    /// Protected constructor for "fire-and-forget".
 | 
			
		||||
    LoggingSMTPClient(const NetAddress& netAddress, uint16_t port,
 | 
			
		||||
        EventManager& eventManager, const std::string& hostName,
 | 
			
		||||
        const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
        const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
        const std::string& message, Log& logger, Log& logError,
 | 
			
		||||
        CompletionNotifier* completionNotifier);
 | 
			
		||||
 | 
			
		||||
    /// Log the start of a new message.
 | 
			
		||||
    virtual void logStart(const std::string& hostName,
 | 
			
		||||
        const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
        const std::string& data);
 | 
			
		||||
 | 
			
		||||
    /// Log message complete.
 | 
			
		||||
    virtual void logDone();
 | 
			
		||||
 | 
			
		||||
    /// Log a data transfer (input).
 | 
			
		||||
    virtual void logRead(const char* data, size_t amt);
 | 
			
		||||
 | 
			
		||||
    /// Log a data transfer (output).
 | 
			
		||||
    virtual void logWrite(const char* data, size_t amt);
 | 
			
		||||
 | 
			
		||||
    /// Log an SMTP error.
 | 
			
		||||
    virtual void logFail(int smtpCode, const std::string& errorString);
 | 
			
		||||
 | 
			
		||||
    /// Log a connection error.
 | 
			
		||||
    virtual void logFail(Exception& e);
 | 
			
		||||
 | 
			
		||||
    /// Log a communications error.
 | 
			
		||||
    virtual void logFail(const std::wstring& s);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~LoggingSMTPClient();
 | 
			
		||||
 | 
			
		||||
    /*! \brief Begins sending email, with logging.
 | 
			
		||||
 | 
			
		||||
    \param netAddress The address of the host to connect to.
 | 
			
		||||
    \param port The TCP port number of the SMTP service.
 | 
			
		||||
    \param eventManager The event manager to use.
 | 
			
		||||
    \param hostName The name of the host to present to the SMTP server.
 | 
			
		||||
    \param mailFrom Email address of the (SMTP envelope) sender.
 | 
			
		||||
    \param rcptTo Email address of the (SMTP envelope) receiver.
 | 
			
		||||
    \param headerLines The header lines to use in the email. These will
 | 
			
		||||
        be properly escaped, whitespace stripped, etc.
 | 
			
		||||
    \param message The actual message to send. It will be properly
 | 
			
		||||
        escaped.
 | 
			
		||||
    \param logger The lw::Log object to use.
 | 
			
		||||
    \param logError The lw::Log object to use to log errors.
 | 
			
		||||
    \param completionNotifier The CompletionNotifier object to use to
 | 
			
		||||
        signal when the process is complete. May be left as 0 in which
 | 
			
		||||
        case no signal will be issued.
 | 
			
		||||
    \throws Exception if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    The constructor will create a properly-formatted email, and begin
 | 
			
		||||
    connecting to the SMTP server.
 | 
			
		||||
 | 
			
		||||
    Several standard header lines will be automatically filled out for
 | 
			
		||||
    you. You may override this by providing values for them in the
 | 
			
		||||
    \a headerLines parameter. These headers are:
 | 
			
		||||
     - \a orig-date (set to current date).
 | 
			
		||||
     - \a from (set to the SMTP envelope \a mailFrom).
 | 
			
		||||
     - \a to (set to the SMTP envelope \a rcptTo).
 | 
			
		||||
 | 
			
		||||
    Blank value strings in the \a headerLines array will cause the
 | 
			
		||||
    corresponding header line \e not to be generated. Blank key strings
 | 
			
		||||
    will be ignored. Whitespace will be stripped from all key strings.
 | 
			
		||||
    Key strings are case insensitive.
 | 
			
		||||
 | 
			
		||||
    Rules for whitespace handling are:
 | 
			
		||||
     - all ASCII CR characters are replaced with an ASCII space;
 | 
			
		||||
     - all ASCII LF characters in header fields are replaced with an
 | 
			
		||||
       ASCII space;
 | 
			
		||||
     - any remaining whitespace in field names causes the field to be
 | 
			
		||||
       skipped;
 | 
			
		||||
     - otherwise, whitespace is left verbatim.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static void sendMail(const NetAddress& netAddress, uint16_t port,
 | 
			
		||||
        EventManager& eventManager, const std::string& hostName,
 | 
			
		||||
        const std::string& mailFrom, const std::string& rcptTo,
 | 
			
		||||
        const std::multimap<std::string, std::string>& headerLines,
 | 
			
		||||
        const std::string& message, Log& logger, Log& logError,
 | 
			
		||||
        CompletionNotifier* completionNotifier);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Server.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetServer::NetServer()
 | 
			
		||||
    : nsCallback(0), eventManager(0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetServer::~NetServer()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetServer::Protocol NetServer::getProtocol(const NetAddress& addr)
 | 
			
		||||
{
 | 
			
		||||
    const NetAddress* a = &addr;
 | 
			
		||||
    if(dynamic_cast<const NetAddressIPv4*>(a)) return IPv4;
 | 
			
		||||
    if(dynamic_cast<const NetAddressIPv6*>(a)) return IPv6;
 | 
			
		||||
    throw UnknownProtocol();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetServer::registerCallback(EventManager& eventManager,
 | 
			
		||||
    EventCallbackNetServer& callback)
 | 
			
		||||
{
 | 
			
		||||
    clearCallback();
 | 
			
		||||
    nsCallback = &callback;
 | 
			
		||||
    this->eventManager = &eventManager;
 | 
			
		||||
    eventManager.registerDevice(this, this, IOEventRead | IOEventError);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetServer::clearCallback()
 | 
			
		||||
{
 | 
			
		||||
    if(eventManager) eventManager->ignoreDevice(this);
 | 
			
		||||
    eventManager = 0;
 | 
			
		||||
    nsCallback = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetServer::ioReady(uint32_t flags) throw()
 | 
			
		||||
{
 | 
			
		||||
    // TODO -- this really needs sorting somehow, since we no longer support
 | 
			
		||||
    // error handling in the event loop
 | 
			
		||||
#if 0
 | 
			
		||||
    // deal with errors
 | 
			
		||||
    try {
 | 
			
		||||
        if(flags & IOEventError) if(!nsCallback->error()) return false;
 | 
			
		||||
    }
 | 
			
		||||
    catch(...) { }
 | 
			
		||||
 | 
			
		||||
    // deal with waiting connections
 | 
			
		||||
    if(flags & IOEventRead) {
 | 
			
		||||
        IODevice* netClient = 0;
 | 
			
		||||
        while(true) {
 | 
			
		||||
            if(!(netClient = accept())) break;
 | 
			
		||||
            try {
 | 
			
		||||
                if(!nsCallback->accept(netClient)) return false;
 | 
			
		||||
            }
 | 
			
		||||
            catch(...) { }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetServer::readBlock(int timeout, IOTimeoutMode timeoutMode)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        int timeout_remaining = timeout;
 | 
			
		||||
        struct pollfd ufd = { fd, POLLIN, 0 };
 | 
			
		||||
        IOCountdown countdown(this, L"readBlock()", timeout, timeoutMode);
 | 
			
		||||
 | 
			
		||||
        while(true) {
 | 
			
		||||
            switch(poll(&ufd, 1, timeout_remaining)) {
 | 
			
		||||
            case -1:
 | 
			
		||||
                if(errno == EINTR) break;
 | 
			
		||||
                throw SystemError().chain(L"poll()");
 | 
			
		||||
 | 
			
		||||
            case 0:
 | 
			
		||||
                throw IOTimeout(this, L"acceptBlocking", timeout, timeoutMode, 0);
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                // perform read
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // update timeout
 | 
			
		||||
            if(!countdown.update(timeout_remaining))
 | 
			
		||||
                throw IOTimeout(this, L"acceptBlocking", timeout, timeoutMode, 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"IOPosixDevice::readBlock()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,155 @@
 | 
			
		|||
/* lw-support/src/lib/Net/Server.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Base class for all network servers.
 | 
			
		||||
 | 
			
		||||
This is the base class for any network server. It provides two main
 | 
			
		||||
functions: you can call accept() (non-blocking) to accept any waiting
 | 
			
		||||
connection; or you can register a callback with an EventManager. Your
 | 
			
		||||
callback will get error and new connection events.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class NetServer
 | 
			
		||||
  : protected IOPosixDevice,
 | 
			
		||||
    private EventCallbackIO
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    // callback stuff
 | 
			
		||||
    EventCallbackNetServer* nsCallback;
 | 
			
		||||
    EventManager* eventManager;
 | 
			
		||||
	virtual void ioReady(uint32_t flags) throw(); // from EventCallbackIO()
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Protocol-specific implementation ////////////////////////
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Exception used for an unknown protocol.
 | 
			
		||||
    class UnknownProtocol : public ProgramException { };
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /// Enumeration representing understood protocols.
 | 
			
		||||
    enum Protocol {
 | 
			
		||||
        IPv4,
 | 
			
		||||
        IPv6
 | 
			
		||||
    }protocol;
 | 
			
		||||
 | 
			
		||||
    /*! \brief Get an address's protocol type.
 | 
			
		||||
 | 
			
		||||
    \param addr The address to inquire about.
 | 
			
		||||
    \throws UnknownProtocol if the protocol is entirely unknown.
 | 
			
		||||
    \returns The type of the address.
 | 
			
		||||
 | 
			
		||||
    This function can be used to ask what type of protocol an address
 | 
			
		||||
    refers to. This isn't a virtual function in NetAddress because all
 | 
			
		||||
    the behaviour changing functionality is (for the time being at
 | 
			
		||||
    least) going to be in this class.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static Protocol getProtocol(const NetAddress& addr);
 | 
			
		||||
 | 
			
		||||
    /*! \brief Block until there's a connection available.
 | 
			
		||||
 | 
			
		||||
    \param timeout Timeout period, in milliseconds. 0 means immediate;
 | 
			
		||||
        negative means forever.
 | 
			
		||||
    \param timeoutMode The timeout mode.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws IOTimeout if a timeout occurs.
 | 
			
		||||
    \returns A newly-allocated I/O device object.
 | 
			
		||||
 | 
			
		||||
    This function can be called from within acceptBlocking(). Its purpose is to aid the
 | 
			
		||||
    implementation of that function. It will wait until there is data available to read on the
 | 
			
		||||
    associated fd.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void readBlock(int timeout = -1, IOTimeoutMode timeoutMode = IOTimeoutHardFull);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Constructors etc. ///////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Constructor. Does nothing.
 | 
			
		||||
    NetServer();
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~NetServer();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Callback system /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Called to retrieve a connection (non-blocking).
 | 
			
		||||
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \returns A newly-allocated I/O device object.
 | 
			
		||||
    \retval 0 if there is no connection waiting.
 | 
			
		||||
 | 
			
		||||
    This function is called whenever you want to accept a new
 | 
			
		||||
    connection. It either creates a new I/O device object or returns 0
 | 
			
		||||
    if there are no connections waiting.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual lw::NetClient* accept() = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Called to retrieve a connection (blocking).
 | 
			
		||||
 | 
			
		||||
    \param timeout Timeout period, in milliseconds. 0 means immediate;
 | 
			
		||||
        negative means forever.
 | 
			
		||||
    \param timeoutMode The timeout mode.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws IOTimeout if a timeout occurs.
 | 
			
		||||
    \returns A newly-allocated I/O device object.
 | 
			
		||||
 | 
			
		||||
    This function is called whenever you want to accept a new connection. It creates a new I/O
 | 
			
		||||
    device object.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual lw::NetClient* acceptBlocking(int timeout = -1,
 | 
			
		||||
                                          IOTimeoutMode timeoutMode = IOTimeoutHardFull) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Registers object with an event manager.
 | 
			
		||||
 | 
			
		||||
    \param eventManager The event manager object.
 | 
			
		||||
    \param callback The NetServerCallback object that will receive
 | 
			
		||||
        events for this server.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    This function will register your callback object with an event
 | 
			
		||||
    manager. Only one callback can be active at once; calling this
 | 
			
		||||
    function with a callback already set will simply clear the old
 | 
			
		||||
    callback.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void registerCallback(EventManager& eventManager,
 | 
			
		||||
        EventCallbackNetServer& callback);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Stops callbacks.
 | 
			
		||||
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    If you want to stop generating callback events, call this function.
 | 
			
		||||
    Alternatively, you can return \a false in the callback
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void clearCallback();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
/* lw-support/src/lib/Net/ServerCallback.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool EventCallbackNetServer::error()
 | 
			
		||||
{
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
/* lw-support/src/lib/Net/ServerCallback.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Base class for network server callback objects.
 | 
			
		||||
 | 
			
		||||
This is the callback objected to be used with IOListen when you want to
 | 
			
		||||
listen for incoming connections on a new server object. You simply need
 | 
			
		||||
to override the accept() function (and possibly the error() function);
 | 
			
		||||
everything else is ready to work with any object derived from the
 | 
			
		||||
NetServer class.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class EventCallbackNetServer {
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor. Does nothing.
 | 
			
		||||
    virtual ~EventCallbackNetServer()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Called when a new connection is accepted.
 | 
			
		||||
 | 
			
		||||
    \param client The new connection object.
 | 
			
		||||
    \retval true if you want to continue listening.
 | 
			
		||||
    \retval false if you want to stop listening.
 | 
			
		||||
 | 
			
		||||
    This function is called whenever the acceptance process for a new
 | 
			
		||||
    socket is complete. This needs to be overridden in derived classes
 | 
			
		||||
    to do something with \a client (such as add it to a global ready
 | 
			
		||||
    list).
 | 
			
		||||
 | 
			
		||||
    \note Any exceptions you throw from here will be ignored, and the
 | 
			
		||||
        callback will still be called in future for new connections.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool accept(IODevice* client) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Called when an error is detected.
 | 
			
		||||
 | 
			
		||||
    \retval true if you want to continue listening.
 | 
			
		||||
    \retval false if you want to stop listening.
 | 
			
		||||
 | 
			
		||||
    This function is called whenever \c epoll detects an error with the
 | 
			
		||||
    server socket. The default behaviour is to ignore the error, but you
 | 
			
		||||
    might have some more complicated error processing to do if you so
 | 
			
		||||
    wish.
 | 
			
		||||
 | 
			
		||||
    \note Any exceptions you throw from here will be ignored, and the
 | 
			
		||||
        callback will still be called in future for further errors.
 | 
			
		||||
 | 
			
		||||
    \todo Presumably this gets called whenever epoll returns the
 | 
			
		||||
        EPOLLERR event; we should therefore find a way to retrieve the
 | 
			
		||||
        error from the socket's fd.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool error();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,201 @@
 | 
			
		|||
/* lw-support/src/lib/Net/TCP/Client.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetClientTCP::NetClientTCP()
 | 
			
		||||
    : sockAddr(0), peerAddr(0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetClientTCP::clearAddr()
 | 
			
		||||
{
 | 
			
		||||
    delete sockAddr;
 | 
			
		||||
    sockAddr = 0;
 | 
			
		||||
    delete peerAddr;
 | 
			
		||||
    peerAddr = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetClientTCP::~NetClientTCP()
 | 
			
		||||
{
 | 
			
		||||
    clearAddr();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetClientTCP::NetClientTCP(int fd, NetAddress* sockAddr, uint16_t sockPort,
 | 
			
		||||
    NetAddress* peerAddr, uint16_t peerPort)
 | 
			
		||||
    : sockAddr(sockAddr), peerAddr(peerAddr),
 | 
			
		||||
      sockPort(sockPort), peerPort(peerPort)
 | 
			
		||||
{
 | 
			
		||||
    this->fd = fd;
 | 
			
		||||
    ioMode = IOReadWrite;
 | 
			
		||||
    setNonBlocking();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetClientTCP::connect(const NetAddress& addr, uint16_t port, bool block)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        // clear old connection
 | 
			
		||||
        clearAddr();
 | 
			
		||||
        close();
 | 
			
		||||
 | 
			
		||||
        // create a socket
 | 
			
		||||
        fd = TEMP_FAILURE_RETRY(socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
 | 
			
		||||
        if(fd == -1)
 | 
			
		||||
            throw SystemError().chain(L"socket()");
 | 
			
		||||
        ioMode = IOReadWrite;
 | 
			
		||||
 | 
			
		||||
        // set non-blocking mode
 | 
			
		||||
        if(!block) setNonBlocking();
 | 
			
		||||
 | 
			
		||||
        // fill out addresses and connect
 | 
			
		||||
        peerAddr = addr.clone();
 | 
			
		||||
        peerPort = port;
 | 
			
		||||
 | 
			
		||||
            // IPv4
 | 
			
		||||
        if(!sockAddr) {
 | 
			
		||||
            const NetAddressIPv4* a = dynamic_cast<const NetAddressIPv4*>(&addr);
 | 
			
		||||
            if(a) {
 | 
			
		||||
                struct sockaddr_in sa;
 | 
			
		||||
                socklen_t sa_len = sizeof(sa);
 | 
			
		||||
 | 
			
		||||
                // get address
 | 
			
		||||
                if(TEMP_FAILURE_RETRY(getsockname(
 | 
			
		||||
                    fd, (struct sockaddr*)&sa, &sa_len)))
 | 
			
		||||
                        throw SystemError().chain(L"getsockname()");
 | 
			
		||||
                sockAddr = new NetAddressIPv4((char*)&(sa.sin_addr.s_addr));
 | 
			
		||||
                sockPort = sa.sin_port;
 | 
			
		||||
 | 
			
		||||
                // connect
 | 
			
		||||
                int ret = TEMP_FAILURE_RETRY(::connect(
 | 
			
		||||
                    fd, a->copy(&sa, port), sa_len));
 | 
			
		||||
 | 
			
		||||
                if(ret == -1 && (block || errno != EINPROGRESS))
 | 
			
		||||
                    throw SystemError().chain(L"connect()");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // IPv6
 | 
			
		||||
        if(!sockAddr) {
 | 
			
		||||
            const NetAddressIPv6* a = dynamic_cast<const NetAddressIPv6*>(&addr);
 | 
			
		||||
            if(a) {
 | 
			
		||||
                struct sockaddr_in6 sa;
 | 
			
		||||
                socklen_t sa_len = sizeof(sa);
 | 
			
		||||
 | 
			
		||||
                // get address
 | 
			
		||||
                if(TEMP_FAILURE_RETRY(getsockname(
 | 
			
		||||
                    fd, (struct sockaddr*)&sa, &sa_len)))
 | 
			
		||||
                        throw SystemError().chain(L"getsockname()");
 | 
			
		||||
                sockAddr = new NetAddressIPv6(sa.sin6_addr.s6_addr);
 | 
			
		||||
                sockPort = sa.sin6_port;
 | 
			
		||||
 | 
			
		||||
                // connect
 | 
			
		||||
                int ret = TEMP_FAILURE_RETRY(::connect(
 | 
			
		||||
                    fd, a->copy(&sa, port), sa_len));
 | 
			
		||||
 | 
			
		||||
                if(ret == -1 && (block || errno != EINPROGRESS))
 | 
			
		||||
                    throw SystemError().chain(L"connect()");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // unknown
 | 
			
		||||
        if(!sockAddr) {
 | 
			
		||||
            throw NetServer::UnknownProtocol();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // if we were doing blocking I/O, set non-blocking mode now
 | 
			
		||||
        setNonBlocking();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // deal with errors gracefully
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        try {
 | 
			
		||||
            close();
 | 
			
		||||
        }
 | 
			
		||||
        catch(...) { }
 | 
			
		||||
        e.chain(L"NetClientTCP::connect()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetClientTCP::connect(const NetAddress& addr, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
    connect(addr, port, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetClientTCP::connectBlocking(const NetAddress& addr, uint16_t port)
 | 
			
		||||
{
 | 
			
		||||
    connect(addr, port, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetClientTCP::NetClientTCP(const NetAddress& addr, uint16_t port)
 | 
			
		||||
    : sockAddr(0), peerAddr(0)
 | 
			
		||||
{
 | 
			
		||||
    connect(addr, port, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint16_t NetClientTCP::localPort() const
 | 
			
		||||
{
 | 
			
		||||
    checkOpen(L"NetClientTCP::localPort()");
 | 
			
		||||
    return sockPort;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint16_t NetClientTCP::remotePort() const
 | 
			
		||||
{
 | 
			
		||||
    checkOpen(L"NetClientTCP::remotePort()");
 | 
			
		||||
    return peerPort;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const NetAddress& NetClientTCP::localAddr() const
 | 
			
		||||
{
 | 
			
		||||
    checkOpen(L"NetClientTCP::localPort()");
 | 
			
		||||
    return *sockAddr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const NetAddress& NetClientTCP::remoteAddr() const
 | 
			
		||||
{
 | 
			
		||||
    checkOpen(L"NetClientTCP::remotePort()");
 | 
			
		||||
    return *peerAddr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetClientTCP::setNoDelay(bool noDelay)
 | 
			
		||||
{
 | 
			
		||||
    int n = noDelay;
 | 
			
		||||
    if(setsockopt(fd, SOL_TCP, TCP_NODELAY, &n, sizeof(n)))
 | 
			
		||||
        throw SystemError()
 | 
			
		||||
            .chain(L"setsockopt(..., SOL_TCP, TCP_NODELAY, ...)")
 | 
			
		||||
            .chain(L"NetClientTCP::setNoDelay()");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,148 @@
 | 
			
		|||
/* lw-support/src/lib/Net/TCP/Client.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief TCP client socket class.
 | 
			
		||||
 | 
			
		||||
\todo docs
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class NetClientTCP : public NetClient {
 | 
			
		||||
private:
 | 
			
		||||
    void clearAddr();
 | 
			
		||||
    void connect(const NetAddress& addr, uint16_t port, bool block);
 | 
			
		||||
    NetAddress* sockAddr, * peerAddr;
 | 
			
		||||
    uint16_t sockPort, peerPort;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Constructors etc. ///////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Constructor. Does not connect client.
 | 
			
		||||
    NetClientTCP();
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    virtual ~NetClientTCP();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from an already-connected socket.
 | 
			
		||||
 | 
			
		||||
    \param fd The file descriptor of the socket.
 | 
			
		||||
    \param sockAddr Local address of the socket.
 | 
			
		||||
    \param sockPort Local port of the socket.
 | 
			
		||||
    \param peerAddr Address of the remote socket.
 | 
			
		||||
    \param peerPort Remote socket port number.
 | 
			
		||||
 | 
			
		||||
    This constructor is intended for use in e.g. NetServerTCP(), and is
 | 
			
		||||
    for situations where you have an already-connected socket and want
 | 
			
		||||
    to build a wrapper around it. It takes ownership of the \a sockAddr
 | 
			
		||||
    and \a peerAddr objects.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    NetClientTCP(int fd, NetAddress* sockAddr, uint16_t sockPort,
 | 
			
		||||
        NetAddress* peerAddr, uint16_t peerPort);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct and connect (blocking).
 | 
			
		||||
 | 
			
		||||
    \param addr The address of the machine to connect to.
 | 
			
		||||
    \param port The TCP port to connect to.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws NetServer::UnknownProtocol if the protocol of \a addr is
 | 
			
		||||
        not known.
 | 
			
		||||
 | 
			
		||||
    Similar to \a connect(), this function will establish a connection
 | 
			
		||||
    to a remote host. However, this is a blocking function: it will
 | 
			
		||||
    only return once the connection has been established.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    NetClientTCP(const NetAddress& addr, uint16_t port);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Connection functions ////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Connect to a remote host (non-blocking).
 | 
			
		||||
 | 
			
		||||
    \param addr The address of the machine to connect to.
 | 
			
		||||
    \param port The TCP port to connect to.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws NetServer::UnknownProtocol if the protocol of \a addr is
 | 
			
		||||
        not known.
 | 
			
		||||
 | 
			
		||||
    This function begins connecting to a remote host. Note that it
 | 
			
		||||
    doesn't actually guarantee the connection will be established,
 | 
			
		||||
    since it is a non-blocking function. You will know the connection
 | 
			
		||||
    is ready when you receive an IOListen event.
 | 
			
		||||
 | 
			
		||||
    If the client is already connected, it will be disconnected first.
 | 
			
		||||
 | 
			
		||||
    \todo figure out what happens in the case of a connection error.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void connect(const NetAddress& addr, uint16_t port);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Connect to a remote host (blocking).
 | 
			
		||||
 | 
			
		||||
    \param addr The address of the machine to connect to.
 | 
			
		||||
    \param port The TCP port to connect to.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
    \throws NetServer::UnknownProtocol if the protocol of \a addr is
 | 
			
		||||
        not known.
 | 
			
		||||
 | 
			
		||||
    Similar to \a connect(), this function will establish a connection
 | 
			
		||||
    to a remote host. However, this is a blocking function: it will
 | 
			
		||||
    only return once the connection has been established.
 | 
			
		||||
 | 
			
		||||
    \todo add some nicer exception classes for networking.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void connectBlocking(const NetAddress& addr, uint16_t port);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Query functions /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Query local port number; throws IOModeError if not connected.
 | 
			
		||||
    uint16_t localPort() const;
 | 
			
		||||
 | 
			
		||||
    /// Query remote port number; throws IOModeError if not connected.
 | 
			
		||||
    uint16_t remotePort() const;
 | 
			
		||||
 | 
			
		||||
    /// Query local host address; throws IOModeError if not connected.
 | 
			
		||||
    const NetAddress& localAddr() const;
 | 
			
		||||
 | 
			
		||||
    /// Query remote host address; throws IOModeError if not connected.
 | 
			
		||||
    const NetAddress& remoteAddr() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Misc. operations ////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Set no delay option.
 | 
			
		||||
 | 
			
		||||
    \param noDelay Set to \a true if you want to turn on the no delay
 | 
			
		||||
        option; set to \a false if you want to use Nagle's algorithm.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    This function allows you to turn on/off Nagle's algorithm, whereby
 | 
			
		||||
    outgoing packets are stored until the remote system returns an ACK.
 | 
			
		||||
    This generally results in better usage of the network but does
 | 
			
		||||
    increase latency.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void setNoDelay(bool noDelay);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,205 @@
 | 
			
		|||
/* lw-support/src/lib/Net/TCP/Server.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetServerTCP::listen(const NetAddress& addr,
 | 
			
		||||
    uint16_t port, int queueLen)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        // shut down any previous server
 | 
			
		||||
        stop();
 | 
			
		||||
 | 
			
		||||
        // create a socket
 | 
			
		||||
        fd = TEMP_FAILURE_RETRY(socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
 | 
			
		||||
        if(fd == -1)
 | 
			
		||||
            throw SystemError().chain(L"socket()");
 | 
			
		||||
 | 
			
		||||
        // set SO_REUSEADDR, so that we can bind to a socket even if
 | 
			
		||||
        // it's in the TCP TIME_WAIT state.
 | 
			
		||||
        int reuse = 1;
 | 
			
		||||
        if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)))
 | 
			
		||||
            throw SystemError().chain(L"setsockopt(..., SOL_SOCKET, SO_REUSEADDR, ...)");
 | 
			
		||||
 | 
			
		||||
        // bind that socket to the requested address
 | 
			
		||||
        switch(protocol = getProtocol(addr)) {
 | 
			
		||||
            case IPv4: {
 | 
			
		||||
                const NetAddressIPv4& a = dynamic_cast<const NetAddressIPv4&>(addr);
 | 
			
		||||
                struct sockaddr_in sa;
 | 
			
		||||
                if(TEMP_FAILURE_RETRY(bind(fd, a.copy(&sa, port), sizeof(sa))))
 | 
			
		||||
                    throw SystemError().chain(L"bind()");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            case IPv6: {
 | 
			
		||||
                const NetAddressIPv6& a = dynamic_cast<const NetAddressIPv6&>(addr);
 | 
			
		||||
                struct sockaddr_in6 sa;
 | 
			
		||||
                if(TEMP_FAILURE_RETRY(bind(fd, a.copy(&sa, port), sizeof(sa))))
 | 
			
		||||
                    throw SystemError().chain(L"bind()");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // start listening
 | 
			
		||||
        if(TEMP_FAILURE_RETRY(::listen(fd, queueLen)))
 | 
			
		||||
            throw SystemError().chain(L"listen()");
 | 
			
		||||
 | 
			
		||||
        // set non-blocking I/O mode
 | 
			
		||||
        setNonBlocking();
 | 
			
		||||
        ioMode = IONone;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // gracefully deal with any errors
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        try {
 | 
			
		||||
            stop();
 | 
			
		||||
        }
 | 
			
		||||
        catch(...) { }
 | 
			
		||||
 | 
			
		||||
        e.chain(L"NetServerTCP::listen()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void NetServerTCP::stop()
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        close();
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"NetServerTCP::stop()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetClientTCP* NetServerTCP::accept()
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        int sockfd = -1;
 | 
			
		||||
 | 
			
		||||
        switch(protocol) {
 | 
			
		||||
            case IPv4: {
 | 
			
		||||
                while(true) {
 | 
			
		||||
                    // get connection and address
 | 
			
		||||
                    struct sockaddr_in sa, sa2;
 | 
			
		||||
                    socklen_t sa_len = sizeof(sa);
 | 
			
		||||
                    sockfd = TEMP_FAILURE_RETRY(::accept(
 | 
			
		||||
                        fd, (struct sockaddr*)(&sa), &sa_len));
 | 
			
		||||
 | 
			
		||||
                    // return 0 if there was nothing waiting
 | 
			
		||||
                    if(sockfd == -1) {
 | 
			
		||||
                        // see accept(2) man page: some errors are propagated
 | 
			
		||||
                        // from elsewhere in the TCP stack and we should
 | 
			
		||||
                        // handle them by retrying accept().
 | 
			
		||||
                        switch(errno) {
 | 
			
		||||
                            case EAGAIN:
 | 
			
		||||
                                return 0;
 | 
			
		||||
 | 
			
		||||
                            case ENETDOWN:
 | 
			
		||||
                            case EPROTO:
 | 
			
		||||
                            case ENOPROTOOPT:
 | 
			
		||||
                            case EHOSTDOWN:
 | 
			
		||||
                            case ENONET:
 | 
			
		||||
                            case EHOSTUNREACH:
 | 
			
		||||
                            case EOPNOTSUPP:
 | 
			
		||||
                            case ENETUNREACH:
 | 
			
		||||
                                continue;
 | 
			
		||||
 | 
			
		||||
                            default:
 | 
			
		||||
                                throw SystemError().chain(L"accept()");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // get local address
 | 
			
		||||
                    sa_len = sizeof(sa2);
 | 
			
		||||
                    if(TEMP_FAILURE_RETRY(getsockname(
 | 
			
		||||
                        sockfd, (struct sockaddr*)(&sa2), &sa_len)))
 | 
			
		||||
                            throw SystemError().chain(L"getsockname()");
 | 
			
		||||
 | 
			
		||||
                    // build client
 | 
			
		||||
                    return new NetClientTCP(sockfd,
 | 
			
		||||
                        new NetAddressIPv4((char*)&(sa2.sin_addr.s_addr)), ntohs(sa2.sin_port),
 | 
			
		||||
                        new NetAddressIPv4((char*)&(sa.sin_addr.s_addr)), ntohs(sa.sin_port));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            case IPv6: {
 | 
			
		||||
                while(true) {
 | 
			
		||||
                    // get connection and address
 | 
			
		||||
                    struct sockaddr_in6 sa, sa2;
 | 
			
		||||
                    socklen_t sa_len = sizeof(sa);
 | 
			
		||||
                    sockfd = TEMP_FAILURE_RETRY(::accept(
 | 
			
		||||
                        fd, (struct sockaddr*)(&sa), &sa_len));
 | 
			
		||||
 | 
			
		||||
                    // return 0 if there was nothing waiting
 | 
			
		||||
                    if(sockfd == -1) {
 | 
			
		||||
                        // see accept(2) man page: some errors are propagated
 | 
			
		||||
                        // from elsewhere in the TCP stack and we should
 | 
			
		||||
                        // handle them by retrying accept().
 | 
			
		||||
                        //
 | 
			
		||||
                        // TODO: is this right for IPv6 as well?
 | 
			
		||||
                        switch(errno) {
 | 
			
		||||
                            case EAGAIN:
 | 
			
		||||
                                return 0;
 | 
			
		||||
 | 
			
		||||
                            case ENETDOWN:
 | 
			
		||||
                            case EPROTO:
 | 
			
		||||
                            case ENOPROTOOPT:
 | 
			
		||||
                            case EHOSTDOWN:
 | 
			
		||||
                            case ENONET:
 | 
			
		||||
                            case EHOSTUNREACH:
 | 
			
		||||
                            case EOPNOTSUPP:
 | 
			
		||||
                            case ENETUNREACH:
 | 
			
		||||
                                continue;
 | 
			
		||||
 | 
			
		||||
                            default:
 | 
			
		||||
                                throw SystemError().chain(L"accept()");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // get local address
 | 
			
		||||
                    sa_len = sizeof(sa2);
 | 
			
		||||
                    if(TEMP_FAILURE_RETRY(getsockname(
 | 
			
		||||
                        sockfd, (struct sockaddr*)(&sa2), &sa_len)))
 | 
			
		||||
                            throw SystemError().chain(L"getsockname()");
 | 
			
		||||
 | 
			
		||||
                    // build client
 | 
			
		||||
                    return new NetClientTCP(sockfd,
 | 
			
		||||
                        new NetAddressIPv6(sa2.sin6_addr.s6_addr), ntohs(sa2.sin6_port),
 | 
			
		||||
                        new NetAddressIPv6(sa.sin6_addr.s6_addr), ntohs(sa.sin6_port));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw ProgramException().chain(L"NetServerTCP::connect()");
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& e) {
 | 
			
		||||
        e.chain(L"NetServerTCP::accept()");
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
NetClientTCP* NetServerTCP::acceptBlocking(int timeout, IOTimeoutMode timeoutMode)
 | 
			
		||||
{
 | 
			
		||||
    readBlock(timeout, timeoutMode);
 | 
			
		||||
    NetClientTCP* client = accept();
 | 
			
		||||
 | 
			
		||||
    // TODO: better error handling here would be appropriate... restart timeout somehow
 | 
			
		||||
    if(!client) throw ProgramException().chain(L"NetServerTCP::acceptBlocking()");
 | 
			
		||||
    return client;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
/* lw-support/src/lib/Net/TCP/Server.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief TCP server class.
 | 
			
		||||
 | 
			
		||||
\todo docs
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class NetServerTCP : public NetServer {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor. Does not open connection.
 | 
			
		||||
    NetServerTCP()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Begin listening on a specific address.
 | 
			
		||||
 | 
			
		||||
    \param address The address to listen on.
 | 
			
		||||
    \param port The port number to listen on.
 | 
			
		||||
    \param queueLen The length of the unaccepted connection queue.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    This function starts the server listening for new connections. The
 | 
			
		||||
    server will listen on any address.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void listen(const NetAddress& address, uint16_t port, int queueLen);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Stop listening to incoming connections.
 | 
			
		||||
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    This function will stop listening for incoming connections, and will
 | 
			
		||||
    reject any connection requests that have not yet been accepted.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void stop();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Implemented virtuals ////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    virtual lw::NetClientTCP* accept();
 | 
			
		||||
    virtual lw::NetClientTCP* acceptBlocking(int timeout, IOTimeoutMode timeoutMode);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,401 @@
 | 
			
		|||
/* lw-support/src/lib/String/basics.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring stripWhitespace(const std::wstring& str)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring::size_type pos, len = str.length();
 | 
			
		||||
    for(pos = 0; pos < len; ++pos) if(!isWhitespace(str[pos])) break;
 | 
			
		||||
    if(pos == len) return std::wstring();
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type endPos = len - 1;
 | 
			
		||||
    while(isWhitespace(str[endPos])) --endPos;
 | 
			
		||||
 | 
			
		||||
    return str.substr(pos, endPos - pos + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::string ucs4ToUtf8(const std::wstring& ucs4, bool force)
 | 
			
		||||
{
 | 
			
		||||
    // compute size of string, skip empty strings
 | 
			
		||||
    std::wstring::size_type len = ucs4.length(), rd_pos, start;
 | 
			
		||||
    if(!len) return std::string();
 | 
			
		||||
 | 
			
		||||
    // skip BOM
 | 
			
		||||
    start = (ucs4[0] == 0xFEFF) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
    // compute required size and check characters
 | 
			
		||||
    int dlen = 0;
 | 
			
		||||
    for(rd_pos = start; rd_pos < len; ++rd_pos) {
 | 
			
		||||
        wchar_t ch = ucs4[rd_pos];
 | 
			
		||||
        if(!force && (ch & ~0x7FFFFFFF)) {
 | 
			
		||||
            throw BadSourceChar((const char*)&ch, (const char*)(&ch + 4),
 | 
			
		||||
                rd_pos * 4, L"UCS-4", L"Character out of range.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(ch < 0x80) ++dlen;
 | 
			
		||||
        else if(ch < 0x800) dlen += 2;
 | 
			
		||||
        else if(ch < 0x10000) dlen += 3;
 | 
			
		||||
        else if(ch < 0x200000) dlen += 4;
 | 
			
		||||
        else if(ch < 0x4000000) dlen += 5;
 | 
			
		||||
        else dlen += 6;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // allocate memory
 | 
			
		||||
    std::string dest(dlen, 0);
 | 
			
		||||
 | 
			
		||||
    // convert string
 | 
			
		||||
    std::string::size_type wr_pos = 0;
 | 
			
		||||
    for(rd_pos = start; rd_pos < len; ++rd_pos) {
 | 
			
		||||
        uint32_t ch = ucs4[rd_pos];
 | 
			
		||||
 | 
			
		||||
        if(ch & ~0x7FFFFFFF) continue;
 | 
			
		||||
 | 
			
		||||
        if(ch < 0x80) {
 | 
			
		||||
            dest[wr_pos++] = ch;
 | 
			
		||||
 | 
			
		||||
        } else if(ch < 0x800) {
 | 
			
		||||
            dest[wr_pos++] = 0xC0 | ((ch >> 6) & 0x1F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | (ch & 0x3F);
 | 
			
		||||
 | 
			
		||||
        } else if(ch < 0x10000) {
 | 
			
		||||
            dest[wr_pos++] = 0xE0 | ((ch >> 12) & 0xF);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 6) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | (ch & 0x3F);
 | 
			
		||||
 | 
			
		||||
        } else if(ch < 0x200000) {
 | 
			
		||||
            dest[wr_pos++] = 0xF0 | ((ch >> 18) & 0x7);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 12) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 6) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | (ch & 0x3F);
 | 
			
		||||
 | 
			
		||||
        } else if(ch < 0x4000000) {
 | 
			
		||||
            dest[wr_pos++] = 0xF8 | ((ch >> 24) & 0x3);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 18) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 12) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 6) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | (ch & 0x3F);
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            dest[wr_pos++] = 0xFC | ((ch >> 30) & 0x1);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 24) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 18) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 12) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | ((ch >> 6) & 0x3F);
 | 
			
		||||
            dest[wr_pos++] = 0x80 | (ch & 0x3F);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return dest;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::string ucs4ToAscii(const std::wstring& ucs4, bool force)
 | 
			
		||||
{
 | 
			
		||||
    // compute size of string, skip empty strings
 | 
			
		||||
    std::wstring::size_type len = ucs4.length(), rd_pos, start;
 | 
			
		||||
    if(!len) return std::string();
 | 
			
		||||
 | 
			
		||||
    // skip BOM
 | 
			
		||||
    start = (ucs4[0] == 0xFEFF) ? 1 : 0;
 | 
			
		||||
 | 
			
		||||
    // allocate destination string
 | 
			
		||||
    int skipped = 0;
 | 
			
		||||
    std::string ascii(len - start, 0);
 | 
			
		||||
 | 
			
		||||
    // convert each char, checking values as we go
 | 
			
		||||
    for(rd_pos = start; rd_pos < len; ++rd_pos) {
 | 
			
		||||
        if(ucs4[rd_pos] & ~0x7F) {
 | 
			
		||||
            ++skipped;
 | 
			
		||||
            if(force) continue;
 | 
			
		||||
            throw CannotConvertChar(ucs4[rd_pos], rd_pos, L"UCS-4", L"ASCII");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ascii[rd_pos] = ucs4[rd_pos];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // strip off any skipped characters
 | 
			
		||||
    if(skipped) ascii.resize(len - skipped);
 | 
			
		||||
 | 
			
		||||
    // done
 | 
			
		||||
    return ascii;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
void utf8AuxConsume(wchar_t& ch, const std::string& utf8,
 | 
			
		||||
    std::string::size_type& rd_pos, std::string::size_type ch_start)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t p = utf8[rd_pos++];
 | 
			
		||||
    if((p & 0xC0) != 0x80) {
 | 
			
		||||
        throw BadSourceChar(utf8.c_str() + ch_start,
 | 
			
		||||
            utf8.c_str() + rd_pos, ch_start, L"UTF-8",
 | 
			
		||||
            L"Invalid character sequence (expecting byte 10xxxxxx).");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ch <<= 6;
 | 
			
		||||
    ch |= p & 0x3F;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::wstring utf8ToUcs4Forced(const std::string& str)
 | 
			
		||||
{
 | 
			
		||||
    // compute size of std::string, skip empty std::strings
 | 
			
		||||
    std::string::size_type len = str.length(), rd_pos = 0;
 | 
			
		||||
    if(!len) return std::wstring();
 | 
			
		||||
 | 
			
		||||
    // skip possible BOM
 | 
			
		||||
    if(len >= 3 && str.substr(0, 3) == "\xEF\xBB\xBF") rd_pos = 3;
 | 
			
		||||
 | 
			
		||||
    // for FSM
 | 
			
		||||
    enum CharType {
 | 
			
		||||
        CTypeAscii,
 | 
			
		||||
        CTypeContinue,
 | 
			
		||||
        CType2,
 | 
			
		||||
        CType3,
 | 
			
		||||
        CType4,
 | 
			
		||||
        CType5,
 | 
			
		||||
        CType6
 | 
			
		||||
    }ctype;
 | 
			
		||||
    int cremain = 0;
 | 
			
		||||
 | 
			
		||||
    // destination buffer
 | 
			
		||||
    std::wstring d;
 | 
			
		||||
 | 
			
		||||
    // begin conversion using FSM
 | 
			
		||||
    uint8_t ch;
 | 
			
		||||
    wchar_t dch;
 | 
			
		||||
 | 
			
		||||
    while(rd_pos < len) {
 | 
			
		||||
        ch = str[rd_pos++];
 | 
			
		||||
 | 
			
		||||
        // classify
 | 
			
		||||
        if(ch & 0x80) {
 | 
			
		||||
            if((ch & 0xC0) == 0x80) ctype = CTypeContinue;
 | 
			
		||||
            else if((ch & 0xE0) == 0xC0) ctype = CType2;
 | 
			
		||||
            else if((ch & 0xF0) == 0xE0) ctype = CType3;
 | 
			
		||||
            else if((ch & 0xF8) == 0xF0) ctype = CType4;
 | 
			
		||||
            else if((ch & 0xFC) == 0xF8) ctype = CType5;
 | 
			
		||||
            else if((ch & 0xFE) == 0xFC) ctype = CType6;
 | 
			
		||||
            else {
 | 
			
		||||
                continue; // must be 0xFE or 0xFF, both invalid
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            ctype = CTypeAscii;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // decode multi-byte
 | 
			
		||||
        if(cremain) {
 | 
			
		||||
            if(ctype != CTypeContinue) {
 | 
			
		||||
                --rd_pos;
 | 
			
		||||
                cremain = 0;  // was expecting multi-byte; abort conversion
 | 
			
		||||
                continue;     // of this char and restart
 | 
			
		||||
            }
 | 
			
		||||
            dch <<= 6;
 | 
			
		||||
            dch |= ch & 0x3F;
 | 
			
		||||
            if(!--cremain) d.append(&dch, 1);
 | 
			
		||||
 | 
			
		||||
        // decode start char
 | 
			
		||||
        } else {
 | 
			
		||||
            switch(ctype) {
 | 
			
		||||
                case CTypeContinue:
 | 
			
		||||
                    // not valid here
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                case CTypeAscii:
 | 
			
		||||
                    dch = ch;
 | 
			
		||||
                    d.append(&dch, 1);
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                case CType2:
 | 
			
		||||
                    cremain = 1;
 | 
			
		||||
                    dch = ch & 0x1F;
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                case CType3:
 | 
			
		||||
                    cremain = 2;
 | 
			
		||||
                    dch = ch & 0x0F;
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                case CType4:
 | 
			
		||||
                    cremain = 3;
 | 
			
		||||
                    dch = ch & 0x07;
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                case CType5:
 | 
			
		||||
                    cremain = 4;
 | 
			
		||||
                    dch = ch & 0x03;
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                case CType6:
 | 
			
		||||
                    cremain = 5;
 | 
			
		||||
                    dch = ch & 0x01;
 | 
			
		||||
                    continue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // done
 | 
			
		||||
    return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring utf8ToUcs4(const std::string& utf8, bool force)
 | 
			
		||||
{
 | 
			
		||||
    // when conversion is forced, we need to use a more hardy algorithm
 | 
			
		||||
    if(force) return utf8ToUcs4Forced(utf8);
 | 
			
		||||
 | 
			
		||||
    // compute size of std::string, skip empty std::strings
 | 
			
		||||
    std::string::size_type len = utf8.length(), rd_pos, ch_start = 0;
 | 
			
		||||
    if(!len) return std::wstring();
 | 
			
		||||
 | 
			
		||||
    // skip BOM
 | 
			
		||||
    std::string::size_type start = 0;
 | 
			
		||||
    if(len >= 3 && utf8.substr(0, 3) == "\xEF\xBB\xBF") start = 3;
 | 
			
		||||
 | 
			
		||||
    // compute required size
 | 
			
		||||
    std::wstring::size_type dlen = 0;
 | 
			
		||||
    rd_pos = start;
 | 
			
		||||
    uint8_t ch;
 | 
			
		||||
    while(rd_pos < len) {
 | 
			
		||||
        ch_start = rd_pos;
 | 
			
		||||
        ch = utf8[rd_pos];
 | 
			
		||||
        if((ch & 0x80) == 0) ++rd_pos;
 | 
			
		||||
        else if((ch & 0xE0) == 0xC0) rd_pos += 2;
 | 
			
		||||
        else if((ch & 0xF0) == 0xE0) rd_pos += 3;
 | 
			
		||||
        else if((ch & 0xF8) == 0xF0) rd_pos += 4;
 | 
			
		||||
        else if((ch & 0xFC) == 0xF8) rd_pos += 5;
 | 
			
		||||
        else if((ch & 0xFE) == 0xFC) rd_pos += 6;
 | 
			
		||||
        else {
 | 
			
		||||
            throw BadSourceChar(utf8.c_str() + ch_start,
 | 
			
		||||
                utf8.c_str() + ch_start + 1, ch_start, L"UTF-8",
 | 
			
		||||
                L"Invalid character in std::string.");
 | 
			
		||||
        }
 | 
			
		||||
        ++dlen;
 | 
			
		||||
    }
 | 
			
		||||
    if(rd_pos != len) {
 | 
			
		||||
        throw BadSourceChar(utf8.c_str() + ch_start,
 | 
			
		||||
            utf8.c_str() + rd_pos, ch_start, L"UTF-8",
 | 
			
		||||
            L"Last character appears to be truncated.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // allocate memory
 | 
			
		||||
    std::wstring dest(dlen, 0);
 | 
			
		||||
 | 
			
		||||
    // helper macros
 | 
			
		||||
#define CONSUME() utf8AuxConsume(ch, utf8, rd_pos, ch_start)
 | 
			
		||||
#define CONSUME_FIRST(mask) ch = (p & mask)
 | 
			
		||||
#define CHECK(min) do \
 | 
			
		||||
    if(ch < min) { \
 | 
			
		||||
        throw BadSourceChar(utf8.c_str() + ch_start, \
 | 
			
		||||
            utf8.c_str() + rd_pos, ch_start, L"UTF-8", \
 | 
			
		||||
            L"Character encoded in an overlong sequence."); \
 | 
			
		||||
    } \
 | 
			
		||||
while(0)
 | 
			
		||||
 | 
			
		||||
    // loop through each character in turn
 | 
			
		||||
    rd_pos = start;
 | 
			
		||||
    for(std::wstring::size_type wr_pos = 0; wr_pos < dlen; ++wr_pos) {
 | 
			
		||||
        wchar_t ch = 0;
 | 
			
		||||
        ch_start = rd_pos;
 | 
			
		||||
        uint8_t p = utf8[rd_pos++];
 | 
			
		||||
 | 
			
		||||
        if(!(p & 0x80)) {
 | 
			
		||||
            ch = p;
 | 
			
		||||
 | 
			
		||||
        } else if((p & 0xFE) == 0xFC) {
 | 
			
		||||
            CONSUME_FIRST(0x01);
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CHECK(0x4000000);
 | 
			
		||||
 | 
			
		||||
        } else if((p & 0xFC) == 0xF8) {
 | 
			
		||||
            CONSUME_FIRST(0x03);
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CHECK(0x200000);
 | 
			
		||||
 | 
			
		||||
        } else if((p & 0xF8) == 0xF0) {
 | 
			
		||||
            CONSUME_FIRST(0x07);
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CHECK(0x10000);
 | 
			
		||||
 | 
			
		||||
        } else if((p & 0xF0) == 0xE0) {
 | 
			
		||||
            CONSUME_FIRST(0x0F);
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CHECK(0x800);
 | 
			
		||||
 | 
			
		||||
        } else if((p & 0xE0) == 0xC0) {
 | 
			
		||||
            CONSUME_FIRST(0x1F);
 | 
			
		||||
            CONSUME();
 | 
			
		||||
            CHECK(0x80);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        dest[wr_pos] = ch;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#undef CONSUME
 | 
			
		||||
#undef CONSUME_FIRST
 | 
			
		||||
#undef CHECK
 | 
			
		||||
 | 
			
		||||
    return dest;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring asciiToUcs4(const std::string& ascii, bool force)
 | 
			
		||||
{
 | 
			
		||||
    int skipped = 0;
 | 
			
		||||
    std::string::size_type size = ascii.size(), pos;
 | 
			
		||||
    std::wstring d(size, 0);
 | 
			
		||||
 | 
			
		||||
    for(pos = 0; pos < size; ++pos) {
 | 
			
		||||
        if(d[pos] & ~0x7F) {
 | 
			
		||||
            ++skipped;
 | 
			
		||||
            if(!force) throw BadSourceChar(ascii.c_str() + pos,
 | 
			
		||||
                ascii.c_str() + pos + 1, pos, L"ASCII",
 | 
			
		||||
                L"Invalid character (out of range).");
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        d[pos] = ascii[pos];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    d.resize(size - skipped);
 | 
			
		||||
 | 
			
		||||
    return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring strToUcs4(const std::string& str)
 | 
			
		||||
{
 | 
			
		||||
    std::string::size_type size = str.size(), pos;
 | 
			
		||||
    std::wstring d(size, 0);
 | 
			
		||||
    for(pos = 0; pos < size; ++pos) d[pos] = str[pos];
 | 
			
		||||
    return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,140 @@
 | 
			
		|||
/* lw-support/src/lib/String/basics.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Return true if character is whitespace.
 | 
			
		||||
 | 
			
		||||
\param ch The character to classify.
 | 
			
		||||
\retval true if character represents whitespace.
 | 
			
		||||
\retval false if character does not represent whitespace.
 | 
			
		||||
 | 
			
		||||
This function is identical to the C library's isspace() function, except
 | 
			
		||||
that it handles Unicode code points.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
inline bool isWhitespace(wchar_t ch) __attribute__((const));
 | 
			
		||||
bool isWhitespace(wchar_t ch)
 | 
			
		||||
{
 | 
			
		||||
    return ((ch >= 0x9 && ch <= 0xD) ||
 | 
			
		||||
        ch == 0x20 ||
 | 
			
		||||
        ch == 0x85 ||
 | 
			
		||||
        ch == 0xA0 ||
 | 
			
		||||
        ch == 0x1680 ||
 | 
			
		||||
        ch == 0x180E ||
 | 
			
		||||
        (ch >= 0x2000 && ch <= 0x200A) ||
 | 
			
		||||
        ch == 0x2028 ||
 | 
			
		||||
        ch == 0x2029 ||
 | 
			
		||||
        ch == 0x202F ||
 | 
			
		||||
        ch == 0x205F ||
 | 
			
		||||
        ch == 0x3000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Strip leading and trailing whitespace.
 | 
			
		||||
 | 
			
		||||
\param str The UCS-4 encoded string to strip whitespace from.
 | 
			
		||||
\returns A copy of the string without trailing/leading whitespace.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
std::wstring stripWhitespace(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Convert a UCS-4 string to UTF-8.
 | 
			
		||||
 | 
			
		||||
\param ucs4 The (UCS-4 encoded) string to convert.
 | 
			
		||||
\param force Set this to \a true if you want to force the conversion to
 | 
			
		||||
    happen even in the face of invalid characters.
 | 
			
		||||
\returns A UTF-8 representation of \a ucs4.
 | 
			
		||||
\throws BadSourceChar if a character in the string is outside the
 | 
			
		||||
    Unicode range (i.e. 0-0x7FFFFFFF).
 | 
			
		||||
 | 
			
		||||
This function can be used to convert a UCS-4 string into a UTF-8 string.
 | 
			
		||||
It will throw an exception if any error is encountered, unless \a force
 | 
			
		||||
is \a true, in which case invalid characters will be skipped. It will
 | 
			
		||||
strip a BOM (byte order mark) from the beginning of a string.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
std::string ucs4ToUtf8(const std::wstring& ucs4, bool force = false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Convert a UCS-4 string to ASCII.
 | 
			
		||||
 | 
			
		||||
\param ucs4 The (UCS-4 encoded) string to convert.
 | 
			
		||||
\param force Set this to \a true if you want to force the conversion to
 | 
			
		||||
    happen even in the face of invalid characters.
 | 
			
		||||
\returns A UTF-8 representation of \a ucs4.
 | 
			
		||||
\throws BadSourceChar if a character in the string is outside the
 | 
			
		||||
    ASCII range (i.e. 0-0x7F).
 | 
			
		||||
 | 
			
		||||
This function can be used to convert a UCS-4 string into an ASCII
 | 
			
		||||
string. It will throw an exception if any error is encountered, unless \a force
 | 
			
		||||
is \a true, in which case invalid characters will be skipped. It will
 | 
			
		||||
strip a BOM (byte order mark) from the beginning of a string.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
std::string ucs4ToAscii(const std::wstring& ucs4, bool force = false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Convert a UTF-8 string to UCS-4.
 | 
			
		||||
 | 
			
		||||
\param utf8 The (UTF-8 encoded) string to convert.
 | 
			
		||||
\param force Set this to \a true if you want to force the conversion to
 | 
			
		||||
    happen even in the face of invalid characters.
 | 
			
		||||
\returns A UCS-4 representation of \a utf8.
 | 
			
		||||
\throws BadSourceChar if an invalid byte sequence is encountered in the
 | 
			
		||||
    UTF-8 string.
 | 
			
		||||
 | 
			
		||||
This function can be used to convert a UTF-8 string into a UCS-4 string.
 | 
			
		||||
It will throw an exception if any error is encountered, unless \a force
 | 
			
		||||
is \a true, in which case invalid characters will be skipped. It will
 | 
			
		||||
strip a BOM (byte order mark) from the beginning of a string.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
std::wstring utf8ToUcs4(const std::string& utf8, bool force = false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Convert an ASCII string to UCS-4.
 | 
			
		||||
 | 
			
		||||
\param ascii The ASCII string to convert.
 | 
			
		||||
\param force Set this to \a true if you want to force the conversion to
 | 
			
		||||
    happen even in the face of invalid characters.
 | 
			
		||||
\returns A UCS-4 representation of \a ascii.
 | 
			
		||||
\throws BadSourceChar if an invalid byte (outside the range 0-x7F) is
 | 
			
		||||
    encountered in the ASCII string.
 | 
			
		||||
 | 
			
		||||
This function can be used to convert an ASCII string into a UCS-4
 | 
			
		||||
string. It will throw an exception if any error is encountered, unless
 | 
			
		||||
\a force is \a true, in which case invalid characters will be skipped.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
std::wstring asciiToUcs4(const std::string& ascii, bool force = false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Convert an 8-bit string to UCS-4.
 | 
			
		||||
 | 
			
		||||
\param str The 8-bit string to convert.
 | 
			
		||||
\returns A UCS-4 representation of \a str.
 | 
			
		||||
 | 
			
		||||
This function is used to perform a direct conversion of an 8-bit string
 | 
			
		||||
into UCS-4. It doesn't take any Unicode issues into account. It always
 | 
			
		||||
succeeds. This function can be used if the resultant string will be
 | 
			
		||||
checked for validity elsewhere anyway.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
std::wstring strToUcs4(const std::string& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,530 @@
 | 
			
		|||
/* lw-support/src/lib/String/strToInt.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//BEGIN // Auxiliary conversion functions //////////////////////////////
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
uint64_t strToIntDec(const std::wstring& str,
 | 
			
		||||
    std::wstring::size_type pos, std::wstring::size_type strLen)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t value = 0;
 | 
			
		||||
    for(; pos < strLen; ++pos) {
 | 
			
		||||
        int ch = str[pos];
 | 
			
		||||
        if(ch < '0' || ch > '9')
 | 
			
		||||
            throw ParseError(L"Not a decimal number.", str, pos);
 | 
			
		||||
        if(value > 1844674407370955161LL) throw NumericOverflow();
 | 
			
		||||
        value *= 10;
 | 
			
		||||
        if(~value < (uint64_t)(ch - '0')) throw NumericOverflow();
 | 
			
		||||
        value += ch - '0';
 | 
			
		||||
    }
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint64_t strToIntHex(const std::wstring& str,
 | 
			
		||||
    std::wstring::size_type pos, std::wstring::size_type strLen)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t value = 0;
 | 
			
		||||
    int maxDigRemaining = 16;
 | 
			
		||||
    for(; pos < strLen; ++pos) {
 | 
			
		||||
        if(!maxDigRemaining--) throw NumericOverflow();
 | 
			
		||||
        value <<= 4;
 | 
			
		||||
 | 
			
		||||
        int ch = str[pos];
 | 
			
		||||
        switch(ch) {
 | 
			
		||||
            case '0' ... '9':
 | 
			
		||||
                value |= ch - '0';
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'a' ... 'f':
 | 
			
		||||
                value |= ch - 'a' + 10;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case 'A' ... 'F':
 | 
			
		||||
                value |= ch - 'A' + 10;
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                throw ParseError(L"Not a decimal number.", str, pos);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
//END // Auxiliary conversion functions ////////////////////////////////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int64_t strToInt64(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    if(!strLen) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    bool negative = false;
 | 
			
		||||
    std::wstring::size_type pos = 0;
 | 
			
		||||
    uint64_t result;
 | 
			
		||||
 | 
			
		||||
    // check for sign
 | 
			
		||||
    if(str[0] == '-') {
 | 
			
		||||
        negative = true;
 | 
			
		||||
        ++pos;
 | 
			
		||||
    } else if(str[0] == '+') {
 | 
			
		||||
        ++pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) {
 | 
			
		||||
        throw ParseError(L"String only contained sign character.",
 | 
			
		||||
            str, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for hexadecimal
 | 
			
		||||
    if(str[pos] == '0' && ++pos != strLen &&
 | 
			
		||||
        (str[pos] == 'x' || str[pos] == 'X'))
 | 
			
		||||
    {
 | 
			
		||||
        if(++pos == strLen)
 | 
			
		||||
            throw ParseError(L"String only contained hex sequence.", str, pos);
 | 
			
		||||
        result = strToIntHex(str, pos, strLen);
 | 
			
		||||
    } else {
 | 
			
		||||
        if(pos == strLen) return 0;
 | 
			
		||||
        result = strToIntDec(str, pos, strLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for overflow
 | 
			
		||||
    if(negative && result == 0x8000000000000000LL) return -0x8000000000000000LL;
 | 
			
		||||
    if(result & ~0x7FFFFFFFFFFFFFFFLL) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    int64_t p = (int64_t)result;
 | 
			
		||||
    if(negative) p *= -1;
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int32_t strToInt32(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    if(!strLen) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    bool negative = false;
 | 
			
		||||
    std::wstring::size_type pos = 0;
 | 
			
		||||
    uint64_t result;
 | 
			
		||||
 | 
			
		||||
    // check for sign
 | 
			
		||||
    if(str[0] == '-') {
 | 
			
		||||
        negative = true;
 | 
			
		||||
        ++pos;
 | 
			
		||||
    } else if(str[0] == '+') {
 | 
			
		||||
        ++pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) {
 | 
			
		||||
        throw ParseError(L"String only contained sign character.",
 | 
			
		||||
            str, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for hexadecimal
 | 
			
		||||
    if(str[pos] == '0' && ++pos != strLen &&
 | 
			
		||||
        (str[pos] == 'x' || str[pos] == 'X'))
 | 
			
		||||
    {
 | 
			
		||||
        if(++pos == strLen)
 | 
			
		||||
            throw ParseError(L"String only contained hex sequence.", str, pos);
 | 
			
		||||
        result = strToIntHex(str, pos, strLen);
 | 
			
		||||
    } else {
 | 
			
		||||
        if(pos == strLen) return 0;
 | 
			
		||||
        result = strToIntDec(str, pos, strLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for overflow
 | 
			
		||||
    if(negative && result == 0x80000000) return -0x80000000;
 | 
			
		||||
    if(result & ~0x7FFFFFFF) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    int32_t p = (int32_t)result;
 | 
			
		||||
    if(negative) p *= -1;
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int16_t strToInt16(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    if(!strLen) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    bool negative = false;
 | 
			
		||||
    std::wstring::size_type pos = 0;
 | 
			
		||||
    uint64_t result;
 | 
			
		||||
 | 
			
		||||
    // check for sign
 | 
			
		||||
    if(str[0] == '-') {
 | 
			
		||||
        negative = true;
 | 
			
		||||
        ++pos;
 | 
			
		||||
    } else if(str[0] == '+') {
 | 
			
		||||
        ++pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) {
 | 
			
		||||
        throw ParseError(L"String only contained sign character.",
 | 
			
		||||
            str, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for hexadecimal
 | 
			
		||||
    if(str[pos] == '0' && ++pos != strLen &&
 | 
			
		||||
        (str[pos] == 'x' || str[pos] == 'X'))
 | 
			
		||||
    {
 | 
			
		||||
        if(++pos == strLen)
 | 
			
		||||
            throw ParseError(L"String only contained hex sequence.", str, pos);
 | 
			
		||||
        result = strToIntHex(str, pos, strLen);
 | 
			
		||||
    } else {
 | 
			
		||||
        if(pos == strLen) return 0;
 | 
			
		||||
        result = strToIntDec(str, pos, strLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for overflow
 | 
			
		||||
    if(negative && result == 0x8000) return -0x8000;
 | 
			
		||||
    if(result & ~0x7FFF) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    int16_t p = (int16_t)result;
 | 
			
		||||
    if(negative) p *= -1;
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int8_t strToInt8(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    if(!strLen) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    bool negative = false;
 | 
			
		||||
    std::wstring::size_type pos = 0;
 | 
			
		||||
    uint64_t result;
 | 
			
		||||
 | 
			
		||||
    // check for sign
 | 
			
		||||
    if(str[0] == '-') {
 | 
			
		||||
        negative = true;
 | 
			
		||||
        ++pos;
 | 
			
		||||
    } else if(str[0] == '+') {
 | 
			
		||||
        ++pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) {
 | 
			
		||||
        throw ParseError(L"String only contained sign character.",
 | 
			
		||||
            str, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for hexadecimal
 | 
			
		||||
    if(str[pos] == '0' && ++pos != strLen &&
 | 
			
		||||
        (str[pos] == 'x' || str[pos] == 'X'))
 | 
			
		||||
    {
 | 
			
		||||
        if(++pos == strLen)
 | 
			
		||||
            throw ParseError(L"String only contained hex sequence.", str, pos);
 | 
			
		||||
        result = strToIntHex(str, pos, strLen);
 | 
			
		||||
    } else {
 | 
			
		||||
        if(pos == strLen) return 0;
 | 
			
		||||
        result = strToIntDec(str, pos, strLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for overflow
 | 
			
		||||
    if(negative && result == 0x80) return -0x80;
 | 
			
		||||
    if(result & ~0x7F) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    int8_t p = (int8_t)result;
 | 
			
		||||
    if(negative) p *= -1;
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint64_t strToUInt64(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    if(!strLen) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type pos = 0;
 | 
			
		||||
 | 
			
		||||
    // check for sign
 | 
			
		||||
    if(str[0] == '-') {
 | 
			
		||||
        throw ParseError(L"Negative number found.", str, 0);
 | 
			
		||||
    } else if(str[0] == '+') {
 | 
			
		||||
        ++pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) {
 | 
			
		||||
        throw ParseError(L"String only contained sign character.",
 | 
			
		||||
            str, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for hexadecimal
 | 
			
		||||
    if(str[pos] == '0' && ++pos != strLen &&
 | 
			
		||||
        (str[pos] == 'x' || str[pos] == 'X'))
 | 
			
		||||
    {
 | 
			
		||||
        if(++pos == strLen)
 | 
			
		||||
            throw ParseError(L"String only contained hex sequence.", str, pos);
 | 
			
		||||
        return strToIntHex(str, pos, strLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) return 0;
 | 
			
		||||
    return strToIntDec(str, pos, strLen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint32_t strToUInt32(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    if(!strLen) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type pos = 0;
 | 
			
		||||
    uint64_t result;
 | 
			
		||||
 | 
			
		||||
    // check for sign
 | 
			
		||||
    if(str[0] == '-') {
 | 
			
		||||
        throw ParseError(L"Negative number found.", str, 0);
 | 
			
		||||
    } else if(str[0] == '+') {
 | 
			
		||||
        ++pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) {
 | 
			
		||||
        throw ParseError(L"String only contained sign character.",
 | 
			
		||||
            str, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for hexadecimal
 | 
			
		||||
    if(str[pos] == '0' && ++pos != strLen &&
 | 
			
		||||
        (str[pos] == 'x' || str[pos] == 'X'))
 | 
			
		||||
    {
 | 
			
		||||
        if(++pos == strLen)
 | 
			
		||||
            throw ParseError(L"String only contained hex sequence.", str, pos);
 | 
			
		||||
        result = strToIntHex(str, pos, strLen);
 | 
			
		||||
    } else {
 | 
			
		||||
        if(pos == strLen) return 0;
 | 
			
		||||
        result = strToIntDec(str, pos, strLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for overflow
 | 
			
		||||
    if(result & ~0xFFFFFFFF) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    return uint32_t(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint16_t strToUInt16(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    if(!strLen) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type pos = 0;
 | 
			
		||||
    uint64_t result;
 | 
			
		||||
 | 
			
		||||
    // check for sign
 | 
			
		||||
    if(str[0] == '-') {
 | 
			
		||||
        throw ParseError(L"Negative number found.", str, 0);
 | 
			
		||||
    } else if(str[0] == '+') {
 | 
			
		||||
        ++pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) {
 | 
			
		||||
        throw ParseError(L"String only contained sign character.",
 | 
			
		||||
            str, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for hexadecimal
 | 
			
		||||
    if(str[pos] == '0' && ++pos != strLen &&
 | 
			
		||||
        (str[pos] == 'x' || str[pos] == 'X'))
 | 
			
		||||
    {
 | 
			
		||||
        if(++pos == strLen)
 | 
			
		||||
            throw ParseError(L"String only contained hex sequence.", str, pos);
 | 
			
		||||
        result = strToIntHex(str, pos, strLen);
 | 
			
		||||
    } else {
 | 
			
		||||
        if(pos == strLen) return 0;
 | 
			
		||||
        result = strToIntDec(str, pos, strLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for overflow
 | 
			
		||||
    if(result & ~0xFFFF) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    return uint16_t(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
uint8_t strToUInt8(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    if(!strLen) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    std::wstring::size_type pos = 0;
 | 
			
		||||
    uint64_t result;
 | 
			
		||||
 | 
			
		||||
    // check for sign
 | 
			
		||||
    if(str[0] == '-') {
 | 
			
		||||
        throw ParseError(L"Negative number found.", str, 0);
 | 
			
		||||
    } else if(str[0] == '+') {
 | 
			
		||||
        ++pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(pos == strLen) {
 | 
			
		||||
        throw ParseError(L"String only contained sign character.",
 | 
			
		||||
            str, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for hexadecimal
 | 
			
		||||
    if(str[pos] == '0' && ++pos != strLen &&
 | 
			
		||||
        (str[pos] == 'x' || str[pos] == 'X'))
 | 
			
		||||
    {
 | 
			
		||||
        if(++pos == strLen)
 | 
			
		||||
            throw ParseError(L"String only contained hex sequence.", str, pos);
 | 
			
		||||
        result = strToIntHex(str, pos, strLen);
 | 
			
		||||
    } else {
 | 
			
		||||
        if(pos == strLen) return 0;
 | 
			
		||||
        result = strToIntDec(str, pos, strLen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // check for overflow
 | 
			
		||||
    if(result & ~0xFF) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    return uint8_t(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool strToBool(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
 | 
			
		||||
    // get string length
 | 
			
		||||
    std::wstring::size_type strLen = str.length();
 | 
			
		||||
    switch(strLen) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
        case 1: // "0" or "1"
 | 
			
		||||
            // check for numeric values
 | 
			
		||||
            if(str[0] == '0') return false;
 | 
			
		||||
            if(str[0] == '1') return true;
 | 
			
		||||
            throw ParseError(L"Unrecognised boolean value.", str, 0);
 | 
			
		||||
 | 
			
		||||
        case 4: // "true"
 | 
			
		||||
            if(tolower(str[0]) != 't' ||
 | 
			
		||||
                tolower(str[1]) != 'r' ||
 | 
			
		||||
                tolower(str[2]) != 'u' ||
 | 
			
		||||
                tolower(str[3]) != 'e')
 | 
			
		||||
                    throw ParseError(L"Unrecognised boolean value.", str, 0);
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case 5: // "false"
 | 
			
		||||
            if(tolower(str[0]) != 'f' ||
 | 
			
		||||
                tolower(str[1]) != 'a' ||
 | 
			
		||||
                tolower(str[2]) != 'l' ||
 | 
			
		||||
                tolower(str[3]) != 's' ||
 | 
			
		||||
                tolower(str[4]) != 'e')
 | 
			
		||||
                    throw ParseError(L"Unrecognised boolean value.", str, 0);
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    throw ParseError(L"Unrecognised boolean value.", str, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
long double strToLongDouble(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
    if(str.empty()) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    // perform conversion
 | 
			
		||||
    errno = 0;
 | 
			
		||||
    wchar_t* tailPtr = 0;
 | 
			
		||||
    long double d = wcstold(str.c_str(), &tailPtr);
 | 
			
		||||
 | 
			
		||||
    // check for errors
 | 
			
		||||
    if(*tailPtr) {
 | 
			
		||||
        throw ParseError(L"Not a valid floating point number.", str,
 | 
			
		||||
            tailPtr - str.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    if(errno) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
double strToDouble(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
    if(str.empty()) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    // perform conversion
 | 
			
		||||
    errno = 0;
 | 
			
		||||
    wchar_t* tailPtr = 0;
 | 
			
		||||
    double d = wcstod(str.c_str(), &tailPtr);
 | 
			
		||||
 | 
			
		||||
    // check for errors
 | 
			
		||||
    if(*tailPtr) {
 | 
			
		||||
        throw ParseError(L"Not a valid floating point number.", str,
 | 
			
		||||
            tailPtr - str.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    if(errno) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
float strToFloat(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
    if(str.empty()) throw ParseError(L"Empty string.", str, 0);
 | 
			
		||||
 | 
			
		||||
    // perform conversion
 | 
			
		||||
    errno = 0;
 | 
			
		||||
    wchar_t* tailPtr = 0;
 | 
			
		||||
    float d = wcstof(str.c_str(), &tailPtr);
 | 
			
		||||
 | 
			
		||||
    // check for errors
 | 
			
		||||
    if(*tailPtr) {
 | 
			
		||||
        throw ParseError(L"Not a valid floating point number.", str,
 | 
			
		||||
            tailPtr - str.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    if(errno) throw NumericOverflow();
 | 
			
		||||
 | 
			
		||||
    return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,229 @@
 | 
			
		|||
/* lw-support/src/lib/String/strToInt.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to an integer.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into an integer.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as an integer. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two types of number:
 | 
			
		||||
 - a decimal, with optional leading sign (i.e. -ddd, ddd or +ddd).
 | 
			
		||||
 - a hexadecimal, with optional leading sign, in C notation (i.e.
 | 
			
		||||
   -0xhhh, 0xhhh or +0xhhh), case insensitive.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
int64_t strToInt64(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to an integer.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into an integer.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as an integer. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two types of number:
 | 
			
		||||
 - a decimal, with optional leading sign (i.e. -ddd, ddd or +ddd).
 | 
			
		||||
 - a hexadecimal, with optional leading sign, in C notation (i.e.
 | 
			
		||||
   -0xhhh, 0xhhh or +0xhhh), case insensitive.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
int32_t strToInt32(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to an integer.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into an integer.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as an integer. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two types of number:
 | 
			
		||||
 - a decimal, with optional leading sign (i.e. -ddd, ddd or +ddd).
 | 
			
		||||
 - a hexadecimal, with optional leading sign, in C notation (i.e.
 | 
			
		||||
   -0xhhh, 0xhhh or +0xhhh), case insensitive.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
int16_t strToInt16(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to an integer.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into an integer.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as an integer. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two types of number:
 | 
			
		||||
 - a decimal, with optional leading sign (i.e. -ddd, ddd or +ddd).
 | 
			
		||||
 - a hexadecimal, with optional leading sign, in C notation (i.e.
 | 
			
		||||
   -0xhhh, 0xhhh or +0xhhh), case insensitive.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
int8_t strToInt8(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to an integer.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into an integer.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as an integer. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two types of number:
 | 
			
		||||
 - a decimal, with optional leading sign (i.e. ddd or +ddd).
 | 
			
		||||
 - a hexadecimal, with optional leading sign, in C notation (i.e.
 | 
			
		||||
   0xhhh or +0xhhh), case insensitive.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
uint64_t strToUInt64(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to an integer.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into an integer.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as an integer. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two types of number:
 | 
			
		||||
 - a decimal, with optional leading sign (i.e. ddd or +ddd).
 | 
			
		||||
 - a hexadecimal, with optional leading sign, in C notation (i.e.
 | 
			
		||||
   0xhhh or +0xhhh), case insensitive.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
uint32_t strToUInt32(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to an integer.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer, or
 | 
			
		||||
    if the integer is negative.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into an integer.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as an integer. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two types of number:
 | 
			
		||||
 - a decimal, with optional leading sign (i.e. ddd or +ddd).
 | 
			
		||||
 - a hexadecimal, with optional leading sign, in C notation (i.e.
 | 
			
		||||
   0xhhh or +0xhhh), case insensitive.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
uint16_t strToUInt16(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to an integer.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer, or
 | 
			
		||||
    if the integer is negative.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into an integer.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as an integer. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two types of number:
 | 
			
		||||
 - a decimal, with optional leading sign (i.e. ddd or +ddd).
 | 
			
		||||
 - a hexadecimal, with optional leading sign, in C notation (i.e.
 | 
			
		||||
   0xhhh or +0xhhh), case insensitive.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
uint8_t strToUInt8(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to a boolean.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse.
 | 
			
		||||
\throws ParseError if the string does not represent a valid boolean.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as a boolean. Any trailing
 | 
			
		||||
or leading whitespace is ignored. It understands two notations:
 | 
			
		||||
 - the strings "0" or "false" (case insensitive), for a false value.
 | 
			
		||||
 - the strings "1" or "true" (case insensitive), for a true value.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
bool strToBool(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to a number.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into a double-precision floating point number.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as a quad-precision
 | 
			
		||||
floating point number. Any trailing or leading whitespace is ignored.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
long double strToLongDouble(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to a number.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into a double-precision floating point number.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as a double-precision
 | 
			
		||||
floating point number. Any trailing or leading whitespace is ignored.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
double strToDouble(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Parse a string, converting it to a number.
 | 
			
		||||
 | 
			
		||||
\param str The string to parse
 | 
			
		||||
\throws ParseError if the string does not represent a valid integer.
 | 
			
		||||
\throws NumericOverflow if the value represented in the string does not
 | 
			
		||||
    fit into a double-precision floating point number.
 | 
			
		||||
\returns The value represented in the string.
 | 
			
		||||
 | 
			
		||||
This function parses a string and returns it as a single-precision
 | 
			
		||||
floating point number. Any trailing or leading whitespace is ignored.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
float strToFloat(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/CompletionNotifier.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CompletionNotifier::~CompletionNotifier()
 | 
			
		||||
{
 | 
			
		||||
    pthread_cond_broadcast(&cond);
 | 
			
		||||
    while(true) {
 | 
			
		||||
        pthread_mutex_lock(&mut);
 | 
			
		||||
        if(!pthread_cond_destroy(&cond)) break;
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
    }
 | 
			
		||||
    pthread_mutex_unlock(&mut);
 | 
			
		||||
    pthread_mutex_destroy(&mut);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionNotifier::wait(int maxTime)
 | 
			
		||||
{
 | 
			
		||||
    if(maxTime < 0) {
 | 
			
		||||
        wait();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct timespec ts;
 | 
			
		||||
    ts.tv_sec = maxTime / 1000;
 | 
			
		||||
    ts.tv_nsec = (maxTime % 1000) * 1000000;
 | 
			
		||||
    pthread_mutex_lock(&mut);
 | 
			
		||||
 | 
			
		||||
    while(!done) {
 | 
			
		||||
        switch(pthread_cond_timedwait(&cond, &mut, &ts)) {
 | 
			
		||||
            case ETIMEDOUT: {
 | 
			
		||||
                pthread_mutex_unlock(&mut);
 | 
			
		||||
                throw Timeout();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            case 0: break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pthread_mutex_unlock(&mut);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/CompletionNotifier.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Completion object.
 | 
			
		||||
 | 
			
		||||
A completion object is a synchronisation device used in the following
 | 
			
		||||
manner:
 | 
			
		||||
 - interested parties register for a notification,
 | 
			
		||||
 - the task is begun,
 | 
			
		||||
 - on completion, all registered parties are notified.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class CompletionNotifier {
 | 
			
		||||
private:
 | 
			
		||||
    pthread_mutex_t mut;
 | 
			
		||||
    pthread_cond_t cond;
 | 
			
		||||
    bool done;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    CompletionNotifier()
 | 
			
		||||
        : done(false)
 | 
			
		||||
    {
 | 
			
		||||
        pthread_mutex_init(&mut, 0);
 | 
			
		||||
        pthread_cond_init(&cond, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    ~CompletionNotifier();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Signal completion.
 | 
			
		||||
 | 
			
		||||
    This will wake all threads currently waiting on the completion
 | 
			
		||||
    condition, and will record the object as completed. Any future calls
 | 
			
		||||
    to the wait() function will automatically succeed.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline void signal()
 | 
			
		||||
    {
 | 
			
		||||
        pthread_mutex_lock(&mut);
 | 
			
		||||
        done = true;
 | 
			
		||||
        pthread_cond_broadcast(&cond);
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Wait for completion.
 | 
			
		||||
 | 
			
		||||
    Threads calling this function will block until the object is marked
 | 
			
		||||
    as complete.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline void wait()
 | 
			
		||||
    {
 | 
			
		||||
        if(done) return; // avoid lock contention if possible
 | 
			
		||||
 | 
			
		||||
        pthread_mutex_lock(&mut);
 | 
			
		||||
        if(done) { // in case variable was changed before lock acquired
 | 
			
		||||
            pthread_mutex_unlock(&mut);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pthread_cond_wait(&cond, &mut);
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Thrown if a timeout occurs.
 | 
			
		||||
    class Timeout : public ProgramException { };
 | 
			
		||||
 | 
			
		||||
    /*! \brief Wait for completion, with a timeout.
 | 
			
		||||
 | 
			
		||||
    \param maxTime The maximum amount of time to wait, in milliseconds.
 | 
			
		||||
    \throws CompletionNotifier::Timeout if a timeout occurs.
 | 
			
		||||
 | 
			
		||||
    Threads calling this function will block until the object is marked
 | 
			
		||||
    as complete. If you pass an negative value for \a maxTime, then it
 | 
			
		||||
    will wait indefinitely. A zero value will cause it to return
 | 
			
		||||
    immediately.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void wait(int maxTime);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/Mutex.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Mutex::Mutex(bool recursive)
 | 
			
		||||
{
 | 
			
		||||
// note the function calls here always succeed
 | 
			
		||||
    // tell pthreads we want a "normal" (simple) mutex
 | 
			
		||||
    pthread_mutexattr_t attr;
 | 
			
		||||
    pthread_mutexattr_init(&attr);
 | 
			
		||||
    if(recursive) pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
 | 
			
		||||
    else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
 | 
			
		||||
 | 
			
		||||
    // initialise the mutex
 | 
			
		||||
    pthread_mutex_init(&mut, &attr);
 | 
			
		||||
 | 
			
		||||
    // clean up
 | 
			
		||||
    pthread_mutexattr_destroy(&attr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Mutex::~Mutex()
 | 
			
		||||
{
 | 
			
		||||
    // destroy the mutex
 | 
			
		||||
    pthread_mutex_destroy(&mut);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/Mutex.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Mutex.
 | 
			
		||||
 | 
			
		||||
This object may be one of two types: fast or recursive. A fast mutex
 | 
			
		||||
cannot be locked twice in the same thread. Attempting to do so will
 | 
			
		||||
result in undefined behaviour (most likely deadlock).
 | 
			
		||||
 | 
			
		||||
However, a recursive mutex can be locked twice in the same thread. It
 | 
			
		||||
must be unlocked an equal number of times. It will only become available
 | 
			
		||||
to other threads when each lock() call has been matched by an unlock()
 | 
			
		||||
call.
 | 
			
		||||
 | 
			
		||||
\warning Behaviour is undefined if a locked mutex is destroyed.
 | 
			
		||||
         Behaviour is undefined if a thread which does not own the lock
 | 
			
		||||
         tries to unlock the mutex.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class Mutex {
 | 
			
		||||
private:
 | 
			
		||||
    pthread_mutex_t mut;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param recursive Indicates whether the mutex should be fast or
 | 
			
		||||
                     recursive.
 | 
			
		||||
 | 
			
		||||
    Creates the mutex object. Note that, once constructed, the type of
 | 
			
		||||
    a mutex cannot be changed. Mutexes are always constructed unlocked.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Mutex(bool recursive = false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Destructor.
 | 
			
		||||
    ~Mutex();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Lock the mutex.
 | 
			
		||||
    inline void lock()
 | 
			
		||||
    {
 | 
			
		||||
        pthread_mutex_lock(&mut);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Unlock the mutex.
 | 
			
		||||
    inline void unlock()
 | 
			
		||||
    {
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,184 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/MutexPadlock.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Mutex utility class for safe locking/unlocking.
 | 
			
		||||
 | 
			
		||||
A Mutex \e padlock will unlock its associated Mutex when it is destroyed.
 | 
			
		||||
This means that you can use it to lock a Mutex and then safely unlock it
 | 
			
		||||
regardless of how the function exit occurs. A padlock is constructed
 | 
			
		||||
unlocked, and you may call the lock and unlock functions at will.
 | 
			
		||||
 | 
			
		||||
\warning Behaviour is undefined if you attempt to unlock an
 | 
			
		||||
         already-unlocked Mutex. This type of padlock is not recursive;
 | 
			
		||||
         calling lock twice in succession will probably result in
 | 
			
		||||
         deadlock. But see MutexPadlockRecursive.
 | 
			
		||||
 | 
			
		||||
\sa MutexHolder
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class MutexPadlock {
 | 
			
		||||
private:
 | 
			
		||||
    Mutex& mut;
 | 
			
		||||
    bool locked;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param m The Mutex to operate on.
 | 
			
		||||
 | 
			
		||||
    Constructs the padlock object, but does not lock the Mutex.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    MutexPadlock(Mutex& m)
 | 
			
		||||
        : mut(m), locked(false)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Destructor (unlocks padlock, if locked).
 | 
			
		||||
 | 
			
		||||
    If the padlock is locked, the destructor will unlock it. However, if
 | 
			
		||||
    it is not locked, then nothing will happen.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ~MutexPadlock()
 | 
			
		||||
    {
 | 
			
		||||
        if(locked) mut.unlock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Lock the Mutex.
 | 
			
		||||
    inline void lock()
 | 
			
		||||
    {
 | 
			
		||||
        mut.lock();
 | 
			
		||||
        locked = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Unlock the Mutex.
 | 
			
		||||
    inline void unlock()
 | 
			
		||||
    {
 | 
			
		||||
        mut.unlock();
 | 
			
		||||
        locked = false;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Mutex utility class for safe, recursive locking/unlocking.
 | 
			
		||||
 | 
			
		||||
A Mutex \e padlock will unlock its associated Mutex when it is destroyed.
 | 
			
		||||
This means that you can use it to lock a Mutex and then safely unlock it
 | 
			
		||||
regardless of how the function exit occurs. A padlock is constructed
 | 
			
		||||
unlocked, and you may call the lock and unlock functions at will.
 | 
			
		||||
 | 
			
		||||
Note that this padlock allows recursive calls. It even works with fast
 | 
			
		||||
Mutexes, because it keeps count internally itself.
 | 
			
		||||
 | 
			
		||||
\warning Behaviour is undefined if you attempt to unlock an
 | 
			
		||||
         already-unlocked Mutex.
 | 
			
		||||
 | 
			
		||||
\sa MutexHolder
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class MutexPadlockRecursive {
 | 
			
		||||
private:
 | 
			
		||||
    Mutex& mut;
 | 
			
		||||
    int count;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param m The Mutex to operate on.
 | 
			
		||||
 | 
			
		||||
    Constructs the padlock object, but does not lock the Mutex.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    MutexPadlockRecursive(Mutex& m)
 | 
			
		||||
        : mut(m), count(0)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Destructor (unlocks padlock, if locked).
 | 
			
		||||
 | 
			
		||||
    If the padlock is locked, the destructor will unlock it. However, if
 | 
			
		||||
    it is not locked, then nothing will happen.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ~MutexPadlockRecursive()
 | 
			
		||||
    {
 | 
			
		||||
        if(count) mut.unlock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Lock the Mutex.
 | 
			
		||||
    inline void lock()
 | 
			
		||||
    {
 | 
			
		||||
        if(!count++) mut.lock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Unlock the Mutex.
 | 
			
		||||
    inline void unlock()
 | 
			
		||||
    {
 | 
			
		||||
        if(!--count) mut.unlock();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Mutex utility class for safe locking/unlocking.
 | 
			
		||||
 | 
			
		||||
On construction, this object locks its associated Mutex. On destruction,
 | 
			
		||||
it releases the lock. In essence, it is a less flexible (but less error
 | 
			
		||||
prone) Mutex padlock.
 | 
			
		||||
 | 
			
		||||
\sa MutexPadlock
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class MutexHolder {
 | 
			
		||||
private:
 | 
			
		||||
    Mutex& mut;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param m The Mutex to lock.
 | 
			
		||||
 | 
			
		||||
    Locks the Mutex.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    MutexHolder(Mutex& m)
 | 
			
		||||
        : mut(m)
 | 
			
		||||
    {
 | 
			
		||||
        mut.lock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Destructor.
 | 
			
		||||
 | 
			
		||||
    Unlocks the Mutex.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ~MutexHolder()
 | 
			
		||||
    {
 | 
			
		||||
        mut.unlock();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/Semaphore.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Semaphore::Semaphore(unsigned int init)
 | 
			
		||||
    : val(init), waiting(0), err(false)
 | 
			
		||||
{
 | 
			
		||||
    pthread_mutex_init(&mut, 0);
 | 
			
		||||
    pthread_cond_init(&cond, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Semaphore::~Semaphore()
 | 
			
		||||
{
 | 
			
		||||
    signalError();
 | 
			
		||||
    while(true) {
 | 
			
		||||
        pthread_mutex_lock(&mut);
 | 
			
		||||
        if(!waiting) break;
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
        pthread_yield();
 | 
			
		||||
    }
 | 
			
		||||
    pthread_mutex_unlock(&mut);
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_destroy(&mut);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Semaphore::wait(int maxTime)
 | 
			
		||||
{
 | 
			
		||||
    if(maxTime < 0) {
 | 
			
		||||
        wait();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // get current time
 | 
			
		||||
    struct timeval tv;
 | 
			
		||||
    gettimeofday(&tv, 0);
 | 
			
		||||
 | 
			
		||||
    // compute absolute time
 | 
			
		||||
    struct timespec ts;
 | 
			
		||||
    ts.tv_sec = tv.tv_sec + (maxTime / 1000);
 | 
			
		||||
    ts.tv_nsec = (tv.tv_usec * 1000) + (maxTime % 1000) * tenExp6;
 | 
			
		||||
    if(ts.tv_nsec > tenExp9) {
 | 
			
		||||
        ++ts.tv_sec;
 | 
			
		||||
        ts.tv_nsec -= tenExp9;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pthread_mutex_lock(&mut);
 | 
			
		||||
    ++waiting;
 | 
			
		||||
 | 
			
		||||
    while(!val && !err) {
 | 
			
		||||
        switch(pthread_cond_timedwait(&cond, &mut, &ts)) {
 | 
			
		||||
            case ETIMEDOUT: {
 | 
			
		||||
                --waiting;
 | 
			
		||||
                pthread_mutex_unlock(&mut);
 | 
			
		||||
                throw Timeout();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            case 0: break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if(err) {
 | 
			
		||||
        --waiting;
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
        throw Error();
 | 
			
		||||
    }
 | 
			
		||||
    --val;
 | 
			
		||||
    --waiting;
 | 
			
		||||
    pthread_mutex_unlock(&mut);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,148 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/Semaphore.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Semaphore object.
 | 
			
		||||
 | 
			
		||||
Supports the usual signal and wait operations, as well as manipulation
 | 
			
		||||
of the value. In addition, there is an error handling mechanism which
 | 
			
		||||
can be used to signal every thread waiting on the semaphore that it has
 | 
			
		||||
become invalid.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class Semaphore {
 | 
			
		||||
private:
 | 
			
		||||
    pthread_mutex_t mut;
 | 
			
		||||
    pthread_cond_t cond;
 | 
			
		||||
    int val, waiting;
 | 
			
		||||
    bool err;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param init The initial value of the semaphore.
 | 
			
		||||
 | 
			
		||||
    Creates the semaphore object, and sets its initial value to
 | 
			
		||||
    \a init.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Semaphore(unsigned int init = 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Destructor.
 | 
			
		||||
 | 
			
		||||
    Destroys the semaphore. Any threads currently waiting on the
 | 
			
		||||
    semaphore receive an error signal, so it is safe to delete a
 | 
			
		||||
    semaphore object while there are threads waiting on it.
 | 
			
		||||
 | 
			
		||||
    \warning If a semaphore is deleted in the time between a thread
 | 
			
		||||
             deciding to call the wait() function and the time the
 | 
			
		||||
             actual system call is executed, behaviour is undefined.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ~Semaphore();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Semaphore error signal.
 | 
			
		||||
 | 
			
		||||
    Thrown if a thread is waiting on the semaphore when an error
 | 
			
		||||
    condition is signalled. This also occurs when the semaphore is
 | 
			
		||||
    destroyed but some threads are waiting on it.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    class Error : public ProgramException { };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Thrown if a semaphore wait operation times out.
 | 
			
		||||
    class Timeout : public ProgramException { };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Increment / post / signal the semaphore.
 | 
			
		||||
    inline void signal()
 | 
			
		||||
    {
 | 
			
		||||
        pthread_mutex_lock(&mut);
 | 
			
		||||
        ++val;
 | 
			
		||||
        pthread_cond_signal(&cond);
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Decrement / wait on the semaphore.
 | 
			
		||||
 | 
			
		||||
    \throws Error if an error is signalled or the semaphore destroyed.
 | 
			
		||||
 | 
			
		||||
    Decrements the value of the semaphore. If the value is zero when
 | 
			
		||||
    this function is called, the calling thread is instead put to sleep,
 | 
			
		||||
    to be awoken when an error is signalled or the semaphore is posted.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline void wait()
 | 
			
		||||
    {
 | 
			
		||||
        pthread_mutex_lock(&mut);
 | 
			
		||||
        ++waiting;
 | 
			
		||||
        while(!val && !err) pthread_cond_wait(&cond, &mut);
 | 
			
		||||
        if(err) {
 | 
			
		||||
            --waiting;
 | 
			
		||||
            pthread_mutex_unlock(&mut);
 | 
			
		||||
            throw Error();
 | 
			
		||||
        }
 | 
			
		||||
        --val;
 | 
			
		||||
        --waiting;
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Decrement / wait on the semaphore, with a timeout.
 | 
			
		||||
 | 
			
		||||
    \param maxTime The maximum length of time to wait before timing
 | 
			
		||||
                    out, in milliseconds.
 | 
			
		||||
    \throws Error if an error is signalled or the semaphore destroyed.
 | 
			
		||||
    \throws Timeout if the timeout is reached before the thread can
 | 
			
		||||
                    decrement the semaphore.
 | 
			
		||||
 | 
			
		||||
    Decrements the value of the semaphore. If the value is zero when
 | 
			
		||||
    this function is called, the calling thread is instead put to sleep,
 | 
			
		||||
    to be awoken when an error is signalled or the semaphore is posted.
 | 
			
		||||
    This version includes a timeout. If you pass a negative \a maxTime,
 | 
			
		||||
    it will wait indefinitely; a zero value will cause the semaphore to
 | 
			
		||||
    be decremented immediately or an error will be thrown.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void wait(int maxTime);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Signal error.
 | 
			
		||||
    inline void signalError()
 | 
			
		||||
    {
 | 
			
		||||
        pthread_mutex_lock(&mut);
 | 
			
		||||
        err = true;
 | 
			
		||||
        pthread_cond_broadcast(&cond);
 | 
			
		||||
        pthread_mutex_unlock(&mut);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Retrieve the semaphore's current value.
 | 
			
		||||
    inline int value()
 | 
			
		||||
    {
 | 
			
		||||
        if(err) throw Error();
 | 
			
		||||
        return val;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,108 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/Thread.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const size_t Thread::minStackSize = PTHREAD_STACK_MIN;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void* Thread::taskWrapper(void* v)
 | 
			
		||||
{
 | 
			
		||||
    Thread* self = (Thread*)v;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        self->semStart.wait();
 | 
			
		||||
        self->run();
 | 
			
		||||
    }
 | 
			
		||||
    catch(...) {
 | 
			
		||||
        self->exception = true;
 | 
			
		||||
    }
 | 
			
		||||
    self->done = true;
 | 
			
		||||
    self->sem.signalError();
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Thread::Thread(size_t stackSize)
 | 
			
		||||
    : done(false), exception(false)
 | 
			
		||||
{
 | 
			
		||||
    pthread_attr_t attr;
 | 
			
		||||
    pthread_attr_init(&attr);
 | 
			
		||||
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 | 
			
		||||
    if(stackSize) pthread_attr_setstacksize(&attr, std::min(stackSize, minStackSize));
 | 
			
		||||
 | 
			
		||||
    int err = pthread_create(&thr, &attr, taskWrapper, this);
 | 
			
		||||
    if(err) throw SystemError(err);
 | 
			
		||||
 | 
			
		||||
    pthread_attr_destroy(&attr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Thread::~Thread()
 | 
			
		||||
{
 | 
			
		||||
    wait(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Thread::start()
 | 
			
		||||
{
 | 
			
		||||
    semStart.signal();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Thread::signalQuit()
 | 
			
		||||
{
 | 
			
		||||
    semStart.signalError();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Thread::wait(int timeout) const
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        sem.wait(timeout);
 | 
			
		||||
    }
 | 
			
		||||
    catch(Semaphore::Error&) { }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Thread::sleep(const ElapsedTime& time)
 | 
			
		||||
{
 | 
			
		||||
    struct timespec go, remain;
 | 
			
		||||
 | 
			
		||||
    lldiv_t div = lldiv(time.to_ns(), tenExp9);
 | 
			
		||||
    go.tv_sec = div.quot;
 | 
			
		||||
    go.tv_nsec = div.rem;
 | 
			
		||||
    while(go.tv_sec || go.tv_nsec) {
 | 
			
		||||
        if(!nanosleep(&go, &remain)) break;
 | 
			
		||||
        if(errno == EINTR) go = remain;
 | 
			
		||||
        else throw lw::SystemError();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Thread::dbg_backtrace()
 | 
			
		||||
{
 | 
			
		||||
    void* array[200];
 | 
			
		||||
    size_t size;
 | 
			
		||||
 | 
			
		||||
    size = ::backtrace(array, sizeof(array) / sizeof(void*));
 | 
			
		||||
    ::backtrace_symbols_fd(array, size, 2); /* 2 = stderr */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,153 @@
 | 
			
		|||
/* lw-support/src/lib/Thread/Thread.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Thread object.
 | 
			
		||||
 | 
			
		||||
This class provides multithreading facilities. You must override its
 | 
			
		||||
run() function. The thread is begun after the first call to the start()
 | 
			
		||||
function. The thread's execution finishes once run() returns.
 | 
			
		||||
 | 
			
		||||
The Thread object also contains some static members, yield() and
 | 
			
		||||
sleep(), that affect the \em calling thread.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class Thread : public Uncopyable {
 | 
			
		||||
private:
 | 
			
		||||
    pthread_t thr;
 | 
			
		||||
    mutable Semaphore sem;
 | 
			
		||||
    bool done, exception; // to store the system state
 | 
			
		||||
 | 
			
		||||
    Semaphore semStart;
 | 
			
		||||
    static void* taskWrapper(void*);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /*! \brief Thread function (override this).
 | 
			
		||||
 | 
			
		||||
    This method must be overridden to provide the thread's behaviour. It
 | 
			
		||||
    may either exit normally or by throwing an exception; this can be
 | 
			
		||||
    determined through the isDone() and isException() functions.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void run() = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param stackSize The size of the stack, in bytes. Must be at least
 | 
			
		||||
        lw::Thread::minStackSize, or 0 for the system default.
 | 
			
		||||
    \throws SystemError if a system error occurs.
 | 
			
		||||
 | 
			
		||||
    Constructing a new thread object starts up a new thread. The thread
 | 
			
		||||
    blocks on a semaphore until you call start() for the first time.
 | 
			
		||||
    This is to avoid the situation where the thread calls a method or
 | 
			
		||||
    refers to a variable in the derived class which may not have been
 | 
			
		||||
    fully instantiated yet.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Thread(size_t stackSize = 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Destructor.
 | 
			
		||||
 | 
			
		||||
    When deleted, the thread object's destructor will free up any thread
 | 
			
		||||
    resources. It will wait for the thread to quit first.
 | 
			
		||||
 | 
			
		||||
    \warning The derived class's destructor will be called first, even
 | 
			
		||||
        while the thread is still running. You should probably call
 | 
			
		||||
        wait() in your derived class's destructor.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual ~Thread();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Start thread running.
 | 
			
		||||
    void start();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Minimum stack size, in bytes.
 | 
			
		||||
    static const size_t minStackSize;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Reports whether the thread has finished executing.
 | 
			
		||||
    bool isDone() const
 | 
			
		||||
    {
 | 
			
		||||
        return done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Reports whether the thread finished because of an exception.
 | 
			
		||||
    bool isException() const
 | 
			
		||||
    {
 | 
			
		||||
        return exception;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Wait for the thread to finish, with a timeout.
 | 
			
		||||
 | 
			
		||||
    \param timeout The maximum length of time to wait before timing
 | 
			
		||||
        out, in milliseconds (negative means forever).
 | 
			
		||||
    \throws Semaphore::Timeout if the timeout is reached before the
 | 
			
		||||
        thread has finished.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void wait(int timeout) const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Signal that the thread needs to quit.
 | 
			
		||||
 | 
			
		||||
    This function, by default, does nothing. However, if you have some
 | 
			
		||||
    specific way of signalling to your thread function that it should
 | 
			
		||||
    quit, then you can use that method here.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void signalQuit();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Yields CPU to another thread.
 | 
			
		||||
 | 
			
		||||
    Briefly yields the CPU to another thread, but does not put the
 | 
			
		||||
    process to sleep.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static inline void yield()
 | 
			
		||||
    {
 | 
			
		||||
        pthread_yield();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Causes the current thread to sleep for a length of time.
 | 
			
		||||
    static void sleep(const ElapsedTime& time);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Prints a stack trace.
 | 
			
		||||
 | 
			
		||||
    This is a debugging function that prints a stack trace onto stderr. It assumes that fd 2 is
 | 
			
		||||
    stderr. It will also be confused by compiler optimisations. See the `backtrace()' libc function
 | 
			
		||||
    for details.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static void dbg_backtrace();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,817 @@
 | 
			
		|||
/* lw-support/src/lib/Time/Date.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Date::fixupDate()
 | 
			
		||||
{
 | 
			
		||||
    while(ord < 1) {
 | 
			
		||||
        --year;
 | 
			
		||||
    ord += isLeapYear(year) ? 366 : 365;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
while(ord > (isLeapYear(year) ? 366 : 365)) {
 | 
			
		||||
ord -= isLeapYear(year) ? 366 : 365;
 | 
			
		||||
    ++year;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* This comes from the algorithm given on Wikipedia at
 | 
			
		||||
http://en.wikipedia.org/wiki/Calculating_the_day_of_the_week */
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
inline int weekDayJan0(int year)
 | 
			
		||||
{
 | 
			
		||||
    div_t cent = div(year, 100);
 | 
			
		||||
    div_t leap = div(cent.quot, 4);
 | 
			
		||||
 | 
			
		||||
    int centOffset = (3 - leap.rem) * 2;
 | 
			
		||||
    int yrOffset = cent.rem + ((cent.rem + 3) >> 2) - 1;
 | 
			
		||||
 | 
			
		||||
    return (centOffset + yrOffset) % 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date::Date(int year, int month, int day)
 | 
			
		||||
    : year(year)
 | 
			
		||||
{
 | 
			
		||||
    if(month < 1 || month > 12 || day < 1 || day > 31 ||
 | 
			
		||||
       !(ord = (isLeapYear(year) ? leapMDtoOrd : normalMDtoOrd)[month - 1][day - 1]) )
 | 
			
		||||
    {
 | 
			
		||||
        throw BadDate();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date::Date(int year, int ordinalDay)
 | 
			
		||||
    : year(year), ord(ordinalDay)
 | 
			
		||||
{
 | 
			
		||||
    if(ord < 1 || ord > 366 || (ord == 366 && !isLeapYear(year))) {
 | 
			
		||||
        throw BadDate();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date Date::now()
 | 
			
		||||
{
 | 
			
		||||
    time_t t = time(0);
 | 
			
		||||
    struct tm tt;
 | 
			
		||||
    localtime_r(&t, &tt);
 | 
			
		||||
    return Date(tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date Date::fromWeek(int year, int week, int weekDay)
 | 
			
		||||
{
 | 
			
		||||
    // sanity checks
 | 
			
		||||
    if(weekDay < 1 || weekDay > 7 || week < 1 || week > 53)
 | 
			
		||||
        throw BadDate();
 | 
			
		||||
    if(week == 53 && (weekDayJan0(year) != 3 &&
 | 
			
		||||
                      (!isLeapYear(year) || weekDayJan0(year) != 2)))
 | 
			
		||||
        throw BadDate();
 | 
			
		||||
 | 
			
		||||
    // find date of Monday, week 1
 | 
			
		||||
    Date d(year, 1);
 | 
			
		||||
    switch(weekDayJan0(year)) {
 | 
			
		||||
    case 0: // Sunday
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 1: // Monday
 | 
			
		||||
        d -= 1;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 2:
 | 
			
		||||
        d -= 2;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 3:
 | 
			
		||||
        d -= 3;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 4:
 | 
			
		||||
        d += 3;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 5:
 | 
			
		||||
        d += 2;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 6:
 | 
			
		||||
        d += 1;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // now move to correct week and day
 | 
			
		||||
    d += (week - 1) * 7 + (weekDay - 1);
 | 
			
		||||
 | 
			
		||||
    return d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int Date::getMonth() const
 | 
			
		||||
{
 | 
			
		||||
return (isLeapYear(year) ? leapOrdtoMD : normalOrdtoMD)[ord][0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int Date::getDay() const
 | 
			
		||||
{
 | 
			
		||||
return (isLeapYear(year) ? leapOrdtoMD : normalOrdtoMD)[ord][1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int Date::getWeekDay() const
 | 
			
		||||
{
 | 
			
		||||
    int wd = (weekDayJan0(year) + ord) % 7;
 | 
			
		||||
return wd ?: 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int Date::getWeek() const
 | 
			
		||||
{
 | 
			
		||||
    if(isLeapYear(year)) return leapOrdtoWeek[weekDayJan0(year)][ord];
 | 
			
		||||
    else return normalOrdtoWeek[weekDayJan0(year)][ord];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date& Date::operator+=(int numDays)
 | 
			
		||||
{
 | 
			
		||||
    ord += numDays;
 | 
			
		||||
    fixupDate();
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date& Date::operator-=(int numDays)
 | 
			
		||||
{
 | 
			
		||||
    ord -= numDays;
 | 
			
		||||
    fixupDate();
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date& Date::operator++()
 | 
			
		||||
{
 | 
			
		||||
    ++ord;
 | 
			
		||||
    fixupDate();
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date Date::operator++(int)
 | 
			
		||||
{
 | 
			
		||||
    Date copy(*this);
 | 
			
		||||
    ++ord;
 | 
			
		||||
    fixupDate();
 | 
			
		||||
    return copy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date& Date::operator--()
 | 
			
		||||
{
 | 
			
		||||
    --ord;
 | 
			
		||||
    fixupDate();
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Date Date::operator--(int)
 | 
			
		||||
{
 | 
			
		||||
    Date copy(*this);
 | 
			
		||||
    --ord;
 | 
			
		||||
    fixupDate();
 | 
			
		||||
    return copy;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int Date::operator-(const Date& sub) const
 | 
			
		||||
{
 | 
			
		||||
    int diff = ord - sub.ord;
 | 
			
		||||
 | 
			
		||||
    for(int xYear = year; xYear < sub.year; ++xYear) {
 | 
			
		||||
    diff -= isLeapYear(xYear) ? 366 : 365;
 | 
			
		||||
    }
 | 
			
		||||
    for(int yYear = year; yYear > sub.year; --yYear) {
 | 
			
		||||
    diff += isLeapYear(yYear - 1) ? 366 : 365;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return diff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* A good ISO8601 description is at Wikipedia:
 | 
			
		||||
http://en.wikipedia.org/wiki/ISO_8601 */
 | 
			
		||||
 | 
			
		||||
std::wstring Date::toString(bool extended, ISO8601Format format,
 | 
			
		||||
                            ISO8601Precision precision) const
 | 
			
		||||
{
 | 
			
		||||
    // sanity check arguments
 | 
			
		||||
    if(format == ISO8601FormatDetect) throw BadFormat();
 | 
			
		||||
    switch(precision) {
 | 
			
		||||
    case ISO8601PrecisionFull:
 | 
			
		||||
    case ISO8601PrecisionDay:
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case ISO8601PrecisionMonth:
 | 
			
		||||
    case ISO8601PrecisionYear:
 | 
			
		||||
        if(format != ISO8601FormatCalendar) throw BadFormat();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case ISO8601PrecisionWeek:
 | 
			
		||||
        if(format != ISO8601FormatWeekDate) throw BadFormat();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        throw BadFormat();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // set up output stream
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
    o << std::setfill(L'0');
 | 
			
		||||
 | 
			
		||||
    // deal with the weekdate format
 | 
			
		||||
    if(format == ISO8601FormatWeekDate) {
 | 
			
		||||
        int wk = getWeek();
 | 
			
		||||
        if(!wk) {
 | 
			
		||||
            o << std::setw(4) << (year + 1);
 | 
			
		||||
            if(extended) o << '-';
 | 
			
		||||
            o << "W01";
 | 
			
		||||
 | 
			
		||||
        } else if(wk < 0) {
 | 
			
		||||
            o << std::setw(4) << (year - 1);
 | 
			
		||||
            if(extended) o << '-';
 | 
			
		||||
            o << 'W' << std::setw(2) << (-wk);
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            o << std::setw(4) << year;
 | 
			
		||||
            if(extended) o << '-';
 | 
			
		||||
            o << 'W' << std::setw(2) << wk;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(precision == ISO8601PrecisionWeek) return o.str();
 | 
			
		||||
        if(extended) o << '-';
 | 
			
		||||
        o << getWeekDay();
 | 
			
		||||
        return o.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // output the year
 | 
			
		||||
    o << std::setw(4) << year;
 | 
			
		||||
 | 
			
		||||
    // deal with the ordinal format
 | 
			
		||||
    if(format == ISO8601FormatOrdinal) {
 | 
			
		||||
        if(extended) o << L'-';
 | 
			
		||||
        o << std::setw(3) << getOrdinal();
 | 
			
		||||
        return o.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // output the month and (possibly) day
 | 
			
		||||
    switch(precision) {
 | 
			
		||||
    case ISO8601PrecisionFull:
 | 
			
		||||
    case ISO8601PrecisionDay:
 | 
			
		||||
        if(extended) o << L'-';
 | 
			
		||||
        o << std::setw(2) << getMonth();
 | 
			
		||||
        if(extended) o << L'-';
 | 
			
		||||
        o << std::setw(2) << getDay();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case ISO8601PrecisionMonth:
 | 
			
		||||
        if(extended) o << L'-';
 | 
			
		||||
        o << std::setw(2) << getMonth();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
/* The ISO8601 date parsing subsystem uses a bunch of parallel state
 | 
			
		||||
machines. Each active state machine is fed a character; if it can't cope
 | 
			
		||||
with that character, the machine becomes inactive. On completion, each
 | 
			
		||||
machine is asked for a probability. The machine with highest probability
 | 
			
		||||
wins. */
 | 
			
		||||
class ISO8601DateParser {
 | 
			
		||||
public:
 | 
			
		||||
    // Processes a char, returning false if the char doesn't fit the
 | 
			
		||||
    // parser.
 | 
			
		||||
    virtual bool input(int ch) = 0;
 | 
			
		||||
 | 
			
		||||
    // Probability system used to determine which state machine should
 | 
			
		||||
    // 'win' if several complete
 | 
			
		||||
    enum Probability {
 | 
			
		||||
        Impossible,
 | 
			
		||||
            Unlikely,
 | 
			
		||||
            Likely,
 | 
			
		||||
            Definite
 | 
			
		||||
    };
 | 
			
		||||
    virtual Probability getProbability() = 0;
 | 
			
		||||
 | 
			
		||||
    // Return a date object.
 | 
			
		||||
    virtual Date getDate(const std::wstring& str) = 0;
 | 
			
		||||
 | 
			
		||||
    virtual ~ISO8601DateParser() { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ISO8601DateParser_CalendarBasic : public ISO8601DateParser {
 | 
			
		||||
private:
 | 
			
		||||
    int numDigits;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ISO8601DateParser_CalendarBasic()
 | 
			
		||||
        : numDigits(0)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    virtual bool input(int ch)
 | 
			
		||||
    {
 | 
			
		||||
        switch(ch) {
 | 
			
		||||
        case '0' ... '9':
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case '-':
 | 
			
		||||
            if(!numDigits) return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Probability getProbability()
 | 
			
		||||
    {
 | 
			
		||||
    return (numDigits < 8) ? Impossible : Likely;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Date getDate(const std::wstring& str)
 | 
			
		||||
    {
 | 
			
		||||
        int strLen = str.length();
 | 
			
		||||
        return Date(strToInt32(str.substr(0, strLen - 4)),
 | 
			
		||||
                    strToInt32(str.substr(strLen - 4, 2)),
 | 
			
		||||
                    strToInt32(str.substr(strLen - 2, 2)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~ISO8601DateParser_CalendarBasic() { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ISO8601DateParser_CalendarExtended : public ISO8601DateParser {
 | 
			
		||||
private:
 | 
			
		||||
    int numDigits;
 | 
			
		||||
    enum {
 | 
			
		||||
        stateNothing,
 | 
			
		||||
            stateYear,
 | 
			
		||||
            stateMonth,
 | 
			
		||||
            stateDay
 | 
			
		||||
    }state;
 | 
			
		||||
    int year, month, day;
 | 
			
		||||
    bool yearNegative;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ISO8601DateParser_CalendarExtended()
 | 
			
		||||
        : numDigits(0), state(stateNothing),
 | 
			
		||||
        year(0), month(0), day(0), yearNegative(false)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    virtual bool input(int ch)
 | 
			
		||||
    {
 | 
			
		||||
        switch(state) {
 | 
			
		||||
        case stateNothing:
 | 
			
		||||
            state = stateYear;
 | 
			
		||||
            switch(ch) {
 | 
			
		||||
            case '-':
 | 
			
		||||
                yearNegative = true;
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            case '0' ... '9':
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
                // fall through
 | 
			
		||||
 | 
			
		||||
        case stateYear:
 | 
			
		||||
            if(ch == '-' && numDigits >= 4) {
 | 
			
		||||
                state = stateMonth;
 | 
			
		||||
                numDigits = 0;
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            year *= 10;
 | 
			
		||||
            year += ch - '0';
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case stateMonth:
 | 
			
		||||
            if(numDigits == 2) {
 | 
			
		||||
                state = stateDay;
 | 
			
		||||
                numDigits = 0;
 | 
			
		||||
                return ch == '-';
 | 
			
		||||
            }
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            month *= 10;
 | 
			
		||||
            month += ch - '0';
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case stateDay:
 | 
			
		||||
            if(numDigits == 2) return false;
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            day *= 10;
 | 
			
		||||
            day += ch - '0';
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Probability getProbability()
 | 
			
		||||
    {
 | 
			
		||||
        return (state == stateDay && numDigits == 2) ?
 | 
			
		||||
            Definite : Impossible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Date getDate(const std::wstring&)
 | 
			
		||||
    {
 | 
			
		||||
        return Date(year, month, day);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~ISO8601DateParser_CalendarExtended() { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ISO8601DateParser_OrdinalBasic : public ISO8601DateParser {
 | 
			
		||||
private:
 | 
			
		||||
    int numDigits;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ISO8601DateParser_OrdinalBasic()
 | 
			
		||||
        : numDigits(0)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    virtual bool input(int ch)
 | 
			
		||||
    {
 | 
			
		||||
        switch(ch) {
 | 
			
		||||
        case '0' ... '9':
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case '-':
 | 
			
		||||
            if(!numDigits) return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Probability getProbability()
 | 
			
		||||
    {
 | 
			
		||||
        if(numDigits < 7) return Impossible;
 | 
			
		||||
    return (numDigits == 7) ? Likely : Unlikely;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Date getDate(const std::wstring& str)
 | 
			
		||||
    {
 | 
			
		||||
        int strLen = str.length();
 | 
			
		||||
        return Date(strToInt32(str.substr(0, strLen - 3)),
 | 
			
		||||
                    strToInt32(str.substr(strLen - 3, 3)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~ISO8601DateParser_OrdinalBasic() { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ISO8601DateParser_OrdinalExtended : public ISO8601DateParser {
 | 
			
		||||
private:
 | 
			
		||||
    int numDigits;
 | 
			
		||||
    enum {
 | 
			
		||||
        stateNothing,
 | 
			
		||||
            stateYear,
 | 
			
		||||
            stateDay
 | 
			
		||||
    }state;
 | 
			
		||||
    int year, day;
 | 
			
		||||
    bool yearNegative;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ISO8601DateParser_OrdinalExtended()
 | 
			
		||||
        : numDigits(0), state(stateNothing),
 | 
			
		||||
        year(0), day(0), yearNegative(false)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    virtual bool input(int ch)
 | 
			
		||||
    {
 | 
			
		||||
        switch(state) {
 | 
			
		||||
        case stateNothing:
 | 
			
		||||
            state = stateYear;
 | 
			
		||||
            switch(ch) {
 | 
			
		||||
            case '-':
 | 
			
		||||
                yearNegative = true;
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            case '0' ... '9':
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
                // fall through
 | 
			
		||||
 | 
			
		||||
        case stateYear:
 | 
			
		||||
            if(ch == '-' && numDigits >= 4) {
 | 
			
		||||
                state = stateDay;
 | 
			
		||||
                numDigits = 0;
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            year *= 10;
 | 
			
		||||
            year += ch - '0';
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case stateDay:
 | 
			
		||||
            if(numDigits == 3) return false;
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            day *= 10;
 | 
			
		||||
            day += ch - '0';
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Probability getProbability()
 | 
			
		||||
    {
 | 
			
		||||
        return (state == stateDay && numDigits == 3) ?
 | 
			
		||||
            Definite : Impossible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Date getDate(const std::wstring&)
 | 
			
		||||
    {
 | 
			
		||||
        return Date(year, day);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~ISO8601DateParser_OrdinalExtended() { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ISO8601DateParser_WeekDateBasic : public ISO8601DateParser {
 | 
			
		||||
private:
 | 
			
		||||
    int numDigits;
 | 
			
		||||
    enum {
 | 
			
		||||
        stateNothing,
 | 
			
		||||
            stateYear,
 | 
			
		||||
            stateWeeks
 | 
			
		||||
    }state;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ISO8601DateParser_WeekDateBasic()
 | 
			
		||||
        : numDigits(0), state(stateNothing)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    virtual bool input(int ch)
 | 
			
		||||
    {
 | 
			
		||||
        switch(state) {
 | 
			
		||||
        case stateNothing:
 | 
			
		||||
            state = stateYear;
 | 
			
		||||
            switch(ch) {
 | 
			
		||||
            case '-':
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            case '0' ... '9':
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
                // fall through
 | 
			
		||||
 | 
			
		||||
        case stateYear:
 | 
			
		||||
            if(ch == 'W' && numDigits >= 4) {
 | 
			
		||||
                state = stateWeeks;
 | 
			
		||||
                numDigits = 0;
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case stateWeeks:
 | 
			
		||||
            if(numDigits++ == 3) return false;
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Probability getProbability()
 | 
			
		||||
    {
 | 
			
		||||
        return (state == stateWeeks && numDigits == 3) ?
 | 
			
		||||
            Definite : Impossible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Date getDate(const std::wstring& str)
 | 
			
		||||
    {
 | 
			
		||||
        std::wstring::size_type strLen = str.length();
 | 
			
		||||
        return Date::fromWeek(strToInt32(str.substr(0, strLen - 4)),
 | 
			
		||||
                              strToInt32(str.substr(strLen - 3, 2)),
 | 
			
		||||
                              str[strLen - 1] - '0');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~ISO8601DateParser_WeekDateBasic() { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ISO8601DateParser_WeekDateExtended : public ISO8601DateParser {
 | 
			
		||||
private:
 | 
			
		||||
    int numDigits;
 | 
			
		||||
    enum {
 | 
			
		||||
        stateNothing,
 | 
			
		||||
            stateYear,
 | 
			
		||||
            stateWeekDash,
 | 
			
		||||
            stateWeek,
 | 
			
		||||
            stateDay
 | 
			
		||||
    }state;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ISO8601DateParser_WeekDateExtended()
 | 
			
		||||
        : numDigits(0), state(stateNothing)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    virtual bool input(int ch)
 | 
			
		||||
    {
 | 
			
		||||
        switch(state) {
 | 
			
		||||
        case stateNothing:
 | 
			
		||||
            state = stateYear;
 | 
			
		||||
            switch(ch) {
 | 
			
		||||
            case '-':
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            case '0' ... '9':
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
                // fall through
 | 
			
		||||
 | 
			
		||||
        case stateYear:
 | 
			
		||||
            if(ch == '-' && numDigits >= 4) {
 | 
			
		||||
                state = stateWeekDash;
 | 
			
		||||
                numDigits = 0;
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case stateWeekDash:
 | 
			
		||||
            state = stateWeek;
 | 
			
		||||
            return ch == 'W';
 | 
			
		||||
 | 
			
		||||
        case stateWeek:
 | 
			
		||||
            if(numDigits == 2) {
 | 
			
		||||
                state = stateDay;
 | 
			
		||||
                numDigits = 0;
 | 
			
		||||
                return ch == '-';
 | 
			
		||||
            }
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        case stateDay:
 | 
			
		||||
            if(numDigits == 1) return false;
 | 
			
		||||
            if(ch < '0' || ch > '9') return false;
 | 
			
		||||
            ++numDigits;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Probability getProbability()
 | 
			
		||||
    {
 | 
			
		||||
        return (state == stateDay && numDigits == 1) ?
 | 
			
		||||
            Definite : Impossible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual Date getDate(const std::wstring& str)
 | 
			
		||||
    {
 | 
			
		||||
        std::wstring::size_type strLen = str.length();
 | 
			
		||||
        return Date::fromWeek(strToInt32(str.substr(0, strLen - 6)),
 | 
			
		||||
                              strToInt32(str.substr(strLen - 4, 2)),
 | 
			
		||||
                              str[strLen - 1] - '0');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual ~ISO8601DateParser_WeekDateExtended() { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Date Date::fromString(const std::wstring& str, ISO8601Format format)
 | 
			
		||||
{
 | 
			
		||||
    // set up a std::list of participating state machines
 | 
			
		||||
    ISO8601DateParser_CalendarBasic machineCalendarBasic;
 | 
			
		||||
    ISO8601DateParser_CalendarExtended machineCalendarExtended;
 | 
			
		||||
    ISO8601DateParser_OrdinalBasic machineOrdinalBasic;
 | 
			
		||||
    ISO8601DateParser_OrdinalExtended machineOrdinalExtended;
 | 
			
		||||
    ISO8601DateParser_WeekDateBasic machineWeekDateBasic;
 | 
			
		||||
    ISO8601DateParser_WeekDateExtended machineWeekDateExtended;
 | 
			
		||||
 | 
			
		||||
    std::vector<ISO8601DateParser*> stateMachines;
 | 
			
		||||
    switch(format) {
 | 
			
		||||
    case ISO8601FormatDetect:
 | 
			
		||||
        stateMachines.push_back(&machineCalendarBasic);
 | 
			
		||||
        stateMachines.push_back(&machineCalendarExtended);
 | 
			
		||||
        stateMachines.push_back(&machineOrdinalBasic);
 | 
			
		||||
        stateMachines.push_back(&machineOrdinalExtended);
 | 
			
		||||
        stateMachines.push_back(&machineWeekDateBasic);
 | 
			
		||||
        stateMachines.push_back(&machineWeekDateExtended);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case ISO8601FormatCalendar:
 | 
			
		||||
        stateMachines.push_back(&machineCalendarBasic);
 | 
			
		||||
        stateMachines.push_back(&machineCalendarExtended);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case ISO8601FormatOrdinal:
 | 
			
		||||
        stateMachines.push_back(&machineOrdinalBasic);
 | 
			
		||||
        stateMachines.push_back(&machineOrdinalExtended);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case ISO8601FormatWeekDate:
 | 
			
		||||
        stateMachines.push_back(&machineWeekDateBasic);
 | 
			
		||||
        stateMachines.push_back(&machineWeekDateExtended);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // pass the std::string through all the state machines
 | 
			
		||||
    int numMachines = stateMachines.size();
 | 
			
		||||
    std::wstring::size_type pos = 0, len = str.length();
 | 
			
		||||
 | 
			
		||||
    for(pos = 0; pos < len; ++pos) {
 | 
			
		||||
        int ch = str[pos];
 | 
			
		||||
 | 
			
		||||
        for(int iter = 0; iter < numMachines; ++iter) {
 | 
			
		||||
            if(!stateMachines[iter]->input(ch)) {
 | 
			
		||||
                // this machine can't process the input any more
 | 
			
		||||
                stateMachines.erase(stateMachines.begin() + iter);
 | 
			
		||||
                --iter;
 | 
			
		||||
                --numMachines;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // choose the machine with the highest likelihood
 | 
			
		||||
    ISO8601DateParser* bestMachine = 0;
 | 
			
		||||
    ISO8601DateParser::Probability bestProbability = ISO8601DateParser::Impossible;
 | 
			
		||||
    for(int iter = 0; iter < numMachines; ++iter) {
 | 
			
		||||
        ISO8601DateParser::Probability p =
 | 
			
		||||
            stateMachines[iter]->getProbability();
 | 
			
		||||
        if(p > bestProbability) {
 | 
			
		||||
            bestProbability = p;
 | 
			
		||||
            bestMachine = stateMachines[iter];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // return the date object or fail
 | 
			
		||||
    if(bestMachine) return bestMachine->getDate(str);
 | 
			
		||||
 | 
			
		||||
    throw ParseError(L"Cannot interpet as ISO8601 date", str, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wostream& operator<<(std::wostream& ostr, const Date& date)
 | 
			
		||||
{
 | 
			
		||||
    ostr << date.toString();
 | 
			
		||||
    return ostr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,288 @@
 | 
			
		|||
/* lw-support/src/lib/Time/Date.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief A Gregorian date.
 | 
			
		||||
 | 
			
		||||
This class holds a single date in the Gregorian calendar. It makes no
 | 
			
		||||
provision for dates in other formats (i.e. it uses the Proleptic
 | 
			
		||||
Gregorian Calendar). It also provides for conversion to/from ISO8601
 | 
			
		||||
formats. It uses astronomical years.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class Date {
 | 
			
		||||
private:
 | 
			
		||||
    int year, ord;
 | 
			
		||||
    void fixupDate(); // fix up after arithmetic
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Constructors etc. ///////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Constructor (calendar date).
 | 
			
		||||
 | 
			
		||||
    \param year The astronimical year.
 | 
			
		||||
    \param month Month of the year (1-12).
 | 
			
		||||
    \param day Day of the month (1-31).
 | 
			
		||||
    \throws BadDate if \a day or \a month are invalid.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Date(int year, int month, int day);
 | 
			
		||||
 | 
			
		||||
    /*! \brief Constructor (ordinal date).
 | 
			
		||||
 | 
			
		||||
    \param year The astronimical year.
 | 
			
		||||
    \param ordinalDay Day of the year (1-366).
 | 
			
		||||
    \throws BadDate if \a ordinalDay is invalid.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Date(int year, int ordinalDay);
 | 
			
		||||
 | 
			
		||||
    /*! \brief Make object from ISO8601 weekday form.
 | 
			
		||||
 | 
			
		||||
    \param year The week's year.
 | 
			
		||||
    \param week The week number (1-53).
 | 
			
		||||
    \param weekDay The week day (1-7).
 | 
			
		||||
    \throws BadDate if \a week or \a weekDay are invalid.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static Date fromWeek(int year, int week, int weekDay);
 | 
			
		||||
 | 
			
		||||
    /// Copy constructor.
 | 
			
		||||
    Date(const Date& date)
 | 
			
		||||
        : year(date.year), ord(date.ord)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Assignment operator.
 | 
			
		||||
    Date& operator=(const Date& date)
 | 
			
		||||
    {
 | 
			
		||||
        year = date.year;
 | 
			
		||||
        ord = date.ord;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create object from current calendar day.
 | 
			
		||||
    static Date now();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // String functions ////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Thrown when format specifiers don't make sense, etc.
 | 
			
		||||
    class BadFormat : public ProgramException { };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Convert to std::string (ISO8601) representation.
 | 
			
		||||
 | 
			
		||||
    \param format Which ISO8601 format to use.
 | 
			
		||||
    \param precision Which precision to use.
 | 
			
		||||
    \param extended Whether to use basic or extended notation.
 | 
			
		||||
    \throws BadFormat if \a format and/or \a precision don't make sense
 | 
			
		||||
        for this type.
 | 
			
		||||
    \returns The ISO8601 representation of the date.
 | 
			
		||||
 | 
			
		||||
    This function converts the date into its ISO8601 notation, with a
 | 
			
		||||
    variety of possible formatting options.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    std::wstring toString(bool extended = false,
 | 
			
		||||
        ISO8601Format format = ISO8601FormatCalendar,
 | 
			
		||||
        ISO8601Precision precision = ISO8601PrecisionFull) const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Parse an ISO8601 date.
 | 
			
		||||
 | 
			
		||||
    \param str The ISO8601 std::string to parse.
 | 
			
		||||
    \param format Which ISO8601 format to use.
 | 
			
		||||
    \throws ParseError if the std::string cannot be parsed.
 | 
			
		||||
    \throws BadDate if the date is parsed correctly but is not valid.
 | 
			
		||||
    \returns A newly-created date object.
 | 
			
		||||
 | 
			
		||||
    \note There is some ambiguity between the ISO8601 basic forms, since
 | 
			
		||||
        an 8-digit sequence could either be a basic calendar form or a
 | 
			
		||||
        basic ordinal form with a 5-digit year. For this reason, all
 | 
			
		||||
        sequences of 8 or more digits are interpreted as basic calendar
 | 
			
		||||
        form if \a format is \a ISO8601FormatDetect.
 | 
			
		||||
 | 
			
		||||
    \note The year part is assumed to always be at least four digits.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static Date fromString(const std::wstring& str,
 | 
			
		||||
        ISO8601Format format = ISO8601FormatDetect);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Access functions ////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Returns the year.
 | 
			
		||||
    inline int getYear() const
 | 
			
		||||
    { return year; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the month.
 | 
			
		||||
    int getMonth() const;
 | 
			
		||||
 | 
			
		||||
    /// Returns the day.
 | 
			
		||||
    int getDay() const;
 | 
			
		||||
 | 
			
		||||
    /// Returns the ordinal day.
 | 
			
		||||
    inline int getOrdinal() const
 | 
			
		||||
    {
 | 
			
		||||
        return ord;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the week day (1 = Monday, 7 = Sunday).
 | 
			
		||||
    int getWeekDay() const;
 | 
			
		||||
 | 
			
		||||
    /*! \brief Returns the week number.
 | 
			
		||||
 | 
			
		||||
    \returns The ISO8601 week number.
 | 
			
		||||
    \retval 0 if this is week 1 of next year.
 | 
			
		||||
    \retval -n if this is week \a n of last year.
 | 
			
		||||
 | 
			
		||||
    Returns the day's week number within the year. There are a couple of
 | 
			
		||||
    special return values; a zero means that this is in fact week 1 of
 | 
			
		||||
    next year, and a negative number \a -n means that this is week \a n
 | 
			
		||||
    of last year.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    int getWeek() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Modify functions / operators ////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Advance a number of days.
 | 
			
		||||
 | 
			
		||||
    \param numDays The number of days to advance by. Can be negative.
 | 
			
		||||
    \returns A reference to the modified object.
 | 
			
		||||
 | 
			
		||||
    Moves the date forward a number of days, correctly accounting for
 | 
			
		||||
    month and year boundaries.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Date& operator+=(int numDays);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Roll back a number of days.
 | 
			
		||||
 | 
			
		||||
    \param numDays The number of days to reverse by. Can be negative.
 | 
			
		||||
    \returns A reference to the modified object.
 | 
			
		||||
 | 
			
		||||
    Moves the date backward a number of days, correctly accounting for
 | 
			
		||||
    month and year boundaries.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    Date& operator-=(int numDays);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Advance a day.
 | 
			
		||||
    Date& operator++();
 | 
			
		||||
 | 
			
		||||
    /// Advance a day.
 | 
			
		||||
    Date operator++(int);
 | 
			
		||||
 | 
			
		||||
    /// Go back a day.
 | 
			
		||||
    Date& operator--();
 | 
			
		||||
 | 
			
		||||
    /// Go back a day.
 | 
			
		||||
    Date operator--(int);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Comparison operators ////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Get difference in days.
 | 
			
		||||
 | 
			
		||||
    \param date The date to subtract from this one.
 | 
			
		||||
    \returns The difference, in days, between two dates.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    int operator-(const Date& date) const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Returns true if two dates are equal.
 | 
			
		||||
    inline bool operator==(const Date& date) const
 | 
			
		||||
    {
 | 
			
		||||
        return (year == date.year) && (ord == date.ord);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if two dates are not equal.
 | 
			
		||||
    inline bool operator!=(const Date& date) const
 | 
			
		||||
    {
 | 
			
		||||
        return (year != date.year) || (ord != date.ord);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a date is less than \a this.
 | 
			
		||||
    inline bool operator<(const Date& date) const
 | 
			
		||||
    {
 | 
			
		||||
        return (year < date.year) || (year == date.year &&
 | 
			
		||||
            (ord < date.ord));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a date is less than or equal to \a this.
 | 
			
		||||
    inline bool operator<=(const Date& date) const
 | 
			
		||||
    {
 | 
			
		||||
        return (year < date.year) || (year == date.year &&
 | 
			
		||||
            (ord <= date.ord));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a date is greater than \a this.
 | 
			
		||||
    inline bool operator>(const Date& date) const
 | 
			
		||||
    {
 | 
			
		||||
        return (year > date.year) || (year == date.year &&
 | 
			
		||||
            (ord > date.ord));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a date is greater than or equal to \a this.
 | 
			
		||||
    inline bool operator>=(const Date& date) const
 | 
			
		||||
    {
 | 
			
		||||
        return (year > date.year) || (year == date.year &&
 | 
			
		||||
            (ord >= date.ord));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Utility functions ///////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a year is a leap year.
 | 
			
		||||
    static inline bool isLeapYear(int year)
 | 
			
		||||
    {
 | 
			
		||||
        if(year < 0) year *= -1;
 | 
			
		||||
        return !(year % 400) || ((year % 100) && !(year & 3));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns number of days in the given \a month and \a year.
 | 
			
		||||
    static inline int daysInMonth(int year, int month)
 | 
			
		||||
    {
 | 
			
		||||
        switch(month) {
 | 
			
		||||
            case 4:
 | 
			
		||||
            case 6:
 | 
			
		||||
            case 9:
 | 
			
		||||
            case 11:
 | 
			
		||||
                return 30;
 | 
			
		||||
 | 
			
		||||
            case 2:
 | 
			
		||||
                return (isLeapYear(year) ? 29 : 28);
 | 
			
		||||
        }
 | 
			
		||||
        return 31;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Stream insertion operator.
 | 
			
		||||
std::wostream& operator<<(std::wostream&, const Date&);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,108 @@
 | 
			
		|||
/* lw-support/src/lib/Time/DateTime.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring DateTime::toString(bool extended, bool decimalTime,
 | 
			
		||||
    ISO8601Format dateFormat, ISO8601Precision precision) const
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
    switch(precision) {
 | 
			
		||||
        case ISO8601PrecisionYear:
 | 
			
		||||
        case ISO8601PrecisionWeek:
 | 
			
		||||
        case ISO8601PrecisionMonth:
 | 
			
		||||
        case ISO8601PrecisionDay:
 | 
			
		||||
            o << date.toString(extended, dateFormat, precision);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case ISO8601PrecisionHour:
 | 
			
		||||
        case ISO8601PrecisionMinute:
 | 
			
		||||
        case ISO8601PrecisionSecond:
 | 
			
		||||
        case ISO8601PrecisionFull:
 | 
			
		||||
            o << date.toString(extended, dateFormat, ISO8601PrecisionFull);
 | 
			
		||||
            o << L'T';
 | 
			
		||||
            o << time.toString(extended, precision, decimalTime);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DateTime DateTime::fromString(const std::wstring& str,
 | 
			
		||||
    ISO8601Format dateFormat)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring::size_type timeStart = str.find('T');
 | 
			
		||||
    if(timeStart == std::string::npos) {
 | 
			
		||||
        return DateTime(Date::fromString(str, dateFormat));
 | 
			
		||||
    }
 | 
			
		||||
    return DateTime(
 | 
			
		||||
        Date::fromString(str.substr(0, timeStart), dateFormat),
 | 
			
		||||
        DayTime::fromString(str.substr(timeStart + 1, std::wstring::npos))
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DateTime& DateTime::addNanoSeconds(int64_t ns)
 | 
			
		||||
{
 | 
			
		||||
    int dayDelta;
 | 
			
		||||
    time.addNanoSeconds(ns, dayDelta);
 | 
			
		||||
    date += dayDelta;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DateTime& DateTime::addSeconds(int s)
 | 
			
		||||
{
 | 
			
		||||
    int dayDelta;
 | 
			
		||||
    time.addSeconds(s, dayDelta);
 | 
			
		||||
    date += dayDelta;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DateTime& DateTime::addMinutes(int m)
 | 
			
		||||
{
 | 
			
		||||
    int dayDelta;
 | 
			
		||||
    time.addMinutes(m, dayDelta);
 | 
			
		||||
    date += dayDelta;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DateTime& DateTime::addHours(int h)
 | 
			
		||||
{
 | 
			
		||||
    int dayDelta;
 | 
			
		||||
    time.addHours(h, dayDelta);
 | 
			
		||||
    date += dayDelta;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DateTime& DateTime::addDays(int d)
 | 
			
		||||
{
 | 
			
		||||
    date += d;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wostream& operator<<(std::wostream& ostr, const DateTime& d)
 | 
			
		||||
{
 | 
			
		||||
    ostr << d.toString();
 | 
			
		||||
    return ostr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,222 @@
 | 
			
		|||
/* lw-support/src/lib/Time/DateTime.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Stores a date and a time.
 | 
			
		||||
 | 
			
		||||
This class uses (internally) a DayTime and a Date class. These allow it
 | 
			
		||||
to represent pretty much arbitrary timescales (well, about +/- 2e9 years
 | 
			
		||||
with the precision of a nanosecond).
 | 
			
		||||
 | 
			
		||||
Altough you can individually operate on the internal Date and DayTime
 | 
			
		||||
instances, this isn't always a good idea. For instance, adding 24 hours
 | 
			
		||||
onto the DayTime object won't alter the Date. Instead, use the
 | 
			
		||||
addHours() function and equivalents.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class DateTime {
 | 
			
		||||
protected:
 | 
			
		||||
    /// The day.
 | 
			
		||||
    Date date;
 | 
			
		||||
 | 
			
		||||
    /// The time.
 | 
			
		||||
    DayTime time;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Constructors, etc. //////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Construct from date and time.
 | 
			
		||||
    DateTime(const Date& date, const DayTime& time)
 | 
			
		||||
        : date(date), time(time)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Construct (at midnight) from date.
 | 
			
		||||
    DateTime(const Date& date)
 | 
			
		||||
        : date(date), time(0, 0, 0)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Copy constructor.
 | 
			
		||||
    DateTime(const DateTime& dateTime)
 | 
			
		||||
        : date(dateTime.date), time(dateTime.time)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Assignment operator.
 | 
			
		||||
    DateTime& operator=(const DateTime& dateTime)
 | 
			
		||||
    {
 | 
			
		||||
        date = dateTime.date;
 | 
			
		||||
        time = dateTime.time;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Construct from calendar time.
 | 
			
		||||
    static DateTime now()
 | 
			
		||||
    { return DateTime(Date::now(), DayTime::now()); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // String routines /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Convert to ISO8601 std::string.
 | 
			
		||||
 | 
			
		||||
    \param extended \a true if you want to use the extended form,
 | 
			
		||||
        \a false if not.
 | 
			
		||||
    \param decimalTime \a true if you want to use the decimal time
 | 
			
		||||
        notation.
 | 
			
		||||
    \param precision How precise you want the converted std::string to be.
 | 
			
		||||
    \param dateFormat Which format to use for the date.
 | 
			
		||||
 | 
			
		||||
    Converts the entire date/time into an ISO8601-formatted std::string. See
 | 
			
		||||
    documentation for Date and DayTime for more information about the
 | 
			
		||||
    parameters and their results.
 | 
			
		||||
 | 
			
		||||
    \sa lw::Date::toString(), lw::DayTime::toString()
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    std::wstring toString(bool extended = false, bool decimalTime = false,
 | 
			
		||||
        ISO8601Format dateFormat = ISO8601FormatCalendar,
 | 
			
		||||
        ISO8601Precision precision = ISO8601PrecisionFull) const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Create from ISO8601 std::string.
 | 
			
		||||
 | 
			
		||||
    \param str The ISO8601 std::string to parse.
 | 
			
		||||
    \param dateFormat Which ISO8601 format to use.
 | 
			
		||||
    \throws ParseError if the std::string cannot be parsed.
 | 
			
		||||
    \throws BadDate if the date is parsed correctly but is not valid.
 | 
			
		||||
    \returns A newly-created date object.
 | 
			
		||||
 | 
			
		||||
    Attempts to parse \a str as an ISO8601 date/time std::string. If the time
 | 
			
		||||
    part is not present, the time will be set to midnight.
 | 
			
		||||
 | 
			
		||||
    \note There is some ambiguity between the ISO8601 basic date forms,
 | 
			
		||||
        since an 8-digit sequence could either be a basic calendar form
 | 
			
		||||
        or a basic ordinal form with a 5-digit year. For this reason,
 | 
			
		||||
        all sequences of 8 or more digits are interpreted as basic
 | 
			
		||||
        calendar form if \a dateFormat is \a ISO8601FormatDetect.
 | 
			
		||||
 | 
			
		||||
    \note The year part is assumed to always be at least four digits.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static DateTime fromString(const std::wstring& str,
 | 
			
		||||
        ISO8601Format dateFormat = ISO8601FormatDetect);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Access functions ////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Get date component.
 | 
			
		||||
    const Date& getDate() const
 | 
			
		||||
    { return date; }
 | 
			
		||||
 | 
			
		||||
    /// Get date component.
 | 
			
		||||
    Date& getDate()
 | 
			
		||||
    { return date; }
 | 
			
		||||
 | 
			
		||||
    /// Get time component.
 | 
			
		||||
    const DayTime& getTime() const
 | 
			
		||||
    { return time; }
 | 
			
		||||
 | 
			
		||||
    /// Get time component.
 | 
			
		||||
    DayTime& getTime()
 | 
			
		||||
    { return time; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Modify functions ////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Set date component.
 | 
			
		||||
    DateTime& setDate(const Date& date)
 | 
			
		||||
    {
 | 
			
		||||
        this->date = date;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set time component.
 | 
			
		||||
    DateTime& setTime(const DayTime& time)
 | 
			
		||||
    {
 | 
			
		||||
        this->time = time;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set date and time.
 | 
			
		||||
    DateTime& set(const Date& date, const DayTime& time)
 | 
			
		||||
    {
 | 
			
		||||
        this->date = date;
 | 
			
		||||
        this->time = time;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Add a number of nanoseconds.
 | 
			
		||||
    DateTime& addNanoSeconds(int64_t ns);
 | 
			
		||||
 | 
			
		||||
    /// Add a number of seconds.
 | 
			
		||||
    DateTime& addSeconds(int s);
 | 
			
		||||
 | 
			
		||||
    /// Add a number of minutes.
 | 
			
		||||
    DateTime& addMinutes(int m);
 | 
			
		||||
 | 
			
		||||
    /// Add a number of hours.
 | 
			
		||||
    DateTime& addHours(int h);
 | 
			
		||||
 | 
			
		||||
    /// Add a number of days.
 | 
			
		||||
    DateTime& addDays(int d);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Operators ///////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the dates are equal.
 | 
			
		||||
    bool operator==(const DateTime& dateTime) const
 | 
			
		||||
    { return date == dateTime.date && time == dateTime.time; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the dates are not equal.
 | 
			
		||||
    bool operator!=(const DateTime& dateTime) const
 | 
			
		||||
    { return date != dateTime.date || time != dateTime.time; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if less than \a dateTime.
 | 
			
		||||
    bool operator<(const DateTime& dateTime) const
 | 
			
		||||
    {
 | 
			
		||||
        return date < dateTime.date ||
 | 
			
		||||
            (date == dateTime.date && time < dateTime.time);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if less than or equal to \a dateTime.
 | 
			
		||||
    bool operator<=(const DateTime& dateTime) const
 | 
			
		||||
    {
 | 
			
		||||
        return date < dateTime.date ||
 | 
			
		||||
            (date == dateTime.date && time <= dateTime.time);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if greater than \a dateTime.
 | 
			
		||||
    bool operator>(const DateTime& dateTime) const
 | 
			
		||||
    {
 | 
			
		||||
        return date > dateTime.date ||
 | 
			
		||||
            (date == dateTime.date && time > dateTime.time);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if greater than or equal to \a dateTime.
 | 
			
		||||
    bool operator>=(const DateTime& dateTime) const
 | 
			
		||||
    {
 | 
			
		||||
        return date > dateTime.date ||
 | 
			
		||||
            (date == dateTime.date && time >= dateTime.time);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Stream insertion operator.
 | 
			
		||||
std::wostream& operator<<(std::wostream&, const DateTime&);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,264 @@
 | 
			
		|||
/* lw-support/src/lib/Time/DayTime.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DayTime& DayTime::wrap(int& dayDelta)
 | 
			
		||||
{
 | 
			
		||||
    int d = 0;
 | 
			
		||||
    dayDelta = 0;
 | 
			
		||||
    if(_ns < 0) {
 | 
			
		||||
        _ns += tenExp9;
 | 
			
		||||
        _s--;
 | 
			
		||||
    }
 | 
			
		||||
    if(_ns >= tenExp9) {
 | 
			
		||||
        _ns -= tenExp9;
 | 
			
		||||
        _s++;
 | 
			
		||||
    }
 | 
			
		||||
    if(_s < 0) {
 | 
			
		||||
        d = 1 + (_s / -60);
 | 
			
		||||
        _s += d * 60;
 | 
			
		||||
        _m -= d;
 | 
			
		||||
    }
 | 
			
		||||
    if(_s >= 60) {
 | 
			
		||||
        d = _s / 60;
 | 
			
		||||
        _s -= d * 60;
 | 
			
		||||
        _m += d;
 | 
			
		||||
    }
 | 
			
		||||
    if(_m < 0) {
 | 
			
		||||
        d = 1 + (_m / -60);
 | 
			
		||||
        _m += d * 60;
 | 
			
		||||
        _h -= d;
 | 
			
		||||
    }
 | 
			
		||||
    if(_m >= 60) {
 | 
			
		||||
        d = _m / 60;
 | 
			
		||||
        _m -= d * 60;
 | 
			
		||||
        _h += d;
 | 
			
		||||
    }
 | 
			
		||||
    if(_h < 0) {
 | 
			
		||||
        d = 1 + (_h / -24);
 | 
			
		||||
        _h += d * 24;
 | 
			
		||||
        dayDelta = -d;
 | 
			
		||||
    }
 | 
			
		||||
    if(_h >= 24) {
 | 
			
		||||
        d = _h / 24;
 | 
			
		||||
        _h -= d * 24;
 | 
			
		||||
        dayDelta = d;
 | 
			
		||||
    }
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring DayTime::toString(bool extended, ISO8601Precision precision,
 | 
			
		||||
    bool decimal) const
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o, o2;
 | 
			
		||||
    o << std::setfill(L'0');
 | 
			
		||||
    o2 << std::fixed;
 | 
			
		||||
 | 
			
		||||
    switch(precision) {
 | 
			
		||||
        case ISO8601PrecisionHour:
 | 
			
		||||
            o << std::setw(2) << _h;
 | 
			
		||||
            if(decimal) {
 | 
			
		||||
                double f = (_m / 60.0 + _s / 3600.0) + _ns / 3600e9;
 | 
			
		||||
                o2 << f;
 | 
			
		||||
                o << (o2.str().substr(1, std::wstring::npos));
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case ISO8601PrecisionMinute:
 | 
			
		||||
            o << std::setw(2) << _h;
 | 
			
		||||
            if(extended) o << ':';
 | 
			
		||||
            o << std::setw(2) << _m;
 | 
			
		||||
            if(decimal) {
 | 
			
		||||
                double f = (_s / 60.0) + _ns / 60e9;
 | 
			
		||||
                o2 << f;
 | 
			
		||||
                o << (o2.str().substr(1, std::wstring::npos));
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case ISO8601PrecisionSecond:
 | 
			
		||||
            o << std::setw(2) << _h;
 | 
			
		||||
            if(extended) o << ':';
 | 
			
		||||
            o << std::setw(2) << _m;
 | 
			
		||||
            if(extended) o << ':';
 | 
			
		||||
            o << std::setw(2) << _s;
 | 
			
		||||
            if(decimal) {
 | 
			
		||||
                double f = _ns / 1e9;
 | 
			
		||||
                o2 << f;
 | 
			
		||||
                o << (o2.str().substr(1, std::wstring::npos));
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case ISO8601PrecisionFull:
 | 
			
		||||
            o << std::setw(2) << _h;
 | 
			
		||||
            if(extended) o << ':';
 | 
			
		||||
            o << std::setw(2) << _m;
 | 
			
		||||
            if(extended) o << ':';
 | 
			
		||||
            o << std::setw(2) << _s;
 | 
			
		||||
            if(_ns) {
 | 
			
		||||
                double f = _ns / 1e9;
 | 
			
		||||
                o2 << f;
 | 
			
		||||
                o << (o2.str().substr(1, std::wstring::npos));
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            throw BadFormat();
 | 
			
		||||
    }
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
/* This function parses basic and extended format ISO8601 times (but not
 | 
			
		||||
the decimal notations, which are handled separately in fromString). It
 | 
			
		||||
returns the number of elements parsed (1, 2 or 3). This can be used to
 | 
			
		||||
determine which element a decimal fraction applies to.
 | 
			
		||||
*/
 | 
			
		||||
int ISO8601TimeParser(const std::wstring& str, int& h, int& m, int& s)
 | 
			
		||||
{
 | 
			
		||||
    // The remaining ISO8601 formats can all be disambiguated on length.
 | 
			
		||||
    switch(str.length()) {
 | 
			
		||||
        case 8: // hh:mm:ss
 | 
			
		||||
            if(str[2] != ':' || str[5] != ':') {
 | 
			
		||||
                throw ParseError(L"Not a recognised ISO8601 format.",
 | 
			
		||||
                    str, str[2] == ':' ? 5 : 2);
 | 
			
		||||
            }
 | 
			
		||||
            h = strToInt32(str.substr(0, 2));
 | 
			
		||||
            m = strToInt32(str.substr(3, 2));
 | 
			
		||||
            s = strToInt32(str.substr(6, 2));
 | 
			
		||||
            return 3;
 | 
			
		||||
 | 
			
		||||
        case 6: // hhmmss
 | 
			
		||||
            h = strToInt32(str.substr(0, 2));
 | 
			
		||||
            m = strToInt32(str.substr(2, 2));
 | 
			
		||||
            s = strToInt32(str.substr(4, 2));
 | 
			
		||||
            return 3;
 | 
			
		||||
 | 
			
		||||
        case 5: // hh:mm
 | 
			
		||||
            if(str[2] != ':') {
 | 
			
		||||
                throw ParseError(L"Not a recognised ISO8601 format.",
 | 
			
		||||
                    str, 2);
 | 
			
		||||
            }
 | 
			
		||||
            h = strToInt32(str.substr(0, 2));
 | 
			
		||||
            m = strToInt32(str.substr(3, 2));
 | 
			
		||||
            return 2;
 | 
			
		||||
 | 
			
		||||
        case 4: // hhmm
 | 
			
		||||
            h = strToInt32(str.substr(0, 2));
 | 
			
		||||
            m = strToInt32(str.substr(2, 2));
 | 
			
		||||
            return 2;
 | 
			
		||||
 | 
			
		||||
        case 2: // hh
 | 
			
		||||
            h = strToInt32(str);
 | 
			
		||||
            return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    throw ParseError(L"Not a recognised ISO8601 format.", str, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DayTime DayTime::fromString(const std::wstring& str)
 | 
			
		||||
{
 | 
			
		||||
    int h = 0, m = 0, s = 0, ns = 0;
 | 
			
		||||
    std::wstring::size_type decimal = std::wstring::npos;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        // Check for decimal time.
 | 
			
		||||
        if( (decimal = str.find('.')) != std::wstring::npos ||
 | 
			
		||||
            (decimal = str.find(',')) != std::wstring::npos)
 | 
			
		||||
        {
 | 
			
		||||
            // get fractional part
 | 
			
		||||
            double fraction = strToDouble(
 | 
			
		||||
                str.substr(decimal, std::wstring::npos));
 | 
			
		||||
 | 
			
		||||
            switch(ISO8601TimeParser(str.substr(0, decimal), h, m, s)) {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    m = (int)(fraction * 60);
 | 
			
		||||
                    s = (int)fmod(fraction * 3600, 60);
 | 
			
		||||
                    ns = (int)fmod(fraction * 3600 * tenExp9, tenExp9);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case 2:
 | 
			
		||||
                    s = (int)(fraction * 60);
 | 
			
		||||
                    ns = (int)fmod(fraction * 60 * tenExp9, tenExp9);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case 3:
 | 
			
		||||
                    ns = (int)(fraction * tenExp9);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            // Interpret as non-decimal time
 | 
			
		||||
            ISO8601TimeParser(str, h, m, s);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DayTime(h, m, s, ns);
 | 
			
		||||
    }
 | 
			
		||||
    catch(Exception& ex) {
 | 
			
		||||
        // build a suitable error message
 | 
			
		||||
        std::wostringstream chainStr;
 | 
			
		||||
        chainStr << "DayTime::fromString(\"" << str << "\")";
 | 
			
		||||
 | 
			
		||||
        ex.chain(chainStr.str());
 | 
			
		||||
        throw;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DayTime DayTime::now()
 | 
			
		||||
{
 | 
			
		||||
    struct timeval tv;
 | 
			
		||||
    gettimeofday(&tv, 0);
 | 
			
		||||
    time_t t = tv.tv_sec;
 | 
			
		||||
    struct tm tt;
 | 
			
		||||
    localtime_r(&t, &tt);
 | 
			
		||||
    return DayTime(tt.tm_hour, tt.tm_min, tt.tm_sec, tv.tv_usec * 1000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DayTime& DayTime::addNanoSeconds(int64_t ns)
 | 
			
		||||
{
 | 
			
		||||
    lldiv_t dv = lldiv(ns, tenExp9);
 | 
			
		||||
    _s += dv.quot;
 | 
			
		||||
    _ns += dv.rem;
 | 
			
		||||
    int dummy;
 | 
			
		||||
    return wrap(dummy);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DayTime& DayTime::addNanoSeconds(int64_t ns, int& dayDelta)
 | 
			
		||||
{
 | 
			
		||||
    lldiv_t dv = lldiv(ns, tenExp9);
 | 
			
		||||
    _s += dv.quot;
 | 
			
		||||
    _ns += dv.rem;
 | 
			
		||||
    return wrap(dayDelta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wostream& operator<<(std::wostream& ostr, const DayTime& d)
 | 
			
		||||
{
 | 
			
		||||
    ostr << d.toString();
 | 
			
		||||
    return ostr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,331 @@
 | 
			
		|||
/* lw-support/src/lib/Time/DayTime.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Represents a 24-hour day time.
 | 
			
		||||
 | 
			
		||||
This class is used to represent a day time (i.e. between 00:00:00 and
 | 
			
		||||
23:59:59). It will "wrap around" if you modify it so that it goes
 | 
			
		||||
outside this range. The function forms will allow you to retrieve the
 | 
			
		||||
number of days difference in such cases. There is no support for
 | 
			
		||||
leap seconds. The class also has a fractional second member (in
 | 
			
		||||
nanoseconds).
 | 
			
		||||
 | 
			
		||||
This class is intended for representing a specific time, not an elapsed
 | 
			
		||||
time. Use the ElapsedTime class for that.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class DayTime {
 | 
			
		||||
private:
 | 
			
		||||
    int _h, _m, _s, _ns;
 | 
			
		||||
 | 
			
		||||
    DayTime& wrap(int& dayDelta);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Constructors ////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from time.
 | 
			
		||||
 | 
			
		||||
    \param h The hour (0-23).
 | 
			
		||||
    \param m The minute (0-59).
 | 
			
		||||
    \param s The second (0-59).
 | 
			
		||||
    \param ns The nanosecond (a positive value < 1s).
 | 
			
		||||
    \throws BadTimeValue if any value is out of range.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    DayTime(int h, int m, int s = 0, int ns = 0)
 | 
			
		||||
        : _h(h), _m(m), _s(s), _ns(ns)
 | 
			
		||||
    {
 | 
			
		||||
        if(_h < 0 || _h > 23 || _m < 0 || _m > 59 || _s < 0 || _s > 59
 | 
			
		||||
            || _ns < 0 || _ns >= tenExp9)
 | 
			
		||||
                throw BadTimeValue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from elapsed time.
 | 
			
		||||
 | 
			
		||||
    \param time The ElapsedTime object to construct from.
 | 
			
		||||
    \throws BadTimeValue if \a time represents a period >= 86400s.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    DayTime(const ElapsedTime& time)
 | 
			
		||||
    {
 | 
			
		||||
        uint64_t p = time.to_s();
 | 
			
		||||
        if(p >= 86400) throw BadTimeValue();
 | 
			
		||||
        _h = p / 3600;
 | 
			
		||||
        _m = (p / 60) % 60;
 | 
			
		||||
        _s = p % 60;
 | 
			
		||||
        _ns = time.to_ns() % tenExp9;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Copy constructor.
 | 
			
		||||
    DayTime(const DayTime& other)
 | 
			
		||||
        : _h(other._h), _m(other._m), _s(other._s), _ns(other._ns)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Construct from calendar time.
 | 
			
		||||
    static DayTime now();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Setting functions ///////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Set time.
 | 
			
		||||
 | 
			
		||||
    \param h The hour (0-23).
 | 
			
		||||
    \param m The minute (0-59).
 | 
			
		||||
    \param s The second (0-59).
 | 
			
		||||
    \param ns The nanosecond (a positive value < 1s).
 | 
			
		||||
    \throws BadTimeValue if any value is out of range.
 | 
			
		||||
    \returns A reference to itself.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    DayTime& set(int h, int m, int s = 0, int ns = 0)
 | 
			
		||||
    {
 | 
			
		||||
        if(h < 0 || h > 23 || m < 0 || m > 59 || s < 0 || s > 59
 | 
			
		||||
            || ns < 0 || ns >= tenExp9)
 | 
			
		||||
                throw BadTimeValue();
 | 
			
		||||
        _h = h;
 | 
			
		||||
        _m = m;
 | 
			
		||||
        _s = s;
 | 
			
		||||
        _ns = ns;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set time.
 | 
			
		||||
    DayTime& operator=(const DayTime& other)
 | 
			
		||||
    {
 | 
			
		||||
        _h = other._h;
 | 
			
		||||
        _m = other._m;
 | 
			
		||||
        _s = other._s;
 | 
			
		||||
        _ns = other._ns;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Set time.
 | 
			
		||||
 | 
			
		||||
    \param time The ElapsedTime object to construct from.
 | 
			
		||||
    \throws BadTimeValue if \a time represents a period >= 86400s.
 | 
			
		||||
    \returns A reference to itself.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    DayTime& operator=(const ElapsedTime& time)
 | 
			
		||||
    {
 | 
			
		||||
        uint64_t p = time.to_s();
 | 
			
		||||
        if(p >= 86400) throw BadTimeValue();
 | 
			
		||||
        _h = p / 3600;
 | 
			
		||||
        _m = (p / 60) % 60;
 | 
			
		||||
        _s = p % 60;
 | 
			
		||||
        _ns = time.to_ns() % tenExp9;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Query functions /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Return hour (0-23).
 | 
			
		||||
    int h() const
 | 
			
		||||
    { return _h; }
 | 
			
		||||
 | 
			
		||||
    /// Return minute (0-59).
 | 
			
		||||
    int m() const
 | 
			
		||||
    { return _m; }
 | 
			
		||||
 | 
			
		||||
    /// Return second (0-59).
 | 
			
		||||
    int s() const
 | 
			
		||||
    { return _s; }
 | 
			
		||||
 | 
			
		||||
    /// Return nanosecond (<1s)
 | 
			
		||||
    int ns() const
 | 
			
		||||
    { return _ns; }
 | 
			
		||||
 | 
			
		||||
    /// Convert to an ElapsedTime object.
 | 
			
		||||
    ElapsedTime toElapsedTime() const
 | 
			
		||||
    {
 | 
			
		||||
        if(_ns) return ElapsedTime(_h * 3600 + _m * 60 + _s,
 | 
			
		||||
            _ns / 1000, _ns % 1000);
 | 
			
		||||
        else return ElapsedTime(_h * 3600 + _m * 60 + _s);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Convert into seconds since midnight.
 | 
			
		||||
    int numSeconds() const
 | 
			
		||||
    { return _h * 3600 + _m * 60 + _s; }
 | 
			
		||||
 | 
			
		||||
    /// Convert into nanoseconds since midnight.
 | 
			
		||||
    int64_t numNanoSeconds() const
 | 
			
		||||
    { return (_h * 3600 + _m * 60 + _s) * tenExp9 + _ns; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Arithmetic functions ////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& addNanoSeconds(int64_t ns);
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& addNanoSeconds(int64_t ns, int& dayDelta);
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& addSeconds(int s)
 | 
			
		||||
    {
 | 
			
		||||
        int dummy;
 | 
			
		||||
        _s += s;
 | 
			
		||||
        return wrap(dummy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& addSeconds(int s, int& dayDelta)
 | 
			
		||||
    {
 | 
			
		||||
        _s += s;
 | 
			
		||||
        return wrap(dayDelta);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& addMinutes(int m)
 | 
			
		||||
    {
 | 
			
		||||
        int dummy;
 | 
			
		||||
        _m += m;
 | 
			
		||||
        return wrap(dummy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& addMinutes(int m, int& dayDelta)
 | 
			
		||||
    {
 | 
			
		||||
        _m += m;
 | 
			
		||||
        return wrap(dayDelta);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& addHours(int h)
 | 
			
		||||
    {
 | 
			
		||||
        int dummy;
 | 
			
		||||
        _h += h;
 | 
			
		||||
        return wrap(dummy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& addHours(int h, int& dayDelta)
 | 
			
		||||
    {
 | 
			
		||||
        _h += h;
 | 
			
		||||
        return wrap(dayDelta);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // String routines /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Thrown when format specifiers don't make sense, etc.
 | 
			
		||||
    class BadFormat : public ProgramException { };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Parse a std::string object to get a day time.
 | 
			
		||||
 | 
			
		||||
    \param str The std::string to parse.
 | 
			
		||||
    \throws ParseError if a parsing error occurs.
 | 
			
		||||
    \returns A new DayTime object constructed from the std::string data.
 | 
			
		||||
 | 
			
		||||
    Parses an ISO8601 std::string, accepting either basic or extended format
 | 
			
		||||
    notations, at any precision. Handles decimal notation as well.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static DayTime fromString(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Convert to std::string.
 | 
			
		||||
 | 
			
		||||
    \param extended True if you want the extended ISO8601
 | 
			
		||||
        representation; false if not.
 | 
			
		||||
    \param precision The precision to which you want to go. This takes
 | 
			
		||||
        a different meaning if \a decimal is true.
 | 
			
		||||
    \param decimal True if you want to use decimal time notation; false
 | 
			
		||||
        if not.
 | 
			
		||||
    \returns A UCS-4 std::string representation of the day time.
 | 
			
		||||
 | 
			
		||||
    Creates a std::string representation of the time in ISO8601 form. If you
 | 
			
		||||
    choose to use decimal time, then the full precision will be printed,
 | 
			
		||||
    but the \a precision argument specifies where to start the fraction.
 | 
			
		||||
    If you use lw::ISO8601PrecisionFull, and \a decimal is set to false,
 | 
			
		||||
    the decimal part will still be printed if and only if the number of
 | 
			
		||||
    nanoseconds is non-zero.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    std::wstring toString(bool extended = true,
 | 
			
		||||
        ISO8601Precision precision = ISO8601PrecisionSecond,
 | 
			
		||||
        bool decimal = false) const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Operators (comparison) //////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a this is equal to \a other.
 | 
			
		||||
    bool operator==(const DayTime& other) const
 | 
			
		||||
    { return _h == other._h && _m == other._m && _s == other._s; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a this is not equal to \a other.
 | 
			
		||||
    bool operator!=(const DayTime& other) const
 | 
			
		||||
    { return _h != other._h || _m != other._m || _s != other._s; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a this is earlier than \a other.
 | 
			
		||||
    bool operator<(const DayTime& other) const
 | 
			
		||||
    { return numNanoSeconds() < other.numNanoSeconds(); }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a this is earlier than or equal to \a other.
 | 
			
		||||
    bool operator<=(const DayTime& other) const
 | 
			
		||||
    { return numNanoSeconds() <= other.numNanoSeconds(); }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a this is later than \a other.
 | 
			
		||||
    bool operator>(const DayTime& other) const
 | 
			
		||||
    { return numNanoSeconds() > other.numNanoSeconds(); }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if \a this is later than or equal to \a other.
 | 
			
		||||
    bool operator>=(const DayTime& other) const
 | 
			
		||||
    { return numNanoSeconds() >= other.numNanoSeconds(); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Operators (arithmetic) //////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Advance time.
 | 
			
		||||
    DayTime& operator+=(int s)
 | 
			
		||||
    { return addSeconds(s); }
 | 
			
		||||
 | 
			
		||||
    /// Retard time.
 | 
			
		||||
    DayTime& operator-=(int s)
 | 
			
		||||
    { return addSeconds(-s); }
 | 
			
		||||
 | 
			
		||||
    /// Advanced time.
 | 
			
		||||
    DayTime operator+(int s) const
 | 
			
		||||
    {
 | 
			
		||||
        DayTime t(*this);
 | 
			
		||||
        return t.addSeconds(s);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Retarded time.
 | 
			
		||||
    DayTime operator-(int s) const
 | 
			
		||||
    {
 | 
			
		||||
        DayTime t(*this);
 | 
			
		||||
        return t.addSeconds(-s);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns time difference in nanoseconds.
 | 
			
		||||
    int64_t operator-(const DayTime& other) const
 | 
			
		||||
    { return numNanoSeconds() - other.numNanoSeconds(); }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Stream insertion operator.
 | 
			
		||||
std::wostream& operator<<(std::wostream&, const DayTime& d);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,118 @@
 | 
			
		|||
/* lw-support/src/lib/Time/ElapsedTime.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring ElapsedTime::toString(int decPlaces, bool exp) const
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream o;
 | 
			
		||||
 | 
			
		||||
    o << std::setprecision(decPlaces);
 | 
			
		||||
 | 
			
		||||
    if(exp) {
 | 
			
		||||
        o << std::scientific;
 | 
			
		||||
        o << (time / 1e9) << 's';
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        o << std::fixed;
 | 
			
		||||
        if(time < 100) {
 | 
			
		||||
            o << time << "ns";
 | 
			
		||||
 | 
			
		||||
        } else if(time < 1e5) {
 | 
			
		||||
            o << (time / 1e3) << "us";
 | 
			
		||||
 | 
			
		||||
        } else if(time < 1e8) {
 | 
			
		||||
            o << (time / 1e6) << "ns";
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            o << (time / 1e9) << 's';
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return o.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ElapsedTime ElapsedTime::fromString(const std::wstring& str)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring::size_type strSize = str.length();
 | 
			
		||||
    std::wistringstream istr(str);
 | 
			
		||||
    double d;
 | 
			
		||||
 | 
			
		||||
    istr >> d >> std::ws;
 | 
			
		||||
    int afterNumPos = (strSize - istr.rdbuf()->in_avail());
 | 
			
		||||
    if(istr.bad() || istr.fail()) {
 | 
			
		||||
        throw ParseError(L"Not a number.", str, afterNumPos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(d < 0 || !isfinite(d)) {
 | 
			
		||||
        throw ParseError(L"Not a valid time period.", str, afterNumPos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(!istr.eof()) {
 | 
			
		||||
        std::wstring unit;
 | 
			
		||||
        istr >> unit >> std::ws;
 | 
			
		||||
 | 
			
		||||
        if(unit == L"s") {
 | 
			
		||||
            d *= 1e9;
 | 
			
		||||
 | 
			
		||||
        } else if(unit == L"ms") {
 | 
			
		||||
            d *= 1e6;
 | 
			
		||||
 | 
			
		||||
        } else if(unit == L"us") {
 | 
			
		||||
            d *= 1e3;
 | 
			
		||||
 | 
			
		||||
        } else if(unit == L"ns") {
 | 
			
		||||
            // nothing to do
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            throw ParseError(L"Unrecognised unit.", str, afterNumPos);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!istr.eof()) {
 | 
			
		||||
            throw ParseError(L"Data after unit.", str,
 | 
			
		||||
                (strSize - istr.rdbuf()->in_avail()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ElapsedTime((uint64_t)(d), true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ElapsedTime::ElapsedTime(struct timeval* tv)
 | 
			
		||||
    : time(tv->tv_sec * tenExp9 + tv->tv_usec * tenExp3)
 | 
			
		||||
{
 | 
			
		||||
    if(tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= tenExp6) {
 | 
			
		||||
        throw BadTimeValue();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ElapsedTime::ElapsedTime(struct timespec* ts)
 | 
			
		||||
    : time(ts->tv_sec * tenExp9 + ts->tv_nsec)
 | 
			
		||||
{
 | 
			
		||||
    if(ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= tenExp9) {
 | 
			
		||||
        throw BadTimeValue();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wostream& operator<<(std::wostream& ostr, const ElapsedTime& t)
 | 
			
		||||
{
 | 
			
		||||
    ostr << t.toString();
 | 
			
		||||
    return ostr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,394 @@
 | 
			
		|||
/* lw-support/src/lib/Time/ElapsedTime.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Integer constant: 1e9.
 | 
			
		||||
const int64_t tenExp9 = 1000000000;
 | 
			
		||||
 | 
			
		||||
/// Integer constant: 1e6.
 | 
			
		||||
const int64_t tenExp6 = 1000000;
 | 
			
		||||
 | 
			
		||||
/// Integer constant: 1e3.
 | 
			
		||||
const int64_t tenExp3 = 1000;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief An elapsed time period.
 | 
			
		||||
 | 
			
		||||
This class is used to represent an elapsed time period. It has a
 | 
			
		||||
resolution of nanoseconds, and uses a 64-bit integer (thus providing
 | 
			
		||||
a maximum representable period of 1.84e10 seconds, or about 584 years).
 | 
			
		||||
It is intended as a direct replacement for the C library's <tt>struct
 | 
			
		||||
timeval</tt> and <tt>struct timespec</tt>. It cannot represent negative
 | 
			
		||||
time periods.
 | 
			
		||||
 | 
			
		||||
\sa lw::StopWatch, lw::DayTime
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class ElapsedTime {
 | 
			
		||||
private:
 | 
			
		||||
    uint64_t time;
 | 
			
		||||
 | 
			
		||||
    // constructor with dummy argument to avoid collisions
 | 
			
		||||
    explicit ElapsedTime(uint64_t ns, bool)
 | 
			
		||||
        : time(ns)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Constructors ////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Default constructor.
 | 
			
		||||
 | 
			
		||||
    \param s The number of seconds (must be >= 0)
 | 
			
		||||
    \param ms The number of milliseconds (must be >= 0 and < 1000)
 | 
			
		||||
    \param us The number of microseconds (must be >= 0 and < 1000)
 | 
			
		||||
    \param ns The number of nanoseconds (must be >= 0 and < 1000)
 | 
			
		||||
    \throws BadTimeValue if any one of the arguments is out of range.
 | 
			
		||||
 | 
			
		||||
    Constructs an elapsed time period. This constructor is designed for
 | 
			
		||||
    intuitive use -- the more arguments you specify, the more accurate
 | 
			
		||||
    the result. Seconds are the most significant argument.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ElapsedTime(int s = 0, int ms = 0, int us = 0, int ns = 0)
 | 
			
		||||
        : time(s * tenExp9 + ms * tenExp6 + us * tenExp3 + ns)
 | 
			
		||||
    {
 | 
			
		||||
        if(s < 0 || ms < 0 || ms >= tenExp3 || us < 0 || us >= tenExp3
 | 
			
		||||
            || ns < 0 || ns >= tenExp3)
 | 
			
		||||
        {
 | 
			
		||||
            throw BadTimeValue();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from floating point value in seconds.
 | 
			
		||||
 | 
			
		||||
    \param s The fractional number of seconds.
 | 
			
		||||
    \throws BadTimeValue if \a s is negative.
 | 
			
		||||
 | 
			
		||||
    Rounds the fractional time to the nearest nanosecond.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    explicit ElapsedTime(long double s)
 | 
			
		||||
        : time(uint64_t(s * tenExp9))
 | 
			
		||||
    {
 | 
			
		||||
        if(s < 0) throw BadTimeValue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from floating point value in seconds.
 | 
			
		||||
 | 
			
		||||
    \param s The fractional number of seconds.
 | 
			
		||||
    \throws BadTimeValue if \a s is negative.
 | 
			
		||||
 | 
			
		||||
    Rounds the fractional time to the nearest nanosecond.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    explicit ElapsedTime(double s)
 | 
			
		||||
        : time(uint64_t(s * tenExp9))
 | 
			
		||||
    {
 | 
			
		||||
        if(s < 0) throw BadTimeValue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from floating point value in seconds.
 | 
			
		||||
 | 
			
		||||
    \param s The fractional number of seconds.
 | 
			
		||||
    \throws BadTimeValue if \a s is negative.
 | 
			
		||||
 | 
			
		||||
    Rounds the fractional time to the nearest nanosecond.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    explicit ElapsedTime(float s)
 | 
			
		||||
        : time(uint64_t(s * tenExp9))
 | 
			
		||||
    {
 | 
			
		||||
        if(s < 0) throw BadTimeValue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Copy constructor.
 | 
			
		||||
    ElapsedTime(const ElapsedTime& other)
 | 
			
		||||
        : time(other.time)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from a struct timeval.
 | 
			
		||||
 | 
			
		||||
    \param tv The timeval to construct from.
 | 
			
		||||
    \throws BadTimeValue if \a tv's members are out of range.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ElapsedTime(struct timeval* tv);
 | 
			
		||||
 | 
			
		||||
    /*! \brief Construct from a struct spec.
 | 
			
		||||
 | 
			
		||||
    \param ts The timespec to construct from.
 | 
			
		||||
    \throws BadTimeValue if \a ts's members are out of range.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ElapsedTime(struct timespec* ts);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Construct from nanoseconds.
 | 
			
		||||
    static ElapsedTime from_ns(uint64_t ns)
 | 
			
		||||
    { return ElapsedTime(ns, true); }
 | 
			
		||||
 | 
			
		||||
    /// Explicitly construct from microseconds.
 | 
			
		||||
    static ElapsedTime from_us(uint64_t us)
 | 
			
		||||
    { return ElapsedTime(us * tenExp3, true); }
 | 
			
		||||
 | 
			
		||||
    /// Explicitly construct from milliseconds.
 | 
			
		||||
    static ElapsedTime from_ms(uint64_t ms)
 | 
			
		||||
    { return ElapsedTime(ms * tenExp6, true); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Query functions /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the time period is zero.
 | 
			
		||||
    inline bool isZero() const { return !time; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the time in nanoseconds.
 | 
			
		||||
    uint64_t to_ns() const { return time; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the time in microseconds.
 | 
			
		||||
    uint64_t to_us() const { return time / tenExp3; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the time in milliseconds.
 | 
			
		||||
    uint64_t to_ms() const { return time / tenExp6; }
 | 
			
		||||
 | 
			
		||||
    /// Returns the time in seconds.
 | 
			
		||||
    uint64_t to_s() const { return time / tenExp9; }
 | 
			
		||||
 | 
			
		||||
    /// Returns fractional time.
 | 
			
		||||
    long double toLongDouble() const { return time / 1e9; }
 | 
			
		||||
 | 
			
		||||
    /// Returns fractional time.
 | 
			
		||||
    double toDouble() const { return time / 1e9; }
 | 
			
		||||
 | 
			
		||||
    /// Returns fractional time.
 | 
			
		||||
    float toFloat() const { return time / 1e9; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Set functions ///////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Assignment operator.
 | 
			
		||||
    ElapsedTime& operator=(const ElapsedTime& other)
 | 
			
		||||
    {
 | 
			
		||||
        time = other.time;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Clears the elapsed time.
 | 
			
		||||
    ElapsedTime clear()
 | 
			
		||||
    {
 | 
			
		||||
        time = 0;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the elapsed time (nanoseconds).
 | 
			
		||||
    ElapsedTime set_ns(uint64_t ns)
 | 
			
		||||
    {
 | 
			
		||||
        time = ns;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the elapsed time (microseconds).
 | 
			
		||||
    ElapsedTime set_us(uint64_t us)
 | 
			
		||||
    {
 | 
			
		||||
        time = us * tenExp3;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the elapsed time (milliseconds).
 | 
			
		||||
    ElapsedTime set_ms(uint64_t ms)
 | 
			
		||||
    {
 | 
			
		||||
        time = ms * tenExp6;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the elapsed time (seconds).
 | 
			
		||||
    ElapsedTime set_s(uint64_t s)
 | 
			
		||||
    {
 | 
			
		||||
        time = s * tenExp9;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Sets the elapsed time from a fraction.
 | 
			
		||||
 | 
			
		||||
    \param s The fractional number of seconds.
 | 
			
		||||
    \throws BadTimeValue if \a s is negative.
 | 
			
		||||
 | 
			
		||||
    Rounds the fractional time to the nearest nanosecond.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ElapsedTime set(long double s)
 | 
			
		||||
    {
 | 
			
		||||
        if(s < 0) throw BadTimeValue();
 | 
			
		||||
        time = (uint64_t)s;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Sets the elapsed time from a fraction.
 | 
			
		||||
 | 
			
		||||
    \param s The fractional number of seconds.
 | 
			
		||||
    \throws BadTimeValue if \a s is negative.
 | 
			
		||||
 | 
			
		||||
    Rounds the fractional time to the nearest nanosecond.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ElapsedTime set(double s)
 | 
			
		||||
    {
 | 
			
		||||
        if(s < 0) throw BadTimeValue();
 | 
			
		||||
        time = (uint64_t)s;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Sets the elapsed time from a fraction.
 | 
			
		||||
 | 
			
		||||
    \param s The fractional number of seconds.
 | 
			
		||||
    \throws BadTimeValue if \a s is negative.
 | 
			
		||||
 | 
			
		||||
    Rounds the fractional time to the nearest nanosecond.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ElapsedTime set(float s)
 | 
			
		||||
    {
 | 
			
		||||
        if(s < 0) throw BadTimeValue();
 | 
			
		||||
        time = (uint64_t)s;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // String Routines /////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Returns a std::string representation of the elapsed time.
 | 
			
		||||
 | 
			
		||||
    \param decPlaces The number of decimal places.
 | 
			
		||||
    \param exp True if you want exponential (scientific) notation.
 | 
			
		||||
 | 
			
		||||
    This function will return a std::string representation of an elapsed time
 | 
			
		||||
    period. The formatting can be specified. Units (<tt>s, ms, us,
 | 
			
		||||
    ns</tt>) will be printed after the number. In scientific notation,
 | 
			
		||||
    the unit is always seconds.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    std::wstring toString(int decPlaces = 1, bool exp = false) const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Constructs a time period from a std::string representation.
 | 
			
		||||
 | 
			
		||||
    \param str The std::string to interpret.
 | 
			
		||||
    \throws ParseError if a parsing error occurs.
 | 
			
		||||
 | 
			
		||||
    This function will try to interpret a std::string as a time period. It
 | 
			
		||||
    accepts an integral or floating point number (possibly in C's
 | 
			
		||||
    exponential notation), possibly followed by a unit. Acceptable
 | 
			
		||||
    units are <tt>s, ms, us, ns</tt>. If omitted, seconds are assumed.
 | 
			
		||||
    Any whitespace in the std::string will be ignored.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    static ElapsedTime fromString(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Operators (comparison) //////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the time period is zero.
 | 
			
		||||
    inline bool operator!() const { return !time; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the time period is non-zero.
 | 
			
		||||
    inline operator bool() const { return time; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if two time periods are equal.
 | 
			
		||||
    inline bool operator==(const ElapsedTime& other) const
 | 
			
		||||
    { return time == other.time; }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if two time periods are not equal.
 | 
			
		||||
    inline bool operator!=(const ElapsedTime& other) const
 | 
			
		||||
    { return time != other.time; }
 | 
			
		||||
 | 
			
		||||
    /// Less than comparison.
 | 
			
		||||
    inline bool operator<(const ElapsedTime& other) const
 | 
			
		||||
    { return time < other.time; }
 | 
			
		||||
 | 
			
		||||
    /// Less than or equal to comparison.
 | 
			
		||||
    inline bool operator<=(const ElapsedTime& other) const
 | 
			
		||||
    { return time <= other.time; }
 | 
			
		||||
 | 
			
		||||
    /// Greater than comparison.
 | 
			
		||||
    inline bool operator>(const ElapsedTime& other) const
 | 
			
		||||
    { return time > other.time; }
 | 
			
		||||
 | 
			
		||||
    /// Greater than or equal to comparison.
 | 
			
		||||
    inline bool operator>=(const ElapsedTime& other) const
 | 
			
		||||
    { return time >= other.time; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Operators (arithmetic) //////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Addition operation.
 | 
			
		||||
    inline ElapsedTime& operator+=(const ElapsedTime& other)
 | 
			
		||||
    {
 | 
			
		||||
        time += other.time;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Subtraction operation.
 | 
			
		||||
 | 
			
		||||
    \param other The time period to subtract from this one.
 | 
			
		||||
    \throws BadTimeOverflow if \a other is a greater time period than
 | 
			
		||||
        \a this
 | 
			
		||||
    \returns A reference to the newly-modified object.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline ElapsedTime& operator-=(const ElapsedTime& other)
 | 
			
		||||
    {
 | 
			
		||||
        if(other.time > time)
 | 
			
		||||
            throw BadTimeOverflow();
 | 
			
		||||
        time -= other.time;
 | 
			
		||||
        return *this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Addition operation.
 | 
			
		||||
    inline ElapsedTime operator+(const ElapsedTime& other) const
 | 
			
		||||
    {
 | 
			
		||||
        return ElapsedTime(time + other.time, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*! \brief Subtraction operation.
 | 
			
		||||
 | 
			
		||||
    \param other The time period to subtract from this one.
 | 
			
		||||
    \throws BadTimeOverflow if \a other is a greater time period than
 | 
			
		||||
        \a this
 | 
			
		||||
    \returns The time period difference.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline ElapsedTime operator-(const ElapsedTime& other) const
 | 
			
		||||
    {
 | 
			
		||||
        if(other.time > time)
 | 
			
		||||
            throw BadTimeOverflow();
 | 
			
		||||
        return ElapsedTime(time - other.time, true);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Stream insertion operator.
 | 
			
		||||
std::wostream& operator<<(std::wostream&, const ElapsedTime&);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
/* lw-support/src/lib/Time/StopWatch.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
StopWatch::StopWatch()
 | 
			
		||||
    : hasStarted(true)
 | 
			
		||||
{
 | 
			
		||||
    if(clock_gettime(CLOCK_REALTIME, &started)) throw SystemError();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void StopWatch::start()
 | 
			
		||||
{
 | 
			
		||||
    hasStarted = true;
 | 
			
		||||
    if(clock_gettime(CLOCK_REALTIME, &started)) throw SystemError();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void StopWatch::stop()
 | 
			
		||||
{
 | 
			
		||||
    hasStarted = false;
 | 
			
		||||
    if(clock_gettime(CLOCK_REALTIME, &stopped)) throw SystemError();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ElapsedTime StopWatch::elapsed() const
 | 
			
		||||
{
 | 
			
		||||
    if(hasStarted && clock_gettime(CLOCK_REALTIME, &stopped))
 | 
			
		||||
        throw SystemError();
 | 
			
		||||
    return ElapsedTime::from_ns((uint64_t)((stopped.tv_sec - started.tv_sec) * tenExp9
 | 
			
		||||
        + (stopped.tv_nsec - started.tv_nsec)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
/* lw-support/src/lib/Time/StopWatch.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Stop watch timer class.
 | 
			
		||||
 | 
			
		||||
This is a simple stopwatch class, which uses the ElapsedTime class to
 | 
			
		||||
record elapsed time to the nearest nanosecond (ostensibly; this depends
 | 
			
		||||
on operating system support).
 | 
			
		||||
 | 
			
		||||
\sa ElapsedTime
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class StopWatch {
 | 
			
		||||
private:
 | 
			
		||||
    struct timespec started;
 | 
			
		||||
    mutable struct timespec stopped;
 | 
			
		||||
    bool hasStarted;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor. Starts timer.
 | 
			
		||||
    StopWatch();
 | 
			
		||||
 | 
			
		||||
    /// Starts (or restarts) the timer.
 | 
			
		||||
    void start();
 | 
			
		||||
 | 
			
		||||
    /// Stops the timer.
 | 
			
		||||
    void stop();
 | 
			
		||||
 | 
			
		||||
    /*! \brief Retrieves elapsed time.
 | 
			
		||||
 | 
			
		||||
    \returns The elapsed time.
 | 
			
		||||
 | 
			
		||||
    If the timer is currently running, it returns the time elapsed since
 | 
			
		||||
    it was started. If the timer is stopped, it returns the time elapsed
 | 
			
		||||
    between starting (or restarting) and stopping the timer.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    ElapsedTime elapsed() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,32 @@
 | 
			
		|||
/* lw-support/src/liblw-support/TopHeader.h
 | 
			
		||||
/* lw-support/src/lib/TopHeader.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2006, Laurence Withers, <l@lwithers.me.uk>.
 | 
			
		||||
 *  Released under the GNU GPLv2. See file COPYING or
 | 
			
		||||
 *  http://www.gnu.org/copyleft/gpl.html for details.
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#ifndef HEADER_liblw_support
 | 
			
		||||
#define HEADER_liblw_support
 | 
			
		||||
// This file contains the include guard, and also any includes that are
 | 
			
		||||
// needed for the header file itself. Includes that are only needed in
 | 
			
		||||
// the source go into TopSource.cpp
 | 
			
		||||
 | 
			
		||||
#ifndef HEADER_LW_Support
 | 
			
		||||
#define HEADER_LW_Support
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// standard includes, or includes needed for type declarations
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
vim: expandtab:ts=4:sw=4
 | 
			
		||||
*/
 | 
			
		||||
#include <sys/epoll.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,36 @@
 | 
			
		|||
/* lw-support/src/liblw-support/TopSource.cpp
 | 
			
		||||
/* lw-support/src/lib/TopSource.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2006, Laurence Withers, <l@lwithers.me.uk>.
 | 
			
		||||
 *  Released under the GNU GPLv2. See file COPYING or
 | 
			
		||||
 *  http://www.gnu.org/copyleft/gpl.html for details.
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "lw-support"
 | 
			
		||||
 | 
			
		||||
// Below are all the includes used throughout the library.
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
vim: expandtab:ts=4:sw=4
 | 
			
		||||
*/
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <wchar.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#include <execinfo.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/poll.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <netinet/tcp.h>
 | 
			
		||||
 | 
			
		||||
// external library includes
 | 
			
		||||
 | 
			
		||||
// Some useful, universal constants.
 | 
			
		||||
 | 
			
		||||
#define pipeRead 0
 | 
			
		||||
#define pipeWrite 1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,159 @@
 | 
			
		|||
/* lw-support/src/lib/Util/Completion.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CompletionTask::CompletionTask(CompletionCallback* callback)
 | 
			
		||||
    : callback(callback)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CompletionTask::~CompletionTask()
 | 
			
		||||
{
 | 
			
		||||
    if(callback) {
 | 
			
		||||
        try {
 | 
			
		||||
            callback->aborted(this, lw::Exception(L"Task still active when destructor called."));
 | 
			
		||||
        }
 | 
			
		||||
        catch(...) { }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionTask::taskCompleted()
 | 
			
		||||
{
 | 
			
		||||
    CompletionCallback* cb = callback;
 | 
			
		||||
    if(!callback) throw TaskAlreadyCompleted();
 | 
			
		||||
    callback = 0;
 | 
			
		||||
    cb->completed(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionTask::taskAborted(const Exception& e)
 | 
			
		||||
{
 | 
			
		||||
    CompletionCallback* cb = callback;
 | 
			
		||||
    if(!callback) throw TaskAlreadyCompleted();
 | 
			
		||||
    callback = 0;
 | 
			
		||||
    cb->aborted(this, e);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CompletionList::CompletionList()
 | 
			
		||||
    : abortingTask(0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CompletionList::~CompletionList()
 | 
			
		||||
{
 | 
			
		||||
    abortAll();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionList::abortAll()
 | 
			
		||||
{
 | 
			
		||||
    while(!tasks.empty()) {
 | 
			
		||||
        abortingTask = tasks.front();
 | 
			
		||||
        tasks.pop_front();
 | 
			
		||||
        try {
 | 
			
		||||
            abortingTask->taskAbort(lw::Exception(L"Task list shutdown."));
 | 
			
		||||
        }
 | 
			
		||||
        catch(...) { }
 | 
			
		||||
        if(abortingTask) dbg_taskNotAborted(abortingTask);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionList::registerTask(CompletionTask* task)
 | 
			
		||||
{
 | 
			
		||||
    NullPointer::check(task, L"task", L"CompletionList::registerTask()");
 | 
			
		||||
    tasks.push_back(task);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool CompletionList::empty()
 | 
			
		||||
{
 | 
			
		||||
    return tasks.empty();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionList::completed(CompletionData* data)
 | 
			
		||||
{
 | 
			
		||||
    CompletionTask* task = dynamic_cast<CompletionTask*>(data);
 | 
			
		||||
    if(!task) {
 | 
			
		||||
        dbg_dataNotTask(data);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    taskIter iter = find(tasks.begin(), tasks.end(), data);
 | 
			
		||||
    if(iter == tasks.end()) {
 | 
			
		||||
        dbg_taskNotActive(task);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    tasks.erase(iter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionList::aborted(CompletionData* data, const lw::Exception&)
 | 
			
		||||
{
 | 
			
		||||
    if(abortingTask == data) abortingTask = 0;
 | 
			
		||||
    completed(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionList::dbg_dataNotTask(CompletionData* data)
 | 
			
		||||
{
 | 
			
		||||
    stderr << lw::Log::start(L"[DEBUG] CompletionList")
 | 
			
		||||
        << L"Data passed to CompletionList::completed() or aborted() was not derived from\n"
 | 
			
		||||
        << L"CompletionTask, as required."
 | 
			
		||||
        << L"\n  this = "
 | 
			
		||||
        << this
 | 
			
		||||
        << L"\n  data = "
 | 
			
		||||
        << data
 | 
			
		||||
        << lw::Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionList::dbg_taskNotAborted(CompletionTask* task)
 | 
			
		||||
{
 | 
			
		||||
    stderr << lw::Log::start(L"[DEBUG] CompletionList")
 | 
			
		||||
        << L"Task not aborted."
 | 
			
		||||
        << L"\n  this = "
 | 
			
		||||
        << this
 | 
			
		||||
        << L"\n  task = "
 | 
			
		||||
        << task
 | 
			
		||||
        << lw::Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CompletionList::dbg_taskNotActive(CompletionTask* task)
 | 
			
		||||
{
 | 
			
		||||
    stderr << lw::Log::start(L"[DEBUG] CompletionList")
 | 
			
		||||
        << L"Task passed to CompletionList::completed() or aborted() not active."
 | 
			
		||||
        << L"\n  this = "
 | 
			
		||||
        << this
 | 
			
		||||
        << L"\n  task = "
 | 
			
		||||
        << task
 | 
			
		||||
        << lw::Log::end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,257 @@
 | 
			
		|||
/* lw-support/src/lib/Util/Completion.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Task completion callback data base class.
 | 
			
		||||
class CompletionData {
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor (does nothing).
 | 
			
		||||
    virtual ~CompletionData()
 | 
			
		||||
    { }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Completion callback interface.
 | 
			
		||||
 | 
			
		||||
Certain tasks use a callback to signal when they have been completed. This interface supports this
 | 
			
		||||
by providing a common callback function. To implement this, you derive your signal's target class
 | 
			
		||||
from lw::CompletionCallback and override the two functions.
 | 
			
		||||
 | 
			
		||||
The task class must take a pointer to a CompletionCallback instance as a parameter. When its task is
 | 
			
		||||
done, it will call the completed() function; if the task must be stopped early, it will call the
 | 
			
		||||
aborted() function. This function also provides an indication of what caused the task to abort. The
 | 
			
		||||
task class may be derived from CompletionTask, but this is not a requirement.
 | 
			
		||||
 | 
			
		||||
It is possible to associate arbitrary data with the task. This must be generated in or passed to the
 | 
			
		||||
task object. It will then be passed as the \a data parameter to the callback function. The data
 | 
			
		||||
%class must be derived from the lw::CompletionData class.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class CompletionCallback {
 | 
			
		||||
public:
 | 
			
		||||
    /// Destructor (does nothing).
 | 
			
		||||
    virtual ~CompletionCallback()
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Task complete signal.
 | 
			
		||||
 | 
			
		||||
    \param data Arbitrary data associated with the task.
 | 
			
		||||
 | 
			
		||||
    This function is called to signal that the task has been completed, presumably successfully.
 | 
			
		||||
    If any data was associated with the task, it will be passed in through the \a data pointer,
 | 
			
		||||
    although it would be valid for this to be zero.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void completed(CompletionData* data) = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Task aborted signal.
 | 
			
		||||
 | 
			
		||||
    \param data Arbitrary data associated with the task.
 | 
			
		||||
    \param e The exception that caused the task to abort.
 | 
			
		||||
 | 
			
		||||
    This function is called to signal that the task has been aborted without completing.
 | 
			
		||||
    If any data was associated with the task, it will be passed in through the \a data pointer,
 | 
			
		||||
    although it would be valid for this to be zero.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void aborted(CompletionData* data, const lw::Exception& e) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Task already completed.
 | 
			
		||||
class TaskAlreadyCompleted : public lw::ProgramException { };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Completion task suggested interface.
 | 
			
		||||
 | 
			
		||||
This class is the suggested (but not required) interface for a task that uses the completion
 | 
			
		||||
notifier interface provided by CompletionCallback. It is a safe and effective way of ensuring that
 | 
			
		||||
all required completion events are generated and only one event is ever generated. It is also the
 | 
			
		||||
required interface for the CompletionList interface.
 | 
			
		||||
 | 
			
		||||
Instances of this class will pass a pointer to themselves as the \a data parameter to the callback
 | 
			
		||||
functions. If the callback class throws an exception, this will be propagated back to the function
 | 
			
		||||
that generated the signal (or discarded if it was the destructor).
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class CompletionTask : public CompletionData {
 | 
			
		||||
private:
 | 
			
		||||
    CompletionCallback* callback;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /*! \brief Report task completed.
 | 
			
		||||
 | 
			
		||||
    \throws lw::TaskAlreadyCompleted if the task is already done.
 | 
			
		||||
 | 
			
		||||
    This function will generate the signal to the callback class. The data passed will be a pointer
 | 
			
		||||
    to the instance itself. Once signalled, no further signal can be sent. Any attempt to send more
 | 
			
		||||
    than one signal will result in an lw::TaskAlreadyCompleted exception.
 | 
			
		||||
 | 
			
		||||
    \sa lw::CompletionCallback::completed()
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void taskCompleted();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Report task aborted.
 | 
			
		||||
 | 
			
		||||
    \param e Exception giving reason for task abort.
 | 
			
		||||
    \throws lw::TaskAlreadyCompleted if the task is already done.
 | 
			
		||||
 | 
			
		||||
    This function will generate the signal to the callback class. The data passed will be a pointer
 | 
			
		||||
    to the instance itself. Once signalled, no further signal can be sent. Any attempt to send more
 | 
			
		||||
    than one signal will result in an lw::TaskAlreadyCompleted exception.
 | 
			
		||||
 | 
			
		||||
    \sa lw::CompletionCallback::completed()
 | 
			
		||||
 | 
			
		||||
     */
 | 
			
		||||
    void taskAborted(const lw::Exception& e);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param callback The callback object.
 | 
			
		||||
    \throws lw::NullPointer if \a callback is zero.
 | 
			
		||||
 | 
			
		||||
    Stores the callback object for later signalling.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    CompletionTask(CompletionCallback* callback);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Destructor. Aborts task if not completed.
 | 
			
		||||
 | 
			
		||||
    The destructor will check to see if the task has been completed or not. If it has, then it will
 | 
			
		||||
    take no action; if it has not, the task will be aborted with a TaskAborted exception.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual ~CompletionTask();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Abort the task.
 | 
			
		||||
 | 
			
		||||
    \param e Exception giving reason for task abort.
 | 
			
		||||
    \throws lw::TaskAlreadyCompleted if the task is already done.
 | 
			
		||||
 | 
			
		||||
    This function is called by the destructor if the task is still in progress when the object is
 | 
			
		||||
    destroyed. It may also be made available to other functions in your class and potentially users
 | 
			
		||||
    of your class (lw::CompletionList for example).
 | 
			
		||||
 | 
			
		||||
    On calling this function, the task must be aborted and the taskAborted() signal must be sent
 | 
			
		||||
    before returning from the function.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual void taskAbort(const lw::Exception& e) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief List of tasks to be completed.
 | 
			
		||||
 | 
			
		||||
This class is a useful utility which maintains a list of tasks in progress. As each task is
 | 
			
		||||
completed or aborted, it is removed from the list. When the object is destroyed, or when abortAll()
 | 
			
		||||
is called, it will call the lw::CompletionTask::taskAbort() function for every task still in the
 | 
			
		||||
list.
 | 
			
		||||
 | 
			
		||||
This class is not thread-safe. A set of three debug functions are provided to cater for
 | 
			
		||||
unexpected conditions. dbg_taskNotAborted() is called if lw::CompletionTask::abortTask() does not
 | 
			
		||||
result in an immediate call to completed() or aborted(). dbg_dataNotTask() is called if completed()
 | 
			
		||||
or aborted() are passed a CompletionData pointer that is not a CompletionTask pointer.
 | 
			
		||||
dbg_taskNotActive() is called if completed() or aborted() are passed a CompletionTask pointer that
 | 
			
		||||
is not in the active task list. Their default behaviour is to print an error message through
 | 
			
		||||
lw::stderr, but they can be overridden.
 | 
			
		||||
 | 
			
		||||
\todo We use an unsorted list here; sort it.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class CompletionList : public CompletionCallback,
 | 
			
		||||
    private Uncopyable
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    std::list<CompletionTask*> tasks;
 | 
			
		||||
    CompletionTask* abortingTask;
 | 
			
		||||
    typedef std::list<CompletionTask*>::iterator taskIter;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /// Debug function.
 | 
			
		||||
    virtual void dbg_taskNotAborted(CompletionTask* task);
 | 
			
		||||
    /// Debug function.
 | 
			
		||||
    virtual void dbg_dataNotTask(CompletionData* data);
 | 
			
		||||
    /// Debug function.
 | 
			
		||||
    virtual void dbg_taskNotActive(CompletionTask* task);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor.
 | 
			
		||||
    CompletionList();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Destructor. Aborts all active tasks.
 | 
			
		||||
 | 
			
		||||
    The destructor will abort all active tasks by calling abortAll() if necessary.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    virtual ~CompletionList();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Register a new task.
 | 
			
		||||
 | 
			
		||||
    \param task The task to be completed.
 | 
			
		||||
    \throws lw::NullPointer if \a task is zero.
 | 
			
		||||
 | 
			
		||||
    Registers a new task for completion. The task must have been instantiated to use this
 | 
			
		||||
    list instance as its callback (or at least the callback signals must reach this object). If the
 | 
			
		||||
    task is still active when the list instance is destroyed, or abortAll() is called, the task's
 | 
			
		||||
    own abort function will be called.
 | 
			
		||||
 | 
			
		||||
    \note There is no guarding against adding the same task twice. Doing so will break things.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void registerTask(CompletionTask* task);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Abort all active tasks.
 | 
			
		||||
 | 
			
		||||
    Attempts to abort all active tasks by calling lw::CompletionTask::abortTask() for each such
 | 
			
		||||
    task. Any exception thrown from this function will be ignored; however, if there are any tasks
 | 
			
		||||
    left unregistered at the end of the function, dbg_taskNotAborted() will be called for each
 | 
			
		||||
    remaining task.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    void abortAll();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Returns true if there are no active tasks.
 | 
			
		||||
    bool empty();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // implemented virtuals from lw::CompletionCallback
 | 
			
		||||
    virtual void completed(CompletionData* data);
 | 
			
		||||
    virtual void aborted(CompletionData* data, const lw::Exception& e);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
/* lw-support/src/lib/Util/FIFO.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void FIFO::reallocBuffer(size_t required)
 | 
			
		||||
{
 | 
			
		||||
    // sanity checks
 | 
			
		||||
    if(required < bufferSize) return;
 | 
			
		||||
    if(required > maxSize) throw FIFOOverflow();
 | 
			
		||||
 | 
			
		||||
    // try to reduce frequency of small allocations
 | 
			
		||||
    required = std::min(required + 128, maxSize);
 | 
			
		||||
 | 
			
		||||
    // try to allocate memory
 | 
			
		||||
    char* newBuffer = 0;
 | 
			
		||||
    try {
 | 
			
		||||
        newBuffer = new char[required];
 | 
			
		||||
    }
 | 
			
		||||
    catch(std::bad_alloc&) {
 | 
			
		||||
        throw FIFOOutOfMemory(required);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // copy across old data
 | 
			
		||||
    if(data) {
 | 
			
		||||
        memcpy(newBuffer, data, dataSize);
 | 
			
		||||
        data = newBuffer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // free old memory
 | 
			
		||||
    delete [] buffer;
 | 
			
		||||
    buffer = newBuffer;
 | 
			
		||||
    bufferSize = required;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,329 @@
 | 
			
		|||
/* lw-support/src/lib/Util/FIFO.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief FIFO memory buffer.
 | 
			
		||||
 | 
			
		||||
This class is an implementation of a FIFO memory buffer. It has an
 | 
			
		||||
absolute maximum upper limit on how much memory has been consumed, and
 | 
			
		||||
you can specify how much of that limit is initially allocated. The
 | 
			
		||||
functions are all inline for speed.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class FIFO {
 | 
			
		||||
private:
 | 
			
		||||
    // These refer to the block of memory that is allocated.
 | 
			
		||||
    char* buffer;
 | 
			
		||||
    size_t bufferSize, maxSize;
 | 
			
		||||
 | 
			
		||||
    // These refer to the data in the buffer.
 | 
			
		||||
    char* data; // set to zero if dataSize == 0
 | 
			
		||||
    size_t dataSize;
 | 
			
		||||
 | 
			
		||||
    // Reallocate buffer ('required' is the new total buffer size that
 | 
			
		||||
    // is needed).
 | 
			
		||||
    void reallocBuffer(size_t required);
 | 
			
		||||
 | 
			
		||||
    // Fast realloc check, also moves data if that would save an
 | 
			
		||||
    // allocation.
 | 
			
		||||
    inline void ensureBuffer(size_t required)
 | 
			
		||||
    {
 | 
			
		||||
        if(required > bufferSize) {
 | 
			
		||||
            reallocBuffer(required);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(data && (data + required) > (buffer + bufferSize)) {
 | 
			
		||||
            memmove(buffer, data, dataSize);
 | 
			
		||||
            data = buffer;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    //BEGIN // Constructors etc. ///////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Constructor.
 | 
			
		||||
 | 
			
		||||
    \param hint This hints at how much memory should be allocated.
 | 
			
		||||
    \param maxSize The absolute maximum number of bytes to allocate.
 | 
			
		||||
 | 
			
		||||
    Sets up the FIFO buffer. The absolute maximum size must be specified
 | 
			
		||||
    here (it can be changed later). A hint as to how much memory to
 | 
			
		||||
    initially allocate must also be provided.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    FIFO(size_t hint, size_t maxSize)
 | 
			
		||||
        : buffer((hint && maxSize) ? new char[std::min(hint, maxSize)] : 0),
 | 
			
		||||
          bufferSize(hint), maxSize(maxSize),
 | 
			
		||||
          data(0), dataSize(0)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Destructor (frees allocated memory).
 | 
			
		||||
    ~FIFO()
 | 
			
		||||
    { delete [] buffer; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Change absolute maximum size.
 | 
			
		||||
 | 
			
		||||
    \param newMaxSize The buffer's new absolute maximum size in bytes.
 | 
			
		||||
    \throws FIFOOverflow if the buffer currently contains more than
 | 
			
		||||
        \a newMaxSize bytes.
 | 
			
		||||
 | 
			
		||||
    This function will change the upper size limit of the FIFO memory
 | 
			
		||||
    buffer. It can be used to make the buffer larger at any time. It can
 | 
			
		||||
    also be used to make the buffer smaller (possibly freeing up memory
 | 
			
		||||
    in the process); but you cannot make the buffer so small that its
 | 
			
		||||
    contents no longer fit.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline void setMaxSize(size_t newMaxSize)
 | 
			
		||||
    {
 | 
			
		||||
        if(newMaxSize < dataSize) throw FIFOOverflow();
 | 
			
		||||
        maxSize = newMaxSize;
 | 
			
		||||
 | 
			
		||||
        // might have to reallocate the buffer if it is too big
 | 
			
		||||
        if(bufferSize > newMaxSize) reallocBuffer(newMaxSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Query the absolute maximum size.
 | 
			
		||||
    inline size_t getMaxSize() const
 | 
			
		||||
    {
 | 
			
		||||
        return maxSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Buffering functions /////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /*! \brief Gets ready to add some data.
 | 
			
		||||
 | 
			
		||||
    \param amt The maximum amount of data the buffer will be required to
 | 
			
		||||
        accept.
 | 
			
		||||
    \returns A pointer to the memory location at which the data should
 | 
			
		||||
        be written.
 | 
			
		||||
    \throws FIFOOverflow if the buffer is too small.
 | 
			
		||||
 | 
			
		||||
    This function ensures that the buffer has enough space to accept at
 | 
			
		||||
    least \a amt bytes of additional data. This might require allocating
 | 
			
		||||
    more memory or moving the current buffer data around. If you call
 | 
			
		||||
    this twice in a row, then the first call is totally forgotten. It
 | 
			
		||||
    doesn't change the object's internal state; it just makes sure that
 | 
			
		||||
    you can add \a amt bytes if you want to, and tells you where.
 | 
			
		||||
 | 
			
		||||
    \sa addedData()
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline char* getBuffer(size_t amt)
 | 
			
		||||
    {
 | 
			
		||||
        ensureBuffer(amt + dataSize);
 | 
			
		||||
        return data ? data + dataSize : buffer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Records how much data was added to the buffer.
 | 
			
		||||
 | 
			
		||||
    \param amt The amount of data that was actually added to the buffer.
 | 
			
		||||
 | 
			
		||||
    Once you have prepared the buffer with getBuffer(), you must add the
 | 
			
		||||
    data. This function records how much data was added (so that new
 | 
			
		||||
    data will be added after it).
 | 
			
		||||
 | 
			
		||||
    \warning No checking is done on \a amt, so make sure you put the
 | 
			
		||||
        right amount. Also make sure that getBuffer() and addedData()
 | 
			
		||||
        calls occur in pairs.
 | 
			
		||||
 | 
			
		||||
    \sa getBuffer(), addData()
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline void addedData(size_t amt)
 | 
			
		||||
    {
 | 
			
		||||
        if(!data) data = buffer;
 | 
			
		||||
        dataSize += amt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Adds some data to the buffer.
 | 
			
		||||
 | 
			
		||||
    \param amt The number of bytes to add.
 | 
			
		||||
    \param newData The data to add.
 | 
			
		||||
    \throws FIFOOverflow if the buffer is too small.
 | 
			
		||||
 | 
			
		||||
    Adds a specific block of data to the buffer. This can only be used
 | 
			
		||||
    if the data is coming from somewhere else in memory (e.g. an
 | 
			
		||||
    mmap()ed file), and you know in advance how much there is.
 | 
			
		||||
 | 
			
		||||
    \sa addedData()
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline void addData(const char* newData, size_t amt)
 | 
			
		||||
    {
 | 
			
		||||
        ensureBuffer(amt + dataSize);
 | 
			
		||||
        if(data) {
 | 
			
		||||
            memcpy(data + dataSize, newData, amt);
 | 
			
		||||
            dataSize += amt;
 | 
			
		||||
        } else {
 | 
			
		||||
            memcpy(data = buffer, newData, amt);
 | 
			
		||||
            dataSize = amt;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Adds a byte of data to the buffer.
 | 
			
		||||
 | 
			
		||||
    \param byte The byte of data to add.
 | 
			
		||||
    \throws FIFOOverflow if the buffer is too small.
 | 
			
		||||
 | 
			
		||||
    Adds a single byte of data to the buffer. This is the most efficient
 | 
			
		||||
    method for byte-oriented buffering.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline void addByte(char byte)
 | 
			
		||||
    {
 | 
			
		||||
        ensureBuffer(dataSize + 1);
 | 
			
		||||
        if(data) {
 | 
			
		||||
            data[dataSize++] = byte;
 | 
			
		||||
        } else {
 | 
			
		||||
            *(data = buffer) = byte;
 | 
			
		||||
            dataSize = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Reading functions ///////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Returns how many bytes of data there are in the buffer.
 | 
			
		||||
    inline size_t getSize() const
 | 
			
		||||
    {
 | 
			
		||||
        return dataSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns how many bytes of data there are in the buffer.
 | 
			
		||||
    inline size_t size() const
 | 
			
		||||
    {
 | 
			
		||||
        return dataSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Returns how many bytes are available to the buffer.
 | 
			
		||||
    inline size_t getRemaining() const
 | 
			
		||||
    {
 | 
			
		||||
        return maxSize - dataSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Returns how many (preallocated) bytes are available to the buffer.
 | 
			
		||||
    inline size_t getRemainingPreallocated() const
 | 
			
		||||
    {
 | 
			
		||||
        return bufferSize - dataSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// Returns pointer to start of buffer (0 if empty).
 | 
			
		||||
    inline const char* getData() const
 | 
			
		||||
    {
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Retrieves some data from the buffer.
 | 
			
		||||
 | 
			
		||||
    \param amt The number of bytes to retrieve.
 | 
			
		||||
    \throws FIFOUnderflow if the buffer contains less than \a amt bytes.
 | 
			
		||||
    \returns A pointer to the start of the data (0 if \a amt = 0).
 | 
			
		||||
 | 
			
		||||
    This function allows you to get data out of the buffer. It will
 | 
			
		||||
    record how many bytes were used, so that space will be free the next
 | 
			
		||||
    time anyone wants to use it.
 | 
			
		||||
 | 
			
		||||
    \warning The pointer returned may become invalid as soon as you
 | 
			
		||||
        perform any other operation on the FIFO, so you must finish
 | 
			
		||||
        using the data it points to before calling any other functions.
 | 
			
		||||
 | 
			
		||||
    \sa getSize()
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline char* getData(size_t amt)
 | 
			
		||||
    {
 | 
			
		||||
        // sanity checking
 | 
			
		||||
        if(!amt) return 0;
 | 
			
		||||
        if(amt > dataSize) throw FIFOUnderflow();
 | 
			
		||||
 | 
			
		||||
        // eat data
 | 
			
		||||
        char* result = data;
 | 
			
		||||
        if(!(dataSize -= amt)) data = 0;
 | 
			
		||||
        else data += amt;
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Gets a single byte of data from the buffer.
 | 
			
		||||
 | 
			
		||||
    \returns The byte of data removed from the buffer.
 | 
			
		||||
    \throws FIFOUnderflow if the buffer is empty.
 | 
			
		||||
    \returns A single byte of data.
 | 
			
		||||
 | 
			
		||||
    Retrieves a single byte of data from the buffer. This is the most
 | 
			
		||||
    efficient method for byte-oriented buffering.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    inline char getByte()
 | 
			
		||||
    {
 | 
			
		||||
        if(!dataSize) throw FIFOUnderflow();
 | 
			
		||||
        char byte = *data;
 | 
			
		||||
        if(!--dataSize) data = 0;
 | 
			
		||||
        else ++data;
 | 
			
		||||
        return byte;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //BEGIN // Convenience functions ///////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the buffer is empty.
 | 
			
		||||
    inline bool isEmpty() const
 | 
			
		||||
    {
 | 
			
		||||
        return !dataSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the buffer is empty.
 | 
			
		||||
    inline bool empty() const
 | 
			
		||||
    {
 | 
			
		||||
        return !dataSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns true if the buffer is full.
 | 
			
		||||
    inline bool isFull() const
 | 
			
		||||
    {
 | 
			
		||||
        return dataSize == maxSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Empties the buffer.
 | 
			
		||||
    inline void clear()
 | 
			
		||||
    {
 | 
			
		||||
        data = 0;
 | 
			
		||||
        dataSize = 0;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
/* lw-support/src/lib/Util/Key.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
Key::keyType Key::globalKey = 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring Key::toString() const
 | 
			
		||||
{
 | 
			
		||||
    std::wostringstream str;
 | 
			
		||||
 | 
			
		||||
    str << L"{KEY{!"
 | 
			
		||||
        << key
 | 
			
		||||
        << L"}}";
 | 
			
		||||
 | 
			
		||||
    return str.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
lw::Key Key::fromString(const std::wstring& strIn)
 | 
			
		||||
{
 | 
			
		||||
    std::wstring str = stripWhitespace(strIn);
 | 
			
		||||
    std::wstring::size_type end = str.size();
 | 
			
		||||
 | 
			
		||||
    if(end < 9 || str.substr(0, 6) != L"!KEY{!" || str.substr(end - 2, 2) != L"}}")
 | 
			
		||||
        throw ParseError(L"Not a valid key.", str, 0);
 | 
			
		||||
    return Key(strToUInt64(str.substr(6, end - 8)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
/* lw-support/src/lib/Util/Key.h
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*! \brief Key object.
 | 
			
		||||
 | 
			
		||||
The key object is used to store some form of identifying key. Typically this will be used to store a
 | 
			
		||||
unique key number.
 | 
			
		||||
 | 
			
		||||
\note We use a 64-bit integer to store the key. It is somewhat conceivable that this could be
 | 
			
		||||
    wrapped around at some point. If you're really generating keys that frequently, don't use this
 | 
			
		||||
    class and instead write a 128-bit or wider class.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
class Key {
 | 
			
		||||
public:
 | 
			
		||||
    /// Constructor. Generates an invalid key.
 | 
			
		||||
    Key()
 | 
			
		||||
        : key(0)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Copy constructor.
 | 
			
		||||
    Key(const Key& other)
 | 
			
		||||
        : key(other.key)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// Assignment operator.
 | 
			
		||||
    Key& operator=(Key other)
 | 
			
		||||
    { key = other.key; return *this; }
 | 
			
		||||
 | 
			
		||||
    /// Compares two keys for equality.
 | 
			
		||||
    bool operator==(Key other) const
 | 
			
		||||
    { return key == other.key; }
 | 
			
		||||
 | 
			
		||||
    /// Compares two keys for inequality.
 | 
			
		||||
    bool operator!=(Key other) const
 | 
			
		||||
    { return key != other.key; }
 | 
			
		||||
 | 
			
		||||
    /// Allows ordering by key.
 | 
			
		||||
    bool operator<(Key other) const
 | 
			
		||||
    { return key < other.key; }
 | 
			
		||||
 | 
			
		||||
    /// Returns status of key.
 | 
			
		||||
    bool operator!() const
 | 
			
		||||
    { return !key; }
 | 
			
		||||
 | 
			
		||||
    /// Returns status of key.
 | 
			
		||||
    operator void*() const
 | 
			
		||||
    { return key ? (void*)this : 0; }
 | 
			
		||||
 | 
			
		||||
    /// Returns status of key.
 | 
			
		||||
    bool valid() const
 | 
			
		||||
    { return key; }
 | 
			
		||||
 | 
			
		||||
    /// Makes the key invalid.
 | 
			
		||||
    void clear()
 | 
			
		||||
    { key = 0; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /*! \brief Represent the key as a string.
 | 
			
		||||
 | 
			
		||||
    \returns A string representing the value of the key.
 | 
			
		||||
 | 
			
		||||
    This function allows the key to be represented as a string. The string will always be started
 | 
			
		||||
    with the sequence \c {KEY{ and it will always end with \c }} but its contents are implementation
 | 
			
		||||
    defined. The string generated will consist only of ASCII characters and all characters will be
 | 
			
		||||
    printable.
 | 
			
		||||
 | 
			
		||||
    */
 | 
			
		||||
    std::wstring toString() const;
 | 
			
		||||
 | 
			
		||||
    /// Construct a key from a string.
 | 
			
		||||
    static lw::Key fromString(const std::wstring& str);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    typedef uint_fast64_t keyType;
 | 
			
		||||
    keyType key;
 | 
			
		||||
 | 
			
		||||
    // Explicitly construct a key from a value.
 | 
			
		||||
    Key(keyType key) : key(key) { }
 | 
			
		||||
 | 
			
		||||
    static keyType globalKey;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /// Generate a program-wide unique key.
 | 
			
		||||
    static Key get()
 | 
			
		||||
    { ++globalKey; return Key(globalKey); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,355 @@
 | 
			
		|||
/* lw-support/src/lib/Util/Log.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  (c)2005, Laurence Withers. Released under the GNU GPL. See file
 | 
			
		||||
 *  COPYING for more information / terms of license.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace lw {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log stderr(std::wcerr, true);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log::Log(std::wostream& outStream, bool enabled)
 | 
			
		||||
  : out(outStream), wstr(0), enabled(enabled), streamWidth(78),
 | 
			
		||||
    dateExtended(true), dateDecimal(false), dateFormat(lw::ISO8601FormatCalendar),
 | 
			
		||||
    datePrecision(lw::ISO8601PrecisionFull)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log::Log(const std::string& fileName, bool enabled)
 | 
			
		||||
  : out(file), wstr(0), enabled(enabled), streamWidth(78),
 | 
			
		||||
    dateExtended(true), dateDecimal(false), dateFormat(lw::ISO8601FormatCalendar),
 | 
			
		||||
    datePrecision(lw::ISO8601PrecisionFull)
 | 
			
		||||
{
 | 
			
		||||
    file.open(fileName.c_str());
 | 
			
		||||
    // FIXME: better exception required
 | 
			
		||||
    if(!file) throw lw::FileNotFound(fileName.c_str())
 | 
			
		||||
        .chain(L"Log::Log()");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log::~Log()
 | 
			
		||||
{
 | 
			
		||||
    delete wstr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void Log::checkBox()
 | 
			
		||||
{
 | 
			
		||||
    if(!wstr) throw BoxException();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::setStreamWidth(size_t streamWidth)
 | 
			
		||||
{
 | 
			
		||||
    this->streamWidth = streamWidth;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::setDateFormat(bool dateExtended, bool dateDecimal,
 | 
			
		||||
    lw::ISO8601Format dateFormat, lw::ISO8601Precision datePrecision)
 | 
			
		||||
{
 | 
			
		||||
    this->dateExtended = dateExtended;
 | 
			
		||||
    this->dateDecimal = dateDecimal;
 | 
			
		||||
    this->dateFormat = dateFormat;
 | 
			
		||||
    this->datePrecision = datePrecision;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::startBox(const std::wstring& subject)
 | 
			
		||||
{
 | 
			
		||||
    if(wstr) throw BoxException();
 | 
			
		||||
    wstr = new std::wostringstream();
 | 
			
		||||
 | 
			
		||||
    wstr->clear();
 | 
			
		||||
    *wstr << L"==== "
 | 
			
		||||
          << DateTime::now().toString(dateExtended, dateDecimal,
 | 
			
		||||
                 dateFormat, datePrecision)
 | 
			
		||||
          << L" ====";
 | 
			
		||||
 | 
			
		||||
    int remaining = streamWidth - (wstr->str().length() + subject.length() + 6);
 | 
			
		||||
    if(remaining > 0) *wstr << std::wstring(remaining, '=');
 | 
			
		||||
    *wstr << L' '
 | 
			
		||||
          << subject
 | 
			
		||||
          << L" ====\n\n";
 | 
			
		||||
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::enable(bool enabled)
 | 
			
		||||
{
 | 
			
		||||
    this->enabled = enabled;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::disable()
 | 
			
		||||
{
 | 
			
		||||
    enabled = false;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::endBox()
 | 
			
		||||
{
 | 
			
		||||
    checkBox();
 | 
			
		||||
    if(enabled) out << wstr->str() << L"\n\n" << std::endl;
 | 
			
		||||
    delete wstr;
 | 
			
		||||
    wstr = 0;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::abortBox()
 | 
			
		||||
{
 | 
			
		||||
    delete wstr;
 | 
			
		||||
    wstr = 0;
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::hexDumpData(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    checkBox();
 | 
			
		||||
 | 
			
		||||
    const size_t blocksPerLine = std::max(streamWidth / 34, size_t(1)),
 | 
			
		||||
        charsPerLine = blocksPerLine * 8,
 | 
			
		||||
        upperBlockSize = charsPerLine * ((amt + charsPerLine - 1) / charsPerLine);
 | 
			
		||||
    size_t blocksThisLine = 0, lineStartPos = 0;
 | 
			
		||||
    wchar_t ascii[charsPerLine + 1];
 | 
			
		||||
    ascii[charsPerLine] = 0;
 | 
			
		||||
 | 
			
		||||
    *wstr << L"Hex dump, "
 | 
			
		||||
          << amt
 | 
			
		||||
          << " bytes:\n"
 | 
			
		||||
          << std::hex
 | 
			
		||||
          << std::setfill(L'0');
 | 
			
		||||
 | 
			
		||||
    for(size_t pos = 0; pos < amt; ++pos) {
 | 
			
		||||
        uint8_t ch = data[pos];
 | 
			
		||||
        if(!(pos & 7)) {
 | 
			
		||||
            *wstr << L' ';
 | 
			
		||||
            if(blocksThisLine++ == blocksPerLine) {
 | 
			
		||||
                blocksThisLine = 1;
 | 
			
		||||
                *wstr << ascii << L"\n ";
 | 
			
		||||
                lineStartPos = pos;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        *wstr << std::setw(2) << uint(ch) << L' ';
 | 
			
		||||
        ascii[pos - lineStartPos] = wchar_t(isprint(ch) ? ch : '.');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for(size_t pos = amt; pos < upperBlockSize; ++pos) {
 | 
			
		||||
        if(!(pos & 7)) {
 | 
			
		||||
            *wstr << L' ';
 | 
			
		||||
            if(blocksThisLine++ == blocksPerLine) break;
 | 
			
		||||
        }
 | 
			
		||||
        *wstr << L"   ";
 | 
			
		||||
        ascii[pos - lineStartPos] = ' ';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *wstr << L' ' << ascii << L'\n';
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log& Log::asciiDumpData(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    checkBox();
 | 
			
		||||
 | 
			
		||||
    *wstr << L"- ASCII dump, "
 | 
			
		||||
          << amt
 | 
			
		||||
          << " bytes: -----------------\n"
 | 
			
		||||
          << std::hex
 | 
			
		||||
          << std::setfill(L'0');
 | 
			
		||||
 | 
			
		||||
    for(size_t pos = 0; pos < amt; ++pos) {
 | 
			
		||||
        switch(data[pos]) {
 | 
			
		||||
            case '\r':
 | 
			
		||||
                *wstr << L"\\r";
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            case '\n':
 | 
			
		||||
                *wstr << L"\\n\n";
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            case '\t':
 | 
			
		||||
                *wstr << L"\\t";
 | 
			
		||||
                continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(isprint(data[pos])) {
 | 
			
		||||
            *wstr << wchar_t(data[pos]);
 | 
			
		||||
        } else {
 | 
			
		||||
            *wstr << L'<'
 | 
			
		||||
                  << std::setw(2)
 | 
			
		||||
                  << uint(uint8_t(data[pos]))
 | 
			
		||||
                  << L'>';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *wstr << L"\n- End of dump ---------------------\n";
 | 
			
		||||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log::LogManipArgStart Log::start(const std::wstring& subject)
 | 
			
		||||
{
 | 
			
		||||
    return LogManipArgStart(subject);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log::LogManipArgHex Log::hexDump(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    return LogManipArgHex(data, amt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Log::LogManipArgAscii Log::asciiDump(const char* data, size_t amt)
 | 
			
		||||
{
 | 
			
		||||
    return LogManipArgAscii(data, amt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
    Log& operator<<(Log& log, const Log::LogManip& l)
 | 
			
		||||
{
 | 
			
		||||
    log.checkBox();
 | 
			
		||||
 | 
			
		||||
    switch(l) {
 | 
			
		||||
    case Log::end:
 | 
			
		||||
        log.endBox();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case Log::abort:
 | 
			
		||||
        log.abortBox();
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return log;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
    Log& operator<<(Log& log, const lw::Log::LogManipArgStart& l)
 | 
			
		||||
{
 | 
			
		||||
    log.startBox(l.subject);
 | 
			
		||||
    return log;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
    Log& operator<<(Log& log, const lw::Log::LogManipArgHex& l)
 | 
			
		||||
{
 | 
			
		||||
    log.hexDumpData(l.data, l.amt);
 | 
			
		||||
    return log;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
    Log& operator<<(Log& log, const lw::Log::LogManipArgAscii& l)
 | 
			
		||||
{
 | 
			
		||||
    log.asciiDumpData(l.data, l.amt);
 | 
			
		||||
    return log;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
    Log& operator<<(Log& log, const char* const& str)
 | 
			
		||||
{
 | 
			
		||||
    if(!str) {
 | 
			
		||||
        *(log.wstr) << L"{{{null}}}";
 | 
			
		||||
        return log;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        // attempt UTF-8 conversion
 | 
			
		||||
        *(log.wstr) << lw::utf8ToUcs4(str);
 | 
			
		||||
    }
 | 
			
		||||
    catch(...) {
 | 
			
		||||
        // `safe' conversion
 | 
			
		||||
        *(log.wstr) << L"{{{unknown encoding}}}";
 | 
			
		||||
        for(const char* q = str; *q; ++q) {
 | 
			
		||||
            switch(*q) {
 | 
			
		||||
            case '\n':
 | 
			
		||||
            case '\t':
 | 
			
		||||
            case ' ' ... '~': // 32 ... 126
 | 
			
		||||
                log.wstr->put(*q);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                log.wstr->put('?');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return log;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
    Log& operator<<(Log& log, const std::string& str)
 | 
			
		||||
{
 | 
			
		||||
    return (log << str.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
lw::Log& operator<<(lw::Log& log, const std::exception& e)
 | 
			
		||||
{
 | 
			
		||||
    return (log << e.what());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
lw::Log& operator<<(lw::Log& log, const lw::Exception& e)
 | 
			
		||||
{
 | 
			
		||||
    std::cerr << L"[DEBUG] in lw::Exception-specific insertion operator\n";
 | 
			
		||||
    return (log << e.toString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wostream& operator<<(std::wostream& ostr, const std::exception& e)
 | 
			
		||||
{
 | 
			
		||||
    ostr << lw::strToUcs4(e.what());
 | 
			
		||||
    return ostr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* options for text editors
 | 
			
		||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
 | 
			
		||||
*/
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue