hpath-0.8.0: Support for well-typed paths

Copyright© 2016 Julian Ospald
LicenseBSD3
MaintainerJulian Ospald <hasufell@posteo.de>
Stabilityexperimental
Portabilityportable
Safe HaskellSafe
LanguageHaskell2010

System.Posix.FilePath

Contents

Description

The equivalent of System.FilePath on raw (byte string) file paths.

Not all functions of System.FilePath are implemented yet. Feel free to contribute!

Synopsis

Separator predicates

pathSeparator :: Word8 Source #

Path separator character

isPathSeparator :: Word8 -> Bool Source #

Check if a character is the path separator

\n ->  (_chr n == '/') == isPathSeparator n

searchPathSeparator :: Word8 Source #

Search path separator

isSearchPathSeparator :: Word8 -> Bool Source #

Check if a character is the search path separator

\n -> (_chr n == ':') == isSearchPathSeparator n

extSeparator :: Word8 Source #

File extension separator

isExtSeparator :: Word8 -> Bool Source #

Check if a character is the file extension separator

\n -> (_chr n == '.') == isExtSeparator n

$PATH methods

splitSearchPath :: ByteString -> [RawFilePath] Source #

Take a ByteString, split it on the searchPathSeparator. Blank items are converted to ..

Follows the recommendations in http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html

>>> splitSearchPath "File1:File2:File3"
["File1","File2","File3"]
>>> splitSearchPath "File1::File2:File3"
["File1",".","File2","File3"]
>>> splitSearchPath ""
["."]

getSearchPath :: IO [RawFilePath] Source #

Get a list of RawFilePaths in the $PATH variable.

Extension functions

splitExtension :: RawFilePath -> (RawFilePath, ByteString) Source #

Split a RawFilePath into a path+filename and extension

>>> splitExtension "file.exe"
("file",".exe")
>>> splitExtension "file"
("file","")
>>> splitExtension "/path/file.tar.gz"
("/path/file.tar",".gz")
\path -> uncurry (BS.append) (splitExtension path) == path

takeExtension :: RawFilePath -> ByteString Source #

Get the final extension from a RawFilePath

>>> takeExtension "file.exe"
".exe"
>>> takeExtension "file"
""
>>> takeExtension "/path/file.tar.gz"
".gz"

replaceExtension :: RawFilePath -> ByteString -> RawFilePath Source #

Change a file's extension

\path -> let ext = takeExtension path in replaceExtension path ext == path

dropExtension :: RawFilePath -> RawFilePath Source #

Drop the final extension from a RawFilePath

>>> dropExtension "file.exe"
"file"
>>> dropExtension "file"
"file"
>>> dropExtension "/path/file.tar.gz"
"/path/file.tar"

addExtension :: RawFilePath -> ByteString -> RawFilePath Source #

Add an extension to a RawFilePath

>>> addExtension "file" ".exe"
"file.exe"
>>> addExtension "file.tar" ".gz"
"file.tar.gz"
>>> addExtension "/path/" ".ext"
"/path/.ext"

hasExtension :: RawFilePath -> Bool Source #

Check if a RawFilePath has an extension

>>> hasExtension "file"
False
>>> hasExtension "file.tar"
True
>>> hasExtension "/path.part1/"
False

splitExtensions :: RawFilePath -> (RawFilePath, ByteString) Source #

Split a RawFilePath on the first extension.

>>> splitExtensions "/path/file.tar.gz"
("/path/file",".tar.gz")
\path -> uncurry addExtension (splitExtensions path) == path

dropExtensions :: RawFilePath -> RawFilePath Source #

Remove all extensions from a RawFilePath

>>> dropExtensions "/path/file.tar.gz"
"/path/file"

takeExtensions :: RawFilePath -> ByteString Source #

Take all extensions from a RawFilePath

>>> takeExtensions "/path/file.tar.gz"
".tar.gz"

stripExtension :: ByteString -> RawFilePath -> Maybe RawFilePath Source #

Drop the given extension from a FilePath, and the "." preceding it. Returns Nothing if the FilePath does not have the given extension, or Just and the part before the extension if it does.

This function can be more predictable than dropExtensions, especially if the filename might itself contain . characters.

>>> stripExtension "hs.o" "foo.x.hs.o"
Just "foo.x"
>>> stripExtension "hi.o" "foo.x.hs.o"
Nothing
>>> stripExtension ".c.d" "a.b.c.d"
Just "a.b"
>>> stripExtension ".c.d" "a.b..c.d"
Just "a.b."
>>> stripExtension "baz"  "foo.bar"
Nothing
>>> stripExtension "bar"  "foobar"
Nothing
\path -> stripExtension "" path == Just path
\path -> dropExtension path  == fromJust (stripExtension (takeExtension path) path)
\path -> dropExtensions path == fromJust (stripExtension (takeExtensions path) path)

Filename/directory functions

splitFileName :: RawFilePath -> (RawFilePath, RawFilePath) Source #

Split a RawFilePath into (path,file). combine is the inverse

>>> splitFileName "path/file.txt"
("path/","file.txt")
>>> splitFileName "path/"
("path/","")
>>> splitFileName "file.txt"
("./","file.txt")
\path -> uncurry combine (splitFileName path) == path || fst (splitFileName path) == "./"

takeFileName :: RawFilePath -> RawFilePath Source #

Get the file name

>>> takeFileName "path/file.txt"
"file.txt"
>>> takeFileName "path/"
""

replaceFileName :: RawFilePath -> ByteString -> RawFilePath Source #

Change the file name

\path -> replaceFileName path (takeFileName path) == path

dropFileName :: RawFilePath -> RawFilePath Source #

Drop the file name

>>> dropFileName "path/file.txt"
"path/"
>>> dropFileName "file.txt"
"./"

takeBaseName :: RawFilePath -> ByteString Source #

Get the file name, without a trailing extension

>>> takeBaseName "path/file.tar.gz"
"file.tar"
>>> takeBaseName ""
""

replaceBaseName :: RawFilePath -> ByteString -> RawFilePath Source #

Change the base name

>>> replaceBaseName "path/file.tar.gz" "bob"
"path/bob.gz"
\path -> replaceBaseName path (takeBaseName path) == path

takeDirectory :: RawFilePath -> RawFilePath Source #

Get the directory, moving up one level if it's already a directory

>>> takeDirectory "path/file.txt"
"path"
>>> takeDirectory "file"
"."
>>> takeDirectory "/path/to/"
"/path/to"
>>> takeDirectory "/path/to"
"/path"

replaceDirectory :: RawFilePath -> ByteString -> RawFilePath Source #

Change the directory component of a RawFilePath

\path -> replaceDirectory path (takeDirectory path) `equalFilePath` path || takeDirectory path == "."

combine :: RawFilePath -> RawFilePath -> RawFilePath Source #

Join two paths together

>>> combine "/" "file"
"/file"
>>> combine "/path/to" "file"
"/path/to/file"
>>> combine "file" "/absolute/path"
"/absolute/path"

(</>) :: RawFilePath -> RawFilePath -> RawFilePath Source #

Operator version of combine

splitPath :: RawFilePath -> [RawFilePath] Source #

Split a path into a list of components:

>>> splitPath "/path/to/file.txt"
["/","path/","to/","file.txt"]
\path -> BS.concat (splitPath path) == path

joinPath :: [RawFilePath] -> RawFilePath Source #

Join a split path back together

\path -> joinPath (splitPath path) == path
>>> joinPath ["path","to","file.txt"]
"path/to/file.txt"

splitDirectories :: RawFilePath -> [RawFilePath] Source #

Like splitPath, but without trailing slashes

>>> splitDirectories "/path/to/file.txt"
["/","path","to","file.txt"]
>>> splitDirectories ""
[]

Trailing slash functions

hasTrailingPathSeparator :: RawFilePath -> Bool Source #

Check if the last character of a RawFilePath is /.

>>> hasTrailingPathSeparator "/path/"
True
>>> hasTrailingPathSeparator "/"
True
>>> hasTrailingPathSeparator "/path"
False

addTrailingPathSeparator :: RawFilePath -> RawFilePath Source #

