libwibble-1.1/0000755000175000017500000000000012231503612012621 5ustar enricoenricolibwibble-1.1/CMakeLists.txt0000644000175000017500000000030612231454443015367 0ustar enricoenricoinclude( FindDoxygen ) set( VERSION "1.1" ) option( HAVE_TUT ON ) if( NOT WIN32 ) add_definitions( -DPOSIX ) endif() add_custom_target( unit ) add_subdirectory( wibble ) add_subdirectory( doc ) libwibble-1.1/fix-perms0000644000175000017500000000007511075401711014462 0ustar enricoenrico#!/bin/sh chmod +x debian/rules wibble/tests/libwibble-check libwibble-1.1/README0000644000175000017500000000036511075401713013511 0ustar enricoenricoTo configure libwibble: mkdir build cd build cmake .. You can use "ccmake .." instead of "cmake .." to get an interactive interface for selecting configuration parameters. To build libwibble: make To install libwibble: make install libwibble-1.1/wibble.spec0000644000175000017500000000244711075401710014751 0ustar enricoenricoSummary: Library of various useful C++ code Name: libwibble-dev Version: 0.1.11 Release: 1 License: BSD Group: Development/Libraries URL: http://packages.qa.debian.org/libwibble Source0: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot BuildRequires: cmake, doxygen, libtool, boost-devel Requires: boost-devel %description libwibble collects the foundation code that has been used over time in various C++ projects by Peter Rockai and Enrico Zini, so that it can be maintained properly and in a single place. . The library contains: * an exception hierarchy; * various useful mixin classes; * shortcuts for set operations; * a commandline parser that supports cvs-style subcommands; * improved macros for tut unit testing; * a non-intrusive polimorphic envelope. %prep %setup -q %build %cmake -DHAVE_TUT=ON make make check %install [ "%{buildroot}" != / ] && rm -rf "%{buildroot}" make install DESTDIR="%{buildroot}" %clean [ "%{buildroot}" != / ] && rm -rf "%{buildroot}" %files %defattr(-,root,root,-) /usr/lib /usr/include /usr/lib/pkgconfig/libwibble* /usr/share/aclocal/libwibble*.m4 #%doc /usr/share/doc/libwibble-dev %post /sbin/ldconfig %postun /sbin/ldconfig %changelog * Wed Sep 19 2007 Enrico Zini - Initial build. libwibble-1.1/wibble/0000755000175000017500000000000012231503400014060 5ustar enricoenricolibwibble-1.1/wibble/CMakeLists.txt0000644000175000017500000000734012231503400016624 0ustar enricoenricoproject( wibble ) include( test.cmake ) include( CheckCXXSourceCompiles ) check_cxx_source_compiles( "#include int main() { struct dirent *d; (void)d->d_type; return 0; }" HAVE_STRUCT_DIRENT_D_TYPE ) configure_file( ${wibble_SOURCE_DIR}/config.h.cmake-in ${wibble_BINARY_DIR}/config.h ) add_definitions( -DHAVE_CONFIG_H -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 ) # glob through source files/headers file( GLOB WSRC_TOP *.cpp ) file( GLOB WSRC_CMDL commandline/*.cpp ) file( GLOB WSRC_SYS sys/*.cpp ) file( GLOB WSRC_LOG log/*.cpp ) file( GLOB WSRC_TEXT text/*.cpp ) file( GLOB WSRC_GRCAL grcal/*.cpp ) file( GLOB WSRC_STREAM stream/*.cpp ) file( GLOB WSRC_NET net/*.cpp ) file( GLOB H_TOP *.h ) file( GLOB H_CMDL commandline/*.h ) file( GLOB H_SYS sys/*.h ) file( GLOB H_LOG log/*.h ) file( GLOB H_TEXT text/*.h ) file( GLOB H_GRCAL grcal/*.h ) file( GLOB H_STREAM stream/*.h ) file( GLOB H_NET net/*.h ) file( GLOB H_TESTS tests/*.h ) file( GLOB TCC_TOP *.tcc ) file( GLOB testh *.test.h commandline/*.test.h sys/*.test.h log/*.test.h text/*.test.h grcal/*.test.h stream/*.test.h net/*.test.h ) set( WSRC ${WSRC_TOP} ${WSRC_CMDL} ${WSRC_SYS} ${WSRC_LOG} ${WSRC_TEXT} ${WSRC_GRCAL} ${WSRC_STREAM} ${WSRC_NET}) # on some mingw32, regex.h is not on the default include path find_path( RX_PATH regex.h ) if( RX_PATH ) include_directories( ${RX_PATH} ) endif() # libwibble.a if( NOT WIN32 ) set_source_files_properties( ${WSRC} COMPILE_FLAGS -fPIC ) endif( NOT WIN32 ) include_directories( ${wibble_SOURCE_DIR}/.. ${wibble_BINARY_DIR}/.. ) add_definitions( ${OPT_FLAGS} ) add_library( wibble STATIC ${WSRC} ) if( WIN32 ) target_link_libraries( wibble wsock32 psapi regex ) else( WIN32 ) target_link_libraries( wibble pthread ) endif( WIN32 ) # make check wibble_add_test( wibble-test ${testh} ) target_link_libraries( wibble-test wibble ) wibble_check_target( wibble-test ) set( prefix "${CMAKE_INSTALL_PREFIX}" ) set( exec_prefix "${prefix}/bin" ) if (NOT DEFINED libdir) set( libdir "${prefix}/lib" ) endif() set( includedir "${prefix}/include" ) # cmake-time configuration configure_file( ${wibble_SOURCE_DIR}/libwibble.pc.in ${wibble_BINARY_DIR}/libwibble.pc @ONLY ) # make install install( TARGETS wibble DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE} COMPONENT wibble_dev ) if( NOT WIN32 ) install( FILES ${wibble_BINARY_DIR}/libwibble.pc DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig COMPONENT wibble_dev ) install( FILES libwibble.m4 DESTINATION share/aclocal COMPONENT wibble_dev ) install( FILES wibble-test-genrunner.1 DESTINATION share/man/man1 COMPONENT wibble_dev ) endif( NOT WIN32 ) install( FILES ${H_TOP} DESTINATION include/wibble COMPONENT wibble_dev ) install( FILES ${TCC_TOP} DESTINATION include/wibble COMPONENT wibble_dev ) install( FILES ${H_CMDL} DESTINATION include/wibble/commandline COMPONENT wibble_dev ) install( FILES ${H_SYS} DESTINATION include/wibble/sys COMPONENT wibble_dev ) install( FILES ${H_LOG} DESTINATION include/wibble/log COMPONENT wibble_dev ) install( FILES ${H_TEXT} DESTINATION include/wibble/text COMPONENT wibble_dev ) install( FILES ${H_GRCAL} DESTINATION include/wibble/grcal COMPONENT wibble_dev ) install( FILES ${H_STREAM} DESTINATION include/wibble/stream COMPONENT wibble_dev ) install( FILES ${H_NET} DESTINATION include/wibble/net COMPONENT wibble_dev ) install( FILES ${H_TESTS} DESTINATION include/wibble/tests COMPONENT wibble_dev ) if( NOT WIN32 ) install( FILES test.cmake DESTINATION share/wibble COMPONENT wibble_dev ) install( FILES test-genrunner.pl RENAME wibble-test-genrunner DESTINATION bin COMPONENT wibble_dev PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif( NOT WIN32 ) libwibble-1.1/wibble/singleton.test.h0000644000175000017500000000120411075401766017227 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include namespace { using namespace std; using namespace wibble; struct TestSingleton { Test simple() { Singleton container = singleton(5); assert_eq(container.size(), 1u); Singleton::iterator i = container.begin(); assert(!(i == container.end())); assert(i != container.end()); assert_eq(*i, 5); ++i; assert(i == container.end()); assert(!(i != container.end())); } }; } libwibble-1.1/wibble/empty.h0000644000175000017500000000411412231005104015365 0ustar enricoenrico// -*- C++ -*- #ifndef WIBBLE_EMPTY_H #define WIBBLE_EMPTY_H /* * Degenerated container to hold a single value * * Copyright (C) 2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace wibble { template class Empty { public: typedef T value_type; class const_iterator : public std::iterator { public: const T& operator*() const { return *reinterpret_cast< T* >( 0 ); } const T* operator->() const { return 0; } const_iterator& operator++() { return *this; } bool operator==(const const_iterator&) const { return true; } bool operator!=(const const_iterator&) const { return false; } }; class iterator : public std::iterator { public: T& operator*() const { return *reinterpret_cast< T* >( 0 ); } T* operator->() const { return 0; } iterator& operator++() { return *this; } bool operator==(const iterator&) const { return true; } bool operator!=(const iterator&) const { return false; } }; bool empty() const { return true; } size_t size() const { return 0; } iterator begin() { return iterator(); } iterator end() { return iterator(); } const_iterator begin() const { return const_iterator(); } const_iterator end() const { return const_iterator(); } }; } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/iterator.h0000644000175000017500000000673211075402054016102 0ustar enricoenrico/** -*- C++ -*- @file wibble/iterator.h @author Peter Rockai */ #include #include #ifndef WIBBLE_ITERATOR_H #define WIBBLE_ITERATOR_H namespace wibble { typedef bool SortabilityTag; template< typename T, typename I > struct IteratorTraits { typedef SortabilityTag Unsorted; }; template< typename T > struct IteratorTraits< T, typename std::set< T >::iterator > { typedef SortabilityTag Sorted; }; template< typename T > struct IteratorTraits< T, typename std::multiset< T >::iterator > { typedef SortabilityTag Sorted; }; template< typename T > struct IteratorInterface { virtual T current() const = 0; virtual void advance() = 0; virtual ~IteratorInterface() {} }; template< typename T > struct IteratorProxy { IteratorProxy( T _x ) : x( _x ) {} T x; const T *operator->() const { return &x; } }; template< typename T, typename W > struct IteratorMorph : Morph< IteratorMorph< T, W >, W, IteratorInterface< T > > { typedef W Wrapped; IteratorMorph() {} IteratorMorph( const Wrapped &w ) : Morph< IteratorMorph, Wrapped, IteratorInterface< T > >( w ) {} virtual void advance() { this->wrapped().advance(); } virtual T current() const { return this->wrapped().current(); } }; template< typename T, typename Self > struct IteratorMixin : mixin::Comparable< Self > { Self &self() { return *static_cast< const Self * >( this ); } const Self &self() const { return *static_cast< const Self * >( this ); } typedef T ElementType; typedef std::forward_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T *pointer; typedef T &reference; typedef const T &const_reference; IteratorProxy< T > operator->() const { return IteratorProxy< T >(self().current()); } Self next() const { Self n( self() ); n.advance(); return n; } T operator*() const { return self().current(); } Self &operator++() { self().advance(); return self(); } Self operator++(int) { Self tmp = self(); self().advance(); return tmp; } }; template< typename T, typename I > typename IteratorTraits< T, I >::Unsorted isSortedT( I, I ) { return false; } template< typename T, typename I > typename IteratorTraits< T, I >::Sorted isSortedT( I, I ) { return true; } template< typename T > struct Iterator : Amorph< Iterator< T >, IteratorInterface< T >, 0 >, IteratorMixin< T, Iterator< T > > { typedef Amorph< Iterator< T >, IteratorInterface< T >, 0 > Super; typedef T ElementType; Iterator( const IteratorInterface< T > &i ) : Super( i ) {} Iterator() {} bool operator<=( const Iterator &i ) const { return leq( i ); } T current() const { return this->implInterface()->current(); } virtual void advance() { this->implInterface()->advance(); } template< typename C > operator Iterator< C >(); }; template< typename It > struct StlIterator : IteratorMixin< typename It::value_type, StlIterator< It > > { typedef typename std::iterator_traits< It >::value_type Value; StlIterator( It i ) : m_iterator( i ) {} virtual void advance() { ++m_iterator; } virtual Value current() const { return *m_iterator; } bool operator==( const StlIterator< It > &o ) { return m_iterator == o.m_iterator; } protected: It m_iterator; }; template< typename I > Iterator< typename I::value_type > iterator( I i ) { return StlIterator< I >( i ); } } #endif libwibble-1.1/wibble/sfinae.h0000644000175000017500000000462112231005104015477 0ustar enricoenrico// -*- C++ -*- Substitution Failure Is Not An Error #ifndef WIBBLE_SFINAE_H #define WIBBLE_SFINAE_H namespace wibble { struct Unit { bool operator<( Unit ) const { return false; } bool operator==( Unit ) const { return true; } }; struct TTrue { static const bool value = true; }; struct TFalse { static const bool value = false; }; // small SFINAE utilities, we probably prefer to avoid full weight of boost here template< typename A, typename B > struct TSame { static const bool value = false; }; template< typename A > struct TSame< A, A > { static const bool value = true; }; template< bool, bool, bool = true, bool = true, bool = true > struct TAndC { static const bool value = false; }; template<> struct TAndC< true, true, true, true, true > { static const bool value = true; }; template< typename A, typename B, typename C = TTrue, typename D = TTrue, typename E = TTrue > struct TAnd : TAndC< A::value, B::value, C::value, D::value, E::value > {}; template< bool, bool, bool = false, bool = false, bool = false > struct TOrC { static const bool value = true; }; template<> struct TOrC< false, false, false, false, false > { static const bool value = false; }; template< typename A, typename B, typename C = TFalse, typename D = TFalse, typename E = TFalse > struct TOr : TOrC< A::value, B::value, C::value, D::value, E::value > {}; /* template< typename T > struct IsT { static const bool value = true; }; */ template< bool a > struct TNotC { static const bool value = !a; }; template< typename T > struct TNot : TNotC< T::value > {}; template< bool a, bool b > struct TImplyC : TNot< TAndC< a, TNotC< b >::value > > {}; template< typename A, typename B > struct TImply : TImplyC< A::value, B::value > {}; template< bool, typename T = Unit > struct EnableIfC {}; template< typename Type > struct EnableIfC< true, Type > { typedef Type T; }; template< bool, typename T = Unit > struct DisableIfC {}; template< typename Type > struct DisableIfC< false, Type > { typedef Type T; }; template< typename X, typename T = Unit > struct EnableIf : EnableIfC< X::value, T > {}; template< typename X, typename T = Unit > struct DisableIf : DisableIfC< X::value, T > {}; template< typename A, typename B > struct TPair { typedef A First; typedef B Second; }; struct Preferred {}; struct NotPreferred { NotPreferred( Preferred ) {} }; } #endif libwibble-1.1/wibble/grcal/0000755000175000017500000000000011463032575015170 5ustar enricoenricolibwibble-1.1/wibble/grcal/grcal.cpp0000644000175000017500000002246411463032575016774 0ustar enricoenrico/* * Gregorian calendar functions * * Copyright (C) 2007--2008 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include //#define DEBUG_GRCAL #ifdef DEBUG_GRCAL #include static std::string tostringall(const int* val) { using namespace std; stringstream s; s << setfill('0') << internal; s << setw(4) << val[0]; s << "-" << setw(2) << val[1]; s << "-" << setw(2) << val[2]; s << " " << setw(2) << val[3]; s << ":" << setw(2) << val[4]; s << ":" << setw(2) << val[5]; return s.str(); } #endif using namespace std; namespace wibble { namespace grcal { namespace date { int daysinmonth(int year, int month) { switch (month) { case 1: return 31; case 2: if (year % 400 == 0 || (year % 4 == 0 && ! (year % 100 == 0))) return 29; return 28; case 3: return 31; case 4: return 30; case 5: return 31; case 6: return 30; case 7: return 31; case 8: return 31; case 9: return 30; case 10: return 31; case 11: return 30; case 12: return 31; default: { stringstream str; str << "Month '" << month << "' is not between 1 and 12"; throw wibble::exception::Consistency("computing number of days in month", str.str()); } } } int daysinyear(int year) { if (year % 400 == 0 || (year % 4 == 0 && ! (year % 100 == 0))) return 366; return 365; } void easter(int year, int* month, int* day) { // Meeus/Jones/Butcher Gregorian algorithm // from http://en.wikipedia.org/wiki/Computus int a = year % 19; int b = year / 100; int c = year % 100; int d = b / 4; int e = b % 4; int f = (b + 8) / 25; int g = (b - f + 1) / 3; int h = (19 * a + b - d - g + 15) % 30; int i = c / 4; int k = c % 4; int L = (32 + 2 * e + 2 * i - h - k) % 7; int m = (a + 11 * h + 22 * L) / 451; *month = (h + L - 7 * m + 114) / 31; *day = ((h + L - 7 * m + 114) % 31) + 1; } void lowerbound(const int* src, int* dst) { dst[0] = src[0]; dst[1] = src[1] != -1 ? src[1] : 1; dst[2] = src[2] != -1 ? src[2] : 1; dst[3] = src[3] != -1 ? src[3] : 0; dst[4] = src[4] != -1 ? src[4] : 0; dst[5] = src[5] != -1 ? src[5] : 0; } void lowerbound(int* val) { if (val[1] == -1) val[1] = 1; else return; if (val[2] == -1) val[2] = 1; else return; if (val[3] == -1) val[3] = 0; else return; if (val[4] == -1) val[4] = 0; else return; if (val[5] == -1) val[5] = 0; else return; } static inline void normalN(int& lo, int& hi, int N) { if (lo < 0) { int m = (-lo)/N; if (lo % N) ++m; hi -= m; lo = (lo + (m*N)) % N; } else { hi += lo / N; lo = lo % N; } } void normalise(int* res) { // Rebase day and month numbers on 0 --res[1]; --res[2]; //cerr << "TOZERO " << tostringall(res) << endl; // Normalise seconds normalN(res[5], res[4], 60); //cerr << "ADJSEC " << tostringall(res) << endl; // Normalise minutes normalN(res[4], res[3], 60); //cerr << "ADJMIN " << tostringall(res) << endl; // Normalise hours normalN(res[3], res[2], 24); //cerr << "ADJHOUR " << tostringall(res) << endl; // Normalise days while (res[2] < 0) { --res[1]; normalN(res[1], res[0], 12); res[2] += daysinmonth(res[0], res[1]+1); } //cerr << "ADJDAY1 " << tostringall(res) << endl; while (true) { normalN(res[1], res[0], 12); int dim = daysinmonth(res[0], res[1]+1); if (res[2] < dim) break; res[2] -= dim; ++res[1]; } //cerr << "ADJDAY2 " << tostringall(res) << endl; // Normalise months normalN(res[1], res[0], 12); //cerr << "ADJMONYEAR " << tostringall(res) << endl; ++res[1]; ++res[2]; //cerr << "FROMZERO " << tostringall(res) << endl; } void upperbound(const int* src, int* dst) { //cerr << "UBSTART " << tostring(src) << endl; // Lowerbound and increment the last valid one for (int i = 0; i < 6; ++i) if (src[i] == -1) dst[i] = (i == 1 || i == 2) ? 1 : 0; else if (i != 5 && src[i+1] != -1) dst[i] = src[i]; else dst[i] = src[i] + 1; // Then decrement the second, to get an inclusive range --dst[5]; //cerr << "UBNORM " << tostring(dst) << endl; // Normalise the result normalise(dst); } void upperbound(int* val) { // Lowerbound and increment the last valid one for (int i = 0; i < 6; ++i) if (val[i] == -1) val[i] = (i == 1 || i == 2) ? 1 : 0; else if (i != 5 && val[i+1] != -1) ; else ++val[i]; // Then decrement the second, to get an inclusive range --val[5]; // Normalise the result normalise(val); } /** * Convert the given time in seconds elapsed since the beginning of the given * year. It is assumed that year <= t[0] */ long long int secondsfrom(int year, const int* val) { long long int res = 0; if (val[5] != -1) res += val[5]; if (val[4] != -1) res += val[4] * 60; if (val[3] != -1) res += val[3] * 3600; if (val[2] != -1) res += (val[2]-1) * 3600 * 24; if (val[1] != -1) for (int i = 1; i < val[1]; ++i) res += daysinmonth(val[0], i) * 3600 * 24; for (int i = year; i < val[0]; ++i) res += daysinyear(i) * 3600 * 24; return res; } // Give the duration of the interval between begin and end in seconds long long int duration(const int* begin, const int* end) { int b[6]; int e[6]; // Fill in missing values appropriately upperbound(begin, b); lowerbound(end, e); // Lowerbound both where both had -1 for (int i = 0; i < 6; ++i) if (begin[i] == -1 && end[i] == -1) b[i] = e[i] = (i == 1 || i == 2) ? 1 : 0; // Find the smaller year, to use as a reference for secondsfrom int y = b[0] < e[0] ? b[0] : e[0]; return secondsfrom(y, e) - secondsfrom(y, b); } void mergetime(const int* date, const int* time, int* dst) { dst[0] = date[0]; dst[1] = date[1] != -1 ? date[1] : 1; dst[2] = date[2] != -1 ? date[2] : 1; for (int i = 0; i < 3; ++i) dst[3+i] = time[i]; } void mergetime(int* date, const int* time) { if (date[1] == -1) date[1] = 1; if (date[2] == -1) date[2] = 1; for (int i = 0; i < 3; ++i) date[3+i] = time[i]; } void totm(const int* src, struct tm* dst) { dst->tm_year = src[0] - 1900; dst->tm_mon = src[1] != -1 ? src[1] - 1 : 0; dst->tm_mday = src[2] != -1 ? src[2] : 1; dst->tm_hour = src[3] != -1 ? src[3] : 0; dst->tm_min = src[4] != -1 ? src[4] : 0; dst->tm_sec = src[5] != -1 ? src[5] : 0; } void fromtm(const struct tm& src, int* dst, int count) { dst[0] = src.tm_year + 1900; dst[1] = count < 2 ? -1 : src.tm_mon + 1; dst[2] = count < 3 ? -1 : src.tm_mday; dst[3] = count < 4 ? -1 : src.tm_hour; dst[4] = count < 5 ? -1 : src.tm_min; dst[5] = count < 6 ? -1 : src.tm_sec; } #ifdef POSIX void today(int* dst) { time_t now = time(NULL); struct tm t; gmtime_r(&now, &t); fromtm(t, dst, 3); } void now(int* dst) { time_t now = time(NULL); struct tm t; gmtime_r(&now, &t); fromtm(t, dst); } #endif std::string tostring(const int* val) { stringstream s; s << setfill('0') << internal; s << setw(4) << val[0]; if (val[1] == -1) return s.str(); s << "-" << setw(2) << val[1]; if (val[2] == -1) return s.str(); s << "-" << setw(2) << val[2]; if (val[3] == -1) return s.str(); s << " " << setw(2) << val[3]; if (val[4] == -1) return s.str(); s << ":" << setw(2) << val[4]; if (val[5] == -1) return s.str(); s << ":" << setw(2) << val[5]; return s.str(); } } namespace dtime { void lowerbound(const int* src, int* dst) { for (int i = 0; i < 3; ++i) dst[i] = src[i] != -1 ? src[i] : 0; } void lowerbound(int* val) { for (int i = 0; i < 3; ++i) if (val[i] == -1) val[1] = 0; } int lowerbound_sec(const int* src) { int res = 0; if (src[0] != -1) res += src[0] * 3600; if (src[1] != -1) res += src[1] * 60; if (src[2] != -1) res += src[2]; return res; } void upperbound(const int* src, int* dst) { dst[0] = src[0] != -1 ? src[0] : 23; dst[1] = src[1] != -1 ? src[1] : 59; dst[2] = src[2] != -1 ? src[2] : 59; } void upperbound(int* val) { if (val[0] == -1) val[0] = 23; if (val[1] == -1) val[1] = 59; if (val[2] == -1) val[2] = 59; } int upperbound_sec(const int* src) { int res = 0; res += (src[0] != -1 ? src[0] : 23) * 3600; res += (src[1] != -1 ? src[1] : 59) * 60; res += (src[2] != -1 ? src[2] : 59); return res; } int duration(const int* begin, const int* end) { return lowerbound_sec(end) - upperbound_sec(begin); } std::string tostring(const int* val) { if (val[0] == -1) return string(); stringstream s; s << setfill('0') << internal; s << setw(2) << val[0]; if (val[1] == -1) return s.str(); s << ":" << setw(2) << val[1]; if (val[2] == -1) return s.str(); s << ":" << setw(2) << val[2]; return s.str(); } std::string tostring(int val) { stringstream s; s << setfill('0') << internal; s << setw(2) << (val / 3600) << ":" << setw(2) << (val % 3600) / 60 << ":" << setw(2) << (val % 60); return s.str(); } } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/grcal/grcal.test.h0000644000175000017500000001427211075401754017413 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include namespace { using namespace std; using namespace wibble; using namespace wibble::grcal; #define assert_dt_eq(x, ...) assert_dt_eq_fn( LOCATION( #x " == " #__VA_ARGS__ ), x, __VA_ARGS__) void assert_dt_eq_fn( Location l, const int* val, int ye, int mo=-1, int da=-1, int ho=-1, int mi=-1, int se=-1 ) { int cmp[6] = { ye, mo, da, ho, mi, se }; std::string a = date::tostring(val); std::string b = date::tostring(cmp); if ( !( a == b ) ) { AssertFailed f( l ); f << " got [" << a << "] != [" << b << "] instead"; } } // This is copied from grcal.cpp, which is dangerous as I may forget to keep // in sync; however, it's not a function I'd like to export, but it's a // function I'd like to test static inline void normalN(int& lo, int& hi, int N) { if (lo < 0) { int m = (-lo)/N; if (lo % N) ++m; hi -= m; lo = (lo + (m*N)) % N; } else { hi += lo / N; lo = lo % N; } } #define assert_nn_eq(x, y, N, x1, y1) assert_nn_eq_fn( LOCATION( #x ", " #y " mod " #N " == " #x1 ", " #y1 ), x, y, N, x1, y1) void assert_nn_eq_fn( Location l, int x, int y, int N, int x1, int y1) { int vx = x; int vy = y; normalN(vx, vy, N); if (vx == x1 && vy == y1) return; AssertFailed f( l ); f << " got [" << vx << ", " << vy << "] != [" << x1 << ", " << y1 << "] instead"; } struct TestGrcalDate { void fill(int* dst, int ye, int mo=-1, int da=-1, int ho=-1, int mi=-1, int se=-1) { dst[0] = ye; dst[1] = mo; dst[2] = da; dst[3] = ho; dst[4] = mi; dst[5] = se; } Test normaln() { assert_nn_eq(0, 0, 60, 0, 0); assert_nn_eq(-1, 0, 60, 59, -1); assert_nn_eq(60, 60, 60, 0, 61); assert_nn_eq(60, 0, 60, 0, 1); assert_nn_eq(0, 60, 60, 0, 60); assert_nn_eq(-3600, 0, 60, 0, -60); assert_nn_eq(-61, 1, 60, 59, -1); assert_nn_eq(-0, 0, 60, 0, 0); } Test daysinmonth() { // Trenta giorni ha novembre assert_eq(date::daysinmonth(2008, 11), 30); // Con april, giugno e settembre assert_eq(date::daysinmonth(2008, 4), 30); assert_eq(date::daysinmonth(2008, 6), 30); assert_eq(date::daysinmonth(2008, 9), 30); // Di ventotto ce n'è uno assert_eq(date::daysinmonth(2001, 2), 28); assert_eq(date::daysinmonth(2004, 2), 29); assert_eq(date::daysinmonth(2100, 2), 28); assert_eq(date::daysinmonth(2000, 2), 29); // Tutti gli altri ne han trentuno assert_eq(date::daysinmonth(2008, 1), 31); assert_eq(date::daysinmonth(2008, 3), 31); assert_eq(date::daysinmonth(2008, 5), 31); assert_eq(date::daysinmonth(2008, 7), 31); assert_eq(date::daysinmonth(2008, 8), 31); assert_eq(date::daysinmonth(2008, 10), 31); assert_eq(date::daysinmonth(2008, 12), 31); } Test daysinyear() { assert_eq(date::daysinyear(2001), 365); assert_eq(date::daysinyear(2004), 366); assert_eq(date::daysinyear(2100), 365); assert_eq(date::daysinyear(2000), 366); } Test easter() { int month, day; date::easter(2008, &month, &day); assert_eq(month, 3); assert_eq(day, 23); } Test tostring() { int val[6]; fill(val, 2008); assert_eq(date::tostring(val), "2008"); fill(val, 2008, 3); assert_eq(date::tostring(val), "2008-03"); fill(val, 2008, 3, 31); assert_eq(date::tostring(val), "2008-03-31"); fill(val, 2008, 3, 31, 3); assert_eq(date::tostring(val), "2008-03-31 03"); fill(val, 2008, 3, 31, 3, 21); assert_eq(date::tostring(val), "2008-03-31 03:21"); fill(val, 2008, 3, 31, 3, 21, 0); assert_eq(date::tostring(val), "2008-03-31 03:21:00"); } Test lowerbound() { int src[6]; int dst[6]; fill(src, 2008); date::lowerbound(src, dst); assert_dt_eq(dst, 2008, 1, 1, 0, 0, 0); date::lowerbound(src); assert_dt_eq(src, 2008, 1, 1, 0, 0, 0); } Test normalise() { int val[6]; fill(val, 2008, 1, 1, 0, 0, 0); date::normalise(val); assert_dt_eq(val, 2008, 1, 1, 0, 0, 0); fill(val, 2008, 1, 1, 0, 0, 0); val[1] -= 12; date::normalise(val); assert_dt_eq(val, 2007, 1, 1, 0, 0, 0); fill(val, 2008, 3, 1, 0, 0, 0); val[5] -= 1; date::normalise(val); assert_dt_eq(val, 2008, 2, 29, 23, 59, 59); fill(val, 2008, 2, 28, 23, 0, 0); val[5] += 3600; date::normalise(val); assert_dt_eq(val, 2008, 2, 29, 0, 0, 0); fill(val, 2008, 2, 28, 23, 0, 0); val[5] += 3600; val[0] += 1; date::normalise(val); assert_dt_eq(val, 2009, 3, 1, 0, 0, 0); fill(val, 2008, 2, 28, 23, 0, 0); val[5] += 3600; val[1] += 12; date::normalise(val); assert_dt_eq(val, 2009, 3, 1, 0, 0, 0); } Test upperbound() { int src[6]; int dst[6]; fill(src, 2008); date::upperbound(src, dst); assert_dt_eq(dst, 2008, 12, 31, 23, 59, 59); date::upperbound(src); assert_dt_eq(src, 2008, 12, 31, 23, 59, 59); fill(src, 2008, 2); date::upperbound(src); assert_dt_eq(src, 2008, 2, 29, 23, 59, 59); } Test duration() { int val1[6]; int val2[6]; fill(val1, 2007, 12, 25); fill(val2, 2007, 12, 26); assert_eq(date::duration(val1, val2), 3600*24); fill(val1, 2007, 1, 2, 3, 4, 5); assert_eq(date::secondsfrom(2006, val1), 3600*24*365+3600*24+3*3600+4*60+5); fill(val2, 2007, 1, 1, 0, 0, 0); assert_eq(date::secondsfrom(2006, val2), 3600*24*365); fill(val2, 2006, 12, 31, 23, 59, 59); assert_eq(date::secondsfrom(2006, val2), 3600*24*365-1); fill(val1, 2006, 12, 31, 23, 59, 59); fill(val2, 2007, 1, 2, 3, 4, 5); assert_eq(date::duration(val1, val2), 1+3600*24+3*3600+4*60+5); } }; struct TestGrcalTime { void fill(int* dst, int ho=-1, int mi=-1, int se=-1) { dst[0] = ho; dst[1] = mi; dst[2] = se; } Test tostring() { int val[3]; fill(val); assert_eq(dtime::tostring(val), ""); fill(val, 9); assert_eq(dtime::tostring(val), "09"); fill(val, 10, 9); assert_eq(dtime::tostring(val), "10:09"); fill(val, 11, 10, 9); assert_eq(dtime::tostring(val), "11:10:09"); } Test tostring_sec() { assert_eq(dtime::tostring(3600), "01:00:00"); assert_eq(dtime::tostring(3661), "01:01:01"); } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/grcal/grcal.h0000644000175000017500000001641711225425476016444 0ustar enricoenrico// -*- C++ -*- #ifndef WIBBLE_GRCAL_GRCAL_H #define WIBBLE_GRCAL_GRCAL_H /* * Gregorian calendar functions * * Copyright (C) 2007--2008 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include /** * @file * * This header provides functions to handle Gregorian calendar dates and times. * * The data type used through the module to represent a date is an int[6], * containing: * \l year * \l month (starting from 1) * \l day of month (starting from 1) * \l hour * \l minute * \l second * * The int[6] array does not need to be completely filled, and any value except * for the year can be left missing, with the value of -1. However, if a * value is set to -1, all the following values in the array must also be -1. * For example, 'March 2008' can be represented as { 2008, 3, -1, -1, -1, -1 }, * but something like { 2008, 3, -1, 12, -1, -1 } is not a valid date, as there * can only be the value -1 after the first -1. * * The date+time in the int[6] array is always stored in UTC: the module does * not attempt to work with timezones or daylight saving. * * The full range of the Gregorian calendar is accepted, so years like 1789 * will work fine, although pay extra attention if you are comparing historical * events of countries that adopted the Gregorian calendar in different times, * like Russia or Greece. * * Some functions work with the time of day only: those functions will work * with int[3] parameters. The time of the day can also have missing values, * with the same rules as the int[6] dates: this is midday: { 12, -1, -1 }, but * this is not valid: { 12, -1, 30 }. However, in the case of int[3] times the * hour can also be missing, so { -1, -1, -1 } is a valid time. * * Some function represent the time as a single integer: that is intended to be * the number of seconds after the start of the day. Therefore, midnight would * be 0, and midday would be 43200 (12*3600). */ struct tm; namespace wibble { namespace grcal { /** * Functions that work with int[6] datetime values */ namespace date { /** * Fill in an int[6] with the UTC values for today (leaving the time of day elements to -1) */ void today(int* dst); /// Fill in an int[6] with the UTC values for now void now(int* dst); /// Return the number of days in a month int daysinmonth(int year, int month); /// Return the number of days in a year int daysinyear(int year); /** * Compute the day of Easter. * * The algorithm used is the Meeus/Jones/Butcher Gregorian algorithm described * at http://en.wikipedia.org/wiki/Computus */ void easter(int year, int* month, int* day); /** * Make a copy of the datetime, filling in missing values with the lowest * possible value they can have */ void lowerbound(const int* src, int* dst); /** * Fill in the missing values of a datetime with the lowest possible value they * can have */ void lowerbound(int* val); /** * Make a copy of the datetime, filling in missing values with the highest * possible value they can have */ void upperbound(const int* src, int* dst); /** * Fill in the missing values of a datetime with the highest possible value they * can have */ void upperbound(int* val); /** * Normalise a datetime, in place. * * This function takes in input a datetime with no missing values, but some * values can be arbitrarily out of range. The datetime will be normalised so * that all the elements will be within range, and it will still represent the * same instant. * * For example (remember that months and days start from 1, so a day of 0 means * "last day of previous month"): * * \l normalise({2007, 0, 1, 0, 0, 0}) gives {2006, 12, 1, 0, 0, 0} * \l normalise({2007, -11, 1, 0, 0, 0}) gives {2006, 1, 1, 0, 0, 0} * \l normalise({2007, 1, -364, 0, 0, 0}) gives {2006, 1, 1, 0, 0, 0} * \l normalise({2007, 1, 366, 0, 0, 0}) gives {2008, 1, 1, 0, 0, 0} * \l normalise({2009, 1, -364, 0, 0, 0}) gives {2008, 1, 2, 0, 0, 0}, because * 2008 is a leap year * \l normalise({2008, 1, 1, 0, 0, -3600}) gives {2007, 12, 31, 23, 0, 0} */ void normalise(int* res); /** * Compute the number of seconds that elapsed from the beginning of the given * year until the given datetime. * * It is assumed that year <= val[0]: giving a year greather than val[0] will * give unpredictable results. */ long long int secondsfrom(int year, const int* val); /** * Give the duration in seconds of the interval between begin and end. * * The result can be negative if end is an earlier date than begin. */ long long int duration(const int* begin, const int* end); /** * Make a copy of \a date, with the time part taken from \a time. * * \note \a time is an int[3] time value; */ void mergetime(const int* date, const int* time, int* dst); /** * Replace the time part of \a date with the values from time. * * \note \a time is an int[3] time value; */ void mergetime(int* date, const int* time); /** * Copy the values from an int[6] datetime into a struct tm. */ void totm(const int* src, struct tm* dst); /** * Copy the values from a struct tm to the first \a count values of the int[6] * \a dst. */ void fromtm(const struct tm& src, int* dst, int count = 6); /** * Convert a datetime to a string */ std::string tostring(const int* val); } /** * Functions that work with int[3] time of day values */ namespace dtime { /** * Make a copy of the time, filling in missing values with the lowest * possible value they can have */ void lowerbound(const int* src, int* dst); /** * Fill in the missing values of a time of day with the lowest possible value * they can have */ void lowerbound(int* val); /** * Convert a time of day in second, filling the missing values with the lowest * possible value they can have. */ int lowerbound_sec(const int* src); /** * Make a copy of the time, filling in missing values with the highest possible * value they can have */ void upperbound(const int* src, int* dst); /** * Fill in the missing values of a time of day with the highest possible value * they can have */ void upperbound(int* val); /** * Convert a time of day in second, filling the missing values with the highest * possible value they can have. */ int upperbound_sec(const int* src); /** * Give the duration in seconds of the interval between the end of begin * and the beginning of end. * * The result can be negative if end is an earlier time than begin. */ int duration(const int* begin, const int* end); /** * Format a time of day to a string */ std::string tostring(const int* val); /** * Format a time of day expressed in seconds to a string */ std::string tostring(int val); } } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/exception.tcc0000644000175000017500000000230511075402051016556 0ustar enricoenrico/* -*- C++ -*- * Generic base exception hierarchy * * Copyright (C) 2003,2004,2005,2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace wibble { namespace exception { template std::string ValOutOfRange::desc() const throw () { std::stringstream str; str << m_var_desc << "(" << m_val << ") out of range (" << m_inf << "-" << m_sup << ")"; return str.str(); } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/raii.h0000644000175000017500000000304712231005104015157 0ustar enricoenrico// -*- C++ -*- (c) 2013 Vladimír Štill #if __cplusplus >= 201103L #include #include #ifndef WIBBLE_RAII_H #define WIBBLE_RAII_H namespace wibble { namespace raii { template< typename Instance, typename Delete = void(*)( typename std::remove_reference< Instance >::type & ) > struct AutoDelete { Instance value; Delete deleter; AutoDelete( Instance &&instance, Delete deleter ) : AutoDelete( true, std::forward< Instance >( instance ), deleter ) { } AutoDelete( bool runDeleter, Instance &&instance, Delete deleter ) : value( std::forward< Instance >( instance ) ), deleter( deleter ), runDeleter( runDeleter ) { } ~AutoDelete() { if ( runDeleter ) deleter( value ); } private: bool runDeleter; }; template< typename Creator, typename Delete > auto autoDeleter( Creator creator, Delete deleter ) -> AutoDelete< typename std::result_of< Creator() >::type, Delete > { using Instance = typename std::result_of< Creator() >::type; return AutoDelete< Instance, Delete >( true, creator(), deleter ); } template< typename Instance, typename Delete > auto refDeleteIf( bool cond, Instance &ref, Delete deleter ) -> AutoDelete< Instance &, Delete > { return AutoDelete< Instance &, Delete >( cond, ref, deleter ); } template< typename Instance, typename Delete > auto refDeleter( Instance &ref, Delete deleter ) -> AutoDelete< Instance &, Delete > { return refDeleteIf( true, ref, deleter ); } } } #endif // WIBBLE_RAII_H #endif libwibble-1.1/wibble/range.h0000644000175000017500000003420012231005104015322 0ustar enricoenrico/** -*- C++ -*- @file wibble/range.h @author Peter Rockai */ #include // for noise #include #include #include #include #include #include #ifndef WIBBLE_RANGE_H #define WIBBLE_RANGE_H namespace wibble { template< typename _ > struct Range; template< typename _ > struct Consumer; // FOO: there was no test catching that we don't implement -> // auxilliary class, used as Range< T >::iterator template< typename R > struct RangeIterator : mixin::Comparable< RangeIterator< R > > { typedef typename R::ElementType T; struct Proxy { Proxy( T _x ) : x( _x ) {} T x; const T *operator->() const { return &x; } }; RangeIterator() {} RangeIterator( const R &r ) : m_range( r ) {} typedef std::forward_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T *pointer; typedef T &reference; typedef const T &const_reference; Proxy operator->() const { return Proxy( *(*this) ); } RangeIterator next() const { RangeIterator n( *this ); ++n; return n; } typename R::ElementType operator*() const { return m_range.head(); } typename R::ElementType current() const { return *(*this); } RangeIterator &operator++() { m_range.removeFirst(); return *this; } RangeIterator operator++(int) { return m_range.removeFirst(); } bool operator<=( const RangeIterator &r ) const { return m_range.operator<=( r.m_range ); } protected: R m_range; }; // the common functionality of all ranges template< typename T, typename Self > struct RangeMixin : mixin::Comparable< Self > { typedef Self RangeImplementation; typedef T ElementType; const Self &self() const { return *static_cast< const Self * >( this ); } typedef IteratorMixin< T, Self > Base; typedef RangeIterator< Self > iterator; friend struct RangeIterator< Self >; iterator begin() const { return iterator( this->self() ); } // STL-style iteration iterator end() const { Self e( this->self() ); e.setToEmpty(); return iterator( e ); } // range terminology T head() { return self().head(); } Self tail() const { Self e( this->self() ); e.removeFirst(); return e; } // Self &tail() { return self().tail(); } void output( Consumer< T > t ) const { std::copy( begin(), end(), t ); } bool empty() const { return begin() == end(); } ~RangeMixin() {} }; // interface to be implemented by all range implementations // refinement of IteratorInterface (see iterator.h) // see also amorph.h on how the Morph/Amorph classes are designed template< typename T > struct RangeInterface { virtual T head() const = 0; virtual void removeFirst() = 0; virtual void setToEmpty() = 0; virtual ~RangeInterface() {} }; template< typename T, typename W > struct RangeMorph: Morph< RangeMorph< T, W >, W, RangeInterface< T > > { typedef typename W::RangeImplementation Wrapped; RangeMorph( const Wrapped &w ) : Morph< RangeMorph, Wrapped, RangeInterface< T > >( w ) {} virtual void setToEmpty() { this->wrapped().setToEmpty(); } virtual void removeFirst() { this->wrapped().removeFirst(); } virtual T head() const { return this->wrapped().head(); } }; // the Amorph of Ranges... if you still didn't check amorph.h, go and // do it... unless you don't care in which case i am not sure why you // are reading this anyway /* Range< T > (and all its Morphs (implementations) alike) work as an iterable, immutable range of items -- in a way like STL const_begin(), const_end() pair of iterators. However, Range packs these two in a single object, which you can then pass as a single argument or return as a value. There are many Range implementations, some of them use STL containers (or just a pair of iterators) as backing (IteratorRange, BackedRange), some use other ranges. The latter are slightly more interesting, since they provide an "adapted" view on the underlying range(s). See FilteredRange, TransformedRange, UniqueRange, CastedRange , IntersectionRange. GeneratedRange has no "real" backing at all, but use a pair of functors and "generates" (by calling those functors) the complete range as it is iterated. Example usage: // create a range from a pair of STL iterators Range< int > i = range( myIntVector.begin(), myIntVector.end() ); // create a range that is filtered view of another range Range< int > j = filteredRange( i, predicate ); std::vector< int > vec; // copy out items in j into vec; see also consumer.h j.output( consumer( vec ) ); // vec now contains all items from i (and thus myIntVector) that // match 'predicate' You don't have to use Range< int > all the time, since it's fairly inefficient (adding level of indirection). However in common cases it is ok. In the uncommon cases you can use the specific implementation type and there you won't suffer the indirection penalty. You can also write generic functions that have type of range as their template parameter and these will work more efficiently if Morph is used directly and less efficiently when the Amorph Range is used instead. */ template< typename T > struct Range : Amorph< Range< T >, RangeInterface< T > >, RangeMixin< T, Range< T > > { typedef Amorph< Range< T >, RangeInterface< T > > Super; template< typename C > Range( const C &i, typename IsType< int, typename C::RangeImplementation >::T fake = 0 ) : Super( RangeMorph< T, C >( i ) ) { (void)fake; } Range() {} T head() const { return this->implementation()->head(); } void removeFirst() { this->implementation()->removeFirst(); } void setToEmpty() { this->implementation()->setToEmpty(); } template< typename C > operator Range< C >(); }; /* template< typename R > Range< typename R::ElementType > rangeMorph( R r ) { return Range< typename R::ElementType > uRangeMorph< typename R::ElementType, R >( r ); } */ } // ----- individual range implementations follow #include namespace wibble { // a simple pair of iterators -- suffers the same invalidation // semantics as normal STL iterators and also same problems when the // backing storage goes out of scope // this is what you get when using range( iterator1, iterator2 ) // see also range() template< typename It > struct IteratorRange : public RangeMixin< typename std::iterator_traits< It >::value_type, IteratorRange< It > > { typedef typename std::iterator_traits< It >::value_type Value; IteratorRange() {} IteratorRange( It c, It e ) : m_current( c ), m_end( e ) {} Value head() const { return *m_current; } void removeFirst() { ++m_current; } bool operator<=( const IteratorRange &r ) const { return r.m_current == m_current && r.m_end == m_end; } void setToEmpty() { m_current = m_end; } protected: It m_current, m_end; }; // first in the series of ranges that use another range as backing // this one just does static_cast to specified type on all the // returned elements // this is what you get when casting Range< S > to Range< T > and S is // static_cast-able to T template< typename T, typename Casted > struct CastedRange : public RangeMixin< T, CastedRange< T, Casted > > { CastedRange() {} CastedRange( Range< Casted > r ) : m_casted( r ) {} T head() const { return static_cast< T >( m_casted.head() ); } void removeFirst() { m_casted.removeFirst(); } bool operator<=( const CastedRange &r ) const { return m_casted <= r.m_casted; } void setToEmpty() { m_casted.setToEmpty(); } protected: Range< Casted > m_casted; }; // explicit range-cast... probably not very useful explicitly, just // use the casting operator of Range template< typename T, typename C > Range< T > castedRange( C r ) { return CastedRange< T, typename C::ElementType >( r ); } // old-code-compat for castedRange... slightly silly template< typename T, typename C > Range< T > upcastRange( C r ) { return CastedRange< T, typename C::ElementType >( r ); } // the range-cast operator, see castedRange and CastedRange template< typename T > template< typename C > Range< T >::operator Range< C >() { return castedRange< C >( *this ); } // range( iterator1, iterator2 ) -- see IteratorRange template< typename In > Range< typename In::value_type > range( In b, In e ) { return IteratorRange< In >( b, e ); } template< typename C > Range< typename C::iterator::value_type > range( C &c ) { return range( c.begin(), c.end() ); } template< typename T > struct IntersectionRange : RangeMixin< T, IntersectionRange< T > > { IntersectionRange() {} IntersectionRange( Range< T > r1, Range< T > r2 ) : m_first( r1 ), m_second( r2 ), m_valid( false ) { } void find() const { if (!m_valid) { while ( !m_first.empty() && !m_second.empty() ) { if ( m_first.head() < m_second.head() ) m_first.removeFirst(); else if ( m_second.head() < m_first.head() ) m_second.removeFirst(); else break; // equal } if ( m_second.empty() ) m_first.setToEmpty(); else if ( m_first.empty() ) m_second.setToEmpty(); } m_valid = true; } void removeFirst() { find(); m_first.removeFirst(); m_second.removeFirst(); m_valid = false; } T head() const { find(); return m_first.head(); } void setToEmpty() { m_first.setToEmpty(); m_second.setToEmpty(); } bool operator<=( const IntersectionRange &f ) const { find(); f.find(); return m_first == f.m_first; } protected: mutable Range< T > m_first, m_second; mutable bool m_valid:1; }; template< typename R > IntersectionRange< typename R::ElementType > intersectionRange( R r1, R r2 ) { return IntersectionRange< typename R::ElementType >( r1, r2 ); } template< typename R, typename Pred > struct FilteredRange : RangeMixin< typename R::ElementType, FilteredRange< R, Pred > > { typedef typename R::ElementType ElementType; // FilteredRange() {} FilteredRange( const R &r, Pred p ) : m_range( r ), m_current( r.begin() ), m_pred( p ), m_valid( false ) {} void find() const { if (!m_valid) m_current = std::find_if( m_current, m_range.end(), pred() ); m_valid = true; } void removeFirst() { find(); ++m_current; m_valid = false; } ElementType head() const { find(); return *m_current; } void setToEmpty() { m_current = m_range.end(); } bool operator<=( const FilteredRange &f ) const { find(); f.find(); return m_current == f.m_current; // return m_pred == f.m_pred && m_range == f.m_range; } protected: const Pred &pred() const { return m_pred; } R m_range; mutable typename R::iterator m_current; Pred m_pred; mutable bool m_valid:1; }; template< typename R, typename Pred > FilteredRange< R, Pred > filteredRange( R r, Pred p ) { return FilteredRange< R, Pred >( r, p ); } template< typename T > struct UniqueRange : RangeMixin< T, UniqueRange< T > > { UniqueRange() {} UniqueRange( Range< T > r ) : m_range( r ), m_valid( false ) {} void find() const { if (!m_valid) while ( !m_range.empty() && !m_range.tail().empty() && m_range.head() == m_range.tail().head() ) m_range = m_range.tail(); m_valid = true; } void removeFirst() { find(); m_range.removeFirst(); m_valid = false; } T head() const { find(); return m_range.head(); } void setToEmpty() { m_range.setToEmpty(); } bool operator<=( const UniqueRange &r ) const { find(); r.find(); return m_range == r.m_range; } protected: mutable Range< T > m_range; mutable bool m_valid:1; }; template< typename R > UniqueRange< typename R::ElementType > uniqueRange( R r1 ) { return UniqueRange< typename R::ElementType >( r1 ); } template< typename Transform > struct TransformedRange : RangeMixin< typename Transform::result_type, TransformedRange< Transform > > { typedef typename Transform::argument_type Source; typedef typename Transform::result_type Result; TransformedRange( Range< Source > r, Transform t ) : m_range( r ), m_transform( t ) {} bool operator<=( const TransformedRange &o ) const { return m_range <= o.m_range; } Result head() const { return m_transform( m_range.head() ); } void removeFirst() { m_range.removeFirst(); } void setToEmpty() { m_range.setToEmpty(); } protected: Range< Source > m_range; Transform m_transform; }; template< typename Trans > TransformedRange< Trans > transformedRange( Range< typename Trans::argument_type > r, Trans t ) { return TransformedRange< Trans >( r, t ); } template< typename T, typename _Advance, typename _End > struct GeneratedRange : RangeMixin< T, GeneratedRange< T, _Advance, _End > > { typedef _Advance Advance; typedef _End End; GeneratedRange() {} GeneratedRange( const T &t, const Advance &a, const End &e ) : m_current( t ), m_advance( a ), m_endPred( e ), m_end( false ) { } void removeFirst() { m_advance( m_current ); } void setToEmpty() { m_end = true; } T head() const { return m_current; } bool isEnd() const { return m_end || m_endPred( m_current ); } bool operator<=( const GeneratedRange &r ) const { if ( isEnd() == r.isEnd() && !isEnd() ) return m_current <= r.m_current; return isEnd() <= r.isEnd(); } protected: T m_current; Advance m_advance; End m_endPred; bool m_end; }; template< typename T, typename A, typename E > GeneratedRange< T, A, E > generatedRange( T t, A a, E e ) { return GeneratedRange< T, A, E >( t, a, e ); } } #endif libwibble-1.1/wibble/amorph.test.h0000644000175000017500000000333111075401774016515 0ustar enricoenrico// -*- C++ -*- (c) 2007 Petr Rockai #include namespace { using namespace wibble; struct TInterface { virtual int value() = 0; }; template< typename W > struct TMorph : Morph< TMorph< W >, W, TInterface > { TMorph() {} TMorph( const W &w ) : Morph< TMorph, W, TInterface >( w ) {} virtual int value() { return this->wrapped().value(); } }; struct T : Amorph< T, TInterface > { T( const MorphInterface< TInterface > &i ) : Amorph< T, TInterface >( i ) {} T() {} int value() { return this->implementation()->value(); } }; struct T1 : VirtualBase { virtual int value() const { return 1; } bool operator<=( const T1 &o ) const { return value() <= o.value(); } }; struct T3 : T1 { virtual int value() const { return 3; } }; struct T2 : VirtualBase { int value() const { return 2; } bool operator<=( const T2 &o ) const { return value() <= o.value(); } }; struct ExtractT1Value { typedef int result_type; typedef T1 argument_type; int operator()( const T1 &t ) { return t.value(); } }; template< typename T > TMorph< T > testMorph( T t ) { return TMorph< T >( t ); } struct TestAmorph { Test basic() { T1 t1; T2 t2; T3 t3; T t = testMorph( t1 ); assert_eq( t.value(), 1 ); assert_eq( t.ifType( ExtractT1Value() ), Maybe< int >::Just( 1 ) ); t = testMorph( t2 ); assert_eq( t.value(), 2 ); assert_eq( t.ifType( ExtractT1Value() ), Maybe< int >::Nothing() ); t = testMorph( t3 ); assert_eq( t.value(), 3 ); assert_eq( t.ifType( ExtractT1Value() ), Maybe< int >::Just( 3 ) ); } }; } libwibble-1.1/wibble/log/0000755000175000017500000000000012231005104014637 5ustar enricoenricolibwibble-1.1/wibble/log/ostream.cpp0000644000175000017500000000044011461640066017033 0ustar enricoenrico#include namespace wibble { namespace log { OstreamSender::OstreamSender(std::ostream& out) : out(out) {} void OstreamSender::send(Level level, const std::string& msg) { out << msg << std::endl; if (level >= WARN) out.flush(); } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/log/file.h0000644000175000017500000000061111461640066015745 0ustar enricoenrico#ifndef WIBBLE_LOG_FILE_H #define WIBBLE_LOG_FILE_H #include namespace wibble { namespace log { /// Discard all messages struct FileSender : public Sender { protected: int out; std::string name; public: FileSender(const std::string& filename); virtual ~FileSender(); virtual void send(Level level, const std::string& msg); }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/log/filters.h0000644000175000017500000000242111461640066016477 0ustar enricoenrico#ifndef WIBBLE_LOG_FILTERS_H #define WIBBLE_LOG_FILTERS_H #include #include namespace wibble { namespace log { /** * Prepend timestamps to log lines * * Timestamps are generated with a strftime format string on POSIX systems, or * with simple asctime() on windows. * * Strftime expands using a buffer 256 characters wide. Please keep that in * mind when creating the format string: longer outputs will be truncated. */ struct Timestamper : public Sender { Sender* next; std::string fmt; Timestamper(Sender* next = 0, const std::string& fmt = "%b %e %T "); virtual ~Timestamper(); virtual void send(Level level, const std::string& msg); }; /** * Log only messages whose level is >= minLevel */ struct LevelFilter : public log::Sender { Sender* next; log::Level minLevel; LevelFilter(Sender* next = 0, log::Level minLevel = log::INFO); virtual ~LevelFilter(); virtual void send(log::Level level, const std::string& msg); }; /** * Send the same message to multiple streams */ struct Tee : public log::Sender { std::vector next; Tee(); // Shortcut to initialise with two streams Tee(Sender* first, Sender* second); ~Tee(); virtual void send(log::Level level, const std::string& msg); }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/log/null.h0000644000175000017500000000046111075402013015770 0ustar enricoenrico#ifndef WIBBLE_LOG_NULL_H #define WIBBLE_LOG_NULL_H #include namespace wibble { namespace log { /// Discard all messages struct NullSender : public Sender { virtual ~NullSender() {} virtual void send(Level level, const std::string& msg) {} }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/log/syslog.h0000644000175000017500000000101111372245054016340 0ustar enricoenrico#include #ifdef POSIX #ifndef WIBBLE_LOG_SYSLOG_H #define WIBBLE_LOG_SYSLOG_H #include #include namespace wibble { namespace log { /// Discard all messages struct SyslogSender : public Sender { protected: void* out; std::string name; public: SyslogSender(const std::string& ident, int option = LOG_PID, int facility = LOG_USER); virtual ~SyslogSender(); virtual void send(Level level, const std::string& msg); }; } } // vim:set ts=4 sw=4: #endif #endif libwibble-1.1/wibble/log/stream.cpp0000644000175000017500000000227611462130515016657 0ustar enricoenrico#include #include namespace wibble { namespace log { Streambuf::Streambuf() : level(defaultLevel), sender(0) {} Streambuf::Streambuf(Sender* s) : level(defaultLevel), sender(s) {} Streambuf::~Streambuf() { send_partial_line(); } void Streambuf::send_partial_line() { if (!line.empty()) send(); } void Streambuf::setSender(Sender* s) { sender = s; } void Streambuf::send() { // Send the message sender->send(level, line); // Reset the level level = defaultLevel; line.clear(); } void Streambuf::setLevel(const Level& level) { this->level = level; } int Streambuf::overflow(int c) { if (c == '\n') send(); else line += c; return c; // or EOF } std::ostream& operator<<(std::ostream& s, Level lev) { if (Streambuf* ls = dynamic_cast(s.rdbuf())) { ls->setLevel(lev); return s; } else { switch (lev) { case DEBUG: s << "DEBUG: "; break; case INFO: s << "INFO: "; break; case UNUSUAL: s << "UNUSUAL: "; break; case WARN: s << "WARN: "; break; case ERR: s << "ERR: "; break; case CRIT: s << "CRITICAL: "; break; default: s << "UNKNOWN: "; break; } return s; } } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/log/file.cpp0000644000175000017500000000257212231177745016315 0ustar enricoenrico#include #include #include #include #include #include #include #include namespace wibble { namespace log { FileSender::FileSender(const std::string& filename) : out(-1), name(filename) { out = open(filename.c_str(), O_WRONLY | O_CREAT, 0666); if (out < 0) throw wibble::exception::File(filename, "opening logfile for append"); } FileSender::~FileSender() { if (out >= 0) close(out); } void FileSender::send(Level level, const std::string& msg) { #ifndef __gnu_hurd__ #ifdef POSIX // FIXME // Write it all in a single write(2) so multiple processes can log to // the same file sys::fs::FileLock lock(out, F_WRLCK); #endif #endif // Seek to end of file if (lseek(out, 0, SEEK_END) < 0) throw wibble::exception::File(name, "moving to end of file"); // Write it all out size_t done = 0; while (done < msg.size()) { ssize_t res = write(out, msg.data() + done, msg.size() - done); if (res < 0) throw wibble::exception::File(name, "writing to file"); done += res; } // Write trailing newline while (true) { ssize_t res = write(out, "\n", 1); if (res < 0) throw wibble::exception::File(name, "writing to file"); if (res > 0) break; } } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/log/filters.cpp0000644000175000017500000000326511461640066017041 0ustar enricoenrico#include #include #ifdef POSIX #include #endif #ifdef _WIN32 #include #endif using namespace std; namespace wibble { namespace log { Timestamper::Timestamper(Sender* next, const std::string& fmt) : next(next), fmt(fmt) { } Timestamper::~Timestamper() { } void Timestamper::send(Level level, const std::string& msg) { if (!next) return; time_t now = time(NULL); #ifdef POSIX struct tm pnow; localtime_r(&now, &pnow); #endif #ifdef _WIN32 struct tm * pnow; pnow = localtime(&now); #endif /* * Strftime specifiers used here: * %b The abbreviated month name according to the current locale. * %d The day of the month as a decimal number (range 01 to 31). * %e Like %d, the day of the month as a decimal number, but a * leading zero is replaced by a space. (SU) * %T The time in 24-hour notation (%H:%M:%S). (SU) */ #ifdef POSIX char timebuf[256]; strftime(timebuf, 256, fmt.c_str(), &pnow); next->send(level, string(timebuf) + msg); #endif #ifdef _WIN32 next->send(level, string(asctime(pnow)) + " " + msg); #endif } LevelFilter::LevelFilter(Sender* next, log::Level minLevel) : next(next), minLevel(minLevel) { } LevelFilter::~LevelFilter() { } void LevelFilter::send(log::Level level, const std::string& msg) { if (level < minLevel || !next) return; next->send(level, msg); } Tee::Tee() {} Tee::Tee(Sender* first, Sender* second) { next.push_back(first); next.push_back(second); } Tee::~Tee() { } void Tee::send(log::Level level, const std::string& msg) { for (std::vector::iterator i = next.begin(); i != next.end(); ++i) (*i)->send(level, msg); } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/log/ostream.h0000644000175000017500000000063211075402005016471 0ustar enricoenrico#ifndef WIBBLE_LOG_OSTREAM_H #define WIBBLE_LOG_OSTREAM_H #include #include namespace wibble { namespace log { /// Discard all messages struct OstreamSender : public Sender { protected: std::ostream& out; public: OstreamSender(std::ostream& out); virtual ~OstreamSender() {} virtual void send(Level level, const std::string& msg); }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/log/stream.h0000644000175000017500000000336611462130515016325 0ustar enricoenrico#ifndef WIBBLE_LOG_STREAM_H #define WIBBLE_LOG_STREAM_H #include #include namespace wibble { namespace log { /// Urgency of a log message enum Level { DEBUG, INFO, UNUSUAL, WARN, ERR, CRIT }; /// Handle sending a log message struct Sender { virtual ~Sender() {} /** * Log one line of text with the given level. * * Do not add a trailing newline */ virtual void send(Level level, const std::string& msg) = 0; }; /// Streambuf class for logging class Streambuf : public std::streambuf { protected: /// Level to use for messages whose level has not been specified static const Level defaultLevel = INFO; /// Line buffer with the log message we are building std::string line; /// Level of the next log message Level level; /// Sender used to send log messages /* Note: we have to use composition instead of overloading because the * sender needs to be called in the destructor, and destructors cannot call * overridden methods */ Sender* sender; /// Send the message "line" with the level "level" void send(); public: /// Construct a nonworking Streambuf to be initialised later. Streambuf(); /** * @param s * The sender to use to send log messages. Streambuf will just use the * pointer, but will not take over memory maintenance */ Streambuf(Sender* s); virtual ~Streambuf(); /// If there is a partial line, send it out void send_partial_line(); /// Set/change the sender to use for this streambuf void setSender(Sender* s); /// Set the level for the next message, and the next message only void setLevel(const Level& level); /// override to get data as a std::streambuf int overflow(int c); }; std::ostream& operator<<(std::ostream& s, Level lev); } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/log/syslog.cpp0000644000175000017500000000126011372245054016701 0ustar enricoenrico#include #ifdef POSIX namespace wibble { namespace log { SyslogSender::SyslogSender(const std::string& ident, int option, int facility) { openlog(ident.c_str(), option, facility); } SyslogSender::~SyslogSender() { closelog(); } void SyslogSender::send(Level level, const std::string& msg) { int prio = LOG_INFO; switch (level) { case DEBUG: prio = LOG_DEBUG; break; case INFO: prio = LOG_INFO; break; case UNUSUAL: prio = LOG_NOTICE; break; case WARN: prio = LOG_WARNING; break; case ERR: prio = LOG_ERR; break; case CRIT: prio = LOG_CRIT; break; //LOG_ALERT, LOG_EMERG } syslog(prio, "%s", msg.c_str()); } } } #endif // vim:set ts=4 sw=4: libwibble-1.1/wibble/mixin.test.h0000644000175000017500000000267311075401774016363 0ustar enricoenrico// -*- C++ -*- (c) 2007 Petr Rockai #include #include #include namespace { using namespace std; class Integer : public wibble::mixin::Comparable { int val; public: Integer(int val) : val(val) {} bool operator<=( const Integer& o ) const { return val <= o.val; } }; class Discard : public wibble::mixin::OutputIterator { public: Discard& operator=(const int&) { return *this; } }; struct TestMixin { // Test Comparable mixin Test comparable() { Integer i10(10); Integer i10a(10); Integer i20(20); // Test the base method first assert(i10 <= i10a); assert(i10a <= i10); assert(i10 <= i20); assert(! (i20 <= i10)); // Test the other methods assert(i10 != i20); assert(!(i10 != i10a)); assert(i10 == i10a); assert(!(i10 == i20)); assert(i10 < i20); assert(!(i20 < i10)); assert(!(i10 < i10a)); assert(i20 > i10); assert(!(i10 > i20)); assert(!(i10 > i10a)); assert(i10 >= i10a); assert(i10a >= i10); assert(i20 >= i10); assert(! (i10 >= i20)); } Test output() { vector data; data.push_back(1); data.push_back(2); data.push_back(3); std::copy(data.begin(), data.end(), Discard()); } }; } libwibble-1.1/wibble/operators.test.h0000644000175000017500000000677111075401773017257 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include namespace { using namespace std; using namespace wibble::operators; static set mkset(int i1) { set a; a.insert(i1); return a; } static set mkset(int i1, int i2) { set a; a.insert(i1); a.insert(i2); return a; } #if 0 static set mkset(int i1, int i2, int i3) { set a; a.insert(i1); a.insert(i2); a.insert(i3); return a; } static set mkset(int i1, int i2, int i3, int i4) { set a; a.insert(i1); a.insert(i2); a.insert(i3); a.insert(i4); return a; } #endif struct TestOperators { Test binarySetOperations() { set< int > a = mkset(4, 5); set< int > b = mkset(5); set< int > c = a & b; assert_eq( c.size(), 1u ); assert( c.find( 4 ) == c.end() ); assert( c.find( 5 ) != c.end() ); c = a | b; assert_eq( c.size(), 2u ); assert( c.find( 4 ) != c.end() ); assert( c.find( 5 ) != c.end() ); c = a - b; assert_eq( c.size(), 1u ); assert( c.find( 4 ) != c.end() ); assert( c.find( 5 ) == c.end() ); } Test mutatingSetOperations() { set< int > a = mkset(4, 3); set< int > b = mkset(5); b |= 3; assert_eq( b.size(), 2u ); assert( b.find( 2 ) == b.end() ); assert( b.find( 3 ) != b.end() ); assert( b.find( 4 ) == b.end() ); assert( b.find( 5 ) != b.end() ); b |= a; assert_eq( b.size(), 3u ); assert( b.find( 3 ) != b.end() ); assert( b.find( 4 ) != b.end() ); assert( b.find( 5 ) != b.end() ); b &= a; assert_eq( b.size(), 2u ); assert( b.find( 3 ) != b.end() ); assert( b.find( 4 ) != b.end() ); assert( b.find( 5 ) == b.end() ); b.insert( b.begin(), 2 ); b -= a; assert_eq( b.size(), 1u ); assert( b.find( 2 ) != b.end() ); assert( b.find( 3 ) == b.end() ); assert( b.find( 4 ) == b.end() ); } Test specialContainerOperations() { set< int > a; a = a | wibble::Empty(); assert_eq( a.size(), 0u ); a = a | wibble::Singleton(1); assert_eq( a.size(), 1u ); assert( a.find( 1 ) != a.end() ); a = a - wibble::Empty(); assert_eq( a.size(), 1u ); assert( a.find( 1 ) != a.end() ); a = a - wibble::Singleton(1); assert_eq( a.size(), 0u ); assert( a.find( 1 ) == a.end() ); a |= wibble::Empty(); assert_eq( a.size(), 0u ); a |= wibble::Singleton(1); assert_eq( a.size(), 1u ); assert( a.find( 1 ) != a.end() ); a -= wibble::Empty(); assert_eq( a.size(), 1u ); assert( a.find( 1 ) != a.end() ); a -= wibble::Singleton(1); assert_eq( a.size(), 0u ); assert( a.find( 1 ) == a.end() ); } Test emptySetInclusion() { set< int > a, b; assert( a <= b ); assert( b <= a ); } Test mutatingIntersectionBug() { // Catches a past bug of in-place intersection that would delete too many // items if the second set had items not present in the first set a = mkset(2); set b = mkset(1, 2); set c = mkset(2); set d = a & b; assert(c == d); d = a; d &= b; assert(c == d); } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/0000755000175000017500000000000012231502504014702 5ustar enricoenricolibwibble-1.1/wibble/sys/mmap.test.h0000644000175000017500000000412312231005104016755 0ustar enricoenrico/* * Simple mmap support * * Copyright (C) 2006--2008 Enrico Zini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include using namespace std; using namespace wibble::sys; struct TestMMap { Test simple() { #ifdef POSIX MMap map; assert_eq(map.filename, string()); assert_eq(map.fd, -1); assert_eq(map.size, 0u); assert_eq(map.buf, static_cast(0)); map.map("/bin/sh"); assert_eq(map.filename, "/bin/sh"); assert(map.fd != -1); assert(map.size != 0u); assert(map.buf != static_cast(0)); assert_eq(map.buf[1], 'E'); assert_eq(map.buf[2], 'L'); assert_eq(map.buf[3], 'F'); MMap map1 = map; assert_eq(map.filename, string()); assert_eq(map.fd, -1); assert_eq(map.size, 0u); assert_eq(map.buf, static_cast(0)); assert_eq(map1.filename, "/bin/sh"); assert(map1.fd != -1); assert(map1.size != 0u); assert(map1.buf != static_cast(0)); assert_eq(map1.buf[1], 'E'); assert_eq(map1.buf[2], 'L'); assert_eq(map1.buf[3], 'F'); map1.unmap(); assert_eq(map1.filename, string()); assert_eq(map1.fd, -1); assert_eq(map1.size, 0u); assert_eq(map1.buf, static_cast(0)); #endif } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/buffer.h0000644000175000017500000001134712231454340016336 0ustar enricoenrico#ifndef WIBBLE_SYS_BUFFER_H #define WIBBLE_SYS_BUFFER_H /* * Variable-size, reference-counted memory buffer * * Copyright (C) 2003--2013 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include // for size_t #include namespace wibble { namespace sys { /** * Variable-size, reference-counted memory buffer. */ class Buffer { public: class Data { protected: mutable int _ref; size_t _size; void* _data; public: Data() throw () : _ref(0), _size(0), _data(0) {} Data(size_t size); // if own == true, take possession of the memory buffer, else copy it Data(void* buf, size_t size, bool own = true); Data(const void* buf, size_t size); ~Data(); /// Increment the reference count for this object void ref() const throw () { ++_ref; } /// Decrement the reference count for this object, returning true when it /// reaches 0 bool unref() const throw () { return --_ref == 0; } /// Resize (enlarging or shrinking it) the buffer to `size' bytes void resize(size_t size); /// Compare the contents of two buffers bool operator==(const Data& d) const throw(); /// Compare the contents of two buffers bool operator<(const Data& d) const throw(); friend class Buffer; }; Data* item; public: /// Create a 0-lenght buffer Buffer() throw () : item(0) {} /// Create a buffer with the specified size Buffer(size_t size) : item(0) { if (size) { item = new Data(size); item->ref(); } } /** * Create a buffer from existing data * * @param buf * The data to put in this buffer * @param size * The dimension of buf * @param own * If true, take ownership of buf, else make a copy of it. * If we take ownership, then buf must have been allocated with malloc, * since we will call free() to deallocate it. */ Buffer(void* buf, size_t size, bool own = true) : item(0) { if (size) { item = new Data(buf, size, own); item->ref(); } } /** * Create a buffer with a copy of the given data * * It will always make a copy of the contents of buf. */ Buffer(const void* buf, size_t size) : item(0) { if (size) { item = new Data(buf, size); item->ref(); } } Buffer(const Buffer& buf) throw () { if (buf.item) buf.item->ref(); item = buf.item; } ~Buffer() { if (item && item->unref()) delete item; } Buffer& operator=(const Buffer& buf) { if (buf.item) buf.item->ref(); // Do it early to correctly handle the case of x = x; if (item && item->unref()) delete item; item = buf.item; return *this; } /// Return a pointer to the buffer void* data() throw () { return item ? item->_data : 0; } /// Return a pointer to the buffer const void* data() const throw () { return item ? item->_data : 0; } /// Return the buffer size size_t size() const throw () { return item ? item->_size : 0; } /// Resize the buffer to hold exactly the specified amount of bytes void resize(size_t newSize) { if (size() == newSize) return; if (!newSize) { if (item && item->unref()) delete item; item = 0; } else if (item) { item->resize(newSize); } else { item = new Data(newSize); item->ref(); } } /// Compare the contents of two buffers bool operator==(const Buffer& buf) const throw () { if (item == 0 && buf.item == 0) return true; if (item == 0 || buf.item == 0) return false; return *item == *buf.item; } bool operator!=(const Buffer& buf) const throw () { return !operator==(buf); } /// Compare the contents of two buffers bool operator<(const Buffer& buf) const throw () { if (item == 0 && buf.item == 0) return false; if (item == 0) return true; if (buf.item == 0) return false; return *item < *buf.item; } /** * Render a c-string escaped print preview of maximum \a size buffer bytes. * * If the buffer is longer than \a size, "[...]" will be appended to the * result. */ std::string print_preview(unsigned size) const; }; std::ostream& operator<<(std::ostream& o, const Buffer& b); } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/thread.cpp0000644000175000017500000000662512231005104016660 0ustar enricoenrico/* * OO encapsulation of Posix threads * * Copyright (C) 2003--2013 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _BSD_SOURCE #include #include #include //using namespace std; namespace wibble { namespace sys { void sleep( int secs ) { #ifdef _WIN32 Sleep( secs * 1000 ); #else ::sleep( secs ); #endif } void usleep( int usecs ) { #ifdef _WIN32 Sleep( usecs / 1000 ); #else ::usleep( usecs ); #endif } #ifdef POSIX void* Thread::Starter(void* parm) { return (static_cast(parm)->main()); } #endif #ifdef _WIN32 unsigned __stdcall Thread::Starter(void* parm) { void* vptemp = reinterpret_cast< Thread* >( parm )->main(); reinterpret_cast< Thread* >( parm )->_result = vptemp; return unsigned( vptemp ); } #endif void Thread::testcancel() { #ifdef POSIX pthread_testcancel(); #endif } void Thread::start() { int res = 1; #ifdef POSIX res = pthread_create(&thread, 0, Starter, this); #endif #ifdef _WIN32 hThread = (HANDLE)_beginthreadex(NULL, 0, &Starter, this, 0, &thread); if(hThread != NULL) res = 0; #endif if (res != 0) throw wibble::exception::System(res, std::string("Creating ") + threadTag() + " thread"); } void Thread::startDetached() { #ifdef POSIX pthread_attr_t thread_attrs; pthread_attr_init(&thread_attrs); pthread_attr_setdetachstate(&thread_attrs, PTHREAD_CREATE_DETACHED); int res = pthread_create(&thread, &thread_attrs, Starter, this); pthread_attr_destroy(&thread_attrs); if (res != 0) throw wibble::exception::System(res, std::string("Creating ") + threadTag() + " thread"); #endif } void* Thread::join() { void* result = 0; int res = 1; #ifdef POSIX res = pthread_join(thread, &result); #endif #ifdef _WIN32 if(GetCurrentThreadId() != thread) if(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0) { res = 0; CloseHandle(hThread); result = _result; } #endif if (res != 0) throw wibble::exception::System(res, std::string("Joining ") + threadTag() + " thread"); return result; } void Thread::detach() { #ifdef POSIX int res = pthread_detach(thread); if (res != 0) throw wibble::exception::System(res, std::string("Detaching ") + threadTag() + " thread"); #endif } void Thread::cancel() { #ifdef POSIX int res = pthread_cancel(thread); if (res != 0) throw wibble::exception::System(res, std::string("Cancelling ") + threadTag() + " thread"); #endif } void Thread::kill(int signal) { #ifdef POSIX int res = pthread_kill(thread, signal); if (res != 0) { std::stringstream str; str << "Killing " << threadTag() << " thread with signal " << signal; throw wibble::exception::System(res, str.str()); } #endif } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/filelock.cpp0000644000175000017500000000120512231054560017200 0ustar enricoenrico#include #include #include #include #include #include #ifdef POSIX namespace wibble { namespace sys { namespace fs { FileLock::FileLock(int fd, short l_type, short l_whence, off_t l_start, off_t l_len) : fd(fd) { lock.l_type = l_type; lock.l_whence = l_whence; lock.l_start = l_start; lock.l_len = l_len; if (fcntl(fd, F_SETLKW, &lock) == -1) throw wibble::exception::System("locking file"); } FileLock::~FileLock() { lock.l_type = F_UNLCK; fcntl(fd, F_SETLK, &lock); } } } } #endif // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/netbuffer.test.h0000644000175000017500000000362211075402032020014 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include using namespace std; using namespace wibble::sys; struct TestNetBuffer { // Ensure that a plain NetBuffer starts from the beginning of the string Test startAtBeginning() { NetBuffer buf("antani", 6); assert_eq(buf.size(), 6u); assert(memcmp(buf.data(), "antani", 6) == 0); assert_eq(*buf.cast(), 'a'); assert(buf.fits()); assert(!buf.fits()); } // Try skipping some bytes Test skipBytes() { NetBuffer buf("antani", 6); NetBuffer buf1(buf); assert_eq(buf1.size(), 6u); assert(memcmp(buf1.data(), "antani", 6) == 0); buf1 += 2; assert_eq(buf1.size(), 4u); assert(memcmp(buf1.data(), "tani", 4) == 0); assert_eq(*buf1.cast(), 't'); buf1 = buf + 2; assert_eq(buf1.size(), 4u); assert(memcmp(buf1.data(), "tani", 4) == 0); assert_eq(*buf1.cast(), 't'); buf1 = buf; buf1.skip(2); assert_eq(buf1.size(), 4u); assert(memcmp(buf1.data(), "tani", 4) == 0); assert_eq(*buf1.cast(), 't'); buf1 = buf.after(2); assert_eq(buf1.size(), 4u); assert(memcmp(buf1.data(), "tani", 4) == 0); assert_eq(*buf1.cast(), 't'); buf1 = buf; buf1.skip(); assert_eq(buf1.size(), 4u); assert(memcmp(buf1.data(), "tani", 4) == 0); assert_eq(*buf1.cast(), 't'); buf1 = buf.after(); assert_eq(buf1.size(), 4u); assert(memcmp(buf1.data(), "tani", 4) == 0); assert_eq(*buf1.cast(), 't'); } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/buffer.cpp0000644000175000017500000000530712222656032016672 0ustar enricoenrico/* * Variable-size, reference-counted memory buffer * * Copyright (C) 2003--2013 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include // memcpy #include // malloc, free, realloc namespace wibble { namespace sys { Buffer::Data::Data(size_t size) : _ref(0), _size(size) { _data = malloc(size); } Buffer::Data::Data(void* buf, size_t size, bool own) : _ref(0), _size(size) { if (own) _data = buf; else { _data = malloc(size); memcpy(_data, buf, size); } } Buffer::Data::Data(const void* buf, size_t size) : _ref(0), _size(size) { _data = malloc(size); memcpy(_data, buf, size); } Buffer::Data::~Data() { if (_data) free(_data); } void Buffer::Data::resize(size_t size) { if (size == 0) { if (_data) { free(_data); _data = 0; } } else if (_data == 0) { _data = malloc(size); } else { _data = realloc(_data, size); } _size = size; } /// Compare the contents of two buffers bool Buffer::Data::operator==(const Data& d) const throw() { if (_size != d._size) return false; if (_data == 0 && d._data == 0) return true; if (_data == 0 || d._data == 0) return false; return memcmp(_data, d._data, _size) == 0; } /// Compare the contents of two buffers bool Buffer::Data::operator<(const Data& d) const throw() { if (_size < d._size) return true; if (_size > d._size) return false; if (_data == 0 && d._data == 0) return false; if (_data == 0) return true; if (d._data == 0) return false; return memcmp(_data, d._data, _size) < 0; } std::string Buffer::print_preview(unsigned size) const { if (this->size() > size) { std::string res = str::c_escape(std::string((const char*)data(), 100)); res += "[...]"; return res; } else { return str::c_escape(std::string((const char*)data(), this->size())); } } std::ostream& operator<<(std::ostream& o, const Buffer& b) { return o << b.print_preview(100); } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/thread.h0000644000175000017500000000717612231005104016327 0ustar enricoenrico/* -*- C++ -*- * OO encapsulation of Posix threads * * Copyright (C) 2003--2013 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef WIBBLE_SYS_THREAD_H #define WIBBLE_SYS_THREAD_H #include #include #ifdef POSIX #include #include #endif #ifdef _WIN32 #include #include #endif #include namespace wibble { namespace sys { /// Portable version of sleep void sleep( int secs ); /// Portable version of usleep void usleep( int usecs ); /** * Encapsulates a thread * * FIXME: C++ resource management and thread cancellation * C++ uses the "resource allocation is instantiation" way of managing * resources: when a function exits, either by terminating or because * an exception has been raised, destructors are called. * * However, when a thread is canceled, the flow of control is * interrupted and no exceptions are thrown. Cleanup should be * performed by registering cleanup functions, but a cleanup function * can't usefully throw exceptions nor call destructors. * At the moment, I don't know what to do to correctly release * resources on thread cancellation. I'm waiting for new ideas. * * The current way out is not to use cancelation, but explicitly set * some boolean exit condition: * * \code * class MyThread * { * bool interrupted; * void* main() * { * // do things * if (interrupted) * throw MyInterruptedException(); * // do things * while (!interrupted) * { * // do things * } * } * MyThread() : interrupted(false) {} * } * \endcode */ class Thread { protected: #ifdef POSIX pthread_t thread; #endif #ifdef _WIN32 void *_result; unsigned int thread; HANDLE hThread; #endif /** * Short tag describing this thread, used in error messages and * identification */ virtual const char* threadTag() { return "generic"; } /** Main thread function, executed in the new thread after creation. * When main() exits, the new thread ends and main() result will be the * thread exit result */ virtual void* main() = 0; /// Callback function used to start the thread #ifdef POSIX static void* Starter(void* parm); #endif #ifdef _WIN32 static unsigned __stdcall Starter(void* parm); #endif void testcancel(); public: virtual ~Thread() {} /// Start the thread void start(); /// Start the thread in the detached state void startDetached(); /// Join the thread void* join(); /// Put the thread in the detached state void detach(); /// Send a cancellation request to the thread void cancel(); /// Sent a signal to the thread void kill(int signal); }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/signal.test.h0000644000175000017500000000206312231005104017301 0ustar enricoenrico/* -*- C++ -*- (c) 2009 Enrico Zini */ #include #include #include #include #include using namespace std; using namespace wibble::sys; static int counter; static void test_signal_action(int signum) { ++counter; } struct TestSignal { Test sigAction() { #ifdef POSIX struct sigaction a; a.sa_handler = test_signal_action; sigemptyset(&a.sa_mask); a.sa_flags = 0; counter = 0; sig::Action act(SIGUSR1, a); kill(getpid(), SIGUSR1); assert_eq(counter, 1); #endif } Test sigProcMask() { #ifdef POSIX sigset_t blocked; struct sigaction a; a.sa_handler = test_signal_action; sigemptyset(&a.sa_mask); a.sa_flags = 0; sigemptyset(&blocked); sigaddset(&blocked, SIGUSR1); counter = 0; sig::Action act(SIGUSR1, a); { sig::ProcMask mask(blocked); kill(getpid(), SIGUSR1); assert_eq(counter, 0); } assert_eq(counter, 1); #endif } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/mutex.h-darcs-backup00000644000175000017500000001251311372245054020645 0ustar enricoenrico#ifndef WIBBLE_SYS_MUTEX_H #define WIBBLE_SYS_MUTEX_H /* * Encapsulated pthread mutex and condition * * Copyright (C) 2003--2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace wibble { namespace sys { /** * pthread mutex wrapper */ class Mutex { private: // Disallow copy Mutex(const Mutex&); Mutex& operator=(const Mutex&); protected: pthread_mutex_t mutex; public: Mutex(bool recursive = false) { pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); if ( recursive ) { #ifdef __APPLE__ pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); #else pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE_NP ); #endif } if (int res = pthread_mutex_init(&mutex, &attr)) throw wibble::exception::System(res, "creating pthread mutex"); } ~Mutex() { if (int res = pthread_mutex_destroy(&mutex)) throw wibble::exception::System(res, "destroying pthread mutex"); } bool trylock() { int res = pthread_mutex_trylock(&mutex); if ( res == EBUSY ) return false; if ( res == 0 ) return true; throw wibble::exception::System(res, "(try)locking pthread mutex"); } /// Lock the mutex /// Normally it's better to use MutexLock void lock() { if (int res = pthread_mutex_lock(&mutex)) throw wibble::exception::System(res, "locking pthread mutex"); } /// Unlock the mutex /// Normally it's better to use MutexLock void unlock() { if (int res = pthread_mutex_unlock(&mutex)) throw wibble::exception::System(res, "unlocking pthread mutex"); } /// Reinitialize the mutex void reinit() { if (int res = pthread_mutex_init(&mutex, 0)) throw wibble::exception::System(res, "reinitialising pthread mutex"); } friend class Condition; }; /** * Acquire a mutex lock, RAII-style */ template< typename Mutex > class MutexLockT { private: // Disallow copy MutexLockT(const MutexLockT&); MutexLockT& operator=(const MutexLockT&); protected: Mutex& mutex; bool locked; bool yield; public: MutexLockT(Mutex& m) : mutex(m), locked( false ), yield( false ) { mutex.lock(); locked = true; } ~MutexLockT() { if ( locked ) { mutex.unlock(); checkYield(); } } void drop() { mutex.unlock(); locked = false; checkYield(); } void reclaim() { mutex.lock(); locked = true; } void setYield( bool y ) { yield = y; } void checkYield() { if ( yield ) sched_yield(); } friend class Condition; }; typedef MutexLockT< Mutex > MutexLock; /* * pthread condition wrapper. * * It works in association with a MutexLock. */ class Condition { private: // Disallow copy Condition(const Condition&); Condition& operator=(const Condition&); protected: pthread_cond_t cond; public: Condition() { if (int res = pthread_cond_init(&cond, 0)) throw wibble::exception::System(res, "creating pthread condition"); } ~Condition() { if (int res = pthread_cond_destroy(&cond)) throw wibble::exception::System(res, "destroying pthread condition"); } /// Wake up one process waiting on the condition void signal() { if (int res = pthread_cond_signal(&cond)) throw wibble::exception::System(res, "signaling on a pthread condition"); } /// Wake up all processes waiting on the condition void broadcast() { if (int res = pthread_cond_broadcast(&cond)) throw wibble::exception::System(res, "broadcasting on a pthread condition"); } /** * Wait on the condition, locking with l. l is unlocked before waiting and * locked again before returning. */ void wait(MutexLock& l) { if (int res = pthread_cond_wait(&cond, &l.mutex.mutex)) throw wibble::exception::System(res, "waiting on a pthread condition"); } void wait(Mutex& l) { if (int res = pthread_cond_wait(&cond, &l.mutex)) throw wibble::exception::System(res, "waiting on a pthread condition"); } /** * Wait on the condition, locking with l. l is unlocked before waiting and * locked again before returning. If the time abstime is reached before * the condition is signaled, then l is locked and the function returns * false. * * @returns * true if the wait succeeded, or false if the timeout was reached before * the condition is signaled. */ bool wait(MutexLock& l, const struct timespec& abstime); }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/mmap.cpp0000644000175000017500000000653512231005104016343 0ustar enricoenrico/* * Simple mmap support * * Copyright (C) 2006--2008 Enrico Zini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #ifdef POSIX #include #include #include #include #include #include #include #include #include using namespace std; namespace wibble { namespace sys { MMap::MMap() : size(0), fd(-1), buf(0) {} MMap::MMap(const std::string& filename) : size(0), fd(-1), buf(0) { // If map throws here, the destructor isn't called (since we're in the // constructor), so we do the cleanup and rethrow. try { map(filename); } catch (...) { unmap(); throw; } } MMap::MMap(const MMap& mmap) : filename(mmap.filename), size(mmap.size), fd(mmap.fd), buf(mmap.buf) { // Cast away const to have auto_ptr semantics MMap* wm = const_cast(&mmap); wm->filename.clear(); wm->size = 0; wm->fd = -1; wm->buf = 0; } MMap& MMap::operator=(const MMap& mmap) { // Handle assignment to self if (this == &mmap) return *this; if (fd) unmap(); filename = mmap.filename; size = mmap.size; fd = mmap.fd; buf = mmap.buf; // Cast away const to have auto_ptr semantics MMap* wm = const_cast(&mmap); wm->filename.clear(); wm->size = 0; wm->fd = -1; wm->buf = 0; return *this; } MMap::~MMap() { unmap(); } void MMap::map(const std::string& filename) { if (buf) unmap(); try { this->filename = filename; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" // Open the file if ((fd = open(filename.c_str(), O_RDONLY)) == -1) throw wibble::exception::System("opening index file " + filename); size = lseek(fd, 0, SEEK_END); if (size == static_cast< off_t >(-1)) throw wibble::exception::System("reading the size of index file " + filename); if (size == 0) throw wibble::exception::Consistency("ensuring that there is data in the index", "the mmap index file " + filename + " is empty"); // Map the file into memory if ( ( buf = reinterpret_cast< const char* >( ::mmap( 0, size, PROT_READ, MAP_PRIVATE, fd, 0 ) ) ) == MAP_FAILED ) throw wibble::exception::System("mmapping file " + filename); #pragma GCC diagnostic pop } catch (...) { unmap(); throw; } } void MMap::unmap() { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" // Unmap and close the file if (buf) { if (buf != MAP_FAILED) munmap(const_cast(buf), size); buf = 0; size = 0; } if (fd != -1) { close(fd); fd = -1; } filename.clear(); #pragma GCC diagnostic pop } } } #endif // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/buffer.test.h0000644000175000017500000000424212231454233017311 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include using namespace std; using namespace wibble::sys; struct TestBuffer { Test emptiness() { Buffer buf; assert_eq(buf.size(), 0u); assert_eq(buf.data(), static_cast(0)); // Empty buffers should be equal Buffer buf1; assert(buf == buf); assert(buf == buf1); assert(!(buf < buf1)); assert(!(buf1 < buf)); } Test nonemptiness() { // Nonempty buffers should be properly nonempty Buffer buf(1); (static_cast(buf.data()))[0] = 'a'; assert_eq(buf.size(), 1u); assert(buf.data() != 0); // Nonempty buffers should compare by content Buffer buf1(1); (static_cast(buf1.data()))[0] = 'z'; assert(buf == buf); assert(buf1 == buf1); assert(!(buf == buf1)); assert(buf != buf1); assert(buf < buf1); assert(!(buf1 < buf)); (static_cast(buf1.data()))[0] = 'a'; assert(buf == buf1); assert(!(buf != buf1)); assert(!(buf < buf1)); assert(!(buf1 < buf)); // Empty buffers should come before the nonempty ones Buffer buf2; assert(!(buf == buf2)); assert(buf != buf2); assert(!(buf < buf2)); assert(buf2 < buf); } // Construct by copy should work Test copy() { const char* str = "Ciao"; Buffer buf(str, 4); assert_eq(buf.size(), 4u); assert(memcmp(str, buf.data(), 4) == 0); } // Resize should work and preserve the contents Test resize() { const char* str = "Ciao"; Buffer buf(str, 4); assert_eq(buf.size(), 4u); assert(memcmp(str, buf.data(), 4) == 0); buf.resize(8); assert_eq(buf.size(), 8u); assert(memcmp(str, buf.data(), 4) == 0); } // Check creation by taking ownership of another buffer Test takeover() { char* str = (char*)malloc(4); memcpy(str, "ciao", 4); Buffer buf(str, 4, true); assert_eq(buf.size(), 4u); assert_eq(static_cast(str), buf.data()); } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/mmap_v2.h0000644000175000017500000000505412231005104016412 0ustar enricoenrico// -*- C++ -*- (c) 2013 Vladimír Štill /* mmap support using C++11 * * mmaped file can be shared accross threads without memory overhead, * but obviously it is not therad safe. It has shared_ptr semantics. * * redistributable under BSD licence */ #if __cplusplus < 201103L #error "mmap_v2 is only supported with c++11 or newer" #endif #include #include #include #ifndef WIBBLE_SYS_MMAP_V2 #define WIBBLE_SYS_MMAP_V2 namespace wibble { namespace sys { inline namespace v2 { struct MMap { enum class ProtectMode { Read = 0x1, Write = 0x2, Execute = 0x4, Shared = 0x8, Private = 0x10 }; #define DEFAULT_MODE (ProtectMode::Read | ProtectMode::Shared) using ProtectModeFlags = StrongEnumFlags< ProtectMode >; constexpr const static ProtectModeFlags defaultMode = DEFAULT_MODE; MMap() : _size( 0 ) { } MMap( const std::string &, ProtectModeFlags = DEFAULT_MODE ); MMap( int fd, ProtectModeFlags ); void map( const std::string &, ProtectModeFlags = DEFAULT_MODE ); void map( int fd, ProtectModeFlags = DEFAULT_MODE ); void unmap(); #undef DEFAULT_MODE size_t size() { return _size; } explicit operator bool() { return bool( _ptr ); } bool valid() { return bool( _ptr ); } ProtectModeFlags mode() { return _flags; } // get value on begining offset bites template< typename T > T &get( size_t offset ) { return *reinterpret_cast< T * >( reinterpret_cast< char * >( _ptr.get() ) + offset ); } template< typename T > const T &cget( size_t offset ) const { return *reinterpret_cast< T * >( reinterpret_cast< char * >( _ptr.get() ) + offset ); } template< typename T > const T &get( size_t offset ) const { return cget< T >( offset ); } template< typename T > T *asArrayOf() { return reinterpret_cast< T * >( _ptr.get() ); } template< typename T > const T *asConstArrayOf() const { return reinterpret_cast< const T * >( _ptr.get() ); } template< typename T > const T *asArrayOf() const { return asConstArrayOf< T >(); } char &operator[]( size_t offset ) { return asArrayOf< char >()[ offset ]; } const char &operator[]( size_t offset ) const { return asArrayOf< char >()[ offset ]; } private: std::shared_ptr< void > _ptr; ProtectModeFlags _flags; size_t _size; void _map( int ); void _map( const std::string & ); }; } } } #endif // WIBBLE_SYS_MMAP_V2 libwibble-1.1/wibble/sys/macros.h0000644000175000017500000000023311404011104016325 0ustar enricoenrico#if !defined(POSIX) && !defined(_WIN32) #ifdef WIN32 #define _WIN32 #endif #ifdef __unix__ #define POSIX #endif #ifdef __xlC__ #define POSIX #endif #endif libwibble-1.1/wibble/sys/exec.cpp0000644000175000017500000000540512231005104016330 0ustar enricoenrico/* * OO wrapper for execve * * Copyright (C) 2003 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include // strdup #include // execve #include // alloca on win32 seems to live there #include extern char **environ; namespace wibble { namespace sys { int Exec::main() { try { exec(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 0; } void Exec::importEnv() { for (char** s = environ; *s; ++s) env.push_back(*s); } void Exec::spawnChild() { #ifdef _WIN32 std::string cmd = pathname; for ( int i = 1; i < args.size(); ++i ) cmd += " \"" + args[i] + "\""; // FIXME: quoting... std::cerr << "CreateProcess: " << cmd << std::endl; CreateProcess( NULL, (char*)cmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ); #endif } void Exec::exec() { // Prepare the argument list char** exec_args = new char*[args.size() + 1]; for (size_t i = 0; i < args.size(); ++i) exec_args[i] = strdup(args[i].c_str()); exec_args[args.size()] = 0; char** exec_env = environ; if (!envFromParent) { // Prepare the custom environment exec_env = new char*[env.size() + 1]; for (size_t i = 0; i < env.size(); ++i) // We can just store a pointer to the internal strings, since later // we're calling exec and no destructors will be called exec_env[i] = strdup(env[i].c_str()); exec_env[env.size()] = 0; } if (searchInPath) { if (execvpe(pathname.c_str(), exec_args, exec_env) == -1) throw wibble::exception::System("trying to run " + pathname); } else { if (execve(pathname.c_str(), exec_args, exec_env) == -1) throw wibble::exception::System("trying to run " + pathname); } delete[] exec_args; if (exec_env != environ) delete[] exec_env; throw wibble::exception::Consistency( "trying to run " + pathname, "Program flow continued after successful exec()"); } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/filelock.h0000644000175000017500000000172212231054545016654 0ustar enricoenrico#ifndef WIBBLE_SYS_FILELOCK_H #define WIBBLE_SYS_FILELOCK_H #include #ifdef POSIX #include namespace wibble { namespace sys { namespace fs { /** * RAII fcntl advisory file lock * * See fcntl(2) for details. */ struct FileLock { int fd; struct flock lock; /** * Create the lockfile with the given name. * * \a lock will be initialised with the parameters and used to unlock * in the destructor. Please feel free to change the contents of the \a * lock structure if you need a different part to be unlocked. * * @param write * If false, use a read lock, else a write lock. */ FileLock(int fd, short l_type, short l_whence=SEEK_SET, off_t l_start=0, off_t l_len=0); /** * Unlocks using the values in \a lock */ ~FileLock(); private: // Disallow copying FileLock(const FileLock&); FileLock& operator=(const FileLock&); }; } } } // vim:set ts=4 sw=4: #endif #endif libwibble-1.1/wibble/sys/lockfile.h0000644000175000017500000000130211463032575016653 0ustar enricoenrico#ifndef WIBBLE_SYS_LOCKFILE_H #define WIBBLE_SYS_LOCKFILE_H #include #include namespace wibble { namespace sys { namespace fs { /** * RAII lock file * * It is implemented using fcntl, so that it should also work over network file * systems. It should work at least on NFS and GFS. */ struct Lockfile { std::string name; int fd; /** * Create the lockfile with the given name. * * @param write * If false, use a read lock, else a write lock. */ Lockfile(const std::string& name, bool write = true); ~Lockfile(); private: // Disallow copying Lockfile(const Lockfile&); Lockfile& operator=(const Lockfile&); }; } } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/mmap_v2.cpp0000644000175000017500000000456112231005104016747 0ustar enricoenrico#if __cplusplus >= 201103L #include #include #include #include #include #include #include using namespace wibble::sys; using wibble::operator|; int mmapProt( MMap::ProtectModeFlags flags ) { int prot = 0; if ( flags.has( MMap::ProtectMode::Read ) ) prot |= PROT_READ; if ( flags.has( MMap::ProtectMode::Write ) ) prot |= PROT_WRITE; if ( flags.has( MMap::ProtectMode::Execute ) ) prot |= PROT_EXEC; return prot; } int mmapFlags( MMap::ProtectModeFlags flags ) { int mf = 0; if ( flags.has( MMap::ProtectMode::Shared ) ) mf |= MAP_SHARED; if ( flags.has( MMap::ProtectMode::Private ) ) mf |= MAP_PRIVATE; return mf; } int openFlags( MMap::ProtectModeFlags flags ) { if ( flags.has( MMap::ProtectMode::Read ) && flags.has( MMap::ProtectMode::Write ) ) return O_RDWR; if ( flags.has( MMap::ProtectMode::Read ) ) return O_RDONLY; if ( flags.has( MMap::ProtectMode::Write ) ) return O_WRONLY; } MMap::MMap( const std::string &file, ProtectModeFlags flags ) : _flags( flags ), _size( 0 ) { _map( file ); } MMap::MMap( int fd, ProtectModeFlags flags ) : _flags( flags ), _size( 0 ) { _map( fd ); } void MMap::map( const std::string &file, ProtectModeFlags flags ) { _flags = flags; _map( file ); } void MMap::map( int fd, ProtectModeFlags flags ) { _flags = flags; _map( fd ); } void MMap::unmap() { _ptr = nullptr; _size = 0; } void MMap::_map( const std::string &file ) { int fd = ::open( file.c_str(), openFlags( _flags ) ); if ( fd < 0 ) throw wibble::exception::System( "opening file failed: " + file ); _map( fd ); } void MMap::_map( int fd ) { struct stat st; if ( fstat( fd, &st ) != 0 ) throw wibble::exception::System( "stat failed while mmaping" ); size_t size = _size = st.st_size; void *ptr = ::mmap( nullptr, _size, mmapProt( _flags ), mmapFlags( _flags ), fd, 0 ); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" if ( ptr == MAP_FAILED ) throw wibble::exception::System( "mmaping file failed" ); #pragma GCC diagnostic pop _ptr = std::shared_ptr< void >( ptr, [ fd, size ]( void *h ) { ::munmap( h, size ); ::close( fd ); } ); } #endif libwibble-1.1/wibble/sys/signal.cpp0000644000175000017500000000016111225337650016673 0ustar enricoenrico#include namespace wibble { namespace sys { namespace sig { } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/mmap_v2.test.h0000644000175000017500000000272412231005104017371 0ustar enricoenrico// -*- C++ -*- (c) 2013 Vladimír Štill #if __cplusplus >= 201103L #include using namespace wibble::sys; #endif #include #include using namespace std; using namespace wibble; struct TestMMapV2 { Test read() { #if defined POSIX && __cplusplus >= 201103L MMap map; assert_eq( map.size(), 0U ); assert( !map ); assert( !map.valid() ); assert( !map.mode() ); map.map( "/bin/sh" ); assert_neq( map.size(), 0U ); assert_eq( map.mode(), MMap::ProtectMode::Read | MMap::ProtectMode::Shared ); assert( map.valid() ); assert_eq( map[ 1 ], 'E' ); assert_eq( map[ 2 ], 'L' ); assert_eq( map[ 3 ], 'F' ); MMap map1 = map; // shared_ptr semantics assert_eq( map.size(), map.size() ); assert_eq( map.asArrayOf< char >(), map1.asArrayOf< char >() ); assert_eq( map.mode(), map1.mode() ); assert_eq( map1.get< char >( 1 ), 'E' ); assert_eq( map1.get< char >( 2 ), 'L' ); assert_eq( map1.get< char >( 3 ), 'F' ); map1.unmap(); assert_eq( map1.size(), 0U ); assert( !map1 ); assert_eq( map.cget< char >( 1 ), 'E' ); assert_eq( map.cget< char >( 2 ), 'L' ); assert_eq( map.cget< char >( 3 ), 'F' ); assert( map.valid() ); map.unmap(); assert_eq( map.size(), 0U ); assert( !map ); #endif } }; libwibble-1.1/wibble/sys/lockfile.test.h0000644000175000017500000000114512231005104017614 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include #include using namespace std; using namespace wibble::sys::fs; struct TestLockfile { // Cannot test the locks without forking, as reacquiring the lock from the // same process is just an update of the previous lock Test readlock() { #ifdef POSIX Lockfile lk1("testlock", false); #endif } Test writelock() { #ifdef POSIX Lockfile lk1("testlock", true); #endif } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/netbuffer.h0000644000175000017500000001037612231005104017034 0ustar enricoenrico#ifndef WIBBLE_SYS_NETBUFFER_H #define WIBBLE_SYS_NETBUFFER_H /* * Variable-size, reference-counted memory buffer used to access network * packets * * Copyright (C) 2003--2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace wibble { namespace sys { /** * Buffer whose starting can be moved back and forth, useful to decapsulate * stacked network packets */ class NetBuffer : public Buffer { public: /** * Offset in bytes of the NetBuffer start, from the beginning of the memory * area we manage */ size_t cursor; public: NetBuffer() throw () : Buffer(), cursor(0) {} NetBuffer(size_t size) : Buffer(size), cursor(0) {} NetBuffer(void* buf, size_t size, bool own = true) : Buffer(buf, size, own), cursor(0) {} NetBuffer(const void* buf, size_t size) : Buffer(buf, size), cursor(0) {} NetBuffer(const Buffer& buf) throw () : Buffer(buf), cursor(0) {} NetBuffer(const NetBuffer& buf) throw () : Buffer(buf), cursor(buf.cursor) {} NetBuffer& operator=(const Buffer& buf) { Buffer::operator=(buf); cursor = 0; return *this; } NetBuffer& operator=(const NetBuffer& buf) { Buffer::operator=(buf); cursor = buf.cursor; return *this; } /// Return a pointer to the buffer const void* data(size_t ofs = 0) const throw () { return static_cast(Buffer::data()) + cursor + ofs; } /// Return a pointer to the buffer void* data(size_t ofs = 0) throw () { return static_cast(Buffer::data()) + cursor + ofs; } /// Return the buffer size size_t size() const throw () { return Buffer::size() - cursor; } /** * Check if the buffer is long enough to contain a structure T at the given * offset */ template bool fits(size_t ofs = 0) const throw () { return cursor + ofs + sizeof(T) < size(); } /** * Access the buffer contents as a structure T at the given offset */ template const T* cast(size_t ofs = 0) const throw (wibble::exception::Consistency) { if (cursor + ofs + sizeof(T) >= size()) throw wibble::exception::Consistency("reading from buffer", "tried to read past the end of the buffer"); return static_cast(data(ofs)); } /** * Return another NetBuffer starting ofs bytes from the beginning of this one */ NetBuffer operator+(size_t ofs) throw (wibble::exception::Consistency) { return after(ofs); } /** * Return another NetBuffer starting ofs bytes from the beginning of this one */ const NetBuffer after(size_t ofs) const throw (wibble::exception::Consistency) { NetBuffer res(*this); res.skip(ofs); return res; } /** * Return another NetBuffer starting just after sizeof(T) from the * beginning of this one */ template const NetBuffer after() const throw (wibble::exception::Consistency) { NetBuffer res(*this); res.skip(sizeof(T)); return res; } /** * Move the starting point of this buffer ofs bytes from the beginning */ NetBuffer& operator+=(size_t ofs) throw (wibble::exception::Consistency) { skip(ofs); return *this; } /** * Move the starting point of this buffer sizeof(T) bytes from the * beginning */ template void skip() throw (wibble::exception::Consistency) { skip(sizeof(T)); } /** * Move the starting point of this buffer ofs bytes from the beginning */ void skip(size_t t) throw (wibble::exception::Consistency) { if (cursor + t >= size()) throw wibble::exception::Consistency("reading from buffer", "tried to skip past the end of the buffer"); cursor += t; } }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/pipe.h0000644000175000017500000001305312231005104016004 0ustar enricoenrico// -*- C++ -*- (c) 2008 Petr Rockai #include #ifdef POSIX #include #include #endif #include #include #include #include #include #include #include #ifndef WIBBLE_SYS_PIPE_H #define WIBBLE_SYS_PIPE_H namespace wibble { namespace sys { namespace wexcept = wibble::exception; struct Pipe { struct Writer : wibble::sys::Thread { int fd; bool close; std::string data; bool running; bool closed; wibble::sys::Mutex mutex; Writer() : fd( -1 ), close( false ), running( false ) {} void *main() { do { int wrote = 0; { wibble::sys::MutexLock __l( mutex ); wrote = ::write( fd, data.c_str(), data.length() ); if ( wrote > 0 ) data.erase( data.begin(), data.begin() + wrote ); } if ( wrote == -1 ) { if ( blocking( errno ) ) #ifdef POSIX sched_yield(); #else ; #endif else throw wexcept::System( "writing to pipe" ); } } while ( !done() ); wibble::sys::MutexLock __l( mutex ); running = false; if ( close ) ::close( fd ); return 0; } bool done() { wibble::sys::MutexLock __l( mutex ); if ( data.empty() ) running = false; return !running; } void run( int _fd, std::string what ) { wibble::sys::MutexLock __l( mutex ); if ( running ) assert_eq( _fd, fd ); fd = _fd; assert_neq( fd, -1 ); data += what; if ( running ) return; running = true; start(); } }; typedef std::deque< char > Buffer; Buffer buffer; int fd; bool _eof; Writer writer; Pipe( int p ) : fd( p ), _eof( false ) { if ( p == -1 ) return; #ifdef POSIX if ( fcntl( fd, F_SETFL, O_NONBLOCK ) == -1 ) throw wexcept::System( "fcntl on a pipe" ); #endif } Pipe() : fd( -1 ), _eof( false ) {} /* Writes data to the pipe, asynchronously. */ void write( std::string what ) { writer.run( fd, what ); } void close() { wibble::sys::MutexLock __l( writer.mutex ); writer.close = true; if ( !writer.running ) ::close( fd ); } bool valid() { return fd != -1; } bool active() { return valid() && !eof(); } bool eof() { return _eof; } static bool blocking( int err ) { #ifdef POSIX return err == EAGAIN || err == EWOULDBLOCK; #else return err == EAGAIN; #endif } int readMore() { assert( valid() ); char _buffer[1024]; int r = ::read( fd, _buffer, 1023 ); if ( r == -1 && !blocking( errno ) ) throw wexcept::System( "reading from pipe" ); else if ( r == -1 ) return 0; if ( r == 0 ) _eof = true; else std::copy( _buffer, _buffer + r, std::back_inserter( buffer ) ); return r; } std::string nextChunk() { std::string line( buffer.begin(), buffer.end() ); buffer.clear(); return line; } std::string nextLine() { assert( valid() ); Buffer::iterator nl = std::find( buffer.begin(), buffer.end(), '\n' ); while ( nl == buffer.end() ) { if ( !readMore() ) return ""; // would block, so give up nl = std::find( buffer.begin(), buffer.end(), '\n' ); } std::string line( buffer.begin(), nl ); if ( nl != buffer.end() ) ++ nl; buffer.erase( buffer.begin(), nl ); return line; } /* Only returns on eof() or when data is buffered. */ void wait() { assert( valid() ); #ifdef POSIX fd_set fds; FD_ZERO( &fds ); #endif while ( buffer.empty() && !eof() ) { if ( readMore() ) return; if ( eof() ) return; #ifdef POSIX #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" FD_SET( fd, &fds ); select( fd + 1, &fds, 0, 0, 0 ); #pragma GCC diagnostic pop #else sleep( 1 ); #endif } } std::string nextLineBlocking() { assert( valid() ); std::string l; while ( !eof() ) { l = nextLine(); if ( !l.empty() ) return l; if ( eof() ) return std::string( buffer.begin(), buffer.end() ); wait(); } return l; } }; struct PipeThrough { std::string cmd; PipeThrough( const std::string& _cmd ) : cmd( _cmd ) {} std::string run( std::string data ) { int _in, _out; #ifdef _WIN32 Exec exec(cmd); #elif defined POSIX ShellCommand exec(cmd); #endif exec.setupRedirects( &_in, &_out, 0 ); exec.fork(); Pipe in( _in ), out( _out ); in.write( data ); in.close(); std::string ret; while ( !out.eof() ) { out.wait(); ret += out.nextChunk(); } return ret; } }; } } #endif libwibble-1.1/wibble/sys/childprocess.test.h0000644000175000017500000000642412231005104020513 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include #include #include #include #include #include using namespace std; using namespace wibble::sys; #ifdef _WIN32 #define sleep Sleep #endif class EndlessChild : public ChildProcess { protected: int main() { while (true) sleep(60); return 0; } }; class TestChild : public ChildProcess { protected: int main() { cout << "antani" << endl; return 0; } }; std::string suckFd(int fd) { std::string res; char c; while (true) { int r = read(fd, &c, 1); if (r == 0) break; if (r < 0) throw wibble::exception::System("reading data from file descriptor"); res += c; } return res; } struct TestChildprocess { // Try running the child process and kill it Test kill() { #ifdef POSIX EndlessChild child; // Start the child pid_t pid = child.fork(); // We should get a nonzero pid assert(pid != 0); // Send SIGQUIT child.kill(2); // Wait for the child to terminate int res = child.wait(); // Check that it was indeed terminated by signal 2 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" assert(WIFSIGNALED(res)); assert_eq(WTERMSIG(res), 2); #pragma GCC diagnostic pop #endif } // Try getting the output of the child process Test output() { #ifdef POSIX TestChild child; int out; // Fork the child redirecting its stdout pid_t pid = child.forkAndRedirect(0, &out, 0); assert(pid != 0); // Read the child output assert_eq(suckFd(out), "antani\n"); // Wait for the child to terminate assert_eq(child.wait(), 0); #endif } Test redirect() { Exec child("echo"); child.searchInPath = true; child.args.push_back("antani"); int out; // Fork the child redirecting its stdout pid_t pid = child.forkAndRedirect(0, &out, 0); assert(pid != 0); // Read the child output assert_eq(suckFd(out), "antani\n"); // Wait for the child to terminate assert_eq(child.wait(), 0); } Test shellCommand() { ShellCommand child("A=antani; echo $A"); int out; // Fork the child redirecting its stdout pid_t pid = child.forkAndRedirect(0, &out, 0); assert(pid != 0); // Read the child output assert_eq(suckFd(out), "antani\n"); // Wait for the child to terminate assert_eq(child.wait(), 0); } Test inout() { Exec child("cat"); child.searchInPath = true; int in, out; // Fork the child redirecting its stdout child.forkAndRedirect(&in, &out, 0); // assert(pid != 0); write(in, "hello\n", 6); close(in); // Read the child output assert_eq(suckFd(out), "hello\n"); // Wait for the child to terminate assert_eq(child.wait(), 0); } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/signal.h0000644000175000017500000000175512231005104016332 0ustar enricoenrico// -*- C++ -*- #ifndef WIBBLE_SYS_SIGNAL_H #define WIBBLE_SYS_SIGNAL_H #include #include #include namespace wibble { namespace sys { namespace sig { #ifdef POSIX /** * RAII-style sigprocmask wrapper */ struct ProcMask { sigset_t oldset; ProcMask(const sigset_t& newset, int how = SIG_BLOCK) { if (sigprocmask(how, &newset, &oldset) < 0) throw wibble::exception::System("setting signal mask"); } ~ProcMask() { if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) throw wibble::exception::System("restoring signal mask"); } }; struct Action { int signum; struct sigaction oldact; Action(int signum, const struct sigaction& act) : signum(signum) { if (sigaction(signum, &act, &oldact) < 0) throw wibble::exception::System("setting signal action"); } ~Action() { if (sigaction(signum, &oldact, NULL) < 0) throw wibble::exception::System("restoring signal action"); } }; #endif } } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/childprocess.cpp0000644000175000017500000002065512231005104020072 0ustar enricoenrico/* * OO base class for process functions and child processes * * Copyright (C) 2003--2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include // EXIT_FAILURE #include // fork, waitpid, kill, open, getpw*, getgr*, initgroups #include // open #include // fork, dup2, pipe, close, setsid, _exit, chdir #include // open #ifdef POSIX #include // getrlimit, setrlimit #include // waitpid #include // kill #include // getpw* #include // getgr*, initgroups #endif #include // flockfile, funlockfile #include // is* #include #include namespace wibble { namespace sys { using namespace std; namespace wexcept = wibble::exception; void ChildProcess::spawnChild() { assert_die(); } #ifndef POSIX void funlockfile( FILE * ) {} void flockfile( FILE * ) {} #endif #ifdef POSIX pid_t ChildProcess::fork() { flockfile(stdin); flockfile(stdout); flockfile(stderr); setupPrefork(); pid_t pid; if ((pid = ::fork()) == 0) { // Tell the logging system we're in a new process //Log::Logger::instance()->setupForkedChild(); // Child process try { setupChild(); // no need to funlockfile here, since the library resets the stream // locks in the child after a fork // Call the process main function and return the exit status _exit(main()); } catch (std::exception& e) { //log_err(string(e.type()) + ": " + e.desc()); } _exit(EXIT_FAILURE); } else if (pid < 0) { funlockfile(stdin); funlockfile(stderr); funlockfile(stdout); throw wexcept::System("trying to fork a child process"); } else { funlockfile(stdin); funlockfile(stderr); funlockfile(stdout); _pid = pid; setupParent(); // Parent process return _pid; } } #elif defined _WIN32 pid_t ChildProcess::fork() { setupPrefork(); /* Copy&paste from MSDN... I don't want to know. */ ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); /* End of MSDN. */ spawnChild(); setupParent(); return 1; // FIXME is there anything like a pid on win32? } #endif void mkpipe( int *fds, int *infd, int *outfd, const char *err ) { #ifdef POSIX if (pipe(fds) == -1) #elif defined _WIN32 if (_pipe(fds, 1000, _O_BINARY) == -1) #endif throw wexcept::System( err ); if (infd) *infd = fds[0]; if (outfd) *outfd = fds[1]; } void renamefd( int _old, int _new, const char *err = "..." ) { if ( dup2( _old, _new ) == -1 ) throw wexcept::System( err ); if ( close( _old ) == -1 ) throw wexcept::System( err ); } void ChildProcess::setupRedirects(int* _stdinfd, int* _stdoutfd, int* _stderrfd) { _stdin = _stdinfd; _stdout = _stdoutfd; _stderr = _stderrfd; if (_stdin) mkpipe( pipes[0], 0, _stdin, "trying to create the pipe to connect to child standard input" ); if (_stdout) mkpipe( pipes[1], _stdout, 0, "trying to create the pipe to connect to child standard output" ); if (_stderr && _stderr != _stdout) mkpipe( pipes[2], _stderr, 0, "trying to create the pipe to connect to child standard output" ); } void ChildProcess::setupPrefork() { #ifdef _WIN32 if (_stdin) { backups[0] = dup( STDIN_FILENO ); SetHandleInformation( (HANDLE)_get_osfhandle( pipes[0][1] ), HANDLE_FLAG_INHERIT, 0 ); dup2( pipes[0][0], STDIN_FILENO ); } if (_stdout) { backups[1] = dup( STDOUT_FILENO ); SetHandleInformation( (HANDLE)_get_osfhandle( pipes[1][0] ), HANDLE_FLAG_INHERIT, 0 ); dup2( pipes[1][1], STDOUT_FILENO ); } if ( _stderr ) { backups[2] = dup( STDERR_FILENO ); SetHandleInformation( (HANDLE)_get_osfhandle( pipes[2][0] ), HANDLE_FLAG_INHERIT, 0 ); dup2( pipes[2][1], STDERR_FILENO ); } #endif } void ChildProcess::setupChild() { if (_stdin) { // Redirect input from the parent to _stdin if (close(pipes[0][1]) == -1) throw wexcept::System("closing write end of parent _stdin pipe"); renamefd( pipes[0][0], STDIN_FILENO, "renaming parent _stdin pipe fd" ); } if (_stdout) { // Redirect output to the parent _stdout fd if (close(pipes[1][0]) == -1) throw wexcept::System("closing read end of parent _stdout pipe"); if (_stderr == _stdout) if (dup2(pipes[1][1], STDERR_FILENO) == -1) throw wexcept::System( "dup2-ing _stderr to parent _stdout/_stderr pipe"); renamefd( pipes[1][1], STDOUT_FILENO, "renaming parent _stdout pipe" ); } if (_stderr && _stderr != _stdout) { // Redirect all output to the parent if (close(pipes[2][0]) == -1) throw wexcept::System("closing read end of parent _stderr pipe"); renamefd( pipes[2][1], STDERR_FILENO, "renaming parent _stderr pipe" ); } } void ChildProcess::setupParent() { funlockfile(stdin); funlockfile(stderr); funlockfile(stdout); if (_stdin && close(pipes[0][0]) == -1) throw wexcept::System("closing read end of _stdin child pipe"); if (_stdout && close(pipes[1][1]) == -1) throw wexcept::System("closing write end of _stdout child pipe"); if (_stderr && _stderr != _stdout && close(pipes[2][1]) == -1) throw wexcept::System("closing write end of _stderr child pipe"); #ifdef _WIN32 if (_stdin) dup2( backups[0], STDIN_FILENO ); if (_stdout) dup2( backups[1], STDOUT_FILENO ); if ( _stderr ) dup2( backups[2], STDERR_FILENO ); #endif } void ChildProcess::waitError() { if (errno == EINTR) throw wexcept::Interrupted("waiting for child termination"); else throw wexcept::System("waiting for child termination"); } bool ChildProcess::running() { #ifdef POSIX if ( _pid == -1 ) { return false; } int res = waitpid(_pid, &m_status, WNOHANG); if ( res == -1 ) { waitError(); } if ( !res ) { return true; } return false; #else assert_die(); #endif } int ChildProcess::wait(struct rusage* ru) { #ifdef POSIX if (_pid == -1) return -1; // FIXME: for lack of better ideas if (wait4(_pid, &m_status, 0, ru) == -1) waitError(); #else m_status = 0; // FIXME WaitForSingleObject( pi.hProcess, INFINITE ); #endif _pid = -1; return m_status; } void ChildProcess::waitForSuccess() { int r = wait(); #ifdef POSIX #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" if ( WIFEXITED( r ) ) { if ( WEXITSTATUS( r ) ) throw exception::Generic( str::fmtf( "Subprocess terminated with error %d.", WEXITSTATUS( r ) ) ); else return; } if ( WIFSIGNALED( r ) ) throw exception::Generic( str::fmtf( "Subprocess terminated by signal %d.", WTERMSIG( r ) ) ); throw exception::Generic( "Error waiting for subprocess." ); #pragma GCC diagnostic pop #endif } void ChildProcess::kill(int signal) { #ifdef POSIX if (_pid == -1) throw wexcept::Consistency( "killing child process", "child process has not been started"); if (::kill(_pid, signal) == -1) { stringstream str; str << "killing process " << _pid; throw wibble::exception::System(str.str()); } #else assert_die(); #endif } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/mmap.h0000644000175000017500000000304612231005104016002 0ustar enricoenrico#ifndef WIBBLE_SYS_MMAP_H #define WIBBLE_SYS_MMAP_H /** \file * Simple mmap support */ /* * Copyright (C) 2006--2008 Enrico Zini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace wibble { namespace sys { #if __cplusplus >= 201103L inline namespace v1 { #endif /** * Map a file into memory. * * Currently, this is only read-only. * * Copy semanthics are the same as auto_ptr * * Note: on 32bit systems, it is not possible to map files larger than 2G into * memory. */ struct MMap { public: MMap(); MMap(const MMap& mmap); MMap(const std::string& filename); ~MMap(); MMap& operator=(const MMap& mmap); void map(const std::string& filename); void unmap(); std::string filename; size_t size; int fd; const char* buf; }; #if __cplusplus >= 201103L } #endif } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/mutex.cpp0000644000175000017500000000237112231005104016545 0ustar enricoenrico/* * Encapsulated pthread mutex and condition * * Copyright (C) 2003--2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace wibble { namespace sys { #ifdef POSIX bool Condition::wait(MutexLock& l, const struct timespec& abstime) { if (int res = pthread_cond_timedwait(&cond, &l.mutex.mutex, &abstime)) { if (res == ETIMEDOUT) return false; else throw wibble::exception::System(res, "waiting on a pthread condition"); } return true; } #endif } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/process.test.h0000644000175000017500000000122212231005104017476 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include using namespace std; using namespace wibble::sys; struct TestProcess { Test getcwdAndChdir() { #ifdef POSIX string cwd = process::getcwd(); process::chdir("/"); assert_eq(process::getcwd(), string("/")); process::chdir(cwd); assert_eq(process::getcwd(), cwd); #endif } Test umask() { #ifdef POSIX mode_t old = process::umask(0012); assert_eq(process::umask(old), 0012u); #endif } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/fs.test.h0000644000175000017500000001445412231502075016454 0ustar enricoenrico/* -*- C++ -*- (c) 2007--2011 Petr Rockai (c) 2007--2013 Enrico Zini */ #include "wibble/sys/fs.h" #include #include #include #include #include #include using namespace std; using namespace wibble::sys::fs; struct TestFs { // Test directory iteration Test directoryIterate() { #ifdef POSIX Directory dir("/"); set files; for (Directory::const_iterator i = dir.begin(); i != dir.end(); ++i) files.insert(*i); assert(files.size() > 0); assert(files.find(".") != files.end()); assert(files.find("..") != files.end()); assert(files.find("etc") != files.end()); assert(files.find("bin") != files.end()); assert(files.find("tmp") != files.end()); files.clear(); for (Directory::const_iterator i = dir.begin(); i != dir.end(); ++i) files.insert(*i); assert(files.size() > 0); assert(files.find(".") != files.end()); assert(files.find("..") != files.end()); assert(files.find("etc") != files.end()); assert(files.find("bin") != files.end()); assert(files.find("tmp") != files.end()); #endif } Test directoryIsdir() { { Directory dir("/"); for (Directory::const_iterator i = dir.begin(); i != dir.end(); ++i) if (*i == "etc") { assert(i.isdir()); assert(!i.isreg()); } } { Directory dir("/etc"); for (Directory::const_iterator i = dir.begin(); i != dir.end(); ++i) if (*i == "passwd") { assert(i.isreg()); assert(!i.isdir()); } } { Directory dir("/dev"); for (Directory::const_iterator i = dir.begin(); i != dir.end(); ++i) { if (*i == "null") { assert(i.ischr()); assert(!i.isblk()); } else if (*i == "sda") { assert(i.isblk()); assert(!i.ischr()); } } } } // Ensure that nonexisting directories and files are reported as not valid Test invalidDirectories() { #ifdef POSIX Directory dir1("/antaniblindalasupercazzola123456"); assert(!dir1.exists()); try { Directory::const_iterator i = dir1.begin(); assert(false); } catch (wibble::exception::System& e) { } Directory dir2("/etc/passwd"); assert(!dir2.exists()); try { Directory::const_iterator i = dir2.begin(); assert(false); } catch (wibble::exception::System& e) { } #endif } Test _mkPath() { #ifdef POSIX // Mkpath should succeed on existing directory mkpath("."); // Mkpath should succeed on existing directory mkpath("./."); // Mkpath should succeed on existing directory mkpath("/"); #endif } Test _mkPath2() { #ifdef POSIX // Try creating a path with mkpath system("rm -rf test-mkpath"); mkpath("test-mkpath/test-mkpath"); assert(wibble::sys::fs::access("test-mkpath", F_OK)); assert(wibble::sys::fs::access("test-mkpath/test-mkpath", F_OK)); system("rm -rf test-mkpath"); #endif } Test _mkFilePath() { #ifdef POSIX // Try creating a path with mkFilePath system("rm -rf test-mkpath"); mkFilePath("test-mkpath/test-mkpath/file"); assert(wibble::sys::fs::access("test-mkpath", F_OK)); assert(wibble::sys::fs::access("test-mkpath/test-mkpath", F_OK)); assert(!wibble::sys::fs::access("test-mkpath/test-mkpath/file", F_OK)); system("rm -rf test-mkpath"); #endif } Test _mkdirIfMissing() { // Creating works and is idempotent { system("rm -rf test-mkpath"); assert(!wibble::sys::fs::access("test-mkpath", F_OK)); wibble::sys::fs::mkdirIfMissing("test-mkpath"); assert(wibble::sys::fs::access("test-mkpath", F_OK)); wibble::sys::fs::mkdirIfMissing("test-mkpath"); } // Creating fails if it exists and it is a file { system("rm -rf test-mkpath; touch test-mkpath"); try { wibble::sys::fs::mkdirIfMissing("test-mkpath"); assert(false); } catch (wibble::exception::Consistency& e) { assert(string(e.what()).find("exists but it is not a directory") != string::npos); } } // Deal with dangling symlinks { system("rm -rf test-mkpath; ln -s ./tmp/tmp/tmp/DOESNOTEXISTS test-mkpath"); try { wibble::sys::fs::mkdirIfMissing("test-mkpath"); assert(false); } catch (wibble::exception::Consistency& e) { assert(string(e.what()).find("looks like a dangling symlink") != string::npos); } } } Test _deleteIfExists() { #ifdef POSIX system("rm -f does-not-exist"); assert(!deleteIfExists("does-not-exist")); system("touch does-exist"); assert(deleteIfExists("does-exist")); #endif } Test _isdir() { #ifdef POSIX system("rm -rf testdir"); assert(!isdir("testdir")); system("touch testdir"); assert(!isdir("testdir")); system("rm testdir; mkdir testdir"); assert(isdir("testdir")); #endif } Test writeFileAtomically() { using namespace wibble::sys; string test("ciao"); fs::writeFileAtomically("testfile", test); string test1 = readFile("testfile"); assert_eq(test1, test); } Test timestamp() { #ifdef POSIX using namespace wibble::sys; system("rm -f testfile"); assert_eq(fs::timestamp("testfile", 0), 0); writeFile("testfile", ""); assert(fs::timestamp("testfile") != 0); assert(fs::timestamp("testfile", 0) != 0); unlink("testfile"); assert_eq(fs::timestamp("testfile", 0), 0); #endif } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/mutex.h0000644000175000017500000003017312231005104016213 0ustar enricoenrico#ifndef WIBBLE_SYS_MUTEX_H #define WIBBLE_SYS_MUTEX_H /* * Encapsulated pthread mutex and condition * * Copyright (C) 2003--2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #ifdef POSIX #include #endif #ifdef _WIN32 #include #include #include #endif #include namespace wibble { namespace sys { /** * pthread mutex wrapper; WARNING: the class allows copying and assignment, * but this is not always safe. You should never copy a locked mutex. It is * however safe to copy when there is no chance of any of the running threads * using the mutex. */ class Mutex { protected: #ifdef POSIX pthread_mutex_t mutex; #endif #ifdef _WIN32 HANDLE mutex; bool singlylocking; #endif public: Mutex(bool recursive = false) { int res = 0; #ifdef POSIX pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); if ( recursive ) { #if (__APPLE__ || __xlC__) pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); #else pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE_NP ); #endif } else { #ifndef NDEBUG pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK_NP ); #endif } res = pthread_mutex_init(&mutex, &attr); #endif #ifdef _WIN32 mutex = CreateMutex( NULL, FALSE, NULL ); singlylocking = false; if (mutex == NULL) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "creating pthread mutex"); } Mutex( const Mutex & ) { int res = 0; #ifdef POSIX pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); res = pthread_mutex_init(&mutex, &attr); #endif #ifdef _WIN32 mutex = CreateMutex(NULL, FALSE, NULL); singlylocking = false; if(mutex == NULL) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "creating pthread mutex"); } ~Mutex() { int res = 0; #ifdef POSIX res = pthread_mutex_destroy(&mutex); #endif #ifdef _WIN32 if(!CloseHandle(mutex)) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "destroying pthread mutex"); } bool trylock() { int res = 0; #ifdef POSIX res = pthread_mutex_trylock(&mutex); if ( res == EBUSY ) return false; if ( res == 0 ) return true; #endif #ifdef _WIN32 DWORD dwWaitResult = !singlylocking ? WaitForSingleObject(mutex, 0) : WAIT_TIMEOUT; if(dwWaitResult == WAIT_OBJECT_0) return true; if(dwWaitResult == WAIT_TIMEOUT) return false; res = (int)GetLastError(); #endif throw wibble::exception::System(res, "(try)locking pthread mutex"); } /// Lock the mutex /// Normally it's better to use MutexLock void lock() { int res = 0; #ifdef POSIX res = pthread_mutex_lock(&mutex); #endif #ifdef _WIN32 while(singlylocking) Sleep(1); if(WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "locking pthread mutex"); } /// Unlock the mutex /// Normally it's better to use MutexLock void unlock() { int res = 0; #ifdef POSIX res = pthread_mutex_unlock(&mutex); #endif #ifdef _WIN32 if(!ReleaseMutex(mutex)) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "unlocking pthread mutex"); } /// Reinitialize the mutex void reinit() { #ifdef POSIX if (int res = pthread_mutex_init(&mutex, 0)) throw wibble::exception::System(res, "reinitialising pthread mutex"); #endif } friend class Condition; }; /** * Acquire a mutex lock, RAII-style */ template< typename Mutex > class MutexLockT { private: // Disallow copy MutexLockT(const MutexLockT&); MutexLockT& operator=(const MutexLockT&); public: Mutex& mutex; bool locked; bool yield; MutexLockT(Mutex& m) : mutex(m), locked( false ), yield( false ) { mutex.lock(); locked = true; } ~MutexLockT() { if ( locked ) { mutex.unlock(); checkYield(); } } void drop() { mutex.unlock(); locked = false; checkYield(); } void reclaim() { mutex.lock(); locked = true; } void setYield( bool y ) { yield = y; } void checkYield() { if ( yield ) #ifdef POSIX sched_yield(); #elif _WIN32 Sleep(0); #else ; #endif } friend class Condition; }; typedef MutexLockT< Mutex > MutexLock; /* * pthread condition wrapper. * * It works in association with a MutexLock. * * WARNING: the class allows copying and assignment; see Mutex: similar caveats * apply. Do not copy or assign a Condition that may be in use. */ class Condition { protected: #ifdef POSIX pthread_cond_t cond; #endif #ifdef _WIN32 int waiters_count_; // number of waiting threads CRITICAL_SECTION waiters_count_lock_; HANDLE sema_; // semaphore used to queue up threads waiting for the condition HANDLE waiters_done_; // An auto-reset event used by the broadcast/signal thread to wait // for all the waiting thread(s) to wake up and be released from the // semaphore. bool was_broadcast_; // Keeps track of whether we were broadcasting or signaling. This // allows us to optimize the code if we're just signaling. #endif public: Condition() { int res = 0; #ifdef POSIX res = pthread_cond_init(&cond, 0); #endif #ifdef _WIN32 waiters_count_ = 0; was_broadcast_ = false; sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); InitializeCriticalSection(&waiters_count_lock_); waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL); if(sema_ == NULL || waiters_done_ == NULL) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "creating pthread condition"); } Condition( const Condition & ) { int res = 0; #ifdef POSIX res = pthread_cond_init(&cond, 0); #endif #ifdef _WIN32 waiters_count_ = 0; was_broadcast_ = false; sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); InitializeCriticalSection(&waiters_count_lock_); waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL); if(sema_ == NULL || waiters_done_ == NULL) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "creating pthread condition"); } ~Condition() { int res = 0; #ifdef POSIX res = pthread_cond_destroy(&cond); #endif #ifdef _WIN32 DeleteCriticalSection(&waiters_count_lock_); if(!CloseHandle(sema_) || !CloseHandle(waiters_done_)) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "destroying pthread condition"); } /// Wake up one process waiting on the condition void signal() { int res = 0; #ifdef POSIX res = pthread_cond_signal(&cond); #endif #ifdef _WIN32 EnterCriticalSection(&waiters_count_lock_); bool have_waiters = waiters_count_ > 0; LeaveCriticalSection(&waiters_count_lock_); // if there aren't any waiters, then this is a no-op if(have_waiters && !ReleaseSemaphore(sema_, 1, 0)) res = (int)GetLastError(); #endif if (res != 0) throw wibble::exception::System(res, "signaling on a pthread condition"); } /// Wake up all processes waiting on the condition void broadcast() { int res = 0; #ifdef POSIX res = pthread_cond_broadcast(&cond); #endif #ifdef _WIN32 for(bool once = true; once; once = false) { EnterCriticalSection(&waiters_count_lock_); bool have_waiters = false; if(waiters_count_ > 0) { was_broadcast_ = true; have_waiters = true; } if(have_waiters) { if(!ReleaseSemaphore(sema_, waiters_count_, 0)) { res = (int)GetLastError(); break; } LeaveCriticalSection(&waiters_count_lock_); if(WaitForSingleObject(waiters_done_, INFINITE) != WAIT_OBJECT_0) { res = (int)GetLastError(); break; } was_broadcast_ = false; } else LeaveCriticalSection(&waiters_count_lock_); } #endif if (res != 0) throw wibble::exception::System(res, "broadcasting on a pthread condition"); } /** * Wait on the condition, locking with l. l is unlocked before waiting and * locked again before returning. */ void wait(MutexLock& l) { int res = 0; #ifdef POSIX res = pthread_cond_wait(&cond, &l.mutex.mutex); #endif #ifdef _WIN32 for(bool once = true; once; once = false) { EnterCriticalSection (&waiters_count_lock_); waiters_count_++; LeaveCriticalSection (&waiters_count_lock_); if(SignalObjectAndWait(l.mutex.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) { res = (int)GetLastError(); break; } EnterCriticalSection (&waiters_count_lock_); waiters_count_--; bool last_waiter = was_broadcast_ && waiters_count_ == 0; LeaveCriticalSection (&waiters_count_lock_); if (last_waiter) { if(SignalObjectAndWait (waiters_done_, l.mutex.mutex, INFINITE, FALSE) != WAIT_OBJECT_0) { res = (int)GetLastError(); break; } } else { if(WaitForSingleObject (l.mutex.mutex, INFINITE) != WAIT_OBJECT_0) { res = (int)GetLastError(); break; } } } #endif if (res != 0) throw wibble::exception::System(res, "waiting on a pthread condition"); } void wait(Mutex& l) { int res = 0; #ifdef POSIX res = pthread_cond_wait(&cond, &l.mutex); #endif #ifdef _WIN32 for(bool once = true; once; once = false) { if(WaitForSingleObject(l.mutex, 0) == WAIT_OBJECT_0) { l.singlylocking = true; while(ReleaseMutex(l.mutex)) ; if ((res = ((int)GetLastError() != 288))) //288 -> MUTEX_NOT_OWNED break; } if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) { res = (int)GetLastError(); break; } l.singlylocking = false; EnterCriticalSection (&waiters_count_lock_); waiters_count_++; LeaveCriticalSection (&waiters_count_lock_); if(SignalObjectAndWait(l.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) { res = (int)GetLastError(); break; } EnterCriticalSection (&waiters_count_lock_); waiters_count_--; bool last_waiter = was_broadcast_ && waiters_count_ == 0; LeaveCriticalSection (&waiters_count_lock_); if(last_waiter) { if(SignalObjectAndWait (waiters_done_, l.mutex, INFINITE, FALSE) != WAIT_OBJECT_0) { res = (int)GetLastError(); break; } } else { if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) { res = (int)GetLastError(); break; } } } #endif if (res != 0) throw wibble::exception::System(res, "waiting on a pthread condition"); } #ifdef POSIX /** * Wait on the condition, locking with l. l is unlocked before waiting and * locked again before returning. If the time abstime is reached before * the condition is signaled, then l is locked and the function returns * false. * * @returns * true if the wait succeeded, or false if the timeout was reached before * the condition is signaled. */ bool wait(MutexLock& l, const struct timespec& abstime); #endif }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/exec.h0000644000175000017500000000555612231005104016004 0ustar enricoenrico#ifndef WIBBLE_SYS_EXEC_H #define WIBBLE_SYS_EXEC_H /* * OO wrapper for execve * * Copyright (C) 2003 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include namespace wibble { namespace sys { /** * Execute external commands, either forked as a ChildProcess or directly using * exec(). */ class Exec : public ChildProcess { protected: /** * Used to run the program as a child process, if one of the * ChildProcess::fork functions is called. Simply calls exec() */ virtual int main(); virtual void spawnChild(); public: virtual ~Exec() {} /** * Filename or pathname of the program to execute. * * If searchInPath is true, this just needs to be the file name. * Otherwise, it needs to be the absolute path of the file to execute. */ std::string pathname; /** * Arguments for the process to execute. * * args[0] will be passed as the name of the child process */ std::vector args; /** * Custom environment for the child process, if envFromParent is false. */ std::vector env; /** * True if the environment is to be taken from the parent, false if it is * explicitly provided in env */ bool envFromParent; /** * Set to true if the file is to be searched in the current $PATH. * * If this is set to true, the environment will always be taken from the * parent regardless of the values of envFromParent and env. */ bool searchInPath; /// Create a new object that will execute program `program' Exec(const std::string& pathname) : pathname(pathname), envFromParent(true), searchInPath(false) { args.push_back(pathname); } /// Import the current environment into env void importEnv(); /// exec the program, never returning if all goes well void exec(); }; /** * Execute a shell command using /bin/sh -c */ class ShellCommand : public Exec { public: ShellCommand(const std::string& cmd) : #ifdef POSIX Exec("/bin/sh") #elif defined _WIN32 Exec("bash") // let's hope for the best... #endif { args.push_back("-c"); args.push_back(cmd); searchInPath = false; envFromParent = true; } }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/fs.h0000644000175000017500000001442312231501407015470 0ustar enricoenrico#ifndef WIBBLE_SYS_DIRECTORY_H #define WIBBLE_SYS_DIRECTORY_H #include #include #include // auto_ptr #include // mode_t #include // struct stat #include // access struct dirent; namespace wibble { namespace sys { namespace fs { /** * stat() the given file and return the struct stat with the results. * If the file does not exist, return NULL. * Raises exceptions in case of errors. */ std::auto_ptr stat(const std::string& pathname); /** * stat() the given file filling in the given structure. * Raises exceptions in case of errors, including if the file does not exist. */ void stat(const std::string& pathname, struct stat& st); /// access() a filename bool access(const std::string& s, int m); /// Same as access(s, F_OK); bool exists(const std::string& s); /** * Get the absolute path of a file */ std::string abspath(const std::string& pathname); // Create a temporary directory based on a template. std::string mkdtemp( std::string templ ); /// Create the given directory, if it does not already exists. /// It will complain if the given pathname already exists but is not a /// directory. void mkdirIfMissing(const std::string& dir, mode_t mode = 0777); /// Create all the component of the given directory, including the directory /// itself. void mkpath(const std::string& dir); /// Ensure that the path to the given file exists, creating it if it does not. /// The file itself will not get created. void mkFilePath(const std::string& file); /// Read whole file into memory. Throws exceptions on failure. std::string readFile(const std::string &file); /** * Read the entire contents of a file into a string * * @param filename * name or description of the stream we are reading. Used only for error * messages. */ std::string readFile(std::istream& file, const std::string& filename); /// Write \a data to \a file, replacing existing contents if it already exists void writeFile(const std::string &file, const std::string &data); /** * Write \a data to \a file, replacing existing contents if it already exists * * Data is written to a temporary file, then moved to its final destination, to * ensure an atomic operation. */ void writeFileAtomically(const std::string &file, const std::string &data); /** * Compute the absolute path of an executable. * * If \a name is specified as a partial path, it ensures it is made absolute. * If \a name is not specified as a path, it looks for the executable in $PATH * and return its absolute pathname. */ std::string findExecutable(const std::string& name); /** * Delete a file if it exists. If it does not exist, do nothing. * * @return true if the file was deleted, false if it did not exist */ bool deleteIfExists(const std::string& file); /// Move \a src to \a dst, without raising exception if \a src does not exist void renameIfExists(const std::string& src, const std::string& dst); /// Delete the file void unlink(const std::string& fname); /// Remove the directory using rmdir(2) void rmdir(const std::string& dirname); /// Delete the directory \a dir and all its content void rmtree(const std::string& dir); /** * Returns true if the given pathname is a directory, else false. * * It also returns false if the pathname does not exist. */ bool isdir(const std::string& pathname); /// Same as isdir but checks for block devices bool isblk(const std::string& pathname); /// Same as isdir but checks for character devices bool ischr(const std::string& pathname); /// Same as isdir but checks for FIFOs bool isfifo(const std::string& pathname); /// Same as isdir but checks for symbolic links bool islnk(const std::string& pathname); /// Same as isdir but checks for regular files bool isreg(const std::string& pathname); /// Same as isdir but checks for sockets bool issock(const std::string& pathname); /// File mtime time_t timestamp(const std::string& file); /// File mtime (or def if the file does not exist) time_t timestamp(const std::string& file, time_t def); /// File size size_t size(const std::string& file); /// File size (or def if the file does not exist) size_t size(const std::string& file, size_t def); /// File inode number ino_t inode(const std::string& file); /// File inode number (or 0 if the file does not exist) ino_t inode(const std::string& file, ino_t def); /// Nicely wrap access to directories class Directory { protected: /// Directory pathname std::string m_path; public: class const_iterator { /// Directory we are iterating const Directory* dir; /// DIR* pointer void* dirp; /// dirent structure used for iterating entries struct dirent* direntbuf; public: // Create an end iterator const_iterator(); // Create a begin iterator const_iterator(const Directory& dir); // Cleanup properly ~const_iterator(); /// auto_ptr style copy semantics const_iterator(const const_iterator& i); const_iterator& operator=(const const_iterator& i); /// Move to the next directory entry const_iterator& operator++(); /// @return the current file name std::string operator*() const; bool operator==(const const_iterator& iter) const; bool operator!=(const const_iterator& iter) const; /// @return true if we refer to a directory, else false bool isdir() const; /// @return true if we refer to a block device, else false bool isblk() const; /// @return true if we refer to a character device, else false bool ischr() const; /// @return true if we refer to a named pipe (FIFO). bool isfifo() const; /// @return true if we refer to a symbolic link. bool islnk() const; /// @return true if we refer to a regular file. bool isreg() const; /// @return true if we refer to a Unix domain socket. bool issock() const; }; Directory(const std::string& path); ~Directory(); /// Pathname of the directory const std::string& path() const { return m_path; } /// Check if the directory exists bool exists() const; /// Begin iterator const_iterator begin() const; /// End iterator const_iterator end() const; }; } } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/lockfile.cpp0000644000175000017500000000165211463032575017216 0ustar enricoenrico#include #ifdef POSIX #include #include #include #include #include namespace wibble { namespace sys { namespace fs { Lockfile::Lockfile(const std::string& name, bool write) : name(name), fd(-1) { fd = open(name.c_str(), (write ? O_RDWR : O_RDONLY) | O_CREAT, 0666); if (fd == -1) throw wibble::exception::System("opening/creating lockfile " + name); struct flock lock; lock.l_type = (write ? F_WRLCK : F_RDLCK); lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) == -1) throw wibble::exception::System("locking the file " + name + " for writing"); } Lockfile::~Lockfile() { if (fd != -1) { struct flock lock; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; fcntl(fd, F_SETLK, &lock); close(fd); } } } } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/childprocess.h0000644000175000017500000001017212231005104017530 0ustar enricoenrico// -*- C++ -*- #ifndef WIBBLE_SYS_CHILDPROCESS_H #define WIBBLE_SYS_CHILDPROCESS_H /* * OO base class for process functions and child processes * * Copyright (C) 2003--2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #ifdef _WIN32 #include #endif struct rusage; namespace wibble { namespace sys { /** * Fork a child process */ class ChildProcess { protected: pid_t _pid; int pipes[3][2]; int *_stdin, *_stdout, *_stderr; int m_status; bool m_doExec; std::string m_command; #ifdef _WIN32 int backups[3]; STARTUPINFO si; PROCESS_INFORMATION pi; #endif /** * Main function to be called in the child process after it has forked */ // TODO: since the destructor is called twice (one in the parent and one in // the child), it could be useful to add a bool isChild() method to let the // destructor and other functions know where they are operating. The value // returned can be set by ChildProcess::fork. virtual int main() = 0; /** * On Windows, it's impossible to fork(), but if you were to fork+exec, * it's not all lost. You can implement spawnChild() instead of main(), * which needs to call CreateProcess, spawn or similar. The redirections * requested by setupRedirects are respected. Exec and ShellProcess * implement spawnChild on Windows. * * NB. For wait() to work, the si/pi member variables need to be filled in * by the implementation. */ virtual void spawnChild(); void waitError(); void setupPipes(); void setupPrefork(); void setupChild(); void setupParent(); public: ChildProcess() : _pid(-1), _stdin( 0 ), _stdout( 0 ), _stderr( 0 ) {} virtual ~ChildProcess() {} /// Instead of calling the main() function of this class, execute an /// external command. The command is passed to the shell interpreter of the /// system (/bin/sh on UNIX, CreateProcess on Windows). void setExec( std::string command ) { m_doExec = true; m_command = command; } /// For a subprocess to run proc. To redirect stdio of the child process to /// pipes, call setupRedirects first. NB. This currently works on Windows /// only when setExec has been called first (any main() overrides have no /// effect on Windows). pid_t fork(); void setupRedirects(int* stdinfd = 0, int* stdoutfd = 0, int* stderrfd = 0); pid_t forkAndRedirect(int* stdinfd = 0, int* stdoutfd = 0, int* stderrfd = 0) { setupRedirects(stdinfd, stdoutfd, stderrfd); return fork(); } /** * Get the pid of the child process or (pid_t)-1 if no child is running * * Note: while ChildProcess::kill() has a safeguard against killing pid -1, * if you are going to run ::kill on the output of pid() make sure to check * what is the semanthics of kill() when pid is -1. */ pid_t pid() const { return _pid; } bool running(); int exitStatus(); void waitForSuccess(); /// Wait for the child to finish, returning its exit status and optionally /// storing resource usage informations in `ru'. Return -1 if no child is /// running. TODO: gracefully handle the EINTR error code int wait(struct rusage* ru = 0); /// Send the given signal to the process void kill(int signal); }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/thread.test.h0000644000175000017500000000341612231005104017276 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include using namespace std; using namespace wibble::sys; struct TestThread { // Test threads that just assigns a value to an int and exists class Thread1 : public Thread { protected: int& res; int val; void* main() { res = val; return reinterpret_cast(val); } public: Thread1(int& res, int val) : res(res), val(val) {} }; // Thread that continuously increments an int value class Thread2 : public Thread { protected: int& res; Mutex& mutex; bool done; void* main() { while (!done) { MutexLock lock(mutex); ++res; } return 0; } public: Thread2(int& res, Mutex& mutex) : res(res), mutex(mutex), done(false) {} void quit() { done = true; } }; // Test that threads are executed Test execution() { int val = 0; Thread1 assigner(val, 42); assigner.start(); assert_eq(assigner.join(), reinterpret_cast(42)); assert_eq(val, 42); } // Use mutexes to access shared memory Test sharedMemory() { int val = 0; Mutex mutex; Thread2 incrementer(val, mutex); incrementer.start(); bool done = false; while (!done) { MutexLock lock(mutex); if (val > 100) done = true; } incrementer.quit(); assert_eq(incrementer.join(), static_cast(0)); } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/fs.cpp0000644000175000017500000003645012231501407016027 0ustar enricoenrico#include #include #include #include #include #include #include // opendir, closedir #include #include #include #include #include // alloca on win32 seems to live there #ifdef _WIN32 #include #include #include #include #endif namespace wibble { namespace sys { namespace fs { #ifdef POSIX std::auto_ptr stat(const std::string& pathname) { std::auto_ptr res(new struct stat); if (::stat(pathname.c_str(), res.get()) == -1) { if (errno == ENOENT) return std::auto_ptr(); else throw wibble::exception::File(pathname, "getting file information"); } return res; } void stat(const std::string& pathname, struct stat& st) { if (::stat(pathname.c_str(), &st) == -1) throw wibble::exception::File(pathname, "getting file information"); } #define common_stat_body(testfunc) \ struct stat st; \ if (::stat(pathname.c_str(), &st) == -1) { \ if (errno == ENOENT) \ return false; \ else \ throw wibble::exception::System("getting file information for " + pathname); \ } \ return testfunc(st.st_mode) bool isdir(const std::string& pathname) { common_stat_body(S_ISDIR); } bool isblk(const std::string& pathname) { common_stat_body(S_ISBLK); } bool ischr(const std::string& pathname) { common_stat_body(S_ISCHR); } bool isfifo(const std::string& pathname) { common_stat_body(S_ISFIFO); } bool islnk(const std::string& pathname) { common_stat_body(S_ISLNK); } bool isreg(const std::string& pathname) { common_stat_body(S_ISREG); } bool issock(const std::string& pathname) { common_stat_body(S_ISSOCK); } #undef common_stat_body bool access(const std::string &s, int m) { return ::access(s.c_str(), m) == 0; } bool exists(const std::string& file) { return sys::fs::access(file, F_OK); } std::string abspath(const std::string& pathname) { if (pathname[0] == '/') return str::normpath(pathname); else return str::normpath(str::joinpath(process::getcwd(), pathname)); } void mkdirIfMissing(const std::string& dir, mode_t mode) { for (int i = 0; i < 5; ++i) { // If it does not exist, make it if (::mkdir(dir.c_str(), mode) != -1) return; // throw on all errors except EEXIST. Note that EEXIST "includes the case // where pathname is a symbolic link, dangling or not." if (errno != EEXIST && errno != EISDIR) throw wibble::exception::System("creating directory " + dir); // Ensure that, if dir exists, it is a directory std::auto_ptr st = wibble::sys::fs::stat(dir); if (st.get() == NULL) { // Either dir has just been deleted, or we hit a dangling // symlink. // // Retry creating a directory: the more we keep failing, the more // the likelyhood of a dangling symlink increases. // // We could lstat here, but it would add yet another case for a // race condition if the broken symlink gets deleted between the // stat and the lstat. continue; } else if (! S_ISDIR(st->st_mode)) // If it exists but it is not a directory, complain throw wibble::exception::Consistency("ensuring path " + dir + " exists", dir + " exists but it is not a directory"); else // If it exists and it is a directory, we're fine return; } throw wibble::exception::Consistency("ensuring path " + dir + " exists", dir + " exists and looks like a dangling symlink"); } void mkpath(const std::string& dir) { size_t pos = dir.rfind('/'); if (pos != 0 && pos != std::string::npos) // First ensure that the upper path exists mkpath(dir.substr(0, pos)); mkdirIfMissing(dir, 0777); } void mkFilePath(const std::string& file) { size_t pos = file.rfind('/'); if (pos != std::string::npos) mkpath(file.substr(0, pos)); } #endif std::string readFile( const std::string &file ) { std::ifstream in( file.c_str(), std::ios::binary ); if (!in.is_open()) throw wibble::exception::System( "reading file " + file ); std::string ret; size_t length; in.seekg(0, std::ios::end); length = in.tellg(); in.seekg(0, std::ios::beg); char *buffer = static_cast( alloca( length ) ); in.read(buffer, length); return std::string( buffer, length ); } std::string readFile(std::istream& input, const std::string& filename) { static const size_t bufsize = 4096; char buf[bufsize]; std::string res; while (true) { input.read(buf, bufsize); res.append(buf, input.gcount()); if (input.eof()) break; if (input.fail()) throw wibble::exception::File(filename, "reading data"); } return res; } void writeFile( const std::string &file, const std::string &data ) { std::ofstream out( file.c_str(), std::ios::binary ); if (!out.is_open()) throw wibble::exception::System( "writing file " + file ); out << data; } void writeFileAtomically(const std::string &file, const std::string &data) { char* fbuf = (char*)alloca(file.size() + 7); memcpy(fbuf, file.data(), file.size()); memcpy(fbuf + file.size(), "XXXXXX", 7); int fd = mkstemp(fbuf); if (fd < 0) throw wibble::exception::File(fbuf, "cannot create temp file"); ssize_t res = write(fd, data.data(), data.size()); if (res != (ssize_t)data.size()) throw wibble::exception::File(fbuf, str::fmtf("cannot write %d bytes", data.size())); if (close(fd) < 0) throw wibble::exception::File(fbuf, "cannot close file"); if (rename(fbuf, file.c_str()) < 0) throw wibble::exception::File(fbuf, "cannot rename to " + file); } std::string findExecutable(const std::string& name) { // argv[0] has an explicit path: ensure it becomes absolute if (name.find('/') != std::string::npos) return sys::fs::abspath(name); // argv[0] has no explicit path, look for it in $PATH const char* path = getenv("PATH"); if (path == NULL) return name; str::Split splitter(":", path); for (str::Split::const_iterator i = splitter.begin(); i != splitter.end(); ++i) { std::string candidate = str::joinpath(*i, name); if (sys::fs::access(candidate, X_OK)) return sys::fs::abspath(candidate); } return name; } bool deleteIfExists(const std::string& file) { if (::unlink(file.c_str()) != 0) if (errno != ENOENT) throw wibble::exception::File(file, "removing file"); else return false; else return true; } void renameIfExists(const std::string& src, const std::string& dst) { int res = ::rename(src.c_str(), dst.c_str()); if (res < 0 && errno != ENOENT) throw wibble::exception::System("moving " + src + " to " + dst); } void unlink(const std::string& fname) { if (::unlink(fname.c_str()) < 0) throw wibble::exception::File(fname, "cannot delete file"); } void rmdir(const std::string& dirname) { if (::rmdir(dirname.c_str()) < 0) throw wibble::exception::System("cannot delete directory " + dirname); } time_t timestamp(const std::string& file) { struct stat st; stat(file, st); return st.st_mtime; } time_t timestamp(const std::string& file, time_t def) { std::auto_ptr st = sys::fs::stat(file); return st.get() == NULL ? def : st->st_mtime; } size_t size(const std::string& file) { struct stat st; stat(file, st); return (size_t)st.st_size; } size_t size(const std::string& file, size_t def) { std::auto_ptr st = sys::fs::stat(file); return st.get() == NULL ? def : (size_t)st->st_size; } ino_t inode(const std::string& file) { struct stat st; stat(file, st); return st.st_ino; } ino_t inode(const std::string& file, ino_t def) { std::auto_ptr st = sys::fs::stat(file); return st.get() == NULL ? def : st->st_ino; } #ifdef POSIX void rmtree(const std::string& dir) { Directory d(dir); for (Directory::const_iterator i = d.begin(); i != d.end(); ++i) { if (*i == "." || *i == "..") continue; if (i.isdir()) rmtree(str::joinpath(dir, *i)); else unlink(str::joinpath(dir, *i)); } rmdir(dir); } #ifdef POSIX Directory::const_iterator::const_iterator() : dir(0), dirp(0), direntbuf(0) {} Directory::const_iterator::const_iterator(const Directory& dir) : dir(&dir), dirp(0), direntbuf(0) { dirp = opendir(dir.m_path.c_str()); if (!dirp) throw wibble::exception::System("reading directory " + dir.m_path); // from man readdir_r (yuck!) size_t name_max = pathconf(dir.m_path.c_str(), _PC_NAME_MAX); if (name_max == -1) // Limit not defined, or error name_max = 4096; // Take a guess size_t len = offsetof(struct dirent, d_name) + name_max + 1; direntbuf = (struct dirent*)malloc(len); ++(*this); } Directory::const_iterator::const_iterator(const const_iterator& i) { dir = i.dir; dirp = i.dirp; direntbuf = i.direntbuf; const_iterator* wi = const_cast(&i); wi->dir = 0; wi->dirp = 0; wi->direntbuf = 0; } Directory::const_iterator::~const_iterator() { if (dirp) closedir((DIR*)dirp); if (direntbuf) free(direntbuf); } bool Directory::const_iterator::operator==(const const_iterator& iter) const { // In fact, this only supports equality to itself return dir == iter.dir && dirp == iter.dirp && direntbuf == iter.direntbuf; } bool Directory::const_iterator::operator!=(const const_iterator& iter) const { // In fact, this only supports equality to itself return dir != iter.dir || dirp != iter.dirp || direntbuf != iter.direntbuf; } Directory::const_iterator& Directory::const_iterator::operator=(const Directory::const_iterator& i) { // Catch a = a if (&i == this) return *this; dir = i.dir; if (dirp && dirp != i.dirp) closedir((DIR*)dirp); dirp = i.dirp; if (direntbuf && direntbuf != i.direntbuf) free(direntbuf); direntbuf = i.direntbuf; const_iterator* wi = const_cast(&i); // Turn i into an end iterator wi->dir = 0; wi->dirp = 0; wi->direntbuf = 0; return *this; } Directory::const_iterator& Directory::const_iterator::operator++() { struct dirent* dres; int res = readdir_r((DIR*)(dirp), direntbuf, &dres); if (res != 0) throw wibble::exception::System(res, "reading directory " + dir->m_path); if (dres == NULL) { // Turn into an end iterator dir = 0; closedir((DIR*)dirp); dirp = 0; free(direntbuf); direntbuf = 0; } return *this; } std::string Directory::const_iterator::operator*() const { return direntbuf->d_name; } bool Directory::const_iterator::isdir() const { #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (direntbuf->d_type == DT_DIR) return true; if (direntbuf->d_type != DT_UNKNOWN) return false; #endif // No d_type, we'll need to stat return wibble::sys::fs::isdir(wibble::str::joinpath(dir->m_path, direntbuf->d_name)); } bool Directory::const_iterator::isblk() const { #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (direntbuf->d_type == DT_BLK) return true; if (direntbuf->d_type != DT_UNKNOWN) return false; #endif // No d_type, we'll need to stat return wibble::sys::fs::isblk(wibble::str::joinpath(dir->m_path, direntbuf->d_name)); } bool Directory::const_iterator::ischr() const { #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (direntbuf->d_type == DT_CHR) return true; if (direntbuf->d_type != DT_UNKNOWN) return false; #endif // No d_type, we'll need to stat return wibble::sys::fs::ischr(wibble::str::joinpath(dir->m_path, direntbuf->d_name)); } bool Directory::const_iterator::isfifo() const { #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (direntbuf->d_type == DT_FIFO) return true; if (direntbuf->d_type != DT_UNKNOWN) return false; #endif // No d_type, we'll need to stat return wibble::sys::fs::isfifo(wibble::str::joinpath(dir->m_path, direntbuf->d_name)); } bool Directory::const_iterator::islnk() const { #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (direntbuf->d_type == DT_LNK) return true; if (direntbuf->d_type != DT_UNKNOWN) return false; #endif // No d_type, we'll need to stat return wibble::sys::fs::islnk(wibble::str::joinpath(dir->m_path, direntbuf->d_name)); } bool Directory::const_iterator::isreg() const { #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (direntbuf->d_type == DT_REG) return true; if (direntbuf->d_type != DT_UNKNOWN) return false; #endif // No d_type, we'll need to stat return wibble::sys::fs::isreg(wibble::str::joinpath(dir->m_path, direntbuf->d_name)); } bool Directory::const_iterator::issock() const { #ifdef HAVE_STRUCT_DIRENT_D_TYPE if (direntbuf->d_type == DT_SOCK) return true; if (direntbuf->d_type != DT_UNKNOWN) return false; #endif // No d_type, we'll need to stat return wibble::sys::fs::issock(wibble::str::joinpath(dir->m_path, direntbuf->d_name)); } Directory::Directory(const std::string& path) : m_path(path) { } Directory::~Directory() { } bool Directory::exists() const { return isdir(m_path); } Directory::const_iterator Directory::begin() const { return const_iterator(*this); } Directory::const_iterator Directory::end() const { return const_iterator(); } #endif #ifdef _WIN32 bool access(const std::string &s, int m) { return 1; /* FIXME */ } #endif std::string mkdtemp( std::string tmpl ) { char *_tmpl = reinterpret_cast< char * >( alloca( tmpl.size() + 1 ) ); strcpy( _tmpl, tmpl.c_str() ); return ::mkdtemp( _tmpl ); } #endif #ifdef _WIN32 bool access(const std::string &s, int m) { return 1; /* FIXME */ } std::string mkdtemp( std::string tmpl ) { char *_tmpl = reinterpret_cast< char * >( alloca( tmpl.size() + 1 ) ); strcpy( _tmpl, tmpl.c_str() ); if ( _mktemp_s( _tmpl, tmpl.size() + 1 ) == 0 ) { if ( ::mkdir( _tmpl ) == 0 ) return _tmpl; else throw wibble::exception::System("creating temporary directory"); } else { throw wibble::exception::System("creating temporary directory path"); } } // Use strictly ANSI variant of structures and functions. void rmtree( const std::string& dir ) { // Use double null terminated path. int len = dir.size(); char* from = reinterpret_cast< char* >( alloca( len + 2 ) ); strcpy( from, dir.c_str() ); from [ len + 1 ] = '\0'; SHFILEOPSTRUCTA fileop; fileop.hwnd = NULL; // no status display fileop.wFunc = FO_DELETE; // delete operation fileop.pFrom = from; // source file name as double null terminated string fileop.pTo = NULL; // no destination needed fileop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; // do not prompt the user fileop.fAnyOperationsAborted = FALSE; fileop.lpszProgressTitle = NULL; fileop.hNameMappings = NULL; int ret = SHFileOperationA( &fileop ); if ( ret )// only zero return value is without error throw wibble::exception::System( "deleting directory" ); } #endif } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/sys/process.h0000644000175000017500000000542712231005104016533 0ustar enricoenrico#ifndef WIBBLE_SYS_PROCESS_H #define WIBBLE_SYS_PROCESS_H /* * OO base class for process functions and child processes * * Copyright (C) 2003--2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace wibble { namespace sys { namespace process { /// Get the absolute path of the current working directory std::string getcwd(); #ifdef POSIX /// Pretty-print the return value of a process into a string std::string formatStatus(int status); /// Change working directory void chdir(const std::string& dir); /// Change root directory void chroot(const std::string& dir); /// Change umask (always succeeds and returns the previous umask) mode_t umask(mode_t mask); /// Set user and group permissions void setPerms(const std::string& user); void setPerms(const std::string& user, const std::string& group); void setPerms(uid_t user); void setPerms(uid_t user, gid_t group); /// Get current resource limits; store also maximum resource limits in max /// if nonzero int getCPUTimeLimit(int* max = 0); int getFileSizeLimit(int* max = 0); int getDataMemoryLimit(int* max = 0); int getChildrenLimit(int* max = 0); int getOpenFilesLimit(int* max = 0); int getCoreSizeLimit(int* max = 0); /// Set resource limits void setCPUTimeLimit(int value); void setFileSizeLimit(int value); void setDataMemoryLimit(int value); void setChildrenLimit(int value); void setOpenFilesLimit(int value); void setCoreSizeLimit(int value); /// Close stdin, stdout and stderr and detach from the tty void detachFromTTY(); /** * Call from main() if you intend to set a different process title later on * * On Linux, this function moves the environment to a different location to * allow its memory to be reused for the process title */ void initproctitle(int argc, char **argv); /** * Change the process title, overwriting the contents of argv * * This is currently only implemented for Linux: on other systems it does not * do anything. */ void setproctitle(const std::string& title); #endif } } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/sys/process.cpp0000644000175000017500000002702212231005104017061 0ustar enricoenrico/* * OO base class for process functions and child processes * * Copyright (C) 2003-2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #ifdef POSIX #include // fork, waitpid, kill, open, getpw*, getgr*, initgroups #include // open #include // getrlimit, setrlimit #include // fork, dup2, pipe, close, setsid, _exit, chdir #include // open #include // waitpid #include // kill #include // flockfile, funlockfile #include // is* #include // getpw* #include // getgr*, initgroups #include #include #include #include extern char** environ; namespace wibble { namespace sys { namespace process { using namespace std; void detachFromTTY() { int devnull = open("/dev/null", O_RDWR); if (devnull == -1) throw wibble::exception::File("/dev/null", "opening for read and write access"); if (dup2(devnull, 0) == -1) throw wibble::exception::System("redirecting stdin to /dev/null"); if (dup2(devnull, 1) == -1) throw wibble::exception::System("redirecting stdout to /dev/null"); if (setsid() == -1) throw wibble::exception::System("trying to become session leader"); if (dup2(devnull, 2) == -1) throw wibble::exception::System("redirecting stderr to /dev/null"); close(devnull); } string formatStatus(int status) { stringstream b_status; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" bool exited_normally = WIFEXITED(status); int exit_code = exited_normally ? WEXITSTATUS(status) : -1; bool dumped_core = status & 128; bool signaled = WIFSIGNALED(status); int signal = signaled ? WTERMSIG(status) : 0; #pragma GCC diagnostic pop if (exited_normally) if (exit_code == 0) b_status << "terminated successfully"; else b_status << "exited with code " << exit_code; else { b_status << "was interrupted, killed by signal " << signal; if (dumped_core) b_status << " (core dumped)"; } return b_status.str(); } void chdir(const string& dir) { if (::chdir(dir.c_str()) == -1) throw wibble::exception::System("changing working directory to " + dir); } std::string getcwd() { #if defined(__GLIBC__) char* cwd = ::get_current_dir_name(); if (cwd == NULL) throw wibble::exception::System("getting the current working directory"); const std::string str(cwd); ::free(cwd); return str; #else size_t size = pathconf(".", _PC_PATH_MAX); char *buf = (char *)alloca( size ); if (::getcwd(buf, size) == NULL) throw wibble::exception::System("getting the current working directory"); return buf; #endif } void chroot(const string& dir) { if (::chroot(dir.c_str()) == -1) throw wibble::exception::System("changing root directory to " + dir); } mode_t umask(mode_t mask) { return ::umask(mask); } struct passwd* getUserInfo(const string& user) { if (isdigit(user[0])) return getpwuid(atoi(user.c_str())); else return getpwnam(user.c_str()); } struct group* getGroupInfo(const string& group) { if (isdigit(group[0])) return getgrgid(atoi(group.c_str())); else return getgrnam(group.c_str()); } void initGroups(const string& name, gid_t gid) { if (::initgroups(name.c_str(), gid) == -1) { stringstream str; str << "initializing group access list for user " << name << " with additional group " << gid; throw wibble::exception::System(str.str()); } } static void set_perms(const string& user, uid_t uid, const string& group, gid_t gid) { initGroups(user, gid); if (setgid(gid) == -1) { stringstream str; str << "setting group id to " << gid << " (" << group << ")"; throw wibble::exception::System(str.str()); } if (setegid(gid) == -1) { stringstream str; str << "setting effective group id to " << gid << " (" << group << ")"; throw wibble::exception::System(str.str()); } if (setuid(uid) == -1) { stringstream str; str << "setting user id to " << uid << " (" << user << ")"; throw wibble::exception::System(str.str()); } if (seteuid(uid) == -1) { stringstream str; str << "setting effective user id to " << uid << " (" << user << ")"; throw wibble::exception::System(str.str()); } } void setPerms(const string& user) { struct passwd* pw = getUserInfo(user); if (!pw) { stringstream str; str << "User " << user << " does not exist on this system"; throw wibble::exception::Consistency("setting process permissions", str.str()); } struct group* gr = getgrgid(pw->pw_gid); if (!gr) { stringstream str; str << "Group " << pw->pw_gid << " (primary group of user " << user << ") does not exist on this system"; throw wibble::exception::Consistency("setting process permissions", str.str()); } set_perms(user, pw->pw_uid, gr->gr_name, gr->gr_gid); } void setPerms(const string& user, const string& group) { struct passwd* pw = getUserInfo(user); if (!pw) { stringstream str; str << "User " << user << " does not exist on this system"; throw wibble::exception::Consistency("setting process permissions", str.str()); } struct group* gr = getGroupInfo(group); if (!gr) { stringstream str; str << "Group " << group << " does not exist on this system"; throw wibble::exception::Consistency("setting process permissions", str.str()); } set_perms(user, pw->pw_uid, group, gr->gr_gid); } void setPerms(uid_t user) { struct passwd* pw = getpwuid(user); if (!pw) { stringstream str; str << "User " << user << " does not exist on this system"; throw wibble::exception::Consistency("setting process permissions", str.str()); } struct group* gr = getgrgid(pw->pw_gid); if (!gr) { stringstream str; str << "Group " << pw->pw_gid << " (primary group of user " << user << ") does not exist on this system"; throw wibble::exception::Consistency("setting process permissions", str.str()); } set_perms(pw->pw_name, pw->pw_uid, gr->gr_name, gr->gr_gid); } void setPerms(uid_t user, gid_t group) { struct passwd* pw = getpwuid(user); if (!pw) { stringstream str; str << "User " << user << " does not exist on this system"; throw wibble::exception::Consistency("setting process permissions", str.str()); } struct group* gr = getgrgid(group); if (!gr) { stringstream str; str << "Group " << group << " does not exist on this system"; throw wibble::exception::Consistency("setting process permissions", str.str()); } set_perms(pw->pw_name, pw->pw_uid, gr->gr_name, gr->gr_gid); } static string describe_rlimit_res_t(int rlim) { switch (rlim) { case RLIMIT_CPU: return "CPU time in seconds"; case RLIMIT_FSIZE: return "Maximum filesize"; case RLIMIT_DATA: return "max data size"; case RLIMIT_STACK: return "max stack size"; case RLIMIT_CORE: return "max core file size"; case RLIMIT_RSS: return "max resident set size"; case RLIMIT_NPROC: return "max number of processes"; case RLIMIT_NOFILE: return "max number of open files"; #ifndef __xlC__ case RLIMIT_MEMLOCK: return "max locked-in-memory address spac"; #endif #ifndef __APPLE__ case RLIMIT_AS: return "address space (virtual memory) limit"; #endif default: return "unknown"; } } static void setLimit(int rlim, int val) { struct rlimit lim; if (getrlimit(rlim, &lim) == -1) throw wibble::exception::System("Getting " + describe_rlimit_res_t(rlim) + " limit"); lim.rlim_cur = val; if (setrlimit(rlim, &lim) == -1) { stringstream str; str << "Setting " << describe_rlimit_res_t(rlim) << " limit to " << val; throw wibble::exception::System(str.str()); } } static int getLimit(int rlim, int* max = 0) { struct rlimit lim; if (getrlimit(rlim, &lim) == -1) throw wibble::exception::System("Getting " + describe_rlimit_res_t(rlim) + " limit"); if (max) *max = lim.rlim_max; return lim.rlim_cur; } int getCPUTimeLimit(int* max) { return getLimit(RLIMIT_CPU, max); } int getFileSizeLimit(int* max) { return getLimit(RLIMIT_FSIZE, max); } int getDataMemoryLimit(int* max) { return getLimit(RLIMIT_DATA, max); } int getCoreSizeLimit(int* max) { return getLimit(RLIMIT_CORE, max); } int getChildrenLimit(int* max) { return getLimit(RLIMIT_NPROC, max); } int getOpenFilesLimit(int* max) { return getLimit(RLIMIT_NOFILE, max); } void setCPUTimeLimit(int value) { setLimit(RLIMIT_CPU, value); } void setFileSizeLimit(int value) { setLimit(RLIMIT_FSIZE, value); } void setDataMemoryLimit(int value) { setLimit(RLIMIT_DATA, value); } void setCoreSizeLimit(int value) { setLimit(RLIMIT_CORE, value); } void setChildrenLimit(int value) { setLimit(RLIMIT_NPROC, value); } void setOpenFilesLimit(int value) { setLimit(RLIMIT_NOFILE, value); } #ifdef __linux__ // Working setproctitle implementation for Linux static char** global_argv = NULL; static size_t global_argv_max_size = 0; void initproctitle(int argc, char **argv) { // Make initproctitle idempotent if (global_argv != NULL) return; // Count the number of items in the environment size_t envc = 0; for (char** e = environ; *e; ++e) ++envc; // At first, set things up so we only reuse argv global_argv = argv; global_argv_max_size = argv[argc-1] + strlen(argv[argc-1]) - argv[0]; if (!envc) return; // There is an environment: try to move it so we can use even more space // Total size of the environment size_t env_size = environ[envc-1] + strlen(environ[envc-1]) - environ[0]; // Allocate and fall back to only_reuse_argv if it fails char* env_copy = new char[env_size]; if (!env_copy) return; char** envp_copy = new char*[envc+1]; if (!envp_copy) { delete[] env_copy; return; } // Copy of the whole environment string table memcpy(env_copy, environ, env_size); // Copy of the environment string pointers envp_copy[0] = env_copy; for (size_t i = 1; i < envc; ++i) envp_copy[i] = envp_copy[i-1] + (environ[i] - environ[i-1]); // Increase max_size to also use the environment space global_argv_max_size += env_size; } void setproctitle(const std::string& title) { if (!global_argv) return; size_t size = title.size() + 1; if (size > global_argv_max_size) size = global_argv_max_size; memcpy(global_argv[0], title.c_str(), size); global_argv[0][size-1] = 0; global_argv[1] = 0; } #else // If we are not on Linux we don't do anything /* * FIXME: BSD systems have a native setproctitle() function that we could use, * but I'd like a BSD programmer to take care of writing autotools and cmake * tests for it and to test it. */ void initproctitle (int argc, char **argv) {} void setproctitle(const std::string& title) {} #endif } } } #elif defined(_WIN32) #include #include #include namespace wibble { namespace sys { namespace process { std::string getcwd() { char *buf = (char *)alloca( 4096 ); if (::getcwd(buf, 4096) == NULL) throw wibble::exception::System("getting the current working directory"); return buf; } } } } #endif // vim:set ts=4 sw=4: libwibble-1.1/wibble/parse.h0000644000175000017500000003215612231014462015357 0ustar enricoenrico// -*- C++ -*- (c) 2011 Petr Rockai // (c) 2013 Jan Kriho // Support code for writing backtracking recursive-descent parsers #include #include #include #include #include #include #include #ifndef WIBBLE_PARSE_H #define WIBBLE_PARSE_H namespace wibble { struct Position { std::string source; int line; int column; Position() : source("-"), line(1), column(1) {} bool operator==( const Position &o ) const { return o.source == source && o.line == line && o.column == column; } }; template< typename _Id > struct Token { typedef _Id Id; Id id; std::string data; Position position; bool _valid; bool valid() { return _valid; } Token( Id _id, char c ) : id( _id ), _valid( true ) { data.push_back( c ); } Token( Id _id, std::string d ) : id( _id ), data( d ), _valid( true ) {} Token() : id( Id( 0 ) ), _valid( false ) {} bool operator==( const Token &o ) const { return o._valid == _valid && o.id == id && o.data == data && o.position == position; } }; template< typename X, typename Y > inline std::ostream &operator<<( std::ostream &o, const std::pair< X, Y > &x ) { return o << "(" << x.first << ", " << x.second << ")"; } /* * This is a SLOW lexer (at least compared to systems like lex/flex, which * build a finite-state machine to make decisions per input character. We could * do that in theory, but it would still be slow, since we would be in effect * interpreting the FSM anyway, while (f)lex uses an optimising compiler like * gcc. So while this is friendly and flexible, it won't give you a fast * scanner. */ template< typename Token, typename Stream > struct Lexer { Stream &stream; typedef std::deque< char > Window; Window _window; Position current; Token _match; void shift() { assert( !stream.eof() ); std::string r = stream.remove(); std::copy( r.begin(), r.end(), std::back_inserter( _window ) ); } bool eof() { return _window.empty() && stream.eof(); } std::string window( unsigned n ) { bool valid = ensure_window( n ); assert( valid ); static_cast< void >( valid ); std::deque< char >::iterator b = _window.begin(), e = b; e += n; return std::string( b, e ); } bool ensure_window( unsigned n ) { while ( _window.size() < n && !stream.eof() ) shift(); return _window.size() >= n; } void consume( int n ) { for( int i = 0; i < n; ++i ) { if ( _window[i] == '\n' ) { current.line ++; current.column = 1; } else current.column ++; } std::deque< char >::iterator b = _window.begin(), e = b; e += n; _window.erase( b, e ); } void consume( const std::string &s ) { consume( s.length() ); } void consume( const Token &t ) { // std::cerr << "consuming " << t << std::endl; consume( t.data ); } void keep( typename Token::Id id, const std::string &data ) { Token t( id, data ); t.position = current; if ( t.data.length() > _match.data.length() ) _match = t; } template< typename I > bool match( I begin, I end ) { if ( !ensure_window( end - begin ) ) return false; return std::equal( begin, end, _window.begin() ); } void match( const std::string &data, typename Token::Id id ) { if ( match( data.begin(), data.end() ) ) return keep( id, data ); } void match( Regexp &r, typename Token::Id id ) { unsigned n = 1, max = 0; while ( r.match( window( n ) ) ) { if ( max && max == r.matchLength( 0 ) ) return keep( id, window( max ) ); max = r.matchLength( 0 ); ++ n; } } void match( int (*first)(int), int (*rest)(int), typename Token::Id id ) { unsigned n = 1; if ( !ensure_window( 1 ) ) return; if ( !first( _window[0] ) ) return; while ( true ) { ++ n; if ( ensure_window( n ) && rest( _window[ n - 1 ] ) ) continue; return keep( id, window( n - 1 ) ); } } void match( const std::string &from, const std::string &to, typename Token::Id id ) { if ( !match( from.begin(), from.end() ) ) return; Window::iterator where = _window.begin(); int n = from.length(); where += n; while ( true ) { if ( !ensure_window( n + to.length() ) ) return; if ( std::equal( to.begin(), to.end(), where ) ) return keep( id, window( n + to.length() ) ); ++ where; ++ n; } } void skipWhitespace() { while ( !eof() && isspace( window( 1 )[ 0 ] ) ) consume( 1 ); } Token decide() { Token t; std::swap( t, _match ); consume( t ); return t; } Lexer( Stream &s ) : stream( s ) {} }; template< typename Token, typename Stream > struct ParseContext { Stream *_stream; Stream &stream() { assert( _stream ); return *_stream; } std::deque< Token > window; int window_pos; int position; std::string name; typedef ParseContext< Token, Stream > This; std::vector< This > children; struct Fail { enum Type { Syntax, Semantic }; int position; const char *expected; Type type; bool operator<( const Fail &other ) const { return position > other.position; } Fail( const char *err, int pos, Type t = Syntax) { expected = err; position = pos; type = t; } ~Fail() throw () {} }; std::priority_queue< Fail > failures; void clearErrors() { failures = std::priority_queue< Fail >(); } void error( std::ostream &o, std::string prefix, const Fail &fail ) { Token t = window[ fail.position ]; switch ( fail.type ) { case Fail::Syntax: o << prefix << "expected " << fail.expected << " at line " << t.position.line << ", column " << t.position.column << ", but seen " << Token::tokenName[ t.id ] << " '" << t.data << "'" << std::endl; return; case Fail::Semantic: o << prefix << fail.expected << " at line " << t.position.line << ", column " << t.position.column << std::endl; return; } } void errors( std::ostream &o ) { for ( typename std::vector< This >::iterator pc = children.begin(); pc != children.end(); ++pc ) pc->errors( o ); if ( failures.empty() ) return; std::string prefix; switch ( failures.top().type ) { case Fail::Syntax: o << "parse"; break; case Fail::Semantic: o << "semantic"; break; } o << " error in context " << name << ": "; if ( failures.size() > 1 ) { o << failures.size() << " rightmost alternatives:" << std::endl; prefix = " "; } while ( !failures.empty() ) { error( o, prefix, failures.top() ); failures.pop(); } } Token remove() { if ( int( window.size() ) <= window_pos ) { Token t; do { t = stream().remove(); } while ( t.id == Token::Comment ); // XXX window.push_back( t ); } ++ window_pos; ++ position; return window[ window_pos - 1 ]; } void rewind( int n ) { assert( n >= 0 ); assert( n <= window_pos ); window_pos -= n; position -= n; } This & createChild( Stream &s, std::string name ) { children.push_back( This( s, name ) ); return children.back(); } ParseContext( Stream &s, std::string name ) : _stream( &s ), window_pos( 0 ), position( 0 ), name( name ) {} }; template< typename Token, typename Stream > struct Parser { typedef typename Token::Id TokenId; typedef ParseContext< Token, Stream > Context; Context *ctx; typedef typename Context::Fail Fail; typedef typename Fail::Type FailType; int _position; bool valid() const { return ctx; } Context &context() { assert( ctx ); return *ctx; } int position() { return context().position; } void rewind( int i ) { context().rewind( i ); _position = context().position; } void fail( const char *what, FailType type = FailType::Syntax ) __attribute__((noreturn)) { Fail f( what, _position, type ); context().failures.push( f ); while ( context().failures.top().position < _position ) context().failures.pop(); throw f; } void semicolon() { Token t = eat(); if ( t.id == Token::Punctuation && t.data == ";" ) return; rewind( 1 ); fail( "semicolon" ); } void colon() { Token t = eat(); if ( t.id == Token::Punctuation && t.data == ":" ) return; rewind( 1 ); fail( "colon" ); } Token eat( TokenId id ) { Token t = eat( false ); if ( t.id == id ) return t; rewind( 1 ); fail( Token::tokenName[id].c_str() ); } #if __cplusplus >= 201103L template< typename F > void either( void (F::*f)() ) { (static_cast< F* >( this )->*f)(); } template< typename F, typename... Args > void either( F f, Args... args ) { if ( maybe( f ) ) return; either( args... ); } #else template< typename F, typename G > void either( F f, void (G::*g)() ) { if ( maybe( f ) ) return; (static_cast< G* >( this )->*g)(); } #endif #if __cplusplus >= 201103L template< typename F, typename... Args > bool maybe( F f, Args... args ) { if ( maybe( f ) ) return true; return maybe( args... ); } #else template< typename F, typename G > bool maybe( F f, G g ) { if ( maybe( f ) ) return true; return maybe( g ); } template< typename F, typename G, typename H > bool maybe( F f, G g, H h ) { if ( maybe( f ) ) return true; if ( maybe( g ) ) return true; return maybe( h ); } #endif template< typename F > bool maybe( void (F::*f)() ) { int fallback = position(); try { (static_cast< F* >( this )->*f)(); return true; } catch ( Fail fail ) { rewind( position() - fallback ); return false; } } bool maybe( TokenId id ) { int fallback = position(); try { eat( id ); return true; } catch (Fail) { rewind( position() - fallback ); return false; } } template< typename T, typename I > void many( I i ) { int fallback = 0; try { while ( true ) { fallback = position(); *i++ = T( context() ); } } catch (Fail) { rewind( position() - fallback ); } } #if __cplusplus >= 201103L template< typename F > bool arbitrary( F f ) { return maybe( f ); } template< typename F, typename... Args > bool arbitrary( F f, Args... args ) { bool retval = arbitrary( args... ); retval |= maybe( f ); retval |= arbitrary( args... ); return retval; } #endif template< typename T, typename I > void list( I i, TokenId sep ) { do { *i++ = T( context() ); } while ( next( sep ) ); } template< typename T, typename I, typename F > void list( I i, void (F::*sep)() ) { int fallback = position(); try { while ( true ) { *i++ = T( context() ); fallback = position(); (static_cast< F* >( this )->*sep)(); } } catch(Fail) { rewind( position() - fallback ); } } template< typename T, typename I > void list( I i, TokenId first, TokenId sep, TokenId last ) { eat( first ); list< T >( i, sep ); eat( last ); } Token eat( bool _fail = true ) { Token t = context().remove(); _position = context().position; if ( _fail && !t.valid() ) { rewind( 1 ); fail( "valid token" ); } return t; } bool next( TokenId id ) { Token t = eat( false ); if ( t.id == id ) return true; rewind( 1 ); return false; } Parser( Context &c ) : ctx( &c ) {} Parser() : ctx( 0 ) {} }; } #endif libwibble-1.1/wibble/range.test.h0000644000175000017500000001174311075401765016331 0ustar enricoenrico// -*- C++ -*- (c) 2005, 2006, 2007 Petr Rockai #include #include #include #include namespace { using namespace wibble; using namespace operators; struct TestRange { Test iteratorRange() { std::list a; a.push_back( 10 ); a.push_back( 20 ); Range< int > r = range( a.begin(), a.end() ); Range< int >::iterator i = r.begin(); assert_eq( *i, 10 ); assert_eq( *(i + 1), 20 ); assert( i + 2 == r.end() ); } Test copy() { std::list a; a.push_back( 10 ); a.push_back( 20 ); Range< int > r = range( a.begin(), a.end() ); std::list b; assert( a != b ); std::copy( r.begin(), r.end(), back_inserter( b ) ); assert( a == b ); } Test copyToConsumer() { // std::vector< int > &vec = *new (GC) std::vector< int >; std::vector< int > vec; std::list< int > a; a.push_back( 10 ); a.push_back( 20 ); Range< int > r = range( a.begin(), a.end() ); std::copy( r.begin(), r.end(), consumer( vec ) ); Range< int > r1 = range( vec ); assert_eq( *r1.begin(), 10 ); assert_eq( *(r1.begin() + 1), 20 ); assert( r1.begin() + 2 == r1.end() ); while ( !r.empty() ) { assert_eq( r.head(), r1.head() ); r = r.tail(); r1 = r1.tail(); } assert( r1.empty() ); } Test _filteredRange() { std::vector< int > v; std::list a; a.push_back( 10 ); a.push_back( 20 ); Range< int > r = range( a.begin(), a.end() ); r.output( consumer( v ) ); Range fr = filteredRange( range( v ), std::bind1st( std::equal_to< int >(), 10 ) ); assert_eq( fr.head(), 10 ); fr = fr.tail(); assert( fr.empty() ); } Test sort() { std::vector< int > v; std::list a; a.push_back( 20 ); a.push_back( 10 ); a.push_back( 30 ); Range< int > r = range( a.begin(), a.end() ); r.output( consumer( v ) ); std::sort( v.begin(), v.end() ); assert_eq( *(v.begin()), 10 ); assert_eq( *(v.begin() + 1), 20 ); assert_eq( *(v.begin() + 2), 30 ); assert( v.begin() + 3 == v.end() ); } Test assignment() { std::vector< int > vec; Range< int > a; a = range( vec ); assert( a.empty() ); vec.push_back( 4 ); Range< int > b = range( vec ); assert_eq( b.head(), 4 ); a = b; assert( !a.empty() ); assert_eq( a.head(), 4 ); } Test _transformedRange() { Range< int > a; std::vector< int > xv; Consumer< int > x = consumer( xv ); x.consume( 4 ); x.consume( 8 ); a = transformedRange( range( xv ), std::bind1st( std::plus< int >(), 2 ) ); assert_eq( a.head(), 6 ); a.removeFirst(); assert_eq( a.head(), 10 ); a.removeFirst(); assert( a.empty() ); } Test _transformedRange2() { Range< int > a; std::vector< unsigned > xv; Consumer< unsigned > x = consumer( xv ); x.consume( 4 ); x.consume( 8 ); a = transformedRange( range( xv ), std::bind1st( std::plus< int >(), 2 ) ); assert_eq( a.head(), 6 ); a.removeFirst(); assert_eq( a.head(), 10 ); a.removeFirst(); assert( a.empty() ); } Test tailOfIteratorRange() { std::vector a; a.insert( a.begin(), 30 ); a.insert( a.begin(), 10 ); a.insert( a.begin(), 20 ); Range< int > r = range( a.begin(), a.end() ); assert_eq( r.head(), 20 ); r = r.tail(); assert_eq( r.head(), 10 ); r = r.tail(); assert_eq( r.head(), 30 ); r = r.tail(); assert( r.empty() ); } Test _castedRange() { std::vector a; a.insert( a.begin(), 30 ); a.insert( a.begin(), 10 ); a.insert( a.begin(), 20 ); Range< unsigned > r = castedRange< unsigned >( range( a.begin(), a.end() ) ); assert_eq( r.head(), 20u ); r = r.tail(); assert_eq( r.head(), 10u ); r = r.tail(); assert_eq( r.head(), 30u ); r = r.tail(); assert( r.empty() ); } static void removeFirst( int &i ) { ++i; } static bool isEnd( const int &i ) { return i >= 5; } Test _generatedRange() { Range< int > r = generatedRange( 0, removeFirst, isEnd ); assert( !r.empty() ); assert_eq( *(r.begin() + 0), 0 ); assert_eq( *(r.begin() + 1), 1 ); assert_eq( *(r.begin() + 2), 2 ); assert_eq( *(r.begin() + 3), 3 ); assert_eq( *(r.begin() + 4), 4 ); assert( (r.begin() + 5) == r.end() ); } }; } libwibble-1.1/wibble/raii.test.h0000644000175000017500000000266712231005104016144 0ustar enricoenrico#if __cplusplus >= 201103L #include using namespace wibble::raii; #endif #include struct TestRAII { #if __cplusplus >= 201103L Test basic() { int y = 0; { auto del = autoDeleter( []() -> int { return 5; }, [ &y ]( int x ) { assert_eq( x, 10 ); y = x; } ); assert_eq( y, 0 ); assert_eq( del.value, 5 ); del.value = 10; } assert_eq( y, 10 ); } Test ref() { int x = 5; { auto del = refDeleter( x, []( int &y ) { y = 10; } ); assert_eq( del.value, 5 ); assert_eq( x, 5 ); } assert_eq( x, 10 ); } Test refIf() { int x = 5; { auto del = refDeleteIf( true, x, []( int &y ) { y = 10; } ); assert_eq( x, 5 ); } assert_eq( x, 10 ); { auto del = refDeleteIf( false, x, []( int &y ) { y = 15; } ); assert_eq( x, 10 ); } assert_eq( x, 10 ); } static void delFn( int &x ) { x = 0; } Test fn() { int x = 5; { AutoDelete< int & > del( x, delFn ); assert_eq( x, 5 ); } assert_eq( x, 0 ); } #else /* FIXME: workaround */ void basic() {} void ref() {} void refIf() {} void fn() {} #endif }; libwibble-1.1/wibble/libwibble.m40000644000175000017500000000040311075402047016264 0ustar enricoenrico# LIBWIBBLE_DEFS([LIBWIBBLE_REQS=libwibble]) # --------------------------------------- AC_DEFUN([LIBWIBBLE_DEFS], [ dnl Import libtagcoll data PKG_CHECK_MODULES(LIBWIBBLE,m4_default([$1], libwibble)) AC_SUBST(LIBWIBBLE_CFLAGS) AC_SUBST(LIBWIBBLE_LIBS) ]) libwibble-1.1/wibble/operators.h0000644000175000017500000001242311075402054016261 0ustar enricoenrico// -*- C++ -*- #include #include #include #include #ifndef WIBBLE_OPERATORS_H #define WIBBLE_OPERATORS_H namespace wibble { namespace operators { /* template< typename S, typename VT > struct IsContainer { typedef S T; }; template< typename S > typename IsContainer< S, typename S::value_type >::T operator &&( const S &a, const S &b ) { S ret; std::set_intersection( a.begin(), a.end(), b.begin(), b.end(), std::inserter( ret, ret.begin() ) ); return ret; } */ template< typename T > T operator+( const T &i, typename T::difference_type o ) { T r = i; std::advance( r, o ); return r; } template< typename T > std::set< T > operator &( const std::set< T > &a, const std::set< T > &b ) { std::set< T > ret; std::set_intersection( a.begin(), a.end(), b.begin(), b.end(), std::inserter( ret, ret.begin() ) ); return ret; } template< typename T > std::set< T > operator &( const std::set< T > &a, const T &b ) { std::set< T > ret; if ( a.find( b ) != a.end() ) { std::set< T > r; r.insert( b ); return r; } return std::set< T >(); } template< typename T > std::set< T > operator |( const std::set< T > &a, const T& item ) { std::set< T > ret = a; ret.insert(item); return ret; } template< typename T > std::set< T > operator |( const std::set< T > &a, const wibble::Empty& ) { return a; } template< typename T > std::set< T > operator |( const std::set< T > &a, const wibble::Singleton& item ) { std::set< T > ret = a; ret.insert(*item.begin()); return ret; } template< typename T > std::set< T > operator |( const std::set< T > &a, const std::set< T > &b ) { std::set< T > ret; std::set_union( a.begin(), a.end(), b.begin(), b.end(), std::inserter( ret, ret.begin() ) ); return ret; } template< typename T > std::set< T > operator -( const std::set< T > &a, const std::set< T > &b ) { std::set< T > ret; std::set_difference( a.begin(), a.end(), b.begin(), b.end(), std::inserter(ret, ret.begin() ) ); return ret; } template< typename T > std::set< T > operator -( const std::set< T > &a, const T& item ) { std::set< T > ret = a; ret.erase(item); return ret; } template< typename T > std::set< T > operator -( const std::set< T > &a, const wibble::Singleton& item ) { std::set< T > ret = a; ret.erase(*item.begin()); return ret; } template< typename T > std::set< T > operator -( const std::set< T > &a, const wibble::Empty& ) { return a; } template< typename T > std::set< T > &operator|=( std::set< T > &a, const wibble::Empty& ) { return a; } template< typename T > std::set< T > &operator|=( std::set< T > &a, const T& item ) { a.insert(item); return a; } // General case template< typename T, typename SEQ > std::set< T > &operator|=( std::set< T > &a, const SEQ& items ) { for (typename SEQ::const_iterator i = items.begin(); i != items.end(); ++i) a.insert(*i); return a; } // Little optimization in case a is empty template< typename T > std::set< T > &operator |=( std::set< T > &a, const std::set< T > &b ) { if (a.empty()) return a = b; for (typename std::set::const_iterator i = b.begin(); i != b.end(); ++i) a.insert(*i); return a; } // General case, but assumes that b is sorted template< typename T, typename SEQ > std::set< T > &operator &=( std::set< T > &a, const SEQ& b ) { // Little optimization: if b is empty, we avoid a run through a if (b.empty()) { a.clear(); return a; } typename std::set::iterator ia = a.begin(); typename SEQ::const_iterator ib = b.begin(); while (ia != a.end()) { if (ib != b.end() && *ib < *ia) { ++ib; } else if (ib == b.end() || *ia != *ib) { typename std::set::iterator tmp = ia; ++ia; a.erase(tmp); } else { ++ia; ++ib; } } return a; } template< typename T > std::set< T > &operator-=( std::set< T > &a, const wibble::Empty& ) { return a; } template< typename T > std::set< T > &operator-=( std::set< T > &a, const T& item ) { a.erase(item); return a; } template< typename T > std::set< T > &operator-=( std::set< T > &a, const wibble::Singleton& item ) { a.erase(*item.begin()); return a; } // General case, but works only if b is sorted template< typename T, typename SEQ > std::set< T > &operator -=( std::set< T > &a, const SEQ& b ) { typename std::set::iterator ia = a.begin(); typename SEQ::const_iterator ib = b.begin(); while (ia != a.end() && ib != b.end()) { if (*ia == *ib) { typename std::set::iterator tmp = ia; ++ia; ++ib; a.erase(tmp); } else if (*ia < *ib) ++ia; else ++ib; } return a; } template< typename T > bool operator<=( const T &a, const std::set< T > &b ) { return b.find( a ) != b.end(); } template< typename T > bool operator<=( const std::set< T > &a, const std::set< T > &b ) { typename std::set::const_iterator x = a.begin(); for ( typename std::set::const_iterator y = b.begin(); y != b.end(); ++y ) if ( x == a.end() ) return true; else if (*x == *y) ++x; else if (*x < *y) return false; return x == a.end(); } } } #endif libwibble-1.1/wibble/test-main.h0000644000175000017500000001611212231005104016131 0ustar enricoenrico// -*- C++ -*- #include #include #ifdef POSIX #include #include #endif #include #include #include struct Main : RunFeedback { int suite, test; wibble::sys::Pipe p_status; wibble::sys::Pipe p_confirm; int status_fds[2]; int confirm_fds[2]; pid_t pid; int argc; char **argv; pid_t finished; int status_code; int test_ok; int suite_ok, suite_failed; int total_ok, total_failed; int announced_suite; std::string current; bool want_fork; RunAll all; Main() : suite(0), test(0) { suite_ok = suite_failed = 0; total_ok = total_failed = 0; test_ok = 0; announced_suite = -1; } void child() { #ifdef POSIX close( status_fds[0] ); close( confirm_fds[1] ); #endif p_confirm = wibble::sys::Pipe( confirm_fds[0] ); if ( argc > 1 ) { RunSuite *s = all.findSuite( argv[1] ); if (!s) { std::cerr << "No such suite " << argv[1] << std::endl; // todo dump possible suites? exit(250); } if ( argc > 2 ) { if ( !test ) { char *end; int t = strtol( argv[2], &end, 0 ); if ( end == argv[2] && t == 0 ) { t = s->findTest( argv[2] ); if ( t < 0 ) { std::cerr << "No such test " << argv[2] << " in suite " << argv[1] << std::endl; // todo dump possible suites? exit(250); } } all.runTest( *s, t ); } } else all.runSuite( *s, test, 0, 1 ); } if ( argc == 1 ) { all.runFrom( suite, test ); } status( "done" ); exit( 0 ); } #ifdef POSIX void testDied() { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" /* std::cerr << "test died: " << test << "/" << suites[suite].testCount << std::endl; */ if ( WIFEXITED( status_code ) ) { if ( WEXITSTATUS( status_code ) == 250 ) exit( 3 ); if ( WEXITSTATUS( status_code ) == 0 ) return; } std::cout << "--> FAILED: "<< current; if ( WIFEXITED( status_code ) ) std::cout << " (exit status " << WEXITSTATUS( status_code ) << ")"; if ( WIFSIGNALED( status_code ) ) std::cout << " (caught signal " << WTERMSIG( status_code ) << ")"; std::cout << std::endl; // re-announce the suite announced_suite --; ++ test; // continue with next test test_ok = 0; suite_failed ++; #pragma GCC diagnostic pop } #endif void processStatus( std::string line ) { // std::cerr << line << std::endl; if ( line == "done" ) { // finished #ifdef POSIX #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" if ( want_fork ) { finished = waitpid( pid, &status_code, 0 ); assert_eq( pid, finished ); assert( WIFEXITED( status_code ) ); assert_eq( WEXITSTATUS( status_code ), 0 ); } #pragma GCC diagnostic pop #endif std::cout << "overall " << total_ok << "/" << total_ok + total_failed << " ok" << std::endl; exit( total_failed == 0 ? 0 : 1 ); } if ( test_ok ) { /* std::cerr << "test ok: " << test << "/" << suites[suite].testCount << std::endl; */ std::cout << "." << std::flush; suite_ok ++; ++ test; test_ok = 0; } if ( line[0] == 's' ) { if ( line[2] == 'd' ) { std::cout << " " << suite_ok << "/" << suite_ok + suite_failed << " ok" << std::endl; ++ suite; test = 0; assert( !test_ok ); total_ok += suite_ok; total_failed += suite_failed; suite_ok = suite_failed = 0; } if ( line[2] == 's' ) { if ( announced_suite < suite ) { std::cout << std::string( line.begin() + 5, line.end() ) << ": " << std::flush; announced_suite = suite; } } } if ( line[0] == 't' ) { if ( line[2] == 'd' ) { confirm(); test_ok = 1; } if ( line[2] == 's' ) { confirm(); current = std::string( line.begin() + 5, line.end() ); } } } #ifdef POSIX void parent() { close( status_fds[1] ); close( confirm_fds[0] ); p_status = wibble::sys::Pipe( status_fds[ 0 ]); std::string line; while ( true ) { if ( p_status.eof() ) { finished = waitpid( pid, &status_code, 0 ); if ( finished < 0 ) { perror( "waitpid failed" ); exit( 5 ); } assert_eq( pid, finished ); testDied(); return; } line = p_status.nextLineBlocking(); processStatus( line ); } } #endif void status( std::string line ) { // std::cerr << "status: " << line << std::endl; #ifdef POSIX if ( want_fork ) { line += "\n"; ::write( status_fds[ 1 ], line.c_str(), line.length() ); } else #endif processStatus( line ); } void confirm() { std::string line( "ack\n" ); if ( want_fork ) ::write( confirm_fds[ 1 ], line.c_str(), line.length() ); } void waitForAck() { if ( want_fork ) { std::string line = p_confirm.nextLineBlocking(); assert_eq( std::string( "ack" ), line ); } } int main( int _argc, char **_argv ) { argc = _argc; argv = _argv; all.suiteCount = sizeof(suites)/sizeof(RunSuite); all.suites = suites; all.feedback = this; #ifdef POSIX want_fork = argc <= 2; #else want_fork = false; #endif while (true) { #ifdef POSIX if ( socketpair( PF_UNIX,SOCK_STREAM, 0, status_fds ) ) return 1; if ( socketpair( PF_UNIX,SOCK_STREAM, 0, confirm_fds ) ) return 1; if ( want_fork ) { pid = fork(); if ( pid < 0 ) return 2; if ( pid == 0 ) { // child child(); } else { parent(); } } else #endif child(); } return 0; } }; int main( int argc, char **argv ) { return Main().main( argc, argv ); } libwibble-1.1/wibble/empty.test.h0000644000175000017500000000070611075401765016370 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include using namespace wibble; struct TestEmpty { Test basic() { Empty container; assert_eq(container.size(), 0u); Empty::iterator i = container.begin(); assert(i == container.end()); assert(!(i != container.end())); } }; libwibble-1.1/wibble/README0000644000175000017500000000637211475707271014775 0ustar enricoenricoWibble repositories =================== There are two darcs repositories for wibble: - http://people.debian.org/~enrico/wibble/standalone - http://people.debian.org/~enrico/wibble/embeddable Standalone contains the whole wibble package, including the toplevel cmake and autotools configuration and debian and fedora packaging. It is used to build the wibble development library. Embeddable contains only the wibble/ directory and can be used to embed wibble into other source directories: that way you can use wibble code without adding a build dependency. Using embedded wibble ===================== With autotools -------------- You can embed wibble in an autotools project, following these instructions: 1. add wibble to SUBDIRS in Makefile.am 2. add AM_CONFIG_HEADER(wibble/config.h) to configure.ac 3. you can use the gl_CHECK_TYPE_STRUCT_DIRENT_D_TYPE autoconf macro from gnulib to define HAVE_STRUCT_DIRENT_D_TYPE: this will enable a faster implementation of sys::fs::Directory::isdir on systems that support it Without darcs ------------- Create a new project of yours (assuming you do not want to use darcs): mkdir my-project cd my-project git/bzr/hg/svn... init mkdir src vi src/main.c git/bzr/hg/svn... commit changes At some point you realise you need to embed wibble: darcs init darcs pull http://people.debian.org/~enrico/wibble/embeddable Now you plug wibble/ into your build system, be it autotools or cmake, and you're done. Someone made changes to wibble that you would like to pull: darcs pull http://people.debian.org/~enrico/wibble/embeddable If you instead make changes to wibble, you can record them with darcs and others can pull from you. With darcs ---------- However, if your project is managed by darcs, the sequence is a little different: mkdir my-project cd my-project darcs init mkdir src vi src/main.c darcs add -r src darcs record ... Now you want wibble, and under assumption, that your repository never contained a directory called "wibble", you can do this: darcs pull http://people.debian.org/~enrico/wibble/embeddable At which point, the both repositories will be combined into a single one, with patches from both. This is fine with darcs, since the patches do not conflict. This way, people using your repository will never need to worry about wibble, since you can integrate it on buildsystem level as well, and it becomes natural part of both your repository and source tree. When you need to update wibble in your source tree, you again just issue darcs pull http://people.debian.org/~enrico/wibble/embeddable And the missing changes to wibble will be brought into your copy. Note: If you have (or had at some point in the past) some sort of wibble/ subdirectory already in your darcs repository, this will lead to conflicts. I do not know how well things will turn out, it may be that it will work, it may be it won't. But since you usually know pretty soon that you want wibble, and using a directory name "wibble" for your own stuff is unlikely anyway, i do not expect this to be a problem. Standalone wibble ================= The standalone wibble is just like any other standalone project that embeds wibble, pulling changes from embeddable. libwibble-1.1/wibble/regexp.cpp0000644000175000017500000000766311075402002016073 0ustar enricoenrico/* * OO wrapper for regular expression functions * * Copyright (C) 2003--2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include using namespace std; namespace wibble { namespace exception { ////// wibble::exception::Regexp Regexp::Regexp(const regex_t& re, int code, const string& context) throw () : Generic(context), m_code(code) { int size = 64; char* msg = new char[size]; int nsize = regerror(code, &re, msg, size); if (nsize > size) { delete[] msg; msg = new char[nsize]; regerror(code, &re, msg, nsize); } m_message = msg; delete[] msg; } } ////// Regexp Regexp::Regexp(const string& expr, int match_count, int flags) throw (wibble::exception::Regexp) : pmatch(0), nmatch(match_count) { if (match_count == 0) flags |= REG_NOSUB; int res = regcomp(&re, expr.c_str(), flags); if (res) throw wibble::exception::Regexp(re, res, "Compiling regexp \"" + expr + "\""); if (match_count > 0) pmatch = new regmatch_t[match_count]; } Regexp::~Regexp() throw () { regfree(&re); if (pmatch) delete[] pmatch; } bool Regexp::match(const string& str, int flags) throw(wibble::exception::Regexp) { int res; if (nmatch) { res = regexec(&re, str.c_str(), nmatch, pmatch, flags); lastMatch = str; } else res = regexec(&re, str.c_str(), 0, 0, flags); switch (res) { case 0: return true; case REG_NOMATCH: return false; default: throw wibble::exception::Regexp(re, res, "Matching string \"" + str + "\""); } } string Regexp::operator[](int idx) throw (wibble::exception::OutOfRange) { if (idx > nmatch) throw wibble::exception::ValOutOfRange("index", idx, 0, nmatch, "getting submatch of regexp"); if (pmatch[idx].rm_so == -1) return string(); return string(lastMatch, pmatch[idx].rm_so, pmatch[idx].rm_eo - pmatch[idx].rm_so); } size_t Regexp::matchStart(int idx) throw (wibble::exception::OutOfRange) { if (idx > nmatch) throw wibble::exception::ValOutOfRange("index", idx, 0, nmatch, "getting submatch of regexp"); return pmatch[idx].rm_so; } size_t Regexp::matchEnd(int idx) throw (wibble::exception::OutOfRange) { if (idx > nmatch) throw wibble::exception::ValOutOfRange("index", idx, 0, nmatch, "getting submatch of regexp"); return pmatch[idx].rm_eo; } size_t Regexp::matchLength(int idx) throw (wibble::exception::OutOfRange) { if (idx > nmatch) throw wibble::exception::ValOutOfRange("index", idx, 0, nmatch, "getting submatch of regexp"); return pmatch[idx].rm_eo - pmatch[idx].rm_so; } Tokenizer::const_iterator& Tokenizer::const_iterator::operator++() { // Skip past the last token beg = end; if (tok.re.match(tok.str.substr(beg))) { beg += tok.re.matchStart(0); end = beg + tok.re.matchLength(0); } else beg = end = tok.str.size(); return *this; } Splitter::const_iterator& Splitter::const_iterator::operator++() { if (re.match(next)) { if (re.matchLength(0)) { cur = next.substr(0, re.matchStart(0)); next = next.substr(re.matchStart(0) + re.matchLength(0)); } else { if (!next.empty()) { cur = next.substr(0, 1); next = next.substr(1); } else { cur = next; } } } else { cur = next; next = string(); } return *this; } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/strongenumflags.test.h0000644000175000017500000000314712231005104020430 0ustar enricoenrico#if __cplusplus >= 201103L #include #endif #include using namespace wibble; #if __cplusplus >= 201103L enum class A : unsigned char { X = 1, Y = 2, Z = 4 }; enum class B : unsigned short { X = 1, Y = 2, Z = 4 }; enum class C : unsigned { X = 1, Y = 2, Z = 4 }; enum class D : unsigned long { X = 1, Y = 2, Z = 4 }; #endif struct TestStrongEnumFlags { #if __cplusplus >= 201103L template< typename Enum > void testEnum() { StrongEnumFlags< Enum > e1; StrongEnumFlags< Enum > e2( Enum::X ); assert( !e1 ); assert( e2 ); assert( e1 | e2 ); assert( Enum::X | Enum::Y ); assert( e2 | Enum::Z ); assert( e2.has( Enum::X ) ); assert( e2 & Enum::X ); assert( !( Enum::X & Enum::Y ) ); assert( Enum::X | Enum::Y | Enum::Z ); assert( !( Enum::X & Enum::Y & Enum::Z ) ); assert( ( Enum::X | Enum::Y | Enum::Z ) & Enum::X ); } #endif #if __cplusplus >= 201103L // we don't want to break classical enums and ints by out operators Test regression() { enum Classic { C_X = 1, C_Y = 2, C_Z = 4 }; assert( C_X | C_Y | C_Z ); assert( 1 | 2 | 4 ); assert( C_X & 1 ); } Test enum_uchar() { testEnum< A >(); } Test enum_ushort() { testEnum< B >(); } Test enum_uint() { testEnum< C >(); } Test enum_ulong() { testEnum< D >(); } #else /* FIXME work around issues with non-C++11 builds */ void regression() {} void enum_uchar() {} void enum_ushort() {} void enum_uint() {} void enum_ulong() {} #endif }; libwibble-1.1/wibble/exception.test.h0000644000175000017500000000624212231005104017207 0ustar enricoenrico/* -*- C++ -*- * Generic base exception hierarchy * * Copyright (C) 2003,2004,2005,2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include using namespace std; namespace wex = wibble::exception; struct TestException { Test generic() { try { throw wex::Generic("antani"); } catch ( std::exception& e ) { assert(string(e.what()).find("antani") != string::npos); } try { throw wex::Generic("antani"); } catch ( wex::Generic& e ) { assert(e.fullInfo().find("antani") != string::npos); } } Test system() { #ifdef POSIX try { assert_eq(access("does-not-exist", F_OK), -1); throw wex::System("checking for existance of nonexisting file"); } catch ( wibble::exception::System& e ) { // Check that we caught the right value of errno assert_eq(e.code(), ENOENT); } try { assert_eq(access("does-not-exist", F_OK), -1); throw wex::File("does-not-exist", "checking for existance of nonexisting file"); } catch ( wex::File& e ) { // Check that we caught the right value of errno assert_eq(e.code(), ENOENT); assert(e.fullInfo().find("does-not-exist") != string::npos); } #endif } Test badCast() { int check = -1; try { check = 0; throw wex::BadCastExt< int, const char * >( "test" ); check = 1; } catch ( wex::BadCast& e ) { assert_eq( e.fullInfo(), "bad cast: from i to PKc. Context:\n test" ); check = 2; } assert_eq( check, 2 ); } Test addContext() { wex::AddContext ctx( "toplevel context" ); int check = -1; try { wex::AddContext ctx( "first context" ); check = 0; { wex::AddContext ctx( "second context" ); throw wex::Generic( "foobar" ); } } catch( wex::Generic &e ) { assert_eq( e.formatContext(), "toplevel context, \n " "first context, \n " "second context, \n " "foobar" ); check = 2; } assert_eq( check, 2 ); } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/maybe.h0000644000175000017500000000261312231005104015326 0ustar enricoenrico// -*- C++ -*- #include #ifndef WIBBLE_MAYBE_H #define WIBBLE_MAYBE_H namespace wibble { /* A Maybe type. Values of type Maybe< T > can be either Just T or Nothing. Maybe< int > foo; foo = Maybe::Nothing(); // or foo = Maybe::Just( 5 ); if ( !foo.nothing() ) { int real = foo; } else { // we haven't got anythig in foo } Maybe takes a default value, which is normally T(). That is what you get if you try to use Nothing as T. */ template struct Maybe : mixin::Comparable< Maybe< T > > { bool nothing() const { return m_nothing; } T &value() { return m_value; } const T &value() const { return m_value; } Maybe( bool n, const T &v ) : m_nothing( n ), m_value( v ) {} Maybe( const T &df = T() ) : m_nothing( true ), m_value( df ) {} static Maybe Just( const T &t ) { return Maybe( false, t ); } static Maybe Nothing( const T &df = T() ) { return Maybe( true, df ); } operator T() const { return value(); } bool operator <=( const Maybe< T > &o ) const { if (o.nothing()) return true; if (nothing()) return false; return value() <= o.value(); } protected: bool m_nothing:1; T m_value; }; template<> struct Maybe< void > { Maybe() {} static Maybe Just() { return Maybe(); } static Maybe Nothing() { return Maybe(); } }; } #endif libwibble-1.1/wibble/log.test.h0000644000175000017500000001035412231005104015771 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include #include #include #include #include #include /** * TODO: find a way to test the syslog sender, if at all possible */ namespace { using namespace std; using namespace wibble; using namespace wibble::log; struct TestLog { // Test sender for log::Streambuf struct Sender1 : public log::Sender { // Here go the log messages std::vector< std::pair > log; virtual ~Sender1() {} // Interface for the streambuf to send messages virtual void send(Level level, const std::string& msg) { log.push_back(make_pair(level, msg)); } // Dump all the logged messages to cerr void dump() { for (size_t i = 0; i < log.size(); ++i) std::cerr << log[i].first << " -> " << log[i].second << " <-" << std::endl; } }; Test streambuf() { // Instantiate a Streambuf and write something in it Sender1 s; { log::Streambuf ls(&s); ostream o(&ls); // Send a normal log message o << "test" << endl; assert_eq(s.log.size(), 1u); assert_eq(s.log[0].first, log::INFO); assert_eq(s.log[0].second, "test"); // Send a log message with a different priority //o << log::lev(log::WARN) << "test" << endl; o << log::WARN << "test" << endl; assert_eq(s.log.size(), 2u); assert_eq(s.log[1].first, log::WARN); assert_eq(s.log[1].second, "test"); // Ensure that log messages are only sent after a newline o << "should eventually appear"; assert_eq(s.log.size(), 2u); } // Or at streambuf destruction assert_eq(s.log.size(), 3u); assert_eq(s.log[2].first, log::INFO); assert_eq(s.log[2].second, "should eventually appear"); //s.dump(); } // Test the NullSender Test nullSender() { // Null does nothing, so we cannot test the results. log::NullSender ns; ns.send(log::INFO, "test"); log::Streambuf null(&ns); ostream o(&null); // Send a normal log message o << "test" << endl; // Send a log message with a different priority //o << log::lev(log::WARN) << "test" << endl; o << log::WARN << "test" << endl; // Ensure that log messages are only sent after a newline o << "should eventually appear"; } // Test the FileSender Test fileSender() { #ifdef POSIX // there's no /dev/null on win32 // We send to /dev/null, so we cannot test the results. log::FileSender ns("/dev/null"); ns.send(log::INFO, "test"); log::Streambuf file(&ns); ostream o(&file); // Send a normal log message o << "test" << endl; // Send a log message with a different priority //o << log::lev(log::WARN) << "test" << endl; o << log::WARN << "test" << endl; // Ensure that log messages are only sent after a newline o << "should eventually appear"; #endif } // Test the OstreamSender Test ostreamSender() { // We send to /dev/null, so we cannot test the results. #ifdef POSIX // there's no /dev/null on win32 std::ofstream null("/dev/null", std::ios::out); assert(!null.fail()); log::OstreamSender sender(null); sender.send(log::INFO, "test"); log::Streambuf log(&sender); ostream o(&log); // Send a normal log message o << "test" << endl; // Send a log message with a different priority //o << log::lev(log::WARN) << "test" << endl; o << log::WARN << "test" << endl; // Ensure that log messages are only sent after a newline o << "should eventually appear"; #endif } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/0000755000175000017500000000000012231005104016344 5ustar enricoenricolibwibble-1.1/wibble/commandline/parser.cpp0000644000175000017500000000367211075402125020365 0ustar enricoenrico#include #include #include using namespace std; namespace wibble { namespace commandline { void StandardParser::outputHelp(std::ostream& out) { commandline::Help help(name(), m_version); commandline::Engine* e = foundCommand(); if (e) // Help on a specific command help.outputHelp(out, *e); else // General help help.outputHelp(out, *this); } bool StandardParser::parse(int argc, const char* argv[]) { if (Parser::parse(argc, argv)) return true; if (help->boolValue()) { // Provide help as requested outputHelp(cout); return true; } if (version->boolValue()) { // Print the program version commandline::Help help(name(), m_version); help.outputVersion(cout); return true; } return false; } bool StandardParserWithManpage::parse(int argc, const char* argv[]) { if (StandardParser::parse(argc, argv)) return true; if (manpage->isSet()) { // Output the manpage commandline::Manpage man(name(), m_version, m_section, m_author); string hooks(manpage->value()); if (!hooks.empty()) man.readHooks(hooks); man.output(cout, *this); return true; } return false; } bool StandardParserWithMandatoryCommand::parse(int argc, const char* argv[]) { if (StandardParserWithManpage::parse(argc, argv)) return true; if (!foundCommand()) { commandline::Help help(name(), m_version); help.outputHelp(cout, *this); return true; } if (foundCommand() == helpCommand) { commandline::Help help(name(), m_version); if (hasNext()) { // Help on a specific command string command = next(); if (Engine* e = this->command(command)) help.outputHelp(cout, *e); else throw exception::BadOption("unknown command " + command + "; run '" + argv[0] + " help' " "for a list of all the available commands"); } else { // General help help.outputHelp(cout, *this); } return true; } return false; } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/engine.cpp0000644000175000017500000002152411477663551020354 0ustar enricoenrico#include #include using namespace std; namespace wibble { namespace commandline { void Engine::addWithoutAna(Option* o) { const vector& shorts = o->shortNames; for (vector::const_iterator i = shorts.begin(); i != shorts.end(); i++) { map::iterator j = m_short.find(*i); if (j != m_short.end()) throw exception::Consistency("building commandline parser", string("short option ") + *i + " is already mapped to " + j->second->name()); m_short[*i] = o; } const vector& longs = o->longNames; for (vector::const_iterator i = longs.begin(); i != longs.end(); i++) { map::iterator j = m_long.find(*i); if (j != m_long.end()) throw exception::Consistency("building commandline parser", string("long option ") + *i + " is already mapped to " + j->second->name()); m_long[*i] = o; } } void Engine::addWithoutAna(const std::vector& o) { for (std::vector::const_iterator i = o.begin(); i != o.end(); ++i) addWithoutAna(*i); } void Engine::add(const std::string& alias, Engine* o) { map::iterator a = m_aliases.find(alias); if (a != m_aliases.end()) throw exception::Consistency("command " + alias + " has already been set to " + a->second->name()); m_aliases[alias] = o; } void Engine::rebuild() { // Clear the engine tables m_short.clear(); m_long.clear(); // Add the options from the groups for (std::vector::const_iterator i = m_groups.begin(); i != m_groups.end(); ++i) addWithoutAna((*i)->options); // Add the stray options addWithoutAna(m_options); // Add the commands m_aliases.clear(); for (vector::const_iterator i = m_commands.begin(); i != m_commands.end(); ++i) { add((*i)->primaryAlias, *i); for (vector::const_iterator j = (*i)->aliases.begin(); j != (*i)->aliases.end(); ++j) add(*j, *i); } } std::pair Engine::parseFirstIfKnown(ArgList& list, ArgList::iterator begin) { std::string& opt = *begin; if (opt[1] != '-') { // Short option char c = opt[1]; // Loopup the option engine map::const_iterator engine = m_short.find(c); if (engine == m_short.end()) return make_pair(begin, false); // Parse the arguments, if any ArgList::iterator next = begin; ++next; engine->second->parse(list, next); // Dispose of the parsed argument if (opt[2] == 0) { // Remove what's left of the switch cluster as well list.eraseAndAdvance(begin); } else { // Else only remove the character from the switch opt.erase(opt.begin() + 1); } } else { // Long option // Split option and argument from "--foo=bar" size_t sep = opt.find('='); string name, arg; bool has_arg; if (sep == string::npos) { // No argument name = opt.substr(2); has_arg = false; } else { name = opt.substr(2, sep - 2); arg = opt.substr(sep + 1); has_arg = true; } map::const_iterator engine = m_long.find(name); if (engine == m_long.end()) return make_pair(begin, false); if (has_arg) engine->second->parse(arg); else engine->second->parse_noarg(); // Remove the parsed element list.eraseAndAdvance(begin); } return make_pair(begin, true); } ArgList::iterator Engine::parseKnownSwitches(ArgList& list, ArgList::iterator begin) { // Parse the first items, chopping them off the list, until it works while (1) { if (begin == list.end()) return begin; if (!list.isSwitch(begin)) break; pair res = parseFirstIfKnown(list, begin); if (!res.second) break; begin = res.first; } // If requested, stop here if (no_switches_after_first_arg) return begin; // Parse the next items, chopping off the list only those that we know for (ArgList::iterator cur = begin; cur != list.end(); ) { // Skip non-switches if (!list.isSwitch(cur)) { ++cur; continue; } pair res = parseFirstIfKnown(list, cur); if (!res.second) // If the switch is not handled, move past it ++cur; else cur = res.first; } return begin; } Option* Engine::add(Option* o) { m_options.push_back(o); return o; } OptionGroup* Engine::add(OptionGroup* group) { m_groups.push_back(group); return group; } Engine* Engine::add(Engine* o) { m_commands.push_back(o); return o; } #if 0 ArgList::iterator OptionEngine::parseConsecutiveSwitches(ArgList& list, ArgList::iterator begin) { while (begin != list.end() && list.isSwitch(*begin)) { pair res = parseFirstIfKnown(list, begin); if (!res.second) { if ((*begin)[1] != '-') throw exception::BadOption(string("unknown short option ") + *begin); else throw exception::BadOption(string("unknown long option ") + *begin); } begin = res.first; } return begin; } #endif #if 0 ArgList::iterator Engine::parse(ArgList& list, ArgList::iterator begin) { rebuild(); bool foundNonSwitches = false; ArgList::iterator firstNonSwitch; while (begin != list.end()) { // Parse a row of switches begin = parseConsecutiveSwitches(list, begin); // End of switches? if (begin == list.end()) break; // If the end is the "--" marker, take it out of the list as well if (*begin == "--") { list.eraseAndAdvance(begin); break; } if (!foundNonSwitches) { // Mark the start of non-switches if we haven't done it already firstNonSwitch = begin; foundNonSwitches = true; } // Skip past the non switches while (begin != list.end() && !list.isSwitch(begin)) ++begin; } return foundNonSwitches ? firstNonSwitch : begin; } #endif ArgList::iterator Engine::parse(ArgList& list, ArgList::iterator begin) { rebuild(); // Parse and remove known switches begin = parseKnownSwitches(list, begin); m_found_command = 0; // Check if we have to handle commands if (!m_commands.empty()) { // Look for the first non-switch in the list ArgList::iterator cmd = begin; while (cmd != list.end() && list.isSwitch(cmd)) ++cmd; if (cmd != list.end()) { // A command has been found, ensure that we can handle it map::iterator a = m_aliases.find(*cmd); if (a == m_aliases.end()) throw exception::BadOption("unknown command " + *cmd); // Remove the command from the list if (cmd == begin) ++begin; list.erase(cmd); // We found a valid command, let's enable subcommand parsing m_found_command = a->second; } } if (!m_found_command) { // If we don't have any more subcommands to parse, then ensure that // there are no switches left to process for (ArgList::iterator i = begin; i != list.end(); ++i) { if (*i == "--") { // Remove '--' and stop looking for switches if (begin == i) { begin++; list.erase(i); } break; } if (list.isSwitch(i)) throw exception::BadOption(string("unknown option ") + *i); else if (no_switches_after_first_arg) // If requested, stop looking for switches // after the first non-switch argument break; } m_found_command = 0; return begin; } else { // Else, invoke the subcommand engine on the list return m_found_command->parse(list, begin); } } void Engine::dump(std::ostream& out, const std::string& pfx) { rebuild(); out << pfx << "Engine " << name() << ": " << endl; if (!m_commands.empty()) { out << pfx << " " << m_commands.size() << " commands:" << endl; for (std::vector::const_iterator i = m_commands.begin(); i != m_commands.end(); ++i) (*i)->dump(out, pfx + " "); } if (!m_aliases.empty()) { out << pfx << " Command parse table:" << endl; for (std::map::const_iterator i = m_aliases.begin(); i != m_aliases.end(); ++i) out << pfx << " " << i->first << " -> " << i->second->name() << endl; } if (!m_groups.empty()) { out << pfx << " " << m_groups.size() << " OptionGroups:" << endl; for (std::vector::const_iterator i = m_groups.begin(); i != m_groups.end(); ++i) out << pfx << " " << (*i)->description << endl; } if (!m_options.empty()) { out << pfx << " " << m_options.size() << " Options:" << endl; for (std::vector::const_iterator i = m_options.begin(); i != m_options.end(); ++i) out << pfx << " " << (*i)->fullUsage() << endl; } if (!m_short.empty()) { out << pfx << " Short options parse table:" << endl; for (std::map::const_iterator i = m_short.begin(); i != m_short.end(); ++i) out << pfx << " " << i->first << " -> " << i->second->fullUsage() << endl; } if (!m_long.empty()) { out << pfx << " Long options parse table:" << endl; for (std::map::const_iterator i = m_long.begin(); i != m_long.end(); ++i) out << pfx << " " << i->first << " -> " << i->second->fullUsage() << endl; } } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/doc.cpp0000644000175000017500000002573311372245054017646 0ustar enricoenrico#include #include #include #include #include #include #include #ifdef _WIN32 #include #endif using namespace std; namespace wibble { namespace commandline { class HelpWriter { // Width of the console std::ostream& out; int m_width; public: HelpWriter(std::ostream& out); // Write 'size' spaces to out void pad(size_t size); // Output an item from a list. The first bulletsize columns will be used to // output bullet, the rest will have text, wordwrapped and properly aligned void outlist(const std::string& bullet, size_t bulletsize, const std::string& text); void outstring(const std::string& str); }; HelpWriter::HelpWriter(std::ostream& out) : out(out) { char* columns = getenv("COLUMNS"); m_width = columns ? atoi(columns) : 80; } void HelpWriter::pad(size_t size) { for (size_t i = 0; i < size; i++) out << " "; } void HelpWriter::outlist(const std::string& bullet, size_t bulletsize, const std::string& text) { text::WordWrap wrapper(text); size_t rightcol = m_width - bulletsize; out << bullet; pad(bulletsize - bullet.size()); out << wrapper.get(rightcol); out << endl; while (wrapper.hasData()) { pad(bulletsize); out << wrapper.get(rightcol); out << endl; } } void HelpWriter::outstring(const std::string& str) { text::WordWrap wrapper(str); while (wrapper.hasData()) { out << wrapper.get(m_width); out << endl; } } void Help::outputOptions(ostream& out, HelpWriter& writer, const Engine& p) { // Compute size of option display size_t maxLeftCol = 0; for (vector::const_iterator i = p.groups().begin(); i != p.groups().end(); i++) { if ((*i)->hidden) continue; for (vector::const_iterator j = (*i)->options.begin(); j != (*i)->options.end(); j++) { if ((*j)->hidden) continue; size_t w = (*j)->fullUsage().size(); if (w > maxLeftCol) maxLeftCol = w; } } for (vector::const_iterator j = p.options().begin(); j != p.options().end(); j++) { if ((*j)->hidden) continue; size_t w = (*j)->fullUsage().size(); if (w > maxLeftCol) maxLeftCol = w; } if (maxLeftCol) { // Output the options out << endl; out << "Options are:" << endl; for (vector::const_iterator i = p.groups().begin(); i != p.groups().end(); i++) { if ((*i)->hidden) continue; if (!(*i)->description.empty()) { out << endl; writer.outstring((*i)->description + ":"); out << endl; } for (vector::const_iterator j = (*i)->options.begin(); j != (*i)->options.end(); j++) { if ((*j)->hidden) continue; writer.outlist(" " + (*j)->fullUsage(), maxLeftCol + 3, (*j)->description); } } if (!p.options().empty()) { out << endl; writer.outstring("Other options:"); out << endl; for (vector::const_iterator j = p.options().begin(); j != p.options().end(); j++) { if ((*j)->hidden) continue; writer.outlist(" " + (*j)->fullUsage(), maxLeftCol + 3, (*j)->description); } } } } void Help::outputVersion(std::ostream& out) { out << m_app << " version " << m_ver << endl; } void Help::outputHelp(std::ostream& out, const Engine& p) { HelpWriter writer(out); if (!p.commands().empty()) { // Dig informations from p const std::vector& commands = p.commands(); // Compute the maximum length of alias names size_t maxAliasSize = 0; for (vector::const_iterator i = commands.begin(); i != commands.end(); i++) { if ((*i)->hidden) continue; const string& str = (*i)->primaryAlias; if (maxAliasSize < str.size()) maxAliasSize = str.size(); } out << "Usage: " << m_app << " [options] " << p.usage << endl; out << endl; writer.outstring("Description: " + p.description); out << endl; out << "Commands are:" << endl; out << endl; // Print the commands for (vector::const_iterator i = commands.begin(); i != commands.end(); i++) { if ((*i)->hidden) continue; string aliases; const vector& v = (*i)->aliases; if (!v.empty()) { aliases += " May also be invoked as "; for (vector::const_iterator j = v.begin(); j != v.end(); j++) if (j == v.begin()) aliases += *j; else aliases += " or " + *j; aliases += "."; } writer.outlist(" " + (*i)->primaryAlias, maxAliasSize + 3, (*i)->description + "." + aliases); } } else { // FIXME the || m_app == thing is a workaround... if (p.primaryAlias.empty() || m_app == p.primaryAlias) out << "Usage: " << m_app << " [options] " << p.usage << endl; else out << "Usage: " << m_app << " [options] " << p.primaryAlias << " [options] " << p.usage << endl; out << endl; if (!p.aliases.empty()) { out << "Command aliases: "; for (vector::const_iterator i = p.aliases.begin(); i != p.aliases.end(); i++) if (i == p.aliases.begin()) out << *i; else out << ", " << *i; out << "." << endl; out << endl; } writer.outstring("Description: " + p.description); } if (p.hasOptions()) outputOptions(out, writer, p); out << endl; } static string toupper(const std::string& str) { string res; for (size_t i = 0; i < str.size(); i++) res += ::toupper(str[i]); return res; } static string man_date() { time_t tnow = time(0); struct tm* now = gmtime(&tnow); char buf[20]; const char* oldlocale = setlocale(LC_TIME, "C"); string res(buf, strftime(buf, 20, "%B %d, %Y", now)); setlocale(LC_TIME, oldlocale); return res; } void Manpage::outputParagraph(std::ostream& out, const std::string& str) { for (size_t i = 0; i < str.size(); i++) switch (str[i]) { case '-': out << "\\-"; break; case '\n': out << "\n.br\n"; break; default: out << str[i]; } out << '\n'; } void Manpage::outputOption(std::ostream& out, const Option* o) { out << ".TP" << endl; out << ".B " << o->fullUsageForMan() << endl; out << o->description << "." << endl; } void Manpage::runHooks(std::ostream& out, const std::string& section, where where) { for (std::vector::const_iterator i = hooks.begin(); i != hooks.end(); i++) if (i->section == section && i->placement == where) out << i->text; } void Manpage::startSection(std::ostream& out, const std::string& name) { runHooks(out, name, BEFORE); out << ".SH " << name << endl; runHooks(out, name, BEGINNING); lastSection = name; } void Manpage::endSection(std::ostream& out) { runHooks(out, lastSection, END); lastSection.clear(); } void Manpage::outputOptions(std::ostream& out, const Engine& p) { for (vector::const_iterator i = p.groups().begin(); i != p.groups().end(); i++) { if ((*i)->hidden) continue; if (!(*i)->description.empty()) out << endl << (*i)->description << ":" << endl; for (vector::const_iterator j = (*i)->options.begin(); j != (*i)->options.end(); ++j) { if ((*j)->hidden) continue; outputOption(out, *j); } out << ".PP" << endl; } if (!p.options().empty()) { out << endl; out << "Other options:" << endl; for (vector::const_iterator j = p.options().begin(); j != p.options().end(); ++j) { if ((*j)->hidden) continue; outputOption(out, *j); } } } void Manpage::output(std::ostream& out, const Engine& p) { // Manpage header out << ".TH " << toupper(m_app) << " " << m_section << " \"" << man_date() << "\" \"" << m_ver << "\"" << endl; startSection(out, "NAME"); out << p.name() << " \\- " << p.description << endl; endSection(out); startSection(out, "SYNOPSIS"); out << "\\fB" << p.name() << "\\fP [options] " << p.usage << endl; endSection(out); startSection(out, "DESCRIPTION"); if (!p.longDescription.empty()) outputParagraph(out, p.longDescription); endSection(out); if (!p.commands().empty()) { const vector& commands = p.commands(); startSection(out, "COMMANDS"); out << "\\fB" << p.name() << "\\fP accepts a non-switch argument, that indicates what is the operation that should be performed:" << endl; for (vector::const_iterator i = commands.begin(); i != commands.end(); i++) { if ((*i)->hidden) continue; out << ".TP" << endl; out << "\\fB" << (*i)->primaryAlias << "\\fP"; const vector& v = (*i)->aliases; for (vector::const_iterator j = v.begin(); j != v.end(); j++) out << " or \\fB" << *j << "\\fP"; out << " " << (*i)->usage << endl; out << ".br" << endl; if ((*i)->longDescription.empty()) outputParagraph(out, (*i)->description); else outputParagraph(out, (*i)->longDescription); } endSection(out); } startSection(out, "OPTIONS"); out << "This program follows the usual GNU command line syntax, with long options starting with two dashes (`\\-')." << endl << endl; if (!p.commands().empty()) out << "Every one of the commands listed above has its own set of options. To keep this manpage readable, all the options are presented together. Please refer to \"\\fB" << p.name() << "\\fP help \\fIcommand\\fP\" to see which options are accepted by a given command." << endl; // Output the general options outputOptions(out, p); // Output group-specific options if (!p.commands().empty()) { const vector& commands = p.commands(); for (vector::const_iterator i = commands.begin(); i != commands.end(); i++) { if ((*i)->hidden) continue; out << "\\fBOptions for command " << (*i)->primaryAlias << "\\fP" << endl; out << ".br" << endl; outputOptions(out, **i); } } endSection(out); startSection(out, "AUTHOR"); out << "\\fB" << p.name() << "\\fP is maintained by " << m_author << "." << endl << endl; out << "This manpage has been automatically generated by the " << m_app << " program." << endl; endSection(out); } static string readline(FILE* in) { string res; int c; while ((c = getc(in)) != EOF && c != '\n') res += c; return res; } void Manpage::readHooks(const std::string& file) { FILE* in = fopen(file.c_str(), "r"); if (!in) throw exception::File(file, "opening for reading"); string section; commandline::Manpage::where placement = commandline::Manpage::BEFORE; string text; while (!feof(in)) { string line(readline(in)); if (line.empty()) continue; if (line[0] == '|') { text += line.substr(1) + "\n"; } else if (isalpha(line[0])) { if (!section.empty()) { addHook(section, placement, text); text.clear(); } size_t sep = line.find(' '); if (sep == string::npos) { fclose(in); throw exception::Consistency("expected two words in line: " + line); } section = line.substr(0, sep); string w(line, sep+1); if (w == "before") { placement = commandline::Manpage::BEFORE; } else if (w == "beginning") { placement = commandline::Manpage::BEGINNING; } else if (w == "end") { placement = commandline::Manpage::END; } else { fclose(in); throw exception::Consistency("expected 'before', 'beginning' or 'end' in line: " + line); } } } if (!section.empty()) addHook(section, placement, text); fclose(in); } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/core.h0000644000175000017500000000332011075402142017453 0ustar enricoenrico#ifndef WIBBLE_COMMANDLINE_CORE_H #define WIBBLE_COMMANDLINE_CORE_H #include #include #include #include namespace wibble { namespace exception { class BadOption : public Consistency { std::string m_error; public: BadOption(const std::string& error, const std::string& context = std::string("parsing commandline options")) throw () : Consistency(context), m_error(error) {} ~BadOption() throw () {} virtual const char* type() const throw () { return "BadOption"; } virtual std::string desc() const throw () { return m_error; } }; } namespace commandline { class ArgList : public std::list { public: // Remove the item pointed by the iterator, and advance the iterator to the // next item. Returns i itself. inline iterator& eraseAndAdvance(iterator& i) { if (i == end()) return i; iterator next = i; ++next; erase(i); i = next; return i; } static bool isSwitch(const char* str); static bool isSwitch(const std::string& str); static bool isSwitch(const const_iterator& iter); static bool isSwitch(const iterator& iter); }; class Managed { public: virtual ~Managed() {} }; /** Keep track of various wibble::commandline components, and deallocate them * at object destruction. * * If an object is added multiple times, it will still be deallocated only once. */ class MemoryManager { std::set components; Managed* addManaged(Managed* o) { components.insert(o); return o; } public: ~MemoryManager() { for (std::set::const_iterator i = components.begin(); i != components.end(); ++i) delete *i; } template T* add(T* item) { addManaged(item); return item; } }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/commandline/options.h0000644000175000017500000002453712220032241020223 0ustar enricoenrico#ifndef WIBBLE_COMMANDLINE_OPTIONS_H #define WIBBLE_COMMANDLINE_OPTIONS_H #include #include #include namespace wibble { namespace commandline { // Types of values for the command line options struct Bool { typedef bool value_type; static bool parse(const std::string& val); static bool toBool(const value_type& val); static int toInt(const value_type& val); static std::string toString(const value_type& val); static bool init_val; }; struct Int { typedef int value_type; static int parse(const std::string& val); static bool toBool(const value_type& val); static int toInt(const value_type& val); static std::string toString(const value_type& val); static int init_val; }; struct String { typedef std::string value_type; static std::string parse(const std::string& val); static bool toBool(const value_type& val); static int toInt(const value_type& val); static std::string toString(const value_type& val); static std::string init_val; }; struct ExistingFile { typedef std::string value_type; static std::string parse(const std::string& val); static std::string toString(const value_type& val); static std::string init_val; }; /// Interface for a parser for one commandline option class Option : public Managed { std::string m_name; mutable std::string m_fullUsage; protected: bool m_isset; Option(const std::string& name) : m_name(name), m_isset(false), hidden(false) {} Option(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) : m_name(name), m_isset(false), usage(usage), description(description), hidden(false) { if (shortName != 0) shortNames.push_back(shortName); if (!longName.empty()) longNames.push_back(longName); } /** * Parse the next commandline parameter after the short form of the command * has been found. It may or may not remove the parameter from the list, * depending on if the option wants a value or not. * * Signal that the option has been found, with the given argument (or 0 if * no argument). * * @returns * true if it used the argument, else false */ virtual ArgList::iterator parse(ArgList& list, ArgList::iterator begin) = 0; /** * Parse the commandline parameter of a long commandline switch * * @returns true if the parameter has been used */ virtual bool parse(const std::string& param) = 0; /** * Notify that the option is present in the command line, but has no * arguments */ virtual void parse_noarg() = 0; /// Return true if the argument to this function can be omitted virtual bool arg_is_optional() const { return false; } public: Option(); virtual ~Option() {} bool isSet() const { return m_isset; } const std::string& name() const { return m_name; } void addAlias(char c) { shortNames.push_back(c); } void addAlias(const std::string& str) { longNames.push_back(str); } /// Return a full usage message including all the aliases for this option const std::string& fullUsage() const; std::string fullUsageForMan() const; std::vector shortNames; std::vector longNames; std::string usage; std::string description; // Set to true if the option should not be documented bool hidden; friend class OptionGroup; friend class Engine; }; /// Boolean option class BoolOption : public Option { bool m_value; protected: BoolOption(const std::string& name) : Option(name), m_value(false) {} BoolOption(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) : Option(name, shortName, longName, usage, description), m_value(false) {} virtual ArgList::iterator parse(ArgList&, ArgList::iterator begin) { parse_noarg(); return begin; } virtual bool parse(const std::string&) { parse_noarg(); return false; } virtual void parse_noarg() { m_isset = true; m_value = true; } public: bool boolValue() const { return m_value; } std::string stringValue() const { return m_value ? "true" : "false"; } friend class OptionGroup; friend class Engine; }; template class SingleOption : public Option { protected: typename T::value_type m_value; SingleOption(const std::string& name) : Option(name), m_value(T::init_val) { usage = ""; } SingleOption(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) : Option(name, shortName, longName, usage, description) { if (usage.empty()) this->usage = ""; } ArgList::iterator parse(ArgList& list, ArgList::iterator begin) { if (begin == list.end()) throw exception::BadOption("option requires an argument"); m_value = T::parse(*begin); m_isset = true; // Remove the parsed element return list.eraseAndAdvance(begin); } bool parse(const std::string& param) { m_value = T::parse(param); m_isset = true; return true; } void parse_noarg() { throw exception::BadOption("option requires an argument"); } public: void setValue( const typename T::value_type &a ) { m_value = a; } typename T::value_type value() const { return m_value; } // Deprecated bool boolValue() const { return T::toBool(m_value); } int intValue() const { return T::toInt(m_value); } std::string stringValue() const { return T::toString(m_value); } friend class OptionGroup; friend class Engine; }; /** * Single option whose value can be or not be specified. * * It works for long option style only: short options with an optional argument * would be ambiguous. */ template class SingleOptvalOption : public Option { protected: typename T::value_type m_value; bool m_hasval; SingleOptvalOption(const std::string& name) : Option(name) { usage = ""; } SingleOptvalOption(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) : Option(name, 0, longName, usage, description) { if (shortName != 0) throw wibble::exception::Consistency( "creating option " + name + " with optional value" "short options with optional values are not allowed"); if (usage.empty()) this->usage = ""; } ArgList::iterator parse(ArgList& list, ArgList::iterator begin) { throw wibble::exception::Consistency( "parsing option with optional value" "short options with optional values are not allowed"); } bool parse(const std::string& param) { m_value = T::parse(param); m_isset = true; m_hasval = true; return true; } void parse_noarg() { m_isset = true; m_hasval = false; } virtual bool arg_is_optional() const { return true; } public: bool hasValue() const { return m_hasval; } void setValue( const typename T::value_type &a ) { m_value = a; } typename T::value_type value() const { return m_value; } friend class OptionGroup; friend class Engine; }; // Option needing a compulsory string value typedef SingleOption StringOption; // Option with an optional string value typedef SingleOptvalOption OptvalStringOption; // Option needing a compulsory int value typedef SingleOption IntOption; // Option with an optional int value typedef SingleOptvalOption OptvalIntOption; /// Commandline option with a mandatory argument naming a file which must exist. typedef SingleOption ExistingFileOption; /// Commandline option with an optional argument naming a file which must exist. typedef SingleOptvalOption OptvalExistingFileOption; // Option that can be specified multiple times template class VectorOption : public Option { std::vector< typename T::value_type > m_values; protected: VectorOption(const std::string& name) : Option(name) { usage = ""; } VectorOption(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) : Option(name, shortName, longName, usage, description) { if (usage.empty()) this->usage = ""; } ArgList::iterator parse(ArgList& list, ArgList::iterator begin) { if (begin == list.end()) throw exception::BadOption("no string argument found"); m_isset = true; m_values.push_back(T::parse(*begin)); // Remove the parsed element return list.eraseAndAdvance(begin); } bool parse(const std::string& param) { m_isset = true; m_values.push_back(T::parse(param)); return true; } void parse_noarg() { throw exception::BadOption("option requires an argument"); } public: bool boolValue() const { return !m_values.empty(); } const std::vector< typename T::value_type >& values() const { return m_values; } friend class OptionGroup; friend class Engine; }; /** * Group related commandline options */ class OptionGroup : public Managed { MemoryManager* m_manager; protected: OptionGroup(MemoryManager* mman = 0, const std::string& description = std::string()) : m_manager(mman), description(description), hidden(false) {} public: Option* add(Option* o) { options.push_back(o); return o; } std::vector options; std::string description; // Set to true if the option group should not be documented bool hidden; /** * Create a new option */ template T* create(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) { T* item = new T(name, shortName, longName, usage, description); if (m_manager) m_manager->add(item); return item; } /** * Create a new option and add it to this group */ template T* add(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) { T* res = create(name, shortName, longName, usage, description); add(res); return res; } friend class Engine; }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/commandline/parser.h0000644000175000017500000000630411075402123020023 0ustar enricoenrico#ifndef WIBBLE_COMMANDLINE_PARSER_H #define WIBBLE_COMMANDLINE_PARSER_H #include #include namespace wibble { namespace commandline { /** * Generic parser for commandline arguments. */ class Parser : public Engine { protected: ArgList m_args; MemoryManager m_manager; public: Parser(const std::string& name, const std::string& usage = std::string(), const std::string& description = std::string(), const std::string& longDescription = std::string()) : Engine(&m_manager, name, usage, description, longDescription) {} /** * Parse the commandline * * @returns true if it also took care of performing the action requested by * the user, or false if the caller should do it instead. */ bool parse(int argc, const char* argv[]) { m_args.clear(); for (int i = 1; i < argc; i++) m_args.push_back(argv[i]); parseList(m_args); return false; } bool hasNext() const { return !m_args.empty(); } std::string next() { if (m_args.empty()) return std::string(); std::string res(*m_args.begin()); m_args.erase(m_args.begin()); return res; } }; /** * Parser for commandline arguments, with builting help functions. */ class StandardParser : public Parser { protected: std::string m_version; public: StandardParser(const std::string& appname, const std::string& version) : Parser(appname), m_version(version) { helpGroup = addGroup("Help options"); help = helpGroup->add("help", 'h', "help", "", "print commandline help and exit"); help->addAlias('?'); this->version = helpGroup->add("version", 0, "version", "", "print the program version and exit"); } void outputHelp(std::ostream& out); bool parse(int argc, const char* argv[]); OptionGroup* helpGroup; BoolOption* help; BoolOption* version; }; /** * Parser for commandline arguments, with builting help functions and manpage * generation. */ class StandardParserWithManpage : public StandardParser { protected: int m_section; std::string m_author; public: StandardParserWithManpage( const std::string& appname, const std::string& version, int section, const std::string& author) : StandardParser(appname, version), m_section(section), m_author(author) { manpage = helpGroup->add("manpage", 0, "manpage", "[hooks]", "output the " + name() + " manpage and exit"); } bool parse(int argc, const char* argv[]); StringOption* manpage; }; /** * Parser for commandline arguments, with builting help functions and manpage * generation, and requiring a mandatory command. */ class StandardParserWithMandatoryCommand : public StandardParserWithManpage { public: StandardParserWithMandatoryCommand( const std::string& appname, const std::string& version, int section, const std::string& author) : StandardParserWithManpage(appname, version, section, author) { helpCommand = addEngine("help", "[command]", "print help information", "With no arguments, print a summary of available commands. " "If given a command name as argument, print detailed informations " "about that command."); } bool parse(int argc, const char* argv[]); Engine* helpCommand; }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/commandline/core.test.h0000644000175000017500000000262211075402115020435 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include using namespace wibble::commandline; struct TestCommandlineCore { Test isSwitch() { assert_eq(ArgList::isSwitch("-a"), true); assert_eq(ArgList::isSwitch("-afdg"), true); assert_eq(ArgList::isSwitch("--antani"), true); assert_eq(ArgList::isSwitch("--antani-blinda"), true); assert_eq(ArgList::isSwitch("-"), false); assert_eq(ArgList::isSwitch("--"), false); assert_eq(ArgList::isSwitch("antani"), false); assert_eq(ArgList::isSwitch("a-ntani"), false); assert_eq(ArgList::isSwitch("a--ntani"), false); } Test eraseAndAdvance() { ArgList list; list.push_back("1"); list.push_back("2"); list.push_back("3"); ArgList::iterator begin = list.begin(); assert_eq(list.size(), 3u); list.eraseAndAdvance(begin); assert(begin == list.begin()); assert_eq(list.size(), 2u); list.eraseAndAdvance(begin); assert(begin == list.begin()); assert_eq(list.size(), 1u); list.eraseAndAdvance(begin); assert(begin == list.begin()); assert_eq(list.size(), 0u); assert(begin == list.end()); } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/doc.test.h0000644000175000017500000000221211075402114020244 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include #include using namespace wibble::commandline; struct TestCommandlineDoc { Test basic() { StandardParserWithMandatoryCommand p("test", "1.0", 1, "enrico@enricozini.org"); //Parser p("test"); //p.add("antani", 'a', "antani", "blinda", "supercazzola"); //p.add("antani", 'a', "antani", "", "supercazzola"); //OptionGroup* g = p.addGroup("Test options"); //g->add("antani", 'a', "antani", "", "supercazzola"); Engine* e = p.addEngine("testEngine"); OptionGroup* g = e->addGroup("Test options"); g->add("antani", 'a', "antani", "", "supercazzola"); Help h("testapp", "1.0"); std::stringstream str; //h.outputHelp(str, p); //const char* opts[] = {"test", "help", "testEngine", NULL}; // XXX p.parse(3, opts); //std::cerr << str.str() << std::endl; } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/doc.h0000644000175000017500000000356711075402137017311 0ustar enricoenrico#ifndef WIBBLE_COMMANDLINE_DOC_H #define WIBBLE_COMMANDLINE_DOC_H #include #include #include #include namespace wibble { namespace commandline { class HelpWriter; class DocMaker { protected: std::string m_app; std::string m_ver; public: DocMaker(const std::string& app, const std::string& ver) : m_app(app), m_ver(ver) {} }; class Help : public DocMaker { protected: void outputOptions(std::ostream& out, HelpWriter& writer, const Engine& cp); public: Help(const std::string& app, const std::string& ver) : DocMaker(app, ver) {} void outputVersion(std::ostream& out); void outputHelp(std::ostream& out, const Engine& cp); }; class Manpage : public DocMaker { public: enum where { BEFORE, BEGINNING, END }; private: struct Hook { std::string section; where placement; std::string text; Hook(const std::string& section, where placement, const std::string& text) : section(section), placement(placement), text(text) {} }; int m_section; std::string m_author; std::vector hooks; std::string lastSection; void outputParagraph(std::ostream& out, const std::string& str); void outputOption(std::ostream& out, const Option* o); void outputOptions(std::ostream& out, const Engine& p); void runHooks(std::ostream& out, const std::string& section, where where); void startSection(std::ostream& out, const std::string& name); void endSection(std::ostream& out); public: Manpage(const std::string& app, const std::string& ver, int section, const std::string& author) : DocMaker(app, ver), m_section(section), m_author(author) {} void addHook(const std::string& section, where placement, const std::string& text) { hooks.push_back(Hook(section, placement, text)); } void readHooks(const std::string& file); void output(std::ostream& out, const Engine& cp); }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/commandline/engine.test.h0000644000175000017500000002071212231005104020742 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include using namespace wibble::commandline; using namespace std; // Happy trick to get access to protected methods we need to use for the tests template class Public : public T { public: Public(MemoryManager* mman = 0, const std::string& name = std::string(), const std::string& usage = std::string(), const std::string& description = std::string(), const std::string& longDescription = std::string()) : T(mman, name, usage, description, longDescription) {} ArgList::iterator parseList(ArgList& list) { return T::parseList(list); } ArgList::iterator parse(ArgList& list, ArgList::iterator begin) { return T::parse(list, begin); } }; class Engine1 : public Public { MemoryManager mman; public: Engine1() : Public(&mman) { antani = add("antani", 'a', "antani"); blinda = add("blinda", 'b', "blinda"); antani->addAlias("an-tani"); } BoolOption* antani; StringOption* blinda; }; class Engine2 : public Public { MemoryManager mman; public: Engine2() : Public(&mman) { help = add("help", 'h', "help", "get help"); scramble = addEngine("scramble"); scramble_random = scramble->add("random", 'r', "random"); scramble_yell = scramble->add("yell", 0, "yell"); scramble->aliases.push_back("mess"); fix = addEngine("fix"); fix_quick = fix->add("quick", 'Q', "quick"); fix_yell = fix->add("yell", 0, "yell"); } BoolOption* help; Engine* scramble; BoolOption* scramble_random; StringOption* scramble_yell; Engine* fix; BoolOption* fix_quick; StringOption* fix_yell; }; struct TestCommandlineEngine { Test optsAndArgs() { ArgList opts; opts.push_back("ciaps"); opts.push_back("-b"); opts.push_back("cippo"); opts.push_back("foobar"); Engine1 engine; ArgList::iterator i = engine.parseList(opts); assert(i == opts.begin()); assert_eq(opts.size(), 2u); assert_eq(string(*opts.begin()), string("ciaps")); assert_eq(string(*opts.rbegin()), string("foobar")); assert_eq(engine.antani->boolValue(), false); assert_eq(engine.blinda->stringValue(), "cippo"); } Test noSwitchesAfterFirstArg() { ArgList opts; opts.push_back("-b"); opts.push_back("cippo"); opts.push_back("foobar"); opts.push_back("--cabal"); Engine1 engine; engine.no_switches_after_first_arg = true; ArgList::iterator i = engine.parseList(opts); assert(i == opts.begin()); assert_eq(opts.size(), 2u); assert_eq(string(*opts.begin()), string("foobar")); assert_eq(string(*opts.rbegin()), string("--cabal")); assert_eq(engine.antani->boolValue(), false); assert_eq(engine.blinda->stringValue(), "cippo"); } Test optsOnly() { ArgList opts; opts.push_back("-a"); opts.push_back("foobar"); Engine1 engine; ArgList::iterator i = engine.parseList(opts); assert(i == opts.begin()); assert_eq(opts.size(), 1u); assert_eq(string(*opts.begin()), string("foobar")); assert_eq(engine.antani->boolValue(), true); assert_eq(engine.blinda->boolValue(), false); } Test clusteredShortOpts() { ArgList opts; opts.push_back("-ab"); opts.push_back("cippo"); Engine1 engine; ArgList::iterator i = engine.parseList(opts); assert(i == opts.end()); assert_eq(opts.size(), 0u); assert_eq(engine.antani->boolValue(), true); assert_eq(engine.blinda->stringValue(), "cippo"); } Test longOptsWithDashes() { ArgList opts; opts.push_back("--an-tani"); opts.push_back("foobar"); Engine1 engine; ArgList::iterator i = engine.parseList(opts); assert(i == opts.begin()); assert_eq(opts.size(), 1u); assert_eq(string(*opts.begin()), string("foobar")); assert_eq(engine.antani->boolValue(), true); assert_eq(engine.blinda->boolValue(), false); } // Test long options with arguments Test longOptsWithArgs() { ArgList opts; opts.push_back("--blinda=cippo"); opts.push_back("foobar"); opts.push_back("--antani"); Engine1 engine; ArgList::iterator i = engine.parseList(opts); assert(i == opts.begin()); assert_eq(opts.size(), 1u); assert_eq(string(*opts.begin()), string("foobar")); assert_eq(engine.antani->boolValue(), true); assert_eq(engine.blinda->stringValue(), "cippo"); } Test commandWithArg() { ArgList opts; opts.push_back("--yell=foo"); opts.push_back("mess"); opts.push_back("-r"); Engine2 engine; ArgList::iterator i = engine.parseList(opts); assert(i == opts.end()); assert_eq(opts.size(), 0u); assert_eq(engine.foundCommand(), engine.scramble); assert_eq(engine.scramble_yell->stringValue(), "foo"); assert_eq(engine.scramble_random->boolValue(), true); assert_eq(engine.fix_yell->stringValue(), string()); assert_eq(engine.fix_quick->boolValue(), false); assert_eq(engine.help->boolValue(), false); } // Test the other command, with overlapping arguments Test commandsWithOverlappingArgs() { ArgList opts; opts.push_back("--yell=foo"); opts.push_back("fix"); opts.push_back("--help"); opts.push_back("-Q"); Engine2 engine; ArgList::iterator i = engine.parseList(opts); assert(i == opts.end()); assert_eq(opts.size(), 0u); assert_eq(engine.foundCommand(), engine.fix); assert_eq(engine.scramble_yell->stringValue(), string()); assert_eq(engine.scramble_random->boolValue(), false); assert_eq(engine.fix_yell->stringValue(), "foo"); assert_eq(engine.fix_quick->boolValue(), true); assert_eq(engine.help->boolValue(), true); } // Test invocation without command Test commandsWithoutCommand() { ArgList opts; opts.push_back("--help"); Engine2 engine; ArgList::iterator i = engine.parseList(opts); assert(i == opts.end()); assert_eq(opts.size(), 0u); assert_eq(engine.foundCommand(), static_cast(0)); assert_eq(engine.scramble_yell->stringValue(), string()); assert_eq(engine.scramble_random->boolValue(), false); assert_eq(engine.fix_yell->stringValue(), string()); assert_eq(engine.fix_quick->boolValue(), false); assert_eq(engine.help->boolValue(), true); } // Test creation shortcuts Test creationShortcuts() { MemoryManager mman; Public engine(&mman, "test", "[options]", "test engine", "this is the long description of a test engine"); OptionGroup* group = engine.addGroup("test option group"); BoolOption* testBool = group->add("tbool", 0, "testbool", "", "a test bool switch"); IntOption* testInt = group->add("tint", 0, "testint", "", "a test int switch"); StringOption* testString = group->add("tstring", 0, "teststring", "", "a test string switch"); BoolOption* testBool1 = engine.add("tbool", 0, "testbool1", "", "a test bool switch"); IntOption* testInt1 = engine.add("tint", 0, "testint1", "", "a test int switch"); StringOption* testString1 = engine.add("tstring", 0, "teststring1", "", "a test string switch"); ArgList opts; opts.push_back("--testbool=true"); opts.push_back("--testint=3"); opts.push_back("--teststring=antani"); opts.push_back("--testbool1=true"); opts.push_back("--testint1=5"); opts.push_back("--teststring1=blinda"); ArgList::iterator i = engine.parseList(opts); assert(i == opts.end()); assert_eq(opts.size(), 0u); assert_eq(testBool->boolValue(), true); assert_eq(testInt->intValue(), 3); assert_eq(testString->stringValue(), "antani"); assert_eq(testBool1->boolValue(), true); assert_eq(testInt1->intValue(), 5); assert_eq(testString1->stringValue(), "blinda"); } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/options.test.h0000644000175000017500000000750511075402117021207 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include using namespace std; using namespace wibble::commandline; struct TestCommandlineOptions { // Happy trick to get access to protected methods we need to use for the tests template class Public : public T { public: Public(const std::string& name) : T(name) {} Public(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) : T(name, shortName, longName, usage, description) {} virtual ArgList::iterator parse(ArgList& a, ArgList::iterator begin) { return T::parse(a, begin); } virtual bool parse(const std::string& str) { return T::parse(str); } }; Test boolOpt() { Public opt("test"); assert_eq(opt.name(), string("test")); assert_eq(opt.isSet(), false); assert_eq(opt.boolValue(), false); assert_eq(opt.stringValue(), string("false")); assert_eq(opt.parse(string()), false); assert_eq(opt.isSet(), true); assert_eq(opt.boolValue(), true); assert_eq(opt.stringValue(), string("true")); } Test intOpt() { Public opt("test"); assert_eq(opt.name(), string("test")); assert_eq(opt.isSet(), false); assert_eq(opt.boolValue(), false); assert_eq(opt.intValue(), 0); assert_eq(opt.stringValue(), string("0")); assert_eq(opt.parse("42"), true); assert_eq(opt.isSet(), true); assert_eq(opt.boolValue(), true); assert_eq(opt.intValue(), 42); assert_eq(opt.stringValue(), string("42")); } Test stringOpt() { Public opt("test"); assert_eq(opt.name(), string("test")); assert_eq(opt.isSet(), false); assert_eq(opt.boolValue(), false); assert_eq(opt.stringValue(), string()); assert_eq(opt.parse("-a"), true); assert_eq(opt.isSet(), true); assert_eq(opt.boolValue(), true); assert_eq(opt.stringValue(), "-a"); } Test vectorBoolOpt() { Public< VectorOption > opt("test"); assert_eq(opt.name(), string("test")); assert_eq(opt.isSet(), false); assert_eq(opt.boolValue(), false); assert_eq(opt.values().size(), 0u); assert_eq(opt.parse("yes"), true); assert_eq(opt.isSet(), true); assert_eq(opt.boolValue(), true); assert_eq(opt.values().size(), 1u); assert_eq(opt.values()[0], true); assert_eq(opt.parse("no"), true); assert_eq(opt.isSet(), true); assert_eq(opt.boolValue(), true); assert_eq(opt.values().size(), 2u); assert_eq(opt.values()[0], true); assert_eq(opt.values()[1], false); } Test vectorStringOpt() { Public< VectorOption > opt("test"); assert_eq(opt.name(), string("test")); assert_eq(opt.isSet(), false); assert_eq(opt.boolValue(), false); assert_eq(opt.values().size(), 0u); assert_eq(opt.parse("-a"), true); assert_eq(opt.isSet(), true); assert_eq(opt.boolValue(), true); assert_eq(opt.values().size(), 1u); assert_eq(opt.values()[0], "-a"); assert_eq(opt.parse("foo"), true); assert_eq(opt.isSet(), true); assert_eq(opt.boolValue(), true); assert_eq(opt.values().size(), 2u); assert_eq(opt.values()[0], "-a"); assert_eq(opt.values()[1], "foo"); } }; // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/core.cpp0000644000175000017500000000200711075402133020007 0ustar enricoenrico#include #include #include namespace wibble { namespace commandline { bool ArgList::isSwitch(const const_iterator& iter) { return ArgList::isSwitch(*iter); } bool ArgList::isSwitch(const iterator& iter) { return ArgList::isSwitch(*iter); } bool ArgList::isSwitch(const std::string& str) { // No empty strings if (str[0] == 0) return false; // Must start with a dash if (str[0] != '-') return false; // Must not be "-" (usually it means 'stdin' file argument) if (str[1] == 0) return false; // Must not be "--" (end of switches) if (str == "--") return false; return true; } bool ArgList::isSwitch(const char* str) { // No empty strings if (str[0] == 0) return false; // Must start with a dash if (str[0] != '-') return false; // Must not be "-" (usually it means 'stdin' file argument) if (str[1] == 0) return false; // Must not be "--" (end of switches) if (strcmp(str, "--") == 0) return false; return true; } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/options.cpp0000644000175000017500000000746512231005104020557 0ustar enricoenrico#include #include #include #include #include #include #include #include #include #include using namespace std; namespace wibble { namespace commandline { bool Bool::parse(const std::string& val) { if (val == "true" || val == "t" || val == "1" || val == "yes" || val == "y") return true; if (val == "false" || val == "f" || val == "0" || val == "no" || val == "n") return false; throw exception::BadOption("invalid true/false value: \"" + val + "\""); } bool Bool::toBool(const bool& val) { return val; } int Bool::toInt(const value_type& val) { return val ? 1 : 0; } std::string Bool::toString(const value_type& val) { return val ? "true" : "false"; } bool Bool::init_val = false; int Int::parse(const std::string& val) { // Ensure that we're all numeric for (string::const_iterator s = val.begin(); s != val.end(); ++s) if (!isdigit(*s)) throw exception::BadOption("value " + val + " must be numeric"); return strtoul(val.c_str(), NULL, 10); } bool Int::toBool(const int& val) { return static_cast(val); } int Int::toInt(const int& val) { return val; } std::string Int::toString(const int& val) { return str::fmt(val); } int Int::init_val = 0; std::string String::parse(const std::string& val) { return val; } bool String::toBool(const std::string& val) { return !val.empty(); } int String::toInt(const std::string& val) { return strtoul(val.c_str(), NULL, 10); } std::string String::toString(const std::string& val) { return val; } std::string String::init_val = std::string(); #ifdef POSIX std::string ExistingFile::parse(const std::string& val) { if (access(val.c_str(), F_OK) == -1) throw exception::BadOption("file " + val + " must exist"); return val; } #endif std::string ExistingFile::toString(const std::string& val) { return val; } std::string ExistingFile::init_val = std::string(); static string fmtshort(char c, const std::string& usage) { if (usage.empty()) return string("-") + c; else return string("-") + c + " " + usage; } static string fmtlong(const std::string& c, const std::string& usage, bool optional=false) { if (usage.empty()) return string("--") + c; else if (optional) return string("--") + c + "[=" + usage + "]"; else return string("--") + c + "=" + usage; } static string manfmtshort(char c, const std::string& usage) { if (usage.empty()) return string("\\-") + c; else return string("\\-") + c + " \\fI" + usage + "\\fP"; } static string manfmtlong(const std::string& c, const std::string& usage, bool optional=false) { if (usage.empty()) return string("\\-\\-") + c; else if (optional) return string("\\-\\-") + c + "[=\\fI" + usage + "\\fP]"; else return string("\\-\\-") + c + "=\\fI" + usage + "\\fP"; } Option::Option() : hidden(false) {} const std::string& Option::fullUsage() const { if (m_fullUsage.empty()) { for (vector::const_iterator i = shortNames.begin(); i != shortNames.end(); i++) { if (!m_fullUsage.empty()) m_fullUsage += ", "; m_fullUsage += fmtshort(*i, usage); } for (vector::const_iterator i = longNames.begin(); i != longNames.end(); i++) { if (!m_fullUsage.empty()) m_fullUsage += ", "; m_fullUsage += fmtlong(*i, usage, arg_is_optional()); } } return m_fullUsage; } std::string Option::fullUsageForMan() const { string res; for (vector::const_iterator i = shortNames.begin(); i != shortNames.end(); i++) { if (!res.empty()) res += ", "; res += manfmtshort(*i, usage); } for (vector::const_iterator i = longNames.begin(); i != longNames.end(); i++) { if (!res.empty()) res += ", "; res += manfmtlong(*i, usage, arg_is_optional()); } return res; } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/commandline/engine.h0000644000175000017500000001515311250221715017777 0ustar enricoenrico#ifndef WIBBLE_COMMANDLINE_ENGINE_H #define WIBBLE_COMMANDLINE_ENGINE_H #include #include #include #include #include namespace wibble { namespace commandline { #if 0 -- This help is left around to be reintegrated when I found something appropriate. It documents the general behavior of functions in the form ArgList::iterator parse(ArgList& list, ArgList::iterator begin); /** * Parse the list of arguments, starting at 'begin' and removing the * arguments it successfully parses. * * The 'begin' iterator can be invalidated by this function. * * @returns * An iterator to the first unparsed argument (can be list.end()) */ #endif /** * Parse commandline options. * * Normally it parses short or long switches all starting with '-' * * If other engines are added, then looks in the commandline for a non-switch * command to select the operation mode. This allow to have a custom set of * commandline options for every non-switch command. */ class Engine : public Managed { MemoryManager* m_manager; std::string m_name; protected: // Elements added to this engine std::vector m_groups; std::vector m_options; std::vector m_commands; // Parse tables for commandline options std::map m_short; std::map m_long; std::map m_aliases; // Command selected with the non-switch command, if any were found, else // NULL Engine* m_found_command; void addWithoutAna(Option* o); void addWithoutAna(const std::vector& o); void add(const std::string& alias, Engine* o); // Rebuild the parse tables void rebuild(); /** * Handle the commandline switch at 'begin'. * * If the switch at 'begin' cannot be handled, the list is untouched and * 'begin',false is returned. Else, the switch is removed and the new begin is * returned. */ std::pair parseFirstIfKnown(ArgList& list, ArgList::iterator begin); #if 0 /// Parse a consecutive sequence of switches ArgList::iterator parseConsecutiveSwitches(ArgList& list, ArgList::iterator begin); #endif /// Parse all known Options and leave the rest in list ArgList::iterator parseKnownSwitches(ArgList& list, ArgList::iterator begin); /** * Parse the list of arguments, starting at the beginning and removing the * arguments it successfully parses. * * @returns * An iterator to the first unparsed argument (can be list.end()) */ ArgList::iterator parseList(ArgList& list) { return parse(list, list.begin()); } /** * Parse all the switches in list, leaving only the non-switch arguments or * the arguments following "--" */ ArgList::iterator parse(ArgList& list, ArgList::iterator begin); Engine(MemoryManager* mman = 0, const std::string& name = std::string(), const std::string& usage = std::string(), const std::string& description = std::string(), const std::string& longDescription = std::string()) : m_manager(mman), m_name(name), m_found_command(0), primaryAlias(name), usage(usage), description(description), longDescription(longDescription), hidden(false), no_switches_after_first_arg(false) {} public: const std::string& name() const { return m_name; } /// Add an Option to this engine Option* add(Option* o); /// Add an OptionGroup to this engine OptionGroup* add(OptionGroup* group); /// Add a Engine to this engine Engine* add(Engine* o); /** * Create an option */ template T* create(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) { T* item = new T(name, shortName, longName, usage, description); if (m_manager) m_manager->add(item); return item; } /** * Create an option and add to this engine */ template T* add(const std::string& name, char shortName, const std::string& longName, const std::string& usage = std::string(), const std::string& description = std::string()) { T* res = create(name, shortName, longName, usage, description); add(res); return res; } /** * Create an OptionGroup */ OptionGroup* createGroup(const std::string& description) { OptionGroup* g = new OptionGroup(m_manager, description); if (m_manager) m_manager->add(g); return g; } /** * Create an OptionGroup and add it to this engine */ OptionGroup* addGroup(const std::string& description) { return add(createGroup(description)); } /** * Create a Engine */ Engine* createEngine(const std::string& name, const std::string& usage = std::string(), const std::string& description = std::string(), const std::string& longDescription = std::string()) { Engine* item = new Engine(m_manager, name, usage, description, longDescription); if (m_manager) m_manager->add(item); return item; } /** * Create a Engine and add it to this engine as a command */ Engine* addEngine(const std::string& name, const std::string& usage = std::string(), const std::string& description = std::string(), const std::string& longDescription = std::string()) { return add(createEngine(name, usage, description, longDescription)); } /// Get the OptionGroups that have been added to this engine const std::vector& groups() const { return m_groups; } /// Get the Options that have been added to this engine const std::vector& options() const { return m_options; } /// Get the Engines that have been added to this engine const std::vector& commands() const { return m_commands; } Engine* command(const std::string& name) const { std::map::const_iterator i = m_aliases.find(name); if (i == m_aliases.end()) return 0; else return i->second; } /// Returns true if this Engine has options to parse bool hasOptions() const { return !m_groups.empty() || !m_options.empty(); } /** * Return the command that has been found in the commandline, or NULL if * none have been found */ Engine* foundCommand() const { return m_found_command; } void dump(std::ostream& out, const std::string& prefix = std::string()); std::string primaryAlias; std::vector aliases; std::string usage; std::string description; std::string longDescription; std::string examples; // Set to true if the engine should not be documented bool hidden; // Set to true if no switches should be parsed after the first // non-switch argument, and they should be just left in the argument // list bool no_switches_after_first_arg; friend class Parser; }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/config.h.cmake-in0000644000175000017500000000017711625163732017206 0ustar enricoenrico#cmakedefine HAVE_STRUCT_DIRENT_D_TYPE #cmakedefine _FILE_OFFSET_BITS #cmakedefine _LARGEFILE_SOURCE #cmakedefine _LARGE_FILES libwibble-1.1/wibble/string.test.h0000644000175000017500000002472412231005104016524 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include #include namespace { using namespace std; using namespace wibble; struct TestString { Test fmt() { assert_eq(str::fmt(5), "5"); assert_eq(str::fmt(5.123), "5.123"); assert_eq(str::fmtf("ciao"), "ciao"); } Test fmtSet() { std::set< int > a; assert_eq(str::fmt(a), "{}"); a.insert( a.begin(), 2 ); assert_eq(str::fmt(a), "{ 2 }"); a.insert( a.begin(), 5 ); assert_eq(str::fmt(a), "{ 2, 5 }"); a.insert( a.begin(), 1 ); assert_eq(str::fmt(a), "{ 1, 2, 5 }"); } Test fmtVec() { std::vector< int > a; assert_eq(str::fmt(a), "[]"); a.push_back( 2 ); assert_eq(str::fmt(a), "[ 2 ]"); a.push_back( 5 ); assert_eq(str::fmt(a), "[ 2, 5 ]"); a.push_back( 1 ); assert_eq(str::fmt(a), "[ 2, 5, 1 ]"); } Test fmtList() { assert_eq( str::fmt( list::Empty< int >() ), "[]" ); assert_eq( str::fmt( list::singular( 0 ) ), "[ 0 ]" ); assert_eq( str::fmt( list::append( list::singular( 0 ), list::singular( 2 ) ) ), "[ 0, 2 ]" ); } Test basename() { assert_eq(str::basename("ciao"), "ciao"); assert_eq(str::basename("a/ciao"), "ciao"); assert_eq(str::basename("a/b/c/c/d/e/ciao"), "ciao"); assert_eq(str::basename("/ciao"), "ciao"); } Test dirname() { assert_eq(str::dirname("ciao"), ""); assert_eq(str::dirname("a/ciao"), "a"); assert_eq(str::dirname("a/b/c/c/d/e/ciao"), "a/b/c/c/d/e"); assert_eq(str::dirname("/a/ciao"), "/a"); assert_eq(str::dirname("/ciao"), "/"); } Test trim() { assert_eq(str::trim(" "), ""); assert_eq(str::trim(" c "), "c"); assert_eq(str::trim("ciao"), "ciao"); assert_eq(str::trim(" ciao"), "ciao"); assert_eq(str::trim(" ciao"), "ciao"); assert_eq(str::trim("ciao "), "ciao"); assert_eq(str::trim("ciao "), "ciao"); assert_eq(str::trim(" ciao "), "ciao"); assert_eq(str::trim(" ciao "), "ciao"); } Test trim2() { assert_eq(str::trim(string("ciao"), ::isalpha), ""); assert_eq(str::trim(" ", ::isalpha), " "); } Test tolower() { assert_eq(str::tolower("ciao"), "ciao"); assert_eq(str::tolower("CIAO"), "ciao"); assert_eq(str::tolower("Ciao"), "ciao"); assert_eq(str::tolower("cIAO"), "ciao"); } Test toupper() { assert_eq(str::toupper("ciao"), "CIAO"); assert_eq(str::toupper("CIAO"), "CIAO"); assert_eq(str::toupper("Ciao"), "CIAO"); assert_eq(str::toupper("cIAO"), "CIAO"); } Test ucfirst() { assert_eq(str::ucfirst("ciao"), "Ciao"); assert_eq(str::ucfirst("CIAO"), "Ciao"); assert_eq(str::ucfirst("Ciao"), "Ciao"); assert_eq(str::ucfirst("cIAO"), "Ciao"); } // Check startsWith Test startsWith() { assert(str::startsWith("ciao", "ci")); assert(str::startsWith("ciao", "")); assert(str::startsWith("ciao", "ciao")); assert(!str::startsWith("ciao", "ciaoa")); assert(!str::startsWith("ciao", "i")); } Test endsWith() { assert(str::endsWith("ciao", "ao")); assert(str::endsWith("ciao", "")); assert(str::endsWith("ciao", "ciao")); assert(!str::endsWith("ciao", "aciao")); assert(!str::endsWith("ciao", "a")); } Test joinpath() { assert_eq(str::joinpath("a", "b"), "a/b"); assert_eq(str::joinpath("a/", "b"), "a/b"); assert_eq(str::joinpath("a", "/b"), "a/b"); assert_eq(str::joinpath("a/", "/b"), "a/b"); } Test appendpath() { assert_eq(str::appendpath("a", "b"), "a/b"); assert_eq(str::appendpath("a/", "b"), "a/b"); assert_eq(str::appendpath("a", "/b"), "/b"); assert_eq(str::appendpath("a/", "/b"), "/b"); assert_eq(str::appendpath("/a", "b"), "/a/b"); assert_eq(str::appendpath("/a/", "b"), "/a/b"); assert_eq(str::appendpath("/a", "/b"), "/b"); assert_eq(str::appendpath("/a/", "/b"), "/b"); } Test urlencode() { assert_eq(str::urlencode(""), ""); assert_eq(str::urlencode("antani"), "antani"); assert_eq(str::urlencode("a b c"), "a%20b%20c"); assert_eq(str::urlencode("a "), "a%20"); assert_eq(str::urldecode(""), ""); assert_eq(str::urldecode("antani"), "antani"); assert_eq(str::urldecode("a%20b"), "a b"); assert_eq(str::urldecode("a%20"), "a "); assert_eq(str::urldecode("a%2"), "a"); assert_eq(str::urldecode("a%"), "a"); assert_eq(str::urldecode(str::urlencode("àá☣☢☠!@#$%^&*(\")/A")), "àá☣☢☠!@#$%^&*(\")/A"); assert_eq(str::urldecode(str::urlencode("http://zz:ss@a.b:31/c?d=e&f=g")), "http://zz:ss@a.b:31/c?d=e&f=g"); } Test split1() { string val = ""; str::Split split("/", val); str::Split::const_iterator i = split.begin(); assert(i == split.end()); } Test split2() { string val = "foo"; str::Split split("/", val); str::Split::const_iterator i = split.begin(); assert(i != split.end()); assert_eq(*i, "foo"); assert_eq(i.remainder(), ""); ++i; assert(i == split.end()); } Test split3() { string val = "foo"; str::Split split("", val); str::Split::const_iterator i = split.begin(); assert(i != split.end()); assert_eq(*i, "f"); assert_eq(i.remainder(), "oo"); ++i; assert_eq(*i, "o"); assert_eq(i.remainder(), "o"); ++i; assert_eq(*i, "o"); assert_eq(i.remainder(), ""); ++i; assert(i == split.end()); } Test split4() { string val = "/a//foo/"; str::Split split("/", val); str::Split::const_iterator i = split.begin(); assert(i != split.end()); assert_eq(*i, ""); assert_eq(i.remainder(), "a//foo/"); ++i; assert(i != split.end()); assert_eq(*i, "a"); assert_eq(i.remainder(), "/foo/"); ++i; assert(i != split.end()); assert_eq(*i, ""); assert_eq(i.remainder(), "foo/"); ++i; assert(i != split.end()); assert_eq(*i, "foo"); assert_eq(i.remainder(), ""); ++i; assert(i == split.end()); } Test join() { string val = "/a//foo/"; str::Split split("/", val); string res = str::join(split.begin(), split.end(), ":"); assert_eq(res, ":a::foo"); } Test normpath() { assert_eq(str::normpath(""), "."); assert_eq(str::normpath("/"), "/"); assert_eq(str::normpath("foo"), "foo"); assert_eq(str::normpath("foo/"), "foo"); assert_eq(str::normpath("/foo"), "/foo"); assert_eq(str::normpath("foo/bar"), "foo/bar"); assert_eq(str::normpath("foo/./bar"), "foo/bar"); assert_eq(str::normpath("././././foo/./././bar/././././"), "foo/bar"); assert_eq(str::normpath("/../../../../../foo"), "/foo"); assert_eq(str::normpath("foo/../foo/../foo/../foo/../"), "."); assert_eq(str::normpath("foo//bar"), "foo/bar"); assert_eq(str::normpath("foo/./bar"), "foo/bar"); assert_eq(str::normpath("foo/foo/../bar"), "foo/bar"); } Test base64() { using namespace str; assert_eq(encodeBase64(""), ""); assert_eq(encodeBase64("c"), "Yw=="); assert_eq(encodeBase64("ci"), "Y2k="); assert_eq(encodeBase64("cia"), "Y2lh"); assert_eq(encodeBase64("ciao"), "Y2lhbw=="); assert_eq(encodeBase64("ciao "), "Y2lhbyA="); assert_eq(encodeBase64("ciao c"), "Y2lhbyBj"); assert_eq(encodeBase64("ciao ci"), "Y2lhbyBjaQ=="); assert_eq(encodeBase64("ciao cia"), "Y2lhbyBjaWE="); assert_eq(encodeBase64("ciao ciao"), "Y2lhbyBjaWFv"); assert_eq(decodeBase64(encodeBase64("")), ""); assert_eq(decodeBase64(encodeBase64("c")), "c"); assert_eq(decodeBase64(encodeBase64("ci")), "ci"); assert_eq(decodeBase64(encodeBase64("cia")), "cia"); assert_eq(decodeBase64(encodeBase64("ciao")), "ciao"); assert_eq(decodeBase64(encodeBase64("ciao ")), "ciao "); assert_eq(decodeBase64(encodeBase64("ciao c")), "ciao c"); assert_eq(decodeBase64(encodeBase64("ciao ci")), "ciao ci"); assert_eq(decodeBase64(encodeBase64("ciao cia")), "ciao cia"); assert_eq(decodeBase64(encodeBase64("ciao ciao")), "ciao ciao"); } Test yaml() { string data = "Name: value\n" "Multiline: value1\n" " value2\n" " value3\n" "Multifield:\n" " Field1: val1\n" " Field2: val2\n" " continue val2\n" "\n" "Name: second record\n"; stringstream input(data, ios_base::in); str::YamlStream yamlStream; str::YamlStream::const_iterator i = yamlStream.begin(input); assert(i != yamlStream.end()); assert_eq(i->first, "Name"); assert_eq(i->second, "value"); ++i; assert(i != yamlStream.end()); assert_eq(i->first, "Multiline"); assert_eq(i->second, "value1\n" "value2\n" " value3\n"); ++i; assert(i != yamlStream.end()); assert_eq(i->first, "Multifield"); assert_eq(i->second, "Field1: val1\n" "Field2: val2\n" " continue val2\n"); ++i; assert(i == yamlStream.end()); i = yamlStream.begin(input); assert(i != yamlStream.end()); assert_eq(i->first, "Name"); assert_eq(i->second, "second record"); ++i; assert(i == yamlStream.end()); i = yamlStream.begin(input); assert(i == yamlStream.end()); } Test yamlComments() { string data = "# comment\n" "Name: value # comment\n" "# comment\n" "Multiline: value1 # comment \n" " value2 # a\n" " value3#b\n" "\n" "# comment\n" "\n" "Name: second record\n"; stringstream input(data, ios_base::in); str::YamlStream yamlStream; str::YamlStream::const_iterator i = yamlStream.begin(input); assert(i != yamlStream.end()); assert_eq(i->first, "Name"); assert_eq(i->second, "value"); ++i; assert(i != yamlStream.end()); assert_eq(i->first, "Multiline"); assert_eq(i->second, "value1\n" "value2 # a\n" " value3#b\n"); ++i; assert(i == yamlStream.end()); i = yamlStream.begin(input); assert(i != yamlStream.end()); assert_eq(i->first, "Name"); assert_eq(i->second, "second record"); ++i; assert(i == yamlStream.end()); i = yamlStream.begin(input); assert(i == yamlStream.end()); } Test c_escape_unescape() { size_t len; assert_eq(str::c_unescape("cia\\x00o", len), string("cia\0o", 5)); assert_eq(len, 8); assert_eq(str::c_escape(string("cia\0o", 5)), "cia\\x00o"); } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/config.h.in0000644000175000017500000000117411625163465016131 0ustar enricoenrico/* Define if there is a member named d_type in the struct describing directory headers. */ #undef HAVE_STRUCT_DIRENT_D_TYPE /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ #undef _LARGEFILE_SOURCE /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* autoconf macros that you can use with this config.h.in: * * - HAVE_STRUCT_DIRENT_D_TYPE: gl_CHECK_TYPE_STRUCT_DIRENT_D_TYPE from gnulib * - _FILE_OFFSET_BITS, _LARGEFILE_SOURCE, _LARGE_FILES: AC_SYS_LARGEFILE, AC_FUNC_FSEEKO * */ libwibble-1.1/wibble/test.h0000644000175000017500000001041112231005104015203 0ustar enricoenrico// -*- C++ -*- #include #include #include #ifndef WIBBLE_TEST_H #define WIBBLE_TEST_H namespace wibble { // TODO use TLS extern int assertFailure; struct Location { const char *file; int line, iteration; std::string stmt; Location( const char *f, int l, std::string st, int iter = -1 ) : file( f ), line( l ), iteration( iter ), stmt( st ) {} }; #define LOCATION(stmt) ::wibble::Location( __FILE__, __LINE__, stmt ) #undef assert // silence a gcc warning if we had assert.h included #ifndef NDEBUG #define LOCATION_I(stmt, i) ::wibble::Location( __FILE__, __LINE__, stmt, i ) #define assert(x) assert_fn( LOCATION( #x ), x ) #define assert_pred(p, x) assert_pred_fn( \ LOCATION( #p "( " #x " )" ), x, p( x ) ) #define assert_eq(x, y) assert_eq_fn( LOCATION( #x " == " #y ), x, y ) #define assert_leq(x, y) assert_leq_fn( LOCATION( #x " <= " #y ), x, y ) #define assert_eq_l(i, x, y) assert_eq_fn( LOCATION_I( #x " == " #y, i ), x, y ) #define assert_neq(x, y) assert_neq_fn( LOCATION( #x " != " #y ), x, y ) #define assert_list_eq(x, y) \ assert_list_eq_fn( LOCATION( #x " == " #y ), \ sizeof( y ) / sizeof( y[0] ), x, y ) #else #define assert(x) ((void)0) #define assert_pred(p, x) ((void)0) #define assert_eq(x, y) ((void)0) #define assert_leq(x, y) ((void)0) #define assert_eq_l(i, x, y) ((void)0) #define assert_neq(x, y) ((void)0) #define assert_list_eq(x, y) ((void)0) #endif #define assert_unreachable(...) assert_die_fn( LOCATION( wibble::str::fmtf(__VA_ARGS__) ) ) #define assert_unimplemented() assert_die_fn( LOCATION( "not imlemented" ) ) #define assert_die() assert_die_fn( LOCATION( "forbidden code path tripped" ) ) struct AssertFailed { std::ostream &stream; std::ostringstream str; bool expect; AssertFailed( Location l, std::ostream &s = std::cerr ) : stream( s ) { expect = assertFailure > 0; str << l.file << ": " << l.line; if ( l.iteration != -1 ) str << " (iteration " << l.iteration << ")"; str << ": assertion `" << l.stmt << "' failed;"; } ~AssertFailed() { if ( expect ) ++assertFailure; else { stream << str.str() << std::endl; abort(); } } }; template< typename X > inline AssertFailed &operator<<( AssertFailed &f, X x ) { f.str << x; return f; } template< typename X > void assert_fn( Location l, X x ) { if ( !x ) { AssertFailed f( l ); } } void assert_die_fn( Location l ) __attribute__((noreturn)); template< typename X, typename Y > void assert_eq_fn( Location l, X x, Y y ) { if ( !( x == y ) ) { AssertFailed f( l ); f << " got [" << x << "] != [" << y << "] instead"; } } template< typename X, typename Y > void assert_leq_fn( Location l, X x, Y y ) { if ( !( x <= y ) ) { AssertFailed f( l ); f << " got [" << x << "] > [" << y << "] instead"; } } template< typename X > void assert_pred_fn( Location l, X x, bool p ) { if ( !p ) { AssertFailed f( l ); f << " for " << x; } } template< typename X > void assert_list_eq_fn( Location loc, int c, X l, const typename X::Type check[] ) { int i = 0; while ( !l.empty() ) { if ( l.head() != check[ i ] ) { AssertFailed f( loc ); f << " list disagrees at position " << i << ": [" << wibble::str::fmt( l.head() ) << "] != [" << wibble::str::fmt( check[ i ] ) << "]"; } l = l.tail(); ++ i; } if ( i != c ) { AssertFailed f( loc ); f << " got [" << i << "] != [" << c << "] instead"; } } template< typename X, typename Y > void assert_neq_fn( Location l, X x, Y y ) { if ( x != y ) return; AssertFailed f( l ); f << " got [" << x << "] == [" << y << "] instead"; } inline void beginAssertFailure() { assertFailure = 1; } inline void endAssertFailure() { #ifndef NDEBUG const int f = assertFailure; assertFailure = 0; #endif assert( f > 1 ); } struct ExpectFailure { ExpectFailure() { beginAssertFailure(); } ~ExpectFailure() { endAssertFailure(); } }; } typedef void Test; #endif libwibble-1.1/wibble/string.cpp0000644000175000017500000002606712231005104016103 0ustar enricoenrico/* * OO wrapper for regular expression functions * * Copyright (C) 2003--2008 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include using namespace std; namespace wibble { namespace str { #if _WIN32 || __xlC__ int vasprintf (char **result, const char *format, va_list args) { const char *p = format; /* Add one to make sure that it is never zero, which might cause malloc to return NULL. */ int total_width = strlen (format) + 1; va_list ap; memcpy ((void *)&ap, (void *)&args, sizeof (va_list)); while (*p != '\0') { if (*p++ == '%') { while (strchr ("-+ #0", *p)) ++p; if (*p == '*') { ++p; total_width += abs (va_arg (ap, int)); } else total_width += strtoul (p, (char **) &p, 10); if (*p == '.') { ++p; if (*p == '*') { ++p; total_width += abs (va_arg (ap, int)); } else total_width += strtoul (p, (char **) &p, 10); } while (strchr ("hlL", *p)) ++p; /* Should be big enough for any format specifier except %s and floats. */ total_width += 30; switch (*p) { case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': case 'c': (void) va_arg (ap, int); break; case 'f': case 'e': case 'E': case 'g': case 'G': (void) va_arg (ap, double); /* Since an ieee double can have an exponent of 307, we'll make the buffer wide enough to cover the gross case. */ total_width += 307; break; case 's': total_width += strlen (va_arg (ap, char *)); break; case 'p': case 'n': (void) va_arg (ap, char *); break; } p++; } } *result = (char*)malloc (total_width); if (*result != NULL) { return vsprintf (*result, format, args);} else { return 0; } } #endif std::string fmtf( const char* f, ... ) { char *c; va_list ap; va_start( ap, f ); vasprintf( &c, f, ap ); std::string ret( c ); free( c ); return ret; } std::string fmt( const char* f, ... ) { char *c; va_list ap; va_start( ap, f ); vasprintf( &c, f, ap ); std::string ret( c ); free( c ); return ret; } std::string normpath(const std::string& pathname) { stack st; if (pathname[0] == '/') st.push("/"); Split splitter("/", pathname); for (Split::const_iterator i = splitter.begin(); i != splitter.end(); ++i) { if (*i == "." || i->empty()) continue; if (*i == "..") if (st.top() == "..") st.push(*i); else if (st.top() == "/") continue; else st.pop(); else st.push(*i); } if (st.empty()) return "."; string res = st.top(); for (st.pop(); !st.empty(); st.pop()) res = joinpath(st.top(), res); return res; } std::string urlencode(const std::string& str) { string res; for (string::const_iterator i = str.begin(); i != str.end(); ++i) { if ( (*i >= '0' && *i <= '9') || (*i >= 'A' && *i <= 'Z') || (*i >= 'a' && *i <= 'z') || *i == '-' || *i == '_' || *i == '!' || *i == '*' || *i == '\'' || *i == '(' || *i == ')') res += *i; else { char buf[4]; snprintf(buf, 4, "%%%02x", static_cast(static_cast(*i))); res += buf; } } return res; } std::string urldecode(const std::string& str) { string res; for (size_t i = 0; i < str.size(); ++i) { if (str[i] == '%') { // If there's a partial %something at the end, ignore it if (i >= str.size() - 2) return res; res += static_cast(strtoul(str.substr(i+1, 2).c_str(), 0, 16)); i += 2; } else res += str[i]; } return res; } static const char* base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; template static const char invbase64(const T& idx) { static const char data[] = {62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; if (idx < 43) return 0; if (static_cast(idx) > 43 + (sizeof(data)/sizeof(data[0]))) return 0; return data[idx - 43]; } std::string encodeBase64(const std::string& str) { std::string res; for (size_t i = 0; i < str.size(); i += 3) { // Pack every triplet into 24 bits unsigned int enc; if (i + 3 < str.size()) enc = (str[i] << 16) + (str[i+1] << 8) + (str[i+2]); else { enc = (str[i] << 16); if (i + 1 < str.size()) enc += str[i+1] << 8; if (i + 2 < str.size()) enc += str[i+2]; } // Divide in 4 6-bit values and use them as indexes in the base64 char // array for (int j = 3; j >= 0; --j) res += base64[(enc >> (j*6)) & 63]; } // Replace padding characters with '=' if (str.size() % 3) for (size_t i = 0; i < 3 - (str.size() % 3); ++i) res[res.size() - i - 1] = '='; return res; } std::string decodeBase64(const std::string& str) { std::string res; for (size_t i = 0; i < str.size(); i += 4) { // Pack every quadruplet into 24 bits unsigned int enc; if (i+4 < str.size()) { enc = (invbase64(str[i]) << 18) + (invbase64(str[i+1]) << 12) + (invbase64(str[i+2]) << 6) + (invbase64(str[i+3])); } else { enc = (invbase64(str[i]) << 18); if (i+1 < str.size()) enc += (invbase64(str[i+1]) << 12); if (i+2 < str.size()) enc += (invbase64(str[i+2]) << 6); if (i+3 < str.size()) enc += (invbase64(str[i+3])); } // Divide in 3 8-bit values and append them to the result res += enc >> 16 & 0xff; res += enc >> 8 & 0xff; res += enc & 0xff; } // Remove trailing padding if (str.size() > 0) for (size_t i = str.size() - 1; str[i] == '='; --i) { if (res.size() > 0) res.resize(res.size() - 1); if (i == 0 || res.size() == 0 ) break; } return res; } static std::string stripYamlComment(const std::string& str) { std::string res; for (string::const_iterator i = str.begin(); i != str.end(); ++i) { if (*i == '#') break; res += *i; } // Remove trailing spaces while (!res.empty() && ::isspace(res[res.size() - 1])) res.resize(res.size() - 1); return res; } YamlStream::const_iterator::const_iterator(std::istream& sin) : in(&sin) { // Read the next line to parse, skipping leading empty lines while (getline(*in, line)) { line = stripYamlComment(line); if (!line.empty()) break; } if (line.empty() && in->eof()) // If we reached EOF without reading anything, become the end iterator in = 0; else // Else do the parsing ++*this; } YamlStream::const_iterator& YamlStream::const_iterator::operator++() { // Reset the data value.first.clear(); value.second.clear(); // If the lookahead line is empty, then we've reached the end of the // record, and we become the end iterator if (line.empty()) { in = 0; return *this; } if (line[0] == ' ') throw wibble::exception::Consistency("parsing yaml line \"" + line + "\"", "field continuation found, but no field has started"); // Field start size_t pos = line.find(':'); if (pos == string::npos) throw wibble::exception::Consistency("parsing Yaml line \"" + line + "\"", "every line that does not start with spaces must have a semicolon"); // Get the field name value.first = line.substr(0, pos); // Skip leading spaces in the value for (++pos; pos < line.size() && line[pos] == ' '; ++pos) ; // Get the (start of the) field value value.second = line.substr(pos); // Look for continuation lines, also preparing the lookahead line size_t indent = 0; while (true) { line.clear(); if (in->eof()) break; if (!getline(*in, line)) break; // End of record if (line.empty()) break; // Full comment line: ignore it if (line[0] == '#') continue; // New field or empty line with comment if (line[0] != ' ') { line = stripYamlComment(line); break; } // Continuation line // See how much we are indented size_t this_indent; for (this_indent = 0; this_indent < line.size() && line[this_indent] == ' '; ++this_indent) ; if (indent == 0) { indent = this_indent; // If it's the first continuation line, and there was content right // after the field name, add a \n to it if (!value.second.empty()) value.second += '\n'; } if (this_indent > indent) // If we're indented the same or more than the first line, deindent // by the amount of indentation found in the first line value.second += line.substr(indent); else // Else, the line is indented less than the first line, just remove // all leading spaces. Ugly, but it's been encoded in an ugly way. value.second += line.substr(this_indent); value.second += '\n'; } return *this; } std::string c_escape(const std::string& str) { string res; for (string::const_iterator i = str.begin(); i != str.end(); ++i) if (*i == '\n') res += "\\n"; else if (*i == '\t') res += "\\t"; else if (*i == 0 || iscntrl(*i)) { char buf[5]; snprintf(buf, 5, "\\x%02x", (unsigned int)*i); res += buf; } else if (*i == '"' || *i == '\\') { res += "\\"; res += *i; } else res += *i; return res; } std::string c_unescape(const std::string& str, size_t& lenParsed) { string res; string::const_iterator i = str.begin(); for ( ; i != str.end() && *i != '"'; ++i) if (*i == '\\' && (i+1) != str.end()) { switch (*(i+1)) { case 'n': res += '\n'; break; case 't': res += '\t'; break; case 'x': { size_t j; char buf[5] = "0x\0\0"; // Read up to 2 extra hex digits for (j = 0; j < 2 && i+2+j != str.end() && isxdigit(*(i+2+j)); ++j) buf[2+j] = *(i+2+j); i += j; res += (char)atoi(buf); break; } default: res += *(i+1); break; } ++i; } else res += *i; if (i != str.end() && *i == '"') ++i; lenParsed = i - str.begin(); return res; } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/strongenumflags.h0000644000175000017500000000463612231005104017456 0ustar enricoenrico// -*- C++ -*- (c) 2013 Vladimír Štill #if __cplusplus < 201103L #error "strongenumflag is only supported with c++11 or newer" #endif #include #ifndef WIBBLE_STRONGENUMFLAG_H #define WIBBLE_STRONGENUMFLAG_H namespace wibble { template< typename E > using is_enum_class = std::integral_constant< bool, std::is_enum< E >::value && !std::is_convertible< E, int >::value >; template< typename Self > struct StrongEnumFlags { static_assert( is_enum_class< Self >::value, "Not an enum class." ); using This = StrongEnumFlags< Self >; using UnderlyingType = typename std::underlying_type< Self >::type; constexpr StrongEnumFlags() noexcept : store( 0 ) { } constexpr StrongEnumFlags( Self flag ) noexcept : store( static_cast< UnderlyingType >( flag ) ) { } explicit constexpr StrongEnumFlags( UnderlyingType st ) noexcept : store( st ) { } constexpr explicit operator UnderlyingType() const noexcept { return store; } This &operator|=( This o ) noexcept { store |= o.store; return *this; } This &operator&=( This o ) noexcept { store &= o.store; return *this; } friend constexpr This operator|( This a, This b ) noexcept { return This( a.store | b.store ); } friend constexpr This operator&( This a, This b ) noexcept { return This( a.store & b.store ); } friend constexpr bool operator==( This a, This b ) noexcept { return a.store == b.store; } friend constexpr bool operator!=( This a, This b ) noexcept { return a.store != b.store; } constexpr bool has( Self x ) const noexcept { return (*this) & x; } constexpr operator bool() const noexcept { return store; } private: UnderlyingType store; }; // don't catch integral types and classical enum! template< typename Self, typename = typename std::enable_if< is_enum_class< Self >::value >::type > constexpr StrongEnumFlags< Self > operator|( Self a, Self b ) noexcept { using Ret = StrongEnumFlags< Self >; return Ret( a ) | Ret( b ); } template< typename Self, typename = typename std::enable_if< is_enum_class< Self >::value >::type > constexpr StrongEnumFlags< Self > operator&( Self a, Self b ) noexcept { using Ret = StrongEnumFlags< Self >; return Ret( a ) & Ret( b ); } } #endif // WIBBLE_STRONGENUMFLAG_H libwibble-1.1/wibble/test.cpp0000644000175000017500000000031712231005104015542 0ustar enricoenrico#include namespace wibble { int assertFailure = 0; void assert_die_fn( Location l ) { { AssertFailed f( l ); } abort(); // in the NDEBUG case, the above is a no-op } } libwibble-1.1/wibble/text/0000755000175000017500000000000011075402004015047 5ustar enricoenricolibwibble-1.1/wibble/text/wordwrap.h0000644000175000017500000000237611075402003017074 0ustar enricoenrico#include using namespace std; namespace wibble { namespace text { /** * Simple string wrapper. * * wibble::text::Wrap takes a string and splits it in lines of the give length * with proper word wrapping. * * Example: * \code * WordWrap ww("The quick brown fox jumps over the lazy dog"); * ww.get(5); // Returns "The" * ww.get(14); // Returns "quick brown" * ww.get(3); // Returns "fox" * // A line always gets some text, to avoid looping indefinitely in case of * // words that are too long. Words that are too long are split crudely. * ww.get(2); // Returns "ju" * ww.get(90); // Returns "mps over the lazy dog" * \endcode */ class WordWrap { std::string s; size_t cursor; public: /** * Creates a new word wrapper that takes data from the given string */ WordWrap(const std::string& s) : s(s), cursor(0) {} /** * Rewind the word wrapper, restarting the output from the beginning of the * string */ void restart() { cursor = 0; } /** * Returns true if there is still data to be read in the string */ bool hasData() const { return cursor < s.size(); } /** * Get a line of text from the string, wrapped to a maximum of \c width * characters */ std::string get(unsigned int width); }; } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/text/wordwrap.cpp0000644000175000017500000000144211075402004017421 0ustar enricoenrico#include #include using namespace std; namespace wibble { namespace text { string WordWrap::get(unsigned int width) { if (cursor >= s.size()) return ""; // Find the last work break before `width' unsigned int brk = cursor; for (unsigned int j = cursor; j < s.size() && j < cursor + width; j++) { if (s[j] == '\n') { brk = j; break; } else if (!isspace(s[j]) && (j + 1 == s.size() || isspace(s[j + 1]))) brk = j + 1; } if (brk == cursor) brk = cursor + width; string res; if (brk >= s.size()) { res = string(s, cursor, string::npos); cursor = s.size(); } else { res = string(s, cursor, brk - cursor); cursor = brk; while (cursor < s.size() && isspace(s[cursor])) cursor++; } return res; } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/libwibble.pc.in0000644000175000017500000000032111075402046016751 0ustar enricoenricoprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libwibble Description: Library of useful C++ code Version: @VERSION@ Libs: -L${libdir} -lwibble Cflags: -I${includedir} libwibble-1.1/wibble/wibble-test-genrunner.10000644000175000017500000000267211075401756020413 0ustar enricoenrico.TH "wibble-test-genrunner" "1" .SH "NAME" wibble-test-genrunner - Code generator for wibble testsuites. .SH "SYNOPSIS" .PP \fBwibble-test-genrunner\fP \fBheader\fP .PP \fBwibble-test-genrunner\fP \fBmain\fP .SH "DESCRIPTION" .PP The program generates .cpp files that are then compiled and linked into a test program for running tests from .test.h files. Examples of such .test.h files may be found among wibble headers, eg. /usr/include/wibble/regexp.test.h. In the first form, the program processes a single header file (usually of the form foo.test.h) and produces a corresponding .cpp file to be compiled. In the second form, it takes as \fBall\fP the .test.h headers and produces a single main.cpp which contains the main() function of the test program, which then runs all the tests in all the .test.h files. The generated source code is always written to standard output and it is left up to the user to redirect it to a meaningful location. The program currently has no options other than the two forms above. The program is intended to be run as part of build process of programs or libraries using the wibble testing framework. For convenient use from CMake, there is a test.cmake script under /usr/share/wibble, that takes care of producing all the .cpp files (both per-header and the main one), compiling them and linking them into a single binary which executes the testsuite. .SH "AUTHOR" .PP Petr Rockai libwibble-1.1/wibble/parse.test.h0000644000175000017500000000232712231005104016323 0ustar enricoenrico// -*- C++ -*- #include #include using namespace wibble; struct TestParse { enum TokenId { Invalid, Number }; typedef wibble::Token< TokenId > Token; struct IOStream { std::istream &i; std::string remove() { char block[1024]; i.read( block, 1023 ); block[i.gcount()] = 0; return block; } bool eof() { return i.eof(); } IOStream( std::istream &i ) : i( i ) {} }; struct Lexer : wibble::Lexer< Token, IOStream > { Token remove() { this->skipWhitespace(); this->match( isdigit, isdigit, Number ); return this->decide(); } Lexer( IOStream &s ) : wibble::Lexer< Token, IOStream >( s ) {} }; Test lexer() { std::stringstream s; IOStream is( s ); Token t; Lexer l( is ); s << "1 2"; t = l.remove(); assert_eq( t.id, Number ); assert_eq( t.data, "1" ); t = l.remove(); assert_eq( t.id, Number ); assert_eq( t.data, "2" ); t = l.remove(); assert_eq( t.id, Invalid ); } }; libwibble-1.1/wibble/string.h0000644000175000017500000002660512231005104015546 0ustar enricoenrico// -*- C++ -*- #ifndef WIBBLE_STRING_H #define WIBBLE_STRING_H /* * Various string functions * * Copyright (C) 2007,2008 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #endif namespace wibble { namespace str { using namespace wibble::operators; #ifdef _WIN32 static int vasprintf (char **, const char *, va_list); #endif std::string fmtf( const char* f, ... ); template< typename T > inline std::string fmt(const T& val); // Formatting lists -- actually, we need to move list handling into wibble, // really. template< typename X > inline typename TPair< std::ostream, typename X::Type >::First &operator<<( std::ostream &o, X list ) { if ( list.empty() ) return o << "[]"; o << "[ "; while( !list.empty() ) { o << fmt( list.head() ); if ( !list.tail().empty() ) o << ", "; list = list.tail(); } return o << " ]"; } /// Format any value into a string using a std::stringstream template< typename T > inline std::string fmt(const T& val) { std::stringstream str; str << val; return str.str(); } template<> inline std::string fmt(const std::string& val) { return val; } template<> inline std::string fmt(char * const & val) { return val; } template< typename C > inline std::string fmt_container( const C &c, char f, char l ) { std::string s; s += f; if ( c.empty() ) return s + l; s += ' '; for ( typename C::const_iterator i = c.begin(); i != c.end(); ++i ) { s += fmt( *i ); if ( i != c.end() && i + 1 != c.end() ) s += ", "; } s += ' '; s += l; return s; } // formatting sets using { ... } notation template< typename X > inline std::string fmt(const std::set< X >& val) { return fmt_container( val, '{', '}' ); } // formatting vectors using [ ... ] notation template< typename X > inline std::string fmt(const std::vector< X > &val) { return fmt_container( val, '[', ']' ); } // formatting vectors using [ ... ] notation template< typename X > inline std::string fmt(const std::deque< X > &val) { return fmt_container( val, '[', ']' ); } /// Given a pathname, return the file name without its path inline std::string basename(const std::string& pathname) { size_t pos = pathname.rfind("/"); if (pos == std::string::npos) return pathname; else return pathname.substr(pos+1); } /// Given a pathname, return the directory name without the file name inline std::string dirname(const std::string& pathname) { size_t pos = pathname.rfind("/"); if (pos == std::string::npos) return std::string(); else if (pos == 0) // Handle the case of '/foo' return std::string("/"); else return pathname.substr(0, pos); } /** * Normalise a pathname. * * For example, A//B, A/./B and A/foo/../B all become A/B. */ std::string normpath(const std::string& pathname); /// Check if a string starts with the given substring inline bool startsWith(const std::string& str, const std::string& part) { if (str.size() < part.size()) return false; return str.substr(0, part.size()) == part; } /// Check if a string ends with the given substring inline bool endsWith(const std::string& str, const std::string& part) { if (str.size() < part.size()) return false; return str.substr(str.size() - part.size()) == part; } inline std::string replace(const std::string& str, char from, char to) { std::string res; res.reserve(str.size()); for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) if (*i == from) res.append(1, to); else res.append(1, *i); return res; } #if !__xlC__ && (! __GNUC__ || __GNUC__ >= 4) /** * Return the substring of 'str' without all leading and trailing characters * for which 'classifier' returns true. */ template inline std::string trim(const std::string& str, const FUN& classifier) { if (str.empty()) return str; size_t beg = 0; size_t end = str.size() - 1; while (beg < end && classifier(str[beg])) ++beg; while (end >= beg && classifier(str[end])) --end; return str.substr(beg, end-beg+1); } /** * Return the substring of 'str' without all leading and trailing spaces. */ inline std::string trim(const std::string& str) { return trim(str, ::isspace); } #else /// Workaround version for older gcc inline std::string trim(const std::string& str) { if (str.empty()) return str; size_t beg = 0; size_t end = str.size() - 1; while (beg < end && ::isspace(str[beg])) ++beg; while (end >= beg && ::isspace(str[end])) --end; return str.substr(beg, end-beg+1); } #endif /// Convert a string to uppercase inline std::string toupper(const std::string& str) { std::string res; res.reserve(str.size()); for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) res += ::toupper(*i); return res; } /// Convert a string to lowercase inline std::string tolower(const std::string& str) { std::string res; res.reserve(str.size()); for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) res += ::tolower(*i); return res; } /// Return the same string, with the first character uppercased inline std::string ucfirst(const std::string& str) { if (str.empty()) return str; std::string res; res += ::toupper(str[0]); return res + tolower(str.substr(1)); } /// Join two paths, adding slashes when appropriate inline std::string joinpath(const std::string& path1, const std::string& path2) { if (path1.empty()) return path2; if (path2.empty()) return path1; if (path1[path1.size() - 1] == '/') if (path2[0] == '/') return path1 + path2.substr(1); else return path1 + path2; else if (path2[0] == '/') return path1 + path2; else return path1 + '/' + path2; } // append path2 to path1 if path2 is not absolute, otherwise return path2 inline std::string appendpath( const std::string &path1, const std::string &path2 ) { #ifdef POSIX if ( path2.size() >= 1 && path2[ 0 ] == '/' ) return path2; #endif #ifdef WIN32 if ( ( path2.size() >= 3 && path2[ 1 ] == ':' && path2[ 2 ] == '\\' ) || ( path2.size() >= 2 && path2[ 0 ] == '\\' && path2[ 1 ] == '\\' ) ) return path2; #endif return joinpath( path1, path2 ); } /// Urlencode a string std::string urlencode(const std::string& str); /// Decode an urlencoded string std::string urldecode(const std::string& str); /// Encode a string in Base64 std::string encodeBase64(const std::string& str); /// Decode a string encoded in Base64 std::string decodeBase64(const std::string& str); /** * Split a string where a given substring is found * * This does a similar work to the split functions of perl, python and ruby. * * Example code: * \code * str::Split splitter("/", myString); * vector split; * std::copy(splitter.begin(), splitter.end(), back_inserter(split)); * \endcode */ class Split { std::string sep; std::string str; public: // TODO: add iterator_traits class const_iterator { const std::string& sep; const std::string& str; std::string cur; size_t pos; public: const_iterator(const std::string& sep, const std::string& str) : sep(sep), str(str), pos(0) { ++*this; } const_iterator(const std::string& sep, const std::string& str, bool) : sep(sep), str(str), pos(std::string::npos) {} const_iterator& operator++() { if (pos == str.size()) pos = std::string::npos; else { size_t end; if (sep.empty()) if (pos + 1 == str.size()) end = std::string::npos; else end = pos + 1; else end = str.find(sep, pos); if (end == std::string::npos) { cur = str.substr(pos); pos = str.size(); } else { cur = str.substr(pos, end-pos); pos = end + sep.size(); } } return *this; } std::string remainder() const { if (pos == std::string::npos) return std::string(); else return str.substr(pos); } const std::string& operator*() const { return cur; } const std::string* operator->() const { return &cur; } bool operator==(const const_iterator& ti) const { // Comparing iterators on different strings is not supported for // performance reasons return pos == ti.pos; } bool operator!=(const const_iterator& ti) const { // Comparing iterators on different strings is not supported for // performance reasons return pos != ti.pos; } }; /** * Create a splitter that uses the given regular expression to find tokens. */ Split(const std::string& sep, const std::string& str) : sep(sep), str(str) {} /** * Split the string and iterate the resulting tokens */ const_iterator begin() const { return const_iterator(sep, str); } const_iterator end() const { return const_iterator(sep, str, false); } }; template std::string join(const ITER& begin, const ITER& end, const std::string& sep = ", ") { std::stringstream res; bool first = true; for (ITER i = begin; i != end; ++i) { if (first) first = false; else res << sep; res << *i; } return res.str(); } /** * Parse a record of Yaml-style field: value couples. * * Parsing stops either at end of record (one empty line) or at end of file. * * The value is deindented properly. * * Example code: * \code * utils::YamlStream stream; * map record; * std::copy(stream.begin(inputstream), stream.end(), inserter(record)); * \endcode */ class YamlStream { public: // TODO: add iterator_traits class const_iterator { std::istream* in; std::pair value; std::string line; public: const_iterator(std::istream& in); const_iterator() : in(0) {} const_iterator& operator++(); const std::pair& operator*() const { return value; } const std::pair* operator->() const { return &value; } bool operator==(const const_iterator& ti) const { return in == ti.in; } bool operator!=(const const_iterator& ti) const { return in != ti.in; } }; const_iterator begin(std::istream& in) { return const_iterator(in); } const_iterator end() { return const_iterator(); } }; /** * Escape the string so it can safely used as a C string inside double quotes */ std::string c_escape(const std::string& str); /** * Unescape a C string, stopping at the first double quotes or at the end of * the string. * * lenParsed is set to the number of characters that were pased (which can be * greather than the size of the resulting string in case escapes were found) */ std::string c_unescape(const std::string& str, size_t& lenParsed); } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/amorph.h0000644000175000017500000002633311075402057015541 0ustar enricoenrico/** -*- C++ -*- @file wibble/amorph.h @author Peter Rockai */ #include // for noise #include #include #include #include #include #ifndef WIBBLE_AMORPH_H #define WIBBLE_AMORPH_H namespace wibble { template< typename _T > struct ReturnType { typedef _T T; }; template<> struct ReturnType< void > { typedef Unit T; }; template< typename F, typename R > struct SanitizeReturn { inline typename ReturnType< R >::T call( typename F::argument_type a ) { return f( a ); }; }; template< typename F > struct SanitizeReturn< F, void > { inline Unit call( typename F::argument_type a ) { f( a ); return Unit(); } }; template< int A > struct IsZero { static const bool value = false; }; template<> struct IsZero< 0 > { static const bool value = true; }; template< typename T > struct IsPolymorphic { struct A : T { virtual ~A(); }; struct B : T {}; static const bool value = IsZero< sizeof( A ) - sizeof( B ) >::value; }; template< typename F > struct SanitizeResultType { SanitizeResultType( F _f ) : f( _f ) {} typedef typename ReturnType< typename F::result_type >::T result_type; result_type operator()( typename F::argument_type a ) { return SanitizeReturn< F, typename F::result_type >().call( a ); } F f; }; #ifndef SWIG_I struct Baseless {}; struct VirtualBase { virtual ~VirtualBase() {} }; /** @brief An interface implemented by all morph classes */ template< typename Interface > struct MorphInterface : public Interface { virtual VirtualBase *virtualBase() { return 0; } virtual MorphInterface *constructCopy( void *where = 0, unsigned int available = 0 ) const = 0; virtual void destroy( unsigned int available = 0 ) = 0; virtual ~MorphInterface() {} virtual bool leq( const MorphInterface * ) const = 0; }; /** @brief custom allocator for morph classes */ struct MorphAllocator { void *operator new( size_t bytes, void *where, unsigned available ) { if ( bytes > available || where == 0 ) { where = ::operator new( bytes ); return where; } return where; } void *operator new( size_t bytes ) { return ::operator new( bytes ); } }; template< typename W, typename Interface > struct MorphBase : MorphInterface< Interface > { MorphBase( const W &w ) : m_wrapped( w ) {} template< typename _W > typename EnableIf< IsPolymorphic< _W >, VirtualBase *>::T virtualBase() { return dynamic_cast< VirtualBase * >( &m_wrapped ); } template< typename _W > typename EnableIf< TNot< IsPolymorphic< _W > >, VirtualBase *>::T virtualBase() { return 0; } virtual VirtualBase *virtualBase() { return virtualBase< W >(); } W &wrapped() { return m_wrapped; } protected: W m_wrapped; }; template< typename Self, typename W, typename Interface > struct Morph : MorphBase< W, Interface >, mixin::Comparable< Morph< Self, W, Interface > >, MorphAllocator { typedef W Wrapped; Morph( const Wrapped &w ) : MorphBase< W, Interface >( w ) {} const Self &self() const { return *static_cast< const Self * >( this ); } bool operator<=( const Morph &o ) const { return wrapped().operator<=( o.wrapped() ); } // evaluation of correctness of definition should be done virtual bool leq( const MorphInterface< Interface > *_o ) const { const Morph *o = dynamic_cast< const Morph * >( _o ); if ( !o ) { if ( typeid( Morph ).before( typeid( _o ) ) ) return true; else return false; } return wrapped() <= o->wrapped(); } virtual MorphInterface< Interface > *constructCopy( void *where, unsigned int available ) const { return new( where, available ) Self( self() ); } virtual void destroy( unsigned int available ) { if ( sizeof( Morph ) <= available ) { this->~Morph(); } else { delete this; } } const Wrapped &wrapped() const { return this->m_wrapped; } Wrapped &wrapped() { return this->m_wrapped; } virtual ~Morph() {} }; #endif /** @brief Amorph base class This class is an Amorph class. An Amorph can hold one of many Morhps of the corresponding kind. That means, Amorph is an envelope that can contain an instance of a Morph. The Amorph envelope can provide methods that all the Morphs that it can hold provide as well. These methods will then act polymoprhically in the Amorph instance, depending on what Morph is inside. You use the Amorph and Morph classes as values, that is, they have value semantics. You usually do not need (nor want) to use pointers to access them. Of course it may be useful sometimes, but is not the normal mode of operation. Amorph objects are equal if they hold same type of Morph and the Morphs themselves are also equal. Different types of Morphs are never equal. When implementing your own Amorph class, you will want it to contain the methods that are shared by all it's Morphs. These will usually look like methodFoo() { return this->impl()->methodFoo(); } If you need to dispatch on the type of the Morph inside the Amorph envelope, you can use if ( amorph.is< MyMorph >() ) { MyMorph morph = amorph; } or if you write adaptable unary function objects (see stl manual) handling the specific morph types, you can write: amorph.ifType( functor ); This will call functor on the morph type if the functor's argument_type matches the type of contained Morph. The returned type is Maybe< functor::result_type >. If the type matches, the returned value is Just (returned value), otherwise Nothing. See wibble::Maybe documentation for details. This also lends itself to template specialisation approach, where you have a template that handles all Morphs but you need to specialize it for certain Morphs. Eventually, an example of this usage will appear in amorph.cpp over time. Often, using Amorph types will save you a template parameter you cannot afford. It also supports value-based programming, which means you need to worry about pointers a lot less. For a complex example of Amorph class set implementation, see range.h. Implementation details: the current Amorph template takes an integral Padding argument. The MorphImpl class contains an overloaded operator new, that allows it to be constructed off-heap. The Padding argument is used as a number of words to reserve inside the Amorph object itself. If the Morph that will be enveloped in the Amorph fits in this space, it will be allocated there, otherwise on heap. The Padding size defaults to 0 and therefore all Morphs are by default heap-allocated. Reserving a reasonable amount of padding should improve performance a fair bit in some applications (and is worthless in others). */ #ifndef WIBBLE_AMORPH_PADDING #define WIBBLE_AMORPH_PADDING 0 #endif template class AmorphPadder { int m_padding[ Padding1 ]; }; template<> class AmorphPadder<0> { }; template< typename Self, typename _Interface, int Padding = WIBBLE_AMORPH_PADDING > struct Amorph { typedef _Interface Interface; // typedef MorphInterface< Interface > Morp; template struct Convert { typedef T type; }; /* Amorph( const Interface &b ) { setInterfacePointer( &b ); } */ Amorph( const MorphInterface< Interface > &b ) { setMorphInterfacePointer( &b ); } Amorph( const Amorph &a ) { setMorphInterfacePointer( a.morphInterface() ); // setInterfacePointer( a.implementation() ); } Amorph() : m_impl( 0 ) {} const Self &self() const { return *static_cast< const Self * >( this ); } Self &self() { return *static_cast( this ); } bool leq( const Self &i ) const { if ( morphInterface() ) if ( i.morphInterface() ) return morphInterface()->leq( i.morphInterface() ); else return false; // it's false that non-0 <= 0 else return !i.morphInterface(); // 0 <= 0 holds, but 0 <= // non-0 doesn't } bool operator<=( const Self &i ) const { return leq( i ); } void setInterfacePointer( const Interface *i ) { if ( !i ) { m_impl = 0; return; } /* assert( dynamic_cast< const MorphInterface * >( i ) ); assert( dynamic_cast< const Interface * >( dynamic_cast< const MorphInterface * >( i ) ) ); */ m_impl = dynamic_cast< const MorphInterface< Interface > * >( i )->constructCopy( &m_padding, sizeof( m_padding ) ); // assert( dynamic_cast< const Interface * >( m_impl ) ); } void setMorphInterfacePointer( const MorphInterface< Interface > *i ) { if ( !i ) { m_impl = 0; return; } m_impl = i->constructCopy( &m_padding, sizeof( m_padding ) ); } Amorph &operator=( const Amorph &i ) { setInterfacePointer( i.implementation() ); return *this; } ~Amorph() { if ( morphInterface() ) morphInterface()->destroy( sizeof( m_padding ) ); } template< typename F > Maybe< typename F::result_type > ifType( F func ) { typedef typename F::argument_type T; typedef Maybe< typename F::result_type > rt; T *ptr = impl(); if (ptr) { return rt::Just( func(*ptr) ); } return rt::Nothing(); } const Interface *implementation() const { // return dynamic_cast< const Interface * >( m_impl ); return static_cast< const Interface * >( m_impl ); } Interface *implementation() { // return dynamic_cast< Interface * >( m_impl ); return static_cast< Interface * >( m_impl ); } MorphInterface< Interface > *morphInterface() const { return m_impl; // return dynamic_cast< MorphInterface< * >( m_impl ); } const Interface &wrapped() const { return *implementation(); } Interface &wrapped() { return *implementation(); } template< typename T > bool is() const { return impl< T >(); } bool isVoid() const { return !m_impl; } template< typename T > T *impl() const { T *p = dynamic_cast< T * >( m_impl ); if ( !p ) { MorphBase< T, Interface > *m = dynamic_cast< MorphBase< T, Interface > * >( m_impl ); if ( m ) p = &(m->wrapped()); } if ( !p ) { p = dynamic_cast< T * >( morphInterface()->virtualBase() ); } return p; } private: unsigned int reservedSize() { return sizeof( m_padding ) + sizeof( m_impl ); } AmorphPadder m_padding; MorphInterface< Interface > *m_impl; // Interface *m_impl; }; #ifndef SWIG_I template< typename T, typename X > typename X::template Convert::type &downcast( const X &a ) { return *a.template impl< T >(); } #endif } #endif libwibble-1.1/wibble/test-runner.h0000644000175000017500000000434512231005104016523 0ustar enricoenrico// -*- C++ -*- #include #include #ifdef POSIX #include #endif #include #define RUN(x, y) x().y() struct RunTest { const char *name; void (*run)(); }; struct RunSuite { const char *name; RunTest *tests; int testCount; int findTest( std::string name ) { for ( int i = 0; i < testCount; ++i ) if ( tests[i].name == name ) return i; return -1; } }; struct RunFeedback { virtual void status( std::string l ) = 0; virtual void waitForAck() = 0; }; struct RunAll { RunSuite *suites; int suiteCount; RunFeedback *feedback; RunSuite *findSuite( std::string name ) { for ( int i = 0; i < suiteCount; ++i ) if ( suites[i].name == name ) return suites + i; return 0; } void runSuite( RunSuite &s, int fromTest, int suite, int suiteCount ) { feedback->status( wibble::str::fmtf( "s/s: (%d/%d) %s", suite + 1, suiteCount, s.name ) ); for ( int i = fromTest; i < s.testCount; ++i ) { feedback->status( wibble::str::fmtf( "t/s: (%d/%d) %s", i, s.testCount, s.tests[i].name ) ); feedback->waitForAck(); s.tests[i].run(); feedback->status( std::string( "t/d: " ) + s.tests[i].name ); feedback->waitForAck(); // exit( 0 ); // TODO make this optional; safety vs // performance tradeoff } feedback->status( std::string( "s/d: " ) + s.name ); } void runTest( RunSuite &s, int test ) { feedback->status( std::string( "s/s: (1/1) " ) + s.name ); feedback->status( std::string( "t/s: (1/1) " ) + s.tests[test].name ); feedback->waitForAck(); s.tests[test].run(); feedback->status( std::string( "t/d: " ) + s.tests[test].name ); feedback->waitForAck(); feedback->status( std::string( "s/d: " ) + s.name ); } void runFrom( int suite, int test ) { for ( int i = suite; i < suiteCount; ++i ) { assert( suite <= suiteCount ); runSuite( suites[i], test, i, suiteCount ); test = 0; } } }; libwibble-1.1/wibble/test.test.h0000644000175000017500000000214111110532142016164 0ustar enricoenrico/* -*- C++ -*- * Implementation of some test utility functions * * Copyright (C) 2003,2004,2005,2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include namespace { struct TestTests { Test assertions() { assert(true); assert_eq(42, 42); for ( int i = 0; i < 10; ++i ) { assert_eq_l( i, i, i ); } } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/list.test.h0000644000175000017500000001030011311744337016172 0ustar enricoenrico// -*- C++ -*- #include #include using namespace wibble; struct TestList { struct My { typedef int Type; int i, max; int head() const { return i; } My tail() const { My t = *this; if ( i < max ) t.i ++; if ( i > max ) t.i --; return t; } bool empty() const { return i == max; } My( int j = 0, int m = 0 ) : i( j ), max( m ) {} }; struct My2 { typedef int Type; int i, max, rep, repmax; int head() const { return i; } My2 tail() const { My2 t = *this; if ( rep > 0 ) t.rep --; else { t.rep = repmax; if ( i < max ) t.i ++; if ( i > max ) t.i --; } return t; } bool empty() const { return i == max; } My2( int j = 0, int m = 0, int r = 0 ) : i( j ), max( m ), rep( r ), repmax( r ) {} }; static bool odd( int i ) { return i % 2 == 1; } template< typename List > void checkOddList( List l ) { int i = 0; while ( !l.empty() ) { assert( odd( l.head() ) ); l = l.tail(); ++ i; } assert_eq( i, 512 ); } template< typename List > void checkListSorted( List l ) { if ( l.empty() ) return; typename List::Type last = l.head(); while ( !l.empty() ) { assert( last <= l.head() ); last = l.head(); l = l.tail(); } } Test count() { My list( 512, 1024 ); assert_eq( list::count( list ), 512u ); list = My( 0, 1024 ); assert_eq( list::count( list ), 1024u ); } Test filtered() { My list( 1, 1024 ); checkOddList( list::filter( list, odd ) ); assert_eq( list::count( list::filter( list, odd ) ), 512 ); } Test sorted() { My list( 1, 1024 ); assert_eq( list::count( list ), list::count( list::sort( list ) ) ); checkListSorted( list ); checkListSorted( list::sort( list ) ); { ExpectFailure fail; checkListSorted( My( 100, 0 ) ); } checkListSorted( list::sort( My( 100, 0 ) ) ); } #if 0 #warning Disabled until mornfall fixes it T-est take() { My list( 0, 1024 ); assert_eq( list::count( list ), 1024 ); assert_eq( list::count( list::take( 50, list ) ), 50 ); } #endif Test unique() { My2 list( 0, 20, 3 ); assert_eq( list::count( list ), 80 ); assert_eq( list::count( list::unique( list ) ), 20 ); assert_eq( list::unique( list ).head(), 0 ); assert_eq( list::unique( list ).tail().head(), 1 ); } Test stl() { My list( 0, 1024 ); std::vector< int > vec; std::copy( list::begin( list ), list::end( list ), std::back_inserter( vec ) ); for ( int i = 0; i < 1024; ++i ) assert_eq( vec[i], i ); } static int mul2add1( int a ) { return a * 2 + 1; } #if 0 #warning Disabled until mornfall fixes it T-est map() { My list( 0, 512 ); checkOddList( list::map( list, std::ptr_fun( mul2add1 ) ) ); } #endif Test empty() { assert( list::Empty< int >().empty() ); } Test single() { assert_eq( list::singular( 0 ).head(), 0 ); assert( list::singular( 0 ).tail().empty() ); } Test append() { assert_eq( list::append( list::singular( 0 ), list::singular( 1 ) ).head(), 0 ); assert_eq( list::append( list::singular( 0 ), list::singular( 1 ) ).tail().head(), 1 ); assert( list::append( list::singular( 0 ), list::singular( 1 ) ).tail().tail().empty() ); } Test appendCount() { assert_eq( list::count( list::append( My( 0, 10 ), My2( 0, 5, 1 ) ) ), 20 ); } }; libwibble-1.1/wibble/regexp.test.h0000644000175000017500000000576311075401762016531 0ustar enricoenrico/* -*- C++ -*- (c) 2007 Petr Rockai (c) 2007 Enrico Zini */ #include #include namespace { using namespace std; using namespace wibble; struct TestRegexp { Test basicMatch() { Regexp re("^fo\\+bar()$"); assert(re.match("fobar()")); assert(re.match("foobar()")); assert(re.match("fooobar()")); assert(!re.match("fbar()")); assert(!re.match(" foobar()")); assert(!re.match("foobar() ")); } Test extendedMatch() { ERegexp re("^fo+bar()$"); assert(re.match("fobar")); assert(re.match("foobar")); assert(re.match("fooobar")); assert(!re.match("fbar")); assert(!re.match(" foobar")); assert(!re.match("foobar ")); } Test capture() { ERegexp re("^f(o+)bar([0-9]*)$", 3); assert(re.match("fobar")); assert_eq(re[0], string("fobar")); assert_eq(re[1], string("o")); assert_eq(re[2], string("")); assert_eq(re.matchStart(0), 0u); assert_eq(re.matchEnd(0), 5u); assert_eq(re.matchLength(0), 5u); assert_eq(re.matchStart(1), 1u); assert_eq(re.matchEnd(1), 2u); assert_eq(re.matchLength(1), 1u); assert(re.match("foobar42")); assert_eq(re[0], string("foobar42")); assert_eq(re[1], string("oo")); assert_eq(re[2], string("42")); } Test tokenize() { string str("antani blinda la supercazzola!"); Tokenizer tok(str, "[a-z]+", REG_EXTENDED); Tokenizer::const_iterator i = tok.begin(); assert(i != tok.end()); assert_eq(*i, "antani"); ++i; assert(i != tok.end()); assert_eq(*i, "blinda"); ++i; assert(i != tok.end()); assert_eq(*i, "la"); ++i; assert(i != tok.end()); assert_eq(*i, "supercazzola"); ++i; assert(i == tok.end()); } Test splitter() { Splitter splitter("[ \t]+or[ \t]+", REG_EXTENDED | REG_ICASE); Splitter::const_iterator i = splitter.begin("a or b OR c or dadada"); assert_eq(*i, "a"); assert_eq(i->size(), 1u); ++i; assert_eq(*i, "b"); assert_eq(i->size(), 1u); ++i; assert_eq(*i, "c"); assert_eq(i->size(), 1u); ++i; assert_eq(*i, "dadada"); assert_eq(i->size(), 6u); ++i; assert(i == splitter.end()); } Test emptySplitter() { Splitter splitter("Z*", REG_EXTENDED | REG_ICASE); Splitter::const_iterator i = splitter.begin("ciao"); assert_eq(*i, "c"); assert_eq(i->size(), 1u); ++i; assert_eq(*i, "i"); assert_eq(i->size(), 1u); ++i; assert_eq(*i, "a"); assert_eq(i->size(), 1u); ++i; assert_eq(*i, "o"); assert_eq(i->size(), 1u); ++i; assert(i == splitter.end()); } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/cast.h0000644000175000017500000000116211075402062015172 0ustar enricoenrico// -*- C++ -*- #include #ifndef WIBBLE_CAST_H #define WIBBLE_CAST_H namespace wibble { template T &downcast(X *v) { if (!v) throw exception::BadCastExt< X, T >( "downcast on null pointer" ); T *x = dynamic_cast(v); if (!x) throw exception::BadCastExt< X, T >( "dynamic downcast failed" ); return *x; } template< typename T > typename T::WrappedType &unwrap( const T &x ) { return x.unwrap(); } template< typename T > T &unwrap( T &x ) { return x; } template< typename _T, typename In > struct IsType { typedef _T T; }; } #endif libwibble-1.1/wibble/tests/0000755000175000017500000000000012227610207015233 5ustar enricoenricolibwibble-1.1/wibble/tests/CMakeLists.txt0000644000175000017500000000155211075402074017777 0ustar enricoenricoSET( T sh ${CMAKE_CURRENT_SOURCE_DIR}/libwibble-check ) file( GLOB TEST_SOURCES test_*.cpp ) ADD_CUSTOM_TARGET( check COMMAND make libwibble-test && ${T} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/data-stamp ) ADD_EXECUTABLE( libwibble-test tut-main.cpp ${TEST_SOURCES} ) TARGET_LINK_LIBRARIES( libwibble-test wibble pthread ) ADD_EXECUTABLE( commandline-demo commandline-demo.cpp ) TARGET_LINK_LIBRARIES( commandline-demo wibble pthread ) #set_target_properties( libwibble-test PROPERTIES LINK_FLAGS ${GC_LINK_FLAGS} ) ADD_CUSTOM_COMMAND( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/data-stamp COMMAND touch data-stamp ) #ADD_CUSTOM_COMMAND( # OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/tut-syms.cpp # DEPENDS ${wibble_BINARY_DIR}/libwibble.a # COMMAND sh ${CMAKE_SOURCE_DIR}/extract_syms.sh ${wibble_BINARY_DIR}/libwibble.a wibble_sym_references > tut-syms.cpp ) libwibble-1.1/wibble/tests/tut-wibble.h0000644000175000017500000000010111075402072017451 0ustar enricoenrico//#include #include libwibble-1.1/wibble/tests/tut-main.cpp0000644000175000017500000000362611075402075017506 0ustar enricoenrico#include #include #include #include namespace tut { test_runner_singleton runner; } using namespace wibble; void signal_to_exception(int) { throw std::runtime_error("killing signal catched"); } int main(int argc,const char* argv[]) { tut::reporter visi; signal(SIGSEGV,signal_to_exception); signal(SIGILL,signal_to_exception); if( (argc == 2 && (! strcmp ("help", argv[1]))) || argc > 3 ) { std::cout << "TUT example test application." << std::endl; std::cout << "Usage: example [regression] | [list] | [ group] [test]" << std::endl; std::cout << " List all groups: example list" << std::endl; std::cout << " Run all tests: example regression" << std::endl; std::cout << " Run one group: example std::auto_ptr" << std::endl; std::cout << " Run one test: example std::auto_ptr 3" << std::endl;; } // std::cout << "\nFAILURE and EXCEPTION in these tests are FAKE ;)\n\n"; tut::runner.get().set_callback(&visi); try { if( argc == 1 || (argc == 2 && std::string(argv[1]) == "regression") ) { tut::runner.get().run_tests(); } else if( argc == 2 && std::string(argv[1]) == "list" ) { std::cout << "registered test groups:" << std::endl; tut::groupnames gl = tut::runner.get().list_groups(); tut::groupnames::const_iterator i = gl.begin(); tut::groupnames::const_iterator e = gl.end(); while( i != e ) { std::cout << " " << *i << std::endl; ++i; } } else if( argc == 2 && std::string(argv[1]) != "regression" ) { tut::runner.get().run_tests(argv[1]); } else if( argc == 3 ) { tut::runner.get().run_test(argv[1],::atoi(argv[2])); } } catch( const std::exception& ex ) { std::cerr << "tut raised exception: " << ex.what() << std::endl; } return 0; } libwibble-1.1/wibble/tests/libwibble-check0000644000175000017500000000060111075402077020165 0ustar enricoenrico#!/bin/sh # Uncomment to use root privileges to enable more accurate timings # run USE_ROOT=1 make check for better timings MAIN="libwibble-test" id=`date +%y%m%d%H%M%S` if [ "$USE_ROOT"x = x ] then $DEBUGGER ./$MAIN $ARGS 2>&1 | tee `pwd`/testrun-$id else sudo nice --5 su $LOGNAME -c \ "`pwd`/$MAIN $ARGS 2>&1 | tee `pwd`/testrun-$id" fi echo Output saved in `pwd`/testrun-$id libwibble-1.1/wibble/tests/commandline-demo.cpp0000644000175000017500000000753111075402101021145 0ustar enricoenrico#include #include using namespace std; using namespace wibble::commandline; int withCommands(int argc, const char* argv[]) { // Main parser StandardParserWithMandatoryCommand parser(argv[0], "0.1", 1, "enrico@enricozini.org"); parser.usage = " [options and arguments]"; parser.description = "Demo commandline parser"; parser.longDescription = "This program is a demo for the commandline parser. It shows a parser with " "subcommands and various kinds of options."; // Grep subcommand Engine* grep = parser.addEngine("grep", " [files...]", "Print lines matching the pattern", "Print all the lines of standard input or the given files that match " "the given pattern."); BoolOption* grep_invert = grep->add("invert", 'v', "invert", "", "invert the match"); StringOption* grep_output = grep->add("output", 'o', "output", "", "write the output to the given file instead of standard output"); // ls subcommand Engine* ls = parser.addEngine("ls", "[directory...]", "List files in a directory", "List all files found in the directories given as parameters to standard output. " "if no directory is given, list the files in the current directory."); // sort option group OptionGroup* ls_sort = ls->addGroup("Options controlling the order of output"); BoolOption* ls_sort_invert = ls_sort->add("invert", 'r', "invert", "", "sort in inverted order"); IntOption* ls_sort_field = ls_sort->add("field", 0, "field", "", "sort the given field (if the switch is omitted, 1 is assumed"); // format option group OptionGroup* ls_format = ls->addGroup("Options controlling the format of output"); BoolOption* ls_format_long = ls_format->add("long", 'l', "long", "", "long output format with all the details"); BoolOption* ls_format_inode = ls_format->add("inode", 'i', "inode", "", "also output the file inode number"); // other ls options BoolOption* ls_all = ls->add("all", 'a', "all", "", "output all files, including the ones starting with a dot"); try { if (parser.parse(argc, argv)) cerr << "The parser handled the command for us." << endl; if (parser.foundCommand()) cerr << "Selected command: " << parser.foundCommand()->name() << endl; else cerr << "No command selected." << endl; cerr << "Option values:" << endl; cerr << " help: " << parser.help->boolValue() << endl; cerr << " version: " << parser.version->boolValue() << endl; cerr << " manpage: " << parser.manpage->boolValue() << endl; cerr << " grep/invert: " << grep_invert->boolValue() << endl; cerr << " grep/output: " << grep_output->stringValue() << endl; cerr << " ls/sort/invert: " << ls_sort_invert->boolValue() << endl; cerr << " ls/sort/field: " << ls_sort_field->intValue() << endl; cerr << " ls/format/long: " << ls_format_long->boolValue() << endl; cerr << " ls/format/inode: " << ls_format_inode->boolValue() << endl; cerr << " ls/all: " << ls_all->boolValue() << endl; return 0; } catch (wibble::exception::BadOption& e) { cerr << e.fullInfo() << endl; parser.outputHelp(cerr); return 1; } return 1; } void usage(ostream& out, const string& argv0) { out << "Usage: " << argv0 << " {commands|switches}" << endl; } int main(int argc, const char* argv[]) { try { if (argc == 1) { usage(cout, argv[0]); return 0; } if (string(argv[1]) == "commands") return withCommands(argc - 1, argv + 1); //else if (string(argv[1]) == "switches") //return withoutCommands(argc - 1, argv + 1); else { usage(cerr, argv[0]); return 1; } return 0; } catch (wibble::exception::Generic& e) { cerr << e.type() << ": " << e.fullInfo() << endl; return 1; } catch (std::exception& e) { cerr << e.what() << endl; return 1; } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/tests/tut-prereq.h0000644000175000017500000000022511075402073017513 0ustar enricoenrico#include #include #include #include #include #include #include #include libwibble-1.1/wibble/tests/tut.h0000644000175000017500000005157011075402071016226 0ustar enricoenrico#ifndef TUT_H_GUARD #define TUT_H_GUARD #include #include #include #include #include #include #include #if defined(TUT_USE_SEH) #include #include #endif /** * Template Unit Tests Framework for C++. * http://tut.dozen.ru * * @author dozen, tut@dozen.ru */ namespace tut { /** * Exception to be throwed when attempted to execute * missed test by number. */ struct no_such_test : public std::logic_error { no_such_test() : std::logic_error("no such test"){}; }; /** * No such test and passed test number is higher than * any test number in current group. Used in one-by-one * test running when upper bound is not known. */ struct beyond_last_test : public no_such_test { beyond_last_test(){}; }; /** * Group not found exception. */ struct no_such_group : public std::logic_error { no_such_group(const std::string& grp) : std::logic_error(grp){}; }; /** * Internal exception to be throwed when * no more tests left in group or journal. */ struct no_more_tests { no_more_tests(){}; }; /** * Internal exception to be throwed when * test constructor has failed. */ struct bad_ctor : public std::logic_error { bad_ctor(const std::string& msg) : std::logic_error(msg){}; }; /** * Exception to be throwed when ensure() fails or fail() called. */ class failure : public std::logic_error { public: failure(const std::string& msg) : std::logic_error(msg){}; }; /** * Exception to be throwed when test desctructor throwed an exception. */ class warning : public std::logic_error { public: warning(const std::string& msg) : std::logic_error(msg){}; }; /** * Exception to be throwed when test issued SEH (Win32) */ class seh : public std::logic_error { public: seh(const std::string& msg) : std::logic_error(msg){}; }; /** * Return type of runned test/test group. * * For test: contains result of test and, possible, message * for failure or exception. */ struct test_result { /** * Test group name. */ std::string group; /** * Test number in group. */ int test; /** * ok - test finished successfully * fail - test failed with ensure() or fail() methods * ex - test throwed an exceptions * warn - test finished successfully, but test destructor throwed * term - test forced test application to terminate abnormally */ typedef enum { ok, fail, ex, warn, term, ex_ctor } result_type; result_type result; /** * Exception message for failed test. */ std::string message; std::string exception_typeid; /** * Default constructor. */ test_result() : test(0),result(ok) { } /** * Constructor. */ test_result( const std::string& grp,int pos,result_type res) : group(grp),test(pos),result(res) { } /** * Constructor with exception. */ test_result( const std::string& grp,int pos, result_type res, const std::exception& ex) : group(grp),test(pos),result(res), message(ex.what()),exception_typeid(typeid(ex).name()) { } }; /** * Interface. * Test group operations. */ struct group_base { virtual ~group_base(){}; // execute tests iteratively virtual void rewind() = 0; virtual test_result run_next() = 0; // execute one test virtual test_result run_test(int n) = 0; }; /** * Test runner callback interface. * Can be implemented by caller to update * tests results in real-time. User can implement * any of callback methods, and leave unused * in default implementation. */ struct callback { /** * Virtual destructor is a must for subclassed types. */ virtual ~callback(){}; /** * Called when new test run started. */ virtual void run_started(){}; /** * Called when a group started * @param name Name of the group */ virtual void group_started(const std::string& /*name*/){}; /** * Called when a test finished. * @param tr Test results. */ virtual void test_completed(const test_result& /*tr*/){}; /** * Called when a group is completed * @param name Name of the group */ virtual void group_completed(const std::string& /*name*/){}; /** * Called when all tests in run completed. */ virtual void run_completed(){}; }; /** * Typedef for runner::list_groups() */ typedef std::vector groupnames; /** * Test runner. */ class test_runner { protected: typedef std::map groups; typedef groups::iterator iterator; typedef groups::const_iterator const_iterator; groups groups_; callback default_callback_; callback* callback_; public: /** * Constructor */ test_runner() : callback_(&default_callback_) { } /** * Stores another group for getting by name. */ void register_group(const std::string& name,group_base* gr) { if( gr == 0 ) { throw std::invalid_argument("group shall be non-null"); } groups::iterator found = groups_.find(name); if( found != groups_.end() ) { std::string msg("attempt to add already existent group "+name); // this exception terminates application so we use cerr also std::cerr << msg << std::endl; throw std::logic_error(msg); } groups_[name] = gr; } /** * Stores callback object. */ void set_callback(callback* cb) { callback_ = cb==0? &default_callback_:cb; } /** * Returns callback object. */ callback& get_callback() const { return *callback_; } /** * Returns list of known test groups. */ const groupnames list_groups() const { groupnames ret; const_iterator i = groups_.begin(); const_iterator e = groups_.end(); while( i != e ) { ret.push_back(i->first); ++i; } return ret; } /** * Runs all tests in all groups. * @param callback Callback object if exists; null otherwise */ void run_tests() const { callback_->run_started(); const_iterator i = groups_.begin(); const_iterator e = groups_.end(); while( i != e ) { callback_->group_started(i->first); try { run_all_tests_in_group_(i); } catch( const no_more_tests& ) { callback_->group_completed(i->first); } ++i; } callback_->run_completed(); } /** * Runs all tests in specified group. */ void run_tests(const std::string& group_name) const { callback_->run_started(); const_iterator i = groups_.find(group_name); if( i == groups_.end() ) { callback_->run_completed(); throw no_such_group(group_name); } callback_->group_started(group_name); try { run_all_tests_in_group_(i); } catch( const no_more_tests& ) { // ok } callback_->group_completed(group_name); callback_->run_completed(); } /** * Runs one test in specified group. */ test_result run_test(const std::string& group_name,int n) const { callback_->run_started(); const_iterator i = groups_.find(group_name); if( i == groups_.end() ) { callback_->run_completed(); throw no_such_group(group_name); } callback_->group_started(group_name); try { test_result tr = i->second->run_test(n); callback_->test_completed(tr); callback_->group_completed(group_name); callback_->run_completed(); return tr; } catch( const beyond_last_test& ) { callback_->group_completed(group_name); callback_->run_completed(); throw; } catch( const no_such_test& ) { callback_->group_completed(group_name); callback_->run_completed(); throw; } } private: void run_all_tests_in_group_(const_iterator i) const { i->second->rewind(); for( ;; ) { test_result tr = i->second->run_next(); callback_->test_completed(tr); if( tr.result == test_result::ex_ctor ) { throw no_more_tests(); } } } }; /** * Singleton for test_runner implementation. * Instance with name runner_singleton shall be implemented * by user. */ class test_runner_singleton { public: static test_runner& get() { static test_runner tr; return tr; } }; extern test_runner_singleton runner; /** * Test object. Contains data test run upon and default test method * implementation. Inherited from Data to allow tests to * access test data as members. */ template class test_object : public Data { public: /** * Default constructor */ test_object(){}; /** * The flag is set to true by default (dummy) test. * Used to detect usused test numbers and avoid unnecessary * test object creation which may be time-consuming depending * on operations described in Data::Data() and Data::~Data(). * TODO: replace with throwing special exception from default test. */ bool called_method_was_a_dummy_test_; /** * Default do-nothing test. */ template void test() { called_method_was_a_dummy_test_ = true; } }; namespace { /** * Tests provided condition. * Throws if false. */ void ensure(bool cond) { if( !cond ) throw failure(""); } /** * Tests provided condition. * Throws if false. */ template void ensure(const T msg,bool cond) { if( !cond ) throw failure(msg); } /** * Tests two objects for being equal. * Throws if false. * * NB: both T and Q must have operator << defined somewhere, or * client code will not compile at all! */ template void ensure_equals(const char* msg,const Q& actual,const T& expected) { if( expected != actual ) { std::stringstream ss; ss << (msg?msg:"") << (msg?": ":"") << "expected " << expected << " actual " << actual; throw failure(ss.str().c_str()); } } template void ensure_equals(const Q& actual,const T& expected) { ensure_equals<>(0,actual,expected); } /** * Tests two objects for being at most in given distance one from another. * Borders are excluded. * Throws if false. * * NB: T must have operator << defined somewhere, or * client code will not compile at all! Also, T shall have * operators + and -, and be comparable. */ template void ensure_distance(const char* msg,const T& actual,const T& expected,const T& distance) { if( expected-distance >= actual || expected+distance <= actual ) { std::stringstream ss; ss << (msg?msg:"") << (msg?": ":"") << "expected [" << expected-distance << ";" << expected+distance << "] actual " << actual; throw failure(ss.str().c_str()); } } template void ensure_distance(const T& actual,const T& expected,const T& distance) { ensure_distance<>(0,actual,expected,distance); } /** * Unconditionally fails with message. */ void fail(const char* msg="") { throw failure(msg); } } /** * Walks through test tree and stores address of each * test method in group. Instantiation stops at 0. */ template struct tests_registerer { static void reg(Group& group) { group.reg(n,&Test::template test); tests_registerer::reg(group); } }; template struct tests_registerer { static void reg(Group&){}; }; /** * Test group; used to recreate test object instance for * each new test since we have to have reinitialized * Data base class. */ template class test_group : public group_base { const char* name_; typedef void (test_object::*testmethod)(); typedef std::map tests; typedef typename tests::iterator tests_iterator; typedef typename tests::const_iterator tests_const_iterator; typedef typename tests::const_reverse_iterator tests_const_reverse_iterator; typedef typename tests::size_type size_type; tests tests_; tests_iterator current_test_; /** * Exception-in-destructor-safe smart-pointer class. */ template class safe_holder { T* p_; bool permit_throw_in_dtor; safe_holder(const safe_holder&); safe_holder& operator = (const safe_holder&); public: safe_holder() : p_(0),permit_throw_in_dtor(false) { } ~safe_holder() { release(); } T* operator -> () const { return p_; }; T* get() const { return p_; }; /** * Tell ptr it can throw from destructor. Right way is to * use std::uncaught_exception(), but some compilers lack * correct implementation of the function. */ void permit_throw(){ permit_throw_in_dtor = true; } /** * Specially treats exceptions in test object destructor; * if test itself failed, exceptions in destructor * are ignored; if test was successful and destructor failed, * warning exception throwed. */ void release() { try { if( delete_obj() == false ) { throw warning("destructor of test object raised an SEH exception"); } } catch( const std::exception& ex ) { if( permit_throw_in_dtor ) { std::string msg = "destructor of test object raised exception: "; msg += ex.what(); throw warning(msg); } } catch( ... ) { if( permit_throw_in_dtor ) { throw warning("destructor of test object raised an exception"); } } } /** * Re-init holder to get brand new object. */ void reset() { release(); permit_throw_in_dtor = false; p_ = new T(); } bool delete_obj() { #if defined(TUT_USE_SEH) __try { #endif T* p = p_; p_ = 0; delete p; #if defined(TUT_USE_SEH) } __except(handle_seh_(::GetExceptionCode())) { if( permit_throw_in_dtor ) { return false; } } #endif return true; } }; public: typedef test_object object; /** * Creates and registers test group with specified name. */ test_group(const char* name) : name_(name) { // register itself runner.get().register_group(name_,this); // register all tests tests_registerer::reg(*this); }; /** * This constructor is used in self-test run only. */ test_group(const char* name,test_runner& another_runner) : name_(name) { // register itself another_runner.register_group(name_,this); // register all tests tests_registerer, test_group,MaxTestsInGroup>::reg(*this); }; /** * Registers test method under given number. */ void reg(int n,testmethod tm) { tests_[n] = tm; } /** * Reset test position before first test. */ void rewind() { current_test_ = tests_.begin(); } /** * Runs next test. */ test_result run_next() { if( current_test_ == tests_.end() ) { throw no_more_tests(); } // find next user-specialized test safe_holder obj; while( current_test_ != tests_.end() ) { try { return run_test_(current_test_++,obj); } catch( const no_such_test& ) { continue; } } throw no_more_tests(); } /** * Runs one test by position. */ test_result run_test(int n) { // beyond tests is special case to discover upper limit if( tests_.rbegin() == tests_.rend() ) throw beyond_last_test(); if( tests_.rbegin()->first < n ) throw beyond_last_test(); // withing scope; check if given test exists tests_iterator ti = tests_.find(n); if( ti == tests_.end() ) throw no_such_test(); safe_holder obj; return run_test_(ti,obj); } private: /** * VC allows only one exception handling type per function, * so I have to split the method */ test_result run_test_(const tests_iterator& ti,safe_holder& obj) { try { if( run_test_seh_(ti->second,obj) == false ) throw seh("seh"); } catch(const no_such_test&) { throw; } catch(const warning& ex) { // test ok, but destructor failed test_result tr(name_,ti->first,test_result::warn,ex); return tr; } catch(const failure& ex) { // test failed because of ensure() or similar method test_result tr(name_,ti->first,test_result::fail,ex); return tr; } catch(const seh& ex) { // test failed with sigsegv, divide by zero, etc test_result tr(name_,ti->first,test_result::term,ex); return tr; } catch(const bad_ctor& ex) { // test failed because test ctor failed; stop the whole group test_result tr(name_,ti->first,test_result::ex_ctor,ex); return tr; } catch(const std::exception& ex) { // test failed with std::exception test_result tr(name_,ti->first,test_result::ex,ex); return tr; } catch(...) { // test failed with unknown exception test_result tr(name_,ti->first,test_result::ex); return tr; } // test passed test_result tr(name_,ti->first,test_result::ok); return tr; } /** * Runs one under SEH if platform supports it. */ bool run_test_seh_(testmethod tm,safe_holder& obj) { #if defined(TUT_USE_SEH) __try { #endif if( obj.get() == 0 ) { reset_holder_(obj); } obj->called_method_was_a_dummy_test_ = false; #if defined(TUT_USE_SEH) __try { #endif (obj.get()->*tm)(); #if defined(TUT_USE_SEH) } __except(handle_seh_(::GetExceptionCode())) { // throw seh("SEH"); return false; } #endif if( obj->called_method_was_a_dummy_test_ ) { // do not call obj.release(); reuse object throw no_such_test(); } obj.permit_throw(); obj.release(); #if defined(TUT_USE_SEH) } __except(handle_seh_(::GetExceptionCode())) { return false; } #endif return true; } void reset_holder_(safe_holder& obj) { try { obj.reset(); } catch(const std::exception& ex) { throw bad_ctor(ex.what()); } catch(...) { throw bad_ctor("test constructor has generated an exception; group execution is terminated"); } } }; #if defined(TUT_USE_SEH) /** * Decides should we execute handler or ignore SE. */ inline int handle_seh_(DWORD excode) { switch(excode) { case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_DATATYPE_MISALIGNMENT: case EXCEPTION_BREAKPOINT: case EXCEPTION_SINGLE_STEP: case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_INEXACT_RESULT: case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_UNDERFLOW: case EXCEPTION_INT_DIVIDE_BY_ZERO: case EXCEPTION_INT_OVERFLOW: case EXCEPTION_PRIV_INSTRUCTION: case EXCEPTION_IN_PAGE_ERROR: case EXCEPTION_ILLEGAL_INSTRUCTION: case EXCEPTION_NONCONTINUABLE_EXCEPTION: case EXCEPTION_STACK_OVERFLOW: case EXCEPTION_INVALID_DISPOSITION: case EXCEPTION_GUARD_PAGE: case EXCEPTION_INVALID_HANDLE: return EXCEPTION_EXECUTE_HANDLER; }; return EXCEPTION_CONTINUE_SEARCH; } #endif } #endif libwibble-1.1/wibble/tests/Makefile.am0000644000175000017500000000050311075402102017256 0ustar enricoenricoTESTS = libwibble-check check_PROGRAMS = libwibble-test check_SCRIPTS = libwibble-check libwibble_test_SOURCES = tut-main.cpp libwibble_test_LDADD = -dlpreopen ../libwibble.la noinst_PROGRAMS = commandline-demo commandline_demo_SOURCES = commandline-demo.cpp commandline_demo_LDADD = ../libwibble.la INCLUDES = -I../.. libwibble-1.1/wibble/tests/tut_reporter.h0000644000175000017500000001052511075402066020147 0ustar enricoenrico#ifndef TUT_REPORTER #define TUT_REPORTER #include /** * Template Unit Tests Framework for C++. * http://tut.dozen.ru * * @author dozen, tut@dozen.ru */ namespace { std::ostream& operator << (std::ostream& os,const tut::test_result& tr) { switch(tr.result) { case tut::test_result::ok: os << '.'; break; case tut::test_result::fail: os << '[' << tr.test << "=F]"; break; case tut::test_result::ex_ctor: os << '[' << tr.test << "=C]"; break; case tut::test_result::ex: os << '[' << tr.test << "=X]"; break; case tut::test_result::warn: os << '[' << tr.test << "=W]"; break; case tut::test_result::term: os << '[' << tr.test << "=T]"; break; } return os; } } namespace tut { /** * Default TUT callback handler. */ class reporter : public tut::callback { std::string current_group; typedef std::vector not_passed_list; not_passed_list not_passed; std::ostream& os; public: int ok_count; int exceptions_count; int failures_count; int terminations_count; int warnings_count; reporter() : os(std::cout) { init(); } reporter(std::ostream& out) : os(out) { init(); } void run_started() { init(); } void test_completed(const tut::test_result& tr) { if( tr.group != current_group ) { os << std::endl << tr.group << ": " << std::flush; current_group = tr.group; } os << tr << std::flush; if( tr.result == tut::test_result::ok ) ok_count++; else if( tr.result == tut::test_result::ex ) exceptions_count++; else if( tr.result == tut::test_result::ex_ctor ) exceptions_count++; else if( tr.result == tut::test_result::fail ) failures_count++; else if( tr.result == tut::test_result::warn ) warnings_count++; else terminations_count++; if( tr.result != tut::test_result::ok ) { not_passed.push_back(tr); } } void run_completed() { os << std::endl; if( not_passed.size() > 0 ) { not_passed_list::const_iterator i = not_passed.begin(); while( i != not_passed.end() ) { tut::test_result tr = *i; os << std::endl; os << "---> " << "group: " << tr.group << ", test: test<" << tr.test << ">" << std::endl; os << " problem: "; switch(tr.result) { case test_result::fail: os << "assertion failed" << std::endl; break; case test_result::ex: case test_result::ex_ctor: os << "unexpected exception" << std::endl; if( tr.exception_typeid != "" ) { os << " exception typeid: " << tr.exception_typeid << std::endl; } break; case test_result::term: os << "would be terminated" << std::endl; break; case test_result::warn: os << "test passed, but cleanup code (destructor) raised an exception" << std::endl; break; default: break; } if( tr.message != "" ) { if( tr.result == test_result::fail ) { os << " failed assertion: \"" << tr.message << "\"" << std::endl; } else { os << " message: \"" << tr.message << "\"" << std::endl; } } ++i; } } os << std::endl; os << "tests summary:"; if( terminations_count > 0 ) os << " terminations:" << terminations_count; if( exceptions_count > 0 ) os << " exceptions:" << exceptions_count; if( failures_count > 0 ) os << " failures:" << failures_count; if( warnings_count > 0 ) os << " warnings:" << warnings_count; os << " ok:" << ok_count; os << std::endl; } bool all_ok() const { return not_passed.size() == 0; } private: void init() { ok_count = 0; exceptions_count = 0; failures_count = 0; terminations_count = 0; warnings_count = 0; not_passed.clear(); } }; }; #endif libwibble-1.1/wibble/tests/tut_restartable.h0000644000175000017500000001775211075402065020625 0ustar enricoenrico#ifndef TUT_RESTARTABLE_H_GUARD #define TUT_RESTARTABLE_H_GUARD #include #include #include /** * Template Unit Tests Framework for C++. * http://tut.dozen.ru * * Optional restartable wrapper for test_runner. * Allows to restart test runs finished due to * abnormal test application termination (such as * segmentation fault or math error). * * @author dozen, tut@dozen.ru */ namespace tut { namespace util { /** * Escapes non-alphabetical characters in string. */ std::string escape(const std::string& orig) { std::string rc; std::string::const_iterator i,e; i = orig.begin(); e = orig.end(); while( i != e ) { if( (*i >= 'a' && *i <= 'z') || (*i >= 'A' && *i <= 'Z') || (*i >= '0' && *i <= '9') ) { rc += *i; } else { rc += '\\'; rc += ('a'+(((unsigned int)*i)>>4)); rc += ('a'+(((unsigned int)*i)&0xF)); } ++i; } return rc; } /** * Un-escapes string. */ std::string unescape(const std::string& orig) { std::string rc; std::string::const_iterator i,e; i = orig.begin(); e = orig.end(); while( i != e ) { if( *i != '\\' ) { rc += *i; } else { ++i; if( i == e ) throw std::invalid_argument("unexpected end of string"); unsigned int c1 = *i; ++i; if( i == e ) throw std::invalid_argument("unexpected end of string"); unsigned int c2 = *i; rc += (((c1-'a')<<4) + (c2-'a')); } ++i; } return rc; } /** * Serialize test_result avoiding interfering with operator <<. */ void serialize(std::ostream& os,const tut::test_result& tr) { os << escape(tr.group) << std::endl; os << tr.test << ' '; switch(tr.result) { case test_result::ok: os << 0; break; case test_result::fail: os << 1; break; case test_result::ex: os << 2; break; case test_result::warn: os << 3; break; case test_result::term: os << 4; break; default: throw std::logic_error("operator << : bad result_type"); } os << ' ' << escape(tr.message) << std::endl; } /** * deserialization for test_result */ void deserialize(std::istream& is,tut::test_result& tr) { std::getline(is,tr.group); if( is.eof() ) throw tut::no_more_tests(); tr.group = unescape(tr.group); tr.test = -1; is >> tr.test; if( tr.test < 0 ) throw std::logic_error("operator >> : bad test number"); int n = -1; is >> n; switch(n) { case 0: tr.result = test_result::ok; break; case 1: tr.result = test_result::fail; break; case 2: tr.result = test_result::ex; break; case 3: tr.result = test_result::warn; break; case 4: tr.result = test_result::term; break; default: throw std::logic_error("operator >> : bad result_type"); } is.ignore(1); // space std::getline(is,tr.message); tr.message = unescape(tr.message); if( !is.good() ) throw std::logic_error("malformed test result"); } }; /** * Restartable test runner wrapper. */ class restartable_wrapper { test_runner& runner_; callback* callback_; std::string dir_; std::string log_; // log file: last test being executed std::string jrn_; // journal file: results of all executed tests public: /** * Default constructor. * @param dir Directory where to search/put log and journal files */ restartable_wrapper(const std::string& dir = ".") : runner_(runner.get()), callback_(0), dir_(dir) { // dozen: it works, but it would be better to use system path separator jrn_ = dir_+'/'+"journal.tut"; log_ = dir_+'/'+"log.tut"; } /** * Stores another group for getting by name. */ void register_group(const std::string& name,group_base* gr) { runner_.register_group(name,gr); } /** * Stores callback object. */ void set_callback(callback* cb) { callback_ = cb; } /** * Returns callback object. */ callback& get_callback() const { return runner_.get_callback(); } /** * Returns list of known test groups. */ groupnames list_groups() const { return runner_.list_groups(); } /** * Runs all tests in all groups. */ void run_tests() const { // where last run was failed std::string fail_group; int fail_test; read_log_(fail_group,fail_test); bool fail_group_reached = (fail_group == ""); // iterate over groups tut::groupnames gn = list_groups(); tut::groupnames::const_iterator gni,gne; gni = gn.begin(); gne = gn.end(); while( gni != gne ) { // skip all groups before one that failed if( !fail_group_reached ) { if( *gni != fail_group ) { ++gni; continue; } fail_group_reached = true; } // first or restarted run int test = (*gni == fail_group && fail_test>=0)? fail_test+1:1; while(true) { // last executed test pos register_execution_(*gni,test); try { tut::test_result tr = runner_.run_test(*gni,test); register_test_(tr); } catch( const tut::beyond_last_test& ex ) { break; } catch( const tut::no_such_test& ex ) { // it's ok } ++test; } ++gni; } // show final results to user invoke_callback_(); // truncate files as mark of successful finish truncate_(); } private: /** * Shows results from journal file. */ void invoke_callback_() const { runner_.set_callback(callback_); runner_.get_callback().run_started(); std::string current_group; std::ifstream ijournal(jrn_.c_str()); while( ijournal.good() ) { // read next test result try { tut::test_result tr; util::deserialize(ijournal,tr); runner_.get_callback().test_completed(tr); } catch( const no_more_tests& ) { break; } } runner_.get_callback().run_completed(); } /** * Register test into journal. */ void register_test_(const test_result& tr) const { std::ofstream ojournal(jrn_.c_str(),std::ios::app); util::serialize(ojournal,tr); ojournal << std::flush; if( !ojournal.good() ) throw std::runtime_error("unable to register test result in file "+jrn_); } /** * Mark the fact test going to be executed */ void register_execution_(const std::string& grp,int test) const { // last executed test pos std::ofstream olog(log_.c_str()); olog << util::escape(grp) << std::endl << test << std::endl << std::flush; if( !olog.good() ) throw std::runtime_error("unable to register execution in file "+log_); } /** * Truncate tests. */ void truncate_() const { std::ofstream olog(log_.c_str()); std::ofstream ojournal(jrn_.c_str()); } /** * Read log file */ void read_log_(std::string& fail_group,int& fail_test) const { // read failure point, if any std::ifstream ilog(log_.c_str()); std::getline(ilog,fail_group); fail_group = util::unescape(fail_group); ilog >> fail_test; if( !ilog.good() ) { fail_group = ""; fail_test = -1; truncate_(); } else { // test was terminated... tut::test_result tr(fail_group,fail_test,tut::test_result::term); register_test_(tr); } } }; } #endif libwibble-1.1/wibble/tests.h0000644000175000017500000003741612231005104015404 0ustar enricoenrico#ifndef WIBBLE_TESTS_H #define WIBBLE_TESTS_H /** * @file test-utils.h * @author Enrico Zini , Peter Rockai (mornfall) * @brief Utility functions for the unit tests * * Copyright (C) 2006--2007 Peter Rockai (mornfall) * Copyright (C) 2003--2013 Enrico Zini */ #include #include #include #include namespace wibble { namespace tests { struct Location; struct LocationInfo; } } /* * These global arguments will be shadowed by local variables in functions that * implement tests. * * They are here to act as default root nodes to fulfill method signatures when * tests are called from outside other tests. */ extern const wibble::tests::Location wibble_test_location; extern const wibble::tests::LocationInfo wibble_test_location_info; #define TESTGRP(name) \ typedef test_group tg; \ typedef tg::object to; \ tg name ## _tg (#name); namespace wibble { namespace tests { #define WIBBLE_TESTS_ALWAYS_THROWS __attribute__ ((noreturn)) class Location { const Location* parent; const wibble::tests::LocationInfo* info; const char* file; int line; const char* args; Location(const Location* parent, const wibble::tests::LocationInfo& info, const char* file, int line, const char* args); public: Location(); // legacy Location(const char* file, int line, const char* args); // legacy Location(const Location& parent, const char* file, int line, const char* args); Location nest(const wibble::tests::LocationInfo& info, const char* file, int line, const char* args=0) const; std::string locstr() const; std::string msg(const std::string m) const; void fail_test(const std::string& msg) const WIBBLE_TESTS_ALWAYS_THROWS; void fail_test(const wibble::tests::LocationInfo& info, const char* file, int line, const char* args, const std::string& msg) const WIBBLE_TESTS_ALWAYS_THROWS; void backtrace(std::ostream& out) const; }; struct LocationInfo : public std::stringstream { /** * Clear the stringstream and return self. * * Example usage: * * test_function(...) * { * WIBBLE_TEST_INFO(info); * for (unsigned i = 0; i < 10; ++i) * { * info() << "Iteration #" << i; * ... * } * } */ std::ostream& operator()(); LocationInfo() {} }; #define WIBBLE_TEST_LOCPRM wibble::tests::Location wibble_test_location /// Use this to declare a local variable with the given name that will be /// picked up by tests as extra local info #define WIBBLE_TEST_INFO(name) \ wibble::tests::LocationInfo wibble_test_location_info; \ wibble::tests::LocationInfo& name = wibble_test_location_info #define ensure(x) wibble::tests::impl_ensure(wibble::tests::Location(__FILE__, __LINE__, #x), (x)) #define inner_ensure(x) wibble::tests::impl_ensure(wibble::tests::Location(loc, __FILE__, __LINE__, #x), (x)) void impl_ensure(const Location& loc, bool res); #define ensure_equals(x, y) wibble::tests::impl_ensure_equals(wibble::tests::Location(__FILE__, __LINE__, #x " == " #y), (x), (y)) #define inner_ensure_equals(x, y) wibble::tests::impl_ensure_equals(wibble::tests::Location(loc, __FILE__, __LINE__, #x " == " #y), (x), (y)) template void impl_ensure_equals(const Location& loc, const Actual& actual, const Expected& expected) { if( expected != actual ) { std::stringstream ss; ss << "expected '" << expected << "' actual '" << actual << "'"; loc.fail_test(ss.str()); } } #define ensure_similar(x, y, prec) wibble::tests::impl_ensure_similar(wibble::tests::Location(__FILE__, __LINE__, #x " == " #y), (x), (y), (prec)) #define inner_ensure_similar(x, y, prec) wibble::tests::impl_ensure_similar(wibble::tests::Location(loc, __FILE__, __LINE__, #x " == " #y), (x), (y), (prec)) template void impl_ensure_similar(const Location& loc, const Actual& actual, const Expected& expected, const Precision& precision) { if( actual < expected - precision || expected + precision < actual ) { std::stringstream ss; ss << "expected '" << expected << "' actual '" << actual << "'"; loc.fail_test(ss.str()); } } #define ensure_contains(x, y) wibble::tests::impl_ensure_contains(wibble::tests::Location(__FILE__, __LINE__, #x " == " #y), (x), (y)) #define inner_ensure_contains(x, y) wibblwibblempl_ensure_contains(wibble::tests::Location(loc, __FILE__, __LINE__, #x " == " #y), (x), (y)) void impl_ensure_contains(const wibble::tests::Location& loc, const std::string& haystack, const std::string& needle); #define ensure_not_contains(x, y) wibble::tests::impl_ensure_not_contains(wibble::tests::Location(__FILE__, __LINE__, #x " == " #y), (x), (y)) #define inner_ensure_not_contains(x, y) wibble::tests::impl_ensure_not_contains(wibble::tests::Location(loc, __FILE__, __LINE__, #x " == " #y), (x), (y)) void impl_ensure_not_contains(const wibble::tests::Location& loc, const std::string& haystack, const std::string& needle); template struct TestBool { const A& actual; bool inverted; TestBool(const A& actual, bool inverted=false) : actual(actual), inverted(inverted) {} TestBool operator!() { return TestBool(actual, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual) return; wibble_test_location.fail_test("actual value is false"); } else { if (!actual) return; wibble_test_location.fail_test("actual value is true"); } } }; template struct TestEquals { A actual; E expected; bool inverted; TestEquals(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestEquals operator!() { return TestEquals(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual == expected) return; std::stringstream ss; ss << "value '" << actual << "' is different than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (actual != expected) return; std::stringstream ss; ss << "value '" << actual << "' is not different than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; template struct TestIsLt { A actual; E expected; bool inverted; TestIsLt(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestIsLt operator!() { return TestIsLt(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual < expected) return; std::stringstream ss; ss << "value '" << actual << "' is not less than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!(actual < expected)) return; std::stringstream ss; ss << "value '" << actual << "' is less than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; template struct TestIsLte { A actual; E expected; bool inverted; TestIsLte(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestIsLte operator!() { return TestIsLte(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual <= expected) return; std::stringstream ss; ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!(actual <= expected)) return; std::stringstream ss; ss << "value '" << actual << "' is less than or equals to the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; template struct TestIsGt { A actual; E expected; bool inverted; TestIsGt(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestIsGt operator!() { return TestIsGt(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual > expected) return; std::stringstream ss; ss << "value '" << actual << "' is not greater than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!(actual > expected)) return; std::stringstream ss; ss << "value '" << actual << "' is greater than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; template struct TestIsGte { A actual; E expected; bool inverted; TestIsGte(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestIsGte operator!() { return TestIsGte(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual >= expected) return; std::stringstream ss; ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!(actual >= expected)) return; std::stringstream ss; ss << "value '" << actual << "' is greater than or equals to the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; struct TestStartsWith { std::string actual; std::string expected; bool inverted; TestStartsWith(const std::string& actual, const std::string& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestStartsWith operator!() { return TestStartsWith(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; struct TestEndsWith { std::string actual; std::string expected; bool inverted; TestEndsWith(const std::string& actual, const std::string& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestEndsWith operator!() { return TestEndsWith(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; struct TestContains { std::string actual; std::string expected; bool inverted; TestContains(const std::string& actual, const std::string& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestContains operator!() { return TestContains(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; struct TestRegexp { std::string actual; std::string regexp; bool inverted; TestRegexp(const std::string& actual, const std::string& regexp, bool inverted=false) : actual(actual), regexp(regexp), inverted(inverted) {} TestRegexp operator!() { return TestRegexp(actual, regexp, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; struct TestFileExists { std::string pathname; bool inverted; TestFileExists(const std::string& pathname, bool inverted=false) : pathname(pathname), inverted(inverted) {} TestFileExists operator!() { return TestFileExists(pathname, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; template struct Actual { A actual; Actual(const A& actual) : actual(actual) {} ~Actual() {} template TestEquals operator==(const E& expected) const { return TestEquals(actual, expected); } template TestEquals operator!=(const E& expected) const { return !TestEquals(actual, expected); } template TestIsLt operator<(const E& expected) const { return TestIsLt(actual, expected); } template TestIsLte operator<=(const E& expected) const { return TestIsLte(actual, expected); } template TestIsGt operator>(const E& expected) const { return TestIsGt(actual, expected); } template TestIsGte operator>=(const E& expected) const { return TestIsGte(actual, expected); } TestBool istrue() const { return TestBool(actual); } TestBool isfalse() const { return TestBool(actual, true); } }; struct ActualString : public Actual { ActualString(const std::string& s) : Actual(s) {} TestEquals operator==(const std::string& expected) const { return TestEquals(actual, expected); } TestEquals operator!=(const std::string& expected) const { return !TestEquals(actual, expected); } TestIsLt operator<(const std::string& expected) const { return TestIsLt(actual, expected); } TestIsLte operator<=(const std::string& expected) const { return TestIsLte(actual, expected); } TestIsGt operator>(const std::string& expected) const { return TestIsGt(actual, expected); } TestIsGte operator>=(const std::string& expected) const { return TestIsGte(actual, expected); } TestStartsWith startswith(const std::string& expected) const { return TestStartsWith(actual, expected); } TestEndsWith endswith(const std::string& expected) const { return TestEndsWith(actual, expected); } TestContains contains(const std::string& expected) const { return TestContains(actual, expected); } TestRegexp matches(const std::string& regexp) const { return TestRegexp(actual, regexp); } TestFileExists fileexists() const { return TestFileExists(actual); } }; template inline Actual actual(const A& actual) { return Actual(actual); } inline ActualString actual(const std::string& actual) { return ActualString(actual); } inline ActualString actual(const char* actual) { return ActualString(actual ? actual : ""); } inline ActualString actual(char* actual) { return ActualString(actual ? actual : ""); } /* template void _wassert(WIBBLE_TEST_LOCPRM, T& a, P& op) { op.invoke(wibble_test_location, a); } */ template static inline void _wassert(WIBBLE_TEST_LOCPRM, const T& expr) { expr.check(wibble_test_location); } #define wibble_test_runner(loc, func, ...) \ do { try { \ func(loc, ##__VA_ARGS__); \ } catch (tut::failure) { \ throw; \ } catch (std::exception& e) { \ loc.fail_test(e.what()); \ } } while(0) #define wrunchecked(func) \ do { try { \ func; \ } catch (tut::failure) { \ throw; \ } catch (std::exception& e) { \ wibble_test_location.fail_test(wibble_test_location_info, __FILE__, __LINE__, #func, e.what()); \ } } while(0) // function test, just runs the function without mangling its name #define wruntest(test, ...) wibble_test_runner(wibble_test_location.nest(wibble_test_location_info, __FILE__, __LINE__, "function: " #test "(" #__VA_ARGS__ ")"), test, ##__VA_ARGS__) #define wassert(...) wibble_test_runner(wibble_test_location.nest(wibble_test_location_info, __FILE__, __LINE__, #__VA_ARGS__), _wassert, ##__VA_ARGS__) } } #endif libwibble-1.1/wibble/regexp.h0000644000175000017500000001277311075402001015535 0ustar enricoenrico#ifndef WIBBLE_REGEXP_H #define WIBBLE_REGEXP_H /* * OO wrapper for regular expression functions * * Copyright (C) 2003--2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace wibble { namespace exception { ////// wibble::exception::Regexp class Regexp : public wibble::exception::Generic { protected: int m_code; std::string m_message; public: Regexp(const regex_t& re, int code, const std::string& context) throw (); ~Regexp() throw () {} /// Get the regexp error code associated to the exception virtual int code() const throw () { return m_code; } virtual const char* type() const throw () { return "Regexp"; } virtual std::string desc() const throw () { return m_message; } }; } class Regexp { protected: regex_t re; regmatch_t* pmatch; int nmatch; std::string lastMatch; public: /* Note that match_count is required to be >1 to enable sub-regexp capture. The maximum *INCLUDES* the whole-regexp match (indexed 0). [TODO we may want to fix this to be more friendly?] */ Regexp(const std::string& expr, int match_count = 0, int flags = 0) throw (wibble::exception::Regexp); ~Regexp() throw (); bool match(const std::string& str, int flags = 0) throw (wibble::exception::Regexp); /* Indexing is from 1 for capture matches, like perl's $0, $1... 0 is whole-regexp match, not a capture. TODO the range is miscalculated (an off-by-one, wrt. the counterintuitive match counting). */ std::string operator[](int idx) throw (wibble::exception::OutOfRange); size_t matchStart(int idx) throw (wibble::exception::OutOfRange); size_t matchEnd(int idx) throw (wibble::exception::OutOfRange); size_t matchLength(int idx) throw (wibble::exception::OutOfRange); }; class ERegexp : public Regexp { public: ERegexp(const std::string& expr, int match_count = 0, int flags = 0) throw (wibble::exception::Regexp) : Regexp(expr, match_count, flags | REG_EXTENDED) {} }; class Tokenizer { const std::string& str; wibble::Regexp re; public: class const_iterator { Tokenizer& tok; size_t beg, end; public: typedef std::string value_type; typedef ptrdiff_t difference_type; typedef value_type *pointer; typedef value_type &reference; typedef std::forward_iterator_tag iterator_category; const_iterator(Tokenizer& tok) : tok(tok), beg(0), end(0) { operator++(); } const_iterator(Tokenizer& tok, bool) : tok(tok), beg(tok.str.size()), end(tok.str.size()) {} const_iterator& operator++(); std::string operator*() const { return tok.str.substr(beg, end-beg); } bool operator==(const const_iterator& ti) const { return beg == ti.beg && end == ti.end; } bool operator!=(const const_iterator& ti) const { return beg != ti.beg || end != ti.end; } }; Tokenizer(const std::string& str, const std::string& re, int flags) : str(str), re(re, 1, flags) {} const_iterator begin() { return const_iterator(*this); } const_iterator end() { return const_iterator(*this, false); } }; /** * Split a string using a regular expression to match the token separators. * * This does a similar work to the split functions of perl, python and ruby. * * Example code: * \code * utils::Splitter splitter("[ \t]*,[ \t]*", REG_EXTENDED); * vector split; * std::copy(splitter.begin(myString), splitter.end(), back_inserter(split)); * \endcode * */ class Splitter { wibble::Regexp re; public: /** * Warning: the various iterators reuse the Regexps and therefore only one * iteration of a Splitter can be done at a given time. */ // TODO: add iterator_traits class const_iterator { wibble::Regexp& re; std::string cur; std::string next; public: typedef std::string value_type; typedef ptrdiff_t difference_type; typedef value_type *pointer; typedef value_type &reference; typedef std::forward_iterator_tag iterator_category; const_iterator(wibble::Regexp& re, const std::string& str) : re(re), next(str) { ++*this; } const_iterator(wibble::Regexp& re) : re(re) {} const_iterator& operator++(); const std::string& operator*() const { return cur; } const std::string* operator->() const { return &cur; } bool operator==(const const_iterator& ti) const { return cur == ti.cur && next == ti.next; } bool operator!=(const const_iterator& ti) const { return cur != ti.cur || next != ti.next; } }; /** * Create a splitter that uses the given regular expression to find tokens. */ Splitter(const std::string& re, int flags) : re(re, 1, flags) {} /** * Split the string and iterate the resulting tokens */ const_iterator begin(const std::string& str) { return const_iterator(re, str); } const_iterator end() { return const_iterator(re); } }; } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/exception.h0000644000175000017500000003212412231005104016227 0ustar enricoenrico// -*- C++ -*- #ifndef WIBBLE_EXCEPTION_H #define WIBBLE_EXCEPTION_H /* * Generic base exception hierarchy * * Copyright (C) 2003,2004,2005,2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include // for assert #include #include #include #include #include #include /*! \file * This file provides the root of the exception hierarchy. The goal of this * hierarchy is to provide the most possible information on what caused the * exception at the least possible cost for the programmer. * * Every exception is the descendent of Exception that, in turn, extends the * std::exception class of the STL. * * Further descendents of Exception add functionality and automatisms to error * message generation: * * - ContextException extends Exception to provide informations on the context * in which the exception was raised * - ConsistencyCheckException extends ContextException to be a base class for * all exception raised on failure of a consistency check; * IndexOutOfRangeException is one example of such exceptions, to be used * when checking that a value (such as an index) fails betweed two given * bounds. * - SystemException extends ContextException to carry informations about * error conditions reported by external services, like the Operating * System, a database interface or an interface used to communicate to some * network server. In particular, it provides the logic needed to make use * of the error descriptions provided by the external services, as * strerror(3) does for the operating system's error conditions. * FileException is an example of such exceptions, to be used to report * error conditions happened during File I/O. * * Example exception raising: * \code * void MyFile::open(const char* fname) throw (FileException) * { * if ((fd = open(fname, O_RDONLY)) == -1) * throw FileException(errno, stringf::fmt("opening %s read-only", fname)); * } * \endcode * * Example exception catching: * \code * try { * myfile.open("/tmp/foo"); * } catch (FileException& e) { * fprintf(stderr, "%.*s: aborting.\n", PFSTR(e.toString())); * exit(1); * } * \endcode */ namespace wibble { namespace exception { /// Basic unexpected handler /** * This is an unexpected handler provided by the library. It prints to stderr * a stack trace and all possible available informations about the escaped * exception. * * To have the function names in the stack trace, the executables need to be * linked using the -rdynamic flag. */ void DefaultUnexpected(); /// Install an unexpected handler for the duration of its scope. Install /// DefaultUnexpected if no handler is provided. class InstallUnexpected { protected: void (*old)(); public: InstallUnexpected(void (*func)() = DefaultUnexpected); ~InstallUnexpected(); }; // TODO this needs to be made useful with threading as well struct AddContext { static std::vector< std::string > *s_context; static std::vector< std::string > &context() { if ( s_context ) return *s_context; s_context = new std::vector< std::string >(); return *s_context; } template< typename O > static void copyContext( O out ) { std::copy( context().begin(), context().end(), out ); } std::string m_context; AddContext( std::string s ) : m_context( s ) { context().push_back( s ); } ~AddContext() { assert_eq( context().back(), m_context ); context().pop_back(); } }; /// Store context information for an exception class Context { protected: std::vector m_context; public: Context() throw () { AddContext::copyContext( std::back_inserter( m_context ) ); } Context(const std::string& context) throw () { AddContext::copyContext( std::back_inserter( m_context ) ); addContext(context); } void addContext(const std::string& c) throw () { m_context.push_back(c); } std::string formatContext() const throw () { if (m_context.empty()) return "no context information available"; std::stringstream res; std::copy( m_context.begin(), m_context.end(), std::ostream_iterator< std::string >( res, ", \n " ) ); std::string r = res.str(); return std::string( r, 0, r.length() - 7 ); } const std::vector& context() const throw () { return m_context; } }; /// Base class for all exceptions /** * This is the base class for all exceptions used in the system. It provides * an interface to get a (hopefully detailed) textual description of the * exception, and a tag describing the type of exception. Further * functionality will be provided by subclassers */ class Generic : public std::exception, public Context { protected: mutable std::string m_formatted; public: Generic() throw () {} Generic(const std::string& context) throw () : Context(context) {} virtual ~Generic() throw () {} /// Get a string tag identifying the exception type virtual const char* type() const throw () { return "Generic"; } /// Get a string describing what happened that threw the exception virtual std::string desc() const throw () { return "an unspecified problem happened; if you see this message, please report a bug to the maintainer"; } /** * Format in a string all available information about the exception. * * The formatted version is cached because this function is used to * implement the default what() method, which needs to return a stable * c_str() pointer. */ virtual const std::string& fullInfo() const throw () { if (m_formatted.empty()) m_formatted = desc() + ". Context:\n " + formatContext(); return m_formatted; } virtual const char* what() const throw () { return fullInfo().c_str(); } }; /// Exception thrown when some long event is interrupted by an external event /// (like a system call being interrupted by a signal) /** * It is a direct child of ContextException, and has the very same semantics. * * \warning Any function throwing InterruptedException must allow to be called * again with the same parameters to retry the operation */ class Interrupted : public Generic { public: Interrupted() throw () {} Interrupted(const std::string& context) throw () : Generic(context) {} virtual const char* type() const throw () { return "Interrupted"; } }; /// Exception thrown when some system wait is interrupted by a signal /** * It is a direct child of InterruptedException, and has the very same * semantics. * * \warning Any function throwing WaitInterruptedException must allow to be * called again with the same parameters to retry the operation */ class WaitInterrupted : public Interrupted { public: WaitInterrupted(const std::string& context) throw () : Interrupted(context) {} virtual const char* type() const throw () { return "WaitInterrupted"; } }; /// Exception thrown when some consistency check fails /** * It is a direct child of ContextException, and has the very same semantics. */ class Consistency : public Generic { std::string m_error; public: Consistency(const std::string& context, const std::string& error = std::string()) throw () : Generic(context), m_error(error) {} ~Consistency() throw () {} virtual const char* type() const throw () { return "Consistency"; } virtual std::string desc() const throw () { if (m_error.empty()) return "consistency check failed"; return m_error; } }; struct BadCast : public Consistency { BadCast( const std::string &context ) throw() : Consistency( context ) {} ~BadCast() throw() {} virtual std::string typeinfo() const throw() { return "unknown types"; } virtual std::string desc() const throw() { return std::string( "bad cast: " ) + typeinfo(); } }; #ifndef NO_RTTI template< typename From, typename To > struct BadCastExt : public BadCast { BadCastExt( const std::string &error = std::string() ) throw() : BadCast( error ) {} ~BadCastExt() throw() {} virtual std::string typeinfo() const throw() { return std::string( "from " ) + typeid( From ).name() + " to " + typeid( To ).name(); } }; #endif /** * Exception thrown when some value is out of range * * Usage: * \code * if (age < 200) * throw OutOfRange("age", "ensuring that no mere mortal is using the system"); * \endcode */ class OutOfRange : public Consistency { protected: std::string m_var_desc; public: OutOfRange(const std::string& var_desc, const std::string& context) throw () : Consistency(context), m_var_desc(var_desc) {} ~OutOfRange() throw () {} virtual const char* type() const throw () { return "OutOfRange"; } /// Get a short description of the variable that has been checked virtual std::string var_desc() const throw () { return m_var_desc; } virtual std::string desc() const throw () { return m_var_desc + " out of range"; } }; /// Exception thrown when index checking fails /** * This exception is to be thrown when an index checking fails, providing * informations on the acceptable index range and on the offending value. * The context should be a description of the index, possibly preceeded by a * description of in what context did the index checking happen. * * Example: * \code * if (age < 18) * throw IndexOutOfRangeException("age", age, 18, 0, "obtaining driver license"); * * if (i < 0 || i > 100) * throw IndexOutOfRangeException("percentage of items sold", i, 0, 100, "checking input consistency"); * \endcode */ template class ValOutOfRange : public OutOfRange { protected: C m_val; C m_inf; C m_sup; public: /** Construct the exception; minBound and maxBound are the bounds of the * valid index range (inclusive). */ ValOutOfRange(const std::string& var_desc, C val, C inf, C sup, const std::string& context) throw () : OutOfRange(var_desc, context), m_val(val), m_inf(inf), m_sup(sup) {} ///@name Methods used to get informations about the index and its bounds //@{ /// Get the value that caused the index to go out-of-bounds virtual C val() const throw () { return m_val; } /// Get the minimum allowed value for this index virtual C inf() const throw () { return m_inf; } /// Get the maximum allowed value for this index virtual C sup() const throw () { return m_sup; } //@} virtual const char* type() const throw () { return "ValOutOfRange<>"; } virtual std::string desc() const throw (); }; /// Base class for system exceptions /** * This is the base class for exceptions that depend on system events, like * exceptions on file or network I/O, on database access and so on. * SystemExceptions introduces the keeping of an error code with an associated * string description, and by defaults provides the textual description for * Unix errno error codes. * The exception context should be phrased like "doing X". * * Example: * \code * const char* fname = "foo.bar"; * if ((fd = open(fname, O_RDONLY)) == -1) * // Should not throw SystemException, but a more specialized derived * // class like FileException * throw SystemException(errno, stringf::fmt("opening %s read-only", fname)); * \endcode */ class System : public Generic { protected: int m_errno; public: System(const std::string& context) throw (); System(int code, const std::string& context) throw (); virtual const char* type() const throw () { return "System"; } /// Get the system error code associated to the exception virtual int code() const throw () { return m_errno; } /// Get the description of the error code virtual std::string desc() const throw (); }; /// Base class for exceptions for file I/O /** * It is a direct child of SystemException, and has the very same semantics. * Like in SystemException, the error code description provided is a * description for errno values. */ class File : public System { protected: std::string m_name; public: File(const std::string& name, const std::string& context) throw () : System(context), m_name(name) {} ~File() throw () {} virtual const char* type() const throw () { return "File"; } virtual std::string desc() const throw () { return m_name + ": " + System::desc(); } }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/Makefile.am0000644000175000017500000000661512231011077016130 0ustar enricoenricoSUBDIRS = . AM_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) if WIBBLE_STANDALONE AM_CPPFLAGS += -DWIBBLE_COMPILE_TESTSUITE -DTEST_DIR=\"`pwd`/$(top_builddir)/wibble/tests/work/\" wibbledir = $(includedir)/wibble wibblecommandlinedir = $(includedir)/wibble/commandline wibblelogdir = $(includedir)/wibble/log wibblesysdir = $(includedir)/wibble/sys wibbletestdir = $(includedir)/wibble/tests wibbletextdir = $(includedir)/wibble/text wibblegrcaldir = $(includedir)/wibble/grcal wibblestreamdir = $(includedir)/wibble/stream wibblenetdir = $(includedir)/wibble/net wibble_HEADERS = \ amorph.h \ cast.h \ consumer.h \ empty.h \ exception.h \ exception.tcc \ fallback.h \ iterator.h \ maybe.h \ mixin.h \ operators.h \ range.h \ regexp.h \ sfinae.h \ singleton.h \ string.h \ test.h \ tests.h wibblesys_HEADERS = \ sys/thread.h \ sys/process.h \ sys/netbuffer.h \ sys/macros.h \ sys/mutex.h \ sys/fs.test.h \ sys/buffer.h \ sys/mmap.h \ sys/childprocess.h \ sys/exec.h \ sys/fs.h \ sys/signal.h \ sys/lockfile.h \ sys/filelock.h wibblelog_HEADERS = \ log/stream.h \ log/null.h \ log/syslog.h \ log/ostream.h \ log/file.h \ log/filters.h wibblecommandline_HEADERS = \ commandline/core.h \ commandline/doc.h \ commandline/engine.h \ commandline/options.h \ commandline/parser.h wibbletest_HEADERS = \ tests/tut-prereq.h \ tests/tut_reporter.h \ tests/tut-wibble.h \ tests/tut.h \ tests/tut_restartable.h wibbletext_HEADERS = \ text/wordwrap.h wibblegrcal_HEADERS = \ grcal/grcal.h wibblestream_HEADERS = \ stream/posix.h wibblenet_HEADERS = \ net/mime.h \ net/server.h \ net/http.h lib_LTLIBRARIES = libwibble.la else noinst_LTLIBRARIES = libwibble.la endif libwibble_la_SOURCES = \ exception.cpp \ regexp.cpp \ string.cpp \ test.cpp \ tests.cpp \ sys/fs.cpp \ sys/lockfile.cpp \ sys/filelock.cpp \ sys/buffer.cpp \ sys/mmap.cpp \ sys/process.cpp \ sys/childprocess.cpp \ sys/mutex.cpp \ sys/exec.cpp \ sys/thread.cpp \ sys/signal.cpp \ log/file.cpp \ log/stream.cpp \ log/ostream.cpp \ log/syslog.cpp \ log/filters.cpp \ commandline/core.cpp \ commandline/options.cpp \ commandline/doc.cpp \ commandline/parser.cpp \ commandline/engine.cpp \ text/wordwrap.cpp \ grcal/grcal.cpp \ net/mime.cpp \ net/server.cpp \ net/http.cpp #libwibble_la_LIBADD = #libwibble_la_LDFLAGS = -no-undefined EXTRA_DIST = README libwibble.m4 libwibble.pc.in \ amorph.test.h \ consumer.test.h \ empty.test.h \ exception.test.h \ log.test.h \ mixin.test.h \ operators.test.h \ range.test.h \ regexp.test.h \ singleton.test.h \ string.test.h \ commandline/core.test.h \ commandline/options.test.h \ commandline/doc.test.h \ commandline/engine.test.h \ test.h \ test-main.h \ test-runner.h \ test.test.h \ sys/childprocess.test.h \ sys/netbuffer.test.h \ sys/buffer.test.h \ sys/mmap.test.h \ sys/process.test.h \ sys/thread.test.h \ sys/fs.test.h \ sys/lockfile.test.h \ sys/signal.test.h \ grcal/grcal.test.h \ stream/posix.test.h \ net/mime.test.h \ net/server.test.h \ net/http.test.h # vim:set ts=4 sw=4: libwibble-1.1/wibble/fallback.h0000644000175000017500000000120311075402052015772 0ustar enricoenrico/** -*- C++ -*- @file wibble/fallback.h @author Peter Rockai */ #include #ifndef WIBBLE_FALLBACK_H #define WIBBLE_FALLBACK_H namespace wibble { struct Error {}; template< typename T > struct Fallback { const T *value; Fallback( const T &v ) : value( &v ) {} Fallback( Error = Error() ) : value( 0 ) {} template< typename E > const T &get( const E &e ) { if ( !value ) throw e; return *value; } const T &get() { if ( !value ) throw exception::Consistency( "tried to use undefined fallback value" ); return *value; } }; } #endif libwibble-1.1/wibble/param.h0000644000175000017500000000052312231005104015327 0ustar enricoenrico// -*- C++ -*- (c) 2013 Vladimír Štill #ifndef WIBLLE_PARAM_H #define WIBLLE_PARAM_H namespace wibble { namespace param { #if __cplusplus >= 201103L /* discard any number of paramentets, taken as const references */ template< typename... X > void discard( const X&... ) { } #endif } } #endif // WIBLLE_PARAM_H libwibble-1.1/wibble/consumer.h0000644000175000017500000000632111463032575016106 0ustar enricoenrico/** -*- C++ -*- @file wibble/consumer.h @author Peter Rockai */ #include #include #include #include #ifndef WIBBLE_CONSUMER_H #define WIBBLE_CONSUMER_H namespace wibble { template< typename T > struct Consumer; template< typename T > struct ConsumerInterface { typedef T InputType; virtual void consume( const T &a ) = 0; virtual void consume( Range< T > s ) = 0; virtual ~ConsumerInterface() {} }; template< typename T, typename W > struct ConsumerMorph : Morph< ConsumerMorph< T, W >, W, ConsumerInterface< T > > { ConsumerMorph() {} ConsumerMorph( const W &w ) : Morph< ConsumerMorph, W, ConsumerInterface< T > >( w ) {} virtual void consume( const T &a ) { return this->wrapped().consume( a ); } virtual void consume( Range< T > s ) { while ( !s.empty() ) { consume( s.head() ); s = s.tail(); } } }; template< typename T, typename Self > struct ConsumerMixin : mixin::Comparable< Self > { Self &self() { return *static_cast< Self * >( this ); } const Self &self() const { return *static_cast< const Self * >( this ); } typedef std::output_iterator_tag iterator_category; typedef T ConsumedType; bool operator<=( const Self &o ) const { return this <= &o; } Consumer< T > &operator++() { return self(); } Consumer< T > &operator++(int) { return self(); } Consumer< T > &operator*() { return self(); } Consumer< T > &operator=( const T &a ) { self()->consume( a ); return self(); } }; template< typename T > struct Consumer: Amorph< Consumer< T >, ConsumerInterface< T > >, ConsumerMixin< T, Consumer< T > > { typedef Amorph< Consumer< T >, ConsumerInterface< T > > Super; typedef void value_type; typedef void difference_type; typedef void pointer; typedef void reference; Consumer( const MorphInterface< ConsumerInterface< T > > &i ) : Super( i ) {} Consumer() {} void consume( const T &a ) { return this->implementation()->consume( a ); } Consumer< T > &operator=( const T &a ) { consume( a ); return *this; } // output iterator - can't read or move }; template< typename T, typename Out > struct ConsumerFromIterator : ConsumerMixin< T, ConsumerFromIterator< T, Out > > { ConsumerFromIterator( Out out ) : m_out( out ) {} void consume( const T& a ) { *(*m_out) = a; ++(*m_out); } protected: Out m_out; }; template< typename R > Consumer< typename R::ConsumedType > consumerMorph( R r ) { return ConsumerMorph< typename R::ConsumedType , R >( r ); } // insert iterators template< typename Out > Consumer< typename Out::container_type::value_type > consumer( Out out ) { return consumerMorph( ConsumerFromIterator< typename Out::container_type::value_type, Out >( out ) ); } // containers template< typename T > typename IsType< Consumer< typename T::value_type >, typename T::iterator >::T consumer( T &c ) { return consumer( std::inserter( c, c.end() ) ); } // consumers template< typename T > Consumer< T > consumer( const ConsumerInterface< T > &t ) { return t; } } #endif libwibble-1.1/wibble/tests.cpp0000644000175000017500000001676412224046645015763 0ustar enricoenrico/* * @file test-utils.cpp * @author Enrico Zini , Peter Rockai (mornfall) * @brief Utility functions for the unit tests * * Copyright (C) 2003--2013 Enrico Zini */ #include #include #include using namespace std; using namespace wibble; const wibble::tests::Location wibble_test_location; const wibble::tests::LocationInfo wibble_test_location_info; namespace wibble { namespace tests { Location::Location() : parent(0), info(0), file(0), line(0), args(0) { // Build a special, root location } Location::Location(const Location* parent, const wibble::tests::LocationInfo& info, const char* file, int line, const char* args) : parent(parent), info(&info), file(file), line(line), args(args) { } Location::Location(const char* file, int line, const char* args) : parent(&wibble_test_location), info(&wibble_test_location_info), file(file), line(line), args(args) { } Location::Location(const Location& parent, const char* file, int line, const char* args) : parent(&parent), info(&wibble_test_location_info), file(file), line(line), args(args) { } Location Location::nest(const wibble::tests::LocationInfo& info, const char* file, int line, const char* args) const { return Location(this, info, file, line, args); } void Location::backtrace(std::ostream& out) const { if (parent) parent->backtrace(out); if (!file) return; // Root node, nothing to print out << file << ":" << line << ":" << args; if (!info->str().empty()) out << " [" << info->str() << "]"; out << endl; } std::string Location::locstr() const { std::stringstream ss; backtrace(ss); return ss.str(); } std::string Location::msg(const std::string msg) const { std::stringstream ss; ss << "Test failed at:" << endl; backtrace(ss); ss << file << ":" << line << ":error: " << msg << endl; return ss.str(); } void Location::fail_test(const std::string& msg) const { throw tut::failure(this->msg(msg)); } void Location::fail_test(const wibble::tests::LocationInfo& info, const char* file, int line, const char* args, const std::string& msg) const { Location loc = nest(info, file, line, args); loc.fail_test(msg); } std::ostream& LocationInfo::operator()() { str(std::string()); clear(); return *this; } void test_assert_re_match(WIBBLE_TEST_LOCPRM, const std::string& regexp, const std::string& actual) { ERegexp re(regexp); if (!re.match(actual)) { std::stringstream ss; ss << "'" << actual << "' does not match regexp '" << regexp << "'"; wibble_test_location.fail_test(ss.str()); } } void test_assert_startswith(WIBBLE_TEST_LOCPRM, const std::string& expected, const std::string& actual) { if (!str::startsWith(actual, expected)) { std::stringstream ss; ss << "'" << actual << "' does not start with '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } void test_assert_endswith(WIBBLE_TEST_LOCPRM, const std::string& expected, const std::string& actual) { if (!str::endsWith(actual, expected)) { std::stringstream ss; ss << "'" << actual << "' does not end with '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } void test_assert_contains(WIBBLE_TEST_LOCPRM, const std::string& expected, const std::string& actual) { if (actual.find(expected) == string::npos) { std::stringstream ss; ss << "'" << actual << "' does not contain '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } void test_assert_istrue(WIBBLE_TEST_LOCPRM, bool val) { if (!val) wibble_test_location.fail_test("result is false"); } void test_assert_file_exists(WIBBLE_TEST_LOCPRM, const std::string& fname) { if (not sys::fs::exists(fname)) { std::stringstream ss; ss << "file '" << fname << "' does not exists"; wibble_test_location.fail_test(ss.str()); } } void test_assert_not_file_exists(WIBBLE_TEST_LOCPRM, const std::string& fname) { if (sys::fs::exists(fname)) { std::stringstream ss; ss << "file '" << fname << "' does exists"; wibble_test_location.fail_test(ss.str()); } } void impl_ensure(const Location& loc, bool res) { if (!res) loc.fail_test("assertion failed"); } void impl_ensure_contains(const wibble::tests::Location& loc, const std::string& haystack, const std::string& needle) { if( haystack.find(needle) == std::string::npos ) { std::stringstream ss; ss << "'" << haystack << "' does not contain '" << needle << "'"; loc.fail_test(ss.str()); } } void impl_ensure_not_contains(const wibble::tests::Location& loc, const std::string& haystack, const std::string& needle) { if( haystack.find(needle) != std::string::npos ) { std::stringstream ss; ss << "'" << haystack << "' must not contain '" << needle << "'"; loc.fail_test(ss.str()); } } void TestStartsWith::check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (str::startsWith(actual, expected)) return; std::stringstream ss; ss << "'" << actual << "' does not start with '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!str::startsWith(actual, expected)) return; std::stringstream ss; ss << "'" << actual << "' starts with '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } void TestEndsWith::check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (str::endsWith(actual, expected)) return; std::stringstream ss; ss << "'" << actual << "' does not end with '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!str::endsWith(actual, expected)) return; std::stringstream ss; ss << "'" << actual << "' ends with '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } void TestContains::check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual.find(expected) != std::string::npos) return; std::stringstream ss; ss << "'" << actual << "' does not contain '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (actual.find(expected) == std::string::npos) return; std::stringstream ss; ss << "'" << actual << "' contains '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } void TestRegexp::check(WIBBLE_TEST_LOCPRM) const { ERegexp re(regexp); if (!inverted) { if (re.match(actual)) return; std::stringstream ss; ss << "'" << actual << "' does not match regexp '" << regexp << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!re.match(actual)) return; std::stringstream ss; ss << "'" << actual << "' matches regexp '" << regexp << "'"; wibble_test_location.fail_test(ss.str()); } } void TestFileExists::check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (sys::fs::exists(pathname)) return; std::stringstream ss; ss << "file '" << pathname << "' does not exists"; wibble_test_location.fail_test(ss.str()); } else { if (not sys::fs::exists(pathname)) return; std::stringstream ss; ss << "file '" << pathname << "' exists"; wibble_test_location.fail_test(ss.str()); } } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/list.h0000644000175000017500000002061312231005104015204 0ustar enricoenrico// -*- C++ -*- #include #include #include #include #include #ifndef WIBBLE_LIST_H #define WIBBLE_LIST_H namespace wibble { namespace list { template< typename List > struct ListIterator { typedef std::forward_iterator_tag iterator_category; typedef typename List::Type value_type; typedef ptrdiff_t difference_type; typedef value_type &pointer; typedef value_type &reference; List l; ListIterator &operator++() { l = l.tail(); return *this; } ListIterator operator++(int) { ListIterator i = *this; operator++(); return i; } typename List::Type operator*() { return l.head(); } bool operator==( const ListIterator &o ) const { return l.empty() && o.l.empty(); } bool operator!=( const ListIterator &o ) const { return !(l.empty() && o.l.empty()); } ListIterator( List _l = List() ) : l( _l ) {} }; template< typename List > struct Sorted { typedef std::vector< typename List::Type > Vec; struct SharedVec { int refs; Vec vec; SharedVec() : refs( 1 ) {} void _ref() { ++refs; } void _deref() { --refs; } }; struct SharedPtr { SharedVec *vec; SharedPtr( bool a = false ) { vec = a ? new SharedVec : 0; } SharedPtr( const SharedPtr &o ) { vec = o.vec; if ( vec ) vec->_ref(); } SharedPtr &operator=( const SharedPtr &o ) { vec = o.vec; if ( vec ) vec->_ref(); return *this; } operator bool() { return vec; } Vec &operator*() { return vec->vec; } Vec *operator->() { return &(vec->vec); } ~SharedPtr() { if ( vec ) { vec->_deref(); if ( !vec->refs ) delete vec; } } }; typedef typename List::Type Type; List m_list; mutable SharedPtr m_sorted; size_t m_pos; void sort() const { if ( m_sorted ) return; m_sorted = SharedPtr( true ); std::copy( ListIterator< List >( m_list ), ListIterator< List >(), std::back_inserter( *m_sorted ) ); std::sort( m_sorted->begin(), m_sorted->end() ); } Type head() const { sort(); return (*m_sorted)[ m_pos ]; } Sorted tail() const { sort(); Sorted s = *this; s.m_pos ++; return s; } bool empty() const { sort(); return m_pos == m_sorted->size(); } Sorted( const Sorted &o ) : m_list( o.m_list ), m_sorted( o.m_sorted ), m_pos( o.m_pos ) {} Sorted &operator=( const Sorted &o ) { m_sorted = o.m_sorted; m_list = o.m_list; m_pos = o.m_pos; return *this; } Sorted( List l = List() ) : m_list( l ), m_sorted( 0 ), m_pos( 0 ) {} }; template< typename List, typename Predicate > struct Filtered { typedef typename List::Type Type; mutable List m_list; Predicate m_pred; bool empty() const { seek(); return m_list.empty(); } Type head() const { seek(); return m_list.head(); } void seek() const { while ( !m_list.empty() && !m_pred( m_list.head() ) ) m_list = m_list.tail(); } Filtered tail() const { Filtered r = *this; r.seek(); r.m_list = r.m_list.tail(); return r; } Filtered( List l, Predicate p ) : m_list( l ), m_pred( p ) { } Filtered() {} }; template< typename List > struct Unique { typedef typename List::Type Type; mutable List m_list; bool empty() const { seek(); return m_list.empty(); } Type head() const { seek(); return m_list.head(); } void seek() const { if ( m_list.empty() ) return; if ( m_list.tail().empty() ) return; if ( m_list.head() == m_list.tail().head() ) { m_list = m_list.tail(); return seek(); } } Unique tail() const { Unique r = *this; r.seek(); r.m_list = r.m_list.tail(); return r; } Unique( List l = List() ) : m_list( l ) { } }; template< typename List > struct Take { List l; int remaining; typedef typename List::Type Type; Type head() const { return l.head(); } bool empty() const { return l.empty() || remaining == 0; } Take tail() const { Take t; t.remaining = remaining - 1; t.l = l.tail(); return t; } Take( List _l, int m ) : l( _l ), remaining( m ) {} Take() : remaining( 0 ) {} }; template< typename List, typename F > struct Map { List l; char f_space[ sizeof( F ) ]; F &f() { return *static_cast(f_space); } const F &f() const { return *static_cast(f_space); } typedef typename F::result_type Type; Type head() const { return f()( l.head() ); } Map tail() const { Map m; m.l = l.tail(); m.f() = f(); return m; } bool empty() const { return l.empty(); } Map() {} Map( const List &_l, const F &_f ) : l( _l ) { f() = _f; } }; template< typename T > struct Empty { typedef T Type; T head() const { return T(); } bool empty() const { return true; } Empty tail() const { return Empty(); } }; template< typename T > struct Singular { typedef T Type; T m_value; bool m_empty; Singular() : m_empty( true ) {} Singular( T i ) : m_value( i ), m_empty( false ) {} T head() const { return m_value; } bool empty() const { return m_empty; } Singular tail() const { return Singular(); } }; template< typename T1, typename T2 > struct Append { typedef typename T1::Type Type; T1 m_1; T2 m_2; Append() {} Append( T1 a, T2 b ) : m_1( a ), m_2( b ) {} Type head() const { if ( m_1.empty() ) return m_2.head(); return m_1.head(); } bool empty() const { return m_1.empty() && m_2.empty(); } Append tail() const { Append t = *this; if ( !t.m_1.empty() ) t.m_1 = t.m_1.tail(); else t.m_2 = t.m_2.tail(); return t; } }; template< typename X > Singular< X > singular( const X &x ) { return Singular< X >( x ); } template< typename X, typename Y > Append< X, Y > append( const X &x, const Y &y ) { return Append< X, Y >( x, y ); } template< typename List > size_t count( List l ) { size_t count = 0; while ( !l.empty() ) { l = l.tail(); ++ count; } return count; } #undef foreach // Work around Qt braindamage. template< typename List, typename F > void foreach( List l, F f ) { while ( !l.empty() ) { f( l.head() ); l = l.tail(); } } template< typename List, template< typename > class F > void foreach( List l, F< typename List::Type > f ) { while ( !l.empty() ) { f( l.head() ); l = l.tail(); } } template< typename List, typename Pred > Filtered< List, Pred > filter( List l, Pred p ) { return Filtered< List, Pred >( l, p ); } template< typename List, template< typename > class Pred > Filtered< List, Pred< List > > filter( List l, Pred< List > p ) { return Filtered< List, Pred< List > >( l, p ); } template< typename List, typename F > Map< List, F > map( const List &l, const F &f ) { return Map< List, F >( l, f ); } template< typename List > Sorted< List > sort( List l ) { return Sorted< List >( l ); } template< typename List > Unique< List > unique( List l ) { return Unique< List >( l ); } template< typename List > Take< List > take( int t, List l ) { return Take< List >( l, t ); } template< typename List > List drop( int t, List l ) { while ( t > 0 ) { l = l.tail(); -- t; } return l; } template< typename List, typename Out > void output( List l, Out it ) { std::copy( ListIterator< List >( l ), ListIterator< List >(), it ); } template< typename List > ListIterator< List > begin( List l ) { return ListIterator< List >( l ); } template< typename List > ListIterator< List > end( List ) { return ListIterator< List >(); } } } #endif libwibble-1.1/wibble/mixin.h0000644000175000017500000000272212231005104015356 0ustar enricoenrico// -*- C++ -*- (c) 2007 Peter Rockai #ifndef WIBBLE_MIXIN_H #define WIBBLE_MIXIN_H #include #include namespace wibble { namespace mixin { template< typename Self > struct Comparable { const Self &cmpSelf() const { return *static_cast< const Self * >( this ); } bool operator!=( const Self &o ) const { return not( cmpSelf() == o ); } bool operator==( const Self &o ) const { return cmpSelf() <= o && o <= cmpSelf(); } bool operator<( const Self &o ) const { return cmpSelf() <= o && cmpSelf() != o; } bool operator>( const Self &o ) const { return o <= cmpSelf() && cmpSelf() != o; } bool operator>=( const Self &o ) const { return o <= cmpSelf(); } // you implement this one in your class // bool operator<=( const Self &o ) const { return this <= &o; } }; /** * Mixin with output iterator paperwork. * * To make an output iterator, one just needs to inherit from this * template and implement Self& operator=(const WhaToAccept&) */ template< typename Self > struct OutputIterator : public std::iterator { Self& operator++() { return *static_cast(this); } Self operator++(int) { Self res = *static_cast(this); ++*this; return res; } Self& operator*() { return *static_cast(this); } }; } } #endif libwibble-1.1/wibble/test.cmake0000644000175000017500000000356312231005104016046 0ustar enricoenricomacro( wibble_add_test name ) string( REPLACE ".test.h" ".cpp" SOURCES "${ARGN}" ) set( SOURCES ";${SOURCES}" ) string( REPLACE "/" "_" SOURCES "${SOURCES}" ) string( REPLACE ":" "_" SOURCES "${SOURCES}" ) set( src_prefix "${CMAKE_CURRENT_BINARY_DIR}/${name}-generated-" ) string( REPLACE ";" ";${src_prefix}" SOURCES "${SOURCES}" ) string( REGEX REPLACE "^;" "" SOURCES "${SOURCES}" ) set( main "${src_prefix}main.cpp" ) if( NOT WIBBLE_TEST_GENRUNNER ) include( FindPerl ) set( generator "${PERL_EXECUTABLE}" "${wibble_SOURCE_DIR}/test-genrunner.pl" ) set( generator_dep"${wibble_SOURCE_DIR}/test-genrunner.pl" ) else( NOT WIBBLE_TEST_GENRUNNER ) set( generator ${WIBBLE_TEST_GENRUNNER} ) set( generator_dep ${generator} ) endif( NOT WIBBLE_TEST_GENRUNNER ) set( HDRS "${ARGN}" ) list( LENGTH SOURCES SOURCE_N ) math( EXPR SOURCE_N "${SOURCE_N} - 1" ) foreach( i RANGE ${SOURCE_N} ) LIST( GET HDRS ${i} HDR ) LIST( GET SOURCES ${i} SRC ) add_custom_command( OUTPUT ${SRC} DEPENDS ${generator_dep} ${HDR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${generator} header ${HDR} > ${SRC} ) endforeach( i ) add_custom_command( OUTPUT ${main} DEPENDS ${generator_dep} ${ARGN} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${generator} main ${ARGN} > ${main} ) set_source_files_properties( ${SOURCES} ${main} PROPERTIES GENERATED ON ) add_executable( ${name} ${SOURCES} ${main} ) endmacro( wibble_add_test ) # TODO the LD_LIBRARY_PATH may need to be set more elaborately macro( wibble_check_target tgt ) add_custom_target( unit_${tgt} COMMAND sh -c "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR} ${WIBBLE_WRAP_TESTS} ${CMAKE_CURRENT_BINARY_DIR}/${tgt}" VERBATIM DEPENDS ${ARGV} ) add_dependencies( unit unit_${tgt} ) endmacro( wibble_check_target ) libwibble-1.1/wibble/stream/0000755000175000017500000000000012231005104015351 5ustar enricoenricolibwibble-1.1/wibble/stream/posix.test.h0000644000175000017500000000114212231005104017640 0ustar enricoenrico/* -*- C++ -*- (c) 2008 Petr Rockai (c) 2008 Enrico Zini */ #include #include #include #include #include namespace { using namespace std; using namespace wibble; using namespace wibble::stream; struct TestStreamPosix { Test basicMatch() { #ifdef POSIX // no /dev/null otherwise int fd = open("/dev/null", O_WRONLY); assert(fd != -1); PosixBuf buf(fd); ostream os(&buf); os << "Foo"; os << "Bar"; os << endl; #endif } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/stream/posix.h0000644000175000017500000000432511225340707016705 0ustar enricoenrico#ifndef WIBBLE_STREAM_POSIX_H #define WIBBLE_STREAM_POSIX_H #include #include #include #include // http://www.icce.rug.nl/documents/cplusplus/cplusplus21.html#CONCRETE // 21.2.1: Classes for output operations namespace wibble { namespace stream { class PosixBuf : public std::streambuf { private: // The output buffer char* m_buf; size_t m_buf_size; int m_fd; // Disallow copy PosixBuf(const PosixBuf&); PosixBuf& operator=(const PosixBuf&); public: // PosixBuf functions PosixBuf() : m_buf(0), m_buf_size(0), m_fd(-1) {} PosixBuf(int fd, size_t bufsize = 4096) : m_buf(0), m_buf_size(0), m_fd(-1) { attach(fd, bufsize); } ~PosixBuf() { if (m_buf) { sync(); delete[] m_buf; } if (m_fd != -1) { ::close(m_fd); } } /** * Attach the stream to a file descriptor, using the given stream size. * * Management of the file descriptor will be taken over by the PosixBuf, * and the file descriptor will be closed with PosixBuf goes out of scope. */ void attach(int fd, size_t bufsize = 4096) { m_buf = new char[1024]; if (!m_buf) throw wibble::exception::Consistency("allocating 1024 bytes for posix stream buffer", "allocation failed"); m_fd = fd; m_buf_size = 1024; setp(m_buf, m_buf + m_buf_size); } /** * Sync the PosixBuf and detach it from the file descriptor. PosixBuf will * not touch the file descriptor anymore, and it is the responsibility of * the caller to close it. * * @returns The file descriptor */ int detach() { sync(); int res = m_fd; delete[] m_buf; m_buf = 0; m_buf_size = 0; m_fd = -1; // TODO: setp or anything like that, to tell the streambuf that there's // no buffer anymore? setp(0, 0); return res; } /// Access the underlying file descriptor int fd() const { return m_fd; } // Functions from strbuf int overflow(int c) { sync(); if (c != EOF) { *pptr() = c; pbump(1); } return c; } int sync() { if (pptr() > pbase()) { int amount = pptr() - pbase(); int res = ::write(m_fd, m_buf, amount); if (res != amount) throw wibble::exception::System("writing to file"); setp(m_buf, m_buf + m_buf_size); } return 0; } }; } } #endif libwibble-1.1/wibble/test-genrunner.pl0000644000175000017500000000531112231005104017373 0ustar enricoenrico#!/usr/bin/perl my $set, %tests, %prefix, %filename; my $mode = $ARGV[0]; shift @ARGV; sub process() { $file = shift; my $in; open IN, "$file"; $in .= $_ while (); close IN; $set = "not defined"; my $nest = 0, $depth = 0; # TODO we should push/pop on { and } for (split /[{]/, $in) { $nest+=2; # for each item in split /}/, we decrement by 1 # meaning decrement by number of }, plus one 1 for (split /[}]/, $_) { $nest--; if ($nest < $depth) { $set = "not defined"; $depth = 0; } for (split /[;]/, $_) { #print "parsing: $_\n"; if (/struct ([tT]est_?)([A-Za-z][A-Za-z0-9_]*)/) { #push @sets, $1; $set = $2; $filename{$set} = $file; $prefix{$set} = $1; $depth = $nest; } elsif (/^[ \t]*Test[ \t\n]+([a-zA-Z_1-9]+)[ \t\n]*\(/sm) { if ($set eq "not defined") { print STDERR "W: test found out of scope of a Test structure, ignoring\n"; } else { $test{$set} = () unless ($test{$set}); push @{$tests{$set}}, $1; } } } } } } sub dumpfile() { my $file = shift; my $filecpp = $file; $filecpp =~ s/.test.h$/.cpp/; $filecpp =~ s,/,_,g; print "#undef NDEBUG\n"; print "#include \"$file\"\n"; print "#define RUN(x,y) x().y()\n"; for (keys %tests) { my $set = $_; if ( $filename{$set} eq $file ) { for (@{$tests{$set}}) { print "void run_${set}_$_() {"; print " RUN( $prefix{$set}$set, $_ ); }\n"; } } } } sub dumpmain() { print "#undef NDEBUG\n"; print "#include \n"; print "#include \n"; for (keys %tests) { my $set = $_; for (@{$tests{$set}}) { print "void run_${set}_$_();\n"; } print "RunTest run_${set}"."[] = {\n"; print "\t{ \"$_\", run_${set}_$_ },\n" for (@{$tests{$set}}); print "};\n"; } print "RunSuite suites[] = {\n"; for (keys %tests) { my $count = scalar @{$tests{$_}}; print "{ \"$_\", run_$_, $count },\n"; } print "};\n"; print "#include \n"; #print "int assertFailure = 0;\n"; } for $file (@ARGV) { &process ($file); } if ($mode eq "header") { for $file (@ARGV) { &dumpfile($file); } } if ($mode eq "main") { &dumpmain; } libwibble-1.1/wibble/net/0000755000175000017500000000000012231005104014644 5ustar enricoenricolibwibble-1.1/wibble/net/http.test.h0000644000175000017500000000211311475672504016776 0ustar enricoenrico/* * Copyright (C) 2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include namespace { using namespace std; using namespace wibble; struct TestHTTP { Test boilerplate() { // TODO: implement real test assert(true); } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/net/server.h0000644000175000017500000000510311462264015016337 0ustar enricoenrico#ifndef WIBBLE_NET_SERVER_H #define WIBBLE_NET_SERVER_H /* * net/server - Network server infrastructure * * Copyright (C) 2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include namespace wibble { namespace net { /** * Generic bind/listen/accept internet server */ struct Server { // Human readable server hostname std::string host; // Human readable server port std::string port; // Type of server socket (default SOCK_STREAM) int socktype; // Server socket int sock; // Saved signal handlers before accept struct sigaction *old_signal_actions; // Signal handlers in use during accept struct sigaction *signal_actions; Server(); ~Server(); // Bind to a given port (and optionally interface hostname) void bind(const char* port, const char* host=NULL); // Set socket to listen, with given backlog void listen(int backlog = 16); // Set FD_CLOEXEC option on master socket, so it does not propagate to // children. The master socket is not FD_CLOEXEC by default. void set_sock_cloexec(); }; struct TCPServer : public Server { // Signals used to stop the server's accept loop std::vector stop_signals; TCPServer(); virtual ~TCPServer(); /** Loop accepting connections on the socket, until interrupted by a * signal in stop_signals * * @returns the signal number that stopped the loop */ int accept_loop(); virtual void handle_client(int sock, const std::string& peer_hostname, const std::string& peer_hostaddr, const std::string& peer_port) = 0; protected: static void signal_handler(int sig); static int last_signal; // Initialize signal handling structures void signal_setup(); void signal_install(); void signal_uninstall(); }; } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/net/server.cpp0000644000175000017500000001565312231005104016670 0ustar enricoenrico/* * net/server - Network server infrastructure * * Copyright (C) 2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #ifdef POSIX #include #include #include #include #include #include #include #include using namespace std; namespace wibble { namespace net { Server::Server() : socktype(SOCK_STREAM), sock(-1), old_signal_actions(0), signal_actions(0) { } Server::~Server() { if (sock >= 0) close(sock); if (old_signal_actions) delete[] old_signal_actions; if (signal_actions) delete[] signal_actions; } void Server::bind(const char* port, const char* host) { struct addrinfo hints; memset(&hints, 0, sizeof(addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = socktype; hints.ai_protocol = 0; hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE; struct addrinfo* res; int gaires = getaddrinfo(host, port, &hints, &res); if (gaires != 0) throw wibble::exception::Consistency( str::fmtf("resolving hostname %s:%s", host, port), gai_strerror(gaires)); for (addrinfo* rp = res; rp != NULL; rp = rp->ai_next) { int sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; // Set SO_REUSEADDR int flag = 1; if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) throw wibble::exception::System("setting SO_REUSEADDR on socket"); if (::bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) { // Success sock = sfd; // Resolve what we found back to human readable // form, to use for logging and error reporting char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; if (getnameinfo(rp->ai_addr, rp->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { this->host = hbuf; this->port = sbuf; } else { this->host = "(unknown)"; this->port = "(unknown)"; } break; } close(sfd); } freeaddrinfo(res); if (sock == -1) // No address succeeded throw wibble::exception::Consistency( str::fmtf("binding to %s:%s", host, port), "could not bind to any of the resolved addresses"); } // Set socket to listen, with given backlog void Server::listen(int backlog) { if (::listen(sock, backlog) < 0) throw wibble::exception::System("listening on port " + port); } void Server::set_sock_cloexec() { // Set close-on-exec on master socket if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) throw wibble::exception::System("setting FD_CLOEXEC on server socket"); } TCPServer::TCPServer() { // By default, stop on sigterm and sigint stop_signals.push_back(SIGTERM); stop_signals.push_back(SIGINT); } TCPServer::~TCPServer() {} void TCPServer::signal_handler(int sig) { last_signal = sig; } int TCPServer::last_signal = -1; void TCPServer::signal_setup() { if (old_signal_actions) delete[] old_signal_actions; if (signal_actions) delete[] signal_actions; old_signal_actions = new struct sigaction[stop_signals.size()]; signal_actions = new struct sigaction[stop_signals.size()]; for (size_t i = 0; i < stop_signals.size(); ++i) { signal_actions[i].sa_handler = signal_handler; sigemptyset(&(signal_actions[i].sa_mask)); signal_actions[i].sa_flags = 0; } } void TCPServer::signal_install() { for (size_t i = 0; i < stop_signals.size(); ++i) if (sigaction(stop_signals[i], &signal_actions[i], &old_signal_actions[i]) < 0) throw wibble::exception::System("installing handler for signal " + str::fmt(stop_signals[i])); TCPServer::last_signal = -1; } void TCPServer::signal_uninstall() { for (size_t i = 0; i < stop_signals.size(); ++i) if (sigaction(stop_signals[i], &old_signal_actions[i], NULL) < 0) throw wibble::exception::System("restoring handler for signal " + str::fmt(stop_signals[i])); } // Loop accepting connections on the socket, until interrupted by a // signal in stop_signals int TCPServer::accept_loop() { struct SignalInstaller { TCPServer& s; SignalInstaller(TCPServer& s) : s(s) { s.signal_install(); } ~SignalInstaller() { s.signal_uninstall(); } }; signal_setup(); while (true) { struct sockaddr_storage peer_addr; socklen_t peer_addr_len = sizeof(struct sockaddr_storage); int fd = -1; { SignalInstaller sigs(*this); fd = accept(sock, reinterpret_cast(&peer_addr), static_cast(&peer_addr_len)); if (fd == -1) { if (errno == EINTR) return TCPServer::last_signal; throw wibble::exception::System("listening on " + host + ":" + port); } } // Resolve the peer char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; int gaires = getnameinfo(reinterpret_cast(&peer_addr), peer_addr_len, hbuf, NI_MAXHOST, sbuf, NI_MAXSERV, NI_NUMERICSERV); if (gaires == 0) { string hostname = hbuf; gaires = getnameinfo(reinterpret_cast(&peer_addr), peer_addr_len, hbuf, NI_MAXHOST, sbuf, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); if (gaires == 0) handle_client(fd, hostname, hbuf, sbuf); else throw wibble::exception::Consistency( "resolving peer name numerically", gai_strerror(gaires)); } else throw wibble::exception::Consistency( "resolving peer name", gai_strerror(gaires)); } } } } #endif // vim:set ts=4 sw=4: libwibble-1.1/wibble/net/mime.cpp0000644000175000017500000001452311523773175016333 0ustar enricoenrico/* * net/mime - MIME utilities * * Copyright (C) 2010--2011 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include using namespace std; namespace wibble { namespace net { namespace mime { Reader::Reader() : header_splitter("^([^:[:blank:]]+)[[:blank:]]*:[[:blank:]]*(.+)", 3) { } bool Reader::read_line(int sock, string& res) { bool has_cr = false; res.clear(); while (true) { char c; ssize_t count = read(sock, &c, 1); if (count == 0) return false; if (count < 0) throw wibble::exception::System("reading from socket"); switch (c) { case '\r': if (has_cr) res.append(1, '\r'); has_cr = true; break; case '\n': if (has_cr) return true; else res.append(1, '\n'); break; default: res.append(1, c); break; } } } bool Reader::read_headers(int sock, std::map& headers) { string line; map::iterator last_inserted = headers.end(); while (read_line(sock, line)) { // Headers are terminated by an empty line if (line.empty()) return true; if (isblank(line[0])) { // Append continuation of previous header body if (last_inserted != headers.end()) { last_inserted->second.append(" "); last_inserted->second.append(str::trim(line)); } } else { if (header_splitter.match(line)) { // rfc2616, 4.2 Message Headers: // Multiple message-header fields with // the same field-name MAY be present // in a message if and only if the // entire field-value for that header // field is defined as a // comma-separated list [i.e., // #(values)]. It MUST be possible to // combine the multiple header fields // into one "field-name: field-value" // pair, without changing the semantics // of the message, by appending each // subsequent field-value to the first, // each separated by a comma. string key = str::tolower(header_splitter[1]); string val = str::trim(header_splitter[2]); map::iterator old = headers.find(key); if (old == headers.end()) { // Insert new pair< map::iterator, bool > res = headers.insert(make_pair(key, val)); last_inserted = res.first; } else { // Append comma-separated old->second.append(","); old->second.append(val); last_inserted = old; } } else last_inserted = headers.end(); } } return false; } bool Reader::readboundarytail(int sock) { // [\-\s]*\r\n bool has_cr = false; bool has_dash = false; while (true) { char c; ssize_t count = read(sock, &c, 1); if (count == 0) throw wibble::exception::Consistency("reading from socket", "data ends before MIME boundary"); if (count < 0) throw wibble::exception::System("reading from socket"); switch (c) { case '\r': has_cr = true; break; case '\n': if (has_cr) return !has_dash; break; case '\t': case ' ': has_cr = false; break; case '-': has_dash = true; has_cr = false; break; default: throw wibble::exception::Consistency("reading from socket", "line ends with non-whitespace"); break; } } } bool Reader::read_until_boundary(int sock, const std::string& boundary, std::ostream& out, size_t max) { size_t read_so_far = 0; unsigned got = 0; while (got < boundary.size()) { char c; ssize_t count = read(sock, &c, 1); if (count == 0) throw wibble::exception::Consistency("reading from socket", "data ends before MIME boundary"); if (count < 0) throw wibble::exception::System("reading from socket"); if (c == boundary[got]) ++got; else { if (max == 0 || read_so_far < max) { if (got > 0) { out.write(boundary.data(), got); read_so_far += got; } out.put(c); ++read_so_far; } got = 0; } } return readboundarytail(sock); } bool Reader::discard_until_boundary(int sock, const std::string& boundary) { unsigned got = 0; while (got < boundary.size()) { char c; ssize_t count = read(sock, &c, 1); if (count == 0) throw wibble::exception::Consistency("reading from socket", "data ends before MIME boundary"); if (count < 0) throw wibble::exception::System("reading from socket"); if (c == boundary[got]) ++got; else got = 0; } return readboundarytail(sock); } } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/net/http.cpp0000644000175000017500000005205612231005104016337 0ustar enricoenrico/* * net/http - HTTP server utilities * * Copyright (C) 2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include using namespace std; namespace wibble { namespace net { namespace http { const char* error::what() const throw () { if (!msg.empty()) return msg.c_str(); return desc.c_str(); } void error::send(Request& req) { stringstream body; body << "" << endl; body << "" << endl; body << " " << desc << "" << endl; body << "" << endl; body << "" << endl; if (msg.empty()) body << "

" + desc + "

" << endl; else body << "

" + msg + "

" << endl; body << "" << endl; body << "" << endl; req.send_status_line(code, desc); req.send_date_header(); req.send_server_header(); req.send_extra_response_headers(); req.send("Content-Type: text/html; charset=utf-8\r\n"); req.send(str::fmtf("Content-Length: %d\r\n", body.str().size())); req.send("\r\n"); req.send(body.str()); } void error404::send(Request& req) { if (msg.empty()) msg = "Resource " + req.script_name + " not found."; error::send(req); } Request::Request() : server_software("wibble"), response_started(false), space_splitter("[[:blank:]]+", REG_EXTENDED) { } bool Request::read_request() { // Set all structures to default values method = "GET"; url = "/"; version = "HTTP/1.0"; headers.clear(); // Read request line if (!read_method()) return false; // Read request headers read_headers(); // Parse the url script_name = "/"; size_t pos = url.find('?'); if (pos == string::npos) { path_info = url; query_string.clear(); } else { path_info = url.substr(0, pos); query_string = url.substr(pos); } // Message body is not read here return true; } std::string Request::path_info_head() { size_t beg = path_info.find_first_not_of('/'); if (beg == string::npos) return string(); else { size_t end = path_info.find('/', beg); if (end == string::npos) return path_info.substr(beg); else return path_info.substr(beg, end-beg); } } std::string Request::pop_path_info() { // Strip leading / size_t beg = path_info.find_first_not_of('/'); if (beg == string::npos) { path_info.clear(); return string(); } else { size_t end = path_info.find('/', beg); string first; if (end == string::npos) { first = path_info.substr(beg); path_info.clear(); } else { first = path_info.substr(beg, end-beg); path_info = path_info.substr(end); } script_name = str::joinpath(script_name, first); return first; } } bool Request::read_buf(std::string& res, size_t size) { res.clear(); res.resize(size); size_t pos = 0; while (true) { ssize_t r = read( sock, const_cast< char* >( res.data() ) + pos, size - pos ); if (r < 0) throw wibble::exception::System("reading data from socket"); if (static_cast(r) == size - pos) break; else if (r == 0) { res.resize(pos); return false; } pos += r; } return true; } // Read HTTP method and its following empty line bool Request::read_method() { // Request line, such as GET /images/logo.png HTTP/1.1, which // requests a resource called /images/logo.png from server string cmdline; if (!mime_reader.read_line(sock, cmdline)) return false; // If we cannot fill some of method, url or version we just let // them be, as they have previously been filled with defaults // by read_request() Splitter::const_iterator i = space_splitter.begin(cmdline); if (i != space_splitter.end()) { method = str::toupper(*i); ++i; if (i != space_splitter.end()) { url = *i; ++i; if (i != space_splitter.end()) version = *i; } } // An empty line return mime_reader.read_line(sock, cmdline); } /** * Read HTTP headers * * @return true if there still data to read and headers are terminated * by an empty line, false if headers are terminated by EOF */ bool Request::read_headers() { return mime_reader.read_headers(sock, headers); } // Set the CGI environment variables for the current process using this // request void Request::set_cgi_env() { // Set CGI server-specific variables #ifdef POSIX // FIXME // SERVER_SOFTWARE — name/version of HTTP server. setenv("SERVER_SOFTWARE", server_software.c_str(), 1); // SERVER_NAME — host name of the server, may be dot-decimal IP address. setenv("SERVER_NAME", server_name.c_str(), 1); // GATEWAY_INTERFACE — CGI/version. setenv("GATEWAY_INTERFACE", "CGI/1.1", 1); // Set request-specific variables // SCRIPT_NAME — relative path to the program, like /cgi-bin/script.cgi. setenv("SCRIPT_NAME", script_name.c_str(), 1); // PATH_INFO — path suffix, if appended to URL after program name and a slash. setenv("PATH_INFO", path_info.c_str(), 1); // TODO: is it really needed? // PATH_TRANSLATED — corresponding full path as supposed by server, if PATH_INFO is present. unsetenv("PATH_TRANSLATED"); // QUERY_STRING — the part of URL after ? character. Must be composed of name=value pairs separated with ampersands (such as var1=val1&var2=val2…) and used when form data are transferred via GET method. setenv("QUERY_STRING", query_string.c_str(), 1); // SERVER_PORT — TCP port (decimal). setenv("SERVER_PORT", server_port.c_str(), 1); // REMOTE_HOST — host name of the client, unset if server did not perform such lookup. setenv("REMOTE_HOST", peer_hostname.c_str(), 1); // REMOTE_ADDR — IP address of the client (dot-decimal). setenv("REMOTE_ADDR", peer_hostaddr.c_str(), 1); // SERVER_PROTOCOL — HTTP/version. setenv("SERVER_PROTOCOL", version.c_str(), 1); // REQUEST_METHOD — name of HTTP method (see above). setenv("REQUEST_METHOD", method.c_str(), 1); // AUTH_TYPE — identification type, if applicable. unsetenv("AUTH_TYPE"); // REMOTE_USER used for certain AUTH_TYPEs. unsetenv("REMOTE_USER"); // REMOTE_IDENT — see ident, only if server performed such lookup. unsetenv("REMOTE_IDENT"); // CONTENT_TYPE — MIME type of input data if PUT or POST method are used, as provided via HTTP header. map::const_iterator i = headers.find("content-type"); if (i != headers.end()) setenv("CONTENT_TYPE", i->second.c_str(), 1); else unsetenv("CONTENT_TYPE"); // CONTENT_LENGTH — size of input data (decimal, in octets) if provided via HTTP header. i = headers.find("content-length"); if (i != headers.end()) setenv("CONTENT_LENGTH", i->second.c_str(), 1); else unsetenv("CONTENT_LENGTH"); // Variables passed by user agent (HTTP_ACCEPT, HTTP_ACCEPT_LANGUAGE, HTTP_USER_AGENT, HTTP_COOKIE and possibly others) contain values of corresponding HTTP headers and therefore have the same sense. for (map::const_iterator i = headers.begin(); i != headers.end(); ++i) { if (i->first == "content-type" or i->first == "content-length") continue; string name = "HTTP_"; for (string::const_iterator j = i->first.begin(); j != i->first.end(); ++j) if (isalnum(*j)) name.append(1, toupper(*j)); else name.append(1, '_'); setenv(name.c_str(), i->second.c_str(), 1); } #endif } void Request::send(const std::string& buf) { size_t pos = 0; while (pos < buf.size()) { ssize_t sent = write(sock, buf.data() + pos, buf.size() - pos); if (sent < 0) throw wibble::exception::System("writing data to client"); pos += sent; } } void Request::send_status_line(int code, const std::string& msg, const std::string& version) { stringstream buf; buf << version << " " << code << " " << msg << "\r\n"; send(buf.str()); response_started = true; } void Request::send_server_header() { stringstream buf; buf << "Server: " << server_software << "\r\n"; send(buf.str()); } void Request::send_date_header() { time_t now = time(NULL); struct tm t; #ifdef POSIX gmtime_r(&now, &t); #else t = *gmtime(&now); #endif char tbuf[256]; size_t size = strftime(tbuf, 256, "%a, %d %b %Y %H:%M:%S GMT", &t); if (size == 0) throw wibble::exception::Consistency("internal buffer too small to store date header"); stringstream buf; buf << "Date: " << tbuf << "\r\n"; send(buf.str()); } void Request::send_extra_response_headers() { stringstream buf; for (map::const_iterator i = extra_response_headers.begin(); i != extra_response_headers.end(); ++i) { // Truncate the body at the first newline // FIXME: mangle in a better way buf << i->first << ": "; size_t pos = i->second.find('\n'); if (pos == string::npos) buf << i->second; else buf << i->second.substr(0, pos); buf << "\r\n"; } if (!buf.str().empty()) send(buf.str()); } void Request::send_result(const std::string& content, const std::string& content_type, const std::string& filename) { send_status_line(200, "OK"); send_date_header(); send_server_header(); send_extra_response_headers(); send("Content-Type: " + content_type + "\r\n"); if (!filename.empty()) send("Content-Disposition: attachment; filename=" + filename + "\r\n"); send(str::fmtf("Content-Length: %d\r\n", content.size())); send("\r\n"); send(content); } void Request::discard_input() { // First try seeking at end int res = lseek(sock, 0, SEEK_END); if (res == -1 && errno == ESPIPE) { // If it fails, we'll have to read our way char buf[4096]; while (true) { int res = read(sock, buf, 4096); if (res < 0) throw wibble::exception::System("reading data from input socket"); if (res == 0) break; } } } Param::~Param() {} void ParamSingle::parse(const std::string& str) { assign(str); } void ParamMulti::parse(const std::string& str) { push_back(str); } FileParam::~FileParam() {} bool FileParam::FileInfo::read( net::mime::Reader& mime_reader, map headers, const std::string& outdir, const std::string& fname_blacklist, const std::string& client_fname, int sock, const std::string& boundary, size_t inputsize) { int openflags = O_CREAT | O_WRONLY; // Store the client provided pathname this->client_fname = client_fname; // Generate the output file name if (fname.empty()) { fname = client_fname; openflags |= O_EXCL; } string preferred_fname = str::basename(fname); // Replace blacklisted chars if (!fname_blacklist.empty()) for (string::iterator i = preferred_fname.begin(); i != preferred_fname.end(); ++i) if (fname_blacklist.find(*i) != string::npos) *i = '_'; preferred_fname = str::joinpath(outdir, preferred_fname); fname = preferred_fname; // Create the file int outfd; for (unsigned i = 1; ; ++i) { outfd = open(fname.c_str(), openflags, 0600); if (outfd >= 0) break; if (errno != EEXIST) throw wibble::exception::File(fname, "creating file"); // Alter the file name and try again fname = preferred_fname + str::fmtf(".%u", i); } // Wrap output FD into a stream, which will take care of // closing it wibble::stream::PosixBuf posixBuf(outfd); ostream out(&posixBuf); // Read until boundary, sending data to temp file bool has_part = mime_reader.read_until_boundary(sock, boundary, out, inputsize); return has_part; } FileParamSingle::FileParamSingle(const std::string& fname) { info.fname = fname; } bool FileParamSingle::read( net::mime::Reader& mime_reader, map headers, const std::string& outdir, const std::string& fname_blacklist, const std::string& client_fname, int sock, const std::string& boundary, size_t inputsize) { return info.read(mime_reader, headers, outdir, fname_blacklist, client_fname, sock, boundary, inputsize); } bool FileParamMulti::read( net::mime::Reader& mime_reader, map headers, const std::string& outdir, const std::string& fname_blacklist, const std::string& client_fname, int sock, const std::string& boundary, size_t inputsize) { files.push_back(FileInfo()); return files.back().read(mime_reader, headers, outdir, fname_blacklist, client_fname, sock, boundary, inputsize); } Params::Params() { conf_max_input_size = 20 * 1024 * 1024; conf_max_field_size = 1024 * 1024; conf_accept_unknown_fields = false; conf_accept_unknown_file_fields = false; } Params::~Params() { for (iterator i = begin(); i != end(); ++i) delete i->second; for (std::map::iterator i = files.begin(); i != files.end(); ++i) delete i->second; } void Params::add(const std::string& name, Param* param) { iterator i = find(name); if (i != end()) { delete i->second; i->second = param; } else insert(make_pair(name, param)); } void Params::add(const std::string& name, FileParam* param) { std::map::iterator i = files.find(name); if (i != files.end()) { delete i->second; i->second = param; } else files.insert(make_pair(name, param)); } Param* Params::obtain_field(const std::string& name) { iterator i = find(name); if (i != end()) return i->second; if (!conf_accept_unknown_fields) return NULL; pair res = insert(make_pair(name, new ParamMulti)); return res.first->second; } FileParam* Params::obtain_file_field(const std::string& name) { std::map::iterator i = files.find(name); if (i != files.end()) return i->second; if (!conf_accept_unknown_file_fields) return NULL; pair::iterator, bool> res = files.insert(make_pair(name, new FileParamMulti)); return res.first->second; } Param* Params::field(const std::string& name) { iterator i = find(name); if (i != end()) return i->second; return NULL; } FileParam* Params::file_field(const std::string& name) { std::map::iterator i = files.find(name); if (i != files.end()) return i->second; return NULL; } void Params::parse_get_or_post(net::http::Request& req) { if (req.method == "GET") { size_t pos = req.url.find('?'); if (pos != string::npos) parse_urlencoded(req.url.substr(pos + 1)); } else if (req.method == "POST") parse_post(req); else throw wibble::exception::Consistency("cannot parse parameters from \"" + req.method + "\" request"); } void Params::parse_urlencoded(const std::string& qstring) { // Split on & str::Split splitter("&", qstring); for (str::Split::const_iterator i = splitter.begin(); i != splitter.end(); ++i) { if (i->empty()) continue; // Split on = size_t pos = i->find('='); if (pos == string::npos) { // foo= Param* p = obtain_field(str::urldecode(*i)); if (p != NULL) p->parse(string()); } else { // foo=bar Param* p = obtain_field(str::urldecode(i->substr(0, pos))); if (p != NULL) p->parse(str::urldecode(i->substr(pos+1))); } } } void Params::parse_multipart(net::http::Request& req, size_t inputsize, const std::string& content_type) { net::mime::Reader mime_reader; // Get the mime boundary size_t pos = content_type.find("boundary="); if (pos == string::npos) throw net::http::error400("no boundary in content-type"); // TODO: strip boundary of leading and trailing " string boundary = "--" + content_type.substr(pos + 9); // Read until first boundary, discarding data mime_reader.discard_until_boundary(req.sock, boundary); boundary = "\r\n" + boundary; // Content-Disposition: form-data; name="submit-name" //wibble::ERegexp re_disposition(";%s*([^%s=]+)=\"(.-)\"", 2); wibble::Splitter cd_splitter("[[:blank:]]*;[[:blank:]]*", REG_EXTENDED); wibble::ERegexp cd_parm("([^=]+)=\"([^\"]+)\"", 3); bool has_part = true; while (has_part) { // Read mime headers for this part map headers; if (!mime_reader.read_headers(req.sock, headers)) throw net::http::error400("request truncated at MIME headers"); // Get name and (optional) filename from content-disposition map::const_iterator i = headers.find("content-disposition"); if (i == headers.end()) throw net::http::error400("no Content-disposition in MIME headers"); wibble::Splitter::const_iterator j = cd_splitter.begin(i->second); if (j == cd_splitter.end()) throw net::http::error400("incomplete content-disposition header"); if (*j != "form-data") throw net::http::error400("Content-disposition is not \"form-data\""); string name; string filename; for (++j; j != cd_splitter.end(); ++j) { if (!cd_parm.match(*j)) continue; string key = cd_parm[1]; if (key == "name") { name = cd_parm[2]; } else if (key == "filename") { filename = cd_parm[2]; } } if (!filename.empty()) { // Get a file param FileParam* p = conf_outdir.empty() ? NULL : obtain_file_field(name); if (p != NULL) has_part = p->read(mime_reader, headers, conf_outdir, conf_fname_blacklist, filename, req.sock, boundary, inputsize); else has_part = mime_reader.discard_until_boundary(req.sock, boundary); } else { // Read until boundary, storing data in string stringstream value; has_part = mime_reader.read_until_boundary(req.sock, boundary, value, conf_max_field_size); // Store the field value Param* p = obtain_field(name); if (p != NULL) p->parse(value.str()); } } } void Params::parse_post(net::http::Request& req) { // Get the supposed size of incoming data map::const_iterator i = req.headers.find("content-length"); if (i == req.headers.end()) throw wibble::exception::Consistency("no Content-Length: found in request header"); // Validate the post size size_t inputsize = strtoul(i->second.c_str(), 0, 10); if (inputsize > conf_max_input_size) { // Discard all input req.discard_input(); throw wibble::exception::Consistency(str::fmtf( "Total size of incoming data (%zdb) exceeds configured maximum (%zdb)", inputsize, conf_max_input_size)); } // Get the content type i = req.headers.find("content-type"); if (i == req.headers.end()) throw wibble::exception::Consistency("no Content-Type: found in request header"); if (i->second.find("x-www-form-urlencoded") != string::npos) { string line; req.read_buf(line, inputsize); parse_urlencoded(line); } else if (i->second.find("multipart/form-data") != string::npos) { parse_multipart(req, inputsize, i->second); } else throw wibble::exception::Consistency("unsupported Content-Type: " + i->second); } } } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/net/mime.h0000644000175000017500000000527411462264015015771 0ustar enricoenrico#ifndef WIBBLE_NET_MIME_H #define WIBBLE_NET_MIME_H /* * net/mime - MIME utilities * * Copyright (C) 2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include namespace wibble { namespace net { namespace mime { struct Reader { wibble::ERegexp header_splitter; Reader(); /** * Read a line from the file descriptor. * * The line is terminated by . The line terminator is not * included in the resulting string. * * @returns true if a line was read, false if EOF * * Note that if EOF is returned, res can still be filled with a partial * line. This may happen if the connection ends after some data has * been sent but before is sent. */ bool read_line(int sock, std::string& res); /** * Read MIME headers * * @return true if there still data to read and headers are terminated * by an empty line, false if headers are terminated by EOF */ bool read_headers(int sock, std::map& headers); /** * Read until boundary is found, sending data to the given ostream * * @param max Maximum number of bytes to read, or 0 for unilimited until boundary * * @returns true if another MIME part follows, false if it is the last part * (boundary has trailing --) */ bool read_until_boundary(int sock, const std::string& boundary, std::ostream& out, size_t max=0); /** * Read until boundary is found, sending data to the given ostream * * @returns true if another MIME part follows, false if it is the last part * (boundary has trailing --) */ bool discard_until_boundary(int sock, const std::string& boundary); /** * Skip until the end of the boundary line * * @return true if the boundary does not end with --, else false */ bool readboundarytail(int sock); }; } } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/net/mime.test.h0000644000175000017500000000211311462264015016734 0ustar enricoenrico/* * Copyright (C) 2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include namespace { using namespace std; using namespace wibble; struct TestMIME { Test boilerplate() { // TODO: implement real test assert(true); } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/net/http.h0000644000175000017500000002603011476214473016022 0ustar enricoenrico#ifndef WIBBLE_NET_HTTP_H #define WIBBLE_NET_HTTP_H /* * net/http - HTTP server utilities * * Copyright (C) 2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include namespace wibble { namespace net { namespace http { struct Request; struct error : public std::exception { int code; std::string desc; std::string msg; error(int code, const std::string& desc) : code(code), desc(desc) {} error(int code, const std::string& desc, const std::string& msg) : code(code), desc(desc), msg(msg) {} virtual ~error() throw () {} virtual const char* what() const throw (); virtual void send(Request& req); }; struct error400 : public error { error400() : error(400, "Bad request") {} error400(const std::string& msg) : error(400, "Bad request", msg) {} }; struct error404 : public error { error404() : error(404, "Not found") {} error404(const std::string& msg) : error(404, "Not found", msg) {} virtual void send(Request& req); }; struct Request { // Request does not take ownership of the socket: it is up to the caller to // close it int sock; std::string peer_hostname; std::string peer_hostaddr; std::string peer_port; std::string server_name; std::string server_port; std::string script_name; std::string path_info; std::string query_string; /// String to use as server software "NAME/version" std::string server_software; /// true if some response has already been sent to the client bool response_started; std::string method; std::string url; std::string version; std::map headers; wibble::Splitter space_splitter; wibble::net::mime::Reader mime_reader; std::map extra_response_headers; Request(); /** * Read request method and headers from sock * * Sock will be positioned at the beginning of the request body, after the * empty line that follows the header. * * The request URL will be parsed in script_name, path_info and * query_string. At the start, script_name is always / and path_info is the * rest of the path in the url. One can move path components from path_info * to script_name as the request is processed. * * @returns true if the request has been read, false if EOF was found * before the end of the headers. */ bool read_request(); /** * Read a fixed amount of data from the file descriptor * * @returns true if all the data were read, false if EOF was encountered * before the end of the buffer */ bool read_buf(std::string& res, size_t size); // Read HTTP method and its following empty line bool read_method(); /** * Read HTTP headers * * @return true if there still data to read and headers are terminated * by an empty line, false if headers are terminated by EOF */ bool read_headers(); /** * Set the CGI environment variables for the current process using this * request */ void set_cgi_env(); /// Send the content of buf, verbatim, to the client void send(const std::string& buf); /// Send the HTTP status line void send_status_line(int code, const std::string& msg, const std::string& version = "HTTP/1.0"); /// Send the HTTP server header void send_server_header(); /// Send the HTTP date header void send_date_header(); /// Send headers in extra_response_headers void send_extra_response_headers(); /// Send a string as result void send_result(const std::string& content, const std::string& content_type="text/html; charset=utf-8", const std::string& filename=std::string()); /// Discard all input from the socket void discard_input(); /** * Remove the first component from path_info, append it to script_name and * return it. * * If path_info if empty or only consisting of '/', returns the empty string. */ std::string pop_path_info(); /** * Return the first component from path_info * * If path_info if empty or only consisting of '/', returns the empty string. */ std::string path_info_head(); }; /// Base interface for GET or POST parameters struct Param { virtual ~Param(); /** * Parse the value of this parameter from the given unescaped string value. * * This can be called more than once, if the value is found multiple * times. It can also never be called, if the value is never found. */ virtual void parse(const std::string& str) = 0; }; /// Single-valued parameter struct ParamSingle : public std::string, public Param { virtual void parse(const std::string& str); }; /// Multi-valued parameter struct ParamMulti : public std::vector, public Param { virtual void parse(const std::string& str); }; /** * File upload parameter */ struct FileParam { /// Infomation about one uploaded file struct FileInfo { /// File pathname on the local file system std::string fname; /// File pathname provided by the client std::string client_fname; /** * Handle a file upload from a multipart/form-data file upload part */ bool read(net::mime::Reader& mime_reader, std::map headers, const std::string& outdir, const std::string& fname_blacklist, const std::string& client_fname, int sock, const std::string& boundary, size_t inputsize); }; virtual ~FileParam(); /** * Handle a file upload from a multipart/form-data file upload part */ virtual bool read( net::mime::Reader& mime_reader, std::map headers, const std::string& outdir, const std::string& fname_blacklist, const std::string& client_fname, int sock, const std::string& boundary, size_t inputsize) = 0; }; /** * Single file upload field */ struct FileParamSingle : public FileParam { FileInfo info; /** * If a file name is given, use its base name for storing the file; * else, use the file name given by the client, without path */ FileParamSingle(const std::string& fname=std::string()); virtual bool read( net::mime::Reader& mime_reader, std::map headers, const std::string& outdir, const std::string& fname_blacklist, const std::string& client_fname, int sock, const std::string& boundary, size_t inputsize); }; /** * Multiple file uploads with the same name */ struct FileParamMulti : public FileParam { std::vector files; virtual bool read( net::mime::Reader& mime_reader, std::map headers, const std::string& outdir, const std::string& fname_blacklist, const std::string& client_fname, int sock, const std::string& boundary, size_t inputsize); }; /** * Parse and store HTTP query parameters * * It is preconfigured by manipulating the various conf_* fields and using the * add() methods, before calling one of the parse_* methods. */ struct Params : public std::map { /// File parameters std::map files; /// Maximum size of POST input data size_t conf_max_input_size; /// Maximum size of field data for one non-file field size_t conf_max_field_size; /** * Whether to accept unknown fields. * * If true, unkown fields are stored as ParamMulti * * If false, unknown fields are ignored. */ bool conf_accept_unknown_fields; /** * Whether to accept unknown file upload fields. * * If true, unkown fields are stored as FileParamMulti * * If false, unknown file upload fields are ignored. */ bool conf_accept_unknown_file_fields; /** * Directory where we write uploaded files * * @warning: if it is not set to anything, it ignores all file uploads */ std::string conf_outdir; /** * String containing blacklist characters that are replaced with "_" in * the file name. If empty, nothing is replaced. * * This only applies to the basename: the pathname is ignored when * building the local file name. */ std::string conf_fname_blacklist; Params(); ~Params(); /// Universal, automatic add method template TYPE* add(const std::string& name) { TYPE* res = new TYPE; add(name, res); return res; } /// Add a normal parameter to be parsed from the request void add(const std::string& name, Param* param); /// Add a file upload parameter to be parsed from the request void add(const std::string& name, FileParam* param); /** * Get a normal fileld during form parsing. Depending on the value of * conf_accept_unknown_fields, when handling a field that has not been * added before it will either create it if missing, or just return NULL. */ Param* obtain_field(const std::string& name); /** * Get a normal fileld during form parsing. Depending on the value of * conf_accept_unknown_file_fields, when handling a field that has not been * added before it will either create it if missing, or just return NULL. */ FileParam* obtain_file_field(const std::string& name); /// Get a field by name Param* field(const std::string& name); /// Get a file field by name FileParam* file_field(const std::string& name); /// Parse parameters as GET or POST according to request method void parse_get_or_post(net::http::Request& req); /// Parse parameters from urlencoded form data void parse_urlencoded(const std::string& qstring); /// Parse parameters from multipart/form-data void parse_multipart(net::http::Request& req, size_t inputsize, const std::string& content_type); /// Parse parameters from HTTP POST input void parse_post(net::http::Request& req); }; } } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/net/server.test.h0000644000175000017500000000203011462264015017311 0ustar enricoenrico/* * Copyright (C) 2010 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include namespace { using namespace std; using namespace wibble; struct TestNetServer { Test boilerplate() { } }; } // vim:set ts=4 sw=4: libwibble-1.1/wibble/exception.cpp0000644000175000017500000000563211463032575016610 0ustar enricoenrico/* * Generic base exception hierarchy * * Copyright (C) 2003,2004,2005,2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include // strerror_r #include #include #include #include #include #if defined(POSIX) && ! defined(__xlC__) #include #endif using namespace std; namespace wibble { namespace exception { std::vector< std::string > *AddContext::s_context = 0; #if defined(POSIX) && ! defined(__xlC__) void DefaultUnexpected() { try { const int trace_size = 50; void *addrs[trace_size]; size_t size = backtrace (addrs, trace_size); char **strings = backtrace_symbols (addrs, size); cerr << "Caught unexpected exception, " << size << " stack frames unwound:" << endl; for (size_t i = 0; i < size; i++) cerr << " " << strings[i] << endl; free (strings); throw; } catch (Generic& e) { cerr << "Exception was: " << e.type() << ": " << e.fullInfo() << endl; throw; } catch (std::exception& e) { cerr << "Exception was: " << typeid(e).name() << ": " << e.what() << endl; throw; } catch (...) { cerr << "Exception was an unknown object" << endl; throw; } } #else void DefaultUnexpected() { cerr << "Caught unexpected exception."; throw; } #endif InstallUnexpected::InstallUnexpected(void (*func)()) { old = set_unexpected(func); } InstallUnexpected::~InstallUnexpected() { set_unexpected(old); } ///// SystemException System::System(const std::string& context) throw () : Generic(context), m_errno(errno) {} System::System(int code, const std::string& context) throw () : Generic(context), m_errno(code) {} #ifdef POSIX string System::desc() const throw () { const int buf_size = 500; char buf[buf_size]; #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || __APPLE__) && ! _GNU_SOURCE if (strerror_r(m_errno, buf, buf_size)) { buf[buf_size - 1] = 0; return string(buf); } else { return "Unable to get a description for errno value"; } #else return string(strerror_r(m_errno, buf, buf_size)); #endif } #endif #ifdef _WIN32 string System::desc() const throw () { return strerror(m_errno); } #endif } } // vim:set ts=4 sw=4: libwibble-1.1/wibble/singleton.h0000644000175000017500000000604712231005104016240 0ustar enricoenrico// -*- C++ -*- #ifndef WIBBLE_SINGLETON_H #define WIBBLE_SINGLETON_H /* * Degenerated container to hold a single value * * Copyright (C) 2006 Enrico Zini * (C) 2013 Petr Rockai * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include namespace wibble { template class Singleton { protected: T value; public: typedef T value_type; class const_iterator : public std::iterator { const T* value; protected: const_iterator(const T* value) : value(value) {} public: const_iterator() : value(0) {} const T& operator*() const { return *value; } const T* operator->() const { return value; } const_iterator& operator++() { value = 0; return *this; } bool operator==(const const_iterator& iter) const { return value == iter.value; } bool operator!=(const const_iterator& iter) const { return value != iter.value; } friend class Singleton; }; class iterator : public std::iterator { T* value; protected: iterator(T* value) : value(value) {} public: iterator() : value(0) {} T& operator*() { return *value; } T* operator->() { return value; } iterator& operator++() { value = 0; return *this; } bool operator==(const iterator& iter) const { return value == iter.value; } bool operator!=(const iterator& iter) const { return value != iter.value; } friend class Singleton; }; explicit Singleton(const T& value) : value(value) {} Singleton() : value() {} bool empty() const { return false; } size_t size() const { return 1; } iterator begin() { return iterator(&value); } iterator end() { return iterator(); } const_iterator begin() const { return const_iterator(&value); } const_iterator end() const { return const_iterator(); } iterator insert( iterator /* position */, const value_type &v ) { value = v; return begin(); } iterator insert( const value_type &v ) { value = v; return begin(); } }; template Singleton singleton(const T& value) { return Singleton(value); } } // vim:set ts=4 sw=4: #endif libwibble-1.1/wibble/consumer.test.h0000644000175000017500000000206511075401765017065 0ustar enricoenrico// -*- C++ -*- (c) 2006 Petr Rockai #include #include #include namespace { using namespace wibble::operators; using namespace wibble; struct TestConsumer { Test stlInserterConsumer() { std::list a; a.push_back( 10 ); a.push_back( 20 ); Range< int > r = range( a.begin(), a.end() ); std::list b; assert( a != b ); Consumer< int > c = consumer( back_inserter( b ) ); std::copy( r.begin(), r.end(), c ); assert( a == b ); } Test stlSetConsumer() { std::set< int > s; Consumer< int > c = consumer( s ); c.consume( 1 ); assert( *s.begin() == 1 ); assert( s.begin() + 1 == s.end() ); } Test stlVectorConsumer() { std::vector< int > v; Consumer< int > c = consumer( v ); c.consume( 2 ); c.consume( 1 ); assert( *v.begin() == 2 ); assert( *(v.begin() + 1) == 1 ); assert( (v.begin() + 2) == v.end() ); } }; } libwibble-1.1/Makefile.am0000644000175000017500000000023011075401737014662 0ustar enricoenricoSUBDIRS = wibble doc m4dir = $(datadir)/aclocal m4_DATA = wibble/libwibble.m4 pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = wibble/libwibble.pc libwibble-1.1/doc/0000755000175000017500000000000011075401706013374 5ustar enricoenricolibwibble-1.1/doc/CMakeLists.txt0000644000175000017500000000055011075401702016130 0ustar enricoenricoset( stamp "${CMAKE_CURRENT_BINARY_DIR}/doc-stamp" ) add_custom_command( DEPENDS wibble ${CMAKE_CURRENT_BINARY_DIR}/wibble.dox OUTPUT ${stamp} COMMAND ${DOXYGEN} wibble.dox && touch ${stamp} ) add_custom_target( doc DEPENDS ${stamp} ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/wibble.dox.in ${CMAKE_CURRENT_BINARY_DIR}/wibble.dox @ONLY IMMEDIATE ) libwibble-1.1/doc/wibble.dox.in0000644000175000017500000013336411075401704015771 0ustar enricoenrico# Doxyfile 1.3.7 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = wibble # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 2 levels of 10 sub-directories under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of source # files, where putting all generated files in the same directory would otherwise # cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with English messages), Korean, Korean-en, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is used # as the annotated text. Otherwise, the brief description is used as-is. If left # blank, the following values are used ("$name" is automatically replaced with the # name of the entity): "The $name class" "The $name widget" "The $name file" # "is" "provides" "specifies" "contains" "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explicit @brief command for a brief description. JAVADOC_AUTOBRIEF = YES # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = YES # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @wibble_SOURCE_DIR@ # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = *-tut.cc # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. INPUT_FILTER = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_PREDEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse the # parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = libwibble.doxytags # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or # super classes. Setting the tag to NO turns the diagrams off. Note that this # option is superseded by the HAVE_DOT option below. This is only a fallback. It is # recommended to install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found on the path. DOT_PATH = @DOXYGEN_DOT_EXECUTABLE@ # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes that # lay further from the root node will be omitted. Note that setting this option to # 1 or 2 may greatly reduce the computation time needed for large code bases. Also # note that a graph may be further truncated if the graph's image dimensions are # not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). # If 0 is used for the depth value (the default), the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO libwibble-1.1/doc/Makefile.am0000644000175000017500000000075411075401706015436 0ustar enricoenrico## Process this file with automake to produce Makefile.in noinst_DATA = html/index.html libwibble.doxytags if DO_DOCS libwibble.doxytags: wibble.dox ../wibble/libwibble.la doxygen $< html/index.html: wibble.dox ../wibble/libwibble.la doxygen $< else libwibble.doxytags: wibble.dox ../wibble/libwibble.la touch $@ html/index.html: wibble.dox ../wibble/libwibble.la mkdir html || true touch $@ endif EXTRA_DIST = wibble.dox CLEANFILES = libwibble.doxytags clean-local: rm -rf html libwibble-1.1/configure.ac0000644000175000017500000000217712231454436015127 0ustar enricoenricodnl Process this file with autoconf to produce a configure script. AC_INIT(libwibble, [1.1], [libapt-front-devel@lists.alioth.debian.org]) AC_CONFIG_SRCDIR([configure.ac]) dnl AC_CONFIG_AUX_DIR(admin) AM_INIT_AUTOMAKE([foreign subdir-objects nostdinc]) AC_ARG_ENABLE(docs, [ --enable-docs Turn on generation of documentation], [case "${enableval}" in yes) docs=true ;; no) docs=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-docs) ;; esac],[docs=true]) AM_CONDITIONAL(DO_DOCS, test x$docs = xtrue) dnl Doen not work when embedded dnl AC_CONFIG_HEADERS(wibble/config.h) dnl To use subdirs AC_PROG_MAKE_SET AC_LANG([C++]) AC_ISC_POSIX AC_PROG_CC AC_PROG_CXX AM_PROG_CC_STDC AC_HEADER_STDC AM_PROG_CC_C_O dnl Support large files on 32bit systems AC_SYS_LARGEFILE AC_FUNC_FSEEKO #AM_PROG_LEX #AC_PROG_YACC dnl Use libtool AM_ENABLE_STATIC AM_DISABLE_SHARED AM_PROG_LIBTOOL if test x"$GXX" = x"yes"; then CXXFLAGS="-Wall -W $CXXFLAGS" fi AM_CONDITIONAL([WIBBLE_STANDALONE], [true]) AC_CONFIG_FILES([ Makefile wibble/Makefile wibble/tests/Makefile wibble/libwibble.pc doc/Makefile doc/wibble.dox ]) AC_OUTPUT