{-# LANGUAGE CPP #-}
module System.EasyFile.FilePath (
FilePath,
pathSeparator, pathSeparators, isPathSeparator,
extSeparator, isExtSeparator,
splitExtension,
takeExtension, replaceExtension, dropExtension, addExtension, hasExtension, (<.>),
splitExtensions, dropExtensions, takeExtensions,
splitDrive, joinDrive,
takeDrive, hasDrive, dropDrive, isDrive,
splitFileName,
takeFileName, replaceFileName, dropFileName,
takeBaseName, replaceBaseName,
takeDirectory, replaceDirectory,
combine, (</>),
splitPath, joinPath, splitDirectories,
hasTrailingPathSeparator,
addTrailingPathSeparator,
dropTrailingPathSeparator,
normalise, equalFilePath,
makeRelative,
isRelative, isAbsolute,
#ifdef TESTING
, isRelativeDrive
#endif
)
where
import Data.Char(toLower, toUpper)
import Data.Maybe(isJust, fromJust)
infixr 7 <.>
infixr 5 </>
isPosix :: Bool
isPosix :: Bool
isPosix = Bool -> Bool
not Bool
isWindows
isWindows :: Bool
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
isWindows = True
#else
isWindows :: Bool
isWindows = Bool
False
#endif
pathSeparator :: Char
pathSeparator :: Char
pathSeparator = Char
'/'
pathSeparators :: [Char]
pathSeparators :: FilePath
pathSeparators = if Bool
isWindows then FilePath
"\\/" else FilePath
"/"
isPathSeparator :: Char -> Bool
isPathSeparator :: Char -> Bool
isPathSeparator = (Char -> FilePath -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` FilePath
pathSeparators)
extSeparator :: Char
extSeparator :: Char
extSeparator = Char
'.'
isExtSeparator :: Char -> Bool
isExtSeparator :: Char -> Bool
isExtSeparator = (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
extSeparator)
splitExtension :: FilePath -> (String, String)
splitExtension :: FilePath -> (FilePath, FilePath)
splitExtension FilePath
x = case FilePath
d of
FilePath
"" -> (FilePath
x,FilePath
"")
(Char
y:FilePath
ys) -> (FilePath
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath -> FilePath
forall a. [a] -> [a]
reverse FilePath
ys, Char
y Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
: FilePath -> FilePath
forall a. [a] -> [a]
reverse FilePath
c)
where
(FilePath
a,FilePath
b) = FilePath -> (FilePath, FilePath)
splitFileName FilePath
x
(FilePath
c,FilePath
d) = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isExtSeparator (FilePath -> (FilePath, FilePath))
-> FilePath -> (FilePath, FilePath)
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
forall a. [a] -> [a]
reverse FilePath
b
takeExtension :: FilePath -> String
takeExtension :: FilePath -> FilePath
takeExtension = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> b
snd ((FilePath, FilePath) -> FilePath)
-> (FilePath -> (FilePath, FilePath)) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> (FilePath, FilePath)
splitExtension
replaceExtension :: FilePath -> String -> FilePath
replaceExtension :: FilePath -> FilePath -> FilePath
replaceExtension FilePath
x FilePath
y = FilePath -> FilePath
dropExtension FilePath
x FilePath -> FilePath -> FilePath
<.> FilePath
y
(<.>) :: FilePath -> String -> FilePath
<.> :: FilePath -> FilePath -> FilePath
(<.>) = FilePath -> FilePath -> FilePath
addExtension
dropExtension :: FilePath -> FilePath
dropExtension :: FilePath -> FilePath
dropExtension = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> a
fst ((FilePath, FilePath) -> FilePath)
-> (FilePath -> (FilePath, FilePath)) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> (FilePath, FilePath)
splitExtension
addExtension :: FilePath -> String -> FilePath
addExtension :: FilePath -> FilePath -> FilePath
addExtension FilePath
file FilePath
"" = FilePath
file
addExtension FilePath
file xs :: FilePath
xs@(Char
x:FilePath
_) = FilePath -> FilePath -> FilePath
joinDrive FilePath
a FilePath
res
where
res :: FilePath
res = if Char -> Bool
isExtSeparator Char
x then FilePath
b FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
xs
else FilePath
b FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ [Char
extSeparator] FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
xs
(FilePath
a,FilePath
b) = FilePath -> (FilePath, FilePath)
splitDrive FilePath
file
hasExtension :: FilePath -> Bool
hasExtension :: FilePath -> Bool
hasExtension = (Char -> Bool) -> FilePath -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Char -> Bool
isExtSeparator (FilePath -> Bool) -> (FilePath -> FilePath) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
takeFileName
splitExtensions :: FilePath -> (FilePath, String)
splitExtensions :: FilePath -> (FilePath, FilePath)
splitExtensions FilePath
x = (FilePath
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
c, FilePath
d)
where
(FilePath
a,FilePath
b) = FilePath -> (FilePath, FilePath)
splitFileName FilePath
x
(FilePath
c,FilePath
d) = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isExtSeparator FilePath
b
dropExtensions :: FilePath -> FilePath
dropExtensions :: FilePath -> FilePath
dropExtensions = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> a
fst ((FilePath, FilePath) -> FilePath)
-> (FilePath -> (FilePath, FilePath)) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> (FilePath, FilePath)
splitExtensions
takeExtensions :: FilePath -> String
takeExtensions :: FilePath -> FilePath
takeExtensions = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> b
snd ((FilePath, FilePath) -> FilePath)
-> (FilePath -> (FilePath, FilePath)) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> (FilePath, FilePath)
splitExtensions
isLetter :: Char -> Bool
isLetter :: Char -> Bool
isLetter Char
x = (Char
x Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'a' Bool -> Bool -> Bool
&& Char
x Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'z') Bool -> Bool -> Bool
|| (Char
x Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'A' Bool -> Bool -> Bool
&& Char
x Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'Z')
splitDrive :: FilePath -> (FilePath, FilePath)
splitDrive :: FilePath -> (FilePath, FilePath)
splitDrive FilePath
x | Bool
isPosix = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'/') FilePath
x
splitDrive FilePath
x | Maybe (FilePath, FilePath) -> Bool
forall a. Maybe a -> Bool
isJust Maybe (FilePath, FilePath)
y = Maybe (FilePath, FilePath) -> (FilePath, FilePath)
forall a. HasCallStack => Maybe a -> a
fromJust Maybe (FilePath, FilePath)
y
where y :: Maybe (FilePath, FilePath)
y = FilePath -> Maybe (FilePath, FilePath)
readDriveLetter FilePath
x
splitDrive FilePath
x | Maybe (FilePath, FilePath) -> Bool
forall a. Maybe a -> Bool
isJust Maybe (FilePath, FilePath)
y = Maybe (FilePath, FilePath) -> (FilePath, FilePath)
forall a. HasCallStack => Maybe a -> a
fromJust Maybe (FilePath, FilePath)
y
where y :: Maybe (FilePath, FilePath)
y = FilePath -> Maybe (FilePath, FilePath)
readDriveUNC FilePath
x
splitDrive FilePath
x | Maybe (FilePath, FilePath) -> Bool
forall a. Maybe a -> Bool
isJust Maybe (FilePath, FilePath)
y = Maybe (FilePath, FilePath) -> (FilePath, FilePath)
forall a. HasCallStack => Maybe a -> a
fromJust Maybe (FilePath, FilePath)
y
where y :: Maybe (FilePath, FilePath)
y = FilePath -> Maybe (FilePath, FilePath)
readDriveShare FilePath
x
splitDrive FilePath
x = (FilePath
"",FilePath
x)
addSlash :: FilePath -> FilePath -> (FilePath, FilePath)
addSlash :: FilePath -> FilePath -> (FilePath, FilePath)
addSlash FilePath
a FilePath
xs = (FilePath
aFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
c,FilePath
d)
where (FilePath
c,FilePath
d) = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Char -> Bool
isPathSeparator FilePath
xs
readDriveUNC :: FilePath -> Maybe (FilePath, FilePath)
readDriveUNC :: FilePath -> Maybe (FilePath, FilePath)
readDriveUNC (Char
s1:Char
s2:Char
'?':Char
s3:FilePath
xs) | (Char -> Bool) -> FilePath -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isPathSeparator [Char
s1,Char
s2,Char
s3] =
case (Char -> Char) -> FilePath -> FilePath
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toUpper FilePath
xs of
(Char
'U':Char
'N':Char
'C':Char
s4:FilePath
_) | Char -> Bool
isPathSeparator Char
s4 ->
let (FilePath
a,FilePath
b) = FilePath -> (FilePath, FilePath)
readDriveShareName (Int -> FilePath -> FilePath
forall a. Int -> [a] -> [a]
drop Int
4 FilePath
xs)
in (FilePath, FilePath) -> Maybe (FilePath, FilePath)
forall a. a -> Maybe a
Just (Char
s1Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:Char
s2Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:Char
'?'Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:Char
s3Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:Int -> FilePath -> FilePath
forall a. Int -> [a] -> [a]
take Int
4 FilePath
xs FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
a, FilePath
b)
FilePath
_ -> case FilePath -> Maybe (FilePath, FilePath)
readDriveLetter FilePath
xs of
Just (FilePath
a,FilePath
b) -> (FilePath, FilePath) -> Maybe (FilePath, FilePath)
forall a. a -> Maybe a
Just (Char
s1Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:Char
s2Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:Char
'?'Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:Char
s3Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:FilePath
a,FilePath
b)
Maybe (FilePath, FilePath)
Nothing -> Maybe (FilePath, FilePath)
forall a. Maybe a
Nothing
readDriveUNC FilePath
_ = Maybe (FilePath, FilePath)
forall a. Maybe a
Nothing
readDriveLetter :: String -> Maybe (FilePath, FilePath)
readDriveLetter :: FilePath -> Maybe (FilePath, FilePath)
readDriveLetter (Char
x:Char
':':Char
y:FilePath
xs) | Char -> Bool
isLetter Char
x Bool -> Bool -> Bool
&& Char -> Bool
isPathSeparator Char
y = (FilePath, FilePath) -> Maybe (FilePath, FilePath)
forall a. a -> Maybe a
Just ((FilePath, FilePath) -> Maybe (FilePath, FilePath))
-> (FilePath, FilePath) -> Maybe (FilePath, FilePath)
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath -> (FilePath, FilePath)
addSlash [Char
x,Char
':'] (Char
yChar -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:FilePath
xs)
readDriveLetter (Char
x:Char
':':FilePath
xs) | Char -> Bool
isLetter Char
x = (FilePath, FilePath) -> Maybe (FilePath, FilePath)
forall a. a -> Maybe a
Just ([Char
x,Char
':'], FilePath
xs)
readDriveLetter FilePath
_ = Maybe (FilePath, FilePath)
forall a. Maybe a
Nothing
readDriveShare :: String -> Maybe (FilePath, FilePath)
readDriveShare :: FilePath -> Maybe (FilePath, FilePath)
readDriveShare (Char
s1:Char
s2:FilePath
xs) | Char -> Bool
isPathSeparator Char
s1 Bool -> Bool -> Bool
&& Char -> Bool
isPathSeparator Char
s2 =
(FilePath, FilePath) -> Maybe (FilePath, FilePath)
forall a. a -> Maybe a
Just (Char
s1Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:Char
s2Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:FilePath
a,FilePath
b)
where (FilePath
a,FilePath
b) = FilePath -> (FilePath, FilePath)
readDriveShareName FilePath
xs
readDriveShare FilePath
_ = Maybe (FilePath, FilePath)
forall a. Maybe a
Nothing
readDriveShareName :: String -> (FilePath, FilePath)
readDriveShareName :: FilePath -> (FilePath, FilePath)
readDriveShareName FilePath
name = FilePath -> FilePath -> (FilePath, FilePath)
addSlash FilePath
a FilePath
b
where (FilePath
a,FilePath
b) = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isPathSeparator FilePath
name
joinDrive :: FilePath -> FilePath -> FilePath
joinDrive :: FilePath -> FilePath -> FilePath
joinDrive FilePath
a FilePath
b | Bool
isPosix = FilePath
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
b
| FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
a = FilePath
b
| FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
b = FilePath
a
| Char -> Bool
isPathSeparator (FilePath -> Char
forall a. HasCallStack => [a] -> a
last FilePath
a) = FilePath
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
b
| Bool
otherwise = case FilePath
a of
[Char
a1,Char
':'] | Char -> Bool
isLetter Char
a1 -> FilePath
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
b
FilePath
_ -> FilePath
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ [Char
pathSeparator] FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
b
takeDrive :: FilePath -> FilePath
takeDrive :: FilePath -> FilePath
takeDrive = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> a
fst ((FilePath, FilePath) -> FilePath)
-> (FilePath -> (FilePath, FilePath)) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> (FilePath, FilePath)
splitDrive
dropDrive :: FilePath -> FilePath
dropDrive :: FilePath -> FilePath
dropDrive = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> b
snd ((FilePath, FilePath) -> FilePath)
-> (FilePath -> (FilePath, FilePath)) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> (FilePath, FilePath)
splitDrive
hasDrive :: FilePath -> Bool
hasDrive :: FilePath -> Bool
hasDrive = Bool -> Bool
not (Bool -> Bool) -> (FilePath -> Bool) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (FilePath -> Bool) -> (FilePath -> FilePath) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
takeDrive
isDrive :: FilePath -> Bool
isDrive :: FilePath -> Bool
isDrive = FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (FilePath -> Bool) -> (FilePath -> FilePath) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
dropDrive
splitFileName :: FilePath -> (String, String)
splitFileName :: FilePath -> (FilePath, FilePath)
splitFileName FilePath
x = (FilePath
c FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath -> FilePath
forall a. [a] -> [a]
reverse FilePath
b, FilePath -> FilePath
forall a. [a] -> [a]
reverse FilePath
a)
where
(FilePath
a,FilePath
b) = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isPathSeparator (FilePath -> (FilePath, FilePath))
-> FilePath -> (FilePath, FilePath)
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
forall a. [a] -> [a]
reverse FilePath
d
(FilePath
c,FilePath
d) = FilePath -> (FilePath, FilePath)
splitDrive FilePath
x
replaceFileName :: FilePath -> String -> FilePath
replaceFileName :: FilePath -> FilePath -> FilePath
replaceFileName FilePath
x FilePath
y = FilePath -> FilePath
dropFileName FilePath
x FilePath -> FilePath -> FilePath
</> FilePath
y
dropFileName :: FilePath -> FilePath
dropFileName :: FilePath -> FilePath
dropFileName = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> a
fst ((FilePath, FilePath) -> FilePath)
-> (FilePath -> (FilePath, FilePath)) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> (FilePath, FilePath)
splitFileName
takeFileName :: FilePath -> FilePath
takeFileName :: FilePath -> FilePath
takeFileName = (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> b
snd ((FilePath, FilePath) -> FilePath)
-> (FilePath -> (FilePath, FilePath)) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> (FilePath, FilePath)
splitFileName
takeBaseName :: FilePath -> String
takeBaseName :: FilePath -> FilePath
takeBaseName = FilePath -> FilePath
dropExtension (FilePath -> FilePath)
-> (FilePath -> FilePath) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
takeFileName
replaceBaseName :: FilePath -> String -> FilePath
replaceBaseName :: FilePath -> FilePath -> FilePath
replaceBaseName FilePath
pth FilePath
nam = FilePath -> FilePath -> FilePath
combineAlways FilePath
a (FilePath
nam FilePath -> FilePath -> FilePath
<.> FilePath
ext)
where
(FilePath
a,FilePath
b) = FilePath -> (FilePath, FilePath)
splitFileName FilePath
pth
ext :: FilePath
ext = FilePath -> FilePath
takeExtension FilePath
b
hasTrailingPathSeparator :: FilePath -> Bool
hasTrailingPathSeparator :: FilePath -> Bool
hasTrailingPathSeparator FilePath
"" = Bool
False
hasTrailingPathSeparator FilePath
x = Char -> Bool
isPathSeparator (FilePath -> Char
forall a. HasCallStack => [a] -> a
last FilePath
x)
addTrailingPathSeparator :: FilePath -> FilePath
addTrailingPathSeparator :: FilePath -> FilePath
addTrailingPathSeparator FilePath
x = if FilePath -> Bool
hasTrailingPathSeparator FilePath
x then FilePath
x else FilePath
x FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ [Char
pathSeparator]
dropTrailingPathSeparator :: FilePath -> FilePath
dropTrailingPathSeparator :: FilePath -> FilePath
dropTrailingPathSeparator FilePath
x =
if FilePath -> Bool
hasTrailingPathSeparator FilePath
x Bool -> Bool -> Bool
&& Bool -> Bool
not (FilePath -> Bool
isDrive FilePath
x)
then let x' :: FilePath
x' = FilePath -> FilePath
forall a. [a] -> [a]
reverse (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isPathSeparator (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
forall a. [a] -> [a]
reverse FilePath
x
in if FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
x' then [Char
pathSeparator] else FilePath
x'
else FilePath
x
takeDirectory :: FilePath -> FilePath
takeDirectory :: FilePath -> FilePath
takeDirectory FilePath
x = if FilePath -> Bool
isDrive FilePath
file then FilePath
file
else if FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
res Bool -> Bool -> Bool
&& Bool -> Bool
not (FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
file) then FilePath
file
else FilePath
res
where
res :: FilePath
res = FilePath -> FilePath
forall a. [a] -> [a]
reverse (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isPathSeparator (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
forall a. [a] -> [a]
reverse FilePath
file
file :: FilePath
file = FilePath -> FilePath
dropFileName FilePath
x
replaceDirectory :: FilePath -> String -> FilePath
replaceDirectory :: FilePath -> FilePath -> FilePath
replaceDirectory FilePath
x FilePath
dir = FilePath -> FilePath -> FilePath
combineAlways FilePath
dir (FilePath -> FilePath
takeFileName FilePath
x)
combine :: FilePath -> FilePath -> FilePath
combine :: FilePath -> FilePath -> FilePath
combine FilePath
a FilePath
b | FilePath -> Bool
hasDrive FilePath
b Bool -> Bool -> Bool
|| (Bool -> Bool
not (FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
b) Bool -> Bool -> Bool
&& Char -> Bool
isPathSeparator (FilePath -> Char
forall a. HasCallStack => [a] -> a
head FilePath
b)) = FilePath
b
| Bool
otherwise = FilePath -> FilePath -> FilePath
combineAlways FilePath
a FilePath
b
combineAlways :: FilePath -> FilePath -> FilePath
combineAlways :: FilePath -> FilePath -> FilePath
combineAlways FilePath
a FilePath
b | FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
a = FilePath
b
| FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
b = FilePath
a
| Char -> Bool
isPathSeparator (FilePath -> Char
forall a. HasCallStack => [a] -> a
last FilePath
a) = FilePath
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
b
| FilePath -> Bool
isDrive FilePath
a = FilePath -> FilePath -> FilePath
joinDrive FilePath
a FilePath
b
| Bool
otherwise = FilePath
a FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ [Char
pathSeparator] FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
b
(</>) :: FilePath -> FilePath -> FilePath
</> :: FilePath -> FilePath -> FilePath
(</>) = FilePath -> FilePath -> FilePath
combine
splitPath :: FilePath -> [FilePath]
splitPath :: FilePath -> [FilePath]
splitPath FilePath
x = [FilePath
drive | FilePath
drive FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
/= FilePath
""] [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ FilePath -> [FilePath]
f FilePath
path
where
(FilePath
drive,FilePath
path) = FilePath -> (FilePath, FilePath)
splitDrive FilePath
x
f :: FilePath -> [FilePath]
f FilePath
"" = []
f FilePath
y = (FilePath
aFilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++FilePath
c) FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: FilePath -> [FilePath]
f FilePath
d
where
(FilePath
a,FilePath
b) = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isPathSeparator FilePath
y
(FilePath
c,FilePath
d) = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isPathSeparator) FilePath
b
splitDirectories :: FilePath -> [FilePath]
splitDirectories :: FilePath -> [FilePath]
splitDirectories FilePath
path =
if FilePath -> Bool
hasDrive FilePath
path then [FilePath] -> FilePath
forall a. HasCallStack => [a] -> a
head [FilePath]
pathComponents FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: [FilePath] -> [FilePath]
f ([FilePath] -> [FilePath]
forall a. HasCallStack => [a] -> [a]
tail [FilePath]
pathComponents)
else [FilePath] -> [FilePath]
f [FilePath]
pathComponents
where
pathComponents :: [FilePath]
pathComponents = FilePath -> [FilePath]
splitPath FilePath
path
f :: [FilePath] -> [FilePath]
f [FilePath]
xs = (FilePath -> FilePath) -> [FilePath] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map FilePath -> FilePath
g [FilePath]
xs
g :: FilePath -> FilePath
g FilePath
x = if FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
res then FilePath
x else FilePath
res
where res :: FilePath
res = (Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isPathSeparator) FilePath
x
joinPath :: [FilePath] -> FilePath
joinPath :: [FilePath] -> FilePath
joinPath [FilePath]
x = (FilePath -> FilePath -> FilePath)
-> FilePath -> [FilePath] -> FilePath
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr FilePath -> FilePath -> FilePath
combine FilePath
"" [FilePath]
x
equalFilePath :: FilePath -> FilePath -> Bool
equalFilePath :: FilePath -> FilePath -> Bool
equalFilePath FilePath
a FilePath
b = FilePath -> FilePath
f FilePath
a FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath -> FilePath
f FilePath
b
where
f :: FilePath -> FilePath
f FilePath
x | Bool
isWindows = FilePath -> FilePath
dropTrailSlash (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ (Char -> Char) -> FilePath -> FilePath
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
normalise FilePath
x
| Bool
otherwise = FilePath -> FilePath
dropTrailSlash (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
normalise FilePath
x
dropTrailSlash :: FilePath -> FilePath
dropTrailSlash FilePath
x | FilePath -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length FilePath
x Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
2 Bool -> Bool -> Bool
&& Char -> Bool
isPathSeparator (FilePath -> Char
forall a. HasCallStack => [a] -> a
last FilePath
x) = FilePath -> FilePath
forall a. HasCallStack => [a] -> [a]
init FilePath
x
| Bool
otherwise = FilePath
x
makeRelative :: FilePath -> FilePath -> FilePath
makeRelative :: FilePath -> FilePath -> FilePath
makeRelative FilePath
root FilePath
path
| FilePath -> FilePath -> Bool
equalFilePath FilePath
root FilePath
path = FilePath
"."
| FilePath -> FilePath
takeAbs FilePath
root FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
/= FilePath -> FilePath
takeAbs FilePath
path = FilePath
path
| Bool
otherwise = FilePath -> FilePath -> FilePath
f (FilePath -> FilePath
dropAbs FilePath
root) (FilePath -> FilePath
dropAbs FilePath
path)
where
f :: FilePath -> FilePath -> FilePath
f FilePath
"" FilePath
y = (Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isPathSeparator FilePath
y
f FilePath
x FilePath
y = let (FilePath
x1,FilePath
x2) = FilePath -> (FilePath, FilePath)
g FilePath
x
(FilePath
y1,FilePath
y2) = FilePath -> (FilePath, FilePath)
g FilePath
y
in if FilePath -> FilePath -> Bool
equalFilePath FilePath
x1 FilePath
y1 then FilePath -> FilePath -> FilePath
f FilePath
x2 FilePath
y2 else FilePath
path
g :: FilePath -> (FilePath, FilePath)
g FilePath
x = ((Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isPathSeparator FilePath
a, (Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isPathSeparator FilePath
b)
where (FilePath
a,FilePath
b) = (Char -> Bool) -> FilePath -> (FilePath, FilePath)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isPathSeparator (FilePath -> (FilePath, FilePath))
-> FilePath -> (FilePath, FilePath)
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> FilePath -> FilePath
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isPathSeparator FilePath
x
dropAbs :: FilePath -> FilePath
dropAbs (Char
x:FilePath
xs) | Char -> Bool
isPathSeparator Char
x = FilePath
xs
dropAbs FilePath
x = FilePath -> FilePath
dropDrive FilePath
x
takeAbs :: FilePath -> FilePath
takeAbs (Char
x:FilePath
_) | Char -> Bool
isPathSeparator Char
x = [Char
pathSeparator]
takeAbs FilePath
x = (Char -> Char) -> FilePath -> FilePath
forall a b. (a -> b) -> [a] -> [b]
map (\Char
y -> if Char -> Bool
isPathSeparator Char
y then Char
pathSeparator else Char -> Char
toLower Char
y) (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
takeDrive FilePath
x
normalise :: FilePath -> FilePath
normalise :: FilePath -> FilePath
normalise FilePath
path = FilePath -> FilePath -> FilePath
joinDrive (FilePath -> FilePath
normaliseDrive FilePath
drv) (FilePath -> FilePath
f FilePath
pth)
FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ [Char
pathSeparator | Bool -> Bool
not (FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
pth) Bool -> Bool -> Bool
&& Char -> Bool
isPathSeparator (FilePath -> Char
forall a. HasCallStack => [a] -> a
last FilePath
pth)]
where
(FilePath
drv,FilePath
pth) = FilePath -> (FilePath, FilePath)
splitDrive FilePath
path
f :: FilePath -> FilePath
f = [FilePath] -> FilePath
joinPath ([FilePath] -> FilePath)
-> (FilePath -> [FilePath]) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [FilePath] -> [FilePath] -> [FilePath]
dropDots [] ([FilePath] -> [FilePath])
-> (FilePath -> [FilePath]) -> FilePath -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath]
splitDirectories (FilePath -> [FilePath])
-> (FilePath -> FilePath) -> FilePath -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
propSep
propSep :: FilePath -> FilePath
propSep (Char
a:Char
b:FilePath
xs)
| Char -> Bool
isPathSeparator Char
a Bool -> Bool -> Bool
&& Char -> Bool
isPathSeparator Char
b = FilePath -> FilePath
propSep (Char
aChar -> FilePath -> FilePath
forall a. a -> [a] -> [a]
:FilePath
xs)
propSep (Char
a:FilePath
xs)
| Char -> Bool
isPathSeparator Char
a = Char
pathSeparator Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
: FilePath -> FilePath
propSep FilePath
xs
propSep (Char
x:FilePath
xs) = Char
x Char -> FilePath -> FilePath
forall a. a -> [a] -> [a]
: FilePath -> FilePath
propSep FilePath
xs
propSep [] = []
dropDots :: [FilePath] -> [FilePath] -> [FilePath]
dropDots [FilePath]
acc (FilePath
".":[FilePath]
xs) | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [FilePath] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
xs = [FilePath] -> [FilePath] -> [FilePath]
dropDots [FilePath]
acc [FilePath]
xs
dropDots [FilePath]
acc (FilePath
x:[FilePath]
xs) = [FilePath] -> [FilePath] -> [FilePath]
dropDots (FilePath
xFilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
:[FilePath]
acc) [FilePath]
xs
dropDots [FilePath]
acc [] = [FilePath] -> [FilePath]
forall a. [a] -> [a]
reverse [FilePath]
acc
normaliseDrive :: FilePath -> FilePath
normaliseDrive :: FilePath -> FilePath
normaliseDrive FilePath
drive | Bool
isPosix = FilePath
drive
normaliseDrive FilePath
drive = if Maybe (FilePath, FilePath) -> Bool
forall a. Maybe a -> Bool
isJust (Maybe (FilePath, FilePath) -> Bool)
-> Maybe (FilePath, FilePath) -> Bool
forall a b. (a -> b) -> a -> b
$ FilePath -> Maybe (FilePath, FilePath)
readDriveLetter FilePath
x2
then (Char -> Char) -> FilePath -> FilePath
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toUpper FilePath
x2
else FilePath
drive
where
x2 :: FilePath
x2 = (Char -> Char) -> FilePath -> FilePath
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
repSlash FilePath
drive
repSlash :: Char -> Char
repSlash Char
x = if Char -> Bool
isPathSeparator Char
x then Char
pathSeparator else Char
x
isRelative :: FilePath -> Bool
isRelative :: FilePath -> Bool
isRelative = FilePath -> Bool
isRelativeDrive (FilePath -> Bool) -> (FilePath -> FilePath) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
takeDrive
isRelativeDrive :: String -> Bool
isRelativeDrive :: FilePath -> Bool
isRelativeDrive FilePath
x = FilePath -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null FilePath
x Bool -> Bool -> Bool
||
Bool
-> ((FilePath, FilePath) -> Bool)
-> Maybe (FilePath, FilePath)
-> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (Bool -> Bool
not (Bool -> Bool)
-> ((FilePath, FilePath) -> Bool) -> (FilePath, FilePath) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isPathSeparator (Char -> Bool)
-> ((FilePath, FilePath) -> Char) -> (FilePath, FilePath) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Char
forall a. HasCallStack => [a] -> a
last (FilePath -> Char)
-> ((FilePath, FilePath) -> FilePath)
-> (FilePath, FilePath)
-> Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath, FilePath) -> FilePath
forall a b. (a, b) -> a
fst) (FilePath -> Maybe (FilePath, FilePath)
readDriveLetter FilePath
x)
isAbsolute :: FilePath -> Bool
isAbsolute :: FilePath -> Bool
isAbsolute = Bool -> Bool
not (Bool -> Bool) -> (FilePath -> Bool) -> FilePath -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Bool
isRelative