|
|
|
(*
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*)
|
|
|
|
|
|
|
|
module U = Unix
|
|
|
|
|
|
|
|
let file_exists name =
|
|
|
|
try
|
|
|
|
U.access name [U.F_OK] ;
|
|
|
|
true
|
|
|
|
with U.Unix_error _ -> false
|
|
|
|
|
|
|
|
|
|
|
|
(* Forked processes will not start from 0 but this is inessential. *)
|
|
|
|
let tmp_counter = ref 0
|
|
|
|
|
|
|
|
let mktemp base =
|
|
|
|
let pid = U.getpid () in
|
|
|
|
let rec aux () =
|
|
|
|
let name = Printf.sprintf "%s.%d.%08d.tmp" base pid !tmp_counter in
|
|
|
|
incr tmp_counter ;
|
|
|
|
if
|
|
|
|
file_exists name
|
|
|
|
(* This should not happen unless the file is very old and the pid is reused. *)
|
|
|
|
then aux ()
|
|
|
|
else name
|
|
|
|
in
|
|
|
|
aux ()
|
|
|
|
|
|
|
|
|
|
|
|
let buffer_size = 8192
|
|
|
|
|
|
|
|
let tee ic ocs =
|
|
|
|
let buffer = Bytes.create buffer_size in
|
|
|
|
let rec loop () =
|
|
|
|
match input ic buffer 0 buffer_size with
|
|
|
|
| 0 ->
|
|
|
|
()
|
|
|
|
| r ->
|
|
|
|
List.iter (fun oc -> output oc buffer 0 r) ocs ;
|
|
|
|
loop ()
|
|
|
|
in
|
|
|
|
loop ()
|
|
|
|
|
|
|
|
|
|
|
|
let gzip ic oc =
|
|
|
|
let ocz = Gzip.open_out_chan oc in
|
|
|
|
let buffer = Bytes.create buffer_size in
|
|
|
|
let rec loop () =
|
|
|
|
match input ic buffer 0 buffer_size with
|
|
|
|
| 0 ->
|
|
|
|
()
|
|
|
|
| r ->
|
|
|
|
Gzip.output ocz buffer 0 r ;
|
|
|
|
loop ()
|
|
|
|
in
|
|
|
|
let success =
|
|
|
|
try
|
|
|
|
loop () ;
|
|
|
|
true
|
|
|
|
with Gzip.Error _ -> false
|
|
|
|
in
|
|
|
|
Gzip.close_out ocz ;
|
|
|
|
success
|
|
|
|
|
|
|
|
|
|
|
|
let gunzip ic oc =
|
|
|
|
let icz = Gzip.open_in_chan ic in
|
|
|
|
let buffer = Bytes.create buffer_size in
|
|
|
|
let rec loop () =
|
|
|
|
match Gzip.input icz buffer 0 buffer_size with
|
|
|
|
| 0 ->
|
|
|
|
()
|
|
|
|
| r ->
|
|
|
|
output oc buffer 0 r ;
|
|
|
|
loop ()
|
|
|
|
in
|
|
|
|
let success =
|
|
|
|
try
|
|
|
|
loop () ;
|
|
|
|
true
|
|
|
|
with Gzip.Error _ -> false
|
|
|
|
in
|
|
|
|
Gzip.close_in icz ;
|
|
|
|
success
|
|
|
|
|
|
|
|
|
|
|
|
let copy ic oc = tee ic [oc]
|
|
|
|
|
|
|
|
let rec restart_on_EINTR f x = try f x with U.Unix_error (U.EINTR, _, _) -> restart_on_EINTR f x
|
|
|
|
|
|
|
|
let close fd = try U.close fd with U.Unix_error _ -> ()
|
|
|
|
|
|
|
|
let close_in = close_in_noerr
|
|
|
|
|
|
|
|
let close_out = close_out_noerr
|
|
|
|
|
|
|
|
let wait pid =
|
|
|
|
match snd (restart_on_EINTR (U.waitpid []) pid) with U.WEXITED 0 -> true | _ -> false
|
|
|
|
|
|
|
|
|
|
|
|
let exec args stdin stdout stderr =
|
|
|
|
wait
|
|
|
|
(U.create_process args.(0) args (U.descr_of_in_channel stdin) (U.descr_of_out_channel stdout)
|
|
|
|
(U.descr_of_out_channel stderr))
|
|
|
|
|
|
|
|
|
|
|
|
let diff file1 file2 oc = exec [|"diff"; file1; file2|] stdin oc stderr
|
|
|
|
|
|
|
|
let fork f =
|
|
|
|
let fd_in, fd_out = U.pipe () in
|
|
|
|
match U.fork () with
|
|
|
|
| 0 -> (
|
|
|
|
U.close fd_in ;
|
|
|
|
try
|
|
|
|
if f (U.out_channel_of_descr fd_out) then exit 0
|
|
|
|
else (
|
|
|
|
close fd_out ;
|
|
|
|
exit 1 )
|
|
|
|
with _ ->
|
|
|
|
close fd_out ;
|
|
|
|
exit 2 )
|
|
|
|
| pid ->
|
|
|
|
if pid < 0 then failwith "fork error"
|
|
|
|
else (
|
|
|
|
U.close fd_out ;
|
|
|
|
(pid, U.in_channel_of_descr fd_in) )
|
|
|
|
|
|
|
|
|
|
|
|
let compose f g ic oc =
|
|
|
|
let pid, ic1 = fork (f ic) in
|
|
|
|
let r1 = g ic1 oc in
|
|
|
|
let r2 = wait pid in
|
|
|
|
close_in ic1 ;
|
|
|
|
r1 && r2
|
|
|
|
|
|
|
|
|
|
|
|
let diff_on_same_input f1 f2 ic oc =
|
|
|
|
let file = mktemp "input" in
|
|
|
|
let ofile = open_out file in
|
|
|
|
copy ic ofile ;
|
|
|
|
close_out ofile ;
|
|
|
|
let ifile1 = open_in file and ifile2 = open_in file in
|
|
|
|
let file1 = mktemp "output1" and file2 = mktemp "output2" in
|
|
|
|
let ofile1 = open_out file1 and ofile2 = open_out file2 in
|
|
|
|
let r1 = f1 ifile1 ofile1 and r2 = f2 ifile2 ofile2 in
|
|
|
|
close_in ifile1 ;
|
|
|
|
close_in ifile2 ;
|
|
|
|
close_out ofile1 ;
|
|
|
|
close_out ofile2 ;
|
|
|
|
let success = if r1 && r2 then diff file1 file2 oc else false in
|
|
|
|
U.unlink file ;
|
|
|
|
U.unlink file1 ;
|
|
|
|
U.unlink file2 ;
|
|
|
|
success
|