1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
include Stdlib.Sys
let mkdir ?(perm = 0o755) dirname = Unix.mkdir dirname perm
let rec mkdir_p ?perm dir =
match dir with
| "." | ".." ->
()
| _ ->
let mkdir_idempotent ?(perm = 0o755) dir =
match Unix.mkdir dir perm with
| () ->
()
| (exception Unix.Unix_error ((EEXIST | EISDIR), _, _))
| (exception Sys_error _) ->
()
in
(match mkdir_idempotent ?perm dir with
| () ->
()
| exception (Unix.Unix_error (ENOENT, _, _) as exn) ->
let parent = Filename.dirname dir in
if String.equal parent dir then
raise exn
else (
mkdir_p ?perm parent;
mkdir_idempotent ?perm dir))
let rec rm_p path =
match is_directory path with
| true ->
readdir path |> Array.iter (fun name -> rm_p (Filename.concat path name));
Unix.rmdir path
| false ->
remove path
let rand_digits () =
let rand = Random.State.(bits (make_self_init ()) land 0xFFFFFF) in
Printf.sprintf "%06x" rand
let mk_temp_dir ?(mode = 0o700) ?dir pat =
let dir = match dir with Some d -> d | None -> Filename.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 = Printf.sprintf "%s/%s%s" dir pat (rand_digits ()) in
try
mkdir dir ~perm:mode;
dir
with
| Unix.Unix_error (Unix.EEXIST, _, _) ->
loop (count - 1)
| Unix.Unix_error (Unix.EINTR, _, _) ->
loop count
| Unix.Unix_error (e, _, _) ->
raise_err ("mk_temp_dir: " ^ Unix.error_message e)
in
loop 1000
let ls_dir ?(recursive = true) directory =
if recursive then
let rec loop result = function
| f :: fs when is_directory f ->
readdir f
|> Array.to_list
|> List.map (Filename.concat f)
|> List.append fs
|> loop result
| f :: fs ->
loop (f :: result) fs
| [] ->
result
in
loop [] [ directory ] |> List.rev
else
readdir directory |> Array.to_list |> List.map (Filename.concat directory)
let with_chdir dir f =
let old_cwd = getcwd () in
Unix.chdir dir;
let result = f () in
Unix.chdir old_cwd;
result
let write_file file content =
let oc = open_out file in
Printf.fprintf oc "%s" content;
close_out oc
let read_lines file : string list =
let ic = open_in file in
let try_read () = try Some (input_line ic) with End_of_file -> None in
let rec loop acc =
match try_read () with
| Some s ->
loop (s :: acc)
| None ->
close_in ic;
List.rev acc
in
loop []
let read_file file = String.concat "\n" (read_lines file)