(******************************************************************************) (* ocaml-fileutils: files and filenames common operations *) (* *) (* Copyright (C) 2003-2014, Sylvain Le Gall *) (* *) (* This library is free software; you can redistribute it and/or modify it *) (* under the terms of the GNU Lesser General Public License as published by *) (* the Free Software Foundation; either version 2.1 of the License, or (at *) (* your option) any later version, with the OCaml static compilation *) (* exception. *) (* *) (* This library is distributed in the hope that it will be useful, but *) (* WITHOUT ANY WARRANTY; without even the implied warranty of *) (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file *) (* COPYING for more details. *) (* *) (* You should have received a copy of the GNU Lesser General Public License *) (* along with this library; if not, write to the Free Software Foundation, *) (* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *) (******************************************************************************) (** POSIX utilities for files and directories. A module to provide the core POSIX utilities to manipulate files and directories. All functions try to mimic common POSIX utilities but are written in pure OCaml. @author Sylvain Le Gall *) open FilePath (*********************************************************************) (** {2 Types and exceptions } *) exception FileDoesntExist of filename exception RecursiveLink of filename (** Generic error handling functions. Whenever such a function is available it helps report the error and allows to raise an exception. The [string] provided is the human readable version of ['a]. In most cases ['a] is a polymorphic variant. *) type 'a error_handler = string -> 'a -> unit (** Exception raised when after an [error_handler] the execution cannot continue. The rest of the workflow logic cannot handle the default case and the whole operation can be in the middle of transformation. *) exception Fatal of string (** Policy concerning links which are directories. *) type action_link = | Follow (** We consider link as simple directory (it is dangerous) *) | Skip (** Just skip it *) | SkipInform of (filename -> unit) (** Skip and execute an action *) | AskFollow of (filename -> bool) (** Ask and wait for input, false means skip *) (** For certain command, you should need to ask the user wether or not he wants to act. *) type interactive = | Force (** Do it anyway *) | Ask of (filename -> bool) (** Promp the user *) (*********************************************************************) (** {2 Permission } *) (** Base permission. This is the permission corresponding to one user or group. *) type base_permission = { sticky: bool; exec: bool; write: bool; read: bool; } (** Full permission. All the base permissions of a file. *) type permission = { user: base_permission; group: base_permission; other: base_permission; } (** Translate POSIX integer permission. *) val permission_of_int: int -> permission (** Return the POSIX integer permission *) val int_of_permission: permission -> int (** Permission symbolic mode. *) module Mode: sig type who = [`User | `Group | `Other | `All] type wholist = [ who | `List of who list ] type permcopy = [`User | `Group | `Other] type perm = [ `Read | `Write | `Exec | `ExecX | `Sticky | `StickyO ] type permlist = [ perm | `List of perm list ] type actionarg = [ permlist | permcopy ] type action = [ `Set of actionarg | `Add of actionarg | `Remove of actionarg] type actionlist = [ action | `List of action list ] type clause = [ `User of actionlist | `Group of actionlist | `Other of actionlist | `All of actionlist | `None of actionlist ] (** Typical symbolic mode: - g+r -> [`Group (`Add `Read)] - u=rw,g+rw,o-rwx -> [`User (`Set (`List [`Read; `Write])); `Group (`Add (`List [`Read; `Write])); `Other (`Remove (`List [`Read; `Write; `Exec]))] *) type t = clause list end (*********************************************************************) (** {2 Size operation} *) (** File size *) type size = TB of int64 (** Tera bytes *) | GB of int64 (** Giga bytes *) | MB of int64 (** Mega bytes *) | KB of int64 (** Kilo bytes *) | B of int64 (** Bytes *) (** Convert size to bytes. *) val byte_of_size: size -> int64 (** Add two sizes. *) val size_add: size -> size -> size (** Compare two sizes, using the classical compare function. If fuzzy is set to true, the comparison is done on the most significant size unit of both value. *) val size_compare: ?fuzzy:bool -> size -> size -> int (** Convert a value to a string representation. If fuzzy is set to true, only consider the most significant unit *) val string_of_size: ?fuzzy:bool -> size -> string (*********************************************************************) (** {2 stat } *) (** Kind of file. This set is a combination of all POSIX file, some of them doesn't exist at all on certain file system or OS. *) type kind = Dir | File | Dev_char | Dev_block | Fifo | Socket | Symlink (** @since 0.4.6 *) (** Information about a file. This type is derived from Unix.stat *) type stat = { kind: kind; is_link: bool; permission: permission; size: size; owner: int; group_owner: int; access_time: float; modification_time: float; creation_time: float; device: int; inode: int; } (** [stat fln] Return information about the file (like Unix.stat) Non POSIX command. *) val stat: ?dereference:bool -> filename -> stat (*********************************************************************) (** {2 umask } *) exception UmaskError of string (** Possible umask errors. *) type umask_error = [ `Exc of exn | `NoStickyBit of int ] (** Get or set the file mode creation mask. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/umask.html}POSIX documentation}. *) val umask: ?error:(umask_error error_handler) -> ?mode:[< `Octal of int | `Symbolic of Mode.t ] -> [< `Octal of int -> 'a | `Symbolic of Mode.t -> 'a] -> 'a (** Apply umask to a given file permission. *) val umask_apply: int -> int (*********************************************************************) (** {2 test } *) (** Pattern you can use to test file. If the file doesn't exist the result is always false. *) type test_file = | Is_dev_block (** FILE is block special *) | Is_dev_char (** FILE is character special *) | Is_dir (** FILE is a directory *) | Exists (** FILE exists *) | Is_file (** FILE is a regular file *) | Is_set_group_ID (** FILE is set-group-ID *) | Has_sticky_bit (** FILE has its sticky bit set *) | Is_link (** FILE is a symbolic link *) | Is_pipe (** FILE is a named pipe *) | Is_readable (** FILE is readable *) | Is_writeable (** FILE is writeable *) | Size_not_null (** FILE has a size greater than zero *) | Size_bigger_than of size (** FILE has a size greater than given size *) | Size_smaller_than of size (** FILE has a size smaller than given size *) | Size_equal_to of size (** FILE has the same size as given size *) | Size_fuzzy_equal_to of size (** FILE has approximatively the same size as given size *) | Is_socket (** FILE is a socket *) | Has_set_user_ID (** FILE its set-user-ID bit is set *) | Is_exec (** FILE is executable *) | Is_owned_by_user_ID (** FILE is owned by the effective user ID *) | Is_owned_by_group_ID (** FILE is owned by the effective group ID *) | Is_newer_than of filename (** FILE1 is newer (modification date) than FILE2 *) | Is_older_than of filename (** FILE1 is older than FILE2 *) | Is_newer_than_date of float (** FILE is newer than given date *) | Is_older_than_date of float (** FILE is older than given date *) | And of test_file * test_file (** Result of TEST1 and TEST2 *) | Or of test_file * test_file (** Result of TEST1 or TEST2 *) | Not of test_file (** Result of not TEST *) | Match of string (** Compilable match (Str or PCRE or ...) *) | True (** Always true *) | False (** Always false *) | Has_extension of extension (** Check extension *) | Has_no_extension (** Check absence of extension *) | Is_parent_dir (** Basename is the parent dir *) | Is_current_dir (** Basename is the current dir *) | Basename_is of filename (** Check the basename *) | Dirname_is of filename (** Check the dirname *) | Custom of (filename -> bool) (** Custom operation on filename *) (** Test a file. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/test.html}POSIX documentation}. *) val test: ?match_compile:(filename -> filename -> bool) -> test_file -> filename -> bool (*********************************************************************) (** {2 chmod } *) exception ChmodError of string (** Possible chmod errors. *) type chmod_error = [`Exc of exn] (** Change permissions of files. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/chmod.html}POSIX documentation}. *) val chmod: ?error:(chmod_error error_handler) -> ?recurse:bool -> [< `Octal of Unix.file_perm | `Symbolic of Mode.t ] -> filename list -> unit (*********************************************************************) (** {2 mkdir } *) exception MkdirError of string (** Possible mkdir errors. *) type mkdir_error = [ `DirnameAlreadyUsed of filename | `Exc of exn | `MissingComponentPath of filename | `MkdirChmod of filename * Unix.file_perm * string * chmod_error ] (** Create the directory which name is provided. Set [~parent] to true if you also want to create every directory of the path. Use mode to provide some specific right. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/mkdir.html}POSIX documentation}. *) val mkdir: ?error:(mkdir_error error_handler) -> ?parent:bool -> ?mode:[< `Octal of Unix.file_perm | `Symbolic of FileUtilMode.t ] -> filename -> unit (*********************************************************************) (** {2 rm } *) exception RmError of string (** Possible rm errors. *) type rm_error = [ `DirNotEmpty of filename | `Exc of exn | `NoRecurse of filename ] (** Remove the filename provided. Set [~recurse] to true in order to completely delete a directory. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/rm.html}POSIX documentation}. *) val rm: ?error:(rm_error error_handler) -> ?force:interactive -> ?recurse:bool -> filename list -> unit (*********************************************************************) (** {2 cp } *) exception CpError of string (** Possible cp errors. *) type cp_error = [ `CannotChmodDstDir of filename * exn | `CannotCopyDir of filename | `CannotCopyFilesToFile of filename list * filename | `CannotCreateDir of filename * exn | `CannotListSrcDir of filename * exn | `CannotOpenDstFile of filename * exn | `CannotOpenSrcFile of filename * exn | `CannotRemoveDstFile of filename * exn | `DstDirNotDir of filename | `ErrorRead of filename * exn | `ErrorWrite of filename * exn | `Exc of exn | `NoSourceFile of filename | `PartialWrite of filename * int * int | `SameFile of filename * filename | `UnhandledType of filename * kind ] (** Copy the hierarchy of files/directory to another destination. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/cp.html}POSIX documentation}. *) val cp: ?follow:action_link -> ?force:interactive -> ?recurse:bool -> ?preserve:bool -> ?error:(cp_error error_handler) -> filename list -> filename -> unit (*********************************************************************) (** {2 mv } *) exception MvError of string (** Possible mv errors. *) type mv_error = [ `Exc of exn | `MvCp of filename * filename * string * cp_error | `MvRm of filename * string * rm_error | `NoSourceFile ] (** Move files/directories to another destination. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/mv.html}POSIX documentation}. *) val mv: ?error:(mv_error error_handler) -> ?force:interactive -> filename -> filename -> unit (*********************************************************************) (** {2 touch } *) (** Time for file *) type touch_time_t = | Touch_now (** Use Unix.gettimeofday *) | Touch_file_time of filename (** Get mtime of file *) | Touch_timestamp of float (** Use GMT timestamp *) (** Modify the timestamp of the given filename. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/touch.html}POSIX documentation}. If atime and mtime are not specified, they are both considered true. If only atime or mtime is sepcified, the other is false. @param atime modify access time. @param mtime modify modification time. @param create if file doesn't exist, create it, default true @param time what time to set, default Touch_now *) val touch: ?atime:bool -> ?mtime:bool -> ?create:bool -> ?time:touch_time_t -> filename -> unit (*********************************************************************) (** {2 ls } *) (** Apply a filtering pattern to a filename. *) val filter: test_file -> filename list -> filename list (** List the content of a directory. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/ls.html}POSIX documentation}. *) val ls: filename -> filename list (*********************************************************************) (** {2 Misc operations } *) (** Return the current dir. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/pwd.html}POSIX documentation}. *) val pwd: unit -> filename (** Resolve to the real filename removing symlink. Non POSIX command. *) val readlink: filename -> filename (** Try to find the executable in the PATH. Use environement variable PATH if none is provided. Non POSIX command. *) val which: ?path:filename list -> filename -> filename (** [cmp skip1 fln1 skip2 fln2] Compare files [fln1] and [fln2] starting at pos [skip1] [skip2] and returning the first octect where a difference occurs. Returns [Some -1] if one of the file is not readable or if it is not a file. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/cmp.html}POSIX documentation}. *) val cmp: ?skip1:int -> filename -> ?skip2:int -> filename -> int option (** [du fln_lst] Return the amount of space of all the file which are subdir of fln_lst. Also return details for each file scanned. See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/du.html}POSIX documentation}. *) val du: filename list -> size * (filename * size) list (** [find ~follow:fol tst fln exec accu] Descend the directory tree starting from the given filename and using the test provided. You cannot match [current_dir] and [parent_dir]. For every file found, the action [exec] is done, using the [accu] to start. For a simple file listing, you can use [find True "." (fun x y -> y :: x) []] See {{:http://pubs.opengroup.org/onlinepubs/007904875/utilities/find.html}POSIX documentation}. *) val find: ?follow:action_link -> ?match_compile:(filename -> filename -> bool) -> test_file -> filename -> ('a -> filename -> 'a) -> 'a -> 'a (** For future release: - [val pathchk: filename -> boolean * string], check whether file names are valid or portable - [val setfacl: filename -> permission -> unit], set file access control lists (UNIX + extended attribute) - [val getfacl: filename -> permission], get file access control lists ACL related function will be handled through a plugin system to handle at runtime which attribute can be read/write (i.e. Win32 ACL, NFS acl, Linux ACL -- or none). *)