The Artima Developer Community
Sponsored Link

Definition of the unixstl::glob_sequence class

This page shows globsequencemethods.h, which is described in the article, "Wild-card Searches of UNIX Directories with Random-Access Iterators":

// globsequencemethods.h: Definition of the unixstl::glob_sequence class

... // Includes (as shown in globsequence.h)

inline /* explicit */ glob_sequence::glob_sequence(char_type const *pattern, unsigned flags /* = noSort */)
  : m_flags(validate_flags_(flags))
  , m_buffer(1)
{
  m_cItems = init_glob_(NULL, pattern);

  unixstl_assert((0 == m_cItems) == (NULL == m_base));
}

inline glob_sequence::glob_sequence(char_type const *directory, char_type const *pattern, unsigned flags /* = noSort */)
  : m_flags(validate_flags_(flags))
  , m_buffer(1)
{
  m_cItems = init_glob_(directory, pattern);

  unixstl_assert((0 == m_cItems) == (NULL == m_base));
}

inline glob_sequence::~glob_sequence()
{
  unixstl_assert((0 == m_cItems) == (NULL == m_base));

  if(NULL != m_base)
  {
    globfree(&m_glob);
  }
}

inline size_t glob_sequence::size() const
{
  return m_cItems;
}

inline us_bool_t glob_sequence::empty() const
{
  return 0 == size();
}

inline value_type const glob_sequence::operator [](size_type index) const
{
  unixstl_message_assert("index access out of range in glob_sequence", index < 1 + size()); // Has to be +1, since legitimate to take address of one-past-the-end

  return m_base[index];
}

inline const_iterator glob_sequence::begin() const
{
  return m_base;
}

inline const_iterator glob_sequence::end() const
{
  return m_base + m_cItems;
}

#ifdef __STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT
inline const_reverse_iterator glob_sequence::rbegin() const
{
  return const_reverse_iterator(end());
}

inline const_reverse_iterator glob_sequence::rend() const
{
  return const_reverse_iterator(begin());
}
#endif /* __STLSOFT_CF_BIDIRECTIONAL_ITERATOR_SUPPORT */

inline /* static */ unsigned glob_sequence::validate_flags_(unsigned flags)
{
  if(0 == (flags & (directories | files)))
  {
    flags |= (directories | files);
  }

  if(0 == (flags & directories))
  {
    // It's more efficient to not bother doing a separate dots check if all
    // directories are being elided.
    flags |= includeDots;

    // Since we're not going to be returning directories to the caller, and
    // it's more efficient to believe the glob() directory marking rather
    // than calling stat, we add the markDirs flag here.
    flags |= markDirs;
  }

  return flags;
}

inline /* static */ us_bool_t glob_sequence::is_end_of_path_elements_(char_type const *pch, difference_type index)
{
  return  pch[index] == '\0' ||
          ( pch[index + 1] == '\0' &&
            pch[index] == '/');
}

inline /* static */ us_bool_t glob_sequence::is_dots_maybe_slashed_(char_type const *s, us_bool_t &bTwoDots)
{
  unixstl_assert(NULL != s);

  return  s[0] == '.' &&
      ( (bTwoDots = false, is_end_of_path_elements_(s, 1)) ||
        (bTwoDots = true, ( s[1] == '.' &&
                  is_end_of_path_elements_(s, 2))));
}

