|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r84677 - in branches/release: boost/log boost/log/detail boost/log/utility libs/log libs/log/build libs/log/src libs/log/test/run
From: andrey.semashev_at_[hidden]
Date: 2013-06-07 13:37:04
Author: andysem
Date: 2013-06-07 13:37:04 EDT (Fri, 07 Jun 2013)
New Revision: 84677
URL: http://svn.boost.org/trac/boost/changeset/84677
Log:
Merged changes from trunk. Fixes #8614.
Added:
branches/release/libs/log/test/run/util_formatting_ostream.cpp
- copied unchanged from r84674, trunk/libs/log/test/run/util_formatting_ostream.cpp
Properties modified:
branches/release/boost/log/ (props changed)
branches/release/libs/log/ (props changed)
Text files modified:
branches/release/boost/log/detail/config.hpp | 10 +
branches/release/boost/log/utility/formatting_ostream.hpp | 123 ++++++++++++++---------
branches/release/libs/log/build/Jamfile.v2 | 3
branches/release/libs/log/src/attribute_name.cpp | 12 -
branches/release/libs/log/src/unhandled_exception_count.cpp | 8 +
branches/release/libs/log/test/run/util_formatting_ostream.cpp | 211 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 306 insertions(+), 61 deletions(-)
Modified: branches/release/boost/log/detail/config.hpp
==============================================================================
--- branches/release/boost/log/detail/config.hpp Fri Jun 7 13:36:56 2013 (r84676)
+++ branches/release/boost/log/detail/config.hpp 2013-06-07 13:37:04 EDT (Fri, 07 Jun 2013) (r84677)
@@ -197,9 +197,13 @@
# define BOOST_LOG_NORETURN
#endif
-#if (defined(__CLANG__) || defined(__GNUC__)) && !defined(__QNX__)
-// The cxxabi.h is available
-#define BOOST_LOG_HAS_CXXABI_H
+// cxxabi.h availability macro
+#if defined(BOOST_CLANG)
+# if defined(__has_include) && __has_include(<cxxabi.h>)
+# define BOOST_LOG_HAS_CXXABI_H
+# endif
+#elif defined(__GNUC__) && !defined(__QNX__)
+# define BOOST_LOG_HAS_CXXABI_H
#endif
#if defined(BOOST_SYMBOL_VISIBLE)
Modified: branches/release/boost/log/utility/formatting_ostream.hpp
==============================================================================
--- branches/release/boost/log/utility/formatting_ostream.hpp Fri Jun 7 13:36:56 2013 (r84676)
+++ branches/release/boost/log/utility/formatting_ostream.hpp 2013-06-07 13:37:04 EDT (Fri, 07 Jun 2013) (r84677)
@@ -19,6 +19,7 @@
#include <string>
#include <memory>
#include <locale>
+#include <boost/utility/string_ref_fwd.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/log/detail/config.hpp>
#include <boost/log/detail/attachable_sstream_buf.hpp>
@@ -34,14 +35,6 @@
namespace boost {
-#ifndef BOOST_LOG_DOXYGEN_PASS
-
-// This forward is needed for operator<<
-template< typename CharT, typename TraitsT >
-class basic_string_ref;
-
-#endif
-
BOOST_LOG_OPEN_NAMESPACE
namespace aux {
@@ -127,6 +120,9 @@
explicit sentry(basic_formatting_ostream& strm) : base_type(strm.stream())
{
}
+
+ BOOST_LOG_DELETED_FUNCTION(sentry(sentry const&))
+ BOOST_LOG_DELETED_FUNCTION(sentry& operator= (sentry const&))
};
private:
@@ -359,11 +355,14 @@
typename aux::enable_if_char_type< OtherCharT, basic_formatting_ostream& >::type
write(const OtherCharT* p, std::streamsize size)
{
- flush();
+ sentry guard(*this);
+ if (guard)
+ {
+ m_stream.flush();
- string_type* storage = m_streambuf.storage();
- BOOST_ASSERT(storage != NULL);
- aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_stream.getloc());
+ string_type* storage = m_streambuf.storage();
+ aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_stream.getloc());
+ }
return *this;
}
@@ -386,49 +385,41 @@
basic_formatting_ostream& operator<< (char c)
{
- this->put(c);
- return *this;
+ return this->formatted_write(&c, 1);
}
basic_formatting_ostream& operator<< (const char* p)
{
- this->write(p, static_cast< std::streamsize >(std::char_traits< char >::length(p)));
- return *this;
+ return this->formatted_write(p, static_cast< std::streamsize >(std::char_traits< char >::length(p)));
}
#if !defined(BOOST_NO_INTRINSIC_WCHAR_T)
basic_formatting_ostream& operator<< (wchar_t c)
{
- this->put(c);
- return *this;
+ return this->formatted_write(&c, 1);
}
basic_formatting_ostream& operator<< (const wchar_t* p)
{
- this->write(p, static_cast< std::streamsize >(std::char_traits< wchar_t >::length(p)));
- return *this;
+ return this->formatted_write(p, static_cast< std::streamsize >(std::char_traits< wchar_t >::length(p)));
}
#endif
#if !defined(BOOST_NO_CXX11_CHAR16_T)
basic_formatting_ostream& operator<< (char16_t c)
{
- this->put(c);
- return *this;
+ return this->formatted_write(&c, 1);
}
basic_formatting_ostream& operator<< (const char16_t* p)
{
- this->write(p, static_cast< std::streamsize >(std::char_traits< char16_t >::length(p)));
- return *this;
+ return this->formatted_write(p, static_cast< std::streamsize >(std::char_traits< char16_t >::length(p)));
}
#endif
#if !defined(BOOST_NO_CXX11_CHAR32_T)
basic_formatting_ostream& operator<< (char32_t c)
{
- this->put(c);
- return *this;
+ return this->formatted_write(&c, 1);
}
basic_formatting_ostream& operator<< (const char32_t* p)
{
- this->write(p, static_cast< std::streamsize >(std::char_traits< char32_t >::length(p)));
- return *this;
+ return this->formatted_write(p, static_cast< std::streamsize >(std::char_traits< char32_t >::length(p)));
}
#endif
@@ -518,6 +509,27 @@
return *this;
}
+ template< typename OtherCharT, typename OtherTraitsT, typename OtherAllocatorT >
+ friend typename aux::enable_if_char_type< OtherCharT, basic_formatting_ostream& >::type
+ operator<< (basic_formatting_ostream& strm, std::basic_string< OtherCharT, OtherTraitsT, OtherAllocatorT > const& str)
+ {
+ return strm.formatted_write(str.c_str(), static_cast< std::streamsize >(str.size()));
+ }
+
+ template< typename OtherCharT, typename OtherTraitsT >
+ friend typename aux::enable_if_char_type< OtherCharT, basic_formatting_ostream& >::type
+ operator<< (basic_formatting_ostream& strm, basic_string_literal< OtherCharT, OtherTraitsT > const& str)
+ {
+ return strm.formatted_write(str.c_str(), static_cast< std::streamsize >(str.size()));
+ }
+
+ template< typename OtherCharT, typename OtherTraitsT >
+ friend typename aux::enable_if_char_type< OtherCharT, basic_formatting_ostream& >::type
+ operator<< (basic_formatting_ostream& strm, basic_string_ref< OtherCharT, OtherTraitsT > const& str)
+ {
+ return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size()));
+ }
+
private:
void init_stream()
{
@@ -533,6 +545,40 @@
m_stream.fill(static_cast< char_type >(' '));
}
+ template< typename OtherCharT >
+ basic_formatting_ostream& formatted_write(const OtherCharT* p, std::streamsize size)
+ {
+ sentry guard(*this);
+ if (guard)
+ {
+ m_stream.flush();
+ string_type* const storage = m_streambuf.storage();
+
+ const std::streamsize w = m_stream.width();
+ if (w <= size)
+ {
+ aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_stream.getloc());
+ }
+ else
+ {
+ const bool align_left = (m_stream.flags() & ostream_type::adjustfield) == ostream_type::left;
+ typename string_type::size_type const alignment_size =
+ static_cast< typename string_type::size_type >(w - size);
+ if (!align_left)
+ storage->append(alignment_size, m_stream.fill());
+
+ aux::code_convert(p, static_cast< std::size_t >(size), *storage, m_stream.getloc());
+
+ if (align_left)
+ storage->append(alignment_size, m_stream.fill());
+ }
+
+ m_stream.width(0);
+ }
+
+ return *this;
+ }
+
//! Copy constructor (closed)
BOOST_LOG_DELETED_FUNCTION(basic_formatting_ostream(basic_formatting_ostream const& that))
//! Assignment (closed)
@@ -619,27 +665,6 @@
return strm;
}
-template< typename CharT, typename TraitsT, typename AllocatorT, typename OtherCharT, typename OtherTraitsT, typename OtherAllocatorT >
-inline typename aux::enable_if_char_type< OtherCharT, basic_formatting_ostream< CharT, TraitsT, AllocatorT >& >::type
-operator<< (basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, std::basic_string< OtherCharT, OtherTraitsT, OtherAllocatorT > const& str)
-{
- return strm.write(str.c_str(), static_cast< std::streamsize >(str.size()));
-}
-
-template< typename CharT, typename TraitsT, typename AllocatorT, typename OtherCharT, typename OtherTraitsT >
-inline typename aux::enable_if_char_type< OtherCharT, basic_formatting_ostream< CharT, TraitsT, AllocatorT >& >::type
-operator<< (basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, basic_string_literal< OtherCharT, OtherTraitsT > const& str)
-{
- return strm.write(str.c_str(), static_cast< std::streamsize >(str.size()));
-}
-
-template< typename CharT, typename TraitsT, typename AllocatorT, typename OtherCharT, typename OtherTraitsT >
-inline typename aux::enable_if_char_type< OtherCharT, basic_formatting_ostream< CharT, TraitsT, AllocatorT >& >::type
-operator<< (basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, basic_string_ref< OtherCharT, OtherTraitsT > const& str)
-{
- return strm.write(str.data(), static_cast< std::streamsize >(str.size()));
-}
-
BOOST_LOG_CLOSE_NAMESPACE // namespace log
} // namespace boost
Modified: branches/release/libs/log/build/Jamfile.v2
==============================================================================
--- branches/release/libs/log/build/Jamfile.v2 Fri Jun 7 13:36:56 2013 (r84676)
+++ branches/release/libs/log/build/Jamfile.v2 2013-06-07 13:37:04 EDT (Fri, 07 Jun 2013) (r84677)
@@ -121,6 +121,9 @@
<target-os>freebsd:<linkflags>"-lrt"
<target-os>linux:<linkflags>"-lrt -lpthread"
<toolset>pgi:<linkflags>"-lrt"
+ : usage-requirements
+ <toolset>clang:<cxxflags>-Wno-bind-to-temporary-copy
+ <toolset>clang:<cxxflags>-Wno-unused-function
;
local BOOST_LOG_COMMON_SRC =
Modified: branches/release/libs/log/src/attribute_name.cpp
==============================================================================
--- branches/release/libs/log/src/attribute_name.cpp Fri Jun 7 13:36:56 2013 (r84676)
+++ branches/release/libs/log/src/attribute_name.cpp 2013-06-07 13:37:04 EDT (Fri, 07 Jun 2013) (r84677)
@@ -13,6 +13,7 @@
* at http://www.boost.org/libs/log/doc/log.html.
*/
+#include <cstring>
#include <deque>
#include <ostream>
#include <stdexcept>
@@ -80,23 +81,18 @@
struct order_by_name
{
typedef bool result_type;
- typedef string_type::traits_type traits_type;
bool operator() (node const& left, node const& right) const
{
- // Include terminating 0 into comparison to also check the length match
- return traits_type::compare(
- left.m_name.c_str(), right.m_name.c_str(), left.m_name.size() + 1) < 0;
+ return std::strcmp(left.m_name.c_str(), right.m_name.c_str()) < 0;
}
bool operator() (node const& left, const char* right) const
{
- // Include terminating 0 into comparison to also check the length match
- return traits_type::compare(left.m_name.c_str(), right, left.m_name.size() + 1) < 0;
+ return std::strcmp(left.m_name.c_str(), right) < 0;
}
bool operator() (const char* left, node const& right) const
{
- // Include terminating 0 into comparison to also check the length match
- return traits_type::compare(left, right.m_name.c_str(), right.m_name.size() + 1) < 0;
+ return std::strcmp(left, right.m_name.c_str()) < 0;
}
};
Modified: branches/release/libs/log/src/unhandled_exception_count.cpp
==============================================================================
--- branches/release/libs/log/src/unhandled_exception_count.cpp Fri Jun 7 13:36:56 2013 (r84676)
+++ branches/release/libs/log/src/unhandled_exception_count.cpp 2013-06-07 13:37:04 EDT (Fri, 07 Jun 2013) (r84677)
@@ -30,8 +30,13 @@
BOOST_LOG_ANONYMOUS_NAMESPACE {
#if defined(BOOST_LOG_HAS_CXXABI_H)
+// MinGW GCC 4.4 seem to not work the same way the newer GCC versions do. As a result, __cxa_get_globals based implementation will always return 0.
+// Just disable it for now and fall back to std::unhandled_exception().
+#if !defined(__MINGW32__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)))
// Only GCC 4.7 declares __cxa_get_globals() in cxxabi.h, older compilers do not expose this function but it's there
+#define BOOST_LOG_HAS_CXA_GET_GLOBALS
extern "C" void* __cxa_get_globals();
+#endif
#elif defined(_MSC_VER) && _MSC_VER >= 1400
#define BOOST_LOG_HAS_GETPTD
extern "C" void* _getptd();
@@ -42,13 +47,14 @@
//! Returns the number of currently pending exceptions
BOOST_LOG_API unsigned int unhandled_exception_count() BOOST_NOEXCEPT
{
-#if defined(BOOST_LOG_HAS_CXXABI_H)
+#if defined(BOOST_LOG_HAS_CXA_GET_GLOBALS)
// Tested on {clang 3.2,GCC 3.5.6,GCC 4.1.2,GCC 4.4.6,GCC 4.4.7}x{x32,x64}
return *(reinterpret_cast< const unsigned int* >(static_cast< const char* >(__cxa_get_globals()) + sizeof(void*))); // __cxa_eh_globals::uncaughtExceptions, x32 offset - 0x4, x64 - 0x8
#elif defined(BOOST_LOG_HAS_GETPTD)
// MSVC specific. Tested on {MSVC2005SP1,MSVC2008SP1,MSVC2010SP1,MSVC2012}x{x32,x64}.
return *(reinterpret_cast< const unsigned int* >(static_cast< const char* >(_getptd()) + (sizeof(void*) == 8 ? 0x100 : 0x90))); // _tiddata::_ProcessingThrow, x32 offset - 0x90, x64 - 0x100
#else
+ // Portable implementation. Does not allow to detect multiple nested exceptions.
return static_cast< unsigned int >(std::unhandled_exception());
#endif
}
Copied: branches/release/libs/log/test/run/util_formatting_ostream.cpp (from r84674, trunk/libs/log/test/run/util_formatting_ostream.cpp)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ branches/release/libs/log/test/run/util_formatting_ostream.cpp 2013-06-07 13:37:04 EDT (Fri, 07 Jun 2013) (r84677, copy of r84674, trunk/libs/log/test/run/util_formatting_ostream.cpp)
@@ -0,0 +1,211 @@
+/*
+ * Copyright Andrey Semashev 2007 - 2013.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+/*!
+ * \file util_formatting_ostream.cpp
+ * \author Andrey Semashev
+ * \date 26.05.2013
+ *
+ * \brief This header contains tests for the formatting output stream wrapper.
+ */
+
+#define BOOST_TEST_MODULE util_formatting_ostream
+
+#include <locale>
+#include <string>
+#include <iomanip>
+#include <sstream>
+#include <algorithm>
+#include <boost/test/unit_test.hpp>
+#include <boost/utility/string_ref.hpp>
+#include <boost/log/utility/formatting_ostream.hpp>
+#include "char_definitions.hpp"
+
+#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T)
+
+#define BOOST_UTF8_DECL
+#define BOOST_UTF8_BEGIN_NAMESPACE namespace {
+#define BOOST_UTF8_END_NAMESPACE }
+
+#include <boost/detail/utf8_codecvt_facet.hpp>
+#include <boost/detail/utf8_codecvt_facet.ipp>
+
+#endif // defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T)
+
+namespace logging = boost::log;
+
+namespace {
+
+template< typename CharT >
+struct test_impl
+{
+ typedef CharT char_type;
+ typedef test_data< char_type > strings;
+ typedef std::basic_string< char_type > string_type;
+ typedef std::basic_ostringstream< char_type > ostream_type;
+ typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type;
+
+ template< typename StringT >
+ static void width_formatting()
+ {
+ // Check that widening works
+ {
+ string_type str_fmt;
+ formatting_ostream_type strm_fmt(str_fmt);
+ strm_fmt << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
+ strm_fmt.flush();
+
+ ostream_type strm_correct;
+ strm_correct << strings::abc() << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
+
+ BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
+ }
+
+ // Check that the string is not truncated
+ {
+ string_type str_fmt;
+ formatting_ostream_type strm_fmt(str_fmt);
+ strm_fmt << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC();
+ strm_fmt.flush();
+
+ ostream_type strm_correct;
+ strm_correct << strings::abc() << std::setw(1) << (StringT)strings::abcd() << strings::ABC();
+
+ BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
+ }
+ }
+
+ template< typename StringT >
+ static void fill_formatting()
+ {
+ string_type str_fmt;
+ formatting_ostream_type strm_fmt(str_fmt);
+ strm_fmt << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
+ strm_fmt.flush();
+
+ ostream_type strm_correct;
+ strm_correct << strings::abc() << std::setfill(static_cast< char_type >('x')) << std::setw(8) << (StringT)strings::abcd() << strings::ABC();
+
+ BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
+ }
+
+ template< typename StringT >
+ static void alignment()
+ {
+ // Left alignment
+ {
+ string_type str_fmt;
+ formatting_ostream_type strm_fmt(str_fmt);
+ strm_fmt << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC();
+ strm_fmt.flush();
+
+ ostream_type strm_correct;
+ strm_correct << strings::abc() << std::setw(8) << std::left << (StringT)strings::abcd() << strings::ABC();
+
+ BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
+ }
+
+ // Right alignment
+ {
+ string_type str_fmt;
+ formatting_ostream_type strm_fmt(str_fmt);
+ strm_fmt << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC();
+ strm_fmt.flush();
+
+ ostream_type strm_correct;
+ strm_correct << strings::abc() << std::setw(8) << std::right << (StringT)strings::abcd() << strings::ABC();
+
+ BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str()));
+ }
+ }
+};
+
+} // namespace
+
+// Test support for width formatting
+BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting, CharT, char_types)
+{
+ typedef test_impl< CharT > test;
+ test::BOOST_NESTED_TEMPLATE width_formatting< const CharT* >();
+ test::BOOST_NESTED_TEMPLATE width_formatting< typename test::string_type >();
+ test::BOOST_NESTED_TEMPLATE width_formatting< boost::basic_string_ref< CharT > >();
+}
+
+// Test support for filler character setup
+BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting, CharT, char_types)
+{
+ typedef test_impl< CharT > test;
+ test::BOOST_NESTED_TEMPLATE fill_formatting< const CharT* >();
+ test::BOOST_NESTED_TEMPLATE fill_formatting< typename test::string_type >();
+ test::BOOST_NESTED_TEMPLATE fill_formatting< boost::basic_string_ref< CharT > >();
+}
+
+// Test support for text alignment
+BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types)
+{
+ typedef test_impl< CharT > test;
+ test::BOOST_NESTED_TEMPLATE alignment< const CharT* >();
+ test::BOOST_NESTED_TEMPLATE alignment< typename test::string_type >();
+ test::BOOST_NESTED_TEMPLATE alignment< boost::basic_string_ref< CharT > >();
+}
+
+#if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T)
+
+namespace {
+
+const char narrow_chars[] =
+{
+ static_cast< char >(0xd0), static_cast< char >(0x9f), static_cast< char >(0xd1), static_cast< char >(0x80),
+ static_cast< char >(0xd0), static_cast< char >(0xb8), static_cast< char >(0xd0), static_cast< char >(0xb2),
+ static_cast< char >(0xd0), static_cast< char >(0xb5), static_cast< char >(0xd1), static_cast< char >(0x82),
+ ',', ' ',
+ static_cast< char >(0xd0), static_cast< char >(0xbc), static_cast< char >(0xd0), static_cast< char >(0xb8),
+ static_cast< char >(0xd1), static_cast< char >(0x80), '!', 0
+};
+const wchar_t wide_chars[] = { 0x041f, 0x0440, 0x0438, 0x0432, 0x0435, 0x0442, L',', L' ', 0x043c, 0x0438, 0x0440, L'!', 0 };
+
+template< typename StringT >
+void test_narrowing_code_conversion()
+{
+ std::locale loc(std::locale::classic(), new utf8_codecvt_facet());
+
+ std::string str_fmt;
+ logging::formatting_ostream strm_fmt(str_fmt);
+ strm_fmt.imbue(loc);
+ strm_fmt << (StringT)wide_chars;
+ strm_fmt.flush();
+
+ BOOST_CHECK(equal_strings(str_fmt, std::string(narrow_chars)));
+}
+
+template< typename StringT >
+void test_widening_code_conversion()
+{
+ std::locale loc(std::locale::classic(), new utf8_codecvt_facet());
+
+ std::wstring str_fmt;
+ logging::wformatting_ostream strm_fmt(str_fmt);
+ strm_fmt.imbue(loc);
+ strm_fmt << (StringT)narrow_chars;
+ strm_fmt.flush();
+
+ BOOST_CHECK(equal_strings(str_fmt, std::wstring(wide_chars)));
+}
+
+} // namespace
+
+// Test character code conversion
+BOOST_AUTO_TEST_CASE(character_code_conversion)
+{
+ test_narrowing_code_conversion< const wchar_t* >();
+ test_widening_code_conversion< const char* >();
+ test_narrowing_code_conversion< std::wstring >();
+ test_widening_code_conversion< std::string >();
+ test_narrowing_code_conversion< boost::wstring_ref >();
+ test_widening_code_conversion< boost::string_ref >();
+}
+
+#endif
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk