reduce the amount of hacky path/string handling code
This commit is contained in:
parent
b6896cf337
commit
2990532d10
|
@ -136,9 +136,10 @@ namespace libtorrent {
|
|||
TORRENT_EXTRA_EXPORT void hard_link(std::string const& file
|
||||
, std::string const& link, error_code& ec);
|
||||
|
||||
TORRENT_EXTRA_EXPORT std::string split_path(std::string const& f
|
||||
, bool only_first_part = false);
|
||||
TORRENT_EXTRA_EXPORT char const* next_path_element(char const* p);
|
||||
// split out a path segment from the left side or right side
|
||||
TORRENT_EXTRA_EXPORT std::pair<string_view, string_view> rsplit_path(string_view p);
|
||||
TORRENT_EXTRA_EXPORT std::pair<string_view, string_view> lsplit_path(string_view p);
|
||||
|
||||
TORRENT_EXTRA_EXPORT std::string extension(std::string const& f);
|
||||
TORRENT_EXTRA_EXPORT std::string remove_extension(std::string const& f);
|
||||
TORRENT_EXTRA_EXPORT bool is_root_path(std::string const& f);
|
||||
|
@ -147,7 +148,6 @@ namespace libtorrent {
|
|||
// internal used by create_torrent.hpp
|
||||
TORRENT_EXTRA_EXPORT std::string parent_path(std::string const& f);
|
||||
TORRENT_EXTRA_EXPORT bool has_parent_path(std::string const& f);
|
||||
TORRENT_EXTRA_EXPORT char const* filename_cstr(char const* f);
|
||||
|
||||
// internal used by create_torrent.hpp
|
||||
TORRENT_EXTRA_EXPORT std::string filename(std::string const& f);
|
||||
|
|
|
@ -597,9 +597,9 @@ namespace {
|
|||
{
|
||||
entry& sympath_e = info["symlink path"];
|
||||
|
||||
std::string const split = split_path(m_files.symlink(first));
|
||||
for (char const* e = split.c_str(); e != nullptr; e = next_path_element(e))
|
||||
sympath_e.list().emplace_back(e);
|
||||
for (auto elems = lsplit_path(m_files.symlink(first)); !elems.first.empty();
|
||||
elems = lsplit_path(elems.second))
|
||||
sympath_e.list().emplace_back(elems.first);
|
||||
}
|
||||
if (!m_filehashes.empty())
|
||||
{
|
||||
|
@ -623,12 +623,13 @@ namespace {
|
|||
|
||||
{
|
||||
entry& path_e = file_e["path"];
|
||||
std::string const split = split_path(m_files.file_path(i));
|
||||
TORRENT_ASSERT(split.c_str() == m_files.name());
|
||||
|
||||
for (char const* e = next_path_element(split.c_str());
|
||||
e != nullptr; e = next_path_element(e))
|
||||
path_e.list().emplace_back(e);
|
||||
std::string const p = m_files.file_path(i);
|
||||
// deliberately skip the first path element, since that's the
|
||||
// "name" of the torrent already
|
||||
string_view path = lsplit_path(p).second;
|
||||
for (auto elems = lsplit_path(path); !elems.first.empty(); elems = lsplit_path(elems.second))
|
||||
path_e.list().emplace_back(elems.first);
|
||||
}
|
||||
|
||||
file_flags_t const flags = m_files.file_flags(i);
|
||||
|
@ -646,9 +647,9 @@ namespace {
|
|||
{
|
||||
entry& sympath_e = file_e["symlink path"];
|
||||
|
||||
std::string const split = split_path(m_files.symlink(i));
|
||||
for (char const* e = split.c_str(); e != nullptr; e = next_path_element(e))
|
||||
sympath_e.list().emplace_back(e);
|
||||
for (auto elems = lsplit_path(m_files.symlink(i)); !elems.first.empty();
|
||||
elems = lsplit_path(elems.second))
|
||||
sympath_e.list().emplace_back(elems.first);
|
||||
}
|
||||
if (!m_filehashes.empty() && m_filehashes[i] != sha1_hash())
|
||||
{
|
||||
|
|
|
@ -114,7 +114,8 @@ namespace {
|
|||
|
||||
}
|
||||
|
||||
// path is not supposed to include the name of the torrent itself.
|
||||
// path is supposed to include the name of the torrent itself.
|
||||
// or an absolute path, to move a file outside of the download directory
|
||||
void file_storage::update_path_index(internal_file_entry& e
|
||||
, std::string const& path, bool const set_name)
|
||||
{
|
||||
|
@ -128,40 +129,31 @@ namespace {
|
|||
|
||||
TORRENT_ASSERT(path[0] != '/');
|
||||
|
||||
// sorry about this messy string handling, but I did
|
||||
// profile it, and it was expensive
|
||||
char const* leaf = filename_cstr(path.c_str());
|
||||
// split the string into the leaf filename
|
||||
// and the branch path
|
||||
string_view leaf;
|
||||
string_view branch_path;
|
||||
if (leaf > path.c_str())
|
||||
{
|
||||
// split the string into the leaf filename
|
||||
// and the branch path
|
||||
branch_path = path;
|
||||
branch_path = branch_path.substr(0
|
||||
, static_cast<std::size_t>(leaf - path.c_str()));
|
||||
std::tie(branch_path, leaf) = rsplit_path(path);
|
||||
|
||||
// trim trailing slashes
|
||||
while (!branch_path.empty() && branch_path.back() == TORRENT_SEPARATOR)
|
||||
{
|
||||
branch_path.remove_suffix(1);
|
||||
}
|
||||
}
|
||||
if (branch_path.empty())
|
||||
{
|
||||
if (set_name) e.set_name(leaf);
|
||||
if (set_name) e.set_name(leaf.data());
|
||||
e.path_index = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (branch_path.size() >= m_name.size()
|
||||
&& branch_path.substr(0, m_name.size()) == m_name
|
||||
&& branch_path[m_name.size()] == TORRENT_SEPARATOR)
|
||||
// if the path *does* contain the name of the torrent (as we expect)
|
||||
// strip it before adding it to m_paths
|
||||
if (lsplit_path(branch_path).first == m_name)
|
||||
{
|
||||
branch_path.remove_prefix(m_name.size());
|
||||
while (!branch_path.empty() && branch_path.front() == TORRENT_SEPARATOR)
|
||||
{
|
||||
branch_path = lsplit_path(branch_path).second;
|
||||
// strip duplicate separators
|
||||
while (!branch_path.empty() && (branch_path.front() == TORRENT_SEPARATOR
|
||||
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
||||
|| branch_path.front() == '/'
|
||||
#endif
|
||||
))
|
||||
branch_path.remove_prefix(1);
|
||||
}
|
||||
e.no_root_dir = false;
|
||||
}
|
||||
else
|
||||
|
@ -170,7 +162,7 @@ namespace {
|
|||
}
|
||||
|
||||
e.path_index = get_or_add_path(branch_path);
|
||||
if (set_name) e.set_name(leaf);
|
||||
if (set_name) e.set_name(leaf.data());
|
||||
}
|
||||
|
||||
int file_storage::get_or_add_path(string_view const path)
|
||||
|
@ -607,7 +599,7 @@ namespace {
|
|||
else
|
||||
{
|
||||
if (m_files.empty())
|
||||
m_name = split_path(path, true);
|
||||
m_name = lsplit_path(path).first.to_string();
|
||||
}
|
||||
|
||||
// this is poor-man's emplace_back()
|
||||
|
@ -616,12 +608,12 @@ namespace {
|
|||
|
||||
// the last argument specified whether the function should also set
|
||||
// the filename. If it does, it will copy the leaf filename from path.
|
||||
// if filename is nullptr, we should copy it. If it isn't, we're borrowing
|
||||
// if filename is empty, we should copy it. If it isn't, we're borrowing
|
||||
// it and we can save the copy by setting it after this call to
|
||||
// update_path_index().
|
||||
update_path_index(e, path, filename.empty());
|
||||
|
||||
// filename is allowed to be nullptr, in which case we just use path
|
||||
// filename is allowed to be empty, in which case we just use path
|
||||
if (!filename.empty())
|
||||
e.set_name(filename.data(), true, int(filename.size()));
|
||||
|
||||
|
|
82
src/path.cpp
82
src/path.cpp
|
@ -468,42 +468,6 @@ namespace {
|
|||
rename(inf, newf, ec);
|
||||
}
|
||||
|
||||
std::string split_path(std::string const& f, bool only_first_part)
|
||||
{
|
||||
if (f.empty()) return f;
|
||||
|
||||
std::string ret;
|
||||
char const* start = f.c_str();
|
||||
char const* p = start;
|
||||
while (*start != 0)
|
||||
{
|
||||
while (*p != '/'
|
||||
&& *p != '\0'
|
||||
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
||||
&& *p != '\\'
|
||||
#endif
|
||||
) ++p;
|
||||
if (p - start > 0)
|
||||
{
|
||||
ret.append(start, aux::numeric_cast<std::size_t>(p - start));
|
||||
if (only_first_part) return ret;
|
||||
ret.append(1, '\0');
|
||||
}
|
||||
if (*p != 0) ++p;
|
||||
start = p;
|
||||
}
|
||||
if (only_first_part) return ret;
|
||||
ret.append(1, '\0');
|
||||
return ret;
|
||||
}
|
||||
|
||||
char const* next_path_element(char const* p)
|
||||
{
|
||||
p += strlen(p) + 1;
|
||||
if (*p == 0) return nullptr;
|
||||
return p;
|
||||
}
|
||||
|
||||
std::string extension(std::string const& f)
|
||||
{
|
||||
for (int i = int(f.size()) - 1; i >= 0; --i)
|
||||
|
@ -625,19 +589,6 @@ namespace {
|
|||
return std::string(f.c_str(), std::size_t(len));
|
||||
}
|
||||
|
||||
char const* filename_cstr(char const* f)
|
||||
{
|
||||
if (f == nullptr) return f;
|
||||
|
||||
char const* sep = std::strrchr(f, '/');
|
||||
#ifdef TORRENT_WINDOWS
|
||||
char const* altsep = std::strrchr(f, '\\');
|
||||
if (sep == 0 || altsep > sep) sep = altsep;
|
||||
#endif
|
||||
if (sep == nullptr) return f;
|
||||
return sep+1;
|
||||
}
|
||||
|
||||
std::string filename(std::string const& f)
|
||||
{
|
||||
if (f.empty()) return "";
|
||||
|
@ -919,6 +870,39 @@ namespace {
|
|||
remove(f, ec);
|
||||
}
|
||||
|
||||
std::pair<string_view, string_view> rsplit_path(string_view p)
|
||||
{
|
||||
if (p.empty()) return {{}, {}};
|
||||
if (p.back() == TORRENT_SEPARATOR_CHAR) p.remove_suffix(1);
|
||||
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
||||
else if (p.back() == '/') p.remove_suffix(1);
|
||||
#endif
|
||||
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
||||
auto const sep = p.find_last_of("/\\");
|
||||
#else
|
||||
auto const sep = p.find_last_of(TORRENT_SEPARATOR_CHAR);
|
||||
#endif
|
||||
if (sep == string_view::npos) return {{}, p};
|
||||
return { p.substr(0, sep), p.substr(sep + 1) };
|
||||
}
|
||||
|
||||
std::pair<string_view, string_view> lsplit_path(string_view p)
|
||||
{
|
||||
if (p.empty()) return {{}, {}};
|
||||
// for absolute paths, skip the initial "/"
|
||||
if (p.front() == TORRENT_SEPARATOR_CHAR) p.remove_prefix(1);
|
||||
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
||||
else if (p.front() == '/') p.remove_prefix(1);
|
||||
#endif
|
||||
#if defined(TORRENT_WINDOWS) || defined(TORRENT_OS2)
|
||||
auto const sep = p.find_first_of("/\\");
|
||||
#else
|
||||
auto const sep = p.find_first_of(TORRENT_SEPARATOR_CHAR);
|
||||
#endif
|
||||
if (sep == string_view::npos) return {p, {}};
|
||||
return { p.substr(0, sep), p.substr(sep + 1) };
|
||||
}
|
||||
|
||||
std::string complete(string_view f)
|
||||
{
|
||||
if (is_complete(f)) return f.to_string();
|
||||
|
|
|
@ -188,11 +188,6 @@ TORRENT_TEST(paths)
|
|||
TEST_EQUAL(remove_extension("blah.foo.bar"), "blah.foo");
|
||||
TEST_EQUAL(remove_extension("blah.foo."), "blah.foo");
|
||||
|
||||
TEST_EQUAL(filename("blah"), "blah");
|
||||
TEST_EQUAL(filename("/blah/foo/bar"), "bar");
|
||||
TEST_EQUAL(filename("/blah/foo/bar/"), "bar");
|
||||
TEST_EQUAL(filename("blah/"), "blah");
|
||||
|
||||
#ifdef TORRENT_WINDOWS
|
||||
TEST_EQUAL(is_root_path("c:\\blah"), false);
|
||||
TEST_EQUAL(is_root_path("c:\\"), true);
|
||||
|
@ -274,6 +269,54 @@ TORRENT_TEST(paths)
|
|||
TEST_EQUAL(complete("."), current_working_directory());
|
||||
}
|
||||
|
||||
TORRENT_TEST(filename)
|
||||
{
|
||||
#ifdef TORRENT_WINDOWS
|
||||
TEST_EQUAL(filename("blah"), "blah");
|
||||
TEST_EQUAL(filename("\\blah\\foo\\bar"), "bar");
|
||||
TEST_EQUAL(filename("\\blah\\foo\\bar\\"), "bar");
|
||||
TEST_EQUAL(filename("blah\\"), "blah");
|
||||
#endif
|
||||
TEST_EQUAL(filename("blah"), "blah");
|
||||
TEST_EQUAL(filename("/blah/foo/bar"), "bar");
|
||||
TEST_EQUAL(filename("/blah/foo/bar/"), "bar");
|
||||
TEST_EQUAL(filename("blah/"), "blah");
|
||||
}
|
||||
|
||||
TORRENT_TEST(split_path)
|
||||
{
|
||||
using r = std::pair<string_view, string_view>;
|
||||
|
||||
#ifdef TORRENT_WINDOWS
|
||||
TEST_CHECK(lsplit_path("\\b\\c\\d") == r("b", "c\\d"));
|
||||
TEST_CHECK(lsplit_path("a\\b\\c\\d") == r("a", "b\\c\\d"));
|
||||
TEST_CHECK(lsplit_path("a") == r("a", ""));
|
||||
TEST_CHECK(lsplit_path("") == r("", ""));
|
||||
|
||||
TEST_CHECK(lsplit_path("a\\b/c\\d") == r("a", "b/c\\d"));
|
||||
TEST_CHECK(lsplit_path("a/b\\c\\d") == r("a", "b\\c\\d"));
|
||||
|
||||
TEST_CHECK(rsplit_path("a\\b\\c\\d\\") == r("a\\b\\c", "d"));
|
||||
TEST_CHECK(rsplit_path("\\a\\b\\c\\d") == r("\\a\\b\\c", "d"));
|
||||
TEST_CHECK(rsplit_path("\\a") == r("", "a"));
|
||||
TEST_CHECK(rsplit_path("a") == r("", "a"));
|
||||
TEST_CHECK(rsplit_path("") == r("", ""));
|
||||
|
||||
TEST_CHECK(rsplit_path("a\\b/c\\d\\") == r("a\\b/c", "d"));
|
||||
TEST_CHECK(rsplit_path("a\\b\\c/d\\") == r("a\\b\\c", "d"));
|
||||
#endif
|
||||
TEST_CHECK(lsplit_path("/b/c/d") == r("b", "c/d"));
|
||||
TEST_CHECK(lsplit_path("a/b/c/d") == r("a", "b/c/d"));
|
||||
TEST_CHECK(lsplit_path("a") == r("a", ""));
|
||||
TEST_CHECK(lsplit_path("") == r("", ""));
|
||||
|
||||
TEST_CHECK(rsplit_path("a/b/c/d/") == r("a/b/c", "d"));
|
||||
TEST_CHECK(rsplit_path("/a/b/c/d") == r("/a/b/c", "d"));
|
||||
TEST_CHECK(rsplit_path("/a") == r("", "a"));
|
||||
TEST_CHECK(rsplit_path("a") == r("", "a"));
|
||||
TEST_CHECK(rsplit_path("") == r("", ""));
|
||||
}
|
||||
|
||||
// file class
|
||||
TORRENT_TEST(file)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue