-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/


-- | Library for manipulating FilePaths in a cross platform way.
gr
grThis package provides functionality for manipulating <tt>FilePath</tt>
grvalues, and is shipped with both <a>GHC</a> and the <a>Haskell
grPlatform</a>. It provides three modules:
gr
gr<ul>
gr<li><a>System.FilePath.Posix</a> manipulates POSIX/Linux style
gr<tt>FilePath</tt> values (with <tt>/</tt> as the path separator).</li>
gr<li><a>System.FilePath.Windows</a> manipulates Windows style
gr<tt>FilePath</tt> values (with either <tt>\</tt> or <tt>/</tt> as the
grpath separator, and deals with drives).</li>
gr<li><a>System.FilePath</a> is an alias for the module appropriate to
gryour platform.</li>
gr</ul>
gr
grAll three modules provide the same API, and the same documentation
gr(calling out differences in the different variants).
@package filepath
@version 1.4.2


-- | A library for <a>FilePath</a> manipulations, using Posix style paths
gron all platforms. Importing <a>System.FilePath</a> is usually better.
gr
grGiven the example <a>FilePath</a>: <tt>/directory/file.ext</tt>
gr
grWe can use the following functions to extract pieces.
gr
gr<ul>
gr<li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
gr<li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
gr<li><a>takeExtension</a> gives <tt>".ext"</tt></li>
gr<li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
gr<li><a>takeBaseName</a> gives <tt>"file"</tt></li>
gr</ul>
gr
grAnd we could have built an equivalent path with the following
grexpressions:
gr
gr<ul>
gr<li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
gr<li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
gr<li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
gr</ul>
gr
grEach function in this module is documented with several examples,
grwhich are also used as tests.
gr
grHere are a few examples of using the <tt>filepath</tt> functions
grtogether:
gr
gr<i>Example 1:</i> Find the possible locations of a Haskell module
gr<tt>Test</tt> imported from module <tt>Main</tt>:
gr
gr<pre>
gr[<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
gr</pre>
gr
gr<i>Example 2:</i> Download a file from <tt>url</tt> and save it to
grdisk:
gr
gr<pre>
grdo let file = <a>makeValid</a> url
gr   System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
gr</pre>
gr
gr<i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
grfile under <tt>interface</tt>:
gr
gr<pre>
gr<a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
gr</pre>
gr
grReferences: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
grMSDN)
module System.FilePath.Posix

-- | File and directory names are values of type <a>String</a>, whose
grprecise meaning is operating system dependent. Files can be opened,
gryielding a handle which can then be used to operate on the contents of
grthat file.
type FilePath = String

-- | The character that separates directories. In the case where more than
grone character is possible, <a>pathSeparator</a> is the 'ideal' one.
gr
gr<pre>
grWindows: pathSeparator == '\\'
grPosix:   pathSeparator ==  '/'
grisPathSeparator pathSeparator
gr</pre>
pathSeparator :: Char

-- | The list of all possible separators.
gr
gr<pre>
grWindows: pathSeparators == ['\\', '/']
grPosix:   pathSeparators == ['/']
grpathSeparator `elem` pathSeparators
gr</pre>
pathSeparators :: [Char]

-- | Rather than using <tt>(== <a>pathSeparator</a>)</tt>, use this. Test
grif something is a path separator.
gr
gr<pre>
grisPathSeparator a == (a `elem` pathSeparators)
gr</pre>
isPathSeparator :: Char -> Bool

-- | The character that is used to separate the entries in the $PATH
grenvironment variable.
gr
gr<pre>
grWindows: searchPathSeparator == ';'
grPosix:   searchPathSeparator == ':'
gr</pre>
searchPathSeparator :: Char

-- | Is the character a file separator?
gr
gr<pre>
grisSearchPathSeparator a == (a == searchPathSeparator)
gr</pre>
isSearchPathSeparator :: Char -> Bool

-- | File extension character
gr
gr<pre>
grextSeparator == '.'
gr</pre>
extSeparator :: Char

-- | Is the character an extension character?
gr
gr<pre>
grisExtSeparator a == (a == extSeparator)
gr</pre>
isExtSeparator :: Char -> Bool

-- | Take a string, split it on the <a>searchPathSeparator</a> character.
grBlank items are ignored on Windows, and converted to <tt>.</tt> on
grPosix. On Windows path elements are stripped of quotes.
gr
grFollows the recommendations in
gr<a>http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html</a>
gr
gr<pre>
grPosix:   splitSearchPath "File1:File2:File3"  == ["File1","File2","File3"]
grPosix:   splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"]
grWindows: splitSearchPath "File1;File2;File3"  == ["File1","File2","File3"]
grWindows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"]
grWindows: splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"]
gr</pre>
splitSearchPath :: String -> [FilePath]

-- | Get a list of <a>FilePath</a>s in the $PATH variable.
getSearchPath :: IO [FilePath]

-- | Split on the extension. <a>addExtension</a> is the inverse.
gr
gr<pre>
grsplitExtension "/directory/path.ext" == ("/directory/path",".ext")
gruncurry (++) (splitExtension x) == x
grValid x =&gt; uncurry addExtension (splitExtension x) == x
grsplitExtension "file.txt" == ("file",".txt")
grsplitExtension "file" == ("file","")
grsplitExtension "file/file.txt" == ("file/file",".txt")
grsplitExtension "file.txt/boris" == ("file.txt/boris","")
grsplitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
grsplitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
grsplitExtension "file/path.txt/" == ("file/path.txt/","")
gr</pre>
splitExtension :: FilePath -> (String, String)

-- | Get the extension of a file, returns <tt>""</tt> for no extension,
gr<tt>.ext</tt> otherwise.
gr
gr<pre>
grtakeExtension "/directory/path.ext" == ".ext"
grtakeExtension x == snd (splitExtension x)
grValid x =&gt; takeExtension (addExtension x "ext") == ".ext"
grValid x =&gt; takeExtension (replaceExtension x "ext") == ".ext"
gr</pre>
takeExtension :: FilePath -> String

-- | Set the extension of a file, overwriting one if already present,
grequivalent to <a>-&lt;.&gt;</a>.
gr
gr<pre>
grreplaceExtension "/directory/path.txt" "ext" == "/directory/path.ext"
grreplaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext"
grreplaceExtension "file.txt" ".bob" == "file.bob"
grreplaceExtension "file.txt" "bob" == "file.bob"
grreplaceExtension "file" ".bob" == "file.bob"
grreplaceExtension "file.txt" "" == "file"
grreplaceExtension "file.fred.bob" "txt" == "file.fred.txt"
grreplaceExtension x y == addExtension (dropExtension x) y
gr</pre>
replaceExtension :: FilePath -> String -> FilePath

-- | Remove the current extension and add another, equivalent to
gr<a>replaceExtension</a>.
gr
gr<pre>
gr"/directory/path.txt" -&lt;.&gt; "ext" == "/directory/path.ext"
gr"/directory/path.txt" -&lt;.&gt; ".ext" == "/directory/path.ext"
gr"foo.o" -&lt;.&gt; "c" == "foo.c"
gr</pre>
(-<.>) :: FilePath -> String -> FilePath
infixr 7 -<.>

