-
Notifications
You must be signed in to change notification settings - Fork 0
/
Glob.hs
76 lines (68 loc) · 3.02 KB
/
Glob.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
{-# LANGUAGE ScopedTypeVariables #-}
module Glob (namesMatching) where
import System.Directory (doesDirectoryExist, doesFileExist,
getCurrentDirectory, getDirectoryContents)
import System.FilePath (dropTrailingPathSeparator, splitFileName, (</>))
import Control.Exception (handle, SomeException)
import Control.Monad (forM, filterM)
import GlobRegex (matchesGlob, GlobError)
import System.FilePath (pathSeparator)
namesMatching :: String -> IO (Either GlobError [String])
namesMatching pat
| not (isPattern pat) = do
exists :: Bool <- doesNameExist pat
return $ Right (if exists then [pat] else [])
| otherwise = do
case splitFileName pat of
("", baseName) -> do
curDir <- getCurrentDirectory
listMatches curDir baseName
(dirName, baseName) -> do
dirs :: Either GlobError [String] <- if isPattern dirName
then namesMatching (dropTrailingPathSeparator dirName)
else return $ Right [dirName]
let listDir = if isPattern baseName
then listMatches
else listPlain
pn :: [String] <- do
pathNames <- do
dirs `eitherBind` (\ds ->
forM ds $ \dir -> do
baseNames <- listDir dir baseName
return (map (dir </>) baseNames))
return pathNames :: Either GlobError [String]
return ((pn >>= return . concat) :: Either GlobError [String])
eitherBind :: Either GlobError a -> (a -> Either GlobError b) -> Either GlobError b
eitherBind = (>>=)
isPattern :: String -> Bool
isPattern = any (`elem` "[*?")
listMatches :: FilePath -> String -> IO (Either GlobError [String])
listMatches dirName pat = do
dirName' <- if null dirName
then getCurrentDirectory
else return dirName
handle (const (return $ Right []) ::
SomeException -> IO (Either GlobError [String])) $
do
names <- getDirectoryContents dirName'
let names' = if isHidden pat
then filter isHidden names
else filter (not . isHidden) names
return (filterM doesMatch names')
where doesMatch :: FilePath -> Either GlobError Bool
doesMatch fp = matchesGlob False fp pat
listPlain :: FilePath -> String -> IO (Either GlobError [String])
listPlain dirName baseName =do
exists <- if null baseName
then doesDirectoryExist dirName
else doesNameExist (dirName </> baseName)
return $ Right (if exists then [baseName] else [])
isHidden :: String -> Bool
isHidden ('.':_) = True
isHidden _ = False
doesNameExist :: FilePath -> IO Bool
doesNameExist name = do
fileExists <- doesFileExist name
if fileExists
then return True
else doesDirectoryExist name