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


-- | Library for manipulating FilePaths in a cross platform way.
--   
--   This package provides functionality for manipulating <tt>FilePath</tt>
--   values, and is shipped with <a>GHC</a>. It provides two variants for
--   filepaths:
--   
--   <ol>
--   <li>legacy filepaths: <tt>type FilePath = String</tt></li>
--   <li>operating system abstracted filepaths (<tt>OsPath</tt>):
--   internally unpinned <tt>ShortByteString</tt> (platform-dependent
--   encoding)</li>
--   </ol>
--   
--   It is recommended to use <tt>OsPath</tt> when possible, because it is
--   more correct.
--   
--   For each variant there are three main modules:
--   
--   <ul>
--   <li><a>System.FilePath.Posix</a> / <a>System.OsPath.Posix</a>
--   manipulates POSIX/Linux style <tt>FilePath</tt> values (with
--   <tt>/</tt> as the path separator).</li>
--   <li><a>System.FilePath.Windows</a> / <a>System.OsPath.Windows</a>
--   manipulates Windows style <tt>FilePath</tt> values (with either
--   <tt>\</tt> or <tt>/</tt> as the path separator, and deals with
--   drives).</li>
--   <li><a>System.FilePath</a> / <a>System.OsPath</a> for dealing with
--   current platform-specific filepaths</li>
--   </ul>
--   
--   For more powerful string manipulation of <tt>OsPath</tt>, you can use
--   the <a>os-string package</a> (<tt>OsPath</tt> is a type synonym for
--   <tt>OsString</tt>).
--   
--   An introduction into the new API can be found in this <a>blog
--   post</a>. Code examples for the new API can be found <a>here</a>.
@package filepath
@version 1.5.4.0


-- | A library for <a>FilePath</a> manipulations, using Posix style paths
--   on all platforms. Importing <a>System.FilePath</a> is usually better.
--   
--   Given the example <a>FilePath</a>: <tt>/directory/file.ext</tt>
--   
--   We can use the following functions to extract pieces.
--   
--   <ul>
--   <li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
--   <li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
--   <li><a>takeExtension</a> gives <tt>".ext"</tt></li>
--   <li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
--   <li><a>takeBaseName</a> gives <tt>"file"</tt></li>
--   </ul>
--   
--   And we could have built an equivalent path with the following
--   expressions:
--   
--   <ul>
--   <li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
--   <li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
--   <li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
--   </ul>
--   
--   Each function in this module is documented with several examples,
--   which are also used as tests.
--   
--   Here are a few examples of using the <tt>filepath</tt> functions
--   together:
--   
--   <i>Example 1:</i> Find the possible locations of a Haskell module
--   <tt>Test</tt> imported from module <tt>Main</tt>:
--   
--   <pre>
--   [<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
--   </pre>
--   
--   <i>Example 2:</i> Download a file from <tt>url</tt> and save it to
--   disk:
--   
--   <pre>
--   do let file = <a>makeValid</a> url
--      System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
--   </pre>
--   
--   <i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
--   file under <tt>interface</tt>:
--   
--   <pre>
--   <a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
--   </pre>
--   
--   References: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
--   MSDN)
module System.FilePath.Posix
type FilePath = String

-- | The character that separates directories. In the case where more than
--   one character is possible, <a>pathSeparator</a> is the 'ideal' one.
--   
--   <pre>
--   Windows: pathSeparator == '\\'
--   Posix:   pathSeparator ==  '/'
--   isPathSeparator pathSeparator
--   </pre>
pathSeparator :: Char

-- | The list of all possible separators.
--   
--   <pre>
--   Windows: pathSeparators == ['\\', '/']
--   Posix:   pathSeparators == ['/']
--   pathSeparator `elem` pathSeparators
--   </pre>
pathSeparators :: [Char]

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

-- | The character that is used to separate the entries in the $PATH
--   environment variable.
--   
--   <pre>
--   Windows: searchPathSeparator == ';'
--   Posix:   searchPathSeparator == ':'
--   </pre>
searchPathSeparator :: Char

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
--   inverse. The first component will often end with a trailing slash.
--   
--   <pre>
--   splitFileName "/directory/file.ext" == ("/directory/","file.ext")
--   Valid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
--   Valid x =&gt; isValid (fst (splitFileName x))
--   splitFileName "file/bob.txt" == ("file/", "bob.txt")
--   splitFileName "file/" == ("file/", "")
--   splitFileName "bob" == ("./", "bob")
--   Posix:   splitFileName "/" == ("/","")
--   Windows: splitFileName "c:" == ("c:","")
--   Windows: splitFileName "\\\\?\\A:\\fred" == ("\\\\?\\A:\\","fred")
--   Windows: splitFileName "\\\\?\\A:" == ("\\\\?\\A:","")
--   </pre>
splitFileName :: FilePath -> (String, String)

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

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

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

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

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

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

-- | Set the directory, keeping the filename the same.
--   
--   <pre>
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
--   </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
--   with a path separator or a drive letter, then it returns the second.
--   The intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
--   will access the same file as <tt>setCurrentDirectory dir; readFile
--   file</tt>.
--   
--   <pre>
--   Posix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
--   Windows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
--            "directory" &lt;/&gt; "/file.ext" == "/file.ext"
--   Valid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
--   </pre>
--   
--   Combined:
--   
--   <pre>
--   Posix:   "/" &lt;/&gt; "test" == "/test"
--   Posix:   "home" &lt;/&gt; "bob" == "home/bob"
--   Posix:   "x:" &lt;/&gt; "foo" == "x:/foo"
--   Windows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
--   Windows: "home" &lt;/&gt; "bob" == "home\\bob"
--   </pre>
--   
--   Not combined:
--   
--   <pre>
--   Posix:   "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
--   </pre>
--   
--   Not combined (tricky):
--   
--   On Windows, if a filepath starts with a single slash, it is relative
--   to the root of the current drive. In [1], this is (confusingly)
--   referred to as an absolute path. The current behavior of
--   <a>&lt;/&gt;</a> is to never combine these forms.
--   
--   <pre>
--   Windows: "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "\\bob" == "\\bob"
--   Windows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
--   </pre>
--   
--   On Windows, from [1]: "If a file name begins with only a disk
--   designator but not the backslash after the colon, it is interpreted as
--   a relative path to the current directory on the drive with the
--   specified letter." The current behavior of <a>&lt;/&gt;</a> is to
--   never combine these forms.
--   
--   <pre>
--   Windows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   Windows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   </pre>
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   <pre>
--   splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
--   concat (splitPath x) == x
--   splitPath "test//item/" == ["test//","item/"]
--   splitPath "test/item/file" == ["test/","item/","file"]
--   splitPath "" == []
--   Windows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
--   Posix:   splitPath "/file/test" == ["/","file/","test"]
--   </pre>
splitPath :: FilePath -> [FilePath]

-- | Join path elements back together.
--   
--   <pre>
--   joinPath z == foldr (&lt;/&gt;) "" z
--   joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
--   Valid x =&gt; joinPath (splitPath x) == x
--   joinPath [] == ""
--   Posix: joinPath ["test","file","path"] == "test/file/path"
--   </pre>
joinPath :: [FilePath] -> FilePath

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

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   <pre>
--   uncurry (&lt;&gt;) (splitDrive x) == x
--   Windows: splitDrive "file" == ("","file")
--   Windows: splitDrive "c:/file" == ("c:/","file")
--   Windows: splitDrive "c:\\file" == ("c:\\","file")
--   Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
--   Windows: splitDrive "\\\\shared" == ("\\\\shared","")
--   Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
--   Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
--   Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
--   Windows: splitDrive "/d" == ("","/d")
--   Posix:   splitDrive "/test" == ("/","test")
--   Posix:   splitDrive "//test" == ("//","test")
--   Posix:   splitDrive "test/file" == ("","test/file")
--   Posix:   splitDrive "file" == ("","file")
--   </pre>
splitDrive :: FilePath -> (FilePath, FilePath)

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

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

-- | Does a path have a drive.
--   
--   <pre>
--   not (hasDrive x) == null (takeDrive x)
--   Posix:   hasDrive "/foo" == True
--   Windows: hasDrive "C:\\foo" == True
--   Windows: hasDrive "C:foo" == True
--            hasDrive "foo" == False
--            hasDrive "" == False
--   </pre>
hasDrive :: FilePath -> Bool

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

-- | Is an element a drive
--   
--   <pre>
--   Posix:   isDrive "/" == True
--   Posix:   isDrive "/foo" == False
--   Windows: isDrive "C:\\" == True
--   Windows: isDrive "C:\\foo" == False
--            isDrive "" == False
--   </pre>
isDrive :: FilePath -> Bool

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

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

-- | Remove any trailing path separators
--   
--   <pre>
--   dropTrailingPathSeparator "file/test/" == "file/test"
--             dropTrailingPathSeparator "/" == "/"
--   Windows:  dropTrailingPathSeparator "\\" == "\\"
--   Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
--   </pre>
dropTrailingPathSeparator :: FilePath -> FilePath

-- | Normalise a file
--   
--   <ul>
--   <li>// outside of the drive can be made blank</li>
--   <li>/ -&gt; <a>pathSeparator</a></li>
--   <li>./ -&gt; ""</li>
--   </ul>
--   
--   Does not remove <tt>".."</tt>, because of symlinks.
--   
--   <pre>
--   Posix:   normalise "/file/\\test////" == "/file/\\test/"
--   Posix:   normalise "/file/./test" == "/file/test"
--   Posix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
--   Posix:   normalise "../bob/fred/" == "../bob/fred/"
--   Posix:   normalise "/a/../c" == "/a/../c"
--   Posix:   normalise "./bob/fred/" == "bob/fred/"
--   Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
--   Windows: normalise "c:\\" == "C:\\"
--   Windows: normalise "c:\\\\\\\\" == "C:\\"
--   Windows: normalise "C:.\\" == "C:"
--   Windows: normalise "\\\\server\\test" == "\\\\server\\test"
--   Windows: normalise "//server/test" == "\\\\server\\test"
--   Windows: normalise "c:/file" == "C:\\file"
--   Windows: normalise "/file" == "\\file"
--   Windows: normalise "\\" == "\\"
--   Windows: normalise "/./" == "\\"
--            normalise "." == "."
--   Posix:   normalise "./" == "./"
--   Posix:   normalise "./." == "./"
--   Posix:   normalise "/./" == "/"
--   Posix:   normalise "/" == "/"
--   Posix:   normalise "bob/fred/." == "bob/fred/"
--   Posix:   normalise "//home" == "/home"
--   </pre>
normalise :: FilePath -> FilePath

-- | Equality of two <tt>FILEPATH</tt>s. If you call
--   <tt>System.Directory.canonicalizePath</tt> first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to <a>normalise</a>, this does not expand <tt>".."</tt>,
--   because of symlinks.
--   
--   <pre>
--            x == y ==&gt; equalFilePath x y
--            normalise x == normalise y ==&gt; equalFilePath x y
--            equalFilePath "foo" "foo/"
--            not (equalFilePath "/a/../c" "/c")
--            not (equalFilePath "foo" "/foo")
--   Posix:   not (equalFilePath "foo" "FOO")
--   Windows: equalFilePath "foo" "FOO"
--   Windows: not (equalFilePath "C:" "C:/")
--   </pre>
equalFilePath :: FilePath -> FilePath -> Bool

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

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

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

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

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


-- | A library for <a>FilePath</a> manipulations, using Posix or Windows
--   filepaths depending on the platform.
--   
--   Both <a>System.FilePath.Posix</a> and <a>System.FilePath.Windows</a>
--   provide the same interface.
--   
--   Given the example <a>FilePath</a>: <tt>/directory/file.ext</tt>
--   
--   We can use the following functions to extract pieces.
--   
--   <ul>
--   <li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
--   <li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
--   <li><a>takeExtension</a> gives <tt>".ext"</tt></li>
--   <li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
--   <li><a>takeBaseName</a> gives <tt>"file"</tt></li>
--   </ul>
--   
--   And we could have built an equivalent path with the following
--   expressions:
--   
--   <ul>
--   <li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
--   <li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
--   <li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
--   </ul>
--   
--   Each function in this module is documented with several examples,
--   which are also used as tests.
--   
--   Here are a few examples of using the <tt>filepath</tt> functions
--   together:
--   
--   <i>Example 1:</i> Find the possible locations of a Haskell module
--   <tt>Test</tt> imported from module <tt>Main</tt>:
--   
--   <pre>
--   [<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
--   </pre>
--   
--   <i>Example 2:</i> Download a file from <tt>url</tt> and save it to
--   disk:
--   
--   <pre>
--   do let file = <a>makeValid</a> url
--     System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
--   </pre>
--   
--   <i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
--   file under <tt>interface</tt>:
--   
--   <pre>
--   <a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
--   </pre>
--   
--   References: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
--   MSDN)
module System.FilePath
type FilePath = String

-- | The character that separates directories. In the case where more than
--   one character is possible, <a>pathSeparator</a> is the 'ideal' one.
--   
--   <pre>
--   Windows: pathSeparator == '\\'
--   Posix:   pathSeparator ==  '/'
--   isPathSeparator pathSeparator
--   </pre>
pathSeparator :: Char

-- | The list of all possible separators.
--   
--   <pre>
--   Windows: pathSeparators == ['\\', '/']
--   Posix:   pathSeparators == ['/']
--   pathSeparator `elem` pathSeparators
--   </pre>
pathSeparators :: [Char]

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

-- | The character that is used to separate the entries in the $PATH
--   environment variable.
--   
--   <pre>
--   Windows: searchPathSeparator == ';'
--   Posix:   searchPathSeparator == ':'
--   </pre>
searchPathSeparator :: Char

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
--   inverse. The first component will often end with a trailing slash.
--   
--   <pre>
--   splitFileName "/directory/file.ext" == ("/directory/","file.ext")
--   Valid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
--   Valid x =&gt; isValid (fst (splitFileName x))
--   splitFileName "file/bob.txt" == ("file/", "bob.txt")
--   splitFileName "file/" == ("file/", "")
--   splitFileName "bob" == ("./", "bob")
--   Posix:   splitFileName "/" == ("/","")
--   Windows: splitFileName "c:" == ("c:","")
--   Windows: splitFileName "\\\\?\\A:\\fred" == ("\\\\?\\A:\\","fred")
--   Windows: splitFileName "\\\\?\\A:" == ("\\\\?\\A:","")
--   </pre>
splitFileName :: FilePath -> (String, String)

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

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

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

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

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

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

-- | Set the directory, keeping the filename the same.
--   
--   <pre>
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
--   </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
--   with a path separator or a drive letter, then it returns the second.
--   The intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
--   will access the same file as <tt>setCurrentDirectory dir; readFile
--   file</tt>.
--   
--   <pre>
--   Posix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
--   Windows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
--            "directory" &lt;/&gt; "/file.ext" == "/file.ext"
--   Valid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
--   </pre>
--   
--   Combined:
--   
--   <pre>
--   Posix:   "/" &lt;/&gt; "test" == "/test"
--   Posix:   "home" &lt;/&gt; "bob" == "home/bob"
--   Posix:   "x:" &lt;/&gt; "foo" == "x:/foo"
--   Windows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
--   Windows: "home" &lt;/&gt; "bob" == "home\\bob"
--   </pre>
--   
--   Not combined:
--   
--   <pre>
--   Posix:   "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
--   </pre>
--   
--   Not combined (tricky):
--   
--   On Windows, if a filepath starts with a single slash, it is relative
--   to the root of the current drive. In [1], this is (confusingly)
--   referred to as an absolute path. The current behavior of
--   <a>&lt;/&gt;</a> is to never combine these forms.
--   
--   <pre>
--   Windows: "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "\\bob" == "\\bob"
--   Windows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
--   </pre>
--   
--   On Windows, from [1]: "If a file name begins with only a disk
--   designator but not the backslash after the colon, it is interpreted as
--   a relative path to the current directory on the drive with the
--   specified letter." The current behavior of <a>&lt;/&gt;</a> is to
--   never combine these forms.
--   
--   <pre>
--   Windows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   Windows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   </pre>
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   <pre>
--   splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
--   concat (splitPath x) == x
--   splitPath "test//item/" == ["test//","item/"]
--   splitPath "test/item/file" == ["test/","item/","file"]
--   splitPath "" == []
--   Windows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
--   Posix:   splitPath "/file/test" == ["/","file/","test"]
--   </pre>
splitPath :: FilePath -> [FilePath]

-- | Join path elements back together.
--   
--   <pre>
--   joinPath z == foldr (&lt;/&gt;) "" z
--   joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
--   Valid x =&gt; joinPath (splitPath x) == x
--   joinPath [] == ""
--   Posix: joinPath ["test","file","path"] == "test/file/path"
--   </pre>
joinPath :: [FilePath] -> FilePath

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

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   <pre>
--   uncurry (&lt;&gt;) (splitDrive x) == x
--   Windows: splitDrive "file" == ("","file")
--   Windows: splitDrive "c:/file" == ("c:/","file")
--   Windows: splitDrive "c:\\file" == ("c:\\","file")
--   Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
--   Windows: splitDrive "\\\\shared" == ("\\\\shared","")
--   Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
--   Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
--   Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
--   Windows: splitDrive "/d" == ("","/d")
--   Posix:   splitDrive "/test" == ("/","test")
--   Posix:   splitDrive "//test" == ("//","test")
--   Posix:   splitDrive "test/file" == ("","test/file")
--   Posix:   splitDrive "file" == ("","file")
--   </pre>
splitDrive :: FilePath -> (FilePath, FilePath)

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

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

-- | Does a path have a drive.
--   
--   <pre>
--   not (hasDrive x) == null (takeDrive x)
--   Posix:   hasDrive "/foo" == True
--   Windows: hasDrive "C:\\foo" == True
--   Windows: hasDrive "C:foo" == True
--            hasDrive "foo" == False
--            hasDrive "" == False
--   </pre>
hasDrive :: FilePath -> Bool

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

-- | Is an element a drive
--   
--   <pre>
--   Posix:   isDrive "/" == True
--   Posix:   isDrive "/foo" == False
--   Windows: isDrive "C:\\" == True
--   Windows: isDrive "C:\\foo" == False
--            isDrive "" == False
--   </pre>
isDrive :: FilePath -> Bool

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

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

-- | Remove any trailing path separators
--   
--   <pre>
--   dropTrailingPathSeparator "file/test/" == "file/test"
--             dropTrailingPathSeparator "/" == "/"
--   Windows:  dropTrailingPathSeparator "\\" == "\\"
--   Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
--   </pre>
dropTrailingPathSeparator :: FilePath -> FilePath

-- | Normalise a file
--   
--   <ul>
--   <li>// outside of the drive can be made blank</li>
--   <li>/ -&gt; <a>pathSeparator</a></li>
--   <li>./ -&gt; ""</li>
--   </ul>
--   
--   Does not remove <tt>".."</tt>, because of symlinks.
--   
--   <pre>
--   Posix:   normalise "/file/\\test////" == "/file/\\test/"
--   Posix:   normalise "/file/./test" == "/file/test"
--   Posix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
--   Posix:   normalise "../bob/fred/" == "../bob/fred/"
--   Posix:   normalise "/a/../c" == "/a/../c"
--   Posix:   normalise "./bob/fred/" == "bob/fred/"
--   Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
--   Windows: normalise "c:\\" == "C:\\"
--   Windows: normalise "c:\\\\\\\\" == "C:\\"
--   Windows: normalise "C:.\\" == "C:"
--   Windows: normalise "\\\\server\\test" == "\\\\server\\test"
--   Windows: normalise "//server/test" == "\\\\server\\test"
--   Windows: normalise "c:/file" == "C:\\file"
--   Windows: normalise "/file" == "\\file"
--   Windows: normalise "\\" == "\\"
--   Windows: normalise "/./" == "\\"
--            normalise "." == "."
--   Posix:   normalise "./" == "./"
--   Posix:   normalise "./." == "./"
--   Posix:   normalise "/./" == "/"
--   Posix:   normalise "/" == "/"
--   Posix:   normalise "bob/fred/." == "bob/fred/"
--   Posix:   normalise "//home" == "/home"
--   </pre>
normalise :: FilePath -> FilePath

-- | Equality of two <tt>FILEPATH</tt>s. If you call
--   <tt>System.Directory.canonicalizePath</tt> first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to <a>normalise</a>, this does not expand <tt>".."</tt>,
--   because of symlinks.
--   
--   <pre>
--            x == y ==&gt; equalFilePath x y
--            normalise x == normalise y ==&gt; equalFilePath x y
--            equalFilePath "foo" "foo/"
--            not (equalFilePath "/a/../c" "/c")
--            not (equalFilePath "foo" "/foo")
--   Posix:   not (equalFilePath "foo" "FOO")
--   Windows: equalFilePath "foo" "FOO"
--   Windows: not (equalFilePath "C:" "C:/")
--   </pre>
equalFilePath :: FilePath -> FilePath -> Bool

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

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

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

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

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


-- | A library for <a>FilePath</a> manipulations, using Windows style paths
--   on all platforms. Importing <a>System.FilePath</a> is usually better.
--   
--   Given the example <a>FilePath</a>: <tt>/directory/file.ext</tt>
--   
--   We can use the following functions to extract pieces.
--   
--   <ul>
--   <li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
--   <li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
--   <li><a>takeExtension</a> gives <tt>".ext"</tt></li>
--   <li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
--   <li><a>takeBaseName</a> gives <tt>"file"</tt></li>
--   </ul>
--   
--   And we could have built an equivalent path with the following
--   expressions:
--   
--   <ul>
--   <li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
--   <li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
--   <li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
--   </ul>
--   
--   Each function in this module is documented with several examples,
--   which are also used as tests.
--   
--   Here are a few examples of using the <tt>filepath</tt> functions
--   together:
--   
--   <i>Example 1:</i> Find the possible locations of a Haskell module
--   <tt>Test</tt> imported from module <tt>Main</tt>:
--   
--   <pre>
--   [<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
--   </pre>
--   
--   <i>Example 2:</i> Download a file from <tt>url</tt> and save it to
--   disk:
--   
--   <pre>
--   do let file = <a>makeValid</a> url
--      System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
--   </pre>
--   
--   <i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
--   file under <tt>interface</tt>:
--   
--   <pre>
--   <a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
--   </pre>
--   
--   References: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
--   MSDN)
module System.FilePath.Windows
type FilePath = String

-- | The character that separates directories. In the case where more than
--   one character is possible, <a>pathSeparator</a> is the 'ideal' one.
--   
--   <pre>
--   Windows: pathSeparator == '\\'
--   Posix:   pathSeparator ==  '/'
--   isPathSeparator pathSeparator
--   </pre>
pathSeparator :: Char

-- | The list of all possible separators.
--   
--   <pre>
--   Windows: pathSeparators == ['\\', '/']
--   Posix:   pathSeparators == ['/']
--   pathSeparator `elem` pathSeparators
--   </pre>
pathSeparators :: [Char]

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

-- | The character that is used to separate the entries in the $PATH
--   environment variable.
--   
--   <pre>
--   Windows: searchPathSeparator == ';'
--   Posix:   searchPathSeparator == ':'
--   </pre>
searchPathSeparator :: Char

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
--   inverse. The first component will often end with a trailing slash.
--   
--   <pre>
--   splitFileName "/directory/file.ext" == ("/directory/","file.ext")
--   Valid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
--   Valid x =&gt; isValid (fst (splitFileName x))
--   splitFileName "file/bob.txt" == ("file/", "bob.txt")
--   splitFileName "file/" == ("file/", "")
--   splitFileName "bob" == ("./", "bob")
--   Posix:   splitFileName "/" == ("/","")
--   Windows: splitFileName "c:" == ("c:","")
--   Windows: splitFileName "\\\\?\\A:\\fred" == ("\\\\?\\A:\\","fred")
--   Windows: splitFileName "\\\\?\\A:" == ("\\\\?\\A:","")
--   </pre>
splitFileName :: FilePath -> (String, String)

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

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

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

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

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

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

-- | Set the directory, keeping the filename the same.
--   
--   <pre>
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
--   </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
--   with a path separator or a drive letter, then it returns the second.
--   The intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
--   will access the same file as <tt>setCurrentDirectory dir; readFile
--   file</tt>.
--   
--   <pre>
--   Posix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
--   Windows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
--            "directory" &lt;/&gt; "/file.ext" == "/file.ext"
--   Valid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
--   </pre>
--   
--   Combined:
--   
--   <pre>
--   Posix:   "/" &lt;/&gt; "test" == "/test"
--   Posix:   "home" &lt;/&gt; "bob" == "home/bob"
--   Posix:   "x:" &lt;/&gt; "foo" == "x:/foo"
--   Windows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
--   Windows: "home" &lt;/&gt; "bob" == "home\\bob"
--   </pre>
--   
--   Not combined:
--   
--   <pre>
--   Posix:   "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
--   </pre>
--   
--   Not combined (tricky):
--   
--   On Windows, if a filepath starts with a single slash, it is relative
--   to the root of the current drive. In [1], this is (confusingly)
--   referred to as an absolute path. The current behavior of
--   <a>&lt;/&gt;</a> is to never combine these forms.
--   
--   <pre>
--   Windows: "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "\\bob" == "\\bob"
--   Windows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
--   </pre>
--   
--   On Windows, from [1]: "If a file name begins with only a disk
--   designator but not the backslash after the colon, it is interpreted as
--   a relative path to the current directory on the drive with the
--   specified letter." The current behavior of <a>&lt;/&gt;</a> is to
--   never combine these forms.
--   
--   <pre>
--   Windows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   Windows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   </pre>
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   <pre>
--   splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
--   concat (splitPath x) == x
--   splitPath "test//item/" == ["test//","item/"]
--   splitPath "test/item/file" == ["test/","item/","file"]
--   splitPath "" == []
--   Windows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
--   Posix:   splitPath "/file/test" == ["/","file/","test"]
--   </pre>
splitPath :: FilePath -> [FilePath]

-- | Join path elements back together.
--   
--   <pre>
--   joinPath z == foldr (&lt;/&gt;) "" z
--   joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
--   Valid x =&gt; joinPath (splitPath x) == x
--   joinPath [] == ""
--   Posix: joinPath ["test","file","path"] == "test/file/path"
--   </pre>
joinPath :: [FilePath] -> FilePath

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

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   <pre>
--   uncurry (&lt;&gt;) (splitDrive x) == x
--   Windows: splitDrive "file" == ("","file")
--   Windows: splitDrive "c:/file" == ("c:/","file")
--   Windows: splitDrive "c:\\file" == ("c:\\","file")
--   Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
--   Windows: splitDrive "\\\\shared" == ("\\\\shared","")
--   Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
--   Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
--   Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
--   Windows: splitDrive "/d" == ("","/d")
--   Posix:   splitDrive "/test" == ("/","test")
--   Posix:   splitDrive "//test" == ("//","test")
--   Posix:   splitDrive "test/file" == ("","test/file")
--   Posix:   splitDrive "file" == ("","file")
--   </pre>
splitDrive :: FilePath -> (FilePath, FilePath)

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

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

-- | Does a path have a drive.
--   
--   <pre>
--   not (hasDrive x) == null (takeDrive x)
--   Posix:   hasDrive "/foo" == True
--   Windows: hasDrive "C:\\foo" == True
--   Windows: hasDrive "C:foo" == True
--            hasDrive "foo" == False
--            hasDrive "" == False
--   </pre>
hasDrive :: FilePath -> Bool

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

-- | Is an element a drive
--   
--   <pre>
--   Posix:   isDrive "/" == True
--   Posix:   isDrive "/foo" == False
--   Windows: isDrive "C:\\" == True
--   Windows: isDrive "C:\\foo" == False
--            isDrive "" == False
--   </pre>
isDrive :: FilePath -> Bool

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

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

-- | Remove any trailing path separators
--   
--   <pre>
--   dropTrailingPathSeparator "file/test/" == "file/test"
--             dropTrailingPathSeparator "/" == "/"
--   Windows:  dropTrailingPathSeparator "\\" == "\\"
--   Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
--   </pre>
dropTrailingPathSeparator :: FilePath -> FilePath

-- | Normalise a file
--   
--   <ul>
--   <li>// outside of the drive can be made blank</li>
--   <li>/ -&gt; <a>pathSeparator</a></li>
--   <li>./ -&gt; ""</li>
--   </ul>
--   
--   Does not remove <tt>".."</tt>, because of symlinks.
--   
--   <pre>
--   Posix:   normalise "/file/\\test////" == "/file/\\test/"
--   Posix:   normalise "/file/./test" == "/file/test"
--   Posix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
--   Posix:   normalise "../bob/fred/" == "../bob/fred/"
--   Posix:   normalise "/a/../c" == "/a/../c"
--   Posix:   normalise "./bob/fred/" == "bob/fred/"
--   Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
--   Windows: normalise "c:\\" == "C:\\"
--   Windows: normalise "c:\\\\\\\\" == "C:\\"
--   Windows: normalise "C:.\\" == "C:"
--   Windows: normalise "\\\\server\\test" == "\\\\server\\test"
--   Windows: normalise "//server/test" == "\\\\server\\test"
--   Windows: normalise "c:/file" == "C:\\file"
--   Windows: normalise "/file" == "\\file"
--   Windows: normalise "\\" == "\\"
--   Windows: normalise "/./" == "\\"
--            normalise "." == "."
--   Posix:   normalise "./" == "./"
--   Posix:   normalise "./." == "./"
--   Posix:   normalise "/./" == "/"
--   Posix:   normalise "/" == "/"
--   Posix:   normalise "bob/fred/." == "bob/fred/"
--   Posix:   normalise "//home" == "/home"
--   </pre>
normalise :: FilePath -> FilePath

-- | Equality of two <tt>FILEPATH</tt>s. If you call
--   <tt>System.Directory.canonicalizePath</tt> first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to <a>normalise</a>, this does not expand <tt>".."</tt>,
--   because of symlinks.
--   
--   <pre>
--            x == y ==&gt; equalFilePath x y
--            normalise x == normalise y ==&gt; equalFilePath x y
--            equalFilePath "foo" "foo/"
--            not (equalFilePath "/a/../c" "/c")
--            not (equalFilePath "foo" "/foo")
--   Posix:   not (equalFilePath "foo" "FOO")
--   Windows: equalFilePath "foo" "FOO"
--   Windows: not (equalFilePath "C:" "C:/")
--   </pre>
equalFilePath :: FilePath -> FilePath -> Bool

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

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

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

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

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

module System.OsPath.Encoding
data EncodingException

-- | Could not decode a byte sequence because it was invalid under the
--   given encoding, or ran out of input in mid-decode.
EncodingError :: String -> Maybe Word8 -> EncodingException
showEncodingException :: EncodingException -> String
ucs2le :: TextEncoding
mkUcs2le :: CodingFailureMode -> TextEncoding
ucs2le_DF :: CodingFailureMode -> IO (TextDecoder ())
ucs2le_EF :: CodingFailureMode -> IO (TextEncoder ())
ucs2le_decode :: DecodeBuffer
ucs2le_encode :: EncodeBuffer

-- | Mimics the base encoding for filesystem operations. This should be
--   total on all inputs (word16 byte arrays).
--   
--   Note that this has a subtle difference to
--   <a>encodeWithBaseWindows</a>/<a>decodeWithBaseWindows</a>: it doesn't
--   care for the <tt>0x0000</tt> end marker and will as such produce
--   different results. Use <tt>takeWhile (/= 'NUL')</tt> on the input to
--   recover this behavior.
utf16le_b :: TextEncoding
mkUTF16le_b :: CodingFailureMode -> TextEncoding
utf16le_b_DF :: CodingFailureMode -> IO (TextDecoder ())
utf16le_b_EF :: CodingFailureMode -> IO (TextEncoder ())
utf16le_b_decode :: DecodeBuffer
utf16le_b_encode :: EncodeBuffer

-- | This mimics the filepath encoder base uses on unix (using PEP-383),
--   with the small distinction that we're not truncating at NUL bytes
--   (because we're not at the outer FFI layer).
encodeWithBasePosix :: String -> IO ShortByteString

-- | This mimics the filepath decoder base uses on unix (using PEP-383),
--   with the small distinction that we're not truncating at NUL bytes
--   (because we're not at the outer FFI layer).
decodeWithBasePosix :: ShortByteString -> IO String

-- | This mimics the filepath dencoder base uses on windows, with the small
--   distinction that we're not truncating at NUL bytes (because we're not
--   at the outer FFI layer).
encodeWithBaseWindows :: String -> IO ShortByteString

-- | This mimics the filepath decoder base uses on windows, with the small
--   distinction that we're not truncating at NUL bytes (because we're not
--   at the outer FFI layer).
decodeWithBaseWindows :: ShortByteString -> IO String


-- | A library for <tt>FilePath</tt> manipulations, using Posix style paths
--   on all platforms. Importing <a>System.FilePath</a> is usually better.
--   
--   Given the example <tt>FilePath</tt>: <tt>/directory/file.ext</tt>
--   
--   We can use the following functions to extract pieces.
--   
--   <ul>
--   <li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
--   <li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
--   <li><a>takeExtension</a> gives <tt>".ext"</tt></li>
--   <li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
--   <li><a>takeBaseName</a> gives <tt>"file"</tt></li>
--   </ul>
--   
--   And we could have built an equivalent path with the following
--   expressions:
--   
--   <ul>
--   <li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
--   <li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
--   <li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
--   </ul>
--   
--   Each function in this module is documented with several examples,
--   which are also used as tests.
--   
--   Here are a few examples of using the <tt>filepath</tt> functions
--   together:
--   
--   <i>Example 1:</i> Find the possible locations of a Haskell module
--   <tt>Test</tt> imported from module <tt>Main</tt>:
--   
--   <pre>
--   [<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
--   </pre>
--   
--   <i>Example 2:</i> Download a file from <tt>url</tt> and save it to
--   disk:
--   
--   <pre>
--   do let file = <a>makeValid</a> url
--      System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
--   </pre>
--   
--   <i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
--   file under <tt>interface</tt>:
--   
--   <pre>
--   <a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
--   </pre>
--   
--   References: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
--   MSDN)
module System.OsPath.Posix.Internal

-- | The character that separates directories. In the case where more than
--   one character is possible, <a>pathSeparator</a> is the 'ideal' one.
--   
--   <pre>
--   Windows: pathSeparator == '\\'
--   Posix:   pathSeparator ==  '/'
--   isPathSeparator pathSeparator
--   </pre>
pathSeparator :: Word8

-- | The list of all possible separators.
--   
--   <pre>
--   Windows: pathSeparators == ['\\', '/']
--   Posix:   pathSeparators == ['/']
--   pathSeparator `elem` pathSeparators
--   </pre>
pathSeparators :: [Word8]

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

-- | The character that is used to separate the entries in the $PATH
--   environment variable.
--   
--   <pre>
--   Windows: searchPathSeparator == ';'
--   Posix:   searchPathSeparator == ':'
--   </pre>
searchPathSeparator :: Word8

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

-- | File extension character
--   
--   <pre>
--   extSeparator == '.'
--   </pre>
extSeparator :: Word8

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

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

-- | Split on the extension. <a>addExtension</a> is the inverse.
--   
--   <pre>
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (&lt;&gt;) (splitExtension x) == x
--   Valid x =&gt; uncurry addExtension (splitExtension x) == x
--   splitExtension "file.txt" == ("file",".txt")
--   splitExtension "file" == ("file","")
--   splitExtension "file/file.txt" == ("file/file",".txt")
--   splitExtension "file.txt/boris" == ("file.txt/boris","")
--   splitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
--   splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
--   splitExtension "file/path.txt/" == ("file/path.txt/","")
--   </pre>
splitExtension :: ShortByteString -> (ShortByteString, ShortByteString)

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

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

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

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

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

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

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

-- | Split on all extensions.
--   
--   <pre>
--   splitExtensions "/directory/path.ext" == ("/directory/path",".ext")
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   uncurry (&lt;&gt;) (splitExtensions x) == x
--   Valid x =&gt; uncurry addExtension (splitExtensions x) == x
--   </pre>
splitExtensions :: ShortByteString -> (ShortByteString, ShortByteString)

-- | Drop all extensions.
--   
--   <pre>
--   dropExtensions "/directory/path.ext" == "/directory/path"
--   dropExtensions "file.tar.gz" == "file"
--   not $ hasExtension $ dropExtensions x
--   not $ any isExtSeparator $ takeFileName $ dropExtensions x
--   </pre>
dropExtensions :: ShortByteString -> ShortByteString

-- | Get all extensions.
--   
--   <pre>
--   takeExtensions "/directory/path.ext" == ".ext"
--   takeExtensions "file.tar.gz" == ".tar.gz"
--   </pre>
takeExtensions :: ShortByteString -> ShortByteString

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

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

-- | Drop the given extension from a ShortByteString, and the <tt>"."</tt>
--   preceding it. Returns <a>Nothing</a> if the ShortByteString does not
--   have the given extension, or <a>Just</a> and the part before the
--   extension if it does.
--   
--   This function can be more predictable than <a>dropExtensions</a>,
--   especially if the filename might itself contain <tt>.</tt> characters.
--   
--   <pre>
--   stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
--   stripExtension "hi.o" "foo.x.hs.o" == Nothing
--   dropExtension x == fromJust (stripExtension (takeExtension x) x)
--   dropExtensions x == fromJust (stripExtension (takeExtensions x) x)
--   stripExtension ".c.d" "a.b.c.d"  == Just "a.b"
--   stripExtension ".c.d" "a.b..c.d" == Just "a.b."
--   stripExtension "baz"  "foo.bar"  == Nothing
--   stripExtension "bar"  "foobar"   == Nothing
--   stripExtension ""     x          == Just x
--   </pre>
stripExtension :: ShortByteString -> ShortByteString -> Maybe ShortByteString

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
--   inverse. The first component will often end with a trailing slash.
--   
--   <pre>
--   splitFileName "/directory/file.ext" == ("/directory/","file.ext")
--   Valid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
--   Valid x =&gt; isValid (fst (splitFileName x))
--   splitFileName "file/bob.txt" == ("file/", "bob.txt")
--   splitFileName "file/" == ("file/", "")
--   splitFileName "bob" == ("./", "bob")
--   Posix:   splitFileName "/" == ("/","")
--   Windows: splitFileName "c:" == ("c:","")
--   Windows: splitFileName "\\\\?\\A:\\fred" == ("\\\\?\\A:\\","fred")
--   Windows: splitFileName "\\\\?\\A:" == ("\\\\?\\A:","")
--   </pre>
splitFileName :: ShortByteString -> (ShortByteString, ShortByteString)

-- | Get the file name.
--   
--   <pre>
--   takeFileName "/directory/file.ext" == "file.ext"
--   takeFileName "test/" == ""
--   isSuffixOf (takeFileName x) x
--   takeFileName x == snd (splitFileName x)
--   Valid x =&gt; takeFileName (replaceFileName x "fred") == "fred"
--   Valid x =&gt; takeFileName (x &lt;/&gt; "fred") == "fred"
--   Valid x =&gt; isRelative (takeFileName x)
--   </pre>
takeFileName :: ShortByteString -> ShortByteString

-- | Set the filename.
--   
--   <pre>
--   replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
--   Valid x =&gt; replaceFileName x (takeFileName x) == x
--   </pre>
replaceFileName :: ShortByteString -> ShortByteString -> ShortByteString

-- | Drop the filename. Unlike <a>takeDirectory</a>, this function will
--   leave a trailing path separator on the directory.
--   
--   <pre>
--   dropFileName "/directory/file.ext" == "/directory/"
--   dropFileName x == fst (splitFileName x)
--   isPrefixOf (takeDrive x) (dropFileName x)
--   </pre>
dropFileName :: ShortByteString -> ShortByteString

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

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

-- | Get the directory name, move up one level.
--   
--   <pre>
--             takeDirectory "/directory/other.ext" == "/directory"
--             isPrefixOf (takeDirectory x) x || takeDirectory x == "."
--             takeDirectory "foo" == "."
--             takeDirectory "/" == "/"
--             takeDirectory "/foo" == "/"
--             takeDirectory "/foo/bar/baz" == "/foo/bar"
--             takeDirectory "/foo/bar/baz/" == "/foo/bar/baz"
--             takeDirectory "foo/bar/baz" == "foo/bar"
--   Windows:  takeDirectory "foo\\bar" == "foo"
--   Windows:  takeDirectory "foo\\bar\\\\" == "foo\\bar"
--   Windows:  takeDirectory "C:\\" == "C:\\"
--   </pre>
takeDirectory :: ShortByteString -> ShortByteString

-- | Set the directory, keeping the filename the same.
--   
--   <pre>
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
--   </pre>
replaceDirectory :: ShortByteString -> ShortByteString -> ShortByteString

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

-- | Combine two paths with a path separator. If the second path starts
--   with a path separator or a drive letter, then it returns the second.
--   The intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
--   will access the same file as <tt>setCurrentDirectory dir; readFile
--   file</tt>.
--   
--   <pre>
--   Posix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
--   Windows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
--            "directory" &lt;/&gt; "/file.ext" == "/file.ext"
--   Valid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
--   </pre>
--   
--   Combined:
--   
--   <pre>
--   Posix:   "/" &lt;/&gt; "test" == "/test"
--   Posix:   "home" &lt;/&gt; "bob" == "home/bob"
--   Posix:   "x:" &lt;/&gt; "foo" == "x:/foo"
--   Windows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
--   Windows: "home" &lt;/&gt; "bob" == "home\\bob"
--   </pre>
--   
--   Not combined:
--   
--   <pre>
--   Posix:   "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
--   </pre>
--   
--   Not combined (tricky):
--   
--   On Windows, if a filepath starts with a single slash, it is relative
--   to the root of the current drive. In [1], this is (confusingly)
--   referred to as an absolute path. The current behavior of
--   <a>&lt;/&gt;</a> is to never combine these forms.
--   
--   <pre>
--   Windows: "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "\\bob" == "\\bob"
--   Windows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
--   </pre>
--   
--   On Windows, from [1]: "If a file name begins with only a disk
--   designator but not the backslash after the colon, it is interpreted as
--   a relative path to the current directory on the drive with the
--   specified letter." The current behavior of <a>&lt;/&gt;</a> is to
--   never combine these forms.
--   
--   <pre>
--   Windows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   Windows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   </pre>
(</>) :: ShortByteString -> ShortByteString -> ShortByteString
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   <pre>
--   splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
--   concat (splitPath x) == x
--   splitPath "test//item/" == ["test//","item/"]
--   splitPath "test/item/file" == ["test/","item/","file"]
--   splitPath "" == []
--   Windows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
--   Posix:   splitPath "/file/test" == ["/","file/","test"]
--   </pre>
splitPath :: ShortByteString -> [ShortByteString]

-- | Join path elements back together.
--   
--   <pre>
--   joinPath z == foldr (&lt;/&gt;) "" z
--   joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
--   Valid x =&gt; joinPath (splitPath x) == x
--   joinPath [] == ""
--   Posix: joinPath ["test","file","path"] == "test/file/path"
--   </pre>
joinPath :: [ShortByteString] -> ShortByteString

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

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   <pre>
--   uncurry (&lt;&gt;) (splitDrive x) == x
--   Windows: splitDrive "file" == ("","file")
--   Windows: splitDrive "c:/file" == ("c:/","file")
--   Windows: splitDrive "c:\\file" == ("c:\\","file")
--   Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
--   Windows: splitDrive "\\\\shared" == ("\\\\shared","")
--   Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
--   Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
--   Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
--   Windows: splitDrive "/d" == ("","/d")
--   Posix:   splitDrive "/test" == ("/","test")
--   Posix:   splitDrive "//test" == ("//","test")
--   Posix:   splitDrive "test/file" == ("","test/file")
--   Posix:   splitDrive "file" == ("","file")
--   </pre>
splitDrive :: ShortByteString -> (ShortByteString, ShortByteString)

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

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

-- | Does a path have a drive.
--   
--   <pre>
--   not (hasDrive x) == null (takeDrive x)
--   Posix:   hasDrive "/foo" == True
--   Windows: hasDrive "C:\\foo" == True
--   Windows: hasDrive "C:foo" == True
--            hasDrive "foo" == False
--            hasDrive "" == False
--   </pre>
hasDrive :: ShortByteString -> Bool

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

-- | Is an element a drive
--   
--   <pre>
--   Posix:   isDrive "/" == True
--   Posix:   isDrive "/foo" == False
--   Windows: isDrive "C:\\" == True
--   Windows: isDrive "C:\\foo" == False
--            isDrive "" == False
--   </pre>
isDrive :: ShortByteString -> Bool

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

-- | Add a trailing file path separator if one is not already present.
--   
--   <pre>
--   hasTrailingPathSeparator (addTrailingPathSeparator x)
--   hasTrailingPathSeparator x ==&gt; addTrailingPathSeparator x == x
--   Posix:    addTrailingPathSeparator "test/rest" == "test/rest/"
--   </pre>
addTrailingPathSeparator :: ShortByteString -> ShortByteString

