Compare file inodes if possible in wxFileName::SameAs().
authorVadim Zeitlin <vadim@wxwidgets.org>
Sat, 25 Feb 2012 23:49:35 +0000 (23:49 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sat, 25 Feb 2012 23:49:35 +0000 (23:49 +0000)
This allows to correctly return when the two files with different names
actually refer to the same file system object.

Closes #910.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@70687 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

src/common/filename.cpp
tests/filename/filenametest.cpp

index a3f8ac3c5d1d5e3f4f08e410d213b0f67ad624e5..0cacb63e47029db9e9f242774008b36776b951aa 100644 (file)
@@ -1794,8 +1794,16 @@ bool wxFileName::SameAs(const wxFileName& filepath, wxPathFormat format) const
     if ( fn1.GetFullPath() == fn2.GetFullPath() )
         return true;
 
-    // TODO: compare inodes for Unix, this works even when filenames are
-    //       different but files are the same (symlinks) (VZ)
+#if defined(__UNIX__)
+    wxStructStat st1, st2;
+    if ( wxStat(fn1.GetFullPath(), &st1) == 0 &&
+            wxStat(fn2.GetFullPath(), &st2) == 0 )
+    {
+        if ( st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev )
+            return true;
+    }
+    //else: It's not an error if one or both files don't exist.
+#endif // defined __UNIX__
 
     return false;
 }
index 56474483454c5c82d8da349ac3baf93abc11184e..aefd7fee5701e0927528e5c47575489868b6a8b8 100644 (file)
     #include "wx/msw/registry.h"
 #endif // __WXMSW__
 
+#ifdef __UNIX__
+    #include <unistd.h>
+#endif // __UNIX__
+
 #include "testfile.h"
 
 // ----------------------------------------------------------------------------
@@ -136,6 +140,7 @@ private:
         CPPUNIT_TEST( TestCreateTempFileName );
         CPPUNIT_TEST( TestGetTimes );
         CPPUNIT_TEST( TestExists );
+        CPPUNIT_TEST( TestIsSame );
     CPPUNIT_TEST_SUITE_END();
 
     void TestConstruction();
@@ -154,6 +159,7 @@ private:
     void TestCreateTempFileName();
     void TestGetTimes();
     void TestExists();
+    void TestIsSame();
 
     DECLARE_NO_COPY_CLASS(FileNameTestCase)
 };
@@ -673,3 +679,51 @@ void FileNameTestCase::TestExists()
     CPPUNIT_ASSERT( wxFileName::Exists("/dev/null") );
 #endif // __UNIX__
 }
+
+void FileNameTestCase::TestIsSame()
+{
+    wxFileName fn1( wxFileName::CreateTempFileName( "filenametest1" ) );
+    CPPUNIT_ASSERT( fn1.IsOk() );
+    wxON_BLOCK_EXIT1( wxRemoveFile, fn1.GetFullPath() );
+
+    wxFileName fn2( wxFileName::CreateTempFileName( "filenametest2" ) );
+    CPPUNIT_ASSERT( fn2.IsOk() );
+    wxON_BLOCK_EXIT1( wxRemoveFile, fn2.GetFullPath() );
+
+    CPPUNIT_ASSERT( fn1.SameAs( fn1 ) );
+    CPPUNIT_ASSERT( !fn1.SameAs( fn2 ) );
+
+#if defined(__UNIX__)
+    // We need to create a temporary directory and a temporary link.
+    // Unfortunately we can't use wxFileName::CreateTempFileName() for neither
+    // as it creates plain files, so use tempnam() explicitly instead.
+    char* tn = tempnam(NULL, "wxfn1");
+    const wxString tempdir1 = wxString::From8BitData(tn);
+    free(tn);
+
+    CPPUNIT_ASSERT( wxFileName::Mkdir(tempdir1) );
+    // Unfortunately the casts are needed to select the overload we need here.
+    wxON_BLOCK_EXIT2( static_cast<bool (*)(const wxString&, int)>(wxFileName::Rmdir),
+                      tempdir1, static_cast<int>(wxPATH_RMDIR_RECURSIVE) );
+
+    tn = tempnam(NULL, "wxfn2");
+    const wxString tempdir2 = wxString::From8BitData(tn);
+    free(tn);
+    CPPUNIT_ASSERT_EQUAL( 0, symlink(tempdir1, tempdir2) );
+    wxON_BLOCK_EXIT1( wxRemoveFile, tempdir2 );
+
+
+    wxFileName fn3(tempdir1, "foo");
+    wxFileName fn4(tempdir2, "foo");
+
+    // These files have different paths, hence are different.
+    CPPUNIT_ASSERT( !fn3.SameAs(fn4) );
+
+    // Create and close a file to trigger creating it.
+    wxFile(fn3.GetFullPath(), wxFile::write);
+
+    // Now that both files do exist we should be able to detect that they are
+    // actually the same file.
+    CPPUNIT_ASSERT( fn3.SameAs(fn4) );
+#endif // __UNIX__
+}