summaryrefslogtreecommitdiff
path: root/Utility/Directory.hs
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2014-07-04 17:59:26 -0400
committerGravatar Joey Hess <joey@kitenet.net>2014-07-04 17:59:26 -0400
commitf33d1d93fa01e16d8b5fa65fb55aafc5114746ac (patch)
tree7d80df3be9a46fe898076812f57f7cf47b9f63a4 /Utility/Directory.hs
parent7d8d873873424326026dc15914787648e83564f4 (diff)
work around getDirectoryContents not streaming lazily
Diffstat (limited to 'Utility/Directory.hs')
-rw-r--r--Utility/Directory.hs44
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