diff -Nru libwibble-0.1.28/CMakeLists.txt libwibble-1.1/CMakeLists.txt --- libwibble-0.1.28/CMakeLists.txt 2011-07-20 10:59:53.000000000 +0000 +++ libwibble-1.1/CMakeLists.txt 2013-10-22 10:53:23.000000000 +0000 @@ -1,6 +1,9 @@ include( FindDoxygen ) -set( VERSION "0.1.28" ) +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 ) diff -Nru libwibble-0.1.28/configure.ac libwibble-1.1/configure.ac --- libwibble-0.1.28/configure.ac 2011-07-20 10:59:56.000000000 +0000 +++ libwibble-1.1/configure.ac 2013-10-22 10:53:18.000000000 +0000 @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(libwibble, [0.1.28], [libapt-front-devel@lists.alioth.debian.org]) +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]) @@ -29,6 +29,10 @@ AM_PROG_CC_C_O +dnl Support large files on 32bit systems +AC_SYS_LARGEFILE +AC_FUNC_FSEEKO + #AM_PROG_LEX #AC_PROG_YACC diff -Nru libwibble-0.1.28/debian/changelog libwibble-1.1/debian/changelog --- libwibble-0.1.28/debian/changelog 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/debian/changelog 2013-10-24 23:52:21.000000000 +0000 @@ -1,9 +1,61 @@ -libwibble (0.1.28-1.1) unstable; urgency=low +libwibble (1.1-1) unstable; urgency=low - * Non maintainer upload. - * Fix build failure with GCC 4.7 (Francesca Ciceri). Closes; #667259. + * New upstream version + - Reimplemented Directory so it can be iterated twice + - Added new sys::fs functions + + -- Enrico Zini Wed, 23 Oct 2013 11:55:11 +0200 + +libwibble (1.0-1) unstable; urgency=low + + * New upstream version + - fixed mkdirIfMissing for freebsd + - use proper (insane) struct direct allocation to make it portable + - skip file locking in file logger for hurd, which doesn't support + F_SETLK + - bumped release to 1.0 + + -- Enrico Zini Mon, 21 Oct 2013 17:12:39 +0200 + +libwibble (0.2.3-1) unstable; urgency=low + + * New upstream version + - reverted an accidental API-incompatible change in sys::fs::stat + - use flock instead of flock64, which is missing on hurd and should do + the right thing + + -- Enrico Zini Sun, 20 Oct 2013 23:36:36 +0200 + +libwibble (0.2.2-1) unstable; urgency=low + + * New upstream version + - fixed uninitialized Option::hidden, which caused options to randomly + disappear from command line help + - sys/thread.h: #include so that sleep and usleep are defined + - added new test infrastructure, with backtraces, exception catching and + the possibility of setting context information, for example the + iteration number for tests run in a loop + - parse.h: added a framework for writing backtracking recursive-descent + parsers + - sys/pipe.h: added PipeThrough to run data through a filter child + process + - [c++11-only]: added RAII helpers + - [c++11-only]: added StrongEnumFlags + - [c++11-only]: added a new MMap interface + * Ported to debhelper 7 + + -- Enrico Zini Sun, 20 Oct 2013 20:09:50 +0200 + +libwibble (0.2.0-1) unstable; urgency=low + + * New upstream version + - API has changed! + - API is now independent of _FILE_OFFSET_BITS on 32bit systems + - Removed deprecated functions sys::fs::isDirectory and printf-style + str::fmt + - sys::fs::Directory has been mostly rewritten with a cleaner interface - -- Matthias Klose Mon, 16 Apr 2012 20:06:09 +0200 + -- Enrico Zini Thu, 11 Aug 2011 16:39:14 +0200 libwibble (0.1.28-1) unstable; urgency=low diff -Nru libwibble-0.1.28/debian/compat libwibble-1.1/debian/compat --- libwibble-0.1.28/debian/compat 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/debian/compat 2013-10-24 23:52:21.000000000 +0000 @@ -1 +1 @@ -5 +9 diff -Nru libwibble-0.1.28/debian/control libwibble-1.1/debian/control --- libwibble-0.1.28/debian/control 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/debian/control 2013-10-24 23:52:21.000000000 +0000 @@ -3,8 +3,8 @@ Priority: optional Maintainer: Enrico Zini Uploaders: Petr Rockai -Build-Depends: cdbs (>= 0.4.51), debhelper (>= 5.0), dh-buildinfo, doxygen, cmake -Standards-Version: 3.9.2.0 +Build-Depends: debhelper (>= 9), dh-buildinfo, doxygen, cmake +Standards-Version: 3.9.4.0 Vcs-Darcs: http://patch-tag.com/r/wibble-standalone/ Homepage: http://web.mornfall.net/libwibble.html diff -Nru libwibble-0.1.28/debian/libwibble.install libwibble-1.1/debian/libwibble.install --- libwibble-0.1.28/debian/libwibble.install 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/debian/libwibble.install 2013-10-24 23:52:21.000000000 +0000 @@ -1,7 +1,6 @@ debian/tmp/usr/include/* -debian/tmp/usr/lib/lib*.a -debian/tmp/usr/lib/pkgconfig/* -debian/tmp/usr/lib/*.la +debian/tmp/usr/lib/*/lib*.a +debian/tmp/usr/lib/*/pkgconfig/* debian/tmp/usr/share/aclocal/*.m4 debian/tmp/usr/bin/wibble-test-genrunner debian/tmp/usr/share/wibble/test.cmake diff -Nru libwibble-0.1.28/debian/rules libwibble-1.1/debian/rules --- libwibble-0.1.28/debian/rules 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/debian/rules 2013-10-24 23:52:21.000000000 +0000 @@ -1,27 +1,22 @@ #!/usr/bin/make -f -include /usr/share/cdbs/1/rules/debhelper.mk -include /usr/share/cdbs/1/class/cmake.mk +BUILDDIR = $(CURDIR)/debian/build -# TODO workaround for cmake breakage; debian bug #459207 -CC = /usr/bin/gcc -CXX = /usr/bin/g++ - -DEB_MAKE_CHECK_TARGET := unit -# Comment out the following line to use cmake -#DEB_CONFIGURE_EXTRA_FLAGS += --disable-shared --with-pic - -# Store build information -common-binary-post-install-arch common-binary-post-install-indep:: - dh_buildinfo - -build/libwibble-dev:: - make -C ${DEB_BUILDDIR} doc - -# I'd like to use debian/libwibble-dev.docs, but I have to invoke -# dh_installdocs here since the name of the build directory changes every time. -binary-install/libwibble-dev:: - dh_installdocs -p$(cdbs_curpkg) -n ${DEB_BUILDDIR}/doc/html +%: + dh $@ --buildsystem=cmake --builddirectory=$(BUILDDIR) + +override_dh_fixperms: + dh_fixperms + test -e /usr/bin/dh_buildinfo && dh_buildinfo + +override_dh_auto_build: + dh_auto_build + make -C $(BUILDDIR) unit + make -C $(BUILDDIR) doc + +override_dh_auto_install: + dh_auto_install + dh_installdocs -plibwibble-dev -n $(BUILDDIR)/doc/html tarball: if darcs wh -l; then (echo "There are uncommitted changes or spurious files"; /bin/false); fi diff -Nru libwibble-0.1.28/wibble/CMakeLists.txt libwibble-1.1/wibble/CMakeLists.txt --- libwibble-0.1.28/wibble/CMakeLists.txt 2010-12-02 12:32:27.000000000 +0000 +++ libwibble-1.1/wibble/CMakeLists.txt 2013-10-22 14:09:04.000000000 +0000 @@ -11,7 +11,7 @@ }" HAVE_STRUCT_DIRENT_D_TYPE ) configure_file( ${wibble_SOURCE_DIR}/config.h.cmake-in ${wibble_BINARY_DIR}/config.h ) -add_definitions( -DHAVE_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 ) @@ -39,7 +39,9 @@ # on some mingw32, regex.h is not on the default include path find_path( RX_PATH regex.h ) -include_directories( ${RX_PATH} ) +if( RX_PATH ) + include_directories( ${RX_PATH} ) +endif() # libwibble.a if( NOT WIN32 ) @@ -56,16 +58,16 @@ target_link_libraries( wibble pthread ) endif( WIN32 ) -if( NOT WIN32 ) # make check wibble_add_test( wibble-test ${testh} ) target_link_libraries( wibble-test wibble ) wibble_check_target( wibble-test ) -endif( NOT WIN32 ) set( prefix "${CMAKE_INSTALL_PREFIX}" ) set( exec_prefix "${prefix}/bin" ) -set( libdir "${prefix}/lib" ) +if (NOT DEFINED libdir) + set( libdir "${prefix}/lib" ) +endif() set( includedir "${prefix}/include" ) # cmake-time configuration @@ -73,10 +75,10 @@ ${wibble_BINARY_DIR}/libwibble.pc @ONLY ) # make install -install( TARGETS wibble DESTINATION lib COMPONENT wibble_dev ) +install( TARGETS wibble DESTINATION lib/${CMAKE_LIBRARY_ARCHITECTURE} COMPONENT wibble_dev ) if( NOT WIN32 ) -install( FILES ${wibble_BINARY_DIR}/libwibble.pc DESTINATION lib/pkgconfig COMPONENT wibble_dev ) +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 ) diff -Nru libwibble-0.1.28/wibble/Makefile.am libwibble-1.1/wibble/Makefile.am --- libwibble-0.1.28/wibble/Makefile.am 2011-06-14 14:45:38.000000000 +0000 +++ libwibble-1.1/wibble/Makefile.am 2013-10-20 17:26:23.000000000 +0000 @@ -1,9 +1,9 @@ SUBDIRS = . -INCLUDES = -I$(top_srcdir) +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir) if WIBBLE_STANDALONE -INCLUDES += -DWIBBLE_COMPILE_TESTSUITE -DTEST_DIR=\"`pwd`/$(top_builddir)/wibble/tests/work/\" +AM_CPPFLAGS += -DWIBBLE_COMPILE_TESTSUITE -DTEST_DIR=\"`pwd`/$(top_builddir)/wibble/tests/work/\" wibbledir = $(includedir)/wibble wibblecommandlinedir = $(includedir)/wibble/commandline diff -Nru libwibble-0.1.28/wibble/commandline/engine.test.h libwibble-1.1/wibble/commandline/engine.test.h --- libwibble-0.1.28/wibble/commandline/engine.test.h 2009-09-04 14:30:31.000000000 +0000 +++ libwibble-1.1/wibble/commandline/engine.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -211,7 +211,7 @@ ArgList::iterator i = engine.parseList(opts); assert(i == opts.end()); assert_eq(opts.size(), 0u); - assert_eq(engine.foundCommand(), (Engine*)0); + 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()); diff -Nru libwibble-0.1.28/wibble/commandline/options.cpp libwibble-1.1/wibble/commandline/options.cpp --- libwibble-0.1.28/wibble/commandline/options.cpp 2010-12-08 11:09:29.000000000 +0000 +++ libwibble-1.1/wibble/commandline/options.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -35,7 +35,7 @@ throw exception::BadOption("value " + val + " must be numeric"); return strtoul(val.c_str(), NULL, 10); } -bool Int::toBool(const int& val) { return (bool)val; } +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; diff -Nru libwibble-0.1.28/wibble/commandline/options.h libwibble-1.1/wibble/commandline/options.h --- libwibble-0.1.28/wibble/commandline/options.h 2010-12-08 11:09:29.000000000 +0000 +++ libwibble-1.1/wibble/commandline/options.h 2013-09-23 12:31:29.000000000 +0000 @@ -60,13 +60,13 @@ protected: bool m_isset; - Option(const std::string& name) : m_name(name), m_isset(false) {} + 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) + : m_name(name), m_isset(false), usage(usage), description(description), hidden(false) { if (shortName != 0) shortNames.push_back(shortName); diff -Nru libwibble-0.1.28/wibble/config.h.cmake-in libwibble-1.1/wibble/config.h.cmake-in --- libwibble-0.1.28/wibble/config.h.cmake-in 2010-12-02 12:23:49.000000000 +0000 +++ libwibble-1.1/wibble/config.h.cmake-in 2011-08-24 12:00:26.000000000 +0000 @@ -1 +1,4 @@ #cmakedefine HAVE_STRUCT_DIRENT_D_TYPE +#cmakedefine _FILE_OFFSET_BITS +#cmakedefine _LARGEFILE_SOURCE +#cmakedefine _LARGE_FILES diff -Nru libwibble-0.1.28/wibble/config.h.in libwibble-1.1/wibble/config.h.in --- libwibble-0.1.28/wibble/config.h.in 2010-12-02 12:40:36.000000000 +0000 +++ libwibble-1.1/wibble/config.h.in 2011-08-24 11:57:41.000000000 +0000 @@ -2,9 +2,18 @@ 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 * */ - diff -Nru libwibble-0.1.28/wibble/empty.h libwibble-1.1/wibble/empty.h --- libwibble-0.1.28/wibble/empty.h 2010-06-09 16:27:04.000000000 +0000 +++ libwibble-1.1/wibble/empty.h 2013-10-20 16:52:20.000000000 +0000 @@ -36,7 +36,7 @@ class const_iterator : public std::iterator { public: - const T& operator*() const { return *(T*)0; } + 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; } @@ -46,7 +46,7 @@ class iterator : public std::iterator { public: - T& operator*() const { return *(T*)0; } + T& operator*() const { return *reinterpret_cast< T* >( 0 ); } T* operator->() const { return 0; } iterator& operator++() { return *this; } bool operator==(const iterator&) const { return true; } diff -Nru libwibble-0.1.28/wibble/exception.h libwibble-1.1/wibble/exception.h --- libwibble-0.1.28/wibble/exception.h 2008-10-15 14:55:33.000000000 +0000 +++ libwibble-1.1/wibble/exception.h 2013-10-20 16:52:20.000000000 +0000 @@ -282,6 +282,7 @@ } }; +#ifndef NO_RTTI template< typename From, typename To > struct BadCastExt : public BadCast { @@ -294,6 +295,7 @@ + " to " + typeid( To ).name(); } }; +#endif /** * Exception thrown when some value is out of range diff -Nru libwibble-0.1.28/wibble/exception.test.h libwibble-1.1/wibble/exception.test.h --- libwibble-0.1.28/wibble/exception.test.h 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/wibble/exception.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -44,6 +44,7 @@ Test system() { +#ifdef POSIX try { assert_eq(access("does-not-exist", F_OK), -1); throw wex::System("checking for existance of nonexisting file"); @@ -60,6 +61,7 @@ assert_eq(e.code(), ENOENT); assert(e.fullInfo().find("does-not-exist") != string::npos); } +#endif } Test badCast() diff -Nru libwibble-0.1.28/wibble/list.h libwibble-1.1/wibble/list.h --- libwibble-0.1.28/wibble/list.h 2011-07-20 10:58:45.000000000 +0000 +++ libwibble-1.1/wibble/list.h 2013-10-20 16:52:20.000000000 +0000 @@ -249,10 +249,10 @@ char f_space[ sizeof( F ) ]; F &f() { - return *(F *)f_space; + return *static_cast(f_space); } const F &f() const { - return *(const F *)f_space; + return *static_cast(f_space); } typedef typename F::result_type Type; @@ -351,7 +351,6 @@ template< typename List, typename F > void foreach( List l, F f ) { - size_t count = 0; while ( !l.empty() ) { f( l.head() ); l = l.tail(); @@ -360,7 +359,6 @@ template< typename List, template< typename > class F > void foreach( List l, F< typename List::Type > f ) { - size_t count = 0; while ( !l.empty() ) { f( l.head() ); l = l.tail(); diff -Nru libwibble-0.1.28/wibble/log/file.cpp libwibble-1.1/wibble/log/file.cpp --- libwibble-0.1.28/wibble/log/file.cpp 2010-10-27 23:44:59.000000000 +0000 +++ libwibble-1.1/wibble/log/file.cpp 2013-10-21 10:20:21.000000000 +0000 @@ -25,9 +25,13 @@ 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) diff -Nru libwibble-0.1.28/wibble/log.test.h libwibble-1.1/wibble/log.test.h --- libwibble-0.1.28/wibble/log.test.h 2008-10-15 14:53:40.000000000 +0000 +++ libwibble-1.1/wibble/log.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -100,7 +100,7 @@ // 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"); @@ -118,12 +118,14 @@ // 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()); @@ -142,6 +144,7 @@ // Ensure that log messages are only sent after a newline o << "should eventually appear"; +#endif } }; diff -Nru libwibble-0.1.28/wibble/maybe.h libwibble-1.1/wibble/maybe.h --- libwibble-0.1.28/wibble/maybe.h 2008-10-15 14:54:38.000000000 +0000 +++ libwibble-1.1/wibble/maybe.h 2013-10-20 16:52:20.000000000 +0000 @@ -1,4 +1,7 @@ // -*- C++ -*- + +#include + #ifndef WIBBLE_MAYBE_H #define WIBBLE_MAYBE_H diff -Nru libwibble-0.1.28/wibble/mixin.h libwibble-1.1/wibble/mixin.h --- libwibble-0.1.28/wibble/mixin.h 2008-10-15 14:54:35.000000000 +0000 +++ libwibble-1.1/wibble/mixin.h 2013-10-20 16:52:20.000000000 +0000 @@ -4,7 +4,7 @@ #define WIBBLE_MIXIN_H #include -#include +#include namespace wibble { namespace mixin { diff -Nru libwibble-0.1.28/wibble/net/http.cpp libwibble-1.1/wibble/net/http.cpp --- libwibble-0.1.28/wibble/net/http.cpp 2010-12-03 16:22:19.000000000 +0000 +++ libwibble-1.1/wibble/net/http.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -163,10 +163,10 @@ size_t pos = 0; while (true) { - ssize_t r = read(sock, (void*)((char*)res.data() + pos), size - pos); + 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 ((size_t)r == size - pos) + if (static_cast(r) == size - pos) break; else if (r == 0) { @@ -225,6 +225,7 @@ { // 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. @@ -288,6 +289,7 @@ name.append(1, '_'); setenv(name.c_str(), i->second.c_str(), 1); } +#endif } void Request::send(const std::string& buf) @@ -321,7 +323,11 @@ { 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) diff -Nru libwibble-0.1.28/wibble/net/server.cpp libwibble-1.1/wibble/net/server.cpp --- libwibble-0.1.28/wibble/net/server.cpp 2010-11-22 13:42:41.000000000 +0000 +++ libwibble-1.1/wibble/net/server.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -22,6 +22,8 @@ #include #include +#ifdef POSIX + #include #include #include @@ -184,7 +186,7 @@ int fd = -1; { SignalInstaller sigs(*this); - fd = accept(sock, (sockaddr*)&peer_addr, (socklen_t*)&peer_addr_len); + fd = accept(sock, reinterpret_cast(&peer_addr), static_cast(&peer_addr_len)); if (fd == -1) { if (errno == EINTR) @@ -195,7 +197,7 @@ // Resolve the peer char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; - int gaires = getnameinfo((struct sockaddr *)&peer_addr, + int gaires = getnameinfo(reinterpret_cast(&peer_addr), peer_addr_len, hbuf, NI_MAXHOST, sbuf, NI_MAXSERV, @@ -203,7 +205,7 @@ if (gaires == 0) { string hostname = hbuf; - gaires = getnameinfo((struct sockaddr *)&peer_addr, + gaires = getnameinfo(reinterpret_cast(&peer_addr), peer_addr_len, hbuf, NI_MAXHOST, sbuf, NI_MAXSERV, @@ -224,4 +226,6 @@ } } + +#endif // vim:set ts=4 sw=4: diff -Nru libwibble-0.1.28/wibble/param.h libwibble-1.1/wibble/param.h --- libwibble-0.1.28/wibble/param.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/param.h 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,20 @@ +// -*- 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 diff -Nru libwibble-0.1.28/wibble/parse.h libwibble-1.1/wibble/parse.h --- libwibble-0.1.28/wibble/parse.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/parse.h 2013-10-20 17:56:02.000000000 +0000 @@ -0,0 +1,533 @@ +// -*- 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 diff -Nru libwibble-0.1.28/wibble/parse.test.h libwibble-1.1/wibble/parse.test.h --- libwibble-0.1.28/wibble/parse.test.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/parse.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,62 @@ +// -*- 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 ); + } +}; diff -Nru libwibble-0.1.28/wibble/raii.h libwibble-1.1/wibble/raii.h --- libwibble-0.1.28/wibble/raii.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/raii.h 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,66 @@ +// -*- 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 diff -Nru libwibble-0.1.28/wibble/raii.test.h libwibble-1.1/wibble/raii.test.h --- libwibble-0.1.28/wibble/raii.test.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/raii.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,67 @@ +#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 +}; diff -Nru libwibble-0.1.28/wibble/range.h libwibble-1.1/wibble/range.h --- libwibble-0.1.28/wibble/range.h 2008-10-15 14:55:03.000000000 +0000 +++ libwibble-1.1/wibble/range.h 2013-10-20 16:52:20.000000000 +0000 @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff -Nru libwibble-0.1.28/wibble/sfinae.h libwibble-1.1/wibble/sfinae.h --- libwibble-0.1.28/wibble/sfinae.h 2010-11-22 10:10:42.000000000 +0000 +++ libwibble-1.1/wibble/sfinae.h 2013-10-20 16:52:20.000000000 +0000 @@ -5,7 +5,10 @@ namespace wibble { -struct Unit {}; +struct Unit { + bool operator<( Unit ) const { return false; } + bool operator==( Unit ) const { return true; } +}; struct TTrue { static const bool value = true; @@ -77,9 +80,18 @@ 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; diff -Nru libwibble-0.1.28/wibble/singleton.h libwibble-1.1/wibble/singleton.h --- libwibble-0.1.28/wibble/singleton.h 2010-06-09 16:27:16.000000000 +0000 +++ libwibble-1.1/wibble/singleton.h 2013-10-20 16:52:20.000000000 +0000 @@ -6,6 +6,7 @@ * 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 @@ -31,64 +32,76 @@ class Singleton { protected: - T value; + T value; public: - typedef T value_type; + 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) {} - - 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(); } + 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); + return Singleton(value); } } diff -Nru libwibble-0.1.28/wibble/stream/posix.test.h libwibble-1.1/wibble/stream/posix.test.h --- libwibble-0.1.28/wibble/stream/posix.test.h 2008-10-15 14:53:24.000000000 +0000 +++ libwibble-1.1/wibble/stream/posix.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -16,6 +16,7 @@ struct TestStreamPosix { Test basicMatch() { +#ifdef POSIX // no /dev/null otherwise int fd = open("/dev/null", O_WRONLY); assert(fd != -1); @@ -25,6 +26,7 @@ os << "Foo"; os << "Bar"; os << endl; +#endif } }; diff -Nru libwibble-0.1.28/wibble/string.cpp libwibble-1.1/wibble/string.cpp --- libwibble-0.1.28/wibble/string.cpp 2010-06-09 16:27:22.000000000 +0000 +++ libwibble-1.1/wibble/string.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -168,7 +168,7 @@ res += *i; else { char buf[4]; - snprintf(buf, 4, "%%%02x", (unsigned)(unsigned char)*i); + snprintf(buf, 4, "%%%02x", static_cast(static_cast(*i))); res += buf; } } @@ -185,7 +185,7 @@ // If there's a partial %something at the end, ignore it if (i >= str.size() - 2) return res; - res += (char)strtoul(str.substr(i+1, 2).c_str(), 0, 16); + res += static_cast(strtoul(str.substr(i+1, 2).c_str(), 0, 16)); i += 2; } else @@ -201,7 +201,7 @@ { 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 ((unsigned)idx > 43 + (sizeof(data)/sizeof(data[0]))) return 0; + if (static_cast(idx) > 43 + (sizeof(data)/sizeof(data[0]))) return 0; return data[idx - 43]; } @@ -269,8 +269,14 @@ } // Remove trailing padding - for (size_t i = str.size() - 1; i >= 0 && str[i] == '='; --i) - res.resize(res.size() - 1); + 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; } @@ -391,6 +397,64 @@ 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; +} + } } diff -Nru libwibble-0.1.28/wibble/string.h libwibble-1.1/wibble/string.h --- libwibble-0.1.28/wibble/string.h 2010-12-23 18:06:13.000000000 +0000 +++ libwibble-1.1/wibble/string.h 2013-10-20 16:52:20.000000000 +0000 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #ifdef _WIN32 @@ -46,7 +47,6 @@ static int vasprintf (char **, const char *, va_list); #endif -std::string fmt( const char* f, ... ) __attribute__ ((deprecated)); std::string fmtf( const char* f, ... ); template< typename T > inline std::string fmt(const T& val); @@ -114,6 +114,12 @@ 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) { @@ -267,6 +273,20 @@ 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); @@ -450,6 +470,21 @@ 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); + + } } diff -Nru libwibble-0.1.28/wibble/string.test.h libwibble-1.1/wibble/string.test.h --- libwibble-0.1.28/wibble/string.test.h 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/string.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -139,6 +139,18 @@ 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(""), ""); @@ -363,6 +375,13 @@ 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"); + } }; } diff -Nru libwibble-0.1.28/wibble/strongenumflags.h libwibble-1.1/wibble/strongenumflags.h --- libwibble-0.1.28/wibble/strongenumflags.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/strongenumflags.h 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,89 @@ +// -*- 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 diff -Nru libwibble-0.1.28/wibble/strongenumflags.test.h libwibble-1.1/wibble/strongenumflags.test.h --- libwibble-0.1.28/wibble/strongenumflags.test.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/strongenumflags.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,62 @@ +#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 +}; + diff -Nru libwibble-0.1.28/wibble/sys/buffer.cpp libwibble-1.1/wibble/sys/buffer.cpp --- libwibble-0.1.28/wibble/sys/buffer.cpp 2008-10-15 14:54:30.000000000 +0000 +++ libwibble-1.1/wibble/sys/buffer.cpp 2013-10-01 23:34:50.000000000 +0000 @@ -1,7 +1,7 @@ /* * Variable-size, reference-counted memory buffer * - * Copyright (C) 2003--2006 Enrico Zini + * 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 @@ -19,6 +19,7 @@ */ #include +#include #include // memcpy #include // malloc, free, realloc @@ -102,6 +103,23 @@ 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); +} + } } diff -Nru libwibble-0.1.28/wibble/sys/buffer.h libwibble-1.1/wibble/sys/buffer.h --- libwibble-0.1.28/wibble/sys/buffer.h 2008-10-15 14:54:29.000000000 +0000 +++ libwibble-1.1/wibble/sys/buffer.h 2013-10-22 10:52:16.000000000 +0000 @@ -4,7 +4,7 @@ /* * Variable-size, reference-counted memory buffer * - * Copyright (C) 2003--2006 Enrico Zini + * 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 @@ -22,6 +22,7 @@ */ #include // for size_t +#include namespace wibble { namespace sys { @@ -91,6 +92,8 @@ * 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) { @@ -189,8 +192,18 @@ 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); + } } diff -Nru libwibble-0.1.28/wibble/sys/buffer.test.h libwibble-1.1/wibble/sys/buffer.test.h --- libwibble-0.1.28/wibble/sys/buffer.test.h 2008-10-15 14:54:19.000000000 +0000 +++ libwibble-1.1/wibble/sys/buffer.test.h 2013-10-22 10:51:07.000000000 +0000 @@ -13,7 +13,7 @@ Test emptiness() { Buffer buf; assert_eq(buf.size(), 0u); - assert_eq(buf.data(), (void*)0); + assert_eq(buf.data(), static_cast(0)); // Empty buffers should be equal Buffer buf1; @@ -26,13 +26,13 @@ Test nonemptiness() { // Nonempty buffers should be properly nonempty Buffer buf(1); - ((char*)buf.data())[0] = 'a'; + (static_cast(buf.data()))[0] = 'a'; assert_eq(buf.size(), 1u); assert(buf.data() != 0); // Nonempty buffers should compare by content Buffer buf1(1); - ((char*)buf1.data())[0] = 'z'; + (static_cast(buf1.data()))[0] = 'z'; assert(buf == buf); assert(buf1 == buf1); assert(!(buf == buf1)); @@ -40,7 +40,7 @@ assert(buf < buf1); assert(!(buf1 < buf)); - ((char*)buf1.data())[0] = 'a'; + (static_cast(buf1.data()))[0] = 'a'; assert(buf == buf1); assert(!(buf != buf1)); assert(!(buf < buf1)); @@ -78,12 +78,12 @@ // Check creation by taking ownership of another buffer Test takeover() { - char* str = new char[4]; + char* str = (char*)malloc(4); memcpy(str, "ciao", 4); Buffer buf(str, 4, true); assert_eq(buf.size(), 4u); - assert_eq((void*)str, buf.data()); + assert_eq(static_cast(str), buf.data()); } }; diff -Nru libwibble-0.1.28/wibble/sys/childprocess.cpp libwibble-1.1/wibble/sys/childprocess.cpp --- libwibble-0.1.28/wibble/sys/childprocess.cpp 2010-11-24 16:49:51.000000000 +0000 +++ libwibble-1.1/wibble/sys/childprocess.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -19,20 +19,22 @@ */ #include -#ifdef POSIX - #include // EXIT_FAILURE #include // fork, waitpid, kill, open, getpw*, getgr*, initgroups #include // open -#include // getrlimit, setrlimit #include // fork, dup2, pipe, close, setsid, _exit, chdir #include // open + +#ifdef POSIX +#include // getrlimit, setrlimit #include // waitpid #include // kill -#include // flockfile, funlockfile -#include // is* #include // getpw* #include // getgr*, initgroups +#endif + +#include // flockfile, funlockfile +#include // is* #include #include @@ -41,200 +43,216 @@ 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); - - pid_t pid; - if ((pid = ::fork()) == 0) - { - // Tell the logging system we're in a new process - //Log::Logger::instance()->setupForkedChild(); - - // Child process - try { - // 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 wibble::exception::System("trying to fork the child process to run action script"); - } else { - funlockfile(stdin); - funlockfile(stderr); - funlockfile(stdout); - - // Parent process - return _pid = pid; - } + 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. */ -pid_t ChildProcess::forkAndRedirect(int* stdinfd, int* stdoutfd, int* stderrfd) + 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 ) { - int pipes[3][2]; +#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]; +} - if (stdinfd) - { - if (pipe(pipes[0]) == -1) - throw wibble::exception::System("trying to create the pipe to connect to child standard input"); - *stdinfd = pipes[0][1]; - } - if (stdoutfd) - { - if (pipe(pipes[1]) == -1) - throw wibble::exception::System("trying to create the pipe to connect to child standard output"); - *stdoutfd = pipes[1][0]; - if (stderrfd == stdoutfd) - *stderrfd = pipes[1][0]; - } - - if (stderrfd && stderrfd != stdoutfd) - { - if (pipe(pipes[2]) == -1) - throw wibble::exception::System("trying to create the pipe to connect to child standard error"); - *stderrfd = pipes[2][0]; - } - - flockfile(stdin); - flockfile(stdout); - flockfile(stderr); - - pid_t pid; - if ((pid = ::fork()) == 0) - { - // Tell the logging system we're in a new process - //Log::Logger::instance()->setupForkedChild(); - - // Child process - try { - if (stdinfd) - { - // Redirect input from the parent to stdin - if (close(pipes[0][1]) == -1) - throw wibble::exception::System("closing write end of parent stdin pipe"); - if (dup2(pipes[0][0], 0) == -1) - throw wibble::exception::System("dup2-ing parent stdin pipe to stdin"); - if (close(pipes[0][0]) == -1) - throw wibble::exception::System("closing original read end of parent stdin pipe"); - } - - if (stdoutfd) - { - // Redirect output to the parent stdout fd - if (close(pipes[1][0]) == -1) - throw wibble::exception::System("closing read end of parent stdout pipe"); - if (dup2(pipes[1][1], 1) == -1) - throw wibble::exception::System("dup2-ing stdout to parent stdout pipe"); - if (stderrfd == stdoutfd) - if (dup2(pipes[1][1], 2) == -1) - throw wibble::exception::System("dup2-ing stderr to parent stdout/stderr pipe"); - if (close(pipes[1][1]) == -1) - throw wibble::exception::System("closing original write end of parent stdout pipe"); - } - - if (stderrfd && stderrfd != stdoutfd) - { - // Redirect all output to the parent - if (close(pipes[2][0]) == -1) - throw wibble::exception::System("closing read end of parent stderr pipe"); - if (dup2(pipes[2][1], 2) == -1) - throw wibble::exception::System("dup2-ing stderr to parent stderr pipe"); - if (close(pipes[2][1]) == -1) - throw wibble::exception::System("closing original write end of parent stderr pipe"); - } - - // 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); - if (stdinfd) - { - close(pipes[0][0]); - close(pipes[0][1]); - } - if (stdoutfd) - { - close(pipes[1][0]); - close(pipes[1][1]); - } - if (stderrfd && stderrfd != stdoutfd) - { - close(pipes[2][0]); - close(pipes[2][1]); - } - throw wibble::exception::System("trying to fork the child process to run action script"); - } else { - funlockfile(stdin); - funlockfile(stderr); - funlockfile(stdout); - - // Parent process - _pid = pid; - try { - if (stdinfd) - if (close(pipes[0][0]) == -1) - throw wibble::exception::System("closing read end of stdin child pipe"); - if (stdoutfd) - if (close(pipes[1][1]) == -1) - throw wibble::exception::System("closing write end of stdout child pipe"); - if (stderrfd && stderrfd != stdoutfd) - if (close(pipes[2][1]) == -1) - throw wibble::exception::System("closing write end of stderr child pipe"); - return pid; - } catch (wibble::exception::System& e) { - // Try to kill the child process if any errors occurs here - ::kill(pid, 15); - throw e; - } - } +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::waitError() { - if (errno == EINTR) - throw wibble::exception::Interrupted("waiting for child termination"); - else - throw wibble::exception::System("waiting for child termination"); +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" ); + } -int ChildProcess::wait() +void ChildProcess::setupPrefork() { - if (_pid == -1) - { - //log_debug("Child already finished"); - return -1; // FIXME: for lack of better ideas - } - - if (waitpid(_pid, &m_status, 0) == -1) - waitError(); - _pid = -1; - return m_status; +#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(); } @@ -244,25 +262,34 @@ } return false; +#else + assert_die(); +#endif } int ChildProcess::wait(struct rusage* ru) { - if (_pid == -1) - { - //log_debug("Child already finished"); - return -1; // FIXME: for lack of better ideas - } +#ifdef POSIX + if (_pid == -1) + return -1; // FIXME: for lack of better ideas - if (wait4(_pid, &m_status, 0, ru) == -1) - waitError(); + if (wait4(_pid, &m_status, 0, ru) == -1) + waitError(); +#else + m_status = 0; // FIXME + WaitForSingleObject( pi.hProcess, INFINITE ); +#endif - _pid = -1; - return m_status; + _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( @@ -276,23 +303,28 @@ 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 wibble::exception::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()); - } + 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 } } } -#endif // vim:set ts=4 sw=4: diff -Nru libwibble-0.1.28/wibble/sys/childprocess.h libwibble-1.1/wibble/sys/childprocess.h --- libwibble-0.1.28/wibble/sys/childprocess.h 2010-11-24 16:49:51.000000000 +0000 +++ libwibble-1.1/wibble/sys/childprocess.h 2013-10-20 16:52:20.000000000 +0000 @@ -1,3 +1,5 @@ +// -*- C++ -*- + #ifndef WIBBLE_SYS_CHILDPROCESS_H #define WIBBLE_SYS_CHILDPROCESS_H @@ -25,6 +27,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + struct rusage; namespace wibble { @@ -36,30 +42,72 @@ class ChildProcess { protected: - pid_t _pid; - int m_status; - void waitError(); - - /** - * 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; + pid_t _pid; + int pipes[3][2]; -public: - ChildProcess() : _pid(-1) {} - virtual ~ChildProcess() {} + 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. - /// For a subprocess to run proc. - pid_t 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() {} - /// Fork a subprocess to run proc. If one of the std*fd variables is - /// non-null, create a pipe connected to the corresponding file descriptor - /// of the child process and store the parent end in the std*fd variable. - pid_t forkAndRedirect(int* stdinfd = 0, int* stdoutfd = 0, int* stderrfd = 0); + /// 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 @@ -70,22 +118,17 @@ */ pid_t pid() const { return _pid; } - /// Wait for the child to finish, returing its exit status. Return -1 if - /// no child is running. - /// TODO: gracefully handle the EINTR error code - int wait(); - - bool running(); - int exitStatus(); - void waitForSuccess(); - - /// Wait for the child to finish, returing its exit status and 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); + 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); + /// Send the given signal to the process + void kill(int signal); }; } diff -Nru libwibble-0.1.28/wibble/sys/childprocess.test.h libwibble-1.1/wibble/sys/childprocess.test.h --- libwibble-0.1.28/wibble/sys/childprocess.test.h 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/wibble/sys/childprocess.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -2,10 +2,10 @@ (c) 2007 Enrico Zini */ #include -#ifdef POSIX #include #include #include +#include #include #include @@ -15,6 +15,10 @@ using namespace std; using namespace wibble::sys; +#ifdef _WIN32 +#define sleep Sleep +#endif + class EndlessChild : public ChildProcess { protected: @@ -56,6 +60,7 @@ // Try running the child process and kill it Test kill() { +#ifdef POSIX EndlessChild child; // Start the child @@ -71,12 +76,17 @@ 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; @@ -89,10 +99,12 @@ // Wait for the child to terminate assert_eq(child.wait(), 0); +#endif } Test redirect() { - Exec child("/bin/echo"); + Exec child("echo"); + child.searchInPath = true; child.args.push_back("antani"); int out; @@ -122,6 +134,23 @@ 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); + } + }; -#endif // vim:set ts=4 sw=4: diff -Nru libwibble-0.1.28/wibble/sys/exec.cpp libwibble-1.1/wibble/sys/exec.cpp --- libwibble-0.1.28/wibble/sys/exec.cpp 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/sys/exec.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -46,33 +46,48 @@ 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 = (char **) alloca(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; - - if (searchInPath) - { - if (execvp(pathname.c_str(), exec_args) == -1) - throw wibble::exception::System("trying to run " + pathname); - } else if (envFromParent) { - if (execve(pathname.c_str(), exec_args, environ) == -1) - throw wibble::exception::System("trying to run " + pathname); - } else { - // Prepare the custom environment - char ** exec_env = (char **) alloca (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 (execve(pathname.c_str(), exec_args, exec_env) == -1) - throw wibble::exception::System("trying to run " + pathname); - } + // 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()"); diff -Nru libwibble-0.1.28/wibble/sys/exec.h libwibble-1.1/wibble/sys/exec.h --- libwibble-0.1.28/wibble/sys/exec.h 2009-09-04 14:50:42.000000000 +0000 +++ libwibble-1.1/wibble/sys/exec.h 2013-10-20 16:52:20.000000000 +0000 @@ -1,5 +1,5 @@ -#ifndef EXEC_H -#define EXEC_H +#ifndef WIBBLE_SYS_EXEC_H +#define WIBBLE_SYS_EXEC_H /* * OO wrapper for execve @@ -38,6 +38,7 @@ * ChildProcess::fork functions is called. Simply calls exec() */ virtual int main(); + virtual void spawnChild(); public: virtual ~Exec() {} @@ -96,7 +97,12 @@ class ShellCommand : public Exec { public: - ShellCommand(const std::string& cmd) : Exec("/bin/sh") + 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); diff -Nru libwibble-0.1.28/wibble/sys/filelock.cpp libwibble-1.1/wibble/sys/filelock.cpp --- libwibble-0.1.28/wibble/sys/filelock.cpp 2010-10-27 23:37:04.000000000 +0000 +++ libwibble-1.1/wibble/sys/filelock.cpp 2013-10-20 22:30:08.000000000 +0000 @@ -5,29 +5,33 @@ #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) + : 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"); + 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); + lock.l_type = F_UNLCK; + fcntl(fd, F_SETLK, &lock); } } } } +#endif + // vim:set ts=4 sw=4: diff -Nru libwibble-0.1.28/wibble/sys/filelock.h libwibble-1.1/wibble/sys/filelock.h --- libwibble-0.1.28/wibble/sys/filelock.h 2010-10-26 20:55:18.000000000 +0000 +++ libwibble-1.1/wibble/sys/filelock.h 2013-10-20 22:29:57.000000000 +0000 @@ -1,6 +1,10 @@ #ifndef WIBBLE_SYS_FILELOCK_H #define WIBBLE_SYS_FILELOCK_H +#include + +#ifdef POSIX + #include namespace wibble { @@ -14,20 +18,20 @@ */ struct FileLock { - int fd; - struct flock lock; + 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); + /** + * 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 @@ -46,3 +50,4 @@ // vim:set ts=4 sw=4: #endif +#endif diff -Nru libwibble-0.1.28/wibble/sys/fs.cpp libwibble-1.1/wibble/sys/fs.cpp --- libwibble-0.1.28/wibble/sys/fs.cpp 2010-12-02 12:23:49.000000000 +0000 +++ libwibble-1.1/wibble/sys/fs.cpp 2013-10-22 13:52:07.000000000 +0000 @@ -1,18 +1,22 @@ +#include + #include #include #include #include #include +#include // opendir, closedir #include #include +#include #include #include // alloca on win32 seems to live there -#ifdef HAVE_CONFIG_H -/* Conditionally include config.h so there is a way of enabling the fast - * Directory::isdir implementation if HAVE_STRUCT_DIRENT_D_TYPE is available - */ -#include +#ifdef _WIN32 +#include +#include +#include +#include #endif namespace wibble { @@ -27,28 +31,64 @@ if (errno == ENOENT) return std::auto_ptr(); else - throw wibble::exception::System("getting file information for " + pathname); + throw wibble::exception::File(pathname, "getting file information"); } return res; } -bool isDirectory(const std::string& pathname) +void stat(const std::string& pathname, struct stat& st) { - return isdir(pathname); + 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) { - 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 S_ISDIR(st.st_mode); + 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; @@ -69,16 +109,40 @@ void mkdirIfMissing(const std::string& dir, mode_t mode) { - std::auto_ptr st = wibble::sys::fs::stat(dir); - if (st.get() == NULL) - { - // If it does not exist, make it - if (::mkdir(dir.c_str(), mode) == -1) - throw wibble::exception::System("creating directory " + dir); - } 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"); - } + 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) @@ -108,12 +172,29 @@ in.seekg(0, std::ios::end); length = in.tellg(); in.seekg(0, std::ios::beg); - char *buffer = (char *) alloca( length ); + 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 ); @@ -122,6 +203,44 @@ 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) @@ -152,13 +271,54 @@ 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 (d.isdir(i)) + if (i.isdir()) rmtree(str::joinpath(dir, *i)); else unlink(str::joinpath(dir, *i)); @@ -167,29 +327,197 @@ } #ifdef POSIX -Directory::const_iterator Directory::begin() + +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) { - DIR* dir = opendir(m_path.c_str()); - if (!dir) - throw wibble::exception::System("reading directory " + m_path); - return const_iterator(dir); + 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); } -Directory::const_iterator Directory::end() const +bool Directory::const_iterator::operator==(const const_iterator& iter) const { - return const_iterator(); + // In fact, this only supports equality to itself + return dir == iter.dir && dirp == iter.dirp && direntbuf == iter.direntbuf; } -bool Directory::valid() +bool Directory::const_iterator::operator!=(const const_iterator& iter) const { - // Check that the directory exists - std::auto_ptr st = stat(path()); - if (st.get() == NULL) - return false; - // Check that it is a directory - if (!S_ISDIR(st->st_mode)) - return false; - return true; + // 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 @@ -200,23 +528,60 @@ } #endif -bool Directory::isdir(const const_iterator& i) const +std::string mkdtemp( std::string tmpl ) { -#ifdef HAVE_STRUCT_DIRENT_D_TYPE - if (i->d_type == DT_DIR) - return true; - if (i->d_type != DT_UNKNOWN) - return false; + char *_tmpl = reinterpret_cast< char * >( alloca( tmpl.size() + 1 ) ); + strcpy( _tmpl, tmpl.c_str() ); + return ::mkdtemp( _tmpl ); +} + #endif - // No d_type, we'll need to stat - std::auto_ptr st = stat(wibble::str::joinpath(m_path, *i)); - if (st.get() == 0) - return false; - if (S_ISDIR(st->st_mode)) - return true; - return false; + +#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 + } } } diff -Nru libwibble-0.1.28/wibble/sys/fs.h libwibble-1.1/wibble/sys/fs.h --- libwibble-0.1.28/wibble/sys/fs.h 2010-12-02 10:26:50.000000000 +0000 +++ libwibble-1.1/wibble/sys/fs.h 2013-10-22 13:52:07.000000000 +0000 @@ -2,12 +2,13 @@ #define WIBBLE_SYS_DIRECTORY_H #include -#include // opendir, closedir -#include // auto_ptr -#include // mode_t -#include // struct stat +#include +#include // auto_ptr +#include // mode_t +#include // struct stat +#include // access -struct stat; +struct dirent; namespace wibble { namespace sys { @@ -20,6 +21,12 @@ */ 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); @@ -31,10 +38,13 @@ */ 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); +void mkdirIfMissing(const std::string& dir, mode_t mode = 0777); /// Create all the component of the given directory, including the directory /// itself. @@ -47,10 +57,36 @@ /// 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 @@ -76,90 +112,117 @@ */ bool isdir(const std::string& pathname); -/// same as isdir, but with a legacy clumsy name -bool isDirectory(const std::string& pathname) __attribute__ ((deprecated)); +/// 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 { - std::string m_path; +protected: + /// Directory pathname + std::string m_path; public: - class const_iterator - { - DIR* dir; - struct dirent* d; - - public: - // Create an end iterator - const_iterator() : dir(0), d(0) {} - // Create a begin iterator - const_iterator(DIR* dir) : dir(dir), d(0) { ++(*this); } - // Cleanup properly - ~const_iterator() { if (dir) closedir(dir); } - - // auto_ptr style copy semantics - const_iterator(const const_iterator& i) - { - dir = i.dir; - d = i.d; - const_iterator* wi = const_cast(&i); - wi->dir = 0; - wi->d = 0; - } - const_iterator& operator=(const const_iterator& i) - { - // Catch a = a - if (&i == this) return *this; - if (dir) closedir(dir); - dir = i.dir; - d = i.d; - const_iterator* wi = const_cast(&i); - wi->dir = 0; - wi->d = 0; - return *this; - } - - const_iterator& operator++() - { - if ((d = readdir(dir)) == 0) - { - closedir(dir); - dir = 0; - } - return *this; - } - - std::string operator*() const { return d->d_name; } - struct dirent* operator->() { return d; } - const struct dirent* operator->() const { return d; } - - bool operator==(const const_iterator& iter) const - { - return dir == iter.dir && d == iter.d; - } - bool operator!=(const const_iterator& iter) const - { - return dir != iter.dir || d != iter.d; - } - }; + 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) : m_path(path) {} + Directory(const std::string& path); + ~Directory(); /// Pathname of the directory const std::string& path() const { return m_path; } - /// Check that the directory exists and is a directory - bool valid(); + /// Check if the directory exists + bool exists() const; - /// Begin iterator - const_iterator begin(); + /// Begin iterator + const_iterator begin() const; /// End iterator const_iterator end() const; - - /// @return true if \a i points to a directory, else false - bool isdir(const const_iterator& i) const; }; } diff -Nru libwibble-0.1.28/wibble/sys/fs.test.h libwibble-1.1/wibble/sys/fs.test.h --- libwibble-0.1.28/wibble/sys/fs.test.h 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/wibble/sys/fs.test.h 2013-10-22 13:57:17.000000000 +0000 @@ -1,6 +1,7 @@ -/* -*- C++ -*- (c) 2007 Petr Rockai - (c) 2007 Enrico Zini */ -#include +/* -*- C++ -*- (c) 2007--2011 Petr Rockai + (c) 2007--2013 Enrico Zini */ +#include "wibble/sys/fs.h" +#include #include #include #include @@ -15,30 +16,94 @@ // 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("usr") != 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.valid()); + assert(!dir1.exists()); + try { + Directory::const_iterator i = dir1.begin(); + assert(false); + } catch (wibble::exception::System& e) { + } Directory dir2("/etc/passwd"); - assert(!dir2.valid()); + 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("."); @@ -47,18 +112,22 @@ // 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"); @@ -66,22 +135,81 @@ 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 _deleteIfExists() { - system("rm -f does-not-exist"); - assert(!deleteIfExists("does-not-exist")); - system("touch does-exist"); - assert(deleteIfExists("does-exist")); + 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 _isDirectory() { - system("rm -rf testdir"); - assert(!isDirectory("testdir")); - system("touch testdir"); - assert(!isDirectory("testdir")); - system("rm testdir; mkdir testdir"); - assert(isDirectory("testdir")); + 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 } }; diff -Nru libwibble-0.1.28/wibble/sys/lockfile.test.h libwibble-1.1/wibble/sys/lockfile.test.h --- libwibble-0.1.28/wibble/sys/lockfile.test.h 2008-10-15 16:41:52.000000000 +0000 +++ libwibble-1.1/wibble/sys/lockfile.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -13,11 +13,15 @@ // 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 } }; diff -Nru libwibble-0.1.28/wibble/sys/mmap.cpp libwibble-1.1/wibble/sys/mmap.cpp --- libwibble-0.1.28/wibble/sys/mmap.cpp 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/sys/mmap.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include @@ -97,21 +98,23 @@ 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 == (off_t)-1) + 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 = (const char*)::mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) + 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; @@ -120,11 +123,13 @@ 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((void*)buf, size); + munmap(const_cast(buf), size); buf = 0; size = 0; } @@ -134,6 +139,7 @@ fd = -1; } filename.clear(); +#pragma GCC diagnostic pop } } diff -Nru libwibble-0.1.28/wibble/sys/mmap.h libwibble-1.1/wibble/sys/mmap.h --- libwibble-0.1.28/wibble/sys/mmap.h 2010-05-11 12:14:36.000000000 +0000 +++ libwibble-1.1/wibble/sys/mmap.h 2013-10-20 16:52:20.000000000 +0000 @@ -28,13 +28,18 @@ 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 { @@ -56,6 +61,9 @@ }; +#if __cplusplus >= 201103L +} +#endif } } diff -Nru libwibble-0.1.28/wibble/sys/mmap.test.h libwibble-1.1/wibble/sys/mmap.test.h --- libwibble-0.1.28/wibble/sys/mmap.test.h 2010-05-11 12:14:36.000000000 +0000 +++ libwibble-1.1/wibble/sys/mmap.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -19,7 +19,6 @@ */ #include -#ifdef POSIX #include #include @@ -28,17 +27,18 @@ 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, (const char*)0); + assert_eq(map.buf, static_cast(0)); - map.map("/bin/ls"); - assert_eq(map.filename, "/bin/ls"); + map.map("/bin/sh"); + assert_eq(map.filename, "/bin/sh"); assert(map.fd != -1); assert(map.size != 0u); - assert(map.buf != (const char*)0); + assert(map.buf != static_cast(0)); assert_eq(map.buf[1], 'E'); assert_eq(map.buf[2], 'L'); assert_eq(map.buf[3], 'F'); @@ -47,12 +47,12 @@ assert_eq(map.filename, string()); assert_eq(map.fd, -1); assert_eq(map.size, 0u); - assert_eq(map.buf, (const char*)0); + assert_eq(map.buf, static_cast(0)); - assert_eq(map1.filename, "/bin/ls"); + assert_eq(map1.filename, "/bin/sh"); assert(map1.fd != -1); assert(map1.size != 0u); - assert(map1.buf != (const char*)0); + assert(map1.buf != static_cast(0)); assert_eq(map1.buf[1], 'E'); assert_eq(map1.buf[2], 'L'); assert_eq(map1.buf[3], 'F'); @@ -61,8 +61,8 @@ assert_eq(map1.filename, string()); assert_eq(map1.fd, -1); assert_eq(map1.size, 0u); - assert_eq(map1.buf, (const char*)0); + assert_eq(map1.buf, static_cast(0)); +#endif } }; -#endif // vim:set ts=4 sw=4: diff -Nru libwibble-0.1.28/wibble/sys/mmap_v2.cpp libwibble-1.1/wibble/sys/mmap_v2.cpp --- libwibble-0.1.28/wibble/sys/mmap_v2.cpp 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/sys/mmap_v2.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,94 @@ +#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 diff -Nru libwibble-0.1.28/wibble/sys/mmap_v2.h libwibble-1.1/wibble/sys/mmap_v2.h --- libwibble-0.1.28/wibble/sys/mmap_v2.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/sys/mmap_v2.h 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,103 @@ +// -*- 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 diff -Nru libwibble-0.1.28/wibble/sys/mmap_v2.test.h libwibble-1.1/wibble/sys/mmap_v2.test.h --- libwibble-0.1.28/wibble/sys/mmap_v2.test.h 1970-01-01 00:00:00.000000000 +0000 +++ libwibble-1.1/wibble/sys/mmap_v2.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -0,0 +1,53 @@ +// -*- 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 + } +}; diff -Nru libwibble-0.1.28/wibble/sys/mutex.cpp libwibble-1.1/wibble/sys/mutex.cpp --- libwibble-0.1.28/wibble/sys/mutex.cpp 2010-05-11 12:14:36.000000000 +0000 +++ libwibble-1.1/wibble/sys/mutex.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -24,17 +24,18 @@ namespace wibble { namespace sys { +#ifdef POSIX bool Condition::wait(MutexLock& l, const struct timespec& abstime) { -#ifdef POSIX - if (int res = pthread_cond_timedwait(&cond, &l.mutex.mutex, &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 } +#endif } } diff -Nru libwibble-0.1.28/wibble/sys/mutex.h libwibble-1.1/wibble/sys/mutex.h --- libwibble-0.1.28/wibble/sys/mutex.h 2010-12-02 11:31:13.000000000 +0000 +++ libwibble-1.1/wibble/sys/mutex.h 2013-10-20 16:52:20.000000000 +0000 @@ -31,12 +31,8 @@ #include #include #include -struct timespec -{ - time_t tv_sec; /* seconds */ - long tv_nsec; /* nanoseconds */ -}; #endif + #include namespace wibble { @@ -73,6 +69,10 @@ #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 @@ -88,7 +88,7 @@ throw wibble::exception::System(res, "creating pthread mutex"); } - Mutex( const Mutex & m ) + Mutex( const Mutex & ) { int res = 0; #ifdef POSIX @@ -299,7 +299,7 @@ throw wibble::exception::System(res, "creating pthread condition"); } - Condition( const Condition & con ) + Condition( const Condition & ) { int res = 0; #ifdef POSIX @@ -498,6 +498,7 @@ 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 @@ -509,6 +510,7 @@ * the condition is signaled. */ bool wait(MutexLock& l, const struct timespec& abstime); +#endif }; } diff -Nru libwibble-0.1.28/wibble/sys/netbuffer.h libwibble-1.1/wibble/sys/netbuffer.h --- libwibble-0.1.28/wibble/sys/netbuffer.h 2008-10-15 14:54:28.000000000 +0000 +++ libwibble-1.1/wibble/sys/netbuffer.h 2013-10-20 16:52:20.000000000 +0000 @@ -94,7 +94,7 @@ { if (cursor + ofs + sizeof(T) >= size()) throw wibble::exception::Consistency("reading from buffer", "tried to read past the end of the buffer"); - return (const T*)data(ofs); + return static_cast(data(ofs)); } /** diff -Nru libwibble-0.1.28/wibble/sys/pipe.h libwibble-1.1/wibble/sys/pipe.h --- libwibble-0.1.28/wibble/sys/pipe.h 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/sys/pipe.h 2013-10-20 16:52:20.000000000 +0000 @@ -5,6 +5,8 @@ #ifdef POSIX #include #include +#endif +#include #include #include @@ -12,6 +14,7 @@ #include #include #include +#include #ifndef WIBBLE_SYS_PIPE_H #define WIBBLE_SYS_PIPE_H @@ -45,13 +48,19 @@ } if ( wrote == -1 ) { - if ( errno == EAGAIN || errno == EWOULDBLOCK ) + 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 ); @@ -91,8 +100,10 @@ { 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 ) {} @@ -102,8 +113,10 @@ } void close() { + wibble::sys::MutexLock __l( writer.mutex ); writer.close = true; - writer.run( fd, "" ); + if ( !writer.running ) + ::close( fd ); } bool valid() { @@ -118,11 +131,19 @@ 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 && errno != EAGAIN && errno != EWOULDBLOCK ) + if ( r == -1 && !blocking( errno ) ) throw wexcept::System( "reading from pipe" ); else if ( r == -1 ) return 0; @@ -160,15 +181,24 @@ /* 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() { @@ -187,7 +217,37 @@ }; +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 -#endif diff -Nru libwibble-0.1.28/wibble/sys/process.cpp libwibble-1.1/wibble/sys/process.cpp --- libwibble-0.1.28/wibble/sys/process.cpp 2010-12-23 18:06:13.000000000 +0000 +++ libwibble-1.1/wibble/sys/process.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -60,11 +60,14 @@ { 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) @@ -400,5 +403,27 @@ } } } +#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: diff -Nru libwibble-0.1.28/wibble/sys/process.h libwibble-1.1/wibble/sys/process.h --- libwibble-0.1.28/wibble/sys/process.h 2010-12-03 16:22:19.000000000 +0000 +++ libwibble-1.1/wibble/sys/process.h 2013-10-20 16:52:20.000000000 +0000 @@ -24,22 +24,23 @@ #include #include -#ifdef POSIX #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); -/// Get the absolute path of the current working directory -std::string getcwd(); - /// Change root directory void chroot(const std::string& dir); @@ -87,12 +88,11 @@ * do anything. */ void setproctitle(const std::string& title); +#endif } } } -#endif - // vim:set ts=4 sw=4: #endif diff -Nru libwibble-0.1.28/wibble/sys/process.test.h libwibble-1.1/wibble/sys/process.test.h --- libwibble-0.1.28/wibble/sys/process.test.h 2008-10-15 14:54:15.000000000 +0000 +++ libwibble-1.1/wibble/sys/process.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -10,16 +10,20 @@ 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 } }; diff -Nru libwibble-0.1.28/wibble/sys/signal.h libwibble-1.1/wibble/sys/signal.h --- libwibble-0.1.28/wibble/sys/signal.h 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/sys/signal.h 2013-10-20 16:52:20.000000000 +0000 @@ -10,59 +10,46 @@ namespace sys { namespace sig { +#ifdef POSIX + /** * RAII-style sigprocmask wrapper */ struct ProcMask { -#ifdef POSIX sigset_t oldset; -#else -#define SIG_BLOCK 0 // FIXME, is this reasonable? -#endif ProcMask(const sigset_t& newset, int how = SIG_BLOCK) { -#ifdef POSIX if (sigprocmask(how, &newset, &oldset) < 0) throw wibble::exception::System("setting signal mask"); -#else - assert_die(); -#endif } + ~ProcMask() { -#ifdef POSIX if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) throw wibble::exception::System("restoring signal mask"); -#endif } }; struct Action { int signum; -#ifdef POSIX struct sigaction oldact; -#endif Action(int signum, const struct sigaction& act) : signum(signum) { -#ifdef POSIX if (sigaction(signum, &act, &oldact) < 0) throw wibble::exception::System("setting signal action"); -#else - assert_die(); -#endif } ~Action() { -#ifdef POSIX if (sigaction(signum, &oldact, NULL) < 0) throw wibble::exception::System("restoring signal action"); -#endif } }; +#endif + } } } diff -Nru libwibble-0.1.28/wibble/sys/signal.test.h libwibble-1.1/wibble/sys/signal.test.h --- libwibble-0.1.28/wibble/sys/signal.test.h 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/wibble/sys/signal.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -17,6 +17,7 @@ struct TestSignal { Test sigAction() { +#ifdef POSIX struct sigaction a; a.sa_handler = test_signal_action; sigemptyset(&a.sa_mask); @@ -27,9 +28,11 @@ 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; @@ -48,6 +51,7 @@ assert_eq(counter, 0); } assert_eq(counter, 1); +#endif } }; diff -Nru libwibble-0.1.28/wibble/sys/thread.cpp libwibble-1.1/wibble/sys/thread.cpp --- libwibble-0.1.28/wibble/sys/thread.cpp 2010-05-11 12:14:36.000000000 +0000 +++ libwibble-1.1/wibble/sys/thread.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -1,7 +1,7 @@ /* * OO encapsulation of Posix threads * - * Copyright (C) 2003--2006 Enrico Zini + * 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 @@ -18,26 +18,45 @@ * 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 ((Thread*)parm)->main(); + return (static_cast(parm)->main()); } #endif #ifdef _WIN32 unsigned __stdcall Thread::Starter(void* parm) { - void* vptemp = ((Thread*)parm)->main(); - return (unsigned)vptemp; + void* vptemp = reinterpret_cast< Thread* >( parm )->main(); + reinterpret_cast< Thread* >( parm )->_result = vptemp; + return unsigned( vptemp ); } #endif @@ -93,6 +112,7 @@ { res = 0; CloseHandle(hThread); + result = _result; } #endif diff -Nru libwibble-0.1.28/wibble/sys/thread.h libwibble-1.1/wibble/sys/thread.h --- libwibble-0.1.28/wibble/sys/thread.h 2013-10-24 23:52:21.000000000 +0000 +++ libwibble-1.1/wibble/sys/thread.h 2013-10-20 16:52:20.000000000 +0000 @@ -1,7 +1,7 @@ /* -*- C++ -*- * OO encapsulation of Posix threads * - * Copyright (C) 2003--2008 Enrico Zini + * 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 @@ -23,9 +23,9 @@ #include #include -#include #ifdef POSIX #include +#include #endif #ifdef _WIN32 @@ -37,21 +37,11 @@ namespace wibble { namespace sys { -static inline void sleep( int secs ) { -#ifdef _WIN32 - Sleep( secs * 1000 ); -#else - ::sleep( secs ); -#endif -} +/// Portable version of sleep +void sleep( int secs ); -static inline void usleep( int usecs ) { -#ifdef _WIN32 - Sleep( usecs / 1000 ); -#else - ::usleep( usecs ); -#endif -} +/// Portable version of usleep +void usleep( int usecs ); /** * Encapsulates a thread @@ -98,6 +88,7 @@ #endif #ifdef _WIN32 + void *_result; unsigned int thread; HANDLE hThread; #endif diff -Nru libwibble-0.1.28/wibble/sys/thread.test.h libwibble-1.1/wibble/sys/thread.test.h --- libwibble-0.1.28/wibble/sys/thread.test.h 2008-10-15 14:54:17.000000000 +0000 +++ libwibble-1.1/wibble/sys/thread.test.h 2013-10-20 16:52:20.000000000 +0000 @@ -21,7 +21,7 @@ void* main() { res = val; - return (void*)val; + return reinterpret_cast(val); } public: Thread1(int& res, int val) : res(res), val(val) {} @@ -57,7 +57,7 @@ Thread1 assigner(val, 42); assigner.start(); - assert_eq(assigner.join(), (void*)42); + assert_eq(assigner.join(), reinterpret_cast(42)); assert_eq(val, 42); } @@ -77,7 +77,7 @@ done = true; } incrementer.quit(); - assert_eq(incrementer.join(), (void*)0); + assert_eq(incrementer.join(), static_cast(0)); } }; diff -Nru libwibble-0.1.28/wibble/test-genrunner.pl libwibble-1.1/wibble/test-genrunner.pl --- libwibble-0.1.28/wibble/test-genrunner.pl 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/test-genrunner.pl 2013-10-20 16:52:20.000000000 +0000 @@ -26,13 +26,13 @@ } for (split /[;]/, $_) { #print "parsing: $_\n"; - if (/struct ([tT]est_?)([A-Za-z]+)/) { + 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 (/Test[ \t\n]+([a-zA-Z_1-9]+)[ \t\n]*\(/) { + } 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 { @@ -76,7 +76,7 @@ print "void run_${set}_$_();\n"; } - print "RunTest run_${set}[] = {\n"; + print "RunTest run_${set}"."[] = {\n"; print "\t{ \"$_\", run_${set}_$_ },\n" for (@{$tests{$set}}); print "};\n"; } diff -Nru libwibble-0.1.28/wibble/test-main.h libwibble-1.1/wibble/test-main.h --- libwibble-0.1.28/wibble/test-main.h 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/test-main.h 2013-10-20 16:52:20.000000000 +0000 @@ -1,12 +1,14 @@ // -*- C++ -*- #include -#ifdef POSIX - #include + +#ifdef POSIX #include -#include #include +#endif + +#include #include #include @@ -42,8 +44,10 @@ } 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] ); @@ -77,8 +81,11 @@ 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 ) ) { @@ -98,17 +105,24 @@ ++ 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; @@ -154,6 +168,7 @@ } } +#ifdef POSIX void parent() { close( status_fds[1] ); close( confirm_fds[0] ); @@ -176,13 +191,16 @@ 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 ); } @@ -207,9 +225,14 @@ 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 ) ) @@ -224,8 +247,10 @@ parent(); } } else +#endif child(); } + return 0; } }; @@ -233,12 +258,3 @@ return Main().main( argc, argv ); } -#else -#include - -int main( int argc, char **argv ) { - std::cerr << "Sorry, test runner not implemented on this non-POSIX platform." << std::endl; - return 0; -} - -#endif diff -Nru libwibble-0.1.28/wibble/test-runner.h libwibble-1.1/wibble/test-runner.h --- libwibble-0.1.28/wibble/test-runner.h 2010-05-11 12:59:06.000000000 +0000 +++ libwibble-1.1/wibble/test-runner.h 2013-10-20 16:52:20.000000000 +0000 @@ -33,7 +33,6 @@ virtual void waitForAck() = 0; }; -#ifdef POSIX struct RunAll { RunSuite *suites; int suiteCount; @@ -83,4 +82,3 @@ } } }; -#endif diff -Nru libwibble-0.1.28/wibble/test.cmake libwibble-1.1/wibble/test.cmake --- libwibble-0.1.28/wibble/test.cmake 2010-05-11 12:14:36.000000000 +0000 +++ libwibble-1.1/wibble/test.cmake 2013-10-20 16:52:20.000000000 +0000 @@ -47,7 +47,7 @@ # 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} ${CMAKE_CURRENT_BINARY_DIR}/${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} ) diff -Nru libwibble-0.1.28/wibble/test.cpp libwibble-1.1/wibble/test.cpp --- libwibble-0.1.28/wibble/test.cpp 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/test.cpp 2013-10-20 16:52:20.000000000 +0000 @@ -1,12 +1,14 @@ #include +namespace wibble { int assertFailure = 0; void assert_die_fn( Location l ) { - AssertFailed f( l ); -#ifdef NDEBUG // in the NDEBUG case, the above is a no-op - abort(); -#endif + { + AssertFailed f( l ); + } + abort(); // in the NDEBUG case, the above is a no-op } +} diff -Nru libwibble-0.1.28/wibble/test.h libwibble-1.1/wibble/test.h --- libwibble-0.1.28/wibble/test.h 2010-10-30 15:10:53.000000000 +0000 +++ libwibble-1.1/wibble/test.h 2013-10-20 16:52:20.000000000 +0000 @@ -7,21 +7,26 @@ #ifndef WIBBLE_TEST_H #define WIBBLE_TEST_H +namespace wibble { + // TODO use TLS extern int assertFailure; struct Location { const char *file; int line, iteration; - const char *stmt; - Location( const char *f, int l, const char *st, int iter = -1 ) + 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) Location( __FILE__, __LINE__, stmt ) +#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) Location( __FILE__, __LINE__, stmt, i ) +#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 ) ) @@ -42,6 +47,8 @@ #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 { @@ -154,8 +161,10 @@ } inline void endAssertFailure() { - int f = assertFailure; +#ifndef NDEBUG + const int f = assertFailure; assertFailure = 0; +#endif assert( f > 1 ); } @@ -164,6 +173,8 @@ ~ExpectFailure() { endAssertFailure(); } }; +} + typedef void Test; #endif diff -Nru libwibble-0.1.28/wibble/tests.cpp libwibble-1.1/wibble/tests.cpp --- libwibble-0.1.28/wibble/tests.cpp 2008-10-15 14:55:39.000000000 +0000 +++ libwibble-1.1/wibble/tests.cpp 2013-10-05 17:34:29.000000000 +0000 @@ -1,47 +1,267 @@ /* - * Implementation of some test utility functions + * @file test-utils.cpp + * @author Enrico Zini , Peter Rockai (mornfall) + * @brief Utility functions for the unit tests * - * 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 + * Copyright (C) 2003--2013 Enrico Zini */ -#include +#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; - ss << file << ":" << line << "(" << str << ")"; - if (!testfile.empty()) - ss << "[" << testfile << ":" << testline << "(" << teststr << ")]"; - ss << ": "; - return ss.str(); + 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()); + } } -std::string Location::msg(const std::string m) const +void test_assert_istrue(WIBBLE_TEST_LOCPRM, bool val) { - return locstr() + ": " + m; + 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) - throw tut::failure(loc.locstr()); + 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()); + } } } diff -Nru libwibble-0.1.28/wibble/tests.h libwibble-1.1/wibble/tests.h --- libwibble-0.1.28/wibble/tests.h 2009-03-25 18:29:48.000000000 +0000 +++ libwibble-1.1/wibble/tests.h 2013-10-20 16:52:20.000000000 +0000 @@ -3,8 +3,11 @@ /** * @file test-utils.h - * @author Peter Rockai (mornfall) , Enrico Zini + * @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 @@ -13,6 +16,22 @@ #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; \ @@ -23,28 +42,62 @@ namespace wibble { namespace tests { +#define WIBBLE_TESTS_ALWAYS_THROWS __attribute__ ((noreturn)) + class Location { - std::string file; - int line; - std::string str; - std::string testfile; - int testline; - std::string teststr; + const Location* parent; + const wibble::tests::LocationInfo* info; + const char* file; + int line; + const char* args; -public: + Location(const Location* parent, const wibble::tests::LocationInfo& info, const char* file, int line, const char* args); - Location(const std::string& file, int line, const std::string& str) - : file(file), line(line), str(str) {} - Location(const Location& loc, - const std::string& testfile, int testline, const std::string& str) : - file(loc.file), line(loc.line), str(loc.str), - testfile(testfile), testline(testline), teststr(str) {} +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; +}; - std::string locstr() const; - std::string msg(const std::string m) 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); @@ -59,8 +112,8 @@ { std::stringstream ss; ss << "expected '" << expected << "' actual '" << actual << "'"; - throw tut::failure(loc.msg(ss.str())); - } + 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)) @@ -73,10 +126,303 @@ { std::stringstream ss; ss << "expected '" << expected << "' actual '" << actual << "'"; - throw tut::failure(loc.msg(ss.str())); - } + 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__) } }