File indexing completed on 2025-01-30 09:44:31
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED
0012 #define BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED
0013
0014 #if defined(_MSC_VER)
0015 # pragma once
0016 #endif
0017
0018 #include <boost/assert.hpp>
0019 #include <cstdio>
0020 #include <stdexcept> // logic_error.
0021 #include <boost/config.hpp> // BOOST_STATIC_CONSTANT.
0022 #include <boost/iostreams/categories.hpp>
0023 #include <boost/iostreams/detail/char_traits.hpp>
0024 #include <boost/iostreams/detail/ios.hpp> // BOOST_IOSTREAMS_FAILURE
0025 #include <boost/iostreams/read.hpp> // get
0026 #include <boost/iostreams/write.hpp> // put
0027 #include <boost/iostreams/pipeline.hpp>
0028 #include <boost/iostreams/putback.hpp>
0029 #include <boost/mpl/bool.hpp>
0030 #include <boost/throw_exception.hpp>
0031 #include <boost/type_traits/is_convertible.hpp>
0032
0033
0034 #include <boost/iostreams/detail/config/disable_warnings.hpp>
0035
0036 #define BOOST_IOSTREAMS_ASSERT_UNREACHABLE(val) \
0037 (BOOST_ASSERT("unreachable code" == 0), val) \
0038
0039
0040 namespace boost { namespace iostreams {
0041
0042 namespace newline {
0043
0044 const char CR = 0x0D;
0045 const char LF = 0x0A;
0046
0047
0048
0049
0050
0051 const int posix = 1;
0052 const int mac = 2;
0053 const int dos = 4;
0054 const int mixed = 8;
0055 const int final_newline = 16;
0056 const int platform_mask = posix | dos | mac;
0057
0058 }
0059
0060 namespace detail {
0061
0062 class newline_base {
0063 public:
0064 bool is_posix() const
0065 {
0066 return !is_mixed() && (flags_ & newline::posix) != 0;
0067 }
0068 bool is_dos() const
0069 {
0070 return !is_mixed() && (flags_ & newline::dos) != 0;
0071 }
0072 bool is_mac() const
0073 {
0074 return !is_mixed() && (flags_ & newline::mac) != 0;
0075 }
0076 bool is_mixed_posix() const { return (flags_ & newline::posix) != 0; }
0077 bool is_mixed_dos() const { return (flags_ & newline::dos) != 0; }
0078 bool is_mixed_mac() const { return (flags_ & newline::mac) != 0; }
0079 bool is_mixed() const
0080 {
0081 int platform =
0082 (flags_ & newline::posix) != 0 ?
0083 newline::posix :
0084 (flags_ & newline::dos) != 0 ?
0085 newline::dos :
0086 (flags_ & newline::mac) != 0 ?
0087 newline::mac :
0088 0;
0089 return (flags_ & ~platform & newline::platform_mask) != 0;
0090 }
0091 bool has_final_newline() const
0092 {
0093 return (flags_ & newline::final_newline) != 0;
0094 }
0095 protected:
0096 newline_base(int flags) : flags_(flags) { }
0097 int flags_;
0098 };
0099
0100 }
0101
0102 class newline_error
0103 : public BOOST_IOSTREAMS_FAILURE, public detail::newline_base
0104 {
0105 private:
0106 friend class newline_checker;
0107 newline_error(int flags)
0108 : BOOST_IOSTREAMS_FAILURE("bad line endings"),
0109 detail::newline_base(flags)
0110 { }
0111 };
0112
0113 class newline_filter {
0114 public:
0115 typedef char char_type;
0116 struct category
0117 : dual_use,
0118 filter_tag,
0119 closable_tag
0120 { };
0121
0122 explicit newline_filter(int target) : flags_(target)
0123 {
0124 if ( target != iostreams::newline::posix &&
0125 target != iostreams::newline::dos &&
0126 target != iostreams::newline::mac )
0127 {
0128 boost::throw_exception(std::logic_error("bad flags"));
0129 }
0130 }
0131
0132 template<typename Source>
0133 int get(Source& src)
0134 {
0135 using iostreams::newline::CR;
0136 using iostreams::newline::LF;
0137
0138 BOOST_ASSERT((flags_ & f_write) == 0);
0139 flags_ |= f_read;
0140
0141 if (flags_ & (f_has_LF | f_has_EOF)) {
0142 if (flags_ & f_has_LF)
0143 return newline();
0144 else
0145 return EOF;
0146 }
0147
0148 int c =
0149 (flags_ & f_has_CR) == 0 ?
0150 iostreams::get(src) :
0151 CR;
0152
0153 if (c == WOULD_BLOCK )
0154 return WOULD_BLOCK;
0155
0156 if (c == CR) {
0157 flags_ |= f_has_CR;
0158
0159 int d = iostreams::get(src);
0160 if (d == WOULD_BLOCK)
0161 return WOULD_BLOCK;
0162
0163 if (d == LF) {
0164 flags_ &= ~f_has_CR;
0165 return newline();
0166 }
0167
0168 if (d == EOF) {
0169 flags_ |= f_has_EOF;
0170 } else {
0171 iostreams::putback(src, d);
0172 }
0173
0174 flags_ &= ~f_has_CR;
0175 return newline();
0176 }
0177
0178 if (c == LF)
0179 return newline();
0180
0181 return c;
0182 }
0183
0184 template<typename Sink>
0185 bool put(Sink& dest, char c)
0186 {
0187 using iostreams::newline::CR;
0188 using iostreams::newline::LF;
0189
0190 BOOST_ASSERT((flags_ & f_read) == 0);
0191 flags_ |= f_write;
0192
0193 if ((flags_ & f_has_LF) != 0)
0194 return c == LF ?
0195 newline(dest) :
0196 newline(dest) && this->put(dest, c);
0197
0198 if (c == LF)
0199 return newline(dest);
0200
0201 if ((flags_ & f_has_CR) != 0)
0202 return newline(dest) ?
0203 this->put(dest, c) :
0204 false;
0205
0206 if (c == CR) {
0207 flags_ |= f_has_CR;
0208 return true;
0209 }
0210
0211 return iostreams::put(dest, c);
0212 }
0213
0214 template<typename Sink>
0215 void close(Sink& dest, BOOST_IOS::openmode)
0216 {
0217 if ((flags_ & f_write) != 0 && (flags_ & f_has_CR) != 0)
0218 newline_if_sink(dest);
0219 flags_ &= ~f_has_LF;
0220 }
0221 private:
0222
0223
0224 int newline()
0225 {
0226 using iostreams::newline::CR;
0227 using iostreams::newline::LF;
0228
0229 switch (flags_ & iostreams::newline::platform_mask) {
0230 case iostreams::newline::posix:
0231 return LF;
0232 case iostreams::newline::mac:
0233 return CR;
0234 case iostreams::newline::dos:
0235 if (flags_ & f_has_LF) {
0236 flags_ &= ~f_has_LF;
0237 return LF;
0238 } else {
0239 flags_ |= f_has_LF;
0240 return CR;
0241 }
0242 }
0243 return BOOST_IOSTREAMS_ASSERT_UNREACHABLE(0);
0244 }
0245
0246
0247 template<typename Sink>
0248 bool newline(Sink& dest)
0249 {
0250 using iostreams::newline::CR;
0251 using iostreams::newline::LF;
0252
0253 bool success = false;
0254 switch (flags_ & iostreams::newline::platform_mask) {
0255 case iostreams::newline::posix:
0256 success = boost::iostreams::put(dest, LF);
0257 break;
0258 case iostreams::newline::mac:
0259 success = boost::iostreams::put(dest, CR);
0260 break;
0261 case iostreams::newline::dos:
0262 if ((flags_ & f_has_LF) != 0) {
0263 success = boost::iostreams::put(dest, LF);
0264 if (success)
0265 flags_ &= ~f_has_LF;
0266 } else if (boost::iostreams::put(dest, CR)) {
0267 success = boost::iostreams::put(dest, LF);
0268 if (!success)
0269 flags_ |= f_has_LF;
0270 }
0271 break;
0272 }
0273 if (success)
0274 flags_ &= ~f_has_CR;
0275 return success;
0276 }
0277
0278
0279 template<typename Device>
0280 void newline_if_sink(Device& dest)
0281 {
0282 typedef typename iostreams::category_of<Device>::type category;
0283 newline_if_sink(dest, is_convertible<category, output>());
0284 }
0285
0286 template<typename Sink>
0287 void newline_if_sink(Sink& dest, mpl::true_) { newline(dest); }
0288
0289 template<typename Source>
0290 void newline_if_sink(Source&, mpl::false_) { }
0291
0292 enum flags {
0293 f_has_LF = 32768,
0294 f_has_CR = f_has_LF << 1,
0295 f_has_newline = f_has_CR << 1,
0296 f_has_EOF = f_has_newline << 1,
0297 f_read = f_has_EOF << 1,
0298 f_write = f_read << 1
0299 };
0300 int flags_;
0301 };
0302 BOOST_IOSTREAMS_PIPABLE(newline_filter, 0)
0303
0304 class newline_checker : public detail::newline_base {
0305 public:
0306 typedef char char_type;
0307 struct category
0308 : dual_use_filter_tag,
0309 closable_tag
0310 { };
0311 explicit newline_checker(int target = newline::mixed)
0312 : detail::newline_base(0), target_(target), open_(false)
0313 { }
0314 template<typename Source>
0315 int get(Source& src)
0316 {
0317 using newline::CR;
0318 using newline::LF;
0319
0320 if (!open_) {
0321 open_ = true;
0322 source() = 0;
0323 }
0324
0325 int c;
0326 if ((c = iostreams::get(src)) == WOULD_BLOCK)
0327 return WOULD_BLOCK;
0328
0329
0330 if (c != EOF)
0331 source() &= ~f_line_complete;
0332 if ((source() & f_has_CR) != 0) {
0333 if (c == LF) {
0334 source() |= newline::dos;
0335 source() |= f_line_complete;
0336 } else {
0337 source() |= newline::mac;
0338 if (c == EOF)
0339 source() |= f_line_complete;
0340 }
0341 } else if (c == LF) {
0342 source() |= newline::posix;
0343 source() |= f_line_complete;
0344 }
0345 source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0);
0346
0347
0348 if ( c == EOF &&
0349 (target_ & newline::final_newline) != 0 &&
0350 (source() & f_line_complete) == 0 )
0351 {
0352 fail();
0353 }
0354 if ( (target_ & newline::platform_mask) != 0 &&
0355 (source() & ~target_ & newline::platform_mask) != 0 )
0356 {
0357 fail();
0358 }
0359
0360 return c;
0361 }
0362
0363 template<typename Sink>
0364 bool put(Sink& dest, int c)
0365 {
0366 using iostreams::newline::CR;
0367 using iostreams::newline::LF;
0368
0369 if (!open_) {
0370 open_ = true;
0371 source() = 0;
0372 }
0373
0374 if (!iostreams::put(dest, c))
0375 return false;
0376
0377
0378 source() &= ~f_line_complete;
0379 if ((source() & f_has_CR) != 0) {
0380 if (c == LF) {
0381 source() |= newline::dos;
0382 source() |= f_line_complete;
0383 } else {
0384 source() |= newline::mac;
0385 }
0386 } else if (c == LF) {
0387 source() |= newline::posix;
0388 source() |= f_line_complete;
0389 }
0390 source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0);
0391
0392
0393 if ( (target_ & newline::platform_mask) != 0 &&
0394 (source() & ~target_ & newline::platform_mask) != 0 )
0395 {
0396 fail();
0397 }
0398
0399 return true;
0400 }
0401
0402 template<typename Sink>
0403 void close(Sink&, BOOST_IOS::openmode)
0404 {
0405 using iostreams::newline::final_newline;
0406
0407
0408 if ( (source() & f_has_CR) != 0 ||
0409 (source() & f_line_complete) != 0 )
0410 {
0411 source() |= final_newline;
0412 }
0413
0414
0415 source() &= ~(f_has_CR | f_line_complete);
0416
0417
0418 if ( (target_ & final_newline) != 0 &&
0419 (source() & final_newline) == 0 )
0420 {
0421 fail();
0422 }
0423 }
0424 private:
0425 void fail() { boost::throw_exception(newline_error(source())); }
0426 int& source() { return flags_; }
0427 int source() const { return flags_; }
0428
0429 enum flags {
0430 f_has_CR = 32768,
0431 f_line_complete = f_has_CR << 1
0432 };
0433
0434 int target_;
0435 bool open_;
0436 };
0437 BOOST_IOSTREAMS_PIPABLE(newline_checker, 0)
0438
0439 } }
0440
0441 #include <boost/iostreams/detail/config/enable_warnings.hpp>
0442
0443 #endif