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


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


-- | A library for <a>FilePath</a> manipulations, using Posix style paths
°5uon all platforms. Importing <a>System.FilePath</a> is usually better.
°5u
°5uGiven the example <a>FilePath</a>: <tt>/directory/file.ext</tt>
°5u
°5uWe can use the following functions to extract pieces.
°5u
°5u<ul>
°5u<li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
°5u<li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
°5u<li><a>takeExtension</a> gives <tt>".ext"</tt></li>
°5u<li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
°5u<li><a>takeBaseName</a> gives <tt>"file"</tt></li>
°5u</ul>
°5u
°5uAnd we could have built an equivalent path with the following
°5uexpressions:
°5u
°5u<ul>
°5u<li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
°5u<li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
°5u<li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
°5u</ul>
°5u
°5uEach function in this module is documented with several examples,
°5uwhich are also used as tests.
°5u
°5uHere are a few examples of using the <tt>filepath</tt> functions
°5utogether:
°5u
°5u<i>Example 1:</i> Find the possible locations of a Haskell module
°5u<tt>Test</tt> imported from module <tt>Main</tt>:
°5u
°5u<pre>
°5u[<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
°5u</pre>
°5u
°5u<i>Example 2:</i> Download a file from <tt>url</tt> and save it to
°5udisk:
°5u
°5u<pre>
°5udo let file = <a>makeValid</a> url
°5u   System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
°5u</pre>
°5u
°5u<i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
°5ufile under <tt>interface</tt>:
°5u
°5u<pre>
°5u<a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
°5u</pre>
°5u
°5uReferences: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
°5uMSDN)
module System.FilePath.Posix

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

-- | The character that separates directories. In the case where more than
°5uone character is possible, <a>pathSeparator</a> is the 'ideal' one.
°5u
°5u<pre>
°5uWindows: pathSeparator == '\\'
°5uPosix:   pathSeparator ==  '/'
°5uisPathSeparator pathSeparator
°5u</pre>
pathSeparator :: Char

-- | The list of all possible separators.
°5u
°5u<pre>
°5uWindows: pathSeparators == ['\\', '/']
°5uPosix:   pathSeparators == ['/']
°5upathSeparator `elem` pathSeparators
°5u</pre>
pathSeparators :: [Char]

-- | Rather than using <tt>(== <a>pathSeparator</a>)</tt>, use this. Test
°5uif something is a path separator.
°5u
°5u<pre>
°5uisPathSeparator a == (a `elem` pathSeparators)
°5u</pre>
isPathSeparator :: Char -> Bool

-- | The character that is used to separate the entries in the $PATH
°5uenvironment variable.
°5u
°5u<pre>
°5uWindows: searchPathSeparator == ';'
°5uPosix:   searchPathSeparator == ':'
°5u</pre>
searchPathSeparator :: Char

-- | Is the character a file separator?
°5u
°5u<pre>
°5uisSearchPathSeparator a == (a == searchPathSeparator)
°5u</pre>
isSearchPathSeparator :: Char -> Bool

-- | File extension character
°5u
°5u<pre>
°5uextSeparator == '.'
°5u</pre>
extSeparator :: Char

-- | Is the character an extension character?
°5u
°5u<pre>
°5uisExtSeparator a == (a == extSeparator)
°5u</pre>
isExtSeparator :: Char -> Bool

-- | Take a string, split it on the <a>searchPathSeparator</a> character.
°5uBlank items are ignored on Windows, and converted to <tt>.</tt> on
°5uPosix. On Windows path elements are stripped of quotes.
°5u
°5uFollows the recommendations in
°5u<a>http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html</a>
°5u
°5u<pre>
°5uPosix:   splitSearchPath "File1:File2:File3"  == ["File1","File2","File3"]
°5uPosix:   splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"]
°5uWindows: splitSearchPath "File1;File2;File3"  == ["File1","File2","File3"]
°5uWindows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"]
°5uWindows: splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"]
°5u</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.
°5u
°5u<pre>
°5usplitExtension "/directory/path.ext" == ("/directory/path",".ext")
°5uuncurry (++) (splitExtension x) == x
°5uValid x =&gt; uncurry addExtension (splitExtension x) == x
°5usplitExtension "file.txt" == ("file",".txt")
°5usplitExtension "file" == ("file","")
°5usplitExtension "file/file.txt" == ("file/file",".txt")
°5usplitExtension "file.txt/boris" == ("file.txt/boris","")
°5usplitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
°5usplitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
°5usplitExtension "file/path.txt/" == ("file/path.txt/","")
°5u</pre>
splitExtension :: FilePath -> (String, String)

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

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

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

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

