9. Directories

Introduction

open Unix 

(* handle_unix_error generates a nice error message and exits *)
let entry = handle_unix_error stat "/usr/bin/vi"
let entry = handle_unix_error stat "/usr/bin/"
let entry = handle_unix_error fstat filedescr

(* without handle_unix_error an exception is raised for errors *)
let inode = stat "/usr/bin/vi"
let ctime = inode.st_ctime
let size = inode.st_size

(* don't know any equivalent in ocaml *)
(* maybe one could use file(1) (to know if it is an ASCII text file) *)
let dirhandle = handle_unix_error opendir "/usr/bin" in
begin
  try
    while true do
      let file = readdir dirhandle in
      Printf.printf "Inside /usr/bin is something called %s\n" file
    done
  with
    | End_of_file -> ()
end;
closedir dirhandle;;

Getting and Setting Timestamps

let (readtime, writetime) =
  let inode = stat filename in
  (inode.st_atime, inode.st_mtime);;

utimes filename newreadtime newwritetime;;

(******************)

let second_per_day = 60. *. 60. *. 24. in
let (atime, mtime) =
  let inode = stat filename in
  (inode.st_atime, inode.st_mtime) in
let newreadtime = atime -. 7. *. second_per_day
and newwritetime = mtime -. 7. *. second_per_day in
try 
  utimes filename newreadtime newwritetime 
with
  | Unix_error (er,_,_) ->
      Printf.eprintf 
        "couldn't backdate %s by a week w/ utime: %s\n"
        filename (error_message er);;

(****************)
let mtime = (stat file).st_mtime in
utimes file (time ()) mtime  ;;

(***************)

(* compile with ocamlc unix.cma uvi.ml -o uvi *)
open Unix

let main () =
  if (Array.length Sys.argv <> 2)
  then
    Printf.eprintf "Usage: uvi filename\n";
  let filename = Sys.argv.(1) in
  let atime,mtime = 
    let st = stat filename in
    (st.st_atime, st.st_mtime) in
  let editor =
    begin
      try
        Sys.getenv "editor"
      with
        | Not_found -> "vi"
    end in
  Sys.command (Printf.sprintf "%s %s" editor filename);
  utimes filename atime mtime in
main ();;
  
(*****************)

Deleting a File

unlink filename;;                       (* use unix library *)
Sys.remove filename;;                   (* in the standard library *)

let error_flag = ref(None) in
let local_unlink filename =
  try
    unlink filename
  with
    | Unix_error (er,_,_) -> 
        error_flag := (Some er) in
List.iter local_unlink filenames;
match !error_flag with
  | Some er ->
      Printf.eprintf "Couldn't unlink all of";
      List.iter (Printf.eprintf " %s") filenames;
      Printf.eprintf ": %s\n" (error_message er)
  | None ();;


(****************)

let error_flag = ref(0) in
let local_unlink count filename =
  try
    unlink filename;
    count + 1
  with
    | Unix_error (er,_,_) -> 
        count in
let count = (List.fold_left local_unlink filenames 0) 
and len = List.length filenames in
if count <> len
then
  Printf.eprintf "Could only delete %i of %i file\n" count len;;

(****************)

Copying or Moving a File

(****************)

(* Note : this doesn't use the unix library, only the standard one *)

let copy oldfile newfile =
  let infile = open_in oldfile
  and outfile = open_out newfile
  and blksize = 16384 in
  let buf = String.create blksize in
  let rec real_copy () =
    let byte_read = input infile buf 0 blksize in
    if byte_read <> 0 then
      begin
        (* Handle partialle write : nothing to do *)
        output outfile buf 0 byte_read;
        real_copy ()
      end in
  real_copy ();
  close_in infile;
  close_out outfile;;

(****************)
Sys.command ("cp " ^ oldfile ^ " " ^ newfile)   (* Unix *)
Sys.command (String.concat " " ["copy";oldfile;newfile]) (* Dos *)

(****************)

Unix.copy "datafile.dat" "datafile.bak";;

Sys.rename "datafile.dat" "datafile.bak";;

(***************)

Recognizing Two Names for the Same File

#load "unix.cma";;

(* Count the number of times a (dev, ino) pair is seen. *)
let seen = Hashtbl.create 0
let do_my_thing filename =
  let {Unix.st_dev=dev; st_ino=ino} = Unix.stat filename in
  Hashtbl.replace seen (dev, ino)
    (try Hashtbl.find seen (dev, ino) + 1
     with Not_found -> 1);
  if Hashtbl.find seen (dev, ino) = 1
  then
    begin
      (* do something with filename because we haven't
         seen it before. *)
    end

(*-----------------------------*)

(* Maintain a list of files for each (dev, ino) pair. *)
let seen = Hashtbl.create 0
let () =
  List.iter
    (fun filename ->
       let {Unix.st_dev=dev; st_ino=ino} = Unix.stat filename in
       Hashtbl.replace seen (dev, ino)
         (try filename :: Hashtbl.find seen (dev, ino)
          with Not_found -> [filename]))
    files
let () =
  Hashtbl.iter
    (fun (dev, ino) filenames ->
       Printf.printf "(%d, %d) => [%s]\n"
         dev ino (String.concat ", " filenames))
    seen

Processing All Files in a Directory

Globbing, or Getting a List of Filenames Matching a Pattern

#load "str.cma";;

(* OCaml does not come with a globbing function. As a workaround, the
   following function builds a regular expression from a glob pattern.
   Only the '*' and '?' wildcards are recognized. *)
let regexp_of_glob pat =
  Str.regexp
    (Printf.sprintf "^%s$"
       (String.concat ""
          (List.map
             (function
                | Str.Text s -> Str.quote s
                | Str.Delim "*" -> ".*"
                | Str.Delim "?" -> "."
                | Str.Delim _ -> assert false)
             (Str.full_split (Str.regexp "[*?]") pat))))

(* Now we can build a very basic globber. Only the filename part will
   be used in the glob pattern, so directory wildcards will break in
   this simple example. *)
let glob pat =
  let basedir = Filename.dirname pat in
  let files = Sys.readdir basedir in
  let regexp = regexp_of_glob (Filename.basename pat) in
  List.map
    (Filename.concat basedir)
    (List.filter
       (fun file -> Str.string_match regexp file 0)
       (Array.to_list files))

(* Find all data files in the pleac directory. *)
let files = glob "pleac/*.data"

(*-----------------------------*)

(* Find and sort directories with numeric names. *)
let dirs =
  List.map snd                             (* extract pathnames *)
    (List.sort compare                     (* sort names numerically *)
       (List.filter                        (* path is a dir *)
          (fun (_, s) -> Sys.is_directory s)
          (List.map                        (* form (name, path) *)
             (fun s -> (int_of_string s, Filename.concat path s))
             (List.filter                  (* just numerics *)
                (fun s ->
                   try ignore (int_of_string s); true
                   with _ -> false)
                (Array.to_list
                   (Sys.readdir path)))))) (* all files *)

Processing All Files in a Directory Recursively

Removing a Directory and Its Contents

Renaming Files

Splitting a Filename into Its Component Parts

Program: symirror

Program: lst