-- | Remove last extension, and the "." preceding it.
gr
gr<pre>
grdropExtension "/directory/path.ext" == "/directory/path"
grdropExtension x == fst (splitExtension x)
gr</pre>
dropExtension :: FilePath -> FilePath

-- | Add an extension, even if there is already one there, equivalent to
gr<a>&lt;.&gt;</a>.
gr
gr<pre>
graddExtension "/directory/path" "ext" == "/directory/path.ext"
graddExtension "file.txt" "bib" == "file.txt.bib"
graddExtension "file." ".bib" == "file..bib"
graddExtension "file" ".bib" == "file.bib"
graddExtension "/" "x" == "/.x"
graddExtension x "" == x
grValid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
grWindows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
gr</pre>
addExtension :: FilePath -> String -> FilePath

-- | Does the given filename have an extension?
gr
gr<pre>
grhasExtension "/directory/path.ext" == True
grhasExtension "/directory/path" == False
grnull (takeExtension x) == not (hasExtension x)
gr</pre>
hasExtension :: FilePath -> Bool

-- | Add an extension, even if there is already one there, equivalent to
gr<a>addExtension</a>.
gr
gr<pre>
gr"/directory/path" &lt;.&gt; "ext" == "/directory/path.ext"
gr"/directory/path" &lt;.&gt; ".ext" == "/directory/path.ext"
gr</pre>
(<.>) :: FilePath -> String -> FilePath
infixr 7 <.>

-- | Split on all extensions.
gr
gr<pre>
grsplitExtensions "/directory/path.ext" == ("/directory/path",".ext")
grsplitExtensions "file.tar.gz" == ("file",".tar.gz")
gruncurry (++) (splitExtensions x) == x
grValid x =&gt; uncurry addExtension (splitExtensions x) == x
grsplitExtensions "file.tar.gz" == ("file",".tar.gz")
gr</pre>
splitExtensions :: FilePath -> (FilePath, String)

-- | Drop all extensions.
gr
gr<pre>
grdropExtensions "/directory/path.ext" == "/directory/path"
grdropExtensions "file.tar.gz" == "file"
grnot $ hasExtension $ dropExtensions x
grnot $ any isExtSeparator $ takeFileName $ dropExtensions x
gr</pre>
dropExtensions :: FilePath -> FilePath

-- | Get all extensions.
gr
gr<pre>
grtakeExtensions "/directory/path.ext" == ".ext"
grtakeExtensions "file.tar.gz" == ".tar.gz"
gr</pre>
takeExtensions :: FilePath -> String

-- | Replace all extensions of a file with a new extension. Note that
gr<a>replaceExtension</a> and <a>addExtension</a> both work for adding
grmultiple extensions, so only required when you need to drop all
grextensions first.
gr
gr<pre>
grreplaceExtensions "file.fred.bob" "txt" == "file.txt"
grreplaceExtensions "file.fred.bob" "tar.gz" == "file.tar.gz"
gr</pre>
replaceExtensions :: FilePath -> String -> FilePath

-- | Does the given filename have the specified extension?
gr
gr<pre>
gr"png" `isExtensionOf` "/directory/file.png" == True
gr".png" `isExtensionOf` "/directory/file.png" == True
gr".tar.gz" `isExtensionOf` "bar/foo.tar.gz" == True
gr"ar.gz" `isExtensionOf` "bar/foo.tar.gz" == False
gr"png" `isExtensionOf` "/directory/file.png.jpg" == False
gr"csv/table.csv" `isExtensionOf` "/data/csv/table.csv" == False
gr</pre>
isExtensionOf :: String -> FilePath -> Bool

-- | Drop the given extension from a FilePath, and the <tt>"."</tt>
grpreceding it. Returns <a>Nothing</a> if the FilePath does not have the
grgiven extension, or <a>Just</a> and the part before the extension if
grit does.
gr
grThis function can be more predictable than <a>dropExtensions</a>,
grespecially if the filename might itself contain <tt>.</tt> characters.
gr
gr<pre>
grstripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
grstripExtension "hi.o" "foo.x.hs.o" == Nothing
grdropExtension x == fromJust (stripExtension (takeExtension x) x)
grdropExtensions x == fromJust (stripExtension (takeExtensions x) x)
grstripExtension ".c.d" "a.b.c.d"  == Just "a.b"
grstripExtension ".c.d" "a.b..c.d" == Just "a.b."
grstripExtension "baz"  "foo.bar"  == Nothing
grstripExtension "bar"  "foobar"   == Nothing
grstripExtension ""     x          == Just x
gr</pre>
stripExtension :: String -> FilePath -> Maybe FilePath

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
grinverse. The first component will often end with a trailing slash.
gr
gr<pre>
grsplitFileName "/directory/file.ext" == ("/directory/","file.ext")
grValid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
grValid x =&gt; isValid (fst (splitFileName x))
grsplitFileName "file/bob.txt" == ("file/", "bob.txt")
grsplitFileName "file/" == ("file/", "")
grsplitFileName "bob" == ("./", "bob")
grPosix:   splitFileName "/" == ("/","")
grWindows: splitFileName "c:" == ("c:","")
gr</pre>
splitFileName :: FilePath -> (String, String)

-- | Get the file name.
gr
gr<pre>
grtakeFileName "/directory/file.ext" == "file.ext"
grtakeFileName "test/" == ""
grtakeFileName x `isSuffixOf` x
grtakeFileName x == snd (splitFileName x)
grValid x =&gt; takeFileName (replaceFileName x "fred") == "fred"
grValid x =&gt; takeFileName (x &lt;/&gt; "fred") == "fred"
grValid x =&gt; isRelative (takeFileName x)
gr</pre>
takeFileName :: FilePath -> FilePath

-- | Set the filename.
gr
gr<pre>
grreplaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
grValid x =&gt; replaceFileName x (takeFileName x) == x
gr</pre>
replaceFileName :: FilePath -> String -> FilePath

-- | Drop the filename. Unlike <a>takeDirectory</a>, this function will
grleave a trailing path separator on the directory.
gr
gr<pre>
grdropFileName "/directory/file.ext" == "/directory/"
grdropFileName x == fst (splitFileName x)
gr</pre>
dropFileName :: FilePath -> FilePath

-- | Get the base name, without an extension or path.
gr
gr<pre>
grtakeBaseName "/directory/file.ext" == "file"
grtakeBaseName "file/test.txt" == "test"
grtakeBaseName "dave.ext" == "dave"
grtakeBaseName "" == ""
grtakeBaseName "test" == "test"
grtakeBaseName (addTrailingPathSeparator x) == ""
grtakeBaseName "file/file.tar.gz" == "file.tar"
gr</pre>
takeBaseName :: FilePath -> String

-- | Set the base name.
gr
gr<pre>
grreplaceBaseName "/directory/other.ext" "file" == "/directory/file.ext"
grreplaceBaseName "file/test.txt" "bob" == "file/bob.txt"
grreplaceBaseName "fred" "bill" == "bill"
grreplaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar"
grValid x =&gt; replaceBaseName x (takeBaseName x) == x
gr</pre>
replaceBaseName :: FilePath -> String -> FilePath

