text: /*=============================================================================
      Copyright (c) 2003 Giovanni Bajo
      Copyright (c) 2003 Martin Wille
      Copyright (c) 2003 Hartmut Kaiser
      http://spirit.sourceforge.net/
  
      Use, modification and distribution is subject to the Boost Software
      License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
      http://www.boost.org/LICENSE_1_0.txt)
  =============================================================================*/
  
  #ifndef BOOST_SPIRIT_FILE_ITERATOR_IPP
  #define BOOST_SPIRIT_FILE_ITERATOR_IPP
  
  #ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS
  #  include <windows.h>
  #endif
  
  #include <cstdio>
  #include <boost/shared_ptr.hpp>
  
  #ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS
  #  include <boost/type_traits/remove_pointer.hpp>
  #endif
  
  #ifdef BOOST_SPIRIT_FILEITERATOR_POSIX
  #  include <sys/types.h> // open, stat, mmap, munmap
  #  include <sys/stat.h>  // stat
  #  include <fcntl.h>     // open
  #  include <unistd.h>    // stat, mmap, munmap
  #  include <sys/mman.h>  // mmap, mmunmap
  #endif
  
  ///////////////////////////////////////////////////////////////////////////////
  namespace boost { namespace spirit {
  
  ///////////////////////////////////////////////////////////////////////////////
  namespace fileiter_impl {
  
  ///////////////////////////////////////////////////////////////////////////////
  //
  //  std_file_iterator
  //
  //  Base class that implements iteration through a file using standard C
  //  stream library (fopen and friends). This class and the following are
  //  the base components on which the iterator is built (through the
  //  iterator adaptor library).
  //
  //  The opened file stream (FILE) is held with a shared_ptr<>, whose
  //  custom deleter invokes fcose(). This makes the syntax of the class
  //  very easy, especially everything related to copying.
  //
  ///////////////////////////////////////////////////////////////////////////////
  
  template <typename CharT>
  class std_file_iterator
  {
  public:
      typedef CharT value_type;
  
      std_file_iterator()
      {}
  
      explicit std_file_iterator(std::string fileName)
      {
          using namespace std;
          FILE* f = fopen(fileName.c_str(), "rb");
  
          // If the file was opened, store it into
          //  the smart pointer.
          if (f)
          {
              m_file.reset(f, fclose);
              m_pos = 0;
              m_eof = false;
              update_char();
          }
      }
  
      std_file_iterator(const std_file_iterator& iter)
      { *this = iter; }
  
      std_file_iterator& operator=(const std_file_iterator& iter)
      {
          m_file = iter.m_file;
          m_curChar = iter.m_curChar;
          m_eof = iter.m_eof;
          m_pos = iter.m_pos;
  
          return *this;
      }
  
      // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context
      //  for shared_ptr to evaluate correctly
      operator bool() const
      { return m_file ? true : false; }
  
      bool operator==(const std_file_iterator& iter) const
      {
          return (m_file == iter.m_file) && (m_eof == iter.m_eof) &&
              (m_pos == iter.m_pos);
      }
  
      const CharT& get_cur_char(void) const
      {
          return m_curChar;
      }
  
      void prev_char(void)
      {
          m_pos -= sizeof(CharT);
          update_char();
      }
  
      void next_char(void)
      {
          m_pos += sizeof(CharT);
          update_char();
      }
  
      void seek_end(void)
      {
          using namespace std;
          fseek(m_file.get(), 0, SEEK_END);
          m_pos = ftell(m_file.get()) / sizeof(CharT);
          m_eof = true;
      }
  
      void advance(std::ptrdiff_t n)
      {
          m_pos += n * sizeof(CharT);
          update_char();
      }
  
      std::ptrdiff_t distance(const std_file_iterator& iter) const
      {
          return (std::ptrdiff_t)(m_pos - iter.m_pos) / sizeof(CharT);
      }
  
  private:
      boost::shared_ptr<std::FILE> m_file;
      std::size_t m_pos;
      CharT m_curChar;
      bool m_eof;
  
      void update_char(void)
      {
          using namespace std;
          if ((std::size_t)ftell(m_file.get()) != m_pos)
              fseek(m_file.get(), m_pos, SEEK_SET);
  
          m_eof = (fread(&m_curChar, sizeof(CharT), 1, m_file.get()) < 1);
      }
  };
  
  
  ///////////////////////////////////////////////////////////////////////////////
  //
  //  mmap_file_iterator
  //
  //  File iterator for memory mapped files, for now implemented on Windows and
  //  POSIX  platforms. This class has the same interface of std_file_iterator,
  //  and can be used in its place (in fact, it's the default for Windows and
  //  POSIX).
  //
  ///////////////////////////////////////////////////////////////////////////////
  
  ///////////////////////////////////////////////////////////////////////////////
  // mmap_file_iterator, Windows version
  #ifdef BOOST_SPIRIT_FILEITERATOR_WINDOWS
  template <typename CharT>
  class mmap_file_iterator
  {
  public:
      typedef CharT value_type;
  
      mmap_file_iterator()
      {}
  
      explicit mmap_file_iterator(std::string fileName)
      {
          HANDLE hFile = ::CreateFileA(
              fileName.c_str(),
              GENERIC_READ,
              FILE_SHARE_READ,
              NULL,
              OPEN_EXISTING,
              FILE_FLAG_SEQUENTIAL_SCAN,
              NULL
          );
  
          if (hFile == INVALID_HANDLE_VALUE)
              return;
  
          // Store the size of the file, it's used to construct
          //  the end iterator
          m_filesize = ::GetFileSize(hFile, NULL);
  
          HANDLE hMap = ::CreateFileMapping(
              hFile,
              NULL,
              PAGE_READONLY,
              0, 0,
              NULL
          );
  
          if (hMap == NULL)
          {
              ::CloseHandle(hFile);
              return;
          }
  
          LPVOID pMem = ::MapViewOfFile(
              hMap,
              FILE_MAP_READ,
              0, 0, 0
          );
  
          if (pMem == NULL)
          {
              ::CloseHandle(hMap);
              ::CloseHandle(hFile);
              return;
          }
  
          // We hold both the file handle and the memory pointer.
          // We can close the hMap handle now because Windows holds internally
          //  a reference to it since there is a view mapped.
          ::CloseHandle(hMap);
  
          // It seems like we can close the file handle as well (because
          //  a reference is hold by the filemap object).
          ::CloseHandle(hFile);
  
          // Store the handles inside the shared_ptr (with the custom destructors)
          m_mem.reset(static_cast<CharT*>(pMem), ::UnmapViewOfFile);
  
          // Start of the file
          m_curChar = m_mem.get();
      }
  
      mmap_file_iterator(const mmap_file_iterator& iter)
      { *this = iter; }
  
      mmap_file_iterator& operator=(const mmap_file_iterator& iter)
      {
          m_curChar = iter.m_curChar;
          m_mem = iter.m_mem;
          m_filesize = iter.m_filesize;
  
          return *this;
      }
  
      // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context
      //  for shared_ptr to evaluate correctly
      operator bool() const
      { return m_mem ? true : false; }
  
      bool operator==(const mmap_file_iterator& iter) const
      { return m_curChar == iter.m_curChar; }
  
      const CharT& get_cur_char(void) const
      { return *m_curChar; }
  
      void next_char(void)
      { m_curChar++; }
  
      void prev_char(void)
      { m_curChar--; }
  
      void advance(std::ptrdiff_t n)
      { m_curChar += n; }
  
      std::ptrdiff_t distance(const mmap_file_iterator& iter) const
      { return m_curChar - iter.m_curChar; }
  
      void seek_end(void)
      {
          m_curChar = m_mem.get() +
              (m_filesize / sizeof(CharT));
      }
  
  private:
  #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
      typedef boost::remove_pointer<HANDLE>::type handle_t;
  #else
      typedef void handle_t;
  #endif
  
      boost::shared_ptr<CharT> m_mem;
      std::size_t m_filesize;
      CharT* m_curChar;
  };
  
  #endif // BOOST_SPIRIT_FILEITERATOR_WINDOWS
  
  ///////////////////////////////////////////////////////////////////////////////
  // mmap_file_iterator, POSIX version
  #ifdef BOOST_SPIRIT_FILEITERATOR_POSIX
  template <typename CharT>
  class mmap_file_iterator
  {
  private:
      struct mapping
      {
          mapping(void *p, off_t len)
              : data(p)
              , size(len)
          { }
  
          CharT const *begin() const
          {
              return static_cast<CharT *>(data);
          }
  
          CharT const *end() const
          {
              return static_cast<CharT *>(data) + size/sizeof(CharT);
          }
  
          ~mapping()
          {
              munmap(data, size);
          }
  
      private:
          void *data;
          off_t size;
      };
  
  public:
      typedef CharT value_type;
  
      mmap_file_iterator()
      {}
  
      explicit mmap_file_iterator(std::string file_name)
      {
          // open the file
         int fd = open(file_name.c_str(),
  #ifdef O_NOCTTY
              O_NOCTTY | // if stdin was closed then opening a file
                         // would cause the file to become the controlling
                         // terminal if the filename refers to a tty. Setting
                         // O_NOCTTY inhibits this.
  #endif
              O_RDONLY);
  
          if (fd == -1)
              return;
  
          // call fstat to find get information about the file just
          // opened (size and file type)
          struct stat stat_buf;
          if ((fstat(fd, &stat_buf) != 0) || !S_ISREG(stat_buf.st_mode))
          {   // if fstat returns an error or if the file isn't a
              // regular file we give up.
              close(fd);
              return;
          }
  
          // perform the actual mapping
          void *p = mmap(0, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
          // it is safe to close() here. POSIX requires that the OS keeps a
          // second handle to the file while the file is mmapped.
          close(fd);
  
          if (p == MAP_FAILED)
              return;
  
          mapping *m = 0;
          try
          {
              m = new mapping(p, stat_buf.st_size);
          }
          catch(...)
          {
              munmap(p, stat_buf.st_size);
              throw;
          }
  
          m_mem.reset(m);
  
          // Start of the file
          m_curChar = m_mem->begin();
      }
  
      mmap_file_iterator(const mmap_file_iterator& iter)
      { *this = iter; }
  
      mmap_file_iterator& operator=(const mmap_file_iterator& iter)
      {
          m_curChar = iter.m_curChar;
          m_mem = iter.m_mem;
  
          return *this;
      }
  
      // Nasty bug in Comeau up to 4.3.0.1, we need explicit boolean context
      //  for shared_ptr to evaluate correctly
      operator bool() const
      { return m_mem ? true : false; }
  
      bool operator==(const mmap_file_iterator& iter) const
      { return m_curChar == iter.m_curChar; }
  
      const CharT& get_cur_char(void) const
      { return *m_curChar; }
  
      void next_char(void)
      { m_curChar++; }
  
      void prev_char(void)
      { m_curChar--; }
  
      void advance(signed long n)
      { m_curChar += n; }
  
      long distance(const mmap_file_iterator& iter) const
      { return m_curChar - iter.m_curChar; }
  
      void seek_end(void)
      {
          m_curChar = m_mem->end();
      }
  
  private:
  
      boost::shared_ptr<mapping> m_mem;
      CharT const* m_curChar;
  };
  
  #endif // BOOST_SPIRIT_FILEITERATOR_POSIX
  
  ///////////////////////////////////////////////////////////////////////////////
  } /* namespace boost::spirit::fileiter_impl */
  
  template <typename CharT, typename BaseIteratorT>
  file_iterator<CharT,BaseIteratorT>
  file_iterator<CharT,BaseIteratorT>::make_end(void)
  {
      file_iterator iter(*this);
      iter.base_reference().seek_end();
      return iter;
  }
  
  template <typename CharT, typename BaseIteratorT>
  file_iterator<CharT,BaseIteratorT>&
  file_iterator<CharT,BaseIteratorT>::operator=(const base_t& iter)
  {
      base_t::operator=(iter);
      return *this;
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  }} /* namespace boost::spirit */
  
  
  #endif /* BOOST_SPIRIT_FILE_ITERATOR_IPP */
  