From 665a1d1b3581dc0fc7076cf66be2fc8982ab7be2 Mon Sep 17 00:00:00 2001 From: Nils Maier Date: Mon, 18 Jul 2016 23:11:55 +0200 Subject: [PATCH] Better auto-renaming Up until now aria2 file auto renaming worked by just appending a new unique number to the file path, behind the file name extension, if any, changing what most other programs consider the file extension in the process. Now, aria2 will attempt to insert the number before the file extension, leaving the extension intact, so that e.g. a ".jpg" still is a ".jpg" file and opens in your configured image viewer when clicking it. If a file has no extension (incl. so called "dot files"), the number will be appended to the file name as usual. Note: This is a potentially breaking change that might break third party scripts that rely on aria2 auto file renaming producing a certain format for renamed files. Please fix your scripts accordingly. Closes GH-709 --- doc/manual-src/en/aria2c.rst | 3 +- src/RequestGroup.cc | 22 ++++++++++++++- src/RequestGroup.h | 4 +-- test/RequestGroupTest.cc | 53 ++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/doc/manual-src/en/aria2c.rst b/doc/manual-src/en/aria2c.rst index 831828605..539b8e662 100644 --- a/doc/manual-src/en/aria2c.rst +++ b/doc/manual-src/en/aria2c.rst @@ -1175,7 +1175,8 @@ Advanced Options Rename file name if the same file already exists. This option works only in HTTP(S)/FTP download. - The new file name has a dot and a number(1..9999) appended. + The new file name has a dot and a number(1..9999) appended after the + name, but before the file extension, if any. Default: ``true`` .. option:: --auto-save-interval= diff --git a/src/RequestGroup.cc b/src/RequestGroup.cc index c89dacbf4..748b6fd45 100644 --- a/src/RequestGroup.cc +++ b/src/RequestGroup.cc @@ -778,8 +778,28 @@ void RequestGroup::tryAutoFileRenaming() fmt("File renaming failed: %s", getFirstFilePath().c_str()), error_code::FILE_RENAMING_FAILED); } + auto fn = filepath; + std::string ext; + const auto idx = fn.find_last_of("."); + const auto slash = fn.find_last_of("\\/"); + // Do extract the extension, as in "file.ext" = "file" and ".ext", + // but do not consider ".file" to be a file name without extension instead + // of a blank file name and an extension of ".file" + if (idx != std::string::npos && + // fn has no path component and starts with a dot, but has no extension + // otherwise + idx != 0 && + // has a file path component if we found a slash. + // if slash == idx - 1 this means a form of "*/.*", so the file name + // starts with a dot, has no extension otherwise, and therefore do not + // extract an extension either + (slash == std::string::npos || slash < idx - 1) + ) { + ext = fn.substr(idx); + fn = fn.substr(0, idx); + } for (int i = 1; i < 10000; ++i) { - auto newfilename = fmt("%s.%d", filepath.c_str(), i); + auto newfilename = fmt("%s.%d%s", fn.c_str(), i, ext.c_str()); File newfile(newfilename); File ctrlfile(newfile.getPath() + DefaultBtProgressInfoFile::getSuffix()); if (!newfile.exists() || (newfile.exists() && ctrlfile.exists())) { diff --git a/src/RequestGroup.h b/src/RequestGroup.h index 8b7862ebd..6698f93de 100644 --- a/src/RequestGroup.h +++ b/src/RequestGroup.h @@ -199,8 +199,6 @@ private: void initializePostDownloadHandler(); - void tryAutoFileRenaming(); - // Returns the result code of this RequestGroup. If the download // finished, then returns error_code::FINISHED. If the // download didn't finish and error result is available in @@ -219,6 +217,8 @@ public: bool isCheckIntegrityReady(); + void tryAutoFileRenaming(); + const std::shared_ptr& getSegmentMan() const { return segmentMan_; diff --git a/test/RequestGroupTest.cc b/test/RequestGroupTest.cc index 47bc6e058..ddbb56c37 100644 --- a/test/RequestGroupTest.cc +++ b/test/RequestGroupTest.cc @@ -14,6 +14,7 @@ class RequestGroupTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(RequestGroupTest); CPPUNIT_TEST(testGetFirstFilePath); + CPPUNIT_TEST(testTryAutoFileRenaming); CPPUNIT_TEST(testCreateDownloadResult); CPPUNIT_TEST_SUITE_END(); @@ -24,6 +25,7 @@ public: void setUp() { option_.reset(new Option()); } void testGetFirstFilePath(); + void testTryAutoFileRenaming(); void testCreateDownloadResult(); }; @@ -44,6 +46,57 @@ void RequestGroupTest::testGetFirstFilePath() CPPUNIT_ASSERT_EQUAL(std::string("[MEMORY]myfile"), group.getFirstFilePath()); } +void RequestGroupTest::testTryAutoFileRenaming() +{ + std::shared_ptr ctx( + new DownloadContext(1_k, 1_k, "/tmp/myfile")); + + RequestGroup group(GroupId::create(), option_); + group.setDownloadContext(ctx); + + option_->put(PREF_AUTO_FILE_RENAMING, "false"); + try { + group.tryAutoFileRenaming(); + } + catch (const Exception& ex) { + CPPUNIT_ASSERT_EQUAL(error_code::FILE_ALREADY_EXISTS, ex.getErrorCode()); + + } + + option_->put(PREF_AUTO_FILE_RENAMING, "true"); + group.tryAutoFileRenaming(); + CPPUNIT_ASSERT_EQUAL(std::string("/tmp/myfile.1"), group.getFirstFilePath()); + + ctx->getFirstFileEntry()->setPath("/tmp/myfile.txt"); + group.tryAutoFileRenaming(); + CPPUNIT_ASSERT_EQUAL(std::string("/tmp/myfile.1.txt"), group.getFirstFilePath()); + + ctx->getFirstFileEntry()->setPath("/tmp.txt/myfile"); + group.tryAutoFileRenaming(); + CPPUNIT_ASSERT_EQUAL(std::string("/tmp.txt/myfile.1"), group.getFirstFilePath()); + + ctx->getFirstFileEntry()->setPath("/tmp.txt/myfile.txt"); + group.tryAutoFileRenaming(); + CPPUNIT_ASSERT_EQUAL(std::string("/tmp.txt/myfile.1.txt"), group.getFirstFilePath()); + + ctx->getFirstFileEntry()->setPath(".bashrc"); + group.tryAutoFileRenaming(); + CPPUNIT_ASSERT_EQUAL(std::string(".bashrc.1"), group.getFirstFilePath()); + + ctx->getFirstFileEntry()->setPath(".bashrc.txt"); + group.tryAutoFileRenaming(); + CPPUNIT_ASSERT_EQUAL(std::string(".bashrc.1.txt"), group.getFirstFilePath()); + + ctx->getFirstFileEntry()->setPath("/tmp.txt/.bashrc"); + group.tryAutoFileRenaming(); + CPPUNIT_ASSERT_EQUAL(std::string("/tmp.txt/.bashrc.1"), group.getFirstFilePath()); + + ctx->getFirstFileEntry()->setPath("/tmp.txt/.bashrc.txt"); + group.tryAutoFileRenaming(); + CPPUNIT_ASSERT_EQUAL(std::string("/tmp.txt/.bashrc.1.txt"), group.getFirstFilePath()); + +} + void RequestGroupTest::testCreateDownloadResult() { std::shared_ptr ctx(