-- | Get the directory name, move up one level.
gr
gr<pre>
gr          takeDirectory "/directory/other.ext" == "/directory"
gr          takeDirectory x `isPrefixOf` x || takeDirectory x == "."
gr          takeDirectory "foo" == "."
gr          takeDirectory "/" == "/"
gr          takeDirectory "/foo" == "/"
gr          takeDirectory "/foo/bar/baz" == "/foo/bar"
gr          takeDirectory "/foo/bar/baz/" == "/foo/bar/baz"
gr          takeDirectory "foo/bar/baz" == "foo/bar"
grWindows:  takeDirectory "foo\\bar" == "foo"
grWindows:  takeDirectory "foo\\bar\\\\" == "foo\\bar"
grWindows:  takeDirectory "C:\\" == "C:\\"
gr</pre>
takeDirectory :: FilePath -> FilePath

-- | Set the directory, keeping the filename the same.
gr
gr<pre>
grreplaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
grValid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
gr</pre>
replaceDirectory :: FilePath -> String -> FilePath

-- | An alias for <a>&lt;/&gt;</a>.
combine :: FilePath -> FilePath -> FilePath

-- | Combine two paths with a path separator. If the second path starts
grwith a path separator or a drive letter, then it returns the second.
grThe intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
grwill access the same file as <tt>setCurrentDirectory dir; readFile
grfile</tt>.
gr
gr<pre>
grPosix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
grWindows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
gr         "directory" &lt;/&gt; "/file.ext" == "/file.ext"
grValid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
gr</pre>
gr
grCombined:
gr
gr<pre>
grPosix:   "/" &lt;/&gt; "test" == "/test"
grPosix:   "home" &lt;/&gt; "bob" == "home/bob"
grPosix:   "x:" &lt;/&gt; "foo" == "x:/foo"
grWindows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
grWindows: "home" &lt;/&gt; "bob" == "home\\bob"
gr</pre>
gr
grNot combined:
gr
gr<pre>
grPosix:   "home" &lt;/&gt; "/bob" == "/bob"
grWindows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
gr</pre>
gr
grNot combined (tricky):
gr
grOn Windows, if a filepath starts with a single slash, it is relative
grto the root of the current drive. In [1], this is (confusingly)
grreferred to as an absolute path. The current behavior of
gr<a>&lt;/&gt;</a> is to never combine these forms.
gr
gr<pre>
grWindows: "home" &lt;/&gt; "/bob" == "/bob"
grWindows: "home" &lt;/&gt; "\\bob" == "\\bob"
grWindows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
gr</pre>
gr
grOn Windows, from [1]: "If a file name begins with only a disk
grdesignator but not the backslash after the colon, it is interpreted as
gra relative path to the current directory on the drive with the
grspecified letter." The current behavior of <a>&lt;/&gt;</a> is to
grnever combine these forms.
gr
gr<pre>
grWindows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
grWindows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
gr</pre>
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
gr
gr<pre>
grsplitPath "/directory/file.ext" == ["/","directory/","file.ext"]
grconcat (splitPath x) == x
grsplitPath "test//item/" == ["test//","item/"]
grsplitPath "test/item/file" == ["test/","item/","file"]
grsplitPath "" == []
grWindows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
grPosix:   splitPath "/file/test" == ["/","file/","test"]
gr</pre>
splitPath :: FilePath -> [FilePath]

-- | Join path elements back together.
gr
gr<pre>
grjoinPath ["/","directory/","file.ext"] == "/directory/file.ext"
grValid x =&gt; joinPath (splitPath x) == x
grjoinPath [] == ""
grPosix: joinPath ["test","file","path"] == "test/file/path"
gr</pre>
joinPath :: [FilePath] -> FilePath

-- | Just as <a>splitPath</a>, but don't add the trailing slashes to each
grelement.
gr
gr<pre>
gr         splitDirectories "/directory/file.ext" == ["/","directory","file.ext"]
gr         splitDirectories "test/file" == ["test","file"]
gr         splitDirectories "/test/file" == ["/","test","file"]
grWindows: splitDirectories "C:\\test\\file" == ["C:\\", "test", "file"]
gr         Valid x =&gt; joinPath (splitDirectories x) `equalFilePath` x
gr         splitDirectories "" == []
grWindows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
gr         splitDirectories "/test///file" == ["/","test","file"]
gr</pre>
splitDirectories :: FilePath -> [FilePath]

-- | Split a path into a drive and a path. On Posix, / is a Drive.
gr
gr<pre>
gruncurry (++) (splitDrive x) == x
grWindows: splitDrive "file" == ("","file")
grWindows: splitDrive "c:/file" == ("c:/","file")
grWindows: splitDrive "c:\\file" == ("c:\\","file")
grWindows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
grWindows: splitDrive "\\\\shared" == ("\\\\shared","")
grWindows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
grWindows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
grWindows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
grWindows: splitDrive "/d" == ("","/d")
grPosix:   splitDrive "/test" == ("/","test")
grPosix:   splitDrive "//test" == ("//","test")
grPosix:   splitDrive "test/file" == ("","test/file")
grPosix:   splitDrive "file" == ("","file")
gr</pre>
splitDrive :: FilePath -> (FilePath, FilePath)

-- | Join a drive and the rest of the path.
gr
gr<pre>
grValid x =&gt; uncurry joinDrive (splitDrive x) == x
grWindows: joinDrive "C:" "foo" == "C:foo"
grWindows: joinDrive "C:\\" "bar" == "C:\\bar"
grWindows: joinDrive "\\\\share" "foo" == "\\\\share\\foo"
grWindows: joinDrive "/:" "foo" == "/:\\foo"
gr</pre>
joinDrive :: FilePath -> FilePath -> FilePath

-- | Get the drive from a filepath.
gr
gr<pre>
grtakeDrive x == fst (splitDrive x)
gr</pre>
takeDrive :: FilePath -> FilePath

-- | Does a path have a drive.
gr
gr<pre>
grnot (hasDrive x) == null (takeDrive x)
grPosix:   hasDrive "/foo" == True
grWindows: hasDrive "C:\\foo" == True
grWindows: hasDrive "C:foo" == True
gr         hasDrive "foo" == False
gr         hasDrive "" == False
gr</pre>
hasDrive :: FilePath -> Bool

-- | Delete the drive, if it exists.
gr
gr<pre>
grdropDrive x == snd (splitDrive x)
gr</pre>
dropDrive :: FilePath -> FilePath

-- | Is an element a drive
gr
gr<pre>
grPosix:   isDrive "/" == True
grPosix:   isDrive "/foo" == False
grWindows: isDrive "C:\\" == True
grWindows: isDrive "C:\\foo" == False
gr         isDrive "" == False
gr</pre>
isDrive :: FilePath -> Bool

-- | Is an item either a directory or the last character a path separator?
gr
gr<pre>
grhasTrailingPathSeparator "test" == False
grhasTrailingPathSeparator "test/" == True
gr</pre>
hasTrailingPathSeparator :: FilePath -> Bool

