add flag to follow symlinks when recursing

This commit is contained in:
rhiannon morris 2021-04-16 21:29:00 +02:00
parent 1ed1659a64
commit 0edef8f7bb
1 changed files with 61 additions and 17 deletions

View File

@ -45,11 +45,15 @@ module System.FilePath.Find (
-- * Simple entry points -- * Simple entry points
, find , find
, findL
, fold , fold
, foldL
-- * More expressive entry points -- * More expressive entry points
, findWithHandler , findWithHandler
, findWithHandlerL
, foldWithHandler , foldWithHandler
, foldWithHandlerL
-- * Helper functions -- * Helper functions
, evalClause , evalClause
@ -215,15 +219,16 @@ getDirContents dir = (sort . filter goodName) `liftM` getDirectoryContents dir
-- 'RecursionPredicate'. Lazily return a sorted list of all files -- 'RecursionPredicate'. Lazily return a sorted list of all files
-- matching the given 'FilterPredicate'. Any errors that occur are -- matching the given 'FilterPredicate'. Any errors that occur are
-- dealt with by the given handler. -- dealt with by the given handler.
findWithHandler :: findWithHandlerL ::
(FilePath -> IOException -> IO [FilePath]) -- ^ error handler Bool -- ^ follow symlinks
-> (FilePath -> IOException -> IO [FilePath]) -- ^ error handler
-> RecursionPredicate -- ^ control recursion into subdirectories -> RecursionPredicate -- ^ control recursion into subdirectories
-> FilterPredicate -- ^ decide whether a file appears in the result -> FilterPredicate -- ^ decide whether a file appears in the result
-> FilePath -- ^ directory to start searching -> FilePath -- ^ directory to start searching
-> IO [FilePath] -- ^ files that matched the 'FilterPredicate' -> IO [FilePath] -- ^ files that matched the 'FilterPredicate'
findWithHandler errHandler recurse filt path0 = findWithHandlerL links errHandler recurse filt path0 =
handle (errHandler path0) $ F.getSymbolicLinkStatus path0 >>= visit path0 0 handle (errHandler path0) $ getStatus links path0 >>= visit path0 0
where visit path depth st = where visit path depth st =
if F.isDirectory st && evalFI recurse path depth st if F.isDirectory st && evalFI recurse path depth st
then unsafeInterleaveIO (traverse path (succ depth) st) then unsafeInterleaveIO (traverse path (succ depth) st)
@ -240,18 +245,38 @@ findWithHandler errHandler recurse filt path0 =
then path:result then path:result
else result else result
getStatus :: Bool -> FilePath -> IO F.FileStatus
getStatus links = if links then F.getFileStatus else F.getSymbolicLinkStatus
-- | Same as 'findWithHandlerL' without following links
findWithHandler ::
(FilePath -> IOException -> IO [FilePath]) -- ^ error handler
-> RecursionPredicate -- ^ control recursion into subdirectories
-> FilterPredicate -- ^ decide whether a file appears in the result
-> FilePath -- ^ directory to start searching
-> IO [FilePath] -- ^ files that matched the 'FilterPredicate'
findWithHandler = findWithHandlerL False
-- | Search a directory recursively, with recursion controlled by a -- | Search a directory recursively, with recursion controlled by a
-- 'RecursionPredicate'. Lazily return a sorted list of all files -- 'RecursionPredicate'. Lazily return a sorted list of all files
-- matching the given 'FilterPredicate'. Any errors that occur are -- matching the given 'FilterPredicate'. Any errors that occur are
-- ignored, with warnings printed to 'stderr'. -- ignored, with warnings printed to 'stderr'.
findL :: Bool -- ^ follow symlinks
-> RecursionPredicate -- ^ control recursion into subdirectories
-> FilterPredicate -- ^ decide whether a file appears in the result
-> FilePath -- ^ directory to start searching
-> IO [FilePath] -- ^ files that matched the 'FilterPredicate'
findL links = findWithHandlerL links warnOnError
where warnOnError path err =
hPutStrLn stderr (path ++ ": " ++ show err) >> return []
-- | Same as 'findL' without following links
find :: RecursionPredicate -- ^ control recursion into subdirectories find :: RecursionPredicate -- ^ control recursion into subdirectories
-> FilterPredicate -- ^ decide whether a file appears in the result -> FilterPredicate -- ^ decide whether a file appears in the result
-> FilePath -- ^ directory to start searching -> FilePath -- ^ directory to start searching
-> IO [FilePath] -- ^ files that matched the 'FilterPredicate' -> IO [FilePath] -- ^ files that matched the 'FilterPredicate'
find = findL False
find = findWithHandler warnOnError
where warnOnError path err =
hPutStrLn stderr (path ++ ": " ++ show err) >> return []
-- | Search a directory recursively, with recursion controlled by a -- | Search a directory recursively, with recursion controlled by a
-- 'RecursionPredicate'. Fold over all files found. Any errors that -- 'RecursionPredicate'. Fold over all files found. Any errors that
@ -260,17 +285,17 @@ find = findWithHandler warnOnError
-- strict in its left argument to avoid space leaks. If you need a -- strict in its left argument to avoid space leaks. If you need a
-- right-to-left fold, use 'foldr' on the result of 'findWithHandler' -- right-to-left fold, use 'foldr' on the result of 'findWithHandler'
-- instead. -- instead.
foldWithHandler foldWithHandlerL
:: (FilePath -> a -> IOException -> IO a) -- ^ error handler :: Bool -- ^ follow symlinks
-> (FilePath -> a -> IOException -> IO a) -- ^ error handler
-> RecursionPredicate -- ^ control recursion into subdirectories -> RecursionPredicate -- ^ control recursion into subdirectories
-> (a -> FileInfo -> a) -- ^ function to fold with -> (a -> FileInfo -> a) -- ^ function to fold with
-> a -- ^ seed value for fold -> a -- ^ seed value for fold
-> FilePath -- ^ directory to start searching -> FilePath -- ^ directory to start searching
-> IO a -- ^ final value after folding -> IO a -- ^ final value after folding
foldWithHandler errHandler recurse f state path = foldWithHandlerL links errHandler recurse f state path =
handle (errHandler path state) $ handle (errHandler path state) $ getStatus links path >>= visit state path 0
F.getSymbolicLinkStatus path >>= visit state path 0
where visit state path depth st = where visit state path depth st =
if F.isDirectory st && evalFI recurse path depth st if F.isDirectory st && evalFI recurse path depth st
then traverse state path (succ depth) st then traverse state path (succ depth) st
@ -284,6 +309,16 @@ foldWithHandler errHandler recurse f state path =
let path = dir </> name let path = dir </> name
in F.getSymbolicLinkStatus path >>= visit state path depth) in F.getSymbolicLinkStatus path >>= visit state path depth)
-- | Same as 'foldWithHandlerL' without following symlinks
foldWithHandler
:: (FilePath -> a -> IOException -> IO a) -- ^ error handler
-> RecursionPredicate -- ^ control recursion into subdirectories
-> (a -> FileInfo -> a) -- ^ function to fold with
-> a -- ^ seed value for fold
-> FilePath -- ^ directory to start searching
-> IO a -- ^ final value after folding
foldWithHandler = foldWithHandlerL False
-- | Search a directory recursively, with recursion controlled by a -- | Search a directory recursively, with recursion controlled by a
-- 'RecursionPredicate'. Fold over all files found. Any errors that -- 'RecursionPredicate'. Fold over all files found. Any errors that
-- occur are ignored, with warnings printed to 'stderr'. The fold -- occur are ignored, with warnings printed to 'stderr'. The fold
@ -291,15 +326,24 @@ foldWithHandler errHandler recurse f state path =
-- in its left argument to avoid space leaks. If you need a -- in its left argument to avoid space leaks. If you need a
-- right-to-left fold, use 'foldr' on the result of 'findWithHandler' -- right-to-left fold, use 'foldr' on the result of 'findWithHandler'
-- instead. -- instead.
foldL :: Bool -- ^ follow symlinks
-> RecursionPredicate
-> (a -> FileInfo -> a)
-> a
-> FilePath
-> IO a
foldL links = foldWithHandlerL links warnOnError
where warnOnError path a err =
hPutStrLn stderr (path ++ ": " ++ show err) >> return a
-- | 'foldL' without following symlinks
fold :: RecursionPredicate fold :: RecursionPredicate
-> (a -> FileInfo -> a) -> (a -> FileInfo -> a)
-> a -> a
-> FilePath -> FilePath
-> IO a -> IO a
fold = foldL False
fold = foldWithHandler warnOnError
where warnOnError path a err =
hPutStrLn stderr (path ++ ": " ++ show err) >> return a
-- | Unconditionally return 'True'. -- | Unconditionally return 'True'.
always :: FindClause Bool always :: FindClause Bool