Added dotFilteredInputStream + fixed CRLFToLFFilteredOutputStream + added unit tests.
This commit is contained in:
parent
ecae17af35
commit
6bf5f9192e
@ -342,6 +342,7 @@ libvmimetest_sources = [
|
||||
[ 'tests/parser/pathTest', [ 'tests/parser/pathTest.cpp' ] ],
|
||||
[ 'tests/parser/parameterTest', [ 'tests/parser/parameterTest.cpp' ] ],
|
||||
[ 'tests/parser/textTest', [ 'tests/parser/textTest.cpp' ] ],
|
||||
[ 'tests/utility/filteredStreamTest', [ 'tests/utility/filteredStreamTest.cpp' ] ],
|
||||
[ 'tests/utility/md5Test', [ 'tests/utility/md5Test.cpp' ] ],
|
||||
[ 'tests/utility/stringProxyTest', [ 'tests/utility/stringProxyTest.cpp' ] ],
|
||||
[ 'tests/utility/stringUtilsTest', [ 'tests/utility/stringUtilsTest.cpp' ] ],
|
||||
|
@ -21,10 +21,99 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace vmime {
|
||||
namespace utility {
|
||||
|
||||
|
||||
// dotFilteredInputStream
|
||||
|
||||
dotFilteredInputStream::dotFilteredInputStream(inputStream& is)
|
||||
: m_stream(is)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
inputStream& dotFilteredInputStream::getPreviousInputStream()
|
||||
{
|
||||
return (m_stream);
|
||||
}
|
||||
|
||||
|
||||
const bool dotFilteredInputStream::eof() const
|
||||
{
|
||||
return (m_stream.eof());
|
||||
}
|
||||
|
||||
|
||||
void dotFilteredInputStream::reset()
|
||||
{
|
||||
m_previousChar2 = '\0';
|
||||
m_previousChar1 = '\0';
|
||||
|
||||
m_stream.reset();
|
||||
}
|
||||
|
||||
|
||||
const stream::size_type dotFilteredInputStream::read(value_type* const data, const size_type count)
|
||||
{
|
||||
const stream::size_type read = m_stream.read(data, count);
|
||||
|
||||
const value_type* readPtr = data;
|
||||
value_type* writePtr = data;
|
||||
|
||||
const value_type* end = data + read;
|
||||
|
||||
stream::size_type written = 0;
|
||||
|
||||
// Replace "\n.." with "\n."
|
||||
while (readPtr < end)
|
||||
{
|
||||
if (*readPtr == '.')
|
||||
{
|
||||
const value_type prevChar2 =
|
||||
(readPtr == data + 1 ? m_previousChar1 :
|
||||
readPtr == data ? m_previousChar2 : *(readPtr - 2));
|
||||
const value_type prevChar1 =
|
||||
(readPtr == data ? m_previousChar1 : *(readPtr - 1));
|
||||
|
||||
if (prevChar2 == '\n' && prevChar1 == '.')
|
||||
{
|
||||
// Ignore last dot
|
||||
}
|
||||
else
|
||||
{
|
||||
*writePtr = *readPtr;
|
||||
|
||||
++writePtr;
|
||||
++written;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*writePtr = *readPtr;
|
||||
|
||||
++writePtr;
|
||||
++written;
|
||||
}
|
||||
|
||||
++readPtr;
|
||||
}
|
||||
|
||||
m_previousChar2 = (read >= 2 ? data[read - 2] : m_previousChar1);
|
||||
m_previousChar1 = (read >= 1 ? data[read - 1] : '\0');
|
||||
|
||||
return (written);
|
||||
}
|
||||
|
||||
|
||||
const stream::size_type dotFilteredInputStream::skip(const size_type /* count */)
|
||||
{
|
||||
// Skipping bytes is not supported
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// dotFilteredOutputStream
|
||||
|
||||
dotFilteredOutputStream::dotFilteredOutputStream(outputStream& os)
|
||||
@ -42,6 +131,9 @@ outputStream& dotFilteredOutputStream::getNextOutputStream()
|
||||
void dotFilteredOutputStream::write
|
||||
(const value_type* const data, const size_type count)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
const value_type* pos = data;
|
||||
const value_type* end = data + count;
|
||||
const value_type* start = data;
|
||||
@ -85,10 +177,25 @@ outputStream& CRLFToLFFilteredOutputStream::getNextOutputStream()
|
||||
void CRLFToLFFilteredOutputStream::write
|
||||
(const value_type* const data, const size_type count)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
const value_type* pos = data;
|
||||
const value_type* end = data + count;
|
||||
const value_type* start = data;
|
||||
|
||||
// Warning: if the whole buffer finishes with '\r', this
|
||||
// last character will not be written back...
|
||||
// TODO: add a finalize() method?
|
||||
if (m_previousChar == '\r')
|
||||
{
|
||||
if (*pos != '\n')
|
||||
{
|
||||
m_stream.write("\r", 1); // write back \r
|
||||
m_previousChar = *pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace "\r\n" (CRLF) with "\n" (LF)
|
||||
while ((pos = std::find(pos, end, '\n')) != end)
|
||||
{
|
||||
@ -97,7 +204,9 @@ void CRLFToLFFilteredOutputStream::write
|
||||
|
||||
if (previousChar == '\r')
|
||||
{
|
||||
if (pos != data)
|
||||
m_stream.write(start, pos - 1 - data); // do not write \r
|
||||
|
||||
m_stream.write("\n", 1);
|
||||
|
||||
start = pos + 1;
|
||||
@ -106,8 +215,16 @@ void CRLFToLFFilteredOutputStream::write
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (data[count - 1] == '\r')
|
||||
{
|
||||
m_stream.write(start, end - start - 1);
|
||||
m_previousChar = '\r';
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stream.write(start, end - start);
|
||||
m_previousChar = data[count - 1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
166
tests/utility/filteredStreamTest.cpp
Normal file
166
tests/utility/filteredStreamTest.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
//
|
||||
// VMime library (http://www.vmime.org)
|
||||
// Copyright (C) 2002-2005 Vincent Richard <vincent@vincent-richard.net>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
|
||||
#include "../lib/unit++/unit++.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "vmime/vmime.hpp"
|
||||
#include "vmime/platforms/posix/posixHandler.hpp"
|
||||
|
||||
#include "vmime/utility/filteredStream.hpp"
|
||||
|
||||
using namespace unitpp;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class filteredStreamTest : public suite
|
||||
{
|
||||
class chunkInputStream : public vmime::utility::inputStream
|
||||
{
|
||||
private:
|
||||
|
||||
std::vector <std::string> m_chunks;
|
||||
std::vector <std::string>::size_type m_index;
|
||||
|
||||
public:
|
||||
|
||||
chunkInputStream() : m_index(0) { }
|
||||
|
||||
void addChunk(const std::string& chunk) { m_chunks.push_back(chunk); }
|
||||
|
||||
const bool eof() const { return (m_index >= m_chunks.size()); }
|
||||
void reset() { m_index = 0; }
|
||||
|
||||
const size_type read(value_type* const data, const size_type /* count */)
|
||||
{
|
||||
const std::string chunk = m_chunks[m_index];
|
||||
|
||||
// Warning: 'count' should be larger than chunk length.
|
||||
// This is OK for our tests.
|
||||
std::copy(chunk.begin(), chunk.end(), data);
|
||||
|
||||
++m_index;
|
||||
|
||||
return chunk.length();
|
||||
}
|
||||
|
||||
const size_type skip(const size_type /* count */)
|
||||
{
|
||||
// Not supported
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void testDotFilteredInputStreamHelper
|
||||
(const std::string& number, const std::string& expected,
|
||||
const std::string& c1, const std::string& c2 = "",
|
||||
const std::string& c3 = "", const std::string& c4 = "")
|
||||
{
|
||||
chunkInputStream cis;
|
||||
cis.addChunk(c1);
|
||||
if (!c2.empty()) cis.addChunk(c2);
|
||||
if (!c3.empty()) cis.addChunk(c3);
|
||||
if (!c4.empty()) cis.addChunk(c4);
|
||||
|
||||
vmime::utility::dotFilteredInputStream is(cis);
|
||||
|
||||
std::ostringstream oss;
|
||||
vmime::utility::outputStreamAdapter os(oss);
|
||||
|
||||
vmime::utility::bufferedStreamCopy(is, os);
|
||||
|
||||
assert_eq(number, expected, oss.str());
|
||||
}
|
||||
|
||||
void testDotFilteredInputStream()
|
||||
{
|
||||
testDotFilteredInputStreamHelper("1", "foo\n.bar", "foo\n..bar");
|
||||
testDotFilteredInputStreamHelper("2", "foo\n.bar", "foo\n", "..bar");
|
||||
testDotFilteredInputStreamHelper("3", "foo\n.bar", "foo\n.", ".bar");
|
||||
testDotFilteredInputStreamHelper("4", "foo\n.bar", "foo\n..", "bar");
|
||||
testDotFilteredInputStreamHelper("5", "foo\n.bar", "foo\n", ".", ".bar");
|
||||
testDotFilteredInputStreamHelper("6", "foo\n.bar", "foo\n", ".", ".", "bar");
|
||||
}
|
||||
|
||||
template <typename FILTER>
|
||||
void testFilteredOutputStreamHelper
|
||||
(const std::string& number, const std::string& expected,
|
||||
const std::string& c1, const std::string& c2 = "",
|
||||
const std::string& c3 = "", const std::string& c4 = "")
|
||||
{
|
||||
std::ostringstream oss;
|
||||
vmime::utility::outputStreamAdapter os(oss);
|
||||
|
||||
FILTER fos(os);
|
||||
|
||||
fos.write(c1.data(), c1.length());
|
||||
if (!c2.empty()) fos.write(c2.data(), c2.length());
|
||||
if (!c3.empty()) fos.write(c3.data(), c3.length());
|
||||
if (!c4.empty()) fos.write(c4.data(), c4.length());
|
||||
|
||||
assert_eq(number, expected, oss.str());
|
||||
}
|
||||
|
||||
void testDotFilteredOutputStream()
|
||||
{
|
||||
typedef vmime::utility::dotFilteredOutputStream FILTER;
|
||||
|
||||
testFilteredOutputStreamHelper<FILTER>("1", "foo\n..bar", "foo\n.bar");
|
||||
testFilteredOutputStreamHelper<FILTER>("2", "foo\n..bar", "foo\n", ".bar");
|
||||
testFilteredOutputStreamHelper<FILTER>("3", "foo\n..bar", "foo", "\n.bar");
|
||||
testFilteredOutputStreamHelper<FILTER>("4", "foo\n..bar", "foo", "\n", ".bar");
|
||||
testFilteredOutputStreamHelper<FILTER>("5", "foo\n..bar", "foo", "\n", ".", "bar");
|
||||
}
|
||||
|
||||
void testCRLFToLFFilteredOutputStream()
|
||||
{
|
||||
typedef vmime::utility::CRLFToLFFilteredOutputStream FILTER;
|
||||
|
||||
testFilteredOutputStreamHelper<FILTER>("1", "foo\nbar", "foo\r\nbar");
|
||||
testFilteredOutputStreamHelper<FILTER>("2", "foo\nbar", "foo\r\n", "bar");
|
||||
testFilteredOutputStreamHelper<FILTER>("3", "foo\nbar", "foo\r", "\nbar");
|
||||
testFilteredOutputStreamHelper<FILTER>("4", "foo\nbar", "foo", "\r\nbar");
|
||||
testFilteredOutputStreamHelper<FILTER>("5", "foo\nbar", "foo", "\r", "\nbar");
|
||||
testFilteredOutputStreamHelper<FILTER>("6", "foo\nbar", "foo", "\r", "\n", "bar");
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
filteredStreamTest() : suite("vmime::utility::filteredStream")
|
||||
{
|
||||
// VMime initialization
|
||||
vmime::platformDependant::setHandler<vmime::platforms::posix::posixHandler>();
|
||||
|
||||
add("dotFilteredInputStream", testcase(this, "dotFilteredInputStream", &filteredStreamTest::testDotFilteredInputStream));
|
||||
add("dotFilteredOutputStream", testcase(this, "dotFilteredOutputStream", &filteredStreamTest::testDotFilteredOutputStream));
|
||||
add("CRLFToLFFilteredOutputStream", testcase(this, "CRLFToLFFilteredOutputStream", &filteredStreamTest::testCRLFToLFFilteredOutputStream));
|
||||
|
||||
suite::main().add("vmime::utility::filteredStream", this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
filteredStreamTest* theTest = new filteredStreamTest();
|
||||
}
|
||||
|
@ -58,6 +58,39 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/** A filtered input stream which replaces "\n.."
|
||||
* sequences with "\n." sequences.
|
||||
*/
|
||||
|
||||
class dotFilteredInputStream : public filteredInputStream
|
||||
{
|
||||
public:
|
||||
|
||||
/** Construct a new filter for the specified input stream.
|
||||
*
|
||||
* @param is stream from which to read data to be filtered
|
||||
*/
|
||||
dotFilteredInputStream(inputStream& is);
|
||||
|
||||
inputStream& getPreviousInputStream();
|
||||
|
||||
const bool eof() const;
|
||||
|
||||
void reset();
|
||||
|
||||
const size_type read(value_type* const data, const size_type count);
|
||||
|
||||
const size_type skip(const size_type count);
|
||||
|
||||
private:
|
||||
|
||||
inputStream& m_stream;
|
||||
|
||||
value_type m_previousChar2; // (N - 1)th character of previous buffer
|
||||
value_type m_previousChar1; // (N)th (last) character of previous buffer
|
||||
};
|
||||
|
||||
|
||||
/** A filtered output stream which replaces "\n."
|
||||
* sequences with "\n.." sequences.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user