You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1361 lines
46 KiB

/* /////////////////////////////////////////////////////////////////////////
* File: unixstl/filesystem/glob_sequence.hpp
*
* Purpose: glob_sequence class.
*
* Created: 15th January 2002
* Updated: 4th June 2012
*
* Thanks: To Carlos Santander Bernal for helping with Mac compatibility.
* To Nevin Liber for pressing upon me the need to lead by
* example when writing books about good design/implementation.
*
* Home: http://stlsoft.org/
*
* Copyright (c) 2002-2012, Matthew Wilson and Synesis Software
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
* any contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* ////////////////////////////////////////////////////////////////////// */
/** \file unixstl/filesystem/glob_sequence.hpp
*
* \brief [C++ only] Definition of the unixstl::glob_sequence class
* (\ref group__library__filesystem "File System" Library).
*/
#ifndef UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_GLOB_SEQUENCE
#define UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_GLOB_SEQUENCE
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_GLOB_SEQUENCE_MAJOR 5
# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_GLOB_SEQUENCE_MINOR 2
# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_GLOB_SEQUENCE_REVISION 5
# define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_GLOB_SEQUENCE_EDIT 159
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
/* /////////////////////////////////////////////////////////////////////////
* Includes
*/
#ifndef UNIXSTL_INCL_UNIXSTL_H_UNIXSTL
# include <unixstl/unixstl.h>
#endif /* !UNIXSTL_INCL_UNIXSTL_H_UNIXSTL */
#ifndef UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS
# include <unixstl/filesystem/filesystem_traits.hpp>
#endif /* !UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS */
#ifndef UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILE_PATH_BUFFER
# include <unixstl/filesystem/file_path_buffer.hpp>
#endif /* !UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILE_PATH_BUFFER */
#ifndef STLSOFT_INCL_STLSOFT_UTIL_STD_HPP_ITERATOR_HELPER
# include <stlsoft/util/std/iterator_helper.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_UTIL_STD_HPP_ITERATOR_HELPER */
#ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER
# include <stlsoft/memory/auto_buffer.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER */
#ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_ALLOCATOR_SELECTOR
# include <stlsoft/memory/allocator_selector.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_ALLOCATOR_SELECTOR */
#ifndef STLSOFT_INCL_STLSOFT_SMARTPTR_HPP_SCOPED_HANDLE
# include <stlsoft/smartptr/scoped_handle.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_SMARTPTR_HPP_SCOPED_HANDLE */
#ifndef STLSOFT_INCL_STLSOFT_STRING_HPP_TOKENISER_FUNCTIONS
//# include <stlsoft/string/tokeniser_functions.hpp> // for find_next_token
#endif /* !STLSOFT_INCL_STLSOFT_STRING_HPP_TOKENISER_FUNCTIONS */
#ifndef STLSOFT_INCL_STLSOFT_COLLECTIONS_UTIL_HPP_COLLECTIONS
# include <stlsoft/collections/util/collections.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_COLLECTIONS_UTIL_HPP_COLLECTIONS */
#ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP
# include <stlsoft/util/std_swap.hpp>
#endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP */
#ifndef STLSOFT_INCL_SYS_H_TYPES
# define STLSOFT_INCL_SYS_H_TYPES
# include <sys/types.h>
#endif /* !STLSOFT_INCL_SYS_H_TYPES */
#ifndef STLSOFT_INCL_SYS_H_STAT
# define STLSOFT_INCL_SYS_H_STAT
# include <sys/stat.h> // for stat()
#endif /* !STLSOFT_INCL_SYS_H_STAT */
#ifndef STLSOFT_INCL_H_ERRNO
# define STLSOFT_INCL_H_ERRNO
# include <errno.h>
#endif /* !STLSOFT_INCL_H_ERRNO */
#ifndef STLSOFT_INCL_H_GLOB
# define STLSOFT_INCL_H_GLOB
# include <glob.h> // for glob(), globfree()
#endif /* !STLSOFT_INCL_H_GLOB */
#ifndef STLSOFT_INCL_ALGORITHM
# define STLSOFT_INCL_ALGORITHM
# include <algorithm> // for std::sort
#endif /* !STLSOFT_INCL_ALGORITHM */
#ifndef STLSOFT_INCL_EXCEPTION
# define STLSOFT_INCL_EXCEPTION
# include <exception> // for std::exception
#endif /* !STLSOFT_INCL_EXCEPTION */
#ifndef STLSOFT_INCL_STDEXCEPT
# define STLSOFT_INCL_STDEXCEPT
# include <stdexcept> // for std::runtime_error
#endif /* !STLSOFT_INCL_STDEXCEPT */
#ifdef STLSOFT_UNITTEST
# include <stlsoft/string/simple_string.hpp>
#endif /* STLSOFT_UNITTEST */
/* /////////////////////////////////////////////////////////////////////////
* Library compatibility
*/
/* User may define UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR to cause the
* component to trust GLOB_ONLYDIR, if present. If GLOB_ONLYDIR is not
* detected, UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR is ignored.
*
* For any implementations that
*/
#ifndef GLOB_ONLYDIR
# ifdef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR
# undef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR
# endif /* UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR */
#endif /* GLOB_ONLYDIR */
/* User may define UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG to cause the
* component to trust GLOB_ONLYREG, if present. If GLOB_ONLYREG is not
* detected, UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG is ignored.
*/
#ifndef GLOB_ONLYREG
# ifdef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG
# undef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG
# endif /* UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG */
#endif /* GLOB_ONLYREG */
/* User may define UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS to cause the
* component to trust GLOB_NODOTSDIRS, if present. If GLOB_NODOTSDIRS is
* not detected, UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS is ignored.
*/
#ifndef GLOB_NODOTSDIRS
# ifdef UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS
# undef UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS
# endif /* UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS */
#endif /* GLOB_NODOTSDIRS */
/* User may define UNIXSTL_GLOB_SEQUENCE_DONT_TRUST_MARK do prevent the
* component from trusting GLOB_MARK; this is meaningless on non-Windows
* systems, since marking cannot be trusted to filter for files by eliding
* directories because entries may also be sockets, etc.
*/
#ifndef _WIN32
# ifndef UNIXSTL_GLOB_SEQUENCE_DONT_TRUST_MARK
# define UNIXSTL_GLOB_SEQUENCE_DONT_TRUST_MARK
# endif /* !UNIXSTL_GLOB_SEQUENCE_DONT_TRUST_MARK */
#endif /* !_WIN32 */
/* /////////////////////////////////////////////////////////////////////////
* Compiler compatibility
*/
#ifdef STLSOFT_DOCUMENTATION_SKIP_SECTION
# define GLOB_SEQUENCE_CTOR_PRIMARY_FORM
#elif defined(STLSOFT_COMPILER_IS_CUSTOM)
# define GLOB_SEQUENCE_CTOR_PRIMARY_FORM
#elif defined(STLSOFT_COMPILER_IS_UNKNOWN)
# define GLOB_SEQUENCE_CTOR_PRIMARY_FORM
#elif defined(STLSOFT_COMPILER_IS_BORLAND)
# define GLOB_SEQUENCE_CTOR_OLD_FORM
#elif defined(STLSOFT_COMPILER_IS_COMO)
# define GLOB_SEQUENCE_CTOR_PRIMARY_FORM
#elif defined(STLSOFT_COMPILER_IS_DMC)
# define GLOB_SEQUENCE_CTOR_PRIMARY_FORM
#elif defined(STLSOFT_COMPILER_IS_GCC)
# define GLOB_SEQUENCE_CTOR_PRIMARY_FORM
#elif defined(STLSOFT_COMPILER_IS_INTEL)
# define GLOB_SEQUENCE_CTOR_PRIMARY_FORM
#elif defined(STLSOFT_COMPILER_IS_MSVC)
# if _MSC_VER >= 1310
# define GLOB_SEQUENCE_CTOR_PRIMARY_FORM
# elif _MSC_VER >= 1020
# define GLOB_SEQUENCE_CTOR_ALT_FORM
# else /* ? compiler */
# define GLOB_SEQUENCE_CTOR_OLD_FORM
# endif /* compiler */
#elif defined(STLSOFT_COMPILER_IS_MWERKS)
# define GLOB_SEQUENCE_CTOR_ALT_FORM
#elif defined(STLSOFT_COMPILER_IS_VECTORC)
# define GLOB_SEQUENCE_CTOR_ALT_FORM
#elif defined(STLSOFT_COMPILER_IS_WATCOM)
# define GLOB_SEQUENCE_CTOR_OLD_FORM
#else /* ? compiler */
# error Unrecognised compiler
#endif /* compiler */
/* /////////////////////////////////////////////////////////////////////////
* Namespace
*/
#ifndef _UNIXSTL_NO_NAMESPACE
# if defined(_STLSOFT_NO_NAMESPACE) || \
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
/* There is no stlsoft namespace, so must define ::unixstl */
namespace unixstl
{
# else
/* Define stlsoft::unixstl_project */
namespace stlsoft
{
namespace unixstl_project
{
# endif /* _STLSOFT_NO_NAMESPACE */
#endif /* !_UNIXSTL_NO_NAMESPACE */
/* /////////////////////////////////////////////////////////////////////////
* Classes
*/
#ifdef STLSOFT_CF_EXCEPTION_SUPPORT
/** \brief The exception-type thrown by unixstl::glob_sequence
*
* \ingroup group__library__filesystem
*/
class glob_sequence_exception
#if defined(STLSOFT_COMPILER_IS_DMC)
: public std::exception
#else /* ? compiler */
: public unixstl_ns_qual_std(exception)
#endif /* compiler */
{
/// \name Types
/// @{
public:
#if defined(STLSOFT_COMPILER_IS_DMC)
typedef std::exception parent_class_type;
#else /* ? compiler */
typedef unixstl_ns_qual_std(exception) parent_class_type;
#endif /* compiler */
typedef glob_sequence_exception class_type;
/// @}
/// \name Construction
/// @{
public:
ss_explicit_k glob_sequence_exception(us_int_t globStatus, us_int_t errno_) stlsoft_throw_0()
: m_globStatus(globStatus)
, m_errno(errno_)
{}
/// @}
/// \name Accessors
/// @{
public:
#if defined(STLSOFT_COMPILER_IS_DMC)
char const* what() const throw()
#else /* ? compiler */
char const* what() const stlsoft_throw_0()
#endif /* compiler */
{
return "glob_sequence failure";
}
us_int_t get_globstatus() const stlsoft_throw_0()
{
return m_globStatus;
}
us_int_t get_errno() const stlsoft_throw_0()
{
return m_errno;
}
/// @}
/// \name Members
/// @{
private:
us_int_t const m_globStatus;
us_int_t const m_errno;
/// @}
/// \name Not to be implemented
/// @{
private:
class_type& operator =(class_type const&);
/// @}
};
#endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
/** \brief STL-like readonly sequence based on the results of file-system wildcard matches
*
* \ingroup group__library__filesystem
*
* This class presents and STL-like readonly sequence interface to allow the
* iteration over the results of file-system wildcard matches.
*/
class glob_sequence
: public stlsoft_ns_qual(stl_collection_tag)
{
/// \name Types
/// @{
public:
/// \brief This class
typedef glob_sequence class_type;
/// \brief The char type
typedef us_char_a_t char_type;
// The traits type
typedef filesystem_traits<char_type> traits_type;
/// \brief The value type
typedef char_type const* value_type;
/// \brief The non-mutating (const) reference type
typedef value_type const& const_reference;
/// \brief The non-mutating (const) pointer type
typedef value_type const* const_pointer;
/// \brief The size type
typedef us_size_t size_type;
/// \brief The difference type
typedef us_ptrdiff_t difference_type;
/// \brief The allocator type
typedef stlsoft_ns_qual(allocator_selector)<value_type>::allocator_type allocator_type;
/// \brief The non-mutating (const) iterator type
typedef stlsoft_ns_qual(pointer_iterator)< value_type const
, const_pointer
, const_reference
>::type const_iterator;
#ifdef STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT
/// \brief The type of the const (non-mutating) reverse iterator
typedef stlsoft_ns_qual(reverse_iterator_base) < const_iterator
, value_type
, const_reference
, const_pointer
, difference_type
> const_reverse_iterator;
#endif /* STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT */
/// @}
/// \name Member Constants
/// @{
public:
enum search_flags
{
includeDots = 0x0008 /*!< \brief Requests that dots directories be included in the returned sequence for wildcard patterns, for which \c matchPeriod must also be specified (if GLOB_PERIOD is defined). Always ignored unless \c directories is specified. */
, directories = 0x0010 /*!< \brief Causes the search to include directories */
, files = 0x0020 /*!< \brief Causes the search to include files */
, noSort = 0x0100 /*!< \brief Does not sort entries. Corresponds to GLOB_NOSORT. */
, markDirs = 0x0200 /*!< \brief Mark directories with a trailing path name separator. Corresponds to GLOB_MARK. */
, absolutePath = 0x0400 /*!< \brief Return all entries in absolute format. Ignored when a dots directory is specified as the pattern. Note, absolute paths may not always be in canonical form, e.g. '/user/me/.' if specify ('/user/me', '.', absolutePath), in which case the caller is responsible for obtaining canonical form. */
, breakOnError = 0x0800 /*!< \brief Causes processing to stop on the first filesystem error. Corresponds to GLOB_ERR. */
, noEscape = 0x1000 /*!< \brief Treats backslashes literally. Corresponds to GLOB_NOESCAPE. */
#ifdef GLOB_PERIOD
, matchPeriod = 0x2000 /*!< \brief Leading '.' can be matched by metacharacters. Corresponds to GLOB_PERIOD. */
#endif /* GLOB_PERIOD */
#ifdef GLOB_BRACE
, bracePatterns = 0x4000 /*!< \brief Allow "{*.cpp;makefile*}" style multi-part patterns. Corresponds to GLOB_BRACE. */
#endif /* GLOB_BRACE */
#ifdef GLOB_TILDE
, expandTilde = 0x8000 /*!< \brief Expand ~ and ~<user> directories. Corresponds to GLOB_TILDE. */
#endif /* GLOB_TILDE */
};
/// @}
/// \name Construction
/// @{
public:
#if defined(GLOB_SEQUENCE_CTOR_PRIMARY_FORM)
/// \brief Constructs a sequence according to the given criteria
///
/// The constructor initialises a glob_sequence instance on the given
/// pattern with the given flags.
///
/// \param pattern The pattern against which to match the file-system contents
/// \param flags Flags to alter the behaviour of the search
///
/// \note If exceptions are supported, then this will throw a glob_sequence_exception
/// on failure of any underlying functions
template<ss_typename_param_k S>
ss_explicit_k glob_sequence(S const& pattern, us_int_t flags = files | directories)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(NULL, stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
# ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
template<ss_typename_param_k S>
glob_sequence(S const& pattern, search_flags flag)
: m_flags(validate_flags_(flag))
, m_buffer(1)
{
m_cItems = init_glob_(NULL, stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
# endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
/// \brief Constructs a sequence according to the given criteria
///
/// The constructor initialises a glob_sequence instance on the given
/// pattern with the given flags.
///
/// \param directory The directory in which the pattern is located
/// \param pattern The pattern against which to match the file-system contents
/// \param flags Flags to alter the behaviour of the search
///
/// \note If exceptions are supported, then this will throw a glob_sequence_exception
/// on failure of any underlying functions
template< ss_typename_param_k S1
, ss_typename_param_k S2
>
glob_sequence(S1 const& directory, S2 const& pattern, us_int_t flags = files | directories)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(stlsoft_ns_qual(c_str_ptr)(directory), stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
# ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
template< ss_typename_param_k S1
, ss_typename_param_k S2
>
glob_sequence(S1 const& directory, S2 const& pattern, search_flags flag)
: m_flags(validate_flags_(flag))
, m_buffer(1)
{
m_cItems = init_glob_(stlsoft_ns_qual(c_str_ptr)(directory), stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
# endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
#elif defined(GLOB_SEQUENCE_CTOR_ALT_FORM)
template<ss_typename_param_k S>
ss_explicit_k glob_sequence(S const& pattern)
: m_flags(validate_flags_(files | directories))
, m_buffer(1)
{
m_cItems = init_glob_(NULL, stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
template<ss_typename_param_k S>
glob_sequence(S const& pattern, us_int_t flags)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(NULL, stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
glob_sequence(char_type const* directory, char_type const* pattern, us_int_t flags = files | directories);
template<ss_typename_param_k S>
glob_sequence(S const& directory, char const* pattern, us_int_t flags = files | directories)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(stlsoft_ns_qual(c_str_ptr)(directory), stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
template<ss_typename_param_k S>
glob_sequence(S const& directory, S const& pattern, us_int_t flags = files | directories)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(stlsoft_ns_qual(c_str_ptr)(directory), stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
#elif defined(GLOB_SEQUENCE_CTOR_OLD_FORM)
ss_explicit_k glob_sequence(char_type const* pattern, us_int_t flags = files | directories);
glob_sequence(char_type const* directory, char_type const* pattern, us_int_t flags = files | directories);
#else /* ? constructor form */
# error Constructor form not recognised
#endif /* constructor form */
#if 0
/// \brief Constructs a sequence according to the given criteria
///
/// The constructor initialises a glob_sequence instance on the given
/// pattern with the given flags.
///
/// \param directory The directory in which the pattern is located
/// \param pattern The pattern against which to match the file-system contents
/// \param flags Flags to alter the behaviour of the search
///
/// \note If exceptions are supported, then this will throw a glob_sequence_exception
/// on failure of any underlying functions
glob_sequence(char_type const* directory, char_type const* pattern, char_type delim, us_int_t flags = files | directories);
#endif /* 0 */
/// \brief Releases any acquired resources
~glob_sequence() stlsoft_throw_0();
/// @}
/// \name Attributes
/// @{
public:
/// \brief Returns the number of elements in the sequence
us_size_t size() const;
/// \brief Indicates whether the search sequence is empty
us_bool_t empty() const;
/// @}
/// \name Element Access
/// @{
public:
/// \brief Returns the value corresponding to the given index
///
/// \note In debug-mode a runtime assert is applied to enforce that the index is valid. There is <b>no</b> release-time checking on the index validity!
const_reference operator [](size_type index) const;
/// @}
/// \name Iteration
/// @{
public:
/// \brief Begins the iteration
///
/// \return An iterator representing the start of the sequence
const_iterator begin() const;
/// \brief Ends the iteration
///
/// \return An iterator representing the end of the sequence
const_iterator end() const;
#ifdef STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT
/// \brief Begins the reverse iteration
///
/// \return An iterator representing the start of the reverse sequence
const_reverse_iterator rbegin() const;
/// \brief Ends the reverse iteration
///
/// \return An iterator representing the end of the reverse sequence
const_reverse_iterator rend() const;
#endif /* STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT */
/// @}
/// \name Implementation
/// @{
private:
// Tests the class invariant
us_bool_t is_valid() const;
// Validates the flags, and sets up defaults
static us_int_t validate_flags_(us_int_t flags);
// Returns true if pch is a path separator "/" (or "\\"); false otherwise
static us_bool_t is_path_separator_(char_type ch);
// Returns true if pch == "" or "/" (or "\\"); false otherwise
static us_bool_t is_end_of_path_elements_(char_type const* pch, difference_type index);
// Returns true if s points to a path that is a dots directory; false otherwise
static us_bool_t is_dots_maybe_slashed_(char_type const* s, us_bool_t* bTwoDots);
// Calls glob() and process the results
//
// init_glob_() - handles any directory and/or pattern (where the pattern may contain rel/abs dir)
// init_glob_1_() - splits combined path into directory+pattern (which are passed to init_glob_2_())
// init_glob_2_() - handles receives properly split directory+pattern (and does test in order to call init_glob_3_())
// init_glob_3_() - handles full pattern, with bool indicating whether pattern contains wildcard at leaf
us_size_t init_glob_(char_type const* directory, char_type const* pattern);
us_size_t init_glob_1_(size_type bufferSize, char_type* combinedPath);
us_size_t init_glob_2_(char_type const* patternDir, char_type const* pattern0);
us_size_t init_glob_3_(char_type const* pattern, bool isPattern0Wild);
/// @}
/// \name Members
/// @{
private:
typedef stlsoft_ns_qual(auto_buffer_old)< char_type const*
, allocator_type
, 128
> buffer_type_;
us_int_t const m_flags;
char_type const** m_base;
us_size_t m_cItems;
buffer_type_ m_buffer;
glob_t m_glob;
/// @}
/// \name Not to be implemented
/// @{
private:
glob_sequence(class_type const&);
class_type const& operator =(class_type const&);
/// @}
};
////////////////////////////////////////////////////////////////////////////
// Unit-testing
#ifdef STLSOFT_UNITTEST
# include "./unittest/glob_sequence_unittest_.h"
#endif /* STLSOFT_UNITTEST */
/* /////////////////////////////////////////////////////////////////////////
* Implementation
*/
#ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
#if defined(GLOB_SEQUENCE_CTOR_OLD_FORM)
inline /* ss_explicit_k */ glob_sequence::glob_sequence(glob_sequence::char_type const* pattern, us_int_t flags)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(NULL, pattern);
UNIXSTL_ASSERT(is_valid());
}
#endif /* GLOB_SEQUENCE_CTOR_OLD_FORM */
#if defined(GLOB_SEQUENCE_CTOR_OLD_FORM) || \
defined(GLOB_SEQUENCE_CTOR_ALT_FORM)
inline glob_sequence::glob_sequence(glob_sequence::char_type const* directory, glob_sequence::char_type const* pattern, us_int_t flags)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(directory, pattern);
UNIXSTL_ASSERT(is_valid());
}
#endif /* GLOB_SEQUENCE_CTOR_OLD_FORM || GLOB_SEQUENCE_CTOR_ALT_FORM */
#if 0
template< ss_typename_param_k S1
, ss_typename_param_k S2
>
inline glob_sequence::glob_sequence(S1 const& directory, S2 const& pattern, us_int_t flags)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(stlsoft_ns_qual(c_str_ptr)(directory), stlsoft_ns_qual(c_str_ptr)(pattern));
UNIXSTL_ASSERT(is_valid());
}
#endif /* 0 */
#if 0
inline glob_sequence::glob_sequence(char_type const* directory, char_type const* pattern, char_type delim, us_int_t flags)
: m_flags(validate_flags_(flags))
, m_buffer(1)
{
m_cItems = init_glob_(directory, pattern);
UNIXSTL_ASSERT(is_valid());
}
#endif /* 0 */
inline glob_sequence::~glob_sequence() stlsoft_throw_0()
{
UNIXSTL_ASSERT(is_valid());
if(NULL != m_base)
{
::globfree(&m_glob);
}
}
inline us_size_t glob_sequence::size() const
{
return m_cItems;
}
inline us_bool_t glob_sequence::empty() const
{
return 0 == size();
}
inline glob_sequence::const_reference glob_sequence::operator [](glob_sequence::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 glob_sequence::const_iterator glob_sequence::begin() const
{
return m_base;
}
inline glob_sequence::const_iterator glob_sequence::end() const
{
return m_base + m_cItems;
}
#ifdef STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT
inline glob_sequence::const_reverse_iterator glob_sequence::rbegin() const
{
return const_reverse_iterator(end());
}
inline glob_sequence::const_reverse_iterator glob_sequence::rend() const
{
return const_reverse_iterator(begin());
}
#endif /* STLSOFT_LF_BIDIRECTIONAL_ITERATOR_SUPPORT */
inline us_bool_t glob_sequence::is_valid() const
{
if((0 != m_cItems) && (NULL == m_base))
{
return false;
}
return true;
}
inline /* static */ us_int_t glob_sequence::validate_flags_(us_int_t flags)
{
const us_int_t validFlags = 0
| includeDots
| directories
| files
| noSort
| markDirs
| absolutePath
| breakOnError
| noEscape
#ifdef GLOB_PERIOD
| matchPeriod
#endif /* GLOB_PERIOD */
#ifdef GLOB_BRACE
| bracePatterns
#endif /* GLOB_BRACE */
#ifdef GLOB_TILDE
| expandTilde
#endif /* GLOB_TILDE */
| 0;
UNIXSTL_MESSAGE_ASSERT("Specification of unrecognised/unsupported flags", flags == (flags & validFlags));
STLSOFT_SUPPRESS_UNUSED(validFlags);
if(0 == (flags & (directories | files)))
{
flags |= (directories | files);
}
#ifndef UNIXSTL_GLOB_SEQUENCE_DONT_TRUST_MARK
// If we're not searching for directories, then we can optimise the
// subsequent filtering by asking for the dots directories (so we
// skip that filtering) and asking for directories to be marked (so
// we can detect the mark rather than making a system call (stat())
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;
}
#endif /* !UNIXSTL_GLOB_SEQUENCE_DONT_TRUST_MARK */
return flags;
}
inline /* static */ us_bool_t glob_sequence::is_path_separator_(glob_sequence::char_type ch)
{
return ch == '/'
#if defined(_UNIXSTL_COMPILER_IS_UNKNOWN) && \
!defined(_UNIXSTL_GLOB_SEQUENCE_NO_BACK_SLASH_TERMINATOR)
|| ch == '\\' /* Allow for testing on Win32 systems */
#endif /* _UNIXSTL_COMPILER_IS_UNKNOWN && !_UNIXSTL_GLOB_SEQUENCE_NO_BACK_SLASH_TERMINATOR */
;
}
inline /* static */ us_bool_t glob_sequence::is_end_of_path_elements_(glob_sequence::char_type const* pch, glob_sequence::difference_type index)
{
return pch[index] == '\0' ||
( pch[index + 1] == '\0' &&
is_path_separator_(pch[index]));
}
inline /* static */ us_bool_t glob_sequence::is_dots_maybe_slashed_(glob_sequence::char_type const* s, us_bool_t* bTwoDots)
{
UNIXSTL_ASSERT(NULL != s);
UNIXSTL_ASSERT(NULL != bTwoDots);
// This must match all patterns like the following:
//
// "." l=1, s[0] = .
// ".." l=2, s[0] = ., s[1] = .
// "./" l=2, s[0] = ., s[1] = sep
// "../" l=3, s[0] = ., s[1] = ., s[2] = sep
// "abc/."
// "abc/.."
// "abc/./"
// "abc/../"
const us_size_t len = traits_type::str_len(s);
us_size_t lastNameChar = len -1;
UNIXSTL_ASSERT(len > 0);
if(is_path_separator_(s[lastNameChar]))
{
--lastNameChar;
}
if('.' == s[lastNameChar])
{
if(0 == lastNameChar)
{
return true;
}
else
{
--lastNameChar;
if( 0 == lastNameChar ||
is_path_separator_(s[lastNameChar]))
{
*bTwoDots = false;
return true;
}
else if( 0 < lastNameChar &&
'.' == s[lastNameChar])
{
--lastNameChar;
if( 0 == lastNameChar ||
is_path_separator_(s[lastNameChar]))
{
*bTwoDots = true;
return true;
}
else
{
return false;
}
}
}
}
return false;
}
inline us_size_t glob_sequence::init_glob_(glob_sequence::char_type const* directory, glob_sequence::char_type const* pattern)
{
UNIXSTL_MESSAGE_ASSERT("Null pattern given to glob_sequence", NULL != pattern);
char_type const* lastSlash = traits_type::find_last_path_name_separator(pattern);
if(NULL == lastSlash)
{
// already properly separated
return init_glob_2_(directory, pattern);
}
else
{
if(traits_type::is_path_rooted(pattern))
{
directory = NULL;
}
basic_file_path_buffer<char_type> scratch_; // Scratch buffer for directory + pattern
size_type dirLen = (NULL == directory) ? 0u : traits_type::str_len(directory);
size_type patLen = traits_type::str_len(pattern);
if(0 != dirLen)
{
traits_type::char_copy(&scratch_[0] + 0, directory, dirLen);
scratch_[dirLen] = '\0';
dirLen += traits_type::str_len(traits_type::ensure_dir_end(&scratch_[0] + (dirLen - 1))) - 1;
}
traits_type::char_copy(&scratch_[0] + dirLen, pattern, patLen);
scratch_[dirLen + patLen] = '\0';
return init_glob_1_(scratch_.size(), scratch_.data());
}
}
inline us_size_t glob_sequence::init_glob_1_(size_type bufferSize, char_type* combinedPath)
{
char_type const* const lastSlash = traits_type::find_last_path_name_separator(combinedPath);
UNIXSTL_ASSERT(NULL != lastSlash);
combinedPath[lastSlash - combinedPath] = '\0';
char_type const* directory = combinedPath;
char_type const* pattern = lastSlash + 1;
return init_glob_2_(directory, pattern);
}
inline us_size_t glob_sequence::init_glob_2_(char_type const* directory, char_type const* pattern0)
{
// Preconditions:
//
// - pattern must not be NULL
// - pattern must not contain a path-name separator
UNIXSTL_ASSERT(NULL != pattern0);
UNIXSTL_ASSERT(NULL == traits_type::str_chr(pattern0, '/'));
#ifdef _WIN32
UNIXSTL_ASSERT(NULL == traits_type::str_chr(pattern0, '\\'));
#endif /* _WIN32 */
static char_type const s_wildChars[] = { '?', '*', '\0' };
us_bool_t const isPattern0Wild = (NULL != traits_type::str_pbrk(pattern0, s_wildChars));
if( NULL != directory &&
'\0' != directory[0])
{
if( absolutePath == (m_flags & absolutePath) &&
!traits_type::is_path_rooted(directory))
{
us_bool_t const isPatternDirWild = (NULL != traits_type::str_pbrk(directory, s_wildChars));
if(isPatternDirWild)
{
errno = EINVAL;
m_base = NULL;
return 0;
}
else
{
basic_file_path_buffer<char_type> scratch2_; // Scratch buffer for absolute path
size_type absLen = traits_type::get_full_path_name(directory, scratch2_.size(), &scratch2_[0]);
if(0 == absLen)
{
m_base = NULL;
return 0;
}
else
{
return init_glob_2_(scratch2_.data(), pattern0);
}
}
}
else
{
basic_file_path_buffer<char_type> scratch_; // Scratch buffer for directory / pattern
#ifndef STLSOFT_CF_EXCEPTION_SUPPORT
if(0 == scratch_.size())
{
m_base = NULL;
return 0;
}
else
#endif /* !STLSOFT_CF_EXCEPTION_SUPPORT */
{
size_type dirLen = traits_type::str_len(directory);
size_type patLen = traits_type::str_len(pattern0);
traits_type::char_copy(&scratch_[0] + 0, directory, dirLen);
scratch_[dirLen] = '\0';
dirLen += traits_type::str_len(traits_type::ensure_dir_end(&scratch_[0] + (dirLen - 1))) - 1;
traits_type::char_copy(&scratch_[0] + dirLen, pattern0, patLen);
scratch_[dirLen + patLen] = '\0';
return init_glob_3_(scratch_.c_str(), isPattern0Wild);
}
}
}
else
{
return init_glob_3_(pattern0, isPattern0Wild);
}
}
inline us_size_t glob_sequence::init_glob_3_(char_type const* pattern, bool isPattern0Wild)
{
us_int_t glob_flags = 0;
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;
}
#ifdef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR // If this is not defined, we rely on stat
if(directories == (m_flags & (directories | files)))
{
// Ask for only directories
glob_flags |= GLOB_ONLYDIR;
}
#endif /* UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR */
#ifdef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG // If this is not defined, we rely on stat
if(files == (m_flags & (directories | files)))
{
// Ask for only files
glob_flags |= GLOB_ONLYREG;
}
#endif /* UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG */
#ifdef GLOB_ERR
if(m_flags & breakOnError)
{
glob_flags |= GLOB_ERR;
}
#endif /* GLOB_ERR */
#ifdef GLOB_NOESCAPE
if(m_flags & noEscape)
{
glob_flags |= GLOB_NOESCAPE;
}
#endif /* GLOB_NOESCAPE */
#ifdef GLOB_PERIOD
if(m_flags & matchPeriod)
{
glob_flags |= GLOB_PERIOD;
}
#endif /* GLOB_PERIOD */
#ifdef GLOB_BRACE
if(m_flags & bracePatterns)
{
glob_flags |= GLOB_BRACE;
}
#endif /* GLOB_BRACE */
#ifdef GLOB_TILDE
if(m_flags & expandTilde)
{
glob_flags |= GLOB_TILDE;
}
#endif /* GLOB_TILDE */
#ifdef UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS
if(0 == (m_flags & includeDots))
{
glob_flags |= GLOB_NODOTSDIRS;
}
#endif /* UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS */
int gr = ::glob(pattern, glob_flags, NULL, &m_glob);
if(0 != gr)
{
#ifdef STLSOFT_CF_EXCEPTION_SUPPORT
# ifdef GLOB_NOMATCH
// When GLOB_NOMATCH is not defined, we can reasonably infer that
// there is no replacement value, so throwing on a non-zero
// return from glob() is appropriate
if(GLOB_NOMATCH != gr)
# endif /* GLOB_NOMATCH */
{
STLSOFT_THROW_X(glob_sequence_exception(gr, 0));
}
#endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
m_base = NULL;
return 0;
}
else
{
// Sink the glob_t instance into a local scoped_handle, to invoke
// ::globfree() automatically if we encounter any problems during
// the processing. At the end we'll call detach(), to "give it"
// to the glob_sequence instance.
stlsoft_ns_qual(scoped_handle)<glob_t*> cleanup(&m_glob, ::globfree);
char_type** base = m_glob.gl_pathv;
us_size_t cItems = static_cast<us_size_t>(m_glob.gl_pathc);
// This section performs a COW on the entry pointers, into
// the m_buffer member, if any of the following hold:
//
// 1. we're eliding dots
// 2. we want directories only, and so will need to
// remove files, AND we do not trust GLOB_ONLYDIR
// 3. we want files only, and so will need
// to remove directories
//
bool const elidingDots = isPattern0Wild && (0 == (m_flags & includeDots));
if( elidingDots || // 1
#ifndef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR
directories == (m_flags & (directories | files)) || // 2
#endif /* !UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR */
files == (m_flags & (directories | files))) // 3
{
if(!m_buffer.resize(cItems))
{
#ifndef STLSOFT_CF_EXCEPTION_SUPPORT
m_base = NULL;
return 0;
#endif /* !STLSOFT_CF_EXCEPTION_SUPPORT */
}
UNIXSTL_ASSERT(m_buffer.size() == cItems);
base = static_cast<char_type**>(memcpy(&m_buffer[0], base, m_buffer.size() * sizeof(char_type*)));
}
// This section elides dots directories.
if(elidingDots)
{
#ifndef UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS
// 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 bFoundDot1 = false;
us_bool_t bFoundDot2 = false;
char_type** begin = base;
char_type** const end = begin + cItems;
for(; begin != end; ++begin)
{
us_bool_t bTwoDots = false;
if(is_dots_maybe_slashed_(*begin, &bTwoDots))
{
// Swap with whatever is at base[0]
if(begin != base)
{
stlsoft_ns_qual(std_swap)(*begin, *base);
}
++base;
--cItems;
// We're only going to get one "." and one ".."
(bTwoDots ? bFoundDot2 : bFoundDot1) = true;
if( bFoundDot1 &&
bFoundDot2)
{
break;
}
}
}
#endif /* !UNIXSTL_GLOB_SEQUENCE_TRUST_NODOTSDIRS */
}
// This section performs the main filtering section, with
// conditional shortcuts for if any of the following hold:
//
// 1. Looking for files only
// - and GLOB_ONLYREG is supported
// 2. Looking for directories only
// - and GLOB_ONLYDIR is supported
// 3. Looking for anything
//
#ifdef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG
// 1. Looking for files only
if(files == (m_flags & (directories | files)))
{
; // Nothing to do
}
else
#endif /* UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYREG */
#ifdef UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR
// 2. Looking for directories only
if(directories == (m_flags & (directories | files)))
{
; // Nothing to do
}
else
#endif /* UNIXSTL_GLOB_SEQUENCE_TRUST_ONLYDIR */
// 3. Looking for anything
if(0 == (m_flags & (directories | files)))
{
// NOTE: this conditional branch is a future-compatibility
// feature, for when sockets and links are supported
; // Nothing to do
}
else
{
// Must filter based on type ascertained by call to stat()
// (except if we trust MARKDIR
char_type** begin = base;
char_type** const end = begin + cItems;
for(; begin != end; ++begin)
{
// Now need to process the file, by using stat
traits_type::stat_data_type st;
char_type const* const entry = *begin;
// Shortcut relying on mark, based on the assumption that
// a strlen()-equiv. operation is faster than a call to
// stat().
# ifndef UNIXSTL_GLOB_SEQUENCE_DONT_TRUST_MARK
if(markDirs == (m_flags & markDirs))
{
bool const isDirectory = traits_type::has_dir_end(entry);
if( isDirectory &&
directories == (m_flags & (directories)))
{
// It is a directory, and we want directories, so
// don't elide (by continue-ing)
continue;
}
else
if( !isDirectory &&
files == (m_flags & (directories | files)))
{
// It is not a directory, and we want files,
// so don't elide (by continue-ing)
continue;
}
}
else
# endif /* !UNIXSTL_GLOB_SEQUENCE_DONT_TRUST_MARK */
if(!traits_type::stat(entry, &st))
{
// 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
// TODO: Consider adding a callback function here, which can elect to throw, if the application requires that. Also, consider a throwOnStat flag
}
else
{ // stat() succeeded
if( files == (m_flags & (files)) &&
traits_type::is_file(&st))
{
continue; // A file, so accept it
}
else
if( directories == (m_flags & (directories)) &&
traits_type::is_directory(&st))
{
continue; // A directory, so accept it
}
else
{
}
}
// This section elides the entry from the list
//
// 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]
stlsoft_ns_qual(std_swap)(*begin, *base);
++base;
--cItems;
}
}
// Ensure we've not corrupted the sort order
if( 0 == (m_flags & noSort) &&
cItems != static_cast<us_size_t>(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<char_type const**>(base);
// Everything has succeeded, so give ownership over to the
// glob_sequence instance
cleanup.detach();
return cItems;
}
}
#endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
/* ////////////////////////////////////////////////////////////////////// */
#ifndef _UNIXSTL_NO_NAMESPACE
# if defined(_STLSOFT_NO_NAMESPACE) || \
defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
} // namespace unixstl
# else
} // namespace unixstl_project
} // namespace stlsoft
# endif /* _STLSOFT_NO_NAMESPACE */
#endif /* !_UNIXSTL_NO_NAMESPACE */
/* ////////////////////////////////////////////////////////////////////// */
#endif /* !UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_GLOB_SEQUENCE */
/* ///////////////////////////// end of file //////////////////////////// */