{-# LANGUAGE CPP #-}
module System.FilePath.Glob.Utils
( isLeft, fromLeft
, increasingSeq
, addToRange, inRange, overlap
, dropLeadingZeroes
, pathParts
, nubOrd
, partitionDL
, getRecursiveContents
, catchIO
) where
import Control.Monad (foldM)
import qualified Control.Exception as E
import Data.List ((\\))
import qualified Data.DList as DL
import Data.DList (DList)
import qualified Data.Set as Set
import System.Directory (getDirectoryContents)
import System.FilePath ((</>), isPathSeparator, dropDrive)
import System.IO.Unsafe (unsafeInterleaveIO)
#if mingw32_HOST_OS
import Data.Bits ((.&.))
import System.Win32.Types (withTString)
import System.Win32.File (c_GetFileAttributes, fILE_ATTRIBUTE_DIRECTORY)
#else
import Foreign.C.String (withCString)
import Foreign.Marshal.Alloc (allocaBytes)
import System.FilePath
(isDrive, dropTrailingPathSeparator, addTrailingPathSeparator)
import System.Posix.Internals (sizeof_stat, lstat, s_isdir, st_mode)
#endif
inRange :: Ord a => (a,a) -> a -> Bool
inRange (a,b) c = c >= a && c <= b
overlap :: Ord a => (a,a) -> (a,a) -> Maybe (a,a)
overlap (a,b) (c,d) =
if b >= c
then if b >= d
then if a <= c
then Just (a,b)
else Just (c,b)
else if a <= c
then Just (a,d)
else Just (c,d)
else Nothing
addToRange :: (Ord a, Enum a) => (a,a) -> a -> Maybe (a,a)
addToRange (a,b) c
| inRange (a,b) c = Just (a,b)
| c == pred a = Just (c,b)
| c == succ b = Just (a,c)
| otherwise = Nothing
increasingSeq :: (Eq a, Enum a) => [a] -> ([a],[a])
increasingSeq [] = ([],[])
increasingSeq (x:xs) = go [x] xs
where
go is [] = (is,[])
go is@(i:_) (y:ys) =
if y == succ i
then go (y:is) ys
else (is, y:ys)
go _ _ = error "Glob.increasingSeq :: internal error"
isLeft :: Either a b -> Bool
isLeft (Left _) = True
isLeft _ = False
fromLeft :: Either a b -> a
fromLeft (Left x) = x
fromLeft _ = error "fromLeft :: Right"
dropLeadingZeroes :: String -> String
dropLeadingZeroes s =
let x = dropWhile (=='0') s
in if null x then "0" else x
pathParts :: FilePath -> [FilePath]
pathParts p = p : let d = dropDrive p
in if null d || d == p
then f d
else d : f d
where
f [] = []
f (x:xs@(y:_)) | isPathSeparator x && isPathSeparator y = f xs
f (x:xs) =
if isPathSeparator x
then xs : f xs
else f xs
doesDirectoryExist :: FilePath -> IO Bool
#if mingw32_HOST_OS
doesDirectoryExist = flip withTString $ \s -> do
a <- c_GetFileAttributes s
return (a /= 0xffffffff && a.&.fILE_ATTRIBUTE_DIRECTORY /= 0)
#else
doesDirectoryExist s =
allocaBytes sizeof_stat $ \p ->
withCString
(if isDrive s
then addTrailingPathSeparator s
else dropTrailingPathSeparator s)
$ \c -> do
st <- lstat c p
if st == 0
then fmap s_isdir (st_mode p)
else return False
#endif
getRecursiveContents :: FilePath -> IO (DList FilePath)
getRecursiveContents dir =
flip catchIO (\_ -> return $ DL.singleton dir) $ do
raw <- getDirectoryContents dir
let entries = map (dir </>) (raw \\ [".",".."])
(dirs,files) <- partitionM doesDirectoryExist entries
subs <- unsafeInterleaveIO . mapM getRecursiveContents $ dirs
return$ DL.cons dir (DL.fromList files `DL.append` DL.concat subs)
partitionM :: (Monad m) => (a -> m Bool) -> [a] -> m ([a], [a])
partitionM p_ = foldM (f p_) ([],[])
where
f p (ts,fs) x = p x >>= \b ->
if b
then return (x:ts, fs)
else return (ts, x:fs)
partitionDL :: (a -> Bool) -> DList a -> (DList a, DList a)
partitionDL p_ = DL.foldr (f p_) (DL.empty,DL.empty)
where
f p x (ts,fs) =
if p x
then (DL.cons x ts, fs)
else (ts, DL.cons x fs)
nubOrd :: Ord a => [a] -> [a]
nubOrd = go Set.empty
where
go _ [] = []
go set (x:xs) =
if Set.member x set
then go set xs
else x : go (Set.insert x set) xs
catchIO :: IO a -> (E.IOException -> IO a) -> IO a
catchIO = E.catch