inline size_t glob_sequence::init_glob_(char_type const *directory, char_type const *pattern)
{
  unixstl_message_assert("Null pattern given to glob_sequence", NULL != pattern);

  unsigned                          glob_flags  = 0;
  basic_file_path_buffer scratch_; // Scratch buffer for directory / pattern

#ifndef __STLSOFT_CF_EXCEPTION_SUPPORT
  if(0 == scratch_.size())
  {
    m_base = NULL;

    return 0;
  }
#endif /* !__STLSOFT_CF_EXCEPTION_SUPPORT */

  // If a directory is given, then ...
  if( NULL != directory &&
      '\0' != *directory)
  {
    // ... optionally turn it into an absolute directory, ...
    if(absolutePath == (m_flags & absolutePath))
    {
      traits_type::get_full_path_name(directory, scratch_.size(), &scratch_[0]);
    }
    else
    {
      traits_type::str_copy(&scratch_[0], directory);
    }

    // ... ensure that it has a trailing path name-separator, and ...
    traits_type::ensure_dir_end(&scratch_[0]);

    // ... prefix directory onto pattern.
    traits_type::str_cat(&scratch_[0], pattern);

    pattern = c_str_ptr(scratch_);
  }

  if(m_flags & noSort)
  {
    // Don't bother sorting
    glob_flags |= GLOB_NOSORT;
  }

  if(m_flags & markDirs)
  {
    // Ask for trailing slashes on directories
    glob_flags |= GLOB_MARK;
  }

  if(directories == (m_flags & (directories | files)))
  {
    // Ask for only directories
    glob_flags |= GLOB_ONLYDIR;
  }

  int gr = glob(pattern, glob_flags, NULL, &m_glob);

  if(0 != gr)
  {
#ifdef __STLSOFT_CF_EXCEPTION_SUPPORT
    throw glob_sequence_exception(gr, 0);
#endif /* __STLSOFT_CF_EXCEPTION_SUPPORT */

    m_base = NULL;

    return 0;
  }
  else
  {
    char_type **base  = m_glob.gl_pathv;
    size_t    cItems  = static_cast(m_glob.gl_pathc);

    // If we are eliding dots, or trimming out directories, then
    // we'll copy the pointers into the buffer, and process them
    // there
    if( 0 == (m_flags & includeDots) ||
        (m_flags & (directories | files)) == files)
    {
#ifdef __STLSOFT_CF_EXCEPTION_SUPPORT
      try
      {
        m_buffer.resize(cItems);
      }
      catch(...)
      {
        globfree(&m_glob);

        throw;
      }
#else /* __STLSOFT_CF_EXCEPTION_SUPPORT */
      if(!m_buffer.resize(cItems))
      {
        globfree(&m_glob);

        m_base = NULL;

        return 0;
      }
#endif /* __STLSOFT_CF_EXCEPTION_SUPPORT */

      unixstl_assert(m_buffer.size() == cItems);

      base = static_cast(memcpy(&m_buffer[0], base, m_buffer.size() * sizeof(char_type*)));
    }

    if(0 == (m_flags & includeDots))
    {
      // Now remove the dots. If located at the start of
      // the gl buffer, then simply increment m_base to
      // be above that. If not then rearrange the base
      // two pointers such that they are there.

      us_bool_t foundDot1 = false;
      us_bool_t foundDot2 = false;
      char_type **begin   = base;
      char_type **end   = begin + cItems;

      for(; begin != end; ++begin)
      {
        us_bool_t bTwoDots;

        if(is_dots_maybe_slashed_(*begin, bTwoDots))
        {
          // Swap with whatever is at base[0]
          if(begin != base)
          {
              std::swap(*begin, *base);
          }
          ++base;
          --cItems;

           // We're only going to get one "." and one ".."
          (bTwoDots ? foundDot2 : foundDot1) = true;

          if( foundDot1 &&
            foundDot2)
          {
            break;
          }
        }
      }
    }

    // We should be able to trust glob() to return only directories when
    // asked, so we assume the following only needs to be done when
    // have asked for files alone
    if((m_flags & (directories | files)) == files)
    {
      basic_file_path_buffer buffer;
      char_type             **begin = base;
      char_type             **end = begin + cItems;

#ifndef __STLSOFT_CF_EXCEPTION_SUPPORT
      if(0 == buffer.size())
      {
        globfree(&m_glob);

        m_base = NULL;

        return 0;
      }
#endif /* !__STLSOFT_CF_EXCEPTION_SUPPORT */

      for(; begin != end; ++begin)
      {
        // Now need to process the file, by using stat
        struct stat     st;
        int             res;
        char_type const *entry  = *begin;

// TODO: If not ultra-cautious, and asked for markDirs, then trust 
        unixstl_assert(files == (m_flags & (directories | files)));

        if( 0 == (m_flags & markDirs) ||
            !traits_type::has_dir_end(entry))
        {
          if(markDirs == (m_flags & markDirs))
          {
            traits_type::str_copy(&buffer[0], entry);
            traits_type::remove_dir_end(&buffer[0]);
            entry = buffer.c_str();
          }
          res = stat(entry, &st);

          if(0 != res)
          {
            // We could throw an exception here, but it might just be
            // the case that a file has been deleted subsequent to its
            // having been included in the glob list. As such, it makes
            // more sense to just kick it from the list
          }
          else
          {
            if(m_flags & files) // Want files
            {
              if(S_IFREG == (st.st_mode & S_IFREG))
              {
                continue; // A file, so accept it
              }
            }
          }
        }

        // Note that there is no test here to determine whether or not
        // begin == base. It is assumed that most cases of file elision
        // will involve several files - how many directories have just
        // one file in them? - so the test would actually be a 
        // pessimisation

        // Swap with whatever is at base[0]
        std::swap(*begin, *base);
        ++base;
        --cItems;
      }
    }

    // Ensure we've not corrupted the sort order
    if( 0 == (m_flags & noSort) &&
      cItems != static_cast(m_glob.gl_pathc))
    {
      unixstl_ns_qual_std(sort)(base, base + cItems);
    }

    // Set m_base and m_cItems to the correct values, with
    // or without dots. m_base is cast here to remove the
    // need for const-casting throughout the rest of the
    // class
    m_base = const_cast(base);

    return cItems;
  }
}

Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use