Module Kwfile

module Kwfile: sig .. end

File-Manipulation Functions

These don't do I/O in general, but include functions that read directories and perform stats.
Author(s): Keith Waclena



Pathname Manipulation


val filesplit : ?sep:string -> string -> string list
filesplit ?sep fn: split a filename into its components

Filename.dirname and Filename.basename have all sorts of wild boundary conditions!

This is "supposedly" better than a string split on "/" because we get a. automatic normalization and b. portability across different pathseps

sep : the path separator (default: Filename.dir_sep)
fn : the filename
val join : string -> string -> string
join dir file returns a file name that designates file file in directory dir.

This is what Filename.concat does, but that function is more literal than I want it to be, e.g. Filename.concat "." "/etc/motd" returns ".//etc/motd", which I think is weird; join "." "/etc/motd" instead returns "/etc/motd".
Returns "joined" filename

dir : directory pathname
val extsplit : string -> string * string option
extsplit path splits a file path into two parts by separating the extension (if any).

Returns a pair, the fst of which is the path with the extension removed, and the snd of which is the extension as a string option.
Returns the pair, front part and optional extension

path : a pathname
val extjoin : string * string option -> string
extjoin path joins an optional extension onto a file path; the dual of Kwfile.extsplit.

So, p = extjoin (extsplit p). Useful for changing an extension:

    # Kwfile.extsplit "foo.xml" |> (id &&& k (Some "jpg")) |> extjoin;;
    \- : string = "foo.jpg"
    # Kwfile.extsplit "foo.xml" |> (id &&& k None) |> extjoin;;
    \- : string = "foo"
    #
 

Returns the new path formed my joining the extension onto the incoming path
pair : a pathname and extension option, as returned by Kwfile.extsplit
val extension : string -> string
extension file returns the file's extension, without the '.' if no extension, return ""
Returns the extension
val squiggle : ?respect_env:bool -> string -> string
expand leading "~" in pathname according to traditional Unix semantics For leading "~/" the "HOME" env var is respected unless ~respect_env is false
Returns possibly expanded file name
respect_env : respect (i.e. trust) the value of env dir "HOME"? (default: true)
fn : file name
val (~~) : ?respect_env:bool -> string -> string
(~~): synonym for Kwfile.squiggle.
val makesafe : string -> string
makesafe path returns a "safe" version of the pathname (intended for web applications).

"Safe" means all instances of ".", ".." and any leading "/"'s are eliminated. "Safe" does not mean that any shell metacharacters are eliminated! This function assumes you will be using system calls on the resulting "safe" pathname, not passing it to a shell.


Directories

Including folding over directories AKA emulating find(1).
val mkdirs : ?mode:Unix.file_perm -> string -> unit
mkdirs ?mode path: make all the directories in path (including parent directories, as needed).

Like the shell's mkdir -p. No error if any of the directories exist.

mode : the mode of newly-created directories
path : the path of the new directory
val fold_dir : ('a -> string -> 'a) -> 'a -> string -> 'a
fold_dir f init dir: fold over the files in a directory (not recursive).

Tail-recursive. Applies f acc fn to each file in dir (excluding . and ..); sub-directories are not recursed into.

f : function f acc filename to apply to each file
dir : the directory (or file, even) to fold over
val fold : ?follow:bool ->
?err:(int -> 'a -> string -> exn -> 'a) ->
(int -> 'a -> string -> 'a) -> 'a -> string -> 'a
fold ?follow ?err f acc dir: fold over a directory hierarchy (recursive).

NOT tail-recursive in depth of tree, but is tail-recursive in breadth of any given sub-directory.

follow : whether to descend into symlinked sub-directories (default: false, as per find(1))
err : alternate function err depth acc filename exn called (instead of f) if there's an exception exn associated with stat'ing or opendir'ing dir (default: re-raise exn)
f : function f depth acc filename to apply to each file
acc : the accumulator
dir : the directory (or file, even) to fold over

Combinators

Handy combinators for use with Kwfile.fold.

Examples:

    fold (igndepth (flip cons)) [] "."
    fold (maxdepth 2 (flip cons)) [] "."
   

val igndepth : 'a -> 'b -> 'a
igndepth f: makes a function that's suitable for List.fold_left suitable for Kwfile.fold by ignoring the depth parameter.

Use this if you don't care about how deep you are when you process a file.

val maxdepth : 'a -> ('b -> 'c -> 'b) -> 'a -> 'b -> 'c -> 'b
maxdepth n f: converts a function that's suitable for List.fold_left into a function suitable for Kwfile.fold that's only applied to files up to a maximum depth of n.
val pred : ('a -> 'b -> bool) -> ('a -> 'c -> 'b -> 'c) -> 'a -> 'c -> 'b -> 'c
pred p f: converts a function that's suitable for Kwfile.fold into one that that's only applied if p depth filename returns true.
val ignexn : ('a -> 'b -> 'c -> 'd) -> 'a -> 'b -> 'c -> 'e -> 'd
ignexn f: makes function that's suitable for Kwfile.fold's f parameter suitable for its ~err parameter by ignoring the exception.

Use this if you want to process un-stat-able files the same way as stat-able ones. For example, to simply count all files in a directory hierarchy, do:

    let count _ a _ = a+1 in fold ~err:(ignexn count) count 0
   

