-- 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>
--   
--   <a>System.OsString</a> is like <a>System.OsPath</a>, but more general
--   purpose. Refer to the documentation of those modules for more
--   information.
--   
--   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.4.100.4


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

-- | File and directory names are values of type <a>String</a>, whose
--   precise meaning is operating system dependent. Files can be opened,
--   yielding a handle which can then be used to operate on the contents of
--   that file.
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
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   </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")
--   </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)
--   </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

-- | File and directory names are values of type <a>String</a>, whose
--   precise meaning is operating system dependent. Files can be opened,
--   yielding a handle which can then be used to operate on the contents of
--   that file.
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
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   </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")
--   </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)
--   </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

-- | File and directory names are values of type <a>String</a>, whose
--   precise meaning is operating system dependent. Files can be opened,
--   yielding a handle which can then be used to operate on the contents of
--   that file.
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
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   </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")
--   </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)
--   </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


-- | Internal low-level utilities mostly for <a>Word16</a>, such as
--   byte-array operations and other stuff not meant to be exported from
--   Word16 module.
module System.OsPath.Data.ByteString.Short.Internal
_nul :: Word16
isSpace :: Word16 -> Bool

-- | Total conversion to char.
word16ToChar :: Word16 -> Char
create :: Int -> (forall s. MBA s -> ST s ()) -> ShortByteString
asBA :: ShortByteString -> BA
data BA
BA# :: ByteArray# -> BA
data MBA s
MBA# :: MutableByteArray# s -> MBA s
newPinnedByteArray :: Int -> ST s (MBA s)
newByteArray :: Int -> ST s (MBA s)
copyByteArray :: BA -> Int -> MBA s -> Int -> Int -> ST s ()
unsafeFreezeByteArray :: MBA s -> ST s BA
copyAddrToByteArray :: Ptr a -> MBA RealWorld -> Int -> Int -> ST RealWorld ()

-- | <i>O(n).</i> Construct a new <tt>ShortByteString</tt> from a
--   <tt>CWString</tt>. The resulting <tt>ShortByteString</tt> is an
--   immutable copy of the original <tt>CWString</tt>, and is managed on
--   the Haskell heap. The original <tt>CWString</tt> must be null
--   terminated.
packCWString :: Ptr Word16 -> IO ShortByteString

-- | <i>O(n).</i> Construct a new <tt>ShortByteString</tt> from a
--   <tt>CWStringLen</tt>. The resulting <tt>ShortByteString</tt> is an
--   immutable copy of the original <tt>CWStringLen</tt>. The
--   <tt>ShortByteString</tt> is a normal Haskell value and will be managed
--   on the Haskell heap.
packCWStringLen :: (Ptr Word16, Int) -> IO ShortByteString

-- | <i>O(n) construction.</i> Use a <tt>ShortByteString</tt> with a
--   function requiring a null-terminated <tt>CWString</tt>. The
--   <tt>CWString</tt> is a copy and will be freed automatically; it must
--   not be stored or used after the subcomputation finishes.
useAsCWString :: ShortByteString -> (Ptr Word16 -> IO a) -> IO a

-- | <i>O(n) construction.</i> Use a <tt>ShortByteString</tt> with a
--   function requiring a <tt>CWStringLen</tt>. As for
--   <tt>useAsCWString</tt> this function makes a copy of the original
--   <tt>ShortByteString</tt>. It must not be stored or used after the
--   subcomputation finishes.
useAsCWStringLen :: ShortByteString -> ((Ptr Word16, Int) -> IO a) -> IO a

-- | <i>O(n) construction.</i> Use a <tt>ShortByteString</tt> with a
--   function requiring a <tt>CWStringLen</tt>. As for
--   <tt>useAsCWString</tt> this function makes a copy of the original
--   <tt>ShortByteString</tt>. It must not be stored or used after the
--   subcomputation finishes.
newCWString :: ShortByteString -> IO (Ptr Word16)
moduleErrorIO :: String -> String -> IO a
moduleErrorMsg :: String -> String -> String
packWord16 :: [Word16] -> ShortByteString
packLenWord16 :: Int -> [Word16] -> ShortByteString
unpackWord16 :: ShortByteString -> [Word16]
packWord16Rev :: [Word16] -> ShortByteString
packLenWord16Rev :: Int -> [Word16] -> ShortByteString

-- | This isn't strictly Word16 array write. Instead it's two consecutive
--   Word8 array writes to avoid endianness issues due to primops doing
--   automatic alignment based on host platform. We want to always write LE
--   to the byte array.
writeWord16Array :: MBA s -> Int -> Word16 -> ST s ()
indexWord8Array :: BA -> Int -> Word8

-- | This isn't strictly Word16 array read. Instead it's two Word8 array
--   reads to avoid endianness issues due to primops doing automatic
--   alignment based on host platform. We expect the byte array to be LE
--   always.
indexWord16Array :: BA -> Int -> Word16
encodeWord16LE# :: Word16# -> (# Word8#, Word8# #)
decodeWord16LE# :: (# Word8#, Word8# #) -> Word16#
setByteArray :: MBA s -> Int -> Int -> Int -> ST s ()
copyMutableByteArray :: MBA s -> Int -> MBA s -> Int -> Int -> ST s ()

-- | Given the maximum size needed and a function to make the contents of a
--   ShortByteString, createAndTrim makes the <a>ShortByteString</a>. The
--   generating function is required to return the actual final size (&lt;=
--   the maximum size) and the result value. The resulting byte array is
--   realloced to this size.
createAndTrim :: Int -> (forall s. MBA s -> ST s (Int, a)) -> (ShortByteString, a)
createAndTrim' :: Int -> (forall s. MBA s -> ST s Int) -> ShortByteString
createAndTrim'' :: Int -> (forall s. MBA s -> MBA s -> ST s (Int, Int)) -> (ShortByteString, ShortByteString)
findIndexOrLength :: (Word16 -> Bool) -> ShortByteString -> Int

-- | Returns the length of the substring matching, not the index. If no
--   match, returns 0.
findFromEndUntil :: (Word16 -> Bool) -> ShortByteString -> Int
assertEven :: ShortByteString -> ShortByteString
errorEmptySBS :: HasCallStack => String -> a
moduleError :: HasCallStack => String -> String -> a
compareByteArraysOff :: BA -> Int -> BA -> Int -> Int -> Int


-- | A compact representation suitable for storing short byte strings in
--   memory.
--   
--   In typical use cases it can be imported alongside
--   <a>Data.ByteString</a>, e.g.
--   
--   <pre>
--   import qualified Data.ByteString       as B
--   import qualified Data.ByteString.Short as B
--            (ShortByteString, toShort, fromShort)
--   </pre>
--   
--   Other <a>ShortByteString</a> operations clash with
--   <a>Data.ByteString</a> or <a>Prelude</a> functions however, so they
--   should be imported <tt>qualified</tt> with a different alias e.g.
--   
--   <pre>
--   import qualified Data.ByteString.Short as B.Short
--   </pre>
module System.OsPath.Data.ByteString.Short

-- | A compact representation of a <a>Word8</a> vector.
--   
--   It has a lower memory overhead than a <a>ByteString</a> and does not
--   contribute to heap fragmentation. It can be converted to or from a
--   <a>ByteString</a> (at the cost of copying the string data). It
--   supports very few other operations.
data () => ShortByteString
SBS :: ByteArray# -> ShortByteString

-- | <i>O(1)</i>. The empty <a>ShortByteString</a>.
empty :: ShortByteString

-- | <i>O(1)</i> Convert a <a>Word8</a> into a <a>ShortByteString</a>
singleton :: Word8 -> ShortByteString

-- | <i>O(n)</i>. Convert a list into a <a>ShortByteString</a>
pack :: [Word8] -> ShortByteString

-- | <i>O(n)</i>. Convert a <a>ShortByteString</a> into a list.
unpack :: ShortByteString -> [Word8]

-- | <i>O(n)</i>. Convert a <a>ShortByteString</a> into a
--   <a>ByteString</a>.
fromShort :: ShortByteString -> ByteString

-- | <i>O(n)</i>. Convert a <a>ByteString</a> into a
--   <a>ShortByteString</a>.
--   
--   This makes a copy, so does not retain the input string.
toShort :: ByteString -> ShortByteString

-- | <i>O(n)</i> Append a byte to the end of a <a>ShortByteString</a>
--   
--   Note: copies the entire byte array
snoc :: ShortByteString -> Word8 -> ShortByteString
infixl 5 `snoc`

