-- 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: -- --
    --
  1. legacy filepaths: type FilePath = String
  2. --
  3. operating system abstracted filepaths (OsPath): -- internally unpinned ShortByteString (platform-dependent -- encoding)
  4. --
-- -- It is recommended to use OsPath when possible, because it is -- more correct. -- -- For each variant there are three main modules: -- -- -- -- System.OsString is like System.OsPath, but more general -- purpose. Refer to the documentation of those modules for more -- information. -- -- An introduction into the new API can be found in this blog -- post. Code examples for the new API can be found here. @package filepath @version 1.4.300.2 -- | 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. -- -- -- -- And we could have built an equivalent path with the following -- expressions: -- -- -- -- 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 -- | File and directory names are values of type String, whose -- precise meaning is operating system dependent. Files can be opened, -- yielding a handle which can then be used to operate on the contents of -- that file. type FilePath = String -- | The character that separates directories. In the case where more than -- one character is possible, 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 "file.tar.gz" == ("file",".tar.gz")
--   
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)
--   
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 -- -- -- -- 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]: -- -- 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. -- -- -- -- And we could have built an equivalent path with the following -- expressions: -- -- -- -- 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 -- | File and directory names are values of type String, whose -- precise meaning is operating system dependent. Files can be opened, -- yielding a handle which can then be used to operate on the contents of -- that file. type FilePath = String -- | The character that separates directories. In the case where more than -- one character is possible, 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 "file.tar.gz" == ("file",".tar.gz")
--   
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)
--   
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 -- -- -- -- 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]: -- -- 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. -- -- -- -- And we could have built an equivalent path with the following -- expressions: -- -- -- -- 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 -- | File and directory names are values of type String, whose -- precise meaning is operating system dependent. Files can be opened, -- yielding a handle which can then be used to operate on the contents of -- that file. type FilePath = String -- | The character that separates directories. In the case where more than -- one character is possible, 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 "file.tar.gz" == ("file",".tar.gz")
--   
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)
--   
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 -- -- -- -- 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]: -- -- 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 -- | Internal low-level utilities mostly for Word16, such as -- byte-array operations and other stuff not meant to be exported from -- Word16 module. -- | Deprecated: Use System.OsString.Data.ByteString.Short.Internal from -- os-string >= 2.0.0 package instead. This module will be removed in -- filepath >= 1.5. module System.OsPath.Data.ByteString.Short.Internal _nul :: Word16 isSpace :: Word16 -> Bool -- | Total conversion to char. word16ToChar :: Word16 -> Char create :: Int -> (forall s. MBA s -> ST s ()) -> ShortByteString asBA :: ShortByteString -> BA data BA BA# :: ByteArray# -> BA data MBA s MBA# :: MutableByteArray# s -> MBA s newPinnedByteArray :: Int -> ST s (MBA s) newByteArray :: Int -> ST s (MBA s) copyByteArray :: BA -> Int -> MBA s -> Int -> Int -> ST s () unsafeFreezeByteArray :: MBA s -> ST s BA copyAddrToByteArray :: Ptr a -> MBA RealWorld -> Int -> Int -> ST RealWorld () -- | O(n). Construct a new ShortByteString from a -- CWString. The resulting ShortByteString is an -- immutable copy of the original CWString, and is managed on -- the Haskell heap. The original CWString must be null -- terminated. packCWString :: Ptr Word16 -> IO ShortByteString -- | O(n). Construct a new ShortByteString from a -- CWStringLen. The resulting ShortByteString is an -- immutable copy of the original CWStringLen. The -- ShortByteString is a normal Haskell value and will be managed -- on the Haskell heap. packCWStringLen :: (Ptr Word16, Int) -> IO ShortByteString -- | O(n) construction. Use a ShortByteString with a -- function requiring a null-terminated CWString. The -- CWString is a copy and will be freed automatically; it must -- not be stored or used after the subcomputation finishes. useAsCWString :: ShortByteString -> (Ptr Word16 -> IO a) -> IO a -- | O(n) construction. Use a ShortByteString with a -- function requiring a CWStringLen. As for -- useAsCWString this function makes a copy of the original -- ShortByteString. It must not be stored or used after the -- subcomputation finishes. useAsCWStringLen :: ShortByteString -> ((Ptr Word16, Int) -> IO a) -> IO a -- | O(n) construction. Use a ShortByteString with a -- function requiring a CWStringLen. As for -- useAsCWString this function makes a copy of the original -- ShortByteString. It must not be stored or used after the -- subcomputation finishes. newCWString :: ShortByteString -> IO (Ptr Word16) moduleErrorIO :: String -> String -> IO a moduleErrorMsg :: String -> String -> String packWord16 :: [Word16] -> ShortByteString packLenWord16 :: Int -> [Word16] -> ShortByteString unpackWord16 :: ShortByteString -> [Word16] packWord16Rev :: [Word16] -> ShortByteString packLenWord16Rev :: Int -> [Word16] -> ShortByteString -- | This isn't strictly Word16 array write. Instead it's two consecutive -- Word8 array writes to avoid endianness issues due to primops doing -- automatic alignment based on host platform. We want to always write LE -- to the byte array. writeWord16Array :: MBA s -> Int -> Word16 -> ST s () indexWord8Array :: BA -> Int -> Word8 -- | This isn't strictly Word16 array read. Instead it's two Word8 array -- reads to avoid endianness issues due to primops doing automatic -- alignment based on host platform. We expect the byte array to be LE -- always. indexWord16Array :: BA -> Int -> Word16 encodeWord16LE# :: Word16# -> (# Word8#, Word8# #) decodeWord16LE# :: (# Word8#, Word8# #) -> Word16# setByteArray :: MBA s -> Int -> Int -> Int -> ST s () copyMutableByteArray :: MBA s -> Int -> MBA s -> Int -> Int -> ST s () -- | Given the maximum size needed and a function to make the contents of a -- ShortByteString, createAndTrim makes the ShortByteString. The -- generating function is required to return the actual final size (<= -- the maximum size) and the result value. The resulting byte array is -- realloced to this size. createAndTrim :: Int -> (forall s. MBA s -> ST s (Int, a)) -> (ShortByteString, a) createAndTrim' :: Int -> (forall s. MBA s -> ST s Int) -> ShortByteString createAndTrim'' :: Int -> (forall s. MBA s -> MBA s -> ST s (Int, Int)) -> (ShortByteString, ShortByteString) findIndexOrLength :: (Word16 -> Bool) -> ShortByteString -> Int -- | Returns the length of the substring matching, not the index. If no -- match, returns 0. findFromEndUntil :: (Word16 -> Bool) -> ShortByteString -> Int assertEven :: ShortByteString -> ShortByteString errorEmptySBS :: HasCallStack => String -> a moduleError :: HasCallStack => String -> String -> a compareByteArraysOff :: BA -> Int -> BA -> Int -> Int -> Int -- | A compact representation suitable for storing short byte strings in -- memory. -- -- In typical use cases it can be imported alongside -- Data.ByteString, e.g. -- --
--   import qualified Data.ByteString       as B
--   import qualified Data.ByteString.Short as B
--            (ShortByteString, toShort, fromShort)
--   
-- -- Other ShortByteString operations clash with -- Data.ByteString or Prelude functions however, so they -- should be imported qualified with a different alias e.g. -- --
--   import qualified Data.ByteString.Short as B.Short
--   
-- | Deprecated: Use System.OsString.Data.ByteString.Short from -- os-string >= 2.0.0 package instead. This module will be removed in -- filepath >= 1.5. module System.OsPath.Data.ByteString.Short -- | A compact representation of a Word8 vector. -- -- It has a lower memory overhead than a ByteString and does not -- contribute to heap fragmentation. It can be converted to or from a -- ByteString (at the cost of copying the string data). It -- supports very few other operations. data () => ShortByteString SBS :: ByteArray# -> ShortByteString -- | O(1). The empty ShortByteString. empty :: ShortByteString -- | O(1) Convert a Word8 into a ShortByteString singleton :: Word8 -> ShortByteString -- | O(n). Convert a list into a ShortByteString pack :: [Word8] -> ShortByteString -- | O(n). Convert a ShortByteString into a list. unpack :: ShortByteString -> [Word8] -- | O(n). Convert a ShortByteString into a -- ByteString. fromShort :: ShortByteString -> ByteString -- | O(n). Convert a ByteString into a -- ShortByteString. -- -- This makes a copy, so does not retain the input string. toShort :: ByteString -> ShortByteString -- | O(n) Append a byte to the end of a ShortByteString -- -- Note: copies the entire byte array snoc :: ShortByteString -> Word8 -> ShortByteString infixl 5 `snoc` -- | O(n) cons is analogous to (:) for lists. -- -- Note: copies the entire byte array cons :: Word8 -> ShortByteString -> ShortByteString infixr 5 `cons` append :: ShortByteString -> ShortByteString -> ShortByteString -- | O(1) Extract the last element of a ShortByteString, which must -- be finite and non-empty. An exception will be thrown in the case of an -- empty ShortByteString. -- -- This is a partial function, consider using unsnoc instead. last :: HasCallStack => ShortByteString -> Word8 -- | O(n) Extract the elements after the head of a ShortByteString, -- which must be non-empty. An exception will be thrown in the case of an -- empty ShortByteString. -- -- This is a partial function, consider using uncons instead. -- -- Note: copies the entire byte array tail :: HasCallStack => ShortByteString -> ShortByteString -- | O(n) Extract the head and tail of a -- ShortByteString, returning Nothing if it is empty. uncons :: ShortByteString -> Maybe (Word8, ShortByteString) uncons2 :: ShortByteString -> Maybe (Word8, Word8, ShortByteString) -- | O(1) Extract the first element of a ShortByteString, which must -- be non-empty. An exception will be thrown in the case of an empty -- ShortByteString. -- -- This is a partial function, consider using uncons instead. head :: HasCallStack => ShortByteString -> Word8 -- | O(n) Return all the elements of a ShortByteString except -- the last one. An exception will be thrown in the case of an empty -- ShortByteString. -- -- This is a partial function, consider using unsnoc instead. -- -- Note: copies the entire byte array init :: HasCallStack => ShortByteString -> ShortByteString -- | O(n) Extract the init and last of a -- ShortByteString, returning Nothing if it is empty. unsnoc :: ShortByteString -> Maybe (ShortByteString, Word8) -- | O(1) Test whether a ShortByteString is empty. null :: ShortByteString -> Bool -- | O(1) The length of a ShortByteString. length :: ShortByteString -> Int -- | O(n) map f xs is the ShortByteString obtained -- by applying f to each element of xs. map :: (Word8 -> Word8) -> ShortByteString -> ShortByteString -- | O(n) reverse xs efficiently returns the -- elements of xs in reverse order. reverse :: ShortByteString -> ShortByteString -- | O(n) The intercalate function takes a -- ShortByteString and a list of ShortByteStrings and -- concatenates the list after interspersing the first argument between -- each element of the list. intercalate :: ShortByteString -> [ShortByteString] -> ShortByteString -- | foldl, applied to a binary operator, a starting value -- (typically the left-identity of the operator), and a ShortByteString, -- reduces the ShortByteString using the binary operator, from left to -- right. foldl :: (a -> Word8 -> a) -> a -> ShortByteString -> a -- | foldl' is like foldl, but strict in the accumulator. foldl' :: (a -> Word8 -> a) -> a -> ShortByteString -> a -- | foldl1 is a variant of foldl that has no starting value -- argument, and thus must be applied to non-empty -- ShortByteStrings. An exception will be thrown in the case of an -- empty ShortByteString. foldl1 :: HasCallStack => (Word8 -> Word8 -> Word8) -> ShortByteString -> Word8 -- | foldl1' is like foldl1, but strict in the accumulator. -- An exception will be thrown in the case of an empty ShortByteString. foldl1' :: HasCallStack => (Word8 -> Word8 -> Word8) -> ShortByteString -> Word8 -- | foldr, applied to a binary operator, a starting value -- (typically the right-identity of the operator), and a ShortByteString, -- reduces the ShortByteString using the binary operator, from right to -- left. foldr :: (Word8 -> a -> a) -> a -> ShortByteString -> a -- | foldr' is like foldr, but strict in the accumulator. foldr' :: (Word8 -> a -> a) -> a -> ShortByteString -> a -- | foldr1 is a variant of foldr that has no starting value -- argument, and thus must be applied to non-empty -- ShortByteStrings An exception will be thrown in the case of an -- empty ShortByteString. foldr1 :: HasCallStack => (Word8 -> Word8 -> Word8) -> ShortByteString -> Word8 -- | foldr1' is a variant of foldr1, but is strict in the -- accumulator. foldr1' :: HasCallStack => (Word8 -> Word8 -> Word8) -> ShortByteString -> Word8 -- | O(n) Applied to a predicate and a ShortByteString, -- all determines if all elements of the ShortByteString -- satisfy the predicate. all :: (Word8 -> Bool) -> ShortByteString -> Bool -- | O(n) Applied to a predicate and a ShortByteString, -- any determines if any element of the ShortByteString -- satisfies the predicate. any :: (Word8 -> Bool) -> ShortByteString -> Bool concat :: [ShortByteString] -> ShortByteString -- | O(n) replicate n x is a ShortByteString of -- length n with x the value of every element. The -- following holds: -- --
--   replicate w c = unfoldr w (\u -> Just (u,u)) c
--   
replicate :: Int -> Word8 -> ShortByteString -- | O(n), where n is the length of the result. The -- unfoldr function is analogous to the List 'unfoldr'. -- unfoldr builds a ShortByteString from a seed value. The -- function takes the element and returns Nothing if it is done -- producing the ShortByteString or returns Just (a,b), -- in which case, a is the next byte in the string, and -- b is the seed value for further production. -- -- This function is not efficient/safe. It will build a list of -- [Word8] and run the generator until it returns -- Nothing, otherwise recurse infinitely, then finally create a -- ShortByteString. -- -- If you know the maximum length, consider using unfoldrN. -- -- Examples: -- --
--      unfoldr (\x -> if x <= 5 then Just (x, x + 1) else Nothing) 0
--   == pack [0, 1, 2, 3, 4, 5]
--   
unfoldr :: (a -> Maybe (Word8, a)) -> a -> ShortByteString -- | O(n) Like unfoldr, unfoldrN builds a -- ShortByteString from a seed value. However, the length of the result -- is limited by the first argument to unfoldrN. This function is -- more efficient than unfoldr when the maximum length of the -- result is known. -- -- The following equation relates unfoldrN and unfoldr: -- --
--   fst (unfoldrN n f s) == take n (unfoldr f s)
--   
unfoldrN :: Int -> (a -> Maybe (Word8, a)) -> a -> (ShortByteString, Maybe a) -- | O(n) take n, applied to a ShortByteString -- xs, returns the prefix of xs of length n, -- or xs itself if n > length xs. -- -- Note: copies the entire byte array take :: Int -> ShortByteString -> ShortByteString -- | O(n) takeEnd n xs is equivalent to -- drop (length xs - n) xs. Takes n -- elements from end of bytestring. -- --
--   >>> takeEnd 3 "abcdefg"
--   "efg"
--   
--   >>> takeEnd 0 "abcdefg"
--   ""
--   
--   >>> takeEnd 4 "abc"
--   "abc"
--   
takeEnd :: Int -> ShortByteString -> ShortByteString -- | Returns the longest (possibly empty) suffix of elements satisfying the -- predicate. -- -- takeWhileEnd p is equivalent to reverse . -- takeWhile p . reverse. takeWhileEnd :: (Word8 -> Bool) -> ShortByteString -> ShortByteString -- | Similar to takeWhile, returns the longest (possibly empty) -- prefix of elements satisfying the predicate. takeWhile :: (Word8 -> Bool) -> ShortByteString -> ShortByteString -- | O(n) drop n xs returns the suffix of -- xs after the first n elements, or empty if n > -- length xs. -- -- Note: copies the entire byte array drop :: Int -> ShortByteString -> ShortByteString -- | O(n) dropEnd n xs is equivalent to -- take (length xs - n) xs. Drops n -- elements from end of bytestring. -- --
--   >>> dropEnd 3 "abcdefg"
--   "abcd"
--   
--   >>> dropEnd 0 "abcdefg"
--   "abcdefg"
--   
--   >>> dropEnd 4 "abc"
--   ""
--   
dropEnd :: Int -> ShortByteString -> ShortByteString -- | Similar to dropWhile, drops the longest (possibly empty) prefix -- of elements satisfying the predicate and returns the remainder. -- -- Note: copies the entire byte array dropWhile :: (Word8 -> Bool) -> ShortByteString -> ShortByteString -- | Similar to dropWhileEnd, drops the longest (possibly empty) -- suffix of elements satisfying the predicate and returns the remainder. -- -- dropWhileEnd p is equivalent to reverse . -- dropWhile p . reverse. dropWhileEnd :: (Word8 -> Bool) -> ShortByteString -> ShortByteString -- | Returns the longest (possibly empty) suffix of elements which do -- not satisfy the predicate and the remainder of the string. -- -- breakEnd p is equivalent to spanEnd (not . -- p) and to (takeWhileEnd (not . p) &&& -- dropWhileEnd (not . p)). breakEnd :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | Similar to break, returns the longest (possibly empty) prefix -- of elements which do not satisfy the predicate and the -- remainder of the string. -- -- break p is equivalent to span (not . -- p) and to (takeWhile (not . p) &&& -- dropWhile (not . p)). break :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | Similar to span, returns the longest (possibly empty) prefix of -- elements satisfying the predicate and the remainder of the string. -- -- span p is equivalent to break (not . -- p) and to (takeWhile p &&& -- dropWhile p). span :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | Returns the longest (possibly empty) suffix of elements satisfying the -- predicate and the remainder of the string. -- -- spanEnd p is equivalent to breakEnd (not . -- p) and to (takeWhileEnd p &&& -- dropWhileEnd p). -- -- We have -- --
--   spanEnd (not . isSpace) "x y z" == ("x y ", "z")
--   
-- -- and -- --
--   spanEnd (not . isSpace) sbs
--      ==
--   let (x, y) = span (not . isSpace) (reverse sbs) in (reverse y, reverse x)
--   
spanEnd :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | O(n) splitAt n sbs is equivalent to -- (take n sbs, drop n sbs). -- -- Note: copies the substrings splitAt :: Int -> ShortByteString -> (ShortByteString, ShortByteString) -- | O(n) Break a ShortByteString into pieces separated by -- the byte argument, consuming the delimiter. I.e. -- --
--   split 10  "a\nb\nd\ne" == ["a","b","d","e"]   -- fromEnum '\n' == 10
--   split 97  "aXaXaXa"    == ["","X","X","X",""] -- fromEnum 'a' == 97
--   split 120 "x"          == ["",""]             -- fromEnum 'x' == 120
--   split undefined ""     == []                  -- and not [""]
--   
-- -- and -- --
--   intercalate [c] . split c == id
--   split == splitWith . (==)
--   
-- -- Note: copies the substrings split :: Word8 -> ShortByteString -> [ShortByteString] -- | O(n) Splits a ShortByteString into components delimited -- by separators, where the predicate returns True for a separator -- element. The resulting components do not contain the separators. Two -- adjacent separators result in an empty component in the output. eg. -- --
--   splitWith (==97) "aabbaca" == ["","","bb","c",""] -- fromEnum 'a' == 97
--   splitWith undefined ""     == []                  -- and not [""]
--   
splitWith :: (Word8 -> Bool) -> ShortByteString -> [ShortByteString] -- | O(n) The stripSuffix function takes two ShortByteStrings -- and returns Just the remainder of the second iff the first is -- its suffix, and otherwise Nothing. stripSuffix :: ShortByteString -> ShortByteString -> Maybe ShortByteString -- | O(n) The stripPrefix function takes two ShortByteStrings -- and returns Just the remainder of the second iff the first is -- its prefix, and otherwise Nothing. stripPrefix :: ShortByteString -> ShortByteString -> Maybe ShortByteString -- | Check whether one string is a substring of another. isInfixOf :: ShortByteString -> ShortByteString -> Bool -- | O(n) The isPrefixOf function takes two ShortByteStrings -- and returns True isPrefixOf :: ShortByteString -> ShortByteString -> Bool -- | O(n) The isSuffixOf function takes two ShortByteStrings -- and returns True iff the first is a suffix of the second. -- -- The following holds: -- --
--   isSuffixOf x y == reverse x `isPrefixOf` reverse y
--   
isSuffixOf :: ShortByteString -> ShortByteString -> Bool -- | Break a string on a substring, returning a pair of the part of the -- string prior to the match, and the rest of the string. -- -- The following relationships hold: -- --
--   break (== c) l == breakSubstring (singleton c) l
--   
-- -- For example, to tokenise a string, dropping delimiters: -- --
--   tokenise x y = h : if null t then [] else tokenise x (drop (length x) t)
--       where (h,t) = breakSubstring x y
--   
-- -- To skip to the first occurrence of a string: -- --
--   snd (breakSubstring x y)
--   
-- -- To take the parts of a string before a delimiter: -- --
--   fst (breakSubstring x y)
--   
-- -- Note that calling `breakSubstring x` does some preprocessing work, so -- you should avoid unnecessarily duplicating breakSubstring calls with -- the same pattern. breakSubstring :: ShortByteString -> ShortByteString -> (ShortByteString, ShortByteString) -- | O(n) elem is the ShortByteString membership -- predicate. elem :: Word8 -> ShortByteString -> Bool -- | O(n) The find function takes a predicate and a -- ShortByteString, and returns the first element in matching the -- predicate, or Nothing if there is no such element. -- --
--   find f p = case findIndex f p of Just n -> Just (p ! n) ; _ -> Nothing
--   
find :: (Word8 -> Bool) -> ShortByteString -> Maybe Word8 -- | O(n) filter, applied to a predicate and a -- ShortByteString, returns a ShortByteString containing those characters -- that satisfy the predicate. filter :: (Word8 -> Bool) -> ShortByteString -> ShortByteString -- | O(n) The partition function takes a predicate a -- ShortByteString and returns the pair of ShortByteStrings with elements -- which do and do not satisfy the predicate, respectively; i.e., -- --
--   partition p bs == (filter p sbs, filter (not . p) sbs)
--   
partition :: (Word8 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | O(1) ShortByteString index (subscript) operator, -- starting from 0. -- -- This is a partial function, consider using indexMaybe instead. index :: HasCallStack => ShortByteString -> Int -> Word8 -- | O(1) ShortByteString index, starting from 0, that -- returns Just if: -- --
--   0 <= n < length bs
--   
indexMaybe :: ShortByteString -> Int -> Maybe Word8 -- | O(1) ShortByteString index, starting from 0, that -- returns Just if: -- --
--   0 <= n < length bs
--   
(!?) :: ShortByteString -> Int -> Maybe Word8 -- | O(n) The elemIndex function returns the index of the -- first element in the given ShortByteString which is equal to -- the query element, or Nothing if there is no such element. elemIndex :: Word8 -> ShortByteString -> Maybe Int -- | O(n) The elemIndices function extends elemIndex, -- by returning the indices of all elements equal to the query element, -- in ascending order. elemIndices :: Word8 -> ShortByteString -> [Int] -- | count returns the number of times its argument appears in the -- ShortByteString count :: Word8 -> ShortByteString -> Int -- | O(n) The findIndex function takes a predicate and a -- ShortByteString and returns the index of the first element in -- the ShortByteString satisfying the predicate. findIndex :: (Word8 -> Bool) -> ShortByteString -> Maybe Int -- | O(n) The findIndices function extends findIndex, -- by returning the indices of all elements satisfying the predicate, in -- ascending order. findIndices :: (Word8 -> Bool) -> ShortByteString -> [Int] -- | O(n). Construct a new ShortByteString from a -- CString. The resulting ShortByteString is an -- immutable copy of the original CString, and is managed on the -- Haskell heap. The original CString must be null terminated. packCString :: CString -> IO ShortByteString -- | O(n). Construct a new ShortByteString from a -- CStringLen. The resulting ShortByteString is an -- immutable copy of the original CStringLen. The -- ShortByteString is a normal Haskell value and will be managed -- on the Haskell heap. packCStringLen :: CStringLen -> IO ShortByteString -- | O(n) construction. Use a ShortByteString with a -- function requiring a null-terminated CString. The -- CString is a copy and will be freed automatically; it must -- not be stored or used after the subcomputation finishes. useAsCString :: ShortByteString -> (CString -> IO a) -> IO a -- | O(n) construction. Use a ShortByteString with a -- function requiring a CStringLen. As for useAsCString -- this function makes a copy of the original ShortByteString. -- It must not be stored or used after the subcomputation finishes. useAsCStringLen :: ShortByteString -> (CStringLen -> IO a) -> IO a -- | ShortByteStrings encoded as UTF16-LE, suitable for windows FFI calls. -- -- Word16s are *always* in BE encoding (both input and output), so e.g. -- pack takes a list of BE encoded [Word16] and produces -- a UTF16-LE encoded ShortByteString. -- -- Likewise, unpack takes a UTF16-LE encoded ShortByteString and -- produces a list of BE encoded [Word16]. -- -- Indices and lengths are always in respect to Word16, not Word8. -- -- All functions will error out if the input string is not a valid UTF16 -- stream (uneven number of bytes). So use this module with caution. -- | Deprecated: Use System.OsString.Data.ByteString.Short.Word16 from -- os-string >= 2.0.0 package instead. This module will be removed in -- filepath >= 1.5. module System.OsPath.Data.ByteString.Short.Word16 -- | A compact representation of a Word8 vector. -- -- It has a lower memory overhead than a ByteString and does not -- contribute to heap fragmentation. It can be converted to or from a -- ByteString (at the cost of copying the string data). It -- supports very few other operations. data () => ShortByteString SBS :: ByteArray# -> ShortByteString -- | O(1). The empty ShortByteString. empty :: ShortByteString -- | O(1) Convert a Word16 into a ShortByteString singleton :: Word16 -> ShortByteString -- | O(n). Convert a list into a ShortByteString pack :: [Word16] -> ShortByteString -- | O(n). Convert a ShortByteString into a list. unpack :: ShortByteString -> [Word16] -- | O(n). Convert a ShortByteString into a -- ByteString. fromShort :: ShortByteString -> ByteString -- | O(n). Convert a ByteString into a -- ShortByteString. -- -- This makes a copy, so does not retain the input string. toShort :: ByteString -> ShortByteString -- | O(n) Append a Word16 to the end of a ShortByteString -- -- Note: copies the entire byte array snoc :: ShortByteString -> Word16 -> ShortByteString infixl 5 `snoc` -- | O(n) cons is analogous to (:) for lists. -- -- Note: copies the entire byte array cons :: Word16 -> ShortByteString -> ShortByteString infixr 5 `cons` append :: ShortByteString -> ShortByteString -> ShortByteString -- | O(1) Extract the last element of a ShortByteString, which must -- be finite and at least one Word16. An exception will be thrown in the -- case of an empty ShortByteString. last :: HasCallStack => ShortByteString -> Word16 -- | O(n) Extract the elements after the head of a ShortByteString, -- which must at least one Word16. An exception will be thrown in the -- case of an empty ShortByteString. -- -- Note: copies the entire byte array tail :: HasCallStack => ShortByteString -> ShortByteString -- | O(n) Extract the head and tail of a ByteString, returning -- Nothing if it is empty. uncons :: ShortByteString -> Maybe (Word16, ShortByteString) -- | O(n) Extract first two elements and the rest of a ByteString, -- returning Nothing if it is shorter than two elements. uncons2 :: ShortByteString -> Maybe (Word16, Word16, ShortByteString) -- | O(1) Extract the first element of a ShortByteString, which must -- be at least one Word16. An exception will be thrown in the case of an -- empty ShortByteString. head :: HasCallStack => ShortByteString -> Word16 -- | O(n) Return all the elements of a ShortByteString except -- the last one. An exception will be thrown in the case of an empty -- ShortByteString. -- -- Note: copies the entire byte array init :: HasCallStack => ShortByteString -> ShortByteString -- | O(n) Extract the init and last of a ByteString, -- returning Nothing if it is empty. unsnoc :: ShortByteString -> Maybe (ShortByteString, Word16) -- | O(1) Test whether a ShortByteString is empty. null :: ShortByteString -> Bool -- | O(1) The length of a ShortByteString. length :: ShortByteString -> Int -- | This is like length, but the number of Word16, not -- Word8. numWord16 :: ShortByteString -> Int -- | O(n) map f xs is the ShortByteString obtained -- by applying f to each element of xs. map :: (Word16 -> Word16) -> ShortByteString -> ShortByteString -- | O(n) reverse xs efficiently returns the -- elements of xs in reverse order. reverse :: ShortByteString -> ShortByteString -- | O(n) The intercalate function takes a -- ShortByteString and a list of ShortByteStrings and -- concatenates the list after interspersing the first argument between -- each element of the list. intercalate :: ShortByteString -> [ShortByteString] -> ShortByteString -- | foldl, applied to a binary operator, a starting value -- (typically the left-identity of the operator), and a ShortByteString, -- reduces the ShortByteString using the binary operator, from left to -- right. foldl :: (a -> Word16 -> a) -> a -> ShortByteString -> a -- | foldl' is like foldl, but strict in the accumulator. foldl' :: (a -> Word16 -> a) -> a -> ShortByteString -> a -- | foldl1 is a variant of foldl that has no starting value -- argument, and thus must be applied to non-empty -- ShortByteStrings. An exception will be thrown in the case of an -- empty ShortByteString. foldl1 :: HasCallStack => (Word16 -> Word16 -> Word16) -> ShortByteString -> Word16 -- | foldl1' is like foldl1, but strict in the accumulator. -- An exception will be thrown in the case of an empty ShortByteString. foldl1' :: HasCallStack => (Word16 -> Word16 -> Word16) -> ShortByteString -> Word16 -- | foldr, applied to a binary operator, a starting value -- (typically the right-identity of the operator), and a ShortByteString, -- reduces the ShortByteString using the binary operator, from right to -- left. foldr :: (Word16 -> a -> a) -> a -> ShortByteString -> a -- | foldr' is like foldr, but strict in the accumulator. foldr' :: (Word16 -> a -> a) -> a -> ShortByteString -> a -- | foldr1 is a variant of foldr that has no starting value -- argument, and thus must be applied to non-empty -- ShortByteStrings An exception will be thrown in the case of an -- empty ShortByteString. foldr1 :: HasCallStack => (Word16 -> Word16 -> Word16) -> ShortByteString -> Word16 -- | foldr1' is a variant of foldr1, but is strict in the -- accumulator. foldr1' :: HasCallStack => (Word16 -> Word16 -> Word16) -> ShortByteString -> Word16 -- | O(n) Applied to a predicate and a ShortByteString, -- all determines if all elements of the ShortByteString -- satisfy the predicate. all :: (Word16 -> Bool) -> ShortByteString -> Bool -- | O(n) Applied to a predicate and a ByteString, any -- determines if any element of the ByteString satisfies the -- predicate. any :: (Word16 -> Bool) -> ShortByteString -> Bool concat :: [ShortByteString] -> ShortByteString -- | O(n) replicate n x is a ByteString of length -- n with x the value of every element. The following -- holds: -- --
--   replicate w c = unfoldr w (\u -> Just (u,u)) c
--   
replicate :: Int -> Word16 -> ShortByteString -- | O(n), where n is the length of the result. The -- unfoldr function is analogous to the List 'unfoldr'. -- unfoldr builds a ShortByteString from a seed value. The -- function takes the element and returns Nothing if it is done -- producing the ShortByteString or returns Just (a,b), -- in which case, a is the next byte in the string, and -- b is the seed value for further production. -- -- This function is not efficient/safe. It will build a list of -- [Word16] and run the generator until it returns -- Nothing, otherwise recurse infinitely, then finally create a -- ShortByteString. -- -- Examples: -- --
--      unfoldr (\x -> if x <= 5 then Just (x, x + 1) else Nothing) 0
--   == pack [0, 1, 2, 3, 4, 5]
--   
unfoldr :: (a -> Maybe (Word16, a)) -> a -> ShortByteString -- | O(n) Like unfoldr, unfoldrN builds a -- ShortByteString from a seed value. However, the length of the result -- is limited by the first argument to unfoldrN. This function is -- more efficient than unfoldr when the maximum length of the -- result is known. -- -- The following equation relates unfoldrN and unfoldr: -- --
--   fst (unfoldrN n f s) == take n (unfoldr f s)
--   
unfoldrN :: forall a. Int -> (a -> Maybe (Word16, a)) -> a -> (ShortByteString, Maybe a) -- | O(n) take n, applied to a ShortByteString -- xs, returns the prefix of xs of length n, -- or xs itself if n > length xs. -- -- Note: copies the entire byte array take :: Int -> ShortByteString -> ShortByteString -- | O(1) takeEnd n xs is equivalent to -- drop (length xs - n) xs. Takes n -- elements from end of bytestring. -- --
--   >>> takeEnd 3 "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   "e\NULf\NULg\NUL"
--   
--   >>> takeEnd 0 "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   ""
--   
--   >>> takeEnd 4 "a\NULb\NULc\NUL"
--   "a\NULb\NULc\NUL"
--   
takeEnd :: Int -> ShortByteString -> ShortByteString -- | Returns the longest (possibly empty) suffix of elements satisfying the -- predicate. -- -- takeWhileEnd p is equivalent to reverse . -- takeWhile p . reverse. takeWhileEnd :: (Word16 -> Bool) -> ShortByteString -> ShortByteString -- | Similar to takeWhile, returns the longest (possibly empty) -- prefix of elements satisfying the predicate. takeWhile :: (Word16 -> Bool) -> ShortByteString -> ShortByteString -- | O(n) drop n xs returns the suffix of -- xs after the first n elements, or [] if n > -- length xs. -- -- Note: copies the entire byte array drop :: Int -> ShortByteString -> ShortByteString -- | O(1) dropEnd n xs is equivalent to -- take (length xs - n) xs. Drops n -- elements from end of bytestring. -- --
--   >>> dropEnd 3 "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   "a\NULb\NULc\NULd\NUL"
--   
--   >>> dropEnd 0 "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   "a\NULb\NULc\NULd\NULe\NULf\NULg\NUL"
--   
--   >>> dropEnd 4 "a\NULb\NULc\NUL"
--   ""
--   
dropEnd :: Int -> ShortByteString -> ShortByteString -- | Similar to dropWhile, drops the longest (possibly empty) prefix -- of elements satisfying the predicate and returns the remainder. -- -- Note: copies the entire byte array dropWhile :: (Word16 -> Bool) -> ShortByteString -> ShortByteString -- | Similar to dropWhileEnd, drops the longest (possibly empty) -- suffix of elements satisfying the predicate and returns the remainder. -- -- dropWhileEnd p is equivalent to reverse . -- dropWhile p . reverse. dropWhileEnd :: (Word16 -> Bool) -> ShortByteString -> ShortByteString -- | Returns the longest (possibly empty) suffix of elements which do -- not satisfy the predicate and the remainder of the string. -- -- breakEnd p is equivalent to spanEnd (not . -- p) and to (takeWhileEnd (not . p) &&& -- dropWhileEnd (not . p)). breakEnd :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | Similar to break, returns the longest (possibly empty) prefix -- of elements which do not satisfy the predicate and the -- remainder of the string. -- -- break p is equivalent to span (not . -- p) and to (takeWhile (not . p) &&& -- dropWhile (not . p)). break :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | Similar to span, returns the longest (possibly empty) prefix of -- elements satisfying the predicate and the remainder of the string. -- -- span p is equivalent to break (not . -- p) and to (takeWhile p &&& -- dropWhile p). span :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | Returns the longest (possibly empty) suffix of elements satisfying the -- predicate and the remainder of the string. -- -- spanEnd p is equivalent to breakEnd (not . -- p) and to (takeWhileEnd p &&& -- dropWhileEnd p). -- -- We have -- --
--   spanEnd (not . isSpace) "x y z" == ("x y ", "z")
--   
-- -- and -- --
--   spanEnd (not . isSpace) ps
--      ==
--   let (x, y) = span (not . isSpace) (reverse ps) in (reverse y, reverse x)
--   
spanEnd :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | O(n) splitAt n xs is equivalent to -- (take n xs, drop n xs). -- -- Note: copies the substrings splitAt :: Int -> ShortByteString -> (ShortByteString, ShortByteString) -- | O(n) Break a ShortByteString into pieces separated by -- the byte argument, consuming the delimiter. I.e. -- --
--   split 10  "a\nb\nd\ne" == ["a","b","d","e"]   -- fromEnum '\n' == 10
--   split 97  "aXaXaXa"    == ["","X","X","X",""] -- fromEnum 'a' == 97
--   split 120 "x"          == ["",""]             -- fromEnum 'x' == 120
--   split undefined ""     == []                  -- and not [""]
--   
-- -- and -- --
--   intercalate [c] . split c == id
--   split == splitWith . (==)
--   
-- -- Note: copies the substrings split :: Word16 -> ShortByteString -> [ShortByteString] -- | O(n) Splits a ShortByteString into components delimited -- by separators, where the predicate returns True for a separator -- element. The resulting components do not contain the separators. Two -- adjacent separators result in an empty component in the output. eg. -- --
--   splitWith (==97) "aabbaca" == ["","","bb","c",""] -- fromEnum 'a' == 97
--   splitWith undefined ""     == []                  -- and not [""]
--   
splitWith :: (Word16 -> Bool) -> ShortByteString -> [ShortByteString] -- | O(n) The stripSuffix function takes two ShortByteStrings -- and returns Just the remainder of the second iff the first is -- its suffix, and otherwise Nothing. stripSuffix :: ShortByteString -> ShortByteString -> Maybe ShortByteString -- | O(n) The stripPrefix function takes two ShortByteStrings -- and returns Just the remainder of the second iff the first is -- its prefix, and otherwise Nothing. stripPrefix :: ShortByteString -> ShortByteString -> Maybe ShortByteString -- | Check whether one string is a substring of another. isInfixOf :: ShortByteString -> ShortByteString -> Bool -- | O(n) The isPrefixOf function takes two ShortByteStrings -- and returns True isPrefixOf :: ShortByteString -> ShortByteString -> Bool -- | O(n) The isSuffixOf function takes two ShortByteStrings -- and returns True iff the first is a suffix of the second. -- -- The following holds: -- --
--   isSuffixOf x y == reverse x `isPrefixOf` reverse y
--   
isSuffixOf :: ShortByteString -> ShortByteString -> Bool breakSubstring :: ShortByteString -> ShortByteString -> (ShortByteString, ShortByteString) -- | O(n) elem is the ShortByteString membership -- predicate. elem :: Word16 -> ShortByteString -> Bool -- | O(n) The find function takes a predicate and a -- ByteString, and returns the first element in matching the predicate, -- or Nothing if there is no such element. -- --
--   find f p = case findIndex f p of Just n -> Just (p ! n) ; _ -> Nothing
--   
find :: (Word16 -> Bool) -> ShortByteString -> Maybe Word16 -- | O(n) filter, applied to a predicate and a ByteString, -- returns a ByteString containing those characters that satisfy the -- predicate. filter :: (Word16 -> Bool) -> ShortByteString -> ShortByteString -- | O(n) The partition function takes a predicate a -- ByteString and returns the pair of ByteStrings with elements which do -- and do not satisfy the predicate, respectively; i.e., -- --
--   partition p bs == (filter p xs, filter (not . p) xs)
--   
partition :: (Word16 -> Bool) -> ShortByteString -> (ShortByteString, ShortByteString) -- | O(1) ShortByteString index (subscript) operator, -- starting from 0. index :: HasCallStack => ShortByteString -> Int -> Word16 -- | O(1) ShortByteString index, starting from 0, that -- returns Just if: -- --
--   0 <= n < length bs
--   
indexMaybe :: ShortByteString -> Int -> Maybe Word16 -- | O(1) ShortByteString index, starting from 0, that -- returns Just if: -- --
--   0 <= n < length bs
--   
(!?) :: ShortByteString -> Int -> Maybe Word16 -- | O(n) The elemIndex function returns the index of the -- first element in the given ShortByteString which is equal to -- the query element, or Nothing if there is no such element. elemIndex :: Word16 -> ShortByteString -> Maybe Int -- | O(n) The elemIndices function extends elemIndex, -- by returning the indices of all elements equal to the query element, -- in ascending order. elemIndices :: Word16 -> ShortByteString -> [Int] -- | count returns the number of times its argument appears in the -- ShortByteString count :: Word16 -> ShortByteString -> Int -- | O(n) The findIndex function takes a predicate and a -- ShortByteString and returns the index of the first element in -- the ByteString satisfying the predicate. findIndex :: (Word16 -> Bool) -> ShortByteString -> Maybe Int -- | O(n) The findIndices function extends findIndex, -- by returning the indices of all elements satisfying the predicate, in -- ascending order. findIndices :: (Word16 -> Bool) -> ShortByteString -> [Int] -- | O(n). Construct a new ShortByteString from a -- CWString. The resulting ShortByteString is an -- immutable copy of the original CWString, and is managed on -- the Haskell heap. The original CWString must be null -- terminated. packCWString :: Ptr Word16 -> IO ShortByteString -- | O(n). Construct a new ShortByteString from a -- CWStringLen. The resulting ShortByteString is an -- immutable copy of the original CWStringLen. The -- ShortByteString is a normal Haskell value and will be managed -- on the Haskell heap. packCWStringLen :: (Ptr Word16, Int) -> IO ShortByteString -- | O(n) construction. Use a ShortByteString with a -- function requiring a CWStringLen. As for -- useAsCWString this function makes a copy of the original -- ShortByteString. It must not be stored or used after the -- subcomputation finishes. newCWString :: ShortByteString -> IO (Ptr Word16) -- | O(n) construction. Use a ShortByteString with a -- function requiring a null-terminated CWString. The -- CWString is a copy and will be freed automatically; it must -- not be stored or used after the subcomputation finishes. useAsCWString :: ShortByteString -> (Ptr Word16 -> IO a) -> IO a -- | O(n) construction. Use a ShortByteString with a -- function requiring a CWStringLen. As for -- useAsCWString this function makes a copy of the original -- ShortByteString. It must not be stored or used after the -- subcomputation finishes. useAsCWStringLen :: ShortByteString -> ((Ptr Word16, Int) -> IO a) -> IO a -- | Deprecated: Use System.OsString.Encoding.Internal from os-string -- >= 2.0.0 package instead. This module will be removed in filepath -- >= 1.5. module System.OsPath.Encoding.Internal ucs2le :: TextEncoding mkUcs2le :: CodingFailureMode -> TextEncoding ucs2le_DF :: CodingFailureMode -> IO (TextDecoder ()) ucs2le_EF :: CodingFailureMode -> IO (TextEncoder ()) ucs2le_decode :: DecodeBuffer ucs2le_encode :: EncodeBuffer -- | Mimics the base encoding for filesystem operations. This should be -- total on all inputs (word16 byte arrays). -- -- Note that this has a subtle difference to -- 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 cWcharsToChars_UCS2 :: [Word16] -> [Char] cWcharsToChars :: [Word16] -> [Char] charsToCWchars :: [Char] -> [Word16] withFilePathWin :: FilePath -> (Int -> Ptr Word16 -> IO a) -> IO a peekFilePathWin :: (Ptr Word16, Int) -> IO FilePath withFilePathPosix :: FilePath -> (CStringLen -> IO a) -> IO a peekFilePathPosix :: CStringLen -> IO FilePath -- | Decode with the given TextEncoding. decodeWithTE :: TextEncoding -> ShortByteString -> Either EncodingException String -- | Encode with the given TextEncoding. encodeWithTE :: TextEncoding -> String -> Either EncodingException ShortByteString -- | This mimics the filepath decoder base uses on unix, with the small -- distinction that we're not truncating at NUL bytes (because we're not -- at the outer FFI layer). decodeWithBasePosix :: ShortByteString -> IO String -- | This mimics the filepath dencoder base uses on unix, with the small -- distinction that we're not truncating at NUL bytes (because we're not -- at the outer FFI layer). encodeWithBasePosix :: String -> IO ShortByteString -- | This mimics the filepath decoder base uses on windows, with the small -- distinction that we're not truncating at NUL bytes (because we're not -- at the outer FFI layer). decodeWithBaseWindows :: ShortByteString -> IO String -- | This mimics the filepath dencoder base uses on windows, with the small -- distinction that we're not truncating at NUL bytes (because we're not -- at the outer FFI layer). encodeWithBaseWindows :: String -> IO ShortByteString data EncodingException -- | Could not decode a byte sequence because it was invalid under the -- given encoding, or ran out of input in mid-decode. EncodingError :: String -> Maybe Word8 -> EncodingException showEncodingException :: EncodingException -> String wNUL :: Word16 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 dencoder base uses on unix, with the small -- distinction that we're not truncating at NUL bytes (because we're not -- at the outer FFI layer). encodeWithBasePosix :: String -> IO ShortByteString -- | This mimics the filepath decoder base uses on unix, with the small -- distinction that we're not truncating at NUL bytes (because we're not -- at the outer FFI layer). decodeWithBasePosix :: ShortByteString -> IO String -- | This mimics the filepath dencoder base uses on windows, with the small -- distinction that we're not truncating at NUL bytes (because we're not -- at the outer FFI layer). encodeWithBaseWindows :: String -> IO ShortByteString -- | This mimics the filepath decoder base uses on windows, with the small -- distinction that we're not truncating at NUL bytes (because we're not -- at the outer FFI layer). decodeWithBaseWindows :: ShortByteString -> IO String -- | A library for 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. -- -- -- -- And we could have built an equivalent path with the following -- expressions: -- -- -- -- 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 "file.tar.gz" == ("file",".tar.gz")
--   
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)
--   
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 -- -- -- -- 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]: -- -- 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 -- | 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. -- -- -- -- And we could have built an equivalent path with the following -- expressions: -- -- -- -- 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 "file.tar.gz" == ("file",".tar.gz")
--   
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)
--   
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 -- -- -- -- 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]: -- -- 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 -- | Deprecated: Use System.OsString.Internal.Types from os-string >= -- 2.0.0 package instead. This module will be removed in filepath >= -- 1.5. module System.OsString.Internal.Types -- | Commonly used windows string as wide character bytes. newtype WindowsString WindowsString :: ShortByteString -> WindowsString [getWindowsString] :: WindowsString -> ShortByteString -- | Just a short bidirectional synonym for WindowsString -- constructor. pattern WS :: ShortByteString -> WindowsString unWS :: WindowsString -> ShortByteString -- | Commonly used Posix string as uninterpreted char[] array. newtype PosixString PosixString :: ShortByteString -> PosixString [getPosixString] :: PosixString -> ShortByteString unPS :: PosixString -> ShortByteString -- | Just a short bidirectional synonym for PosixString constructor. pattern PS :: ShortByteString -> PosixString type PlatformString = PosixString newtype WindowsChar WindowsChar :: Word16 -> WindowsChar [getWindowsChar] :: WindowsChar -> Word16 unWW :: WindowsChar -> Word16 -- | Just a short bidirectional synonym for WindowsChar constructor. pattern WW :: Word16 -> WindowsChar newtype PosixChar PosixChar :: Word8 -> PosixChar [getPosixChar] :: PosixChar -> Word8 unPW :: PosixChar -> Word8 -- | Just a short bidirectional synonym for PosixChar constructor. pattern PW :: Word8 -> PosixChar type PlatformChar = 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. newtype OsString OsString :: PlatformString -> OsString [getOsString] :: OsString -> PlatformString -- | Newtype representing a code unit. -- -- On Windows, this is restricted to two-octet codepoints Word16, -- on POSIX one-octet (Word8). newtype OsChar OsChar :: PlatformChar -> OsChar [getOsChar] :: OsChar -> PlatformChar 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 -- | Deprecated: Use System.OsString.Posix from os-string >= 2.0.0 -- package instead. This module will be removed in filepath >= -- 1.5. module System.OsString.Posix -- | Commonly used Posix string as uninterpreted char[] array. data PosixString data PosixChar -- | Partial unicode friendly encoding. -- -- This encodes as UTF8 (strictly), which is a good guess. -- -- Throws an EncodingException if encoding fails. encodeUtf :: MonadThrow m => String -> m PosixString -- | Encode a String with the specified encoding. encodeWith :: TextEncoding -> String -> Either EncodingException PosixString -- | This mimics the behavior of the base library when doing filesystem -- operations, which uses shady PEP 383 style encoding (based on the -- current locale, but PEP 383 only works properly on UTF-8 encodings, so -- good luck). -- -- Looking up the locale requires IO. If you're not worried about calls -- to setFileSystemEncoding, then unsafePerformIO may be -- feasible (make sure to deeply evaluate the result to catch -- exceptions). encodeFS :: String -> IO PosixString -- | Constructs a platform string from a ByteString. -- -- This is a no-op. fromBytes :: MonadThrow m => ByteString -> m PosixString -- | QuasiQuote a PosixString. This accepts Unicode characters and -- encodes as UTF-8 on unix. pstr :: QuasiQuoter -- | Pack a list of platform words to a platform string. -- -- Note that using this in conjunction with 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 -- | This mimics the behavior of the base library when doing filesystem -- operations, which uses shady PEP 383 style encoding (based on the -- current locale, but PEP 383 only works properly on UTF-8 encodings, so -- good luck). -- -- Looking up the locale requires IO. If you're not worried about calls -- to setFileSystemEncoding, then unsafePerformIO may be -- feasible (make sure to deeply evaluate the result to catch -- exceptions). decodeFS :: PosixString -> IO String -- | Unpack a platform string to a list of platform words. unpack :: PosixString -> [PosixChar] -- | Truncates to 1 octet. unsafeFromChar :: Char -> PosixChar -- | Converts back to a unicode codepoint (total). toChar :: PosixChar -> Char -- | Deprecated: Use System.OsString.Internal from os-string >= 2.0.0 -- package instead. This module will be removed in filepath >= -- 1.5. module System.OsString.Internal -- | Partial unicode friendly encoding. -- -- On windows this encodes as UTF16-LE (strictly), which is a pretty good -- guess. On unix this encodes as UTF8 (strictly), which is a good guess. -- -- Throws a EncodingException if encoding fails. encodeUtf :: MonadThrow m => String -> m OsString -- | Encode an OsString given the platform specific encodings. encodeWith :: TextEncoding -> TextEncoding -> String -> Either EncodingException OsString -- | Like encodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which is: -- --
    --
  1. 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)
  2. --
  3. on windows does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range
  4. --
-- -- 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 :: String -> IO OsString -- | Partial unicode friendly decoding. -- -- On windows this decodes as UTF16-LE (strictly), which is a pretty good -- guess. On unix this decodes as UTF8 (strictly), which is a good guess. -- Note that filenames on unix are encoding agnostic char arrays. -- -- Throws a EncodingException if decoding fails. decodeUtf :: MonadThrow m => OsString -> m String -- | Decode an OsString with the specified encoding. -- -- The String is forced into memory to catch all exceptions. decodeWith :: TextEncoding -> TextEncoding -> OsString -> Either EncodingException String -- | Like decodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which is: -- --
    --
  1. 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)
  2. --
  3. on windows does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range
  4. --
-- -- 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 :: OsString -> IO String -- | Constructs an OsString 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 OsString -- | QuasiQuote an OsString. This accepts Unicode characters and -- encodes as UTF-8 on unix and UTF-16 on windows. osstr :: QuasiQuoter -- | Unpack an OsString to a list of OsChar. unpack :: OsString -> [OsChar] -- | Pack a list of OsChar to an OsString -- -- Note that using this in conjunction with unsafeFromChar to -- convert from [Char] to OsString is probably not what -- you want, because it will truncate unicode code points. pack :: [OsChar] -> OsString -- | Truncates on unix to 1 and on Windows to 2 octets. unsafeFromChar :: Char -> OsChar -- | Converts back to a unicode codepoint (total). toChar :: OsChar -> Char -- | An implementation of platform specific short OsString, which -- is: -- --
    --
  1. on windows wide char bytes ([Word16])
  2. --
  3. on unix char bytes ([Word8])
  4. --
