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