content: {-# 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)
  