Add a trailing path separator.

>>> addTrailingPathSeparator "/path"
"/path/"
>>> addTrailingPathSeparator "/path/"
"/path/"
>>> addTrailingPathSeparator "/"
"/"

dropTrailingPathSeparator :: RawFilePath -> RawFilePath Source #

Remove a trailing path separator

>>> dropTrailingPathSeparator "/path/"
"/path"
>>> dropTrailingPathSeparator "/path////"
"/path"
>>> dropTrailingPathSeparator "/"
"/"
>>> dropTrailingPathSeparator "//"
"/"

File name manipulations

normalise :: RawFilePath -> RawFilePath Source #

Normalise a file.

>>> normalise "/file/\\test////"
"/file/\\test/"
>>> normalise "/file/./test"
"/file/test"
>>> normalise "/test/file/../bob/fred/"
"/test/file/../bob/fred/"
>>> normalise "../bob/fred/"
"../bob/fred/"
>>> normalise "./bob/fred/"
"bob/fred/"
>>> normalise "./bob////.fred/./...///./..///#."
"bob/.fred/.../../#."
>>> normalise "."
"."
>>> normalise "./"
"./"
>>> normalise "./."
"./"
>>> normalise "/./"
"/"
>>> normalise "/"
"/"
>>> normalise "bob/fred/."
"bob/fred/"
>>> normalise "//home"
"/home"

makeRelative :: RawFilePath -> RawFilePath -> RawFilePath Source #

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 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"
\p -> makeRelative p p == "."
\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

equalFilePath :: RawFilePath -> RawFilePath -> Bool Source #

Equality of two filepaths. The filepaths are normalised and trailing path separators are dropped.

>>> equalFilePath "foo" "foo"
True
>>> equalFilePath "foo" "foo/"
True
>>> equalFilePath "foo" "./foo"
True
>>> equalFilePath "" ""
True
>>> equalFilePath "foo" "/foo"
False
>>> equalFilePath "foo" "FOO"
False
>>> equalFilePath "foo" "../foo"
False
\p -> equalFilePath p p

isRelative :: RawFilePath -> Bool Source #

Check if a path is relative

\path -> isRelative path /= isAbsolute path

isAbsolute :: RawFilePath -> Bool Source #

Check if a path is absolute

>>> isAbsolute "/path"
True
>>> isAbsolute "path"
False
>>> isAbsolute ""
False

isValid :: RawFilePath -> Bool Source #

Is a FilePath valid, i.e. could you create a file like it?

>>> isValid ""
False
>>> isValid "\0"
False
>>> isValid "/random_ path:*"
True

makeValid :: RawFilePath -> RawFilePath Source #

Take a FilePath and make it valid; does not change already valid FilePaths.

>>> makeValid ""
"_"
>>> makeValid "file\0name"
"file_name"
\p -> if isValid p then makeValid p == p else makeValid p /= p
\p -> isValid (makeValid p)

isFileName :: RawFilePath -> Bool Source #

Is the given path a valid filename? This includes "." and "..".

>>> isFileName "lal"
True
>>> isFileName "."
True
>>> isFileName ".."
True
>>> isFileName ""
False
>>> isFileName "\0"
False
>>> isFileName "/random_ path:*"
False

hasParentDir :: RawFilePath -> Bool Source #

Check if the filepath has any parent directories in it.

>>> hasParentDir "/.."
True
>>> hasParentDir "foo/bar/.."
True
>>> hasParentDir "foo/../bar/."
True
>>> hasParentDir "foo/bar"
False
>>> hasParentDir "foo"
False
>>> hasParentDir ""
False
>>> hasParentDir ".."
False

hiddenFile :: RawFilePath -> Bool Source #

Whether the file is a hidden file.

>>> hiddenFile ".foo"
True
>>> hiddenFile "..foo.bar"
True
>>> hiddenFile "some/path/.bar"
True
>>> hiddenFile "..."
True
>>> hiddenFile "dod.bar"
False
>>> hiddenFile "."
False
>>> hiddenFile ".."
False
>>> hiddenFile ""
False