module Find: sig .. end
val glob : ?pred:(string -> bool) ->
(string -> string -> string -> 'a) -> string -> 'a list
Deprecated.You should use Kwfile.fold_dir.
glob ?pred map dir: return list of results of applying map to each file in a directory

NOT tail-recursive.
Returns list of "mapped" results of all matching files

pred : predicate (string -> bool) on a filename (default: (fun x -> true))
map : function (string -> string -> string -> 'a) to apply to each filename in result list; saves you a List.map over the result; parameters are directoryname, basename, and full path (i.e. (Filename.concat directoryname basename))
dir : directory to process

Predicates


val isemptydir : string -> bool
Is path (presumed a directory) an empty directory?

This is faster (and uses less space) than 0 = List.length (glob (fun _ _ _ -> ()) path) if the directory contains a lot of files.
Raises Unix.Unix_error (Unix.ENOTDIR, "opendir", ...) if path not a directory
Returns bool

path : path to a directory
val isfile : string -> bool
Is a file a plain file?
Returns bool
f : filename
val isdirectory : string -> bool
Is a file a directory?
Returns bool
f : filename
val ischaracter : string -> bool
Is a file a character special file?
Returns bool
f : filename
val isblock : string -> bool
Is a file a block special file?
Returns bool
f : filename
val islink : string -> bool
Is a file a symbolic link?
Returns bool
f : filename
val isfifo : string -> bool
Is a file a fifo?
Returns bool
f : filename
val issocket : string -> bool
Is a file a socket?
Returns bool
f : filename
val access : string -> Unix.access_permission list -> bool
Unix.access with bool range and no exceptions
Returns bool
f : filename
perms : Unix.access_permission list
val isreadable : string -> bool
Is a file readable?
Returns bool
f : filename
val iswriteable : string -> bool
Is a file writeable?
Returns bool
f : filename
val isexecutable : string -> bool
Is a file executable?
Returns bool
f : filename
val isexefile : string -> bool
Is a file an executable plain file?
Returns bool
f : filename
val exists : string -> bool
Does a file exist?
Returns bool
f : filename

Which Executable?


val which : string -> string list
Find occurances of potential executables in environment variable PATH

Lookup a (potential) executable name in (Sys.getenv "$PATH") and return all the candidates found, in PATH order.
Returns list of (basename,fullpath) pairs for each executable found

sought : name of the executable in question

Temporary Files


type tempfile = {
   filename : string;
   chan : Pervasives.out_channel;
}
The type of open tempfiles
val temp_file_name : ?tmpdir:string -> string -> string -> string
temp_file_name ?tmpdir prefix suffix: return a random filename
tmpdir : directory in which to make tmpfile (default: value of TMPDIR env var, else /tmp)
prefix : temp filename prefix
suffix : temp filename suffix
val open_temp_file : ?tmpdir:string ->
?mode:Pervasives.open_flag list ->
?perm:int -> string -> string -> string * Pervasives.out_channel
Like Filename.open_temp_file, but with ?tmpdir parameter to set the tempdir.
Returns a pair of (filename,out_channel)
tmpdir : directory in which to make tmpfile (default: value of TMPDIR env var, else /tmp)
mode : list of open modes
perm : file permissions (default: 0o600)
prefix : temp filename prefix
suffix : temp filename suffix
val tempfiles : ?n:int ->
?tmpdir:string ->
string -> string list -> (string, tempfile) Hashtbl.t
securely open many tempfiles, one for each name in a list of strings
Returns hashtable mapping handles to tempfile's

Example:

  let t = Kwfile.tempfiles "myself" ["foo"; "bar"] in
    Printf.fprintf (Hashtbl.find t "foo").Kwfile.chan "foo stuff";
    Printf.fprintf (Hashtbl.find t "bar").Kwfile.chan "bar stuff";
    Hashtbl.iter (fun _ {Kwfile.chan=c} -> close_out c) t;
    Hashtbl.iter (fun _ {Kwfile.filename=f} -> Sys.remove f) t;
  

n : initial size of hash table (default: 2)
tmpdir : directory in which to make tmpfile
prefix : string to prefix to each temp file name, typically something like (Filename.basename Sys.argv(0))
handles : list of tempfile "handles" (strings)
val with_temp_file : ?remove:(string -> unit) ->
?tmpdir:string -> string -> string -> (string -> 'a) -> 'a
with_temp_file ?remove ?tmpdir prefix suffix f: call f tmp where tmp is a newly-created temp file, making sure to remove the temp file after the evaluation of f.

If you want to examine the temp file for debugging purposes, you can use e..g. with_temp_file ~remove:ignore or to see the name of the temp file, something like: with_temp_file ~remove:(fun fn -> prerr_endline fn; Sys.remove fn).

If it's possible that f might not create the temp file, and you want to avoid a Sys_error "No such file or directory" exception, you can use e.g. with_temp_file ~remove:(ignex Sys.remove).

remove : function to remove the temp file (default: Sys.remove)
tmpdir : directory in which to make tmpfile (default: value of TMPDIR env var, else /tmp)
prefix : temp filename prefix
suffix : temp filename suffix
f : the function