module PathMap : Map.S with type key = Path.t = Map.Make (struct
type t = Path.t
let compare p1 p2 = String.compare (Path.to_string p1) (Path.to_string p2)
end)
type t = {
stems: Path.t list;
stem_map: (string * Str.regexp) list PathMap.t;
}
let empty = { stems = []; stem_map = PathMap.empty }
let stems matcher = matcher.stems
let path_stem =
let wc = Str.regexp "^[^*?]*[*?]" in
fun path ->
let path =
if Path.file_exists path && not (Path.is_directory path) then
Path.parent path
else
path
in
let path_str = Path.to_string path in
let stem =
if Str.string_match wc path_str 0 then
Filename.dirname (Str.matched_string path_str)
else
path_str
in
Path.make stem
let path_patt =
let star = Str.regexp_string "*" in
let star2 = Str.regexp_string "**" in
let qmark = Str.regexp_string "?" in
fun path ->
let str = Path.to_string path |> Sys_utils.normalize_filename_dir_sep in
let results = Str.full_split star2 str in
let results =
Base.List.map
~f:(fun r ->
match r with
| Str.Text s ->
let s = Str.global_replace star "[^/]*" s in
Str.global_replace qmark "." s
| Str.Delim _ -> ".*")
results
in
let str = String.concat "" results in
Str.regexp str
let dir_sep = Str.regexp_string Filename.dir_sep
let fixup_path p =
let s = Path.to_string p in
let is_normalized =
match Sys_utils.realpath s with
| Some s' -> s' = s
| None -> false
in
if is_normalized then
p
else
let abs = not (Filename.is_relative s) in
let entries = Str.split_delim dir_sep s in
let rec loop revbase entries =
match entries with
| h :: t when h = Filename.current_dir_name -> loop revbase t
| h :: t when h = Filename.parent_dir_name ->
(match revbase with
| _ :: rt -> loop rt t
| _ -> loop (h :: revbase) t)
| h :: t -> loop (h :: revbase) t
| [] -> List.rev revbase
in
let entries = loop [] entries in
let s = List.fold_left Filename.concat "" entries in
let s =
if abs then
Filename.dir_sep ^ s
else
s
in
Path.make s
let add { stems; stem_map } path =
let path = fixup_path path in
let stem = path_stem path in
let patt = path_patt path in
let pstr = Path.to_string path in
let (stems, stem_map) =
match PathMap.find_opt stem stem_map with
| None ->
let stem_map = PathMap.add stem [(pstr, patt)] stem_map in
(stem :: stems, stem_map)
| Some entries ->
let stem_map = PathMap.add stem ((pstr, patt) :: entries) stem_map in
(stems, stem_map)
in
{ stems; stem_map }
let find_prefixes f =
List.filter (fun prefix -> String_utils.string_starts_with f (Path.to_string prefix))
let rec match_patt f = function
| [] -> None
| (path, patt) :: _ when Str.string_match patt f 0 -> Some path
| (_, _) :: t -> match_patt f t
let matches path_matcher f =
let matching_stems = find_prefixes f path_matcher.stems in
let normalized_f = Sys_utils.normalize_filename_dir_sep f in
List.exists
(fun stem ->
let patts = PathMap.find stem path_matcher.stem_map in
match_patt normalized_f patts != None)
matching_stems