-- | Remove any trailing path separators
--   
--   <pre>
--   dropTrailingPathSeparator "file/test/" == "file/test"
--             dropTrailingPathSeparator "/" == "/"
--   Windows:  dropTrailingPathSeparator "\\" == "\\"
--   Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
--   </pre>
dropTrailingPathSeparator :: ShortByteString -> ShortByteString

-- | Normalise a file
--   
--   <ul>
--   <li>// outside of the drive can be made blank</li>
--   <li>/ -&gt; <a>pathSeparator</a></li>
--   <li>./ -&gt; ""</li>
--   </ul>
--   
--   Does not remove <tt>".."</tt>, because of symlinks.
--   
--   <pre>
--   Posix:   normalise "/file/\\test////" == "/file/\\test/"
--   Posix:   normalise "/file/./test" == "/file/test"
--   Posix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
--   Posix:   normalise "../bob/fred/" == "../bob/fred/"
--   Posix:   normalise "/a/../c" == "/a/../c"
--   Posix:   normalise "./bob/fred/" == "bob/fred/"
--   Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
--   Windows: normalise "c:\\" == "C:\\"
--   Windows: normalise "c:\\\\\\\\" == "C:\\"
--   Windows: normalise "C:.\\" == "C:"
--   Windows: normalise "\\\\server\\test" == "\\\\server\\test"
--   Windows: normalise "//server/test" == "\\\\server\\test"
--   Windows: normalise "c:/file" == "C:\\file"
--   Windows: normalise "/file" == "\\file"
--   Windows: normalise "\\" == "\\"
--   Windows: normalise "/./" == "\\"
--            normalise "." == "."
--   Posix:   normalise "./" == "./"
--   Posix:   normalise "./." == "./"
--   Posix:   normalise "/./" == "/"
--   Posix:   normalise "/" == "/"
--   Posix:   normalise "bob/fred/." == "bob/fred/"
--   Posix:   normalise "//home" == "/home"
--   </pre>
normalise :: ShortByteString -> ShortByteString

-- | Equality of two <tt>FILEPATH</tt>s. If you call
--   <tt>System.Directory.canonicalizePath</tt> first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to <a>normalise</a>, this does not expand <tt>".."</tt>,
--   because of symlinks.
--   
--   <pre>
--            x == y ==&gt; equalFilePath x y
--            normalise x == normalise y ==&gt; equalFilePath x y
--            equalFilePath "foo" "foo/"
--            not (equalFilePath "/a/../c" "/c")
--            not (equalFilePath "foo" "/foo")
--   Posix:   not (equalFilePath "foo" "FOO")
--   Windows: equalFilePath "foo" "FOO"
--   Windows: not (equalFilePath "C:" "C:/")
--   </pre>
equalFilePath :: ShortByteString -> ShortByteString -> Bool

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

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

-- | <pre>
--   not . <a>isRelative</a>
--   </pre>
--   
--   <pre>
--   isAbsolute x == not (isRelative x)
--   </pre>
isAbsolute :: ShortByteString -> Bool

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

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

module System.OsPath.Types

-- | Type representing filenames/pathnames.
--   
--   This type doesn't add any guarantees over <a>OsString</a>.
type OsPath = OsString

-- | Filepaths are <tt>wchar_t*</tt> data on windows as passed to syscalls.
type WindowsPath = WindowsString

-- | Filepaths are <tt>char[]</tt> data on unix as passed to syscalls.
type PosixPath = PosixString

-- | Ifdef around current platform (either <a>WindowsPath</a> or
--   <a>PosixPath</a>).
type PlatformPath = PosixPath

-- | Commonly used windows string as wide character bytes.
data WindowsString

-- | Commonly used Posix string as uninterpreted <tt>char[]</tt> array.
data PosixString
data WindowsChar
data PosixChar

-- | Newtype representing short operating system specific strings.
--   
--   Internally this is either <a>WindowsString</a> or <a>PosixString</a>,
--   depending on the platform. Both use unpinned <tt>ShortByteString</tt>
--   for efficiency.
--   
--   The constructor is only exported via
--   <a>System.OsString.Internal.Types</a>, since dealing with the
--   internals isn't generally recommended, but supported in case you need
--   to write platform specific code.
data OsString

-- | Newtype representing a code unit.
--   
--   On Windows, this is restricted to two-octet codepoints <a>Word16</a>,
--   on POSIX one-octet (<a>Word8</a>).
data OsChar

module System.OsPath.Posix

-- | Commonly used Posix string as uninterpreted <tt>char[]</tt> array.
data PosixString
data PosixChar

-- | Filepaths are <tt>char[]</tt> data on unix as passed to syscalls.
type PosixPath = PosixString

-- | Partial unicode friendly encoding.
--   
--   This encodes as UTF8 (strictly), which is a good guess.
--   
--   Throws an <a>EncodingException</a> if encoding fails. If the input
--   does not contain surrogate chars, you can use <a>unsafeEncodeUtf</a>.
encodeUtf :: MonadThrow m => String -> m PosixString

-- | Unsafe unicode friendly encoding.
--   
--   Like <a>encodeUtf</a>, except it crashes when the input contains
--   surrogate chars. For sanitized input, this can be useful.
unsafeEncodeUtf :: HasCallStack => String -> PosixString

-- | Encode a <a>String</a> with the specified encoding.
encodeWith :: TextEncoding -> String -> Either EncodingException PosixString
encodeFS :: String -> IO PosixPath

-- | QuasiQuote a <a>PosixPath</a>. This accepts Unicode characters and
--   encodes as UTF-8. Runs <a>isValid</a> on the input.
pstr :: QuasiQuoter

-- | Pack a list of platform words to a platform string.
--   
--   Note that using this in conjunction with <a>unsafeFromChar</a> to
--   convert from <tt>[Char]</tt> to platform string is probably not what
--   you want, because it will truncate unicode code points.
pack :: [PosixChar] -> PosixString

-- | Partial unicode friendly decoding.
--   
--   This decodes as UTF8 (strictly), which is a good guess. Note that
--   filenames on unix are encoding agnostic char arrays.
--   
--   Throws a <a>EncodingException</a> if decoding fails.
decodeUtf :: MonadThrow m => PosixString -> m String

-- | Decode a <a>PosixString</a> with the specified encoding.
--   
--   The String is forced into memory to catch all exceptions.
decodeWith :: TextEncoding -> PosixString -> Either EncodingException String
decodeFS :: PosixPath -> IO String

-- | Unpack a platform string to a list of platform words.
unpack :: PosixString -> [PosixChar]

-- | Truncates to 1 octet.
unsafeFromChar :: Char -> PosixChar

-- | Converts back to a unicode codepoint (total).
toChar :: PosixChar -> Char

-- | The character that separates directories. In the case where more than
--   one character is possible, <a>pathSeparator</a> is the 'ideal' one.
--   
--   <pre>
--   pathSeparator ==  '/'
--   </pre>
pathSeparator :: PosixChar

-- | The list of all possible separators.
--   
--   <pre>
--   pathSeparators == ['/']
--   pathSeparator `elem` pathSeparators
--   </pre>
pathSeparators :: [PosixChar]

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

-- | The character that is used to separate the entries in the $PATH
--   environment variable.
--   
--   <pre>
--   searchPathSeparator == ':'
--   </pre>
searchPathSeparator :: PosixChar

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

-- | File extension character
--   
--   <pre>
--   extSeparator == '.'
--   </pre>
extSeparator :: PosixChar

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

-- | Take a string, split it on the <a>searchPathSeparator</a> character.
--   
--   Blank items are converted to <tt>.</tt> on , and quotes are not
--   treated specially.
--   
--   Follows the recommendations in
--   <a>http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html</a>
--   
--   <pre>
--   splitSearchPath "File1:File2:File3"  == ["File1","File2","File3"]
--   splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"]
--   </pre>
splitSearchPath :: PosixString -> [PosixPath]

-- | Split on the extension. <a>addExtension</a> is the inverse.
--   
--   <pre>
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (&lt;&gt;) (splitExtension x) == x
--   Valid x =&gt; uncurry addExtension (splitExtension x) == x
--   splitExtension "file.txt" == ("file",".txt")
--   splitExtension "file" == ("file","")
--   splitExtension "file/file.txt" == ("file/file",".txt")
--   splitExtension "file.txt/boris" == ("file.txt/boris","")
--   splitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
--   splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
--   splitExtension "file/path.txt/" == ("file/path.txt/","")
--   </pre>
splitExtension :: PosixPath -> (PosixPath, PosixString)

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

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

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <a>&lt;.&gt;</a>.
--   
--   <pre>
--   addExtension "/directory/path" "ext" == "/directory/path.ext"
--   addExtension "file.txt" "bib" == "file.txt.bib"
--   addExtension "file." ".bib" == "file..bib"
--   addExtension "file" ".bib" == "file.bib"
--   addExtension "/" "x" == "/.x"
--   addExtension x "" == x
--   Valid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   </pre>
--   
--   Add an extension, even if there is already one there, equivalent to
--   <a>&lt;.&gt;</a>.
--   
--   <pre>
--   addExtension "/directory/path" "ext" == "/directory/path.ext"
--   addExtension "file.txt" "bib" == "file.txt.bib"
--   addExtension "file." ".bib" == "file..bib"
--   addExtension "file" ".bib" == "file.bib"
--   addExtension "/" "x" == "/.x"
--   addExtension x "" == x
--   Valid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   </pre>
addExtension :: PosixPath -> PosixString -> PosixPath

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

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

-- | Split on all extensions.
--   
--   <pre>
--   splitExtensions "/directory/path.ext" == ("/directory/path",".ext")
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   uncurry (&lt;&gt;) (splitExtensions x) == x
--   Valid x =&gt; uncurry addExtension (splitExtensions x) == x
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   </pre>
splitExtensions :: PosixPath -> (PosixPath, PosixString)

-- | Drop all extensions.
--   
--   <pre>
--   dropExtensions "/directory/path.ext" == "/directory/path"
--   dropExtensions "file.tar.gz" == "file"
--   not $ hasExtension $ dropExtensions x
--   not $ any isExtSeparator $ takeFileName $ dropExtensions x
--   </pre>
dropExtensions :: PosixPath -> PosixPath

-- | Get all extensions.
--   
--   <pre>
--   takeExtensions "/directory/path.ext" == ".ext"
--   takeExtensions "file.tar.gz" == ".tar.gz"
--   </pre>
takeExtensions :: PosixPath -> PosixString

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

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

-- | Drop the given extension from a filepath, and the <tt>"."</tt>
--   preceding it. Returns <a>Nothing</a> if the filepath does not have the
--   given extension, or <a>Just</a> and the part before the extension if
--   it does.
--   
--   This function can be more predictable than <a>dropExtensions</a>,
--   especially if the filename might itself contain <tt>.</tt> characters.
--   
--   <pre>
--   stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
--   stripExtension "hi.o" "foo.x.hs.o" == Nothing
--   dropExtension x == fromJust (stripExtension (takeExtension x) x)
--   dropExtensions x == fromJust (stripExtension (takeExtensions x) x)
--   stripExtension ".c.d" "a.b.c.d"  == Just "a.b"
--   stripExtension ".c.d" "a.b..c.d" == Just "a.b."
--   stripExtension "baz"  "foo.bar"  == Nothing
--   stripExtension "bar"  "foobar"   == Nothing
--   stripExtension ""     x          == Just x
--   </pre>
stripExtension :: PosixString -> PosixPath -> Maybe PosixPath

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
--   inverse. The first component will often end with a trailing slash.
--   
--   <pre>
--   splitFileName "/directory/file.ext" == ("/directory/","file.ext")
--   Valid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
--   Valid x =&gt; isValid (fst (splitFileName x))
--   splitFileName "file/bob.txt" == ("file/", "bob.txt")
--   splitFileName "file/" == ("file/", "")
--   splitFileName "bob" == ("./", "bob")
--   splitFileName "/" == ("/","")
--   </pre>
splitFileName :: PosixPath -> (PosixPath, PosixPath)

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

-- | Set the filename.
--   
--   <pre>
--   replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
--   Valid x =&gt; replaceFileName x (takeFileName x) == x
--   </pre>
replaceFileName :: PosixPath -> PosixString -> PosixPath

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

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

-- | Set the base name.
--   
--   <pre>
--   replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext"
--   replaceBaseName "file/test.txt" "bob" == "file/bob.txt"
--   replaceBaseName "fred" "bill" == "bill"
--   replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar"
--   Valid x =&gt; replaceBaseName x (takeBaseName x) == x
--   </pre>
replaceBaseName :: PosixPath -> PosixString -> PosixPath

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

-- | Set the directory, keeping the filename the same.
--   
--   <pre>
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
--   </pre>
replaceDirectory :: PosixPath -> PosixPath -> PosixPath

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

-- | Combine two paths with a path separator. If the second path starts
--   with a path separator or a drive letter, then it returns the second.
--   The intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
--   will access the same file as <tt>setCurrentDirectory dir; readFile
--   file</tt>.
--   
--   <pre>
--   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
--   Valid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
--   </pre>
--   
--   Combined:
--   
--   <pre>
--   "/" &lt;/&gt; "test" == "/test"
--   "home" &lt;/&gt; "bob" == "home/bob"
--   "x:" &lt;/&gt; "foo" == "x:/foo"
--   </pre>
--   
--   Not combined:
--   
--   <pre>
--   "home" &lt;/&gt; "/bob" == "/bob"
--   </pre>
(</>) :: PosixPath -> PosixPath -> PosixPath

-- | Split a path by the directory separator.
--   
--   <pre>
--   splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
--   concat (splitPath x) == x
--   splitPath "test//item/" == ["test//","item/"]
--   splitPath "test/item/file" == ["test/","item/","file"]
--   splitPath "" == []
--   splitPath "/file/test" == ["/","file/","test"]
--   </pre>
splitPath :: PosixPath -> [PosixPath]

-- | Join path elements back together.
--   
--   <pre>
--   joinPath z == foldr (&lt;/&gt;) "" z
--   joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
--   Valid x =&gt; joinPath (splitPath x) == x
--   joinPath [] == ""
--   joinPath ["test","file","path"] == "test/file/path"
--   </pre>
joinPath :: [PosixPath] -> PosixPath

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

-- | Split a path into a drive and a path. / is a Drive.
--   
--   <pre>
--   uncurry (&lt;&gt;) (splitDrive x) == x
--   splitDrive "/test" == ("/","test")
--   splitDrive "//test" == ("//","test")
--   splitDrive "test/file" == ("","test/file")
--   splitDrive "file" == ("","file")
--   </pre>
splitDrive :: PosixPath -> (PosixPath, PosixPath)

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

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

-- | Does a path have a drive.
--   
--   <pre>
--   not (hasDrive x) == null (takeDrive x)
--   hasDrive "/foo" == True
--   hasDrive "foo" == False
--   hasDrive "" == False
--   </pre>
hasDrive :: PosixPath -> Bool

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

