1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
// **************************************************************************
//
#include "watcher.h"
#include "../shared/file_handling.h"
#include "../shared/i18n.h"
#include <set>
#include <wx/timer.h>
#include "../shared/resolve_path.h"
#include "../shared/dir_watcher.h"
#include "../shared/string_conv.h"
//#include "../library/db_file.h" //SYNC_DB_FILE_ENDING -> complete file too much of a dependency; file ending too little to decouple into single header
//#include "../library/lock_holder.h" //LOCK_FILE_ENDING
#include <wx/msgdlg.h>
using namespace zen;
bool rts::updateUiIsAllowed()
{
static wxLongLong lastExec;
const wxLongLong newExec = wxGetLocalTimeMillis();
if (newExec - lastExec >= rts::UI_UPDATE_INTERVAL) //perform ui updates not more often than necessary
{
lastExec = newExec;
return true;
}
return false;
}
class MonitorExistence //detect changes to directory availability
{
public:
MonitorExistence() : allExisting_(true) {}
//initialization
void addForMonitoring(const Zstring& dirName)
{
dirList.insert(dirName);
}
bool allExisting() const //polling explicitly allowed!
{
const int UPDATE_INTERVAL = 1000; //1 second interval
const wxLongLong current = wxGetLocalTimeMillis();
if (current - lastCheck >= UPDATE_INTERVAL)
{
lastCheck = current;
allExisting_ = std::find_if(dirList.begin(), dirList.end(), std::not1(std::ptr_fun(&zen::dirExists))) == dirList.end();
}
return allExisting_;
}
private:
mutable wxLongLong lastCheck;
mutable bool allExisting_;
std::set<Zstring, LessFilename> dirList; //save avail. directories, avoid double-entries
};
rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNames, WaitCallback* statusHandler) //throw(FileError)
{
if (dirNames.empty()) //pathological case, but check is needed nevertheless
throw zen::FileError(_("A directory input field is empty."));
//detect when volumes are removed/are not available anymore
MonitorExistence checkExist;
std::vector<std::shared_ptr<DirWatcher>> watches;
for (std::vector<Zstring>::const_iterator i = dirNames.begin(); i != dirNames.end(); ++i)
{
const Zstring formattedDir = zen::getFormattedDirectoryName(*i);
if (formattedDir.empty())
throw zen::FileError(_("A directory input field is empty."));
checkExist.addForMonitoring(formattedDir);
try
{
watches.push_back(std::make_shared<DirWatcher>(formattedDir)); //throw FileError
}
catch (zen::FileError&)
{
if (!zen::dirExists(formattedDir)) //that's no good locking behavior, but better than nothing
return CHANGE_DIR_MISSING;
throw;
}
}
while (true)
{
//IMPORTANT CHECK: dirwatcher has problems detecting removal of top watched directories!
if (!checkExist.allExisting()) //check for removed devices:
return CHANGE_DIR_MISSING;
try
{
for (auto iter = watches.begin(); iter != watches.end(); ++iter)
{
std::vector<Zstring> changedFiles = (*iter)->getChanges(); //throw FileError
//remove to be ignored changes
changedFiles.erase(std::remove_if(changedFiles.begin(), changedFiles.end(),
[](const Zstring& name)
{
return endsWith(name, Zstr(".ffs_lock")) || //sync.ffs_lock, sync.Del.ffs_lock
endsWith(name, Zstr(".ffs_db")); //sync.ffs_db, .sync.tmp.ffs_db
//no need to ignore temporal recycle bin directory: this must be caused by a file deletion anyway
}), changedFiles.end());
if (!changedFiles.empty())
{
/*
std::for_each(changedFiles.begin(), changedFiles.end(),
[](const Zstring& fn) { wxMessageBox(toWx(fn));});
const wxString filename = toWx(changedFiles[0]);
::wxSetEnv(wxT("RTS_CHANGE"), filename);
*/
return CHANGE_DETECTED; //directory change detected
}
}
}
catch (FileError&)
{
//maybe some error is caused due to some unexpected removal/unavailability of a watched directory? If so we can remedy this error:
if (!checkExist.allExisting())
return CHANGE_DIR_MISSING;
throw;
}
wxMilliSleep(rts::UI_UPDATE_INTERVAL);
statusHandler->requestUiRefresh();
}
}
//support for monitoring newly connected directories volumes (e.g.: USB-sticks)
void rts::waitForMissingDirs(const std::vector<Zstring>& dirNames, WaitCallback* statusHandler) //throw(FileError)
{
wxLongLong lastCheck;
while (true)
{
const int UPDATE_INTERVAL = 1000; //1 second interval
const wxLongLong current = wxGetLocalTimeMillis();
if (current - lastCheck >= UPDATE_INTERVAL)
{
lastCheck = current;
bool allExisting = true;
for (std::vector<Zstring>::const_iterator i = dirNames.begin(); i != dirNames.end(); ++i)
{
//support specifying volume by name => call getFormattedDirectoryName() repeatedly
const Zstring formattedDir = zen::getFormattedDirectoryName(*i);
if (formattedDir.empty())
throw zen::FileError(_("A directory input field is empty."));
if (!zen::dirExists(formattedDir))
{
allExisting = false;
break;
}
}
if (allExisting) //check for newly arrived devices:
return;
}
wxMilliSleep(rts::UI_UPDATE_INTERVAL);
statusHandler->requestUiRefresh();
}
}
|