• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED
12 #define BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED
13 
14 #if defined(_MSC_VER)
15 # pragma once
16 #endif
17 
18 #include <boost/assert.hpp>
19 #include <cstdio>
20 #include <stdexcept>                       // logic_error.
21 #include <boost/config.hpp>                // BOOST_STATIC_CONSTANT.
22 #include <boost/iostreams/categories.hpp>
23 #include <boost/iostreams/detail/char_traits.hpp>
24 #include <boost/iostreams/detail/ios.hpp>  // BOOST_IOSTREAMS_FAILURE
25 #include <boost/iostreams/read.hpp>        // get
26 #include <boost/iostreams/write.hpp>       // put
27 #include <boost/iostreams/pipeline.hpp>
28 #include <boost/iostreams/putback.hpp>
29 #include <boost/mpl/bool.hpp>
30 #include <boost/throw_exception.hpp>
31 #include <boost/type_traits/is_convertible.hpp>
32 
33 // Must come last.
34 #include <boost/iostreams/detail/config/disable_warnings.hpp>
35 
36 #define BOOST_IOSTREAMS_ASSERT_UNREACHABLE(val) \
37     (BOOST_ASSERT("unreachable code" == 0), val) \
38     /**/
39 
40 namespace boost { namespace iostreams {
41 
42 namespace newline {
43 
44 const char CR                   = 0x0D;
45 const char LF                   = 0x0A;
46 
47     // Flags for configuring newline_filter.
48 
49 // Exactly one of the following three flags must be present.
50 
51 const int posix             = 1;    // Use CR as line separator.
52 const int mac               = 2;    // Use LF as line separator.
53 const int dos               = 4;    // Use CRLF as line separator.
54 const int mixed             = 8;    // Mixed line endings.
55 const int final_newline     = 16;
56 const int platform_mask     = posix | dos | mac;
57 
58 } // End namespace newline.
59 
60 namespace detail {
61 
62 class newline_base {
63 public:
is_posix() const64     bool is_posix() const
65     {
66         return !is_mixed() && (flags_ & newline::posix) != 0;
67     }
is_dos() const68     bool is_dos() const
69     {
70         return !is_mixed() && (flags_ & newline::dos) != 0;
71     }
is_mac() const72     bool is_mac() const
73     {
74         return !is_mixed() && (flags_ & newline::mac) != 0;
75     }
is_mixed_posix() const76     bool is_mixed_posix() const { return (flags_ & newline::posix) != 0; }
is_mixed_dos() const77     bool is_mixed_dos() const { return (flags_ & newline::dos) != 0; }
is_mixed_mac() const78     bool is_mixed_mac() const { return (flags_ & newline::mac) != 0; }
is_mixed() const79     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     }
has_final_newline() const91     bool has_final_newline() const
92     {
93         return (flags_ & newline::final_newline) != 0;
94     }
95 protected:
newline_base(int flags)96     newline_base(int flags) : flags_(flags) { }
97     int flags_;
98 };
99 
100 } // End namespace detail.
101 
102 class newline_error
103     : public BOOST_IOSTREAMS_FAILURE, public detail::newline_base
104 {
105 private:
106     friend class newline_checker;
newline_error(int flags)107     newline_error(int flags)
108         : BOOST_IOSTREAMS_FAILURE("bad line endings"),
109           detail::newline_base(flags)
110         { }
111 };
112 
113 class newline_filter {
114 public:
115     typedef char char_type;
116     struct category
117         : dual_use,
118           filter_tag,
119           closable_tag
120         { };
121 
newline_filter(int target)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             boost::throw_exception(std::logic_error("bad flags"));
129         }
130     }
131 
132     template<typename Source>
get(Source & src)133     int get(Source& src)
134     {
135         using iostreams::newline::CR;
136         using iostreams::newline::LF;
137 
138         BOOST_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>
put(Sink & dest,char c)185     bool put(Sink& dest, char c)
186     {
187         using iostreams::newline::CR;
188         using iostreams::newline::LF;
189 
190         BOOST_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>
close(Sink & dest,BOOST_IOS::openmode)215     void close(Sink& dest, BOOST_IOS::openmode)
216     {
217         if ((flags_ & f_write) != 0 && (flags_ & f_has_CR) != 0)
218             newline_if_sink(dest);
219         flags_ &= ~f_has_LF; // Restore original flags.
220     }
221 private:
222 
223     // Returns the appropriate element of a newline sequence.
newline()224     int newline()
225     {
226         using iostreams::newline::CR;
227         using iostreams::newline::LF;
228 
229         switch (flags_ & iostreams::newline::platform_mask) {
230         case iostreams::newline::posix:
231             return LF;
232         case iostreams::newline::mac:
233             return CR;
234         case iostreams::newline::dos:
235             if (flags_ & f_has_LF) {
236                 flags_ &= ~f_has_LF;
237                 return LF;
238             } else {
239                 flags_ |= f_has_LF;
240                 return CR;
241             }
242         }
243         return BOOST_IOSTREAMS_ASSERT_UNREACHABLE(0);
244     }
245 
246     // Writes a newline sequence.
247     template<typename Sink>
newline(Sink & dest)248     bool newline(Sink& dest)
249     {
250         using iostreams::newline::CR;
251         using iostreams::newline::LF;
252 
253         bool success = false;
254         switch (flags_ & iostreams::newline::platform_mask) {
255         case iostreams::newline::posix:
256             success = boost::iostreams::put(dest, LF);
257             break;
258         case iostreams::newline::mac:
259             success = boost::iostreams::put(dest, CR);
260             break;
261         case iostreams::newline::dos:
262             if ((flags_ & f_has_LF) != 0) {
263                 if ((success = boost::iostreams::put(dest, LF)))
264                     flags_ &= ~f_has_LF;
265             } else if (boost::iostreams::put(dest, CR)) {
266                 if (!(success = boost::iostreams::put(dest, LF)))
267                     flags_ |= f_has_LF;
268             }
269             break;
270         }
271         if (success)
272             flags_ &= ~f_has_CR;
273         return success;
274     }
275 
276     // Writes a newline sequence if the given device is a Sink.
277     template<typename Device>
newline_if_sink(Device & dest)278     void newline_if_sink(Device& dest)
279     {
280         typedef typename iostreams::category_of<Device>::type category;
281         newline_if_sink(dest, is_convertible<category, output>());
282     }
283 
284     template<typename Sink>
newline_if_sink(Sink & dest,mpl::true_)285     void newline_if_sink(Sink& dest, mpl::true_) { newline(dest); }
286 
287     template<typename Source>
newline_if_sink(Source &,mpl::false_)288     void newline_if_sink(Source&, mpl::false_) { }
289 
290     enum flags {
291         f_has_LF         = 32768,
292         f_has_CR         = f_has_LF << 1,
293         f_has_newline    = f_has_CR << 1,
294         f_has_EOF        = f_has_newline << 1,
295         f_read           = f_has_EOF << 1,
296         f_write          = f_read << 1
297     };
298     int       flags_;
299 };
300 BOOST_IOSTREAMS_PIPABLE(newline_filter, 0)
301 
302 class newline_checker : public detail::newline_base {
303 public:
304     typedef char                 char_type;
305     struct category
306         : dual_use_filter_tag,
307           closable_tag
308         { };
newline_checker(int target=newline::mixed)309     explicit newline_checker(int target = newline::mixed)
310         : detail::newline_base(0), target_(target), open_(false)
311         { }
312     template<typename Source>
get(Source & src)313     int get(Source& src)
314     {
315         using newline::CR;
316         using newline::LF;
317 
318         if (!open_) {
319             open_ = true;
320             source() = 0;
321         }
322 
323         int c;
324         if ((c = iostreams::get(src)) == WOULD_BLOCK)
325             return WOULD_BLOCK;
326 
327         // Update source flags.
328         if (c != EOF)
329             source() &= ~f_line_complete;
330         if ((source() & f_has_CR) != 0) {
331             if (c == LF) {
332                 source() |= newline::dos;
333                 source() |= f_line_complete;
334             } else {
335                 source() |= newline::mac;
336                 if (c == EOF)
337                     source() |= f_line_complete;
338             }
339         } else if (c == LF) {
340             source() |= newline::posix;
341             source() |= f_line_complete;
342         }
343         source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0);
344 
345         // Check for errors.
346         if ( c == EOF &&
347             (target_ & newline::final_newline) != 0 &&
348             (source() & f_line_complete) == 0 )
349         {
350             fail();
351         }
352         if ( (target_ & newline::platform_mask) != 0 &&
353              (source() & ~target_ & newline::platform_mask) != 0 )
354         {
355             fail();
356         }
357 
358         return c;
359     }
360 
361     template<typename Sink>
put(Sink & dest,int c)362     bool put(Sink& dest, int c)
363     {
364         using iostreams::newline::CR;
365         using iostreams::newline::LF;
366 
367         if (!open_) {
368             open_ = true;
369             source() = 0;
370         }
371 
372         if (!iostreams::put(dest, c))
373             return false;
374 
375          // Update source flags.
376         source() &= ~f_line_complete;
377         if ((source() & f_has_CR) != 0) {
378             if (c == LF) {
379                 source() |= newline::dos;
380                 source() |= f_line_complete;
381             } else {
382                 source() |= newline::mac;
383             }
384         } else if (c == LF) {
385             source() |= newline::posix;
386             source() |= f_line_complete;
387         }
388         source() = (source() & ~f_has_CR) | (c == CR ? f_has_CR : 0);
389 
390         // Check for errors.
391         if ( (target_ & newline::platform_mask) != 0 &&
392              (source() & ~target_ & newline::platform_mask) != 0 )
393         {
394             fail();
395         }
396 
397         return true;
398     }
399 
400     template<typename Sink>
close(Sink &,BOOST_IOS::openmode)401     void close(Sink&, BOOST_IOS::openmode)
402     {
403         using iostreams::newline::final_newline;
404 
405         // Update final_newline flag.
406         if ( (source() & f_has_CR) != 0 ||
407              (source() & f_line_complete) != 0 )
408         {
409             source() |= final_newline;
410         }
411 
412         // Clear non-sticky flags.
413         source() &= ~(f_has_CR | f_line_complete);
414 
415         // Check for errors.
416         if ( (target_ & final_newline) != 0 &&
417              (source() & final_newline) == 0 )
418         {
419             fail();
420         }
421     }
422 private:
fail()423     void fail() { boost::throw_exception(newline_error(source())); }
source()424     int& source() { return flags_; }
source() const425     int source() const { return flags_; }
426 
427     enum flags {
428         f_has_CR = 32768,
429         f_line_complete = f_has_CR << 1
430     };
431 
432     int   target_;  // Represents expected input.
433     bool  open_;
434 };
435 BOOST_IOSTREAMS_PIPABLE(newline_checker, 0)
436 
437 } } // End namespaces iostreams, boost.
438 
439 #include <boost/iostreams/detail/config/enable_warnings.hpp>
440 
441 #endif // #ifndef BOOST_IOSTREAMS_NEWLINE_FILTER_HPP_INCLUDED
442