summaryrefslogtreecommitdiff
path: root/indra/llvfs/lldiriterator.cpp
diff options
context:
space:
mode:
authorAndrey Lihatskiy <alihatskiy@productengine.com>2021-03-09 22:00:08 +0200
committerAndrey Lihatskiy <alihatskiy@productengine.com>2021-03-09 22:00:08 +0200
commit19ff86fabd84ba45bbbdfd1fa5393fb9ab89dddf (patch)
tree4160038613ed7efe5c806bdb11379ea453e48900 /indra/llvfs/lldiriterator.cpp
parentbe83a004db5831958837d9caa3bf7c98fedc3bf2 (diff)
parent88d837c16e768c5262073a7df965066d4bd8842c (diff)
Merge branch 'master' into DRTVWR-522-maint
Diffstat (limited to 'indra/llvfs/lldiriterator.cpp')
-rw-r--r--indra/llvfs/lldiriterator.cpp243
1 files changed, 243 insertions, 0 deletions
diff --git a/indra/llvfs/lldiriterator.cpp b/indra/llvfs/lldiriterator.cpp
new file mode 100644
index 0000000000..f57bf4ebc6
--- /dev/null
+++ b/indra/llvfs/lldiriterator.cpp
@@ -0,0 +1,243 @@
+/**
+ * @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 "fix_macros.h"
+#include "llregex.h"
+#include <boost/filesystem.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)
+{
+#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
+ fs::path dir_path(utf8str_to_utf16str(dirname));
+#else
+ fs::path dir_path(dirname);
+#endif
+
+ bool is_dir = false;
+
+ // Check if path is a directory.
+ try
+ {
+ is_dir = fs::is_directory(dir_path);
+ }
+ catch (const fs::filesystem_error& e)
+ {
+ LL_WARNS() << e.what() << LL_ENDL;
+ return;
+ }
+
+ if (!is_dir)
+ {
+ LL_WARNS() << "Invalid path: \"" << dir_path.string() << "\"" << LL_ENDL;
+ return;
+ }
+
+ // Initialize the directory iterator for the given path.
+ try
+ {
+ mIter = fs::directory_iterator(dir_path);
+ }
+ catch (const fs::filesystem_error& e)
+ {
+ LL_WARNS() << e.what() << LL_ENDL;
+ 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)
+ {
+ LL_WARNS() << "\"" << exp << "\" is not a valid regular expression: "
+ << e.what() << LL_ENDL;
+ return;
+ }
+
+ mIsValid = true;
+}
+
+LLDirIterator::Impl::~Impl()
+{
+}
+
+bool LLDirIterator::Impl::next(std::string &fname)
+{
+ fname = "";
+
+ if (!mIsValid)
+ {
+ LL_WARNS() << "The iterator is not correctly initialized." << LL_ENDL;
+ return false;
+ }
+
+ fs::directory_iterator end_itr; // default construction yields past-the-end
+ bool found = false;
+
+ // Check if path is a directory.
+ try
+ {
+ while (mIter != end_itr && !found)
+ {
+ boost::smatch match;
+ std::string name = mIter->path().filename().string();
+ found = ll_regex_match(name, match, mFilterExp);
+ if (found)
+ {
+ fname = name;
+ }
+
+ ++mIter;
+ }
+ }
+ catch (const fs::filesystem_error& e)
+ {
+ LL_WARNS() << e.what() << LL_ENDL;
+ }
+
+ return found;
+}
+
+/**
+Converts the incoming glob into a regex. This involves
+converting incoming glob expressions to regex equivilents and
+at the same time, escaping any regex meaningful characters which
+do not have glob meaning, i.e.
+ .()+|^$
+in the input.
+*/
+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 '*':
+ if (glob.begin() == i)
+ {
+ regex+="[^.].*";
+ }
+ else
+ {
+ regex+= escaped ? "*" : ".*";
+ }
+ break;
+ case '?':
+ regex+= escaped ? '?' : '.';
+ break;
+ case '{':
+ braces++;
+ regex+='(';
+ break;
+ case '}':
+ if (!braces)
+ {
+ LL_ERRS() << "glob_to_regex: Closing brace without an equivalent opening brace: " << glob << LL_ENDL;
+ }
+
+ regex+=')';
+ braces--;
+ break;
+ case ',':
+ regex+= braces ? '|' : c;
+ break;
+ case '!':
+ regex+= square_brace_open ? '^' : c;
+ break;
+ case '.': // This collection have different regex meaning
+ case '^': // and so need escaping.
+ case '(':
+ case ')':
+ case '+':
+ case '|':
+ case '$':
+ regex += '\\';
+ default:
+ regex += c;
+ break;
+ }
+
+ escaped = ('\\' == c);
+ square_brace_open = ('[' == c);
+ }
+
+ if (braces)
+ {
+ LL_ERRS() << "glob_to_regex: Unterminated brace expression: " << glob << LL_ENDL;
+ }
+
+ 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);
+}