First commit of (in progress) TeamCity reporter

Should run but is not complete
diff --git a/include/catch.hpp b/include/catch.hpp
index 4251cd0..9f82c72 100644
--- a/include/catch.hpp
+++ b/include/catch.hpp
@@ -11,11 +11,11 @@
 
 #include "internal/catch_suppress_warnings.h"
 
-#ifdef CATCH_CONFIG_MAIN
-#  define CATCH_CONFIG_RUNNER
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+#  define CATCH_IMPL
 #endif
 
-#ifdef CATCH_CONFIG_RUNNER
+#ifdef CATCH_IMPL
 #  ifndef CLARA_CONFIG_MAIN
 #    define CLARA_CONFIG_MAIN_NOT_DEFINED
 #    define CLARA_CONFIG_MAIN
@@ -43,7 +43,7 @@
 #include "internal/catch_objc.hpp"
 #endif
 
-#ifdef CATCH_CONFIG_RUNNER
+#ifdef CATCH_IMPL
 #include "internal/catch_impl.hpp"
 #endif
 
diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h
index 872b65c..6e058bf 100644
--- a/include/internal/catch_interfaces_reporter.h
+++ b/include/internal/catch_interfaces_reporter.h
@@ -238,6 +238,7 @@
 
         virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
 
+        // The return value indicates if the messages buffer should be cleared:
         virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
         virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
         virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
diff --git a/include/internal/catch_list.hpp b/include/internal/catch_list.hpp
index 34ffbc2..1c510ef 100644
--- a/include/internal/catch_list.hpp
+++ b/include/internal/catch_list.hpp
@@ -139,7 +139,7 @@
     }
 
     inline std::size_t listReporters( Config const& /*config*/ ) {
-        Catch::cout() << "Available reports:\n";
+        Catch::cout() << "Available reporters:\n";
         IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
         IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
         std::size_t maxNameLen = 0;
diff --git a/include/reporters/catch_reporter_teamcity.hpp b/include/reporters/catch_reporter_teamcity.hpp
new file mode 100644
index 0000000..127ad7b
--- /dev/null
+++ b/include/reporters/catch_reporter_teamcity.hpp
@@ -0,0 +1,124 @@
+/*
+ *  Created by Phil Nash on 19th December 2014
+ *  Copyright 2014 Two Blue Cubes Ltd. All rights reserved.
+ *
+ *  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)
+ */
+#ifndef TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
+#define TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
+
+#include "catch_reporter_bases.hpp"
+
+#include "../internal/catch_reporter_registrars.hpp"
+
+#include <cstring>
+
+namespace Catch {
+    
+    struct TeamCityReporter : StreamingReporterBase {
+        TeamCityReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config )
+        {}
+        
+        static bool replace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+            std::size_t i = str.find( replaceThis );
+            if( i != std::string::npos ) {
+                str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+                return true;
+            }
+            return false;
+        }
+        static std::string escape( std::string const& str ) {
+            std::string escaped = str;
+            while(  replace( escaped, "\'", "|\'" ) ||
+                    replace( escaped, "\n", "|n" ) ||
+                    replace( escaped, "\r", "|r" ) ||
+                    replace( escaped, "|", "||" ) ||
+                    replace( escaped, "[", "|[" ) ||
+                    replace( escaped, "]", "|]" ) );
+            return escaped;
+        }
+        virtual ~TeamCityReporter();
+
+        static std::string getDescription() {
+            return "Reports test results as TeamCity service messages";
+        }
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = true;
+            return prefs;
+        }
+        
+        // !TBD: ignored tests
+        
+        virtual void noMatchingTestCases( std::string const& /* spec */ ) {}
+        
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+            StreamingReporterBase::testGroupStarting( groupInfo );
+            stream << "##teamcity[testSuiteStarted name='"
+                << escape( groupInfo.name ) << "']\n";
+        }
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+            StreamingReporterBase::testGroupEnded( testGroupStats );
+            stream << "##teamcity[testSuiteFinished name='"
+                << escape( testGroupStats.groupInfo.name ) << "']\n";
+        }
+
+        
+        virtual void assertionStarting( AssertionInfo const& ) {
+        }
+        
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+            if( !assertionStats.assertionResult.isOk() ) {
+                stream << "##teamcity[testFailed"
+                    << " name='" << escape( currentTestCaseInfo->name )<< "'"
+                    << " message='message here'" // !TBD
+                    << " details='details?'" // !TBD
+                    << "]\n";
+            }
+            return true;
+        }
+        
+//        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+//            // !TBD
+//        }
+//        virtual void sectionEnded( SectionStats const& _sectionStats ) {
+//            // !TBD
+//        }
+        
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
+            StreamingReporterBase::testCaseStarting( testInfo );
+            stream << "##teamcity[testStarted name='"
+                << escape( testInfo.name ) << "']\n";
+        }
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+            StreamingReporterBase::testCaseEnded( testCaseStats );
+            if( !testCaseStats.stdOut.empty() )
+                stream << "##teamcity[testStdOut name='"
+                    << escape( testCaseStats.testInfo.name )
+                    << "' out='" << escape( testCaseStats.stdOut ) << "']\n";
+            if( !testCaseStats.stdErr.empty() )
+                stream << "##teamcity[testStdErr name='"
+                    << escape( testCaseStats.testInfo.name )
+                    << "' out='" << escape( testCaseStats.stdErr ) << "']\n";
+            stream << "##teamcity[testFinished name='"
+                << escape( testCaseStats.testInfo.name ) << "']\n";
+        }
+//        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+//            // !TBD
+//        }
+        
+    private:
+        
+    };
+    
+#ifdef CATCH_IMPL
+    TeamCityReporter::~TeamCityReporter() {}
+#endif
+    
+    INTERNAL_CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter )
+    
+} // end namespace Catch
+
+#endif // TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED