3 module Array = ArrayLabels
4 module List = ListLabels
8 val lines : in_channel -> string t
9 val rec_file_paths : root:string -> string t
10 val iter : 'a t -> f:('a -> unit) -> unit
17 let rec_file_paths ~root =
18 let dirs = Queue.create () in
19 let files = Queue.create () in
22 Array.iter (Sys.readdir parent) ~f:(fun child ->
23 let path = Filename.concat parent child in
24 let {Unix.st_kind = file_kind; _} = Unix.lstat path in
39 match Queue.take dirs with
40 | exception Queue.Empty ->
46 match Queue.take files with
47 | exception Queue.Empty ->
59 match input_line ic with
60 | exception End_of_file ->
77 | Paths_on_stdin -> Stream.lines stdin
78 | Root_path root -> Stream.rec_file_paths ~root
80 let paths_by_digest = Hashtbl.create 1_000_000 in
81 Stream.iter paths ~f:(fun path ->
83 let digest = Digest.file path in
85 match Hashtbl.find_opt paths_by_digest digest with
91 Hashtbl.replace paths_by_digest digest (path :: paths)
93 eprintf "WARNING: Failed to process %S: %S\n%!" path e
97 let n_paths = List.length paths in
98 if n_paths > 1 then begin
99 printf "%s %d\n%!" (Digest.to_hex digest) n_paths;
100 List.iter paths ~f:(fun path -> printf " %s\n%!" path)
106 let input = ref Paths_on_stdin in
107 Arg.parse [] (fun path -> input := Root_path path) "";