line.hpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
  2. // (C) Copyright 2005-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. #ifndef BOOST_IOSTREAMS_LINE_FILTER_HPP_INCLUDED
  7. #define BOOST_IOSTREAMS_LINE_FILTER_HPP_INCLUDED
  8. #if defined(_MSC_VER)
  9. # pragma once
  10. #endif
  11. #include <algorithm> // min.
  12. #include <boost/assert.hpp>
  13. #include <memory> // allocator.
  14. #include <string>
  15. #include <boost/config.hpp> // BOOST_STATIC_CONSTANT.
  16. #include <boost/iostreams/categories.hpp>
  17. #include <boost/iostreams/checked_operations.hpp>
  18. #include <boost/iostreams/detail/ios.hpp> // openmode, streamsize.
  19. #include <boost/iostreams/read.hpp> // check_eof
  20. #include <boost/iostreams/pipeline.hpp>
  21. #include <boost/iostreams/write.hpp>
  22. // Must come last.
  23. #include <boost/iostreams/detail/config/disable_warnings.hpp> // VC7.1 C4244.
  24. namespace boost { namespace iostreams {
  25. //
  26. // Template name: line_filter.
  27. // Template parameters:
  28. // Ch - The character type.
  29. // Alloc - The allocator type.
  30. // Description: Filter which processes data one line at a time.
  31. //
  32. template< typename Ch,
  33. typename Alloc = std::allocator<Ch> >
  34. class basic_line_filter {
  35. private:
  36. typedef typename std::basic_string<Ch>::traits_type string_traits;
  37. public:
  38. typedef Ch char_type;
  39. typedef char_traits<char_type> traits_type;
  40. typedef std::basic_string<
  41. Ch,
  42. string_traits,
  43. Alloc
  44. > string_type;
  45. struct category
  46. : dual_use,
  47. filter_tag,
  48. multichar_tag,
  49. closable_tag
  50. { };
  51. protected:
  52. basic_line_filter(bool suppress_newlines = false)
  53. : pos_(string_type::npos),
  54. flags_(suppress_newlines ? f_suppress : 0)
  55. { }
  56. public:
  57. virtual ~basic_line_filter() { }
  58. template<typename Source>
  59. std::streamsize read(Source& src, char_type* s, std::streamsize n)
  60. {
  61. using namespace std;
  62. BOOST_ASSERT(!(flags_ & f_write));
  63. flags_ |= f_read;
  64. // Handle unfinished business.
  65. std::streamsize result = 0;
  66. if (!cur_line_.empty() && (result = read_line(s, n)) == n)
  67. return n;
  68. typename traits_type::int_type status = traits_type::good();
  69. while (result < n && !traits_type::is_eof(status)) {
  70. // Call next_line() to retrieve a line of filtered text, and
  71. // read_line() to copy it into buffer s.
  72. if (traits_type::would_block(status = next_line(src)))
  73. return result;
  74. result += read_line(s + result, n - result);
  75. }
  76. return detail::check_eof(result);
  77. }
  78. template<typename Sink>
  79. std::streamsize write(Sink& snk, const char_type* s, std::streamsize n)
  80. {
  81. using namespace std;
  82. BOOST_ASSERT(!(flags_ & f_read));
  83. flags_ |= f_write;
  84. // Handle unfinished business.
  85. if (pos_ != string_type::npos && !write_line(snk))
  86. return 0;
  87. const char_type *cur = s, *next;
  88. while (true) {
  89. // Search for the next full line in [cur, s + n), filter it
  90. // and write it to snk.
  91. typename string_type::size_type rest = n - (cur - s);
  92. if ((next = traits_type::find(cur, rest, traits_type::newline()))) {
  93. cur_line_.append(cur, next - cur);
  94. cur = next + 1;
  95. if (!write_line(snk))
  96. return static_cast<std::streamsize>(cur - s);
  97. } else {
  98. cur_line_.append(cur, rest);
  99. return n;
  100. }
  101. }
  102. }
  103. template<typename Sink>
  104. void close(Sink& snk, BOOST_IOS::openmode which)
  105. {
  106. if ((flags_ & f_read) && which == BOOST_IOS::in)
  107. close_impl();
  108. if ((flags_ & f_write) && which == BOOST_IOS::out) {
  109. try {
  110. if (!cur_line_.empty())
  111. write_line(snk);
  112. } catch (...) {
  113. try {
  114. close_impl();
  115. } catch (...) { }
  116. throw;
  117. }
  118. close_impl();
  119. }
  120. }
  121. private:
  122. virtual string_type do_filter(const string_type& line) = 0;
  123. // Copies filtered characters fron the current line into
  124. // the given buffer.
  125. std::streamsize read_line(char_type* s, std::streamsize n)
  126. {
  127. using namespace std;
  128. std::streamsize result =
  129. (std::min) (n, static_cast<std::streamsize>(cur_line_.size()));
  130. traits_type::copy(s, cur_line_.data(), result);
  131. cur_line_.erase(0, result);
  132. return result;
  133. }
  134. // Attempts to retrieve a line of text from the given source; returns
  135. // an int_type as a good/eof/would_block status code.
  136. template<typename Source>
  137. typename traits_type::int_type next_line(Source& src)
  138. {
  139. using namespace std;
  140. typename traits_type::int_type c;
  141. while ( traits_type::is_good(c = iostreams::get(src)) &&
  142. c != traits_type::newline() )
  143. {
  144. cur_line_ += traits_type::to_int_type(c);
  145. }
  146. if (!traits_type::would_block(c)) {
  147. if (!cur_line_.empty() || c == traits_type::newline())
  148. cur_line_ = do_filter(cur_line_);
  149. if (c == traits_type::newline() && (flags_ & f_suppress) == 0)
  150. cur_line_ += c;
  151. }
  152. return c; // status indicator.
  153. }
  154. // Filters the current line and attemps to write it to the given sink.
  155. // Returns true for success.
  156. template<typename Sink>
  157. bool write_line(Sink& snk)
  158. {
  159. string_type line = do_filter(cur_line_);
  160. if ((flags_ & f_suppress) == 0)
  161. line += traits_type::newline();
  162. std::streamsize amt = static_cast<std::streamsize>(line.size());
  163. bool result = iostreams::write_if(snk, line.data(), amt) == amt;
  164. if (result)
  165. clear();
  166. return result;
  167. }
  168. void close_impl()
  169. {
  170. clear();
  171. flags_ &= f_suppress;
  172. }
  173. void clear()
  174. {
  175. cur_line_.erase();
  176. pos_ = string_type::npos;
  177. }
  178. enum flag_type {
  179. f_read = 1,
  180. f_write = f_read << 1,
  181. f_suppress = f_write << 1
  182. };
  183. string_type cur_line_;
  184. typename string_type::size_type pos_;
  185. int flags_;
  186. };
  187. BOOST_IOSTREAMS_PIPABLE(basic_line_filter, 2)
  188. typedef basic_line_filter<char> line_filter;
  189. typedef basic_line_filter<wchar_t> wline_filter;
  190. } } // End namespaces iostreams, boost.
  191. #include <boost/iostreams/detail/config/enable_warnings.hpp>
  192. #endif // #ifndef BOOST_IOSTREAMS_LINE_FILTER_HPP_INCLUDED