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

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

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

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

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

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

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

-- | File extension character
--   
--   
--   extSeparator == '.'
--   
extSeparator :: Char

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

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

-- | Get a list of FILEPATHs in the $PATH variable.
getSearchPath :: IO [FilePath]

-- | Split on the extension. addExtension is the inverse.
--   
--   
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (<>) (splitExtension x) == x
--   Valid x => 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/","")
--   
splitExtension :: FilePath -> (String, String)

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

-- | Set the extension of a file, overwriting one if already present,
--   equivalent to -<.>.
--   
--   
--   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
--   
replaceExtension :: FilePath -> String -> FilePath

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
addExtension :: FilePath -> String -> FilePath

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

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

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

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

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

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

-- | Does the given filename have the specified extension?
--   
--   
--   "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
--   
isExtensionOf :: String -> FilePath -> Bool

-- | Drop the given extension from a FilePath, and the "."
--   preceding it. Returns Nothing if the FilePath does not have the
--   given extension, or Just and the part before the extension if
--   it does.
--   
--   This function can be more predictable than dropExtensions,
--   especially if the filename might itself contain . characters.
--   
--   
--   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
--   
stripExtension :: String -> FilePath -> Maybe FilePath

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

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

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

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

-- | Get the base name, without an extension or path.
--   
--   
--   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"
--   
takeBaseName :: FilePath -> String

-- | Set the base name.
--   
--   
--   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 => replaceBaseName x (takeBaseName x) == x
--   
replaceBaseName :: FilePath -> String -> FilePath

-- | Get the directory name, move up one level.
--   
--   
--             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:\\"
--   
takeDirectory :: FilePath -> FilePath

-- | Set the directory, keeping the filename the same.
--   
--   
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x
--   
replaceDirectory :: FilePath -> String -> FilePath

-- | An alias for </>.
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 readFile (dir </> file)
--   will access the same file as setCurrentDirectory dir; readFile
--   file.
--   
--   
--   Posix:   "/directory" </> "file.ext" == "/directory/file.ext"
--   Windows: "/directory" </> "file.ext" == "/directory\\file.ext"
--            "directory" </> "/file.ext" == "/file.ext"
--   Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x
--   
--   
--   Combined:
--   
--   
--   Posix:   "/" </> "test" == "/test"
--   Posix:   "home" </> "bob" == "home/bob"
--   Posix:   "x:" </> "foo" == "x:/foo"
--   Windows: "C:\\foo" </> "bar" == "C:\\foo\\bar"
--   Windows: "home" </> "bob" == "home\\bob"
--   
--   
--   Not combined:
--   
--   
--   Posix:   "home" </> "/bob" == "/bob"
--   Windows: "home" </> "C:\\bob" == "C:\\bob"
--   
--   
--   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
--   </> is to never combine these forms.
--   
--   
--   Windows: "home" </> "/bob" == "/bob"
--   Windows: "home" </> "\\bob" == "\\bob"
--   Windows: "C:\\home" </> "\\bob" == "\\bob"
--   
--   
--   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 </> is to
--   never combine these forms.
--   
--   
--   Windows: "D:\\foo" </> "C:bar" == "C:bar"
--   Windows: "C:\\foo" </> "C:bar" == "C:bar"
--   
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   
--   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"]
--   
splitPath :: FilePath -> [FilePath]

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

-- | Just as splitPath, but don't add the trailing slashes to each
--   element.
--   
--   
--            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 => joinPath (splitDirectories x) `equalFilePath` x
--            splitDirectories "" == []
--   Windows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
--            splitDirectories "/test///file" == ["/","test","file"]
--   
splitDirectories :: FilePath -> [FilePath]

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   
--   uncurry (<>) (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")
--   
splitDrive :: FilePath -> (FilePath, FilePath)

-- | Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
joinDrive :: FilePath -> FilePath -> FilePath

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

-- | Does a path have a drive.
--   
--   
--   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
--   
hasDrive :: FilePath -> Bool

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

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

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

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

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

-- | Normalise a file
--   
--   
--   
- // outside of the drive can be made blank
--   
- / -> pathSeparator
--   
- ./ -> ""
--   
--   
--   Does not remove "..", because of symlinks.
--   
--   
--   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"
--   
normalise :: FilePath -> FilePath

-- | Equality of two FILEPATHs. If you call
--   System.Directory.canonicalizePath first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to normalise, this does not expand "..",
--   because of symlinks.
--   
--   
--            x == y ==> equalFilePath x y
--            normalise x == normalise y ==> 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:/")
--   
equalFilePath :: FilePath -> FilePath -> Bool

-- | Contract a filename, based on a relative path. Note that the resulting
--   path will never introduce .. paths, as the presence of
--   symlinks means ../b may not reach a/b if it starts
--   from a/c. For a worked example see this blog post.
--   
--   The corresponding makeAbsolute function can be found in
--   System.Directory.
--   
--   
--            makeRelative "/directory" "/directory/file.ext" == "file.ext"
--            Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
--            makeRelative x x == "."
--            Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> 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"
--   
makeRelative :: FilePath -> FilePath -> FilePath

-- | Is a path relative, or is it fixed to the root?
--   
--   
--   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
--   
--   
--   According to [1]:
--   
--   
--   
- "A UNC name of any format [is never relative]."
--   
- "You cannot use the "\?" prefix with a relative path."
--   
isRelative :: FilePath -> Bool

-- | 
--   not . isRelative
--   
--   
--   
--   isAbsolute x == not (isRelative x)
--   
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.
--   
--   
--            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
--   
isValid :: FilePath -> Bool

-- | Take a FilePath and make it valid; does not change already valid
--   FILEPATHs.
--   
--   
--   isValid (makeValid x)
--   isValid x ==> 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"
--   
makeValid :: FilePath -> FilePath

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

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

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

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

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

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

-- | File extension character
--   
--   
--   extSeparator == '.'
--   
extSeparator :: Char

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

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

-- | Get a list of FILEPATHs in the $PATH variable.
getSearchPath :: IO [FilePath]

-- | Split on the extension. addExtension is the inverse.
--   
--   
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (<>) (splitExtension x) == x
--   Valid x => 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/","")
--   
splitExtension :: FilePath -> (String, String)

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

-- | Set the extension of a file, overwriting one if already present,
--   equivalent to -<.>.
--   
--   
--   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
--   
replaceExtension :: FilePath -> String -> FilePath

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
addExtension :: FilePath -> String -> FilePath

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

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

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

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

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

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

-- | Does the given filename have the specified extension?
--   
--   
--   "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
--   
isExtensionOf :: String -> FilePath -> Bool

-- | Drop the given extension from a FilePath, and the "."
--   preceding it. Returns Nothing if the FilePath does not have the
--   given extension, or Just and the part before the extension if
--   it does.
--   
--   This function can be more predictable than dropExtensions,
--   especially if the filename might itself contain . characters.
--   
--   
--   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
--   
stripExtension :: String -> FilePath -> Maybe FilePath

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

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

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

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

-- | Get the base name, without an extension or path.
--   
--   
--   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"
--   
takeBaseName :: FilePath -> String

-- | Set the base name.
--   
--   
--   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 => replaceBaseName x (takeBaseName x) == x
--   
replaceBaseName :: FilePath -> String -> FilePath

-- | Get the directory name, move up one level.
--   
--   
--             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:\\"
--   
takeDirectory :: FilePath -> FilePath

-- | Set the directory, keeping the filename the same.
--   
--   
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x
--   
replaceDirectory :: FilePath -> String -> FilePath

-- | An alias for </>.
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 readFile (dir </> file)
--   will access the same file as setCurrentDirectory dir; readFile
--   file.
--   
--   
--   Posix:   "/directory" </> "file.ext" == "/directory/file.ext"
--   Windows: "/directory" </> "file.ext" == "/directory\\file.ext"
--            "directory" </> "/file.ext" == "/file.ext"
--   Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x
--   
--   
--   Combined:
--   
--   
--   Posix:   "/" </> "test" == "/test"
--   Posix:   "home" </> "bob" == "home/bob"
--   Posix:   "x:" </> "foo" == "x:/foo"
--   Windows: "C:\\foo" </> "bar" == "C:\\foo\\bar"
--   Windows: "home" </> "bob" == "home\\bob"
--   
--   
--   Not combined:
--   
--   
--   Posix:   "home" </> "/bob" == "/bob"
--   Windows: "home" </> "C:\\bob" == "C:\\bob"
--   
--   
--   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
--   </> is to never combine these forms.
--   
--   
--   Windows: "home" </> "/bob" == "/bob"
--   Windows: "home" </> "\\bob" == "\\bob"
--   Windows: "C:\\home" </> "\\bob" == "\\bob"
--   
--   
--   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 </> is to
--   never combine these forms.
--   
--   
--   Windows: "D:\\foo" </> "C:bar" == "C:bar"
--   Windows: "C:\\foo" </> "C:bar" == "C:bar"
--   
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   
--   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"]
--   
splitPath :: FilePath -> [FilePath]

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

-- | Just as splitPath, but don't add the trailing slashes to each
--   element.
--   
--   
--            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 => joinPath (splitDirectories x) `equalFilePath` x
--            splitDirectories "" == []
--   Windows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
--            splitDirectories "/test///file" == ["/","test","file"]
--   
splitDirectories :: FilePath -> [FilePath]

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   
--   uncurry (<>) (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")
--   
splitDrive :: FilePath -> (FilePath, FilePath)

-- | Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
joinDrive :: FilePath -> FilePath -> FilePath

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

-- | Does a path have a drive.
--   
--   
--   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
--   
hasDrive :: FilePath -> Bool

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

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

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

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

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

-- | Normalise a file
--   
--   
--   
- // outside of the drive can be made blank
--   
- / -> pathSeparator
--   
- ./ -> ""
--   
--   
--   Does not remove "..", because of symlinks.
--   
--   
--   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"
--   
normalise :: FilePath -> FilePath

-- | Equality of two FILEPATHs. If you call
--   System.Directory.canonicalizePath first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to normalise, this does not expand "..",
--   because of symlinks.
--   
--   
--            x == y ==> equalFilePath x y
--            normalise x == normalise y ==> 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:/")
--   
equalFilePath :: FilePath -> FilePath -> Bool

-- | Contract a filename, based on a relative path. Note that the resulting
--   path will never introduce .. paths, as the presence of
--   symlinks means ../b may not reach a/b if it starts
--   from a/c. For a worked example see this blog post.
--   
--   The corresponding makeAbsolute function can be found in
--   System.Directory.
--   
--   
--            makeRelative "/directory" "/directory/file.ext" == "file.ext"
--            Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
--            makeRelative x x == "."
--            Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> 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"
--   
makeRelative :: FilePath -> FilePath -> FilePath

-- | Is a path relative, or is it fixed to the root?
--   
--   
--   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
--   
--   
--   According to [1]:
--   
--   
--   
- "A UNC name of any format [is never relative]."
--   
- "You cannot use the "\?" prefix with a relative path."
--   
isRelative :: FilePath -> Bool

-- | 
--   not . isRelative
--   
--   
--   
--   isAbsolute x == not (isRelative x)
--   
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.
--   
--   
--            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
--   
isValid :: FilePath -> Bool

-- | Take a FilePath and make it valid; does not change already valid
--   FILEPATHs.
--   
--   
--   isValid (makeValid x)
--   isValid x ==> 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"
--   
makeValid :: FilePath -> FilePath

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

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

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

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

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

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

-- | File extension character
--   
--   
--   extSeparator == '.'
--   
extSeparator :: Char

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

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

-- | Get a list of FILEPATHs in the $PATH variable.
getSearchPath :: IO [FilePath]

-- | Split on the extension. addExtension is the inverse.
--   
--   
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (<>) (splitExtension x) == x
--   Valid x => 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/","")
--   
splitExtension :: FilePath -> (String, String)

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

-- | Set the extension of a file, overwriting one if already present,
--   equivalent to -<.>.
--   
--   
--   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
--   
replaceExtension :: FilePath -> String -> FilePath

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
addExtension :: FilePath -> String -> FilePath

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

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

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

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

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

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

-- | Does the given filename have the specified extension?
--   
--   
--   "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
--   
isExtensionOf :: String -> FilePath -> Bool

-- | Drop the given extension from a FilePath, and the "."
--   preceding it. Returns Nothing if the FilePath does not have the
--   given extension, or Just and the part before the extension if
--   it does.
--   
--   This function can be more predictable than dropExtensions,
--   especially if the filename might itself contain . characters.
--   
--   
--   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
--   
stripExtension :: String -> FilePath -> Maybe FilePath

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

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

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

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

-- | Get the base name, without an extension or path.
--   
--   
--   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"
--   
takeBaseName :: FilePath -> String

-- | Set the base name.
--   
--   
--   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 => replaceBaseName x (takeBaseName x) == x
--   
replaceBaseName :: FilePath -> String -> FilePath

-- | Get the directory name, move up one level.
--   
--   
--             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:\\"
--   
takeDirectory :: FilePath -> FilePath

-- | Set the directory, keeping the filename the same.
--   
--   
--   replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
--   Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x
--   
replaceDirectory :: FilePath -> String -> FilePath

-- | An alias for </>.
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 readFile (dir </> file)
--   will access the same file as setCurrentDirectory dir; readFile
--   file.
--   
--   
--   Posix:   "/directory" </> "file.ext" == "/directory/file.ext"
--   Windows: "/directory" </> "file.ext" == "/directory\\file.ext"
--            "directory" </> "/file.ext" == "/file.ext"
--   Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x
--   
--   
--   Combined:
--   
--   
--   Posix:   "/" </> "test" == "/test"
--   Posix:   "home" </> "bob" == "home/bob"
--   Posix:   "x:" </> "foo" == "x:/foo"
--   Windows: "C:\\foo" </> "bar" == "C:\\foo\\bar"
--   Windows: "home" </> "bob" == "home\\bob"
--   
--   
--   Not combined:
--   
--   
--   Posix:   "home" </> "/bob" == "/bob"
--   Windows: "home" </> "C:\\bob" == "C:\\bob"
--   
--   
--   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
--   </> is to never combine these forms.
--   
--   
--   Windows: "home" </> "/bob" == "/bob"
--   Windows: "home" </> "\\bob" == "\\bob"
--   Windows: "C:\\home" </> "\\bob" == "\\bob"
--   
--   
--   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 </> is to
--   never combine these forms.
--   
--   
--   Windows: "D:\\foo" </> "C:bar" == "C:bar"
--   Windows: "C:\\foo" </> "C:bar" == "C:bar"
--   
(</>) :: FilePath -> FilePath -> FilePath
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   
--   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"]
--   
splitPath :: FilePath -> [FilePath]

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

-- | Just as splitPath, but don't add the trailing slashes to each
--   element.
--   
--   
--            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 => joinPath (splitDirectories x) `equalFilePath` x
--            splitDirectories "" == []
--   Windows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
--            splitDirectories "/test///file" == ["/","test","file"]
--   
splitDirectories :: FilePath -> [FilePath]

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   
--   uncurry (<>) (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")
--   
splitDrive :: FilePath -> (FilePath, FilePath)

-- | Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
joinDrive :: FilePath -> FilePath -> FilePath

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

-- | Does a path have a drive.
--   
--   
--   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
--   
hasDrive :: FilePath -> Bool

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

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

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

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

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

-- | Normalise a file
--   
--   
--   
- // outside of the drive can be made blank
--   
- / -> pathSeparator
--   
- ./ -> ""
--   
--   
--   Does not remove "..", because of symlinks.
--   
--   
--   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"
--   
normalise :: FilePath -> FilePath

-- | Equality of two FILEPATHs. If you call
--   System.Directory.canonicalizePath first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to normalise, this does not expand "..",
--   because of symlinks.
--   
--   
--            x == y ==> equalFilePath x y
--            normalise x == normalise y ==> 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:/")
--   
equalFilePath :: FilePath -> FilePath -> Bool

-- | Contract a filename, based on a relative path. Note that the resulting
--   path will never introduce .. paths, as the presence of
--   symlinks means ../b may not reach a/b if it starts
--   from a/c. For a worked example see this blog post.
--   
--   The corresponding makeAbsolute function can be found in
--   System.Directory.
--   
--   
--            makeRelative "/directory" "/directory/file.ext" == "file.ext"
--            Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
--            makeRelative x x == "."
--            Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> 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"
--   
makeRelative :: FilePath -> FilePath -> FilePath

-- | Is a path relative, or is it fixed to the root?
--   
--   
--   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
--   
--   
--   According to [1]:
--   
--   
--   
- "A UNC name of any format [is never relative]."
--   
- "You cannot use the "\?" prefix with a relative path."
--   
isRelative :: FilePath -> Bool

-- | 
--   not . isRelative
--   
--   
--   
--   isAbsolute x == not (isRelative x)
--   
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.
--   
--   
--            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
--   
isValid :: FilePath -> Bool

-- | Take a FilePath and make it valid; does not change already valid
--   FILEPATHs.
--   
--   
--   isValid (makeValid x)
--   isValid x ==> 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"
--   
makeValid :: FilePath -> FilePath

module System.OsPath.Encoding
data EncodingException

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

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

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

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

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

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

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

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

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

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

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

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

-- | File extension character
--   
--   
--   extSeparator == '.'
--   
extSeparator :: Word8

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

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

-- | Split on the extension. addExtension is the inverse.
--   
--   
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (<>) (splitExtension x) == x
--   Valid x => 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/","")
--   
splitExtension :: ShortByteString -> (ShortByteString, ShortByteString)

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

-- | Set the extension of a file, overwriting one if already present,
--   equivalent to -<.>.
--   
--   
--   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
--   
replaceExtension :: ShortByteString -> ShortByteString -> ShortByteString

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
addExtension :: ShortByteString -> ShortByteString -> ShortByteString

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

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

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

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

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

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

-- | Does the given filename have the specified extension?
--   
--   
--   "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
--   
isExtensionOf :: ShortByteString -> ShortByteString -> Bool

-- | Drop the given extension from a ShortByteString, and the "."
--   preceding it. Returns Nothing if the ShortByteString does not
--   have the given extension, or Just and the part before the
--   extension if it does.
--   
--   This function can be more predictable than dropExtensions,
--   especially if the filename might itself contain . characters.
--   
--   
--   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
--   
stripExtension :: ShortByteString -> ShortByteString -> Maybe ShortByteString

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

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

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

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

-- | Get the base name, without an extension or path.
--   
--   
--   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"
--   
takeBaseName :: ShortByteString -> ShortByteString

-- | Set the base name.
--   
--   
--   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 => replaceBaseName x (takeBaseName x) == x
--   
replaceBaseName :: ShortByteString -> ShortByteString -> ShortByteString

-- | Get the directory name, move up one level.
--   
--   
--             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:\\"
--   
takeDirectory :: ShortByteString -> ShortByteString

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

-- | An alias for </>.
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 readFile (dir </> file)
--   will access the same file as setCurrentDirectory dir; readFile
--   file.
--   
--   
--   Posix:   "/directory" </> "file.ext" == "/directory/file.ext"
--   Windows: "/directory" </> "file.ext" == "/directory\\file.ext"
--            "directory" </> "/file.ext" == "/file.ext"
--   Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x
--   
--   
--   Combined:
--   
--   
--   Posix:   "/" </> "test" == "/test"
--   Posix:   "home" </> "bob" == "home/bob"
--   Posix:   "x:" </> "foo" == "x:/foo"
--   Windows: "C:\\foo" </> "bar" == "C:\\foo\\bar"
--   Windows: "home" </> "bob" == "home\\bob"
--   
--   
--   Not combined:
--   
--   
--   Posix:   "home" </> "/bob" == "/bob"
--   Windows: "home" </> "C:\\bob" == "C:\\bob"
--   
--   
--   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
--   </> is to never combine these forms.
--   
--   
--   Windows: "home" </> "/bob" == "/bob"
--   Windows: "home" </> "\\bob" == "\\bob"
--   Windows: "C:\\home" </> "\\bob" == "\\bob"
--   
--   
--   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 </> is to
--   never combine these forms.
--   
--   
--   Windows: "D:\\foo" </> "C:bar" == "C:bar"
--   Windows: "C:\\foo" </> "C:bar" == "C:bar"
--   
(</>) :: ShortByteString -> ShortByteString -> ShortByteString
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   
--   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"]
--   
splitPath :: ShortByteString -> [ShortByteString]

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

-- | Just as splitPath, but don't add the trailing slashes to each
--   element.
--   
--   
--            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 => joinPath (splitDirectories x) `equalFilePath` x
--            splitDirectories "" == []
--   Windows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
--            splitDirectories "/test///file" == ["/","test","file"]
--   
splitDirectories :: ShortByteString -> [ShortByteString]

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   
--   uncurry (<>) (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")
--   
splitDrive :: ShortByteString -> (ShortByteString, ShortByteString)

-- | Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
joinDrive :: ShortByteString -> ShortByteString -> ShortByteString

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

-- | Does a path have a drive.
--   
--   
--   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
--   
hasDrive :: ShortByteString -> Bool

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

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

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

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

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

-- | Normalise a file
--   
--   
--   
- // outside of the drive can be made blank
--   
- / -> pathSeparator
--   
- ./ -> ""
--   
--   
--   Does not remove "..", because of symlinks.
--   
--   
--   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"
--   
normalise :: ShortByteString -> ShortByteString

-- | Equality of two FILEPATHs. If you call
--   System.Directory.canonicalizePath first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to normalise, this does not expand "..",
--   because of symlinks.
--   
--   
--            x == y ==> equalFilePath x y
--            normalise x == normalise y ==> 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:/")
--   
equalFilePath :: ShortByteString -> ShortByteString -> Bool

-- | Contract a filename, based on a relative path. Note that the resulting
--   path will never introduce .. paths, as the presence of
--   symlinks means ../b may not reach a/b if it starts
--   from a/c. For a worked example see this blog post.
--   
--   The corresponding makeAbsolute function can be found in
--   System.Directory.
--   
--   
--            makeRelative "/directory" "/directory/file.ext" == "file.ext"
--            Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
--            makeRelative x x == "."
--            Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> 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"
--   
makeRelative :: ShortByteString -> ShortByteString -> ShortByteString

-- | Is a path relative, or is it fixed to the root?
--   
--   
--   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
--   
--   
--   According to [1]:
--   
--   
--   
- "A UNC name of any format [is never relative]."
--   
- "You cannot use the "\?" prefix with a relative path."
--   
isRelative :: ShortByteString -> Bool

-- | 
--   not . isRelative
--   
--   
--   
--   isAbsolute x == not (isRelative x)
--   
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.
--   
--   
--            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
--   
isValid :: ShortByteString -> Bool

-- | Take a ShortByteString and make it valid; does not change already
--   valid FILEPATHs.
--   
--   
--   isValid (makeValid x)
--   isValid x ==> 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"
--   
makeValid :: ShortByteString -> ShortByteString

module System.OsPath.Types

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

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

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

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

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

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

-- | Newtype representing short operating system specific strings.
--   
--   Internally this is either WindowsString or PosixString,
--   depending on the platform. Both use unpinned ShortByteString
--   for efficiency.
--   
--   The constructor is only exported via
--   System.OsString.Internal.Types, 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 Word16,
--   on POSIX one-octet (Word8).
data OsChar

module System.OsPath.Posix

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

-- | Filepaths are char[] 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 EncodingException if encoding fails. If the input
--   does not contain surrogate chars, you can use unsafeEncodeUtf.
encodeUtf :: MonadThrow m => String -> m PosixString

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

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

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

-- | Pack a list of platform words to a platform string.
--   
--   Note that using this in conjunction with unsafeFromChar to
--   convert from [Char] 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 EncodingException if decoding fails.
decodeUtf :: MonadThrow m => PosixString -> m String

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

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

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

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

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

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

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

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

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

-- | File extension character
--   
--   
--   extSeparator == '.'
--   
extSeparator :: PosixChar

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

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

-- | Split on the extension. addExtension is the inverse.
--   
--   
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (<>) (splitExtension x) == x
--   Valid x => 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/","")
--   
splitExtension :: PosixPath -> (PosixPath, PosixString)

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

-- | Set the extension of a file, overwriting one if already present,
--   equivalent to -<.>.
--   
--   
--   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
--   
replaceExtension :: PosixPath -> PosixString -> PosixPath

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
--   
--   Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   
addExtension :: PosixPath -> PosixString -> PosixPath

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

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

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

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

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

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

-- | Does the given filename have the specified extension?
--   
--   
--   "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
--   
isExtensionOf :: PosixString -> PosixPath -> Bool

-- | Drop the given extension from a filepath, and the "."
--   preceding it. Returns Nothing if the filepath does not have the
--   given extension, or Just and the part before the extension if
--   it does.
--   
--   This function can be more predictable than dropExtensions,
--   especially if the filename might itself contain . characters.
--   
--   
--   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
--   
stripExtension :: PosixString -> PosixPath -> Maybe PosixPath

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

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

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

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

-- | Get the base name, without an extension or path.
--   
--   
--   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"
--   
takeBaseName :: PosixPath -> PosixPath

-- | Set the base name.
--   
--   
--   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 => replaceBaseName x (takeBaseName x) == x
--   
replaceBaseName :: PosixPath -> PosixString -> PosixPath

-- | Get the directory name, move up one level.
--   
--   
--   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 :: PosixPath -> PosixPath

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

-- | An alias for </>.
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 readFile (dir </> file)
--   will access the same file as setCurrentDirectory dir; readFile
--   file.
--   
--   
--   "/directory" </> "file.ext" == "/directory/file.ext"
--   Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x
--   
--   
--   Combined:
--   
--   
--   "/" </> "test" == "/test"
--   "home" </> "bob" == "home/bob"
--   "x:" </> "foo" == "x:/foo"
--   
--   
--   Not combined:
--   
--   
--   "home" </> "/bob" == "/bob"
--   
(</>) :: PosixPath -> PosixPath -> PosixPath

-- | Split a path by the directory separator.
--   
--   
--   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"]
--   
splitPath :: PosixPath -> [PosixPath]

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

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

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

-- | Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
--   
--   Join a drive and the rest of the path.
--   
--   
--   Valid x => uncurry joinDrive (splitDrive x) == x
--   
joinDrive :: PosixPath -> PosixPath -> PosixPath

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

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

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

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

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

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

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

-- | Normalise a file
--   
--   
--   
- // outside of the drive can be made blank
--   
- / -> pathSeparator
--   
- ./ -> ""
--   
--   
--   Does not remove "..", because of symlinks.
--   
--   
--   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"
--   
normalise :: PosixPath -> PosixPath

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

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

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

-- | 
--   not . isRelative
--   
--   
--   
--   isAbsolute x == not (isRelative x)
--   
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.
--   
--   
--   isValid "" == False
--   isValid "\0" == False
--   isValid "/random_ path:*" == True
--   isValid x == not (null x)
--   
isValid :: PosixPath -> Bool

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

module System.OsPath.Internal

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

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

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

-- | Like encodeUtf, except this mimics the behavior of the base
--   library when doing filesystem operations, which is:
--   
--   
--   
- 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)
--   
- on windows does permissive UTF-16 encoding, where coding errors
--   generate Chars in the surrogate range
--   
--   
--   Looking up the locale requires IO. If you're not worried about calls
--   to setFileSystemEncoding, then unsafePerformIO 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 EncodingException if decoding fails.
decodeUtf :: MonadThrow m => OsPath -> m FilePath

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

-- | Like decodeUtf, except this mimics the behavior of the base
--   library when doing filesystem operations, which is:
--   
--   
--   
- 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)
--   
- on windows does permissive UTF-16 encoding, where coding errors
--   generate Chars in the surrogate range
--   
--   
--   Looking up the locale requires IO. If you're not worried about calls
--   to setFileSystemEncoding, then unsafePerformIO may
--   be feasible (make sure to deeply evaluate the result to catch
--   exceptions).
decodeFS :: OsPath -> IO FilePath

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

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

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

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

-- | An implementation of the Abstract FilePath Proposal, which aims
--   to supersede type FilePath = String for various reasons:
--   
--   
--   
- it is more efficient and avoids memory fragmentation (uses
--   unpinned ShortByteString under the hood)
--   
- it is more type-safe (newtype over ShortByteString)
--   
- avoids round-tripping issues by not converting to String (which is
--   not total and loses the encoding)
--   
- abstracts over unix and windows while keeping the original
--   bytes
--   
--   
--   It is important to know that filenames/filepaths have different
--   representations across platforms:
--   
--   
--   
- On Windows, filepaths are expected to be encoded as
--   UTF16-LE as per the documentation, but may also include invalid
--   surrogate pairs, in which case UCS-2 can be used. They are passed as
--   wchar_t* to syscalls. OsPath only maintains the wide
--   character invariant.
--   
- On Unix, filepaths don't have a predefined encoding
--   (although they are often interpreted as UTF8) as per the POSIX
--   specification and are passed as char[] to syscalls.
--   OsPath maintains no invariant here.
--   
--   
--   Apart from encoding, filepaths have additional restrictions per
--   platform:
--   
--   
--   
- On Windows the naming convention may apply
--   
- On Unix, only NUL bytes are disallowed as per the
--   POSIX specification
--   
--   
--   Use isValid to check for these restrictions (OsPath
--   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:
--   
--   
--   
- Avoid interpreting filenames that the OS returns, unless
--   absolutely necessary. For example, the filepath separator is usually a
--   predefined Word8/Word16, regardless of encoding. So
--   even if we need to split filepaths, it might still not be necessary to
--   understand the encoding of the filename.
--   
- When interpreting OS returned filenames consider that these might
--   not be UTF8 on unix 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 TextEncoding, c) mimic the behavior of
--   the base library (permissive UTF16 on windows, current
--   filesystem encoding on unix).
--   
- Avoid comparing String based filepaths, because filenames
--   of different encodings may have the same String
--   representation, although they're not the same byte-wise.
--   
module System.OsPath

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

-- | Newtype representing short operating system specific strings.
--   
--   Internally this is either WindowsString or PosixString,
--   depending on the platform. Both use unpinned ShortByteString
--   for efficiency.
--   
--   The constructor is only exported via
--   System.OsString.Internal.Types, 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 Word16,
--   on POSIX one-octet (Word8).
data OsChar

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

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

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

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

-- | Pack a list of OsChar to an OsPath.
--   
--   Note that using this in conjunction with unsafeFromChar to
--   convert from [Char] to OsPath 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 EncodingException if decoding fails.
decodeUtf :: MonadThrow m => OsPath -> m FilePath

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

-- | Unpack an OsPath to a list of OsChar.
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, pathSeparator is the 'ideal' one.
--   
--   
--   Windows: pathSeparator == '\\'S
--   Posix:   pathSeparator ==  '/'
--   
pathSeparator :: OsChar

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

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

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

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

-- | File extension character
--   
--   
--   extSeparator == '.'
--   
extSeparator :: OsChar

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

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

-- | Split on the extension. addExtension is the inverse.
--   
--   
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (<>) (splitExtension x) == x
--   Valid x => 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/","")
--   
splitExtension :: OsPath -> (OsPath, OsString)

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

-- | Set the extension of a file, overwriting one if already present,
--   equivalent to -<.>.
--   
--   
--   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
--   
replaceExtension :: OsPath -> OsString -> OsPath

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
--   
--   Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
addExtension :: OsPath -> OsString -> OsPath

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

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

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

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

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

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

-- | Does the given filename have the specified extension?
--   
--   
--   "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
--   
isExtensionOf :: OsString -> OsPath -> Bool

-- | Drop the given extension from a filepath, and the "."
--   preceding it. Returns Nothing if the filepath does not have the
--   given extension, or Just and the part before the extension if
--   it does.
--   
--   This function can be more predictable than dropExtensions,
--   especially if the filename might itself contain . characters.
--   
--   
--   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
--   
stripExtension :: OsString -> OsPath -> Maybe OsPath

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

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

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

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

-- | Get the base name, without an extension or path.
--   
--   
--   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"
--   
takeBaseName :: OsPath -> OsPath

-- | Set the base name.
--   
--   
--   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 => replaceBaseName x (takeBaseName x) == x
--   
replaceBaseName :: OsPath -> OsString -> OsPath

-- | Get the directory name, move up one level.
--   
--   
--             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:\\"
--   
takeDirectory :: OsPath -> OsPath

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

-- | An alias for </>.
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 readFile (dir </> file)
--   will access the same file as setCurrentDirectory dir; readFile
--   file.
--   
--   
--   Posix:   "/directory" </> "file.ext" == "/directory/file.ext"
--   Windows: "/directory" </> "file.ext" == "/directory\\file.ext"
--            "directory" </> "/file.ext" == "/file.ext"
--   Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x
--   
--   
--   Combined:
--   
--   
--   Posix:   "/" </> "test" == "/test"
--   Posix:   "home" </> "bob" == "home/bob"
--   Posix:   "x:" </> "foo" == "x:/foo"
--   Windows: "C:\\foo" </> "bar" == "C:\\foo\\bar"
--   Windows: "home" </> "bob" == "home\\bob"
--   
--   
--   Not combined:
--   
--   
--   Posix:   "home" </> "/bob" == "/bob"
--   Windows: "home" </> "C:\\bob" == "C:\\bob"
--   
--   
--   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
--   </> is to never combine these forms.
--   
--   
--   Windows: "home" </> "/bob" == "/bob"
--   Windows: "home" </> "\\bob" == "\\bob"
--   Windows: "C:\\home" </> "\\bob" == "\\bob"
--   
--   
--   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 </> is to
--   never combine these forms.
--   
--   
--   Windows: "D:\\foo" </> "C:bar" == "C:bar"
--   Windows: "C:\\foo" </> "C:bar" == "C:bar"
--   
(</>) :: OsPath -> OsPath -> OsPath

-- | Split a path by the directory separator.
--   
--   
--   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"]
--   
splitPath :: OsPath -> [OsPath]

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

-- | Just as splitPath, but don't add the trailing slashes to each
--   element.
--   
--   
--            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 => joinPath (splitDirectories x) `equalFilePath` x
--            splitDirectories "" == []
--   Windows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
--            splitDirectories "/test///file" == ["/","test","file"]
--   
splitDirectories :: OsPath -> [OsPath]

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   
--   uncurry (<>) (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")
--   
splitDrive :: OsPath -> (OsPath, OsPath)

-- | Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
--   
--   Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
joinDrive :: OsPath -> OsPath -> OsPath

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

-- | Does a path have a drive.
--   
--   
--   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
--   
hasDrive :: OsPath -> Bool

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

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

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

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

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

-- | Normalise a file
--   
--   
--   
- // outside of the drive can be made blank
--   
- / -> pathSeparator
--   
- ./ -> ""
--   
--   
--   Does not remove "..", because of symlinks.
--   
--   
--   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"
--   
normalise :: OsPath -> OsPath

-- | Equality of two filepaths. If you call
--   System.Directory.canonicalizePath first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to normalise, this does not expand "..",
--   because of symlinks.
--   
--   
--            x == y ==> equalFilePath x y
--            normalise x == normalise y ==> 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:/")
--   
equalFilePath :: OsPath -> OsPath -> Bool

-- | Contract a filename, based on a relative path. Note that the resulting
--   path will never introduce .. paths, as the presence of
--   symlinks means ../b may not reach a/b if it starts
--   from a/c. For a worked example see this blog post.
--   
--   The corresponding makeAbsolute function can be found in
--   System.Directory.
--   
--   
--            makeRelative "/directory" "/directory/file.ext" == "file.ext"
--            Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
--            makeRelative x x == "."
--            Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> 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"
--   
makeRelative :: OsPath -> OsPath -> OsPath

-- | Is a path relative, or is it fixed to the root?
--   
--   
--   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
--   
--   
--   According to [1]:
--   
--   
--   
- "A UNC name of any format [is never relative]."
--   
- "You cannot use the "\?" prefix with a relative path."
--   
isRelative :: OsPath -> Bool

-- | 
--   not . isRelative
--   
--   
--   
--   isAbsolute x == not (isRelative x)
--   
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.
--   
--   
--            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
--   
isValid :: OsPath -> Bool

-- | Take a filepath and make it valid; does not change already valid
--   filepaths.
--   
--   
--   isValid (makeValid x)
--   isValid x ==> 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"
--   
makeValid :: OsPath -> OsPath

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

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

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

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

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

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

-- | File extension character
--   
--   
--   extSeparator == '.'
--   
extSeparator :: Word16

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

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

-- | Split on the extension. addExtension is the inverse.
--   
--   
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (<>) (splitExtension x) == x
--   Valid x => 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/","")
--   
splitExtension :: ShortByteString -> (ShortByteString, ShortByteString)

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

-- | Set the extension of a file, overwriting one if already present,
--   equivalent to -<.>.
--   
--   
--   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
--   
replaceExtension :: ShortByteString -> ShortByteString -> ShortByteString

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
addExtension :: ShortByteString -> ShortByteString -> ShortByteString

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

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

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

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

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

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

-- | Does the given filename have the specified extension?
--   
--   
--   "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
--   
isExtensionOf :: ShortByteString -> ShortByteString -> Bool

-- | Drop the given extension from a ShortByteString, and the "."
--   preceding it. Returns Nothing if the ShortByteString does not
--   have the given extension, or Just and the part before the
--   extension if it does.
--   
--   This function can be more predictable than dropExtensions,
--   especially if the filename might itself contain . characters.
--   
--   
--   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
--   
stripExtension :: ShortByteString -> ShortByteString -> Maybe ShortByteString

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

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

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

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

-- | Get the base name, without an extension or path.
--   
--   
--   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"
--   
takeBaseName :: ShortByteString -> ShortByteString

-- | Set the base name.
--   
--   
--   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 => replaceBaseName x (takeBaseName x) == x
--   
replaceBaseName :: ShortByteString -> ShortByteString -> ShortByteString

-- | Get the directory name, move up one level.
--   
--   
--             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:\\"
--   
takeDirectory :: ShortByteString -> ShortByteString

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

-- | An alias for </>.
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 readFile (dir </> file)
--   will access the same file as setCurrentDirectory dir; readFile
--   file.
--   
--   
--   Posix:   "/directory" </> "file.ext" == "/directory/file.ext"
--   Windows: "/directory" </> "file.ext" == "/directory\\file.ext"
--            "directory" </> "/file.ext" == "/file.ext"
--   Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x
--   
--   
--   Combined:
--   
--   
--   Posix:   "/" </> "test" == "/test"
--   Posix:   "home" </> "bob" == "home/bob"
--   Posix:   "x:" </> "foo" == "x:/foo"
--   Windows: "C:\\foo" </> "bar" == "C:\\foo\\bar"
--   Windows: "home" </> "bob" == "home\\bob"
--   
--   
--   Not combined:
--   
--   
--   Posix:   "home" </> "/bob" == "/bob"
--   Windows: "home" </> "C:\\bob" == "C:\\bob"
--   
--   
--   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
--   </> is to never combine these forms.
--   
--   
--   Windows: "home" </> "/bob" == "/bob"
--   Windows: "home" </> "\\bob" == "\\bob"
--   Windows: "C:\\home" </> "\\bob" == "\\bob"
--   
--   
--   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 </> is to
--   never combine these forms.
--   
--   
--   Windows: "D:\\foo" </> "C:bar" == "C:bar"
--   Windows: "C:\\foo" </> "C:bar" == "C:bar"
--   
(</>) :: ShortByteString -> ShortByteString -> ShortByteString
infixr 5 </>

-- | Split a path by the directory separator.
--   
--   
--   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"]
--   
splitPath :: ShortByteString -> [ShortByteString]

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

-- | Just as splitPath, but don't add the trailing slashes to each
--   element.
--   
--   
--            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 => joinPath (splitDirectories x) `equalFilePath` x
--            splitDirectories "" == []
--   Windows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
--            splitDirectories "/test///file" == ["/","test","file"]
--   
splitDirectories :: ShortByteString -> [ShortByteString]

-- | Split a path into a drive and a path. On Posix, / is a Drive.
--   
--   
--   uncurry (<>) (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")
--   
splitDrive :: ShortByteString -> (ShortByteString, ShortByteString)

-- | Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
joinDrive :: ShortByteString -> ShortByteString -> ShortByteString

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

-- | Does a path have a drive.
--   
--   
--   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
--   
hasDrive :: ShortByteString -> Bool

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

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

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

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

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

-- | Normalise a file
--   
--   
--   
- // outside of the drive can be made blank
--   
- / -> pathSeparator
--   
- ./ -> ""
--   
--   
--   Does not remove "..", because of symlinks.
--   
--   
--   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"
--   
normalise :: ShortByteString -> ShortByteString

-- | Equality of two FILEPATHs. If you call
--   System.Directory.canonicalizePath first this has a much
--   better chance of working. Note that this doesn't follow symlinks or
--   DOSNAM~1s.
--   
--   Similar to normalise, this does not expand "..",
--   because of symlinks.
--   
--   
--            x == y ==> equalFilePath x y
--            normalise x == normalise y ==> 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:/")
--   
equalFilePath :: ShortByteString -> ShortByteString -> Bool

-- | Contract a filename, based on a relative path. Note that the resulting
--   path will never introduce .. paths, as the presence of
--   symlinks means ../b may not reach a/b if it starts
--   from a/c. For a worked example see this blog post.
--   
--   The corresponding makeAbsolute function can be found in
--   System.Directory.
--   
--   
--            makeRelative "/directory" "/directory/file.ext" == "file.ext"
--            Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
--            makeRelative x x == "."
--            Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> 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"
--   
makeRelative :: ShortByteString -> ShortByteString -> ShortByteString

-- | Is a path relative, or is it fixed to the root?
--   
--   
--   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
--   
--   
--   According to [1]:
--   
--   
--   
- "A UNC name of any format [is never relative]."
--   
- "You cannot use the "\?" prefix with a relative path."
--   
isRelative :: ShortByteString -> Bool

-- | 
--   not . isRelative
--   
--   
--   
--   isAbsolute x == not (isRelative x)
--   
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.
--   
--   
--            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
--   
isValid :: ShortByteString -> Bool

-- | Take a ShortByteString and make it valid; does not change already
--   valid FILEPATHs.
--   
--   
--   isValid (makeValid x)
--   isValid x ==> 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"
--   
makeValid :: ShortByteString -> ShortByteString

module System.OsPath.Windows

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

-- | Filepaths are wchar_t* 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 EncodingException if encoding fails. If the input
--   does not contain surrogate chars, you can use
--   unsafeEncodeUtf.
encodeUtf :: MonadThrow m => String -> m WindowsString

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

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

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

-- | Pack a list of platform words to a platform string.
--   
--   Note that using this in conjunction with unsafeFromChar to
--   convert from [Char] 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 EncodingException if decoding fails.
decodeUtf :: MonadThrow m => WindowsString -> m String

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

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

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

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

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

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

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

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

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

-- | File extension character
--   
--   
--   extSeparator == '.'
--   
extSeparator :: WindowsChar

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

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

-- | Split on the extension. addExtension is the inverse.
--   
--   
--   splitExtension "/directory/path.ext" == ("/directory/path",".ext")
--   uncurry (<>) (splitExtension x) == x
--   Valid x => 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/","")
--   
splitExtension :: WindowsPath -> (WindowsPath, WindowsString)

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

-- | Set the extension of a file, overwriting one if already present,
--   equivalent to -<.>.
--   
--   
--   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
--   
replaceExtension :: WindowsPath -> WindowsString -> WindowsPath

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

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

-- | Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
--   
--   Add an extension, even if there is already one there, equivalent to
--   <.>.
--   
--   
--   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 => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext"
--   addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
--   
addExtension :: WindowsPath -> WindowsString -> WindowsPath

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

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

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

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

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

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

-- | Does the given filename have the specified extension?
--   
--   
--   "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
--   
isExtensionOf :: WindowsString -> WindowsPath -> Bool

-- | Drop the given extension from a filepath, and the "."
--   preceding it. Returns Nothing if the filepath does not have the
--   given extension, or Just and the part before the extension if
--   it does.
--   
--   This function can be more predictable than dropExtensions,
--   especially if the filename might itself contain . characters.
--   
--   
--   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
--   
stripExtension :: WindowsString -> WindowsPath -> Maybe WindowsPath

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

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

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

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

-- | Get the base name, without an extension or path.
--   
--   
--   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"
--   
takeBaseName :: WindowsPath -> WindowsPath

-- | Set the base name.
--   
--   
--   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 => replaceBaseName x (takeBaseName x) == x
--   
replaceBaseName :: WindowsPath -> WindowsString -> WindowsPath

-- | Get the directory name, move up one level.
--   
--   
--   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:\\"
--   
takeDirectory :: WindowsPath -> WindowsPath

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

-- | An alias for </>.
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 readFile (dir </> file)
--   will access the same file as setCurrentDirectory dir; readFile
--   file.
--   
--   
--   "/directory" </> "file.ext" == "/directory\\file.ext"
--   "directory" </> "/file.ext" == "/file.ext"
--   Valid x => (takeDirectory x </> takeFileName x) `equalFilePath` x
--   
--   
--   Combined:
--   
--   
--   "C:\\foo" </> "bar" == "C:\\foo\\bar"
--   "home" </> "bob" == "home\\bob"
--   
--   
--   Not combined:
--   
--   
--   "home" </> "C:\\bob" == "C:\\bob"
--   
--   
--   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 </> is to never
--   combine these forms.
--   
--   
--   "home" </> "/bob" == "/bob"
--   "home" </> "\\bob" == "\\bob"
--   "C:\\home" </> "\\bob" == "\\bob"
--   
--   
--   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 </> is to never combine these forms.
--   
--   
--   "D:\\foo" </> "C:bar" == "C:bar"
--   "C:\\foo" </> "C:bar" == "C:bar"
--   
(</>) :: WindowsPath -> WindowsPath -> WindowsPath

-- | Split a path by the directory separator.
--   
--   
--   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"]
--   
splitPath :: WindowsPath -> [WindowsPath]

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

-- | Just as splitPath, but don't add the trailing slashes to each
--   element.
--   
--   
--   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 => joinPath (splitDirectories x) `equalFilePath` x
--   splitDirectories "" == []
--   splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
--   splitDirectories "/test///file" == ["/","test","file"]
--   
splitDirectories :: WindowsPath -> [WindowsPath]

-- | Split a path into a drive and a path.
--   
--   
--   uncurry (<>) (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")
--   
splitDrive :: WindowsPath -> (WindowsPath, WindowsPath)

-- | Join a drive and the rest of the path.
--   
--   
--   Valid x => 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"
--   
--   
--   Join a drive and the rest of the path.
--   
--   
--   Valid x => uncurry joinDrive (splitDrive x) == x
--   joinDrive "C:" "foo" == "C:foo"
--   joinDrive "C:\\" "bar" == "C:\\bar"
--   joinDrive "\\\\share" "foo" == "\\\\share\\foo"
--   joinDrive "/:" "foo" == "/:\\foo"
--   
joinDrive :: WindowsPath -> WindowsPath -> WindowsPath

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

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

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

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

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

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

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

-- | Normalise a file
--   
--   
--   
- // outside of the drive can be made blank
--   
- / -> pathSeparator
--   
- ./ -> ""
--   
--   
--   Does not remove "..", because of symlinks.
--   
--   
--   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 "." == "."
--   
normalise :: WindowsPath -> WindowsPath

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

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

-- | Is a path relative, or is it fixed to the root?
--   
--   
--   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
--   
--   
--   According to [1]:
--   
--   
--   
- "A UNC name of any format [is never relative]."
--   
- "You cannot use the "\?" prefix with a relative path."
--   
isRelative :: WindowsPath -> Bool

-- | 
--   not . isRelative
--   
--   
--   
--   isAbsolute x == not (isRelative x)
--   
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.
--   
--   
--   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
--   
isValid :: WindowsPath -> Bool

-- | Take a filepath and make it valid; does not change already valid
--   filepaths.
--   
--   
--   isValid (makeValid x)
--   isValid x ==> 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"
--   
makeValid :: WindowsPath -> WindowsPath
