newline.hpp 12 KB

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