-- | Is an element a drive
--   
--   <pre>
--   isDrive "/" == True
--   isDrive "/foo" == False
--   isDrive "" == False
--   </pre>
isDrive :: PosixPath -> Bool

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

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

-- | Remove any trailing path separators
--   
--   <pre>
--   dropTrailingPathSeparator "file/test/" == "file/test"
--   dropTrailingPathSeparator "/" == "/"
--   not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
--   </pre>
dropTrailingPathSeparator :: PosixPath -> PosixPath

-- | Normalise a file
--   
--   <ul>
--   <li>// outside of the drive can be made blank</li>
--   <li>/ -&gt; <a>pathSeparator</a></li>
--   <li>./ -&gt; ""</li>
--   </ul>
--   
--   Does not remove <tt>".."</tt>, because of symlinks.
--   
--   <pre>
--   normalise "/file/\\test////" == "/file/\\test/"
--   normalise "/file/./test" == "/file/test"
--   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
--   normalise "../bob/fred/" == "../bob/fred/"
--   normalise "/a/../c" == "/a/../c"
--   normalise "./bob/fred/" == "bob/fred/"
--   normalise "." == "."
--   normalise "./" == "./"
--   normalise "./." == "./"
--   normalise "/./" == "/"
--   normalise "/" == "/"
--   normalise "bob/fred/." == "bob/fred/"
--   normalise "//home" == "/home"
--   </pre>
normalise :: PosixPath -> PosixPath

-- | Equality of two filepaths. If you call
--   <tt>System.Directory.canonicalizePath</tt> first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to <a>normalise</a>, this does not expand <tt>".."</tt>,
--   because of symlinks.
--   
--   <pre>
--   x == y ==&gt; equalFilePath x y
--   normalise x == normalise y ==&gt; equalFilePath x y
--   equalFilePath "foo" "foo/"
--   not (equalFilePath "/a/../c" "/c")
--   not (equalFilePath "foo" "/foo")
--   not (equalFilePath "foo" "FOO")
--   </pre>
equalFilePath :: PosixPath -> PosixPath -> Bool

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

-- | Is a path relative, or is it fixed to the root?
--   
--   <pre>
--   isRelative "test/path" == True
--   isRelative "/test" == False
--   isRelative "/" == False
--   </pre>
isRelative :: PosixPath -> Bool

-- | <pre>
--   not . <a>isRelative</a>
--   </pre>
--   
--   <pre>
--   isAbsolute x == not (isRelative x)
--   </pre>
isAbsolute :: PosixPath -> Bool

-- | Is a filepath valid, i.e. could you create a file like it? This
--   function checks for invalid names, and invalid characters, but does
--   not check if length limits are exceeded, as these are typically
--   filesystem dependent.
--   
--   <pre>
--   isValid "" == False
--   isValid "\0" == False
--   isValid "/random_ path:*" == True
--   isValid x == not (null x)
--   </pre>
isValid :: PosixPath -> Bool

-- | Take a filepath and make it valid; does not change already valid
--   filepaths.
--   
--   <pre>
--   isValid (makeValid x)
--   isValid x ==&gt; makeValid x == x
--   makeValid "" == "_"
--   makeValid "file\0name" == "file_name"
--   </pre>
makeValid :: PosixPath -> PosixPath

module System.OsPath.Internal

-- | Partial unicode friendly encoding.
--   
--   On windows this encodes as UTF16-LE (strictly), which is a pretty good
--   guess. On unix this encodes as UTF8 (strictly), which is a good guess.
--   
--   Throws an <a>EncodingException</a> if encoding fails. If the input
--   does not contain surrogate chars, you can use <a>unsafeEncodeUtf</a>.
encodeUtf :: MonadThrow m => FilePath -> m OsPath

-- | Unsafe unicode friendly encoding.
--   
--   Like <a>encodeUtf</a>, except it crashes when the input contains
--   surrogate chars. For sanitized input, this can be useful.
unsafeEncodeUtf :: HasCallStack => String -> OsString

-- | Encode a <a>FilePath</a> with the specified encoding.
--   
--   Note: on windows, we expect a "wide char" encoding (e.g. UCS-2 or
--   UTF-16). Anything that works with <tt>Word16</tt> boundaries. Picking
--   an incompatible encoding may crash filepath operations.
encodeWith :: TextEncoding -> TextEncoding -> FilePath -> Either EncodingException OsPath

-- | Like <a>encodeUtf</a>, except this mimics the behavior of the base
--   library when doing filesystem operations, which is:
--   
--   <ol>
--   <li>on unix, uses shady PEP 383 style encoding (based on the current
--   locale, but PEP 383 only works properly on UTF-8 encodings, so good
--   luck)</li>
--   <li>on windows does permissive UTF-16 encoding, where coding errors
--   generate Chars in the surrogate range</li>
--   </ol>
--   
--   Looking up the locale requires IO. If you're not worried about calls
--   to <tt>setFileSystemEncoding</tt>, then <tt>unsafePerformIO</tt> may
--   be feasible (make sure to deeply evaluate the result to catch
--   exceptions).
encodeFS :: FilePath -> IO OsPath

-- | Partial unicode friendly decoding.
--   
--   On windows this decodes as UTF16-LE (strictly), which is a pretty good
--   guess. On unix this decodes as UTF8 (strictly), which is a good guess.
--   
--   Throws a <a>EncodingException</a> if decoding fails.
decodeUtf :: MonadThrow m => OsPath -> m FilePath

-- | Decode an <a>OsPath</a> with the specified encoding.
decodeWith :: TextEncoding -> TextEncoding -> OsPath -> Either EncodingException FilePath

-- | Like <a>decodeUtf</a>, except this mimics the behavior of the base
--   library when doing filesystem operations, which is:
--   
--   <ol>
--   <li>on unix, uses shady PEP 383 style encoding (based on the current
--   locale, but PEP 383 only works properly on UTF-8 encodings, so good
--   luck)</li>
--   <li>on windows does permissive UTF-16 encoding, where coding errors
--   generate Chars in the surrogate range</li>
--   </ol>
--   
--   Looking up the locale requires IO. If you're not worried about calls
--   to <tt>setFileSystemEncoding</tt>, then <tt>unsafePerformIO</tt> may
--   be feasible (make sure to deeply evaluate the result to catch
--   exceptions).
decodeFS :: OsPath -> IO FilePath

-- | Constructs an <tt>OsPath</tt> from a ByteString.
--   
--   On windows, this ensures valid UCS-2LE, on unix it is passed
--   unchanged/unchecked.
--   
--   Throws <a>EncodingException</a> on invalid UCS-2LE on windows
--   (although unlikely).
fromBytes :: MonadThrow m => ByteString -> m OsPath

-- | QuasiQuote an <a>OsPath</a>. This accepts Unicode characters and
--   encodes as UTF-8 on unix and UTF-16LE on windows. Runs <a>isValid</a>
--   on the input. If used as a pattern, requires turning on the
--   <tt>ViewPatterns</tt> extension.
osp :: QuasiQuoter

-- | Unpack an <a>OsPath</a> to a list of <a>OsChar</a>.
unpack :: OsPath -> [OsChar]

-- | Pack a list of <a>OsChar</a> to an <a>OsPath</a>.
--   
--   Note that using this in conjunction with <tt>unsafeFromChar</tt> to
--   convert from <tt>[Char]</tt> to <a>OsPath</a> is probably not what you
--   want, because it will truncate unicode code points.
pack :: [OsChar] -> OsPath


-- | An implementation of the <a>Abstract FilePath Proposal</a>, which aims
--   to supersede <tt>type FilePath = String</tt> for various reasons:
--   
--   <ol>
--   <li>it is more efficient and avoids memory fragmentation (uses
--   unpinned <tt>ShortByteString</tt> under the hood)</li>
--   <li>it is more type-safe (newtype over <tt>ShortByteString</tt>)</li>
--   <li>avoids round-tripping issues by not converting to String (which is
--   not total and loses the encoding)</li>
--   <li>abstracts over unix and windows while keeping the original
--   bytes</li>
--   </ol>
--   
--   It is important to know that filenames/filepaths have different
--   representations across platforms:
--   
--   <ul>
--   <li>On <i>Windows</i>, filepaths are expected to be encoded as
--   UTF16-LE <a>as per the documentation</a>, but may also include invalid
--   surrogate pairs, in which case UCS-2 can be used. They are passed as
--   <tt>wchar_t*</tt> to syscalls. <a>OsPath</a> only maintains the wide
--   character invariant.</li>
--   <li>On <i>Unix</i>, filepaths don't have a predefined encoding
--   (although they are often interpreted as UTF8) as per the <a>POSIX
--   specification</a> and are passed as <tt>char[]</tt> to syscalls.
--   <a>OsPath</a> maintains no invariant here.</li>
--   </ul>
--   
--   Apart from encoding, filepaths have additional restrictions per
--   platform:
--   
--   <ul>
--   <li>On <i>Windows</i> the <a>naming convention</a> may apply</li>
--   <li>On <i>Unix</i>, only <tt>NUL</tt> bytes are disallowed as per the
--   <a>POSIX specification</a></li>
--   </ul>
--   
--   Use <a>isValid</a> to check for these restrictions (<a>OsPath</a>
--   doesn't maintain this invariant).
--   
--   Also note that these restrictions are not exhaustive and further
--   filesystem specific restrictions may apply on all platforms. This
--   library makes no attempt at satisfying these. Library users may need
--   to account for that, depending on what filesystems they want to
--   support.
--   
--   It is advised to follow these principles when dealing with
--   filepaths/filenames:
--   
--   <ol>
--   <li>Avoid interpreting filenames that the OS returns, unless
--   absolutely necessary. For example, the filepath separator is usually a
--   predefined <tt>Word8</tt>/<tt>Word16</tt>, regardless of encoding. So
--   even if we need to split filepaths, it might still not be necessary to
--   understand the encoding of the filename.</li>
--   <li>When interpreting OS returned filenames consider that these might
--   not be UTF8 on <i>unix</i> or at worst don't have an ASCII compatible
--   encoding. The are 3 available strategies fer decoding/encoding: a)
--   pick the best UTF (UTF-8 on unix, UTF-16LE on windows), b) decode with
--   an explicitly defined <tt>TextEncoding</tt>, c) mimic the behavior of
--   the <tt>base</tt> library (permissive UTF16 on windows, current
--   filesystem encoding on unix).</li>
--   <li>Avoid comparing <tt>String</tt> based filepaths, because filenames
--   of different encodings may have the same <tt>String</tt>
--   representation, although they're not the same byte-wise.</li>
--   </ol>
module System.OsPath

-- | Type representing filenames/pathnames.
--   
--   This type doesn't add any guarantees over <a>OsString</a>.
type OsPath = OsString

-- | Newtype representing short operating system specific strings.
--   
--   Internally this is either <a>WindowsString</a> or <a>PosixString</a>,
--   depending on the platform. Both use unpinned <tt>ShortByteString</tt>
--   for efficiency.
--   
--   The constructor is only exported via
--   <a>System.OsString.Internal.Types</a>, since dealing with the
--   internals isn't generally recommended, but supported in case you need
--   to write platform specific code.
data OsString

-- | Newtype representing a code unit.
--   
--   On Windows, this is restricted to two-octet codepoints <a>Word16</a>,
--   on POSIX one-octet (<a>Word8</a>).
data OsChar

-- | Partial unicode friendly encoding.
--   
--   On windows this encodes as UTF16-LE (strictly), which is a pretty good
--   guess. On unix this encodes as UTF8 (strictly), which is a good guess.
--   
--   Throws an <a>EncodingException</a> if encoding fails. If the input
--   does not contain surrogate chars, you can use <a>unsafeEncodeUtf</a>.
encodeUtf :: MonadThrow m => FilePath -> m OsPath

-- | Unsafe unicode friendly encoding.
--   
--   Like <a>encodeUtf</a>, except it crashes when the input contains
--   surrogate chars. For sanitized input, this can be useful.
unsafeEncodeUtf :: HasCallStack => String -> OsString

-- | Encode a <a>FilePath</a> with the specified encoding.
--   
--   Note: on windows, we expect a "wide char" encoding (e.g. UCS-2 or
--   UTF-16). Anything that works with <tt>Word16</tt> boundaries. Picking
--   an incompatible encoding may crash filepath operations.
encodeWith :: TextEncoding -> TextEncoding -> FilePath -> Either EncodingException OsPath
encodeFS :: String -> IO OsPath

-- | QuasiQuote an <a>OsPath</a>. This accepts Unicode characters and
--   encodes as UTF-8 on unix and UTF-16LE on windows. Runs <a>isValid</a>
--   on the input. If used as a pattern, requires turning on the
--   <tt>ViewPatterns</tt> extension.
osp :: QuasiQuoter

-- | Pack a list of <a>OsChar</a> to an <a>OsPath</a>.
--   
--   Note that using this in conjunction with <tt>unsafeFromChar</tt> to
--   convert from <tt>[Char]</tt> to <a>OsPath</a> is probably not what you
--   want, because it will truncate unicode code points.
pack :: [OsChar] -> OsPath

-- | Partial unicode friendly decoding.
--   
--   On windows this decodes as UTF16-LE (strictly), which is a pretty good
--   guess. On unix this decodes as UTF8 (strictly), which is a good guess.
--   
--   Throws a <a>EncodingException</a> if decoding fails.
decodeUtf :: MonadThrow m => OsPath -> m FilePath

-- | Decode an <a>OsPath</a> with the specified encoding.
decodeWith :: TextEncoding -> TextEncoding -> OsPath -> Either EncodingException FilePath
decodeFS :: OsPath -> IO String

-- | Unpack an <a>OsPath</a> to a list of <a>OsChar</a>.
unpack :: OsPath -> [OsChar]

-- | Truncates on unix to 1 and on Windows to 2 octets.
unsafeFromChar :: Char -> OsChar

-- | Converts back to a unicode codepoint (total).
toChar :: OsChar -> Char

-- | The character that separates directories. In the case where more than
--   one character is possible, <a>pathSeparator</a> is the 'ideal' one.
--   
--   <pre>
--   Windows: pathSeparator == '\\'S
--   Posix:   pathSeparator ==  '/'
--   </pre>
pathSeparator :: OsChar

-- | The list of all possible separators.
--   
--   <pre>
--   Windows: pathSeparators == ['\\', '/']
--   Posix:   pathSeparators == ['/']
--   pathSeparator `elem` pathSeparators
--   </pre>
pathSeparators :: [OsChar]

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

-- | The character that is used to separate the entries in the $PATH
--   environment variable.
--   
--   <pre>
--   Posix:   searchPathSeparator == ':'
--   Windows: searchPathSeparator == ';'
--   </pre>
searchPathSeparator :: OsChar

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

-- | File extension character
--   
--   <pre>
--   extSeparator == '.'
--   </pre>
extSeparator :: OsChar

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