-- -- It captures the notion of syscall specific encoding (or the lack -- thereof) to avoid roundtrip issues and memory fragmentation by using -- unpinned byte arrays. Bytes are not touched or interpreted. -- | Deprecated: Use System.OsString from os-string >= 2.0.0 package -- instead. This module will be removed in filepath >= 1.5. module System.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 -- | Partial unicode friendly encoding. -- -- On windows this encodes as UTF16-LE (strictly), which is a pretty good -- guess. On unix this encodes as UTF8 (strictly), which is a good guess. -- -- Throws a EncodingException if encoding fails. encodeUtf :: MonadThrow m => String -> m OsString -- | Encode an OsString given the platform specific encodings. encodeWith :: TextEncoding -> TextEncoding -> String -> Either EncodingException OsString -- | Like encodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which is: -- --
    --
  1. 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)
  2. --
  3. on windows does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range
  4. --
-- -- 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 :: String -> IO OsString -- | QuasiQuote an OsString. This accepts Unicode characters and -- encodes as UTF-8 on unix and UTF-16 on windows. osstr :: QuasiQuoter -- | Pack a list of OsChar to an OsString -- -- Note that using this in conjunction with unsafeFromChar to -- convert from [Char] to OsString is probably not what -- you want, because it will truncate unicode code points. pack :: [OsChar] -> OsString -- | Partial unicode friendly decoding. -- -- On windows this decodes as UTF16-LE (strictly), which is a pretty good -- guess. On unix this decodes as UTF8 (strictly), which is a good guess. -- Note that filenames on unix are encoding agnostic char arrays. -- -- Throws a EncodingException if decoding fails. decodeUtf :: MonadThrow m => OsString -> m String -- | Decode an OsString with the specified encoding. -- -- The String is forced into memory to catch all exceptions. decodeWith :: TextEncoding -> TextEncoding -> OsString -> Either EncodingException String -- | Like decodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which is: -- --
    --
  1. 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)
  2. --
  3. on windows does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range
  4. --
