Support 'file://' URLs also when saving

This commit is contained in:
Martchus 2019-03-29 16:08:02 +01:00
parent 7efa7a0d5a
commit 3288d49d62
7 changed files with 42 additions and 29 deletions

View File

@ -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()));

View File

@ -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<std::uint64_t>(m_file.tellg());
m_file.seekg(0, ios_base::beg);

View File

@ -3,6 +3,7 @@
#include "./global.h"
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/io/nativefilestream.h>
#include <cstdint>
@ -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

View File

@ -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;

View File

@ -1516,7 +1516,7 @@ void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &pr
}
progress.updateStep("Removing ID3v1 tag ...");
stream().close();
if (truncate(path().data(), static_cast<std::streamoff>(size() - 128)) == 0) {
if (truncate(BasicFileInfo::pathForOpen(path()), static_cast<std::streamoff>(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<streamoff>(newSize)) == 0) {
if (truncate(BasicFileInfo::pathForOpen(path()), static_cast<streamoff>(newSize)) == 0) {
reportSizeChanged(newSize);
} else {
diag.emplace_back(DiagLevel::Critical, "Unable to truncate the file.", context);

View File

@ -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<std::uint64_t>(outputStream.tellp());
@ -830,13 +830,13 @@ calculatePadding:
// -> close stream before truncating
outputStream.close();
// -> truncate file
if (truncate(fileInfo().path().c_str(), static_cast<iostream::off_type>(newSize)) == 0) {
if (truncate(BasicFileInfo::pathForOpen(fileInfo().path()), static_cast<iostream::off_type>(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);

View File

@ -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;