diff options
author | 2014-07-04 17:59:26 -0400 | |
---|---|---|
committer | 2014-07-04 17:59:26 -0400 | |
commit | f33d1d93fa01e16d8b5fa65fb55aafc5114746ac (patch) | |
tree | 7d80df3be9a46fe898076812f57f7cf47b9f63a4 /Utility | |
parent | 7d8d873873424326026dc15914787648e83564f4 (diff) |
work around getDirectoryContents not streaming lazily
Diffstat (limited to 'Utility')
-rw-r--r-- | Utility/Directory.hs | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/Utility/Directory.hs b/Utility/Directory.hs index d92327c09..e3706e785 100644 --- a/Utility/Directory.hs +++ b/Utility/Directory.hs @@ -18,6 +18,12 @@ import System.FilePath import Control.Applicative import System.IO.Unsafe (unsafeInterleaveIO) +#ifdef mingw32_HOST_OS +import qualified System.Win32 as Win32 +#else +import qualified System.Posix as Posix +#endif + import Utility.PosixFiles import Utility.SafeCommand import Utility.Tmp @@ -25,6 +31,44 @@ import Utility.Exception import Utility.Monad import Utility.Applicative +{- Unlike getDirectoryContents, this can be used in arbitrarily + - large directories without using much memory; the list steams lazily. + - + - However, any errors that may be encountered while reading the directory + - contents are *ignored*, rather than throw them in the context of + - whatever code consumes the lazy list. + - + - See https://ghc.haskell.org/trac/ghc/ticket/9266 + -} +getDirectoryContents' :: FilePath -> IO [FilePath] +getDirectoryContents' path = loop =<< opendir + where +#ifndef mingw32_HOST_OS + opendir = Posix.openDirStream path + loop dirp = do + v <- tryNonAsync $ Posix.readDirStream dirp + case v of + (Right ent) | not (null ent) -> do + rest <- unsafeInterleaveIO (loop dirp) + return (ent:rest) + _ -> do + void $ tryNonAsync $ Posix.closeDirStream dirp + return [] +#else + opendir = Win32.findFirstFile (path </> "*") + loop (h, fdat) = do + -- there is always at least 1 file ("." and "..") + ent <- Win32.getFindDataFileName fdat + v <- tryNonAsync $ Win32.findNextFile h fdat + case v of + Right True -> + rest <- unsafeInterleaveIO loop (h, fdat) + return (ent:rest) + _ -> + void $ tryNonAsync $ Win32.findClose h + return [ent] +#endif + dirCruft :: FilePath -> Bool dirCruft "." = True dirCruft ".." = True |