source: NonGTP/Boost/boost/iostreams/filter/newline.hpp @ 857

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