Replace rak::regex with fnmatch from POSIX <fnmatch.h>

rak::regex was a hand-rolled glob matcher supporting only '*'
wildcards. Replace it with the standard POSIX fnmatch() which supports
the same glob patterns plus '?' and '[...]' character classes.

- src/core/manager.cc: use fnmatch for directory entry filtering
- src/command_download.cc: use fnmatch for file path pattern matching
- Makefile.am: remove rak/regex.h from EXTRA_DIST
- rak/regex.h: deleted, no longer needed
This commit is contained in:
trim21
2026-06-24 20:16:03 +08:00
committed by Jari Sundell
parent ce4cd27697
commit 8598873627
4 changed files with 8 additions and 118 deletions
-1
View File
@@ -7,7 +7,6 @@ nobase_dist_pkgdata_DATA = \
lua/rtorrent.lua lua/rtorrent.lua
EXTRA_DIST= \ EXTRA_DIST= \
rak/regex.h \
scripts/checks.m4 \ scripts/checks.m4 \
scripts/common.m4 \ scripts/common.m4 \
scripts/attributes.m4 scripts/attributes.m4
-109
View File
@@ -1,109 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
// This is a hacked up whole string pattern matching. Replace with
// TR1's regex when that becomes widely available. It is intended for
// small strings.
#ifndef RAK_REGEX_H
#define RAK_REGEX_H
#include <sys/types.h>
#include <algorithm>
#include <functional>
#include <string>
#include <list>
namespace rak {
class regex {
public:
regex() {}
regex(const std::string& p) : m_pattern(p) {}
const std::string& pattern() const { return m_pattern; }
bool operator () (const std::string& p) const;
private:
std::string m_pattern;
};
// This isn't optimized, or very clean. A simple hack that should work.
inline bool
regex::operator () (const std::string& text) const {
if (m_pattern.empty() ||
text.empty() ||
(m_pattern[0] != '*' && m_pattern[0] != text[0]))
return false;
// Replace with unordered_vector?
std::list<unsigned int> paths;
paths.push_front(0);
for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) {
for (std::list<unsigned int>::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) {
unsigned int next = *itrPaths + 1;
if (m_pattern[*itrPaths] != '*')
itrPaths = paths.erase(itrPaths);
else
itrPaths++;
// When we reach the end of 'm_pattern', we don't have a whole
// match of 'text'.
if (next == m_pattern.size())
continue;
// Push to the back so that '*' will match zero length strings.
if (m_pattern[next] == '*')
paths.push_back(next);
if (m_pattern[next] == *itrText)
paths.push_front(next);
}
if (paths.empty())
return false;
}
return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end();
}
}
#endif
+3 -3
View File
@@ -5,7 +5,7 @@
#include <functional> #include <functional>
#include <netdb.h> #include <netdb.h>
#include <unistd.h> #include <unistd.h>
#include <rak/regex.h> #include <fnmatch.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
@@ -340,7 +340,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
// parsing and searching command map for every single call. // parsing and searching command map for every single call.
torrent::Object resultRaw = torrent::Object::create_list(); torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list(); torrent::Object::list_type& result = resultRaw.as_list();
std::vector<rak::regex> regex_list; std::vector<std::string> regex_list;
bool use_regex = true; bool use_regex = true;
@@ -354,7 +354,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
for (const auto& file : *download->file_list()) { for (const auto& file : *download->file_list()) {
if (use_regex && if (use_regex &&
std::none_of(regex_list.begin(), regex_list.end(), [&file](const auto& r) { return r(file->path()->as_string()); })) std::none_of(regex_list.begin(), regex_list.end(), [&file](const auto& pattern) { return fnmatch(pattern.c_str(), file->path()->as_string().c_str(), 0) == 0; }))
continue; continue;
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list(); torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
+5 -5
View File
@@ -8,7 +8,7 @@
#include <sstream> #include <sstream>
#include <unistd.h> #include <unistd.h>
#include <sys/select.h> #include <sys/select.h>
#include <rak/regex.h> #include <fnmatch.h>
#include <torrent/utils/resume.h> #include <torrent/utils/resume.h>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
@@ -392,9 +392,9 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
// Might be an idea to use depth-first search instead. // Might be an idea to use depth-first search instead.
for (; first != last; ++first) { for (; first != last; ++first) {
rak::regex r(*first); const std::string& pattern = *first;
if (r.pattern().empty()) if (pattern.empty())
continue; continue;
// Special case for ".."? // Special case for ".."?
@@ -402,8 +402,8 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
for (auto& itr : currentCache) { for (auto& itr : currentCache) {
// Only include filenames starting with '.' if the pattern // Only include filenames starting with '.' if the pattern
// starts with the same. // starts with the same.
itr.update((r.pattern()[0] != '.') ? utils::Directory::update_hide_dot : 0); itr.update((pattern[0] != '.') ? utils::Directory::update_hide_dot : 0);
itr.erase(std::remove_if(itr.begin(), itr.end(), [r](const utils::directory_entry& entry) { return !r(entry.s_name); }), itr.end()); itr.erase(std::remove_if(itr.begin(), itr.end(), [&pattern](const utils::directory_entry& entry) { return fnmatch(pattern.c_str(), entry.s_name.c_str(), 0) != 0; }), itr.end());
for (const auto& cache : itr) for (const auto& cache : itr)
nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache)); nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache));