First, if you're having trouble loading Glob.hs, that's probably because you're using a too-new version of GHC. Try replacing the line
import Control.Exception (handle)
with
import Control.OldException (handle)
to use a backwards-compatible version of handle, which solves this problem.
#1. The trick to detecting the current OS is examining the path-separator character. It's '/' on Unix and Unix-like systems, and '\\' (a backslash) on Windows.
I'll be using the GlobRegex module which was updated in the previous post, which includes the matchesGlobIgnoreCase function. Here's the updated Glog.hs; changed or new lines are in bold.
import System.Directory (doesDirectoryExist, doesFileExist,
getCurrentDirectory, getDirectoryContents)
import System.FilePath (dropTrailingPathSeparator, splitFileName, (>))
import Control.OldException (handle)
import Control.Monad (forM)
import GlobRegex (matchesGlob, matchesGlobIgnoreCase)
import System.FilePath (pathSeparator)
isPattern :: String -> Bool
isPattern = any (`elem` "[*?")
namesMatching :: String -> IO [FilePath]
namesMatching pat
| not (isPattern pat) = do
exists <- doesNameExist pat
return (if exists then [pat] else [])
| otherwise = do
case splitFileName pat of
("", baseName) -> do
curDir <- getCurrentDirectory
listMatches curDir baseName
(dirName, baseName) -> do
dirs <- if isPattern dirName
then namesMatching (dropTrailingPathSeparator dirName)
else return [dirName]
let listDir = if isPattern baseName
then listMatches
else listPlain
pathNames <- forM dirs $ \dir -> do
baseNames <- listDir dir baseName
return (map (dir >) baseNames)
return (concat pathNames)
doesNameExist :: FilePath -> IO Bool
doesNameExist name = do
fileExists <- doesFileExist name
if fileExists then return True else doesDirectoryExist name
listMatches :: FilePath -> String -> IO [FilePath]
listMatches dirName pat = do
dirName' <- if null dirName
then getCurrentDirectory
else return dirName
let matcher = if isOsCaseInsensitive
then matchesGlobIgnoreCase
else matchesGlob
handle (const (return [])) $ do
names <- getDirectoryContents dirName'
let names' = if isHidden pat
then filter isHidden names
else filter (not . isHidden) names
return (filter (`matcher` pat) names')
isHidden ('.':_) = True
isHidden _ = False
listPlain :: FilePath -> String -> IO [FilePath]
listPlain dirName baseName = do
exists <- if null baseName
then doesDirectoryExist dirName
else doesNameExist (dirName > baseName)
return (if exists then [baseName] else [])
-- Only the case-insensitive Windows uses backlash as a path separator
isOsCaseInsensitive :: Bool
isOsCaseInsensitive = pathSeparator == '\\'
#2. The System.Posix.Files module includes the function getFileStatus, with the signature FilePath -> IO FileStatus. There's also the function isDirectory :: FileStatus -> Bool, so getFileStatus obviously works for directories, too.
Of course, using an OS-specific function where OS-agnostic code can do is a poor idea.
0 comments:
Post a Comment