sync
[gitlive] / Monitor.vala
1 //<Script type="text/javascript">
2 //var Gio     = imports.gi.Gio;
3 //var GLib    = imports.gi.GLib;
4
5 //var XObject = imports.XObject.XObject;
6 //var File    = imports.File.File;
7
8 /// # valac  --pkg gio-2.0  --pkg posix Monitor.vala -o /tmp/Monitor
9
10  
11 //using Gee; // for array list?
12 /*
13 static int main (string[] args) {
14     // A reference to our file
15     //var file = File.new_for_path ("data.txt");
16     MainLoop loop = new MainLoop ();
17     print("starting");
18     var m = new Monitor();
19     
20     m.add("/home/alan/gitlive");
21     m.start();
22     loop.run ();
23
24     return 0;
25
26 }
27 */
28
29 public class  MonitorNamePathDir {
30     public string action;
31     public string name;
32     public string path;
33     public string dir;
34     
35     public MonitorNamePathDir(string name, string path, string dir)
36     {
37         this.name = name;
38         this.path = path;
39         this.dir = dir;
40         this.action = "?";
41         
42     }
43 }
44
45
46
47 public delegate void onEventHander (FileMonitor fm, File f_orig, File of_orig, FileMonitorEvent event_type);
48
49
50 /**
51  * Monitor class - handles monitor managment for a large tree...
52  *
53  *
54  * This 
55  * 
56  * usage : 
57  * x = new Monitor({
58      change : function () {
59          * ....
60          *}
61   }
62  * x.add('/somepath')
63  * x.stop() // stops all scanning.
64  * x.play(); // starts the scanning
65  * 
66  * 
67  * 
68  * 
69  */
70  
71 public class Monitor : Object
72 {
73
74
75
76     public Monitor()
77     {
78        
79      
80         this.monitors = new Array<FileMonitor> ();
81         this.top = new Array<string> ();
82         this.paused = false;
83     }
84      
85     public Array<FileMonitor> monitors;// Array of MonitorNamePathDirileMonitors
86     public Array<string> top; // list of top level directories..
87     public bool paused;
88     /**
89      * add a directory or file to monitor
90      */
91     public void add (string add)
92     {
93         
94         print("Monitor.add: " + add);
95         this.top.append_val(add);
96     }
97     /**
98      * start monitoring
99      */
100     public void start()
101     {
102         for(int i = 0; i < this.top.length ; i++) {
103             this.monitor(this.top.index(i));
104         }
105     }
106     /**
107      * stop / pause monitoring
108      * 
109      */
110     public void stop()
111     {
112         
113         for(int i = 0; i < this.monitors.length ; i++) {
114             this.monitors.index(i).cancel();
115         } 
116         this.monitors = new Array<FileMonitor>(); // clean /destroy/ kill old?
117     }
118     /**
119      * pause monitoring - without changing what's monitored 
120      */
121     public void pause()
122     {
123         this.paused = true;
124     }
125     /**
126      * resume monitoring - without changing what's monitored 
127      */
128     public void resume()
129     {
130         this.paused = false;
131     }
132     /**
133      * monitor a file or directory (privatish)
134      *
135      * initially called with ~/gitlive  null 0 (effectvely)
136      * 
137      * 
138      */
139     public void monitor(string path, int depth = 0)
140     {
141          
142         stdout.printf("ADD: (%d): %s\n", depth, path);
143         
144         //depth = typeof(depth) == 'number'  ? depth *1 : 0;
145         depth = depth > 0  ? depth *1 : 0;
146         
147         
148         //fn = fn || function (fm, f, of, event_type, uh) {
149         //    _this.onEvent(fm, f, of, event_type, uh);
150         //}
151        
152           
153         var f = File.new_for_path(path);
154             //var cancel = new Gio.Cancellable ();
155         if (depth > 0) { 
156   
157             try {
158   
159                  var fm = f.monitor(FileMonitorFlags.SEND_MOVED + FileMonitorFlags.WATCH_MOVES,null); //Gio.FileMonitorFlags.SEND_MOVED
160  
161                  fm.changed.connect( this.onEvent );
162                 this.monitors.append_val(fm);
163
164             } catch (Error e) {
165                   GLib.debug("Error adding monitor: %s", e.message);
166                 // FIXME -- show error? do nothing..            
167             }
168             // print("ADD path " + depth + ' ' + path);
169         }
170         // iterate children?
171         // - this is not used.
172         //if (GLib.file_test(path + '/.git' , GLib.FileTest.IS_DIR) && this.initRepo) {
173             
174         //    this.initRepo(path);
175         //}
176         FileEnumerator file_enum;
177         var cancellable = new Cancellable ();
178         try {      
179             file_enum = f.enumerate_children(
180                FileAttribute.STANDARD_DISPLAY_NAME + "," +   FileAttribute.STANDARD_TYPE,
181                         FileQueryInfoFlags.NOFOLLOW_SYMLINKS,  // FileQueryInfoFlags.NONE,
182                cancellable);
183         } catch (Error e) {
184             // FIXME - show error..
185             return;
186         }
187         FileInfo next_file;
188         
189         while (cancellable.is_cancelled () == false ) {
190             try {
191                 next_file = file_enum .next_file (cancellable);
192             } catch(Error e) {
193                 print(e.message);
194                 break;
195             }
196
197             if (next_file == null) {
198                 break;
199             }
200
201             //print("got a file " + next_file.sudo () + '?=' + Gio.FileType.DIRECTORY);
202
203             if (next_file.get_file_type() != FileType.DIRECTORY) {
204                 next_file = null;
205                 continue;
206             }
207
208
209             //stdout.printf("Monitor.monitor: got file %s : type :%u\n",
210             //        next_file.get_display_name(), next_file.get_file_type());
211
212
213             if (next_file.get_is_symlink()) {
214                 next_file = null;
215                 continue;
216             }
217             
218             if (next_file.get_display_name()[0] == '.') {
219                 next_file = null;
220                 continue;
221             }
222             var sp = path+"/"+next_file.get_display_name();
223             // skip modules.
224             //print("got a file : " + sp);
225          
226             next_file = null;
227             
228             
229             
230             this.monitor(sp, depth + 1);
231             
232         }
233         try {
234             file_enum.close(null);
235         } catch(Error e) {
236             // ignore?
237         }
238     }
239     
240     
241     
242     public File realpath(File file)
243     {
244         if (file != null) {
245             return file;
246         }
247         
248         if (FileUtils.test(file.get_path(), FileTest.EXISTS)) {
249             var rp = Posix.realpath(file.get_path());
250             return File.new_for_path(rp);  
251             
252         }
253         // file does not currently exist..
254         // check parent.
255         
256 // FIX ME - string split?/? 
257         var bn = file.get_basename();
258         var ar =  file.get_path().split("/");
259         ar.resize(ar.length-1);
260         var dirname = string.joinv("/",ar );
261         var rp = Posix.realpath(dirname);
262         return File.new_for_path(rp + "/" + bn);
263         
264     }
265    
266     
267     
268      
269     public void onEvent(File f_orig, File? of_orig, FileMonitorEvent event_type)
270     {
271         if (this.paused) {
272             return;
273         }
274        // print("onEvent\n");
275         var f = this.realpath(f_orig);
276         
277  
278         MonitorNamePathDir src = new MonitorNamePathDir( f.get_basename(), f.get_path() , Path.get_dirname(f.get_path()));
279  
280         
281        
282         //string event_name = "UKNOWN";
283         
284         
285         // extract the event names ... - not sure if introspection is feasible in vala..
286         //for(var i in Gio.FileMonitorEvent) {
287          //    if (Gio.FileMonitorEvent[i] == event_type) {
288          //        event_name = i;
289          //    }
290          //}
291         
292
293
294
295         //print (JSON.stringify([event_name , f.get_path(), of ? of.get_path() : false ] ));
296         //print ("got src: " + src.toString());
297         //print ("got event: " + src.toString());
298         try {
299                 
300
301             switch(event_type) {
302                 case FileMonitorEvent.CHANGED:
303                     src.action = "changed";
304                     this.onChanged(src);
305                     return; // ingore thise?? -wait for changes_done_htin?
306                     
307                 case FileMonitorEvent.CHANGES_DONE_HINT:
308                     src.action = "changed";
309                     this.onChangesDoneHint(src);
310                     return;
311                     
312                 case FileMonitorEvent.DELETED:
313                     src.action = "rm";
314                     this.onDeleted(src);
315                     return;
316                     
317                 case FileMonitorEvent.CREATED:
318                     src.action = "created";
319                     this.onCreated(src);
320                     return;
321                 
322                 case FileMonitorEvent.ATTRIBUTE_CHANGED: // eg. chmod/chatt
323                     src.action = "attrib";
324                     this.onAttributeChanged(src);
325                     return;
326                 
327                 case FileMonitorEvent.MOVED: // eg. chmod/chatt
328
329                       var of = this.realpath(of_orig);
330                        var   dest = new MonitorNamePathDir(
331                                      of.get_basename(), 
332                                     of.get_path(),  
333                                     Path.get_dirname(of.get_path())
334                                 );
335                         
336
337                     src.action = "moved";
338                     dest.action = "moved";
339                     this.onMoved(src,dest);
340                     return; 
341                 default:
342                     stdout.printf("event type not handled %u", event_type);
343                     break;
344                 // rest are mount related - not really relivant.. maybe add later..
345             } 
346         } catch(Error e) {
347             print(e.message);
348         }
349         
350     }
351     
352
353     /** override these to do stuff.. */
354     //public void initRepo(MonitorNamePathDir src) { } // called on startup at the top level repo dir.
355     public virtual void  onChanged(MonitorNamePathDir src) { }
356     public virtual void onChangesDoneHint(MonitorNamePathDir src) { }
357     public virtual void onDeleted(MonitorNamePathDir src) { }
358     public virtual void onCreated(MonitorNamePathDir src) { }
359     public virtual void onAttributeChanged(MonitorNamePathDir src) { }
360     public virtual void onMoved(MonitorNamePathDir src,MonitorNamePathDir dest) { }
361           
362     
363 }
364  
365  
366
367
368
369
370