{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Streaming.Filesystem
( DirStream
, openDirStream
, readDirStream
, closeDirStream
, FileType (..)
, getFileType
) where
import Data.Typeable (Typeable)
#if WINDOWS
import qualified System.Win32 as Win32
import System.FilePath ((</>))
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import System.Directory (doesFileExist, doesDirectoryExist)
data DirStream = DirStream !Win32.HANDLE !Win32.FindData !(IORef Bool)
deriving Typeable
openDirStream :: FilePath -> IO DirStream
openDirStream fp = do
(h, fdat) <- Win32.findFirstFile $ fp </> "*"
imore <- newIORef True
return $! DirStream h fdat imore
closeDirStream :: DirStream -> IO ()
closeDirStream (DirStream h _ _) = Win32.findClose h
readDirStream :: DirStream -> IO (Maybe FilePath)
readDirStream ds@(DirStream h fdat imore) = do
more <- readIORef imore
if more
then do
filename <- Win32.getFindDataFileName fdat
Win32.findNextFile h fdat >>= writeIORef imore
if filename == "." || filename == ".."
then readDirStream ds
else return $ Just filename
else return Nothing
isSymlink :: FilePath -> IO Bool
isSymlink _ = return False
getFileType :: FilePath -> IO FileType
getFileType fp = do
isFile <- doesFileExist fp
if isFile
then return FTFile
else do
isDir <- doesDirectoryExist fp
return $ if isDir then FTDirectory else FTOther
#else
import System.Posix.Directory (DirStream, openDirStream, closeDirStream)
import qualified System.Posix.Directory as Posix
import qualified System.Posix.Files as PosixF
import Control.Exception (try, IOException)
readDirStream :: DirStream -> IO (Maybe FilePath)
readDirStream ds = do
fp <- Posix.readDirStream ds
case fp of
"" -> return Nothing
"." -> readDirStream ds
".." -> readDirStream ds
_ -> return $ Just fp
getFileType :: FilePath -> IO FileType
getFileType fp = do
s <- PosixF.getSymbolicLinkStatus fp
case () of
()
| PosixF.isRegularFile s -> return FTFile
| PosixF.isDirectory s -> return FTDirectory
| PosixF.isSymbolicLink s -> do
es' <- try $ PosixF.getFileStatus fp
case es' of
Left (_ :: IOException) -> return FTOther
Right s'
| PosixF.isRegularFile s' -> return FTFileSym
| PosixF.isDirectory s' -> return FTDirectorySym
| otherwise -> return FTOther
| otherwise -> return FTOther
#endif
data FileType
= FTFile
| FTFileSym
| FTDirectory
| FTDirectorySym
| FTOther
deriving (Show, Read, Eq, Ord, Typeable)