LIB/GTK: use AnchoredFile in IO.File

What's the point of having it anyway if we don't?
This commit is contained in:
Julian Ospald 2015-12-22 14:15:48 +01:00
parent 06151a3a08
commit 2486d83260
No known key found for this signature in database
GPG Key ID: 220CD1C5BDEED020
4 changed files with 215 additions and 273 deletions

View File

@ -251,14 +251,13 @@ readFileWith :: (FilePath -> IO a)
-> FilePath -> FilePath
-> IO (AnchoredFile a b) -> IO (AnchoredFile a b)
readFileWith fd ff fp = do readFileWith fd ff fp = do
cfp <- canonicalizePath' fp let fn = topDir fp
let fn = topDir cfp bd = baseDir fp
bd = baseDir cfp file <- handleDT (topDir fp) $ do
file <- handleDT fn $ do isFile <- doesFileExist fp
isFile <- doesFileExist cfp
if isFile if isFile
then RegFile fn <$> ff cfp then RegFile fn <$> ff fp
else Dir fn <$> fd cfp else Dir fn <$> fd fp
return (bd :/ file) return (bd :/ file)
@ -267,19 +266,29 @@ readFile fp = readFileWith getFileInfo getFileInfo =<< canonicalizePath' fp
-- |Build a list of AnchoredFile, given the path to a directory, filling -- |Build a list of AnchoredFile, given the path to a directory, filling
-- the free variables via `getFileInfo`. -- the free variables via `getFileInfo`. This includes the "." and ".."
-- directories.
readDirectory :: FilePath -> IO [AnchoredFile FileInfo FileInfo] readDirectory :: FilePath -> IO [AnchoredFile FileInfo FileInfo]
readDirectory fp = readDirectoryWith getFileInfo getFileInfo readDirectory fp = readDirectoryWith getAllDirsFiles getFileInfo getFileInfo
=<< canonicalizePath' fp
-- |Build a list of AnchoredFile, given the path to a directory, filling
-- the free variables via `getFileInfo`. This excludes the "." and ".."
-- directories.
readDirectory' :: FilePath -> IO [AnchoredFile FileInfo FileInfo]
readDirectory' fp = readDirectoryWith getDirsFiles getFileInfo getFileInfo
=<< canonicalizePath' fp =<< canonicalizePath' fp
-- | same as readDirectory but allows us to, for example, use -- | same as readDirectory but allows us to, for example, use
-- ByteString.readFile to return a tree of ByteStrings. -- ByteString.readFile to return a tree of ByteStrings.
readDirectoryWith :: (FilePath -> IO a) readDirectoryWith :: (FilePath -> IO [FilePath])
-> (FilePath -> IO a)
-> (FilePath -> IO b) -> (FilePath -> IO b)
-> FilePath -> FilePath
-> IO [AnchoredFile a b] -> IO [AnchoredFile a b]
readDirectoryWith fd ff p = buildWith' buildAtOnce' fd ff readDirectoryWith getfiles fd ff p = buildWith' (buildAtOnce' getfiles) fd ff
=<< canonicalizePath' p =<< canonicalizePath' p
@ -314,12 +323,11 @@ buildWith' bf' fd ff p =
-- IO function passed to our builder and finally executed here: -- IO function passed to our builder and finally executed here:
buildAtOnce' :: Builder a b buildAtOnce' :: (FilePath -> IO [FilePath]) -> Builder a b
buildAtOnce' fd ff p = do buildAtOnce' getfiles fd ff fp = do
cfp <- canonicalizePath' p contents <- getfiles fp
contents <- getAllDirsFiles cfp
for contents $ \n -> handleDT n $ do for contents $ \n -> handleDT n $ do
let subf = cfp </> n let subf = fp </> n
do isFile <- doesFileExist subf do isFile <- doesFileExist subf
if isFile if isFile
then RegFile n <$> ff subf then RegFile n <$> ff subf
@ -327,7 +335,6 @@ buildAtOnce' fd ff p = do
----------------- -----------------
--[ UTILITIES ]-- --[ UTILITIES ]--
----------------- -----------------

View File

