Skip to content

Instantly share code, notes, and snippets.

@gregorycollins
Last active August 29, 2015 14:01
Show Gist options
  • Save gregorycollins/00c51e7e33cf1f9c8cc0 to your computer and use it in GitHub Desktop.
Save gregorycollins/00c51e7e33cf1f9c8cc0 to your computer and use it in GitHub Desktop.
Directory traversal with io-streams
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Control.Exception as E
import Control.Monad ((>=>))
import qualified Data.ByteString.Char8 as S
import System.IO.Streams (InputStream)
import qualified System.IO.Streams as Streams
import System.Posix.ByteString.FilePath (RawFilePath)
import qualified System.Posix.Directory.ByteString as D
traverseDirectory :: RawFilePath -> (InputStream RawFilePath -> IO a) -> IO a
traverseDirectory fp m = E.bracket (D.openDirStream fp) D.closeDirStream go
where
go d = Streams.makeInputStream (readDirStr d) >>= m
readDirStr d = do s <- D.readDirStream d
return $! if S.null s then Nothing else Just s
main :: IO ()
main = traverseDirectory "." $ Streams.map (`S.append` "\n") >=>
Streams.connectTo Streams.stdout
@snoyberg
Copy link

Just in case this code is going to be used in production somewhere: the deallocation code is not exception safe. If one of the closedir calls fails, then all succeeding calls will not be called. In practice, it's likely impossible for that situation to arise, since (according to the man page) the only time closedir will fail is when it's passed an invalid structure, but I'd still be cautious of it.

@gregorycollins
Copy link
Author

Yes, if closedir ever fails, you already have memory corruption. Otherwise this pattern would be unsafe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment