1/*=============================================================================
2    Copyright (c) 2003 Giovanni Bajo
3    Copyright (c) 2003 Martin Wille
4    Copyright (c) 2003 Hartmut Kaiser
5    http://spirit.sourceforge.net/
6
7    Use, modification and distribution is subject to the Boost Software
8    License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
9    http://www.boost.org/LICENSE_1_0.txt)
10=============================================================================*/
11
12#ifndef BOOST_SPIRIT_FILE_ITERATOR_IPP
13#define BOOST_SPIRIT_FILE_ITERATOR_IPP
14
15#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS
16#  include <windows.h>
17#endif
18
19#include <cstdio>
20#include <boost/shared_ptr.hpp>
21
22#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS
23#  include <boost/type_traits/remove_pointer.hpp>
24#endif
25
26#ifdef BOOST_SPIRIT_FILEITERATOR_POSIX
27#  include <sys/types.h> // open, stat, mmap, munmap
28#  include <sys/stat.h>  // stat
29#  include <fcntl.h>     // open
30#  include <unistd.h>    // stat, mmap, munmap
31#  include <sys/mman.h>  // mmap, mmunmap
32#endif
33
34///////////////////////////////////////////////////////////////////////////////
35namespace boost { namespace spirit {
36
37BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN
38
39///////////////////////////////////////////////////////////////////////////////
40namespace fileiter_impl {
41
42///////////////////////////////////////////////////////////////////////////////
43//
44//  std_file_iterator
45//
46//  Base class that implements iteration through a file using standard C
47//  stream library (fopen and friends). This class and the following are
48//  the base components on which the iterator is built (through the
49//  iterator adaptor library).
50//
51//  The opened file stream (FILE) is held with a shared_ptr<>, whose
52//  custom deleter invokes fcose(). This makes the syntax of the class
53//  very easy, especially everything related to copying.
54//
55///////////////////////////////////////////////////////////////////////////////
56
57template <typename CharT>
58class std_file_iterator
59{
60public:
61    typedef CharT value_type;
62
63    std_file_iterator()
64    {}
65
66    explicit std_file_iterator(std::string const& fileName)
67    {
68        using namespace std;
69        FILE* f = fopen(fileName.c_str(), "rb");
70
71        // If the file was opened, store it into
72        //  the smart pointer.
73        if (f)
74        {
75            m_file.reset(f, fclose);
76            m_pos = 0;
77            m_eof = false;
78            update_char();
79        }
80    }
81
82    std_file_iterator(const std_file_iterator& iter)
83    { *this = iter; }
84
85    std_file_iterator& operator=(const std_file_iterator& iter)
86    {
87        m_file = iter.m_file;
88        m_curChar = iter.m_curChar;
89        m_eof = iter.m_eof;
90        m_pos = iter.m_pos;
91
92        return *this;
93    }
94
95    // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context
96    //  for shared_ptr to evaluate correctly
97    operator bool() const
98    { return m_file ? true : false; }
99
100    bool operator==(const std_file_iterator& iter) const
101    {
102        return (m_file == iter.m_file) && (m_eof == iter.m_eof) &&
103            (m_pos == iter.m_pos);
104    }
105
106    const CharT& get_cur_char(void) const
107    {
108        return m_curChar;
109    }
110
111    void prev_char(void)
112    {
113        m_pos -= sizeof(CharT);
114        update_char();
115    }
116
117    void next_char(void)
118    {
119        m_pos += sizeof(CharT);
120        update_char();
121    }
122
123    void seek_end(void)
124    {
125        using namespace std;
126        fseek(m_file.get(), 0, SEEK_END);
127        m_pos = ftell(m_file.get()) / sizeof(CharT);
128        m_eof = true;
129    }
130
131    void advance(std::ptrdiff_t n)
132    {
133        m_pos += static_cast<long>(n) * sizeof(CharT);
134        update_char();
135    }
136
137    std::ptrdiff_t distance(const std_file_iterator& iter) const
138    {
139        return (std::ptrdiff_t)(m_pos - iter.m_pos) / sizeof(CharT);
140    }
141
142private:
143    boost::shared_ptr<std::FILE> m_file;
144    long m_pos;
145    CharT m_curChar;
146    bool m_eof;
147
148    void update_char(void)
149    {
150        using namespace std;
151        if (ftell(m_file.get()) != m_pos)
152            fseek(m_file.get(), m_pos, SEEK_SET);
153
154        m_eof = (fread(&m_curChar, sizeof(CharT), 1, m_file.get()) < 1);
155    }
156};
157
158
159///////////////////////////////////////////////////////////////////////////////
160//
161//  mmap_file_iterator
162//
163//  File iterator for memory mapped files, for now implemented on Windows and
164//  POSIX  platforms. This class has the same interface of std_file_iterator,
165//  and can be used in its place (in fact, it's the default for Windows and
166//  POSIX).
167//
168///////////////////////////////////////////////////////////////////////////////
169
170///////////////////////////////////////////////////////////////////////////////
171// mmap_file_iterator, Windows version
172#ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS
173template <typename CharT>
174class mmap_file_iterator
175{
176public:
177    typedef CharT value_type;
178
179    mmap_file_iterator()
180      : m_filesize(0), m_curChar(0)
181    {}
182
183    explicit mmap_file_iterator(std::string const& fileName)
184      : m_filesize(0), m_curChar(0)
185    {
186        HANDLE hFile = ::CreateFileA(
187            fileName.c_str(),
188            GENERIC_READ,
189            FILE_SHARE_READ,
190            NULL,
191            OPEN_EXISTING,
192            FILE_FLAG_SEQUENTIAL_SCAN,
193            NULL
194        );
195
196        if (hFile == INVALID_HANDLE_VALUE)
197            return;
198
199        // Store the size of the file, it's used to construct
200        //  the end iterator
201        m_filesize = ::GetFileSize(hFile, NULL);
202
203        HANDLE hMap = ::CreateFileMapping(
204            hFile,
205            NULL,
206            PAGE_READONLY,
207            0, 0,
208            NULL
209        );
210
211        if (hMap == NULL)
212        {
213            ::CloseHandle(hFile);
214            return;
215        }
216
217        LPVOID pMem = ::MapViewOfFile(
218            hMap,
219            FILE_MAP_READ,
220            0, 0, 0
221        );
222
223        if (pMem == NULL)
224        {
225            ::CloseHandle(hMap);
226            ::CloseHandle(hFile);
227            return;
228        }
229
230        // We hold both the file handle and the memory pointer.
231        // We can close the hMap handle now because Windows holds internally
232        //  a reference to it since there is a view mapped.
233        ::CloseHandle(hMap);
234
235        // It seems like we can close the file handle as well (because
236        //  a reference is hold by the filemap object).
237        ::CloseHandle(hFile);
238
239        // Store the handles inside the shared_ptr (with the custom destructors)
240        m_mem.reset(static_cast<CharT*>(pMem), ::UnmapViewOfFile);
241
242        // Start of the file
243        m_curChar = m_mem.get();
244    }
245
246    mmap_file_iterator(const mmap_file_iterator& iter)
247    { *this = iter; }
248
249    mmap_file_iterator& operator=(const mmap_file_iterator& iter)
250    {
251        m_curChar = iter.m_curChar;
252        m_mem = iter.m_mem;
253        m_filesize = iter.m_filesize;
254
255        return *this;
256    }
257
258    // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context
259    //  for shared_ptr to evaluate correctly
260    operator bool() const
261    { return m_mem ? true : false; }
262
263    bool operator==(const mmap_file_iterator& iter) const
264    { return m_curChar == iter.m_curChar; }
265
266    const CharT& get_cur_char(void) const
267    { return *m_curChar; }
268
269    void next_char(void)
270    { m_curChar++; }
271
272    void prev_char(void)
273    { m_curChar--; }
274
275    void advance(std::ptrdiff_t n)
276    { m_curChar += n; }
277
278    std::ptrdiff_t distance(const mmap_file_iterator& iter) const
279    { return m_curChar - iter.m_curChar; }
280
281    void seek_end(void)
282    {
283        m_curChar = m_mem.get() +
284            (m_filesize / sizeof(CharT));
285    }
286
287private:
288    typedef boost::remove_pointer<HANDLE>::type handle_t;
289
290    boost::shared_ptr<CharT> m_mem;
291    std::size_t m_filesize;
292    CharT* m_curChar;
293};
294
295#endif // BOOST_SPIRIT_FILEITERATOR_WINDOWS
296
297///////////////////////////////////////////////////////////////////////////////
298// mmap_file_iterator, POSIX version
299#ifdef BOOST_SPIRIT_FILEITERATOR_POSIX
300template <typename CharT>
301class mmap_file_iterator
302{
303private:
304    struct mapping
305    {
306        mapping(void *p, off_t len)
307            : data(p)
308            , size(len)
309        { }
310
311        CharT const *begin() const
312        {
313            return static_cast<CharT *>(data);
314        }
315
316        CharT const *end() const
317        {
318            return static_cast<CharT *>(data) + size/sizeof(CharT);
319        }
320
321        ~mapping()
322        {
323            munmap(static_cast<char*>(data), size);
324        }
325
326    private:
327        void *data;
328        off_t size;
329    };
330
331public:
332    typedef CharT value_type;
333
334    mmap_file_iterator()
335      : m_curChar(0)
336    {}
337
338    explicit mmap_file_iterator(std::string const& file_name)
339      : m_curChar(0)
340    {
341        // open the file
342       int fd = open(file_name.c_str(),
343#ifdef O_NOCTTY
344            O_NOCTTY | // if stdin was closed then opening a file
345                       // would cause the file to become the controlling
346                       // terminal if the filename refers to a tty. Setting
347                       // O_NOCTTY inhibits this.
348#endif
349            O_RDONLY);
350
351        if (fd == -1)
352            return;
353
354        // call fstat to find get information about the file just
355        // opened (size and file type)
356        struct stat stat_buf;
357        if ((fstat(fd, &stat_buf) != 0) || !S_ISREG(stat_buf.st_mode))
358        {   // if fstat returns an error or if the file isn't a
359            // regular file we give up.
360            close(fd);
361            return;
362        }
363
364        // perform the actual mapping
365        void *p = mmap(0, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
366        // it is safe to close() here. POSIX requires that the OS keeps a
367        // second handle to the file while the file is mmapped.
368        close(fd);
369
370        if (p == MAP_FAILED)
371            return;
372
373        mapping *m = 0;
374        try
375        {
376            m = new mapping(p, stat_buf.st_size);
377        }
378        catch(...)
379        {
380            munmap(static_cast<char*>(p), stat_buf.st_size);
381            throw;
382        }
383
384        m_mem.reset(m);
385
386        // Start of the file
387        m_curChar = m_mem->begin();
388    }
389
390    mmap_file_iterator(const mmap_file_iterator& iter)
391    { *this = iter; }
392
393    mmap_file_iterator& operator=(const mmap_file_iterator& iter)
394    {
395        m_curChar = iter.m_curChar;
396        m_mem = iter.m_mem;
397
398        return *this;
399    }
400
401    // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context
402    //  for shared_ptr to evaluate correctly
403    operator bool() const
404    { return m_mem ? true : false; }
405
406    bool operator==(const mmap_file_iterator& iter) const
407    { return m_curChar == iter.m_curChar; }
408
409    const CharT& get_cur_char(void) const
410    { return *m_curChar; }
411
412    void next_char(void)
413    { m_curChar++; }
414
415    void prev_char(void)
416    { m_curChar--; }
417
418    void advance(signed long n)
419    { m_curChar += n; }
420
421    long distance(const mmap_file_iterator& iter) const
422    { return m_curChar - iter.m_curChar; }
423
424    void seek_end(void)
425    {
426        m_curChar = m_mem->end();
427    }
428
429private:
430
431    boost::shared_ptr<mapping> m_mem;
432    CharT const* m_curChar;
433};
434
435#endif // BOOST_SPIRIT_FILEITERATOR_POSIX
436
437///////////////////////////////////////////////////////////////////////////////
438} /* namespace boost::spirit::fileiter_impl */
439
440template <typename CharT, typename BaseIteratorT>
441file_iterator<CharT,BaseIteratorT>
442file_iterator<CharT,BaseIteratorT>::make_end(void)
443{
444    file_iterator iter(*this);
445    iter.base_reference().seek_end();
446    return iter;
447}
448
449template <typename CharT, typename BaseIteratorT>
450file_iterator<CharT,BaseIteratorT>&
451file_iterator<CharT,BaseIteratorT>::operator=(const base_t& iter)
452{
453    base_t::operator=(iter);
454    return *this;
455}
456
457///////////////////////////////////////////////////////////////////////////////
458BOOST_SPIRIT_CLASSIC_NAMESPACE_END
459
460}} /* namespace boost::spirit */
461
462
463#endif /* BOOST_SPIRIT_FILE_ITERATOR_IPP */
464