-- | <i>O(n)</i> <a>cons</a> is analogous to (:) for lists.
--   
--   Note: copies the entire byte array
cons :: Word8 -> ShortByteString -> ShortByteString
infixr 5 `cons`
append :: ShortByteString -> ShortByteString -> ShortByteString

-- | <i>O(1)</i> Extract the last element of a ShortByteString, which must
--   be finite and non-empty. An exception will be thrown in the case of an
--   empty ShortByteString.
--   
--   This is a partial function, consider using <a>unsnoc</a> instead.
last :: HasCallStack => ShortByteString -> Word8

-- | <i>O(n)</i> Extract the elements after the head of a ShortByteString,
--   which must be non-empty. An exception will be thrown in the case of an
--   empty ShortByteString.
--   
--   This is a partial function, consider using <a>uncons</a> instead.
--   
--   Note: copies the entire byte array
tail :: HasCallStack => ShortByteString -> ShortByteString

-- | <i>O(n)</i> Extract the <a>head</a> and <a>tail</a> of a
--   ShortByteString, returning <a>Nothing</a> if it is empty.
uncons :: ShortByteString -> Maybe (Word8, ShortByteString)
uncons2 :: ShortByteString -> Maybe (Word8, Word8, ShortByteString)

-- | <i>O(1)</i> Extract the first element of a ShortByteString, which must
--   be non-empty. An exception will be thrown in the case of an empty
--   ShortByteString.
--   
--   This is a partial function, consider using <a>uncons</a> instead.
head :: HasCallStack => ShortByteString -> Word8

-- | <i>O(n)</i> Return all the elements of a <a>ShortByteString</a> except
--   the last one. An exception will be thrown in the case of an empty
--   ShortByteString.
--   
--   This is a partial function, consider using <a>unsnoc</a> instead.
--   
--   Note: copies the entire byte array
init :: HasCallStack => ShortByteString -> ShortByteString

-- | <i>O(n)</i> Extract the <a>init</a> and <a>last</a> of a
--   ShortByteString, returning <a>Nothing</a> if it is empty.
unsnoc :: ShortByteString -> Maybe (ShortByteString, Word8)

-- | <i>O(1)</i> Test whether a <a>ShortByteString</a> is empty.
null :: ShortByteString -> Bool

-- | <i>O(1)</i> The length of a <a>ShortByteString</a>.
length :: ShortByteString -> Int

-- | <i>O(n)</i> <a>map</a> <tt>f xs</tt> is the ShortByteString obtained
--   by applying <tt>f</tt> to each element of <tt>xs</tt>.
map :: (Word8 -> Word8) -> ShortByteString -> ShortByteString

-- | <i>O(n)</i> <a>reverse</a> <tt>xs</tt> efficiently returns the
--   elements of <tt>xs</tt> in reverse order.
reverse :: ShortByteString -> ShortByteString

-- | <i>O(n)</i> The <a>intercalate</a> function takes a
--   <a>ShortByteString</a> and a list of <a>ShortByteString</a>s and
--   concatenates the list after interspersing the first argument between
--   each element of the list.
intercalate :: ShortByteString -> [ShortByteString] -> ShortByteString

-- | <a>foldl</a>, applied to a binary operator, a starting value
--   (typically the left-identity of the operator), and a ShortByteString,
--   reduces the ShortByteString using the binary operator, from left to
--   right.
foldl :: (a -> Word8 -> a) -> a -> ShortByteString -> a

-- | <a>foldl'</a> is like <a>foldl</a>, but strict in the accumulator.
foldl' :: (a -> Word8 -> a) -> a -> ShortByteString -> a

-- | <a>foldl1</a> is a variant of <a>foldl</a> that has no starting value
--   argument, and thus must be applied to non-empty
--   <a>ShortByteString</a>s. An exception will be thrown in the case of an
--   empty ShortByteString.
foldl1 :: HasCallStack => (Word8 -> Word8 -> Word8) -> ShortByteString -> Word8

-- | <a>foldl1'</a> is like <a>foldl1</a>, but strict in the accumulator.
--   An exception will be thrown in the case of an empty ShortByteString.
foldl1' :: HasCallStack => (Word8 -> Word8 -> Word8) -> ShortByteString -> Word8

-- | <a>foldr</a>, applied to a binary operator, a starting value
--   (typically the right-identity of the operator), and a ShortByteString,
--   reduces the ShortByteString using the binary operator, from right to
--   left.
foldr :: (Word8 -> a -> a) -> a -> ShortByteString -> a

-- | <a>foldr'</a> is like <a>foldr</a>, but strict in the accumulator.
foldr' :: (Word8 -> a -> a) -> a -> ShortByteString -> a

-- | <a>foldr1</a> is a variant of <a>foldr</a> that has no starting value
--   argument, and thus must be applied to non-empty
--   <a>ShortByteString</a>s An exception will be thrown in the case of an
--   empty ShortByteString.
foldr1 :: HasCallStack => (Word8 -> Word8 -> Word8) -> ShortByteString -> Word8

-- | <a>foldr1'</a> is a variant of <a>foldr1</a>, but is strict in the
--   accumulator.
foldr1' :: HasCallStack => (Word8 -> Word8 -> Word8) -> ShortByteString -> Word8

-- | <i>O(n)</i> Applied to a predicate and a <a>ShortByteString</a>,
--   <a>all</a> determines if all elements of the <a>ShortByteString</a>
--   satisfy the predicate.
all :: (Word8 -> Bool) -> ShortByteString -> Bool

-- | <i>O(n)</i> Applied to a predicate and a <a>ShortByteString</a>,
--   <a>any</a> determines if any element of the <a>ShortByteString</a>
--   satisfies the predicate.
any :: (Word8 -> Bool) -> ShortByteString -> Bool
concat :: [ShortByteString] -> ShortByteString

-- | <i>O(n)</i> <a>replicate</a> <tt>n x</tt> is a ShortByteString of
--   length <tt>n</tt> with <tt>x</tt> the value of every element. The
--   following holds:
--   
--   <pre>
--   replicate w c = unfoldr w (\u -&gt; Just (u,u)) c
--   </pre>
replicate :: Int -> Word8 -> ShortByteString

-- | <i>O(n)</i>, where <i>n</i> is the length of the result. The
--   <a>unfoldr</a> function is analogous to the List 'unfoldr'.
--   <a>unfoldr</a> builds a ShortByteString from a seed value. The
--   function takes the element and returns <a>Nothing</a> if it is done
--   producing the ShortByteString or returns <a>Just</a> <tt>(a,b)</tt>,
--   in which case, <tt>a</tt> is the next byte in the string, and
--   <tt>b</tt> is the seed value for further production.
--   
--   This function is not efficient/safe. It will build a list of
--   <tt>[Word8]</tt> and run the generator until it returns
--   <a>Nothing</a>, otherwise recurse infinitely, then finally create a
--   <a>ShortByteString</a>.
--   
--   If you know the maximum length, consider using <a>unfoldrN</a>.
--   
--   Examples:
--   
--   <pre>
--      unfoldr (\x -&gt; if x &lt;= 5 then Just (x, x + 1) else Nothing) 0
--   == pack [0, 1, 2, 3, 4, 5]
--   </pre>
unfoldr :: (a -> Maybe (Word8, a)) -> a -> ShortByteString

-- | <i>O(n)</i> Like <a>unfoldr</a>, <a>unfoldrN</a> builds a
--   ShortByteString from a seed value. However, the length of the result
--   is limited by the first argument to <a>unfoldrN</a>. This function is
--   more efficient than <a>unfoldr</a> when the maximum length of the
--   result is known.
--   
--   The following equation relates <a>unfoldrN</a> and <a>unfoldr</a>:
--   
--   <pre>
--   fst (unfoldrN n f s) == take n (unfoldr f s)
--   </pre>
unfoldrN :: Int -> (a -> Maybe (Word8, a)) -> a -> (ShortByteString, Maybe a)

-- | <i>O(n)</i> <a>take</a> <tt>n</tt>, applied to a ShortByteString
--   <tt>xs</tt>, returns the prefix of <tt>xs</tt> of length <tt>n</tt>,
--   or <tt>xs</tt> itself if <tt>n &gt; <a>length</a> xs</tt>.
--   
--   Note: copies the entire byte array
take :: Int -> ShortByteString -> ShortByteString

