-- 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