@ -14,6 +14,10 @@ import Control.Concurrent.STM
, newTVarIO , newTVarIO
, readTVarIO , readTVarIO
) )
import Control.Monad
(
void
)
import Control.Monad.IO.Class import Control.Monad.IO.Class
( (
liftIO liftIO
@ -63,10 +67,10 @@ setCallbacks mygui myview = do
_ <- treeView mygui `on` keyPressEvent $ tryEvent $ do _ <- treeView mygui `on` keyPressEvent $ tryEvent $ do
[Control] <- eventModifier [Control] <- eventModifier
"h" <- fmap glibToString eventKeyName "h" <- fmap glibToString eventKeyName
mcdir <- liftIO $ getCwdFromFirstRow myview mcdir <- liftIO $ getFirstRow myview
liftIO $ modifyTVarIO (settings mygui) liftIO $ modifyTVarIO (settings mygui)
(\x -> x { showHidden = not . showHidden $ x}) (\x -> x { showHidden = not . showHidden $ x})
>> refreshTreeView mygui myview (Just mcdir) >> refreshTreeView' mygui myview mcdir
_ <- treeView mygui `on` keyPressEvent $ tryEvent $ do _ <- treeView mygui `on` keyPressEvent $ tryEvent $ do
[Alt] <- eventModifier [Alt] <- eventModifier
"Up" <- fmap glibToString eventKeyName "Up" <- fmap glibToString eventKeyName
@ -106,45 +110,16 @@ open row mygui myview =
r@(_ :/ Dir _ _) -> do r@(_ :/ Dir _ _) -> do
nv <- Data.DirTree.readFile $ fullPath r nv <- Data.DirTree.readFile $ fullPath r
refreshTreeView' mygui myview nv refreshTreeView' mygui myview nv
r@(_ :/ RegFile _ _) -> r ->
withErrorDialog $ openFile $ fullPath r withErrorDialog $ openFile r
_ -> return ()
-- |Supposed to be used with 'withRow'. Deletes a file or directory. -- |Supposed to be used with 'withRow'. Deletes a file or directory.
del :: Row -> MyGUI -> MyView -> IO () del :: Row -> MyGUI -> MyView -> IO ()
del row mygui myview = del row mygui myview = do
case row of let cmsg = "Really delete \"" ++ fullPath row ++ "\"?"
r@(_ :/ Dir { dir = FileInfo { isSymbolicLink = True } }) -> withConfirmationDialog cmsg . withErrorDialog
delSym r $ easyDelete row >> refreshTreeView mygui myview Nothing
r@(_ :/ RegFile { regFile = FileInfo { isSymbolicLink = True } }) ->
delSym r
r@(_ :/ Dir _ _) -> do
let fp = fullPath r
subADT <- readDirectory fp
let cmsg = "Really delete directory \"" ++ fp ++ "\"?"
cmsg2 = "Directory \"" ++ fp ++
"\" is not empty! Delete all contents?"
withConfirmationDialog cmsg $
if null subADT
then withErrorDialog (deleteDir fp
>> refreshTreeView mygui myview Nothing)
else withConfirmationDialog cmsg2 $ withErrorDialog
(deleteDirRecursive fp
>> refreshTreeView mygui myview Nothing)
r@(_ :/ RegFile _ _) -> do
let fp = fullPath r
cmsg = "Really delete file \"" ++ fp ++ "\"?"
withConfirmationDialog cmsg
$ withErrorDialog (deleteFile fp
>> refreshTreeView mygui myview Nothing)
where
delSym r = do
let fp = fullPath r
cmsg = "Really delete symlink \"" ++ fp ++ "\"?"
withConfirmationDialog cmsg
$ withErrorDialog (deleteSymlink fp
>> refreshTreeView mygui myview Nothing)
-- |Supposed to be used with 'withRow'. Initializes a file copy operation. -- |Supposed to be used with 'withRow'. Initializes a file copy operation.
@ -154,7 +129,7 @@ del row mygui myview =
-- * 'operationBuffer' writes -- * 'operationBuffer' writes
copyInit :: Row -> MyGUI -> MyView -> IO () copyInit :: Row -> MyGUI -> MyView -> IO ()
copyInit row mygui myview = copyInit row mygui myview =
writeTVarIO (operationBuffer myview) (FCopy . CP1 $ fullPath row) writeTVarIO (operationBuffer myview) (FCopy . CP1 $ row)
-- |Finalizes a file copy operation. -- |Finalizes a file copy operation.
@ -165,17 +140,18 @@ copyInit row mygui myview =
copyFinal :: MyGUI -> MyView -> IO () copyFinal :: MyGUI -> MyView -> IO ()
copyFinal mygui myview = do copyFinal mygui myview = do
op <- readTVarIO (operationBuffer myview) op <- readTVarIO (operationBuffer myview)
mcdir <- getCwdFromFirstRow myview mcdir <- getFirstRow myview
case op of case op of
FCopy (CP1 source) -> do FCopy (CP1 s) -> do
let dest = mcdir dest <- goUp mcdir
isFile <- doesFileExist source print dest
let cmsg = "Really copy file \"" ++ source print s
++ "\"" ++ " to \"" ++ dest ++ "\"?" let cmsg = "Really copy \"" ++ fullPath s
withConfirmationDialog cmsg $ do ++ "\"" ++ " to \"" ++ fullPath dest ++ "\"?"
copyMode <- if isFile then return Strict else showCopyModeChooserDialog withConfirmationDialog cmsg . withErrorDialog
withErrorDialog ((runFileOp . FCopy . CC source dest $ copyMode) $ (runFileOp (FCopy . CC s dest $ Strict)
>> refreshTreeView mygui myview Nothing) >> refreshTreeView mygui myview Nothing)
return ()
_ -> return () _ -> return ()
@ -187,8 +163,8 @@ copyFinal mygui myview = do
-- * 'sortedModel' reads -- * 'sortedModel' reads
upDir :: MyGUI -> MyView -> IO () upDir :: MyGUI -> MyView -> IO ()
upDir mygui myview = do upDir mygui myview = do
mcdir <- getCwdFromFirstRow myview mcdir <- getFirstRow myview
rawModel' <- readTVarIO $ rawModel myview rawModel' <- readTVarIO $ rawModel myview
sortedModel' <- readTVarIO $ sortedModel myview sortedModel' <- readTVarIO $ sortedModel myview
nv <- goUp' mcdir nv <- goUp =<< goUp mcdir
refreshTreeView' mygui myview nv refreshTreeView' mygui myview nv

View File

@ -99,13 +99,12 @@ fileListStore dt myview = do
-- Interaction with mutable references: -- Interaction with mutable references:
-- --
-- * 'rawModel' reads -- * 'rawModel' reads
getCwdFromFirstRow :: MyView getFirstRow :: MyView
-> IO FilePath -> IO (AnchoredFile FileInfo FileInfo)
getCwdFromFirstRow myview = do getFirstRow myview = do
rawModel' <- readTVarIO $ rawModel myview rawModel' <- readTVarIO $ rawModel myview
iter <- fromJust <$> treeModelGetIterFirst rawModel' iter <- fromJust <$> treeModelGetIterFirst rawModel'
af <- treeModelGetRow rawModel' iter treeModelGetRow rawModel' iter
return $ anchor af
-- |Re-reads the current directory or the given one and updates the TreeView. -- |Re-reads the current directory or the given one and updates the TreeView.
@ -123,8 +122,8 @@ refreshTreeView :: MyGUI
-> Maybe FilePath -> Maybe FilePath
-> IO () -> IO ()
refreshTreeView mygui myview mfp = do refreshTreeView mygui myview mfp = do
mcdir <- getCwdFromFirstRow myview mcdir <- getFirstRow myview
let fp = fromMaybe mcdir mfp let fp = fromMaybe (anchor mcdir) mfp
-- TODO catch exceptions -- TODO catch exceptions
dirSanityThrow fp dirSanityThrow fp
@ -170,10 +169,10 @@ constructTreeView mygui myview = do
cMD' = cMD mygui cMD' = cMD mygui
render' = renderTxt mygui render' = renderTxt mygui
mcdir <- getCwdFromFirstRow myview mcdir <- getFirstRow myview
-- update urlBar -- update urlBar
entrySetText (urlBar mygui) mcdir entrySetText (urlBar mygui) (anchor mcdir)
rawModel' <- readTVarIO $ rawModel myview rawModel' <- readTVarIO $ rawModel myview

View File

@ -77,23 +77,27 @@ import qualified System.Posix.Files as PF
-- or delay operations. -- or delay operations.
data FileOperation = FCopy Copy data FileOperation = FCopy Copy
| FMove Move | FMove Move
| FDelete FilePath | FDelete (AnchoredFile FileInfo FileInfo)
| FOpen FilePath | FOpen (AnchoredFile FileInfo FileInfo)
| FExecute FilePath [String] | FExecute (AnchoredFile FileInfo FileInfo) [String]
| None | None
-- |Data type describing partial or complete file copy operation. -- |Data type describing partial or complete file copy operation.
-- CC stands for a complete operation and can be used for `runFileOp`. -- CC stands for a complete operation and can be used for `runFileOp`.
data Copy = CP1 FilePath data Copy = CP1 (AnchoredFile FileInfo FileInfo)
| CP2 FilePath FilePath | CP2 (AnchoredFile FileInfo FileInfo)
| CC FilePath FilePath DirCopyMode (AnchoredFile FileInfo FileInfo)
| CC (AnchoredFile FileInfo FileInfo)
(AnchoredFile FileInfo FileInfo)
DirCopyMode
-- |Data type describing partial or complete file move operation. -- |Data type describing partial or complete file move operation.
-- MC stands for a complete operation and can be used for `runFileOp`. -- MC stands for a complete operation and can be used for `runFileOp`.
data Move = MP1 FilePath data Move = MP1 (AnchoredFile FileInfo FileInfo)
| MC FilePath FilePath | MC (AnchoredFile FileInfo FileInfo)
(AnchoredFile FileInfo FileInfo)
-- |Directory copy modes. -- |Directory copy modes.
@ -142,34 +146,36 @@ runFileOp _ = return Nothing
-- `removeDirectoryRecursive`, `recreateSymlink`, `copyDir`, -- `removeDirectoryRecursive`, `recreateSymlink`, `copyDir`,
-- `copyFileToDir`, `getDirectoryContents` throws -- `copyFileToDir`, `getDirectoryContents` throws
copyDir :: DirCopyMode copyDir :: DirCopyMode
-> FilePath -- ^ source dir -> AnchoredFile FileInfo FileInfo -- ^ source dir
-> FilePath -- ^ destination dir -> AnchoredFile FileInfo FileInfo -- ^ destination dir
-> IO () -> IO ()
copyDir cm from' to' = do copyDir cm from@(_ :/ Dir _ FileInfo { isSymbolicLink = True }) _
from <- canonicalizePath' from' = return ()
to <- canonicalizePath' to' copyDir cm from@(_ :/ Dir fromn _)
onSymlinkOr from (copyFileToDir from to) (go from to) to@(_ :/ Dir {})
= do
let fromp = fullPath from
top = fullPath to
destdirp = fullPath to </> fromn
print destdirp
throwDestinationInSource fromp destdirp
throwSameFile fromp destdirp
createDestdir destdirp
destdir <- Data.DirTree.readFile destdirp
contents <- readDirectory' (fullPath from)
for_ contents $ \f ->
case f of
(_ :/ Dir _ FileInfo { isSymbolicLink = True }) ->
recreateSymlink f destdir
(_ :/ RegFile _ FileInfo { isSymbolicLink = True }) ->
recreateSymlink f destdir
(_ :/ Dir {}) -> copyDir cm f destdir
(_ :/ RegFile {}) -> copyFileToDir f destdir
_ -> return ()
where where
go from to = do
let fn = takeFileName from
destdir = to </> fn
dirSanityThrow from
dirSanityThrow to
throwDestinationInSource from to
throwSameFile from destdir
createDestdir destdir
contents <- getDirsFiles from
for_ contents $ \f -> do
let ffn = from </> f
fs <- PF.getSymbolicLinkStatus ffn
case (PF.isSymbolicLink fs, PF.isDirectory fs) of
(True, _) -> recreateSymlink' destdir f ffn
(_, True) -> copyDir cm ffn destdir
(_, _) -> copyFileToDir ffn destdir
createDestdir destdir = createDestdir destdir =
case cm of case cm of
Merge -> Merge ->
@ -180,31 +186,38 @@ copyDir cm from' to' = do
Replace -> do Replace -> do
whenM (doesDirectoryExist destdir) (removeDirectoryRecursive destdir) whenM (doesDirectoryExist destdir) (removeDirectoryRecursive destdir)
createDirectory destdir createDirectory destdir
recreateSymlink' destdir n f = do recreateSymlink' f destdir = do
let sympoint = destdir </> n let destfilep = fullPath destdir </> (name . file $ f)
destfile <- Data.DirTree.readFile destfilep
_ <- case cm of _ <- case cm of
-- delete old file/dir to be able to create symlink -- delete old file/dir to be able to create symlink
Merge -> easyDelete sympoint Merge -> easyDelete destfile
_ -> return () _ -> return ()
recreateSymlink f sympoint recreateSymlink f destdir
copyDir _ _ _ = return ()
-- |Recreate a symlink. -- |Recreate a symlink.
recreateSymlink :: FilePath -- ^ the old symlink file recreateSymlink :: AnchoredFile FileInfo FileInfo -- ^ the old symlink file
-> FilePath -- ^ destination of the new symlink file -> AnchoredFile FileInfo FileInfo -- ^ destination dir of the
-- new symlink file
-> IO () -> IO ()
recreateSymlink symf' symdest' = do recreateSymlink symf@(_ :/ RegFile n FileInfo { isSymbolicLink = True })
symf <- canonicalizePath' symf' symdest@(_ :/ Dir {})
symname <- readSymbolicLink symf = do
symdestd <- canonicalizePath' (baseDir symdest') symname <- readSymbolicLink (fullPath symf)
let symdest = symdestd </> takeFileName symdest' createSymbolicLink symname (fullPath symdest </> n)
createSymbolicLink symname symdest recreateSymlink symf@(_ :/ Dir n FileInfo { isSymbolicLink = True })
symdest@(_ :/ Dir {})
= do
symname <- readSymbolicLink (fullPath symf)
createSymbolicLink symname (fullPath symdest </> n)
recreateSymlink _ _ = return ()
-- |Copies the given file. This can also be called on symlinks. -- |Copies the given file to the given file destination. Not symlinks.
-- --
-- The operation may fail with: -- The operation may fail with:
-- --
@ -215,44 +228,57 @@ recreateSymlink symf' symdest' = do
-- * `PathNotAbsolute` if either of the filepaths are not absolute -- * `PathNotAbsolute` if either of the filepaths are not absolute
-- * `SameFile` if the source and destination files are the same -- * `SameFile` if the source and destination files are the same
-- * anything that `canonicalizePath` or `System.Directory.copyFile` throws -- * anything that `canonicalizePath` or `System.Directory.copyFile` throws
copyFile :: FilePath -- ^ source file copyFile :: AnchoredFile FileInfo FileInfo -- ^ source file
-> FilePath -- ^ destination file -> AnchoredFile FileInfo FileInfo -- ^ destination file
-> IO () -> IO ()
copyFile from' to' = do copyFile from@(_ :/ RegFile _ FileInfo { isSymbolicLink = True }) _ = return ()
from <- canonicalizePath' from' copyFile from@(_ :/ Dir _ FileInfo { isSymbolicLink = True }) _ = return ()
tod <- canonicalizePath' (baseDir to') copyFile from@(_ :/ RegFile {}) to@(_ :/ RegFile {}) = do
let to = tod </> takeFileName to' let from' = fullPath from
onSymlinkOr from (recreateSymlink from to) $ do to' = fullPath to
fileSanityThrow from throwSameFile from' to'
throwNotAbsolute to SD.copyFile from' to'
throwDirDoesExist to copyFile _ _ = return ()
toC <- canonicalizePath' (takeDirectory to)
let to' = toC </> takeFileName to
throwSameFile from to'
SD.copyFile from to'
-- |Copies the given file to the given dir with the same filename. -- |Copies the given file to the given dir with the same filename.
-- This can also be called on symlinks. -- This can also be called on symlinks.
-- copyFileToDir :: AnchoredFile FileInfo FileInfo
-- The operation may fail with: -> AnchoredFile FileInfo FileInfo
-- -> IO ()
-- * `DirDoesNotExist` if the target directory does not exist copyFileToDir from@(_ :/ RegFile _ FileInfo { isSymbolicLink = True }) _
-- * `PathNotAbsolute` if the target directory is not absolute = return ()
-- * anything that `copyFile` throws copyFileToDir from@(_ :/ Dir _ FileInfo { isSymbolicLink = True }) _
copyFileToDir :: FilePath -> FilePath -> IO () = return ()
copyFileToDir from' to' = do copyFileToDir from@(_ :/ RegFile fn _)
from <- canonicalizePath' from' to@(_ :/ Dir {}) =
to <- canonicalizePath' to' do
let name = takeFileName from let from' = fullPath from
dirSanityThrow to to' = fullPath to </> fn
copyFile from (to </> name) SD.copyFile from' to'
copyFileToDir _ _ = return ()
easyCopy :: DirCopyMode -> FilePath -> FilePath -> IO () easyCopy :: DirCopyMode
easyCopy cm from to = onDirOrFile from (copyDir cm from to) -> AnchoredFile FileInfo FileInfo
(copyFileToDir from to) -> AnchoredFile FileInfo FileInfo
-> IO ()
easyCopy _ from@(_ :/ RegFile _ FileInfo { isSymbolicLink = True })
to@(_ :/ Dir {})
= recreateSymlink from to
easyCopy _ from@(_ :/ Dir _ FileInfo { isSymbolicLink = True })
to@(_ :/ Dir {})
= recreateSymlink from to
easyCopy _ from@(_ :/ RegFile fn _)
to@(_ :/ Dir {})
= copyFileToDir from to
easyCopy _ from@(_ :/ RegFile fn _)
to@(_ :/ RegFile {})
= copyFile from to
easyCopy cm from@(_ :/ Dir fn _)
to@(_ :/ Dir {})
= copyDir cm from to
easyCopy _ _ _ = return ()
@ -262,77 +288,57 @@ easyCopy cm from to = onDirOrFile from (copyDir cm from to)
-- |Deletes a symlink, which can either point to a file or directory. -- |Deletes a symlink, which can either point to a file or directory.
-- deleteSymlink :: AnchoredFile FileInfo FileInfo -> IO ()
-- The operation may fail with: deleteSymlink f@(_ :/ RegFile _ FileInfo { isSymbolicLink = True })
-- = removeFile (fullPath f)
-- * `dirSanityThrow` deleteSymlink f@(_ :/ Dir _ FileInfo { isSymbolicLink = True })
-- * `fileSanityThrow` = removeFile (fullPath f)
deleteSymlink :: FilePath -> IO () deleteSymlink _
deleteSymlink fp' = do = return ()
fp <- canonicalizePath' fp'
onDirOrFile fp (dirSanityThrow fp >> removeFile fp)
(fileSanityThrow fp >> removeFile fp)
-- |Deletes the given file. -- |Deletes the given file, never symlinks.
-- deleteFile :: AnchoredFile FileInfo FileInfo -> IO ()
-- The operation may fail with: deleteFile f@(_ :/ RegFile _ FileInfo { isSymbolicLink = True })
-- = return ()
-- * `FileDoesNotExist` if the file does not exist deleteFile f@(_ :/ RegFile {})
-- * `PathNotAbsolute` if the file is not absolute = removeFile (fullPath f)
-- * anything that `removeFile` throws deleteFile _
deleteFile :: FilePath -> IO () = return ()
deleteFile fp' = do
fp <- canonicalizePath' fp'
fileSanityThrow fp
throwIsSymlink fp
removeFile fp
-- |Deletes the given directory. -- |Deletes the given directory, never symlinks.
-- deleteDir :: AnchoredFile FileInfo FileInfo -> IO ()
-- The operation may fail with: deleteDir f@(_ :/ Dir _ FileInfo { isSymbolicLink = True })
-- = return ()
-- * `DirDoesNotExist` if the dir does not exist deleteDir f@(_ :/ Dir {})
-- * `PathNotAbsolute` if the dir is not absolute = removeDirectory (fullPath f)
-- * anything that `removeDirectory` throws deleteDir _ = return ()
deleteDir :: FilePath -> IO ()
deleteDir fp' =
onSymlinkOr fp' (deleteFile fp') $ do
fp <- canonicalizePath' fp'
dirSanityThrow fp
throwIsSymlink fp
removeDirectory fp
-- |Deletes the given directory recursively. -- |Deletes the given directory recursively, never symlinks.
-- deleteDirRecursive :: AnchoredFile FileInfo FileInfo -> IO ()
-- The operation may fail with: deleteDirRecursive f@(_ :/ Dir _ FileInfo { isSymbolicLink = True })
-- = return ()
-- * `DirDoesNotExist` if the dir does not exist deleteDirRecursive f@(_ :/ Dir {})
-- * `PathNotAbsolute` if the dir is not absolute = removeDirectoryRecursive (fullPath f)
-- * anything that `removeDirectoryRecursive` throws deleteDirRecursive _ = return ()
deleteDirRecursive :: FilePath -> IO ()
deleteDirRecursive fp' =
onSymlinkOr fp' (deleteFile fp') $ do
fp <- canonicalizePath' fp'
dirSanityThrow fp
throwIsSymlink fp
removeDirectoryRecursive fp
-- |Deletes a file, directory or symlink, whatever it may be. -- |Deletes a file, directory or symlink, whatever it may be.
-- -- In case of directory, performs recursive deletion.
-- The operation may fail with: easyDelete :: AnchoredFile FileInfo FileInfo -> IO ()
-- easyDelete f@(_ :/ RegFile _ FileInfo { isSymbolicLink = True })
-- * `DirDoesNotExist`/`FileDoesNotExist` if the file/dir does not exist = deleteSymlink f
-- * `PathNotAbsolute` if the file/dir is not absolute easyDelete f@(_ :/ Dir _ FileInfo { isSymbolicLink = True })
-- * anything that `deleteDir`/`deleteFile` throws = deleteSymlink f
easyDelete :: FilePath -> IO () easyDelete f@(_ :/ RegFile {})
easyDelete fp' = do = deleteFile f
fp <- canonicalizePath' fp' easyDelete f@(_ :/ Dir {})
onSymlinkOr fp (deleteSymlink fp) $ = deleteDirRecursive f
onDirOrFile fp (deleteDir fp) (deleteFile fp) easyDelete _
= return ()
@ -347,12 +353,9 @@ easyDelete fp' = do
-- --
-- * `FileDoesNotExist` if the file does not exist -- * `FileDoesNotExist` if the file does not exist
-- * `PathNotAbsolute` if the file is not absolute -- * `PathNotAbsolute` if the file is not absolute
openFile :: FilePath openFile :: AnchoredFile a b
-> IO ProcessHandle -> IO ProcessHandle
openFile fp' = do openFile f = spawnProcess "xdg-open" [fullPath f]
fp <- canonicalizePath' fp'
fileSanityThrow fp
spawnProcess "xdg-open" [fp]
-- |Executes a program with the given arguments. -- |Executes a program with the given arguments.
@ -362,53 +365,10 @@ openFile fp' = do
-- * `FileDoesNotExist` if the program does not exist -- * `FileDoesNotExist` if the program does not exist
-- * `PathNotAbsolute` if the program is not absolute -- * `PathNotAbsolute` if the program is not absolute
-- * `FileNotExecutable` if the program is not executable -- * `FileNotExecutable` if the program is not executable
executeFile :: FilePath -- ^ program executeFile :: AnchoredFile FileInfo FileInfo -- ^ program
-> [String] -- ^ arguments -> [String] -- ^ arguments
-> IO ProcessHandle -> IO (Maybe ProcessHandle)
executeFile prog' args = do executeFile prog@(_ :/ RegFile _ FileInfo { permissions = perms }) args
prog <- canonicalizePath' prog' | executable perms = Just <$> spawnProcess (fullPath prog) args
fileSanityThrow prog | otherwise = return Nothing
unlessM (fileAccess prog False False True) (throw $ FileNotExecutable prog) executeFile _ _ = return Nothing
spawnProcess prog args
--------------------
--[ Utilities ]--
--------------------
-- |Carries out the given action if the filepath is a symlink. If not,
-- carries out an alternative action.
onSymlinkOr :: FilePath
-> IO () -- ^ action if symlink
-> IO () -- ^ action if not symlink
-> IO ()
onSymlinkOr fp a1 a2 = do
isSymlink <- PF.isSymbolicLink <$> PF.getSymbolicLinkStatus fp
if isSymlink then a1 else a2
-- |Executes either a directory or file related IO action, depending on
-- the input filepath.
--
-- The operation may fail with:
--
-- * `throwFileDoesNotExist` if the filepath is neither a file or directory
onDirOrFile :: FilePath
-> IO () -- ^ action if directory
-> IO () -- ^ action if file
-> IO ()
onDirOrFile fp' iod iof = do
fp <- canonicalizePath' fp'
isD <- doesDirectoryExist fp
isF <- doesFileExist fp
case (isD, isF) of
(True, False) -> do
dirSanityThrow fp
iod
(False, True) -> do
fileSanityThrow fp
iof
_ -> throwFileDoesNotExist fp