-- | <i>O(n)</i> <tt><a>takeEnd</a> n xs</tt> is equivalent to
--   <tt><a>drop</a> (<a>length</a> xs - n) xs</tt>. Takes <tt>n</tt>
--   elements from end of bytestring.
--   
--   <pre>
--   &gt;&gt;&gt; takeEnd 3 "abcdefg"
--   "efg"
--   
--   &gt;&gt;&gt; takeEnd 0 "abcdefg"
--   ""
--   
--   &gt;&gt;&gt; takeEnd 4 "abc"
--   "abc"
--   </pre>
takeEnd :: Int -> ShortByteString -> ShortByteString

-- | Returns the longest (possibly empty) suffix of elements satisfying the
--   predicate.
--   
--   <tt><a>takeWhileEnd</a> p</tt> is equivalent to <tt><a>reverse</a> .
--   <a>takeWhile</a> p . <a>reverse</a></tt>.
takeWhileEnd :: (Word8 -> Bool) -> ShortByteString -> ShortByteString

-- | Similar to <a>takeWhile</a>, returns the longest (possibly empty)
--   prefix of elements satisfying the predicate.
takeWhile :: (Word8 -> Bool) -> ShortByteString -> ShortByteString

-- | <i>O(n)</i> <a>drop</a> <tt>n</tt> <tt>xs</tt> returns the suffix of
--   <tt>xs</tt> after the first n elements, or <a>empty</a> if <tt>n &gt;
--   <a>length</a> xs</tt>.
--   
--   Note: copies the entire byte array
drop :: Int -> ShortByteString -> ShortByteString

-- | <i>O(n)</i> <tt><a>dropEnd</a> n xs</tt> is equivalent to
--   <tt><a>take</a> (<a>length</a> xs - n) xs</tt>. Drops <tt>n</tt>
--   elements from end of bytestring.
--   
--   <pre>
--   &gt;&gt;&gt; dropEnd 3 "abcdefg"
--   "abcd"
--   
--   &gt;&gt;&gt; dropEnd 0 "abcdefg"
--   "abcdefg"
--   
--   &gt;&gt;&gt; dropEnd 4 "abc"
--   ""
--   </pre>
dropEnd :: Int -> ShortByteString -> ShortByteString

-- | Similar to <a>dropWhile</a>, drops the longest (possibly empty) prefix
--   of elements satisfying the predicate and returns the remainder.
--   
--   Note: copies the entire byte array
dropWhile :: (Word8 -> Bool) -> ShortByteString -> ShortByteString

-- | Similar to <a>dropWhileEnd</a>, drops the longest (possibly empty)
--   suffix of elements satisfying the predicate and returns the remainder.
--   
--   <tt><a>dropWhileEnd</a> p</tt> is equivalent to <tt><a>reverse</a> .
--   <a>dropWhile</a> p . <a>reverse</a></tt>.
dropWhileEnd :: (Word8 -> Bool) -> ShortByteString -> ShortByteString

-- | Returns the longest (possibly empty) suffix of elements which <b>do
--   not</b> satisfy the predicate and the remainder of the string.
--   
--   <a>breakEnd</a> <tt>p</tt> is equivalent to <tt><a>spanEnd</a> (not .
--   p)</tt> and to <tt>(<a>takeWhileEnd</a> (not . p) &amp;&amp;&amp;
--   <a>dropWhileEnd</a> (not . p))</tt>.
breakEnd :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | Similar to <a>break</a>, returns the longest (possibly empty) prefix
--   of elements which <b>do not</b> satisfy the predicate and the
--   remainder of the string.
--   
--   <a>break</a> <tt>p</tt> is equivalent to <tt><a>span</a> (not .
--   p)</tt> and to <tt>(<a>takeWhile</a> (not . p) &amp;&amp;&amp;
--   <a>dropWhile</a> (not . p))</tt>.
break :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | Similar to <a>span</a>, returns the longest (possibly empty) prefix of
--   elements satisfying the predicate and the remainder of the string.
--   
--   <a>span</a> <tt>p</tt> is equivalent to <tt><a>break</a> (not .
--   p)</tt> and to <tt>(<a>takeWhile</a> p &amp;&amp;&amp;
--   <a>dropWhile</a> p)</tt>.
span :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | Returns the longest (possibly empty) suffix of elements satisfying the
--   predicate and the remainder of the string.
--   
--   <a>spanEnd</a> <tt>p</tt> is equivalent to <tt><a>breakEnd</a> (not .
--   p)</tt> and to <tt>(<a>takeWhileEnd</a> p &amp;&amp;&amp;
--   <a>dropWhileEnd</a> p)</tt>.
--   
--   We have
--   
--   <pre>
--   spanEnd (not . isSpace) "x y z" == ("x y ", "z")
--   </pre>
--   
--   and
--   
--   <pre>
--   spanEnd (not . isSpace) sbs
--      ==
--   let (x, y) = span (not . isSpace) (reverse sbs) in (reverse y, reverse x)
--   </pre>
spanEnd :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | <i>O(n)</i> <a>splitAt</a> <tt>n sbs</tt> is equivalent to
--   <tt>(<a>take</a> n sbs, <a>drop</a> n sbs)</tt>.
--   
--   Note: copies the substrings
splitAt :: Int -> ShortByteString -> (ShortByteString, ShortByteString)

-- | <i>O(n)</i> Break a <a>ShortByteString</a> into pieces separated by
--   the byte argument, consuming the delimiter. I.e.
--   
--   <pre>
--   split 10  "a\nb\nd\ne" == ["a","b","d","e"]   -- fromEnum '\n' == 10
--   split 97  "aXaXaXa"    == ["","X","X","X",""] -- fromEnum 'a' == 97
--   split 120 "x"          == ["",""]             -- fromEnum 'x' == 120
--   split undefined ""     == []                  -- and not [""]
--   </pre>
--   
--   and
--   
--   <pre>
--   intercalate [c] . split c == id
--   split == splitWith . (==)
--   </pre>
--   
--   Note: copies the substrings
split :: Word8 -> ShortByteString -> [ShortByteString]

-- | <i>O(n)</i> Splits a <a>ShortByteString</a> into components delimited
--   by separators, where the predicate returns True for a separator
--   element. The resulting components do not contain the separators. Two
--   adjacent separators result in an empty component in the output. eg.
--   
--   <pre>
--   splitWith (==97) "aabbaca" == ["","","bb","c",""] -- fromEnum 'a' == 97
--   splitWith undefined ""     == []                  -- and not [""]
--   </pre>
splitWith :: (Word8 -> Bool) -> ShortByteString -> [ShortByteString]

-- | <i>O(n)</i> The <a>stripSuffix</a> function takes two ShortByteStrings
--   and returns <a>Just</a> the remainder of the second iff the first is
--   its suffix, and otherwise <a>Nothing</a>.
stripSuffix :: ShortByteString -> ShortByteString -> Maybe ShortByteString

-- | <i>O(n)</i> The <a>stripPrefix</a> function takes two ShortByteStrings
--   and returns <a>Just</a> the remainder of the second iff the first is
--   its prefix, and otherwise <a>Nothing</a>.
stripPrefix :: ShortByteString -> ShortByteString -> Maybe ShortByteString

-- | Check whether one string is a substring of another.
isInfixOf :: ShortByteString -> ShortByteString -> Bool

-- | <i>O(n)</i> The <a>isPrefixOf</a> function takes two ShortByteStrings
--   and returns <a>True</a>
isPrefixOf :: ShortByteString -> ShortByteString -> Bool

-- | <i>O(n)</i> The <a>isSuffixOf</a> function takes two ShortByteStrings
--   and returns <a>True</a> iff the first is a suffix of the second.
--   
--   The following holds:
--   
--   <pre>
--   isSuffixOf x y == reverse x `isPrefixOf` reverse y
--   </pre>
isSuffixOf :: ShortByteString -> ShortByteString -> Bool

-- | Break a string on a substring, returning a pair of the part of the
--   string prior to the match, and the rest of the string.
--   
--   The following relationships hold:
--   
--   <pre>
--   break (== c) l == breakSubstring (singleton c) l
--   </pre>
--   
--   For example, to tokenise a string, dropping delimiters:
--   
--   <pre>
--   tokenise x y = h : if null t then [] else tokenise x (drop (length x) t)
--       where (h,t) = breakSubstring x y
--   </pre>
--   
--   To skip to the first occurrence of a string:
--   
--   <pre>
--   snd (breakSubstring x y)
--   </pre>
--   
--   To take the parts of a string before a delimiter:
--   
--   <pre>
--   fst (breakSubstring x y)
--   </pre>
--   
--   Note that calling `breakSubstring x` does some preprocessing work, so
--   you should avoid unnecessarily duplicating breakSubstring calls with
--   the same pattern.
breakSubstring :: ShortByteString -> ShortByteString -> (ShortByteString, ShortByteString)

