Hello,
Searching the reason why of an issue in my app's code, I find an inconsistency between too wxWidgets functions: wxFileExists() and wxDir::GetAllFiles().
Say I have the file "Test.txt" on disk. If I check for its existence passing "test.txt" to my two functions, wxFileExists says YES, it exists, while GetAllFiles doesn't found it (of course, this latter function is more dedicated to files spec, but this one can target a single named file too).
I observed this from my dev station in OS X 10.9 Mavericks with files on an Apple HFS+ partition. From what I know, HFS+ is case-insensitive (but case-preserving when we're not talking about comparison) by default, but can be formated as case-sensitive. Mine is case-insensitive (seen using "diskutil info /").
So, if the filesystem's sensitivity has to be considered, in my current specific context, wxFileExists is OK, while wxDir::GetAllFiles() is not! I have no time right now to do some tests on different OSs and filesystems...
What's the behavior of these two functions in your own contexts?
Do you think it's a bug or a feature?
--
EDIT: In fact, in my mind, it would be better to be filesystem-independent in a cross-platform spirit... So, wxFileExists() shouldn't consider that "test.txt" matches "Test.txt"; and should always return false whatever the underlying context.
wxFileExists/wxDir::GetAllFiles difference; does this inconsistency is filesystem relative or not?
- eranon
- Can't get richer than this
- Posts: 867
- Joined: Sun May 13, 2012 11:42 pm
- Location: France
- Contact:
wxFileExists/wxDir::GetAllFiles difference; does this inconsistency is filesystem relative or not?
[Ind. dev. - wxWidgets 3.0/3.1 under "Win 7 64-bit, TDM64-GCC" + "OS X 10.9, LLVM Clang"]
Re: wxFileExists/wxDir::GetAllFiles difference; does this inconsistency is filesystem relative or not?
I tested under Windows and the two functions return a consistent result - as they should.
As both functions use the underlying OS functions, i suspect OSX to be at fault here. Can you trace through both functions and check where exactly the problem lies?
As a quick workaround, you could write your own replacement for wxFileExists using wxDir::GetFirst() (which GetAllFiles() uses internally). Then you should get a consistent result at least.
As both functions use the underlying OS functions, i suspect OSX to be at fault here. Can you trace through both functions and check where exactly the problem lies?
As a quick workaround, you could write your own replacement for wxFileExists using wxDir::GetFirst() (which GetAllFiles() uses internally). Then you should get a consistent result at least.
Use the source, Luke!
- eranon
- Can't get richer than this
- Posts: 867
- Joined: Sun May 13, 2012 11:42 pm
- Location: France
- Contact:
Re: wxFileExists/wxDir::GetAllFiles difference; does this inconsistency is filesystem relative or not?
Hm, OK, so it shouldn't found it... Sounds effectivelly more consistent to me too!
Well, so I only traced the faulty wxFileExists (since wxDir::GetAllFiles is right).
Searching for "test.txt" while there's only "Test.txt" on disk.
The S_ISREG macro being defined like this:
And OK for the workaround; thanks, doublemax.
--
EDIT: If it matter, the concerned app is built with LLVM Clang in OS X 10.9 against wxWidgets 3.1.0 and SDK 10.9 (as minimum supported OS X).
Well, so I only traced the faulty wxFileExists (since wxDir::GetAllFiles is right).
Searching for "test.txt" while there's only "Test.txt" on disk.
Code: Select all
wxFileExists("test.txt"):
=> bool wxFileExists (const wxString& filename){return wxFileName::FileExists(filename);}
=> bool wxFileName::FileExists(const wxString &filePath){return wxFileSystemObjectExists(filePath, wxFILE_EXISTS_REGULAR);}
=>
wxFileSystemObjectExists(const wxString& path, int flags)
{ // Reduced to useful code (removed all non-Mac parts and the ones about dirs, symlinks, etc)
const bool acceptFile = (flags & wxFILE_EXISTS_REGULAR) != 0;
const bool acceptDir = (flags & wxFILE_EXISTS_DIR) != 0;
wxString strPath(path);
wxStructStat st;
if ( !StatAny(st, strPath, flags) )
return false;
if ( S_ISREG(st.st_mode) )
return acceptFile; // It reaches this line!
}
Code: Select all
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /* regular file */
--
EDIT: If it matter, the concerned app is built with LLVM Clang in OS X 10.9 against wxWidgets 3.1.0 and SDK 10.9 (as minimum supported OS X).
[Ind. dev. - wxWidgets 3.0/3.1 under "Win 7 64-bit, TDM64-GCC" + "OS X 10.9, LLVM Clang"]
Re: wxFileExists/wxDir::GetAllFiles difference; does this inconsistency is filesystem relative or not?
No. Both functions find the file under Windows.OK, so it shouldn't found it... Sounds effectivelly more consistent to me too!
Use the source, Luke!
- eranon
- Can't get richer than this
- Posts: 867
- Joined: Sun May 13, 2012 11:42 pm
- Location: France
- Contact:
Re: wxFileExists/wxDir::GetAllFiles difference; does this inconsistency is filesystem relative or not?
Oh! so, wxFileExists gives the same result as your Windows one on my Mac platform. Then, if it's the right behavior, I have no reason to write my own wxFileExists replacement (unless I want a ::CaseSensitiveFileExists()).
About wxDir::GetAllFiles, from what I see, it goes through wxDir::Traverse -> wxDir::GetFirst/GetNext -> wxDirData::Read... More difficult to quickly trace (especially on Mac where debugging is quite special because of lack of support around; CodeLite requires OS X 10.10+, CodeBlocks doesn't know lldb and Clang binaries are not gdb compliants, so I use an alternate front-end which sometime succeeds and sometime freezes and hangs in the middle) and locate where if fails when it encounters "Test.txt" while searching for "test.txt".
Well, I'm wondering if the files related functions are all case-insensitive... Using the same example I taken, what will happen if I want to delete "Test.txt" while there's "test.txt" on disk? Does the deletion functions will succeed?
Something like (not tested, I'm writing it on fly):
And, in this case (sorry for eventual typo, not tested again):
About wxDir::GetAllFiles, from what I see, it goes through wxDir::Traverse -> wxDir::GetFirst/GetNext -> wxDirData::Read... More difficult to quickly trace (especially on Mac where debugging is quite special because of lack of support around; CodeLite requires OS X 10.10+, CodeBlocks doesn't know lldb and Clang binaries are not gdb compliants, so I use an alternate front-end which sometime succeeds and sometime freezes and hangs in the middle) and locate where if fails when it encounters "Test.txt" while searching for "test.txt".
Well, I'm wondering if the files related functions are all case-insensitive... Using the same example I taken, what will happen if I want to delete "Test.txt" while there's "test.txt" on disk? Does the deletion functions will succeed?
Something like (not tested, I'm writing it on fly):
Code: Select all
#ifdef __WXMSW__
wxString strFile("c:/Test.txt");
#endif
#ifdef __WXOSX__
wxString strFile("/Test.txt");
#endif
if (wxFileExists(strFile)
wxRemoveFile(strFile); // Does wxRemoveFile will succeed to delete "test.txt"?
Code: Select all
wxFileName fn(strFile);
wxString strPath = fn.GetPath();
wxString strSpec = fn.GetFullName();
wxArrayString arFiles;
int nFiles = wxDir::GetAllFiles(strPath, &arFiles, strSpec, wxDIR_FILES); // From what I saw, it doesn't see "test.txt" on disk,
for (int idx = 0 ; idx < nFiles ; idx++){wxRemoveFile(arFiles.Item(idx));} // then "test.txt" will never be deleted.
[Ind. dev. - wxWidgets 3.0/3.1 under "Win 7 64-bit, TDM64-GCC" + "OS X 10.9, LLVM Clang"]
Re: wxFileExists/wxDir::GetAllFiles difference; does this inconsistency is filesystem relative or not?
That should depend only on whether the underlying file system is case sensitive or not. Under Windows/NTFS attempting to delete "Test.txt" should also delete "test.txt". AFAIK all file systems under Linux are case sensitive, so the file should not get deleted there.Well, I'm wondering if the files related functions are all case-insensitive... Using the same example I taken, what will happen if I want to delete "Test.txt" while there's "test.txt" on disk? Does the deletion functions will succeed?
OSX/HFS should behave the same as under Windows.
I think you should write a small test program to confirm the wrong behavior under OSX and open a bug report.
Use the source, Luke!
- eranon
- Can't get richer than this
- Posts: 867
- Joined: Sun May 13, 2012 11:42 pm
- Location: France
- Contact:
Re: wxFileExists/wxDir::GetAllFiles difference; does this inconsistency is filesystem relative or not?
Well, wxRemoveFile is well case-insensitive too. So, the issue is only with wxDir::GetAllFiles() and underlying functions which behaves case-sensitivelly in OS X 10.8 and 10.9 (I'll check with 10.10 and 10.12 too). This both with wxWidgets 3.0 and 3.1.
Test code, replacing the About-box code in minimal sample coming with wxWidgets:
Test code, replacing the About-box code in minimal sample coming with wxWidgets:
Code: Select all
#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <wx/dir.h>
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxFileName fnExe(wxStandardPaths::Get().GetExecutablePath());
wxString strExePath(fnExe.GetPath());
wxString strFile(strExePath.Append("/../../../Test.txt"));
// Way #1: both functions are case-insensitive => it succeeds to delete "test.txt" even when searching for "Test.txt"
// if (wxFileExists(strFile))
// wxRemoveFile(strFile); // Does wxRemoveFile will succeed to delete "test.txt"?
// Way #2: since wxDir::GetAllFiles shows a case-sensitive behavior, "test.txt" is not found (then not deleted)
wxFileName fn(strFile);
wxString strPath = fn.GetPath();
wxString strSpec = fn.GetFullName();
wxArrayString arFiles;
int nFiles = wxDir::GetAllFiles(strPath, &arFiles, strSpec, wxDIR_FILES);
wxMessageBox(wxString::Format("Searching for: %s\n\nNumber of files found: %i", strFile, nFiles));
for (int idx = 0 ; idx < nFiles ; idx++){
wxString strCurr = arFiles.Item(idx);
wxMessageBox(wxString::Format("Current file to delete (%i/%i): %s", idx+1, nFiles, strCurr));
wxRemoveFile(strCurr);}
}
[Ind. dev. - wxWidgets 3.0/3.1 under "Win 7 64-bit, TDM64-GCC" + "OS X 10.9, LLVM Clang"]