Refactor wxFileName existence checking code.

Merge FileExists() and DirExists() together in a single
wxFileSystemObjectExists() helper in preparation for adding a function
checking for the existence of any kind of file system object.

There should be no changes for MSW/Unix but OS/2 file detection was changed
slightly to always use DosQueryPathInfo() instead of using it only for the
directories and stat() for files. Not sure if this is the right thing to do
there but it seems like the code should work.

Also add a simple unit test for these functions.

See #953.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@70599 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2012-02-15 15:39:41 +00:00
parent 06bfd04527
commit 901504c383
2 changed files with 139 additions and 70 deletions

View File

@ -148,6 +148,12 @@
#define MAX_PATH _MAX_PATH
#endif
#ifndef S_ISREG
#define S_ISREG(mode) ((mode) & S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(mode) ((mode) & S_IFDIR)
#endif
#if wxUSE_LONGLONG
extern const wxULongLong wxInvalidSize = (unsigned)-1;
@ -593,6 +599,117 @@ wxFileName wxFileName::DirName(const wxString& dir, wxPathFormat format)
// existence tests
// ----------------------------------------------------------------------------
namespace
{
// Flags for wxFileSystemObjectExists() asking it to check for:
enum
{
wxFileSystemObject_File = 1, // file existence
wxFileSystemObject_Dir = 2, // directory existence
wxFileSystemObject_Other = 4, // existence of something else, e.g.
// device, socket, FIFO under Unix
wxFileSystemObject_Any = 7 // existence of anything at all
};
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
void RemoveTrailingSeparatorsFromPath(wxString& strPath)
{
// Windows fails to find directory named "c:\dir\" even if "c:\dir" exists,
// so remove all trailing backslashes from the path - but don't do this for
// the paths "d:\" (which are different from "d:"), for just "\" or for
// windows unique volume names ("\\?\Volume{GUID}\")
while ( wxEndsWithPathSeparator( strPath ) )
{
size_t len = strPath.length();
if ( len == 1 || (len == 3 && strPath[len - 2] == wxT(':')) ||
(len == wxMSWUniqueVolumePrefixLength &&
wxFileName::IsMSWUniqueVolumeNamePath(strPath)))
{
break;
}
strPath.Truncate(len - 1);
}
}
#endif // __WINDOWS__ || __OS2__
bool wxFileSystemObjectExists(const wxString& path, int flags)
{
// Should the existence of file/directory with this name be accepted, i.e.
// result in the true return value from this function?
const bool acceptFile = flags & wxFileSystemObject_File;
const bool acceptDir = flags & wxFileSystemObject_Dir;
wxString strPath(path);
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
if ( acceptDir )
{
// Ensure that the path doesn't have any trailing separators when
// checking for directories.
RemoveTrailingSeparatorsFromPath(strPath);
}
// we must use GetFileAttributes() instead of the ANSI C functions because
// it can cope with network (UNC) paths unlike them
DWORD ret = ::GetFileAttributes(path.t_str());
if ( ret == INVALID_FILE_ATTRIBUTES )
return false;
if ( ret & FILE_ATTRIBUTE_DIRECTORY )
return acceptDir;
// Anything else must be a file (perhaps we should check for
// FILE_ATTRIBUTE_REPARSE_POINT?)
return acceptFile;
#elif defined(__OS2__)
if ( acceptDir )
{
// OS/2 can't handle "d:", it wants either "d:\" or "d:."
if (strPath.length() == 2 && strPath[1u] == wxT(':'))
strPath << wxT('.');
}
FILESTATUS3 Info = {{0}};
APIRET rc = ::DosQueryPathInfo((PSZ)(WXSTRINGCAST strPath), FIL_STANDARD,
(void*) &Info, sizeof(FILESTATUS3));
if ( rc == NO_ERROR )
{
if ( Info.attrFile & FILE_DIRECTORY )
return acceptDir;
else
return acceptFile;
}
// We consider that the path must exist if we get a sharing violation for
// it but we don't know what is it in this case.
if ( rc == ERROR_SHARING_VIOLATION )
return flags & wxFileSystemObject_Other;
// Any other error (usually ERROR_PATH_NOT_FOUND), means there is nothing
// there.
return false;
#else // Non-MSW, non-OS/2
wxStructStat st;
if ( wxStat(strPath, &st) != 0 )
return false;
if ( S_ISREG(st.st_mode) )
return acceptFile;
if ( S_ISDIR(st.st_mode) )
return acceptDir;
return flags & wxFileSystemObject_Other;
#endif // Platforms
}
} // anonymous namespace
bool wxFileName::FileExists() const
{
return wxFileName::FileExists( GetFullPath() );
@ -601,25 +718,7 @@ bool wxFileName::FileExists() const
/* static */
bool wxFileName::FileExists( const wxString &filePath )
{
#if defined(__WIN32__) && !defined(__WXMICROWIN__)
// we must use GetFileAttributes() instead of the ANSI C functions because
// it can cope with network (UNC) paths unlike them
DWORD ret = ::GetFileAttributes(filePath.t_str());
return (ret != INVALID_FILE_ATTRIBUTES) && !(ret & FILE_ATTRIBUTE_DIRECTORY);
#else // !__WIN32__
#ifndef S_ISREG
#define S_ISREG(mode) ((mode) & S_IFREG)
#endif
wxStructStat st;
return (wxStat( filePath, &st) == 0 && S_ISREG(st.st_mode))
#ifdef __OS2__
|| (errno == EACCES) // if access is denied something with that name
// exists and is opened in exclusive mode.
#endif
;
#endif // __WIN32__/!__WIN32__
return wxFileSystemObjectExists(filePath, wxFileSystemObject_File);
}
bool wxFileName::DirExists() const
@ -630,57 +729,7 @@ bool wxFileName::DirExists() const
/* static */
bool wxFileName::DirExists( const wxString &dirPath )
{
wxString strPath(dirPath);
#if defined(__WINDOWS__) || defined(__OS2__)
// Windows fails to find directory named "c:\dir\" even if "c:\dir" exists,
// so remove all trailing backslashes from the path - but don't do this for
// the paths "d:\" (which are different from "d:"), for just "\" or for
// windows unique volume names ("\\?\Volume{GUID}\")
while ( wxEndsWithPathSeparator(strPath) )
{
size_t len = strPath.length();
if ( len == 1 || (len == 3 && strPath[len - 2] == wxT(':')) ||
(len == wxMSWUniqueVolumePrefixLength &&
wxFileName::IsMSWUniqueVolumeNamePath(strPath)))
{
break;
}
strPath.Truncate(len - 1);
}
#endif // __WINDOWS__
#ifdef __OS2__
// OS/2 can't handle "d:", it wants either "d:\" or "d:."
if (strPath.length() == 2 && strPath[1u] == wxT(':'))
strPath << wxT('.');
#endif
#if defined(__WIN32__) && !defined(__WXMICROWIN__)
// stat() can't cope with network paths
DWORD ret = ::GetFileAttributes(strPath.t_str());
return (ret != INVALID_FILE_ATTRIBUTES) && (ret & FILE_ATTRIBUTE_DIRECTORY);
#elif defined(__OS2__)
FILESTATUS3 Info = {{0}};
APIRET rc = ::DosQueryPathInfo((PSZ)(WXSTRINGCAST strPath), FIL_STANDARD,
(void*) &Info, sizeof(FILESTATUS3));
return ((rc == NO_ERROR) && (Info.attrFile & FILE_DIRECTORY)) ||
(rc == ERROR_SHARING_VIOLATION);
// If we got a sharing violation, there must be something with this name.
#else // !__WIN32__
wxStructStat st;
#ifndef __VISAGECPP__
return wxStat(strPath, &st) == 0 && ((st.st_mode & S_IFMT) == S_IFDIR);
#else
// S_IFMT not supported in VA compilers.. st_mode is a 2byte value only
return wxStat(strPath, &st) == 0 && (st.st_mode == S_IFDIR);
#endif
#endif // __WIN32__/!__WIN32__
return wxFileSystemObjectExists(dirPath, wxFileSystemObject_Dir);
}
// ----------------------------------------------------------------------------

View File

@ -134,6 +134,7 @@ private:
CPPUNIT_TEST( TestVolumeUniqueName );
CPPUNIT_TEST( TestCreateTempFileName );
CPPUNIT_TEST( TestGetTimes );
CPPUNIT_TEST( TestExists );
CPPUNIT_TEST_SUITE_END();
void TestConstruction();
@ -151,6 +152,7 @@ private:
void TestVolumeUniqueName();
void TestCreateTempFileName();
void TestGetTimes();
void TestExists();
DECLARE_NO_COPY_CLASS(FileNameTestCase)
};
@ -647,3 +649,21 @@ void FileNameTestCase::TestGetTimes()
CPPUNIT_ASSERT(dtMod.IsEqualUpTo(wxDateTime::Now(), wxTimeSpan(0,1)));
CPPUNIT_ASSERT(dtAccess.IsEqualUpTo(wxDateTime::Now(), wxTimeSpan(0,1)));
}
void FileNameTestCase::TestExists()
{
wxFileName fn(wxFileName::CreateTempFileName("filenametest"));
CPPUNIT_ASSERT( fn.IsOk() );
CPPUNIT_ASSERT( fn.FileExists() );
CPPUNIT_ASSERT( !wxFileName::DirExists(fn.GetFullPath()) );
wxFileName dirTemp(wxFileName::DirName(wxFileName::GetTempDir()));
CPPUNIT_ASSERT( !dirTemp.FileExists() );
CPPUNIT_ASSERT( dirTemp.DirExists() );
#ifdef __UNIX__
CPPUNIT_ASSERT( !wxFileName::FileExists("/dev/null") );
CPPUNIT_ASSERT( !wxFileName::DirExists("/dev/null") );
#endif // __UNIX__
}