Respect wxFileName::DontFollowLink() in wxFileSystemWatcher.
Watch the link itself and not its target if DontFollowLink() had been called. Closes #14543. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72751 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -60,6 +60,10 @@ public:
|
|||||||
to this directory itself or its immediate children will generate the
|
to this directory itself or its immediate children will generate the
|
||||||
events. Use AddTree() to monitor the directory recursively.
|
events. Use AddTree() to monitor the directory recursively.
|
||||||
|
|
||||||
|
Note that on platforms that use symbolic links, you should consider the
|
||||||
|
possibility that @a path is a symlink. To watch the symlink itself and
|
||||||
|
not its target you may call wxFileName::DontFollowLink() on @a path.
|
||||||
|
|
||||||
@param path
|
@param path
|
||||||
The name of the path to watch.
|
The name of the path to watch.
|
||||||
@param events
|
@param events
|
||||||
@@ -68,29 +72,38 @@ public:
|
|||||||
virtual bool Add(const wxFileName& path, int events = wxFSW_EVENT_ALL);
|
virtual bool Add(const wxFileName& path, int events = wxFSW_EVENT_ALL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This is the same as Add(), but recursively adds every file/directory in
|
This is the same as Add(), but also recursively adds every
|
||||||
the tree rooted at @a path.
|
file/directory in the tree rooted at @a path.
|
||||||
|
|
||||||
Additionally a file mask can be specified to include only files
|
Additionally a file mask can be specified to include only files
|
||||||
matching that particular mask.
|
matching that particular mask.
|
||||||
|
|
||||||
This method is implemented efficiently under MSW but shouldn't be used
|
This method is implemented efficiently on MSW, but should be used with
|
||||||
for the directories with a lot of children (such as e.g. the root
|
care on other platforms for directories with lots of children (e.g. the
|
||||||
directory) under the other platforms as it calls Add() there for each
|
root directory) as it calls Add() for each subdirectory, potentially
|
||||||
subdirectory potentially creating a lot of watches and taking a long
|
creating a lot of watches and taking a long time to execute.
|
||||||
time to execute.
|
|
||||||
|
Note that on platforms that use symbolic links, you will probably want
|
||||||
|
to have called wxFileName::DontFollowLink on @a path. This is especially
|
||||||
|
important if the symlink targets may themselves be watched.
|
||||||
*/
|
*/
|
||||||
virtual bool AddTree(const wxFileName& path, int events = wxFSW_EVENT_ALL,
|
virtual bool AddTree(const wxFileName& path, int events = wxFSW_EVENT_ALL,
|
||||||
const wxString& filter = wxEmptyString);
|
const wxString& filter = wxEmptyString);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Removes @a path from the list of watched paths.
|
Removes @a path from the list of watched paths.
|
||||||
|
|
||||||
|
See the comment in Add() about symbolic links. @a path should treat
|
||||||
|
symbolic links in the same way as in the original Add() call.
|
||||||
*/
|
*/
|
||||||
virtual bool Remove(const wxFileName& path);
|
virtual bool Remove(const wxFileName& path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Same as Remove(), but also removes every file/directory belonging to
|
This is the same as Remove(), but also removes every file/directory
|
||||||
the tree rooted at @a path.
|
belonging to the tree rooted at @a path.
|
||||||
|
|
||||||
|
See the comment in AddTree() about symbolic links. @a path should treat
|
||||||
|
symbolic links in the same way as in the original AddTree() call.
|
||||||
*/
|
*/
|
||||||
virtual bool RemoveTree(const wxFileName& path);
|
virtual bool RemoveTree(const wxFileName& path);
|
||||||
|
|
||||||
|
@@ -46,11 +46,13 @@ private:
|
|||||||
void OnClear(wxCommandEvent& WXUNUSED(event)) { m_evtConsole->Clear(); }
|
void OnClear(wxCommandEvent& WXUNUSED(event)) { m_evtConsole->Clear(); }
|
||||||
void OnQuit(wxCommandEvent& WXUNUSED(event)) { Close(true); }
|
void OnQuit(wxCommandEvent& WXUNUSED(event)) { Close(true); }
|
||||||
void OnWatch(wxCommandEvent& event);
|
void OnWatch(wxCommandEvent& event);
|
||||||
|
void OnFollowLinks(wxCommandEvent& event);
|
||||||
void OnAbout(wxCommandEvent& event);
|
void OnAbout(wxCommandEvent& event);
|
||||||
|
|
||||||
void OnAdd(wxCommandEvent& event);
|
void OnAdd(wxCommandEvent& event);
|
||||||
void OnAddTree(wxCommandEvent& event);
|
void OnAddTree(wxCommandEvent& event);
|
||||||
void OnRemove(wxCommandEvent& event);
|
void OnRemove(wxCommandEvent& event);
|
||||||
|
void OnRemoveUpdateUI(wxUpdateUIEvent& event);
|
||||||
|
|
||||||
void OnFileSystemEvent(wxFileSystemWatcherEvent& event);
|
void OnFileSystemEvent(wxFileSystemWatcherEvent& event);
|
||||||
void LogEvent(const wxFileSystemWatcherEvent& event);
|
void LogEvent(const wxFileSystemWatcherEvent& event);
|
||||||
@@ -58,6 +60,7 @@ private:
|
|||||||
wxTextCtrl *m_evtConsole; // events console
|
wxTextCtrl *m_evtConsole; // events console
|
||||||
wxListView *m_filesList; // list of watched paths
|
wxListView *m_filesList; // list of watched paths
|
||||||
wxFileSystemWatcher* m_watcher; // file system watcher
|
wxFileSystemWatcher* m_watcher; // file system watcher
|
||||||
|
bool m_followLinks; // should symlinks be dereferenced
|
||||||
|
|
||||||
const static wxString LOG_FORMAT; // how to format events
|
const static wxString LOG_FORMAT; // how to format events
|
||||||
};
|
};
|
||||||
@@ -135,7 +138,7 @@ IMPLEMENT_APP(MyApp)
|
|||||||
// frame constructor
|
// frame constructor
|
||||||
MyFrame::MyFrame(const wxString& title)
|
MyFrame::MyFrame(const wxString& title)
|
||||||
: wxFrame(NULL, wxID_ANY, title),
|
: wxFrame(NULL, wxID_ANY, title),
|
||||||
m_watcher(NULL)
|
m_watcher(NULL), m_followLinks(false)
|
||||||
{
|
{
|
||||||
SetIcon(wxICON(sample));
|
SetIcon(wxICON(sample));
|
||||||
|
|
||||||
@@ -145,6 +148,7 @@ MyFrame::MyFrame(const wxString& title)
|
|||||||
MENU_ID_QUIT = wxID_EXIT,
|
MENU_ID_QUIT = wxID_EXIT,
|
||||||
MENU_ID_CLEAR = wxID_CLEAR,
|
MENU_ID_CLEAR = wxID_CLEAR,
|
||||||
MENU_ID_WATCH = 101,
|
MENU_ID_WATCH = 101,
|
||||||
|
MENU_ID_DEREFERENCE,
|
||||||
|
|
||||||
BTN_ID_ADD = 200,
|
BTN_ID_ADD = 200,
|
||||||
BTN_ID_ADD_TREE,
|
BTN_ID_ADD_TREE,
|
||||||
@@ -166,6 +170,18 @@ MyFrame::MyFrame(const wxString& title)
|
|||||||
// started by default, because file system watcher is started by default
|
// started by default, because file system watcher is started by default
|
||||||
it->Check(true);
|
it->Check(true);
|
||||||
|
|
||||||
|
#if defined(__UNIX__)
|
||||||
|
// Let the user decide whether to dereference symlinks. If he makes the
|
||||||
|
// wrong choice, asserts will occur if the symlink target is also watched
|
||||||
|
it = menuMon->AppendCheckItem(MENU_ID_DEREFERENCE,
|
||||||
|
"&Follow symlinks\tCtrl-F",
|
||||||
|
_("If checked, dereference symlinks")
|
||||||
|
);
|
||||||
|
it->Check(false);
|
||||||
|
Connect(MENU_ID_DEREFERENCE, wxEVT_COMMAND_MENU_SELECTED,
|
||||||
|
wxCommandEventHandler(MyFrame::OnFollowLinks));
|
||||||
|
#endif // __UNIX__
|
||||||
|
|
||||||
// the "About" item should be in the help menu
|
// the "About" item should be in the help menu
|
||||||
wxMenu *menuHelp = new wxMenu;
|
wxMenu *menuHelp = new wxMenu;
|
||||||
menuHelp->Append(wxID_ABOUT, "&About\tF1", "Show about dialog");
|
menuHelp->Append(wxID_ABOUT, "&About\tF1", "Show about dialog");
|
||||||
@@ -263,6 +279,8 @@ MyFrame::MyFrame(const wxString& title)
|
|||||||
wxCommandEventHandler(MyFrame::OnAddTree));
|
wxCommandEventHandler(MyFrame::OnAddTree));
|
||||||
Connect(BTN_ID_REMOVE, wxEVT_COMMAND_BUTTON_CLICKED,
|
Connect(BTN_ID_REMOVE, wxEVT_COMMAND_BUTTON_CLICKED,
|
||||||
wxCommandEventHandler(MyFrame::OnRemove));
|
wxCommandEventHandler(MyFrame::OnRemove));
|
||||||
|
Connect(BTN_ID_REMOVE, wxEVT_UPDATE_UI,
|
||||||
|
wxUpdateUIEventHandler(MyFrame::OnRemoveUpdateUI));
|
||||||
|
|
||||||
// and show itself (the frames, unlike simple controls, are not shown when
|
// and show itself (the frames, unlike simple controls, are not shown when
|
||||||
// created initially)
|
// created initially)
|
||||||
@@ -324,6 +342,11 @@ void MyFrame::OnWatch(wxCommandEvent& event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyFrame::OnFollowLinks(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
m_followLinks = event.IsChecked();
|
||||||
|
}
|
||||||
|
|
||||||
void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event))
|
void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
AddEntry(wxFSWPath_Dir);
|
AddEntry(wxFSWPath_Dir);
|
||||||
@@ -353,15 +376,23 @@ void MyFrame::AddEntry(wxFSWPathType type, wxString filename)
|
|||||||
|
|
||||||
wxString prefix;
|
wxString prefix;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
|
// This will tell wxFileSystemWatcher whether to dereference symlinks
|
||||||
|
wxFileName fn = wxFileName::DirName(filename);
|
||||||
|
if (!m_followLinks)
|
||||||
|
{
|
||||||
|
fn.DontFollowLink();
|
||||||
|
}
|
||||||
|
|
||||||
switch ( type )
|
switch ( type )
|
||||||
{
|
{
|
||||||
case wxFSWPath_Dir:
|
case wxFSWPath_Dir:
|
||||||
ok = m_watcher->Add(wxFileName::DirName(filename));
|
ok = m_watcher->Add(fn);
|
||||||
prefix = "Dir: ";
|
prefix = "Dir: ";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case wxFSWPath_Tree:
|
case wxFSWPath_Tree:
|
||||||
ok = m_watcher->AddTree(wxFileName::DirName(filename));
|
ok = m_watcher->AddTree(fn);
|
||||||
prefix = "Tree: ";
|
prefix = "Tree: ";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -390,15 +421,23 @@ void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event))
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
bool ret;
|
bool ret;
|
||||||
wxString path;
|
wxString path = m_filesList->GetItemText(idx).Mid(6);
|
||||||
// TODO we know it is a dir, but it doesn't have to be
|
|
||||||
if (m_filesList->GetItemText(idx).StartsWith("Dir: ", &path))
|
// This will tell wxFileSystemWatcher whether to dereference symlinks
|
||||||
|
wxFileName fn = wxFileName::DirName(path);
|
||||||
|
if (!m_followLinks)
|
||||||
{
|
{
|
||||||
ret = m_watcher->Remove(wxFileName::DirName(path));
|
fn.DontFollowLink();
|
||||||
}
|
}
|
||||||
else if (m_filesList->GetItemText(idx).StartsWith("Tree: ", &path))
|
|
||||||
|
// TODO we know it is a dir, but it doesn't have to be
|
||||||
|
if (m_filesList->GetItemText(idx).StartsWith("Dir: "))
|
||||||
{
|
{
|
||||||
ret = m_watcher->RemoveTree(wxFileName::DirName(path));
|
ret = m_watcher->Remove(fn);
|
||||||
|
}
|
||||||
|
else if (m_filesList->GetItemText(idx).StartsWith("Tree: "))
|
||||||
|
{
|
||||||
|
ret = m_watcher->RemoveTree(fn);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -415,6 +454,11 @@ void MyFrame::OnRemove(wxCommandEvent& WXUNUSED(event))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyFrame::OnRemoveUpdateUI(wxUpdateUIEvent& event)
|
||||||
|
{
|
||||||
|
event.Enable(m_filesList->GetFirstSelected() != wxNOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
void MyFrame::OnFileSystemEvent(wxFileSystemWatcherEvent& event)
|
void MyFrame::OnFileSystemEvent(wxFileSystemWatcherEvent& event)
|
||||||
{
|
{
|
||||||
// TODO remove when code is rock-solid
|
// TODO remove when code is rock-solid
|
||||||
|
@@ -198,8 +198,14 @@ bool wxFileSystemWatcherBase::AddTree(const wxFileName& path, int events,
|
|||||||
};
|
};
|
||||||
|
|
||||||
wxDir dir(path.GetFullPath());
|
wxDir dir(path.GetFullPath());
|
||||||
|
// Prevent asserts or infinite loops in trees containing symlinks
|
||||||
|
int flags = wxDIR_DEFAULT; // TODO: we ignore files, so why use wxDIR_FILES?
|
||||||
|
if ( !path.ShouldFollowLink() )
|
||||||
|
{
|
||||||
|
flags |= wxDIR_NO_FOLLOW;
|
||||||
|
}
|
||||||
AddTraverser traverser(this, events, filespec);
|
AddTraverser traverser(this, events, filespec);
|
||||||
dir.Traverse(traverser, filespec);
|
dir.Traverse(traverser, filespec, flags);
|
||||||
|
|
||||||
// Add the path itself explicitly as Traverse() doesn't return it.
|
// Add the path itself explicitly as Traverse() doesn't return it.
|
||||||
AddAny(path.GetPathWithSep(), events, wxFSWPath_Tree, filespec);
|
AddAny(path.GetPathWithSep(), events, wxFSWPath_Tree, filespec);
|
||||||
@@ -260,8 +266,17 @@ bool wxFileSystemWatcherBase::RemoveTree(const wxFileName& path)
|
|||||||
#endif // __WINDOWS__
|
#endif // __WINDOWS__
|
||||||
|
|
||||||
wxDir dir(path.GetFullPath());
|
wxDir dir(path.GetFullPath());
|
||||||
|
// AddTree() might have used the wxDIR_NO_FOLLOW to prevent asserts or
|
||||||
|
// infinite loops in trees containing symlinks. We need to do the same
|
||||||
|
// or we'll try to remove unwatched items. Let's hope the caller used
|
||||||
|
// the same ShouldFollowLink() setting as in AddTree()...
|
||||||
|
int flags = wxDIR_DEFAULT; // See the TODO in AddTree()
|
||||||
|
if ( !path.ShouldFollowLink() )
|
||||||
|
{
|
||||||
|
flags |= wxDIR_NO_FOLLOW;
|
||||||
|
}
|
||||||
RemoveTraverser traverser(this, filespec);
|
RemoveTraverser traverser(this, filespec);
|
||||||
dir.Traverse(traverser, filespec);
|
dir.Traverse(traverser, filespec, flags);
|
||||||
|
|
||||||
// As in AddTree() above, handle the path itself explicitly.
|
// As in AddTree() above, handle the path itself explicitly.
|
||||||
Remove(path);
|
Remove(path);
|
||||||
|
@@ -650,12 +650,13 @@ void FileSystemWatcherTestCase::TestTrees()
|
|||||||
public:
|
public:
|
||||||
TreeTester() : subdirs(5), files(3) {}
|
TreeTester() : subdirs(5), files(3) {}
|
||||||
|
|
||||||
void GrowTree(wxFileName dir)
|
void GrowTree(wxFileName dir, bool withSymlinks)
|
||||||
{
|
{
|
||||||
CPPUNIT_ASSERT(dir.Mkdir());
|
CPPUNIT_ASSERT(dir.Mkdir());
|
||||||
// Now add a subdir with an easy name to remember in WatchTree()
|
// Now add a subdir with an easy name to remember in WatchTree()
|
||||||
dir.AppendDir("child");
|
dir.AppendDir("child");
|
||||||
CPPUNIT_ASSERT(dir.Mkdir());
|
CPPUNIT_ASSERT(dir.Mkdir());
|
||||||
|
wxFileName child(dir); // Create a copy to which to symlink
|
||||||
|
|
||||||
// Create a branch of 5 numbered subdirs, each containing 3
|
// Create a branch of 5 numbered subdirs, each containing 3
|
||||||
// numbered files
|
// numbered files
|
||||||
@@ -672,6 +673,18 @@ void FileSystemWatcherTestCase::TestTrees()
|
|||||||
wxFile(prefix + wxString::Format("file%u", f+1) + ext[f],
|
wxFile(prefix + wxString::Format("file%u", f+1) + ext[f],
|
||||||
wxFile::write);
|
wxFile::write);
|
||||||
}
|
}
|
||||||
|
#if defined(__UNIX__)
|
||||||
|
if ( withSymlinks )
|
||||||
|
{
|
||||||
|
// Create a symlink to a files, and another to 'child'
|
||||||
|
CPPUNIT_ASSERT_EQUAL(0,
|
||||||
|
symlink(wxString(prefix + "file1").c_str(),
|
||||||
|
wxString(prefix + "file.lnk").c_str()));
|
||||||
|
CPPUNIT_ASSERT_EQUAL(0,
|
||||||
|
symlink(child.GetFullPath().c_str(),
|
||||||
|
wxString(prefix + "dir.lnk").c_str()));
|
||||||
|
}
|
||||||
|
#endif // __UNIX__
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,7 +733,7 @@ void FileSystemWatcherTestCase::TestTrees()
|
|||||||
// Store the initial count; there may already be some watches
|
// Store the initial count; there may already be some watches
|
||||||
const int initial = m_watcher->GetWatchedPathsCount();
|
const int initial = m_watcher->GetWatchedPathsCount();
|
||||||
|
|
||||||
GrowTree(dir);
|
GrowTree(dir, false /* no symlinks */);
|
||||||
|
|
||||||
m_watcher->AddTree(dir);
|
m_watcher->AddTree(dir);
|
||||||
const int plustree = m_watcher->GetWatchedPathsCount();
|
const int plustree = m_watcher->GetWatchedPathsCount();
|
||||||
@@ -750,6 +763,22 @@ void FileSystemWatcherTestCase::TestTrees()
|
|||||||
CPPUNIT_ASSERT(initial < m_watcher->GetWatchedPathsCount());
|
CPPUNIT_ASSERT(initial < m_watcher->GetWatchedPathsCount());
|
||||||
m_watcher->RemoveTree(dir);
|
m_watcher->RemoveTree(dir);
|
||||||
CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount());
|
CPPUNIT_ASSERT_EQUAL(initial, m_watcher->GetWatchedPathsCount());
|
||||||
|
#if defined(__UNIX__)
|
||||||
|
// Finally, test a tree containing internal symlinks
|
||||||
|
RmDir(dir);
|
||||||
|
GrowTree(dir, true /* test symlinks */);
|
||||||
|
|
||||||
|
// Without the DontFollowLink() call AddTree() would now assert
|
||||||
|
// (and without the assert, it would infinitely loop)
|
||||||
|
wxFileName fn = dir;
|
||||||
|
fn.DontFollowLink();
|
||||||
|
CPPUNIT_ASSERT(m_watcher->AddTree(fn));
|
||||||
|
CPPUNIT_ASSERT(m_watcher->RemoveTree(fn));
|
||||||
|
|
||||||
|
// Regrow the tree without symlinks, ready for the next test
|
||||||
|
RmDir(dir);
|
||||||
|
GrowTree(dir, false);
|
||||||
|
#endif // __UNIX__
|
||||||
}
|
}
|
||||||
|
|
||||||
void WatchTreeWithFilespec(const wxFileName& dir)
|
void WatchTreeWithFilespec(const wxFileName& dir)
|
||||||
|
Reference in New Issue
Block a user