-- | Add an extension, even if there is already one there, equivalent to
°5u<a>&lt;.&gt;</a>.
°5u
°5u<pre>
°5uaddExtension "/directory/path" "ext" == "/directory/path.ext"
°5uaddExtension "file.txt" "bib" == "file.txt.bib"
°5uaddExtension "file." ".bib" == "file..bib"
°5uaddExtension "file" ".bib" == "file.bib"
°5uaddExtension "/" "x" == "/.x"
°5uaddExtension x "" == x
°5uValid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
°5uWindows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
°5u</pre>
addExtension :: FilePath -> String -> FilePath

-- | Does the given filename have an extension?
°5u
°5u<pre>
°5uhasExtension "/directory/path.ext" == True
°5uhasExtension "/directory/path" == False
°5unull (takeExtension x) == not (hasExtension x)
°5u</pre>
hasExtension :: FilePath -> Bool

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

-- | Split on all extensions.
°5u
°5u<pre>
°5usplitExtensions "/directory/path.ext" == ("/directory/path",".ext")
°5usplitExtensions "file.tar.gz" == ("file",".tar.gz")
°5uuncurry (++) (splitExtensions x) == x
°5uValid x =&gt; uncurry addExtension (splitExtensions x) == x
°5usplitExtensions "file.tar.gz" == ("file",".tar.gz")
°5u</pre>
splitExtensions :: FilePath -> (FilePath, String)

-- | Drop all extensions.
°5u
°5u<pre>
°5udropExtensions "/directory/path.ext" == "/directory/path"
°5udropExtensions "file.tar.gz" == "file"
°5unot $ hasExtension $ dropExtensions x
°5unot $ any isExtSeparator $ takeFileName $ dropExtensions x
°5u</pre>
dropExtensions :: FilePath -> FilePath

-- | Get all extensions.
°5u
°5u<pre>
°5utakeExtensions "/directory/path.ext" == ".ext"
°5utakeExtensions "file.tar.gz" == ".tar.gz"
°5u</pre>
takeExtensions :: FilePath -> String

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

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

-- | Drop the given extension from a FilePath, and the <tt>"."</tt>
°5upreceding it. Returns <a>Nothing</a> if the FilePath does not have the
°5ugiven extension, or <a>Just</a> and the part before the extension if
°5uit does.
°5u
°5uThis function can be more predictable than <a>dropExtensions</a>,
°5uespecially if the filename might itself contain <tt>.</tt> characters.
°5u
°5u<pre>
°5ustripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
°5ustripExtension "hi.o" "foo.x.hs.o" == Nothing
°5udropExtension x == fromJust (stripExtension (takeExtension x) x)
°5udropExtensions x == fromJust (stripExtension (takeExtensions x) x)
°5ustripExtension ".c.d" "a.b.c.d"  == Just "a.b"
°5ustripExtension ".c.d" "a.b..c.d" == Just "a.b."
°5ustripExtension "baz"  "foo.bar"  == Nothing
°5ustripExtension "bar"  "foobar"   == Nothing
°5ustripExtension ""     x          == Just x
°5u</pre>
stripExtension :: String -> FilePath -> Maybe FilePath

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
°5uinverse. The first component will often end with a trailing slash.
°5u
°5u<pre>
°5usplitFileName "/directory/file.ext" == ("/directory/","file.ext")
°5uValid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
°5uValid x =&gt; isValid (fst (splitFileName x))
°5usplitFileName "file/bob.txt" == ("file/", "bob.txt")
°5usplitFileName "file/" == ("file/", "")
°5usplitFileName "bob" == ("./", "bob")
°5uPosix:   splitFileName "/" == ("/","")
°5uWindows: splitFileName "c:" == ("c:","")
°5u</pre>
splitFileName :: FilePath -> (String, String)

