Add makeRelative and makeValid

This commit is contained in:
Julian Ospald 2016-05-24 15:31:14 +02:00
parent 3cb3a822d7
commit 51609781b2
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28

View File

@ -17,7 +17,6 @@
{-# OPTIONS_GHC -Wall #-} {-# OPTIONS_GHC -Wall #-}
-- TODO: makeValid, makeRelative
module System.Posix.FilePath ( module System.Posix.FilePath (
@ -68,10 +67,12 @@ module System.Posix.FilePath (
-- * File name manipulations -- * File name manipulations
, normalise , normalise
, makeRelative
, equalFilePath , equalFilePath
, isRelative , isRelative
, isAbsolute , isAbsolute
, isValid , isValid
, makeValid
, isFileName , isFileName
, hasParentDir , hasParentDir
, hiddenFile , hiddenFile
@ -604,6 +605,48 @@ normalise filepath =
dropDots = filter (BS.singleton _period /=) dropDots = filter (BS.singleton _period /=)
-- | Contract a filename, based on a relative path. Note that the resulting
-- path will never introduce @..@ paths, as the presence of symlinks
-- means @..\/b@ may not reach @a\/b@ if it starts from @a\/c@. For a
-- worked example see
-- <http://neilmitchell.blogspot.co.uk/2015/10/filepaths-are-subtle-symlinks-are-hard.html this blog post>.
--
-- >>> makeRelative "/directory" "/directory/file.ext"
-- "file.ext"
-- >>> makeRelative "/Home" "/home/bob"
-- "/home/bob"
-- >>> makeRelative "/home/" "/home/bob/foo/bar"
-- "bob/foo/bar"
-- >>> makeRelative "/fred" "bob"
-- "bob"
-- >>> makeRelative "/file/test" "/file/test/fred"
-- "fred"
-- >>> makeRelative "/file/test" "/file/test/fred/"
-- "fred/"
-- >>> makeRelative "some/path" "some/path/a/b/c"
-- "a/b/c"
--
-- prop> \p -> makeRelative p p == "."
-- prop> \p -> makeRelative (takeDirectory p) p `equalFilePath` takeFileName p
-- prop \x y -> equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> makeRelative y x) x
makeRelative :: RawFilePath -> RawFilePath -> RawFilePath
makeRelative root path
| equalFilePath root path = BS.singleton _period
| takeAbs root /= takeAbs path = path
| otherwise = f (dropAbs root) (dropAbs path)
where
f x y
| BS.null x = BS.dropWhile isPathSeparator y
| otherwise = let (x1,x2) = g x
(y1,y2) = g y
in if equalFilePath x1 y1 then f x2 y2 else path
g x = (BS.dropWhile isPathSeparator a, BS.dropWhile isPathSeparator b)
where (a, b) = BS.break isPathSeparator $ BS.dropWhile isPathSeparator x
dropAbs x = snd $ BS.span (== _slash) x
takeAbs x = fst $ BS.span (== _slash) x
-- |Equality of two filepaths. The filepaths are normalised -- |Equality of two filepaths. The filepaths are normalised
-- and trailing path separators are dropped. -- and trailing path separators are dropped.
-- --
@ -665,6 +708,21 @@ isValid filepath
| otherwise = True | otherwise = True
-- | Take a FilePath and make it valid; does not change already valid FilePaths.
--
-- >>> makeValid ""
-- "_"
-- >>> makeValid "file\0name"
-- "file_name"
--
-- prop> \p -> if isValid p then makeValid p == p else makeValid p /= p
-- prop> \p -> isValid (makeValid p)
makeValid :: RawFilePath -> RawFilePath
makeValid path
| BS.null path = BS.singleton _underscore
| otherwise = BS.map (\x -> if x == _nul then _underscore else x) path
-- | Is the given path a valid filename? This includes -- | Is the given path a valid filename? This includes
-- "." and "..". -- "." and "..".
-- --