-- | <i>O(n)</i> <a>elem</a> is the <a>ShortByteString</a> membership
--   predicate.
elem :: Word8 -> ShortByteString -> Bool

-- | <i>O(n)</i> The <a>find</a> function takes a predicate and a
--   ShortByteString, and returns the first element in matching the
--   predicate, or <a>Nothing</a> if there is no such element.
--   
--   <pre>
--   find f p = case findIndex f p of Just n -&gt; Just (p ! n) ; _ -&gt; Nothing
--   </pre>
find :: (Word8 -> Bool) -> ShortByteString -> Maybe Word8

-- | <i>O(n)</i> <a>filter</a>, applied to a predicate and a
--   ShortByteString, returns a ShortByteString containing those characters
--   that satisfy the predicate.
filter :: (Word8 -> Bool) -> ShortByteString -> ShortByteString

-- | <i>O(n)</i> The <a>partition</a> function takes a predicate a
--   ShortByteString and returns the pair of ShortByteStrings with elements
--   which do and do not satisfy the predicate, respectively; i.e.,
--   
--   <pre>
--   partition p bs == (filter p sbs, filter (not . p) sbs)
--   </pre>
partition :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | <i>O(1)</i> <a>ShortByteString</a> index (subscript) operator,
--   starting from 0.
--   
--   This is a partial function, consider using <a>indexMaybe</a> instead.
index :: HasCallStack => ShortByteString -> Int -> Word8

-- | <i>O(1)</i> <a>ShortByteString</a> index, starting from 0, that
--   returns <a>Just</a> if:
--   
--   <pre>
--   0 &lt;= n &lt; length bs
--   </pre>
indexMaybe :: ShortByteString -> Int -> Maybe Word8

-- | <i>O(1)</i> <a>ShortByteString</a> index, starting from 0, that
--   returns <a>Just</a> if:
--   
--   <pre>
--   0 &lt;= n &lt; length bs
--   </pre>
(!?) :: ShortByteString -> Int -> Maybe Word8

-- | <i>O(n)</i> The <a>elemIndex</a> function returns the index of the
--   first element in the given <a>ShortByteString</a> which is equal to
--   the query element, or <a>Nothing</a> if there is no such element.
elemIndex :: Word8 -> ShortByteString -> Maybe Int

-- | <i>O(n)</i> The <a>elemIndices</a> function extends <a>elemIndex</a>,
--   by returning the indices of all elements equal to the query element,
--   in ascending order.
elemIndices :: Word8 -> ShortByteString -> [Int]

-- | count returns the number of times its argument appears in the
--   ShortByteString
count :: Word8 -> ShortByteString -> Int

-- | <i>O(n)</i> The <a>findIndex</a> function takes a predicate and a
--   <a>ShortByteString</a> and returns the index of the first element in
--   the ShortByteString satisfying the predicate.
findIndex :: (Word8 -> Bool) -> ShortByteString -> Maybe Int

-- | <i>O(n)</i> The <a>findIndices</a> function extends <a>findIndex</a>,
--   by returning the indices of all elements satisfying the predicate, in
--   ascending order.
findIndices :: (Word8 -> Bool) -> ShortByteString -> [Int]

-- | <i>O(n).</i> Construct a new <tt>ShortByteString</tt> from a
--   <tt>CString</tt>. The resulting <tt>ShortByteString</tt> is an
--   immutable copy of the original <tt>CString</tt>, and is managed on the
--   Haskell heap. The original <tt>CString</tt> must be null terminated.
packCString :: CString -> IO ShortByteString

-- | <i>O(n).</i> Construct a new <tt>ShortByteString</tt> from a
--   <tt>CStringLen</tt>. The resulting <tt>ShortByteString</tt> is an
--   immutable copy of the original <tt>CStringLen</tt>. The
--   <tt>ShortByteString</tt> is a normal Haskell value and will be managed
--   on the Haskell heap.
packCStringLen :: CStringLen -> IO ShortByteString

-- | <i>O(n) construction.</i> Use a <tt>ShortByteString</tt> with a
--   function requiring a null-terminated <tt>CString</tt>. The
--   <tt>CString</tt> is a copy and will be freed automatically; it must
--   not be stored or used after the subcomputation finishes.
useAsCString :: ShortByteString -> (CString -> IO a) -> IO a

-- | <i>O(n) construction.</i> Use a <tt>ShortByteString</tt> with a
--   function requiring a <tt>CStringLen</tt>. As for <tt>useAsCString</tt>
--   this function makes a copy of the original <tt>ShortByteString</tt>.
--   It must not be stored or used after the subcomputation finishes.
useAsCStringLen :: ShortByteString -> (CStringLen -> IO a) -> IO a


-- | ShortByteStrings encoded as UTF16-LE, suitable for windows FFI calls.
--   
--   Word16s are *always* in BE encoding (both input and output), so e.g.
--   <a>pack</a> takes a list of BE encoded <tt>[Word16]</tt> and produces
--   a UTF16-LE encoded ShortByteString.
--   
--   Likewise, <a>unpack</a> takes a UTF16-LE encoded ShortByteString and
--   produces a list of BE encoded <tt>[Word16]</tt>.
--   
--   Indices and lengths are always in respect to Word16, not Word8.
--   
--   All functions will error out if the input string is not a valid UTF16
--   stream (uneven number of bytes). So use this module with caution.
module System.OsPath.Data.ByteString.Short.Word16

-- | A compact representation of a <a>Word8</a> vector.
--   
--   It has a lower memory overhead than a <a>ByteString</a> and does not
--   contribute to heap fragmentation. It can be converted to or from a
--   <a>ByteString</a> (at the cost of copying the string data). It
--   supports very few other operations.
data () => ShortByteString
SBS :: ByteArray# -> ShortByteString

-- | <i>O(1)</i>. The empty <a>ShortByteString</a>.
empty :: ShortByteString

-- | <i>O(1)</i> Convert a <a>Word16</a> into a <a>ShortByteString</a>
singleton :: Word16 -> ShortByteString

-- | <i>O(n)</i>. Convert a list into a <a>ShortByteString</a>
pack :: [Word16] -> ShortByteString

-- | <i>O(n)</i>. Convert a <a>ShortByteString</a> into a list.
unpack :: ShortByteString -> [Word16]

-- | <i>O(n)</i>. Convert a <a>ShortByteString</a> into a
--   <a>ByteString</a>.
fromShort :: ShortByteString -> ByteString

-- | <i>O(n)</i>. Convert a <a>ByteString</a> into a
--   <a>ShortByteString</a>.
--   
--   This makes a copy, so does not retain the input string.
toShort :: ByteString -> ShortByteString

-- | <i>O(n)</i> Append a Word16 to the end of a <a>ShortByteString</a>
--   
--   Note: copies the entire byte array
snoc :: ShortByteString -> Word16 -> ShortByteString
infixl 5 `snoc`

-- | <i>O(n)</i> <a>cons</a> is analogous to (:) for lists.
--   
--   Note: copies the entire byte array
cons :: Word16 -> ShortByteString -> ShortByteString
infixr 5 `cons`
append :: ShortByteString -> ShortByteString -> ShortByteString

-- | <i>O(1)</i> Extract the last element of a ShortByteString, which must
--   be finite and at least one Word16. An exception will be thrown in the
--   case of an empty ShortByteString.
last :: HasCallStack => ShortByteString -> Word16

-- | <i>O(n)</i> Extract the elements after the head of a ShortByteString,
--   which must at least one Word16. An exception will be thrown in the
--   case of an empty ShortByteString.
--   
--   Note: copies the entire byte array
tail :: HasCallStack => ShortByteString -> ShortByteString

-- | <i>O(n)</i> Extract the head and tail of a ByteString, returning
--   Nothing if it is empty.
uncons :: ShortByteString -> Maybe (Word16, ShortByteString)

-- | <i>O(n)</i> Extract first two elements and the rest of a ByteString,
--   returning Nothing if it is shorter than two elements.
uncons2 :: ShortByteString -> Maybe (Word16, Word16, ShortByteString)

-- | <i>O(1)</i> Extract the first element of a ShortByteString, which must
--   be at least one Word16. An exception will be thrown in the case of an
--   empty ShortByteString.
head :: HasCallStack => ShortByteString -> Word16

