Skip to content
Snippets Groups Projects
Commit 4e150d32 authored by Seth ProductEngine's avatar Seth ProductEngine
Browse files

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.
parent aab57594
No related branches found
No related tags found
No related merge requests found
...@@ -10,6 +10,8 @@ if (STANDALONE) ...@@ -10,6 +10,8 @@ if (STANDALONE)
set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt) set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-mt)
set(BOOST_REGEX_LIBRARY boost_regex-mt) set(BOOST_REGEX_LIBRARY boost_regex-mt)
set(BOOST_SIGNALS_LIBRARY boost_signals-mt) set(BOOST_SIGNALS_LIBRARY boost_signals-mt)
set(BOOST_SYSTEM_LIBRARY boost_system-mt)
set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt)
else (STANDALONE) else (STANDALONE)
use_prebuilt_binary(boost) use_prebuilt_binary(boost)
set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
...@@ -26,6 +28,12 @@ else (STANDALONE) ...@@ -26,6 +28,12 @@ else (STANDALONE)
set(BOOST_SIGNALS_LIBRARY set(BOOST_SIGNALS_LIBRARY
optimized libboost_signals-vc71-mt-s-${BOOST_VERSION} optimized libboost_signals-vc71-mt-s-${BOOST_VERSION}
debug libboost_signals-vc71-mt-sgd-${BOOST_VERSION}) debug libboost_signals-vc71-mt-sgd-${BOOST_VERSION})
set(BOOST_SYSTEM_LIBRARY
optimized libboost_system-vc71-mt-s-${BOOST_VERSION}
debug libboost_system-vc71-mt-sgd-${BOOST_VERSION})
set(BOOST_FILESYSTEM_LIBRARY
optimized libboost_filesystem-vc71-mt-s-${BOOST_VERSION}
debug libboost_filesystem-vc71-mt-sgd-${BOOST_VERSION})
else (MSVC71) else (MSVC71)
set(BOOST_PROGRAM_OPTIONS_LIBRARY set(BOOST_PROGRAM_OPTIONS_LIBRARY
optimized libboost_program_options-vc80-mt-${BOOST_VERSION} optimized libboost_program_options-vc80-mt-${BOOST_VERSION}
...@@ -36,14 +44,24 @@ else (STANDALONE) ...@@ -36,14 +44,24 @@ else (STANDALONE)
set(BOOST_SIGNALS_LIBRARY set(BOOST_SIGNALS_LIBRARY
optimized libboost_signals-vc80-mt-${BOOST_VERSION} optimized libboost_signals-vc80-mt-${BOOST_VERSION}
debug libboost_signals-vc80-mt-gd-${BOOST_VERSION}) debug libboost_signals-vc80-mt-gd-${BOOST_VERSION})
set(BOOST_SYSTEM_LIBRARY
optimized libboost_system-vc80-mt-${BOOST_VERSION}
debug libboost_system-vc80-mt-gd-${BOOST_VERSION})
set(BOOST_FILESYSTEM_LIBRARY
optimized libboost_filesystem-vc80-mt-${BOOST_VERSION}
debug libboost_filesystem-vc80-mt-gd-${BOOST_VERSION})
endif (MSVC71) endif (MSVC71)
elseif (DARWIN) elseif (DARWIN)
set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-xgcc40-mt) set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-xgcc40-mt)
set(BOOST_REGEX_LIBRARY boost_regex-xgcc40-mt) set(BOOST_REGEX_LIBRARY boost_regex-xgcc40-mt)
set(BOOST_SIGNALS_LIBRARY boost_signals-xgcc40-mt) set(BOOST_SIGNALS_LIBRARY boost_signals-xgcc40-mt)
set(BOOST_SYSTEM_LIBRARY boost_system-xgcc40-mt)
set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-xgcc40-mt)
elseif (LINUX) elseif (LINUX)
set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-gcc41-mt) set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options-gcc41-mt)
set(BOOST_REGEX_LIBRARY boost_regex-gcc41-mt) set(BOOST_REGEX_LIBRARY boost_regex-gcc41-mt)
set(BOOST_SIGNALS_LIBRARY boost_signals-gcc41-mt) set(BOOST_SIGNALS_LIBRARY boost_signals-gcc41-mt)
set(BOOST_SYSTEM_LIBRARY boost_system-gcc41-mt)
set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-gcc41-mt)
endif (WINDOWS) endif (WINDOWS)
endif (STANDALONE) endif (STANDALONE)
...@@ -12,6 +12,7 @@ include_directories( ...@@ -12,6 +12,7 @@ include_directories(
set(llvfs_SOURCE_FILES set(llvfs_SOURCE_FILES
lldir.cpp lldir.cpp
lldiriterator.cpp
lllfsthread.cpp lllfsthread.cpp
llpidlock.cpp llpidlock.cpp
llvfile.cpp llvfile.cpp
...@@ -24,6 +25,7 @@ set(llvfs_HEADER_FILES ...@@ -24,6 +25,7 @@ set(llvfs_HEADER_FILES
lldir.h lldir.h
lldirguard.h lldirguard.h
lldiriterator.h
lllfsthread.h lllfsthread.h
llpidlock.h llpidlock.h
llvfile.h llvfile.h
...@@ -60,6 +62,11 @@ list(APPEND llvfs_SOURCE_FILES ${llvfs_HEADER_FILES}) ...@@ -60,6 +62,11 @@ list(APPEND llvfs_SOURCE_FILES ${llvfs_HEADER_FILES})
add_library (llvfs ${llvfs_SOURCE_FILES}) add_library (llvfs ${llvfs_SOURCE_FILES})
target_link_libraries(llvfs
${BOOST_FILESYSTEM_LIBRARY}
${BOOST_SYSTEM_LIBRARY}
)
if (DARWIN) if (DARWIN)
include(CMakeFindFrameworks) include(CMakeFindFrameworks)
find_library(CARBON_LIBRARY Carbon) find_library(CARBON_LIBRARY Carbon)
......
/**
* @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);
}
/**
* @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
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "linden_common.h" #include "linden_common.h"
#include "../lldir.h" #include "../lldir.h"
#include "../lldiriterator.h"
#include "../test/lltut.h" #include "../test/lltut.h"
...@@ -259,13 +260,12 @@ namespace tut ...@@ -259,13 +260,12 @@ namespace tut
std::string makeTestFile( const std::string& dir, const std::string& file ) std::string makeTestFile( const std::string& dir, const std::string& file )
{ {
std::string delim = gDirUtilp->getDirDelimiter(); std::string path = dir + file;
std::string path = dir + delim + file;
LLFILE* handle = LLFile::fopen( path, "w" ); LLFILE* handle = LLFile::fopen( path, "w" );
ensure("failed to open test file '"+path+"'", handle != NULL ); ensure("failed to open test file '"+path+"'", handle != NULL );
// Harbison & Steele, 4th ed., p. 366: "If an error occurs, fputs // Harbison & Steele, 4th ed., p. 366: "If an error occurs, fputs
// returns EOF; otherwise, it returns some other, nonnegative value." // 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); fclose(handle);
return path; return path;
} }
...@@ -300,7 +300,8 @@ namespace tut ...@@ -300,7 +300,8 @@ namespace tut
bool filesFound[5] = { false, false, false, false, false }; bool filesFound[5] = { false, false, false, false, false };
//std::cerr << "searching '"+directory+"' for '"+pattern+"'\n"; //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++; found++;
//std::cerr << " found '"+scanResult+"'\n"; //std::cerr << " found '"+scanResult+"'\n";
...@@ -334,15 +335,15 @@ namespace tut ...@@ -334,15 +335,15 @@ namespace tut
template<> template<> template<> template<>
void LLDirTest_object_t::test<5>() void LLDirTest_object_t::test<5>()
// getNextFileInDir // LLDirIterator::next
{ {
std::string delim = gDirUtilp->getDirDelimiter(); std::string delim = gDirUtilp->getDirDelimiter();
std::string dirTemp = LLFile::tmpdir(); std::string dirTemp = LLFile::tmpdir();
// Create the same 5 file names of the two directories // Create the same 5 file names of the two directories
std::string dir1 = makeTestDir(dirTemp + "getNextFileInDir"); std::string dir1 = makeTestDir(dirTemp + "LLDirIterator");
std::string dir2 = makeTestDir(dirTemp + "getNextFileInDir"); std::string dir2 = makeTestDir(dirTemp + "LLDirIterator");
std::string dir1files[5]; std::string dir1files[5];
std::string dir2files[5]; std::string dir2files[5];
for (int i=0; i<5; i++) for (int i=0; i<5; i++)
...@@ -380,19 +381,17 @@ namespace tut ...@@ -380,19 +381,17 @@ namespace tut
scanTest(dir2, "file?.x?z", expected7); scanTest(dir2, "file?.x?z", expected7);
// Scan dir2 and see if any file?.??c files are found // 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 };
// bool expected8[5] = { true, true, false, false, false }; scanTest(dir2, "file?.??c", expected8);
// scanTest(dir2, "file?.??c", expected8); scanTest(dir2, "*.??c", expected8);
// scanTest(dir2, "*.??c", expected8);
// Scan dir1 and see if any *.?n? files are found // Scan dir1 and see if any *.?n? files are found
bool expected9[5] = { false, false, false, false, true }; bool expected9[5] = { false, false, false, false, true };
scanTest(dir1, "*.?n?", expected9); scanTest(dir1, "*.?n?", expected9);
// Scan dir1 and see if any *.???? files are found // 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 };
// bool expected10[5] = { false, false, false, false, false }; scanTest(dir1, "*.????", expected10);
// scanTest(dir1, "*.????", expected10);
// Scan dir1 and see if any ?????.* files are found // Scan dir1 and see if any ?????.* files are found
bool expected11[5] = { true, true, true, true, true }; bool expected11[5] = { true, true, true, true, true };
...@@ -402,6 +401,15 @@ namespace tut ...@@ -402,6 +401,15 @@ namespace tut
bool expected12[5] = { false, false, true, true, false }; bool expected12[5] = { false, false, true, true, false };
scanTest(dir1, "??l??.xyz", expected12); 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 // clean up all test files and directories
for (int i=0; i<5; i++) for (int i=0; i<5; i++)
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment