diff --git a/include/libtorrent/aux_/path.hpp b/include/libtorrent/aux_/path.hpp index 30b2f0a38..7823601a6 100644 --- a/include/libtorrent/aux_/path.hpp +++ b/include/libtorrent/aux_/path.hpp @@ -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 rsplit_path(string_view p); + TORRENT_EXTRA_EXPORT std::pair 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); diff --git a/src/create_torrent.cpp b/src/create_torrent.cpp index c20fdb72a..3dd23fbc7 100644 --- a/src/create_torrent.cpp +++ b/src/create_torrent.cpp @@ -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()) { diff --git a/src/file_storage.cpp b/src/file_storage.cpp index 82f4aa193..35d626c9c 100644 --- a/src/file_storage.cpp +++ b/src/file_storage.cpp @@ -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(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())); diff --git a/src/path.cpp b/src/path.cpp index 25f717948..47f48159d 100644 --- a/src/path.cpp +++ b/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(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 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 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(); diff --git a/test/test_file.cpp b/test/test_file.cpp index c6716d8a4..8f6590f37 100644 --- a/test/test_file.cpp +++ b/test/test_file.cpp @@ -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; + +#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) {