{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE OverloadedStrings #-} module Sync.MerkleTree.Analyse ( analyse , isRealFile ) where import Control.Monad import Data.Maybe import System.FilePath import Foreign.C.Types import Prelude hiding (lookup) import Sync.MerkleTree.Types import System.Directory import System.Posix.Types import System.Posix.Files import Text.Regex (matchRegex, mkRegex, Regex) import qualified Data.Text as T -- | Returns True if the given string is not "." or ".." isRealFile :: String -> Bool isRealFile x | x `elem` [".", ".."] = False | otherwise = True shouldIgnore :: Path -> [Regex] -> Bool shouldIgnore p regexes = any (isJust . (`matchRegex` (toFilePath "" p))) regexes -- | Returns all files and directories below the given FilePath analyse :: FilePath -- ^ Root file path to analyse` -> [String] -- ^ List of regular expressions to be excluded from the resulting list -> IO [Entry] -- ^ List of file or directory entries with paths relative to the given root analyse fp ignore = analyseSubDirectory fp (map mkRegex ignore) Root analyseSubDirectory :: FilePath -> [Regex] -> Path -> IO [Entry] analyseSubDirectory fp ignore path = do files <- getDirectoryContents fp liftM concat $ mapM (analyseEntry fp ignore path) $ filter isRealFile files analyseEntry :: FilePath -> [Regex] -> Path -> String -> IO [Entry] analyseEntry fp ignore path name | shouldIgnore path' ignore = return [] | otherwise = do status <- getFileStatus fp' analyse' status where path' = Path (T.pack name) path fp' = fp name analyse' status | isRegularFile status = let CTime modtime = modificationTime status COff filesize = fileSize status in return [ FileEntry $ File { f_name = path' , f_size = FileSize filesize , f_modtime = FileModTime modtime } ] | isDirectory status = liftM ((DirectoryEntry path'):) $ analyseSubDirectory fp' ignore path' | otherwise = return [] -- No support for devices, sockets yet.