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.) 5 6 // See http://www.boost.org/libs/iostreams for documentation. 7 8 // NOTE: I hope to replace the current implementation with a much simpler 9 // one. 10 11 #ifndef BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED 12 #define BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED 13 14 #if defined(_MSC_VER) 15 # pragma once 16 #endif 17 18 #include <boost/assert.hpp> 19 #include <cstdio> 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> 32 33 // Must come last. 34 #include <boost/iostreams/detail/config/disable_warnings.hpp> 35 36 #define BOOST_IOSTREAMS_ASSERT_UNREACHABLE(val) \ 37 (BOOST_ASSERT("unreachable code" == 0), val) \ 38 /**/ 39 40 namespace boost { namespace iostreams { 41 42 namespace newline { 43 44 const char CR = 0x0D; 45 const char LF = 0x0A; 46 47 // Flags for configuring newline_filter. 48 49 // Exactly one of the following three flags must be present. 50 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; 57 58 } // End namespace newline. 59 60 namespace detail { 61 62 class newline_base { 63 public: is_posix() const64 bool is_posix() const 65 { 66 return !is_mixed() && (flags_ & newline::posix) != 0; 67 } is_dos() const68 bool is_dos() const 69 { 70 return !is_mixed() && (flags_ & newline::dos) != 0; 71 } is_mac() const72 bool is_mac() const 73 { 74 return !is_mixed() && (flags_ & newline::mac) != 0; 75 } is_mixed_posix() const76 bool is_mixed_posix() const { return (flags_ & newline::posix) != 0; } is_mixed_dos() const77 bool is_mixed_dos() const { return (flags_ & newline::dos) != 0; } is_mixed_mac() const78 bool is_mixed_mac() const { return (flags_ & newline::mac) != 0; } is_mixed() const79 bool is_mixed() const 80 { 81 int platform = 82 (flags_ & newline::posix) != 0 ? 83 newline::posix : 84 (flags_ & newline::dos) != 0 ? 85 newline::dos : 86 (flags_ & newline::mac) != 0 ? 87 newline::mac : 88 0; 89 return (flags_ & ~platform & newline::platform_mask) != 0; 90 } has_final_newline() const91 bool has_final_newline() const 92 { 93 return (flags_ & newline::final_newline) != 0; 94 } 95 protected: newline_base(int flags)96 newline_base(int flags) : flags_(flags) { } 97 int flags_; 98 }; 99 100 } // End namespace detail. 101 102 class newline_error 103 : public BOOST_IOSTREAMS_FAILURE, public detail::newline_base 104 { 105 private: 106 friend class newline_checker; newline_error(int flags)107 newline_error(int flags) 108 : BOOST_IOSTREAMS_FAILURE("bad line endings"), 109 detail::newline_base(flags) 110 { } 111 }; 112 113 class newline_filter { 114 public: 115 typedef char char_type; 116 struct category 117 : dual_use, 118 filter_tag, 119 closable_tag 120 { }; 121 newline_filter(int target)122 explicit newline_filter(int target) : flags_(target) 123 { 124 if ( target != iostreams::newline::posix && 125 target != iostreams::newline::dos && 126 target != iostreams::newline::mac ) 127 { 128 boost::throw_exception(std::logic_error("bad flags")); 129 } 130 } 131 132 template<typename Source> get(Source & src)133 int get(Source& src) 134 { 135 using iostreams::newline::CR; 136 using iostreams::newline::LF; 137 138 BOOST_ASSERT((flags_ & f_write) == 0); 139 flags_ |= f_read; 140 141 if (flags_ & (f_has_LF | f_has_EOF)) { 142 if (flags_ & f_has_LF) 143 return newline(); 144 else 145 return EOF; 146 } 147 148 int c = 149 (flags_ & f_has_CR) == 0 ? 150 iostreams::get(src) : 151 CR; 152 153 if (c == WOULD_BLOCK ) 154 return WOULD_BLOCK; 155 156 if (c == CR) { 157 flags_ |= f_has_CR; 158 159 int d; 160 if ((d = iostreams::get(src)) == WOULD_BLOCK) 161 return WOULD_BLOCK; 162 163 if (d == LF) { 164 flags_ &= ~f_has_CR; 165 return newline(); 166 } 167 168 if (d == EOF) { 169 flags_ |= f_has_EOF; 170 } else { 171 iostreams::putback(src, d); 172 } 173 174 flags_ &= ~f_has_CR; 175 return newline(); 176 } 177 178 if (c == LF) 179 return newline(); 180 181 return c; 182 } 183 184 template<typename Sink> put(Sink & dest,char c)185 bool put(Sink& dest, char c) 186 { 187 using iostreams::newline::CR; 188 using iostreams::newline::LF; 189 190 BOOST_ASSERT((flags_ & f_read) == 0); 191 flags_ |= f_write; 192 193 if ((flags_ & f_has_LF) != 0) 194 return c == LF ? 195 newline(dest) : 196 newline(dest) && this->put(dest, c); 197 198 if (c == LF) 199 return newline(dest); 200 201 if ((flags_ & f_has_CR) != 0) 202 return newline(dest) ? 203 this->put(dest, c) : 204 false; 205 206 if (c == CR) { 207 flags_ |= f_has_CR; 208 return true; 209 } 210 211 return iostreams::put(dest, c); 212 } 213 214 template<typename Sink> close(Sink & dest,BOOST_IOS::openmode)215 void close(Sink& dest, BOOST_IOS::openmode) 216 { 217 if ((flags_ & f_write) != 0 && (flags_ & f_has_CR) != 0) 218 newline_if_sink(dest); 219 flags_ &= ~f_has_LF; // Restore original flags. 220 } 221 private: 222 223 // Returns the appropriate element of a newline sequence. newline()224 int newline() 225 { 226 using iostreams::newline::CR; 227 using iostreams::newline::LF; 228 229 switch (flags_ & iostreams::newline::platform_mask) { 230 case iostreams::newline::posix: 231 return LF; 232 case iostreams::newline::mac: 233 return CR; 234 case iostreams::newline::dos: 235 if (flags_ & f_has_LF) { 236 flags_ &= ~f_has_LF; 237 return LF; 238 } else { 239 flags_ |= f_has_LF; 240 return CR; 241 } 242 } 243 return BOOST_IOSTREAMS_ASSERT_UNREACHABLE(0); 244 } 245 246 // Writes a newline sequence. 247 template<typename Sink> newline(Sink & dest)248 bool newline(Sink& dest) 249 { 250 using iostreams::newline::CR; 251 using iostreams::newline::LF; 252 253 bool success = false; 254 switch (flags_ & iostreams::newline::platform_mask) { 255 case iostreams::newline::posix: 256 success = boost::iostreams::put(dest, LF); 257 break; 258 case iostreams::newline::mac: 259 success = boost::iostreams::put(dest, CR); 260 break; 261 case iostreams::newline::dos: 262 if ((flags_ & f_has_LF) != 0) { 263 if ((success = boost::iostreams::put(dest, LF))) 264 flags_ &= ~f_has_LF; 265 } else if (boost::iostreams::put(dest, CR)) { 266 if (!(success = boost::iostreams::put(dest, LF))) 267 flags_ |= f_has_LF; 268 } 269 break; 270 } 271 if (success) 272 flags_ &= ~f_has_CR; 273 return success; 274 } 275 276 // Writes a newline sequence if the given device is a Sink. 277 template<typename Device> newline_if_sink(Device & dest)278 void newline_if_sink(Device& dest) 279 { 280 typedef typename iostreams::category_of<Device>::type category; 281 newline_if_sink(dest, is_convertible<category, output>()); 282 } 283 284 template<typename Sink> newline_if_sink(Sink & dest,mpl::true_)285 void newline_if_sink(Sink& dest, mpl::true_) { newline(dest); } 286 287 template<typename Source> newline_if_sink(Source &,mpl::false_)288 void newline_if_sink(Source&, mpl::false_) { } 289 290 enum flags { 291 f_has_LF = 32768, 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 297 }; 298 int flags_; 299 }; 300 BOOST_IOSTREAMS_PIPABLE(newline_filter, 0) 301 302 class newline_checker : public detail::newline_base { 303 public: 304 typedef char char_type; 305 struct category 306 : dual_use_filter_tag, 307 closable_tag 308 { }; newline_checker(int target=newline::mixed)309 explicit newline_checker(int target = newline::mixed) 310 : detail::newline_base(0), target_(target), open_(false) 311 { } 312 template<typename Source> get(Source & src)313 int get(Source& src) 314 { 315 using newline::CR; 316 using newline::LF; 317 318 if (!open_) { 319 open_ = true; 320 source() = 0; 321 } 322 323 int c; 324 if ((c = iostreams::get(src)) == WOULD_BLOCK) 325 return WOULD_BLOCK; 326 327 // Update source flags. 328 if (c != EOF) 329 source() &= ~f_line_complete; 330 if ((source() & f_has_CR) != 0) { 331 if (c == LF) { 332 source() |= newline::dos; 333 source() |= f_line_complete; 334 } else { 335 source() |= newline::mac; 336 if (c == EOF) 337 source() |= f_line_complete; 338 } 339 } else if (c == LF) { 340 source() |= newline::posix; 341 source() |= f_line_complete; 342 } 343 source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0); 344 345 // Check for errors. 346 if ( c == EOF && 347 (target_ & newline::final_newline) != 0 && 348 (source() & f_line_complete) == 0 ) 349 { 350 fail(); 351 } 352 if ( (target_ & newline::platform_mask) != 0 && 353 (source() & ~target_ & newline::platform_mask) != 0 ) 354 { 355 fail(); 356 } 357 358 return c; 359 } 360 361 template<typename Sink> put(Sink & dest,int c)362 bool put(Sink& dest, int c) 363 { 364 using iostreams::newline::CR; 365 using iostreams::newline::LF; 366 367 if (!open_) { 368 open_ = true; 369 source() = 0; 370 } 371 372 if (!iostreams::put(dest, c)) 373 return false; 374 375 // Update source flags. 376 source() &= ~f_line_complete; 377 if ((source() & f_has_CR) != 0) { 378 if (c == LF) { 379 source() |= newline::dos; 380 source() |= f_line_complete; 381 } else { 382 source() |= newline::mac; 383 } 384 } else if (c == LF) { 385 source() |= newline::posix; 386 source() |= f_line_complete; 387 } 388 source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0); 389 390 // Check for errors. 391 if ( (target_ & newline::platform_mask) != 0 && 392 (source() & ~target_ & newline::platform_mask) != 0 ) 393 { 394 fail(); 395 } 396 397 return true; 398 } 399 400 template<typename Sink> close(Sink &,BOOST_IOS::openmode)401 void close(Sink&, BOOST_IOS::openmode) 402 { 403 using iostreams::newline::final_newline; 404 405 // Update final_newline flag. 406 if ( (source() & f_has_CR) != 0 || 407 (source() & f_line_complete) != 0 ) 408 { 409 source() |= final_newline; 410 } 411 412 // Clear non-sticky flags. 413 source() &= ~(f_has_CR | f_line_complete); 414 415 // Check for errors. 416 if ( (target_ & final_newline) != 0 && 417 (source() & final_newline) == 0 ) 418 { 419 fail(); 420 } 421 } 422 private: fail()423 void fail() { boost::throw_exception(newline_error(source())); } source()424 int& source() { return flags_; } source() const425 int source() const { return flags_; } 426 427 enum flags { 428 f_has_CR = 32768, 429 f_line_complete = f_has_CR << 1 430 }; 431 432 int target_; // Represents expected input. 433 bool open_; 434 }; 435 BOOST_IOSTREAMS_PIPABLE(newline_checker, 0) 436 437 } } // End namespaces iostreams, boost. 438 439 #include <boost/iostreams/detail/config/enable_warnings.hpp> 440 441 #endif // #ifndef BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED 442