diff --git a/src/core/CCIO.ml b/src/core/CCIO.ml index e1469ee8..01f446d5 100644 --- a/src/core/CCIO.ml +++ b/src/core/CCIO.ml @@ -351,3 +351,47 @@ module File = struct let name = Filename.temp_file ?temp_dir prefix suffix in finally_ f name ~h:remove_noerr end + +let rand_digits_ = + let st = lazy (Random.State.make_self_init()) in + fun () -> + let rand = Random.State.bits (Lazy.force st) land 0xFFFFFF in + Printf.sprintf "%06x" rand + +let rmdir_ dir = + try ignore (Sys.command ("rm -r " ^ dir) : int) + with _ -> () + +let with_temp_dir ?(mode=0o700) ?dir pat (f: string -> 'a) : 'a = + let dir = match dir with + | Some d -> d + | None -> Filename.get_temp_dir_name () + in + let raise_err msg = raise (Sys_error msg) in + let rec loop count = + if count < 0 then ( + raise_err "mk_temp_dir: too many failing attemps" + ) else ( + let dir = Filename.concat dir (pat ^ rand_digits_ ()) in + match Unix.mkdir dir mode with + | () -> + finally_ f dir ~h:rmdir_ + | exception Unix.Unix_error (Unix.EEXIST, _, _) -> loop (count - 1) + | exception Unix.Unix_error (Unix.EINTR, _, _) -> loop count + | exception Unix.Unix_error (e, _, _) -> + raise_err ("mk_temp_dir: " ^ (Unix.error_message e)) + ) + in + loop 1000 + +(*$R + let filename = with_temp_dir "test_containers" + (fun dir -> + let name = Filename.concat dir "test" in + CCIO.with_out name (fun oc -> output_string oc "content"; flush oc); + assert_bool ("file exists:"^name) (Sys.file_exists name); + name) + in + assert_bool ("file does not exist"^filename) (not (Sys.file_exists filename)); + () +*) diff --git a/src/core/CCIO.mli b/src/core/CCIO.mli index 76f8a9b3..b4d39b5d 100644 --- a/src/core/CCIO.mli +++ b/src/core/CCIO.mli @@ -250,3 +250,19 @@ module File : sig See {!Filename.temp_file}. @since 0.17 *) end + +val with_temp_dir : + ?mode:int -> ?dir:string -> + string -> (string -> 'a) -> 'a +(** Create a temporary directory, call the function, and then destroy the + directory afterwards. Usage [with_temp_dir pattern f]. + @param pattern the naming pattern for the temporary directory. + Helps avoiding collisions. + @param mode mode for the directory + @param dir the directory under which to make a temporary directory (default [/tmp]) + + Note that this is implemented following the discussion at: + https://discuss.ocaml.org/t/how-to-create-a-temporary-directory-in-ocaml/1815/ + + @since NEXT_RELEASE + *) diff --git a/src/core/CCString.mli b/src/core/CCString.mli index 5632b169..74379a0f 100644 --- a/src/core/CCString.mli +++ b/src/core/CCString.mli @@ -145,8 +145,8 @@ val replace : ?which:[`Left|`Right|`All] -> sub:string -> by:string -> string -> @since 0.14 *) val is_sub : sub:string -> int -> string -> int -> sub_len:int -> bool -(** [is_sub ~sub i s j ~len] returns [true] iff the substring of - [sub] starting at position [i] and of length [len] is a substring +(** [is_sub ~sub i s j ~sub_len] returns [true] iff the substring of + [sub] starting at position [i] and of length [sub_len] is a substring of [s] starting at position [j]. *) val repeat : string -> int -> string