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;
}
}