diff options
| author | Seth ProductEngine <slitovchuk@productengine.com> | 2010-12-17 22:16:14 +0200 | 
|---|---|---|
| committer | Seth ProductEngine <slitovchuk@productengine.com> | 2010-12-17 22:16:14 +0200 | 
| commit | 4e150d3271334e17bca0b9b47022fa3c529dc8c8 (patch) | |
| tree | 0eccffee6a9b0a9f63aedb6c5a5c4a9c7b65dd15 /indra/llvfs | |
| parent | aab5759467a4d24aaebbdc95f052fbcb46826657 (diff) | |
STORM-477 WIP Re-implemented LLDir::getNextFileInDir() as an iterator object.
- Added a class implementing directory entries iteration with pattern matching which is used in unit tests instead of LLDir::getNextFileInDir.
STORM-550 FIXED Fixed LLDir unit test which failed for some complex wildcard combinations.
Diffstat (limited to 'indra/llvfs')
| -rw-r--r-- | indra/llvfs/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | indra/llvfs/lldiriterator.cpp | 203 | ||||
| -rw-r--r-- | indra/llvfs/lldiriterator.h | 87 | ||||
| -rw-r--r-- | indra/llvfs/tests/lldir_test.cpp | 38 | 
4 files changed, 320 insertions, 15 deletions
| diff --git a/indra/llvfs/CMakeLists.txt b/indra/llvfs/CMakeLists.txt index 722f4e2bfd..a3782d824b 100644 --- a/indra/llvfs/CMakeLists.txt +++ b/indra/llvfs/CMakeLists.txt @@ -12,6 +12,7 @@ include_directories(  set(llvfs_SOURCE_FILES      lldir.cpp +    lldiriterator.cpp      lllfsthread.cpp      llpidlock.cpp      llvfile.cpp @@ -24,6 +25,7 @@ set(llvfs_HEADER_FILES      lldir.h      lldirguard.h +    lldiriterator.h      lllfsthread.h      llpidlock.h      llvfile.h @@ -60,6 +62,11 @@ list(APPEND llvfs_SOURCE_FILES ${llvfs_HEADER_FILES})  add_library (llvfs ${llvfs_SOURCE_FILES}) +target_link_libraries(llvfs +    ${BOOST_FILESYSTEM_LIBRARY} +    ${BOOST_SYSTEM_LIBRARY} +    ) +  if (DARWIN)    include(CMakeFindFrameworks)    find_library(CARBON_LIBRARY Carbon) diff --git a/indra/llvfs/lldiriterator.cpp b/indra/llvfs/lldiriterator.cpp new file mode 100644 index 0000000000..5536ed8f69 --- /dev/null +++ b/indra/llvfs/lldiriterator.cpp @@ -0,0 +1,203 @@ +/** + * @file lldiriterator.cpp + * @brief Iterator through directory entries matching the search pattern. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "lldiriterator.h" + +#include <boost/filesystem.hpp> +#include <boost/regex.hpp> + +namespace fs = boost::filesystem; + +static std::string glob_to_regex(const std::string& glob); + +class LLDirIterator::Impl +{ +public: +	Impl(const std::string &dirname, const std::string &mask); +	~Impl(); + +	bool next(std::string &fname); + +private: +	boost::regex			mFilterExp; +	fs::directory_iterator	mIter; +	bool					mIsValid; +}; + +LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask) +	: mIsValid(false) +{ +	fs::path dir_path(dirname); + +	// Check if path exists. +	if (!fs::exists(dir_path)) +	{ +		llerrs << "Invalid path: \"" << dir_path.string() << "\"" << llendl; +		return; +	} + +	// Initialize the directory iterator for the given path. +	try +	{ +		mIter = fs::directory_iterator(dir_path); +	} +	catch (fs::basic_filesystem_error<fs::path>& e) +	{ +		llerrs << e.what() << llendl; +		return; +	} + +	// Convert the glob mask to a regular expression +	std::string exp = glob_to_regex(mask); + +	// Initialize boost::regex with the expression converted from +	// the glob mask. +	// An exception is thrown if the expression is not valid. +	try +	{ +		mFilterExp.assign(exp); +	} +	catch (boost::regex_error& e) +	{ +		llerrs << "\"" << exp << "\" is not a valid regular expression: " +				<< e.what() << llendl; +		return; +	} + +	mIsValid = true; +} + +LLDirIterator::Impl::~Impl() +{ +} + +bool LLDirIterator::Impl::next(std::string &fname) +{ +	fname = ""; + +	if (!mIsValid) +	{ +		llerrs << "The iterator is not correctly initialized." << llendl; +		return false; +	} + +	fs::directory_iterator end_itr; // default construction yields past-the-end +	bool found = false; +	while (mIter != end_itr && !found) +	{ +		boost::smatch match; +		std::string name = mIter->path().filename(); +		if (found = boost::regex_match(name, match, mFilterExp)) +		{ +			fname = name; +		} + +		++mIter; +	} + +	return found; +} + +std::string glob_to_regex(const std::string& glob) +{ +	std::string regex; +	regex.reserve(glob.size()<<1); +	S32 braces = 0; +	bool escaped = false; +	bool square_brace_open = false; + +	for (std::string::const_iterator i = glob.begin(); i != glob.end(); ++i) +	{ +		char c = *i; + +		switch (c) +		{ +			case '.': +				regex+="\\."; +				break; +			case '*': +				if (glob.begin() == i) +				{ +					regex+="[^.].*"; +				} +				else +				{ +					regex+= escaped ? "*" : ".*"; +				} +				break; +			case '?': +				regex+= escaped ? '?' : '.'; +				break; +			case '{': +				braces++; +				regex+='('; +				break; +			case '}': +				if (!braces) +				{ +					llerrs << "glob_to_regex: Closing brace without an equivalent opening brace: " << glob << llendl; +				} + +				regex+=')'; +				braces--; +				break; +			case ',': +				regex+= braces ? '|' : c; +				break; +			case '!': +				regex+= square_brace_open ? '^' : c; +				break; +			default: +				regex+=c; +				break; +		} + +		escaped = ('\\' == c); +		square_brace_open = ('[' == c); +	} + +	if (braces) +	{ +		llerrs << "glob_to_regex: Unterminated brace expression: " << glob << llendl; +	} + +	return regex; +} + +LLDirIterator::LLDirIterator(const std::string &dirname, const std::string &mask) +{ +	mImpl = new Impl(dirname, mask); +} + +LLDirIterator::~LLDirIterator() +{ +	delete mImpl; +} + +bool LLDirIterator::next(std::string &fname) +{ +	return mImpl->next(fname); +} diff --git a/indra/llvfs/lldiriterator.h b/indra/llvfs/lldiriterator.h new file mode 100644 index 0000000000..0b48be41b3 --- /dev/null +++ b/indra/llvfs/lldiriterator.h @@ -0,0 +1,87 @@ +/** + * @file lldiriterator.h + * @brief Iterator through directory entries matching the search pattern. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLDIRITERATOR_H +#define LL_LLDIRITERATOR_H + +#include "linden_common.h" + +/** + * Class LLDirIterator + * + * Iterates through directory entries matching the search pattern. + */ +class LLDirIterator +{ +public: +	/** +	 * Constructs LLDirIterator object to search for glob pattern +	 * matches in a directory. +	 * +	 * @param dirname - name of a directory to search in. +	 * @param mask - search pattern, a glob expression +	 * +	 * Wildcards supported in glob expressions: +	 * -------------------------------------------------------------- +	 * | Wildcard 	| Matches										| +	 * -------------------------------------------------------------- +	 * | 	* 		|zero or more characters						| +	 * | 	?		|exactly one character							| +	 * | [abcde]	|exactly one character listed					| +	 * | [a-e]		|exactly one character in the given range		| +	 * | [!abcde]	|any character that is not listed				| +	 * | [!a-e]		|any character that is not in the given range	| +	 * | {abc,xyz}	|exactly one entire word in the options given	| +	 * -------------------------------------------------------------- +	 */ +	LLDirIterator(const std::string &dirname, const std::string &mask); + +	~LLDirIterator(); + +	/** +	 * Searches for the next directory entry matching the glob mask +	 * specified upon iterator construction. +	 * Returns true if a match is found, sets fname +	 * parameter to the name of the matched directory entry and +	 * increments the iterator position. +	 * +	 * Typical usage: +	 * <code> +	 * LLDirIterator iter(directory, pattern); +	 * if ( iter.next(scanResult) ) +	 * </code> +	 * +	 * @param fname - name of the matched directory entry. +	 * @return true if a match is found, false otherwise. +	 */ +	bool next(std::string &fname); + +protected: +	class Impl; +	Impl* mImpl; +}; + +#endif //LL_LLDIRITERATOR_H diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index 8788bd63e8..ea321c5ae9 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -28,6 +28,7 @@  #include "linden_common.h"  #include "../lldir.h" +#include "../lldiriterator.h"  #include "../test/lltut.h" @@ -259,13 +260,12 @@ namespace tut     std::string makeTestFile( const std::string& dir, const std::string& file )     { -      std::string delim = gDirUtilp->getDirDelimiter(); -      std::string path = dir + delim + file; +      std::string path = dir + file;        LLFILE* handle = LLFile::fopen( path, "w" );        ensure("failed to open test file '"+path+"'", handle != NULL );        // Harbison & Steele, 4th ed., p. 366: "If an error occurs, fputs        // returns EOF; otherwise, it returns some other, nonnegative value." -      ensure("failed to write to test file '"+path+"'", fputs("test file", handle) >= 0); +      ensure("failed to write to test file '"+path+"'", EOF != fputs("test file", handle) );        fclose(handle);        return path;     } @@ -290,7 +290,7 @@ namespace tut     }     static const char* DirScanFilename[5] = { "file1.abc", "file2.abc", "file1.xyz", "file2.xyz", "file1.mno" }; -    +     void scanTest(const std::string& directory, const std::string& pattern, bool correctResult[5])     { @@ -300,7 +300,8 @@ namespace tut        bool  filesFound[5] = { false, false, false, false, false };        //std::cerr << "searching '"+directory+"' for '"+pattern+"'\n"; -      while ( found <= 5 && gDirUtilp->getNextFileInDir(directory, pattern, scanResult) ) +      LLDirIterator iter(directory, pattern); +      while ( found <= 5 && iter.next(scanResult) )        {           found++;           //std::cerr << "  found '"+scanResult+"'\n"; @@ -334,15 +335,15 @@ namespace tut     template<> template<>     void LLDirTest_object_t::test<5>() -      // getNextFileInDir +      // LLDirIterator::next     {        std::string delim = gDirUtilp->getDirDelimiter();        std::string dirTemp = LLFile::tmpdir();        // Create the same 5 file names of the two directories -      std::string dir1 = makeTestDir(dirTemp + "getNextFileInDir"); -      std::string dir2 = makeTestDir(dirTemp + "getNextFileInDir"); +      std::string dir1 = makeTestDir(dirTemp + "LLDirIterator"); +      std::string dir2 = makeTestDir(dirTemp + "LLDirIterator");        std::string dir1files[5];        std::string dir2files[5];        for (int i=0; i<5; i++) @@ -380,19 +381,17 @@ namespace tut        scanTest(dir2, "file?.x?z", expected7);        // Scan dir2 and see if any file?.??c files are found -      // THESE FAIL ON Mac and Windows, SO ARE COMMENTED OUT FOR NOW -      //      bool  expected8[5] = { true, true, false, false, false }; -      //      scanTest(dir2, "file?.??c", expected8); -      //      scanTest(dir2, "*.??c", expected8); +      bool  expected8[5] = { true, true, false, false, false }; +      scanTest(dir2, "file?.??c", expected8); +      scanTest(dir2, "*.??c", expected8);        // Scan dir1 and see if any *.?n? files are found        bool  expected9[5] = { false, false, false, false, true };        scanTest(dir1, "*.?n?", expected9);        // Scan dir1 and see if any *.???? files are found -      // THIS ONE FAILS ON WINDOWS (returns three charater suffixes) SO IS COMMENTED OUT FOR NOW -      // bool  expected10[5] = { false, false, false, false, false }; -      // scanTest(dir1, "*.????", expected10); +      bool  expected10[5] = { false, false, false, false, false }; +      scanTest(dir1, "*.????", expected10);        // Scan dir1 and see if any ?????.* files are found        bool  expected11[5] = { true, true, true, true, true }; @@ -402,6 +401,15 @@ namespace tut        bool  expected12[5] = { false, false, true, true, false };        scanTest(dir1, "??l??.xyz", expected12); +      bool expected13[5] = { true, false, true, false, false }; +      scanTest(dir1, "file1.{abc,xyz}", expected13); + +      bool expected14[5] = { true, true, false, false, false }; +      scanTest(dir1, "file[0-9].abc", expected14); + +      bool expected15[5] = { true, true, false, false, false }; +      scanTest(dir1, "file[!a-z].abc", expected15); +        // clean up all test files and directories        for (int i=0; i<5; i++)        { | 
