From 99f8ce096bc5569adbfea1911dbcda24c28d8d8b Mon Sep 17 00:00:00 2001 From: Ben Summers Date: Fri, 14 Oct 2005 08:50:54 +0000 Subject: Box Backup 0.09 with a few tweeks --- lib/common/BannerText.h | 17 + lib/common/BeginStructPackForWire.h | 23 + lib/common/Box.h | 180 ++++++++ lib/common/BoxException.cpp | 21 + lib/common/BoxException.h | 37 ++ lib/common/BoxPlatform.h | 236 ++++++++++ lib/common/BoxPortsAndFiles.h | 31 ++ lib/common/BoxTime.cpp | 32 ++ lib/common/BoxTime.h | 44 ++ lib/common/BoxTimeToText.cpp | 41 ++ lib/common/BoxTimeToText.h | 19 + lib/common/BoxTimeToUnix.h | 30 ++ lib/common/CollectInBufferStream.cpp | 274 +++++++++++ lib/common/CollectInBufferStream.h | 60 +++ lib/common/CommonException.h | 17 + lib/common/CommonException.txt | 44 ++ lib/common/Configuration.cpp | 741 ++++++++++++++++++++++++++++++ lib/common/Configuration.h | 98 ++++ lib/common/Conversion.h | 98 ++++ lib/common/ConversionException.txt | 8 + lib/common/ConversionString.cpp | 123 +++++ lib/common/DebugAssertFailed.cpp | 32 ++ lib/common/DebugMemLeakFinder.cpp | 377 +++++++++++++++ lib/common/DebugPrintf.cpp | 72 +++ lib/common/EndStructPackForWire.h | 23 + lib/common/EventWatchFilesystemObject.cpp | 99 ++++ lib/common/EventWatchFilesystemObject.h | 48 ++ lib/common/ExcludeList.cpp | 219 +++++++++ lib/common/ExcludeList.h | 65 +++ lib/common/FdGetLine.cpp | 211 +++++++++ lib/common/FdGetLine.h | 61 +++ lib/common/FileModificationTime.h | 57 +++ lib/common/FileStream.cpp | 245 ++++++++++ lib/common/FileStream.h | 46 ++ lib/common/Guards.h | 112 +++++ lib/common/IOStream.cpp | 229 +++++++++ lib/common/IOStream.h | 67 +++ lib/common/IOStreamGetLine.cpp | 227 +++++++++ lib/common/IOStreamGetLine.h | 71 +++ lib/common/LinuxWorkaround.cpp | 73 +++ lib/common/LinuxWorkaround.h | 20 + lib/common/MainHelper.h | 42 ++ lib/common/Makefile.extra | 11 + lib/common/MemBlockStream.cpp | 235 ++++++++++ lib/common/MemBlockStream.h | 52 +++ lib/common/MemLeakFindOff.h | 27 ++ lib/common/MemLeakFindOn.h | 25 + lib/common/MemLeakFinder.h | 60 +++ lib/common/NamedLock.cpp | 144 ++++++ lib/common/NamedLock.h | 41 ++ lib/common/PartialReadStream.cpp | 135 ++++++ lib/common/PartialReadStream.h | 46 ++ lib/common/ReadGatherStream.cpp | 262 +++++++++++ lib/common/ReadGatherStream.h | 67 +++ lib/common/StreamableMemBlock.cpp | 365 +++++++++++++++ lib/common/StreamableMemBlock.h | 71 +++ lib/common/TemporaryDirectory.h | 26 ++ lib/common/Test.h | 165 +++++++ lib/common/UnixUser.cpp | 121 +++++ lib/common/UnixUser.h | 37 ++ lib/common/Utils.cpp | 159 +++++++ lib/common/Utils.h | 36 ++ lib/common/WaitForEvent.cpp | 194 ++++++++ lib/common/WaitForEvent.h | 146 ++++++ lib/common/makeexception.pl | 277 +++++++++++ 65 files changed, 7272 insertions(+) create mode 100755 lib/common/BannerText.h create mode 100644 lib/common/BeginStructPackForWire.h create mode 100755 lib/common/Box.h create mode 100755 lib/common/BoxException.cpp create mode 100755 lib/common/BoxException.h create mode 100755 lib/common/BoxPlatform.h create mode 100755 lib/common/BoxPortsAndFiles.h create mode 100755 lib/common/BoxTime.cpp create mode 100755 lib/common/BoxTime.h create mode 100755 lib/common/BoxTimeToText.cpp create mode 100755 lib/common/BoxTimeToText.h create mode 100755 lib/common/BoxTimeToUnix.h create mode 100755 lib/common/CollectInBufferStream.cpp create mode 100755 lib/common/CollectInBufferStream.h create mode 100755 lib/common/CommonException.h create mode 100644 lib/common/CommonException.txt create mode 100755 lib/common/Configuration.cpp create mode 100755 lib/common/Configuration.h create mode 100644 lib/common/Conversion.h create mode 100644 lib/common/ConversionException.txt create mode 100644 lib/common/ConversionString.cpp create mode 100755 lib/common/DebugAssertFailed.cpp create mode 100755 lib/common/DebugMemLeakFinder.cpp create mode 100755 lib/common/DebugPrintf.cpp create mode 100644 lib/common/EndStructPackForWire.h create mode 100644 lib/common/EventWatchFilesystemObject.cpp create mode 100644 lib/common/EventWatchFilesystemObject.h create mode 100755 lib/common/ExcludeList.cpp create mode 100755 lib/common/ExcludeList.h create mode 100755 lib/common/FdGetLine.cpp create mode 100755 lib/common/FdGetLine.h create mode 100755 lib/common/FileModificationTime.h create mode 100755 lib/common/FileStream.cpp create mode 100755 lib/common/FileStream.h create mode 100755 lib/common/Guards.h create mode 100755 lib/common/IOStream.cpp create mode 100755 lib/common/IOStream.h create mode 100755 lib/common/IOStreamGetLine.cpp create mode 100755 lib/common/IOStreamGetLine.h create mode 100755 lib/common/LinuxWorkaround.cpp create mode 100755 lib/common/LinuxWorkaround.h create mode 100755 lib/common/MainHelper.h create mode 100755 lib/common/Makefile.extra create mode 100755 lib/common/MemBlockStream.cpp create mode 100755 lib/common/MemBlockStream.h create mode 100755 lib/common/MemLeakFindOff.h create mode 100755 lib/common/MemLeakFindOn.h create mode 100755 lib/common/MemLeakFinder.h create mode 100755 lib/common/NamedLock.cpp create mode 100755 lib/common/NamedLock.h create mode 100755 lib/common/PartialReadStream.cpp create mode 100755 lib/common/PartialReadStream.h create mode 100755 lib/common/ReadGatherStream.cpp create mode 100755 lib/common/ReadGatherStream.h create mode 100755 lib/common/StreamableMemBlock.cpp create mode 100755 lib/common/StreamableMemBlock.h create mode 100755 lib/common/TemporaryDirectory.h create mode 100755 lib/common/Test.h create mode 100755 lib/common/UnixUser.cpp create mode 100755 lib/common/UnixUser.h create mode 100755 lib/common/Utils.cpp create mode 100755 lib/common/Utils.h create mode 100644 lib/common/WaitForEvent.cpp create mode 100644 lib/common/WaitForEvent.h create mode 100755 lib/common/makeexception.pl (limited to 'lib/common') diff --git a/lib/common/BannerText.h b/lib/common/BannerText.h new file mode 100755 index 00000000..8c471171 --- /dev/null +++ b/lib/common/BannerText.h @@ -0,0 +1,17 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BannerText.h +// Purpose: Banner text for daemons and utilities +// Created: 1/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef BANNERTEXT__H +#define BANNERTEXT__H + +#define BANNER_TEXT(UtilityName) \ + "Box " UtilityName " v" BOX_VERSION ", (c) Ben Summers 2003, 2004\n" + +#endif // BANNERTEXT__H + diff --git a/lib/common/BeginStructPackForWire.h b/lib/common/BeginStructPackForWire.h new file mode 100644 index 00000000..f9f8f616 --- /dev/null +++ b/lib/common/BeginStructPackForWire.h @@ -0,0 +1,23 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BeginStructPackForWire.h +// Purpose: Begin structure packing for wire +// Created: 25/11/03 +// +// -------------------------------------------------------------------------- + +// No header guard -- this is intentional + +#ifdef STRUCTURE_PATCKING_FOR_WIRE_USE_HEADERS + +#pragma pack(1) + +#else + + logical error -- check BoxPlatform.h and including file + +#endif + + + diff --git a/lib/common/Box.h b/lib/common/Box.h new file mode 100755 index 00000000..19e78ada --- /dev/null +++ b/lib/common/Box.h @@ -0,0 +1,180 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Box.h +// Purpose: Main header file for the Box project +// Created: 2003/07/08 +// +// -------------------------------------------------------------------------- + +#ifndef BOX__H +#define BOX__H + +// Use the same changes as gcc3 for gcc4 +#ifdef PLATFORM_GCC4 + #define PLATFORM_GCC3 +#endif + +#include "BoxPlatform.h" + +// uncomment this line to enable full memory leak finding on all malloc-ed blocks (at least, ones used by the STL) +//#define MEMLEAKFINDER_FULL_MALLOC_MONITORING + +#ifndef NDEBUG + // not available on OpenBSD... oh well. + //#define SHOW_BACKTRACE_ON_EXCEPTION +#endif + +#ifdef SHOW_BACKTRACE_ON_EXCEPTION + // include "Utils.h" + #define OPTIONAL_DO_BACKTRACE DumpStackBacktrace(); +#else + #define OPTIONAL_DO_BACKTRACE +#endif + +#include "CommonException.h" + +#ifndef NDEBUG + + extern bool AssertFailuresToSyslog; + #define ASSERT_FAILS_TO_SYSLOG_ON {AssertFailuresToSyslog = true;} + void BoxDebugAssertFailed(char *cond, char *file, int line); + #define ASSERT(cond) {if(!(cond)) {BoxDebugAssertFailed(#cond, __FILE__, __LINE__); THROW_EXCEPTION(CommonException, AssertFailed)}} + + // Note that syslog tracing is independent of BoxDebugTraceOn, but stdout tracing is not + extern bool BoxDebugTraceToSyslog; + #define TRACE_TO_SYSLOG(x) {BoxDebugTraceToSyslog = x;} + extern bool BoxDebugTraceToStdout; + #define TRACE_TO_STDOUT(x) {BoxDebugTraceToStdout = x;} + + extern bool BoxDebugTraceOn; + int BoxDebug_printf(const char *format, ...); + int BoxDebugTrace(const char *format, ...); + #define TRACE0(msg) {BoxDebugTrace("%s", msg);} + #define TRACE1(msg, a0) {BoxDebugTrace(msg, a0);} + #define TRACE2(msg, a0, a1) {BoxDebugTrace(msg, a0, a1);} + #define TRACE3(msg, a0, a1, a2) {BoxDebugTrace(msg, a0, a1, a2);} + #define TRACE4(msg, a0, a1, a2, a3) {BoxDebugTrace(msg, a0, a1, a2, a3);} + #define TRACE5(msg, a0, a1, a2, a3, a4) {BoxDebugTrace(msg, a0, a1, a2, a3, a4);} + #define TRACE6(msg, a0, a1, a2, a3, a4, a5) {BoxDebugTrace(msg, a0, a1, a2, a3, a4, a5);} + #define TRACE7(msg, a0, a1, a2, a3, a4, a5, a6) {BoxDebugTrace(msg, a0, a1, a2, a3, a4, a5, a6);} + #define TRACE8(msg, a0, a1, a2, a3, a4, a5, a6, a7) {BoxDebugTrace(msg, a0, a1, a2, a3, a4, a5, a6, a7);} + + #ifndef PLATFORM_DISABLE_MEM_LEAK_TESTING + #define BOX_MEMORY_LEAK_TESTING + #endif + + // Exception names + #define EXCEPTION_CODENAMES_EXTENDED + +#else + #define ASSERT_FAILS_TO_SYSLOG_ON + #define ASSERT(cond) + + #define TRACE_TO_SYSLOG(x) {} + #define TRACE_TO_STDOUT(x) {} + + #define TRACE0(msg) + #define TRACE1(msg, a0) + #define TRACE2(msg, a0, a1) + #define TRACE3(msg, a0, a1, a2) + #define TRACE4(msg, a0, a1, a2, a3) + #define TRACE5(msg, a0, a1, a2, a3, a4) + #define TRACE6(msg, a0, a1, a2, a3, a4, a5) + #define TRACE7(msg, a0, a1, a2, a3, a4, a5, a6) + #define TRACE8(msg, a0, a1, a2, a3, a4, a5, a6, a7) + + // Box Backup builds release get extra information for exception logging + #define EXCEPTION_CODENAMES_EXTENDED + #define EXCEPTION_CODENAMES_EXTENDED_WITH_DESCRIPTION + +#endif + +#ifdef BOX_MEMORY_LEAK_TESTING + // Memory leak testing + #include "MemLeakFinder.h" + #define MEMLEAKFINDER_NOT_A_LEAK(x) memleakfinder_notaleak(x); + #define MEMLEAKFINDER_START {memleakfinder_global_enable = true;} + #define MEMLEAKFINDER_STOP {memleakfinder_global_enable = false;} +#else + #define DEBUG_NEW new + #define MEMLEAKFINDER_NOT_A_LEAK(x) + #define MEMLEAKFINDER_START + #define MEMLEAKFINDER_STOP +#endif + + +#define THROW_EXCEPTION(type, subtype) \ + { \ + OPTIONAL_DO_BACKTRACE \ + TRACE1("Exception thrown: " #type "(" #subtype ") at " __FILE__ "(%d)\n", __LINE__) \ + throw type(type::subtype); \ + } + +// extra macros for converting to network byte order + +// Always define a swap64 function, as it's useful. +inline uint64_t box_swap64(uint64_t x) +{ + return ((x & 0xff) << 56 | + (x & 0xff00LL) << 40 | + (x & 0xff0000LL) << 24 | + (x & 0xff000000LL) << 8 | + (x & 0xff00000000LL) >> 8 | + (x & 0xff0000000000LL) >> 24 | + (x & 0xff000000000000LL) >> 40 | + (x & 0xff00000000000000LL) >> 56); +} + +// Does the platform provide a built in SWAP64 we can use? +#ifdef PLATFORM_NO_BUILT_IN_SWAP64 + + #define hton64(x) box_swap64(x) + #define ntoh64(x) box_swap64(x) + +#else + + #if BYTE_ORDER == BIG_ENDIAN + + // Less hassle than trying to find some working things + // on Darwin PPC + #define hton64(x) (x) + #define ntoh64(x) (x) + + #else + + #ifdef PLATFORM_LINUX + // On Linux, use some internal kernal stuff to do this + #include + #define hton64 __cpu_to_be64 + #define ntoh64 __be64_to_cpu + #else + #define hton64 htobe64 + #define ntoh64 betoh64 + #endif + + // hack to make some of these work + // version in /usr/include/sys/endian.h doesn't include the 'LL' at the end of the constants + // provoking complaints from the compiler + #ifdef __GNUC__ + #undef __swap64gen + #define __swap64gen(x) __extension__({ \ + u_int64_t __swap64gen_x = (x); \ + \ + (u_int64_t)((__swap64gen_x & 0xff) << 56 | \ + (__swap64gen_x & 0xff00LL) << 40 | \ + (__swap64gen_x & 0xff0000LL) << 24 | \ + (__swap64gen_x & 0xff000000LL) << 8 | \ + (__swap64gen_x & 0xff00000000LL) >> 8 | \ + (__swap64gen_x & 0xff0000000000LL) >> 24 | \ + (__swap64gen_x & 0xff000000000000LL) >> 40 | \ + (__swap64gen_x & 0xff00000000000000LL) >> 56); \ + }) + #endif // __GNUC__ + + #endif // n BYTE_ORDER == BIG_ENDIAN + +#endif // PLATFORM_NO_BUILT_IN_SWAP64 + +#endif // BOX__H + diff --git a/lib/common/BoxException.cpp b/lib/common/BoxException.cpp new file mode 100755 index 00000000..2503ca63 --- /dev/null +++ b/lib/common/BoxException.cpp @@ -0,0 +1,21 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxException.cpp +// Purpose: Exception +// Created: 2003/07/10 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "BoxException.h" + +#include "MemLeakFindOn.h" + +BoxException::BoxException() +{ +} + +BoxException::~BoxException() throw () +{ +} diff --git a/lib/common/BoxException.h b/lib/common/BoxException.h new file mode 100755 index 00000000..eb992f57 --- /dev/null +++ b/lib/common/BoxException.h @@ -0,0 +1,37 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxException.h +// Purpose: Exception +// Created: 2003/07/10 +// +// -------------------------------------------------------------------------- + +#ifndef BOXEXCEPTION__H +#define BOXEXCEPTION__H + +#include + +// -------------------------------------------------------------------------- +// +// Class +// Name: BoxException +// Purpose: Exception +// Created: 2003/07/10 +// +// -------------------------------------------------------------------------- +class BoxException : public std::exception +{ +public: + BoxException(); + ~BoxException() throw (); + + virtual unsigned int GetType() const throw() = 0; + virtual unsigned int GetSubType() const throw() = 0; + +private: +}; + + +#endif // BOXEXCEPTION__H + diff --git a/lib/common/BoxPlatform.h b/lib/common/BoxPlatform.h new file mode 100755 index 00000000..716366d2 --- /dev/null +++ b/lib/common/BoxPlatform.h @@ -0,0 +1,236 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxPlatform.h +// Purpose: Specifies what each platform supports in more detail, and includes +// extra files to get basic support for types. +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- + +#ifndef BOXPLATFORM__H +#define BOXPLATFORM__H + +#define DIRECTORY_SEPARATOR "/" +#define DIRECTORY_SEPARATOR_ASCHAR '/' + +#define PLATFORM_DEV_NULL "/dev/null" + + +// Other flags which might be useful... +// +// #define PLATFORM_BERKELEY_DB_NOT_SUPPORTED +// -- dbopen etc not on this platform +// +// #define PLATFORM_REGEX_NOT_SUPPORTED +// -- regex support not available on this platform + + +#ifdef PLATFORM_OPENBSD + + #include + + #define PLATFORM_HAVE_setproctitle + + #define PLATFORM_STATIC_TEMP_DIRECTORY_NAME "/tmp" + + #define PLATFORM_HAVE_getpeereid + + #define PLATFORM_RANDOM_DEVICE "/dev/arandom" + +#endif // PLATFORM_OPENBSD + +#ifdef PLATFORM_NETBSD + + #include + + #define PLATFORM_HAVE_setproctitle + + #define PLATFORM_NO_BUILT_IN_SWAP64 + + #define PLATFORM_STATIC_TEMP_DIRECTORY_NAME "/tmp" + + #define PLATFORM_KQUEUE_NOT_SUPPORTED + + #define PLATFORM_RANDOM_DEVICE "/dev/urandom" + +#endif + +#ifdef PLATFORM_FREEBSD + + #include + #include + + #define PLATFORM_HAVE_setproctitle + + #define PLATFORM_NO_BUILT_IN_SWAP64 + + #define PLATFORM_STATIC_TEMP_DIRECTORY_NAME "/tmp" + + #define PLATFORM_HAVE_getpeereid + + #define PLATFORM_RANDOM_DEVICE "/dev/urandom" + +#endif // PLATFORM_FREEBSD + +#ifdef PLATFORM_DARWIN + + #include + #include + + // types 'missing' + #ifndef _SOCKLEN_T + typedef int socklen_t; + #endif + typedef u_int8_t uint8_t; + typedef signed char int8_t; + typedef u_int64_t uint64_t; + typedef u_int32_t uint32_t; + typedef u_int16_t uint16_t; + + // poll() emulator on Darwin misses this declaration + #define INFTIM -1 + + #define PLATFORM_STATIC_TEMP_DIRECTORY_NAME "/tmp" + + #define PLATFORM_LCHOWN_NOT_SUPPORTED + + #define PLATFORM_READLINE_NOT_SUPPORTED + + #define PLATFORM_RANDOM_DEVICE "/dev/random" + +#endif // PLATFORM_DARWIN + +#ifdef PLATFORM_LINUX + + #include + + // for ntohl etc... + #include + + // types 'missing' + typedef u_int8_t uint8_t; + typedef signed char int8_t; + typedef u_int32_t uint32_t; + typedef u_int16_t uint16_t; + typedef u_int64_t uint64_t; + + // not defined in Linux, a BSD thing + #define INFTIM -1 + + #define LLONG_MAX 9223372036854775807LL + #define LLONG_MIN (-LLONG_MAX - 1LL) + + #define PLATFORM_STATIC_TEMP_DIRECTORY_NAME "/tmp" + + #define PLATFORM_HAVE_getsockopt_SO_PEERCRED + + // load in installation specific linux configuration + #include "../../local/_linux_platform.h" + + #define PLATFORM_KQUEUE_NOT_SUPPORTED + #define PLATFORM_dirent_BROKEN_d_type + #define PLATFORM_stat_SHORT_mtime + #define PLATFORM_stat_NO_st_flags + #define PLATFORM_USES_MTAB_FILE_FOR_MOUNTS + #define PLATFORM_open_NO_O_EXLOCK + #define PLATFORM_sockaddr_NO_len + + #define PLATFORM_RANDOM_DEVICE "/dev/urandom" + + // If large file support is on, can't do the intercepts in the test/raidfile + #if _FILE_OFFSET_BITS == 64 + #define PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE + #endif + +#endif // PLATFORM_LINUX + +#ifdef PLATFORM_CYGWIN + + #define PLATFORM_BERKELEY_DB_NOT_SUPPORTED + + #define PLATFORM_KQUEUE_NOT_SUPPORTED + #define PLATFORM_dirent_BROKEN_d_type + #define PLATFORM_stat_SHORT_mtime + #define PLATFORM_stat_NO_st_flags + #define PLATFORM_USES_MTAB_FILE_FOR_MOUNTS + #define PLATFORM_open_NO_O_EXLOCK + #define PLATFORM_sockaddr_NO_len + #define PLATFORM_NO_BUILT_IN_SWAP64 + + #define PLATFORM_STATIC_TEMP_DIRECTORY_NAME "/tmp" + + #define PLATFORM_READLINE_NOT_SUPPORTED + + #define LLONG_MAX 9223372036854775807LL + #define LLONG_MIN (-LLONG_MAX - 1LL) + + #define INFTIM -1 + + // File listing canonical interesting mount points. + #define MNTTAB _PATH_MNTTAB + + // File listing currently active mount points. + #define MOUNTED _PATH_MOUNTED + + #define __need_FILE + + // Extra includes + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // No easy random entropy source + #define PLATFORM_RANDOM_DEVICE_NONE + +#endif // PLATFORM_CYGWIN + + +// Find out if credentials on UNIX sockets can be obtained +#ifndef PLATFORM_HAVE_getpeereid + #ifndef PLATFORM_HAVE_getsockopt_SO_PEERCRED + #define PLATFORM_CANNOT_FIND_PEER_UID_OF_UNIX_SOCKET + #endif +#endif + + +// Compiler issues +#ifdef __GNUC__ + + #ifdef PLATFORM_GCC3 + + // GCC v3 doesn't like pragmas in #defines + #define STRUCTURE_PATCKING_FOR_WIRE_USE_HEADERS + + // But fortunately, the STL allocations are much better behaved. + + #else + + // Force STL to use malloc() for memory allocation + // -- slower, but doesn't gradually use more and more memory + // HOWEVER -- this 'fix' is broken on some platforms. Lots of fun! + #ifndef PLATFORM_STL_USE_MALLOC_BROKEN + #define __USE_MALLOC + #endif + + // set packing to one bytes (can't use push/pop on gcc) + #define BEGIN_STRUCTURE_PACKING_FOR_WIRE #pragma pack(1) + + // Use default packing + #define END_STRUCTURE_PACKING_FOR_WIRE #pragma pack() + + #endif + +#else + compiler not supported! +#endif + + +#endif // BOXPLATFORM__H + diff --git a/lib/common/BoxPortsAndFiles.h b/lib/common/BoxPortsAndFiles.h new file mode 100755 index 00000000..7788537e --- /dev/null +++ b/lib/common/BoxPortsAndFiles.h @@ -0,0 +1,31 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxPortsAndFiles.h +// Purpose: Central list of which tcp/ip ports and hardcoded file locations +// Created: 2003/08/20 +// +// -------------------------------------------------------------------------- + +#ifndef BOXPORTSANDFILES__H +#define BOXPORTSANDFILES__H + +#define BOX_PORT_BASE 2200 + + +// Backup store daemon +#define BOX_PORT_BBSTORED (BOX_PORT_BASE+1) +#define BOX_FILE_BBSTORED_DEFAULT_CONFIG "/etc/box/bbstored.conf" +// directory within the RAIDFILE root for the backup store daemon +#define BOX_RAIDFILE_ROOT_BBSTORED "backup" + +// Backup client daemon +#define BOX_FILE_BBACKUPD_DEFAULT_CONFIG "/etc/box/bbackupd.conf" + + +// RaidFile conf location efault +#define BOX_FILE_RAIDFILE_DEFAULT_CONFIG "/etc/box/raidfile.conf" + + +#endif // BOXPORTSANDFILES__H + diff --git a/lib/common/BoxTime.cpp b/lib/common/BoxTime.cpp new file mode 100755 index 00000000..feada309 --- /dev/null +++ b/lib/common/BoxTime.cpp @@ -0,0 +1,32 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTime.cpp +// Purpose: Time for the box +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "BoxTime.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: GetCurrentBoxTime() +// Purpose: Returns the current time as a box time. (1 sec precision) +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- +box_time_t GetCurrentBoxTime() +{ + ASSERT(sizeof(uint32_t) == sizeof(time_t)); + return SecondsToBoxTime((uint32_t)time(0)); +} + + diff --git a/lib/common/BoxTime.h b/lib/common/BoxTime.h new file mode 100755 index 00000000..a7a25ac6 --- /dev/null +++ b/lib/common/BoxTime.h @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTime.h +// Purpose: How time is represented +// Created: 2003/10/08 +// +// -------------------------------------------------------------------------- + +#ifndef BOXTIME__H +#define BOXTIME__H + +// Time is presented as an unsigned 64 bit integer, in microseconds +typedef uint64_t box_time_t; + +#define NANO_SEC_IN_SEC (1000000000LL) +#define NANO_SEC_IN_USEC (1000) +#define NANO_SEC_IN_USEC_LL (1000LL) +#define MICRO_SEC_IN_SEC (1000000) +#define MICRO_SEC_IN_SEC_LL (1000000LL) +#define MILLI_SEC_IN_NANO_SEC (1000) +#define MILLI_SEC_IN_NANO_SEC_LL (1000LL) + +box_time_t GetCurrentBoxTime(); + +inline box_time_t SecondsToBoxTime(uint32_t Seconds) +{ + return ((box_time_t)Seconds * MICRO_SEC_IN_SEC_LL); +} +inline box_time_t SecondsToBoxTime(uint64_t Seconds) +{ + return ((box_time_t)Seconds * MICRO_SEC_IN_SEC_LL); +} +inline int64_t BoxTimeToSeconds(box_time_t Time) +{ + return Time / MICRO_SEC_IN_SEC_LL; +} +inline int64_t BoxTimeToMilliSeconds(box_time_t Time) +{ + return Time / MILLI_SEC_IN_NANO_SEC_LL; +} + +#endif // BOXTIME__H + diff --git a/lib/common/BoxTimeToText.cpp b/lib/common/BoxTimeToText.cpp new file mode 100755 index 00000000..94b0d152 --- /dev/null +++ b/lib/common/BoxTimeToText.cpp @@ -0,0 +1,41 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTimeToText.cpp +// Purpose: Convert box time to text +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include "BoxTimeToText.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BoxTimeToISO8601String(box_time_t) +// Purpose: Convert a 64 bit box time to a ISO 8601 complient string +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- +std::string BoxTimeToISO8601String(box_time_t Time) +{ + time_t timeInSecs = (time_t)BoxTimeToSeconds(Time); + struct tm time; + gmtime_r(&timeInSecs, &time); + + char str[128]; // more than enough space + sprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d", time.tm_year + 1900, + time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); + + return std::string(str); +} + + diff --git a/lib/common/BoxTimeToText.h b/lib/common/BoxTimeToText.h new file mode 100755 index 00000000..a25c70b6 --- /dev/null +++ b/lib/common/BoxTimeToText.h @@ -0,0 +1,19 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTimeToText.h +// Purpose: Convert box time to text +// Created: 2003/10/10 +// +// -------------------------------------------------------------------------- + +#ifndef BOXTIMETOTEXT__H +#define BOXTIMETOTEXT__H + +#include +#include "BoxTime.h" + +std::string BoxTimeToISO8601String(box_time_t Time); + +#endif // BOXTIMETOTEXT__H + diff --git a/lib/common/BoxTimeToUnix.h b/lib/common/BoxTimeToUnix.h new file mode 100755 index 00000000..17e57e27 --- /dev/null +++ b/lib/common/BoxTimeToUnix.h @@ -0,0 +1,30 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: BoxTimeToUnix.h +// Purpose: Convert times in 64 bit values to UNIX structures +// Created: 2003/10/07 +// +// -------------------------------------------------------------------------- + +#ifndef FILEMODIFICATIONTIMETOTIMEVAL__H +#define FILEMODIFICATIONTIMETOTIMEVAL__H + +#include + +#include "BoxTime.h" + +inline void BoxTimeToTimeval(box_time_t Time, struct timeval &tv) +{ + tv.tv_sec = (long)(Time / MICRO_SEC_IN_SEC_LL); + tv.tv_usec = (long)(Time % MICRO_SEC_IN_SEC_LL); +} + +inline void BoxTimeToTimespec(box_time_t Time, struct timespec &tv) +{ + tv.tv_sec = (time_t)(Time / MICRO_SEC_IN_SEC_LL); + tv.tv_nsec = ((long)(Time % MICRO_SEC_IN_SEC_LL)) * NANO_SEC_IN_USEC; +} + +#endif // FILEMODIFICATIONTIMETOTIMEVAL__H + diff --git a/lib/common/CollectInBufferStream.cpp b/lib/common/CollectInBufferStream.cpp new file mode 100755 index 00000000..90e2e7bc --- /dev/null +++ b/lib/common/CollectInBufferStream.cpp @@ -0,0 +1,274 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: CollectInBufferStream.cpp +// Purpose: Collect data in a buffer, and then read it out. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "CollectInBufferStream.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +#define INITIAL_BUFFER_SIZE 1024 +#define MAX_BUFFER_ADDITION (1024*64) + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::CollectInBufferStream() +// Purpose: Constructor +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +CollectInBufferStream::CollectInBufferStream() + : mBuffer(INITIAL_BUFFER_SIZE), + mBufferSize(INITIAL_BUFFER_SIZE), + mBytesInBuffer(0), + mReadPosition(0), + mInWritePhase(true) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::~CollectInBufferStream() +// Purpose: Destructor +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +CollectInBufferStream::~CollectInBufferStream() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::Read(void *, int, int) +// Purpose: As interface. But only works in read phase +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +int CollectInBufferStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + if(mInWritePhase != false) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + // Adjust to number of bytes left + if(NBytes > (mBytesInBuffer - mReadPosition)) + { + NBytes = (mBytesInBuffer - mReadPosition); + } + ASSERT(NBytes >= 0); + if(NBytes <= 0) return 0; // careful now + + // Copy in the requested number of bytes and adjust the read pointer + ::memcpy(pBuffer, ((char*)mBuffer) + mReadPosition, NBytes); + mReadPosition += NBytes; + + return NBytes; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::BytesLeftToRead() +// Purpose: As interface. But only works in read phase +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +IOStream::pos_type CollectInBufferStream::BytesLeftToRead() +{ + if(mInWritePhase != false) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + return (mBytesInBuffer - mReadPosition); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::Write(void *, int) +// Purpose: As interface. But only works in write phase +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void CollectInBufferStream::Write(const void *pBuffer, int NBytes) +{ + if(mInWritePhase != true) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + // Enough space in the buffer + if((mBytesInBuffer + NBytes) > mBufferSize) + { + // Need to reallocate... what's the block size we'll use? + int allocateBlockSize = mBufferSize; + if(allocateBlockSize > MAX_BUFFER_ADDITION) + { + allocateBlockSize = MAX_BUFFER_ADDITION; + } + + // Write it the easy way. Although it's not the most efficient... + int newSize = mBufferSize; + while(newSize < (mBytesInBuffer + NBytes)) + { + newSize += allocateBlockSize; + } + + // Reallocate buffer + mBuffer.Resize(newSize); + + // Store new size + mBufferSize = newSize; + } + + // Copy in data and adjust counter + ::memcpy(((char*)mBuffer) + mBytesInBuffer, pBuffer, NBytes); + mBytesInBuffer += NBytes; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::GetPosition() +// Purpose: In write phase, returns the number of bytes written, in read +// phase, the number of bytes to go +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +IOStream::pos_type CollectInBufferStream::GetPosition() const +{ + return mInWritePhase?mBytesInBuffer:mReadPosition; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::Seek(pos_type, int) +// Purpose: As interface. But read phase only. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void CollectInBufferStream::Seek(pos_type Offset, int SeekType) +{ + if(mInWritePhase != false) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + int newPos = 0; + switch(SeekType) + { + case IOStream::SeekType_Absolute: + newPos = Offset; + break; + case IOStream::SeekType_Relative: + newPos = mReadPosition + Offset; + break; + case IOStream::SeekType_End: + newPos = mBytesInBuffer + Offset; + break; + default: + THROW_EXCEPTION(CommonException, IOStreamBadSeekType) + break; + } + + // Make sure it doesn't go over + if(newPos > mBytesInBuffer) + { + newPos = mBytesInBuffer; + } + // or under + if(newPos < 0) + { + newPos = 0; + } + + // Set the new read position + mReadPosition = newPos; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::StreamDataLeft() +// Purpose: As interface +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool CollectInBufferStream::StreamDataLeft() +{ + return mInWritePhase?(false):(mReadPosition < mBytesInBuffer); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::StreamClosed() +// Purpose: As interface +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool CollectInBufferStream::StreamClosed() +{ + return !mInWritePhase; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::SetForReading() +// Purpose: Switch to read phase, after all data written +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void CollectInBufferStream::SetForReading() +{ + if(mInWritePhase != true) { THROW_EXCEPTION(CommonException, CollectInBufferStreamNotInCorrectPhase) } + + // Move to read phase + mInWritePhase = false; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::GetBuffer() +// Purpose: Returns the buffer +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void *CollectInBufferStream::GetBuffer() const +{ + return mBuffer.GetPtr(); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::GetSize() +// Purpose: Returns the buffer size +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +int CollectInBufferStream::GetSize() const +{ + return mBytesInBuffer; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: CollectInBufferStream::Reset() +// Purpose: Reset the stream, so it is empty and ready to be written to. +// Created: 8/12/03 +// +// -------------------------------------------------------------------------- +void CollectInBufferStream::Reset() +{ + mInWritePhase = true; + mBytesInBuffer = 0; + mReadPosition = 0; +} + diff --git a/lib/common/CollectInBufferStream.h b/lib/common/CollectInBufferStream.h new file mode 100755 index 00000000..d73af8db --- /dev/null +++ b/lib/common/CollectInBufferStream.h @@ -0,0 +1,60 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: CollectInBufferStream.h +// Purpose: Collect data in a buffer, and then read it out. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- + +#ifndef COLLECTINBUFFERSTREAM__H +#define COLLECTINBUFFERSTREAM__H + +#include "IOStream.h" +#include "Guards.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: CollectInBufferStream +// Purpose: Collect data in a buffer, and then read it out. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +class CollectInBufferStream : public IOStream +{ +public: + CollectInBufferStream(); + ~CollectInBufferStream(); +private: + // No copying + CollectInBufferStream(const CollectInBufferStream &); + CollectInBufferStream(const IOStream &); +public: + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes); + virtual pos_type GetPosition() const; + virtual void Seek(pos_type Offset, int SeekType); + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + + void SetForReading(); + + void Reset(); + + void *GetBuffer() const; + int GetSize() const; + bool IsSetForReading() const {return !mInWritePhase;} + +private: + MemoryBlockGuard mBuffer; + int mBufferSize; + int mBytesInBuffer; + int mReadPosition; + bool mInWritePhase; +}; + +#endif // COLLECTINBUFFERSTREAM__H + diff --git a/lib/common/CommonException.h b/lib/common/CommonException.h new file mode 100755 index 00000000..a0eb3bf5 --- /dev/null +++ b/lib/common/CommonException.h @@ -0,0 +1,17 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: CommonException.h +// Purpose: Exception +// Created: 2003/07/08 +// +// -------------------------------------------------------------------------- + +#ifndef COMMONEXCEPTION__H +#define COMMONEXCEPTION__H + +// Compatibility header with old non-autogen exception scheme +#include "autogen_CommonException.h" + +#endif // COMMONEXCEPTION__H + diff --git a/lib/common/CommonException.txt b/lib/common/CommonException.txt new file mode 100644 index 00000000..3875ed71 --- /dev/null +++ b/lib/common/CommonException.txt @@ -0,0 +1,44 @@ + +# NOTE: Exception descriptions are for public distributions of Box Backup only -- do not rely for other applications. + + +EXCEPTION Common 1 + +Internal 0 +AssertFailed 1 +OSFileOpenError 2 Can't open a file -- attempted to load a non-existant config file or bad file referenced within? +OSFileCloseError 3 +FileAlreadyClosed 4 +BadArguments 5 +ConfigNoKey 6 +ConfigNoSubConfig 7 +GetLineNoHandle 8 +OSFileError 9 Error accessing a file. Check permissions. +GetLineEOF 10 +ConfigBadIntValue 11 +GetLineTooLarge 12 Protects against very large lines using up lots of memory. +NotSupported 13 +OSFileReadError 14 +OSFileWriteError 15 +FileClosed 16 +IOStreamBadSeekType 17 +CantWriteToPartialReadStream 18 +CollectInBufferStreamNotInCorrectPhase 19 +NamedLockAlreadyLockingSomething 20 +NamedLockNotHeld 21 +StreamableMemBlockIncompleteRead 22 +MemBlockStreamNotSupported 23 +StreamDoesntHaveRequiredProperty 24 +CannotWriteToReadGatherStream 25 +ReadGatherStreamAddingBadBlock 26 +CouldNotLookUpUsername 27 +CouldNotRestoreProcessUser 28 +CouldNotChangeProcessUser 29 +RegexNotSupportedOnThisPlatform 30 Your platform does not have built in regular expression libraries. +BadRegularExpression 31 +CouldNotCreateKQueue 32 +KEventErrorAdd 33 +KEventErrorWait 34 +KEventErrorRemove 35 +KQueueNotSupportedOnThisPlatform 36 +IOStreamGetLineNotEnoughDataToIgnore 37 Bad value passed to IOStreamGetLine::IgnoreBufferedData() diff --git a/lib/common/Configuration.cpp b/lib/common/Configuration.cpp new file mode 100755 index 00000000..def93571 --- /dev/null +++ b/lib/common/Configuration.cpp @@ -0,0 +1,741 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Configuration.cpp +// Purpose: Reading configuration files +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include + +#include "Configuration.h" +#include "CommonException.h" +#include "Guards.h" +#include "FdGetLine.h" + +#include "MemLeakFindOn.h" + +// utility whitespace function +inline bool iw(int c) +{ + return (c == ' ' || c == '\t' || c == '\v' || c == '\f'); // \r, \n are already excluded +} + +// boolean values +static const char *sValueBooleanStrings[] = {"yes", "true", "no", "false", 0}; +static const bool sValueBooleanValue[] = {true, true, false, false}; + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::Configuration(const std::string &) +// Purpose: Constructor +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +Configuration::Configuration(const std::string &rName) + : mName(rName) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::Configuration(const Configuration &) +// Purpose: Copy constructor +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +Configuration::Configuration(const Configuration &rToCopy) + : mName(rToCopy.mName), + mSubConfigurations(rToCopy.mSubConfigurations), + mKeys(rToCopy.mKeys) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::~Configuration() +// Purpose: Destructor +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +Configuration::~Configuration() +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::LoadAndVerify(const std::string &, const ConfigurationVerify *, std::string &) +// Purpose: Loads a configuration file from disc, checks it. Returns NULL if it was faulting, in which +// case they'll be an error message. +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +std::auto_ptr Configuration::LoadAndVerify(const char *Filename, const ConfigurationVerify *pVerify, std::string &rErrorMsg) +{ + // Check arguments + if(Filename == 0) + { + THROW_EXCEPTION(CommonException, BadArguments) + } + + // Just to make sure + rErrorMsg.erase(); + + // Open the file + FileHandleGuard file(Filename); + + // GetLine object + FdGetLine getline(file); + + // Object to create + Configuration *pconfig = new Configuration(std::string("")); + + try + { + // Load + LoadInto(*pconfig, getline, rErrorMsg, true); + + if(!rErrorMsg.empty()) + { + // An error occured, return now + //TRACE1("Error message from LoadInto: %s", rErrorMsg.c_str()); + TRACE0("Error at Configuration::LoadInfo\n"); + delete pconfig; + pconfig = 0; + return std::auto_ptr(0); + } + + // Verify? + if(pVerify) + { + if(!Verify(*pconfig, *pVerify, std::string(), rErrorMsg)) + { + //TRACE1("Error message from Verify: %s", rErrorMsg.c_str()); + TRACE0("Error at Configuration::Verify\n"); + delete pconfig; + pconfig = 0; + return std::auto_ptr(0); + } + } + } + catch(...) + { + // Clean up + delete pconfig; + pconfig = 0; + throw; + } + + // Success. Return result. + return std::auto_ptr(pconfig); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: LoadInto(Configuration &, FdGetLine &, std::string &, bool) +// Purpose: Private. Load configuration information from the file into the config object. +// Returns 'abort' flag, if error, will be appended to rErrorMsg. +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::string &rErrorMsg, bool RootLevel) +{ + bool startBlockExpected = false; + std::string blockName; + + //TRACE1("BLOCK: |%s|\n", rConfig.mName.c_str()); + + while(!rGetLine.IsEOF()) + { + std::string line(rGetLine.GetLine(true)); /* preprocess out whitespace and comments */ + + if(line.empty()) + { + // Ignore blank lines + continue; + } + + // Line an open block string? + if(line == "{") + { + if(startBlockExpected) + { + // New config object + Configuration config(blockName); + + // Continue processing into this block + if(!LoadInto(config, rGetLine, rErrorMsg, false)) + { + // Abort error + return false; + } + + startBlockExpected = false; + + // Store... + rConfig.mSubConfigurations.push_back(std::pair(blockName, config)); + } + else + { + rErrorMsg += "Unexpected start block in " + rConfig.mName + "\n"; + } + } + else + { + // Close block? + if(line == "}") + { + if(RootLevel) + { + // error -- root level doesn't have a close + rErrorMsg += "Root level has close block -- forget to terminate subblock?\n"; + // but otherwise ignore + } + else + { + //TRACE0("ENDBLOCK\n"); + return true; // All very good and nice + } + } + // Either a key, or a sub block beginning + else + { + // Can't be a start block + if(startBlockExpected) + { + rErrorMsg += "Block " + blockName + " wasn't started correctly (no '{' on line of it's own)\n"; + startBlockExpected = false; + } + + // Has the line got an = in it? + unsigned int equals = 0; + for(; equals < line.size(); ++equals) + { + if(line[equals] == '=') + { + // found! + break; + } + } + if(equals < line.size()) + { + // Make key value pair + unsigned int keyend = equals; + while(keyend > 0 && iw(line[keyend-1])) + { + keyend--; + } + unsigned int valuestart = equals+1; + while(valuestart < line.size() && iw(line[valuestart])) + { + valuestart++; + } + if(keyend > 0 && valuestart <= line.size()) + { + std::string key(line.substr(0, keyend)); + std::string value(line.substr(valuestart)); + //TRACE2("KEY: |%s|=|%s|\n", key.c_str(), value.c_str()); + + // Check for duplicate values + if(rConfig.mKeys.find(key) != rConfig.mKeys.end()) + { + // Multi-values allowed here, but checked later on + rConfig.mKeys[key] += MultiValueSeparator; + rConfig.mKeys[key] += value; + } + else + { + // Store + rConfig.mKeys[key] = value; + } + } + else + { + rErrorMsg += "Invalid key in block "+rConfig.mName+"\n"; + } + } + else + { + // Start of sub block + blockName = line; + startBlockExpected = true; + } + } + } + } + + // End of file? + if(!RootLevel && rGetLine.IsEOF()) + { + // Error if EOF and this isn't the root level + rErrorMsg += "File ended without terminating all subblocks\n"; + } + + return true; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::KeyExists(const char *) +// Purpose: Checks to see if a key exists +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +bool Configuration::KeyExists(const char *pKeyName) const +{ + if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)} + + return mKeys.find(pKeyName) != mKeys.end(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyValue(const char *) +// Purpose: Returns the value of a configuration variable +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +const std::string &Configuration::GetKeyValue(const char *pKeyName) const +{ + if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)} + + std::map::const_iterator i(mKeys.find(pKeyName)); + + if(i == mKeys.end()) + { + THROW_EXCEPTION(CommonException, ConfigNoKey) + } + else + { + return i->second; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyValueInt(const char *) +// Purpose: Gets a key value as an integer +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +int Configuration::GetKeyValueInt(const char *pKeyName) const +{ + if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)} + + std::map::const_iterator i(mKeys.find(pKeyName)); + + if(i == mKeys.end()) + { + THROW_EXCEPTION(CommonException, ConfigNoKey) + } + else + { + long value = ::strtol((i->second).c_str(), NULL, 0 /* C style handling */); + if(value == LONG_MAX || value == LONG_MIN) + { + THROW_EXCEPTION(CommonException, ConfigBadIntValue) + } + return (int)value; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyValueBool(const char *) const +// Purpose: Gets a key value as a boolean +// Created: 17/2/04 +// +// -------------------------------------------------------------------------- +bool Configuration::GetKeyValueBool(const char *pKeyName) const +{ + if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)} + + std::map::const_iterator i(mKeys.find(pKeyName)); + + if(i == mKeys.end()) + { + THROW_EXCEPTION(CommonException, ConfigNoKey) + } + else + { + bool value = false; + + // Anything this is called for should have been verified as having a correct + // string in the verification section. However, this does default to false + // if it isn't in the string table. + + for(int l = 0; sValueBooleanStrings[l] != 0; ++l) + { + if(::strcasecmp((i->second).c_str(), sValueBooleanStrings[l]) == 0) + { + // Found. + value = sValueBooleanValue[l]; + break; + } + } + + return value; + } + +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetKeyNames() +// Purpose: Returns list of key names +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +std::vector Configuration::GetKeyNames() const +{ + std::map::const_iterator i(mKeys.begin()); + + std::vector r; + + for(; i != mKeys.end(); ++i) + { + r.push_back(i->first); + } + + return r; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::SubConfigurationExists(const char *) +// Purpose: Checks to see if a sub configuration exists +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +bool Configuration::SubConfigurationExists(const char *pSubName) const +{ + if(pSubName == 0) {THROW_EXCEPTION(CommonException, BadArguments)} + + // Attempt to find it... + std::list >::const_iterator i(mSubConfigurations.begin()); + + for(; i != mSubConfigurations.end(); ++i) + { + // This the one? + if(i->first == pSubName) + { + // Yes. + return true; + } + } + + // didn't find it. + return false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetSubConfiguration(const char *) +// Purpose: Gets a sub configuration +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +const Configuration &Configuration::GetSubConfiguration(const char *pSubName) const +{ + if(pSubName == 0) {THROW_EXCEPTION(CommonException, BadArguments)} + + // Attempt to find it... + std::list >::const_iterator i(mSubConfigurations.begin()); + + for(; i != mSubConfigurations.end(); ++i) + { + // This the one? + if(i->first == pSubName) + { + // Yes. + return i->second; + } + } + + THROW_EXCEPTION(CommonException, ConfigNoSubConfig) +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::GetSubConfigurationNames() +// Purpose: Return list of sub configuration names +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +std::vector Configuration::GetSubConfigurationNames() const +{ + std::list >::const_iterator i(mSubConfigurations.begin()); + + std::vector r; + + for(; i != mSubConfigurations.end(); ++i) + { + r.push_back(i->first); + } + + return r; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: Configuration::Verify(const Configuration &, const ConfigurationVerify &, const std::string &, std::string &) +// Purpose: Return list of sub configuration names +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rVerify, const std::string &rLevel, std::string &rErrorMsg) +{ + bool ok = true; + + // First... check the keys + if(rVerify.mpKeys != 0) + { + const ConfigurationVerifyKey *pvkey = rVerify.mpKeys; + + bool todo = true; + do + { + // Can the key be found? + ASSERT(pvkey->mpName); + if(rConfig.KeyExists(pvkey->mpName)) + { + // Get value + const std::string &rval = rConfig.GetKeyValue(pvkey->mpName); + const char *val = rval.c_str(); + + // Check it's a number? + if((pvkey->Tests & ConfigTest_IsInt) == ConfigTest_IsInt) + { + // Test it... + char *end; + long r = ::strtol(val, &end, 0); + if(r == LONG_MIN || r == LONG_MAX || end != (val + rval.size())) + { + // not a good value + ok = false; + rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) is not a valid integer.\n"; + } + } + + // Check it's a bool? + if((pvkey->Tests & ConfigTest_IsBool) == ConfigTest_IsBool) + { + // See if it's one of the allowed strings. + bool found = false; + for(int l = 0; sValueBooleanStrings[l] != 0; ++l) + { + if(::strcasecmp(val, sValueBooleanStrings[l]) == 0) + { + // Found. + found = true; + break; + } + } + + // Error if it's not one of them. + if(!found) + { + ok = false; + rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) is not a valid boolean value.\n"; + } + } + + // Check for multi valued statments where they're not allowed + if((pvkey->Tests & ConfigTest_MultiValueAllowed) == 0) + { + // Check to see if this key is a multi-value -- it shouldn't be + if(rval.find(MultiValueSeparator) != rval.npos) + { + ok = false; + rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) multi value not allowed (duplicated key?).\n"; + } + } + } + else + { + // Is it required to exist? + if((pvkey->Tests & ConfigTest_Exists) == ConfigTest_Exists) + { + // Should exist, but doesn't. + ok = false; + rErrorMsg += rLevel + rConfig.mName + "." + pvkey->mpName + " (key) is missing.\n"; + } + else if(pvkey->mpDefaultValue) + { + rConfig.mKeys[std::string(pvkey->mpName)] = std::string(pvkey->mpDefaultValue); + } + } + + if((pvkey->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry) + { + // No more! + todo = false; + } + + // next + pvkey++; + + } while(todo); + + // Check for additional keys + for(std::map::const_iterator i = rConfig.mKeys.begin(); + i != rConfig.mKeys.end(); ++i) + { + // Is the name in the list? + const ConfigurationVerifyKey *scan = rVerify.mpKeys; + bool found = false; + while(scan) + { + if(scan->mpName == i->first) + { + found = true; + break; + } + + // Next? + if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry) + { + break; + } + scan++; + } + + if(!found) + { + // Shouldn't exist, but does. + ok = false; + rErrorMsg += rLevel + rConfig.mName + "." + i->first + " (key) is not a known key. Check spelling and placement.\n"; + } + } + } + + // Then the sub configurations + if(rVerify.mpSubConfigurations) + { + // Find the wildcard entry, if it exists, and check that required subconfigs are there + const ConfigurationVerify *wildcardverify = 0; + + const ConfigurationVerify *scan = rVerify.mpSubConfigurations; + while(scan) + { + ASSERT(scan->mpName); + if(scan->mpName[0] == '*') + { + wildcardverify = scan; + } + + // Required? + if((scan->Tests & ConfigTest_Exists) == ConfigTest_Exists) + { + if(scan->mpName[0] == '*') + { + // Check something exists + if(rConfig.mSubConfigurations.size() < 1) + { + // A sub config should exist, but doesn't. + ok = false; + rErrorMsg += rLevel + rConfig.mName + ".* (block) is missing (a block must be present).\n"; + } + } + else + { + // Check real thing exists + if(!rConfig.SubConfigurationExists(scan->mpName)) + { + // Should exist, but doesn't. + ok = false; + rErrorMsg += rLevel + rConfig.mName + "." + scan->mpName + " (block) is missing.\n"; + } + } + } + + // Next? + if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry) + { + break; + } + scan++; + } + + // Go through the sub configurations, one by one + for(std::list >::const_iterator i(rConfig.mSubConfigurations.begin()); + i != rConfig.mSubConfigurations.end(); ++i) + { + // Can this be found? + const ConfigurationVerify *subverify = 0; + + const ConfigurationVerify *scan = rVerify.mpSubConfigurations; + const char *name = i->first.c_str(); + ASSERT(name); + while(scan) + { + if(strcmp(scan->mpName, name) == 0) + { + // found it! + subverify = scan; + } + + // Next? + if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry) + { + break; + } + scan++; + } + + // Use wildcard? + if(subverify == 0) + { + subverify = wildcardverify; + } + + // Verify + if(subverify) + { + // override const-ness here... + if(!Verify((Configuration&)i->second, *subverify, rConfig.mName + '.', rErrorMsg)) + { + ok = false; + } + } + } + } + + return ok; +} + + diff --git a/lib/common/Configuration.h b/lib/common/Configuration.h new file mode 100755 index 00000000..4c455b0f --- /dev/null +++ b/lib/common/Configuration.h @@ -0,0 +1,98 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Configuration +// Purpose: Reading configuration files +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- + +#ifndef CONFIGURATION__H +#define CONFIGURATION__H + +#include +#include +#include +#include +#include + +// For defining tests +enum +{ + ConfigTest_LastEntry = 1, + ConfigTest_Exists = 2, + ConfigTest_IsInt = 4, + ConfigTest_MultiValueAllowed = 8, + ConfigTest_IsBool = 16 +}; + +class ConfigurationVerifyKey +{ +public: + const char *mpName; // "*" for all other keys (not implemented yet) + const char *mpDefaultValue; // default for when it's not present + int Tests; + void *TestFunction; // set to zero for now, will implement later +}; + +class ConfigurationVerify +{ +public: + const char *mpName; // "*" for all other sub config names + const ConfigurationVerify *mpSubConfigurations; + const ConfigurationVerifyKey *mpKeys; + int Tests; + void *TestFunction; // set to zero for now, will implement later +}; + +class FdGetLine; + +// -------------------------------------------------------------------------- +// +// Class +// Name: Configuration +// Purpose: Loading, checking, and representing configuration files +// Created: 2003/07/23 +// +// -------------------------------------------------------------------------- +class Configuration +{ +private: + Configuration(const std::string &rName); +public: + Configuration(const Configuration &rToCopy); + ~Configuration(); + + enum + { + // The character to separate multi-values + MultiValueSeparator = '\x01' + }; + + static std::auto_ptr LoadAndVerify(const char *Filename, const ConfigurationVerify *pVerify, std::string &rErrorMsg); + static std::auto_ptr Load(const char *Filename, std::string &rErrorMsg) { return LoadAndVerify(Filename, 0, rErrorMsg); } + + bool KeyExists(const char *pKeyName) const; + const std::string &GetKeyValue(const char *pKeyName) const; + int GetKeyValueInt(const char *pKeyName) const; + bool GetKeyValueBool(const char *pKeyName) const; + std::vector GetKeyNames() const; + + bool SubConfigurationExists(const char *pSubName) const; + const Configuration &GetSubConfiguration(const char *pSubName) const; + std::vector GetSubConfigurationNames() const; + + std::string mName; + // Order of sub blocks preserved + typedef std::list > SubConfigListType; + SubConfigListType mSubConfigurations; + // Order of keys, not preserved + std::map mKeys; + +private: + static bool LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::string &rErrorMsg, bool RootLevel); + static bool Verify(Configuration &rConfig, const ConfigurationVerify &rVerify, const std::string &rLevel, std::string &rErrorMsg); +}; + +#endif // CONFIGURATION__H + diff --git a/lib/common/Conversion.h b/lib/common/Conversion.h new file mode 100644 index 00000000..cba5bb08 --- /dev/null +++ b/lib/common/Conversion.h @@ -0,0 +1,98 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Conversion.h +// Purpose: Convert between various types +// Created: 9/4/04 +// +// -------------------------------------------------------------------------- + +#ifndef CONVERSION__H +#define CONVERSION__H + +#include + +namespace BoxConvert +{ + // -------------------------------------------------------------------------- + // + // Function + // Name: BoxConvert::Convert(to_type &, from_type) + // Purpose: Convert from types to types + // Created: 9/4/04 + // + // -------------------------------------------------------------------------- + template + inline to_type Convert(from_type From) + { + // Default conversion, simply use C++ conversion + return From; + } + + // Specialise for string -> integer + int32_t _ConvertStringToInt(const char *pString, int Size); + template<> + inline int32_t Convert(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 32); + } + template<> + inline int16_t Convert(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 16); + } + template<> + inline int8_t Convert(const std::string &rFrom) + { + return BoxConvert::_ConvertStringToInt(rFrom.c_str(), 8); + } + template<> + inline int32_t Convert(const char *pFrom) + { + return BoxConvert::_ConvertStringToInt(pFrom, 32); + } + template<> + inline int16_t Convert(const char *pFrom) + { + return BoxConvert::_ConvertStringToInt(pFrom, 16); + } + template<> + inline int8_t Convert(const char *pFrom) + { + return BoxConvert::_ConvertStringToInt(pFrom, 8); + } + + // Specialise for integer -> string + void _ConvertIntToString(std::string &rTo, int32_t From); + template<> + inline std::string Convert(int32_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + template<> + inline std::string Convert(int16_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + template<> + inline std::string Convert(int8_t From) + { + std::string r; + BoxConvert::_ConvertIntToString(r, From); + return r; + } + + // Specialise for bool -> string + template<> + inline std::string Convert(bool From) + { + return std::string(From?"true":"false"); + } +}; + +#endif // CONVERSION__H + diff --git a/lib/common/ConversionException.txt b/lib/common/ConversionException.txt new file mode 100644 index 00000000..91b5fa9a --- /dev/null +++ b/lib/common/ConversionException.txt @@ -0,0 +1,8 @@ + +EXCEPTION Conversion 12 + +Internal 0 +CannotConvertEmptyStringToInt 1 +BadStringRepresentationOfInt 2 +IntOverflowInConvertFromString 3 +BadIntSize 4 diff --git a/lib/common/ConversionString.cpp b/lib/common/ConversionString.cpp new file mode 100644 index 00000000..9e237a77 --- /dev/null +++ b/lib/common/ConversionString.cpp @@ -0,0 +1,123 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ConversionString.cpp +// Purpose: Conversions to and from strings +// Created: 9/4/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include +#include + +#include "Conversion.h" +#include "autogen_ConversionException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: BoxConvert::_ConvertStringToInt(const char *, int) +// Purpose: Convert from string to integer, with range checking. +// Always does signed -- no point in unsigned as C++ type checking +// isn't up to handling it properly. +// If a null pointer is passed in, then returns 0. +// Created: 9/4/04 +// +// -------------------------------------------------------------------------- +int32_t BoxConvert::_ConvertStringToInt(const char *pString, int Size) +{ + // Handle null strings gracefully. + if(pString == 0) + { + return 0; + } + + // Check for initial validity + if(*pString == '\0') + { + THROW_EXCEPTION(ConversionException, CannotConvertEmptyStringToInt) + } + + // Convert. + char *numEnd = 0; + errno = 0; // Some platforms don't reset it. + long r = ::strtol(pString, &numEnd, 0); + + // Check that all the characters were used + if(*numEnd != '\0') + { + THROW_EXCEPTION(ConversionException, BadStringRepresentationOfInt) + } + + // Error check + if(r == 0 && errno == EINVAL) + { + THROW_EXCEPTION(ConversionException, BadStringRepresentationOfInt) + } + + // Range check from strtol + if((r == LONG_MIN || r == LONG_MAX) && errno == ERANGE) + { + THROW_EXCEPTION(ConversionException, IntOverflowInConvertFromString) + } + + // Check range for size of integer + switch(Size) + { + case 32: + { + // No extra checking needed, if this assert holds true + ASSERT(sizeof(long) == sizeof(int32_t)); + } + break; + + case 16: + { + if(r <= (0 - 0x7fff) || r > 0x7fff) + { + THROW_EXCEPTION(ConversionException, IntOverflowInConvertFromString) + } + break; + } + + case 8: + { + if(r <= (0 - 0x7f) || r > 0x7f) + { + THROW_EXCEPTION(ConversionException, IntOverflowInConvertFromString) + } + break; + } + + default: + { + THROW_EXCEPTION(ConversionException, BadIntSize) + break; + } + } + + // Return number + return r; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: BoxConvert::_ConvertIntToString(std::string &, int32_t) +// Purpose: Convert signed interger to a string +// Created: 9/4/04 +// +// -------------------------------------------------------------------------- +void BoxConvert::_ConvertIntToString(std::string &rTo, int32_t From) +{ + char text[64]; // size more than enough + ::sprintf(text, "%d", From); + rTo = text; +} + diff --git a/lib/common/DebugAssertFailed.cpp b/lib/common/DebugAssertFailed.cpp new file mode 100755 index 00000000..2acf4602 --- /dev/null +++ b/lib/common/DebugAssertFailed.cpp @@ -0,0 +1,32 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: AssertFailed.cpp +// Purpose: Assert failure code +// Created: 2003/09/04 +// +// -------------------------------------------------------------------------- + +#ifndef NDEBUG + +#include "Box.h" + +#include +#include + +#include "MemLeakFindOn.h" + +bool AssertFailuresToSyslog = false; + +void BoxDebugAssertFailed(char *cond, char *file, int line) +{ + printf("ASSERT FAILED: [%s] at %s(%d)\n", cond, file, line); + if(AssertFailuresToSyslog) + { + ::syslog(LOG_ERR, "ASSERT FAILED: [%s] at %s(%d)\n", cond, file, line); + } +} + + +#endif // NDEBUG + diff --git a/lib/common/DebugMemLeakFinder.cpp b/lib/common/DebugMemLeakFinder.cpp new file mode 100755 index 00000000..e1877a13 --- /dev/null +++ b/lib/common/DebugMemLeakFinder.cpp @@ -0,0 +1,377 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFinder.cpp +// Purpose: Memory leak finder +// Created: 12/1/04 +// +// -------------------------------------------------------------------------- + + +#ifndef NDEBUG + +#include "Box.h" + +#undef malloc +#undef realloc +#undef free + +#include +#include +#include +#include +#include + +bool memleakfinder_global_enable = false; + +typedef struct +{ + size_t size; + const char *file; + int line; +} MallocBlockInfo; + +typedef struct +{ + size_t size; + const char *file; + int line; + bool array; +} ObjectInfo; + +namespace +{ + static std::map sMallocBlocks; + static std::map sObjectBlocks; + + static bool sTrackMallocInSection = false; + static std::set sSectionMallocBlocks; + static bool sTrackObjectsInSection = false; + static std::map sSectionObjectBlocks; + + static std::set sNotLeaks; + + void *sNotLeaksPre[1024]; + int sNotLeaksPreNum = 0; +} + +void memleakfinder_malloc_add_block(void *b, size_t size, const char *file, int line) +{ + if(b != 0) + { + MallocBlockInfo i; + i.size = size; + i.file = file; + i.line = line; + sMallocBlocks[b] = i; + + if(sTrackMallocInSection) + { + sSectionMallocBlocks.insert(b); + } + } +} + + +void *memleakfinder_malloc(size_t size, const char *file, int line) +{ + void *b = ::malloc(size); + if(!memleakfinder_global_enable) return b; + + memleakfinder_malloc_add_block(b, size, file, line); + + //TRACE4("malloc(), %d, %s, %d, %08x\n", size, file, line, b); + return b; +} + +void *memleakfinder_realloc(void *ptr, size_t size) +{ + if(!memleakfinder_global_enable) + { + return ::realloc(ptr, size); + } + + // Check it's been allocated + std::map::iterator i(sMallocBlocks.find(ptr)); + if(i == sMallocBlocks.end()) + { + TRACE1("Block %x realloc(), but not in list. Error? Or allocated in startup static objects?\n", ptr); + void *b = ::realloc(ptr, size); + memleakfinder_malloc_add_block(b, size, "FOUND-IN-REALLOC", 0); + return b; + } + + void *b = ::realloc(ptr, size); + + // Worked? + if(b != 0) + { + // Update map + MallocBlockInfo inf = i->second; + inf.size = size; + sMallocBlocks.erase(i); + sMallocBlocks[b] = inf; + + if(sTrackMallocInSection) + { + std::set::iterator si(sSectionMallocBlocks.find(ptr)); + if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); + sSectionMallocBlocks.insert(b); + } + } + + //TRACE3("realloc(), %d, %08x->%08x\n", size, ptr, b); + return b; +} + +void memleakfinder_free(void *ptr) +{ + if(memleakfinder_global_enable) + { + // Check it's been allocated + std::map::iterator i(sMallocBlocks.find(ptr)); + if(i != sMallocBlocks.end()) + { + sMallocBlocks.erase(i); + } + else + { + TRACE1("Block %x freed, but not known. Error? Or allocated in startup static allocation?\n", ptr); + } + + if(sTrackMallocInSection) + { + std::set::iterator si(sSectionMallocBlocks.find(ptr)); + if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); + } + } + + //TRACE1("free(), %08x\n", ptr); + ::free(ptr); +} + + +void memleakfinder_notaleak_insert_pre() +{ + if(!memleakfinder_global_enable) return; + for(int l = 0; l < sNotLeaksPreNum; l++) + { + sNotLeaks.insert(sNotLeaksPre[l]); + } + sNotLeaksPreNum = 0; +} + +bool is_leak(void *ptr) +{ + memleakfinder_notaleak_insert_pre(); + return sNotLeaks.find(ptr) == sNotLeaks.end(); +} + +void memleakfinder_notaleak(void *ptr) +{ + memleakfinder_notaleak_insert_pre(); + if(memleakfinder_global_enable) + { + sNotLeaks.insert(ptr); + } + else + { + sNotLeaksPre[sNotLeaksPreNum++] = ptr; + } +/* { + std::map::iterator i(sMallocBlocks.find(ptr)); + if(i != sMallocBlocks.end()) sMallocBlocks.erase(i); + } + { + std::set::iterator si(sSectionMallocBlocks.find(ptr)); + if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si); + } + { + std::map::iterator i(sObjectBlocks.find(ptr)); + if(i != sObjectBlocks.end()) sObjectBlocks.erase(i); + }*/ +} + + + +// start monitoring a section of code +void memleakfinder_startsectionmonitor() +{ + sTrackMallocInSection = true; + sSectionMallocBlocks.clear(); + sTrackObjectsInSection = true; + sSectionObjectBlocks.clear(); +} + +// trace all blocks allocated and still allocated since memleakfinder_startsectionmonitor() called +void memleakfinder_traceblocksinsection() +{ + std::set::iterator s(sSectionMallocBlocks.begin()); + for(; s != sSectionMallocBlocks.end(); ++s) + { + std::map::const_iterator i(sMallocBlocks.find(*s)); + if(i == sMallocBlocks.end()) + { + TRACE0("Logical error in section block finding\n"); + } + else + { + TRACE4("Block 0x%08x size %d allocated at %s:%d\n", (int)i->first, i->second.size, i->second.file, i->second.line); + } + } + for(std::map::const_iterator i(sSectionObjectBlocks.begin()); i != sSectionObjectBlocks.end(); ++i) + { + TRACE5("Object%s 0x%08x size %d allocated at %s:%d\n", i->second.array?" []":"", (int)i->first, i->second.size, i->second.file, i->second.line); + } +} + +int memleakfinder_numleaks() +{ + int n = 0; + + for(std::map::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) + { + if(is_leak(i->first)) ++n; + } + + for(std::map::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) + { + if(is_leak(i->first)) ++n; + } + + return n; +} + +void memleakfinder_reportleaks_file(FILE *file) +{ + for(std::map::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i) + { + if(is_leak(i->first)) ::fprintf(file, "Block 0x%08x size %d allocated at %s:%d\n", (int)i->first, i->second.size, i->second.file, i->second.line); + } + for(std::map::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i) + { + if(is_leak(i->first)) ::fprintf(file, "Object%s 0x%08x size %d allocated at %s:%d\n", i->second.array?" []":"", (int)i->first, i->second.size, i->second.file, i->second.line); + } +} + +void memleakfinder_reportleaks() +{ + // report to stdout + memleakfinder_reportleaks_file(stdout); +} + +void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext) +{ + FILE *file = ::fopen(filename, "a"); + if(file != 0) + { + if(memleakfinder_numleaks() > 0) + { + fprintf(file, "MEMORY LEAKS FROM PROCESS %d (%s)\n", getpid(), markertext); + memleakfinder_reportleaks_file(file); + } + + ::fclose(file); + } + else + { + printf("WARNING: Couldn't open memory leak results file %s for appending\n", filename); + } +} + +static char atexit_filename[512]; +static char atexit_markertext[512]; +static bool atexit_registered = false; + +void memleakfinder_atexit() +{ + memleakfinder_reportleaks_appendfile(atexit_filename, atexit_markertext); +} + +void memleakfinder_setup_exit_report(const char *filename, const char *markertext) +{ + ::strcpy(atexit_filename, filename); + ::strcpy(atexit_markertext, markertext); + if(!atexit_registered) + { + atexit(memleakfinder_atexit); + atexit_registered = true; + } +} + + + + +void add_object_block(void *block, size_t size, const char *file, int line, bool array) +{ + if(!memleakfinder_global_enable) return; + + if(block != 0) + { + ObjectInfo i; + i.size = size; + i.file = file; + i.line = line; + i.array = array; + sObjectBlocks[block] = i; + + if(sTrackObjectsInSection) + { + sSectionObjectBlocks[block] = i; + } + } +} + +void remove_object_block(void *block) +{ + if(!memleakfinder_global_enable) return; + + std::map::iterator i(sObjectBlocks.find(block)); + if(i != sObjectBlocks.end()) + { + sObjectBlocks.erase(i); + } + + if(sTrackObjectsInSection) + { + std::map::iterator i(sSectionObjectBlocks.find(block)); + if(i != sSectionObjectBlocks.end()) + { + sSectionObjectBlocks.erase(i); + } + } + + // If it's not in the list, just ignore it, as lots of stuff goes this way... +} + +void *operator new(size_t size, const char *file, int line) +{ + void *r = ::malloc(size); + add_object_block(r, size, file, line, false); + //TRACE4("new(), %d, %s, %d, %08x\n", size, file, line, r); + return r; +} + +void *operator new[](size_t size, const char *file, int line) +{ + void *r = ::malloc(size); + add_object_block(r, size, file, line, true); + //TRACE4("new[](), %d, %s, %d, %08x\n", size, file, line, r); + return r; +} + +void operator delete[](void *ptr) throw () +{ + ::free(ptr); + remove_object_block(ptr); + //TRACE1("delete[]() called, %08x\n", ptr); +} + +void operator delete(void *ptr) throw () +{ + ::free(ptr); + remove_object_block(ptr); + //TRACE1("delete() called, %08x\n", ptr); +} + +#endif // NDEBUG diff --git a/lib/common/DebugPrintf.cpp b/lib/common/DebugPrintf.cpp new file mode 100755 index 00000000..02c25496 --- /dev/null +++ b/lib/common/DebugPrintf.cpp @@ -0,0 +1,72 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: DebugPrintf.cpp +// Purpose: Implementation of a printf function, to avoid a stdio.h include in Box.h +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- + +#ifndef NDEBUG + +#include "Box.h" + +#include +#include +#include + +#include "MemLeakFindOn.h" + +// Use this apparently superflous printf function to avoid having to +// include stdio.h in every file in the project. + +int BoxDebug_printf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + int r = vprintf(format, ap); + va_end(ap); + return r; +} + + +bool BoxDebugTraceOn = true; +bool BoxDebugTraceToStdout = true; +bool BoxDebugTraceToSyslog = false; + +int BoxDebugTrace(const char *format, ...) +{ + char text[512]; + int r = 0; + if(BoxDebugTraceOn || BoxDebugTraceToSyslog) + { + va_list ap; + va_start(ap, format); + r = vsnprintf(text, sizeof(text), format, ap); + va_end(ap); + } + + // Send to stdout if trace is on and std out is enabled + if(BoxDebugTraceOn && BoxDebugTraceToStdout) + { + printf("%s", text); + } + + // But tracing to syslog is independent of tracing being on or not + if(BoxDebugTraceToSyslog) + { + // Remove trailing '\n', if it's there + if(r > 0 && text[r] == '\n') + { + text[r] = '\0'; + --r; + } + // Log it + ::syslog(LOG_INFO, "TRACE: %s", text); + } + + return r; +} + + +#endif // NDEBUG diff --git a/lib/common/EndStructPackForWire.h b/lib/common/EndStructPackForWire.h new file mode 100644 index 00000000..c9655cd7 --- /dev/null +++ b/lib/common/EndStructPackForWire.h @@ -0,0 +1,23 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: EndStructPackForWire.h +// Purpose: End structure packing for wire +// Created: 25/11/03 +// +// -------------------------------------------------------------------------- + +// No header guard -- this is intentional + +#ifdef STRUCTURE_PATCKING_FOR_WIRE_USE_HEADERS + +#pragma pack() + +#else + + logical error -- check BoxPlatform.h and including file + +#endif + + + diff --git a/lib/common/EventWatchFilesystemObject.cpp b/lib/common/EventWatchFilesystemObject.cpp new file mode 100644 index 00000000..1611d5bd --- /dev/null +++ b/lib/common/EventWatchFilesystemObject.cpp @@ -0,0 +1,99 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: EventWatchFilesystemObject.cpp +// Purpose: WaitForEvent compatible object for watching directories +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include + +#include "EventWatchFilesystemObject.h" +#include "autogen_CommonException.h" + +#include "MemLeakFindOn.h" + + +// -------------------------------------------------------------------------- +// +// Function +// Name: EventWatchFilesystemObject::EventWatchFilesystemObject(const char *) +// Purpose: Constructor -- opens the file object +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +EventWatchFilesystemObject::EventWatchFilesystemObject(const char *Filename) +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + : mDescriptor(::open(Filename, O_RDONLY /*O_EVTONLY*/, 0)) +#endif +{ +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + if(mDescriptor == -1) + { + THROW_EXCEPTION(CommonException, OSFileOpenError) + } +#else + THROW_EXCEPTION(CommonException, KQueueNotSupportedOnThisPlatform) +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: EventWatchFilesystemObject::~EventWatchFilesystemObject() +// Purpose: Destructor +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +EventWatchFilesystemObject::~EventWatchFilesystemObject() +{ + if(mDescriptor != -1) + { + ::close(mDescriptor); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: EventWatchFilesystemObject::EventWatchFilesystemObject(const EventWatchFilesystemObject &) +// Purpose: Copy constructor +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +EventWatchFilesystemObject::EventWatchFilesystemObject(const EventWatchFilesystemObject &rToCopy) + : mDescriptor(::dup(rToCopy.mDescriptor)) +{ + if(mDescriptor == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } +} + + +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED +// -------------------------------------------------------------------------- +// +// Function +// Name: EventWatchFilesystemObject::FillInKEvent(struct kevent &, int) +// Purpose: For WaitForEvent +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +void EventWatchFilesystemObject::FillInKEvent(struct kevent &rEvent, int Flags) const +{ + EV_SET(&rEvent, mDescriptor, EVFILT_VNODE, EV_CLEAR, NOTE_DELETE | NOTE_WRITE, 0, (void*)this); +} +#else +void EventWatchFilesystemObject::FillInPoll(int &fd, short &events, int Flags) const +{ + THROW_EXCEPTION(CommonException, KQueueNotSupportedOnThisPlatform) +} +#endif + diff --git a/lib/common/EventWatchFilesystemObject.h b/lib/common/EventWatchFilesystemObject.h new file mode 100644 index 00000000..d8a7245b --- /dev/null +++ b/lib/common/EventWatchFilesystemObject.h @@ -0,0 +1,48 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: EventWatchFilesystemObject.h +// Purpose: WaitForEvent compatible object for watching directories +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- + +#ifndef EVENTWATCHFILESYSTEMOBJECT__H +#define EVENTWATCHFILESYSTEMOBJECT__H + +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + #include +#endif + + +// -------------------------------------------------------------------------- +// +// Class +// Name: EventWatchFilesystemObject +// Purpose: WaitForEvent compatible object for watching files and directories +// Created: 12/3/04 +// +// -------------------------------------------------------------------------- +class EventWatchFilesystemObject +{ +public: + EventWatchFilesystemObject(const char *Filename); + ~EventWatchFilesystemObject(); + EventWatchFilesystemObject(const EventWatchFilesystemObject &rToCopy); +private: + // Assignment not allowed + EventWatchFilesystemObject &operator=(const EventWatchFilesystemObject &); +public: + +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + void FillInKEvent(struct kevent &rEvent, int Flags = 0) const; +#else + void FillInPoll(int &fd, short &events, int Flags = 0) const; +#endif + +private: + int mDescriptor; +}; + +#endif // EventWatchFilesystemObject__H + diff --git a/lib/common/ExcludeList.cpp b/lib/common/ExcludeList.cpp new file mode 100755 index 00000000..6350869d --- /dev/null +++ b/lib/common/ExcludeList.cpp @@ -0,0 +1,219 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ExcludeList.cpp +// Purpose: General purpose exclusion list +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#ifndef PLATFORM_REGEX_NOT_SUPPORTED + #include + #define EXCLUDELIST_IMPLEMENTATION_REGEX_T_DEFINED +#endif + +#include "ExcludeList.h" +#include "Utils.h" +#include "Configuration.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::ExcludeList() +// Purpose: Constructor. Generates an exclude list which will allow everything +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +ExcludeList::ExcludeList() + : mpAlwaysInclude(0) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::~ExcludeList() +// Purpose: Destructor +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +ExcludeList::~ExcludeList() +{ +#ifndef PLATFORM_REGEX_NOT_SUPPORTED + // free regex memory + while(mRegex.size() > 0) + { + regex_t *pregex = mRegex.back(); + mRegex.pop_back(); + // Free regex storage, and the structure itself + ::regfree(pregex); + delete pregex; + } +#endif + + // Clean up exceptions list + if(mpAlwaysInclude != 0) + { + delete mpAlwaysInclude; + mpAlwaysInclude = 0; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::AddDefiniteEntries(const std::string &) +// Purpose: Adds a number of definite entries to the exclude list -- ones which +// will be excluded if and only if the test string matches exactly. +// Uses the Configuration classes' multi-value conventions, with +// multiple entires in one string separated by Configuration::MultiValueSeparator +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +void ExcludeList::AddDefiniteEntries(const std::string &rEntries) +{ + // Split strings up + std::vector ens; + SplitString(rEntries, Configuration::MultiValueSeparator, ens); + + // Add to set of excluded strings + for(std::vector::const_iterator i(ens.begin()); i != ens.end(); ++i) + { + if(i->size() > 0) + { + mDefinite.insert(*i); + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::AddRegexEntries(const std::string &) +// Purpose: Adds a number of regular expression entries to the exclude list -- +// if the test expression matches any of these regex, it will be excluded. +// Uses the Configuration classes' multi-value conventions, with +// multiple entires in one string separated by Configuration::MultiValueSeparator +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +void ExcludeList::AddRegexEntries(const std::string &rEntries) +{ +#ifndef PLATFORM_REGEX_NOT_SUPPORTED + + // Split strings up + std::vector ens; + SplitString(rEntries, Configuration::MultiValueSeparator, ens); + + // Create and add new regular expressions + for(std::vector::const_iterator i(ens.begin()); i != ens.end(); ++i) + { + if(i->size() > 0) + { + // Allocate memory + regex_t *pregex = new regex_t; + + try + { + // Compile + if(::regcomp(pregex, i->c_str(), REG_EXTENDED | REG_NOSUB) != 0) + { + THROW_EXCEPTION(CommonException, BadRegularExpression) + } + + // Store in list of regular expressions + mRegex.push_back(pregex); + } + catch(...) + { + delete pregex; + throw; + } + } + } + +#else + THROW_EXCEPTION(CommonException, RegexNotSupportedOnThisPlatform) +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::IsExcluded(const std::string &) +// Purpose: Returns true if the entry should be excluded +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +bool ExcludeList::IsExcluded(const std::string &rTest) const +{ + // Check against the always include list + if(mpAlwaysInclude != 0) + { + if(mpAlwaysInclude->IsExcluded(rTest)) + { + // Because the "always include" list says it's 'excluded' + // this means it should actually be included. + return false; + } + } + + // Is it in the set of definite entries? + if(mDefinite.find(rTest) != mDefinite.end()) + { + return true; + } + + // Check against regular expressions +#ifndef PLATFORM_REGEX_NOT_SUPPORTED + for(std::vector::const_iterator i(mRegex.begin()); i != mRegex.end(); ++i) + { + // Test against this expression + if(regexec(*i, rTest.c_str(), 0, 0 /* no match information required */, 0 /* no flags */) == 0) + { + // match happened + return true; + } + // In all other cases, including an error, just continue to the next expression + } +#endif + + return false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ExcludeList::SetAlwaysIncludeList(ExcludeList *) +// Purpose: Takes ownership of the list, deletes any pre-existing list. +// NULL is acceptable to delete the list. +// The AlwaysInclude list is a list of exceptions to the exclusions. +// Created: 19/2/04 +// +// -------------------------------------------------------------------------- +void ExcludeList::SetAlwaysIncludeList(ExcludeList *pAlwaysInclude) +{ + // Delete old list + if(mpAlwaysInclude != 0) + { + delete mpAlwaysInclude; + mpAlwaysInclude = 0; + } + + // Store the pointer + mpAlwaysInclude = pAlwaysInclude; +} + + + + + diff --git a/lib/common/ExcludeList.h b/lib/common/ExcludeList.h new file mode 100755 index 00000000..a1954044 --- /dev/null +++ b/lib/common/ExcludeList.h @@ -0,0 +1,65 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ExcludeList.h +// Purpose: General purpose exclusion list +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef EXCLUDELIST__H +#define EXCLUDELIST__H + +#include +#include +#include + +// avoid including regex.h in lots of places +#ifndef EXCLUDELIST_IMPLEMENTATION_REGEX_T_DEFINED + typedef int regex_t; +#endif + +// -------------------------------------------------------------------------- +// +// Class +// Name: ExcludeList +// Purpose: General purpose exclusion list +// Created: 28/1/04 +// +// -------------------------------------------------------------------------- +class ExcludeList +{ +public: + ExcludeList(); + ~ExcludeList(); + + void AddDefiniteEntries(const std::string &rEntries); + void AddRegexEntries(const std::string &rEntries); + + // Add exceptions to the exclusions (takes ownership) + void SetAlwaysIncludeList(ExcludeList *pAlwaysInclude); + + // Test function + bool IsExcluded(const std::string &rTest) const; + + // Mainly for tests + unsigned int SizeOfDefiniteList() const {return mDefinite.size();} + unsigned int SizeOfRegexList() const +#ifndef PLATFORM_REGEX_NOT_SUPPORTED + {return mRegex.size();} +#else + {return 0;} +#endif + +private: + std::set mDefinite; +#ifndef PLATFORM_REGEX_NOT_SUPPORTED + std::vector mRegex; +#endif + + // For exceptions to the excludes + ExcludeList *mpAlwaysInclude; +}; + +#endif // EXCLUDELIST__H + diff --git a/lib/common/FdGetLine.cpp b/lib/common/FdGetLine.cpp new file mode 100755 index 00000000..dee02604 --- /dev/null +++ b/lib/common/FdGetLine.cpp @@ -0,0 +1,211 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FdGetLine.cpp +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include + +#include "FdGetLine.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// utility whitespace function +inline bool iw(int c) +{ + return (c == ' ' || c == '\t' || c == '\v' || c == '\f'); // \r, \n are already excluded +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::FdGetLine(int) +// Purpose: Constructor, taking file descriptor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +FdGetLine::FdGetLine(int fd) + : mFileHandle(fd), + mLineNumber(0), + mBufferBegin(0), + mBytesInBuffer(0), + mPendingEOF(false), + mEOF(false) +{ + if(mFileHandle < 0) {THROW_EXCEPTION(CommonException, BadArguments)} + //printf("FdGetLine buffer size = %d\n", sizeof(mBuffer)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::~FdGetLine() +// Purpose: Destructor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +FdGetLine::~FdGetLine() +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::GetLine(bool) +// Purpose: Returns a file from the file. If Preprocess is true, leading +// and trailing whitespace is removed, and comments (after #) +// are deleted. +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +std::string FdGetLine::GetLine(bool Preprocess) +{ + if(mFileHandle == -1) {THROW_EXCEPTION(CommonException, GetLineNoHandle)} + + // EOF? + if(mEOF) {THROW_EXCEPTION(CommonException, GetLineEOF)} + + std::string r; + + bool foundLineEnd = false; + + while(!foundLineEnd && !mEOF) + { + // Use any bytes left in the buffer + while(mBufferBegin < mBytesInBuffer) + { + int c = mBuffer[mBufferBegin++]; + if(c == '\r') + { + // Ignore nasty Windows line ending extra chars + } + else if(c == '\n') + { + // Line end! + foundLineEnd = true; + break; + } + else + { + // Add to string + r += c; + } + + // Implicit line ending at EOF + if(mBufferBegin >= mBytesInBuffer && mPendingEOF) + { + foundLineEnd = true; + } + } + + // Check size + if(r.size() > FDGETLINE_MAX_LINE_SIZE) + { + THROW_EXCEPTION(CommonException, GetLineTooLarge) + } + + // Read more in? + if(!foundLineEnd && mBufferBegin >= mBytesInBuffer && !mPendingEOF) + { + int bytes = ::read(mFileHandle, mBuffer, sizeof(mBuffer)); + + // Error? + if(bytes == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + + // Adjust buffer info + mBytesInBuffer = bytes; + mBufferBegin = 0; + + // EOF / closed? + if(bytes == 0) + { + mPendingEOF = true; + } + } + + // EOF? + if(mPendingEOF && mBufferBegin >= mBytesInBuffer) + { + // File is EOF, and now we've depleted the buffer completely, so tell caller as well. + mEOF = true; + } + } + + if(!Preprocess) + { + return r; + } + else + { + // Check for comment char, but char before must be whitespace + int end = 0; + int size = r.size(); + while(end < size) + { + if(r[end] == '#' && (end == 0 || (iw(r[end-1])))) + { + break; + } + end++; + } + + // Remove whitespace + int begin = 0; + while(begin < size && iw(r[begin])) + { + begin++; + } + if(!iw(r[end])) end--; + while(end > begin && iw(r[end])) + { + end--; + } + + // Return a sub string + return r.substr(begin, end - begin + 1); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FdGetLine::DetachFile() +// Purpose: Detaches the file handle, setting the file pointer correctly. +// Probably not good for sockets... +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +void FdGetLine::DetachFile() +{ + if(mFileHandle == -1) {THROW_EXCEPTION(CommonException, GetLineNoHandle)} + + // Adjust file pointer + int bytesOver = mBufferBegin - mBufferBegin; + ASSERT(bytesOver >= 0); + if(bytesOver > 0) + { + if(::lseek(mFileHandle, 0 - bytesOver, SEEK_CUR) == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + } + + // Unset file pointer + mFileHandle = -1; +} + + diff --git a/lib/common/FdGetLine.h b/lib/common/FdGetLine.h new file mode 100755 index 00000000..fecb0371 --- /dev/null +++ b/lib/common/FdGetLine.h @@ -0,0 +1,61 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FdGetLine.h +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#ifndef FDGETLINE__H +#define FDGETLINE__H + +#include + +#ifdef NDEBUG + #define FDGETLINE_BUFFER_SIZE 1024 +#else + #define FDGETLINE_BUFFER_SIZE 4 +#endif + +// Just a very large upper bound for line size to avoid +// people sending lots of data over sockets and causing memory problems. +#define FDGETLINE_MAX_LINE_SIZE (1024*256) + +// -------------------------------------------------------------------------- +// +// Class +// Name: FdGetLine +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +class FdGetLine +{ +public: + FdGetLine(int fd); + ~FdGetLine(); +private: + FdGetLine(const FdGetLine &rToCopy); + +public: + std::string GetLine(bool Preprocess = false); + bool IsEOF() {return mEOF;} + int GetLineNumber() {return mLineNumber;} + + // Call to detach, setting file pointer correctly to last bit read. + // Only works for lseek-able file descriptors. + void DetachFile(); + +private: + char mBuffer[FDGETLINE_BUFFER_SIZE]; + int mFileHandle; + int mLineNumber; + int mBufferBegin; + int mBytesInBuffer; + bool mPendingEOF; + bool mEOF; +}; + +#endif // FDGETLINE__H + diff --git a/lib/common/FileModificationTime.h b/lib/common/FileModificationTime.h new file mode 100755 index 00000000..78f5c115 --- /dev/null +++ b/lib/common/FileModificationTime.h @@ -0,0 +1,57 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileModificationTime.h +// Purpose: Function for getting file modification time. +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- + +#ifndef FILEMODIFICATIONTIME__H +#define FILEMODIFICATIONTIME__H + +#include + +#include "BoxTime.h" + +inline box_time_t FileModificationTime(struct stat &st) +{ +#ifdef PLATFORM_stat_SHORT_mtime + box_time_t datamodified = ((int64_t)st.st_mtime) * (MICRO_SEC_IN_SEC_LL); +#else + box_time_t datamodified = (((int64_t)st.st_mtimespec.tv_nsec) / NANO_SEC_IN_USEC_LL) + + (((int64_t)st.st_mtimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#endif + + return datamodified; +} + +inline box_time_t FileAttrModificationTime(struct stat &st) +{ +#ifdef PLATFORM_stat_SHORT_mtime + box_time_t statusmodified = ((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL); +#else + box_time_t statusmodified = (((int64_t)st.st_ctimespec.tv_nsec) / NANO_SEC_IN_USEC_LL) + + (((int64_t)st.st_ctimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#endif + + return statusmodified; +} + +inline box_time_t FileModificationTimeMaxModAndAttr(struct stat &st) +{ +#ifdef PLATFORM_stat_SHORT_mtime + box_time_t datamodified = ((int64_t)st.st_mtime) * (MICRO_SEC_IN_SEC_LL); + box_time_t statusmodified = ((int64_t)st.st_ctime) * (MICRO_SEC_IN_SEC_LL); +#else + box_time_t datamodified = (((int64_t)st.st_mtimespec.tv_nsec) / NANO_SEC_IN_USEC_LL) + + (((int64_t)st.st_mtimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL)); + box_time_t statusmodified = (((int64_t)st.st_ctimespec.tv_nsec) / NANO_SEC_IN_USEC_LL) + + (((int64_t)st.st_ctimespec.tv_sec) * (MICRO_SEC_IN_SEC_LL)); +#endif + + return (datamodified > statusmodified)?datamodified:statusmodified; +} + +#endif // FILEMODIFICATIONTIME__H + diff --git a/lib/common/FileStream.cpp b/lib/common/FileStream.cpp new file mode 100755 index 00000000..ca6894ae --- /dev/null +++ b/lib/common/FileStream.cpp @@ -0,0 +1,245 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileStream.cpp +// Purpose: IOStream interface to files +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "FileStream.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::FileStream(const char *, int, int) +// Purpose: Constructor, opens file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +FileStream::FileStream(const char *Filename, int flags, int mode) + : mOSFileHandle(::open(Filename, flags, mode)), + mIsEOF(false) +{ + if(mOSFileHandle < 0) + { + MEMLEAKFINDER_NOT_A_LEAK(this); + THROW_EXCEPTION(CommonException, OSFileOpenError) + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::FileStream(int) +// Purpose: Constructor, using existing file descriptor +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +FileStream::FileStream(int FileDescriptor) + : mOSFileHandle(FileDescriptor), + mIsEOF(false) +{ + if(mOSFileHandle < 0) + { + MEMLEAKFINDER_NOT_A_LEAK(this); + THROW_EXCEPTION(CommonException, OSFileOpenError) + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::FileStream(const FileStream &) +// Purpose: Copy constructor, creates a duplicate of the file handle +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +FileStream::FileStream(const FileStream &rToCopy) + : mOSFileHandle(::dup(rToCopy.mOSFileHandle)), + mIsEOF(rToCopy.mIsEOF) +{ + if(mOSFileHandle < 0) + { + MEMLEAKFINDER_NOT_A_LEAK(this); + THROW_EXCEPTION(CommonException, OSFileOpenError) + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::~FileStream() +// Purpose: Destructor, closes file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +FileStream::~FileStream() +{ + if(mOSFileHandle >= 0) + { + Close(); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::Read(void *, int) +// Purpose: Reads bytes from the file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +int FileStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + if(mOSFileHandle == -1) {THROW_EXCEPTION(CommonException, FileClosed)} + int r = ::read(mOSFileHandle, pBuffer, NBytes); + if(r == -1) + { + THROW_EXCEPTION(CommonException, OSFileReadError) + } + if(r == 0) + { + mIsEOF = true; + } + + return r; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::BytesLeftToRead() +// Purpose: Returns number of bytes to read (may not be most efficient function ever) +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +IOStream::pos_type FileStream::BytesLeftToRead() +{ + struct stat st; + if(::fstat(mOSFileHandle, &st) != 0) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + + return st.st_size - GetPosition(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::Write(void *, int) +// Purpose: Writes bytes to the file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void FileStream::Write(const void *pBuffer, int NBytes) +{ + if(mOSFileHandle == -1) {THROW_EXCEPTION(CommonException, FileClosed)} + if(::write(mOSFileHandle, pBuffer, NBytes) != NBytes) + { + THROW_EXCEPTION(CommonException, OSFileWriteError) + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::GetPosition() +// Purpose: Get position in stream +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +IOStream::pos_type FileStream::GetPosition() const +{ + if(mOSFileHandle == -1) {THROW_EXCEPTION(CommonException, FileClosed)} + off_t p = ::lseek(mOSFileHandle, 0, SEEK_CUR); + if(p == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + + return (IOStream::pos_type)p; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::Seek(pos_type, int) +// Purpose: Seeks within file, as lseek +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void FileStream::Seek(IOStream::pos_type Offset, int SeekType) +{ + if(mOSFileHandle == -1) {THROW_EXCEPTION(CommonException, FileClosed)} + if(::lseek(mOSFileHandle, Offset, ConvertSeekTypeToOSWhence(SeekType)) == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + + // Not end of file any more! + mIsEOF = false; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::Close() +// Purpose: Closes the underlying file +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void FileStream::Close() +{ + if(mOSFileHandle < 0) + { + THROW_EXCEPTION(CommonException, FileAlreadyClosed) + } + if(::close(mOSFileHandle) != 0) + { + THROW_EXCEPTION(CommonException, OSFileCloseError) + } + mOSFileHandle = -1; + mIsEOF = true; +} + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::StreamDataLeft() +// Purpose: Any data left to write? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool FileStream::StreamDataLeft() +{ + return !mIsEOF; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileStream::StreamClosed() +// Purpose: Is the stream closed? +// Created: 2003/08/02 +// +// -------------------------------------------------------------------------- +bool FileStream::StreamClosed() +{ + return mIsEOF; +} + diff --git a/lib/common/FileStream.h b/lib/common/FileStream.h new file mode 100755 index 00000000..bb3cc459 --- /dev/null +++ b/lib/common/FileStream.h @@ -0,0 +1,46 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: FileStream.h +// Purpose: FileStream interface to files +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#ifndef FILESTREAM__H +#define FILESTREAM__H + +#include "IOStream.h" + +#include +#include +#include +#include + +class FileStream : public IOStream +{ +public: + FileStream(const char *Filename, int flags = O_RDONLY, int mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)); + FileStream(int FileDescriptor); + FileStream(const FileStream &rToCopy); + virtual ~FileStream(); + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes); + virtual pos_type GetPosition() const; + virtual void Seek(IOStream::pos_type Offset, int SeekType); + virtual void Close(); + + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +private: + int mOSFileHandle; + bool mIsEOF; +}; + + +#endif // FILESTREAM__H + + diff --git a/lib/common/Guards.h b/lib/common/Guards.h new file mode 100755 index 00000000..6efc5614 --- /dev/null +++ b/lib/common/Guards.h @@ -0,0 +1,112 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Guards.h +// Purpose: Classes which ensure things are closed/deleted properly when +// going out of scope. Easy exception proof code, etc +// Created: 2003/07/12 +// +// -------------------------------------------------------------------------- + +#ifndef GUARDS__H +#define GUARDS__H + +#include +#include +#include +#include +#include + +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +template +class FileHandleGuard +{ +public: + FileHandleGuard(const char *filename) + : mOSFileHandle(::open(filename, flags, mode)) + { + if(mOSFileHandle < 0) + { + THROW_EXCEPTION(CommonException, OSFileOpenError) + } + } + + ~FileHandleGuard() + { + if(mOSFileHandle >= 0) + { + Close(); + } + } + + void Close() + { + if(mOSFileHandle < 0) + { + THROW_EXCEPTION(CommonException, FileAlreadyClosed) + } + if(::close(mOSFileHandle) != 0) + { + THROW_EXCEPTION(CommonException, OSFileCloseError) + } + mOSFileHandle = -1; + } + + operator int() const + { + return mOSFileHandle; + } + +private: + int mOSFileHandle; +}; + +template +class MemoryBlockGuard +{ +public: + MemoryBlockGuard(int BlockSize) + : mpBlock(::malloc(BlockSize)) + { + if(mpBlock == 0) + { + throw std::bad_alloc(); + } + } + + ~MemoryBlockGuard() + { + free(mpBlock); + } + + operator type() const + { + return (type)mpBlock; + } + + type GetPtr() const + { + return (type)mpBlock; + } + + void Resize(int NewSize) + { + void *ptrn = ::realloc(mpBlock, NewSize); + if(ptrn == 0) + { + throw std::bad_alloc(); + } + mpBlock = ptrn; + } + +private: + void *mpBlock; +}; + +#include "MemLeakFindOff.h" + +#endif // GUARDS__H + diff --git a/lib/common/IOStream.cpp b/lib/common/IOStream.cpp new file mode 100755 index 00000000..024eefcc --- /dev/null +++ b/lib/common/IOStream.cpp @@ -0,0 +1,229 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStream.cpp +// Purpose: I/O Stream abstraction +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "IOStream.h" +#include "CommonException.h" +#include "Guards.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::IOStream() +// Purpose: Constructor +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +IOStream::IOStream() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::IOStream(const IOStream &) +// Purpose: Copy constructor (exceptions) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +IOStream::IOStream(const IOStream &rToCopy) +{ + THROW_EXCEPTION(CommonException, NotSupported) +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::~IOStream() +// Purpose: Destructor +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +IOStream::~IOStream() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::Close() +// Purpose: Close the stream +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void IOStream::Close() +{ + // Do nothing by default -- let the destructor clear everything up. +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::Seek(int, int) +// Purpose: Seek in stream (if supported) +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void IOStream::Seek(IOStream::pos_type Offset, int SeekType) +{ + THROW_EXCEPTION(CommonException, NotSupported) +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::GetPosition() +// Purpose: Returns current position in stream (if supported) +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +IOStream::pos_type IOStream::GetPosition() const +{ + THROW_EXCEPTION(CommonException, NotSupported) +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::ConvertSeekTypeToOSWhence(int) +// Purpose: Return an whence arg for lseek given a IOStream seek type +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- +int IOStream::ConvertSeekTypeToOSWhence(int SeekType) +{ + // Should be nicely optimised out as values are choosen in header file to match OS values. + int ostype = SEEK_SET; + switch(SeekType) + { + case SeekType_Absolute: + ostype = SEEK_SET; + break; + case SeekType_Relative: + ostype = SEEK_CUR; + break; + case SeekType_End: + ostype = SEEK_END; + break; + + default: + THROW_EXCEPTION(CommonException, IOStreamBadSeekType) + } + + return ostype; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::ReadFullBuffer(void *, int, int) +// Purpose: Reads bytes into buffer, returning whether or not it managed to +// get all the bytes required. Exception and abort use of stream +// if this returns false. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool IOStream::ReadFullBuffer(void *pBuffer, int NBytes, int *pNBytesRead, int Timeout) +{ + int bytesToGo = NBytes; + char *buffer = (char*)pBuffer; + if(pNBytesRead) (*pNBytesRead) = 0; + + while(bytesToGo > 0) + { + int bytesRead = Read(buffer, bytesToGo, Timeout); + if(bytesRead == 0) + { + // Timeout or something + return false; + } + // Increment things + bytesToGo -= bytesRead; + buffer += bytesRead; + if(pNBytesRead) (*pNBytesRead) += bytesRead; + } + + // Got everything + return true; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::WriteAllBuffered() +// Purpose: Ensures that any data which has been buffered is written to the stream +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void IOStream::WriteAllBuffered() +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::BytesLeftToRead() +// Purpose: Numbers of bytes left to read in the stream, or +// IOStream::SizeOfStreamUnknown if this isn't known. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +IOStream::pos_type IOStream::BytesLeftToRead() +{ + return IOStream::SizeOfStreamUnknown; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStream::CopyStreamTo(IOStream &, int Timeout) +// Purpose: Copies the entire stream to another stream (reading from this, +// writing to rCopyTo). Returns whether the copy completed (ie +// StreamDataLeft() returns false) +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool IOStream::CopyStreamTo(IOStream &rCopyTo, int Timeout, int BufferSize) +{ + // Make sure there's something to do before allocating that buffer + if(!StreamDataLeft()) + { + return true; // complete, even though nothing happened + } + + // Buffer + MemoryBlockGuard buffer(BufferSize); + + // Get copying! + while(StreamDataLeft()) + { + // Read some data + int bytes = Read(buffer, BufferSize, Timeout); + if(bytes == 0 && StreamDataLeft()) + { + return false; // incomplete, timed out + } + + // Write some data + if(bytes != 0) + { + rCopyTo.Write(buffer, bytes); + } + } + + return true; // completed +} + + diff --git a/lib/common/IOStream.h b/lib/common/IOStream.h new file mode 100755 index 00000000..042ccca4 --- /dev/null +++ b/lib/common/IOStream.h @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStream.h +// Purpose: I/O Stream abstraction +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#ifndef IOSTREAM__H +#define IOSTREAM__H + +// -------------------------------------------------------------------------- +// +// Class +// Name: IOStream +// Purpose: Abstract interface to streams of data +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +class IOStream +{ +public: + IOStream(); + IOStream(const IOStream &rToCopy); + virtual ~IOStream(); + + enum + { + TimeOutInfinite = -1, + SizeOfStreamUnknown = -1 + }; + + enum + { + SeekType_Absolute = 0, + SeekType_Relative = 1, + SeekType_End = 2 + }; + + // Timeout in milliseconds + // Read may return 0 -- does not mean end of stream. + typedef int64_t pos_type; + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite) = 0; + virtual pos_type BytesLeftToRead(); // may return IOStream::SizeOfStreamUnknown (and will for most stream types) + virtual void Write(const void *pBuffer, int NBytes) = 0; + virtual void WriteAllBuffered(); + virtual pos_type GetPosition() const; + virtual void Seek(pos_type Offset, int SeekType); + virtual void Close(); + + // Has all data that can be read been read? + virtual bool StreamDataLeft() = 0; + // Has the stream been closed (writing not possible) + virtual bool StreamClosed() = 0; + + // Utility functions + bool ReadFullBuffer(void *pBuffer, int NBytes, int *pNBytesRead, int Timeout = IOStream::TimeOutInfinite); + bool CopyStreamTo(IOStream &rCopyTo, int Timeout = IOStream::TimeOutInfinite, int BufferSize = 1024); + + static int ConvertSeekTypeToOSWhence(int SeekType); +}; + + +#endif // IOSTREAM__H + + diff --git a/lib/common/IOStreamGetLine.cpp b/lib/common/IOStreamGetLine.cpp new file mode 100755 index 00000000..27a77c29 --- /dev/null +++ b/lib/common/IOStreamGetLine.cpp @@ -0,0 +1,227 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStreamGetLine.cpp +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "IOStreamGetLine.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// utility whitespace function +inline bool iw(int c) +{ + return (c == ' ' || c == '\t' || c == '\v' || c == '\f'); // \r, \n are already excluded +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::IOStreamGetLine(int) +// Purpose: Constructor, taking file descriptor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +IOStreamGetLine::IOStreamGetLine(IOStream &Stream) + : mrStream(Stream), + mLineNumber(0), + mBufferBegin(0), + mBytesInBuffer(0), + mPendingEOF(false), + mEOF(false) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::~IOStreamGetLine() +// Purpose: Destructor +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +IOStreamGetLine::~IOStreamGetLine() +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::GetLine(std::string &, bool, int) +// Purpose: Gets a line from the file, returning it in rOutput. If Preprocess is true, leading +// and trailing whitespace is removed, and comments (after #) +// are deleted. +// Returns true if a line is available now, false if retrying may get a line (eg timeout, signal), +// and exceptions if it's EOF. +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +bool IOStreamGetLine::GetLine(std::string &rOutput, bool Preprocess, int Timeout) +{ + // EOF? + if(mEOF) {THROW_EXCEPTION(CommonException, GetLineEOF)} + + // Initialise string to stored into + std::string r(mPendingString); + mPendingString.erase(); + + bool foundLineEnd = false; + + while(!foundLineEnd && !mEOF) + { + // Use any bytes left in the buffer + while(mBufferBegin < mBytesInBuffer) + { + int c = mBuffer[mBufferBegin++]; + if(c == '\r') + { + // Ignore nasty Windows line ending extra chars + } + else if(c == '\n') + { + // Line end! + foundLineEnd = true; + break; + } + else + { + // Add to string + r += c; + } + + // Implicit line ending at EOF + if(mBufferBegin >= mBytesInBuffer && mPendingEOF) + { + foundLineEnd = true; + } + } + + // Check size + if(r.size() > IOSTREAMGETLINE_MAX_LINE_SIZE) + { + THROW_EXCEPTION(CommonException, GetLineTooLarge) + } + + // Read more in? + if(!foundLineEnd && mBufferBegin >= mBytesInBuffer && !mPendingEOF) + { + int bytes = mrStream.Read(mBuffer, sizeof(mBuffer), Timeout); + + // Adjust buffer info + mBytesInBuffer = bytes; + mBufferBegin = 0; + + // EOF / closed? + if(!mrStream.StreamDataLeft()) + { + mPendingEOF = true; + } + + // No data returned? + if(bytes == 0 && mrStream.StreamDataLeft()) + { + // store string away + mPendingString = r; + // Return false; + return false; + } + } + + // EOF? + if(mPendingEOF && mBufferBegin >= mBytesInBuffer) + { + // File is EOF, and now we've depleted the buffer completely, so tell caller as well. + mEOF = true; + } + } + + if(!Preprocess) + { + rOutput = r; + return true; + } + else + { + // Check for comment char, but char before must be whitespace + int end = 0; + int size = r.size(); + while(end < size) + { + if(r[end] == '#' && (end == 0 || (iw(r[end-1])))) + { + break; + } + end++; + } + + // Remove whitespace + int begin = 0; + while(begin < size && iw(r[begin])) + { + begin++; + } + if(!iw(r[end])) end--; + while(end > begin && iw(r[end])) + { + end--; + } + + // Return a sub string + rOutput = r.substr(begin, end - begin + 1); + return true; + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::DetachFile() +// Purpose: Detaches the file handle, setting the file pointer correctly. +// Probably not good for sockets... +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +void IOStreamGetLine::DetachFile() +{ + // Adjust file pointer + int bytesOver = mBytesInBuffer - mBufferBegin; + ASSERT(bytesOver >= 0); + if(bytesOver > 0) + { + mrStream.Seek(0 - bytesOver, IOStream::SeekType_Relative); + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: IOStreamGetLine::IgnoreBufferedData(int) +// Purpose: Ignore buffered bytes (effectively removing them from the +// beginning of the buffered data.) +// Cannot remove more bytes than are currently in the buffer. +// Be careful when this is used! +// Created: 22/12/04 +// +// -------------------------------------------------------------------------- +void IOStreamGetLine::IgnoreBufferedData(int BytesToIgnore) +{ + int bytesInBuffer = mBytesInBuffer - mBufferBegin; + if(BytesToIgnore < 0 || BytesToIgnore > bytesInBuffer) + { + THROW_EXCEPTION(CommonException, IOStreamGetLineNotEnoughDataToIgnore) + } + mBufferBegin += BytesToIgnore; +} + + + diff --git a/lib/common/IOStreamGetLine.h b/lib/common/IOStreamGetLine.h new file mode 100755 index 00000000..cf152e5a --- /dev/null +++ b/lib/common/IOStreamGetLine.h @@ -0,0 +1,71 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: IOStreamGetLine.h +// Purpose: Line based file descriptor reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- + +#ifndef IOSTREAMGETLINE__H +#define IOSTREAMGETLINE__H + +#include + +#include "IOStream.h" + +#ifdef NDEBUG + #define IOSTREAMGETLINE_BUFFER_SIZE 1024 +#else + #define IOSTREAMGETLINE_BUFFER_SIZE 4 +#endif + +// Just a very large upper bound for line size to avoid +// people sending lots of data over sockets and causing memory problems. +#define IOSTREAMGETLINE_MAX_LINE_SIZE (1024*256) + +// -------------------------------------------------------------------------- +// +// Class +// Name: IOStreamGetLine +// Purpose: Line based stream reading +// Created: 2003/07/24 +// +// -------------------------------------------------------------------------- +class IOStreamGetLine +{ +public: + IOStreamGetLine(IOStream &Stream); + ~IOStreamGetLine(); +private: + IOStreamGetLine(const IOStreamGetLine &rToCopy); + +public: + bool GetLine(std::string &rOutput, bool Preprocess = false, int Timeout = IOStream::TimeOutInfinite); + bool IsEOF() {return mEOF;} + int GetLineNumber() {return mLineNumber;} + + // Call to detach, setting file pointer correctly to last bit read. + // Only works for lseek-able file descriptors. + void DetachFile(); + + // For doing interesting stuff with the remaining data... + // Be careful with this! + const void *GetBufferedData() const {return mBuffer + mBufferBegin;} + int GetSizeOfBufferedData() const {return mBytesInBuffer - mBufferBegin;} + void IgnoreBufferedData(int BytesToIgnore); + IOStream &GetUnderlyingStream() {return mrStream;} + +private: + char mBuffer[IOSTREAMGETLINE_BUFFER_SIZE]; + IOStream &mrStream; + int mLineNumber; + int mBufferBegin; + int mBytesInBuffer; + bool mPendingEOF; + bool mEOF; + std::string mPendingString; +}; + +#endif // IOSTREAMGETLINE__H + diff --git a/lib/common/LinuxWorkaround.cpp b/lib/common/LinuxWorkaround.cpp new file mode 100755 index 00000000..7900fa6e --- /dev/null +++ b/lib/common/LinuxWorkaround.cpp @@ -0,0 +1,73 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: LinuxWorkaround.cpp +// Purpose: Workarounds for Linux +// Created: 2003/10/31 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include + +#include "LinuxWorkaround.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +#ifdef PLATFORM_LINUX + +// -------------------------------------------------------------------------- +// +// Function +// Name: LinuxWorkaround_FinishDirentStruct(struct dirent *, const char *) +// Purpose: Finishes off filling in a dirent structure, which Linux leaves incomplete. +// Created: 2003/10/31 +// +// -------------------------------------------------------------------------- +void LinuxWorkaround_FinishDirentStruct(struct dirent *entry, const char *DirectoryName) +{ + // From man readdir under Linux: + // + // BUGS + // Field d_type is not implemented as of libc6 2.1 and will always return + // DT_UNKNOWN (0). + // + // What kind of an OS is this? + + + // Build filename of this entry + std::string fn(DirectoryName); + fn += '/'; + fn += entry->d_name; + + // Do a stat on it + struct stat st; + if(::lstat(fn.c_str(), &st) != 0) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + + // Fill in the d_type field. + if(S_ISREG(st.st_mode)) + { + entry->d_type = DT_REG; + } + else if(S_ISDIR(st.st_mode)) + { + entry->d_type = DT_DIR; + } + else if(S_ISLNK(st.st_mode)) + { + entry->d_type = DT_LNK; + } + // otherwise leave it as we found it +} + +#endif // PLATFORM_LINUX + diff --git a/lib/common/LinuxWorkaround.h b/lib/common/LinuxWorkaround.h new file mode 100755 index 00000000..bcd27495 --- /dev/null +++ b/lib/common/LinuxWorkaround.h @@ -0,0 +1,20 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: LinuxWorkaround.h +// Purpose: Workarounds for Linux +// Created: 2003/10/31 +// +// -------------------------------------------------------------------------- + +#ifndef LINUXWORKAROUND__H +#define LINUXWORKAROUND__H + +#ifdef PLATFORM_LINUX + +void LinuxWorkaround_FinishDirentStruct(struct dirent *entry, const char *DirectoryName); + +#endif // PLATFORM_LINUX + +#endif // LINUXWORKAROUND__H + diff --git a/lib/common/MainHelper.h b/lib/common/MainHelper.h new file mode 100755 index 00000000..ab75af96 --- /dev/null +++ b/lib/common/MainHelper.h @@ -0,0 +1,42 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MainHelper.h +// Purpose: Helper stuff for main() programs +// Created: 2003/08/21 +// +// -------------------------------------------------------------------------- + +#ifndef MAINHELPER__H +#define MAINHELPER__H + +#include + +#include "BoxException.h" + +#define MAINHELPER_START \ + if(argc == 2 && ::strcmp(argv[1], "--version") == 0) \ + { printf(BOX_VERSION "\n"); return 0; } \ + MEMLEAKFINDER_START \ + try { +#define MAINHELPER_END \ + } catch(BoxException &e) { \ + printf("Exception: %s (%d/%d)\n", e.what(), e.GetType(), e.GetSubType()); \ + return 1; \ + } catch(std::exception &e) { \ + printf("Exception: %s\n", e.what()); \ + return 1; \ + } catch(...) { \ + printf("Exception: \n"); \ + return 1; } + +#ifdef BOX_MEMORY_LEAK_TESTING + #define MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT(file, marker) \ + memleakfinder_setup_exit_report(file, marker); +#else + #define MAINHELPER_SETUP_MEMORY_LEAK_EXIT_REPORT(file, marker) +#endif // BOX_MEMORY_LEAK_TESTING + + +#endif // MAINHELPER__H + diff --git a/lib/common/Makefile.extra b/lib/common/Makefile.extra new file mode 100755 index 00000000..92e6fbb3 --- /dev/null +++ b/lib/common/Makefile.extra @@ -0,0 +1,11 @@ + +MAKEEXCEPTION = ../../lib/common/makeexception.pl + +# AUTOGEN SEEDING +autogen_CommonException.h autogen_CommonException.cpp: $(MAKEEXCEPTION) CommonException.txt + perl $(MAKEEXCEPTION) CommonException.txt + +# AUTOGEN SEEDING +autogen_ConversionException.h autogen_ConversionException.cpp: $(MAKEEXCEPTION) ConversionException.txt + perl $(MAKEEXCEPTION) ConversionException.txt + diff --git a/lib/common/MemBlockStream.cpp b/lib/common/MemBlockStream.cpp new file mode 100755 index 00000000..538a7ef8 --- /dev/null +++ b/lib/common/MemBlockStream.cpp @@ -0,0 +1,235 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemBlockStream.cpp +// Purpose: Stream out data from any memory block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include + +#include "MemBlockStream.h" +#include "CommonException.h" +#include "StreamableMemBlock.h" +#include "CollectInBufferStream.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::MemBlockStream() +// Purpose: Constructor (doesn't copy block, careful with lifetimes) +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const void *pBuffer, int Size) + : mpBuffer((char*)pBuffer), + mBytesInBuffer(Size), + mReadPosition(0) +{ + ASSERT(pBuffer != 0); + ASSERT(Size >= 0); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::MemBlockStream(const StreamableMemBlock &) +// Purpose: Constructor (doesn't copy block, careful with lifetimes) +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const StreamableMemBlock &rBlock) + : mpBuffer((char*)rBlock.GetBuffer()), + mBytesInBuffer(rBlock.GetSize()), + mReadPosition(0) +{ + ASSERT(mpBuffer != 0); + ASSERT(mBytesInBuffer >= 0); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::MemBlockStream(const StreamableMemBlock &) +// Purpose: Constructor (doesn't copy block, careful with lifetimes) +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const CollectInBufferStream &rBuffer) + : mpBuffer((char*)rBuffer.GetBuffer()), + mBytesInBuffer(rBuffer.GetSize()), + mReadPosition(0) +{ + ASSERT(mpBuffer != 0); + ASSERT(mBytesInBuffer >= 0); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::MemBlockStream(const MemBlockStream &) +// Purpose: Copy constructor +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +MemBlockStream::MemBlockStream(const MemBlockStream &rToCopy) + : mpBuffer(rToCopy.mpBuffer), + mBytesInBuffer(rToCopy.mBytesInBuffer), + mReadPosition(0) +{ + ASSERT(mpBuffer != 0); + ASSERT(mBytesInBuffer >= 0); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::~MemBlockStream() +// Purpose: Destructor +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +MemBlockStream::~MemBlockStream() +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::Read(void *, int, int) +// Purpose: As interface. But only works in read phase +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +int MemBlockStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + // Adjust to number of bytes left + if(NBytes > (mBytesInBuffer - mReadPosition)) + { + NBytes = (mBytesInBuffer - mReadPosition); + } + ASSERT(NBytes >= 0); + if(NBytes <= 0) return 0; // careful now + + // Copy in the requested number of bytes and adjust the read pointer + ::memcpy(pBuffer, mpBuffer + mReadPosition, NBytes); + mReadPosition += NBytes; + + return NBytes; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::BytesLeftToRead() +// Purpose: As interface. But only works in read phase +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +IOStream::pos_type MemBlockStream::BytesLeftToRead() +{ + return (mBytesInBuffer - mReadPosition); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::Write(void *, int) +// Purpose: As interface. But only works in write phase +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void MemBlockStream::Write(const void *pBuffer, int NBytes) +{ + THROW_EXCEPTION(CommonException, MemBlockStreamNotSupported) +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::GetPosition() +// Purpose: In write phase, returns the number of bytes written, in read +// phase, the number of bytes to go +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +IOStream::pos_type MemBlockStream::GetPosition() const +{ + return mReadPosition; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::Seek(pos_type, int) +// Purpose: As interface. +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void MemBlockStream::Seek(pos_type Offset, int SeekType) +{ + int newPos = 0; + switch(SeekType) + { + case IOStream::SeekType_Absolute: + newPos = Offset; + break; + case IOStream::SeekType_Relative: + newPos = mReadPosition + Offset; + break; + case IOStream::SeekType_End: + newPos = mBytesInBuffer + Offset; + break; + default: + THROW_EXCEPTION(CommonException, IOStreamBadSeekType) + break; + } + + // Make sure it doesn't go over + if(newPos > mBytesInBuffer) + { + newPos = mBytesInBuffer; + } + // or under + if(newPos < 0) + { + newPos = 0; + } + + // Set the new read position + mReadPosition = newPos; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::StreamDataLeft() +// Purpose: As interface +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +bool MemBlockStream::StreamDataLeft() +{ + return mReadPosition < mBytesInBuffer; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: MemBlockStream::StreamClosed() +// Purpose: As interface +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +bool MemBlockStream::StreamClosed() +{ + return true; +} + diff --git a/lib/common/MemBlockStream.h b/lib/common/MemBlockStream.h new file mode 100755 index 00000000..f78ff8e6 --- /dev/null +++ b/lib/common/MemBlockStream.h @@ -0,0 +1,52 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemBlockStream.h +// Purpose: Stream out data from any memory block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#ifndef MEMBLOCKSTREAM__H +#define MEMBLOCKSTREAM__H + +#include "IOStream.h" + +class StreamableMemBlock; +class CollectInBufferStream; + +// -------------------------------------------------------------------------- +// +// Class +// Name: MemBlockStream +// Purpose: Stream out data from any memory block -- be careful the lifetime +// of the block is greater than the lifetime of this stream. +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +class MemBlockStream : public IOStream +{ +public: + MemBlockStream(const void *pBuffer, int Size); + MemBlockStream(const StreamableMemBlock &rBlock); + MemBlockStream(const CollectInBufferStream &rBuffer); + MemBlockStream(const MemBlockStream &rToCopy); + ~MemBlockStream(); +public: + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes); + virtual pos_type GetPosition() const; + virtual void Seek(pos_type Offset, int SeekType); + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +private: + const char *mpBuffer; + int mBytesInBuffer; + int mReadPosition; +}; + +#endif // MEMBLOCKSTREAM__H + diff --git a/lib/common/MemLeakFindOff.h b/lib/common/MemLeakFindOff.h new file mode 100755 index 00000000..1cc98bac --- /dev/null +++ b/lib/common/MemLeakFindOff.h @@ -0,0 +1,27 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFindOff.h +// Purpose: Switch memory leak finding off +// Created: 13/1/04 +// +// -------------------------------------------------------------------------- + +// no header guard + +#ifdef BOX_MEMORY_LEAK_TESTING + +#undef new + +#ifndef MEMLEAKFINDER_FULL_MALLOC_MONITORING + #ifdef MEMLEAKFINDER_MALLOC_MONITORING_DEFINED + #undef malloc + #undef realloc + #undef free + #undef MEMLEAKFINDER_MALLOC_MONITORING_DEFINED + #endif +#endif + +#undef MEMLEAKFINDER_ENABLED + +#endif diff --git a/lib/common/MemLeakFindOn.h b/lib/common/MemLeakFindOn.h new file mode 100755 index 00000000..c20fe25a --- /dev/null +++ b/lib/common/MemLeakFindOn.h @@ -0,0 +1,25 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFindOn.h +// Purpose: Switch memory leak finding on +// Created: 13/1/04 +// +// -------------------------------------------------------------------------- + +// no header guard + +#ifdef BOX_MEMORY_LEAK_TESTING + +#define new DEBUG_NEW + +#ifndef MEMLEAKFINDER_MALLOC_MONITORING_DEFINED + #define malloc(X) memleakfinder_malloc(X, __FILE__, __LINE__) + #define realloc memleakfinder_realloc + #define free memleakfinder_free + #define MEMLEAKFINDER_MALLOC_MONITORING_DEFINED +#endif + +#define MEMLEAKFINDER_ENABLED + +#endif diff --git a/lib/common/MemLeakFinder.h b/lib/common/MemLeakFinder.h new file mode 100755 index 00000000..f5887dac --- /dev/null +++ b/lib/common/MemLeakFinder.h @@ -0,0 +1,60 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: MemLeakFinder.h +// Purpose: Memory leak finder +// Created: 12/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef MEMLEAKFINDER__H +#define MEMLEAKFINDER__H + +#define DEBUG_NEW new(__FILE__,__LINE__) + +#ifdef MEMLEAKFINDER_FULL_MALLOC_MONITORING + // include stdlib now, to avoid problems with having the macros defined already + #include +#endif + +// global enable flag +extern bool memleakfinder_global_enable; + +extern "C" +{ + void *memleakfinder_malloc(size_t size, const char *file, int line); + void *memleakfinder_realloc(void *ptr, size_t size); + void memleakfinder_free(void *ptr); +} + +int memleakfinder_numleaks(); + +void memleakfinder_reportleaks(); + +void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext); + +void memleakfinder_setup_exit_report(const char *filename, const char *markertext); + +void memleakfinder_startsectionmonitor(); + +void memleakfinder_traceblocksinsection(); + +void memleakfinder_notaleak(void *ptr); + +void *operator new(size_t size, const char *file, int line); +void *operator new[](size_t size, const char *file, int line); + +void operator delete(void *ptr) throw (); +void operator delete[](void *ptr) throw (); + +// define the malloc functions now, if required +#ifdef MEMLEAKFINDER_FULL_MALLOC_MONITORING + #define malloc(X) memleakfinder_malloc(X, __FILE__, __LINE__) + #define realloc memleakfinder_realloc + #define free memleakfinder_free + #define MEMLEAKFINDER_MALLOC_MONITORING_DEFINED +#endif + + +#endif // MEMLEAKFINDER__H + diff --git a/lib/common/NamedLock.cpp b/lib/common/NamedLock.cpp new file mode 100755 index 00000000..1f6e038a --- /dev/null +++ b/lib/common/NamedLock.cpp @@ -0,0 +1,144 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: NamedLock.cpp +// Purpose: A global named lock, implemented as a lock file in file system +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include +#ifdef PLATFORM_LINUX + #include +#endif // PLATFORM_LINUX +#ifdef PLATFORM_CYGWIN + #include +#endif // PLATFORM_CYGWIN + +#include "NamedLock.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::NamedLock() +// Purpose: Constructor +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +NamedLock::NamedLock() + : mFileDescriptor(-1) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::~NamedLock() +// Purpose: Destructor (automatically unlocks if locked) +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +NamedLock::~NamedLock() +{ + if(mFileDescriptor != -1) + { + ReleaseLock(); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::TryAndGetLock(const char *, int) +// Purpose: Trys to get a lock on the name in the file system. +// IMPORTANT NOTE: If a file exists with this name, it will be deleted. +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +bool NamedLock::TryAndGetLock(const char *Filename, int mode) +{ + // Check + if(mFileDescriptor != -1) + { + THROW_EXCEPTION(CommonException, NamedLockAlreadyLockingSomething) + } + + // See if the lock can be got +#ifdef PLATFORM_open_NO_O_EXLOCK + int fd = ::open(Filename, O_WRONLY | O_CREAT | O_TRUNC, mode); + if(fd == -1) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + if(::flock(fd, LOCK_EX | LOCK_NB) != 0) + { + ::close(fd); + if(errno == EWOULDBLOCK) + { + return false; + } + else + { + THROW_EXCEPTION(CommonException, OSFileError) + } + } + + // Success + mFileDescriptor = fd; + + return true; +#else + int fd = ::open(Filename, O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC | O_EXLOCK, mode); + if(fd != -1) + { + // Got a lock, lovely + mFileDescriptor = fd; + return true; + } + + // Failed. Why? + if(errno != EWOULDBLOCK) + { + // Not the expected error + THROW_EXCEPTION(CommonException, OSFileError) + } + + return false; +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: NamedLock::ReleaseLock() +// Purpose: Release the lock. Exceptions if the lock is not held +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +void NamedLock::ReleaseLock() +{ + // Got a lock? + if(mFileDescriptor == -1) + { + THROW_EXCEPTION(CommonException, NamedLockNotHeld) + } + + // Close the file + if(::close(mFileDescriptor) != 0) + { + THROW_EXCEPTION(CommonException, OSFileError) + } + // Mark as unlocked + mFileDescriptor = -1; +} + + + + diff --git a/lib/common/NamedLock.h b/lib/common/NamedLock.h new file mode 100755 index 00000000..5eac712a --- /dev/null +++ b/lib/common/NamedLock.h @@ -0,0 +1,41 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: NamedLock.h +// Purpose: A global named lock, implemented as a lock file in file system +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- + +#ifndef NAMEDLOCK__H +#define NAMEDLOCK__H + +// -------------------------------------------------------------------------- +// +// Class +// Name: NamedLock +// Purpose: A global named lock, implemented as a lock file in file system +// Created: 2003/08/28 +// +// -------------------------------------------------------------------------- +class NamedLock +{ +public: + NamedLock(); + ~NamedLock(); +private: + // No copying allowed + NamedLock(const NamedLock &); + +public: + bool TryAndGetLock(const char *Filename, int mode = 0755); + bool GotLock() {return mFileDescriptor != -1;} + void ReleaseLock(); + + +private: + int mFileDescriptor; +}; + +#endif // NAMEDLOCK__H + diff --git a/lib/common/PartialReadStream.cpp b/lib/common/PartialReadStream.cpp new file mode 100755 index 00000000..0b5c4cf6 --- /dev/null +++ b/lib/common/PartialReadStream.cpp @@ -0,0 +1,135 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: PartialReadStream.h +// Purpose: Read part of another stream +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- + +#include "Box.h" +#include "PartialReadStream.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::PartialReadStream(IOStream &, int) +// Purpose: Constructor, taking another stream and the number of bytes +// to be read from it. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +PartialReadStream::PartialReadStream(IOStream &rSource, int BytesToRead) + : mrSource(rSource), + mBytesLeft(BytesToRead) +{ + ASSERT(BytesToRead > 0); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::~PartialReadStream() +// Purpose: Destructor. Won't absorb any unread bytes. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +PartialReadStream::~PartialReadStream() +{ + // Warn in debug mode + if(mBytesLeft != 0) + { + TRACE1("PartialReadStream::~PartialReadStream when mBytesLeft = %d\n", mBytesLeft); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::Read(void *, int, int) +// Purpose: As interface. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +int PartialReadStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + // Finished? + if(mBytesLeft <= 0) + { + return 0; + } + + // Asking for more than is allowed? + if(NBytes > mBytesLeft) + { + // Adjust downwards + NBytes = mBytesLeft; + } + + // Route the request to the source + int read = mrSource.Read(pBuffer, NBytes, Timeout); + ASSERT(read <= mBytesLeft); + + // Adjust the count + mBytesLeft -= read; + + // Return the number read + return read; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::BytesLeftToRead() +// Purpose: As interface. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +IOStream::pos_type PartialReadStream::BytesLeftToRead() +{ + return mBytesLeft; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::Write(const void *, int) +// Purpose: As interface. But will exception. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +void PartialReadStream::Write(const void *pBuffer, int NBytes) +{ + THROW_EXCEPTION(CommonException, CantWriteToPartialReadStream) +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::StreamDataLeft() +// Purpose: As interface. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool PartialReadStream::StreamDataLeft() +{ + return mBytesLeft != 0; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: PartialReadStream::StreamClosed() +// Purpose: As interface. +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +bool PartialReadStream::StreamClosed() +{ + // always closed + return true; +} + diff --git a/lib/common/PartialReadStream.h b/lib/common/PartialReadStream.h new file mode 100755 index 00000000..42cb7aeb --- /dev/null +++ b/lib/common/PartialReadStream.h @@ -0,0 +1,46 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: PartialReadStream.h +// Purpose: Read part of another stream +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- + +#ifndef PARTIALREADSTREAM__H +#define PARTIALREADSTREAM__H + +#include "IOStream.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: PartialReadStream +// Purpose: Read part of another stream +// Created: 2003/08/26 +// +// -------------------------------------------------------------------------- +class PartialReadStream : public IOStream +{ +public: + PartialReadStream(IOStream &rSource, int BytesToRead); + ~PartialReadStream(); +private: + // no copying allowed + PartialReadStream(const IOStream &); + PartialReadStream(const PartialReadStream &); + +public: + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes); + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + +private: + IOStream &mrSource; + int mBytesLeft; +}; + +#endif // PARTIALREADSTREAM__H + diff --git a/lib/common/ReadGatherStream.cpp b/lib/common/ReadGatherStream.cpp new file mode 100755 index 00000000..9ccc3a54 --- /dev/null +++ b/lib/common/ReadGatherStream.cpp @@ -0,0 +1,262 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ReadGatherStream.cpp +// Purpose: Build a stream (for reading only) out of a number of other streams. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include "ReadGatherStream.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::ReadGatherStream(bool) +// Purpose: Constructor. Args says whether or not all the component streams will be deleted when this +// object is deleted. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +ReadGatherStream::ReadGatherStream(bool DeleteComponentStreamsOnDestruction) + : mDeleteComponentStreamsOnDestruction(DeleteComponentStreamsOnDestruction), + mCurrentPosition(0), + mTotalSize(0), + mCurrentBlock(0), + mPositionInCurrentBlock(0), + mSeekDoneForCurrent(false) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::~ReadGatherStream() +// Purpose: Destructor. Will delete all the stream objects, if required. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +ReadGatherStream::~ReadGatherStream() +{ + // Delete compoenent streams? + if(mDeleteComponentStreamsOnDestruction) + { + for(unsigned int l = 0; l < mComponents.size(); ++l) + { + delete mComponents[l]; + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::AddComponent(IOStream *) +// Purpose: Add a component to this stream, returning the index of this component +// in the internal list. Use this with AddBlock() +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +int ReadGatherStream::AddComponent(IOStream *pStream) +{ + ASSERT(pStream != 0); + + // Just add the component to the list, returning it's index. + int index = mComponents.size(); + mComponents.push_back(pStream); + return index; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::AddBlock(int, pos_type, bool, pos_type) +// Purpose: Add a block to the list of blocks being gathered into one stream. +// Length is length of block to read from this component, Seek == true +// if a seek is required, and if true, SeekTo is the position (absolute) +// in the stream to be seeked to when this block is required. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +void ReadGatherStream::AddBlock(int Component, pos_type Length, bool Seek, pos_type SeekTo) +{ + // Check block + if(Component < 0 || Component >= (int)mComponents.size() || Length < 0 || SeekTo < 0) + { + THROW_EXCEPTION(CommonException, ReadGatherStreamAddingBadBlock); + } + + // Add to list + Block b; + b.mLength = Length; + b.mSeekTo = SeekTo; + b.mComponent = Component; + b.mSeek = Seek; + + mBlocks.push_back(b); + + // And update the total size + mTotalSize += Length; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::Read(void *, int, int) +// Purpose: As interface. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +int ReadGatherStream::Read(void *pBuffer, int NBytes, int Timeout) +{ + int bytesToRead = NBytes; + uint8_t *buffer = (uint8_t*)pBuffer; + + while(bytesToRead > 0) + { + // Done? + if(mCurrentBlock >= mBlocks.size()) + { + // Stop now, as have finished the last block + return NBytes - bytesToRead; + } + + // Seek? + if(mPositionInCurrentBlock == 0 && mBlocks[mCurrentBlock].mSeek && !mSeekDoneForCurrent) + { + // Do seeks in this manner so that seeks are done regardless of whether the block + // has length > 0, and it will only be done once, and at as late a stage as possible. + + mComponents[mBlocks[mCurrentBlock].mComponent]->Seek(mBlocks[mCurrentBlock].mSeekTo, IOStream::SeekType_Absolute); + + mSeekDoneForCurrent = true; + } + + // Anything in the current block? + if(mPositionInCurrentBlock < mBlocks[mCurrentBlock].mLength) + { + // Read! + int s = mBlocks[mCurrentBlock].mLength - mPositionInCurrentBlock; + if(s > bytesToRead) s = bytesToRead; + + int r = mComponents[mBlocks[mCurrentBlock].mComponent]->Read(buffer, s, Timeout); + + // update variables + mPositionInCurrentBlock += r; + buffer += r; + bytesToRead -= r; + mCurrentPosition += r; + + if(r != s) + { + // Stream returned less than requested. To avoid blocking when not necessary, + // return now. + return NBytes - bytesToRead; + } + } + else + { + // Move to next block + ++mCurrentBlock; + mPositionInCurrentBlock = 0; + mSeekDoneForCurrent = false; + } + } + + return NBytes - bytesToRead; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::GetPosition() +// Purpose: As interface +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +IOStream::pos_type ReadGatherStream::GetPosition() const +{ + return mCurrentPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::BytesLeftToRead() +// Purpose: As interface +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +IOStream::pos_type ReadGatherStream::BytesLeftToRead() +{ + return mTotalSize - mCurrentPosition; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::Write(const void *, int) +// Purpose: As interface. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +void ReadGatherStream::Write(const void *pBuffer, int NBytes) +{ + THROW_EXCEPTION(CommonException, CannotWriteToReadGatherStream); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::StreamDataLeft() +// Purpose: As interface. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +bool ReadGatherStream::StreamDataLeft() +{ + if(mCurrentBlock >= mBlocks.size()) + { + // Done all the blocks + return false; + } + + if(mCurrentBlock == (mBlocks.size() - 1) + && mPositionInCurrentBlock >= mBlocks[mCurrentBlock].mLength) + { + // Are on the last block, and have got all the data from it. + return false; + } + + // Otherwise, there's more data to be read + return true; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: ReadGatherStream::StreamClosed() +// Purpose: As interface. But the stream is always closed. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +bool ReadGatherStream::StreamClosed() +{ + return true; +} + + diff --git a/lib/common/ReadGatherStream.h b/lib/common/ReadGatherStream.h new file mode 100755 index 00000000..613ede3e --- /dev/null +++ b/lib/common/ReadGatherStream.h @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: ReadGatherStream.h +// Purpose: Build a stream (for reading only) out of a number of other streams. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- + +#ifndef READGATHERSTREAM_H +#define READGATHERSTREAM_H + +#include "IOStream.h" + +#include + +// -------------------------------------------------------------------------- +// +// Class +// Name: ReadGatherStream +// Purpose: Build a stream (for reading only) out of a number of other streams. +// Created: 10/12/03 +// +// -------------------------------------------------------------------------- +class ReadGatherStream : public IOStream +{ +public: + ReadGatherStream(bool DeleteComponentStreamsOnDestruction); + ~ReadGatherStream(); +private: + ReadGatherStream(const ReadGatherStream &); + ReadGatherStream &operator=(const ReadGatherStream &); +public: + + int AddComponent(IOStream *pStream); + void AddBlock(int Component, pos_type Length, bool Seek = false, pos_type SeekTo = 0); + + virtual int Read(void *pBuffer, int NBytes, int Timeout = IOStream::TimeOutInfinite); + virtual pos_type BytesLeftToRead(); + virtual void Write(const void *pBuffer, int NBytes); + virtual bool StreamDataLeft(); + virtual bool StreamClosed(); + virtual pos_type GetPosition() const; + +private: + bool mDeleteComponentStreamsOnDestruction; + std::vector mComponents; + + typedef struct + { + pos_type mLength; + pos_type mSeekTo; + int mComponent; + bool mSeek; + } Block; + + std::vector mBlocks; + + pos_type mCurrentPosition; + pos_type mTotalSize; + unsigned int mCurrentBlock; + pos_type mPositionInCurrentBlock; + bool mSeekDoneForCurrent; +}; + + +#endif // READGATHERSTREAM_H diff --git a/lib/common/StreamableMemBlock.cpp b/lib/common/StreamableMemBlock.cpp new file mode 100755 index 00000000..4ea9e398 --- /dev/null +++ b/lib/common/StreamableMemBlock.cpp @@ -0,0 +1,365 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: StreamableMemBlock.cpp +// Purpose: Memory blocks which can be loaded and saved from streams +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include "StreamableMemBlock.h" +#include "IOStream.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::StreamableMemBlock() +// Purpose: Constructor, making empty block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::StreamableMemBlock() + : mpBuffer(0), + mSize(0) +{ +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::StreamableMemBlock(void *, int) +// Purpose: Create block, copying data from another bit of memory +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::StreamableMemBlock(void *pBuffer, int Size) + : mpBuffer(0), + mSize(0) +{ + AllocateBlock(Size); + ::memcpy(mpBuffer, pBuffer, Size); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::StreamableMemBlock(int) +// Purpose: Create block, initialising it to all zeros +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::StreamableMemBlock(int Size) + : mpBuffer(0), + mSize(0) +{ + AllocateBlock(Size); + ::memset(mpBuffer, 0, Size); +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::StreamableMemBlock(const StreamableMemBlock &) +// Purpose: Copy constructor +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::StreamableMemBlock(const StreamableMemBlock &rToCopy) + : mpBuffer(0), + mSize(0) +{ + AllocateBlock(rToCopy.mSize); + ::memcpy(mpBuffer, rToCopy.mpBuffer, mSize); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::Set(void *, int) +// Purpose: Set the contents of the block +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::Set(void *pBuffer, int Size) +{ + FreeBlock(); + AllocateBlock(Size); + ::memcpy(mpBuffer, pBuffer, Size); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::Set(IOStream &) +// Purpose: Set from stream. Stream must support BytesLeftToRead() +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::Set(IOStream &rStream, int Timeout) +{ + // Get size + IOStream::pos_type size = rStream.BytesLeftToRead(); + if(size == IOStream::SizeOfStreamUnknown) + { + THROW_EXCEPTION(CommonException, StreamDoesntHaveRequiredProperty) + } + + // Allocate a new block (this way to be exception safe) + char *pblock = (char*)malloc(size); + if(pblock == 0) + { + throw std::bad_alloc(); + } + + try + { + // Read in + if(!rStream.ReadFullBuffer(pblock, size, 0 /* not interested in bytes read if this fails */)) + { + THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) + } + + // Free the block ready for replacement + FreeBlock(); + } + catch(...) + { + ::free(pblock); + throw; + } + + // store... + ASSERT(mpBuffer == 0); + mpBuffer = pblock; + mSize = size; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::Set(const StreamableMemBlock &) +// Purpose: Set from other block. +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::Set(const StreamableMemBlock &rBlock) +{ + Set(rBlock.mpBuffer, rBlock.mSize); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::~StreamableMemBlock() +// Purpose: Destructor +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +StreamableMemBlock::~StreamableMemBlock() +{ + FreeBlock(); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::FreeBlock() +// Purpose: Protected. Frees block of memory +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::FreeBlock() +{ + if(mpBuffer != 0) + { + ::free(mpBuffer); + } + mpBuffer = 0; + mSize = 0; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::AllocateBlock(int) +// Purpose: Protected. Allocate the block of memory +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::AllocateBlock(int Size) +{ + ASSERT(mpBuffer == 0); + if(Size > 0) + { + mpBuffer = ::malloc(Size); + if(mpBuffer == 0) + { + throw std::bad_alloc(); + } + } + mSize = Size; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::ResizeBlock(int) +// Purpose: Protected. Resizes the allocated block. +// Created: 3/12/03 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::ResizeBlock(int Size) +{ + ASSERT(mpBuffer != 0); + ASSERT(Size > 0); + if(Size > 0) + { + void *pnewBuffer = ::realloc(mpBuffer, Size); + if(pnewBuffer == 0) + { + throw std::bad_alloc(); + } + mpBuffer = pnewBuffer; + } + mSize = Size; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::ReadFromStream(IOStream &, int) +// Purpose: Read the block in from a stream +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::ReadFromStream(IOStream &rStream, int Timeout) +{ + // Get the size of the block + int32_t size_s; + if(!rStream.ReadFullBuffer(&size_s, sizeof(size_s), 0 /* not interested in bytes read if this fails */)) + { + THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) + } + + int size = ntohl(size_s); + + + // Allocate a new block (this way to be exception safe) + char *pblock = (char*)malloc(size); + if(pblock == 0) + { + throw std::bad_alloc(); + } + + try + { + // Read in + if(!rStream.ReadFullBuffer(pblock, size, 0 /* not interested in bytes read if this fails */)) + { + THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead) + } + + // Free the block ready for replacement + FreeBlock(); + } + catch(...) + { + ::free(pblock); + throw; + } + + // store... + ASSERT(mpBuffer == 0); + mpBuffer = pblock; + mSize = size; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::WriteToStream(IOStream &) +// Purpose: Write the block to a stream +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::WriteToStream(IOStream &rStream) const +{ + int32_t sizenbo = htonl(mSize); + // Size + rStream.Write(&sizenbo, sizeof(sizenbo)); + // Buffer + if(mSize > 0) + { + rStream.Write(mpBuffer, mSize); + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::WriteEmptyBlockToStream(IOStream &) +// Purpose: Writes an empty block to a stream. +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void StreamableMemBlock::WriteEmptyBlockToStream(IOStream &rStream) +{ + int32_t sizenbo = htonl(0); + rStream.Write(&sizenbo, sizeof(sizenbo)); +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::GetBuffer() +// Purpose: Get pointer to buffer +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +void *StreamableMemBlock::GetBuffer() const +{ + if(mSize == 0) + { + // Return something which isn't a null pointer + static const int validptr = 0; + return (void*)&validptr; + } + + // return the buffer + ASSERT(mpBuffer != 0); + return mpBuffer; +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: StreamableMemBlock::operator==(const StreamableMemBlock &) +// Purpose: Test for equality of memory blocks +// Created: 2003/09/06 +// +// -------------------------------------------------------------------------- +bool StreamableMemBlock::operator==(const StreamableMemBlock &rCompare) const +{ + if(mSize != rCompare.mSize) return false; + if(mSize == 0 && rCompare.mSize == 0) return true; // without memory comparison! + return ::memcmp(mpBuffer, rCompare.mpBuffer, mSize) == 0; +} + + diff --git a/lib/common/StreamableMemBlock.h b/lib/common/StreamableMemBlock.h new file mode 100755 index 00000000..250c0aea --- /dev/null +++ b/lib/common/StreamableMemBlock.h @@ -0,0 +1,71 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: StreamableMemBlock.h +// Purpose: Memory blocks which can be loaded and saved from streams +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- + +#ifndef STREAMABLEMEMBLOCK__H +#define STREAMABLEMEMBLOCK__H + +class IOStream; + +// -------------------------------------------------------------------------- +// +// Class +// Name: StreamableMemBlock +// Purpose: Memory blocks which can be loaded and saved from streams +// Created: 2003/09/05 +// +// -------------------------------------------------------------------------- +class StreamableMemBlock +{ +public: + StreamableMemBlock(); + StreamableMemBlock(int Size); + StreamableMemBlock(void *pBuffer, int Size); + StreamableMemBlock(const StreamableMemBlock &rToCopy); + ~StreamableMemBlock(); + + void Set(const StreamableMemBlock &rBlock); + void Set(void *pBuffer, int Size); + void Set(IOStream &rStream, int Timeout); + StreamableMemBlock &operator=(const StreamableMemBlock &rBlock) + { + Set(rBlock); + return *this; + } + + void ReadFromStream(IOStream &rStream, int Timeout); + void WriteToStream(IOStream &rStream) const; + + static void WriteEmptyBlockToStream(IOStream &rStream); + + void *GetBuffer() const; + + // Size of block + int GetSize() const {return mSize;} + + // Buffer empty? + bool IsEmpty() const {return mSize == 0;} + + // Clear the contents of the block + void Clear() {FreeBlock();} + + bool operator==(const StreamableMemBlock &rCompare) const; + + void ResizeBlock(int Size); + +protected: // be careful with these! + void AllocateBlock(int Size); + void FreeBlock(); + +private: + void *mpBuffer; + int mSize; +}; + +#endif // STREAMABLEMEMBLOCK__H + diff --git a/lib/common/TemporaryDirectory.h b/lib/common/TemporaryDirectory.h new file mode 100755 index 00000000..62010f79 --- /dev/null +++ b/lib/common/TemporaryDirectory.h @@ -0,0 +1,26 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: TemporaryDirectory.h +// Purpose: Location of temporary directory +// Created: 2003/10/13 +// +// -------------------------------------------------------------------------- + +#ifndef TEMPORARYDIRECTORY__H +#define TEMPORARYDIRECTORY__H + +#include + +#ifdef PLATFORM_STATIC_TEMP_DIRECTORY_NAME + // Prefix name with Box to avoid clashing with OS API names + inline std::string BoxGetTemporaryDirectoryName() + { + return std::string(PLATFORM_STATIC_TEMP_DIRECTORY_NAME); + } +#else + non-static temporary directory names not supported yet +#endif + +#endif // TEMPORARYDIRECTORY__H + diff --git a/lib/common/Test.h b/lib/common/Test.h new file mode 100755 index 00000000..bd69cd5a --- /dev/null +++ b/lib/common/Test.h @@ -0,0 +1,165 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Test.h +// Purpose: Useful stuff for tests +// Created: 2003/07/11 +// +// -------------------------------------------------------------------------- + +#ifndef TEST__H +#define TEST__H + +#include +#include +#include +#include +#include + +#include + +extern int failures; + +#define TEST_FAIL_WITH_MESSAGE(msg) {failures++; printf("FAILURE: " msg " at " __FILE__ "(%d)\n", __LINE__);} +#define TEST_ABORT_WITH_MESSAGE(msg) {failures++; printf("FAILURE: " msg " at " __FILE__ "(%d)\n", __LINE__); return 1;} + +#define TEST_THAT(condition) {if(!(condition)) TEST_FAIL_WITH_MESSAGE("Condition [" #condition "] failed")} +#define TEST_THAT_ABORTONFAIL(condition) {if(!(condition)) TEST_ABORT_WITH_MESSAGE("Condition [" #condition "] failed")} + +// NOTE: The 0- bit it to allow this to work with stuff which has negative constants for flags (eg ConnectionException) +#define TEST_CHECK_THROWS(statement, excepttype, subtype) \ + { \ + bool didthrow = false; \ + try \ + { \ + statement; \ + } \ + catch(excepttype &e) \ + { \ + if(e.GetSubType() != ((unsigned int)excepttype::subtype) \ + && e.GetSubType() != (unsigned int)(0-excepttype::subtype)) \ + { \ + throw; \ + } \ + didthrow = true; \ + } \ + catch(...) \ + { \ + throw; \ + } \ + if(!didthrow) \ + { \ + TEST_FAIL_WITH_MESSAGE("Didn't throw exception " #excepttype "(" #subtype ")") \ + } \ + } + +inline bool TestFileExists(const char *Filename) +{ + struct stat st; + return ::stat(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0; +} + +inline bool TestDirExists(const char *Filename) +{ + struct stat st; + return ::stat(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == S_IFDIR; +} + +// -1 if doesn't exist +inline int TestGetFileSize(const char *Filename) +{ + struct stat st; + if(::stat(Filename, &st) == 0) + { + return st.st_size; + } + return -1; +} + +inline int LaunchServer(const char *CommandLine, const char *pidFile) +{ + if(::system(CommandLine) != 0) + { + printf("Server: %s\n", CommandLine); + TEST_FAIL_WITH_MESSAGE("Couldn't start server"); + return -1; + } + // time for it to start up + ::sleep(1); + + // read pid file + if(!TestFileExists(pidFile)) + { + printf("Server: %s\n", CommandLine); + TEST_FAIL_WITH_MESSAGE("Server didn't save PID file"); + return -1; + } + + FILE *f = fopen(pidFile, "r"); + int pid = -1; + if(f == NULL || fscanf(f, "%d", &pid) != 1) + { + printf("Server: %s (pidfile %s)\n", CommandLine, pidFile); + TEST_FAIL_WITH_MESSAGE("Couldn't read PID file"); + return -1; + } + fclose(f); + + return pid; +} + +inline bool ServerIsAlive(int pid) +{ + if(pid == 0) return false; + return ::kill(pid, 0) != -1; +} + +inline bool HUPServer(int pid) +{ + if(pid == 0) return false; + return ::kill(pid, SIGHUP) != -1; +} + +inline bool KillServer(int pid) +{ + if(pid == 0 || pid == -1) return false; + bool KilledOK = ::kill(pid, SIGTERM) != -1; + TEST_THAT(KilledOK); + ::sleep(1); + return !ServerIsAlive(pid); +} + +inline void TestRemoteProcessMemLeaks(const char *filename) +{ +#ifdef BOX_MEMORY_LEAK_TESTING + // Does the file exist? + if(!TestFileExists(filename)) + { + ++failures; + printf("FAILURE: MemLeak report not available (file %s)\n", filename); + } + else + { + // Is it empty? + if(TestGetFileSize(filename) > 0) + { + ++failures; + printf("FAILURE: Memory leaks found in other process (file %s)\n==========\n", filename); + FILE *f = fopen(filename, "r"); + char line[512]; + while(::fgets(line, sizeof(line), f) != 0) + { + printf("%s", line); + } + fclose(f); + printf("==========\n"); + } + + // Delete it + ::unlink(filename); + } +#endif +} + +#endif // TEST__H + diff --git a/lib/common/UnixUser.cpp b/lib/common/UnixUser.cpp new file mode 100755 index 00000000..7a60b263 --- /dev/null +++ b/lib/common/UnixUser.cpp @@ -0,0 +1,121 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: UnixUser.cpp +// Purpose: Interface for managing the UNIX user of the current process +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include + +#include "UnixUser.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + + +// -------------------------------------------------------------------------- +// +// Function +// Name: UnixUser::UnixUser(const char *) +// Purpose: Constructor, initialises to info of given username +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- +UnixUser::UnixUser(const char *Username) + : mUID(0), + mGID(0), + mRevertOnDestruction(false) +{ + // Get password info + struct passwd *pwd = ::getpwnam(Username); + if(pwd == 0) + { + THROW_EXCEPTION(CommonException, CouldNotLookUpUsername) + } + + // Store UID and GID + mUID = pwd->pw_uid; + mGID = pwd->pw_gid; +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: UnixUser::UnixUser(uid_t, gid_t) +// Purpose: Construct from given UNIX user ID and group ID +// Created: 15/3/04 +// +// -------------------------------------------------------------------------- +UnixUser::UnixUser(uid_t UID, gid_t GID) + : mUID(UID), + mGID(GID), + mRevertOnDestruction(false) +{ +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: UnixUser::~UnixUser() +// Purpose: Destructor -- reverts to previous user if the change wasn't perminant +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- +UnixUser::~UnixUser() +{ + if(mRevertOnDestruction) + { + // Revert to "real" user and group id of the process + if(::setegid(::getgid()) != 0 + || ::seteuid(::getuid()) != 0) + { + THROW_EXCEPTION(CommonException, CouldNotRestoreProcessUser) + } + } +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: UnixUser::ChangeProcessUser(bool) +// Purpose: Change the process user and group ID to the user. If Temporary == true +// the process username will be changed back when the object is destructed. +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- +void UnixUser::ChangeProcessUser(bool Temporary) +{ + if(Temporary) + { + // Change temporarily (change effective only) + if(::setegid(mGID) != 0 + || ::seteuid(mUID) != 0) + { + THROW_EXCEPTION(CommonException, CouldNotChangeProcessUser) + } + + // Mark for change on destruction + mRevertOnDestruction = true; + } + else + { + // Change perminantely (change all UIDs and GIDs) + if(::setgid(mGID) != 0 + || ::setuid(mUID) != 0) + { + THROW_EXCEPTION(CommonException, CouldNotChangeProcessUser) + } + } +} + + + + diff --git a/lib/common/UnixUser.h b/lib/common/UnixUser.h new file mode 100755 index 00000000..c895eb2a --- /dev/null +++ b/lib/common/UnixUser.h @@ -0,0 +1,37 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: UnixUser.h +// Purpose: Interface for managing the UNIX user of the current process +// Created: 21/1/04 +// +// -------------------------------------------------------------------------- + +#ifndef UNIXUSER__H +#define UNIXUSER__H + +class UnixUser +{ +public: + UnixUser(const char *Username); + UnixUser(uid_t UID, gid_t GID); + ~UnixUser(); +private: + // no copying allowed + UnixUser(const UnixUser &); + UnixUser &operator=(const UnixUser &); +public: + + void ChangeProcessUser(bool Temporary = false); + + uid_t GetUID() {return mUID;} + gid_t GetGID() {return mGID;} + +private: + uid_t mUID; + gid_t mGID; + bool mRevertOnDestruction; +}; + +#endif // UNIXUSER__H + diff --git a/lib/common/Utils.cpp b/lib/common/Utils.cpp new file mode 100755 index 00000000..b4d3144b --- /dev/null +++ b/lib/common/Utils.cpp @@ -0,0 +1,159 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Utils.cpp +// Purpose: Utility function +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#ifdef SHOW_BACKTRACE_ON_EXCEPTION + #include + #include +#endif + +#include "Utils.h" +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: SplitString(const std::string &, char, std::vector &) +// Purpose: Splits a string at a given character +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- +void SplitString(const std::string &String, char SplitOn, std::vector &rOutput) +{ + // Split it up. + std::string::size_type b = 0; + std::string::size_type e = 0; + while(e = String.find_first_of(SplitOn, b), e != String.npos) + { + // Get this string + unsigned int len = e - b; + if(len >= 1) + { + rOutput.push_back(String.substr(b, len)); + } + b = e + 1; + } + // Last string + if(b < String.size()) + { + rOutput.push_back(String.substr(b)); + } +/*#ifndef NDEBUG + TRACE2("Splitting string '%s' on %c\n", String.c_str(), SplitOn); + for(unsigned int l = 0; l < rOutput.size(); ++l) + { + TRACE2("%d = '%s'\n", l, rOutput[l].c_str()); + } +#endif*/ +} + +#ifdef SHOW_BACKTRACE_ON_EXCEPTION +void DumpStackBacktrace() +{ + void *array[10]; + size_t size; + char **strings; + size_t i; + + size = backtrace (array, 10); + strings = backtrace_symbols (array, size); + + printf ("Obtained %zd stack frames.\n", size); + + for(i = 0; i < size; i++) + printf("%s\n", strings[i]); + + free (strings); +} +#endif + + + +// -------------------------------------------------------------------------- +// +// Function +// Name: FileExists(const char *) +// Purpose: Does a file exist? +// Created: 20/11/03 +// +// -------------------------------------------------------------------------- +bool FileExists(const char *Filename, int64_t *pFileSize, bool TreatLinksAsNotExisting) +{ + struct stat st; + if(::stat(Filename, &st) != 0) + { + if(errno == ENOENT) + { + return false; + } + else + { + THROW_EXCEPTION(CommonException, OSFileError); + } + } + + // is it a file? + if((st.st_mode & S_IFDIR) == 0) + { + if(TreatLinksAsNotExisting && ((st.st_mode & S_IFLNK) != 0)) + { + return false; + } + + // Yes. Tell caller the size? + if(pFileSize != 0) + { + *pFileSize = st.st_size; + } + + return true; + } + else + { + return false; + } +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: ObjectExists(const char *) +// Purpose: Does a object exist, and if so, is it a file or a directory? +// Created: 23/11/03 +// +// -------------------------------------------------------------------------- +int ObjectExists(const char *Filename) +{ + struct stat st; + if(::stat(Filename, &st) != 0) + { + if(errno == ENOENT) + { + return ObjectExists_NoObject; + } + else + { + THROW_EXCEPTION(CommonException, OSFileError); + } + } + + // is it a file or a dir? + return ((st.st_mode & S_IFDIR) == 0)?ObjectExists_File:ObjectExists_Dir; +} + + + + diff --git a/lib/common/Utils.h b/lib/common/Utils.h new file mode 100755 index 00000000..5da84d9a --- /dev/null +++ b/lib/common/Utils.h @@ -0,0 +1,36 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: Utils.h +// Purpose: Utility function +// Created: 2003/07/31 +// +// -------------------------------------------------------------------------- + +#ifndef UTILS__H +#define UTILS__H + +#include +#include + +#include "MemLeakFindOn.h" + +void SplitString(const std::string &String, char SplitOn, std::vector &rOutput); + +#ifdef SHOW_BACKTRACE_ON_EXCEPTION + void DumpStackBacktrace(); +#endif + +bool FileExists(const char *Filename, int64_t *pFileSize = 0, bool TreatLinksAsNotExisting = false); + +enum +{ + ObjectExists_NoObject = 0, + ObjectExists_File = 1, + ObjectExists_Dir = 2 +}; +int ObjectExists(const char *Filename); + +#include "MemLeakFindOff.h" + +#endif // UTILS__H diff --git a/lib/common/WaitForEvent.cpp b/lib/common/WaitForEvent.cpp new file mode 100644 index 00000000..e9ee58d2 --- /dev/null +++ b/lib/common/WaitForEvent.cpp @@ -0,0 +1,194 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: WaitForEvent.cpp +// Purpose: Generic waiting for events, using an efficient method (platform dependent) +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- + +#include "Box.h" + +#include +#include +#include + +#include "WaitForEvent.h" + +#include "MemLeakFindOn.h" + +// -------------------------------------------------------------------------- +// +// Function +// Name: WaitForEvent::WaitForEvent() +// Purpose: Constructor +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED +WaitForEvent::WaitForEvent(int Timeout) + : mKQueue(::kqueue()), + mpTimeout(0) +{ + if(mKQueue == -1) + { + THROW_EXCEPTION(CommonException, CouldNotCreateKQueue) + } + + // Set the choosen timeout + SetTimeout(Timeout); +} +#else +WaitForEvent::WaitForEvent(int Timeout) + : mTimeout(Timeout), + mpPollInfo(0) +{ +} +#endif + +// -------------------------------------------------------------------------- +// +// Function +// Name: WaitForEvent::~WaitForEvent() +// Purpose: Destructor +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- +WaitForEvent::~WaitForEvent() +{ +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + ::close(mKQueue); + mKQueue = -1; +#else + if(mpPollInfo != 0) + { + ::free(mpPollInfo); + mpPollInfo = 0; + } +#endif +} + + +// -------------------------------------------------------------------------- +// +// Function +// Name: WaitForEvent::SetTimeout +// Purpose: Sets the timeout for future wait calls +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- +void WaitForEvent::SetTimeout(int Timeout) +{ +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + // Generate timeout + if(Timeout != TimeoutInfinite) + { + mTimeout.tv_sec = Timeout / 1000; + mTimeout.tv_nsec = (Timeout % 1000) * 1000000; + } + + // Infinite or not? + mpTimeout = (Timeout != TimeoutInfinite)?(&mTimeout):(NULL); +#else + mTimeout = Timeout; +#endif +} + +// -------------------------------------------------------------------------- +// +// Function +// Name: WaitForEvent::Wait(int) +// Purpose: Wait for an event to take place. Returns a pointer to the object +// which has been signalled, or returns 0 for the timeout condition. +// Timeout specified in milliseconds. +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- +void *WaitForEvent::Wait() +{ +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + // Event return structure + struct kevent e; + ::memset(&e, 0, sizeof(e)); + + switch(::kevent(mKQueue, NULL, 0, &e, 1, mpTimeout)) + { + case 0: + // Timeout + return 0; + break; + + case 1: + // Event happened! + return e.udata; + break; + + default: + // Interrupted system calls aren't an error, just equivalent to a timeout + if(errno != EINTR) + { + THROW_EXCEPTION(CommonException, KEventErrorWait) + } + return 0; + break; + } +#else + // Use poll() instead. + // Need to build the structures? + if(mpPollInfo == 0) + { + // Yes... + mpPollInfo = (struct pollfd *)::malloc((sizeof(struct pollfd) * mItems.size()) + 4); + if(mpPollInfo == 0) + { + throw std::bad_alloc(); + } + + // Build... + for(unsigned int l = 0; l < mItems.size(); ++l) + { + mpPollInfo[l].fd = mItems[l].fd; + mpPollInfo[l].events = mItems[l].events; + mpPollInfo[l].revents = 0; + } + } + + // Make sure everything is reset (don't really have to do this, but don't trust the OS) + for(unsigned int l = 0; l < mItems.size(); ++l) + { + mpPollInfo[l].revents = 0; + } + + // Poll! + switch(::poll(mpPollInfo, mItems.size(), mTimeout)) + { + case -1: + // Interrupted system calls aren't an error, just equivalent to a timeout + if(errno != EINTR) + { + THROW_EXCEPTION(CommonException, KEventErrorWait) + } + return 0; + break; + case 0: // timed out + return 0; + break; + default: // got some thing... + // control flows on... + break; + } + + // Find the item which was ready + for(unsigned int s = 0; s < mItems.size(); ++s) + { + if(mpPollInfo[s].revents & POLLIN) + { + return mItems[s].item; + break; + } + } +#endif + + return 0; +} + diff --git a/lib/common/WaitForEvent.h b/lib/common/WaitForEvent.h new file mode 100644 index 00000000..b8f79da6 --- /dev/null +++ b/lib/common/WaitForEvent.h @@ -0,0 +1,146 @@ +// -------------------------------------------------------------------------- +// +// File +// Name: WaitForEvent.h +// Purpose: Generic waiting for events, using an efficient method (platform dependent) +// Created: 9/3/04 +// +// -------------------------------------------------------------------------- + +#ifndef WAITFOREVENT__H +#define WAITFOREVENT__H + +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + #include + #include +#else + #include + #include +#endif + +#include "CommonException.h" + +#include "MemLeakFindOn.h" + +class WaitForEvent +{ +public: + WaitForEvent(int Timeout = TimeoutInfinite); + ~WaitForEvent(); +private: + // No copying. + WaitForEvent(const WaitForEvent &); + WaitForEvent &operator=(const WaitForEvent &); +public: + + enum + { + TimeoutInfinite = -1 + }; + + void SetTimeout(int Timeout = TimeoutInfinite); + + void *Wait(); + +#ifdef PLATFORM_KQUEUE_NOT_SUPPORTED + typedef struct + { + int fd; + short events; + void *item; + } ItemInfo; +#endif + + // -------------------------------------------------------------------------- + // + // Function + // Name: WaitForEvent::Add(const Type &, int) + // Purpose: Adds an event to the list of items to wait on. The flags are passed to the object. + // Created: 9/3/04 + // + // -------------------------------------------------------------------------- + template + void Add(const T *pItem, int Flags = 0) + { + ASSERT(pItem != 0); +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + struct kevent e; + pItem->FillInKEvent(e, Flags); + // Fill in extra flags to say what to do + e.flags |= EV_ADD; + e.udata = (void*)pItem; + if(::kevent(mKQueue, &e, 1, NULL, 0, NULL) == -1) + { + THROW_EXCEPTION(CommonException, KEventErrorAdd) + } +#else + // Add item + ItemInfo i; + pItem->FillInPoll(i.fd, i.events, Flags); + i.item = (void*)pItem; + mItems.push_back(i); + // Delete any pre-prepared poll info, as it's now out of date + if(mpPollInfo != 0) + { + ::free(mpPollInfo); + mpPollInfo = 0; + } +#endif + } + + // -------------------------------------------------------------------------- + // + // Function + // Name: WaitForEvent::Remove(const Type &, int) + // Purpose: Removes an event from the list of items to wait on. The flags are passed to the object. + // Created: 9/3/04 + // + // -------------------------------------------------------------------------- + template + void Remove(const T *pItem, int Flags = 0) + { + ASSERT(pItem != 0); +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + struct kevent e; + pItem->FillInKEvent(e, Flags); + // Fill in extra flags to say what to do + e.flags |= EV_DELETE; + e.udata = (void*)pItem; + if(::kevent(mKQueue, &e, 1, NULL, 0, NULL) == -1) + { + THROW_EXCEPTION(CommonException, KEventErrorRemove) + } +#else + if(mpPollInfo != 0) + { + ::free(mpPollInfo); + mpPollInfo = 0; + } + for(std::vector::iterator i(mItems.begin()); i != mItems.end(); ++i) + { + if(i->item == pItem) + { + mItems.erase(i); + return; + } + } +#endif + } + +private: +#ifndef PLATFORM_KQUEUE_NOT_SUPPORTED + int mKQueue; + struct timespec mTimeout; + struct timespec *mpTimeout; +#else + int mTimeout; + std::vector mItems; + struct pollfd *mpPollInfo; +#endif +}; + +#include "MemLeakFindOff.h" + +#endif // WAITFOREVENT__H + + diff --git a/lib/common/makeexception.pl b/lib/common/makeexception.pl new file mode 100755 index 00000000..c0388286 --- /dev/null +++ b/lib/common/makeexception.pl @@ -0,0 +1,277 @@ +#!/usr/bin/perl + +# global exception list file +my $global_list = '../../ExceptionCodes.txt'; + + +my @exception; +my @exception_desc; +my $class; +my $class_number; + +# read the description! + +open EXCEPTION_DESC,$ARGV[0] or die "Can't open $ARGV[0]"; + +while() +{ + chomp; s/\A\s+//; s/#.+\Z//; s/\s+\Z//; s/\s+/ /g; + next unless m/\S/; + + if(m/\AEXCEPTION\s+(.+)\s+(\d+)\Z/) + { + $class = $1; + $class_number = $2; + } + else + { + my ($name,$number,$description) = split /\s+/,$_,3; + if($name eq '' || $number =~ m/\D/) + { + die "Bad line '$_'"; + } + if($exception[$number] ne '') + { + die "Duplicate exception number $number"; + } + $exception[$number] = $name; + $exception_desc[$number] = $description; + } +} + +die "Exception class and number not specified" unless $class ne '' && $class_number ne ''; + +close EXCEPTION_DESC; + +# write the code +print "Generating $class exception...\n"; + +open CPP,">autogen_${class}Exception.cpp" or die "Can't open cpp file for writing"; +open H,">autogen_${class}Exception.h" or die "Can't open h file for writing"; + +# write header file +my $guardname = uc 'AUTOGEN_'.$class.'EXCEPTION_H'; +print H <<__E; + +// Auto-generated file -- do not edit + +#ifndef $guardname +#define $guardname + +#include "BoxException.h" + +// -------------------------------------------------------------------------- +// +// Class +// Name: ${class}Exception +// Purpose: Exception +// Created: autogen +// +// -------------------------------------------------------------------------- +class ${class}Exception : public BoxException +{ +public: + ${class}Exception(unsigned int SubType) + : mSubType(SubType) + { + } + + ${class}Exception(const ${class}Exception &rToCopy) + : mSubType(rToCopy.mSubType) + { + } + + ~${class}Exception() throw () + { + } + + enum + { + ExceptionType = $class_number + }; + + enum + { +__E + +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + print H "\t\t".$exception[$e].' = '.$e.(($e==$#exception)?'':',')."\n" + } +} + +print H <<__E; + }; + + virtual unsigned int GetType() const throw(); + virtual unsigned int GetSubType() const throw(); + virtual const char *what() const throw(); + +private: + unsigned int mSubType; +}; + +#endif // $guardname +__E + +# ----------------------------------------------------------------------------------------------------------- + +print CPP <<__E; + +// Auto-generated file -- do not edit + +#include "Box.h" +#include "autogen_${class}Exception.h" + +#include "MemLeakFindOn.h" + +#ifdef EXCEPTION_CODENAMES_EXTENDED + #ifdef EXCEPTION_CODENAMES_EXTENDED_WITH_DESCRIPTION +static const char *whats[] = { +__E + +my $last_seen = -1; +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + for(my $s = $last_seen + 1; $s < $e; $s++) + { + print CPP "\t\"UNUSED\",\n" + } + my $ext = ($exception_desc[$e] ne '')?" ($exception_desc[$e])":''; + print CPP "\t\"${class} ".$exception[$e].$ext.'"'.(($e==$#exception)?'':',')."\n"; + $last_seen = $e; + } +} + +print CPP <<__E; +}; + #else +static const char *whats[] = { +__E + +$last_seen = -1; +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + for(my $s = $last_seen + 1; $s < $e; $s++) + { + print CPP "\t\"UNUSED\",\n" + } + print CPP "\t\"${class} ".$exception[$e].'"'.(($e==$#exception)?'':',')."\n"; + $last_seen = $e; + } +} + +print CPP <<__E; +}; + #endif +#endif + +unsigned int ${class}Exception::GetType() const throw() +{ + return ${class}Exception::ExceptionType; +} + +unsigned int ${class}Exception::GetSubType() const throw() +{ + return mSubType; +} + +const char *${class}Exception::what() const throw() +{ +#ifdef EXCEPTION_CODENAMES_EXTENDED + if(mSubType < 0 || mSubType > (sizeof(whats) / sizeof(whats[0]))) + { + return "${class}"; + } + return whats[mSubType]; +#else + return "${class}"; +#endif +} + +__E + +close H; +close CPP; + +# update the global exception list +my $list_before; +my $list_after; +my $is_after = 0; +if(open CURRENT,$global_list) +{ + while() + { + next if m/\A#/; + + if(m/\AEXCEPTION TYPE (\w+) (\d+)/) + { + # check that the number isn't being reused + if($2 == $class_number && $1 ne $class) + { + die "Class number $class_number is being used by $class and $1 -- correct this.\n"; + } + if($2 > $class_number) + { + # This class comes after the current one (ensures numerical ordering) + $is_after = 1; + } + if($1 eq $class) + { + # skip this entry + while() + { + last if m/\AEND TYPE/; + } + $_ = ''; + } + } + + if($is_after) + { + $list_after .= $_; + } + else + { + $list_before .= $_; + } + } + + close CURRENT; +} + +open GLOBAL,">$global_list" or die "Can't open global exception code listing for writing"; + +print GLOBAL <<__E; +# +# automatically generated file, do not edit. +# +# This file lists all the exception codes used by the system. +# Use to look up more detailed descriptions of meanings of errors. +# +__E + +print GLOBAL $list_before; + +print GLOBAL "EXCEPTION TYPE $class $class_number\n"; +for(my $e = 0; $e <= $#exception; $e++) +{ + if($exception[$e] ne '') + { + my $ext = ($exception_desc[$e] ne '')?" - $exception_desc[$e]":''; + print GLOBAL "($class_number/$e) - ${class} ".$exception[$e].$ext."\n"; + } +} +print GLOBAL "END TYPE\n"; + +print GLOBAL $list_after; + +close GLOBAL; + + -- cgit v1.2.3