{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-} -- | Streaming functions for interacting with the filesystem. 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 -- always at least two records, "." and ".." 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 -- ^ symlink to file | FTDirectory | FTDirectorySym -- ^ symlink to a directory | FTOther deriving (Show, Read, Eq, Ord, Typeable)