-- | Add a trailing file path separator if one is not already present.
gr
gr<pre>
grhasTrailingPathSeparator (addTrailingPathSeparator x)
grhasTrailingPathSeparator x ==&gt; addTrailingPathSeparator x == x
grPosix:    addTrailingPathSeparator "test/rest" == "test/rest/"
gr</pre>
addTrailingPathSeparator :: FilePath -> FilePath

-- | Remove any trailing path separators
gr
gr<pre>
grdropTrailingPathSeparator "file/test/" == "file/test"
gr          dropTrailingPathSeparator "/" == "/"
grWindows:  dropTrailingPathSeparator "\\" == "\\"
grPosix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
gr</pre>
dropTrailingPathSeparator :: FilePath -> FilePath

-- | Normalise a file
gr
gr<ul>
gr<li>// outside of the drive can be made blank</li>
gr<li>/ -&gt; <a>pathSeparator</a></li>
gr<li>./ -&gt; ""</li>
gr</ul>
gr
gr<pre>
grPosix:   normalise "/file/\\test////" == "/file/\\test/"
grPosix:   normalise "/file/./test" == "/file/test"
grPosix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
grPosix:   normalise "../bob/fred/" == "../bob/fred/"
grPosix:   normalise "./bob/fred/" == "bob/fred/"
grWindows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
grWindows: normalise "c:\\" == "C:\\"
grWindows: normalise "C:.\\" == "C:"
grWindows: normalise "\\\\server\\test" == "\\\\server\\test"
grWindows: normalise "//server/test" == "\\\\server\\test"
grWindows: normalise "c:/file" == "C:\\file"
grWindows: normalise "/file" == "\\file"
grWindows: normalise "\\" == "\\"
grWindows: normalise "/./" == "\\"
gr         normalise "." == "."
grPosix:   normalise "./" == "./"
grPosix:   normalise "./." == "./"
grPosix:   normalise "/./" == "/"
grPosix:   normalise "/" == "/"
grPosix:   normalise "bob/fred/." == "bob/fred/"
grPosix:   normalise "//home" == "/home"
gr</pre>
normalise :: FilePath -> FilePath

-- | Equality of two <a>FilePath</a>s. If you call
gr<tt>System.Directory.canonicalizePath</tt> first this has a much
grbetter chance of working. Note that this doesn't follow symlinks or
grDOSNAM~1s.
gr
gr<pre>
gr         x == y ==&gt; equalFilePath x y
gr         normalise x == normalise y ==&gt; equalFilePath x y
gr         equalFilePath "foo" "foo/"
gr         not (equalFilePath "foo" "/foo")
grPosix:   not (equalFilePath "foo" "FOO")
grWindows: equalFilePath "foo" "FOO"
grWindows: not (equalFilePath "C:" "C:/")
gr</pre>
equalFilePath :: FilePath -> FilePath -> Bool

-- | Contract a filename, based on a relative path. Note that the resulting
grpath will never introduce <tt>..</tt> paths, as the presence of
grsymlinks means <tt>../b</tt> may not reach <tt>a/b</tt> if it starts
grfrom <tt>a/c</tt>. For a worked example see <a>this blog post</a>.
gr
grThe corresponding <tt>makeAbsolute</tt> function can be found in
gr<tt>System.Directory</tt>.
gr
gr<pre>
gr         makeRelative "/directory" "/directory/file.ext" == "file.ext"
gr         Valid x =&gt; makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
gr         makeRelative x x == "."
gr         Valid x y =&gt; equalFilePath x y || (isRelative x &amp;&amp; makeRelative y x == x) || equalFilePath (y &lt;/&gt; makeRelative y x) x
grWindows: makeRelative "C:\\Home" "c:\\home\\bob" == "bob"
grWindows: makeRelative "C:\\Home" "c:/home/bob" == "bob"
grWindows: makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob"
grWindows: makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob"
grWindows: makeRelative "/Home" "/home/bob" == "bob"
grWindows: makeRelative "/" "//" == "//"
grPosix:   makeRelative "/Home" "/home/bob" == "/home/bob"
grPosix:   makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar"
grPosix:   makeRelative "/fred" "bob" == "bob"
grPosix:   makeRelative "/file/test" "/file/test/fred" == "fred"
grPosix:   makeRelative "/file/test" "/file/test/fred/" == "fred/"
grPosix:   makeRelative "some/path" "some/path/a/b/c" == "a/b/c"
gr</pre>
makeRelative :: FilePath -> FilePath -> FilePath

-- | Is a path relative, or is it fixed to the root?
gr
gr<pre>
grWindows: isRelative "path\\test" == True
grWindows: isRelative "c:\\test" == False
grWindows: isRelative "c:test" == True
grWindows: isRelative "c:\\" == False
grWindows: isRelative "c:/" == False
grWindows: isRelative "c:" == True
grWindows: isRelative "\\\\foo" == False
grWindows: isRelative "\\\\?\\foo" == False
grWindows: isRelative "\\\\?\\UNC\\foo" == False
grWindows: isRelative "/foo" == True
grWindows: isRelative "\\foo" == True
grPosix:   isRelative "test/path" == True
grPosix:   isRelative "/test" == False
grPosix:   isRelative "/" == False
gr</pre>
gr
grAccording to [1]:
gr
gr<ul>
gr<li>"A UNC name of any format [is never relative]."</li>
gr<li>"You cannot use the "\?" prefix with a relative path."</li>
gr</ul>
isRelative :: FilePath -> Bool

-- | <pre>
grnot . <a>isRelative</a>
gr</pre>
gr
gr<pre>
grisAbsolute x == not (isRelative x)
gr</pre>
isAbsolute :: FilePath -> Bool

-- | Is a FilePath valid, i.e. could you create a file like it? This
grfunction checks for invalid names, and invalid characters, but does
grnot check if length limits are exceeded, as these are typically
grfilesystem dependent.
gr
gr<pre>
gr         isValid "" == False
gr         isValid "\0" == False
grPosix:   isValid "/random_ path:*" == True
grPosix:   isValid x == not (null x)
grWindows: isValid "c:\\test" == True
grWindows: isValid "c:\\test:of_test" == False
grWindows: isValid "test*" == False
grWindows: isValid "c:\\test\\nul" == False
grWindows: isValid "c:\\test\\prn.txt" == False
grWindows: isValid "c:\\nul\\file" == False
grWindows: isValid "\\\\" == False
grWindows: isValid "\\\\\\foo" == False
grWindows: isValid "\\\\?\\D:file" == False
grWindows: isValid "foo\tbar" == False
grWindows: isValid "nul .txt" == False
grWindows: isValid " nul.txt" == True
gr</pre>
isValid :: FilePath -> Bool

-- | Take a FilePath and make it valid; does not change already valid
grFilePaths.
gr
gr<pre>
grisValid (makeValid x)
grisValid x ==&gt; makeValid x == x
grmakeValid "" == "_"
grmakeValid "file\0name" == "file_name"
grWindows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid"
grWindows: makeValid "c:\\test:of_test" == "c:\\test_of_test"
grWindows: makeValid "test*" == "test_"
grWindows: makeValid "c:\\test\\nul" == "c:\\test\\nul_"
grWindows: makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt"
grWindows: makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt"
grWindows: makeValid "c:\\nul\\file" == "c:\\nul_\\file"
grWindows: makeValid "\\\\\\foo" == "\\\\drive"
grWindows: makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file"
grWindows: makeValid "nul .txt" == "nul _.txt"
gr</pre>
makeValid :: FilePath -> FilePath