-- -- 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 :: OsString -> IO String -- | Unpack an OsString to a list of OsChar. unpack :: OsString -> [OsChar] -- | Newtype representing a code unit. -- -- On Windows, this is restricted to two-octet codepoints Word16, -- on POSIX one-octet (Word8). data OsChar -- | Truncates on unix to 1 and on Windows to 2 octets. unsafeFromChar :: Char -> OsChar -- | Converts back to a unicode codepoint (total). toChar :: OsChar -> Char module System.OsPath.Posix -- | Commonly used Posix string as uninterpreted 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. encodeUtf :: MonadThrow m => String -> m PosixString -- | Encode a String with the specified encoding. encodeWith :: TextEncoding -> String -> Either EncodingException PosixString -- | This mimics the behavior of the base library when doing filesystem -- operations, which uses shady PEP 383 style encoding (based on the -- current locale, but PEP 383 only works properly on UTF-8 encodings, so -- good luck). -- -- Looking up the locale requires IO. If you're not worried about calls -- to setFileSystemEncoding, then unsafePerformIO may be -- feasible (make sure to deeply evaluate the result to catch -- exceptions). encodeFS :: String -> IO PosixString -- | 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 -- | This mimics the behavior of the base library when doing filesystem -- operations, which uses shady PEP 383 style encoding (based on the -- current locale, but PEP 383 only works properly on UTF-8 encodings, so -- good luck). -- -- Looking up the locale requires IO. If you're not worried about calls -- to setFileSystemEncoding, then unsafePerformIO may be -- feasible (make sure to deeply evaluate the result to catch -- exceptions). decodeFS :: PosixString -> IO String -- | Unpack a platform string to a list of platform words. unpack :: PosixString -> [PosixChar] -- | Truncates to 1 octet. unsafeFromChar :: Char -> PosixChar -- | Converts back to a unicode codepoint (total). toChar :: PosixChar -> Char -- | The character that separates directories. In the case where more than -- one character is possible, 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 -- -- -- -- 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 a EncodingException if encoding fails. encodeUtf :: MonadThrow m => FilePath -> m OsPath -- | Encode a FilePath with the specified encoding. encodeWith :: TextEncoding -> TextEncoding -> FilePath -> Either EncodingException OsPath -- | Like encodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which is: -- --
    --
  1. 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)
  2. --
  3. on windows does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range
  4. --