-- | Take a string, split it on the <a>searchPathSeparator</a> character.
--   
--   On Windows, blank items are ignored on Windows, and path elements are
--   stripped of quotes.
--   
--   On Posix, blank items are converted to <tt>.</tt> on Posix, and quotes
--   are not treated specially.
--   
--   Follows the recommendations in
--   <a>http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html</a>
--   
--   <pre>
--   Windows: splitSearchPath "File1;File2;File3"  == ["File1","File2","File3"]
--   Windows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"]
--   Windows: splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"]
--   Posix:   splitSearchPath "File1:File2:File3"  == ["File1","File2","File3"]
--   Posix:   splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"]
--   </pre>
splitSearchPath :: OsString -> [OsPath]

-- | Split on the extension. <a>addExtension</a> is the inverse.
--   
--   <pre>
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (&lt;&gt;) (splitExtension x) == x
--   Valid x =&gt; uncurry addExtension (splitExtension x) == x
--   splitExtension "file.txt" == ("file",".txt")
--   splitExtension "file" == ("file","")
--   splitExtension "file/file.txt" == ("file/file",".txt")
--   splitExtension "file.txt/boris" == ("file.txt/boris","")
--   splitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
--   splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
--   splitExtension "file/path.txt/" == ("file/path.txt/","")
--   </pre>
splitExtension :: OsPath -> (OsPath, OsString)

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

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

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <a>&lt;.&gt;</a>.
--   
--   <pre>
--   addExtension "/directory/path" "ext" == "/directory/path.ext"
--   addExtension "file.txt" "bib" == "file.txt.bib"
--   addExtension "file." ".bib" == "file..bib"
--   addExtension "file" ".bib" == "file.bib"
--   addExtension "/" "x" == "/.x"
--   addExtension x "" == x
--   Valid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   </pre>
--   
--   Add an extension, even if there is already one there, equivalent to
--   <a>&lt;.&gt;</a>.
--   
--   <pre>
--   addExtension "/directory/path" "ext" == "/directory/path.ext"
--   addExtension "file.txt" "bib" == "file.txt.bib"
--   addExtension "file." ".bib" == "file..bib"
--   addExtension "file" ".bib" == "file.bib"
--   addExtension "/" "x" == "/.x"
--   addExtension x "" == x
--   Valid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   </pre>
addExtension :: OsPath -> OsString -> OsPath

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

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

-- | Split on all extensions.
--   
--   <pre>
--   splitExtensions "/directory/path.ext" == ("/directory/path",".ext")
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   uncurry (&lt;&gt;) (splitExtensions x) == x
--   Valid x =&gt; uncurry addExtension (splitExtensions x) == x
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   </pre>
splitExtensions :: OsPath -> (OsPath, OsString)

-- | Drop all extensions.
--   
--   <pre>
--   dropExtensions "/directory/path.ext" == "/directory/path"
--   dropExtensions "file.tar.gz" == "file"
--   not $ hasExtension $ dropExtensions x
--   not $ any isExtSeparator $ takeFileName $ dropExtensions x
--   </pre>
dropExtensions :: OsPath -> OsPath

-- | Get all extensions.
--   
--   <pre>
--   takeExtensions "/directory/path.ext" == ".ext"
--   takeExtensions "file.tar.gz" == ".tar.gz"
--   </pre>
takeExtensions :: OsPath -> OsString

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

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

-- | Drop the given extension from a filepath, and the <tt>"."</tt>
--   preceding it. Returns <a>Nothing</a> if the filepath does not have the
--   given extension, or <a>Just</a> and the part before the extension if
--   it does.
--   
--   This function can be more predictable than <a>dropExtensions</a>,
--   especially if the filename might itself contain <tt>.</tt> characters.
--   
--   <pre>
--   stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
--   stripExtension "hi.o" "foo.x.hs.o" == Nothing
--   dropExtension x == fromJust (stripExtension (takeExtension x) x)
--   dropExtensions x == fromJust (stripExtension (takeExtensions x) x)
--   stripExtension ".c.d" "a.b.c.d"  == Just "a.b"
--   stripExtension ".c.d" "a.b..c.d" == Just "a.b."
--   stripExtension "baz"  "foo.bar"  == Nothing
--   stripExtension "bar"  "foobar"   == Nothing
--   stripExtension ""     x          == Just x
--   </pre>
stripExtension :: OsString -> OsPath -> Maybe OsPath

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
--   inverse. The first component will often end with a trailing slash.
--   
--   <pre>
--   splitFileName "/directory/file.ext" == ("/directory/","file.ext")
--   Valid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
--   Valid x =&gt; isValid (fst (splitFileName x))
--   splitFileName "file/bob.txt" == ("file/", "bob.txt")
--   splitFileName "file/" == ("file/", "")
--   splitFileName "bob" == ("./", "bob")
--   Posix:   splitFileName "/" == ("/","")
--   Windows: splitFileName "c:" == ("c:","")
--   </pre>
splitFileName :: OsPath -> (OsPath, OsPath)

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

-- | Set the filename.
--   
--   <pre>
--   replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
--   Valid x =&gt; replaceFileName x (takeFileName x) == x
--   </pre>
replaceFileName :: OsPath -> OsString -> OsPath

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

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

-- | Set the base name.
--   
--   <pre>
--   replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext"
--   replaceBaseName "file/test.txt" "bob" == "file/bob.txt"
--   replaceBaseName "fred" "bill" == "bill"
--   replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar"
--   Valid x =&gt; replaceBaseName x (takeBaseName x) == x
--   </pre>
replaceBaseName :: OsPath -> OsString -> OsPath

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

-- | Set the directory, keeping the filename the same.
--   
--   <pre>
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
--   </pre>
replaceDirectory :: OsPath -> OsPath -> OsPath

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

-- | Combine two paths with a path separator. If the second path starts
--   with a path separator or a drive letter, then it returns the second.
--   The intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
--   will access the same file as <tt>setCurrentDirectory dir; readFile
--   file</tt>.
--   
--   <pre>
--   Posix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
--   Windows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
--            "directory" &lt;/&gt; "/file.ext" == "/file.ext"
--   Valid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
--   </pre>
--   
--   Combined:
--   
--   <pre>
--   Posix:   "/" &lt;/&gt; "test" == "/test"
--   Posix:   "home" &lt;/&gt; "bob" == "home/bob"
--   Posix:   "x:" &lt;/&gt; "foo" == "x:/foo"
--   Windows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
--   Windows: "home" &lt;/&gt; "bob" == "home\\bob"
--   </pre>
--   
--   Not combined:
--   
--   <pre>
--   Posix:   "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
--   </pre>
--   
--   Not combined (tricky):
--   
--   On Windows, if a filepath starts with a single slash, it is relative
--   to the root of the current drive. In [1], this is (confusingly)
--   referred to as an absolute path. The current behavior of
--   <a>&lt;/&gt;</a> is to never combine these forms.
--   
--   <pre>
--   Windows: "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "\\bob" == "\\bob"
--   Windows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
--   </pre>
--   
--   On Windows, from [1]: "If a file name begins with only a disk
--   designator but not the backslash after the colon, it is interpreted as
--   a relative path to the current directory on the drive with the
--   specified letter." The current behavior of <a>&lt;/&gt;</a> is to
--   never combine these forms.
--   
--   <pre>
--   Windows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   Windows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   </pre>
(</>) :: OsPath -> OsPath -> OsPath

-- | Split a path by the directory separator.
--   
--   <pre>
--   splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
--   concat (splitPath x) == x
--   splitPath "test//item/" == ["test//","item/"]
--   splitPath "test/item/file" == ["test/","item/","file"]
--   splitPath "" == []
--   Windows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
--   Posix:   splitPath "/file/test" == ["/","file/","test"]
--   </pre>
splitPath :: OsPath -> [OsPath]

-- | Join path elements back together.
--   
--   <pre>
--   joinPath z == foldr (&lt;/&gt;) "" z
--   joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
--   Valid x =&gt; joinPath (splitPath x) == x
--   joinPath [] == ""
--   Posix: joinPath ["test","file","path"] == "test/file/path"
--   </pre>
joinPath :: [OsPath] -> OsPath

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

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   <pre>
--   uncurry (&lt;&gt;) (splitDrive x) == x
--   Windows: splitDrive "file" == ("","file")
--   Windows: splitDrive "c:/file" == ("c:/","file")
--   Windows: splitDrive "c:\\file" == ("c:\\","file")
--   Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
--   Windows: splitDrive "\\\\shared" == ("\\\\shared","")
--   Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
--   Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
--   Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
--   Windows: splitDrive "/d" == ("","/d")
--   Posix:   splitDrive "/test" == ("/","test")
--   Posix:   splitDrive "//test" == ("//","test")
--   Posix:   splitDrive "test/file" == ("","test/file")
--   Posix:   splitDrive "file" == ("","file")
--   </pre>
splitDrive :: OsPath -> (OsPath, OsPath)

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

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

-- | Does a path have a drive.
--   
--   <pre>
--   not (hasDrive x) == null (takeDrive x)
--   Posix:   hasDrive "/foo" == True
--   Windows: hasDrive "C:\\foo" == True
--   Windows: hasDrive "C:foo" == True
--            hasDrive "foo" == False
--            hasDrive "" == False
--   </pre>
hasDrive :: OsPath -> Bool

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

-- | Is an element a drive
--   
--   <pre>
--   Posix:   isDrive "/" == True
--   Posix:   isDrive "/foo" == False
--   Windows: isDrive "C:\\" == True
--   Windows: isDrive "C:\\foo" == False
--            isDrive "" == False
--   </pre>
isDrive :: OsPath -> Bool

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

-- | Add a trailing file path separator if one is not already present.
--   
--   <pre>
--   hasTrailingPathSeparator (addTrailingPathSeparator x)
--   hasTrailingPathSeparator x ==&gt; addTrailingPathSeparator x == x
--   Posix:    addTrailingPathSeparator "test/rest" == "test/rest/"
--   </pre>
addTrailingPathSeparator :: OsPath -> OsPath

-- | Remove any trailing path separators
--   
--   <pre>
--   dropTrailingPathSeparator "file/test/" == "file/test"
--             dropTrailingPathSeparator "/" == "/"
--   Windows:  dropTrailingPathSeparator "\\" == "\\"
--   Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
--   </pre>
dropTrailingPathSeparator :: OsPath -> OsPath

-- | Normalise a file
--   
--   <ul>
--   <li>// outside of the drive can be made blank</li>
--   <li>/ -&gt; <a>pathSeparator</a></li>
--   <li>./ -&gt; ""</li>
--   </ul>
--   
--   Does not remove <tt>".."</tt>, because of symlinks.
--   
--   <pre>
--   Posix:   normalise "/file/\\test////" == "/file/\\test/"
--   Posix:   normalise "/file/./test" == "/file/test"
--   Posix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
--   Posix:   normalise "../bob/fred/" == "../bob/fred/"
--   Posix:   normalise "/a/../c" == "/a/../c"
--   Posix:   normalise "./bob/fred/" == "bob/fred/"
--   Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
--   Windows: normalise "c:\\" == "C:\\"
--   Windows: normalise "C:.\\" == "C:"
--   Windows: normalise "\\\\server\\test" == "\\\\server\\test"
--   Windows: normalise "//server/test" == "\\\\server\\test"
--   Windows: normalise "c:/file" == "C:\\file"
--   Windows: normalise "/file" == "\\file"
--   Windows: normalise "\\" == "\\"
--   Windows: normalise "/./" == "\\"
--            normalise "." == "."
--   Posix:   normalise "./" == "./"
--   Posix:   normalise "./." == "./"
--   Posix:   normalise "/./" == "/"
--   Posix:   normalise "/" == "/"
--   Posix:   normalise "bob/fred/." == "bob/fred/"
--   Posix:   normalise "//home" == "/home"
--   </pre>
normalise :: OsPath -> OsPath

-- | Equality of two filepaths. If you call
--   <tt>System.Directory.canonicalizePath</tt> first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to <a>normalise</a>, this does not expand <tt>".."</tt>,
--   because of symlinks.
--   
--   <pre>
--            x == y ==&gt; equalFilePath x y
--            normalise x == normalise y ==&gt; equalFilePath x y
--            equalFilePath "foo" "foo/"
--            not (equalFilePath "/a/../c" "/c")
--            not (equalFilePath "foo" "/foo")
--   Posix:   not (equalFilePath "foo" "FOO")
--   Windows: equalFilePath "foo" "FOO"
--   Windows: not (equalFilePath "C:" "C:/")
--   </pre>
equalFilePath :: OsPath -> OsPath -> Bool

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

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

-- | <pre>
--   not . <a>isRelative</a>
--   </pre>
--   
--   <pre>
--   isAbsolute x == not (isRelative x)
--   </pre>
isAbsolute :: OsPath -> Bool

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

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


-- | A library for <tt>FilePath</tt> manipulations, using Windows style
--   paths on all platforms. Importing <a>System.FilePath</a> is usually
--   better.
--   
--   Given the example <tt>FilePath</tt>: <tt>/directory/file.ext</tt>
--   
--   We can use the following functions to extract pieces.
--   
--   <ul>
--   <li><a>takeFileName</a> gives <tt>"file.ext"</tt></li>
--   <li><a>takeDirectory</a> gives <tt>"/directory"</tt></li>
--   <li><a>takeExtension</a> gives <tt>".ext"</tt></li>
--   <li><a>dropExtension</a> gives <tt>"/directory/file"</tt></li>
--   <li><a>takeBaseName</a> gives <tt>"file"</tt></li>
--   </ul>
--   
--   And we could have built an equivalent path with the following
--   expressions:
--   
--   <ul>
--   <li><tt>"/directory" <a>&lt;/&gt;</a> "file.ext"</tt>.</li>
--   <li><tt>"/directory/file" <a>&lt;.&gt;</a> "ext"</tt>.</li>
--   <li><tt>"/directory/file.txt" <a>-&lt;.&gt;</a> "ext"</tt>.</li>
--   </ul>
--   
--   Each function in this module is documented with several examples,
--   which are also used as tests.
--   
--   Here are a few examples of using the <tt>filepath</tt> functions
--   together:
--   
--   <i>Example 1:</i> Find the possible locations of a Haskell module
--   <tt>Test</tt> imported from module <tt>Main</tt>:
--   
--   <pre>
--   [<a>replaceFileName</a> path_to_main "Test" <a>&lt;.&gt;</a> ext | ext &lt;- ["hs","lhs"] ]
--   </pre>
--   
--   <i>Example 2:</i> Download a file from <tt>url</tt> and save it to
--   disk:
--   
--   <pre>
--   do let file = <a>makeValid</a> url
--      System.Directory.createDirectoryIfMissing True (<a>takeDirectory</a> file)
--   </pre>
--   
--   <i>Example 3:</i> Compile a Haskell file, putting the <tt>.hi</tt>
--   file under <tt>interface</tt>:
--   
--   <pre>
--   <a>takeDirectory</a> file <a>&lt;/&gt;</a> "interface" <a>&lt;/&gt;</a> (<a>takeFileName</a> file <a>-&lt;.&gt;</a> "hi")
--   </pre>
--   
--   References: [1] <a>Naming Files, Paths and Namespaces</a> (Microsoft
--   MSDN)
module System.OsPath.Windows.Internal

