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;
86 public static bool GetInstance (out IFileWatcher watcher)
93 if (instance != null) {
98 watches = Hashtable.Synchronized (new Hashtable ());
99 requests = Hashtable.Synchronized (new Hashtable ());
100 if (FAMOpen (out conn) == -1) {
106 instance = new FAMWatcher ();
111 public void StartDispatching (FileSystemWatcher fsw)
115 if (thread == null) {
116 thread = new Thread (new ThreadStart (Monitor));
117 thread.IsBackground = true;
121 data = (FAMData) watches [fsw];
125 data = new FAMData ();
127 data.Directory = fsw.FullPath;
128 data.FileMask = fsw.MangledFilter;
129 data.IncludeSubdirs = fsw.IncludeSubdirectories;
130 if (data.IncludeSubdirs)
131 data.SubDirs = new Hashtable ();
134 StartMonitoringDirectory (data);
136 watches [fsw] = data;
137 requests [data.Request.ReqNum] = data;
143 static void StartMonitoringDirectory (FAMData data)
146 if (FAMMonitorDirectory (ref conn, data.Directory, out fr, IntPtr.Zero) == -1)
147 throw new Win32Exception ();
150 if (!data.IncludeSubdirs)
153 foreach (string directory in Directory.GetDirectories (data.Directory)) {
154 FAMData fd = new FAMData ();
156 fd.Directory = directory;
157 fd.FileMask = data.FSW.MangledFilter;
158 fd.IncludeSubdirs = true;
159 fd.SubDirs = new Hashtable ();
162 StartMonitoringDirectory (fd);
163 data.SubDirs [directory] = fd;
164 requests [fd.Request.ReqNum] = fd;
168 public void StopDispatching (FileSystemWatcher fsw)
172 data = (FAMData) watches [fsw];
176 StopMonitoringDirectory (data);
177 watches.Remove (fsw);
178 requests.Remove (data.Request.ReqNum);
179 if (watches.Count == 0)
182 if (!data.IncludeSubdirs)
185 foreach (FAMData fd in data.SubDirs.Values) {
186 StopMonitoringDirectory (fd);
187 requests.Remove (fd.Request.ReqNum);
192 static void StopMonitoringDirectory (FAMData data)
194 if (FAMCancelMonitor (ref conn, ref data.Request) == -1)
195 throw new Win32Exception ();
201 int code, request_number;
207 res = InternalFAMNextEvent (ref conn, out filename, out code, out request_number);
208 } catch (ThreadAbortException) {
209 Thread.ResetAbort ();
219 ProcessEvents (filename, code, request_number);
228 const NotifyFilters changed = NotifyFilters.Attributes |
229 NotifyFilters.LastAccess |
231 NotifyFilters.LastWrite;
233 void ProcessEvents (string filename, int code, int requestNumber)
235 ArrayList newdirs = null;
237 FileSystemWatcher fsw;
239 switch ((FAMCodes) code) {
240 case FAMCodes.Changed:
241 case FAMCodes.Deleted:
242 case FAMCodes.Created:
243 found = requests.ContainsKey (requestNumber);
246 case FAMCodes.StartExecuting:
247 case FAMCodes.StopExecuting:
248 case FAMCodes.Acknowledge:
249 case FAMCodes.Exists:
250 case FAMCodes.EndExist:
259 FAMData data = (FAMData) requests [requestNumber];
264 NotifyFilters flt = fsw.NotifyFilter;
265 RenamedEventArgs renamed = null;
267 if (code == (int) FAMCodes.Changed && (flt & changed) != 0)
268 fa = FileAction.Modified;
269 else if (code == (int) FAMCodes.Deleted)
270 fa = FileAction.Removed;
271 else if (code == (int) FAMCodes.Created)
272 fa = FileAction.Added;
277 if (fsw.IncludeSubdirectories) {
278 string full = fsw.FullPath;
279 string datadir = data.Directory;
280 if (datadir != full) {
281 string reldir = datadir.Substring (full.Length + 1);
282 datadir = Path.Combine (datadir, filename);
283 filename = Path.Combine (reldir, filename);
285 datadir = Path.Combine (fsw.FullPath, filename);
288 if (fa == FileAction.Added && Directory.Exists (datadir)) {
290 newdirs = new ArrayList (4);
292 FAMData fd = new FAMData ();
294 fd.Directory = datadir;
295 fd.FileMask = fsw.MangledFilter;
296 fd.IncludeSubdirs = true;
297 fd.SubDirs = new Hashtable ();
301 requests [fd.Request.ReqNum] = fd;
305 if (filename != data.Directory && !fsw.Pattern.IsMatch (filename))
309 fsw.DispatchEvents (fa, filename, ref renamed);
312 System.Threading.Monitor.PulseAll (fsw);
318 if (newdirs != null) {
319 int count = newdirs.Count;
320 for (int n = 0; n < count; n++) {
321 FAMData newdir = (FAMData) newdirs [n];
322 FAMData parent = (FAMData) newdirs [n + 1];
323 StartMonitoringDirectory (newdir);
325 parent.SubDirs [newdir.Directory] = newdir;
337 [DllImport ("libfam.so.0")]
338 extern static int FAMOpen (out FAMConnection fc);
340 [DllImport ("libfam.so.0")]
341 extern static int FAMClose (ref FAMConnection fc);
343 [DllImport ("libfam.so.0")]
344 extern static int FAMMonitorDirectory (ref FAMConnection fc, string filename,
345 out FAMRequest fr, IntPtr user_data);
347 [DllImport ("libfam.so.0")]
348 extern static int FAMCancelMonitor (ref FAMConnection fc, ref FAMRequest fr);
350 [MethodImplAttribute(MethodImplOptions.InternalCall)]
351 extern static int InternalFAMNextEvent (ref FAMConnection fc, out string filename,
352 out int code, out int reqnum);
354 [DllImport ("libfam.so.0")]
355 extern static int FAMPending (ref FAMConnection fc);