-- | <i>O(n)</i> Return all the elements of a <a>ShortByteString</a> except
--   the last one. An exception will be thrown in the case of an empty
--   ShortByteString.
--   
--   Note: copies the entire byte array
init :: HasCallStack => ShortByteString -> ShortByteString

-- | <i>O(n)</i> Extract the <a>init</a> and <a>last</a> of a ByteString,
--   returning Nothing if it is empty.
unsnoc :: ShortByteString -> Maybe (ShortByteString, Word16)

-- | <i>O(1)</i> Test whether a <a>ShortByteString</a> is empty.
null :: ShortByteString -> Bool

-- | <i>O(1)</i> The length of a <a>ShortByteString</a>.
length :: ShortByteString -> Int

-- | This is like <a>length</a>, but the number of <a>Word16</a>, not
--   <a>Word8</a>.
numWord16 :: ShortByteString -> Int

-- | <i>O(n)</i> <a>map</a> <tt>f xs</tt> is the ShortByteString obtained
--   by applying <tt>f</tt> to each element of <tt>xs</tt>.
map :: (Word16 -> Word16) -> ShortByteString -> ShortByteString

-- | <i>O(n)</i> <a>reverse</a> <tt>xs</tt> efficiently returns the
--   elements of <tt>xs</tt> in reverse order.
reverse :: ShortByteString -> ShortByteString

-- | <i>O(n)</i> The <a>intercalate</a> function takes a
--   <a>ShortByteString</a> and a list of <a>ShortByteString</a>s and
--   concatenates the list after interspersing the first argument between
--   each element of the list.
intercalate :: ShortByteString -> [ShortByteString] -> ShortByteString

-- | <a>foldl</a>, applied to a binary operator, a starting value
--   (typically the left-identity of the operator), and a ShortByteString,
--   reduces the ShortByteString using the binary operator, from left to
--   right.
foldl :: (a -> Word16 -> a) -> a -> ShortByteString -> a

-- | <a>foldl'</a> is like <a>foldl</a>, but strict in the accumulator.
foldl' :: (a -> Word16 -> a) -> a -> ShortByteString -> a

-- | <a>foldl1</a> is a variant of <a>foldl</a> that has no starting value
--   argument, and thus must be applied to non-empty
--   <a>ShortByteString</a>s. An exception will be thrown in the case of an
--   empty ShortByteString.
foldl1 :: HasCallStack => (Word16 -> Word16 -> Word16) -> ShortByteString -> Word16

-- | <a>foldl1'</a> is like <a>foldl1</a>, but strict in the accumulator.
--   An exception will be thrown in the case of an empty ShortByteString.
foldl1' :: HasCallStack => (Word16 -> Word16 -> Word16) -> ShortByteString -> Word16

-- | <a>foldr</a>, applied to a binary operator, a starting value
--   (typically the right-identity of the operator), and a ShortByteString,
--   reduces the ShortByteString using the binary operator, from right to
--   left.
foldr :: (Word16 -> a -> a) -> a -> ShortByteString -> a

-- | <a>foldr'</a> is like <a>foldr</a>, but strict in the accumulator.
foldr' :: (Word16 -> a -> a) -> a -> ShortByteString -> a

-- | <a>foldr1</a> is a variant of <a>foldr</a> that has no starting value
--   argument, and thus must be applied to non-empty
--   <a>ShortByteString</a>s An exception will be thrown in the case of an
--   empty ShortByteString.
foldr1 :: HasCallStack => (Word16 -> Word16 -> Word16) -> ShortByteString -> Word16

-- | <a>foldr1'</a> is a variant of <a>foldr1</a>, but is strict in the
--   accumulator.
foldr1' :: HasCallStack => (Word16 -> Word16 -> Word16) -> ShortByteString -> Word16

-- | <i>O(n)</i> Applied to a predicate and a <a>ShortByteString</a>,
--   <a>all</a> determines if all elements of the <a>ShortByteString</a>
--   satisfy the predicate.
all :: (Word16 -> Bool) -> ShortByteString -> Bool

-- | <i>O(n)</i> Applied to a predicate and a ByteString, <a>any</a>
--   determines if any element of the <tt>ByteString</tt> satisfies the
--   predicate.
any :: (Word16 -> Bool) -> ShortByteString -> Bool
concat :: [ShortByteString] -> ShortByteString

-- | <i>O(n)</i> <a>replicate</a> <tt>n x</tt> is a ByteString of length
--   <tt>n</tt> with <tt>x</tt> the value of every element. The following
--   holds:
--   
--   <pre>
--   replicate w c = unfoldr w (\u -&gt; Just (u,u)) c
--   </pre>
replicate :: Int -> Word16 -> ShortByteString

-- | <i>O(n)</i>, where <i>n</i> is the length of the result. The
--   <a>unfoldr</a> function is analogous to the List 'unfoldr'.
--   <a>unfoldr</a> builds a ShortByteString from a seed value. The
--   function takes the element and returns <a>Nothing</a> if it is done
--   producing the ShortByteString or returns <a>Just</a> <tt>(a,b)</tt>,
--   in which case, <tt>a</tt> is the next byte in the string, and
--   <tt>b</tt> is the seed value for further production.
--   
--   This function is not efficient/safe. It will build a list of
--   <tt>[Word16]</tt> and run the generator until it returns
--   <a>Nothing</a>, otherwise recurse infinitely, then finally create a
--   <a>ShortByteString</a>.
--   
--   Examples:
--   
--   <pre>
--      unfoldr (\x -&gt; if x &lt;= 5 then Just (x, x + 1) else Nothing) 0
--   == pack [0, 1, 2, 3, 4, 5]
--   </pre>
unfoldr :: (a -> Maybe (Word16, a)) -> a -> ShortByteString

-- | <i>O(n)</i> Like <a>unfoldr</a>, <a>unfoldrN</a> builds a
--   ShortByteString from a seed value. However, the length of the result
--   is limited by the first argument to <a>unfoldrN</a>. This function is
--   more efficient than <a>unfoldr</a> when the maximum length of the
--   result is known.
--   
--   The following equation relates <a>unfoldrN</a> and <a>unfoldr</a>:
--   
--   <pre>
--   fst (unfoldrN n f s) == take n (unfoldr f s)
--   </pre>
unfoldrN :: forall a. Int -> (a -> Maybe (Word16, a)) -> a -> (ShortByteString, Maybe a)

-- | <i>O(n)</i> <a>take</a> <tt>n</tt>, applied to a ShortByteString
--   <tt>xs</tt>, returns the prefix of <tt>xs</tt> of length <tt>n</tt>,
--   or <tt>xs</tt> itself if <tt>n &gt; <a>length</a> xs</tt>.
--   
--   Note: copies the entire byte array
take :: Int -> ShortByteString -> ShortByteString

-- | <i>O(1)</i> <tt><a>takeEnd</a> n xs</tt> is equivalent to
--   <tt><a>drop</a> (<a>length</a> xs - n) xs</tt>. Takes <tt>n</tt>
--   elements from end of bytestring.
--   
--   <pre>
--   &gt;&gt;&gt; takeEnd 3 "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   "e\NULf\NULg\NUL"
--   
--   &gt;&gt;&gt; takeEnd 0 "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   ""
--   
--   &gt;&gt;&gt; takeEnd 4 "a\NULb\NULc\NUL"
--   "a\NULb\NULc\NUL"
--   </pre>
takeEnd :: Int -> ShortByteString -> ShortByteString

-- | Returns the longest (possibly empty) suffix of elements satisfying the
--   predicate.
--   
--   <tt><a>takeWhileEnd</a> p</tt> is equivalent to <tt><a>reverse</a> .
--   <a>takeWhile</a> p . <a>reverse</a></tt>.
takeWhileEnd :: (Word16 -> Bool) -> ShortByteString -> ShortByteString

-- | Similar to <a>takeWhile</a>, returns the longest (possibly empty)
--   prefix of elements satisfying the predicate.
takeWhile :: (Word16 -> Bool) -> ShortByteString -> ShortByteString

-- | <i>O(n)</i> <a>drop</a> <tt>n</tt> <tt>xs</tt> returns the suffix of
--   <tt>xs</tt> after the first n elements, or <tt>[]</tt> if <tt>n &gt;
--   <a>length</a> xs</tt>.
--   
--   Note: copies the entire byte array
drop :: Int -> ShortByteString -> ShortByteString

