h&3      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl/(c) Neil Mitchell 2005-2014, (c) Joey Hess 2019BSD3 id@joeyh.namestableportable Safe-Inferred"W:filepath-bytestring%Convert from FilePath to RawFilePath.=When run on Unix, this applies the filesystem encoding (see ).+When run on Windows, this encodes as UTF-8.the implementation of this function assumes that the filesystem encoding will not be changed while the program is running.filepath-bytestring$Convert from RawFilePath to FilePath=When run on Unix, this applies the filesystem encoding (see ).(When run on Windows, this decodes UTF-8.mfilepath-bytestring*Is the operating system Unix or Linux likenfilepath-bytestring$Is the operating system Windows likefilepath-bytestringThe character that separates directories. In the case where more than one character is possible,  is the 'ideal' one. Windows: pathSeparator == fromIntegral (ord '\\') Posix: pathSeparator == fromIntegral (ord '/') isPathSeparator pathSeparatorfilepath-bytestring$The list of all possible separators. Windows: pathSeparators == [fromIntegral (ord '\\'), fromIntegral (ord '/')] Posix: pathSeparators == [fromIntegral (ord '/')] pathSeparator `elem` pathSeparatorsfilepath-bytestringRather than using (== )5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)filepath-bytestringThe character that is used to separate the entries in the $PATH environment variable. Windows: searchPathSeparator == fromIntegral (ord ';') Posix: searchPathSeparator == fromIntegral (ord ':')filepath-bytestring"Is the character a file separator? 5isSearchPathSeparator a == (a == searchPathSeparator)filepath-bytestringFile extension character &extSeparator == fromIntegral (ord '.') filepath-bytestring(Is the character an extension character? 'isExtSeparator a == (a == extSeparator) filepath-bytestringTake a string, split it on the  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"] filepath-bytestringGet a list of s in the $PATH variable. filepath-bytestringSplit on the extension.  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/","") filepath-bytestring%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"filepath-bytestring "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"filepath-bytestringSet 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) yfilepath-bytestringAdd an extension, even if there is already one there, equivalent to . "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"filepath-bytestring0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)filepath-bytestringAdd 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"filepath-bytestring*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)filepath-bytestring5Does 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" == Falsefilepath-bytestring2Drop the given extension from a FilePath, and the "." preceding it. Returns o: if the FilePath does not have the given extension, or p. and the part before the extension if it does.+This function can be more predictable than 5, 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 xfilepath-bytestringSplit 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")filepath-bytestringDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions xfilepath-bytestringGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"filepath-bytestringReplace all extensions of a file with a new extension. Note that  and  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"qfilepath-bytestringIs the given character a valid drive letter? only a-z and A-Z are letters, not isAlpha which is more unicodeyfilepath-bytestringSplit 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")filepath-bytestring&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"filepath-bytestringGet the drive from a filepath. !takeDrive x == fst (splitDrive x)filepath-bytestringDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)filepath-bytestringDoes 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 "" == Falsefilepath-bytestringIs an element a drive Posix: isDrive "/" == True Posix: isDrive "/foo" == False Windows: isDrive "C:\\" == True Windows: isDrive "C:\\foo" == False isDrive "" == False filepath-bytestring*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:","")!filepath-bytestringSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == x"filepath-bytestringDrop the filename. Unlike ), this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)#filepath-bytestringGet 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)$filepath-bytestring0Get 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"%filepath-bytestringSet 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&filepath-bytestringIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == True'filepath-bytestringAdd 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/"(filepath-bytestring#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x)filepath-bytestring*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:\\"*filepath-bytestring1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x+filepath-bytestring An alias for ,.rfilepath-bytestring0Combine two paths, assuming rhs is NOT absolute.,filepath-bytestringCombine 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-bytestring(Split a path by the directory separator. splitPath "/directory/file.ext" == ["/","directory/","file.ext"] mconcat (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"].filepath-bytestringJust as -5, 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"]/filepath-bytestring!Join path elements back together. joinPath ["/","directory/","file.ext"] == "/directory/file.ext" Valid x => joinPath (splitPath x) == x joinPath [] == "" Posix: joinPath ["test","file","path"] == "test/file/path"0filepath-bytestringEquality of two ss. 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.  x == y ==> equalFilePath x y normalise x == normalise y ==> equalFilePath x y equalFilePath "foo" "foo/" not (equalFilePath "foo" "/foo") Posix: not (equalFilePath "foo" "FOO") Windows: equalFilePath "foo" "FOO" Windows: not (equalFilePath "C:" "C:/")1filepath-bytestringContract 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  http://neilmitchell.blogspot.co.uk/2015/10/filepaths-are-subtle-symlinks-are-hard.htmlthis 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"2filepath-bytestringNormalise a file)// outside of the drive can be made blank/ -> ./ -> "" 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 "./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"3filepath-bytestringIs a RawFilePath 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 == (x /= mempty) 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" == True4filepath-bytestringTake 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"5filepath-bytestring/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 "/" == FalseAccording to [1]:/"A UNC name of any format [is never relative]."6"You cannot use the "\?" prefix with a relative path."6filepath-bytestring not . 5 "isAbsolute x == not (isRelative x)7  !"#$%&'()*+,-./01234567  #!"$%)*+,-/.&'(201563477,5/(c) Neil Mitchell 2005-2014, (c) Joey Hess 2019BSD3 id@joeyh.namestableportable Safe-InferredX7  !"#$%&'()*+,-./0123456/(c) Neil Mitchell 2005-2014, (c) Joey Hess 2019BSD3 id@joeyh.namestableportable Safe-Inferred":7filepath-bytestring%Convert from FilePath to RawFilePath.=When run on Unix, this applies the filesystem encoding (see ).+When run on Windows, this encodes as UTF-8.the implementation of this function assumes that the filesystem encoding will not be changed while the program is running.8filepath-bytestring$Convert from RawFilePath to FilePath=When run on Unix, this applies the filesystem encoding (see ).(When run on Windows, this decodes UTF-8.tfilepath-bytestring*Is the operating system Unix or Linux likeufilepath-bytestring$Is the operating system Windows like9filepath-bytestringThe character that separates directories. In the case where more than one character is possible, 9 is the 'ideal' one. Windows: pathSeparator == fromIntegral (ord '\\') Posix: pathSeparator == fromIntegral (ord '/') isPathSeparator pathSeparator:filepath-bytestring$The list of all possible separators. Windows: pathSeparators == [fromIntegral (ord '\\'), fromIntegral (ord '/')] Posix: pathSeparators == [fromIntegral (ord '/')] pathSeparator `elem` pathSeparators;filepath-bytestringRather than using (== 9)5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)<filepath-bytestringThe character that is used to separate the entries in the $PATH environment variable. Windows: searchPathSeparator == fromIntegral (ord ';') Posix: searchPathSeparator == fromIntegral (ord ':')=filepath-bytestring"Is the character a file separator? 5isSearchPathSeparator a == (a == searchPathSeparator)>filepath-bytestringFile extension character &extSeparator == fromIntegral (ord '.')?filepath-bytestring(Is the character an extension character? 'isExtSeparator a == (a == extSeparator)@filepath-bytestringTake a string, split it on the < 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"]Afilepath-bytestringGet a list of s in the $PATH variable.Bfilepath-bytestringSplit on the extension. H 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/","")Cfilepath-bytestring%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"Dfilepath-bytestring "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"Efilepath-bytestringSet the extension of a file, overwriting one if already present, equivalent to D. 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) yFfilepath-bytestringAdd an extension, even if there is already one there, equivalent to H. "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"Gfilepath-bytestring0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)Hfilepath-bytestringAdd an extension, even if there is already one there, equivalent to F. 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"Ifilepath-bytestring*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)Jfilepath-bytestring5Does 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" == FalseKfilepath-bytestring2Drop the given extension from a FilePath, and the "." preceding it. Returns o: if the FilePath does not have the given extension, or p. and the part before the extension if it does.+This function can be more predictable than M5, 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 xLfilepath-bytestringSplit 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")Mfilepath-bytestringDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions xNfilepath-bytestringGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"Ofilepath-bytestringReplace all extensions of a file with a new extension. Note that E and H 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"vfilepath-bytestringIs the given character a valid drive letter? only a-z and A-Z are letters, not isAlpha which is more unicodeyPfilepath-bytestringSplit 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")Qfilepath-bytestring&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"Rfilepath-bytestringGet the drive from a filepath. !takeDrive x == fst (splitDrive x)Sfilepath-bytestringDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)Tfilepath-bytestringDoes 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 "" == FalseUfilepath-bytestringIs an element a drive Posix: isDrive "/" == True Posix: isDrive "/foo" == False Windows: isDrive "C:\\" == True Windows: isDrive "C:\\foo" == False isDrive "" == FalseVfilepath-bytestring*Split a filename into directory and file. b 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:","")Wfilepath-bytestringSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == xXfilepath-bytestringDrop the filename. Unlike _, this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)Yfilepath-bytestringGet 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)Zfilepath-bytestring0Get 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"[filepath-bytestringSet 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\filepath-bytestringIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == True]filepath-bytestringAdd 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/"^filepath-bytestring#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x_filepath-bytestring*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:\\"`filepath-bytestring1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` xafilepath-bytestring An alias for b.wfilepath-bytestring0Combine two paths, assuming rhs is NOT absolute.bfilepath-bytestringCombine 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 b 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 b! 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 b! is to never combine these forms. Windows: "D:\\foo" "C:bar" == "C:bar" Windows: "C:\\foo" "C:bar" == "C:bar"cfilepath-bytestring(Split a path by the directory separator. splitPath "/directory/file.ext" == ["/","directory/","file.ext"] mconcat (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"]dfilepath-bytestringJust as c5, 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"]efilepath-bytestring!Join path elements back together. joinPath ["/","directory/","file.ext"] == "/directory/file.ext" Valid x => joinPath (splitPath x) == x joinPath [] == "" Posix: joinPath ["test","file","path"] == "test/file/path"ffilepath-bytestringEquality of two ss. 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.  x == y ==> equalFilePath x y normalise x == normalise y ==> equalFilePath x y equalFilePath "foo" "foo/" not (equalFilePath "foo" "/foo") Posix: not (equalFilePath "foo" "FOO") Windows: equalFilePath "foo" "FOO" Windows: not (equalFilePath "C:" "C:/")gfilepath-bytestringContract 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  http://neilmitchell.blogspot.co.uk/2015/10/filepaths-are-subtle-symlinks-are-hard.htmlthis 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"hfilepath-bytestringNormalise a file)// outside of the drive can be made blank/ -> 9./ -> "" 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 "./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"ifilepath-bytestringIs a RawFilePath 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 == (x /= mempty) 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" == Truejfilepath-bytestringTake 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"kfilepath-bytestring/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 "/" == FalseAccording to [1]:/"A UNC name of any format [is never relative]."6"You cannot use the "\?" prefix with a relative path."lfilepath-bytestring not . k "isAbsolute x == not (isRelative x)7789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl7789:;<=>?@ABCEDGHIFLMNOJKVYWXZ[_`abcedPQRTSU\]^hfgklijD7F7b5      !"#$%&'()*+,-./0123456789:;<=>      !"#$%&'()*+,-./0123456789:;<=>?@ABCABDEFAGH?@EF5filepath-bytestring-1.4.2.1.13-D5LTb4h2Yf8F4bzlYtEK4P System.FilePath.Posix.ByteString"System.FilePath.Windows.ByteStringEncodinggetFileSystemEncodingSystem.FilePath.ByteString unix-2.7.2.2 System.Posix.ByteString.FilePath RawFilePathencodeFilePathdecodeFilePath pathSeparatorpathSeparatorsisPathSeparatorsearchPathSeparatorisSearchPathSeparator extSeparatorisExtSeparatorsplitSearchPath getSearchPathsplitExtension takeExtension-<.>replaceExtension<.> dropExtension addExtension hasExtension isExtensionOfstripExtensionsplitExtensionsdropExtensionstakeExtensionsreplaceExtensions splitDrive joinDrive takeDrive dropDrivehasDriveisDrive splitFileNamereplaceFileName dropFileName takeFileName takeBaseNamereplaceBaseNamehasTrailingPathSeparatoraddTrailingPathSeparatordropTrailingPathSeparator takeDirectoryreplaceDirectorycombine splitPathsplitDirectoriesjoinPath equalFilePath makeRelative normaliseisValid makeValid isRelative isAbsoluteisPosix isWindowsbase GHC.MaybeNothingJustisLetter combineAlwaysGHC.IOFilePath