-- -- 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: -- --
    --
  1. 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)
  2. --
  3. on windows does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range
  4. --
-- -- 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. 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: -- --
    --
  1. it is more efficient and avoids memory fragmentation (uses -- unpinned ShortByteString under the hood)
  2. --
  3. it is more type-safe (newtype over ShortByteString)
  4. --
  5. avoids round-tripping issues by not converting to String (which is -- not total and loses the encoding)
  6. --
  7. abstracts over unix and windows while keeping the original -- bytes
  8. --
-- -- It is important to know that filenames/filepaths have different -- representations across platforms: -- -- -- -- Apart from encoding, filepaths have additional restrictions per -- platform: -- -- -- -- 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: -- --
    --
  1. 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.
  2. --
  3. 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).
  4. --
  5. Avoid comparing String based filepaths, because filenames -- of different encodings may have the same String -- representation, although they're not the same byte-wise.
  6. --
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 a EncodingException if encoding fails. encodeUtf :: MonadThrow m => FilePath -> m OsPath -- | Encode a FilePath with the specified encoding. encodeWith :: TextEncoding -> TextEncoding -> FilePath -> Either EncodingException OsPath -- | Like encodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which is: -- --
    --
  1. 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)
  2. --
  3. on windows does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range
  4. --
-- -- 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 -- | QuasiQuote an OsPath. This accepts Unicode characters and -- encodes as UTF-8 on unix and UTF-16LE on windows. Runs isValid -- on the input. 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 -- | Like decodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which is: -- --
    --
  1. 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)
  2. --
  3. on windows does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range
  4. --
