LIB/GTK: use AnchoredFile in IO.File
What's the point of having it anyway if we don't?
This commit is contained in:
parent
06151a3a08
commit
2486d83260
@ -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 ]--
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
350
src/IO/File.hs
350
src/IO/File.hs
@ -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
|
|
||||||
|
Loading…
Reference in New Issue
Block a user