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);
148 globalSystem.fileMonitorMutex.Wait();
150 files.Free(FileEntry::Free);
151 fileNotifies.Free(FileNotify::Free);
153 if(!active && !toBeFreed)
154 globalSystem.fileMonitors.Add(this);
155 this.exists = exists && !exists.isDirectory;
157 fileName = CopyString(filePath);
158 this.watchFor = watchFor;
160 if(FileGetStats(filePath, stats))
162 attribs = stats.attribs;
164 modified = stats.modified;
166 /*if(strstr(filePath, "Debugger"))
167 printf("modified is %x", modified);*/
174 globalSystem.fileMonitorMutex.Release();
176 if(!globalSystem.fileMonitorThread)
178 globalSystem.fileMonitorThread = MonitorThread { };
179 //incref globalSystem.fileMonitorThread;
180 globalSystem.fileMonitorThread.Create();
184 void MonitorDirectory(char * filePath)
189 globalSystem.fileMonitorMutex.Wait();
191 files.Free(FileEntry::Free);
192 fileNotifies.Free(FileNotify::Free);
194 if(!active && !toBeFreed)
195 globalSystem.fileMonitors.Add(this);
197 exists = FileExists(filePath).isDirectory;
199 this.watchFor = watchFor;
201 fileName = CopyString(filePath);
203 if(FileGetStats(fileName, stats))
205 attribs = stats.attribs;
207 modified = stats.modified;
210 // Initialize files in directory
211 GetFileEntries(&files, fileName);
216 globalSystem.fileMonitorMutex.Release();
218 if(!globalSystem.fileMonitorThread)
220 globalSystem.fileMonitorThread = MonitorThread { };
221 globalSystem.fileMonitorThread.Create();
227 files.Free(FileEntry::Free);
228 fileNotifies.Free(FileNotify::Free);
230 globalSystem.fileMonitors.Remove(this);
234 bool AddFileNotify(FileChange action, char * fileName, char * param)
236 if(watchFor & action)
238 fileNotifies.Add(FileNotify {
239 monitor = this, action = action, fileName = CopyString(fileName), param = CopyString(param)
247 private class FileNotify : struct
249 FileNotify prev, next;
262 static define FILE_MONITOR_RESOLUTION = 1.0; // Once every second
264 static int CompareFiles(FileEntry e1, FileEntry e2, void * data)
266 if(!e1.attribs.isDirectory && e2.attribs.isDirectory)
268 else if(e1.attribs.isDirectory && !e2.attribs.isDirectory)
271 return strcmp(e1.name, e2.name);
274 static void GetFileEntries(OldList list, char * path)
276 FileListing listing { path, null };
277 while(listing.Find())
280 name = CopyString(listing.name),
281 attribs = listing.stats.attribs,
282 modified = listing.stats.modified,
283 size = listing.stats.size
286 list.Sort(CompareFiles, null);
289 static class MonitorThread : Thread
295 while(!globalSystem.systemTerminate)
297 Time currentTime = GetTime();
298 if(currentTime - lastTime > FILE_MONITOR_RESOLUTION)
301 bool fileActivity = false;
302 lastTime = currentTime;
304 // printf("[%d] Waiting in MonitorThread for fileMonitor Mutex %x...\n", GetCurrentThreadID(), globalSystem.fileMonitorMutex);
305 globalSystem.fileMonitorMutex.Wait();
307 for(monitor = globalSystem.fileMonitors.first; monitor; monitor = monitor.next)
309 if(monitor.directory)
311 bool exists = FileExists(monitor.fileName).isDirectory;
312 if(exists && !monitor.exists)
314 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, monitor.fileName, null);
315 monitor.exists = exists;
317 else if(!exists && monitor.exists)
319 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, monitor.fileName, null);
320 monitor.exists = exists;
322 else if(exists && monitor.watchFor & FileChange { created = true, deleted = true, modified = true, attribs = true})
326 OldList newEntries = { 0 };
327 GetFileEntries(&newEntries, monitor.fileName);
329 // Directory comparison
330 entry = monitor.files.first;
331 newEntry = newEntries.first;
333 while(entry || newEntry)
336 if(!entry) comparison = 1;
337 else if(!newEntry) comparison = -1;
338 else comparison = strcmp(entry.name,newEntry.name);
342 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, newEntry.name, null);
343 newEntry = newEntry.next;
345 else if(comparison < 0)
347 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, entry.name, null);
352 if(newEntry.modified != entry.modified || newEntry.size != entry.size)
354 fileActivity |= monitor.AddFileNotify(FileChange { modified = true }, entry.name, null);
356 else if(newEntry.attribs != entry.attribs)
358 fileActivity |= monitor.AddFileNotify(FileChange { attribs = true }, entry.name, null);
361 newEntry = newEntry.next;
364 monitor.files.Free(FileEntry::Free);
365 monitor.files = newEntries;
368 else if(monitor.fileName)
370 FileAttribs exists = FileExists(monitor.fileName);
371 // printf("Iterating on Monitor for %s...\n", monitor.fileName);
373 if((exists && !exists.isDirectory) && !monitor.exists)
375 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, monitor.fileName, null);
376 monitor.exists = true;
378 else if((!exists || exists.isDirectory) && monitor.exists)
380 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, monitor.fileName, null);
381 monitor.exists = false;
383 else if(monitor.exists)
386 if(FileGetStats(monitor.fileName, stats))
389 /*if(strstr(monitor.fileName, "Debugger"))
390 printf("Now modified is %x\n", stats.modified);*/
393 if(stats.modified != monitor.modified || stats.size != monitor.size)
395 // printf("Modified/size changed, adding file notify...\n");
396 if(stats.modified > monitor.modified || monitor.modified - (TimeStamp)stats.modified > 2)
397 fileActivity |= monitor.AddFileNotify(FileChange { modified = true }, monitor.fileName, null);
398 monitor.size = stats.size;
399 monitor.modified = stats.modified;
401 /*if(strstr(monitor.fileName, "Debugger"))
402 printf("Setting modified to %x\n", monitor.modified);*/
405 else if(stats.attribs != monitor.attribs)
407 // printf("Attribs changed, adding file notify...\n");
409 fileActivity |= monitor.AddFileNotify(FileChange { attribs = true }, monitor.fileName, null);
410 monitor.attribs = stats.attribs;
418 // printf("[%d] Signaling Event...\n", GetCurrentThreadID());
419 guiApp.SignalEvent();
422 // printf("[%d] Releasing Mutex...\n", GetCurrentThreadID());
423 globalSystem.fileMonitorMutex.Release();
426 // printf("[%d] Sleeping...\n", GetCurrentThreadID());