/* ///////////////////////////////////////////////////////////////////////// * 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 #endif /* !UNIXSTL_INCL_UNIXSTL_H_UNIXSTL */ #ifndef UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS # include #endif /* !UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS */ #ifndef UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILE_PATH_BUFFER # include #endif /* !UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILE_PATH_BUFFER */ #ifndef STLSOFT_INCL_STLSOFT_UTIL_STD_HPP_ITERATOR_HELPER # include #endif /* !STLSOFT_INCL_STLSOFT_UTIL_STD_HPP_ITERATOR_HELPER */ #ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER # include #endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_AUTO_BUFFER */ #ifndef STLSOFT_INCL_STLSOFT_MEMORY_HPP_ALLOCATOR_SELECTOR # include #endif /* !STLSOFT_INCL_STLSOFT_MEMORY_HPP_ALLOCATOR_SELECTOR */ #ifndef STLSOFT_INCL_STLSOFT_SMARTPTR_HPP_SCOPED_HANDLE # include #endif /* !STLSOFT_INCL_STLSOFT_SMARTPTR_HPP_SCOPED_HANDLE */ #ifndef STLSOFT_INCL_STLSOFT_STRING_HPP_TOKENISER_FUNCTIONS //# include // for find_next_token #endif /* !STLSOFT_INCL_STLSOFT_STRING_HPP_TOKENISER_FUNCTIONS */ #ifndef STLSOFT_INCL_STLSOFT_COLLECTIONS_UTIL_HPP_COLLECTIONS # include #endif /* !STLSOFT_INCL_STLSOFT_COLLECTIONS_UTIL_HPP_COLLECTIONS */ #ifndef STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP # include #endif /* !STLSOFT_INCL_STLSOFT_UTIL_HPP_STD_SWAP */ #ifndef STLSOFT_INCL_SYS_H_TYPES # define STLSOFT_INCL_SYS_H_TYPES # include #endif /* !STLSOFT_INCL_SYS_H_TYPES */ #ifndef STLSOFT_INCL_SYS_H_STAT # define STLSOFT_INCL_SYS_H_STAT # include // for stat() #endif /* !STLSOFT_INCL_SYS_H_STAT */ #ifndef STLSOFT_INCL_H_ERRNO # define STLSOFT_INCL_H_ERRNO # include #endif /* !STLSOFT_INCL_H_ERRNO */ #ifndef STLSOFT_INCL_H_GLOB # define STLSOFT_INCL_H_GLOB # include // for glob(), globfree() #endif /* !STLSOFT_INCL_H_GLOB */ #ifndef STLSOFT_INCL_ALGORITHM # define STLSOFT_INCL_ALGORITHM # include // for std::sort #endif /* !STLSOFT_INCL_ALGORITHM */ #ifndef STLSOFT_INCL_EXCEPTION # define STLSOFT_INCL_EXCEPTION # include // for std::exception #endif /* !STLSOFT_INCL_EXCEPTION */ #ifndef STLSOFT_INCL_STDEXCEPT # define STLSOFT_INCL_STDEXCEPT # include // for std::runtime_error #endif /* !STLSOFT_INCL_STDEXCEPT */ #ifdef STLSOFT_UNITTEST # include #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 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)::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 ~ 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_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 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_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 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 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 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 no 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 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 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 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) cleanup(&m_glob, ::globfree); char_type** base = m_glob.gl_pathv; us_size_t cItems = static_cast(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(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(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); // 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 //////////////////////////// */