2015-12-24 17:25:05 +00:00
|
|
|
{--
|
|
|
|
HSFM, a filemanager written in Haskell.
|
2016-03-30 22:28:23 +00:00
|
|
|
Copyright (C) 2016 Julian Ospald
|
2015-12-24 17:25:05 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
version 2 as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
This program 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
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
--}
|
|
|
|
|
2015-12-17 03:42:22 +00:00
|
|
|
{-# OPTIONS_HADDOCK ignore-exports #-}
|
2016-04-03 16:19:02 +00:00
|
|
|
{-# LANGUAGE PackageImports #-}
|
2015-12-17 03:42:22 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |This module provides all the atomic IO related file operations like
|
2016-04-03 01:57:35 +00:00
|
|
|
-- copy, delete, move and so on. It operates primarily on `AnchoredFile`, which
|
|
|
|
-- is guaranteed to be well-formed.
|
2015-12-19 15:13:48 +00:00
|
|
|
--
|
|
|
|
-- It would be nicer to pass states around, but the filesystem state changes
|
|
|
|
-- too quickly and cannot be relied upon. Lazy implementations of filesystem
|
|
|
|
-- trees have been tried as well, but they can introduce subtle bugs.
|
2016-03-30 18:16:34 +00:00
|
|
|
module HSFM.FileSystem.FileOperations where
|
2015-12-17 03:42:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
import Control.Exception
|
|
|
|
(
|
2015-12-27 15:25:24 +00:00
|
|
|
throw
|
2015-12-17 03:42:22 +00:00
|
|
|
)
|
2015-12-28 02:04:02 +00:00
|
|
|
import Control.Monad
|
|
|
|
(
|
|
|
|
unless
|
|
|
|
)
|
2015-12-18 03:22:13 +00:00
|
|
|
import Data.Foldable
|
|
|
|
(
|
|
|
|
for_
|
|
|
|
)
|
2015-12-27 15:25:24 +00:00
|
|
|
import Foreign.C.Error
|
|
|
|
(
|
|
|
|
eXDEV
|
|
|
|
)
|
2016-03-30 00:50:32 +00:00
|
|
|
import HPath
|
|
|
|
(
|
|
|
|
Path
|
2016-04-03 01:57:11 +00:00
|
|
|
, Abs
|
2016-03-30 00:50:32 +00:00
|
|
|
, Fn
|
|
|
|
)
|
|
|
|
import qualified HPath as P
|
2016-03-30 18:16:34 +00:00
|
|
|
import HSFM.FileSystem.Errors
|
|
|
|
import HSFM.FileSystem.FileType
|
|
|
|
import HSFM.Utils.IO
|
2015-12-26 15:02:25 +00:00
|
|
|
import System.Posix.Directory
|
|
|
|
(
|
|
|
|
createDirectory
|
|
|
|
, removeDirectory
|
|
|
|
)
|
2015-12-18 03:22:13 +00:00
|
|
|
import System.Posix.Files
|
|
|
|
(
|
|
|
|
createSymbolicLink
|
2015-12-27 18:26:58 +00:00
|
|
|
, fileMode
|
2015-12-18 03:22:13 +00:00
|
|
|
, readSymbolicLink
|
2015-12-27 18:26:58 +00:00
|
|
|
, getSymbolicLinkStatus
|
2015-12-26 15:02:25 +00:00
|
|
|
, groupExecuteMode
|
2015-12-25 21:51:45 +00:00
|
|
|
, groupReadMode
|
|
|
|
, groupWriteMode
|
2015-12-26 15:02:25 +00:00
|
|
|
, otherExecuteMode
|
2015-12-25 21:51:45 +00:00
|
|
|
, otherReadMode
|
|
|
|
, otherWriteMode
|
2015-12-26 15:02:25 +00:00
|
|
|
, ownerModes
|
2015-12-25 21:51:45 +00:00
|
|
|
, ownerReadMode
|
|
|
|
, ownerWriteMode
|
2015-12-26 02:04:28 +00:00
|
|
|
, rename
|
2015-12-25 21:51:45 +00:00
|
|
|
, unionFileModes
|
2015-12-26 15:02:25 +00:00
|
|
|
, removeLink
|
2015-12-25 21:51:45 +00:00
|
|
|
)
|
2016-04-03 16:19:02 +00:00
|
|
|
import qualified "unix" System.Posix.IO as SPI
|
|
|
|
import "unix-bytestring" System.Posix.IO.ByteString
|
2015-12-25 21:51:45 +00:00
|
|
|
(
|
2016-04-03 16:19:02 +00:00
|
|
|
fdWrite
|
2015-12-18 03:22:13 +00:00
|
|
|
)
|
2015-12-26 15:02:25 +00:00
|
|
|
import System.Posix.Types
|
|
|
|
(
|
|
|
|
FileMode
|
|
|
|
)
|
2015-12-17 03:42:22 +00:00
|
|
|
import System.Process
|
|
|
|
(
|
|
|
|
spawnProcess
|
|
|
|
, ProcessHandle
|
|
|
|
)
|
|
|
|
|
2015-12-27 18:26:58 +00:00
|
|
|
import qualified Data.ByteString as BS
|
2015-12-17 03:42:22 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
|
2015-12-17 22:08:02 +00:00
|
|
|
|
2015-12-17 22:11:18 +00:00
|
|
|
-- TODO: file operations should be threaded and not block the UI
|
2015-12-28 00:49:18 +00:00
|
|
|
-- TODO: make sure we do the right thing for BlockDev, CharDev etc...
|
|
|
|
-- most operations are not implemented for these
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Data type describing an actual file operation that can be
|
|
|
|
-- carried out via `doFile`. Useful to build up a list of operations
|
|
|
|
-- or delay operations.
|
2015-12-18 15:55:46 +00:00
|
|
|
data FileOperation = FCopy Copy
|
|
|
|
| FMove Move
|
2015-12-23 21:50:04 +00:00
|
|
|
| FDelete (AnchoredFile FileInfo)
|
|
|
|
| FOpen (AnchoredFile FileInfo)
|
|
|
|
| FExecute (AnchoredFile FileInfo) [String]
|
2015-12-18 03:24:47 +00:00
|
|
|
| None
|
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Data type describing partial or complete file copy operation.
|
|
|
|
-- CC stands for a complete operation and can be used for `runFileOp`.
|
2015-12-23 21:50:04 +00:00
|
|
|
data Copy = CP1 (AnchoredFile FileInfo)
|
|
|
|
| CP2 (AnchoredFile FileInfo)
|
|
|
|
(AnchoredFile FileInfo)
|
|
|
|
| CC (AnchoredFile FileInfo)
|
|
|
|
(AnchoredFile FileInfo)
|
2015-12-28 02:04:02 +00:00
|
|
|
CopyMode
|
2015-12-18 15:55:46 +00:00
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Data type describing partial or complete file move operation.
|
|
|
|
-- MC stands for a complete operation and can be used for `runFileOp`.
|
2015-12-23 21:50:04 +00:00
|
|
|
data Move = MP1 (AnchoredFile FileInfo)
|
|
|
|
| MC (AnchoredFile FileInfo)
|
|
|
|
(AnchoredFile FileInfo)
|
2015-12-28 02:04:02 +00:00
|
|
|
CopyMode
|
2015-12-18 15:55:46 +00:00
|
|
|
|
|
|
|
|
2015-12-28 02:04:02 +00:00
|
|
|
-- |Copy modes.
|
|
|
|
data CopyMode = Strict -- ^ fail if the target already exists
|
|
|
|
| Merge -- ^ overwrite files if necessary, for files, this
|
|
|
|
-- is the same as Replace
|
|
|
|
| Replace -- ^ remove targets before copying, this is
|
|
|
|
-- only useful if the target is a directorty
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Run a given FileOperation. If the FileOperation is partial, it will
|
|
|
|
-- be returned.
|
2015-12-18 15:55:46 +00:00
|
|
|
runFileOp :: FileOperation -> IO (Maybe FileOperation)
|
2015-12-19 15:13:48 +00:00
|
|
|
runFileOp (FCopy (CC from to cm)) = easyCopy cm from to >> return Nothing
|
2015-12-23 15:08:39 +00:00
|
|
|
runFileOp (FCopy fo) = return $ Just $ FCopy fo
|
2015-12-28 02:04:02 +00:00
|
|
|
runFileOp (FMove (MC from to cm)) = moveFile cm from to >> return Nothing
|
2015-12-23 15:08:39 +00:00
|
|
|
runFileOp (FMove fo) = return $ Just $ FMove fo
|
|
|
|
runFileOp (FDelete fp) = easyDelete fp >> return Nothing
|
|
|
|
runFileOp (FOpen fp) = openFile fp >> return Nothing
|
|
|
|
runFileOp (FExecute fp args) = executeFile fp args >> return Nothing
|
2016-03-31 14:19:31 +00:00
|
|
|
runFileOp _ = return Nothing
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
2015-12-18 14:28:56 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
--------------------
|
|
|
|
--[ File Copying ]--
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
2015-12-18 13:21:57 +00:00
|
|
|
-- TODO: allow renaming
|
2015-12-19 15:13:48 +00:00
|
|
|
-- |Copies a directory to the given destination with the specified
|
2015-12-23 15:10:08 +00:00
|
|
|
-- `DirCopyMode`. Excludes symlinks.
|
2015-12-28 02:04:02 +00:00
|
|
|
copyDir :: CopyMode
|
2015-12-23 21:50:04 +00:00
|
|
|
-> AnchoredFile FileInfo -- ^ source dir
|
|
|
|
-> AnchoredFile FileInfo -- ^ destination dir
|
2015-12-18 03:22:13 +00:00
|
|
|
-> IO ()
|
2015-12-27 19:17:14 +00:00
|
|
|
copyDir _ AFileInvFN _ = throw InvalidFileName
|
|
|
|
copyDir _ _ AFileInvFN = throw InvalidFileName
|
2016-03-31 14:19:31 +00:00
|
|
|
copyDir cm from@(_ :/ Dir fromn FileInfo{ fileMode = fmode })
|
2015-12-22 13:15:48 +00:00
|
|
|
to@(_ :/ Dir {})
|
|
|
|
= do
|
|
|
|
let fromp = fullPath from
|
|
|
|
top = fullPath to
|
2016-03-30 00:50:32 +00:00
|
|
|
destdirp = top P.</> fromn
|
2016-03-31 13:49:35 +00:00
|
|
|
throwDestinationInSource fromp destdirp
|
2016-04-03 15:13:45 +00:00
|
|
|
throwSameFile fromp destdirp
|
2016-04-03 12:36:56 +00:00
|
|
|
throwCantOpenDirectory fromp
|
|
|
|
throwCantOpenDirectory top
|
2015-12-22 13:15:48 +00:00
|
|
|
|
2015-12-27 19:39:40 +00:00
|
|
|
createDestdir destdirp fmode
|
2016-03-30 18:16:34 +00:00
|
|
|
destdir <- HSFM.FileSystem.FileType.readFileWithFileInfo destdirp
|
2015-12-22 13:15:48 +00:00
|
|
|
|
2016-03-30 00:50:32 +00:00
|
|
|
contents <- readDirectoryContents' (fullPath from)
|
2015-12-22 13:15:48 +00:00
|
|
|
|
|
|
|
for_ contents $ \f ->
|
|
|
|
case f of
|
2015-12-28 02:04:02 +00:00
|
|
|
(_ :/ SymLink {}) -> recreateSymlink cm f destdir
|
2015-12-28 00:49:18 +00:00
|
|
|
(_ :/ Dir {}) -> copyDir cm f destdir
|
2015-12-28 02:04:02 +00:00
|
|
|
(_ :/ RegFile {}) -> copyFileToDir Replace f destdir
|
2015-12-28 00:49:18 +00:00
|
|
|
_ -> return ()
|
2015-12-18 14:42:24 +00:00
|
|
|
where
|
2016-03-31 14:19:31 +00:00
|
|
|
createDestdir destdir fmode' =
|
2016-03-30 00:50:32 +00:00
|
|
|
let destdir' = P.toFilePath destdir
|
|
|
|
in case cm of
|
2015-12-18 14:42:24 +00:00
|
|
|
Merge ->
|
2016-03-31 13:49:35 +00:00
|
|
|
unlessM (doesDirectoryExist destdir)
|
2016-03-31 14:19:31 +00:00
|
|
|
(createDirectory destdir' fmode')
|
2015-12-18 14:42:24 +00:00
|
|
|
Strict -> do
|
2016-03-31 13:49:35 +00:00
|
|
|
throwDirDoesExist destdir
|
2016-03-31 14:19:31 +00:00
|
|
|
createDirectory destdir' fmode'
|
2015-12-18 14:42:24 +00:00
|
|
|
Replace -> do
|
2016-03-31 13:49:35 +00:00
|
|
|
whenM (doesDirectoryExist destdir)
|
2016-03-30 18:16:34 +00:00
|
|
|
(deleteDirRecursive =<<
|
|
|
|
HSFM.FileSystem.FileType.readFileWithFileInfo destdir)
|
2016-03-31 14:19:31 +00:00
|
|
|
createDirectory destdir' fmode'
|
2015-12-27 15:25:24 +00:00
|
|
|
copyDir _ _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-21 17:32:53 +00:00
|
|
|
|
2015-12-18 14:28:56 +00:00
|
|
|
|
2015-12-21 17:36:45 +00:00
|
|
|
-- |Recreate a symlink.
|
2015-12-28 02:04:02 +00:00
|
|
|
recreateSymlink :: CopyMode
|
|
|
|
-> AnchoredFile FileInfo -- ^ the old symlink file
|
2015-12-23 21:50:04 +00:00
|
|
|
-> AnchoredFile FileInfo -- ^ destination dir of the
|
2015-12-28 00:49:18 +00:00
|
|
|
-- new symlink file
|
2015-12-21 17:32:53 +00:00
|
|
|
-> IO ()
|
2015-12-28 02:04:02 +00:00
|
|
|
recreateSymlink _ AFileInvFN _ = throw InvalidFileName
|
|
|
|
recreateSymlink _ _ AFileInvFN = throw InvalidFileName
|
|
|
|
recreateSymlink cm symf@(_ :/ SymLink {})
|
|
|
|
symdest@(_ :/ Dir {})
|
2015-12-22 13:15:48 +00:00
|
|
|
= do
|
2016-04-03 12:36:56 +00:00
|
|
|
throwCantOpenDirectory $ fullPath symdest
|
2016-03-30 22:25:03 +00:00
|
|
|
sympoint <- readSymbolicLink (fullPathS $ symf)
|
2016-03-30 00:50:32 +00:00
|
|
|
let symname = fullPath symdest P.</> (name . file $ symf)
|
2015-12-28 02:04:02 +00:00
|
|
|
case cm of
|
|
|
|
Merge -> delOld symname
|
|
|
|
Replace -> delOld symname
|
|
|
|
_ -> return ()
|
2016-03-30 00:50:32 +00:00
|
|
|
createSymbolicLink sympoint (P.fromAbs symname)
|
2015-12-28 02:04:02 +00:00
|
|
|
where
|
|
|
|
delOld symname = do
|
2016-03-30 18:16:34 +00:00
|
|
|
f <- HSFM.FileSystem.FileType.readFileWithFileInfo symname
|
2015-12-28 02:04:02 +00:00
|
|
|
unless (failed . file $ f)
|
|
|
|
(easyDelete f)
|
|
|
|
recreateSymlink _ _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-22 13:15:48 +00:00
|
|
|
|
|
|
|
|
2015-12-27 18:26:58 +00:00
|
|
|
-- |TODO: handle EAGAIN exception for non-blocking IO
|
|
|
|
-- |Low-level function to copy a given file to the given path. The fileMode
|
2015-12-28 02:04:02 +00:00
|
|
|
-- is preserved. The file is always overwritten if accessible.
|
2016-04-03 01:57:11 +00:00
|
|
|
copyFile' :: Path Abs -> Path Abs -> IO ()
|
2015-12-27 18:26:58 +00:00
|
|
|
copyFile' from to = do
|
2016-04-03 01:57:11 +00:00
|
|
|
let from' = P.fromAbs from
|
|
|
|
to' = P.fromAbs to
|
2016-04-03 12:36:56 +00:00
|
|
|
throwCantOpenDirectory $ P.dirname from
|
|
|
|
throwCantOpenDirectory $ P.dirname to
|
2016-04-03 01:57:11 +00:00
|
|
|
fromFstatus <- getSymbolicLinkStatus from'
|
|
|
|
fromContent <- BS.readFile from'
|
2016-04-03 16:19:02 +00:00
|
|
|
fd <- SPI.createFile to'
|
2015-12-27 18:26:58 +00:00
|
|
|
(System.Posix.Files.fileMode fromFstatus)
|
2016-04-03 16:19:02 +00:00
|
|
|
_ <- fdWrite fd fromContent
|
|
|
|
SPI.closeFd fd
|
2015-12-27 18:26:58 +00:00
|
|
|
|
|
|
|
|
2015-12-28 02:04:02 +00:00
|
|
|
-- |Copies the given file to the given file destination, overwriting it.
|
2015-12-23 15:10:08 +00:00
|
|
|
-- Excludes symlinks.
|
2015-12-28 02:04:02 +00:00
|
|
|
overwriteFile :: AnchoredFile FileInfo -- ^ source file
|
|
|
|
-> AnchoredFile FileInfo -- ^ destination file
|
|
|
|
-> IO ()
|
|
|
|
overwriteFile AFileInvFN _ = throw InvalidFileName
|
|
|
|
overwriteFile _ AFileInvFN = throw InvalidFileName
|
|
|
|
overwriteFile from@(_ :/ RegFile {})
|
|
|
|
to@(_ :/ RegFile {})
|
|
|
|
= do
|
2016-03-31 13:49:35 +00:00
|
|
|
let from' = fullPath from
|
|
|
|
to' = fullPath to
|
2016-04-03 12:36:56 +00:00
|
|
|
throwCantOpenDirectory $ P.dirname from'
|
|
|
|
throwCantOpenDirectory $ P.dirname to'
|
2015-12-28 02:04:02 +00:00
|
|
|
throwSameFile from' to'
|
2016-04-03 01:57:11 +00:00
|
|
|
copyFile' from' to'
|
2015-12-28 02:04:02 +00:00
|
|
|
overwriteFile _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-17 22:08:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Copies the given file to the given dir with the same filename.
|
2015-12-23 15:10:08 +00:00
|
|
|
-- Excludes symlinks.
|
2015-12-28 02:04:02 +00:00
|
|
|
copyFileToDir :: CopyMode
|
|
|
|
-> AnchoredFile FileInfo
|
2015-12-23 21:50:04 +00:00
|
|
|
-> AnchoredFile FileInfo
|
2015-12-22 13:15:48 +00:00
|
|
|
-> IO ()
|
2015-12-28 02:04:02 +00:00
|
|
|
copyFileToDir _ AFileInvFN _ = throw InvalidFileName
|
|
|
|
copyFileToDir _ _ AFileInvFN = throw InvalidFileName
|
|
|
|
copyFileToDir cm from@(_ :/ RegFile fn _)
|
|
|
|
to@(_ :/ Dir {})
|
|
|
|
= do
|
2016-03-31 13:49:35 +00:00
|
|
|
let from' = fullPath from
|
|
|
|
to' = fullPath to P.</> fn
|
2016-04-03 12:36:56 +00:00
|
|
|
throwCantOpenDirectory $ fullPath to
|
2015-12-28 02:04:02 +00:00
|
|
|
case cm of
|
|
|
|
Strict -> throwFileDoesExist to'
|
|
|
|
_ -> return ()
|
2016-04-03 01:57:11 +00:00
|
|
|
copyFile' from' to'
|
2015-12-28 02:04:02 +00:00
|
|
|
copyFileToDir _ _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-22 13:15:48 +00:00
|
|
|
|
|
|
|
|
2015-12-23 15:10:08 +00:00
|
|
|
-- |Copies a file, directory or symlink. In case of a symlink, it is just
|
|
|
|
-- recreated, even if it points to a directory.
|
2015-12-28 02:04:02 +00:00
|
|
|
easyCopy :: CopyMode
|
2015-12-23 21:50:04 +00:00
|
|
|
-> AnchoredFile FileInfo
|
|
|
|
-> AnchoredFile FileInfo
|
2015-12-22 13:15:48 +00:00
|
|
|
-> IO ()
|
2016-03-31 14:19:31 +00:00
|
|
|
easyCopy cm from@(_ :/ SymLink{})
|
|
|
|
to@(_ :/ Dir{})
|
2015-12-28 02:04:02 +00:00
|
|
|
= recreateSymlink cm from to
|
2016-03-31 14:19:31 +00:00
|
|
|
easyCopy cm from@(_ :/ RegFile{})
|
|
|
|
to@(_ :/ Dir{})
|
2015-12-28 02:04:02 +00:00
|
|
|
= copyFileToDir cm from to
|
2016-03-31 14:19:31 +00:00
|
|
|
easyCopy cm from@(_ :/ Dir{})
|
|
|
|
to@(_ :/ Dir{})
|
2015-12-22 13:15:48 +00:00
|
|
|
= copyDir cm from to
|
2015-12-27 15:25:24 +00:00
|
|
|
easyCopy _ _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-23 15:08:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-17 15:25:37 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
---------------------
|
|
|
|
--[ File Deletion ]--
|
|
|
|
---------------------
|
2015-12-17 15:25:37 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
|
2015-12-21 17:32:53 +00:00
|
|
|
-- |Deletes a symlink, which can either point to a file or directory.
|
2015-12-23 21:50:04 +00:00
|
|
|
deleteSymlink :: AnchoredFile FileInfo -> IO ()
|
2015-12-27 19:17:14 +00:00
|
|
|
deleteSymlink AFileInvFN = throw InvalidFileName
|
2015-12-23 21:50:04 +00:00
|
|
|
deleteSymlink f@(_ :/ SymLink {})
|
2016-03-30 00:50:32 +00:00
|
|
|
= removeLink (P.toFilePath . fullPath $ f)
|
2015-12-27 15:25:24 +00:00
|
|
|
deleteSymlink _ = throw $ InvalidOperation "wrong input type"
|
2015-12-22 13:15:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Deletes the given file, never symlinks.
|
2015-12-23 21:50:04 +00:00
|
|
|
deleteFile :: AnchoredFile FileInfo -> IO ()
|
2015-12-27 19:17:14 +00:00
|
|
|
deleteFile AFileInvFN = throw InvalidFileName
|
2015-12-22 13:15:48 +00:00
|
|
|
deleteFile f@(_ :/ RegFile {})
|
2016-03-30 00:50:32 +00:00
|
|
|
= removeLink (P.toFilePath . fullPath $ f)
|
2015-12-27 15:25:24 +00:00
|
|
|
deleteFile _ = throw $ InvalidOperation "wrong input type"
|
2015-12-22 13:15:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Deletes the given directory, never symlinks.
|
2015-12-23 21:50:04 +00:00
|
|
|
deleteDir :: AnchoredFile FileInfo -> IO ()
|
2015-12-27 19:17:14 +00:00
|
|
|
deleteDir AFileInvFN = throw InvalidFileName
|
2015-12-22 13:15:48 +00:00
|
|
|
deleteDir f@(_ :/ Dir {})
|
2016-03-30 00:50:32 +00:00
|
|
|
= removeDirectory (P.toFilePath . fullPath $ f)
|
2015-12-27 15:25:24 +00:00
|
|
|
deleteDir _ = throw $ InvalidOperation "wrong input type"
|
2015-12-22 13:15:48 +00:00
|
|
|
|
|
|
|
|
2015-12-27 18:26:58 +00:00
|
|
|
-- |Deletes the given directory recursively.
|
2015-12-23 21:50:04 +00:00
|
|
|
deleteDirRecursive :: AnchoredFile FileInfo -> IO ()
|
2015-12-27 19:17:14 +00:00
|
|
|
deleteDirRecursive AFileInvFN = throw InvalidFileName
|
2015-12-27 18:26:58 +00:00
|
|
|
deleteDirRecursive f@(_ :/ Dir {}) = do
|
|
|
|
let fp = fullPath f
|
2016-04-03 12:36:56 +00:00
|
|
|
throwCantOpenDirectory fp
|
2016-03-30 00:50:32 +00:00
|
|
|
files <- readDirectoryContents' fp
|
2015-12-27 18:26:58 +00:00
|
|
|
for_ files $ \file ->
|
|
|
|
case file of
|
|
|
|
(_ :/ SymLink {}) -> deleteSymlink file
|
|
|
|
(_ :/ Dir {}) -> deleteDirRecursive file
|
2016-03-30 00:50:32 +00:00
|
|
|
(_ :/ RegFile {}) -> removeLink (P.toFilePath . fullPath $ file)
|
|
|
|
_ -> throw $ FileDoesExist (P.toFilePath . fullPath $ file)
|
|
|
|
removeDirectory . P.toFilePath $ fp
|
2015-12-27 15:25:24 +00:00
|
|
|
deleteDirRecursive _ = throw $ InvalidOperation "wrong input type"
|
2015-12-18 14:28:56 +00:00
|
|
|
|
|
|
|
|
2015-12-21 17:32:53 +00:00
|
|
|
-- |Deletes a file, directory or symlink, whatever it may be.
|
2015-12-23 15:10:08 +00:00
|
|
|
-- In case of directory, performs recursive deletion. In case of
|
|
|
|
-- a symlink, the symlink file is deleted.
|
2015-12-23 21:50:04 +00:00
|
|
|
easyDelete :: AnchoredFile FileInfo -> IO ()
|
|
|
|
easyDelete f@(_ :/ SymLink {}) = deleteSymlink f
|
2015-12-22 13:15:48 +00:00
|
|
|
easyDelete f@(_ :/ RegFile {})
|
|
|
|
= deleteFile f
|
|
|
|
easyDelete f@(_ :/ Dir {})
|
|
|
|
= deleteDirRecursive f
|
2015-12-27 15:25:24 +00:00
|
|
|
easyDelete _ = throw $ InvalidOperation "wrong input type"
|
2015-12-22 13:15:48 +00:00
|
|
|
|
2015-12-19 15:13:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--------------------
|
|
|
|
--[ File Opening ]--
|
|
|
|
--------------------
|
2015-12-17 15:25:37 +00:00
|
|
|
|
|
|
|
|
2015-12-17 03:42:22 +00:00
|
|
|
-- |Opens a file appropriately by invoking xdg-open.
|
2015-12-23 21:50:04 +00:00
|
|
|
openFile :: AnchoredFile a
|
2015-12-17 03:42:22 +00:00
|
|
|
-> IO ProcessHandle
|
2015-12-27 19:17:14 +00:00
|
|
|
openFile AFileInvFN = throw InvalidFileName
|
2016-03-30 22:25:03 +00:00
|
|
|
openFile f = spawnProcess "xdg-open" [fullPathS f]
|
2015-12-17 03:42:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Executes a program with the given arguments.
|
2015-12-23 21:50:04 +00:00
|
|
|
executeFile :: AnchoredFile FileInfo -- ^ program
|
2015-12-24 04:53:11 +00:00
|
|
|
-> [String] -- ^ arguments
|
2015-12-27 15:25:24 +00:00
|
|
|
-> IO ProcessHandle
|
2015-12-27 19:17:14 +00:00
|
|
|
executeFile AFileInvFN _ = throw InvalidFileName
|
2015-12-23 21:50:04 +00:00
|
|
|
executeFile prog@(_ :/ RegFile {}) args
|
2016-03-30 22:25:03 +00:00
|
|
|
= spawnProcess (fullPathS prog) args
|
2015-12-27 15:25:24 +00:00
|
|
|
executeFile _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-25 21:51:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---------------------
|
|
|
|
--[ File Creation ]--
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
2016-03-30 00:50:32 +00:00
|
|
|
createFile :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
2015-12-27 19:17:14 +00:00
|
|
|
createFile AFileInvFN _ = throw InvalidFileName
|
|
|
|
createFile _ InvFN = throw InvalidFileName
|
2015-12-26 21:00:08 +00:00
|
|
|
createFile (ADirOrSym td) (ValFN fn) = do
|
2016-03-31 13:49:35 +00:00
|
|
|
let fullp = fullPath td P.</> fn
|
2015-12-25 21:51:45 +00:00
|
|
|
throwFileDoesExist fullp
|
2016-04-03 16:19:02 +00:00
|
|
|
fd <- SPI.createFile (P.fromAbs fullp) newFilePerms
|
|
|
|
SPI.closeFd fd
|
2015-12-27 15:25:24 +00:00
|
|
|
createFile _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-26 02:04:28 +00:00
|
|
|
|
|
|
|
|
2016-03-30 00:50:32 +00:00
|
|
|
createDir :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
2015-12-27 19:17:14 +00:00
|
|
|
createDir AFileInvFN _ = throw InvalidFileName
|
|
|
|
createDir _ InvFN = throw InvalidFileName
|
2015-12-26 21:00:08 +00:00
|
|
|
createDir (ADirOrSym td) (ValFN fn) = do
|
2016-03-31 13:49:35 +00:00
|
|
|
let fullp = fullPath td P.</> fn
|
2015-12-26 14:58:41 +00:00
|
|
|
throwDirDoesExist fullp
|
2016-03-31 13:49:35 +00:00
|
|
|
createDirectory (P.fromAbs fullp) newFilePerms
|
2015-12-27 15:25:24 +00:00
|
|
|
createDir _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-26 14:58:41 +00:00
|
|
|
|
|
|
|
|
2015-12-26 02:04:28 +00:00
|
|
|
|
|
|
|
|
2015-12-26 20:18:42 +00:00
|
|
|
----------------------------
|
|
|
|
--[ File Renaming/Moving ]--
|
|
|
|
----------------------------
|
2015-12-26 02:04:28 +00:00
|
|
|
|
|
|
|
|
2016-03-30 00:50:32 +00:00
|
|
|
renameFile :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
2015-12-27 19:17:14 +00:00
|
|
|
renameFile AFileInvFN _ = throw InvalidFileName
|
|
|
|
renameFile _ InvFN = throw InvalidFileName
|
2015-12-26 19:28:00 +00:00
|
|
|
renameFile af (ValFN fn) = do
|
2016-03-31 13:49:35 +00:00
|
|
|
let fromf = fullPath af
|
|
|
|
tof = anchor af P.</> fn
|
2015-12-26 02:04:28 +00:00
|
|
|
throwFileDoesExist tof
|
|
|
|
throwSameFile fromf tof
|
2016-03-31 13:49:35 +00:00
|
|
|
rename (P.fromAbs fromf) (P.fromAbs tof)
|
2015-12-27 15:25:24 +00:00
|
|
|
renameFile _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-26 14:58:41 +00:00
|
|
|
|
|
|
|
|
2015-12-26 20:18:42 +00:00
|
|
|
-- |Move a given file to the given target directory.
|
2015-12-28 02:04:02 +00:00
|
|
|
moveFile :: CopyMode
|
|
|
|
-> AnchoredFile FileInfo -- ^ file to move
|
2015-12-26 20:18:42 +00:00
|
|
|
-> AnchoredFile FileInfo -- ^ base target directory
|
|
|
|
-> IO ()
|
2015-12-28 02:04:02 +00:00
|
|
|
moveFile _ AFileInvFN _ = throw InvalidFileName
|
|
|
|
moveFile _ _ AFileInvFN = throw InvalidFileName
|
|
|
|
moveFile cm from to@(_ :/ Dir {}) = do
|
2016-03-30 00:50:32 +00:00
|
|
|
let from' = fullPath from
|
2016-03-30 22:25:03 +00:00
|
|
|
froms' = fullPathS from
|
2016-03-30 00:50:32 +00:00
|
|
|
to' = fullPath to P.</> (name . file $ from)
|
|
|
|
tos' = P.fromAbs (fullPath to P.</> (name . file $ from))
|
2015-12-28 02:04:02 +00:00
|
|
|
case cm of
|
2016-03-31 13:49:35 +00:00
|
|
|
Strict -> throwFileDoesExist to'
|
2015-12-28 02:04:02 +00:00
|
|
|
Merge -> delOld to'
|
|
|
|
Replace -> delOld to'
|
2016-03-31 13:49:35 +00:00
|
|
|
throwSameFile from' to'
|
2016-03-30 00:50:32 +00:00
|
|
|
catchErrno eXDEV (rename froms' tos') $ do
|
2015-12-26 22:21:02 +00:00
|
|
|
easyCopy Strict from to
|
|
|
|
easyDelete from
|
2015-12-28 02:04:02 +00:00
|
|
|
where
|
2016-03-31 13:49:35 +00:00
|
|
|
delOld fp = do
|
|
|
|
to' <- HSFM.FileSystem.FileType.readFileWithFileInfo fp
|
2015-12-28 02:04:02 +00:00
|
|
|
unless (failed . file $ to') (easyDelete to')
|
|
|
|
moveFile _ _ _ = throw $ InvalidOperation "wrong input type"
|
2015-12-26 20:18:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-26 14:58:41 +00:00
|
|
|
|
|
|
|
-----------------------
|
|
|
|
--[ File Permissions]--
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
|
|
|
|
newFilePerms :: FileMode
|
|
|
|
newFilePerms
|
|
|
|
= ownerWriteMode
|
|
|
|
`unionFileModes` ownerReadMode
|
|
|
|
`unionFileModes` groupWriteMode
|
|
|
|
`unionFileModes` groupReadMode
|
|
|
|
`unionFileModes` otherWriteMode
|
|
|
|
`unionFileModes` otherReadMode
|
|
|
|
|
|
|
|
|
|
|
|
newDirPerms :: FileMode
|
|
|
|
newDirPerms
|
|
|
|
= ownerModes
|
|
|
|
`unionFileModes` groupExecuteMode
|
|
|
|
`unionFileModes` groupReadMode
|
|
|
|
`unionFileModes` otherExecuteMode
|
|
|
|
`unionFileModes` otherReadMode
|