-- | The character that separates directories. In the case where more than
--   one character is possible, <a>pathSeparator</a> is the 'ideal' one.
--   
--   <pre>
--   Windows: pathSeparator == '\\'
--   Posix:   pathSeparator ==  '/'
--   isPathSeparator pathSeparator
--   </pre>
pathSeparator :: Word16

-- | The list of all possible separators.
--   
--   <pre>
--   Windows: pathSeparators == ['\\', '/']
--   Posix:   pathSeparators == ['/']
--   pathSeparator `elem` pathSeparators
--   </pre>
pathSeparators :: [Word16]

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

-- | The character that is used to separate the entries in the $PATH
--   environment variable.
--   
--   <pre>
--   Windows: searchPathSeparator == ';'
--   Posix:   searchPathSeparator == ':'
--   </pre>
searchPathSeparator :: Word16

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

-- | File extension character
--   
--   <pre>
--   extSeparator == '.'
--   </pre>
extSeparator :: Word16

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

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

-- | Split on the extension. <a>addExtension</a> is the inverse.
--   
--   <pre>
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (&lt;&gt;) (splitExtension x) == x
--   Valid x =&gt; uncurry addExtension (splitExtension x) == x
--   splitExtension "file.txt" == ("file",".txt")
--   splitExtension "file" == ("file","")
--   splitExtension "file/file.txt" == ("file/file",".txt")
--   splitExtension "file.txt/boris" == ("file.txt/boris","")
--   splitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
--   splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
--   splitExtension "file/path.txt/" == ("file/path.txt/","")
--   </pre>
splitExtension :: ShortByteString -> (ShortByteString, ShortByteString)

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

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

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

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

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

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

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

-- | Split on all extensions.
--   
--   <pre>
--   splitExtensions "/directory/path.ext" == ("/directory/path",".ext")
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   uncurry (&lt;&gt;) (splitExtensions x) == x
--   Valid x =&gt; uncurry addExtension (splitExtensions x) == x
--   </pre>
splitExtensions :: ShortByteString -> (ShortByteString, ShortByteString)

-- | Drop all extensions.
--   
--   <pre>
--   dropExtensions "/directory/path.ext" == "/directory/path"
--   dropExtensions "file.tar.gz" == "file"
--   not $ hasExtension $ dropExtensions x
--   not $ any isExtSeparator $ takeFileName $ dropExtensions x
--   </pre>
dropExtensions :: ShortByteString -> ShortByteString

-- | Get all extensions.
--   
--   <pre>
--   takeExtensions "/directory/path.ext" == ".ext"
--   takeExtensions "file.tar.gz" == ".tar.gz"
--   </pre>
takeExtensions :: ShortByteString -> ShortByteString

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

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

-- | Drop the given extension from a ShortByteString, and the <tt>"."</tt>
--   preceding it. Returns <a>Nothing</a> if the ShortByteString does not
--   have the given extension, or <a>Just</a> and the part before the
--   extension if it does.
--   
--   This function can be more predictable than <a>dropExtensions</a>,
--   especially if the filename might itself contain <tt>.</tt> characters.
--   
--   <pre>
--   stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
--   stripExtension "hi.o" "foo.x.hs.o" == Nothing
--   dropExtension x == fromJust (stripExtension (takeExtension x) x)
--   dropExtensions x == fromJust (stripExtension (takeExtensions x) x)
--   stripExtension ".c.d" "a.b.c.d"  == Just "a.b"
--   stripExtension ".c.d" "a.b..c.d" == Just "a.b."
--   stripExtension "baz"  "foo.bar"  == Nothing
--   stripExtension "bar"  "foobar"   == Nothing
--   stripExtension ""     x          == Just x
--   </pre>
stripExtension :: ShortByteString -> ShortByteString -> Maybe ShortByteString

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
--   inverse. The first component will often end with a trailing slash.
--   
--   <pre>
--   splitFileName "/directory/file.ext" == ("/directory/","file.ext")
--   Valid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
--   Valid x =&gt; isValid (fst (splitFileName x))
--   splitFileName "file/bob.txt" == ("file/", "bob.txt")
--   splitFileName "file/" == ("file/", "")
--   splitFileName "bob" == ("./", "bob")
--   Posix:   splitFileName "/" == ("/","")
--   Windows: splitFileName "c:" == ("c:","")
--   Windows: splitFileName "\\\\?\\A:\\fred" == ("\\\\?\\A:\\","fred")
--   Windows: splitFileName "\\\\?\\A:" == ("\\\\?\\A:","")
--   </pre>
splitFileName :: ShortByteString -> (ShortByteString, ShortByteString)

-- | Get the file name.
--   
--   <pre>
--   takeFileName "/directory/file.ext" == "file.ext"
--   takeFileName "test/" == ""
--   isSuffixOf (takeFileName x) x
--   takeFileName x == snd (splitFileName x)
--   Valid x =&gt; takeFileName (replaceFileName x "fred") == "fred"
--   Valid x =&gt; takeFileName (x &lt;/&gt; "fred") == "fred"
--   Valid x =&gt; isRelative (takeFileName x)
--   </pre>
takeFileName :: ShortByteString -> ShortByteString

-- | Set the filename.
--   
--   <pre>
--   replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
--   Valid x =&gt; replaceFileName x (takeFileName x) == x
--   </pre>
replaceFileName :: ShortByteString -> ShortByteString -> ShortByteString

-- | Drop the filename. Unlike <a>takeDirectory</a>, this function will
--   leave a trailing path separator on the directory.
--   
--   <pre>
--   dropFileName "/directory/file.ext" == "/directory/"
--   dropFileName x == fst (splitFileName x)
--   isPrefixOf (takeDrive x) (dropFileName x)
--   </pre>
dropFileName :: ShortByteString -> ShortByteString

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

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

-- | Get the directory name, move up one level.
--   
--   <pre>
--             takeDirectory "/directory/other.ext" == "/directory"
--             isPrefixOf (takeDirectory x) x || takeDirectory x == "."
--             takeDirectory "foo" == "."
--             takeDirectory "/" == "/"
--             takeDirectory "/foo" == "/"
--             takeDirectory "/foo/bar/baz" == "/foo/bar"
--             takeDirectory "/foo/bar/baz/" == "/foo/bar/baz"
--             takeDirectory "foo/bar/baz" == "foo/bar"
--   Windows:  takeDirectory "foo\\bar" == "foo"
--   Windows:  takeDirectory "foo\\bar\\\\" == "foo\\bar"
--   Windows:  takeDirectory "C:\\" == "C:\\"
--   </pre>
takeDirectory :: ShortByteString -> ShortByteString

-- | Set the directory, keeping the filename the same.
--   
--   <pre>
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
--   </pre>
replaceDirectory :: ShortByteString -> ShortByteString -> ShortByteString

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

-- | Combine two paths with a path separator. If the second path starts
--   with a path separator or a drive letter, then it returns the second.
--   The intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
--   will access the same file as <tt>setCurrentDirectory dir; readFile
--   file</tt>.
--   
--   <pre>
--   Posix:   "/directory" &lt;/&gt; "file.ext" == "/directory/file.ext"
--   Windows: "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
--            "directory" &lt;/&gt; "/file.ext" == "/file.ext"
--   Valid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
--   </pre>
--   
--   Combined:
--   
--   <pre>
--   Posix:   "/" &lt;/&gt; "test" == "/test"
--   Posix:   "home" &lt;/&gt; "bob" == "home/bob"
--   Posix:   "x:" &lt;/&gt; "foo" == "x:/foo"
--   Windows: "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
--   Windows: "home" &lt;/&gt; "bob" == "home\\bob"
--   </pre>
--   
--   Not combined:
--   
--   <pre>
--   Posix:   "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
--   </pre>
--   
--   Not combined (tricky):
--   
--   On Windows, if a filepath starts with a single slash, it is relative
--   to the root of the current drive. In [1], this is (confusingly)
--   referred to as an absolute path. The current behavior of
--   <a>&lt;/&gt;</a> is to never combine these forms.
--   
--   <pre>
--   Windows: "home" &lt;/&gt; "/bob" == "/bob"
--   Windows: "home" &lt;/&gt; "\\bob" == "\\bob"
--   Windows: "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
--   </pre>
--   
--   On Windows, from [1]: "If a file name begins with only a disk
--   designator but not the backslash after the colon, it is interpreted as
--   a relative path to the current directory on the drive with the
--   specified letter." The current behavior of <a>&lt;/&gt;</a> is to
--   never combine these forms.
--   
--   <pre>
--   Windows: "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   Windows: "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   </pre>
(</>) :: ShortByteString -> ShortByteString -> ShortByteString
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   <pre>
--   splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
--   concat (splitPath x) == x
--   splitPath "test//item/" == ["test//","item/"]
--   splitPath "test/item/file" == ["test/","item/","file"]
--   splitPath "" == []
--   Windows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
--   Posix:   splitPath "/file/test" == ["/","file/","test"]
--   </pre>
splitPath :: ShortByteString -> [ShortByteString]

-- | Join path elements back together.
--   
--   <pre>
--   joinPath z == foldr (&lt;/&gt;) "" z
--   joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
--   Valid x =&gt; joinPath (splitPath x) == x
--   joinPath [] == ""
--   Posix: joinPath ["test","file","path"] == "test/file/path"
--   </pre>
joinPath :: [ShortByteString] -> ShortByteString

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

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   <pre>
--   uncurry (&lt;&gt;) (splitDrive x) == x
--   Windows: splitDrive "file" == ("","file")
--   Windows: splitDrive "c:/file" == ("c:/","file")
--   Windows: splitDrive "c:\\file" == ("c:\\","file")
--   Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
--   Windows: splitDrive "\\\\shared" == ("\\\\shared","")
--   Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
--   Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
--   Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
--   Windows: splitDrive "/d" == ("","/d")
--   Posix:   splitDrive "/test" == ("/","test")
--   Posix:   splitDrive "//test" == ("//","test")
--   Posix:   splitDrive "test/file" == ("","test/file")
--   Posix:   splitDrive "file" == ("","file")
--   </pre>
splitDrive :: ShortByteString -> (ShortByteString, ShortByteString)

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

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

-- | Does a path have a drive.
--   
--   <pre>
--   not (hasDrive x) == null (takeDrive x)
--   Posix:   hasDrive "/foo" == True
--   Windows: hasDrive "C:\\foo" == True
--   Windows: hasDrive "C:foo" == True
--            hasDrive "foo" == False
--            hasDrive "" == False
--   </pre>
hasDrive :: ShortByteString -> Bool

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

-- | Is an element a drive
--   
--   <pre>
--   Posix:   isDrive "/" == True
--   Posix:   isDrive "/foo" == False
--   Windows: isDrive "C:\\" == True
--   Windows: isDrive "C:\\foo" == False
--            isDrive "" == False
--   </pre>
isDrive :: ShortByteString -> Bool

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

-- | Add a trailing file path separator if one is not already present.
--   
--   <pre>
--   hasTrailingPathSeparator (addTrailingPathSeparator x)
--   hasTrailingPathSeparator x ==&gt; addTrailingPathSeparator x == x
--   Posix:    addTrailingPathSeparator "test/rest" == "test/rest/"
--   </pre>
addTrailingPathSeparator :: ShortByteString -> ShortByteString

-- | Remove any trailing path separators
--   
--   <pre>
--   dropTrailingPathSeparator "file/test/" == "file/test"
--             dropTrailingPathSeparator "/" == "/"
--   Windows:  dropTrailingPathSeparator "\\" == "\\"
--   Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
--   </pre>
dropTrailingPathSeparator :: ShortByteString -> ShortByteString

-- | Normalise a file
--   
--   <ul>
--   <li>// outside of the drive can be made blank</li>
--   <li>/ -&gt; <a>pathSeparator</a></li>
--   <li>./ -&gt; ""</li>
--   </ul>
--   
--   Does not remove <tt>".."</tt>, because of symlinks.
--   
--   <pre>
--   Posix:   normalise "/file/\\test////" == "/file/\\test/"
--   Posix:   normalise "/file/./test" == "/file/test"
--   Posix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"
--   Posix:   normalise "../bob/fred/" == "../bob/fred/"
--   Posix:   normalise "/a/../c" == "/a/../c"
--   Posix:   normalise "./bob/fred/" == "bob/fred/"
--   Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
--   Windows: normalise "c:\\" == "C:\\"
--   Windows: normalise "c:\\\\\\\\" == "C:\\"
--   Windows: normalise "C:.\\" == "C:"
--   Windows: normalise "\\\\server\\test" == "\\\\server\\test"
--   Windows: normalise "//server/test" == "\\\\server\\test"
--   Windows: normalise "c:/file" == "C:\\file"
--   Windows: normalise "/file" == "\\file"
--   Windows: normalise "\\" == "\\"
--   Windows: normalise "/./" == "\\"
--            normalise "." == "."
--   Posix:   normalise "./" == "./"
--   Posix:   normalise "./." == "./"
--   Posix:   normalise "/./" == "/"
--   Posix:   normalise "/" == "/"
--   Posix:   normalise "bob/fred/." == "bob/fred/"
--   Posix:   normalise "//home" == "/home"
--   </pre>
normalise :: ShortByteString -> ShortByteString

-- | Equality of two <tt>FILEPATH</tt>s. If you call
--   <tt>System.Directory.canonicalizePath</tt> first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to <a>normalise</a>, this does not expand <tt>".."</tt>,
--   because of symlinks.
--   
--   <pre>
--            x == y ==&gt; equalFilePath x y
--            normalise x == normalise y ==&gt; equalFilePath x y
--            equalFilePath "foo" "foo/"
--            not (equalFilePath "/a/../c" "/c")
--            not (equalFilePath "foo" "/foo")
--   Posix:   not (equalFilePath "foo" "FOO")
--   Windows: equalFilePath "foo" "FOO"
--   Windows: not (equalFilePath "C:" "C:/")
--   </pre>
equalFilePath :: ShortByteString -> ShortByteString -> Bool

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

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

-- | <pre>
--   not . <a>isRelative</a>
--   </pre>
--   
--   <pre>
--   isAbsolute x == not (isRelative x)
--   </pre>
isAbsolute :: ShortByteString -> Bool

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

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

module System.OsPath.Windows

-- | Commonly used windows string as wide character bytes.
data WindowsString
data WindowsChar

-- | Filepaths are <tt>wchar_t*</tt> data on windows as passed to syscalls.
type WindowsPath = WindowsString

-- | Partial unicode friendly encoding.
--   
--   This encodes as UTF16-LE (strictly), which is a pretty good guess.
--   
--   Throws an <a>EncodingException</a> if encoding fails. If the input
--   does not contain surrogate chars, you can use
--   <tt>unsafeEncodeUtf</tt>.
encodeUtf :: MonadThrow m => String -> m WindowsString

-- | Unsafe unicode friendly encoding.
--   
--   Like <a>encodeUtf</a>, except it crashes when the input contains
--   surrogate chars. For sanitized input, this can be useful.
unsafeEncodeUtf :: HasCallStack => String -> WindowsString

