From 3288d49d62d9386853109e9f7cc8441c941ac104 Mon Sep 17 00:00:00 2001 From: Martchus Date: Fri, 29 Mar 2019 16:08:02 +0100 Subject: [PATCH] Support 'file://' URLs also when saving --- backuphelper.cpp | 16 ++++++++-------- basicfileinfo.cpp | 3 +-- basicfileinfo.h | 15 ++++++++++++++- matroska/matroskacontainer.cpp | 6 +++--- mediafileinfo.cpp | 13 +++++++------ mp4/mp4container.cpp | 12 ++++++------ ogg/oggcontainer.cpp | 6 +++--- 7 files changed, 42 insertions(+), 29 deletions(-) diff --git a/backuphelper.cpp b/backuphelper.cpp index 5dbcd2a..91c2054 100644 --- a/backuphelper.cpp +++ b/backuphelper.cpp @@ -69,7 +69,7 @@ void restoreOriginalFileFromBackupFile( } // remove original file and restore backup std::remove(originalPath.c_str()); - if (std::rename(backupPath.c_str(), originalPath.c_str())) { + if (std::rename(BasicFileInfo::pathForOpen(backupPath), BasicFileInfo::pathForOpen(originalPath))) { // can't rename/move the file (maybe backup dir on another partition) -> make a copy instead try { // need to open all streams again @@ -153,10 +153,10 @@ void createBackupFile(const std::string &backupDir, const std::string &originalP // test whether the backup path is still unused; otherwise continue loop #ifdef PLATFORM_WINDOWS - if (GetFileAttributes(backupPath.c_str()) == INVALID_FILE_ATTRIBUTES) { + if (GetFileAttributes(BasicFileInfo::pathForOpen(backupPath)) == INVALID_FILE_ATTRIBUTES) { #else struct stat backupStat; - if (stat(backupPath.c_str(), &backupStat)) { + if (stat(BasicFileInfo::pathForOpen(backupPath), &backupStat)) { #endif break; } @@ -168,7 +168,7 @@ void createBackupFile(const std::string &backupDir, const std::string &originalP } // rename original file - if (std::rename(originalPath.c_str(), backupPath.c_str())) { + if (std::rename(BasicFileInfo::pathForOpen(originalPath), BasicFileInfo::pathForOpen(backupPath))) { // can't rename/move the file (maybe backup dir on another partition) -> make a copy instead try { backupStream.exceptions(ios_base::failbit | ios_base::badbit); @@ -177,9 +177,9 @@ void createBackupFile(const std::string &backupDir, const std::string &originalP if (backupStream.is_open()) { backupStream.close(); } - backupStream.open(backupPath, ios_base::out | ios_base::binary); + backupStream.open(BasicFileInfo::pathForOpen(backupPath), ios_base::out | ios_base::binary); // ensure originalStream is opened with read permissions - originalStream.open(originalPath, ios_base::in | ios_base::binary); + originalStream.open(BasicFileInfo::pathForOpen(originalPath), ios_base::in | ios_base::binary); // do the actual copying backupStream << originalStream.rdbuf(); // streams are closed in the next try-block @@ -201,11 +201,11 @@ void createBackupFile(const std::string &backupDir, const std::string &originalP } // open backup stream backupStream.exceptions(ios_base::failbit | ios_base::badbit); - backupStream.open(backupPath, ios_base::in | ios_base::binary); + backupStream.open(BasicFileInfo::pathForOpen(backupPath), ios_base::in | ios_base::binary); } catch (const std::ios_base::failure &failure) { // can't open the new file // -> try to re-rename backup file in the error case to restore previous state - if (std::rename(backupPath.c_str(), originalPath.c_str())) { + if (std::rename(BasicFileInfo::pathForOpen(backupPath), BasicFileInfo::pathForOpen(originalPath))) { throw std::ios_base::failure("Unable to restore original file from backup file \"" % backupPath % "\" after failure: " + failure.what()); } else { throw std::ios_base::failure(argsToString("Unable to open backup file: ", failure.what())); diff --git a/basicfileinfo.cpp b/basicfileinfo.cpp index 8c39cf9..c20843b 100644 --- a/basicfileinfo.cpp +++ b/basicfileinfo.cpp @@ -59,8 +59,7 @@ void BasicFileInfo::open(bool readOnly) void BasicFileInfo::reopen(bool readOnly) { invalidated(); - m_file.open(startsWith(m_path, "file:/") ? m_path.data() + 6 : m_path.data(), - (m_readOnly = readOnly) ? ios_base::in | ios_base::binary : ios_base::in | ios_base::out | ios_base::binary); + m_file.open(pathForOpen(path()), (m_readOnly = readOnly) ? ios_base::in | ios_base::binary : ios_base::in | ios_base::out | ios_base::binary); m_file.seekg(0, ios_base::end); m_size = static_cast(m_file.tellg()); m_file.seekg(0, ios_base::beg); diff --git a/basicfileinfo.h b/basicfileinfo.h index 55530ce..4a37753 100644 --- a/basicfileinfo.h +++ b/basicfileinfo.h @@ -3,6 +3,7 @@ #include "./global.h" +#include #include #include @@ -20,7 +21,7 @@ public: // methods to control associated file stream void open(bool readOnly = false); - void reopen(bool readonly = false); + void reopen(bool readOnly = false); bool isOpen() const; bool isReadOnly() const; void close(); @@ -39,6 +40,7 @@ public: std::string pathWithoutExtension() const; static std::string containingDirectory(const std::string &path); std::string containingDirectory() const; + static const char *pathForOpen(const std::string &url); // methods to get, set the file size std::uint64_t size() const; @@ -129,6 +131,17 @@ inline void BasicFileInfo::reportPathChanged(const std::string &newPath) m_path = newPath; } +/*! + * \brief Returns removes the "file:/" prefix from \a url to be able to pass it to functions + * like open(), stat() and truncate(). + * \remarks If \a url is already a plain path it won't changed. + * \returns Returns a pointer the URL data itself. No copy is made. + */ +inline const char *BasicFileInfo::pathForOpen(const std::string &url) +{ + return ConversionUtilities::startsWith(url, "file:/") ? url.data() + 6 : url.data(); +} + } // namespace TagParser #endif // TAG_PARSER_BASICFILEINFO_H diff --git a/matroska/matroskacontainer.cpp b/matroska/matroskacontainer.cpp index 04112bf..723161a 100644 --- a/matroska/matroskacontainer.cpp +++ b/matroska/matroskacontainer.cpp @@ -1497,7 +1497,7 @@ void MatroskaContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFee try { BackupHelper::createBackupFile(fileInfo().backupDirectory(), fileInfo().path(), backupPath, outputStream, backupStream); // recreate original file, define buffer variables - outputStream.open(fileInfo().path(), ios_base::out | ios_base::binary | ios_base::trunc); + outputStream.open(BasicFileInfo::pathForOpen(fileInfo().path()), ios_base::out | ios_base::binary | ios_base::trunc); } catch (const std::ios_base::failure &failure) { diag.emplace_back( DiagLevel::Critical, argsToString("Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context); @@ -1507,9 +1507,9 @@ void MatroskaContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFee // open the current file as backupStream and create a new outputStream at the specified "save file path" try { backupStream.exceptions(ios_base::badbit | ios_base::failbit); - backupStream.open(fileInfo().path(), ios_base::in | ios_base::binary); + backupStream.open(BasicFileInfo::pathForOpen(fileInfo().path()), ios_base::in | ios_base::binary); fileInfo().close(); - outputStream.open(fileInfo().saveFilePath(), ios_base::out | ios_base::binary | ios_base::trunc); + outputStream.open(BasicFileInfo::pathForOpen(fileInfo().saveFilePath()), ios_base::out | ios_base::binary | ios_base::trunc); } catch (const std::ios_base::failure &failure) { diag.emplace_back(DiagLevel::Critical, argsToString("Opening streams to write output file failed: ", failure.what()), context); throw; diff --git a/mediafileinfo.cpp b/mediafileinfo.cpp index 92bc758..a535be4 100644 --- a/mediafileinfo.cpp +++ b/mediafileinfo.cpp @@ -1516,7 +1516,7 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr } progress.updateStep("Removing ID3v1 tag ..."); stream().close(); - if (truncate(path().data(), static_cast(size() - 128)) == 0) { + if (truncate(BasicFileInfo::pathForOpen(path()), static_cast(size() - 128)) == 0) { reportSizeChanged(size() - 128); } else { diag.emplace_back(DiagLevel::Critical, "Unable to truncate file to remove ID3v1 tag.", context); @@ -1630,7 +1630,7 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr try { BackupHelper::createBackupFile(backupDirectory(), path(), backupPath, outputStream, backupStream); // recreate original file, define buffer variables - outputStream.open(path(), ios_base::out | ios_base::binary | ios_base::trunc); + outputStream.open(BasicFileInfo::pathForOpen(path()), ios_base::out | ios_base::binary | ios_base::trunc); } catch (const std::ios_base::failure &failure) { diag.emplace_back( DiagLevel::Critical, argsToString("Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context); @@ -1641,8 +1641,8 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr try { close(); backupStream.exceptions(ios_base::badbit | ios_base::failbit); - backupStream.open(path(), ios_base::in | ios_base::binary); - outputStream.open(m_saveFilePath, ios_base::out | ios_base::binary | ios_base::trunc); + backupStream.open(BasicFileInfo::pathForOpen(path()), ios_base::in | ios_base::binary); + outputStream.open(BasicFileInfo::pathForOpen(m_saveFilePath), ios_base::out | ios_base::binary | ios_base::trunc); } catch (const std::ios_base::failure &failure) { diag.emplace_back(DiagLevel::Critical, argsToString("Opening streams to write output file failed: ", failure.what()), context); throw; @@ -1653,12 +1653,13 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr // reopen original file to ensure it is opened for writing try { close(); - outputStream.open(path(), ios_base::in | ios_base::out | ios_base::binary); + outputStream.open(BasicFileInfo::pathForOpen(path()), ios_base::in | ios_base::out | ios_base::binary); } catch (const std::ios_base::failure &failure) { diag.emplace_back(DiagLevel::Critical, argsToString("Opening the file with write permissions failed: ", failure.what()), context); throw; } } + // TODO: fix code duplication // start actual writing try { @@ -1757,7 +1758,7 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr // -> close stream before truncating outputStream.close(); // -> truncate file - if (truncate(path().c_str(), static_cast(newSize)) == 0) { + if (truncate(BasicFileInfo::pathForOpen(path()), static_cast(newSize)) == 0) { reportSizeChanged(newSize); } else { diag.emplace_back(DiagLevel::Critical, "Unable to truncate the file.", context); diff --git a/mp4/mp4container.cpp b/mp4/mp4container.cpp index b49f720..1f335ac 100644 --- a/mp4/mp4container.cpp +++ b/mp4/mp4container.cpp @@ -518,7 +518,7 @@ calculatePadding: try { BackupHelper::createBackupFile(fileInfo().backupDirectory(), fileInfo().path(), backupPath, outputStream, backupStream); // recreate original file, define buffer variables - outputStream.open(fileInfo().path(), ios_base::out | ios_base::binary | ios_base::trunc); + outputStream.open(BasicFileInfo::pathForOpen(fileInfo().path()), ios_base::out | ios_base::binary | ios_base::trunc); } catch (const std::ios_base::failure &failure) { diag.emplace_back( DiagLevel::Critical, argsToString("Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context); @@ -528,9 +528,9 @@ calculatePadding: // open the current file as backupStream and create a new outputStream at the specified "save file path" try { backupStream.exceptions(ios_base::badbit | ios_base::failbit); - backupStream.open(fileInfo().path(), ios_base::in | ios_base::binary); + backupStream.open(BasicFileInfo::pathForOpen(fileInfo().path()), ios_base::in | ios_base::binary); fileInfo().close(); - outputStream.open(fileInfo().saveFilePath(), ios_base::out | ios_base::binary | ios_base::trunc); + outputStream.open(BasicFileInfo::pathForOpen(fileInfo().saveFilePath()), ios_base::out | ios_base::binary | ios_base::trunc); } catch (const std::ios_base::failure &failure) { diag.emplace_back(DiagLevel::Critical, argsToString("Opening streams to write output file failed: ", failure.what()), context); throw; @@ -821,7 +821,7 @@ calculatePadding: } // the outputStream needs to be reopened to be able to read again outputStream.close(); - outputStream.open(fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary); + outputStream.open(BasicFileInfo::pathForOpen(fileInfo().path()), ios_base::in | ios_base::out | ios_base::binary); setStream(outputStream); } else { const auto newSize = static_cast(outputStream.tellp()); @@ -830,13 +830,13 @@ calculatePadding: // -> close stream before truncating outputStream.close(); // -> truncate file - if (truncate(fileInfo().path().c_str(), static_cast(newSize)) == 0) { + if (truncate(BasicFileInfo::pathForOpen(fileInfo().path()), static_cast(newSize)) == 0) { fileInfo().reportSizeChanged(newSize); } else { diag.emplace_back(DiagLevel::Critical, "Unable to truncate the file.", context); } // -> reopen the stream again - outputStream.open(fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary); + outputStream.open(BasicFileInfo::pathForOpen(fileInfo().path()), ios_base::in | ios_base::out | ios_base::binary); } else { // file is longer after the modification -> just report new size fileInfo().reportSizeChanged(newSize); diff --git a/ogg/oggcontainer.cpp b/ogg/oggcontainer.cpp index e112d9a..2a0fca7 100644 --- a/ogg/oggcontainer.cpp +++ b/ogg/oggcontainer.cpp @@ -377,7 +377,7 @@ void OggContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFeedback try { BackupHelper::createBackupFile(fileInfo().backupDirectory(), fileInfo().path(), backupPath, fileInfo().stream(), backupStream); // recreate original file, define buffer variables - fileInfo().stream().open(fileInfo().path(), ios_base::out | ios_base::binary | ios_base::trunc); + fileInfo().stream().open(BasicFileInfo::pathForOpen(fileInfo().path()), ios_base::out | ios_base::binary | ios_base::trunc); } catch (const std::ios_base::failure &failure) { diag.emplace_back( DiagLevel::Critical, argsToString("Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context); @@ -387,9 +387,9 @@ void OggContainer::internalMakeFile(Diagnostics &diag, AbortableProgressFeedback // open the current file as backupStream and create a new outputStream at the specified "save file path" try { backupStream.exceptions(ios_base::badbit | ios_base::failbit); - backupStream.open(fileInfo().path(), ios_base::in | ios_base::binary); + backupStream.open(BasicFileInfo::pathForOpen(fileInfo().path()), ios_base::in | ios_base::binary); fileInfo().close(); - fileInfo().stream().open(fileInfo().saveFilePath(), ios_base::out | ios_base::binary | ios_base::trunc); + fileInfo().stream().open(BasicFileInfo::pathForOpen(fileInfo().saveFilePath()), ios_base::out | ios_base::binary | ios_base::trunc); } catch (const std::ios_base::failure &failure) { diag.emplace_back(DiagLevel::Critical, argsToString("Opening streams to write output file failed: ", failure.what()), context); throw;