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