-- | A library for <a>FilePath</a> manipulations, using Posix or Windows
grfilepaths depending on the platform.
gr
grBoth <a>System.FilePath.Posix</a> and <a>System.FilePath.Windows</a>
grprovide the same interface. See either for examples and a list of the
gravailable functions.
module System.FilePath


-- | A library for <a>FilePath</a> manipulations, using Windows style paths
gron all platforms. Importing <a>System.FilePath</a> is usually better.
gr
grGiven the example <a>FilePath</a>: <tt>/directory/file.ext</tt>
gr
grWe can use the following functions to extract pieces.
gr
gr<ul>
gr<li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
gr<li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
gr<li><a>takeExtension</a> gives <tt>".ext"</tt></li>
gr<li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
gr<li><a>takeBaseName</a> gives <tt>"file"</tt></li>
gr</ul>
gr
grAnd we could have built an equivalent path with the following
grexpressions:
gr
gr<ul>
gr<li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
gr<li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
gr<li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
gr</ul>
gr
grEach function in this module is documented with several examples,
grwhich are also used as tests.
gr
grHere are a few examples of using the <tt>filepath</tt> functions
grtogether:
gr
gr<i>Example 1:</i> Find the possible locations of a Haskell module
gr<tt>Test</tt> imported from module <tt>Main</tt>:
gr
gr<pre>
gr[<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
gr</pre>
gr
gr<i>Example 2:</i> Download a file from <tt>url</tt> and save it to
grdisk:
gr
gr<pre>
grdo let file = <a>makeValid</a> url
gr   System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
gr</pre>
gr
gr<i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
grfile under <tt>interface</tt>:
gr
gr<pre>
gr<a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
gr</pre>
gr
grReferences: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
grMSDN)
module System.FilePath.Windows

-- | File and directory names are values of type <a>String</a>, whose
grprecise meaning is operating system dependent. Files can be opened,
gryielding a handle which can then be used to operate on the contents of
grthat file.
type FilePath = String

-- | The character that separates directories. In the case where more than
grone character is possible, <a>pathSeparator</a> is the 'ideal' one.
gr
gr<pre>
grWindows: pathSeparator == '\\'
grPosix:   pathSeparator ==  '/'
grisPathSeparator pathSeparator
gr</pre>
pathSeparator :: Char

-- | The list of all possible separators.
gr
gr<pre>
grWindows: pathSeparators == ['\\', '/']
grPosix:   pathSeparators == ['/']
grpathSeparator `elem` pathSeparators
gr</pre>
pathSeparators :: [Char]

-- | Rather than using <tt>(== <a>pathSeparator</a>)</tt>, use this. Test
grif something is a path separator.
gr
gr<pre>
grisPathSeparator a == (a `elem` pathSeparators)
gr</pre>
isPathSeparator :: Char -> Bool

-- | The character that is used to separate the entries in the $PATH
grenvironment variable.
gr
gr<pre>
grWindows: searchPathSeparator == ';'
grPosix:   searchPathSeparator == ':'
gr</pre>
searchPathSeparator :: Char

-- | Is the character a file separator?
gr
gr<pre>
grisSearchPathSeparator a == (a == searchPathSeparator)
gr</pre>
isSearchPathSeparator :: Char -> Bool

-- | File extension character
gr
gr<pre>
grextSeparator == '.'
gr</pre>
extSeparator :: Char

-- | Is the character an extension character?
gr
gr<pre>
grisExtSeparator a == (a == extSeparator)
gr</pre>
isExtSeparator :: Char -> Bool

-- | Take a string, split it on the <a>searchPathSeparator</a> character.
grBlank items are ignored on Windows, and converted to <tt>.</tt> on
grPosix. On Windows path elements are stripped of quotes.
gr
grFollows the recommendations in
gr<a>http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html</a>
gr
gr<pre>
grPosix:   splitSearchPath "File1:File2:File3"  == ["File1","File2","File3"]
grPosix:   splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"]
grWindows: splitSearchPath "File1;File2;File3"  == ["File1","File2","File3"]
grWindows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"]
grWindows: splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"]
gr</pre>
splitSearchPath :: String -> [FilePath]

-- | Get a list of <a>FilePath</a>s in the $PATH variable.
getSearchPath :: IO [FilePath]

-- | Split on the extension. <a>addExtension</a> is the inverse.
gr
gr<pre>
grsplitExtension "/directory/path.ext" == ("/directory/path",".ext")
gruncurry (++) (splitExtension x) == x
grValid x =&gt; uncurry addExtension (splitExtension x) == x
grsplitExtension "file.txt" == ("file",".txt")
grsplitExtension "file" == ("file","")
grsplitExtension "file/file.txt" == ("file/file",".txt")
grsplitExtension "file.txt/boris" == ("file.txt/boris","")
grsplitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
grsplitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
grsplitExtension "file/path.txt/" == ("file/path.txt/","")
gr</pre>
splitExtension :: FilePath -> (String, String)

-- | Get the extension of a file, returns <tt>""</tt> for no extension,
gr<tt>.ext</tt> otherwise.
gr
gr<pre>
grtakeExtension "/directory/path.ext" == ".ext"
grtakeExtension x == snd (splitExtension x)
grValid x =&gt; takeExtension (addExtension x "ext") == ".ext"
grValid x =&gt; takeExtension (replaceExtension x "ext") == ".ext"
gr</pre>
takeExtension :: FilePath -> String

-- | Set the extension of a file, overwriting one if already present,
grequivalent to <a>-&lt;.&gt;</a>.
gr
gr<pre>
grreplaceExtension "/directory/path.txt" "ext" == "/directory/path.ext"
grreplaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext"
grreplaceExtension "file.txt" ".bob" == "file.bob"
grreplaceExtension "file.txt" "bob" == "file.bob"
grreplaceExtension "file" ".bob" == "file.bob"
grreplaceExtension "file.txt" "" == "file"
grreplaceExtension "file.fred.bob" "txt" == "file.fred.txt"
grreplaceExtension x y == addExtension (dropExtension x) y
gr</pre>
replaceExtension :: FilePath -> String -> FilePath

-- | Remove the current extension and add another, equivalent to
gr<a>replaceExtension</a>.
gr
gr<pre>
gr"/directory/path.txt" -&lt;.&gt; "ext" == "/directory/path.ext"
gr"/directory/path.txt" -&lt;.&gt; ".ext" == "/directory/path.ext"
gr"foo.o" -&lt;.&gt; "c" == "foo.c"
gr</pre>
(-<.>) :: FilePath -> String -> FilePath
infixr 7 -<.>

-- | Remove last extension, and the "." preceding it.
gr
gr<pre>
grdropExtension "/directory/path.ext" == "/directory/path"
grdropExtension x == fst (splitExtension x)
gr</pre>
dropExtension :: FilePath -> FilePath

