|
Packit |
9ff65e |
(******************************************************************************)
|
|
Packit |
9ff65e |
(* ocaml-fileutils: files and filenames common operations *)
|
|
Packit |
9ff65e |
(* *)
|
|
Packit |
9ff65e |
(* Copyright (C) 2003-2014, Sylvain Le Gall *)
|
|
Packit |
9ff65e |
(* *)
|
|
Packit |
9ff65e |
(* This library is free software; you can redistribute it and/or modify it *)
|
|
Packit |
9ff65e |
(* under the terms of the GNU Lesser General Public License as published by *)
|
|
Packit |
9ff65e |
(* the Free Software Foundation; either version 2.1 of the License, or (at *)
|
|
Packit |
9ff65e |
(* your option) any later version, with the OCaml static compilation *)
|
|
Packit |
9ff65e |
(* exception. *)
|
|
Packit |
9ff65e |
(* *)
|
|
Packit |
9ff65e |
(* This library is distributed in the hope that it will be useful, but *)
|
|
Packit |
9ff65e |
(* WITHOUT ANY WARRANTY; without even the implied warranty of *)
|
|
Packit |
9ff65e |
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file *)
|
|
Packit |
9ff65e |
(* COPYING for more details. *)
|
|
Packit |
9ff65e |
(* *)
|
|
Packit |
9ff65e |
(* You should have received a copy of the GNU Lesser General Public License *)
|
|
Packit |
9ff65e |
(* along with this library; if not, write to the Free Software Foundation, *)
|
|
Packit |
9ff65e |
(* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *)
|
|
Packit |
9ff65e |
(******************************************************************************)
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
open FileUtilTypes
|
|
Packit |
9ff65e |
open FilePath
|
|
Packit |
9ff65e |
open FileUtilMisc
|
|
Packit |
9ff65e |
open FileUtilPermission
|
|
Packit |
9ff65e |
open FileUtilTOUCH
|
|
Packit |
9ff65e |
open FileUtilRM
|
|
Packit |
9ff65e |
open FileUtilSTAT
|
|
Packit |
9ff65e |
open FileUtilUMASK
|
|
Packit |
9ff65e |
open FileUtilMKDIR
|
|
Packit |
9ff65e |
open FileUtilCHMOD
|
|
Packit |
9ff65e |
open FileUtilTEST
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
exception CpError of string
|
|
Packit |
9ff65e |
exception CpSkip
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
type cp_error =
|
|
Packit |
9ff65e |
[ `CannotChmodDstDir of filename * exn
|
|
Packit |
9ff65e |
| `CannotCopyDir of filename
|
|
Packit |
9ff65e |
| `CannotCopyFilesToFile of filename list * filename
|
|
Packit |
9ff65e |
| `CannotCreateDir of filename * exn
|
|
Packit |
9ff65e |
| `CannotListSrcDir of filename * exn
|
|
Packit |
9ff65e |
| `CannotOpenDstFile of filename * exn
|
|
Packit |
9ff65e |
| `CannotOpenSrcFile of filename * exn
|
|
Packit |
9ff65e |
| `CannotRemoveDstFile of filename * exn
|
|
Packit |
9ff65e |
| `DstDirNotDir of filename
|
|
Packit |
9ff65e |
| `ErrorRead of filename * exn
|
|
Packit |
9ff65e |
| `ErrorWrite of filename * exn
|
|
Packit |
9ff65e |
| `Exc of exn
|
|
Packit |
9ff65e |
| `NoSourceFile of filename
|
|
Packit |
9ff65e |
| `PartialWrite of filename * int * int
|
|
Packit |
9ff65e |
| `SameFile of filename * filename
|
|
Packit |
9ff65e |
| `UnhandledType of filename * kind ]
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
let same_file st1 st2 =
|
|
Packit |
9ff65e |
st1.device = st2.device && st1.inode = st2.inode
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
let cp
|
|
Packit |
9ff65e |
?(follow=Skip)
|
|
Packit |
9ff65e |
?(force=Force)
|
|
Packit |
9ff65e |
?(recurse=false)
|
|
Packit |
9ff65e |
?(preserve=false)
|
|
Packit |
9ff65e |
?(error=(fun str _ -> raise (CpError str)))
|
|
Packit |
9ff65e |
fln_src_lst
|
|
Packit |
9ff65e |
fln_dst =
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
let herror, _ =
|
|
Packit |
9ff65e |
let spf fmt = Printf.sprintf fmt in
|
|
Packit |
9ff65e |
let exs () e =
|
|
Packit |
9ff65e |
match e with
|
|
Packit |
9ff65e |
| Unix.Unix_error(err, _, _) -> Unix.error_message err
|
|
Packit |
9ff65e |
| e -> Printexc.to_string e
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
handle_error_gen "cp" error
|
|
Packit |
9ff65e |
(function
|
|
Packit |
9ff65e |
| `CannotRemoveDstFile(fn_dst, e) ->
|
|
Packit |
9ff65e |
spf "Cannot remove destination file '%s': %a." fn_dst exs e
|
|
Packit |
9ff65e |
| `CannotOpenDstFile(fn_dst, e) ->
|
|
Packit |
9ff65e |
spf "Cannot open destination file '%s': %a." fn_dst exs e
|
|
Packit |
9ff65e |
| `CannotOpenSrcFile(fn_src, e) ->
|
|
Packit |
9ff65e |
spf "Cannot open source file '%s': %a." fn_src exs e
|
|
Packit |
9ff65e |
| `ErrorRead(fn_src, e) ->
|
|
Packit |
9ff65e |
spf "Error reading file '%s': %a." fn_src exs e
|
|
Packit |
9ff65e |
| `ErrorWrite(fn_dst, e) ->
|
|
Packit |
9ff65e |
spf "Error writing file '%s': %a." fn_dst exs e
|
|
Packit |
9ff65e |
| `PartialWrite(fn_dst, read, written) ->
|
|
Packit |
9ff65e |
spf
|
|
Packit |
9ff65e |
"Partial write to file '%s': %d read, %d written."
|
|
Packit |
9ff65e |
fn_dst
|
|
Packit |
9ff65e |
read
|
|
Packit |
9ff65e |
written
|
|
Packit |
9ff65e |
| `CannotCopyDir fn_src ->
|
|
Packit |
9ff65e |
spf "Cannot copy directory '%s' recursively." fn_src
|
|
Packit |
9ff65e |
| `DstDirNotDir fn_dst ->
|
|
Packit |
9ff65e |
spf "Destination '%s' is not a directory." fn_dst
|
|
Packit |
9ff65e |
| `CannotCreateDir(fn_dst, e) ->
|
|
Packit |
9ff65e |
spf "Cannot create directory '%s': %a." fn_dst exs e
|
|
Packit |
9ff65e |
| `CannotListSrcDir(fn_src, e) ->
|
|
Packit |
9ff65e |
spf "Cannot list directory '%s': %a." fn_src exs e
|
|
Packit |
9ff65e |
| `CannotChmodDstDir(fn_dst, e) ->
|
|
Packit |
9ff65e |
spf "'Cannot chmod directory %s': %a." fn_dst exs e
|
|
Packit |
9ff65e |
| `NoSourceFile fn_src ->
|
|
Packit |
9ff65e |
spf "Source file '%s' doesn't exist." fn_src
|
|
Packit |
9ff65e |
| `SameFile(fn_src, fn_dst) ->
|
|
Packit |
9ff65e |
spf "'%s' and '%s' are the same file." fn_src fn_dst
|
|
Packit |
9ff65e |
| `UnhandledType(fn_src, _) ->
|
|
Packit |
9ff65e |
spf "Cannot handle the type of kind for file '%s'." fn_src
|
|
Packit |
9ff65e |
| `CannotCopyFilesToFile(fn_src_lst, fn_dst) ->
|
|
Packit |
9ff65e |
spf "Cannot copy a list of files to another file '%s'." fn_dst
|
|
Packit |
9ff65e |
| #exc -> "")
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
let handle_error e =
|
|
Packit |
9ff65e |
herror ~fatal:false e;
|
|
Packit |
9ff65e |
raise CpSkip
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
let handle_exception f a h =
|
|
Packit |
9ff65e |
try
|
|
Packit |
9ff65e |
f a
|
|
Packit |
9ff65e |
with e ->
|
|
Packit |
9ff65e |
herror ~fatal:false (h e);
|
|
Packit |
9ff65e |
raise CpSkip
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
let copy_time_props st_src fln_dst =
|
|
Packit |
9ff65e |
if preserve then begin
|
|
Packit |
9ff65e |
touch
|
|
Packit |
9ff65e |
~time:(Touch_timestamp st_src.modification_time)
|
|
Packit |
9ff65e |
~mtime:true
|
|
Packit |
9ff65e |
~create:false
|
|
Packit |
9ff65e |
fln_dst;
|
|
Packit |
9ff65e |
touch
|
|
Packit |
9ff65e |
~time:(Touch_timestamp st_src.access_time)
|
|
Packit |
9ff65e |
~atime:true
|
|
Packit |
9ff65e |
~create:false
|
|
Packit |
9ff65e |
fln_dst;
|
|
Packit |
9ff65e |
end
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
|
|
Packit Service |
4ae7da |
let buffer = Bytes.make 1024 ' ' in
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
let cp_file st_src dst_exists fn_src fn_dst =
|
|
Packit |
9ff65e |
let mode = int_of_permission st_src.permission in
|
|
Packit |
9ff65e |
(* POSIX conditions: *)
|
|
Packit |
9ff65e |
(* 3a *)
|
|
Packit |
9ff65e |
let fd_dst =
|
|
Packit |
9ff65e |
(* 3ai *)
|
|
Packit |
9ff65e |
if dst_exists && doit force fn_dst then begin
|
|
Packit |
9ff65e |
try
|
|
Packit |
9ff65e |
(* 3aii *)
|
|
Packit |
9ff65e |
Unix.openfile fn_dst [Unix.O_WRONLY; Unix.O_TRUNC] mode
|
|
Packit |
9ff65e |
with _ ->
|
|
Packit |
9ff65e |
(* 3aii *)
|
|
Packit |
9ff65e |
handle_exception
|
|
Packit |
9ff65e |
(fun lst -> rm lst) [fn_dst]
|
|
Packit |
9ff65e |
(fun e -> `CannotRemoveDstFile(fn_dst, e));
|
|
Packit |
9ff65e |
handle_exception
|
|
Packit |
9ff65e |
(Unix.openfile fn_dst [Unix.O_WRONLY; Unix.O_CREAT]) mode
|
|
Packit |
9ff65e |
(fun e -> `CannotOpenDstFile(fn_dst, e))
|
|
Packit |
9ff65e |
end else if not dst_exists then begin
|
|
Packit |
9ff65e |
handle_exception
|
|
Packit |
9ff65e |
(Unix.openfile fn_dst [Unix.O_WRONLY; Unix.O_CREAT]) mode
|
|
Packit |
9ff65e |
(fun e -> `CannotOpenDstFile(fn_dst, e))
|
|
Packit |
9ff65e |
end else begin
|
|
Packit |
9ff65e |
raise CpSkip
|
|
Packit |
9ff65e |
end
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
let read = ref 0 in
|
|
Packit |
9ff65e |
try
|
|
Packit |
9ff65e |
let fd_src =
|
|
Packit |
9ff65e |
handle_exception
|
|
Packit |
9ff65e |
(Unix.openfile fn_src [Unix.O_RDONLY]) 0o600
|
|
Packit |
9ff65e |
(fun e -> `CannotOpenSrcFile(fn_src, e))
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
try
|
|
Packit |
9ff65e |
while (read :=
|
|
Packit |
9ff65e |
handle_exception
|
|
Packit Service |
4ae7da |
(Unix.read fd_src buffer 0) (Bytes.length buffer)
|
|
Packit |
9ff65e |
(fun e -> `ErrorRead(fn_src, e));
|
|
Packit |
9ff65e |
!read <> 0) do
|
|
Packit |
9ff65e |
let written =
|
|
Packit |
9ff65e |
handle_exception
|
|
Packit |
9ff65e |
(Unix.write fd_dst buffer 0) !read
|
|
Packit |
9ff65e |
(fun e -> `ErrorWrite(fn_dst, e))
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
if written != !read then
|
|
Packit |
9ff65e |
handle_error (`PartialWrite(fn_src, !read, written))
|
|
Packit |
9ff65e |
done;
|
|
Packit |
9ff65e |
Unix.close fd_src;
|
|
Packit |
9ff65e |
Unix.close fd_dst;
|
|
Packit |
9ff65e |
copy_time_props st_src fn_dst
|
|
Packit |
9ff65e |
with e ->
|
|
Packit |
9ff65e |
Unix.close fd_src;
|
|
Packit |
9ff65e |
raise e
|
|
Packit |
9ff65e |
with e ->
|
|
Packit |
9ff65e |
Unix.close fd_dst;
|
|
Packit |
9ff65e |
raise e
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
let cp_symlink fn_src fn_dst =
|
|
Packit |
9ff65e |
(* No Unix.lutimes to set time of the symlink. *)
|
|
Packit |
9ff65e |
Unix.symlink (Unix.readlink fn_src) fn_dst
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
let rec cp_dir st_src dst_exists fn_src fn_dst =
|
|
Packit |
9ff65e |
(* 2a *)
|
|
Packit |
9ff65e |
if not recurse then begin
|
|
Packit |
9ff65e |
handle_error (`CannotCopyDir fn_src)
|
|
Packit |
9ff65e |
(* 2d, 2c *)
|
|
Packit |
9ff65e |
end else if dst_exists && (stat fn_dst).kind <> Dir then begin
|
|
Packit |
9ff65e |
handle_error (`DstDirNotDir fn_dst)
|
|
Packit |
9ff65e |
end else begin
|
|
Packit |
9ff65e |
(* 2e *)
|
|
Packit |
9ff65e |
let dst_created =
|
|
Packit |
9ff65e |
if not dst_exists then begin
|
|
Packit |
9ff65e |
let mode =
|
|
Packit |
9ff65e |
let src_mode = int_of_permission st_src.permission in
|
|
Packit |
9ff65e |
let dst_mode =
|
|
Packit |
9ff65e |
if preserve then src_mode else umask_apply src_mode
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
`Octal (dst_mode lor 0o0700)
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
handle_exception
|
|
Packit |
9ff65e |
(fun fn -> mkdir ~mode fn) fn_dst
|
|
Packit |
9ff65e |
(fun e -> `CannotCreateDir(fn_dst, e));
|
|
Packit |
9ff65e |
true
|
|
Packit |
9ff65e |
end else begin
|
|
Packit |
9ff65e |
false
|
|
Packit |
9ff65e |
end
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
(* 2f *)
|
|
Packit |
9ff65e |
Array.iter
|
|
Packit |
9ff65e |
(fun bn ->
|
|
Packit |
9ff65e |
if not (is_current bn || is_parent bn) then
|
|
Packit |
9ff65e |
cp_one (concat fn_src bn) (concat fn_dst bn))
|
|
Packit |
9ff65e |
(handle_exception
|
|
Packit |
9ff65e |
Sys.readdir fn_src
|
|
Packit |
9ff65e |
(fun e -> `CannotListSrcDir(fn_src, e)));
|
|
Packit |
9ff65e |
(* 2g *)
|
|
Packit |
9ff65e |
if dst_created then begin
|
|
Packit |
9ff65e |
let mode =
|
|
Packit |
9ff65e |
let src_mode = int_of_permission st_src.permission in
|
|
Packit |
9ff65e |
`Octal (if preserve then src_mode else umask_apply src_mode)
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
handle_exception
|
|
Packit |
9ff65e |
(chmod mode) [fn_dst]
|
|
Packit |
9ff65e |
(fun e -> `CannotChmodDstDir(fn_dst, e));
|
|
Packit |
9ff65e |
copy_time_props st_src fn_dst
|
|
Packit |
9ff65e |
end
|
|
Packit |
9ff65e |
end
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
and cp_one fn_src fn_dst =
|
|
Packit |
9ff65e |
let st_src, st_src_deref =
|
|
Packit |
9ff65e |
(* Check existence of source files. *)
|
|
Packit |
9ff65e |
if test_exists fn_src then begin
|
|
Packit |
9ff65e |
let st = stat fn_src in
|
|
Packit |
9ff65e |
if st.kind = Symlink && not recurse then begin
|
|
Packit |
9ff65e |
st, stat ~dereference:true fn_src
|
|
Packit |
9ff65e |
end else begin
|
|
Packit |
9ff65e |
st, st
|
|
Packit |
9ff65e |
end
|
|
Packit |
9ff65e |
end else begin
|
|
Packit |
9ff65e |
handle_error (`NoSourceFile fn_src)
|
|
Packit |
9ff65e |
end
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
let same_file, dst_exists =
|
|
Packit |
9ff65e |
(* Test if fn_dst exists and if it is the same file as fn_src. *)
|
|
Packit |
9ff65e |
try
|
|
Packit |
9ff65e |
same_file st_src (stat fn_dst), true
|
|
Packit |
9ff65e |
with FileDoesntExist _ ->
|
|
Packit |
9ff65e |
false, false
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
|
|
Packit |
9ff65e |
if same_file then begin
|
|
Packit |
9ff65e |
handle_error (`SameFile(fn_src, fn_dst))
|
|
Packit |
9ff65e |
end;
|
|
Packit |
9ff65e |
try
|
|
Packit |
9ff65e |
match st_src.kind with
|
|
Packit |
9ff65e |
| Dir -> cp_dir st_src dst_exists fn_src fn_dst
|
|
Packit |
9ff65e |
| File -> cp_file st_src dst_exists fn_src fn_dst
|
|
Packit |
9ff65e |
| Symlink ->
|
|
Packit |
9ff65e |
if st_src_deref.kind = Dir || recurse then
|
|
Packit |
9ff65e |
cp_symlink fn_src fn_dst
|
|
Packit |
9ff65e |
else
|
|
Packit |
9ff65e |
cp_file st_src_deref dst_exists fn_src fn_dst
|
|
Packit |
9ff65e |
| Fifo | Dev_char | Dev_block | Socket ->
|
|
Packit |
9ff65e |
handle_error (`UnhandledType(fn_src, st_src.kind))
|
|
Packit |
9ff65e |
with CpSkip ->
|
|
Packit |
9ff65e |
()
|
|
Packit |
9ff65e |
in
|
|
Packit |
9ff65e |
if test Is_dir fln_dst then
|
|
Packit |
9ff65e |
List.iter
|
|
Packit |
9ff65e |
(fun fn_src ->
|
|
Packit |
9ff65e |
cp_one fn_src (concat fln_dst (basename fn_src)))
|
|
Packit |
9ff65e |
fln_src_lst
|
|
Packit |
9ff65e |
else if List.length fln_src_lst <= 1 then
|
|
Packit |
9ff65e |
List.iter
|
|
Packit |
9ff65e |
(fun fn_src -> cp_one fn_src fln_dst)
|
|
Packit |
9ff65e |
fln_src_lst
|
|
Packit |
9ff65e |
else
|
|
Packit |
9ff65e |
handle_error (`CannotCopyFilesToFile(fln_src_lst, fln_dst))
|