You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
3.4 KiB

(*
* 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