]> Some of my projects - localmylist.git/commitdiff
Improve renaming on windows.
authorAPTX <marek321@gmail.com>
Sun, 5 May 2013 00:53:19 +0000 (02:53 +0200)
committerAPTX <marek321@gmail.com>
Sun, 5 May 2013 00:53:19 +0000 (02:53 +0200)
Files can now be renamed, even if they differ in case only. Case changes are more difficult to handle on windows, because a case change does not change the path, requiring checking and/or renaming of every path element. The path stored in file location is now the path reported by the system.

localmylist/filelocationchecktask.cpp
localmylist/renamehandler.cpp
localmylist/renameutils.cpp
localmylist/renameutils.h

index 1e8b58465e07aa8f55d58870287bba7f13331229..bba82864607255e455581f8d901694482f47adbe 100644 (file)
@@ -1,6 +1,8 @@
 #include "filelocationchecktask.h"
 
 #include "database.h"
+#include "renameutils.h"
+
 #include <QFileInfo>
 
 #include <QDebug>
@@ -28,7 +30,7 @@ void FileLocationCheckTask::workUnit()
        db->transaction();
        QList<FileLocation> fileLocations = db->getFileLocationBatch(lastLocationId, OPERATIONS_PER_UNIT);
 
-       for (const FileLocation &fl : fileLocations)
+       for (FileLocation &fl : fileLocations)
        {
                if (!QFileInfo(fl.path).exists())
                {
@@ -37,6 +39,21 @@ void FileLocationCheckTask::workUnit()
                                        .arg(fl.path));
                        db->removeFileLocation(fl.locationId);
                }
+               else
+               {
+                       QString canonicalPath = exactPath(QFileInfo(fl.path).canonicalFilePath());
+
+                       if (!canonicalPath.isEmpty() && canonicalPath != fl.path)
+                       {
+                               qDebug() << QObject::tr("Fixing File location %1: %2")
+                                                       .arg(fl.locationId)
+                                                       .arg(fl.path);
+                               fl.path = canonicalPath;
+                               db->setFileLocation(fl);
+                       }
+
+               }
+
                lastLocationId = fl.locationId;
        }
        db->commit();
index 52c160c664c4cec2bf37d5f49d2c4d3717e13ba3..fd597179e5b466285c21bd6e0b5e041a7c303b14 100644 (file)
@@ -103,11 +103,8 @@ void RenameHandler::handleRename()
                QString newFileString = newFilePath + "/" + newFileName;
                QFileInfo newFile(newFileString);
 
-#ifdef Q_OS_WIN
-               bool isSamePath = oldFile.canonicalFilePath().compare(newFileString, Qt::CaseInsensitive) == 0;
-#else
                bool isSamePath = oldFile.canonicalFilePath() == newFileString;
-#endif
+               bool differenceInCaseOnly = !isSamePath && oldFile.canonicalFilePath().compare(newFileString, Qt::CaseInsensitive) == 0;
 
                if (isSamePath)
                {
@@ -116,7 +113,7 @@ void RenameHandler::handleRename()
                        continue;
                }
 
-               if (newFile.exists())
+               if (!differenceInCaseOnly && newFile.exists())
                {
                        fl.failedRename = true;
                        fl.renameError = tr("Rename: Failed to rename file <%1>. Destination <%2> exists.").arg(oldFile.filePath()).arg(newFileString);
@@ -166,7 +163,8 @@ void RenameHandler::handleRename()
                }
 #endif
 
-               if (!QFile::rename(oldFile.canonicalFilePath(), newFileString))
+               QString exactFileName;
+               if (!renameFile(oldFile.canonicalFilePath(), newFileString, &exactFileName))
                {
                        fl.failedRename = true;
                        fl.renameError = tr("Rename: Failed to rename file <%1>. Failed to rename file to <%2>").arg(oldFile.canonicalFilePath()).arg(newFileString);
@@ -175,12 +173,21 @@ void RenameHandler::handleRename()
                        continue;
                }
 
-               fl.path = newFileString;
+               if (exactFileName.isEmpty())
+               {
+                       qWarning() << "exactFileName is empty";
+                       fl.path = newFileString;
+               }
+               else
+               {
+                       fl.path = exactFileName;
+               }
+
                fl.failedRename = false;
                fl.renameError = "";
                db->setFileLocation(fl);
 