-- | Get the file name.
°5u
°5u<pre>
°5utakeFileName "/directory/file.ext" == "file.ext"
°5utakeFileName "test/" == ""
°5utakeFileName x `isSuffixOf` x
°5utakeFileName x == snd (splitFileName x)
°5uValid x =&gt; takeFileName (replaceFileName x "fred") == "fred"
°5uValid x =&gt; takeFileName (x &lt;/&gt; "fred") == "fred"
°5uValid x =&gt; isRelative (takeFileName x)
°5u</pre>
takeFileName :: FilePath -> FilePath

-- | Set the filename.
°5u
°5u<pre>
°5ureplaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
°5uValid x =&gt; replaceFileName x (takeFileName x) == x
°5u</pre>
replaceFileName :: FilePath -> String -> FilePath

-- | Drop the filename. Unlike <a>takeDirectory</a>, this function will
°5uleave a trailing path separator on the directory.
°5u
°5u<pre>
°5udropFileName "/directory/file.ext" == "/directory/"
°5udropFileName x == fst (splitFileName x)
°5u</pre>
dropFileName :: FilePath -> FilePath

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

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

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

-- | Set the directory, keeping the filename the same.
°5u
°5u<pre>
°5ureplaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
°5uValid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
°5u</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
°5uwith a path separator or a drive letter, then it returns the second.
°5uThe intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
°5uwill access the same file as <tt>setCurrentDirectory dir; readFile
°5ufile</tt>.
°5u
°5u<pre>
°5uPosix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
°5uWindows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
°5u         "directory" &lt;/&gt; "/file.ext" == "/file.ext"
°5uValid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
°5u</pre>
°5u
°5uCombined:
°5u
°5u<pre>
°5uPosix:   "/" &lt;/&gt; "test" == "/test"
°5uPosix:   "home" &lt;/&gt; "bob" == "home/bob"
°5uPosix:   "x:" &lt;/&gt; "foo" == "x:/foo"
°5uWindows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
°5uWindows: "home" &lt;/&gt; "bob" == "home\\bob"
°5u</pre>
°5u
°5uNot combined:
°5u
°5u<pre>
°5uPosix:   "home" &lt;/&gt; "/bob" == "/bob"
°5uWindows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
°5u</pre>
°5u
°5uNot combined (tricky):
°5u
°5uOn Windows, if a filepath starts with a single slash, it is relative
°5uto the root of the current drive. In [1], this is (confusingly)
°5ureferred to as an absolute path. The current behavior of
°5u<a>&lt;/&gt;</a> is to never combine these forms.
°5u
°5u<pre>
°5uWindows: "home" &lt;/&gt; "/bob" == "/bob"
°5uWindows: "home" &lt;/&gt; "\\bob" == "\\bob"
°5uWindows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
°5u</pre>
°5u
°5uOn Windows, from [1]: "If a file name begins with only a disk
°5udesignator but not the backslash after the colon, it is interpreted as
°5ua relative path to the current directory on the drive with the
°5uspecified letter." The current behavior of <a>&lt;/&gt;</a> is to
°5unever combine these forms.
°5u
°5u<pre>
°5uWindows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
°5uWindows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
°5u</pre>
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
°5u
°5u<pre>
°5usplitPath "/directory/file.ext" == ["/","directory/","file.ext"]
°5uconcat (splitPath x) == x
°5usplitPath "test//item/" == ["test//","item/"]
°5usplitPath "test/item/file" == ["test/","item/","file"]
°5usplitPath "" == []
°5uWindows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
°5uPosix:   splitPath "/file/test" == ["/","file/","test"]
°5u</pre>
splitPath :: FilePath -> [FilePath]

-- | Join path elements back together.
°5u
°5u<pre>
°5ujoinPath ["/","directory/","file.ext"] == "/directory/file.ext"
°5uValid x =&gt; joinPath (splitPath x) == x
°5ujoinPath [] == ""
°5uPosix: joinPath ["test","file","path"] == "test/file/path"
°5u</pre>
joinPath :: [FilePath] -> FilePath

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

