12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879(*
Reimplementation of file opening operations
This is for better Windows support and to allow multicore
support for users of OCaml 5+, as well as cloning the process with
fork(). Note that Testo doesn't use these features directly but
users of temporary files might.
*)openFpath_.Operators(* Random-number generator + some safety against fork() resulting in
two processes generating the same file names. *)typeprng={pid:int;state:Random.State.t}letcreate_prng()={pid=Unix.getpid();state=Random.State.make_self_init()}(* This is used for compatibility with OCaml < 5.
This is not suitable for multidomain uses, where each domain
should have a different PRNG seed. *)letdefault_prng=ref(create_prng())(* Ensure that we don't use a duplicated random number generator state
after a fork() *)letcheck_prng()=ifUnix.getpid()<>!default_prng.pidthendefault_prng:=create_prng()letdefault_get_random_key()=check_prng();Random.State.bits!default_prng.state(* Note that the temp dir depends on environment variables which
can change during the lifetime of the process. *)letget_temp_dir_path()=Fpath.v(Filename.get_temp_dir_name())letget_temp_file_path?(get_random_key=default_get_random_key)?(prefix="")?(suffix="")?(temp_dir=get_temp_dir_path())()=letrnd=get_random_key()land0xFFFFFFinFpath.add_segtemp_dir(Printf.sprintf"%s%06x%s"prefixrndsuffix)letbase_fd_flags:Unix.open_flaglist=[(* Same flags as those used by 'open_out' and 'open_out_gen'
for creating a file descriptor (Unix) or a file handle (Windows). *)O_WRONLY;O_CREAT;O_EXCL;]letopen_out?(perms=0o666)?(windows_binary=false)?(windows_file_share_delete=true)path=letfd_flags=ifwindows_file_share_deletethenUnix.O_SHARE_DELETE::base_fd_flagselsebase_fd_flagsinletfd=Unix.openfile!!pathfd_flagspermsinletoc=Unix.out_channel_of_descrfdinset_binary_mode_outocwindows_binary;ocletopen_temp_file?get_random_key?(perms=0o600)?temp_dir?windows_binary?windows_file_share_delete?prefix?suffix()=(* Same tactic as 'Filename.open_temp_file' from the standard library *)letrectry_namecounter=letpath=get_temp_file_path?get_random_key?prefix?suffix?temp_dir()intry(path,open_out~perms?windows_binary?windows_file_share_deletepath)with|Sys_error_ase->ifcounter>=20thenfailwith("Cannot create a temporary file after trying 20 different names: "^Printexc.to_stringe)elsetry_name(counter+1)intry_name0