-- -- 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 -- | 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 -- -- -- -- 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]: -- -- 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 -- | Deprecated: Use System.OsString.Posix from os-string >= 2.0.0 -- package instead. This module will be removed in filepath >= -- 1.5. module System.OsString.Windows -- | Commonly used windows string as wide character bytes. data WindowsString data WindowsChar -- | Partial unicode friendly encoding. -- -- This encodes as UTF16-LE (strictly), which is a pretty good guess. -- -- Throws an EncodingException if encoding fails. encodeUtf :: MonadThrow m => String -> m WindowsString -- | Encode a String with the specified encoding. encodeWith :: TextEncoding -> String -> Either EncodingException WindowsString -- | This mimics the behavior of the base library when doing filesystem -- operations, which does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range. -- -- The reason this is in IO is because it unifies with the Posix -- counterpart, which does require IO. This is safe to -- unsafePerformIO/unsafeDupablePerformIO. encodeFS :: String -> IO WindowsString -- | Constructs a platform string from a ByteString. -- -- This ensures valid UCS-2LE. Note that this doesn't expand Word8 to -- Word16 on windows, so you may get invalid UTF-16. -- -- Throws EncodingException on invalid UCS-2LE (although -- unlikely). fromBytes :: MonadThrow m => ByteString -> m WindowsString -- | QuasiQuote a WindowsString. This accepts Unicode characters and -- encodes as UTF-16LE on windows. pstr :: QuasiQuoter -- | Pack a list of platform words to a platform string. -- -- Note that using this in conjunction with 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 -- | Like decodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which does permissive UTF-16 -- encoding, where coding errors generate Chars in the surrogate range. -- -- The reason this is in IO is because it unifies with the Posix -- counterpart, which does require IO. -- unsafePerformIO/unsafeDupablePerformIO are safe, -- however. decodeFS :: WindowsString -> IO String -- | Unpack a platform string to a list of platform words. unpack :: WindowsString -> [WindowsChar] -- | Truncates to 2 octets. unsafeFromChar :: Char -> WindowsChar -- | Converts back to a unicode codepoint (total). toChar :: WindowsChar -> Char module System.OsPath.Windows -- | Commonly used windows string as wide character bytes. data WindowsString data WindowsChar -- | Filepaths are 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. encodeUtf :: MonadThrow m => String -> m WindowsString -- | Encode a String with the specified encoding. encodeWith :: TextEncoding -> String -> Either EncodingException WindowsString -- | This mimics the behavior of the base library when doing filesystem -- operations, which does permissive UTF-16 encoding, where coding errors -- generate Chars in the surrogate range. -- -- The reason this is in IO is because it unifies with the Posix -- counterpart, which does require IO. This is safe to -- unsafePerformIO/unsafeDupablePerformIO. encodeFS :: String -> IO WindowsString -- | 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 -- | Like decodeUtf, except this mimics the behavior of the base -- library when doing filesystem operations, which does permissive UTF-16 -- encoding, where coding errors generate Chars in the surrogate range. -- -- The reason this is in IO is because it unifies with the Posix -- counterpart, which does require IO. -- unsafePerformIO/unsafeDupablePerformIO are safe, -- however. decodeFS :: WindowsString -> IO String -- | Unpack a platform string to a list of platform words. unpack :: WindowsString -> [WindowsChar] -- | Truncates to 2 octets. unsafeFromChar :: Char -> WindowsChar -- | Converts back to a unicode codepoint (total). toChar :: WindowsChar -> Char -- | The character that separates directories. In the case where more than -- one character is possible, 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 -- -- -- -- 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]: -- -- 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