lib/exec: intro `ProcessDuplex::write_and_read`
[nit.git] / lib / standard / exec.nit
index a635a00..3e0d97a 100644 (file)
@@ -22,6 +22,25 @@ in "C" `{
        #include <string.h>
        #include <errno.h>
        #include <stdio.h>
+       #include <unistd.h>
+       #include <sys/wait.h>
+       #include <signal.h>
+`}
+
+in "C Header" `{
+       #include <sys/types.h>
+
+       // FIXME this should be in the "C" block when bug on module blocks is fixed
+       // or, even better, replace the C structure by a Nit object.
+       typedef struct se_exec_data se_exec_data_t;
+       struct se_exec_data {
+               pid_t id;
+               int running;
+               int status;
+               int in_fd;
+               int out_fd;
+               int err_fd;
+       };
 `}
 
 # Simple sub-process
@@ -253,9 +272,49 @@ class ProcessDuplex
 
        redef fun pipeflags do return 3
 
-       redef fun execute
+       redef fun execute do super
+
+       # Write `input` to process and return its output
+       #
+       # Writing and reading are processed line by line,
+       # reading only when something is available.
+       #
+       # ~~~
+       # var proc = new ProcessDuplex("tr", "[:lower:]", "[:upper:]")
+       # assert proc.write_and_read("""
+       #     Alice
+       #     Bob
+       # """) == """
+       #     ALICE
+       #     BOB
+       # """
+       # ~~~
+       fun write_and_read(input: Text): String
        do
-               super
+               var read = new Buffer #new Array[String]
+
+               # Main loop, read and write line by line
+               var prev = 0
+               for delimiter in input.search_all('\n') do
+                       write input.substring(prev, delimiter.after-prev)
+                       prev = delimiter.after
+
+                       while stream_in.poll_in do
+                               read.append stream_in.read_line
+                       end
+               end
+
+               # Write the last line
+               write input.substring_from(prev)
+               stream_out.close
+
+               # Read the rest, may be everything for some programs
+               read.append stream_in.read_all
+               stream_in.close
+
+               # Clean up
+               wait
+               return read.to_s
        end
 end
 
@@ -281,8 +340,13 @@ redef class NativeString
        `}
 end
 
-private extern class NativeProcess
-       fun id: Int is extern "exec_NativeProcess_NativeProcess_id_0"
+private extern class NativeProcess `{ se_exec_data_t* `}
+
+       fun id: Int `{ return self->id; `}
+       fun status: Int `{ return self->status; `}
+       fun in_fd: Int `{ return self->in_fd; `}
+       fun out_fd: Int `{ return self->out_fd; `}
+       fun err_fd: Int `{ return self->err_fd; `}
 
        fun is_finished: Bool `{
                int result = (int)0;
@@ -302,7 +366,6 @@ private extern class NativeProcess
                return result;
        `}
 
-       fun status: Int is extern "exec_NativeProcess_NativeProcess_status_0"
        fun wait `{
                int status;
                if (self->running) {
@@ -311,9 +374,5 @@ private extern class NativeProcess
                        self->running = 0;
                }
        `}
-
-       fun in_fd: Int is extern "exec_NativeProcess_NativeProcess_in_fd_0"
-       fun out_fd: Int is extern "exec_NativeProcess_NativeProcess_out_fd_0"
-       fun err_fd: Int is extern "exec_NativeProcess_NativeProcess_err_fd_0"
 end