-- | Split a path into a drive and a path. On Posix, / is a Drive.
°5u
°5u<pre>
°5uuncurry (++) (splitDrive x) == x
°5uWindows: splitDrive "file" == ("","file")
°5uWindows: splitDrive "c:/file" == ("c:/","file")
°5uWindows: splitDrive "c:\\file" == ("c:\\","file")
°5uWindows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
°5uWindows: splitDrive "\\\\shared" == ("\\\\shared","")
°5uWindows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
°5uWindows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
°5uWindows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
°5uWindows: splitDrive "/d" == ("","/d")
°5uPosix:   splitDrive "/test" == ("/","test")
°5uPosix:   splitDrive "//test" == ("//","test")
°5uPosix:   splitDrive "test/file" == ("","test/file")
°5uPosix:   splitDrive "file" == ("","file")
°5u</pre>
splitDrive :: FilePath -> (FilePath, FilePath)

-- | Join a drive and the rest of the path.
°5u
°5u<pre>
°5uValid x =&gt; uncurry joinDrive (splitDrive x) == x
°5uWindows: joinDrive "C:" "foo" == "C:foo"
°5uWindows: joinDrive "C:\\" "bar" == "C:\\bar"
°5uWindows: joinDrive "\\\\share" "foo" == "\\\\share\\foo"
°5uWindows: joinDrive "/:" "foo" == "/:\\foo"
°5u</pre>
joinDrive :: FilePath -> FilePath -> FilePath

-- | Get the drive from a filepath.
°5u
°5u<pre>
°5utakeDrive x == fst (splitDrive x)
°5u</pre>
takeDrive :: FilePath -> FilePath

-- | Does a path have a drive.
°5u
°5u<pre>
°5unot (hasDrive x) == null (takeDrive x)
°5uPosix:   hasDrive "/foo" == True
°5uWindows: hasDrive "C:\\foo" == True
°5uWindows: hasDrive "C:foo" == True
°5u         hasDrive "foo" == False
°5u         hasDrive "" == False
°5u</pre>
hasDrive :: FilePath -> Bool

-- | Delete the drive, if it exists.
°5u
°5u<pre>
°5udropDrive x == snd (splitDrive x)
°5u</pre>
dropDrive :: FilePath -> FilePath

-- | Is an element a drive
°5u
°5u<pre>
°5uPosix:   isDrive "/" == True
°5uPosix:   isDrive "/foo" == False
°5uWindows: isDrive "C:\\" == True
°5uWindows: isDrive "C:\\foo" == False
°5u         isDrive "" == False
°5u</pre>
isDrive :: FilePath -> Bool

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

-- | Add a trailing file path separator if one is not already present.
°5u
°5u<pre>
°5uhasTrailingPathSeparator (addTrailingPathSeparator x)
°5uhasTrailingPathSeparator x ==&gt; addTrailingPathSeparator x == x
°5uPosix:    addTrailingPathSeparator "test/rest" == "test/rest/"
°5u</pre>
addTrailingPathSeparator :: FilePath -> FilePath

-- | Remove any trailing path separators
°5u
°5u<pre>
°5udropTrailingPathSeparator "file/test/" == "file/test"
°5u          dropTrailingPathSeparator "/" == "/"
°5uWindows:  dropTrailingPathSeparator "\\" == "\\"
°5uPosix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
°5u</pre>
dropTrailingPathSeparator :: FilePath -> FilePath

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

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

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

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

-- | <pre>
°5unot . <a>isRelative</a>
°5u</pre>
°5u
°5u<pre>
°5uisAbsolute x == not (isRelative x)
°5u</pre>
isAbsolute :: FilePath -> Bool

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

-- | Take a FilePath and make it valid; does not change already valid
°5uFilePaths.
°5u
°5u<pre>
°5uisValid (makeValid x)
°5uisValid x ==&gt; makeValid x == x
°5umakeValid "" == "_"
°5umakeValid "file\0name" == "file_name"
°5uWindows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid"
°5uWindows: makeValid "c:\\test:of_test" == "c:\\test_of_test"
°5uWindows: makeValid "test*" == "test_"
°5uWindows: makeValid "c:\\test\\nul" == "c:\\test\\nul_"
°5uWindows: makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt"
°5uWindows: makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt"
°5uWindows: makeValid "c:\\nul\\file" == "c:\\nul_\\file"
°5uWindows: makeValid "\\\\\\foo" == "\\\\drive"
°5uWindows: makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file"
°5uWindows: makeValid "nul .txt" == "nul _.txt"
°5u</pre>
makeValid :: FilePath -> FilePath