-- | <i>O(1)</i> <tt><a>dropEnd</a> n xs</tt> is equivalent to
--   <tt><a>take</a> (<a>length</a> xs - n) xs</tt>. Drops <tt>n</tt>
--   elements from end of bytestring.
--   
--   <pre>
--   &gt;&gt;&gt; dropEnd 3 "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   "a\NULb\NULc\NULd\NUL"
--   
--   &gt;&gt;&gt; dropEnd 0 "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   
--   &gt;&gt;&gt; dropEnd 4 "a\NULb\NULc\NUL"
--   ""
--   </pre>
dropEnd :: Int -> ShortByteString -> ShortByteString

-- | Similar to <a>dropWhile</a>, drops the longest (possibly empty) prefix
--   of elements satisfying the predicate and returns the remainder.
--   
--   Note: copies the entire byte array
dropWhile :: (Word16 -> Bool) -> ShortByteString -> ShortByteString

-- | Similar to <a>dropWhileEnd</a>, drops the longest (possibly empty)
--   suffix of elements satisfying the predicate and returns the remainder.
--   
--   <tt><a>dropWhileEnd</a> p</tt> is equivalent to <tt><a>reverse</a> .
--   <a>dropWhile</a> p . <a>reverse</a></tt>.
dropWhileEnd :: (Word16 -> Bool) -> ShortByteString -> ShortByteString

-- | Returns the longest (possibly empty) suffix of elements which <b>do
--   not</b> satisfy the predicate and the remainder of the string.
--   
--   <a>breakEnd</a> <tt>p</tt> is equivalent to <tt><a>spanEnd</a> (not .
--   p)</tt> and to <tt>(<a>takeWhileEnd</a> (not . p) &amp;&amp;&amp;
--   <a>dropWhileEnd</a> (not . p))</tt>.
breakEnd :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | Similar to <a>break</a>, returns the longest (possibly empty) prefix
--   of elements which <b>do not</b> satisfy the predicate and the
--   remainder of the string.
--   
--   <a>break</a> <tt>p</tt> is equivalent to <tt><a>span</a> (not .
--   p)</tt> and to <tt>(<a>takeWhile</a> (not . p) &amp;&amp;&amp;
--   <a>dropWhile</a> (not . p))</tt>.
break :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | Similar to <a>span</a>, returns the longest (possibly empty) prefix of
--   elements satisfying the predicate and the remainder of the string.
--   
--   <a>span</a> <tt>p</tt> is equivalent to <tt><a>break</a> (not .
--   p)</tt> and to <tt>(<a>takeWhile</a> p &amp;&amp;&amp;
--   <a>dropWhile</a> p)</tt>.
span :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | Returns the longest (possibly empty) suffix of elements satisfying the
--   predicate and the remainder of the string.
--   
--   <a>spanEnd</a> <tt>p</tt> is equivalent to <tt><a>breakEnd</a> (not .
--   p)</tt> and to <tt>(<a>takeWhileEnd</a> p &amp;&amp;&amp;
--   <a>dropWhileEnd</a> p)</tt>.
--   
--   We have
--   
--   <pre>
--   spanEnd (not . isSpace) "x y z" == ("x y ", "z")
--   </pre>
--   
--   and
--   
--   <pre>
--   spanEnd (not . isSpace) ps
--      ==
--   let (x, y) = span (not . isSpace) (reverse ps) in (reverse y, reverse x)
--   </pre>
spanEnd :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | <i>O(n)</i> <a>splitAt</a> <tt>n xs</tt> is equivalent to
--   <tt>(<a>take</a> n xs, <a>drop</a> n xs)</tt>.
--   
--   Note: copies the substrings
splitAt :: Int -> ShortByteString -> (ShortByteString, ShortByteString)

-- | <i>O(n)</i> Break a <a>ShortByteString</a> into pieces separated by
--   the byte argument, consuming the delimiter. I.e.
--   
--   <pre>
--   split 10  "a\nb\nd\ne" == ["a","b","d","e"]   -- fromEnum '\n' == 10
--   split 97  "aXaXaXa"    == ["","X","X","X",""] -- fromEnum 'a' == 97
--   split 120 "x"          == ["",""]             -- fromEnum 'x' == 120
--   split undefined ""     == []                  -- and not [""]
--   </pre>
--   
--   and
--   
--   <pre>
--   intercalate [c] . split c == id
--   split == splitWith . (==)
--   </pre>
--   
--   Note: copies the substrings
split :: Word16 -> ShortByteString -> [ShortByteString]

-- | <i>O(n)</i> Splits a <a>ShortByteString</a> into components delimited
--   by separators, where the predicate returns True for a separator
--   element. The resulting components do not contain the separators. Two
--   adjacent separators result in an empty component in the output. eg.
--   
--   <pre>
--   splitWith (==97) "aabbaca" == ["","","bb","c",""] -- fromEnum 'a' == 97
--   splitWith undefined ""     == []                  -- and not [""]
--   </pre>
splitWith :: (Word16 -> Bool) -> ShortByteString -> [ShortByteString]

-- | <i>O(n)</i> The <a>stripSuffix</a> function takes two ShortByteStrings
--   and returns <a>Just</a> the remainder of the second iff the first is
--   its suffix, and otherwise <a>Nothing</a>.
stripSuffix :: ShortByteString -> ShortByteString -> Maybe ShortByteString

-- | <i>O(n)</i> The <a>stripPrefix</a> function takes two ShortByteStrings
--   and returns <a>Just</a> the remainder of the second iff the first is
--   its prefix, and otherwise <a>Nothing</a>.
stripPrefix :: ShortByteString -> ShortByteString -> Maybe ShortByteString

-- | Check whether one string is a substring of another.
isInfixOf :: ShortByteString -> ShortByteString -> Bool

-- | <i>O(n)</i> The <a>isPrefixOf</a> function takes two ShortByteStrings
--   and returns <a>True</a>
isPrefixOf :: ShortByteString -> ShortByteString -> Bool

-- | <i>O(n)</i> The <a>isSuffixOf</a> function takes two ShortByteStrings
--   and returns <a>True</a> iff the first is a suffix of the second.
--   
--   The following holds:
--   
--   <pre>
--   isSuffixOf x y == reverse x `isPrefixOf` reverse y
--   </pre>
isSuffixOf :: ShortByteString -> ShortByteString -> Bool
breakSubstring :: ShortByteString -> ShortByteString -> (ShortByteString, ShortByteString)

-- | <i>O(n)</i> <a>elem</a> is the <a>ShortByteString</a> membership
--   predicate.
elem :: Word16 -> ShortByteString -> Bool

-- | <i>O(n)</i> The <a>find</a> function takes a predicate and a
--   ByteString, and returns the first element in matching the predicate,
--   or <a>Nothing</a> if there is no such element.
--   
--   <pre>
--   find f p = case findIndex f p of Just n -&gt; Just (p ! n) ; _ -&gt; Nothing
--   </pre>
find :: (Word16 -> Bool) -> ShortByteString -> Maybe Word16

-- | <i>O(n)</i> <a>filter</a>, applied to a predicate and a ByteString,
--   returns a ByteString containing those characters that satisfy the
--   predicate.
filter :: (Word16 -> Bool) -> ShortByteString -> ShortByteString

-- | <i>O(n)</i> The <a>partition</a> function takes a predicate a
--   ByteString and returns the pair of ByteStrings with elements which do
--   and do not satisfy the predicate, respectively; i.e.,
--   
--   <pre>
--   partition p bs == (filter p xs, filter (not . p) xs)
--   </pre>
partition :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString)

-- | <i>O(1)</i> <a>ShortByteString</a> index (subscript) operator,
--   starting from 0.
index :: HasCallStack => ShortByteString -> Int -> Word16

-- | <i>O(1)</i> <a>ShortByteString</a> index, starting from 0, that
--   returns <a>Just</a> if:
--   
--   <pre>
--   0 &lt;= n &lt; length bs
--   </pre>
indexMaybe :: ShortByteString -> Int -> Maybe Word16

-- | <i>O(1)</i> <a>ShortByteString</a> index, starting from 0, that
--   returns <a>Just</a> if:
--   
--   <pre>
--   0 &lt;= n &lt; length bs
--   </pre>
(!?) :: ShortByteString -> Int -> Maybe Word16

-- | <i>O(n)</i> The <a>elemIndex</a> function returns the index of the
--   first element in the given <a>ShortByteString</a> which is equal to
--   the query element, or <a>Nothing</a> if there is no such element.
elemIndex :: Word16 -> ShortByteString -> Maybe Int

