diff --git a/src/support/FileName.cpp b/src/support/FileName.cpp index 65c682c2c6..0af295ad25 100644 --- a/src/support/FileName.cpp +++ b/src/support/FileName.cpp @@ -921,49 +921,18 @@ docstring const FileName::relPath(string const & path) const } -// Note: According to Qt, QFileInfo::operator== is undefined when -// both files do not exist (Qt4.5 gives true for all non-existent -// files, while Qt4.4 compares the filenames). -// see: -// http://www.qtsoftware.com/developer/task-tracker/ -// index_html?id=248471&method=entry. -bool operator==(FileName const & l, FileName const & r) +bool operator==(FileName const & lhs, FileName const & rhs) { - // FIXME: In future use Qt. - // Qt 4.4: We need to solve this warning from Qt documentation: - // * Long and short file names that refer to the same file on Windows are - // treated as if they referred to different files. - // This is supposed to be fixed for Qt5. - FileName const lhs(os::internal_path(l.absFilename())); - FileName const rhs(os::internal_path(r.absFilename())); - - if (lhs.empty()) - // QFileInfo::operator==() returns false if the two QFileInfo are empty. - return rhs.empty(); - - if (rhs.empty()) - // Avoid unnecessary checks below. - return false; - - lhs.d->refresh(); - rhs.d->refresh(); - - if (!lhs.d->fi.isSymLink() && !rhs.d->fi.isSymLink()) { - // Qt already checks if the filesystem is case sensitive or not. - // see note above why the extra check with fileName is needed. - return lhs.d->fi == rhs.d->fi - && lhs.d->fi.fileName() == rhs.d->fi.fileName(); + // Firstly, compare the filenames. + if (QString::compare(toqstr(lhs.absFilename()), + toqstr(rhs.absFilename()), + os::isFilesystemCaseSensitive() ? + Qt::CaseSensitive : Qt::CaseInsensitive) == 0) { + return true; } - // FIXME: When/if QFileInfo support symlink comparison, remove this code. - QFileInfo fi1(lhs.d->fi); - if (fi1.isSymLink()) - fi1 = QFileInfo(fi1.symLinkTarget()); - QFileInfo fi2(rhs.d->fi); - if (fi2.isSymLink()) - fi2 = QFileInfo(fi2.symLinkTarget()); - // see note above why the extra check with fileName is needed. - return fi1 == fi2 && fi1.fileName() == fi2.fileName(); + // They don't match, so check whether they point to the same file. + return os::isSameFile(lhs.toFilesystemEncoding(), rhs.toFilesystemEncoding()); } diff --git a/src/support/os.h b/src/support/os.h index c4277ef4fe..872858bb53 100644 --- a/src/support/os.h +++ b/src/support/os.h @@ -112,6 +112,9 @@ bool canAutoOpenFile(std::string const & ext, auto_open_mode const mode = VIEW); */ bool autoOpenFile(std::string const & filename, auto_open_mode const mode = VIEW); +/// Check whether two filenames point to the same file. +bool isSameFile(std::string const & fileone, std::string const & filetwo); + } // namespace os } // namespace support } // namespace lyx diff --git a/src/support/os_cygwin.cpp b/src/support/os_cygwin.cpp index 0e741a70c4..44f6caf87e 100644 --- a/src/support/os_cygwin.cpp +++ b/src/support/os_cygwin.cpp @@ -6,6 +6,7 @@ * \author Ruurd A. Reitsma * \author Claus Hentschel * \author Angus Leeming + * \author Enrico Forestieri * * Full author contact details are available in file CREDITS. * @@ -26,8 +27,8 @@ #include #include #include - #include +#include using namespace std; @@ -281,6 +282,21 @@ bool autoOpenFile(string const & filename, auto_open_mode const mode) win_path.c_str(), NULL, NULL, 1)) > 32; } + +bool isSameFile(string const & fileone, string const & filetwo) +{ + struct stat st1; + struct stat st2; + + if (::stat(fileone.c_str(), &st1) == 0 + && ::stat(filetwo.c_str(), &st2) == 0) { + return st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev; + } + + // One or both files cannot be accessed. + return false; +} + } // namespace os } // namespace support } // namespace lyx diff --git a/src/support/os_unix.cpp b/src/support/os_unix.cpp index 2c3f3262fd..6ca1094fdd 100644 --- a/src/support/os_unix.cpp +++ b/src/support/os_unix.cpp @@ -17,6 +17,8 @@ #include "support/FileName.h" #include "support/lstrings.h" +#include + #ifdef __APPLE__ #include #endif @@ -215,6 +217,21 @@ bool autoOpenFile(string const & filename, auto_open_mode const mode) #endif } + +bool isSameFile(string const & fileone, string const & filetwo) +{ + struct stat st1; + struct stat st2; + + if (::stat(fileone.c_str(), &st1) == 0 + && ::stat(filetwo.c_str(), &st2) == 0) { + return st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev; + } + + // One or both files cannot be accessed. + return false; +} + } // namespace os } // namespace support } // namespace lyx diff --git a/src/support/os_win32.cpp b/src/support/os_win32.cpp index fd1b6ad397..5035edb296 100644 --- a/src/support/os_win32.cpp +++ b/src/support/os_win32.cpp @@ -389,6 +389,43 @@ bool autoOpenFile(string const & filename, auto_open_mode const mode) to_local8bit(from_utf8(filename)).c_str(), NULL, NULL, 1)) > 32; } + +bool isSameFile(string const & fileone, string const & filetwo) +{ + HANDLE h1 = CreateFile(fileone.c_str(), 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + HANDLE h2 = CreateFile(filetwo.c_str(), 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + + if (h1 == INVALID_HANDLE_VALUE || h2 == INVALID_HANDLE_VALUE) { + // One or both files cannot be accessed. + if (h1 != INVALID_HANDLE_VALUE) + CloseHandle(h1); + if (h2 != INVALID_HANDLE_VALUE) + CloseHandle(h2); + return false; + } + + BY_HANDLE_FILE_INFORMATION info1; + BY_HANDLE_FILE_INFORMATION info2; + bool samefile = false; + if (GetFileInformationByHandle(h1, &info1) != 0 + && GetFileInformationByHandle(h2, &info2) != 0) { + // Serial number of the volumes containing the files. + ULONG st1_dev = info1.dwVolumeSerialNumber; + ULONG st2_dev = info2.dwVolumeSerialNumber; + // Unique identifiers associated to the files on the volumes. + ULONGLONG highbits = info1.nFileIndexHigh & 0x0000FFFF; + ULONGLONG st1_ino = (highbits << sizeof(ULONG)) | info1.nFileIndexLow; + highbits = info2.nFileIndexHigh & 0x0000FFFF; + ULONGLONG st2_ino = (highbits << sizeof(ULONG)) | info2.nFileIndexLow; + samefile = st1_ino == st2_ino && st1_dev == st2_dev; + } + CloseHandle(h1); + CloseHandle(h2); + return samefile; +} + } // namespace os } // namespace support } // namespace lyx