-- | A library for <a>FilePath</a> manipulations, using Posix or Windows
°5ufilepaths depending on the platform.
°5u
°5uBoth <a>System.FilePath.Posix</a> and <a>System.FilePath.Windows</a>
°5uprovide the same interface. See either for examples and a list of the
°5uavailable functions.
module System.FilePath


-- | A library for <a>FilePath</a> manipulations, using Windows style paths
°5uon all platforms. Importing <a>System.FilePath</a> is usually better.
°5u
°5uGiven the example <a>FilePath</a>: <tt>/directory/file.ext</tt>
°5u
°5uWe can use the following functions to extract pieces.
°5u
°5u<ul>
°5u<li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
°5u<li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
°5u<li><a>takeExtension</a> gives <tt>".ext"</tt></li>
°5u<li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
°5u<li><a>takeBaseName</a> gives <tt>"file"</tt></li>
°5u</ul>
°5u
°5uAnd we could have built an equivalent path with the following
°5uexpressions:
°5u
°5u<ul>
°5u<li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
°5u<li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
°5u<li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
°5u</ul>
°5u
°5uEach function in this module is documented with several examples,
°5uwhich are also used as tests.
°5u
°5uHere are a few examples of using the <tt>filepath</tt> functions
°5utogether:
°5u
°5u<i>Example 1:</i> Find the possible locations of a Haskell module
°5u<tt>Test</tt> imported from module <tt>Main</tt>:
°5u
°5u<pre>
°5u[<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
°5u</pre>
°5u
°5u<i>Example 2:</i> Download a file from <tt>url</tt> and save it to
°5udisk:
°5u
°5u<pre>
°5udo let file = <a>makeValid</a> url
°5u   System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
°5u</pre>
°5u
°5u<i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
°5ufile under <tt>interface</tt>:
°5u
°5u<pre>
°5u<a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
°5u</pre>
°5u
°5uReferences: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
°5uMSDN)
module System.FilePath.Windows

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

-- | The character that separates directories. In the case where more than
°5uone character is possible, <a>pathSeparator</a> is the 'ideal' one.
°5u
°5u<pre>
°5uWindows: pathSeparator == '\\'
°5uPosix:   pathSeparator ==  '/'
°5uisPathSeparator pathSeparator
°5u</pre>
pathSeparator :: Char

-- | The list of all possible separators.
°5u
°5u<pre>
°5uWindows: pathSeparators == ['\\', '/']
°5uPosix:   pathSeparators == ['/']
°5upathSeparator `elem` pathSeparators
°5u</pre>
pathSeparators :: [Char]

-- | Rather than using <tt>(== <a>pathSeparator</a>)</tt>, use this. Test
°5uif something is a path separator.
°5u
°5u<pre>
°5uisPathSeparator a == (a `elem` pathSeparators)
°5u</pre>
isPathSeparator :: Char -> Bool

-- | The character that is used to separate the entries in the $PATH
°5uenvironment variable.
°5u
°5u<pre>
°5uWindows: searchPathSeparator == ';'
°5uPosix:   searchPathSeparator == ':'
°5u</pre>
searchPathSeparator :: Char

-- | Is the character a file separator?
°5u
°5u<pre>
°5uisSearchPathSeparator a == (a == searchPathSeparator)
°5u</pre>
isSearchPathSeparator :: Char -> Bool

-- | File extension character
°5u
°5u<pre>
°5uextSeparator == '.'
°5u</pre>
extSeparator :: Char

-- | Is the character an extension character?
°5u
°5u<pre>
°5uisExtSeparator a == (a == extSeparator)
°5u</pre>
isExtSeparator :: Char -> Bool

-- | Take a string, split it on the <a>searchPathSeparator</a> character.
°5uBlank items are ignored on Windows, and converted to <tt>.</tt> on
°5uPosix. On Windows path elements are stripped of quotes.
°5u
°5uFollows the recommendations in
°5u<a>http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html</a>
°5u
°5u<pre>
°5uPosix:   splitSearchPath "File1:File2:File3"  == ["File1","File2","File3"]
°5uPosix:   splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"]
°5uWindows: splitSearchPath "File1;File2;File3"  == ["File1","File2","File3"]
°5uWindows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"]
°5uWindows: splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"]
°5u</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.
°5u
°5u<pre>
°5usplitExtension "/directory/path.ext" == ("/directory/path",".ext")
°5uuncurry (++) (splitExtension x) == x
°5uValid x =&gt; uncurry addExtension (splitExtension x) == x
°5usplitExtension "file.txt" == ("file",".txt")
°5usplitExtension "file" == ("file","")
°5usplitExtension "file/file.txt" == ("file/file",".txt")
°5usplitExtension "file.txt/boris" == ("file.txt/boris","")
°5usplitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
°5usplitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
°5usplitExtension "file/path.txt/" == ("file/path.txt/","")
°5u</pre>
splitExtension :: FilePath -> (String, String)

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

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

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

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