-- | Encode a <a>String</a> with the specified encoding.
--   
--   Note: We expect a "wide char" encoding (e.g. UCS-2 or UTF-16).
--   Anything that works with <tt>Word16</tt> boundaries. Picking an
--   incompatible encoding may crash filepath operations.
encodeWith :: TextEncoding -> String -> Either EncodingException WindowsString
encodeFS :: String -> IO WindowsPath

-- | QuasiQuote a <a>WindowsPath</a>. This accepts Unicode characters and
--   encodes as UTF-16LE. Runs <a>isValid</a> on the input.
pstr :: QuasiQuoter

-- | Pack a list of platform words to a platform string.
--   
--   Note that using this in conjunction with <a>unsafeFromChar</a> to
--   convert from <tt>[Char]</tt> to platform string is probably not what
--   you want, because it will truncate unicode code points.
pack :: [WindowsChar] -> WindowsString

-- | Partial unicode friendly decoding.
--   
--   This decodes as UTF16-LE (strictly), which is a pretty good.
--   
--   Throws a <a>EncodingException</a> if decoding fails.
decodeUtf :: MonadThrow m => WindowsString -> m String

-- | Decode a <a>WindowsString</a> with the specified encoding.
--   
--   The String is forced into memory to catch all exceptions.
decodeWith :: TextEncoding -> WindowsString -> Either EncodingException String
decodeFS :: WindowsPath -> IO String

-- | Unpack a platform string to a list of platform words.
unpack :: WindowsString -> [WindowsChar]

-- | Truncates to 2 octets.
unsafeFromChar :: Char -> WindowsChar

-- | Converts back to a unicode codepoint (total).
toChar :: WindowsChar -> Char

-- | The character that separates directories. In the case where more than
--   one character is possible, <a>pathSeparator</a> is the 'ideal' one.
--   
--   <pre>
--   pathSeparator == '\\'S
--   </pre>
pathSeparator :: WindowsChar

-- | The list of all possible separators.
--   
--   <pre>
--   pathSeparators == ['\\', '/']
--   pathSeparator `elem` pathSeparators
--   </pre>
pathSeparators :: [WindowsChar]

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

-- | The character that is used to separate the entries in the $PATH
--   environment variable.
--   
--   <pre>
--   searchPathSeparator == ';'
--   </pre>
searchPathSeparator :: WindowsChar

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

-- | File extension character
--   
--   <pre>
--   extSeparator == '.'
--   </pre>
extSeparator :: WindowsChar

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

-- | Take a string, split it on the <a>searchPathSeparator</a> character.
--   
--   Blank items are ignored and path elements are stripped of quotes.
--   
--   <pre>
--   splitSearchPath "File1;File2;File3"  == ["File1","File2","File3"]
--   splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"]
--   splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"]
--   </pre>
splitSearchPath :: WindowsString -> [WindowsPath]

-- | Split on the extension. <a>addExtension</a> is the inverse.
--   
--   <pre>
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (&lt;&gt;) (splitExtension x) == x
--   Valid x =&gt; uncurry addExtension (splitExtension x) == x
--   splitExtension "file.txt" == ("file",".txt")
--   splitExtension "file" == ("file","")
--   splitExtension "file/file.txt" == ("file/file",".txt")
--   splitExtension "file.txt/boris" == ("file.txt/boris","")
--   splitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext")
--   splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
--   splitExtension "file/path.txt/" == ("file/path.txt/","")
--   </pre>
splitExtension :: WindowsPath -> (WindowsPath, WindowsString)

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

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

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <a>&lt;.&gt;</a>.
--   
--   <pre>
--   addExtension "/directory/path" "ext" == "/directory/path.ext"
--   addExtension "file.txt" "bib" == "file.txt.bib"
--   addExtension "file." ".bib" == "file..bib"
--   addExtension "file" ".bib" == "file.bib"
--   addExtension "/" "x" == "/.x"
--   addExtension x "" == x
--   Valid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   </pre>
--   
--   Add an extension, even if there is already one there, equivalent to
--   <a>&lt;.&gt;</a>.
--   
--   <pre>
--   addExtension "/directory/path" "ext" == "/directory/path.ext"
--   addExtension "file.txt" "bib" == "file.txt.bib"
--   addExtension "file." ".bib" == "file..bib"
--   addExtension "file" ".bib" == "file.bib"
--   addExtension "/" "x" == "/.x"
--   addExtension x "" == x
--   Valid x =&gt; takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   </pre>
addExtension :: WindowsPath -> WindowsString -> WindowsPath

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

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

-- | Split on all extensions.
--   
--   <pre>
--   splitExtensions "/directory/path.ext" == ("/directory/path",".ext")
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   uncurry (&lt;&gt;) (splitExtensions x) == x
--   Valid x =&gt; uncurry addExtension (splitExtensions x) == x
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   </pre>
splitExtensions :: WindowsPath -> (WindowsPath, WindowsString)

-- | Drop all extensions.
--   
--   <pre>
--   dropExtensions "/directory/path.ext" == "/directory/path"
--   dropExtensions "file.tar.gz" == "file"
--   not $ hasExtension $ dropExtensions x
--   not $ any isExtSeparator $ takeFileName $ dropExtensions x
--   </pre>
dropExtensions :: WindowsPath -> WindowsPath

-- | Get all extensions.
--   
--   <pre>
--   takeExtensions "/directory/path.ext" == ".ext"
--   takeExtensions "file.tar.gz" == ".tar.gz"
--   </pre>
takeExtensions :: WindowsPath -> WindowsString

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

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

-- | Drop the given extension from a filepath, and the <tt>"."</tt>
--   preceding it. Returns <a>Nothing</a> if the filepath does not have the
--   given extension, or <a>Just</a> and the part before the extension if
--   it does.
--   
--   This function can be more predictable than <a>dropExtensions</a>,
--   especially if the filename might itself contain <tt>.</tt> characters.
--   
--   <pre>
--   stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x"
--   stripExtension "hi.o" "foo.x.hs.o" == Nothing
--   dropExtension x == fromJust (stripExtension (takeExtension x) x)
--   dropExtensions x == fromJust (stripExtension (takeExtensions x) x)
--   stripExtension ".c.d" "a.b.c.d"  == Just "a.b"
--   stripExtension ".c.d" "a.b..c.d" == Just "a.b."
--   stripExtension "baz"  "foo.bar"  == Nothing
--   stripExtension "bar"  "foobar"   == Nothing
--   stripExtension ""     x          == Just x
--   </pre>
stripExtension :: WindowsString -> WindowsPath -> Maybe WindowsPath

-- | Split a filename into directory and file. <a>&lt;/&gt;</a> is the
--   inverse. The first component will often end with a trailing slash.
--   
--   <pre>
--   splitFileName "/directory/file.ext" == ("/directory/","file.ext")
--   Valid x =&gt; uncurry (&lt;/&gt;) (splitFileName x) == x || fst (splitFileName x) == "./"
--   Valid x =&gt; isValid (fst (splitFileName x))
--   splitFileName "file/bob.txt" == ("file/", "bob.txt")
--   splitFileName "file/" == ("file/", "")
--   splitFileName "bob" == ("./", "bob")
--   splitFileName "c:" == ("c:","")
--   </pre>
splitFileName :: WindowsPath -> (WindowsPath, WindowsPath)

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

-- | Set the filename.
--   
--   <pre>
--   replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
--   Valid x =&gt; replaceFileName x (takeFileName x) == x
--   </pre>
replaceFileName :: WindowsPath -> WindowsString -> WindowsPath

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

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

-- | Set the base name.
--   
--   <pre>
--   replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext"
--   replaceBaseName "file/test.txt" "bob" == "file/bob.txt"
--   replaceBaseName "fred" "bill" == "bill"
--   replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar"
--   Valid x =&gt; replaceBaseName x (takeBaseName x) == x
--   </pre>
replaceBaseName :: WindowsPath -> WindowsString -> WindowsPath

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

-- | Set the directory, keeping the filename the same.
--   
--   <pre>
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x =&gt; replaceDirectory x (takeDirectory x) `equalFilePath` x
--   </pre>
replaceDirectory :: WindowsPath -> WindowsPath -> WindowsPath

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

-- | Combine two paths with a path separator. If the second path starts
--   with a path separator or a drive letter, then it returns the second.
--   The intention is that <tt>readFile (dir <a>&lt;/&gt;</a> file)</tt>
--   will access the same file as <tt>setCurrentDirectory dir; readFile
--   file</tt>.
--   
--   <pre>
--   "/directory" &lt;/&gt; "file.ext" == "/directory\\file.ext"
--   "directory" &lt;/&gt; "/file.ext" == "/file.ext"
--   Valid x =&gt; (takeDirectory x &lt;/&gt; takeFileName x) `equalFilePath` x
--   </pre>
--   
--   Combined:
--   
--   <pre>
--   "C:\\foo" &lt;/&gt; "bar" == "C:\\foo\\bar"
--   "home" &lt;/&gt; "bob" == "home\\bob"
--   </pre>
--   
--   Not combined:
--   
--   <pre>
--   "home" &lt;/&gt; "C:\\bob" == "C:\\bob"
--   </pre>
--   
--   Not combined (tricky):
--   
--   If a filepath starts with a single slash, it is relative to the root
--   of the current drive. In [1], this is (confusingly) referred to as an
--   absolute path. The current behavior of <a>&lt;/&gt;</a> is to never
--   combine these forms.
--   
--   <pre>
--   "home" &lt;/&gt; "/bob" == "/bob"
--   "home" &lt;/&gt; "\\bob" == "\\bob"
--   "C:\\home" &lt;/&gt; "\\bob" == "\\bob"
--   </pre>
--   
--   From [1]: "If a file name begins with only a disk designator but not
--   the backslash after the colon, it is interpreted as a relative path to
--   the current directory on the drive with the specified letter." The
--   current behavior of <a>&lt;/&gt;</a> is to never combine these forms.
--   
--   <pre>
--   "D:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   "C:\\foo" &lt;/&gt; "C:bar" == "C:bar"
--   </pre>
(</>) :: WindowsPath -> WindowsPath -> WindowsPath

-- | Split a path by the directory separator.
--   
--   <pre>
--   splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
--   concat (splitPath x) == x
--   splitPath "test//item/" == ["test//","item/"]
--   splitPath "test/item/file" == ["test/","item/","file"]
--   splitPath "" == []
--   splitPath "c:\\test\\path" == ["c:\\","test\\","path"]
--   </pre>
splitPath :: WindowsPath -> [WindowsPath]

-- | Join path elements back together.
--   
--   <pre>
--   joinPath z == foldr (&lt;/&gt;) "" z
--   joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
--   Valid x =&gt; joinPath (splitPath x) == x
--   joinPath [] == ""
--   </pre>
joinPath :: [WindowsPath] -> WindowsPath

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

-- | Split a path into a drive and a path.
--   
--   <pre>
--   uncurry (&lt;&gt;) (splitDrive x) == x
--   splitDrive "file" == ("","file")
--   splitDrive "c:/file" == ("c:/","file")
--   splitDrive "c:\\file" == ("c:\\","file")
--   splitDrive "\\\\shared\\test" == ("\\\\shared\\","test")
--   splitDrive "\\\\shared" == ("\\\\shared","")
--   splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file")
--   splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file")
--   splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file")
--   splitDrive "/d" == ("","/d")
--   </pre>
splitDrive :: WindowsPath -> (WindowsPath, WindowsPath)

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

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

-- | Does a path have a drive.
--   
--   <pre>
--   not (hasDrive x) == null (takeDrive x)
--   hasDrive "C:\\foo" == True
--   hasDrive "C:foo" == True
--   hasDrive "foo" == False
--   hasDrive "" == False
--   </pre>
hasDrive :: WindowsPath -> Bool

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

-- | Is an element a drive
--   
--   <pre>
--   isDrive "C:\\" == True
--   isDrive "C:\\foo" == False
--   isDrive "" == False
--   </pre>
isDrive :: WindowsPath -> Bool

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

-- | Add a trailing file path separator if one is not already present.
--   
--   <pre>
--   hasTrailingPathSeparator (addTrailingPathSeparator x)
--   hasTrailingPathSeparator x ==&gt; addTrailingPathSeparator x == x
--   </pre>
addTrailingPathSeparator :: WindowsPath -> WindowsPath

-- | Remove any trailing path separators
--   
--   <pre>
--   dropTrailingPathSeparator "file/test/" == "file/test"
--   dropTrailingPathSeparator "/" == "/"
--   dropTrailingPathSeparator "\\" == "\\"
--   </pre>
dropTrailingPathSeparator :: WindowsPath -> WindowsPath

-- | Normalise a file
--   
--   <ul>
--   <li>// outside of the drive can be made blank</li>
--   <li>/ -&gt; <a>pathSeparator</a></li>
--   <li>./ -&gt; ""</li>
--   </ul>
--   
--   Does not remove <tt>".."</tt>, because of symlinks.
--   
--   <pre>
--   normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
--   normalise "c:\\" == "C:\\"
--   normalise "C:.\\" == "C:"
--   normalise "\\\\server\\test" == "\\\\server\\test"
--   normalise "//server/test" == "\\\\server\\test"
--   normalise "c:/file" == "C:\\file"
--   normalise "/file" == "\\file"
--   normalise "\\" == "\\"
--   normalise "/./" == "\\"
--   normalise "." == "."
--   </pre>
normalise :: WindowsPath -> WindowsPath

-- | Equality of two filepaths. If you call
--   <tt>System.Directory.canonicalizePath</tt> first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to <a>normalise</a>, this does not expand <tt>".."</tt>,
--   because of symlinks.
--   
--   <pre>
--   x == y ==&gt; equalFilePath x y
--   normalise x == normalise y ==&gt; equalFilePath x y
--   equalFilePath "foo" "foo/"
--   not (equalFilePath "/a/../c" "/c")
--   not (equalFilePath "foo" "/foo")
--   equalFilePath "foo" "FOO"
--   not (equalFilePath "C:" "C:/")
--   </pre>
equalFilePath :: WindowsPath -> WindowsPath -> Bool

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

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

-- | <pre>
--   not . <a>isRelative</a>
--   </pre>
--   
--   <pre>
--   isAbsolute x == not (isRelative x)
--   </pre>
isAbsolute :: WindowsPath -> Bool

-- | Is a filepath valid, i.e. could you create a file like it? This
--   function checks for invalid names, and invalid characters, but does
--   not check if length limits are exceeded, as these are typically
--   filesystem dependent.
--   
--   <pre>
--   isValid "" == False
--   isValid "\0" == False
--   isValid "c:\\test" == True
--   isValid "c:\\test:of_test" == False
--   isValid "test*" == False
--   isValid "c:\\test\\nul" == False
--   isValid "c:\\test\\prn.txt" == False
--   isValid "c:\\nul\\file" == False
--   isValid "\\\\" == False
--   isValid "\\\\\\foo" == False
--   isValid "\\\\?\\D:file" == False
--   isValid "foo\tbar" == False
--   isValid "nul .txt" == False
--   isValid " nul.txt" == True
--   </pre>
isValid :: WindowsPath -> Bool

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