1 // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
2 // (C) Copyright 2003-2007 Jonathan Turkanis
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
6 // See http://www.boost.org/libs/iostreams for documentation.
8 // NOTE: I hope to replace the current implementation with a much simpler
11 #ifndef BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED
12 #define BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED
18 #include <boost/assert.hpp>
20 #include <stdexcept> // logic_error.
21 #include <boost/config.hpp> // BOOST_STATIC_CONSTANT.
22 #include <boost/iostreams/categories.hpp>
23 #include <boost/iostreams/detail/char_traits.hpp>
24 #include <boost/iostreams/detail/ios.hpp> // BOOST_IOSTREAMS_FAILURE
25 #include <boost/iostreams/read.hpp> // get
26 #include <boost/iostreams/write.hpp> // put
27 #include <boost/iostreams/pipeline.hpp>
28 #include <boost/iostreams/putback.hpp>
29 #include <boost/mpl/bool.hpp>
30 #include <boost/throw_exception.hpp>
31 #include <boost/type_traits/is_convertible.hpp>
34 #include <boost/iostreams/detail/config/disable_warnings.hpp>
36 #define BOOST_IOSTREAMS_ASSERT_UNREACHABLE(val) \
37 (BOOST_ASSERT("unreachable code" == 0), val) \
40 namespace boost { namespace iostreams {
47 // Flags for configuring newline_filter.
49 // Exactly one of the following three flags must be present.
51 const int posix = 1; // Use CR as line separator.
52 const int mac = 2; // Use LF as line separator.
53 const int dos = 4; // Use CRLF as line separator.
54 const int mixed = 8; // Mixed line endings.
55 const int final_newline = 16;
56 const int platform_mask = posix | dos | mac;
58 } // End namespace newline.
66 return !is_mixed() && (flags_ & newline::posix) != 0;
70 return !is_mixed() && (flags_ & newline::dos) != 0;
74 return !is_mixed() && (flags_ & newline::mac) != 0;
76 bool is_mixed_posix() const { return (flags_ & newline::posix) != 0; }
77 bool is_mixed_dos() const { return (flags_ & newline::dos) != 0; }
78 bool is_mixed_mac() const { return (flags_ & newline::mac) != 0; }
82 (flags_ & newline::posix) != 0 ?
84 (flags_ & newline::dos) != 0 ?
86 (flags_ & newline::mac) != 0 ?
89 return (flags_ & ~platform & newline::platform_mask) != 0;
91 bool has_final_newline() const
93 return (flags_ & newline::final_newline) != 0;
96 newline_base(int flags) : flags_(flags) { }
100 } // End namespace detail.
103 : public BOOST_IOSTREAMS_FAILURE, public detail::newline_base
106 friend class newline_checker;
107 newline_error(int flags)
108 : BOOST_IOSTREAMS_FAILURE("bad line endings"),
109 detail::newline_base(flags)
113 class newline_filter {
115 typedef char char_type;
122 explicit newline_filter(int target) : flags_(target)
124 if ( target != iostreams::newline::posix &&
125 target != iostreams::newline::dos &&
126 target != iostreams::newline::mac )
128 boost::throw_exception(std::logic_error("bad flags"));
132 template<typename Source>
135 using iostreams::newline::CR;
136 using iostreams::newline::LF;
138 BOOST_ASSERT((flags_ & f_write) == 0);
141 if (flags_ & (f_has_LF | f_has_EOF)) {
142 if (flags_ & f_has_LF)
149 (flags_ & f_has_CR) == 0 ?
150 iostreams::get(src) :
153 if (c == WOULD_BLOCK )
160 if ((d = iostreams::get(src)) == WOULD_BLOCK)
171 iostreams::putback(src, d);
184 template<typename Sink>
185 bool put(Sink& dest, char c)
187 using iostreams::newline::CR;
188 using iostreams::newline::LF;
190 BOOST_ASSERT((flags_ & f_read) == 0);
193 if ((flags_ & f_has_LF) != 0)
196 newline(dest) && this->put(dest, c);
199 return newline(dest);
201 if ((flags_ & f_has_CR) != 0)
202 return newline(dest) ?
211 return iostreams::put(dest, c);
214 template<typename Sink>
215 void close(Sink& dest, BOOST_IOS::openmode)
217 if ((flags_ & f_write) != 0 && (flags_ & f_has_CR) != 0)
218 newline_if_sink(dest);
219 flags_ &= ~f_has_LF; // Restore original flags.
223 // Returns the appropriate element of a newline sequence.
226 using iostreams::newline::CR;
227 using iostreams::newline::LF;
229 switch (flags_ & iostreams::newline::platform_mask) {
230 case iostreams::newline::posix:
232 case iostreams::newline::mac:
234 case iostreams::newline::dos:
235 if (flags_ & f_has_LF) {
243 return BOOST_IOSTREAMS_ASSERT_UNREACHABLE(0);
246 // Writes a newline sequence.
247 template<typename Sink>
248 bool newline(Sink& dest)
250 using iostreams::newline::CR;
251 using iostreams::newline::LF;
253 bool success = false;
254 switch (flags_ & iostreams::newline::platform_mask) {
255 case iostreams::newline::posix:
256 success = boost::iostreams::put(dest, LF);
258 case iostreams::newline::mac:
259 success = boost::iostreams::put(dest, CR);
261 case iostreams::newline::dos:
262 if ((flags_ & f_has_LF) != 0) {
263 if ((success = boost::iostreams::put(dest, LF)))
265 } else if (boost::iostreams::put(dest, CR)) {
266 if (!(success = boost::iostreams::put(dest, LF)))
276 // Writes a newline sequence if the given device is a Sink.
277 template<typename Device>
278 void newline_if_sink(Device& dest)
280 typedef typename iostreams::category_of<Device>::type category;
281 newline_if_sink(dest, is_convertible<category, output>());
284 template<typename Sink>
285 void newline_if_sink(Sink& dest, mpl::true_) { newline(dest); }
287 template<typename Source>
288 void newline_if_sink(Source&, mpl::false_) { }
292 f_has_CR = f_has_LF << 1,
293 f_has_newline = f_has_CR << 1,
294 f_has_EOF = f_has_newline << 1,
295 f_read = f_has_EOF << 1,
296 f_write = f_read << 1
300 BOOST_IOSTREAMS_PIPABLE(newline_filter, 0)
302 class newline_checker : public detail::newline_base {
304 typedef char char_type;
306 : dual_use_filter_tag,
309 explicit newline_checker(int target = newline::mixed)
310 : detail::newline_base(0), target_(target), open_(false)
312 template<typename Source>
324 if ((c = iostreams::get(src)) == WOULD_BLOCK)
327 // Update source flags.
329 source() &= ~f_line_complete;
330 if ((source() & f_has_CR) != 0) {
332 source() |= newline::dos;
333 source() |= f_line_complete;
335 source() |= newline::mac;
337 source() |= f_line_complete;
339 } else if (c == LF) {
340 source() |= newline::posix;
341 source() |= f_line_complete;
343 source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0);
347 (target_ & newline::final_newline) != 0 &&
348 (source() & f_line_complete) == 0 )
352 if ( (target_ & newline::platform_mask) != 0 &&
353 (source() & ~target_ & newline::platform_mask) != 0 )
361 template<typename Sink>
362 bool put(Sink& dest, int c)
364 using iostreams::newline::CR;
365 using iostreams::newline::LF;
372 if (!iostreams::put(dest, c))
375 // Update source flags.
376 source() &= ~f_line_complete;
377 if ((source() & f_has_CR) != 0) {
379 source() |= newline::dos;
380 source() |= f_line_complete;
382 source() |= newline::mac;
384 } else if (c == LF) {
385 source() |= newline::posix;
386 source() |= f_line_complete;
388 source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0);
391 if ( (target_ & newline::platform_mask) != 0 &&
392 (source() & ~target_ & newline::platform_mask) != 0 )
400 template<typename Sink>
401 void close(Sink&, BOOST_IOS::openmode)
403 using iostreams::newline::final_newline;
405 // Update final_newline flag.
406 if ( (source() & f_has_CR) != 0 ||
407 (source() & f_line_complete) != 0 )
409 source() |= final_newline;
412 // Clear non-sticky flags.
413 source() &= ~(f_has_CR | f_line_complete);
416 if ( (target_ & final_newline) != 0 &&
417 (source() & final_newline) == 0 )
423 void fail() { boost::throw_exception(newline_error(source())); }
424 int& source() { return flags_; }
425 int source() const { return flags_; }
429 f_line_complete = f_has_CR << 1
432 int target_; // Represents expected input.
435 BOOST_IOSTREAMS_PIPABLE(newline_checker, 0)
437 } } // End namespaces iostreams, boost.
439 #include <boost/iostreams/detail/config/enable_warnings.hpp>
441 #endif // #ifndef BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED