2 // System.IO.FAM.cs: interface with libfam
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (c) 2004 Novell, Inc. (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.ComponentModel;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
37 using System.Threading;
40 struct FAMConnection {
62 public FileSystemWatcher FSW;
63 public string Directory;
64 public string FileMask;
65 public bool IncludeSubdirs;
67 public FAMRequest Request;
68 public Hashtable SubDirs;
71 class FAMWatcher : IFileWatcher
74 static FAMWatcher instance;
75 static Hashtable watches;
76 static Hashtable requests;
77 static FAMConnection conn;
80 static bool use_gamin;
87 public static bool GetInstance (out IFileWatcher watcher, bool gamin)
94 if (instance != null) {
100 watches = Hashtable.Synchronized (new Hashtable ());
101 requests = Hashtable.Synchronized (new Hashtable ());
102 if (FAMOpen (out conn) == -1) {
108 instance = new FAMWatcher ();
113 public void StartDispatching (FileSystemWatcher fsw)
117 if (thread == null) {
118 thread = new Thread (new ThreadStart (Monitor));
119 thread.IsBackground = true;
123 data = (FAMData) watches [fsw];
127 data = new FAMData ();
129 data.Directory = fsw.FullPath;
130 data.FileMask = fsw.MangledFilter;
131 data.IncludeSubdirs = fsw.IncludeSubdirectories;
132 if (data.IncludeSubdirs)
133 data.SubDirs = new Hashtable ();
136 StartMonitoringDirectory (data, false);
138 watches [fsw] = data;
139 requests [data.Request.ReqNum] = data;
145 static void StartMonitoringDirectory (FAMData data, bool justcreated)
148 FileSystemWatcher fsw;
149 if (FAMMonitorDirectory (ref conn, data.Directory, out fr, IntPtr.Zero) == -1)
150 throw new Win32Exception ();
155 if (data.IncludeSubdirs) {
156 foreach (string directory in Directory.GetDirectories (data.Directory)) {
157 FAMData fd = new FAMData ();
159 fd.Directory = directory;
160 fd.FileMask = data.FSW.MangledFilter;
161 fd.IncludeSubdirs = true;
162 fd.SubDirs = new Hashtable ();
167 RenamedEventArgs renamed = null;
169 fsw.DispatchEvents (FileAction.Added, directory, ref renamed);
173 System.Threading.Monitor.PulseAll (fsw);
178 StartMonitoringDirectory (fd, justcreated);
179 data.SubDirs [directory] = fd;
180 requests [fd.Request.ReqNum] = fd;
185 foreach (string filename in Directory.GetFiles(data.Directory)) {
187 RenamedEventArgs renamed = null;
189 fsw.DispatchEvents (FileAction.Added, filename, ref renamed);
190 /* If a file has been created, then it has been written to */
191 fsw.DispatchEvents (FileAction.Modified, filename, ref renamed);
195 System.Threading.Monitor.PulseAll(fsw);
202 public void StopDispatching (FileSystemWatcher fsw)
206 data = (FAMData) watches [fsw];
210 StopMonitoringDirectory (data);
211 watches.Remove (fsw);
212 requests.Remove (data.Request.ReqNum);
213 if (watches.Count == 0)
216 if (!data.IncludeSubdirs)
219 foreach (FAMData fd in data.SubDirs.Values) {
220 StopMonitoringDirectory (fd);
221 requests.Remove (fd.Request.ReqNum);
226 static void StopMonitoringDirectory (FAMData data)
228 if (FAMCancelMonitor (ref conn, ref data.Request) == -1)
229 throw new Win32Exception ();
237 haveEvents = FAMPending (ref conn);
240 if (haveEvents > 0) {
253 const NotifyFilters changed = NotifyFilters.Attributes |
254 NotifyFilters.LastAccess |
256 NotifyFilters.LastWrite;
258 void ProcessEvents ()
260 ArrayList newdirs = null;
266 FileSystemWatcher fsw;
268 if (InternalFAMNextEvent (ref conn, out filename,
269 out code, out requestNumber) != 1)
273 switch ((FAMCodes) code) {
274 case FAMCodes.Changed:
275 case FAMCodes.Deleted:
276 case FAMCodes.Created:
277 found = requests.ContainsKey (requestNumber);
280 case FAMCodes.StartExecuting:
281 case FAMCodes.StopExecuting:
282 case FAMCodes.Acknowledge:
283 case FAMCodes.Exists:
284 case FAMCodes.EndExist:
293 FAMData data = (FAMData) requests [requestNumber];
298 NotifyFilters flt = fsw.NotifyFilter;
299 RenamedEventArgs renamed = null;
301 if (code == (int) FAMCodes.Changed && (flt & changed) != 0)
302 fa = FileAction.Modified;
303 else if (code == (int) FAMCodes.Deleted)
304 fa = FileAction.Removed;
305 else if (code == (int) FAMCodes.Created)
306 fa = FileAction.Added;
311 if (fsw.IncludeSubdirectories) {
312 string full = fsw.FullPath;
313 string datadir = data.Directory;
314 if (datadir != full) {
315 int len = full.Length;
317 if (len > 1 && full [len - 1] == Path.DirectorySeparatorChar)
319 string reldir = datadir.Substring (full.Length + slash);
320 datadir = Path.Combine (datadir, filename);
321 filename = Path.Combine (reldir, filename);
323 datadir = Path.Combine (full, filename);
326 if (fa == FileAction.Added && Directory.Exists (datadir)) {
328 newdirs = new ArrayList (4);
330 FAMData fd = new FAMData ();
332 fd.Directory = datadir;
333 fd.FileMask = fsw.MangledFilter;
334 fd.IncludeSubdirs = true;
335 fd.SubDirs = new Hashtable ();
342 if (filename != data.Directory && !fsw.Pattern.IsMatch (filename))
346 fsw.DispatchEvents (fa, filename, ref renamed);
349 System.Threading.Monitor.PulseAll (fsw);
352 } while (FAMPending (ref conn) > 0);
356 if (newdirs != null) {
357 int count = newdirs.Count;
358 for (int n = 0; n < count; n += 2) {
359 FAMData newdir = (FAMData) newdirs [n];
360 FAMData parent = (FAMData) newdirs [n + 1];
361 StartMonitoringDirectory (newdir, true);
362 requests [newdir.Request.ReqNum] = newdir;
364 parent.SubDirs [newdir.Directory] = newdir;
376 static int FAMOpen (out FAMConnection fc)
379 return gamin_Open (out fc);
380 return fam_Open (out fc);
383 static int FAMClose (ref FAMConnection fc)
386 return gamin_Close (ref fc);
387 return fam_Close (ref fc);
390 static int FAMMonitorDirectory (ref FAMConnection fc, string filename, out FAMRequest fr, IntPtr user_data)
393 return gamin_MonitorDirectory (ref fc, filename, out fr, user_data);
394 return fam_MonitorDirectory (ref fc, filename, out fr, user_data);
397 static int FAMCancelMonitor (ref FAMConnection fc, ref FAMRequest fr)
400 return gamin_CancelMonitor (ref fc, ref fr);
401 return fam_CancelMonitor (ref fc, ref fr);
404 static int FAMPending (ref FAMConnection fc)
407 return gamin_Pending (ref fc);
408 return fam_Pending (ref fc);
413 [DllImport ("libfam.so.0", EntryPoint="FAMOpen")]
414 extern static int fam_Open (out FAMConnection fc);
416 [DllImport ("libfam.so.0", EntryPoint="FAMClose")]
417 extern static int fam_Close (ref FAMConnection fc);
419 [DllImport ("libfam.so.0", EntryPoint="FAMMonitorDirectory")]
420 extern static int fam_MonitorDirectory (ref FAMConnection fc, string filename,
421 out FAMRequest fr, IntPtr user_data);
423 [DllImport ("libfam.so.0", EntryPoint="FAMCancelMonitor")]
424 extern static int fam_CancelMonitor (ref FAMConnection fc, ref FAMRequest fr);
426 [DllImport ("libfam.so.0", EntryPoint="FAMPending")]
427 extern static int fam_Pending (ref FAMConnection fc);
429 [DllImport ("libgamin-1.so.0", EntryPoint="FAMOpen")]
430 extern static int gamin_Open (out FAMConnection fc);
432 [DllImport ("libgamin-1.so.0", EntryPoint="FAMClose")]
433 extern static int gamin_Close (ref FAMConnection fc);
435 [DllImport ("libgamin-1.so.0", EntryPoint="FAMMonitorDirectory")]
436 extern static int gamin_MonitorDirectory (ref FAMConnection fc, string filename,
437 out FAMRequest fr, IntPtr user_data);
439 [DllImport ("libgamin-1.so.0", EntryPoint="FAMCancelMonitor")]
440 extern static int gamin_CancelMonitor (ref FAMConnection fc, ref FAMRequest fr);
442 [DllImport ("libgamin-1.so.0", EntryPoint="FAMPending")]
443 extern static int gamin_Pending (ref FAMConnection fc);
445 [MethodImplAttribute(MethodImplOptions.InternalCall)]
446 extern static int InternalFAMNextEvent (ref FAMConnection fc, out string filename,
447 out int code, out int reqnum);