Commits

camlspotter committed 3db961b

Spotlib.Unix.Command improvement

  • Participants
  • Parent commits e562742

Comments (0)

Files changed (4)

 open Xunix
 open Command
 
-let com name tokens = command (name :: tokens) |> print
+let com name tokens = execvp (name :: tokens) |> print
 
 let cp = com "/bin/cp"
 let mv = com "/bin/mv"
 let cat = com "/bin/cat"
 
 let cmp p1 p2 = 
-  match command ["cmp"; p1; p2] |> ignore_output with
+  match execvp ["cmp"; p1; p2] |> ignore_output |> fst with
   | WEXITED 0 -> `Same
   | WEXITED 1 -> `Different
   | WEXITED 2 -> `Error
 
 let file path = 
   match 
-    command ["/usr/bin/file"; path] |> fold ~init:[] ~f:(fun revls -> function
+    execvp ["/usr/bin/file"; path] |> fold ~init:[] ~f:(fun revls -> function
       | `Out, `Read s -> s::revls
       | _ -> revls)
   with
   | WEXITED 0, lines -> `Ok (Some (Xlist.last lines))
   | st, _ -> `Error st
 
-let grep args ~init ~f = command ("/bin/grep" :: args) |> fold ~init ~f
+let grep args ~init ~f = execvp ("/bin/grep" :: args) |> fold ~init ~f
 
-let grep_ = grep ~init:() ~f:(fun _st _ -> ()) *> fst
+let grep_ = grep ~init:() ~f:(fun _st _ -> ())
   
 
-val cp : 
-  string list
-  -> Unix.process_status
+val cp : string list -> Unix.process_status * unit
 
-val mv :
-  string list
-  -> Unix.process_status
+val mv : string list -> Unix.process_status * unit
 
-val rm :
-  string list
-  -> Unix.process_status
+val rm : string list -> Unix.process_status * unit
 
-val cat :
-  string list
-  -> Unix.process_status
+val cat : string list -> Unix.process_status * unit
 
 val cmp : string -> string -> [`Same | `Different | `Error]
 (** Execute "cmp", the file comparison unix command by execvp *)
   -> Unix.process_status * 'a
 (** Run grep command *)
 
-val grep_ : string list -> Unix.process_status
+val grep_ : string list -> Unix.process_status * unit
 (** Run grep command but just returns the result *)
   try set_close_on_exec fd; true with Invalid_argument _ -> false
 
     
-module Process_status = struct
-  let failwith ?name = 
-    let name = match name with None -> "" | Some n -> n ^ ": " in
-    function
-      | WEXITED n   -> Exn.failwithf "%sprocess exited with id %d" name n
-      | WSIGNALED n -> Exn.failwithf "%sprocess killed by signal %d" name n
-      | WSTOPPED n  -> Exn.failwithf "%sprocess stopped by signal %d" name n
-
-  let must_exit_with ?name n = function
-    | WEXITED m when n = m -> ()
-    | e -> failwith ?name e
-
-  let from_exit ?name = function
-    | WEXITED m -> m
-    | e -> failwith ?name e
-
-end
-
 let open_proc_full cmdargs input output error toclose =
   let cmd = match cmdargs with
     | x :: _ -> x
   with Unix_error (EINTR, _, _) -> waitpid_non_intr pid
 
 module Command = struct
+
+  type 'a result = Unix.process_status * 'a
+
+  let failwith ?name = 
+    let name = match name with None -> "" | Some n -> n ^ ": " in
+    function
+      | WEXITED n   -> Exn.failwithf "%sprocess exited with id %d" name n
+      | WSIGNALED n -> Exn.failwithf "%sprocess killed by signal %d" name n
+      | WSTOPPED n  -> Exn.failwithf "%sprocess stopped by signal %d" name n
+
+  let must_exit_with ?name n = function
+    | (WEXITED m, res) when n = m -> res
+    | (e,_) -> failwith ?name e
+
+  let from_exit ?name = function
+    | WEXITED m -> m
+    | e -> failwith ?name e
+
   let buf_flush_limit = 100000
   
   let command_aux readers stat =
     loop readers stat
 
 
-  let command_wrapper (pid, (out, in_, err)) ~f ~init:stat =
+  let command_wrapper (pid, (out, in_, err)) ~init:stat ~f =
     try
       close in_;
       set_nonblock out;
         ignore (waitpid_non_intr pid);
         raise e
   
-  type 'st t = f:('st -> [`Out | `Err] * [ `Read of string | `EOF ] -> 'st) -> init:'st -> Unix.process_status * 'st 
+  type 'st t = init:'st -> f:('st -> [`Out | `Err] * [ `Read of string | `EOF ] -> 'st) -> Unix.process_status * 'st 
   