-               db->log(tr("Rename: File <%1> was renamed to <%2>").arg(oldFile.canonicalFilePath()).arg(newFileString));
+               db->log(tr("Rename: File <%1> was renamed to <%2>").arg(oldFile.canonicalFilePath()).arg(fl.path));
 
                if (settings->get("renameRemoveEmptyDirectories", false))
                {
index a1e115f7b0ce8ccf6b411e409b83980b3a9f1bfb..e6f4bdb3851a6ebcec733a53b7a392e6c7124973 100644 (file)
@@ -5,6 +5,8 @@
 #include <QDateTime>
 #include <QFileInfo>
 
+#include <QDebug>
+
 namespace LocalMyList {
 
 void setupRenameEnv(const QSqlRecord &record, RenameParser::Environment &env)
@@ -65,4 +67,117 @@ void setupRenameEnv(const QSqlRecord &record, RenameParser::Environment &env)
        env["MaxRelatedEpNo"] = record.value("max_related_epno").toString();
 }
 
+#ifdef Q_OS_WIN
+#include <QDir>
+#include <windows.h>
+#endif
+
+
+QString toWindowsPath(const QString &path)
+{
+       return QDir::toNativeSeparators(path).prepend("\\\\?\\");
+}
+
+QString exactPath(const QString &path)
+{
+#ifndef Q_OS_WIN
+       return QFileInfo(path).canonicalFilePath();
+#else
+       HANDLE handle = INVALID_HANDLE_VALUE;
+       static const DWORD BUFFERSIZE = 10000;
+       wchar_t finalName[BUFFERSIZE];
+
+       QString pathWindows = toWindowsPath(path);
+       const wchar_t *pathW = reinterpret_cast<const wchar_t *>(pathWindows.utf16());
+
+       handle = CreateFileW(pathW,
+                                               0,
+                                               FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                               NULL,
+                                               OPEN_EXISTING,
+                                               FILE_FLAG_BACKUP_SEMANTICS,
+                                               NULL);
+
+       if (handle == INVALID_HANDLE_VALUE)
+       {
+               DWORD lastError = GetLastError();
+               qDebug() << "exactPath invalid handle" << lastError << path;
+               return QString();
+       }
+
+       DWORD length = GetFinalPathNameByHandleW(handle,
+                                                                                        finalName,
+                                                                                        BUFFERSIZE,
+                                                                                        VOLUME_NAME_DOS);
+
+       if (length > BUFFERSIZE)
+               return QString();
+
+       QString ret = QString::fromWCharArray(finalName, length);
+       CloseHandle(handle);
+
+       ret = ret.mid(4);
+       ret = QDir::fromNativeSeparators(ret);
+
+       return ret;
+#endif
+}
+
+bool renameFile(const QString &oldName, const QString &newName, QString *actualPath)
+{
+#ifndef Q_OS_WIN
+       if (!QFile::rename(oldName, newName))
+               return false;
+
+       if (actualPath) *actualPath = exactPath(oldName);
+       return true;
+
+#else
+       if (oldName == newName)
+       {
+               if (actualPath) *actualPath = exactPath(oldName);
+               return true;
+       }
+
+       const QString oldNameWindows = toWindowsPath(oldName);
+       const QString newNameWindows = toWindowsPath(newName);
+       const wchar_t *oldNameW = reinterpret_cast<const wchar_t *>(oldNameWindows.utf16());
+       const wchar_t *newNameW = reinterpret_cast<const wchar_t *>(newNameWindows.utf16());
+
+       bool ret = MoveFileW(oldNameW, newNameW);
+
+       if (!ret)
+       {
+               DWORD lastError = GetLastError();
+               qDebug() << "Move Failed" << lastError;
+               return false;
+       }
+
+       const QString newNameExact = exactPath(newName);
+
+       if (newNameExact.isEmpty())
+       {
+               qDebug() << "Failed to get exact path";
+               return false;
+       }
+
+       const QString newNameDir = QFileInfo(newName).absolutePath();
+       const QString newNameExactDir = QFileInfo(newNameExact).absolutePath();
+
+       // Paths differ in case only
+       if (newNameDir != newNameExactDir && newNameDir.compare(newNameExactDir, Qt::CaseInsensitive) == 0)
+       {
+               bool ret = renameFile(newNameExactDir, newNameDir);
+               if (actualPath) *actualPath = exactPath(newName);
+               return ret;
+       }
+
+       // Either paths are identical or the new path differs by more than case.
+       if (actualPath) *actualPath = newNameExact;
+
+       return true;
+#endif
+}
+
+
 } // namespace LocalMyList
index c5628a04764d10aa178d2cdd015926c1c9984fd7..874942d61c59e0b04dfbb9885c90e4dfa970147e 100644 (file)
@@ -4,11 +4,15 @@
 #include "localmylist_global.h"
 #include <RenameParser/RenameEngine>
 #include <QSqlRecord>
+#include <QString>
 
 namespace LocalMyList {
 
 void LOCALMYLISTSHARED_EXPORT setupRenameEnv(const QSqlRecord &record, RenameParser::Environment &env);
 
+QString exactPath(const QString &path);
+bool LOCALMYLISTSHARED_EXPORT renameFile(const QString &oldName, const QString &newName, QString *actualPath = 0);
+
 } // namespace LocalMyList
 
 #endif // RENAMEUTILS_H