LIB: various cleanups
This commit is contained in:
parent
bad817d32d
commit
038b0d0377
@ -158,14 +158,19 @@ doesDirectoryExist fp =
|
|||||||
return $ PF.isDirectory fs
|
return $ PF.isDirectory fs
|
||||||
|
|
||||||
|
|
||||||
|
-- |Checks whether the directory at the given path exists and can be
|
||||||
|
-- opened. This invokes `openDirStream`.
|
||||||
canOpenDirectory :: Path Abs -> IO Bool
|
canOpenDirectory :: Path Abs -> IO Bool
|
||||||
canOpenDirectory fp =
|
canOpenDirectory fp =
|
||||||
handleIOError (\_ -> return False) $ do
|
handleIOError (\_ -> return False) $ do
|
||||||
dirstream <- PFD.openDirStream . P.fromAbs $ fp
|
bracket (PFD.openDirStream . P.fromAbs $ fp)
|
||||||
PFD.closeDirStream dirstream
|
PFD.closeDirStream
|
||||||
|
(\_ -> return ())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
-- |Throws a `Can'tOpenDirectory` FmIOException if the directory at the given
|
||||||
|
-- path cannot be opened.
|
||||||
throwCantOpenDirectory :: Path Abs -> IO ()
|
throwCantOpenDirectory :: Path Abs -> IO ()
|
||||||
throwCantOpenDirectory fp =
|
throwCantOpenDirectory fp =
|
||||||
unlessM (canOpenDirectory fp)
|
unlessM (canOpenDirectory fp)
|
||||||
@ -194,6 +199,7 @@ catchErrno en a1 a2 =
|
|||||||
else ioError e
|
else ioError e
|
||||||
|
|
||||||
|
|
||||||
|
-- |Like `catchIOError`, with arguments swapped.
|
||||||
handleIOError :: (IOError -> IO a) -> IO a -> IO a
|
handleIOError :: (IOError -> IO a) -> IO a -> IO a
|
||||||
handleIOError a1 a2 = catchIOError a2 a1
|
handleIOError a1 a2 = catchIOError a2 a1
|
||||||
|
|
||||||
|
@ -32,12 +32,13 @@ module HSFM.FileSystem.FileOperations where
|
|||||||
|
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
(
|
(
|
||||||
throw
|
bracket
|
||||||
, onException
|
, throw
|
||||||
)
|
)
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
(
|
(
|
||||||
unless
|
unless
|
||||||
|
, void
|
||||||
)
|
)
|
||||||
import Data.ByteString
|
import Data.ByteString
|
||||||
(
|
(
|
||||||
@ -185,7 +186,7 @@ copyDir cm from@(_ :/ Dir _ FileInfo{ fileMode = fmode })
|
|||||||
createDestdir destdirp fmode
|
createDestdir destdirp fmode
|
||||||
destdir <- HSFM.FileSystem.FileType.readFileWithFileInfo destdirp
|
destdir <- HSFM.FileSystem.FileType.readFileWithFileInfo destdirp
|
||||||
|
|
||||||
contents <- readDirectoryContents' (fullPath from)
|
contents <- readDirectoryContentsWithFileInfo' (fullPath from)
|
||||||
|
|
||||||
for_ contents $ \f ->
|
for_ contents $ \f ->
|
||||||
case f of
|
case f of
|
||||||
@ -240,9 +241,7 @@ recreateSymlink _ _ _ _ = throw $ InvalidOperation "wrong input type"
|
|||||||
|
|
||||||
|
|
||||||
-- |TODO: handle EAGAIN exception for non-blocking IO
|
-- |TODO: handle EAGAIN exception for non-blocking IO
|
||||||
-- TODO: implement for non-regular file? This would deprecate the logic
|
-- |Copies the given regular file to the given dir with the given filename.
|
||||||
-- in copyDir
|
|
||||||
-- |Copies the given file to the given dir with the given filename.
|
|
||||||
-- Excludes symlinks.
|
-- Excludes symlinks.
|
||||||
copyFile :: CopyMode
|
copyFile :: CopyMode
|
||||||
-> AnchoredFile FileInfo -- ^ source file
|
-> AnchoredFile FileInfo -- ^ source file
|
||||||
@ -265,16 +264,16 @@ copyFile cm from@(_ :/ RegFile {}) to@(_ :/ Dir {}) fn
|
|||||||
throwCantOpenDirectory . fullPath $ to
|
throwCantOpenDirectory . fullPath $ to
|
||||||
fromFstatus <- getSymbolicLinkStatus (P.fromAbs from')
|
fromFstatus <- getSymbolicLinkStatus (P.fromAbs from')
|
||||||
fromContent <- readFileContents from
|
fromContent <- readFileContents from
|
||||||
fd <- SPI.createFile (P.fromAbs to')
|
bracket (SPI.createFile (P.fromAbs to')
|
||||||
(System.Posix.Files.ByteString.fileMode fromFstatus)
|
$ System.Posix.Files.ByteString.fileMode fromFstatus)
|
||||||
_ <- onException (fdWrite fd fromContent) (SPI.closeFd fd)
|
SPI.closeFd
|
||||||
SPI.closeFd fd
|
(\fd -> void $ fdWrite fd fromContent)
|
||||||
|
|
||||||
copyFile _ _ _ _ = throw $ InvalidOperation "wrong input type"
|
copyFile _ _ _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Copies a file, directory or symlink. In case of a symlink, it is just
|
-- |Copies a regular file, directory or symlink. In case of a symlink,
|
||||||
-- recreated, even if it points to a directory.
|
-- it is just recreated, even if it points to a directory.
|
||||||
easyCopy :: CopyMode
|
easyCopy :: CopyMode
|
||||||
-> AnchoredFile FileInfo
|
-> AnchoredFile FileInfo
|
||||||
-> AnchoredFile FileInfo
|
-> AnchoredFile FileInfo
|
||||||
@ -307,7 +306,7 @@ deleteSymlink f@(_ :/ SymLink {})
|
|||||||
deleteSymlink _ = throw $ InvalidOperation "wrong input type"
|
deleteSymlink _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given file, never symlinks.
|
-- |Deletes the given regular file, never symlinks.
|
||||||
deleteFile :: AnchoredFile FileInfo -> IO ()
|
deleteFile :: AnchoredFile FileInfo -> IO ()
|
||||||
deleteFile AFileInvFN = throw InvalidFileName
|
deleteFile AFileInvFN = throw InvalidFileName
|
||||||
deleteFile f@(_ :/ RegFile {})
|
deleteFile f@(_ :/ RegFile {})
|
||||||
@ -329,7 +328,7 @@ deleteDirRecursive AFileInvFN = throw InvalidFileName
|
|||||||
deleteDirRecursive f@(_ :/ Dir {}) = do
|
deleteDirRecursive f@(_ :/ Dir {}) = do
|
||||||
let fp = fullPath f
|
let fp = fullPath f
|
||||||
throwCantOpenDirectory fp
|
throwCantOpenDirectory fp
|
||||||
files <- readDirectoryContents' fp
|
files <- readDirectoryContentsWithFileInfo' fp
|
||||||
for_ files $ \file ->
|
for_ files $ \file ->
|
||||||
case file of
|
case file of
|
||||||
(_ :/ SymLink {}) -> deleteSymlink file
|
(_ :/ SymLink {}) -> deleteSymlink file
|
||||||
@ -361,7 +360,8 @@ easyDelete _ = throw $ InvalidOperation "wrong input type"
|
|||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
|
||||||
-- |Opens a file appropriately by invoking xdg-open.
|
-- |Opens a file appropriately by invoking xdg-open. The file type
|
||||||
|
-- is not checked.
|
||||||
openFile :: AnchoredFile a
|
openFile :: AnchoredFile a
|
||||||
-> IO ProcessID
|
-> IO ProcessID
|
||||||
openFile AFileInvFN = throw InvalidFileName
|
openFile AFileInvFN = throw InvalidFileName
|
||||||
@ -376,6 +376,8 @@ executeFile :: AnchoredFile FileInfo -- ^ program
|
|||||||
executeFile AFileInvFN _ = throw InvalidFileName
|
executeFile AFileInvFN _ = throw InvalidFileName
|
||||||
executeFile prog@(_ :/ RegFile {}) args
|
executeFile prog@(_ :/ RegFile {}) args
|
||||||
= SPP.forkProcess $ SPP.executeFile (fullPathS prog) True args Nothing
|
= SPP.forkProcess $ SPP.executeFile (fullPathS prog) True args Nothing
|
||||||
|
executeFile prog@(_ :/ SymLink { sdest = (_ :/ RegFile {}) }) args
|
||||||
|
= SPP.forkProcess $ SPP.executeFile (fullPathS prog) True args Nothing
|
||||||
executeFile _ _ = throw $ InvalidOperation "wrong input type"
|
executeFile _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
@ -386,6 +388,7 @@ executeFile _ _ = throw $ InvalidOperation "wrong input type"
|
|||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- |Create an empty regular file at the given directory with the given filename.
|
||||||
createFile :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
createFile :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
||||||
createFile AFileInvFN _ = throw InvalidFileName
|
createFile AFileInvFN _ = throw InvalidFileName
|
||||||
createFile _ InvFN = throw InvalidFileName
|
createFile _ InvFN = throw InvalidFileName
|
||||||
@ -397,6 +400,7 @@ createFile (ADirOrSym td) (ValFN fn) = do
|
|||||||
createFile _ _ = throw $ InvalidOperation "wrong input type"
|
createFile _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
|
-- |Create an empty directory at the given directory with the given filename.
|
||||||
createDir :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
createDir :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
||||||
createDir AFileInvFN _ = throw InvalidFileName
|
createDir AFileInvFN _ = throw InvalidFileName
|
||||||
createDir _ InvFN = throw InvalidFileName
|
createDir _ InvFN = throw InvalidFileName
|
||||||
@ -414,6 +418,7 @@ createDir _ _ = throw $ InvalidOperation "wrong input type"
|
|||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- |Rename a given file with the provided filename.
|
||||||
renameFile :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
renameFile :: AnchoredFile FileInfo -> Path Fn -> IO ()
|
||||||
renameFile AFileInvFN _ = throw InvalidFileName
|
renameFile AFileInvFN _ = throw InvalidFileName
|
||||||
renameFile _ InvFN = throw InvalidFileName
|
renameFile _ InvFN = throw InvalidFileName
|
||||||
@ -461,6 +466,7 @@ moveFile _ _ _ = throw $ InvalidOperation "wrong input type"
|
|||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- |Default permissions for a new file.
|
||||||
newFilePerms :: FileMode
|
newFilePerms :: FileMode
|
||||||
newFilePerms
|
newFilePerms
|
||||||
= ownerWriteMode
|
= ownerWriteMode
|
||||||
@ -471,6 +477,7 @@ newFilePerms
|
|||||||
`unionFileModes` otherReadMode
|
`unionFileModes` otherReadMode
|
||||||
|
|
||||||
|
|
||||||
|
-- |Default permissions for a new directory.
|
||||||
newDirPerms :: FileMode
|
newDirPerms :: FileMode
|
||||||
newDirPerms
|
newDirPerms
|
||||||
= ownerModes
|
= ownerModes
|
||||||
@ -478,3 +485,4 @@ newDirPerms
|
|||||||
`unionFileModes` groupReadMode
|
`unionFileModes` groupReadMode
|
||||||
`unionFileModes` otherExecuteMode
|
`unionFileModes` otherExecuteMode
|
||||||
`unionFileModes` otherReadMode
|
`unionFileModes` otherReadMode
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||||||
{-# OPTIONS_HADDOCK ignore-exports #-}
|
{-# OPTIONS_HADDOCK ignore-exports #-}
|
||||||
|
|
||||||
-- |This module provides data types for representing directories/files
|
-- |This module provides data types for representing directories/files
|
||||||
-- and related operations on it, mostly internal stuff, not actual IO actions.
|
-- and related operations on it, mostly internal stuff.
|
||||||
--
|
--
|
||||||
-- It doesn't allow to represent the whole filesystem, since that's only
|
-- It doesn't allow to represent the whole filesystem, since that's only
|
||||||
-- possible through IO laziness, which introduces too much internal state.
|
-- possible through IO laziness, which introduces too much internal state.
|
||||||
@ -44,6 +44,7 @@ import Control.Monad.State.Lazy
|
|||||||
|
|
||||||
)
|
)
|
||||||
import Data.ByteString(ByteString)
|
import Data.ByteString(ByteString)
|
||||||
|
import qualified Data.ByteString as B
|
||||||
import Data.Default
|
import Data.Default
|
||||||
import Data.Time.Clock.POSIX
|
import Data.Time.Clock.POSIX
|
||||||
(
|
(
|
||||||
@ -60,11 +61,16 @@ import HPath
|
|||||||
)
|
)
|
||||||
import qualified HPath as P
|
import qualified HPath as P
|
||||||
import HSFM.Utils.MyPrelude
|
import HSFM.Utils.MyPrelude
|
||||||
|
import Prelude hiding(readFile)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
(
|
(
|
||||||
ioeGetErrorType
|
ioeGetErrorType
|
||||||
, isDoesNotExistErrorType
|
, isDoesNotExistErrorType
|
||||||
)
|
)
|
||||||
|
import qualified System.Posix.Directory.ByteString as PFD
|
||||||
|
import qualified System.Posix.Files.ByteString as PF
|
||||||
|
import qualified "unix" System.Posix.IO.ByteString as PIO
|
||||||
|
import qualified "unix-bytestring" System.Posix.IO.ByteString as PIOB
|
||||||
import System.Posix.Types
|
import System.Posix.Types
|
||||||
(
|
(
|
||||||
DeviceID
|
DeviceID
|
||||||
@ -77,11 +83,6 @@ import System.Posix.Types
|
|||||||
, UserID
|
, UserID
|
||||||
)
|
)
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
|
||||||
import qualified System.Posix.Directory.ByteString as PFD
|
|
||||||
import qualified System.Posix.Files.ByteString as PF
|
|
||||||
import qualified "unix" System.Posix.IO.ByteString as PIO
|
|
||||||
import qualified "unix-bytestring" System.Posix.IO.ByteString as PIOB
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -117,7 +118,6 @@ data File a =
|
|||||||
name :: Path Fn
|
name :: Path Fn
|
||||||
, fvar :: a
|
, fvar :: a
|
||||||
}
|
}
|
||||||
-- TODO: add raw symlink dest (not normalized) to SymLink?
|
|
||||||
| SymLink {
|
| SymLink {
|
||||||
name :: Path Fn
|
name :: Path Fn
|
||||||
, fvar :: a
|
, fvar :: a
|
||||||
@ -143,7 +143,7 @@ data File a =
|
|||||||
} deriving (Show, Eq)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
|
|
||||||
-- |All possible file information we could ever need.
|
-- |Low-level file information.
|
||||||
data FileInfo = FileInfo {
|
data FileInfo = FileInfo {
|
||||||
deviceID :: DeviceID
|
deviceID :: DeviceID
|
||||||
, fileID :: FileID
|
, fileID :: FileID
|
||||||
@ -383,11 +383,11 @@ instance Ord (AnchoredFile FileInfo) where
|
|||||||
-- anchor of `AnchoredFile` is always canonicalized.
|
-- anchor of `AnchoredFile` is always canonicalized.
|
||||||
--
|
--
|
||||||
-- Exceptions: when `canonicalizePath` fails, throws IOError
|
-- Exceptions: when `canonicalizePath` fails, throws IOError
|
||||||
readWith :: (Path Abs -> IO a) -- ^ function that fills the free
|
readFile :: (Path Abs -> IO a) -- ^ function that fills the free
|
||||||
-- a variable
|
-- a variable
|
||||||
-> Path Abs -- ^ Path to read
|
-> Path Abs -- ^ Path to read
|
||||||
-> IO (AnchoredFile a)
|
-> IO (AnchoredFile a)
|
||||||
readWith ff p = do
|
readFile ff p = do
|
||||||
let fn = P.basename p
|
let fn = P.basename p
|
||||||
bd = P.dirname p
|
bd = P.dirname p
|
||||||
p' = P.toFilePath p
|
p' = P.toFilePath p
|
||||||
@ -410,7 +410,7 @@ readWith ff p = do
|
|||||||
-- to something like '/' after normalization?
|
-- to something like '/' after normalization?
|
||||||
let sfp = (P.fromAbs bd') `P.combine` x
|
let sfp = (P.fromAbs bd') `P.combine` x
|
||||||
rsfp <- P.realPath sfp
|
rsfp <- P.realPath sfp
|
||||||
readWith ff =<< P.parseAbs rsfp
|
readFile ff =<< P.parseAbs rsfp
|
||||||
return $ SymLink fn' fv resolvedSyml x
|
return $ SymLink fn' fv resolvedSyml x
|
||||||
| PF.isDirectory fs = return $ Dir fn' fv
|
| PF.isDirectory fs = return $ Dir fn' fv
|
||||||
| PF.isRegularFile fs = return $ RegFile fn' fv
|
| PF.isRegularFile fs = return $ RegFile fn' fv
|
||||||
@ -421,40 +421,63 @@ readWith ff p = do
|
|||||||
| otherwise = return $ Failed fn' (userError
|
| otherwise = return $ Failed fn' (userError
|
||||||
"Unknown filetype!")
|
"Unknown filetype!")
|
||||||
|
|
||||||
|
-- |Reads a file via `readFile` and fills the free variable via `getFileInfo`.
|
||||||
-- |Reads a file Path into an AnchoredFile.
|
|
||||||
readFile :: (Path Abs -> IO a) -> Path Abs -> IO (AnchoredFile a)
|
|
||||||
readFile ff fp = readWith ff fp
|
|
||||||
|
|
||||||
readFileWithFileInfo :: Path Abs -> IO (AnchoredFile FileInfo)
|
readFileWithFileInfo :: Path Abs -> IO (AnchoredFile FileInfo)
|
||||||
readFileWithFileInfo = HSFM.FileSystem.FileType.readFile getFileInfo
|
readFileWithFileInfo = readFile getFileInfo
|
||||||
|
|
||||||
|
|
||||||
|
-- |Same as readDirectoryContents but allows us to, for example, use
|
||||||
|
-- ByteString.readFile to return a tree of ByteStrings.
|
||||||
|
readDirectoryContents :: (Path Abs -> IO [Path Fn])
|
||||||
|
-> (Path Abs -> IO a)
|
||||||
|
-> Path Abs
|
||||||
|
-> IO [AnchoredFile a]
|
||||||
|
readDirectoryContents getfiles ff p = do
|
||||||
|
files <- getfiles p
|
||||||
|
fcs <- mapM (\x -> readFile ff $ p P.</> x) files
|
||||||
|
return $ removeNonexistent fcs
|
||||||
|
|
||||||
|
|
||||||
-- |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`. This includes the "." and ".."
|
-- the free variables via `getFileInfo`. This includes the "." and ".."
|
||||||
-- directories.
|
-- directories.
|
||||||
readDirectoryContents :: Path Abs -> IO [AnchoredFile FileInfo]
|
readDirectoryContentsWithFileInfo :: Path Abs -> IO [AnchoredFile FileInfo]
|
||||||
readDirectoryContents fp = readDirectoryContentsWith getAllDirsFiles getFileInfo fp
|
readDirectoryContentsWithFileInfo fp
|
||||||
|
= readDirectoryContents getAllDirsFiles getFileInfo 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`. This excludes the "." and ".."
|
-- the free variables via `getFileInfo`. This excludes the "." and ".."
|
||||||
-- directories.
|
-- directories.
|
||||||
readDirectoryContents' :: Path Abs -> IO [AnchoredFile FileInfo]
|
readDirectoryContentsWithFileInfo' :: Path Abs -> IO [AnchoredFile FileInfo]
|
||||||
readDirectoryContents' fp = readDirectoryContentsWith getDirsFiles getFileInfo fp
|
readDirectoryContentsWithFileInfo' fp
|
||||||
|
= readDirectoryContents getDirsFiles getFileInfo fp
|
||||||
|
|
||||||
|
|
||||||
-- |Same as readDirectoryContents but allows us to, for example, use
|
-- |Get the contents of a directory, including "." and "..".
|
||||||
-- ByteString.readFile to return a tree of ByteStrings.
|
getContents :: AnchoredFile FileInfo
|
||||||
readDirectoryContentsWith :: (Path Abs -> IO [Path Fn])
|
-> IO [AnchoredFile FileInfo]
|
||||||
-> (Path Abs -> IO a)
|
getContents (ADirOrSym af) = readDirectoryContentsWithFileInfo (fullPath af)
|
||||||
-> Path Abs
|
getContents _ = return []
|
||||||
-> IO [AnchoredFile a]
|
|
||||||
readDirectoryContentsWith getfiles ff p = do
|
|
||||||
files <- getfiles p
|
|
||||||
fcs <- mapM (\x -> HSFM.FileSystem.FileType.readFile ff $ p P.</> x) files
|
|
||||||
return $ removeNonexistent fcs
|
|
||||||
|
|
||||||
|
|
||||||
|
-- |Get the contents of a directory, including "." and "..".
|
||||||
|
getContents' :: AnchoredFile FileInfo
|
||||||
|
-> IO [AnchoredFile FileInfo]
|
||||||
|
getContents' (ADirOrSym af) = readDirectoryContentsWithFileInfo' (fullPath af)
|
||||||
|
getContents' _ = return []
|
||||||
|
|
||||||
|
|
||||||
|
-- |Go up one directory in the filesystem hierarchy.
|
||||||
|
goUp :: AnchoredFile FileInfo -> IO (AnchoredFile FileInfo)
|
||||||
|
goUp af@(Path "" :/ _) = return af
|
||||||
|
goUp (bp :/ _) = readFile getFileInfo bp
|
||||||
|
|
||||||
|
|
||||||
|
-- |Go up one directory in the filesystem hierarchy.
|
||||||
|
goUp' :: Path Abs -> IO (AnchoredFile FileInfo)
|
||||||
|
goUp' fp = readFile getFileInfo $ P.dirname fp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -467,22 +490,22 @@ readDirectoryContentsWith getfiles ff p = do
|
|||||||
---- HANDLING FAILURES ----
|
---- HANDLING FAILURES ----
|
||||||
|
|
||||||
|
|
||||||
-- | True if any Failed constructors in the tree
|
-- |True if any Failed constructors in the tree.
|
||||||
anyFailed :: [File a] -> Bool
|
anyFailed :: [File a] -> Bool
|
||||||
anyFailed = not . successful
|
anyFailed = not . successful
|
||||||
|
|
||||||
-- | True if there are no Failed constructors in the tree
|
-- |True if there are no Failed constructors in the tree.
|
||||||
successful :: [File a] -> Bool
|
successful :: [File a] -> Bool
|
||||||
successful = null . failures
|
successful = null . failures
|
||||||
|
|
||||||
|
|
||||||
-- | returns true if argument is a `Failed` constructor:
|
-- |Returns true if argument is a `Failed` constructor.
|
||||||
failed :: File a -> Bool
|
failed :: File a -> Bool
|
||||||
failed (Failed _ _) = True
|
failed (Failed _ _) = True
|
||||||
failed _ = False
|
failed _ = False
|
||||||
|
|
||||||
|
|
||||||
-- | returns a list of 'Failed' constructors only:
|
-- |Returns a list of 'Failed' constructors only.
|
||||||
failures :: [File a] -> [File a]
|
failures :: [File a] -> [File a]
|
||||||
failures = filter failed
|
failures = filter failed
|
||||||
|
|
||||||
@ -515,14 +538,15 @@ comparingConstr t t' = compare (name t) (name t')
|
|||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|
||||||
-- |Follows symbolic links.
|
-- |Reads a file and returns the content as a ByteString.
|
||||||
|
-- Follows symbolic links.
|
||||||
readFileContents :: AnchoredFile a -> IO ByteString
|
readFileContents :: AnchoredFile a -> IO ByteString
|
||||||
readFileContents af@(_ :/ RegFile{}) =
|
readFileContents af@(_ :/ RegFile{}) =
|
||||||
bracket (PIO.openFd f PIO.ReadOnly Nothing PIO.defaultFileFlags)
|
bracket (PIO.openFd f PIO.ReadOnly Nothing PIO.defaultFileFlags)
|
||||||
PIO.closeFd
|
PIO.closeFd
|
||||||
$ \fd -> do
|
$ \fd -> do
|
||||||
filesz <- fmap PF.fileSize $ PF.getFdStatus fd
|
filesz <- fmap PF.fileSize $ PF.getFdStatus fd
|
||||||
PIOB.fdRead fd ((fromIntegral filesz `max` 0) + 1)
|
PIOB.fdRead fd ((fromIntegral filesz `max` 0) + 1)
|
||||||
where
|
where
|
||||||
f = fullPathS af
|
f = fullPathS af
|
||||||
readFileContents _ = return B.empty
|
readFileContents _ = return B.empty
|
||||||
@ -562,7 +586,6 @@ isCharC (CharDev _ _) = True
|
|||||||
isCharC _ = False
|
isCharC _ = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
isNamedC :: File a -> Bool
|
isNamedC :: File a -> Bool
|
||||||
isNamedC (NamedPipe _ _) = True
|
isNamedC (NamedPipe _ _) = True
|
||||||
isNamedC _ = False
|
isNamedC _ = False
|
||||||
@ -578,44 +601,29 @@ isSocketC _ = False
|
|||||||
---- IO HELPERS: ----
|
---- IO HELPERS: ----
|
||||||
|
|
||||||
|
|
||||||
-- |Go up one directory in the filesystem hierarchy.
|
-- |Gets all filenames of the given directory.
|
||||||
goUp :: AnchoredFile FileInfo -> IO (AnchoredFile FileInfo)
|
-- The first argument is a filter function that allows to exclude
|
||||||
goUp af@(Path "" :/ _) = return af
|
-- filenames from the result.
|
||||||
goUp (bp :/ _) = HSFM.FileSystem.FileType.readFile getFileInfo bp
|
getDirsFiles' :: (Path Fn -> [Path Fn] -> [Path Fn]) -- ^ filter function
|
||||||
|
-> Path Abs -- ^ dir to read
|
||||||
|
|
||||||
-- |Go up one directory in the filesystem hierarchy.
|
|
||||||
goUp' :: Path Abs -> IO (AnchoredFile FileInfo)
|
|
||||||
goUp' fp = HSFM.FileSystem.FileType.readFile getFileInfo $ P.dirname fp
|
|
||||||
|
|
||||||
|
|
||||||
-- |Get the contents of a directory.
|
|
||||||
getContents :: AnchoredFile FileInfo
|
|
||||||
-> IO [AnchoredFile FileInfo]
|
|
||||||
getContents (ADirOrSym af) = readDirectoryContents (fullPath af)
|
|
||||||
getContents _ = return []
|
|
||||||
|
|
||||||
|
|
||||||
getDirsFiles' :: (Path Fn -> [Path Fn] -> [Path Fn])
|
|
||||||
-> Path Abs
|
|
||||||
-> IO [Path Fn]
|
-> IO [Path Fn]
|
||||||
getDirsFiles' filterf fp = do
|
getDirsFiles' filterf fp =
|
||||||
dirstream <- PFD.openDirStream . P.toFilePath $ fp
|
bracket (PFD.openDirStream . P.toFilePath $ fp)
|
||||||
let mdirs :: [Path Fn] -> IO [Path Fn]
|
PFD.closeDirStream
|
||||||
mdirs dirs = do
|
$ \dirstream ->
|
||||||
-- make sure we close the directory stream in case of errors
|
let mdirs :: [Path Fn] -> IO [Path Fn]
|
||||||
-- TODO: more explicit error handling?
|
mdirs dirs = do
|
||||||
-- both the parsing and readin the stream can fail!
|
-- make sure we close the directory stream in case of errors
|
||||||
dir <- onException (PFD.readDirStream dirstream)
|
-- TODO: more explicit error handling?
|
||||||
(PFD.closeDirStream dirstream)
|
-- both the parsing and readin the stream can fail!
|
||||||
case dir of
|
dir <- onException (PFD.readDirStream dirstream)
|
||||||
"" -> return dirs
|
(PFD.closeDirStream dirstream)
|
||||||
_ -> do
|
case dir of
|
||||||
pdir <- P.parseFn dir
|
"" -> return dirs
|
||||||
mdirs $ pdir `filterf` dirs
|
_ -> do
|
||||||
dirs <- mdirs []
|
pdir <- P.parseFn dir
|
||||||
PFD.closeDirStream dirstream
|
mdirs $ pdir `filterf` dirs
|
||||||
return dirs
|
in mdirs []
|
||||||
|
|
||||||
|
|
||||||
-- |Get all files of a given directory and return them as a List.
|
-- |Get all files of a given directory and return them as a List.
|
||||||
@ -772,3 +780,4 @@ packPermissions dt = fromFreeVar (pStr . fileMode) dt
|
|||||||
| hasFM fm = str
|
| hasFM fm = str
|
||||||
| otherwise = "-"
|
| otherwise = "-"
|
||||||
hasFM fm = ffm `PF.intersectFileModes` fm == fm
|
hasFM fm = ffm `PF.intersectFileModes` fm == fm
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{--
|
{--
|
||||||
HSFM, a filemanager written in Haskell.
|
HSFM, a filemanager written in Haskell.
|
||||||
Copyright (C) 2015 Julian Ospald
|
Copyright (C) 2016 Julian Ospald
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU General Public License
|
modify it under the terms of the GNU General Public License
|
||||||
@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||||||
|
|
||||||
{-# OPTIONS_HADDOCK ignore-exports #-}
|
{-# OPTIONS_HADDOCK ignore-exports #-}
|
||||||
|
|
||||||
|
|
||||||
-- |Random and general IO utilities.
|
-- |Random and general IO utilities.
|
||||||
module HSFM.Utils.IO where
|
module HSFM.Utils.IO where
|
||||||
|
|
||||||
@ -39,17 +40,23 @@ import Control.Monad
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-- |Atomically write a TVar.
|
||||||
writeTVarIO :: TVar a -> a -> IO ()
|
writeTVarIO :: TVar a -> a -> IO ()
|
||||||
writeTVarIO tvar val = atomically $ writeTVar tvar val
|
writeTVarIO tvar val = atomically $ writeTVar tvar val
|
||||||
|
|
||||||
|
|
||||||
|
-- |Atomically modify a TVar.
|
||||||
modifyTVarIO :: TVar a -> (a -> a) -> IO ()
|
modifyTVarIO :: TVar a -> (a -> a) -> IO ()
|
||||||
modifyTVarIO tvar f = atomically $ modifyTVar tvar f
|
modifyTVarIO tvar f = atomically $ modifyTVar tvar f
|
||||||
|
|
||||||
|
|
||||||
|
-- |If the value of the first argument is True, then execute the action
|
||||||
|
-- provided in the second argument, otherwise do nothing.
|
||||||
whenM :: Monad m => m Bool -> m () -> m ()
|
whenM :: Monad m => m Bool -> m () -> m ()
|
||||||
whenM mb a = mb >>= (`when` a)
|
whenM mb a = mb >>= (`when` a)
|
||||||
|
|
||||||
|
|
||||||
|
-- |If the value of the first argument is False, then execute the action
|
||||||
|
-- provided in the second argument, otherwise do nothing.
|
||||||
unlessM :: Monad m => m Bool -> m () -> m ()
|
unlessM :: Monad m => m Bool -> m () -> m ()
|
||||||
unlessM mb a = mb >>= (`unless` a)
|
unlessM mb a = mb >>= (`unless` a)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{--
|
{--
|
||||||
HSFM, a filemanager written in Haskell.
|
HSFM, a filemanager written in Haskell.
|
||||||
Copyright (C) 2015 Julian Ospald
|
Copyright (C) 2016 Julian Ospald
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU General Public License
|
modify it under the terms of the GNU General Public License
|
||||||
@ -24,6 +24,9 @@ import Data.List
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- |Turns any list into a list of the same length with the values
|
||||||
|
-- being the indices.
|
||||||
|
-- E.g.: "abdasd" -> [0,1,2,3,4,5]
|
||||||
listIndices :: [a] -> [Int]
|
listIndices :: [a] -> [Int]
|
||||||
listIndices = findIndices (const True)
|
listIndices = findIndices (const True)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user