-- | Add an extension, even if there is already one there, equivalent to
°5u<a>&lt;.&gt;</a>.
°5u
°5u<pre>
°5uaddExtension "/directory/path" "ext" == "/directory/path.ext"
°5uaddExtension "file.txt" "bib" == "file.txt.bib"
°5uaddExtension "file." ".bib" == "file..bib"
°5uaddExtension "file" ".bib" == "file.bib"
°5uaddExtension "/" "x" == "/.x"
°5uaddExtension x "" == x
°5uValid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
°5uWindows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
°5u</pre>
addExtension :: FilePath -> String -> FilePath

-- | Does the given filename have an extension?
°5u
°5u<pre>
°5uhasExtension "/directory/path.ext" == True
°5uhasExtension "/directory/path" == False
°5unull (takeExtension x) == not (hasExtension x)
°5u</pre>
hasExtension :: FilePath -> Bool

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

-- | Split on all extensions.
°5u
°5u<pre>
°5usplitExtensions "/directory/path.ext" == ("/directory/path",".ext")
°5usplitExtensions "file.tar.gz" == ("file",".tar.gz")
°5uuncurry (++) (splitExtensions x) == x
°5uValid x =&gt; uncurry addExtension (splitExtensions x) == x
°5usplitExtensions "file.tar.gz" == ("file",".tar.gz")
°5u</pre>
splitExtensions :: FilePath -> (FilePath, String)

-- | Drop all extensions.
°5u
°5u<pre>
°5udropExtensions "/directory/path.ext" == "/directory/path"
°5udropExtensions "file.tar.gz" == "file"
°5unot $ hasExtension $ dropExtensions x
°5unot $ any isExtSeparator $ takeFileName $ dropExtensions x
°5u</pre>
dropExtensions :: FilePath -> FilePath

-- | Get all extensions.
°5u
°5u<pre>
°5utakeExtensions "/directory/path.ext" == ".ext"
°5utakeExtensions "file.tar.gz" == ".tar.gz"
°5u</pre>
takeExtensions :: FilePath -> String

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

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

-- | Drop the given extension from a FilePath, and the <tt>"."</tt>
°5upreceding it. Returns <a>Nothing</a> if the FilePath does not have the
°5ugiven extension, or <a>Just</a> and the part before the extension if
°5uit does.
°5u
°5uThis function can be more predictable than <a>dropExtensions</a>,
°5uespecially if the filename might itself contain <tt>.</tt> characters.
°5u
°5u<pre>
°5ustripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
°5ustripExtension "hi.o" "foo.x.hs.o" == Nothing
°5udropExtension x == fromJust (stripExtension (takeExtension x) x)
°5udropExtensions x == fromJust (stripExtension (takeExtensions x) x)
°5ustripExtension ".c.d" "a.b.c.d"  == Just "a.b"
°5ustripExtension ".c.d" "a.b..c.d" == Just "a.b."
°5ustripExtension "baz"  "foo.bar"  == Nothing
°5ustripExtension "bar"  "foobar"   == Nothing
°5ustripExtension ""     x          == Just x
°5u</pre>
stripExtension :: String -> FilePath -> Maybe FilePath

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
°5uinverse. The first component will often end with a trailing slash.
°5u
°5u<pre>
°5usplitFileName "/directory/file.ext" == ("/directory/","file.ext")
°5uValid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
°5uValid x =&gt; isValid (fst (splitFileName x))
°5usplitFileName "file/bob.txt" == ("file/", "bob.txt")
°5usplitFileName "file/" == ("file/", "")
°5usplitFileName "bob" == ("./", "bob")
°5uPosix:   splitFileName "/" == ("/","")
°5uWindows: splitFileName "c:" == ("c:","")
°5u</pre>
splitFileName :: FilePath -> (String, String)

