module Array = ArrayLabels
module List = ListLabels
+module StrSet= Set.Make(String)
module Stream : sig
type 'a t
val create : (unit -> 'a option) -> 'a t
val iter : 'a t -> f:('a -> unit) -> unit
+
+ val concat : ('a t) list -> 'a t
end = struct
module S = Stream
type 'a t =
- 'a S.t
+ ('a S.t) list
let create f =
- S.from (fun _ -> f ())
+ [S.from (fun _ -> f ())]
let iter t ~f =
- S.iter f t
+ List.iter t ~f:(S.iter f)
+
+ let concat ts =
+ List.concat ts
end
module In_channel : sig
)
end
-module Directory : sig
+module Directory_tree : sig
val find_files : string -> string Stream.t
end = struct
let find_files root =
end
type input =
- | Root_path of string
+ | Root_paths of string list
| Paths_on_stdin
let main input =
let paths =
match input with
- | Paths_on_stdin -> In_channel.lines stdin
- | Root_path root -> Directory.find_files root
+ | Paths_on_stdin ->
+ In_channel.lines stdin
+ | Root_paths paths ->
+ let paths = StrSet.elements (StrSet.of_list paths) in
+ Stream.concat (List.map paths ~f:Directory_tree.find_files)
in
let paths_by_digest = Hashtbl.create 1_000_000 in
let path_count = ref 0 in
let paths =
match Hashtbl.find_opt paths_by_digest digest with
| None ->
- []
+ StrSet.empty
| Some paths ->
paths
in
- Hashtbl.replace paths_by_digest digest (path :: paths)
+ Hashtbl.replace paths_by_digest digest (StrSet.add path paths)
with Sys_error e ->
eprintf "WARNING: Failed to process %S: %S\n%!" path e
);
Hashtbl.iter
(fun digest paths ->
- let n_paths = List.length paths in
+ let n_paths = StrSet.cardinal paths in
if n_paths > 1 then begin
printf "%s %d\n%!" (Digest.to_hex digest) n_paths;
- List.iter paths ~f:(fun path -> printf " %s\n%!" path)
+ List.iter (StrSet.elements paths) ~f:(printf " %S\n%!")
end
)
paths_by_digest;
[]
(function
| path when Sys.file_exists path ->
- input := Root_path path
+ (match !input with
+ | Paths_on_stdin ->
+ input := Root_paths [path]
+ | Root_paths paths ->
+ input := Root_paths (path :: paths)
+ )
| path ->
eprintf "File does not exist: %S\n%!" path;
exit 1