-- | Add an extension, even if there is already one there, equivalent to
gr<a>&lt;.&gt;</a>.
gr
gr<pre>
graddExtension "/directory/path" "ext" == "/directory/path.ext"
graddExtension "file.txt" "bib" == "file.txt.bib"
graddExtension "file." ".bib" == "file..bib"
graddExtension "file" ".bib" == "file.bib"
graddExtension "/" "x" == "/.x"
graddExtension x "" == x
grValid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
grWindows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
gr</pre>
addExtension :: FilePath -> String -> FilePath

-- | Does the given filename have an extension?
gr
gr<pre>
grhasExtension "/directory/path.ext" == True
grhasExtension "/directory/path" == False
grnull (takeExtension x) == not (hasExtension x)
gr</pre>
hasExtension :: FilePath -> Bool

-- | Add an extension, even if there is already one there, equivalent to
gr<a>addExtension</a>.
gr
gr<pre>
gr"/directory/path" &lt;.&gt; "ext" == "/directory/path.ext"
gr"/directory/path" &lt;.&gt; ".ext" == "/directory/path.ext"
gr</pre>
(<.>) :: FilePath -> String -> FilePath
infixr 7 <.>

-- | Split on all extensions.
gr
gr<pre>
grsplitExtensions "/directory/path.ext" == ("/directory/path",".ext")
grsplitExtensions "file.tar.gz" == ("file",".tar.gz")
gruncurry (++) (splitExtensions x) == x
grValid x =&gt; uncurry addExtension (splitExtensions x) == x
grsplitExtensions "file.tar.gz" == ("file",".tar.gz")
gr</pre>
splitExtensions :: FilePath -> (FilePath, String)

-- | Drop all extensions.
gr
gr<pre>
grdropExtensions "/directory/path.ext" == "/directory/path"
grdropExtensions "file.tar.gz" == "file"
grnot $ hasExtension $ dropExtensions x
grnot $ any isExtSeparator $ takeFileName $ dropExtensions x
gr</pre>
dropExtensions :: FilePath -> FilePath

-- | Get all extensions.
gr
gr<pre>
grtakeExtensions "/directory/path.ext" == ".ext"
grtakeExtensions "file.tar.gz" == ".tar.gz"
gr</pre>
takeExtensions :: FilePath -> String

-- | Replace all extensions of a file with a new extension. Note that
gr<a>replaceExtension</a> and <a>addExtension</a> both work for adding
grmultiple extensions, so only required when you need to drop all
grextensions first.
gr
gr<pre>
grreplaceExtensions "file.fred.bob" "txt" == "file.txt"
grreplaceExtensions "file.fred.bob" "tar.gz" == "file.tar.gz"
gr</pre>
replaceExtensions :: FilePath -> String -> FilePath

-- | Does the given filename have the specified extension?
gr
gr<pre>
gr"png" `isExtensionOf` "/directory/file.png" == True
gr".png" `isExtensionOf` "/directory/file.png" == True
gr".tar.gz" `isExtensionOf` "bar/foo.tar.gz" == True
gr"ar.gz" `isExtensionOf` "bar/foo.tar.gz" == False
gr"png" `isExtensionOf` "/directory/file.png.jpg" == False
gr"csv/table.csv" `isExtensionOf` "/data/csv/table.csv" == False
gr</pre>
isExtensionOf :: String -> FilePath -> Bool

-- | Drop the given extension from a FilePath, and the <tt>"."</tt>
grpreceding it. Returns <a>Nothing</a> if the FilePath does not have the
grgiven extension, or <a>Just</a> and the part before the extension if
grit does.
gr
grThis function can be more predictable than <a>dropExtensions</a>,
grespecially if the filename might itself contain <tt>.</tt> characters.
gr
gr<pre>
grstripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
grstripExtension "hi.o" "foo.x.hs.o" == Nothing
grdropExtension x == fromJust (stripExtension (takeExtension x) x)
grdropExtensions x == fromJust (stripExtension (takeExtensions x) x)
grstripExtension ".c.d" "a.b.c.d"  == Just "a.b"
grstripExtension ".c.d" "a.b..c.d" == Just "a.b."
grstripExtension "baz"  "foo.bar"  == Nothing
grstripExtension "bar"  "foobar"   == Nothing
grstripExtension ""     x          == Just x
gr</pre>
stripExtension :: String -> FilePath -> Maybe FilePath

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
grinverse. The first component will often end with a trailing slash.
gr
gr<pre>
grsplitFileName "/directory/file.ext" == ("/directory/","file.ext")
grValid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
grValid x =&gt; isValid (fst (splitFileName x))
grsplitFileName "file/bob.txt" == ("file/", "bob.txt")
grsplitFileName "file/" == ("file/", "")
grsplitFileName "bob" == ("./", "bob")
grPosix:   splitFileName "/" == ("/","")
grWindows: splitFileName "c:" == ("c:","")
gr</pre>
splitFileName :: FilePath -> (String, String)

-- | Get the file name.
gr
gr<pre>
grtakeFileName "/directory/file.ext" == "file.ext"
grtakeFileName "test/" == ""
grtakeFileName x `isSuffixOf` x
grtakeFileName x == snd (splitFileName x)
grValid x =&gt; takeFileName (replaceFileName x "fred") == "fred"
grValid x =&gt; takeFileName (x &lt;/&gt; "fred") == "fred"
grValid x =&gt; isRelative (takeFileName x)
gr</pre>
takeFileName :: FilePath -> FilePath

-- | Set the filename.
gr
gr<pre>
grreplaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
grValid x =&gt; replaceFileName x (takeFileName x) == x
gr</pre>
replaceFileName :: FilePath -> String -> FilePath

-- | Drop the filename. Unlike <a>takeDirectory</a>, this function will
grleave a trailing path separator on the directory.
gr
gr<pre>
grdropFileName "/directory/file.ext" == "/directory/"
grdropFileName x == fst (splitFileName x)
gr</pre>
dropFileName :: FilePath -> FilePath

-- | Get the base name, without an extension or path.
gr
gr<pre>
grtakeBaseName "/directory/file.ext" == "file"
grtakeBaseName "file/test.txt" == "test"
grtakeBaseName "dave.ext" == "dave"
grtakeBaseName "" == ""
grtakeBaseName "test" == "test"
grtakeBaseName (addTrailingPathSeparator x) == ""
grtakeBaseName "file/file.tar.gz" == "file.tar"
gr</pre>
takeBaseName :: FilePath -> String

-- | Set the base name.
gr
gr<pre>
grreplaceBaseName "/directory/other.ext" "file" == "/directory/file.ext"
grreplaceBaseName "file/test.txt" "bob" == "file/bob.txt"
grreplaceBaseName "fred" "bill" == "bill"
grreplaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar"
grValid x =&gt; replaceBaseName x (takeBaseName x) == x
gr</pre>
replaceBaseName :: FilePath -> String -> FilePath