-- | Get the file name.
°5u
°5u<pre>
°5utakeFileName "/directory/file.ext" == "file.ext"
°5utakeFileName "test/" == ""
°5utakeFileName x `isSuffixOf` x
°5utakeFileName x == snd (splitFileName x)
°5uValid x =&gt; takeFileName (replaceFileName x "fred") == "fred"
°5uValid x =&gt; takeFileName (x &lt;/&gt; "fred") == "fred"
°5uValid x =&gt; isRelative (takeFileName x)
°5u</pre>
takeFileName :: FilePath -> FilePath

-- | Set the filename.
°5u
°5u<pre>
°5ureplaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
°5uValid x =&gt; replaceFileName x (takeFileName x) == x
°5u</pre>
replaceFileName :: FilePath -> String -> FilePath

-- | Drop the filename. Unlike <a>takeDirectory</a>, this function will
°5uleave a trailing path separator on the directory.
°5u
°5u<pre>
°5udropFileName "/directory/file.ext" == "/directory/"
°5udropFileName x == fst (splitFileName x)
°5u</pre>
dropFileName :: FilePath -> FilePath

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

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

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

-- | Set the directory, keeping the filename the same.
°5u
°5u<pre>
°5ureplaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
°5uValid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
°5u</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
°5uwith a path separator or a drive letter, then it returns the second.
°5uThe intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
°5uwill access the same file as <tt>setCurrentDirectory dir; readFile
°5ufile</tt>.
°5u
°5u<pre>
°5uPosix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
°5uWindows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
°5u         "directory" &lt;/&gt; "/file.ext" == "/file.ext"
°5uValid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
°5u</pre>
°5u
°5uCombined:
°5u
°5u<pre>
°5uPosix:   "/" &lt;/&gt; "test" == "/test"
°5uPosix:   "home" &lt;/&gt; "bob" == "home/bob"
°5uPosix:   "x:" &lt;/&gt; "foo" == "x:/foo"
°5uWindows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
°5uWindows: "home" &lt;/&gt; "bob" == "home\\bob"
°5u</pre>
°5u
°5uNot combined:
°5u
°5u<pre>
°5uPosix:   "home" &lt;/&gt; "/bob" == "/bob"
°5uWindows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
°5u</pre>
°5u
°5uNot combined (tricky):
°5u
°5uOn Windows, if a filepath starts with a single slash, it is relative
°5uto the root of the current drive. In [1], this is (confusingly)
°5ureferred to as an absolute path. The current behavior of
°5u<a>&lt;/&gt;</a> is to never combine these forms.
°5u
°5u<pre>
°5uWindows: "home" &lt;/&gt; "/bob" == "/bob"
°5uWindows: "home" &lt;/&gt; "\\bob" == "\\bob"
°5uWindows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
°5u</pre>
°5u
°5uOn Windows, from [1]: "If a file name begins with only a disk
°5udesignator but not the backslash after the colon, it is interpreted as
°5ua relative path to the current directory on the drive with the
°5uspecified letter." The current behavior of <a>&lt;/&gt;</a> is to
°5unever combine these forms.
°5u
°5u<pre>
°5uWindows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
°5uWindows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
°5u</pre>
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
°5u
°5u<pre>
°5usplitPath "/directory/file.ext" == ["/","directory/","file.ext"]
°5uconcat (splitPath x) == x
°5usplitPath "test//item/" == ["test//","item/"]
°5usplitPath "test/item/file" == ["test/","item/","file"]
°5usplitPath "" == []
°5uWindows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
°5uPosix:   splitPath "/file/test" == ["/","file/","test"]
°5u</pre>
splitPath :: FilePath -> [FilePath]

-- | Join path elements back together.
°5u
°5u<pre>
°5ujoinPath ["/","directory/","file.ext"] == "/directory/file.ext"
°5uValid x =&gt; joinPath (splitPath x) == x
°5ujoinPath [] == ""
°5uPosix: joinPath ["test","file","path"] == "test/file/path"
°5u</pre>
joinPath :: [FilePath] -> FilePath

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