-- | <i>O(n)</i> The <a>elemIndices</a> function extends <a>elemIndex</a>,
--   by returning the indices of all elements equal to the query element,
--   in ascending order.
elemIndices :: Word16 -> ShortByteString -> [Int]

-- | count returns the number of times its argument appears in the
--   ShortByteString
count :: Word16 -> ShortByteString -> Int

-- | <i>O(n)</i> The <a>findIndex</a> function takes a predicate and a
--   <a>ShortByteString</a> and returns the index of the first element in
--   the ByteString satisfying the predicate.
findIndex :: (Word16 -> Bool) -> ShortByteString -> Maybe Int

-- | <i>O(n)</i> The <a>findIndices</a> function extends <a>findIndex</a>,
--   by returning the indices of all elements satisfying the predicate, in
--   ascending order.
findIndices :: (Word16 -> Bool) -> ShortByteString -> [Int]

-- | <i>O(n).</i> Construct a new <tt>ShortByteString</tt> from a
--   <tt>CWString</tt>. The resulting <tt>ShortByteString</tt> is an
--   immutable copy of the original <tt>CWString</tt>, and is managed on
--   the Haskell heap. The original <tt>CWString</tt> must be null
--   terminated.
packCWString :: Ptr Word16 -> IO ShortByteString

-- | <i>O(n).</i> Construct a new <tt>ShortByteString</tt> from a
--   <tt>CWStringLen</tt>. The resulting <tt>ShortByteString</tt> is an
--   immutable copy of the original <tt>CWStringLen</tt>. The
--   <tt>ShortByteString</tt> is a normal Haskell value and will be managed
--   on the Haskell heap.
packCWStringLen :: (Ptr Word16, Int) -> IO ShortByteString

-- | <i>O(n) construction.</i> Use a <tt>ShortByteString</tt> with a
--   function requiring a <tt>CWStringLen</tt>. As for
--   <tt>useAsCWString</tt> this function makes a copy of the original
--   <tt>ShortByteString</tt>. It must not be stored or used after the
--   subcomputation finishes.
newCWString :: ShortByteString -> IO (Ptr Word16)

-- | <i>O(n) construction.</i> Use a <tt>ShortByteString</tt> with a
--   function requiring a null-terminated <tt>CWString</tt>. The
--   <tt>CWString</tt> is a copy and will be freed automatically; it must
--   not be stored or used after the subcomputation finishes.
useAsCWString :: ShortByteString -> (Ptr Word16 -> IO a) -> IO a

-- | <i>O(n) construction.</i> Use a <tt>ShortByteString</tt> with a
--   function requiring a <tt>CWStringLen</tt>. As for
--   <tt>useAsCWString</tt> this function makes a copy of the original
--   <tt>ShortByteString</tt>. It must not be stored or used after the
--   subcomputation finishes.
useAsCWStringLen :: ShortByteString -> ((Ptr Word16, Int) -> IO a) -> IO a

module System.OsPath.Encoding.Internal
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
cWcharsToChars_UCS2 :: [Word16] -> [Char]
cWcharsToChars :: [Word16] -> [Char]
charsToCWchars :: [Char] -> [Word16]
withFilePathWin :: FilePath -> (Int -> Ptr Word16 -> IO a) -> IO a
peekFilePathWin :: (Ptr Word16, Int) -> IO FilePath
withFilePathPosix :: FilePath -> (CStringLen -> IO a) -> IO a
peekFilePathPosix :: CStringLen -> IO FilePath

-- | Decode with the given <a>TextEncoding</a>.
decodeWithTE :: TextEncoding -> ShortByteString -> Either EncodingException String

-- | Encode with the given <a>TextEncoding</a>.
encodeWithTE :: TextEncoding -> String -> Either EncodingException ShortByteString

-- | This mimics the filepath decoder base uses on unix, 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 unix, 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 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

-- | 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
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
wNUL :: Word16
instance GHC.Classes.Eq System.OsPath.Encoding.Internal.EncodingException
instance GHC.Show.Show System.OsPath.Encoding.Internal.EncodingException
instance GHC.Exception.Type.Exception System.OsPath.Encoding.Internal.EncodingException
instance Control.DeepSeq.NFData System.OsPath.Encoding.Internal.EncodingException

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 dencoder base uses on unix, 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, 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
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   </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")
--   </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)
--   </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


-- | 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
--   splitExtensions "file.tar.gz" == ("file",".tar.gz")
--   </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")
--   </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)
--   </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.OsString.Internal.Types

-- | Commonly used windows string as wide character bytes.
newtype WindowsString
WindowsString :: ShortByteString -> WindowsString
[getWindowsString] :: WindowsString -> ShortByteString

-- | Just a short bidirectional synonym for <a>WindowsString</a>
--   constructor.
pattern WS :: ShortByteString -> WindowsString
unWS :: WindowsString -> ShortByteString

-- | Commonly used Posix string as uninterpreted <tt>char[]</tt> array.
newtype PosixString
PosixString :: ShortByteString -> PosixString
[getPosixString] :: PosixString -> ShortByteString
unPS :: PosixString -> ShortByteString

-- | Just a short bidirectional synonym for <a>PosixString</a> constructor.
pattern PS :: ShortByteString -> PosixString
type PlatformString = PosixString
newtype WindowsChar
WindowsChar :: Word16 -> WindowsChar
[getWindowsChar] :: WindowsChar -> Word16
unWW :: WindowsChar -> Word16

-- | Just a short bidirectional synonym for <a>WindowsChar</a> constructor.
pattern WW :: Word16 -> WindowsChar
newtype PosixChar
PosixChar :: Word8 -> PosixChar
[getPosixChar] :: PosixChar -> Word8
unPW :: PosixChar -> Word8

-- | Just a short bidirectional synonym for <a>PosixChar</a> constructor.
pattern PW :: Word8 -> PosixChar
type PlatformChar = 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.
newtype OsString
OsString :: PlatformString -> OsString
[getOsString] :: OsString -> PlatformString

-- | Newtype representing a code unit.
--   
--   On Windows, this is restricted to two-octet codepoints <a>Word16</a>,
--   on POSIX one-octet (<a>Word8</a>).
newtype OsChar
OsChar :: PlatformChar -> OsChar
[getOsChar] :: OsChar -> PlatformChar
instance Control.DeepSeq.NFData System.OsString.Internal.Types.WindowsString
instance GHC.Generics.Generic System.OsString.Internal.Types.WindowsString
instance GHC.Base.Monoid System.OsString.Internal.Types.WindowsString
instance GHC.Base.Semigroup System.OsString.Internal.Types.WindowsString
instance GHC.Classes.Ord System.OsString.Internal.Types.WindowsString
instance GHC.Classes.Eq System.OsString.Internal.Types.WindowsString
instance Control.DeepSeq.NFData System.OsString.Internal.Types.PosixString
instance GHC.Generics.Generic System.OsString.Internal.Types.PosixString
instance GHC.Base.Monoid System.OsString.Internal.Types.PosixString
instance GHC.Base.Semigroup System.OsString.Internal.Types.PosixString
instance GHC.Classes.Ord System.OsString.Internal.Types.PosixString
instance GHC.Classes.Eq System.OsString.Internal.Types.PosixString
instance Control.DeepSeq.NFData System.OsString.Internal.Types.WindowsChar
instance GHC.Generics.Generic System.OsString.Internal.Types.WindowsChar
instance GHC.Classes.Ord System.OsString.Internal.Types.WindowsChar
instance GHC.Classes.Eq System.OsString.Internal.Types.WindowsChar
instance Control.DeepSeq.NFData System.OsString.Internal.Types.PosixChar
instance GHC.Generics.Generic System.OsString.Internal.Types.PosixChar
instance GHC.Classes.Ord System.OsString.Internal.Types.PosixChar
instance GHC.Classes.Eq System.OsString.Internal.Types.PosixChar
instance Control.DeepSeq.NFData System.OsString.Internal.Types.OsString
instance GHC.Generics.Generic System.OsString.Internal.Types.OsString
instance Control.DeepSeq.NFData System.OsString.Internal.Types.OsChar
instance GHC.Generics.Generic System.OsString.Internal.Types.OsChar
instance GHC.Show.Show System.OsString.Internal.Types.OsChar
instance GHC.Classes.Eq System.OsString.Internal.Types.OsChar
instance GHC.Classes.Ord System.OsString.Internal.Types.OsChar
instance GHC.Show.Show System.OsString.Internal.Types.OsString
instance GHC.Classes.Eq System.OsString.Internal.Types.OsString
instance GHC.Classes.Ord System.OsString.Internal.Types.OsString
instance GHC.Base.Monoid System.OsString.Internal.Types.OsString
instance GHC.Base.Semigroup System.OsString.Internal.Types.OsString
instance Language.Haskell.TH.Syntax.Lift System.OsString.Internal.Types.OsString
instance GHC.Show.Show System.OsString.Internal.Types.PosixChar
instance GHC.Show.Show System.OsString.Internal.Types.WindowsChar
instance GHC.Show.Show System.OsString.Internal.Types.PosixString
instance Language.Haskell.TH.Syntax.Lift System.OsString.Internal.Types.PosixString
instance GHC.Show.Show System.OsString.Internal.Types.WindowsString
instance Language.Haskell.TH.Syntax.Lift System.OsString.Internal.Types.WindowsString

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.OsString.Posix

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

