(* Slurp *)
(* $Id: slurp.ml,v 1.1 2004/07/20 14:17:04 berke Exp $ *)

open Unix

type entry =
| File of string * file_info
| Directory of string * entry list
| Error of string * exn
and file_info = {
  file_size : int;
}

let with_chdir_to path f =
  let cwd = Sys.getcwd () in
  try
    Sys.chdir path;
    let r = f () in
    Sys.chdir cwd;
    r
  with
  | x ->
      Sys.chdir cwd;
      raise x

let slurp path =
  with_chdir_to path (fun () ->
    let marked = Hashtbl.create 256 in
    let rec examine path =
      let d = opendir path in
      Sys.chdir path;
      let r = ref [] in
      try
        while true do
          let fn = readdir d in
          match fn with
          | "."|".." -> ()
          | x ->
            try
              let st = lstat fn in
              match st.st_kind with
              | S_REG ->
                  if not (Hashtbl.mem marked st.st_ino) then
                    begin
                      Hashtbl.add marked st.st_ino true;
                      r := (File(fn,{ file_size = st.st_size }))::!r
                    end
              | S_DIR ->
                  r := (Directory(fn,examine (Filename.concat path fn)))::!r;
                  Sys.chdir path
              | _ -> ()
            with
            | y -> r := (Error(fn,y))::!r
        done;
        assert false
      with
      | End_of_file ->
          closedir d;
          !r
      | x ->
          closedir d;
          raise x
    in
    Directory(path,examine path))