-  let command cmd = command_wrapper (open_process_full cmd)
-  let shell_command cmd = command_wrapper (open_shell_process_full cmd)
+  let execvp cmd = command_wrapper (open_process_full cmd)
+  let shell cmd = command_wrapper (open_shell_process_full cmd)
   
-  let fold com ~init ~f = com ~f ~init
-  let iter (com : _ t) ~f = com ~f:(fun () i -> f i)  ~init:() |> fst
+  let fold com = com
+  let iter (com : _ t) ~f = com ~init:() ~f:(fun () i -> f i)
 
   let print com = iter com ~f:(function
     | `Err, `Read s -> prerr_endline & Xstring.chop_newline s
   
   let ignore_output com = iter com ~f:(fun _ -> ())
   
-  let shell_command_stdout cmd = 
-    match 
-      command_wrapper (open_shell_process_full cmd) 
-        ~init:[]
-        ~f: (fun rev -> function 
-          | `Err, `Read s -> prerr_endline & Xstring.chop_newline s; rev
-          | `Out, `Read s ->  s :: rev
-          | _ -> rev)
-    with
-    | stat, rev -> stat, List.rev rev
+  let get_stdout com = 
+    let pst, rev =
+      com ~init:[] ~f:(fun rev -> function
+        | `Err, `Read s -> prerr_endline & Xstring.chop_newline s; rev
+        | `Out, `Read s -> s :: rev
+        | _ -> rev)
+    in
+    pst, List.rev rev
 end
 
 let gen_timed get minus f v = 
 
 end
 
-module Process_status : sig
-
-  val failwith : ?name:string -> Unix.process_status -> 'a
-  val must_exit_with : ?name:string -> int -> Unix.process_status -> unit
-  val from_exit : ?name:string -> Unix.process_status -> int
-
-end
-
 module Command : sig
 
+  (** Command invocation 
+
+      Typical usage is:
+
+      [ starter |> scanner ], for example
+      [ shell "ls /" |> print ]
+
+      or
+
+      [ starter |> scanner |> result_checker ], for example
+      [ shell "ls /" |> get_stdout |> must_exist_with ~name:"ls /" 0 ]
+  *)
+
+  (** Starter 
+
+      Starters invoke processes and returns a value of ['st t]. 
+      You do not need to care about ['st]: it is determined by a output scanner. 
+  *)
+    
   type 'st t
 
-  val shell_command : string -> 'st t
+  val shell : string -> 'st t
   (** Execute a shell command using /bin/sh *)
 
-  val command : string list -> 'st t
+  val execvp : string list -> 'st t
   (** Same as [shell_command] but it takes the command and arguments as a list of string,
       then directly executed by [Unix.execvp]. *)
 
+
+  (** Output scanner
+
+      Output scanner takes the result of a starter and scans its stdout+stderr output.
+      After all the stdout+stderr outputs are sent to the scanner, it returns
+      the final result of the scan and the process status
+
+  *)
+
+  type 'a result = Unix.process_status * 'a
+
   val fold : 
     'st t
     -> init:'st 
     -> f:('st -> [`Out | `Err] * [`Read of string | `EOF] -> 'st) 
-    -> Unix.process_status * 'st
+    -> 'st result
+  (** Generic scanner *)
 
   val iter : 
     unit t
     -> f:([`Out | `Err] * [`Read of string | `EOF] -> unit) 
-    -> Unix.process_status
+    -> unit result
+  (** Iteration over stdout+stderr outputs *)
 
   val print :
     unit t
-    -> Unix.process_status
-  (** Output to stdout and stderr of the command are sent to stdout and stderr
+    -> unit result
+  (** Output to stdout and stderr of the command are printed to stdout and stderr
       resp.ly. *)
     
   val ignore_output :
     unit t
-    -> Unix.process_status
+    -> unit result
+  (** Completely ignore the outputs *)
     
-  val shell_command_stdout :
-    string -> Unix.process_status * string list
-  (** Execute a shell command using /bin/sh
-      Err is printed to stderr.
+  val get_stdout :
+    string list t -> string list result
+  (** Gather stdout lines. Stderr outputs are printed to stderr. *)
+
+  (** Result tools
   *)
+
+  val failwith : ?name:string -> Unix.process_status -> 'no_return
+  val from_exit : ?name:string -> Unix.process_status -> int
+  val must_exit_with : ?name:string -> int -> 'res result -> 'res
+
 end
 
 val timed : ('a -> 'b) -> 'a -> 'b * float (* in sec *)