5 public class FileChange
8 bool created:1; // param: new file name
9 bool renamed:1; // param: new file name
12 bool attribs:1; // param: new attributes
15 public define AnyFileChange = FileChange { true, true, true, true, true };
17 // *** File Monitoring ***
19 static class FileEntry : struct
33 public class FileMonitor
36 property void * userData { set { data = value; } };
37 property FileChange fileChange { set { watchFor = value; } };
38 property char * fileName
45 if(value) MonitorFile(value);
48 get { return fileName; }
50 property char * directoryName
57 if(value) MonitorDirectory(value);
60 get { return fileName; }
67 globalSystem.fileMonitorMutex.Wait();
72 fileNotifies.Free(FileNotify::Free);
78 globalSystem.fileMonitorMutex.Release();
82 void StartMonitoring()
84 if(fileName && !active)
86 globalSystem.fileMonitorMutex.Wait();
95 MonitorDirectory(fileName);
97 MonitorFile(fileName);
99 globalSystem.fileMonitorMutex.Release();
104 FileMonitor prev, next;
108 bool reentrant, toBeFreed;
109 OldList fileNotifies;
117 public virtual bool any_object::OnDirNotify(FileChange action, char * fileName, char * param);
119 // For directory monitors
122 public virtual bool any_object::OnFileNotify(FileChange action, char * param);
125 #if defined(__WIN32__)
127 #elif defined(__linux__)
134 globalSystem.fileMonitorMutex.Wait();
139 globalSystem.fileMonitorMutex.Release();
142 void MonitorFile(char * filePath)
144 FileAttribs exists = FileExists(filePath);
146 String oldFileName = fileName;
149 globalSystem.fileMonitorMutex.Wait();
151 files.Free(FileEntry::Free);
152 fileNotifies.Free(FileNotify::Free);
154 if(!active && !toBeFreed)
155 globalSystem.fileMonitors.Add(this);
156 this.exists = exists && !exists.isDirectory;
157 fileName = CopyString(filePath);
159 this.watchFor = watchFor;
161 if(FileGetStats(filePath, stats))
163 attribs = stats.attribs;
165 modified = stats.modified;
167 /*if(strstr(filePath, "Debugger"))
168 printf("modified is %x", modified);*/
175 globalSystem.fileMonitorMutex.Release();
177 if(!globalSystem.fileMonitorThread)
179 globalSystem.fileMonitorThread = MonitorThread { };
180 //incref globalSystem.fileMonitorThread;
181 globalSystem.fileMonitorThread.Create();
185 void MonitorDirectory(char * filePath)
188 String oldFileName = fileName;
191 globalSystem.fileMonitorMutex.Wait();
193 files.Free(FileEntry::Free);
194 fileNotifies.Free(FileNotify::Free);
196 if(!active && !toBeFreed)
197 globalSystem.fileMonitors.Add(this);
199 exists = FileExists(filePath).isDirectory;
201 this.watchFor = watchFor;
202 fileName = CopyString(filePath);
205 if(FileGetStats(fileName, stats))
207 attribs = stats.attribs;
209 modified = stats.modified;
212 // Initialize files in directory
213 GetFileEntries(&files, fileName);
218 globalSystem.fileMonitorMutex.Release();
220 if(!globalSystem.fileMonitorThread)
222 globalSystem.fileMonitorThread = MonitorThread { };
223 globalSystem.fileMonitorThread.Create();
229 files.Free(FileEntry::Free);
230 fileNotifies.Free(FileNotify::Free);
232 globalSystem.fileMonitors.Remove(this);
236 bool AddFileNotify(FileChange action, char * fileName, char * param)
238 if(watchFor & action)
240 fileNotifies.Add(FileNotify {
241 monitor = this, action = action, fileName = CopyString(fileName), param = CopyString(param)
249 private class FileNotify : struct
251 FileNotify prev, next;
264 static define FILE_MONITOR_RESOLUTION = 1.0; // Once every second
266 static int CompareFiles(FileEntry e1, FileEntry e2, void * data)
268 if(!e1.attribs.isDirectory && e2.attribs.isDirectory)
270 else if(e1.attribs.isDirectory && !e2.attribs.isDirectory)
273 return strcmp(e1.name, e2.name);
276 static void GetFileEntries(OldList list, char * path)
278 FileListing listing { path, null };
279 while(listing.Find())
282 name = CopyString(listing.name),
283 attribs = listing.stats.attribs,
284 modified = listing.stats.modified,
285 size = listing.stats.size
288 list.Sort(CompareFiles, null);
291 static class MonitorThread : Thread
297 while(!globalSystem.systemTerminate)
299 Time currentTime = GetTime();
300 if(currentTime - lastTime > FILE_MONITOR_RESOLUTION)
303 bool fileActivity = false;
304 lastTime = currentTime;
306 // printf("[%d] Waiting in MonitorThread for fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
307 globalSystem.fileMonitorMutex.Wait();
309 for(monitor = globalSystem.fileMonitors.first; monitor; monitor = monitor.next)
311 if(monitor.directory)
313 bool exists = FileExists(monitor.fileName).isDirectory;
314 if(exists && !monitor.exists)
316 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, monitor.fileName, null);
317 monitor.exists = exists;
319 else if(!exists && monitor.exists)
321 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, monitor.fileName, null);
322 monitor.exists = exists;
324 else if(exists && monitor.watchFor & FileChange { created = true, deleted = true, modified = true, attribs = true})
328 OldList newEntries = { 0 };
329 GetFileEntries(&newEntries, monitor.fileName);
331 // Directory comparison
332 entry = monitor.files.first;
333 newEntry = newEntries.first;
335 while(entry || newEntry)
338 if(!entry) comparison = 1;
339 else if(!newEntry) comparison = -1;
340 else comparison = strcmp(entry.name,newEntry.name);
344 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, newEntry.name, null);
345 newEntry = newEntry.next;
347 else if(comparison < 0)
349 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, entry.name, null);
354 if(newEntry.modified != entry.modified || newEntry.size != entry.size)
356 fileActivity |= monitor.AddFileNotify(FileChange { modified = true }, entry.name, null);
358 else if(newEntry.attribs != entry.attribs)
360 fileActivity |= monitor.AddFileNotify(FileChange { attribs = true }, entry.name, null);
363 newEntry = newEntry.next;
366 monitor.files.Free(FileEntry::Free);
367 monitor.files = newEntries;
370 else if(monitor.fileName)
372 FileAttribs exists = FileExists(monitor.fileName);
373 // printf("Iterating on Monitor for %s...\n", monitor.fileName);
375 if((exists && !exists.isDirectory) && !monitor.exists)
377 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, monitor.fileName, null);
378 monitor.exists = true;
380 else if((!exists || exists.isDirectory) && monitor.exists)
382 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, monitor.fileName, null);
383 monitor.exists = false;
385 else if(monitor.exists)
388 if(FileGetStats(monitor.fileName, stats))
391 /*if(strstr(monitor.fileName, "Debugger"))
392 printf("Now modified is %x\n", stats.modified);*/
395 if(stats.modified != monitor.modified || stats.size != monitor.size)
397 // printf("Modified/size changed, adding file notify...\n");
398 if(stats.modified > monitor.modified || monitor.modified - (TimeStamp)stats.modified > 2)
399 fileActivity |= monitor.AddFileNotify(FileChange { modified = true }, monitor.fileName, null);
400 monitor.size = stats.size;
401 monitor.modified = stats.modified;
403 /*if(strstr(monitor.fileName, "Debugger"))
404 printf("Setting modified to %x\n", monitor.modified);*/
407 else if(stats.attribs != monitor.attribs)
409 // printf("Attribs changed, adding file notify...\n");
411 fileActivity |= monitor.AddFileNotify(FileChange { attribs = true }, monitor.fileName, null);
412 monitor.attribs = stats.attribs;
420 // printf("[%d] Signaling Event...\n", (int)GetCurrentThreadID());
421 guiApp.SignalEvent();
424 // printf("[%d] Releasing Mutex...\n", (int)GetCurrentThreadID());
425 globalSystem.fileMonitorMutex.Release();
428 // printf("[%d] Sleeping...\n", (int)GetCurrentThreadID());