summaryrefslogtreecommitdiffstats
path: root/ocamlbuild/slurp.ml
diff options
context:
space:
mode:
Diffstat (limited to 'ocamlbuild/slurp.ml')
-rw-r--r--ocamlbuild/slurp.ml186
1 files changed, 186 insertions, 0 deletions
diff --git a/ocamlbuild/slurp.ml b/ocamlbuild/slurp.ml
new file mode 100644
index 000000000..4446336e5
--- /dev/null
+++ b/ocamlbuild/slurp.ml
@@ -0,0 +1,186 @@
+(***********************************************************************)
+(* ocamlbuild *)
+(* *)
+(* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *)
+(* *)
+(* Copyright 2007 Institut National de Recherche en Informatique et *)
+(* en Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the Q Public License version 1.0. *)
+(* *)
+(***********************************************************************)
+
+(* $Id$ *)
+(* Original author: Berke Durak *)
+(* Slurp *)
+open My_std
+open Outcome
+
+type 'a entry =
+ | Dir of string * string * My_unix.stats Lazy.t * 'a * 'a entry list Lazy.t
+ | File of string * string * My_unix.stats Lazy.t * 'a
+ | Error of exn
+ | Nothing
+
+let (/) = filename_concat
+
+let rec filter predicate = function
+ | Dir(path, name, st, attr, entries) ->
+ if predicate path name attr then
+ Dir(path, name, st, attr, lazy (List.map (filter predicate) !*entries))
+ else
+ Nothing
+ | File(path, name, _, attr) as f ->
+ if predicate path name attr then
+ f
+ else
+ Nothing
+ | Nothing -> Nothing
+ | Error _ as e -> e
+
+let real_slurp path =
+ let cwd = Sys.getcwd () in
+ let abs x = if Filename.is_implicit x || Filename.is_relative x then cwd/x else x in
+ let visited = Hashtbl.create 1024 in
+ let rec scandir path names =
+ let (file_acc, dir_acc) =
+ Array.fold_left begin fun ((file_acc, dir_acc) as acc) name ->
+ match do_entry true path name with
+ | None -> acc
+ | Some((Dir _|Error _) as entry) -> (file_acc, entry :: dir_acc)
+ | Some((File _) as entry) -> (entry :: file_acc, dir_acc)
+ | Some Nothing -> acc
+ end
+ ([], [])
+ names
+ in
+ file_acc @ dir_acc
+ and do_entry link_mode path name =
+ let fn = path/name in
+ let absfn = abs fn in
+ match
+ try
+ Good(if link_mode then My_unix.lstat absfn else My_unix.stat absfn)
+ with
+ | x -> Bad x
+ with
+ | Bad x -> Some(Error x)
+ | Good st ->
+ let key = st.My_unix.stat_key in
+ if try Hashtbl.find visited key with Not_found -> false
+ then None
+ else
+ begin
+ Hashtbl.add visited key true;
+ let res =
+ match st.My_unix.stat_file_kind with
+ | My_unix.FK_link ->
+ let fn' = My_unix.readlink absfn in
+ if sys_file_exists (abs fn') then
+ do_entry false path name
+ else
+ Some(File(path, name, lazy st, ()))
+ | My_unix.FK_dir ->
+ (match sys_readdir absfn with
+ | Good names -> Some(Dir(path, name, lazy st, (), lazy (scandir fn names)))
+ | Bad exn -> Some(Error exn))
+ | My_unix.FK_other -> None
+ | My_unix.FK_file -> Some(File(path, name, lazy st, ())) in
+ Hashtbl.replace visited key false;
+ res
+ end
+ in
+ match do_entry true "" path with
+ | None -> raise Not_found
+ | Some entry -> entry
+
+let split path =
+ let rec aux path =
+ if path = Filename.current_dir_name then []
+ else (Filename.basename path) :: aux (Filename.dirname path)
+ in List.rev (aux path)
+
+let rec join =
+ function
+ | [] -> assert false
+ | [x] -> x
+ | x :: xs -> x/(join xs)
+
+let rec add root path entries =
+ match path, entries with
+ | [], _ -> entries
+ | xpath :: xspath, (Dir(dpath, dname, dst, dattr, dentries) as d) :: entries ->
+ if xpath = dname then
+ Dir(dpath, dname, dst, dattr, lazy (add (root/xpath) xspath !*dentries)) :: entries
+ else d :: add root path entries
+ | [xpath], [] ->
+ [File(root, xpath, lazy (My_unix.stat (root/xpath)), ())]
+ | xpath :: xspath, [] ->
+ [Dir(root/(join xspath), xpath,
+ lazy (My_unix.stat (root/(join path))), (),
+ lazy (add (root/xpath) xspath []))]
+ | _, Nothing :: entries -> add root path entries
+ | _, Error _ :: _ -> entries
+ | [xpath], (File(_, fname, _, _) as f) :: entries' ->
+ if xpath = fname then entries
+ else f :: add root path entries'
+ | xpath :: xspath, (File(fpath, fname, fst, fattr) as f) :: entries' ->
+ if xpath = fname then
+ Dir(fpath, fname, fst, fattr, lazy (add (root/xpath) xspath [])) :: entries'
+ else f :: add root path entries'
+
+let slurp_with_find path =
+ let lines =
+ My_unix.run_and_open (Printf.sprintf "find %s" (Filename.quote path)) begin fun ic ->
+ let acc = ref [] in
+ try while true do acc := input_line ic :: !acc done; []
+ with End_of_file -> !acc
+ end in
+ let res =
+ List.fold_right begin fun line acc ->
+ add path (split line) acc
+ end lines [] in
+ match res with
+ | [] -> Nothing
+ | [entry] -> entry
+ | entries -> Dir(path, Filename.basename path, lazy (My_unix.stat path), (), lazy entries)
+
+let slurp x = if !*My_unix.is_degraded then slurp_with_find x else real_slurp x
+
+let rec print print_attr f entry =
+ match entry with
+ | Dir(path, name, _, attr, entries) ->
+ Format.fprintf f "@[<2>Dir(%S,@ %S,@ _,@ %a,@ %a)@]"
+ path name print_attr attr (List.print (print print_attr)) !*entries
+ | File(path, name, _, attr) ->
+ Format.fprintf f "@[<2>File(%S,@ %S,@ _,@ %a)@]" path name print_attr attr
+ | Nothing ->
+ Format.fprintf f "Nothing"
+ | Error(_) ->
+ Format.fprintf f "Error(_)"
+
+let rec fold f entry acc =
+ match entry with
+ | Dir(path, name, _, attr, contents) ->
+ f path name attr (List.fold_right (fold f) !*contents acc)
+ | File(path, name, _, attr) ->
+ f path name attr acc
+ | Nothing | Error _ -> acc
+
+let map f entry =
+ let rec self entry =
+ match entry with
+ | Dir(path, name, st, attr, contents) ->
+ Dir(path, name, st, f path name attr, lazy (List.map self !*contents))
+ | File(path, name, st, attr) ->
+ File(path, name, st, f path name attr)
+ | Nothing -> Nothing
+ | Error e -> Error e
+ in self entry
+
+let rec force =
+ function
+ | Dir(_, _, st, _, contents) ->
+ let _ = !*st in List.iter force !*contents
+ | File(_, _, st, _) ->
+ ignore !*st
+ | Nothing | Error _ -> ()