// (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) | |
// (C) Copyright 2003-2007 Jonathan Turkanis | |
// 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.) | |
// See http://www.boost.org/libs/iostreams for documentation. | |
// NOTE: I hope to replace the current implementation with a much simpler | |
// one. | |
#ifndef BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED | |
#define BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED | |
#if defined(_MSC_VER) && (_MSC_VER >= 1020) | |
# pragma once | |
#endif | |
#include <boost/assert.hpp> | |
#include <cstdio> | |
#include <stdexcept> // logic_error. | |
#include <boost/config.hpp> // BOOST_STATIC_CONSTANT. | |
#include <boost/iostreams/categories.hpp> | |
#include <boost/iostreams/detail/char_traits.hpp> | |
#include <boost/iostreams/detail/ios.hpp> // BOOST_IOSTREAMS_FAILURE | |
#include <boost/iostreams/read.hpp> // get | |
#include <boost/iostreams/write.hpp> // put | |
#include <boost/iostreams/pipeline.hpp> | |
#include <boost/iostreams/putback.hpp> | |
#include <boost/mpl/bool.hpp> | |
#include <boost/throw_exception.hpp> | |
#include <boost/type_traits/is_convertible.hpp> | |
// Must come last. | |
#include <boost/iostreams/detail/config/disable_warnings.hpp> | |
#define BOOST_IOSTREAMS_ASSERT_UNREACHABLE(val) \ | |
(BOOST_ASSERT("unreachable code" == 0), val) \ | |
/**/ | |
namespace boost { namespace iostreams { | |
namespace newline { | |
const char CR = 0x0D; | |
const char LF = 0x0A; | |
// Flags for configuring newline_filter. | |
// Exactly one of the following three flags must be present. | |
const int posix = 1; // Use CR as line separator. | |
const int mac = 2; // Use LF as line separator. | |
const int dos = 4; // Use CRLF as line separator. | |
const int mixed = 8; // Mixed line endings. | |
const int final_newline = 16; | |
const int platform_mask = posix | dos | mac; | |
} // End namespace newline. | |
namespace detail { | |
class newline_base { | |
public: | |
bool is_posix() const | |
{ | |
return !is_mixed() && (flags_ & newline::posix) != 0; | |
} | |
bool is_dos() const | |
{ | |
return !is_mixed() && (flags_ & newline::dos) != 0; | |
} | |
bool is_mac() const | |
{ | |
return !is_mixed() && (flags_ & newline::mac) != 0; | |
} | |
bool is_mixed_posix() const { return (flags_ & newline::posix) != 0; } | |
bool is_mixed_dos() const { return (flags_ & newline::dos) != 0; } | |
bool is_mixed_mac() const { return (flags_ & newline::mac) != 0; } | |
bool is_mixed() const | |
{ | |
int platform = | |
(flags_ & newline::posix) != 0 ? | |
newline::posix : | |
(flags_ & newline::dos) != 0 ? | |
newline::dos : | |
(flags_ & newline::mac) != 0 ? | |
newline::mac : | |
0; | |
return (flags_ & ~platform & newline::platform_mask) != 0; | |
} | |
bool has_final_newline() const | |
{ | |
return (flags_ & newline::final_newline) != 0; | |
} | |
protected: | |
newline_base(int flags) : flags_(flags) { } | |
int flags_; | |
}; | |
} // End namespace detail. | |
class newline_error | |
: public BOOST_IOSTREAMS_FAILURE, public detail::newline_base | |
{ | |
private: | |
friend class newline_checker; | |
newline_error(int flags) | |
: BOOST_IOSTREAMS_FAILURE("bad line endings"), | |
detail::newline_base(flags) | |
{ } | |
}; | |
class newline_filter { | |
public: | |
typedef char char_type; | |
struct category | |
: dual_use, | |
filter_tag, | |
closable_tag | |
{ }; | |
explicit newline_filter(int target) : flags_(target) | |
{ | |
if ( target != iostreams::newline::posix && | |
target != iostreams::newline::dos && | |
target != iostreams::newline::mac ) | |
{ | |
boost::throw_exception(std::logic_error("bad flags")); | |
} | |
} | |
template<typename Source> | |
int get(Source& src) | |
{ | |
using iostreams::newline::CR; | |
using iostreams::newline::LF; | |
BOOST_ASSERT((flags_ & f_write) == 0); | |
flags_ |= f_read; | |
if (flags_ & (f_has_LF | f_has_EOF)) { | |
if (flags_ & f_has_LF) | |
return newline(); | |
else | |
return EOF; | |
} | |
int c = | |
(flags_ & f_has_CR) == 0 ? | |
iostreams::get(src) : | |
CR; | |
if (c == WOULD_BLOCK ) | |
return WOULD_BLOCK; | |
if (c == CR) { | |
flags_ |= f_has_CR; | |
int d; | |
if ((d = iostreams::get(src)) == WOULD_BLOCK) | |
return WOULD_BLOCK; | |
if (d == LF) { | |
flags_ &= ~f_has_CR; | |
return newline(); | |
} | |
if (d == EOF) { | |
flags_ |= f_has_EOF; | |
} else { | |
iostreams::putback(src, d); | |
} | |
flags_ &= ~f_has_CR; | |
return newline(); | |
} | |
if (c == LF) | |
return newline(); | |
return c; | |
} | |
template<typename Sink> | |
bool put(Sink& dest, char c) | |
{ | |
using iostreams::newline::CR; | |
using iostreams::newline::LF; | |
BOOST_ASSERT((flags_ & f_read) == 0); | |
flags_ |= f_write; | |
if ((flags_ & f_has_LF) != 0) | |
return c == LF ? | |
newline(dest) : | |
newline(dest) && this->put(dest, c); | |
if (c == LF) | |
return newline(dest); | |
if ((flags_ & f_has_CR) != 0) | |
return newline(dest) ? | |
this->put(dest, c) : | |
false; | |
if (c == CR) { | |
flags_ |= f_has_CR; | |
return true; | |
} | |
return iostreams::put(dest, c); | |
} | |
template<typename Sink> | |
void close(Sink& dest, BOOST_IOS::openmode) | |
{ | |
typedef typename iostreams::category_of<Sink>::type category; | |
if ((flags_ & f_write) != 0 && (flags_ & f_has_CR) != 0) | |
newline_if_sink(dest); | |
flags_ &= ~f_has_LF; // Restore original flags. | |
} | |
private: | |
// Returns the appropriate element of a newline sequence. | |
int newline() | |
{ | |
using iostreams::newline::CR; | |
using iostreams::newline::LF; | |
switch (flags_ & iostreams::newline::platform_mask) { | |
case iostreams::newline::posix: | |
return LF; | |
case iostreams::newline::mac: | |
return CR; | |
case iostreams::newline::dos: | |
if (flags_ & f_has_LF) { | |
flags_ &= ~f_has_LF; | |
return LF; | |
} else { | |
flags_ |= f_has_LF; | |
return CR; | |
} | |
} | |
return BOOST_IOSTREAMS_ASSERT_UNREACHABLE(0); | |
} | |
// Writes a newline sequence. | |
template<typename Sink> | |
bool newline(Sink& dest) | |
{ | |
using iostreams::newline::CR; | |
using iostreams::newline::LF; | |
bool success = false; | |
switch (flags_ & iostreams::newline::platform_mask) { | |
case iostreams::newline::posix: | |
success = boost::iostreams::put(dest, LF); | |
break; | |
case iostreams::newline::mac: | |
success = boost::iostreams::put(dest, CR); | |
break; | |
case iostreams::newline::dos: | |
if ((flags_ & f_has_LF) != 0) { | |
if ((success = boost::iostreams::put(dest, LF))) | |
flags_ &= ~f_has_LF; | |
} else if (boost::iostreams::put(dest, CR)) { | |
if (!(success = boost::iostreams::put(dest, LF))) | |
flags_ |= f_has_LF; | |
} | |
break; | |
} | |
if (success) | |
flags_ &= ~f_has_CR; | |
return success; | |
} | |
// Writes a newline sequence if the given device is a Sink. | |
template<typename Device> | |
void newline_if_sink(Device& dest) | |
{ | |
typedef typename iostreams::category_of<Device>::type category; | |
newline_if_sink(dest, is_convertible<category, output>()); | |
} | |
template<typename Sink> | |
void newline_if_sink(Sink& dest, mpl::true_) { newline(dest); } | |
template<typename Source> | |
void newline_if_sink(Source&, mpl::false_) { } | |
enum flags { | |
f_has_LF = 32768, | |
f_has_CR = f_has_LF << 1, | |
f_has_newline = f_has_CR << 1, | |
f_has_EOF = f_has_newline << 1, | |
f_read = f_has_EOF << 1, | |
f_write = f_read << 1 | |
}; | |
int flags_; | |
}; | |
BOOST_IOSTREAMS_PIPABLE(newline_filter, 0) | |
class newline_checker : public detail::newline_base { | |
public: | |
typedef char char_type; | |
struct category | |
: dual_use_filter_tag, | |
closable_tag | |
{ }; | |
explicit newline_checker(int target = newline::mixed) | |
: detail::newline_base(0), target_(target), open_(false) | |
{ } | |
template<typename Source> | |
int get(Source& src) | |
{ | |
using newline::CR; | |
using newline::LF; | |
if (!open_) { | |
open_ = true; | |
source() = 0; | |
} | |
int c; | |
if ((c = iostreams::get(src)) == WOULD_BLOCK) | |
return WOULD_BLOCK; | |
// Update source flags. | |
if (c != EOF) | |
source() &= ~f_line_complete; | |
if ((source() & f_has_CR) != 0) { | |
if (c == LF) { | |
source() |= newline::dos; | |
source() |= f_line_complete; | |
} else { | |
source() |= newline::mac; | |
if (c == EOF) | |
source() |= f_line_complete; | |
} | |
} else if (c == LF) { | |
source() |= newline::posix; | |
source() |= f_line_complete; | |
} | |
source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0); | |
// Check for errors. | |
if ( c == EOF && | |
(target_ & newline::final_newline) != 0 && | |
(source() & f_line_complete) == 0 ) | |
{ | |
fail(); | |
} | |
if ( (target_ & newline::platform_mask) != 0 && | |
(source() & ~target_ & newline::platform_mask) != 0 ) | |
{ | |
fail(); | |
} | |
return c; | |
} | |
template<typename Sink> | |
bool put(Sink& dest, int c) | |
{ | |
using iostreams::newline::CR; | |
using iostreams::newline::LF; | |
if (!open_) { | |
open_ = true; | |
source() = 0; | |
} | |
if (!iostreams::put(dest, c)) | |
return false; | |
// Update source flags. | |
source() &= ~f_line_complete; | |
if ((source() & f_has_CR) != 0) { | |
if (c == LF) { | |
source() |= newline::dos; | |
source() |= f_line_complete; | |
} else { | |
source() |= newline::mac; | |
} | |
} else if (c == LF) { | |
source() |= newline::posix; | |
source() |= f_line_complete; | |
} | |
source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0); | |
// Check for errors. | |
if ( (target_ & newline::platform_mask) != 0 && | |
(source() & ~target_ & newline::platform_mask) != 0 ) | |
{ | |
fail(); | |
} | |
return true; | |
} | |
template<typename Sink> | |
void close(Sink&, BOOST_IOS::openmode) | |
{ | |
using iostreams::newline::final_newline; | |
// Update final_newline flag. | |
if ( (source() & f_has_CR) != 0 || | |
(source() & f_line_complete) != 0 ) | |
{ | |
source() |= final_newline; | |
} | |
// Clear non-sticky flags. | |
source() &= ~(f_has_CR | f_line_complete); | |
// Check for errors. | |
if ( (target_ & final_newline) != 0 && | |
(source() & final_newline) == 0 ) | |
{ | |
fail(); | |
} | |
} | |
private: | |
void fail() { boost::throw_exception(newline_error(source())); } | |
int& source() { return flags_; } | |
int source() const { return flags_; } | |
enum flags { | |
f_has_CR = 32768, | |
f_line_complete = f_has_CR << 1 | |
}; | |
int target_; // Represents expected input. | |
bool open_; | |
}; | |
BOOST_IOSTREAMS_PIPABLE(newline_checker, 0) | |
} } // End namespaces iostreams, boost. | |
#include <boost/iostreams/detail/config/enable_warnings.hpp> | |
#endif // #ifndef BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED |