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
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 //////////////////////////// */
|