-- | Split a path into a drive and a path. On Posix, / is a Drive.
°5u
°5u<pre>
°5uuncurry (++) (splitDrive x) == x
°5uWindows: splitDrive "file" == ("","file")
°5uWindows: splitDrive "c:/file" == ("c:/","file")
°5uWindows: splitDrive "c:\\file" == ("c:\\","file")
°5uWindows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
°5uWindows: splitDrive "\\\\shared" == ("\\\\shared","")
°5uWindows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
°5uWindows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
°5uWindows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
°5uWindows: splitDrive "/d" == ("","/d")
°5uPosix:   splitDrive "/test" == ("/","test")
°5uPosix:   splitDrive "//test" == ("//","test")
°5uPosix:   splitDrive "test/file" == ("","test/file")
°5uPosix:   splitDrive "file" == ("","file")
°5u</pre>
splitDrive :: FilePath -> (FilePath, FilePath)

-- | Join a drive and the rest of the path.
°5u
°5u<pre>
°5uValid x =&gt; uncurry joinDrive (splitDrive x) == x
°5uWindows: joinDrive "C:" "foo" == "C:foo"
°5uWindows: joinDrive "C:\\" "bar" == "C:\\bar"
°5uWindows: joinDrive "\\\\share" "foo" == "\\\\share\\foo"
°5uWindows: joinDrive "/:" "foo" == "/:\\foo"
°5u</pre>
joinDrive :: FilePath -> FilePath -> FilePath

-- | Get the drive from a filepath.
°5u
°5u<pre>
°5utakeDrive x == fst (splitDrive x)
°5u</pre>
takeDrive :: FilePath -> FilePath

-- | Does a path have a drive.
°5u
°5u<pre>
°5unot (hasDrive x) == null (takeDrive x)
°5uPosix:   hasDrive "/foo" == True
°5uWindows: hasDrive "C:\\foo" == True
°5uWindows: hasDrive "C:foo" == True
°5u         hasDrive "foo" == False
°5u         hasDrive "" == False
°5u</pre>
hasDrive :: FilePath -> Bool

-- | Delete the drive, if it exists.
°5u
°5u<pre>
°5udropDrive x == snd (splitDrive x)
°5u</pre>
dropDrive :: FilePath -> FilePath

-- | Is an element a drive
°5u
°5u<pre>
°5uPosix:   isDrive "/" == True
°5uPosix:   isDrive "/foo" == False
°5uWindows: isDrive "C:\\" == True
°5uWindows: isDrive "C:\\foo" == False
°5u         isDrive "" == False
°5u</pre>
isDrive :: FilePath -> Bool

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

-- | Add a trailing file path separator if one is not already present.
°5u
°5u<pre>
°5uhasTrailingPathSeparator (addTrailingPathSeparator x)
°5uhasTrailingPathSeparator x ==&gt; addTrailingPathSeparator x == x
°5uPosix:    addTrailingPathSeparator "test/rest" == "test/rest/"
°5u</pre>
addTrailingPathSeparator :: FilePath -> FilePath

-- | Remove any trailing path separators
°5u
°5u<pre>
°5udropTrailingPathSeparator "file/test/" == "file/test"
°5u          dropTrailingPathSeparator "/" == "/"
°5uWindows:  dropTrailingPathSeparator "\\" == "\\"
°5uPosix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
°5u</pre>
dropTrailingPathSeparator :: FilePath -> FilePath

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

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

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

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

-- | <pre>
°5unot . <a>isRelative</a>
°5u</pre>
°5u
°5u<pre>
°5uisAbsolute x == not (isRelative x)
°5u</pre>
isAbsolute :: FilePath -> Bool

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

-- | Take a FilePath and make it valid; does not change already valid
°5uFilePaths.
°5u
°5u<pre>
°5uisValid (makeValid x)
°5uisValid x ==&gt; makeValid x == x
°5umakeValid "" == "_"
°5umakeValid "file\0name" == "file_name"
°5uWindows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid"
°5uWindows: makeValid "c:\\test:of_test" == "c:\\test_of_test"
°5uWindows: makeValid "test*" == "test_"
°5uWindows: makeValid "c:\\test\\nul" == "c:\\test\\nul_"
°5uWindows: makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt"
°5uWindows: makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt"
°5uWindows: makeValid "c:\\nul\\file" == "c:\\nul_\\file"
°5uWindows: makeValid "\\\\\\foo" == "\\\\drive"
°5uWindows: makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file"
°5uWindows: makeValid "nul .txt" == "nul _.txt"
°5u</pre>
makeValid :: FilePath -> FilePath
