resources/RooUsage.txt
[app.Builder.js] / src / Spawn.vala
index 1a135ed..d3b9428 100644 (file)
@@ -1,29 +1,29 @@
 
-/// # valac  --pkg gio-2.0 --pkg gtk+-3.0  --pkg posix Spawn.vala -o /tmp/Spawn
+/// # valac  --pkg gio-2.0    --pkg posix Spawn.vala -o /tmp/Spawn
 
 using GLib;
-using Gtk;
 
 /**
  * Revised version?
  * 
- * x = new Spawn( working dir, args)
+ * x = new Spawn( "/tmp", {"ls", "-l" })
+ * 
+ * // these are optionall..
  * x.env = ..... (if you need to set one...
- * x.output_line.connect((string str) => { ... });
+ * x.output_line.connect((string str) => { 
+ *             if ( Gtk.events_pending()) { Gtk.main_iteration(); } 
+ * });
  * x.input_line.connect(() => { return string });
- * x.finish.connect((int res, string output, string stderr) => { ... });
- * x.run();
+ * 
+ * x.run((int res, string output, string stderr) => { ... });
+
  * 
  * 
  */
 
 
-
-
-  
  
-}
-
 public errordomain SpawnError {
     NO_ARGS,
     WRITE_ERROR,
@@ -33,31 +33,45 @@ public errordomain SpawnError {
 
 /**
  * @class Spawn
- * @param cfg {SpawnConfig} settings - see properties.
+ * @param cwd {String}            working directory. (defaults to home directory)
+ * @param args {Array}            arguments eg. [ 'ls', '-l' ]
  * 
- * @arg cwd {String}            working directory. (defaults to home directory)
- * @arg args {Array}            arguments eg. [ 'ls', '-l' ]
- * @arg listeners {Object} (optional) handlers for output, stderr, input
- *     stderr/output both receive output line as argument
- *     input should return any standard input
- *     finish recieves result as argument.
  * @arg env {Array}             enviroment eg. [ 'GITDIR=/home/test' ]
- * @arg async {Boolean} (optional)return instantly, or wait for exit. (default no)
- * @arg exceptions {Boolean}    throw exception on failure (default no)
- * @arg debug {Boolean}    print out what's going on.. (default no)
+ * @arg is_async {Boolean} (optional)return instantly, or wait for exit. (default no)
+ * @arg trhow_exceptions {Boolean}    throw exception on failure (default no)
  * 
  */
-
-
+  
 public class Spawn : Object
 {
-       public signal string input();
+       /**
+        * @signal input called at start to send input when process starts?
+        * @return the string or null 
+        */
+       public signal string? input();
+       /**
+        * @signal complete called at when the command has completed.
+        * 
+        */
+       public signal void complete(int res, string str, string stderr);
+       /**
+        * @signal output_line called when a line is recieved from the process.
+        * Note you may want to connect this and run 
+        *   if ( Gtk.events_pending()) { Gtk.main_iteration(); }
+        * 
+        * @param {string} str 
+        */
     public signal void output_line(string str);
-    public signal void finish(int res, string str, string stderr);
-
+    
        public string cwd;
        public string[] args;
        public string[] env;
+       
+       public bool is_async = true;
+       public bool throw_exceptions = false;
+       public bool detach = false;
 
     public Spawn(string cwd, string[] args) throws Error
     {
@@ -92,7 +106,7 @@ public class Spawn : Object
      /**
      * @property result {Number} execution result.
      */
-    int result= 0;
+    public int result= 0;
     /**
      * @property pid {Number} pid of child process (of false if it's not running)
      */
@@ -126,66 +140,78 @@ public class Spawn : Object
      * result is applied to object properties (eg. '?' or 'stderr')
      * @returns {Object} self.
      */
-    public void run() throws SpawnError, GLib.SpawnError, GLib.IOChannelError
-    {
-        
-         
-        err_src = -1;
-        out_src = -1;
-        int standard_input;
-        int standard_output;
-        int standard_error;
+       public void run( ) throws SpawnError, GLib.SpawnError, GLib.IOChannelError
+       {
+               
+                
+               err_src = -1;
+               out_src = -1;
+               int standard_input;
+               int standard_output;
+               int standard_error;
 
 
-        
-        if (this.cfg.debug) {
-           stdout.printf("cd %s; %s" , this.cfg.cwd , string.joinv(" ", this.cfg.args));
-        }
-        
-        Process.spawn_async_with_pipes (
-                this.cfg.cwd,
-                this.cfg.args,
-                this.cfg.env,
-                SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
-                null,
-                out this.pid,
-                out standard_input,
-                out standard_output,
-                           out standard_error);
+                
+               GLib.debug("cd %s; %s" , this.cwd , string.joinv(" ", this.args));
+               
+               if (this.detach) { 
+                       Process.spawn_async_with_pipes (
+                               this.cwd,
+                               this.args,
+                               this.env.length > 0 ? this.env : null,
+                               SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
+                               null,
+                               out this.pid);
+                               ChildWatch.add (this.pid, (pid, status) => {
+                                       // Triggered when the child indicated by child_pid exits
+                                       Process.close_pid (pid);
+                                        
+                               });
+                               
+                               return;
+
+               }
+               Process.spawn_async_with_pipes (
+                               this.cwd,
+                               this.args,
+                               this.env.length > 0 ? this.env : null,
+                               SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
+                               null,
+                               out this.pid,
+                               out standard_input,
+                               out standard_output,
+                               out standard_error);
 
                // stdout:
-       
-               
-       //print(JSON.stringify(gret));    
-         
-        if (this.cfg.debug) {
-            
-            stdout.printf("PID: %d" ,this.pid);
-        }
-        
-        this.in_ch = new GLib.IOChannel.unix_new(standard_input);
-        this.out_ch = new GLib.IOChannel.unix_new(standard_output);
-        this.err_ch = new GLib.IOChannel.unix_new(standard_error);
-        
-        // make everything non-blocking!
-        
-        
-            
-                  // using NONBLOCKING only works if io_add_watch
-          //returns true/false in right conditions
-          this.in_ch.set_flags (GLib.IOFlags.NONBLOCK);
-          this.out_ch.set_flags (GLib.IOFlags.NONBLOCK);
-          this.err_ch.set_flags (GLib.IOFlags.NONBLOCK);
-                   
-      
+                
+                       
+               //print(JSON.stringify(gret));    
+                
+               GLib.debug("PID: %d" ,this.pid);
+                
+               
+               this.in_ch = new GLib.IOChannel.unix_new(standard_input);
+               this.out_ch = new GLib.IOChannel.unix_new(standard_output);
+               this.err_ch = new GLib.IOChannel.unix_new(standard_error);
+               
+               // make everything non-blocking!
+
+
+
+                         // using NONBLOCKING only works if io_add_watch
+               //returns true/false in right conditions
+               this.in_ch.set_flags (GLib.IOFlags.NONBLOCK);
+               this.out_ch.set_flags (GLib.IOFlags.NONBLOCK);
+               this.err_ch.set_flags (GLib.IOFlags.NONBLOCK);
+                          
+
 
  
                ChildWatch.add (this.pid, (w_pid, result) => {
                
                        this.result = result;
-                       if (this.cfg.debug) {
-                               stdout.printf("child_watch_add : result:%d ", result);
-                       }
+                       GLib.debug("child_watch_add : result:%d ", result);
+                       
                   
                        this.read(this.out_ch);
                        this.read(this.err_ch);
@@ -199,9 +225,9 @@ public class Spawn : Object
                        }
                        this.tidyup();
                        //print("DONE TIDYUP");
-                       if (this.cfg.finish != null) {
-                               this.cfg.finish(this.result);
-                       }
+                       
+                       this.complete(this.result, this.output, this.stderr);
+                       
                });
            
                          
@@ -228,10 +254,11 @@ public class Spawn : Object
         // call input.. 
         if (this.pid > -1) {
             // child can exit before we get this far..
-            if (this.cfg.input != null) {
-                       if (this.cfg.debug) print("Trying to call listeners");
+            var input = this.input();
+            if (input != null) {
+                       
                 try {
-                    this.write(this.cfg.input());
+                    this.write(input);
                      // this probably needs to be a bit smarter...
                     //but... let's close input now..
                     this.in_ch.shutdown(true);
@@ -249,26 +276,26 @@ public class Spawn : Object
             
         }
         // async - if running - return..
-        if (this.cfg.async && this.pid > -1) {
+        if (this.is_async && this.pid > -1) {
             return;
         }
          
         // start mainloop if not async..
         
         if (this.pid > -1) {
-             print("starting main loop");
+            GLib.debug("starting main loop");
              //if (this.cfg.debug) {
              //  
              // }
                this.ctx = new MainLoop ();
             this.ctx.run(); // wait fore exit?
             
-            print("main_loop done!");
+            GLib.debug("main_loop done!");
         } else {
             this.tidyup(); // tidyup get's called in main loop. 
         }
         
-        if (this.cfg.exceptions && this.result != 0) {
+        if (this.throw_exceptions && this.result != 0) {
            
             throw new SpawnError.EXECUTE_ERROR(this.stderr);
             //this.toString = function() { return this.stderr; };
@@ -344,7 +371,7 @@ public class Spawn : Object
      */
     private bool read(IOChannel ch) 
     {
-        string prop = (ch == this.out_ch) ? "output" : "stderr";
+       // string prop = (ch == this.out_ch) ? "output" : "stderr";
        // print("prop: " + prop);
 
         
@@ -374,28 +401,28 @@ public class Spawn : Object
                     //}
                     if (ch == this.out_ch) {
                         this.output += buffer;
-                        if (this.cfg.output != null) {
-                                this.cfg.output(  buffer);                  
-                        }
+                        this.output_line(  buffer);                  
+                        
                     } else {
                         this.stderr += buffer;
+                        this.output_line(  buffer); 
                     }
                     //_this[prop] += x.str_return;
                     //if (this.cfg.debug) {
-                        stdout.printf("%s : %s", prop , buffer);
+                        //GLib.debug("%s : %s", prop , buffer);
                     //}
-                    if (this.cfg.async) {
+                    if (this.is_async) {
                          
-                        if ( Gtk.events_pending()) {
-                             Gtk.main_iteration();
-                        }
+                        //if ( Gtk.events_pending()) {
+                        //     Gtk.main_iteration();
+                        //}
                          
                     }
                     
                     //this.ctx.iteration(true);
                    continue;
                 case GLib.IOStatus.AGAIN:
-                   //print("Should be called again.. waiting for more data..");
+                                       //print("Should be called again.. waiting for more data..");
                            return true;
                     //break;
                 case GLib.IOStatus.ERROR:    
@@ -412,24 +439,25 @@ public class Spawn : Object
     }
     
 }
-  /*
-// test
-try { 
-    Seed.print(run({
-        args: ['ls', '/tmp'],
-        debug : true
-    }));
-} catch (e) { print(JSON.stringify(e)); }
+/*
  
-var secs = (new Date()).getSeconds() 
-
-try {      
-Seed.print(run({
-    args: ['/bin/touch', '/tmp/spawntest-' + secs ],
-    debug : true
-}));
-} catch (e) { print( 'Error: ' + JSON.stringify(e)); }
+int main (string[] args) {
+       GLib.Log.set_handler(null, 
+               GLib.LogLevelFlags.LEVEL_DEBUG | GLib.LogLevelFlags.LEVEL_WARNING, 
+               (dom, lvl, msg) => {
+               print("%s: %s\n", dom, msg);
+       });
 
+       var ctx = new GLib.MainLoop ();
+       var a = new Spawn("", { "ls" , "-l"});
+       a.run((res, str, stderr) => {
+               print(str);
+               ctx.quit();
+       });
+       
+       
+       ctx.run(); // wait for exit?
+            
+       return 0;
+}
  */