-- | Get the directory name, move up one level.
gr
gr<pre>
gr          takeDirectory "/directory/other.ext" == "/directory"
gr          takeDirectory x `isPrefixOf` x || takeDirectory x == "."
gr          takeDirectory "foo" == "."
gr          takeDirectory "/" == "/"
gr          takeDirectory "/foo" == "/"
gr          takeDirectory "/foo/bar/baz" == "/foo/bar"
gr          takeDirectory "/foo/bar/baz/" == "/foo/bar/baz"
gr          takeDirectory "foo/bar/baz" == "foo/bar"
grWindows:  takeDirectory "foo\\bar" == "foo"
grWindows:  takeDirectory "foo\\bar\\\\" == "foo\\bar"
grWindows:  takeDirectory "C:\\" == "C:\\"
gr</pre>
takeDirectory :: FilePath -> FilePath

-- | Set the directory, keeping the filename the same.
gr
gr<pre>
grreplaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
grValid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
gr</pre>
replaceDirectory :: FilePath -> String -> FilePath

-- | An alias for <a>&lt;/&gt;</a>.
combine :: FilePath -> FilePath -> FilePath

-- | Combine two paths with a path separator. If the second path starts
grwith a path separator or a drive letter, then it returns the second.
grThe intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
grwill access the same file as <tt>setCurrentDirectory dir; readFile
grfile</tt>.
gr
gr<pre>
grPosix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
grWindows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
gr         "directory" &lt;/&gt; "/file.ext" == "/file.ext"
grValid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
gr</pre>
gr
grCombined:
gr
gr<pre>
grPosix:   "/" &lt;/&gt; "test" == "/test"
grPosix:   "home" &lt;/&gt; "bob" == "home/bob"
grPosix:   "x:" &lt;/&gt; "foo" == "x:/foo"
grWindows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
grWindows: "home" &lt;/&gt; "bob" == "home\\bob"
gr</pre>
gr
grNot combined:
gr
gr<pre>
grPosix:   "home" &lt;/&gt; "/bob" == "/bob"
grWindows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
gr</pre>
gr
grNot combined (tricky):
gr
grOn Windows, if a filepath starts with a single slash, it is relative
grto the root of the current drive. In [1], this is (confusingly)
grreferred to as an absolute path. The current behavior of
gr<a>&lt;/&gt;</a> is to never combine these forms.
gr
gr<pre>
grWindows: "home" &lt;/&gt; "/bob" == "/bob"
grWindows: "home" &lt;/&gt; "\\bob" == "\\bob"
grWindows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
gr</pre>
gr
grOn Windows, from [1]: "If a file name begins with only a disk
grdesignator but not the backslash after the colon, it is interpreted as
gra relative path to the current directory on the drive with the
grspecified letter." The current behavior of <a>&lt;/&gt;</a> is to
grnever combine these forms.
gr
gr<pre>
grWindows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
grWindows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
gr</pre>
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
gr
gr<pre>
grsplitPath "/directory/file.ext" == ["/","directory/","file.ext"]
grconcat (splitPath x) == x
grsplitPath "test//item/" == ["test//","item/"]
grsplitPath "test/item/file" == ["test/","item/","file"]
grsplitPath "" == []
grWindows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
grPosix:   splitPath "/file/test" == ["/","file/","test"]
gr</pre>
splitPath :: FilePath -> [FilePath]

-- | Join path elements back together.
gr
gr<pre>
grjoinPath ["/","directory/","file.ext"] == "/directory/file.ext"
grValid x =&gt; joinPath (splitPath x) == x
grjoinPath [] == ""
grPosix: joinPath ["test","file","path"] == "test/file/path"
gr</pre>
joinPath :: [FilePath] -> FilePath

-- | Just as <a>splitPath</a>, but don't add the trailing slashes to each
grelement.
gr
gr<pre>
gr         splitDirectories "/directory/file.ext" == ["/","directory","file.ext"]
gr         splitDirectories "test/file" == ["test","file"]
gr         splitDirectories "/test/file" == ["/","test","file"]
grWindows: splitDirectories "C:\\test\\file" == ["C:\\", "test", "file"]
gr         Valid x =&gt; joinPath (splitDirectories x) `equalFilePath` x
gr         splitDirectories "" == []
grWindows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
gr         splitDirectories "/test///file" == ["/","test","file"]
gr</pre>
splitDirectories :: FilePath -> [FilePath]

-- | Split a path into a drive and a path. On Posix, / is a Drive.
gr
gr<pre>
gruncurry (++) (splitDrive x) == x
grWindows: splitDrive "file" == ("","file")
grWindows: splitDrive "c:/file" == ("c:/","file")
grWindows: splitDrive "c:\\file" == ("c:\\","file")
grWindows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
grWindows: splitDrive "\\\\shared" == ("\\\\shared","")
grWindows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
grWindows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
grWindows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
grWindows: splitDrive "/d" == ("","/d")
grPosix:   splitDrive "/test" == ("/","test")
grPosix:   splitDrive "//test" == ("//","test")
grPosix:   splitDrive "test/file" == ("","test/file")
grPosix:   splitDrive "file" == ("","file")
gr</pre>
splitDrive :: FilePath -> (FilePath, FilePath)

-- | Join a drive and the rest of the path.
gr
gr<pre>
grValid x =&gt; uncurry joinDrive (splitDrive x) == x
grWindows: joinDrive "C:" "foo" == "C:foo"
grWindows: joinDrive "C:\\" "bar" == "C:\\bar"
grWindows: joinDrive "\\\\share" "foo" == "\\\\share\\foo"
grWindows: joinDrive "/:" "foo" == "/:\\foo"
gr</pre>
joinDrive :: FilePath -> FilePath -> FilePath

-- | Get the drive from a filepath.
gr
gr<pre>
grtakeDrive x == fst (splitDrive x)
gr</pre>
takeDrive :: FilePath -> FilePath

-- | Does a path have a drive.
gr
gr<pre>
grnot (hasDrive x) == null (takeDrive x)
grPosix:   hasDrive "/foo" == True
grWindows: hasDrive "C:\\foo" == True
grWindows: hasDrive "C:foo" == True
gr         hasDrive "foo" == False
gr         hasDrive "" == False
gr</pre>
hasDrive :: FilePath -> Bool

-- | Delete the drive, if it exists.
gr
gr<pre>
grdropDrive x == snd (splitDrive x)
gr</pre>
dropDrive :: FilePath -> FilePath

-- | Is an element a drive
gr
gr<pre>
grPosix:   isDrive "/" == True
grPosix:   isDrive "/foo" == False
grWindows: isDrive "C:\\" == True
grWindows: isDrive "C:\\foo" == False
gr         isDrive "" == False
gr</pre>
isDrive :: FilePath -> Bool

-- | Is an item either a directory or the last character a path separator?
gr
gr<pre>
grhasTrailingPathSeparator "test" == False
grhasTrailingPathSeparator "test/" == True
gr</pre>
hasTrailingPathSeparator :: FilePath -> Bool

-- | Add a trailing file path separator if one is not already present.
gr
gr<pre>
grhasTrailingPathSeparator (addTrailingPathSeparator x)
grhasTrailingPathSeparator x ==&gt; addTrailingPathSeparator x == x
grPosix:    addTrailingPathSeparator "test/rest" == "test/rest/"
gr</pre>
addTrailingPathSeparator :: FilePath -> FilePath

-- | Remove any trailing path separators
gr
gr<pre>
grdropTrailingPathSeparator "file/test/" == "file/test"
gr          dropTrailingPathSeparator "/" == "/"
grWindows:  dropTrailingPathSeparator "\\" == "\\"
grPosix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
gr</pre>
dropTrailingPathSeparator :: FilePath -> FilePath

-- | Normalise a file
gr
gr<ul>
gr<li>// outside of the drive can be made blank</li>
gr<li>/ -&gt; <a>pathSeparator</a></li>
gr<li>./ -&gt; ""</li>
gr</ul>
gr
gr<pre>
grPosix:   normalise "/file/\\test////" == "/file/\\test/"
grPosix:   normalise "/file/./test" == "/file/test"
grPosix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
grPosix:   normalise "../bob/fred/" == "../bob/fred/"
grPosix:   normalise "./bob/fred/" == "bob/fred/"
grWindows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
grWindows: normalise "c:\\" == "C:\\"
grWindows: normalise "C:.\\" == "C:"
grWindows: normalise "\\\\server\\test" == "\\\\server\\test"
grWindows: normalise "//server/test" == "\\\\server\\test"
grWindows: normalise "c:/file" == "C:\\file"
grWindows: normalise "/file" == "\\file"
grWindows: normalise "\\" == "\\"
grWindows: normalise "/./" == "\\"
gr         normalise "." == "."
grPosix:   normalise "./" == "./"
grPosix:   normalise "./." == "./"
grPosix:   normalise "/./" == "/"
grPosix:   normalise "/" == "/"
grPosix:   normalise "bob/fred/." == "bob/fred/"
grPosix:   normalise "//home" == "/home"
gr</pre>
normalise :: FilePath -> FilePath

-- | Equality of two <a>FilePath</a>s. If you call
gr<tt>System.Directory.canonicalizePath</tt> first this has a much
grbetter chance of working. Note that this doesn't follow symlinks or
grDOSNAM~1s.
gr
gr<pre>
gr         x == y ==&gt; equalFilePath x y
gr         normalise x == normalise y ==&gt; equalFilePath x y
gr         equalFilePath "foo" "foo/"
gr         not (equalFilePath "foo" "/foo")
grPosix:   not (equalFilePath "foo" "FOO")
grWindows: equalFilePath "foo" "FOO"
grWindows: not (equalFilePath "C:" "C:/")
gr</pre>
equalFilePath :: FilePath -> FilePath -> Bool

-- | Contract a filename, based on a relative path. Note that the resulting
grpath will never introduce <tt>..</tt> paths, as the presence of
grsymlinks means <tt>../b</tt> may not reach <tt>a/b</tt> if it starts
grfrom <tt>a/c</tt>. For a worked example see <a>this blog post</a>.
gr
grThe corresponding <tt>makeAbsolute</tt> function can be found in
gr<tt>System.Directory</tt>.
gr
gr<pre>
gr         makeRelative "/directory" "/directory/file.ext" == "file.ext"
gr         Valid x =&gt; makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
gr         makeRelative x x == "."
gr         Valid x y =&gt; equalFilePath x y || (isRelative x &amp;&amp; makeRelative y x == x) || equalFilePath (y &lt;/&gt; makeRelative y x) x
grWindows: makeRelative "C:\\Home" "c:\\home\\bob" == "bob"
grWindows: makeRelative "C:\\Home" "c:/home/bob" == "bob"
grWindows: makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob"
grWindows: makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob"
grWindows: makeRelative "/Home" "/home/bob" == "bob"
grWindows: makeRelative "/" "//" == "//"
grPosix:   makeRelative "/Home" "/home/bob" == "/home/bob"
grPosix:   makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar"
grPosix:   makeRelative "/fred" "bob" == "bob"
grPosix:   makeRelative "/file/test" "/file/test/fred" == "fred"
grPosix:   makeRelative "/file/test" "/file/test/fred/" == "fred/"
grPosix:   makeRelative "some/path" "some/path/a/b/c" == "a/b/c"
gr</pre>
makeRelative :: FilePath -> FilePath -> FilePath

-- | Is a path relative, or is it fixed to the root?
gr
gr<pre>
grWindows: isRelative "path\\test" == True
grWindows: isRelative "c:\\test" == False
grWindows: isRelative "c:test" == True
grWindows: isRelative "c:\\" == False
grWindows: isRelative "c:/" == False
grWindows: isRelative "c:" == True
grWindows: isRelative "\\\\foo" == False
grWindows: isRelative "\\\\?\\foo" == False
grWindows: isRelative "\\\\?\\UNC\\foo" == False
grWindows: isRelative "/foo" == True
grWindows: isRelative "\\foo" == True
grPosix:   isRelative "test/path" == True
grPosix:   isRelative "/test" == False
grPosix:   isRelative "/" == False
gr</pre>
gr
grAccording to [1]:
gr
gr<ul>
gr<li>"A UNC name of any format [is never relative]."</li>
gr<li>"You cannot use the "\?" prefix with a relative path."</li>
gr</ul>
isRelative :: FilePath -> Bool

-- | <pre>
grnot . <a>isRelative</a>
gr</pre>
gr
gr<pre>
grisAbsolute x == not (isRelative x)
gr</pre>
isAbsolute :: FilePath -> Bool

-- | Is a FilePath valid, i.e. could you create a file like it? This
grfunction checks for invalid names, and invalid characters, but does
grnot check if length limits are exceeded, as these are typically
grfilesystem dependent.
gr
gr<pre>
gr         isValid "" == False
gr         isValid "\0" == False
grPosix:   isValid "/random_ path:*" == True
grPosix:   isValid x == not (null x)
grWindows: isValid "c:\\test" == True
grWindows: isValid "c:\\test:of_test" == False
grWindows: isValid "test*" == False
grWindows: isValid "c:\\test\\nul" == False
grWindows: isValid "c:\\test\\prn.txt" == False
grWindows: isValid "c:\\nul\\file" == False
grWindows: isValid "\\\\" == False
grWindows: isValid "\\\\\\foo" == False
grWindows: isValid "\\\\?\\D:file" == False
grWindows: isValid "foo\tbar" == False
grWindows: isValid "nul .txt" == False
grWindows: isValid " nul.txt" == True
gr</pre>
isValid :: FilePath -> Bool

-- | Take a FilePath and make it valid; does not change already valid
grFilePaths.
gr
gr<pre>
grisValid (makeValid x)
grisValid x ==&gt; makeValid x == x
grmakeValid "" == "_"
grmakeValid "file\0name" == "file_name"
grWindows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid"
grWindows: makeValid "c:\\test:of_test" == "c:\\test_of_test"
grWindows: makeValid "test*" == "test_"
grWindows: makeValid "c:\\test\\nul" == "c:\\test\\nul_"
grWindows: makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt"
grWindows: makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt"
grWindows: makeValid "c:\\nul\\file" == "c:\\nul_\\file"
grWindows: makeValid "\\\\\\foo" == "\\\\drive"
grWindows: makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file"
grWindows: makeValid "nul .txt" == "nul _.txt"
gr</pre>
makeValid :: FilePath -> FilePath
