mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 03:05:28 -05:00
richer CCUnix.call API, with tests
This commit is contained in:
parent
62426ed4dc
commit
6fb26288ad
5 changed files with 93 additions and 32 deletions
2
Makefile
2
Makefile
|
|
@ -65,6 +65,8 @@ QTESTABLE=$(filter-out $(DONTTEST), \
|
||||||
$(wildcard src/string/*.mli) \
|
$(wildcard src/string/*.mli) \
|
||||||
$(wildcard src/io/*.ml) \
|
$(wildcard src/io/*.ml) \
|
||||||
$(wildcard src/io/*.mli) \
|
$(wildcard src/io/*.mli) \
|
||||||
|
$(wildcard src/unix/*.ml) \
|
||||||
|
$(wildcard src/unix/*.mli) \
|
||||||
$(wildcard src/sexp/*.ml) \
|
$(wildcard src/sexp/*.ml) \
|
||||||
$(wildcard src/sexp/*.mli) \
|
$(wildcard src/sexp/*.mli) \
|
||||||
$(wildcard src/advanced/*.ml) \
|
$(wildcard src/advanced/*.ml) \
|
||||||
|
|
|
||||||
4
_oasis
4
_oasis
|
|
@ -203,8 +203,8 @@ Executable run_qtest
|
||||||
Build$: flag(tests) && flag(bigarray)
|
Build$: flag(tests) && flag(bigarray)
|
||||||
BuildDepends: containers, containers.misc, containers.string, containers.iter,
|
BuildDepends: containers, containers.misc, containers.string, containers.iter,
|
||||||
containers.io, containers.advanced, containers.sexp,
|
containers.io, containers.advanced, containers.sexp,
|
||||||
containers.bigarray,
|
containers.bigarray, containers.unix,
|
||||||
sequence, gen, oUnit, QTest2Lib
|
sequence, gen, unix, oUnit, QTest2Lib
|
||||||
|
|
||||||
Executable run_qtest_lwt
|
Executable run_qtest_lwt
|
||||||
Path: qtest/lwt/
|
Path: qtest/lwt/
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ let rec insert_ c k v t = match t with
|
||||||
|
|
||||||
let add k v t = insert_ (fun ~old:_ v -> v) k v t
|
let add k v t = insert_ (fun ~old:_ v -> v) k v t
|
||||||
|
|
||||||
(*$Q
|
(*$Q & ~count:20
|
||||||
Q.(list (pair int int)) (fun l -> \
|
Q.(list (pair int int)) (fun l -> \
|
||||||
let l = CCList.Set.uniq l in let m = of_list l in \
|
let l = CCList.Set.uniq l in let m = of_list l in \
|
||||||
List.for_all (fun (k,v) -> find_exn k m = v) l)
|
List.for_all (fun (k,v) -> find_exn k m = v) l)
|
||||||
|
|
|
||||||
|
|
@ -27,19 +27,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
(** {1 High-level Functions on top of Unix} *)
|
(** {1 High-level Functions on top of Unix} *)
|
||||||
|
|
||||||
type 'a or_error = [`Ok of 'a | `Error of string]
|
type 'a or_error = [`Ok of 'a | `Error of string]
|
||||||
|
type 'a gen = unit -> 'a option
|
||||||
|
|
||||||
(** {2 Calling Commands} *)
|
(** {2 Calling Commands} *)
|
||||||
|
|
||||||
type cmd = string * string array
|
|
||||||
(** A command: program + arguments *)
|
|
||||||
|
|
||||||
let cmd_of_sh s = "/bin/sh", [| "/bin/sh"; "-c"; s |]
|
|
||||||
|
|
||||||
let int_of_process_status = function
|
let int_of_process_status = function
|
||||||
| Unix.WEXITED i
|
| Unix.WEXITED i
|
||||||
| Unix.WSIGNALED i
|
| Unix.WSIGNALED i
|
||||||
| Unix.WSTOPPED i -> i
|
| Unix.WSTOPPED i -> i
|
||||||
|
|
||||||
|
let str_exists s p =
|
||||||
|
let rec f s p i =
|
||||||
|
if i = String.length s then false
|
||||||
|
else p s.[i] || f s p (i+1)
|
||||||
|
in
|
||||||
|
f s p 0
|
||||||
|
|
||||||
|
let rec iter_gen f g = match g() with
|
||||||
|
| None -> ()
|
||||||
|
| Some x -> f x; iter_gen f g
|
||||||
|
|
||||||
|
(* print a string, but escaped if required *)
|
||||||
|
let escape_str buf s =
|
||||||
|
if str_exists s
|
||||||
|
(function ' ' | '"' | '\'' -> true | _ -> false)
|
||||||
|
then (
|
||||||
|
Buffer.add_char buf '\'';
|
||||||
|
String.iter
|
||||||
|
(function
|
||||||
|
| '\'' -> Buffer.add_string buf "''"
|
||||||
|
| c -> Buffer.add_char buf c
|
||||||
|
) s;
|
||||||
|
Buffer.add_char buf '\'';
|
||||||
|
) else Buffer.add_string buf s
|
||||||
|
|
||||||
let read_all ?(size=1024) ic =
|
let read_all ?(size=1024) ic =
|
||||||
let buf = ref (Bytes.create size) in
|
let buf = ref (Bytes.create size) in
|
||||||
let len = ref 0 in
|
let len = ref 0 in
|
||||||
|
|
@ -58,24 +79,37 @@ let read_all ?(size=1024) ic =
|
||||||
with Exit ->
|
with Exit ->
|
||||||
Bytes.sub_string !buf 0 !len
|
Bytes.sub_string !buf 0 !len
|
||||||
|
|
||||||
let call ?(stdin="") cmd =
|
type call_result =
|
||||||
let cmd, args = match cmd with
|
< stdout:string;
|
||||||
| `Sh s -> cmd_of_sh s
|
stderr:string;
|
||||||
| `Cmd (c, args) -> c, args
|
status:Unix.process_status;
|
||||||
in
|
errcode:int; (** extracted from status *)
|
||||||
let oc, ic, errc = Unix.open_process_full cmd args in
|
>
|
||||||
(* send stdin *)
|
|
||||||
output_string ic stdin;
|
|
||||||
close_out ic;
|
|
||||||
(* read out and err *)
|
|
||||||
let out = read_all oc in
|
|
||||||
let err = read_all errc in
|
|
||||||
let status = Unix.close_process_full (oc, ic, errc) in
|
|
||||||
object
|
|
||||||
method stdout = out
|
|
||||||
method stderr = err
|
|
||||||
method status = status
|
|
||||||
method errcode = int_of_process_status status
|
|
||||||
end
|
|
||||||
|
|
||||||
|
let kbprintf' buf fmt k = Printf.kbprintf k buf fmt
|
||||||
|
|
||||||
|
let call ?(bufsize=2048) ?(stdin=`Str "") ?(env=[||]) cmd =
|
||||||
|
(* render the command *)
|
||||||
|
let buf = Buffer.create 256 in
|
||||||
|
kbprintf' buf cmd
|
||||||
|
(fun buf ->
|
||||||
|
let cmd = Buffer.contents buf in
|
||||||
|
let oc, ic, errc = Unix.open_process_full cmd env in
|
||||||
|
(* send stdin *)
|
||||||
|
begin match stdin with
|
||||||
|
| `Str s -> output_string ic s
|
||||||
|
| `Gen g -> iter_gen (output_string ic) g
|
||||||
|
end;
|
||||||
|
close_out ic;
|
||||||
|
(* read out and err *)
|
||||||
|
let out = read_all ~size:bufsize oc in
|
||||||
|
let err = read_all ~size:bufsize errc in
|
||||||
|
let status = Unix.close_process_full (oc, ic, errc) in
|
||||||
|
object
|
||||||
|
method stdout = out
|
||||||
|
method stderr = err
|
||||||
|
method status = status
|
||||||
|
method errcode = int_of_process_status status
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,21 +31,46 @@ Some useful functions built on top of Unix.
|
||||||
@since NEXT_RELEASE *)
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
type 'a or_error = [`Ok of 'a | `Error of string]
|
type 'a or_error = [`Ok of 'a | `Error of string]
|
||||||
|
type 'a gen = unit -> 'a option
|
||||||
|
|
||||||
(** {2 Calling Commands} *)
|
(** {2 Calling Commands} *)
|
||||||
|
|
||||||
type cmd = string * string array
|
val escape_str : Buffer.t -> string -> unit
|
||||||
(** A command: program + arguments *)
|
(** Escape a string so it can be a shell argument.
|
||||||
|
*)
|
||||||
|
|
||||||
val call :
|
(*$T
|
||||||
?stdin:string ->
|
CCPrint.sprintf "%a" escape_str "foo" = "foo"
|
||||||
[`Sh of string | `Cmd of cmd] ->
|
CCPrint.sprintf "%a" escape_str "foo bar" = "'foo bar'"
|
||||||
|
CCPrint.sprintf "%a" escape_str "fo'o b'ar" = "'fo''o b''ar'"
|
||||||
|
*)
|
||||||
|
|
||||||
|
type call_result =
|
||||||
< stdout:string;
|
< stdout:string;
|
||||||
stderr:string;
|
stderr:string;
|
||||||
status:Unix.process_status;
|
status:Unix.process_status;
|
||||||
errcode:int; (** extracted from status *)
|
errcode:int; (** extracted from status *)
|
||||||
>
|
>
|
||||||
|
|
||||||
|
val call : ?bufsize:int ->
|
||||||
|
?stdin:[`Gen of string gen | `Str of string] ->
|
||||||
|
?env:string array ->
|
||||||
|
('a, Buffer.t, unit, call_result) format4 ->
|
||||||
|
'a
|
||||||
|
(** [call cmd] wraps the result of [Unix.open_process_full cmd] into an
|
||||||
|
object. It reads the full stdout and stderr of the subprocess before
|
||||||
|
returning.
|
||||||
|
@param stdin if provided, the generator or string is consumed and fed to
|
||||||
|
the subprocess input channel, which is then closed.
|
||||||
|
@param bufsize buffer size used to read stdout and stderr
|
||||||
|
@param env environment to run the command in
|
||||||
|
*)
|
||||||
|
|
||||||
|
(*$T
|
||||||
|
(call ~stdin:(`Str "abc") "cat")#stdout = "abc"
|
||||||
|
(call "echo %a" escape_str "a'b'c")#stdout = "abc\n"
|
||||||
|
(call "echo %s" "a'b'c")#stdout = "abc\n"
|
||||||
|
*)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue