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