-- | Partial unicode friendly encoding.
--   
--   This encodes as UTF8 (strictly), which is a good guess.
--   
--   Throws an <a>EncodingException</a> if encoding fails.
encodeUtf :: MonadThrow m => String -> m PosixString

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

-- | This mimics the behavior of the base library when doing filesystem
--   operations, which uses shady PEP 383 style encoding (based on the
--   current locale, but PEP 383 only works properly on UTF-8 encodings, so
--   good luck).
--   
--   Looking up the locale requires IO. If you're not worried about calls
--   to <tt>setFileSystemEncoding</tt>, then <a>unsafePerformIO</a> may be
--   feasible (make sure to deeply evaluate the result to catch
--   exceptions).
encodeFS :: String -> IO PosixString

-- | Constructs a platform string from a ByteString.
--   
--   This is a no-op.
fromBytes :: MonadThrow m => ByteString -> m PosixString

-- | QuasiQuote a <a>PosixString</a>. This accepts Unicode characters and
--   encodes as UTF-8 on unix.
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

-- | This mimics the behavior of the base library when doing filesystem
--   operations, which uses shady PEP 383 style encoding (based on the
--   current locale, but PEP 383 only works properly on UTF-8 encodings, so
--   good luck).
--   
--   Looking up the locale requires IO. If you're not worried about calls
--   to <tt>setFileSystemEncoding</tt>, then <a>unsafePerformIO</a> may be
--   feasible (make sure to deeply evaluate the result to catch
--   exceptions).
decodeFS :: PosixString -> 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

module System.OsString.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 a <a>EncodingException</a> if encoding fails.
encodeUtf :: MonadThrow m => String -> m OsString

-- | Encode an <a>OsString</a> given the platform specific encodings.
encodeWith :: TextEncoding -> TextEncoding -> String -> Either EncodingException OsString

-- | 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 :: String -> IO OsString

-- | 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.
--   Note that filenames on unix are encoding agnostic char arrays.
--   
--   Throws a <a>EncodingException</a> if decoding fails.
decodeUtf :: MonadThrow m => OsString -> m String

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

-- | 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 :: OsString -> IO String

-- | Constructs an <tt>OsString</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 OsString

-- | QuasiQuote an <a>OsString</a>. This accepts Unicode characters and
--   encodes as UTF-8 on unix and UTF-16 on windows.
osstr :: QuasiQuoter

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

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

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

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


-- | An implementation of platform specific short <a>OsString</a>, which
--   is:
--   
--   <ol>
--   <li>on windows wide char bytes (<tt>[Word16]</tt>)</li>
--   <li>on unix char bytes (<tt>[Word8]</tt>)</li>
--   </ol>
--   
--   It captures the notion of syscall specific encoding (or the lack
--   thereof) to avoid roundtrip issues and memory fragmentation by using
--   unpinned byte arrays. Bytes are not touched or interpreted.
module System.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

-- | 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 a <a>EncodingException</a> if encoding fails.
encodeUtf :: MonadThrow m => String -> m OsString

-- | Encode an <a>OsString</a> given the platform specific encodings.
encodeWith :: TextEncoding -> TextEncoding -> String -> Either EncodingException OsString

-- | 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 :: String -> IO OsString

-- | QuasiQuote an <a>OsString</a>. This accepts Unicode characters and
--   encodes as UTF-8 on unix and UTF-16 on windows.
osstr :: QuasiQuoter

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

-- | 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.
--   Note that filenames on unix are encoding agnostic char arrays.
--   
--   Throws a <a>EncodingException</a> if decoding fails.
decodeUtf :: MonadThrow m => OsString -> m String

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

-- | 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 :: OsString -> IO String

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

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

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

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

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.
encodeUtf :: MonadThrow m => String -> m PosixString

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

-- | This mimics the behavior of the base library when doing filesystem
--   operations, which uses shady PEP 383 style encoding (based on the
--   current locale, but PEP 383 only works properly on UTF-8 encodings, so
--   good luck).
--   
--   Looking up the locale requires IO. If you're not worried about calls
--   to <tt>setFileSystemEncoding</tt>, then <a>unsafePerformIO</a> may be
--   feasible (make sure to deeply evaluate the result to catch
--   exceptions).
encodeFS :: String -> IO PosixString

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

-- | This mimics the behavior of the base library when doing filesystem
--   operations, which uses shady PEP 383 style encoding (based on the
--   current locale, but PEP 383 only works properly on UTF-8 encodings, so
--   good luck).
--   
--   Looking up the locale requires IO. If you're not worried about calls
--   to <tt>setFileSystemEncoding</tt>, then <a>unsafePerformIO</a> may be
--   feasible (make sure to deeply evaluate the result to catch
--   exceptions).
decodeFS :: PosixString -> 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 a <a>EncodingException</a> if encoding fails.
encodeUtf :: MonadThrow m => FilePath -> m OsPath

-- | Encode a <a>FilePath</a> with the specified encoding.
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.
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 a <a>EncodingException</a> if encoding fails.
encodeUtf :: MonadThrow m => FilePath -> m OsPath

-- | Encode a <a>FilePath</a> with the specified encoding.
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

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

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

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

module System.OsString.Windows

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

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

-- | Encode a <a>String</a> with the specified encoding.
encodeWith :: TextEncoding -> String -> Either EncodingException WindowsString

-- | This mimics the behavior of the base library when doing filesystem
--   operations, which does permissive UTF-16 encoding, where coding errors
--   generate Chars in the surrogate range.
--   
--   The reason this is in IO is because it unifies with the Posix
--   counterpart, which does require IO. This is safe to
--   <a>unsafePerformIO</a>/<tt>unsafeDupablePerformIO</tt>.
encodeFS :: String -> IO WindowsString

-- | Constructs a platform string from a ByteString.
--   
--   This ensures valid UCS-2LE. Note that this doesn't expand Word8 to
--   Word16 on windows, so you may get invalid UTF-16.
--   
--   Throws <a>EncodingException</a> on invalid UCS-2LE (although
--   unlikely).
fromBytes :: MonadThrow m => ByteString -> m WindowsString

-- | QuasiQuote a <a>WindowsString</a>. This accepts Unicode characters and
--   encodes as UTF-16LE on windows.
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

-- | Like <a>decodeUtf</a>, except this mimics the behavior of the base
--   library when doing filesystem operations, which does permissive UTF-16
--   encoding, where coding errors generate Chars in the surrogate range.
--   
--   The reason this is in IO is because it unifies with the Posix
--   counterpart, which does require IO.
--   <a>unsafePerformIO</a>/<tt>unsafeDupablePerformIO</tt> are safe,
--   however.
decodeFS :: WindowsString -> 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

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.
encodeUtf :: MonadThrow m => String -> m WindowsString

-- | Encode a <a>String</a> with the specified encoding.
encodeWith :: TextEncoding -> String -> Either EncodingException WindowsString

-- | This mimics the behavior of the base library when doing filesystem
--   operations, which does permissive UTF-16 encoding, where coding errors
--   generate Chars in the surrogate range.
--   
--   The reason this is in IO is because it unifies with the Posix
--   counterpart, which does require IO. This is safe to
--   <a>unsafePerformIO</a>/<tt>unsafeDupablePerformIO</tt>.
encodeFS :: String -> IO WindowsString

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

-- | Like <a>decodeUtf</a>, except this mimics the behavior of the base
--   library when doing filesystem operations, which does permissive UTF-16
--   encoding, where coding errors generate Chars in the surrogate range.
--   
--   The reason this is in IO is because it unifies with the Posix
--   counterpart, which does require IO.
--   <a>unsafePerformIO</a>/<tt>unsafeDupablePerformIO</tt> are safe,
--   however.
decodeFS :: WindowsString -> 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
