let streamall dir = let handle = Unix.opendir dir in Stream.from (fun n -> let rec loop () = try Some (Unix.readdir handle) with End_of_file -> Unix.closedir handle; None in loop ()) let stream dir = Stream2.filter (function "." | ".." -> false | _ -> true) (streamall dir) let path_stream dir = Stream2.map (fun s -> dir ^ "/" ^ s) (stream dir) (** Return a stream of (path, stat) pairs resulting from a pre-order traversal of a directory tree. If follow=true the stat records contain the info for the target of a symbolic link, if false they contain the stat of the link itself. The traversal never follows symbolic links to directories. *) let rstream ?(follow=false) root = let stack = ref [root, stream root] in Stream.from (fun n -> let rec loop () = match !stack with (dir, strm) :: etc -> begin match Stream.peek strm with Some name -> let name = Stream.next strm in let path = dir ^ "/" ^ name in let stat = Unix.LargeFile.lstat path in if stat.Unix.LargeFile.st_kind = Unix.S_DIR then stack := (path, stream path) :: !stack; let stat = if follow && stat.Unix.LargeFile.st_kind = Unix.S_LNK then Unix.LargeFile.stat path else stat in Some (path, stat) | None -> stack := etc; loop () end | [] -> None in loop ()) (* We can't just use Stream.iter here because we must catch exceptions in f and close the handle. *) let iterall f dir = let handle = Unix.opendir dir in try let rec loop () = f (Unix.readdir handle); loop () in loop () with End_of_file -> Unix.closedir handle | exn -> Unix.closedir handle; raise exn let iter f dir = iterall (function "." | ".." -> () | name -> f name) dir let name_list dir = let lst = ref [] in Stream.iter (fun name -> lst := name :: !lst) (stream dir); List.rev !lst let path_list dir = List.map (fun name -> dir ^ "/" ^ name) (name_list dir) (* Recursively traverse a directory and call f for every path. *) let rec find ?(prune=(fun path -> false)) f path = let linfo = Unix.LargeFile.lstat path in (* If this is a link, we want to pass f the stat of what the link points to, not the link itself. *) let info = match linfo with {Unix.LargeFile.st_kind = Unix.S_LNK} -> (* Be careful of bad symlink *) (try Unix.LargeFile.stat path with _ -> linfo) | _ -> linfo in f path info; (* For the traversal, we use the info about the link itself to avoid infinite recursion. *) match linfo with {Unix.LargeFile.st_kind = kind} when kind = Unix.S_DIR -> iter (fun name -> let subdir = path ^ "/" ^ name in if not (prune subdir) then find ~prune:prune f subdir) path | _ -> () (* Like find, but instead of paths it passes a list of directory names to f and prune, deepest first. *) let rec find2 ?(prune=(fun stack -> false)) ?(stack=[]) f start = let path = start ^ "/" ^ (String.concat "/" (List.rev stack)) in let linfo = Unix.LargeFile.lstat path in let info = match linfo with {Unix.LargeFile.st_kind = Unix.S_LNK} -> (* Be careful of bad symlink *) (try Unix.LargeFile.stat path with _ -> linfo) | _ -> linfo in f stack info; match linfo with {Unix.LargeFile.st_kind = kind} when kind = Unix.S_DIR -> iter (fun name -> let stack = name :: stack in if not (prune stack) then find2 ~prune:prune ~stack:stack f start) path | _ -> () let rec make path perm = if Sys.file_exists path then () else begin make (Filename.dirname path) perm; Unix.mkdir path perm end