h&\      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                                                                                                                                                           (c) Neil Mitchell 2005-2014BSD3ndmitchell@gmail.comstableportable Safe-Inferred_O9filepath*Is the operating system Unix or Linux likefilepath$Is the operating system Windows likeJfilepathThe character that separates directories. In the case where more than one character is possible, J is the 'ideal' one. Windows: pathSeparator == '\\' Posix: pathSeparator == '/' isPathSeparator pathSeparatorKfilepath$The list of all possible separators. Windows: pathSeparators == ['\\', '/'] Posix: pathSeparators == ['/'] pathSeparator `elem` pathSeparatorsLfilepathRather than using (== J)5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)MfilepathThe character that is used to separate the entries in the $PATH environment variable. Windows: searchPathSeparator == ';' Posix: searchPathSeparator == ':'Nfilepath"Is the character a file separator? 5isSearchPathSeparator a == (a == searchPathSeparator)OfilepathFile extension character extSeparator == '.'Pfilepath(Is the character an extension character? 'isExtSeparator a == (a == extSeparator)QfilepathTake a string, split it on the M 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"]RfilepathGet a list of FILEPATHs in the $PATH variable.SfilepathSplit on the extension. Y 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/","")Tfilepath%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"Ufilepath "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"VfilepathSet the extension of a file, overwriting one if already present, equivalent to U. 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) yWfilepathAdd an extension, even if there is already one there, equivalent to Y. "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"Xfilepath0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)YfilepathAdd an extension, even if there is already one there, equivalent to W. 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"Zfilepath*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)[filepath5Does 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\filepath2Drop the given extension from a FilePath, and the "." preceding it. Returns : if the FilePath does not have the given extension, or . 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 x]filepathSplit 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")^filepathDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions x_filepathGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"`filepathReplace all extensions of a file with a new extension. Note that V and Y 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"filepathIs the given character a valid drive letter? only a-z and A-Z are letters, not isAlpha which is more unicodeyafilepathSplit 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")bfilepath&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"cfilepathGet the drive from a filepath. !takeDrive x == fst (splitDrive x)dfilepathDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)efilepathDoes 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 "" == FalseffilepathIs an element a drive Posix: isDrive "/" == True Posix: isDrive "/foo" == False Windows: isDrive "C:\\" == True Windows: isDrive "C:\\foo" == False isDrive "" == Falsegfilepath*Split a filename into directory and file. s 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")hfilepathSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == xifilepathDrop the filename. Unlike p, this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)jfilepathGet 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)kfilepath0Get 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"lfilepathSet 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) == xmfilepathIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == TruenfilepathAdd 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/"ofilepath#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive xpfilepath*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:\\"qfilepath1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` xrfilepath An alias for s.filepath0Combine two paths, assuming rhs is NOT absolute.sfilepathCombine 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 s 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 s! 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 s! is to never combine these forms. Windows: "D:\\foo" "C:bar" == "C:bar" Windows: "C:\\foo" "C:bar" == "C:bar"tfilepath(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"]ufilepathJust as t5, 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"]vfilepath!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"wfilepathEquality 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 y, 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:/")xfilepathContract 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"yfilepathNormalise a file)// outside of the drive can be made blank/ -> J./ -> ""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"zfilepathIs 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{filepathTake 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"|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 "/" == FalseAccording to [1]:/"A UNC name of any format [is never relative]."6"You cannot use the "\?" prefix with a relative path."}filepath not . | "isAbsolute x == not (isRelative x)filepathThe stripSuffix function drops the given suffix from a list. It returns Nothing if the list did not end with the suffix given, or Just the list before the suffix, if it does.5JKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}5JKLMNOPQRSTVUXYZW]^_`[\gjhiklpqrstvuabcedfmnoywx|}z{U7W7s5(c) Neil Mitchell 2005-2014BSD3ndmitchell@gmail.comstableportableSafe`/5JKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}5JKLMNOPQRSTVUXYZW]^_`[\gjhiklpqrstvuabcedfmnoywx|}z{(c) Neil Mitchell 2005-2014BSD3ndmitchell@gmail.comstableportable Safe-InferredN9filepath*Is the operating system Unix or Linux likefilepath$Is the operating system Windows like~filepathThe character that separates directories. In the case where more than one character is possible, ~ is the 'ideal' one. Windows: pathSeparator == '\\' Posix: pathSeparator == '/' isPathSeparator pathSeparatorfilepath$The list of all possible separators. Windows: pathSeparators == ['\\', '/'] Posix: pathSeparators == ['/'] pathSeparator `elem` pathSeparatorsfilepathRather than using (== ~)5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)filepathThe character that is used to separate the entries in the $PATH environment variable. Windows: searchPathSeparator == ';' Posix: searchPathSeparator == ':'filepath"Is the character a file separator? 5isSearchPathSeparator a == (a == searchPathSeparator)filepathFile extension character extSeparator == '.'filepath(Is the character an extension character? 'isExtSeparator a == (a == extSeparator)filepathTake 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"]filepathGet a list of FILEPATHs in the $PATH variable.filepathSplit 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%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 "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"filepathSet 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) yfilepathAdd an extension, even if there is already one there, equivalent to . "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"filepath0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)filepathAdd 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*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)filepath5Does 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" == Falsefilepath2Drop the given extension from a FilePath, and the "." preceding it. Returns : if the FilePath does not have the given extension, or . 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 xfilepathSplit 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")filepathDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions xfilepathGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"filepathReplace 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"filepathIs the given character a valid drive letter? only a-z and A-Z are letters, not isAlpha which is more unicodeyfilepathSplit 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&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"filepathGet the drive from a filepath. !takeDrive x == fst (splitDrive x)filepathDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)filepathDoes 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 "" == FalsefilepathIs an element a drive Posix: isDrive "/" == True Posix: isDrive "/foo" == False Windows: isDrive "C:\\" == True Windows: isDrive "C:\\foo" == False isDrive "" == Falsefilepath*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")filepathSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == xfilepathDrop the filename. Unlike , this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)filepathGet 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)filepath0Get 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"filepathSet 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) == xfilepathIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == TruefilepathAdd 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#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive xfilepath*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:\\"filepath1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` xfilepath An alias for .filepath0Combine two paths, assuming rhs is NOT absolute.filepathCombine 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(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"]filepathJust 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!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"filepathEquality 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 , 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:/")filepathContract 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"filepathNormalise a file)// outside of the drive can be made blank/ -> ~./ -> ""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"filepathIs 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" == TruefilepathTake 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"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 "/" == FalseAccording to [1]:/"A UNC name of any format [is never relative]."6"You cannot use the "\?" prefix with a relative path."filepath not .  "isAbsolute x == not (isRelative x)filepathThe stripSuffix function drops the given suffix from a list. It returns Nothing if the list did not end with the suffix given, or Just the list before the suffix, if it does.5~5~775 2022 Julian OspaldMIT"Julian Ospald  experimentalportable Safe-Inferred( filepathTotal conversion to char. filepathO(n). Construct a new ShortByteString from a CWString. The resulting ShortByteString' is an immutable copy of the original CWString4, and is managed on the Haskell heap. The original CWString must be null terminated. filepathO(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. filepathO(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. filepathO(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. filepathO(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.filepathThis 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.filepathThis 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.filepathGiven the maximum size needed and a function to make the contents of a ShortByteString, createAndTrim makes the . 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.filepathReturns the length of the substring matching, not the index. If no match, returns 0.filepathWord8 index (not Word16)filepath Word8 indexfilepathWord8 index (not Word16)filepathWord16filepathWord8 (LSB, MSB)filepathWord8 (LSB, MSB)filepathWord16))/(c) Duncan Coutts 2012-2013, Julian Ospald 2022 BSD-stylehasufell@posteo.destableghc only Safe-Inferred  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI8+ AF" *'! &3210/.-,HGB ?>= experimentalportable Safe-Inferred(7filepathO(1) Convert a  into a filepathO(n). Convert a list into a filepathO(n) . Convert a  into a list.filepath This is like !, but the number of , not .filepathO(n)! Append a Word16 to the end of a "Note: copies the entire byte arrayfilepathO(n)  is analogous to (:) for lists."Note: copies the entire byte arrayfilepathO(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.filepathO(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 arrayfilepathO(n) Extract the head and tail of a ByteString, returning Nothing if it is empty.filepathO(n) Extract first two elements and the rest of a ByteString, returning Nothing if it is shorter than two elements.filepathO(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.filepathO(n) Return all the elements of a  except the last one. An exception will be thrown in the case of an empty ShortByteString."Note: copies the entire byte arrayfilepathO(n) Extract the  and 4 of a ByteString, returning Nothing if it is empty.filepathO(n)  f xs- is the ShortByteString obtained by applying f to each element of xs.filepathO(n)  xs% efficiently returns the elements of xs in reverse order.filepathO(n) Applied to a predicate and a , $ determines if all elements of the  satisfy the predicate.filepathO(n)* Applied to a predicate and a ByteString, # determines if any element of the  ByteString satisfies the predicate.filepathO(n)  n x is a ByteString of length n with x2 the value of every element. The following holds: .replicate w c = unfoldr w (\u -> Just (u,u)) cfilepathO(n), where n# is the length of the result. The 0 function is analogous to the List 'unfoldr'.  builds a ShortByteString from a seed value. The function takes the element and returns 9 if it is done producing the ShortByteString or returns  (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 7, otherwise recurse infinitely, then finally create a . Examples:  unfoldr (\x -> if x <= 5 then Just (x, x + 1) else Nothing) 0 == pack [0, 1, 2, 3, 4, 5]filepathO(n) Like ,  builds a ShortByteString from a seed value. However, the length of the result is limited by the first argument to (. This function is more efficient than 1 when the maximum length of the result is known.The following equation relates  and : ,fst (unfoldrN n f s) == take n (unfoldr f s)filepathO(n)  n, applied to a ShortByteString xs, returns the prefix of xs of length n, or xs itself if n > ! xs."Note: copies the entire byte arrayfilepathO(1)  n xs is equivalent to  (! 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"filepath Similar to , returns the longest (possibly empty) prefix of elements satisfying the predicate.filepathReturns the longest (possibly empty) suffix of elements satisfying the predicate. p is equivalent to  .  p . .filepathO(n)  n xs returns the suffix of xs after the first n elements, or [] if n > ! xs."Note: copies the entire byte arrayfilepathO(1)  n xs is equivalent to  (! 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"""filepath Similar to , drops the longest (possibly empty) prefix of elements satisfying the predicate and returns the remainder."Note: copies the entire byte array filepath Similar to , drops the longest (possibly empty) suffix of elements satisfying the predicate and returns the remainder. p is equivalent to  .  p . .filepath>Returns the longest (possibly empty) suffix of elements which do not8 satisfy the predicate and the remainder of the string. p is equivalent to  (not . p) and to ( (not . p) &&&  (not . p)).filepath Similar to , returns the longest (possibly empty) prefix of elements which do not8 satisfy the predicate and the remainder of the string. p is equivalent to  (not . p) and to ( (not . p) &&&  (not . p)).filepath Similar to , returns the longest (possibly empty) prefix of elements satisfying the predicate and the remainder of the string. p is equivalent to  (not . p) and to ( p &&&  p).filepathReturns the longest (possibly empty) suffix of elements satisfying the predicate and the remainder of the string. p is equivalent to  (not . p) and to ( p &&&  p).We have 0spanEnd (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)filepathO(n)  n xs is equivalent to ( n xs,  n xs).Note: copies the substringsfilepathO(n) Break a  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 9intercalate [c] . split c == id split == splitWith . (==)Note: copies the substringsfilepathO(n) Splits a  into components delimited by separators, where the predicate returns True for a separator element. The resulting components do not contain the separators. Two adjacent separators result in an empty component in the output. eg. splitWith (==97) "aabbaca" == ["","","bb","c",""] -- fromEnum 'a' == 97 splitWith undefined "" == [] -- and not [""]filepath, 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.filepath is like  , but strict in the accumulator.filepath, 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.filepath is like  , but strict in the accumulator.filepath is a variant of  that has no starting value argument, and thus must be applied to non-empty s. An exception will be thrown in the case of an empty ShortByteString.filepath is like , but strict in the accumulator. An exception will be thrown in the case of an empty ShortByteString.filepath is a variant of  that has no starting value argument, and thus must be applied to non-empty s An exception will be thrown in the case of an empty ShortByteString.filepath is a variant of $, but is strict in the accumulator.filepathO(1) - index (subscript) operator, starting from 0. filepathO(1) & index, starting from 0, that returns  if: 0 <= n < length bs filepathO(1) & index, starting from 0, that returns  if: 0 <= n < length bsfilepathO(n)  is the  membership predicate.filepathO(n) , applied to a predicate and a ByteString, returns a ByteString containing those characters that satisfy the predicate.filepathO(n) The  function takes a predicate and a ByteString, and returns the first element in matching the predicate, or  if there is no such element. find f p = case findIndex f p of Just n -> Just (p ! n) ; _ -> NothingfilepathO(n) The  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., 4partition p bs == (filter p xs, filter (not . p) xs)filepathO(n) The ? function returns the index of the first element in the given * which is equal to the query element, or  if there is no such element.filepathO(n) The  function extends , by returning the indices of all elements equal to the query element, in ascending order.filepathcount returns the number of times its argument appears in the ShortByteStringfilepathO(n) The " function takes a predicate and a  and returns the index of the first element in the ByteString satisfying the predicate.filepathO(n) The  function extends , by returning the indices of all elements satisfying the predicate, in ascending order. filepath number of filepathnumber of Word16filepath number of filepath number of filepath number of filepathnumber of Word16filepath number of filepath number of filepath number of filepath number of filepath number of  !#$%&+8BCF8+ F!&B%$#C55 Safe-InferredLfilepathCould not decode a byte sequence because it was invalid under the given encoding, or ran out of input in mid-decode.filepathMimics the base encoding for filesystem operations. This should be total on all inputs (word16 byte arrays).*Note that this has a subtle difference to /: 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.filepathDecode with the given .filepathEncode with the given .filepathThis 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).filepathThis 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).filepathThis 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).filepathThis 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). Safe-Inferred(c) Neil Mitchell 2005-2014BSD3ndmitchell@gmail.comstableportable Safe-InferredDi9filepath*Is the operating system Unix or Linux likefilepath$Is the operating system Windows likefilepathThe character that separates directories. In the case where more than one character is possible,  is the 'ideal' one. Windows: pathSeparator == '\\' Posix: pathSeparator == '/' isPathSeparator pathSeparatorfilepath$The list of all possible separators. Windows: pathSeparators == ['\\', '/'] Posix: pathSeparators == ['/'] pathSeparator `elem` pathSeparatorsfilepathRather than using (== )5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)filepathThe character that is used to separate the entries in the $PATH environment variable. Windows: searchPathSeparator == ';' Posix: searchPathSeparator == ':'filepath"Is the character a file separator? 5isSearchPathSeparator a == (a == searchPathSeparator)filepathFile extension character extSeparator == '.'filepath(Is the character an extension character? 'isExtSeparator a == (a == extSeparator)filepathTake 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"]filepathSplit 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%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 "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"filepathSet 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) yfilepathAdd an extension, even if there is already one there, equivalent to . "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"filepath0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)filepathAdd 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*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)filepath5Does 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" == Falsefilepath9Drop the given extension from a ShortByteString, and the "." preceding it. Returns  if the ShortByteString does not have the given extension, or . 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 xfilepathSplit 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")filepathDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions xfilepathGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"filepathReplace 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"filepathIs the given character a valid drive letter? only a-z and A-Z are letters, not isAlpha which is more unicodeyfilepathSplit 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&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"filepathGet the drive from a filepath. !takeDrive x == fst (splitDrive x)filepathDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)filepathDoes 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 "" == FalsefilepathIs an element a drive Posix: isDrive "/" == True Posix: isDrive "/foo" == False Windows: isDrive "C:\\" == True Windows: isDrive "C:\\foo" == False isDrive "" == Falsefilepath*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")filepathSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == xfilepathDrop the filename. Unlike , this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)filepathGet 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)filepath0Get 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"filepathSet 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) == xfilepathIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == TruefilepathAdd 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#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive xfilepath*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:\\"filepath1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` xfilepath An alias for .filepath0Combine two paths, assuming rhs is NOT absolute.filepathCombine 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(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"]filepathJust 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!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"filepathEquality 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 , 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:/")filepathContract 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"filepathNormalise a file)// outside of the drive can be made blank/ -> ./ -> ""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"filepathIs 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" == TruefilepathTake 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"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 "/" == FalseAccording to [1]:/"A UNC name of any format [is never relative]."6"You cannot use the "\?" prefix with a relative path."filepath not .  "isAbsolute x == not (isRelative x)filepathTotal conversion to char.filepath-This is unsafe and clamps at Word16 maxbound.33775(c) Neil Mitchell 2005-2014BSD3ndmitchell@gmail.comstableportable Safe-Inferred9filepath*Is the operating system Unix or Linux likefilepath$Is the operating system Windows likefilepathThe character that separates directories. In the case where more than one character is possible,  is the 'ideal' one. Windows: pathSeparator == '\\' Posix: pathSeparator == '/' isPathSeparator pathSeparatorfilepath$The list of all possible separators. Windows: pathSeparators == ['\\', '/'] Posix: pathSeparators == ['/'] pathSeparator `elem` pathSeparatorsfilepathRather than using (== )5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)filepathThe character that is used to separate the entries in the $PATH environment variable. Windows: searchPathSeparator == ';' Posix: searchPathSeparator == ':'filepath"Is the character a file separator? 5isSearchPathSeparator a == (a == searchPathSeparator)filepathFile extension character extSeparator == '.'filepath(Is the character an extension character? 'isExtSeparator a == (a == extSeparator)filepathTake 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"]filepathSplit 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%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 "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"filepathSet 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) yfilepathAdd an extension, even if there is already one there, equivalent to . "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"filepath0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)filepathAdd 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*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)filepath5Does 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" == Falsefilepath9Drop the given extension from a ShortByteString, and the "." preceding it. Returns  if the ShortByteString does not have the given extension, or . 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 xfilepathSplit 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")filepathDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions xfilepathGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"filepathReplace 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"filepathIs the given character a valid drive letter? only a-z and A-Z are letters, not isAlpha which is more unicodeyfilepathSplit 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&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"filepathGet the drive from a filepath. !takeDrive x == fst (splitDrive x)filepathDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)filepathDoes 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 "" == FalsefilepathIs an element a drive Posix: isDrive "/" == True Posix: isDrive "/foo" == False Windows: isDrive "C:\\" == True Windows: isDrive "C:\\foo" == False isDrive "" == Falsefilepath*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")filepathSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == xfilepathDrop the filename. Unlike , this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)filepathGet 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)filepath0Get 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"filepathSet 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) == xfilepathIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == TruefilepathAdd 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#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive xfilepath*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:\\"filepath1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` xfilepath An alias for .filepath0Combine two paths, assuming rhs is NOT absolute.filepathCombine 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(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"]filepathJust 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!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"filepathEquality 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 , 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:/")filepathContract 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"filepathNormalise a file)// outside of the drive can be made blank/ -> ./ -> ""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"filepathIs 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" == TruefilepathTake 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"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 "/" == FalseAccording to [1]:/"A UNC name of any format [is never relative]."6"You cannot use the "\?" prefix with a relative path."filepath not .  "isAbsolute x == not (isRelative x)filepathTotal conversion to char.filepath-This is unsafe and clamps at Word16 maxbound.33775  Safe-Inferred;filepath!Newtype representing a code unit.7On Windows, this is restricted to two-octet codepoints , on POSIX one-octet ().filepath=Newtype representing short operating system specific strings.Internally this is either  or 1, 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.filepath,Commonly used Posix string as uninterpreted char[] array.filepath5Commonly used windows string as wide character bytes.filepath'Just a short bidirectional synonym for  constructor.filepath'Just a short bidirectional synonym for  constructor.filepath'Just a short bidirectional synonym for  constructor.filepath'Just a short bidirectional synonym for  constructor.filepathDecodes as UCS-2.filepath&Prints the raw bytes without decoding.filepath"String-Concatenation" for  . This is not the same as ().filepath-Byte ordering of the internal representation.filepath-Byte equality of the internal representation.filepathOn windows, decodes as UCS-2. On unix prints the raw bytes without decoding.filepath-Byte ordering of the internal representation.filepath-Byte equality of the internal representation.  Safe-Inferredfilepath&Type representing filenames/pathnames.*This type doesn't add any guarantees over .filepath&Ifdef around current platform (either  or ).filepathFilepaths are char[]$ data on unix as passed to syscalls.filepathFilepaths are wchar_t*' data on windows as passed to syscalls.    Safe-Inferred filepath"Partial unicode friendly encoding.7This encodes as UTF8 (strictly), which is a good guess. Throws an  if encoding fails.filepath Encode a  with the specified encoding.filepathThis 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  may be feasible (make sure to deeply evaluate the result to catch exceptions).filepath"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  if decoding fails.filepath Decode a  with the specified encoding.9The String is forced into memory to catch all exceptions.filepathThis 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  may be feasible (make sure to deeply evaluate the result to catch exceptions).filepath/Constructs a platform string from a ByteString.This is a no-op.filepath QuasiQuote a . This accepts Unicode characters and encodes as UTF-8 on unix.filepath5Unpack a platform string to a list of platform words.filepath3Pack a list of platform words to a platform string.)Note that using this in conjunction with  to convert from [Char] to platform string is probably not what you want, because it will truncate unicode code points.filepathTruncates to 1 octet.filepath-Converts back to a unicode codepoint (total).  Safe-Inferred O filepath"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  if encoding fails.filepath Encode an ' given the platform specific encodings.filepathLike , except this mimics the behavior of the base library when doing filesystem operations, which is: on unix, uses shady PEP 383 style encoding (based on the current locale, but PEP 383 only works properly on UTF-8 encodings, so good luck)on windows does permissive UTF-16 encoding, where coding errors generate Chars in the surrogate rangeLooking 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).filepath"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  if decoding fails.filepath Decode an  with the specified encoding.9The String is forced into memory to catch all exceptions.filepathLike , except this mimics the behavior of the base library when doing filesystem operations, which is: on unix, uses shady PEP 383 style encoding (based on the current locale, but PEP 383 only works properly on UTF-8 encodings, so good luck)on windows does permissive UTF-16 encoding, where coding errors generate Chars in the surrogate rangeLooking 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).filepathConstructs an OsString from a ByteString.On windows, this ensures valid UCS-2LE, on unix it is passed unchanged/unchecked.Throws 3 on invalid UCS-2LE on windows (although unlikely).filepathQuasiQuote an . This accepts Unicode characters and encodes as UTF-8 on unix and UTF-16 on windows.filepath Unpack an  to a list of .filepathPack a list of  to an )Note that using this in conjunction with  to convert from [Char] to  is probably not what you want, because it will truncate unicode code points.filepath2Truncates on unix to 1 and on Windows to 2 octets.filepath-Converts back to a unicode codepoint (total).filepathunix text encodingfilepathwindows text encodingfilepathunix text encodingfilepathwindows text encoding   2021 Julian OspaldMIT"Julian Ospald  experimentalportable Safe-Inferred   Safe-Inferred 4filepathThe character that separates directories. In the case where more than one character is possible,  is the 'ideal' one. pathSeparator == '/'filepath$The list of all possible separators. ;pathSeparators == ['/'] pathSeparator `elem` pathSeparatorsfilepathRather than using (== )5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)filepathThe character that is used to separate the entries in the $PATH environment variable. searchPathSeparator == ':'filepath"Is the character a file separator? 5isSearchPathSeparator a == (a == searchPathSeparator)filepathFile extension character extSeparator == '.'filepath(Is the character an extension character? 'isExtSeparator a == (a == extSeparator)filepathTake a string, split it on the  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"]filepathSplit 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%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 "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"filepathSet 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) yfilepathAdd an extension, even if there is already one there, equivalent to . "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"filepath0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)filepathAdd 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"filepath*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)filepath5Does 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" == Falsefilepath2Drop the given extension from a filepath, and the "." preceding it. Returns : if the filepath does not have the given extension, or . 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 xfilepathSplit 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")filepathDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions xfilepathGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"filepathReplace 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"filepath6Split 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")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"&Join a drive and the rest of the path. 0Valid x => uncurry joinDrive (splitDrive x) == xfilepathGet the drive from a filepath. !takeDrive x == fst (splitDrive x)filepathDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)filepathDoes a path have a drive. not (hasDrive x) == null (takeDrive x) hasDrive "/foo" == True hasDrive "foo" == False hasDrive "" == FalsefilepathIs an element a drive ?isDrive "/" == True isDrive "/foo" == False isDrive "" == Falsefilepath*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 "/" == ("/","")filepathSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == xfilepathDrop the filename. Unlike , this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)filepathGet 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)filepath0Get 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"filepathSet 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) == xfilepathIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == TruefilepathAdd a trailing file path separator if one is not already present. hasTrailingPathSeparator (addTrailingPathSeparator x) hasTrailingPathSeparator x ==> addTrailingPathSeparator x == x addTrailingPathSeparator "test/rest" == "test/rest/"filepath#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive xfilepath*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"filepath1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` xfilepath An alias for .filepathCombine 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"filepath(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"]filepathJust 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"] Valid x => joinPath (splitDirectories x) `equalFilePath` x splitDirectories "" == [] splitDirectories "/test///file" == ["/","test","file"]filepath!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"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 , 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")filepathContract 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 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"filepathNormalise a file)// outside of the drive can be made blank/ -> ./ -> ""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"filepathIs 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)filepathTake 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"filepath/Is a path relative, or is it fixed to the root? isRelative "test/path" == True isRelative "/test" == False isRelative "/" == Falsefilepath not .  "isAbsolute x == not (isRelative x)filepath QuasiQuote a >. This accepts Unicode characters and encodes as UTF-8. Runs  on the input.  2021 Julian OspaldMIT"Julian Ospald  experimentalportable Safe-InferredFz3filepathIs 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" == TruefilepathThe character that separates directories. In the case where more than one character is possible,  is the 'ideal' one. >Windows: pathSeparator == '\\'S Posix: pathSeparator == '/'filepath$The list of all possible separators. Windows: pathSeparators == ['\\', '/'] Posix: pathSeparators == ['/'] pathSeparator `elem` pathSeparatorsfilepathRather than using (== )5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)filepathThe character that is used to separate the entries in the $PATH environment variable. Posix: searchPathSeparator == ':' Windows: searchPathSeparator == ';'filepath"Is the character a file separator? 5isSearchPathSeparator a == (a == searchPathSeparator)filepathFile extension character extSeparator == '.'filepath(Is the character an extension character? 'isExtSeparator a == (a == extSeparator)filepathTake a string, split it on the  character.On Windows, blank items are ignored on Windows, and path elements are stripped of quotes.'On Posix, blank items are converted to .3 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"]filepathSplit 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%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 "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"filepathSet 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) yfilepathAdd an extension, even if there is already one there, equivalent to . "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"filepath0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)filepathAdd 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"filepath*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)filepath5Does 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" == Falsefilepath2Drop the given extension from a filepath, and the "." preceding it. Returns : if the filepath does not have the given extension, or . 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 xfilepathSplit 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")filepathDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions xfilepathGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"filepathReplace 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"filepathSplit 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&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"filepathGet the drive from a filepath. !takeDrive x == fst (splitDrive x)filepathDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)filepathDoes 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 "" == FalsefilepathIs an element a drive Posix: isDrive "/" == True Posix: isDrive "/foo" == False Windows: isDrive "C:\\" == True Windows: isDrive "C:\\foo" == False isDrive "" == Falsefilepath*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:","")filepathSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == xfilepathDrop the filename. Unlike , this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)filepathGet 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)filepath0Get 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"filepathSet 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) == xfilepathIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == TruefilepathAdd 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#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive xfilepath*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:\\"filepath1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` xfilepath An alias for .filepathCombine 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(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"]filepathJust 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!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"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 , 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:/")filepathContract 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"filepathNormalise a file)// outside of the drive can be made blank/ -> ./ -> ""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"filepathTake 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"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 "/" == FalseAccording to [1]:/"A UNC name of any format [is never relative]."6"You cannot use the "\?" prefix with a relative path."filepath not .  "isAbsolute x == not (isRelative x) Safe-Inferred Q filepath"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  if encoding fails.filepath Encode a  with the specified encoding.filepathLike , except this mimics the behavior of the base library when doing filesystem operations, which is: on unix, uses shady PEP 383 style encoding (based on the current locale, but PEP 383 only works properly on UTF-8 encodings, so good luck)on windows does permissive UTF-16 encoding, where coding errors generate Chars in the surrogate rangeLooking 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).filepath"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  if decoding fails.filepath Decode an  with the specified encoding.filepathLike , except this mimics the behavior of the base library when doing filesystem operations, which is: on unix, uses shady PEP 383 style encoding (based on the current locale, but PEP 383 only works properly on UTF-8 encodings, so good luck)on windows does permissive UTF-16 encoding, where coding errors generate Chars in the surrogate rangeLooking 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).filepathConstructs an OsPath from a ByteString.On windows, this ensures valid UCS-2LE, on unix it is passed unchanged/unchecked.Throws 3 on invalid UCS-2LE on windows (although unlikely).filepathQuasiQuote an . This accepts Unicode characters and encodes as UTF-8 on unix and UTF-16LE on windows. Runs  on the input.filepath Unpack an  to a list of .filepathPack a list of  to an .)Note that using this in conjunction with unsafeFromChar to convert from [Char] to  is probably not what you want, because it will truncate unicode code points.filepathunix text encodingfilepathwindows text encodingfilepathunix text encodingfilepathwindows text encoding   Safe-InferredZ filepath"Partial unicode friendly encoding.This encodes as UTF16-LE (strictly), which is a pretty good guess. Throws an  if encoding fails.filepath Encode a  with the specified encoding.filepathThis 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 /unsafeDupablePerformIO.filepath"Partial unicode friendly decoding.) (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%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 "ext" == "/directory/path.ext" "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" "foo.o" -<.> "c" == "foo.c"filepathSet 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) yfilepathAdd an extension, even if there is already one there, equivalent to . "/directory/path" <.> "ext" == "/directory/path.ext" "/directory/path" <.> ".ext" == "/directory/path.ext"filepath0Remove last extension, and the "." preceding it. dropExtension "/directory/path.ext" == "/directory/path" dropExtension x == fst (splitExtension x)filepathAdd 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"filepath*Does the given filename have an extension? hasExtension "/directory/path.ext" == True hasExtension "/directory/path" == False null (takeExtension x) == not (hasExtension x)filepath5Does 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" == Falsefilepath2Drop the given extension from a filepath, and the "." preceding it. Returns : if the filepath does not have the given extension, or . 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 xfilepathSplit 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")filepathDrop all extensions. dropExtensions "/directory/path.ext" == "/directory/path" dropExtensions "file.tar.gz" == "file" not $ hasExtension $ dropExtensions x not $ any isExtSeparator $ takeFileName $ dropExtensions xfilepathGet all extensions. takeExtensions "/directory/path.ext" == ".ext" takeExtensions "file.tar.gz" == ".tar.gz"filepathReplace 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"filepath%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")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"&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"filepathGet the drive from a filepath. !takeDrive x == fst (splitDrive x)filepathDelete the drive, if it exists. !dropDrive x == snd (splitDrive x)filepathDoes a path have a drive. not (hasDrive x) == null (takeDrive x) hasDrive "C:\\foo" == True hasDrive "C:foo" == True hasDrive "foo" == False hasDrive "" == FalsefilepathIs an element a drive isDrive "C:\\" == True isDrive "C:\\foo" == False isDrive "" == Falsefilepath*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:","")filepathSet the filename. replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" Valid x => replaceFileName x (takeFileName x) == xfilepathDrop the filename. Unlike , this function will leave a trailing path separator on the directory. dropFileName "/directory/file.ext" == "/directory/" dropFileName x == fst (splitFileName x)filepathGet 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)filepath0Get 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"filepathSet 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) == xfilepathIs an item either a directory or the last character a path separator? hasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == TruefilepathAdd a trailing file path separator if one is not already present. hasTrailingPathSeparator (addTrailingPathSeparator x) hasTrailingPathSeparator x ==> addTrailingPathSeparator x == xfilepath#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" dropTrailingPathSeparator "/" == "/" dropTrailingPathSeparator "\\" == "\\"filepath*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:\\"filepath1Set the directory, keeping the filename the same. replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` xfilepath An alias for .filepathCombine 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"filepath(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"]filepathJust 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"] 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"]filepath!Join path elements back together. joinPath z == foldr () "" z joinPath ["/","directory/","file.ext"] == "/directory/file.ext" Valid x => joinPath (splitPath x) == x joinPath [] == ""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 , 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:/")filepathContract 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 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 "/" "//" == "//"filepathNormalise a file)// outside of the drive can be made blank/ -> ./ -> ""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 "." == "."filepathIs 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" == TruefilepathTake 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"filepath/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" == TrueAccording to [1]:/"A UNC name of any format [is never relative]."6"You cannot use the "\?" prefix with a relative path."filepath not .  "isAbsolute x == not (isRelative x)filepath QuasiQuote a . This accepts Unicode characters and encodes as UTF-16LE. Runs  on the input. !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~defghijklmnopqrstuvwxyz{|}~5;%4\?-(GD$=6ba7'&,+*ZY_3120POLKNMJIFEcXTS8WV[RQdefghijkmnopqrstuvwxyz{|}~defghijkmnopqrstuvwxyz{|}~                                                                                 % ;           % ;  defghijkmnopqrstuvwxyz{|}~%; d e f g h i j k m n o p q r s t u v w x y z { | } ~                        %;defghijkmnopqrstuvwxyz{|}~..)filepath-1.4.100.3-39mvyRs9KwlCA9FbUoZgHJSystem.FilePath.Posix#System.OsPath.Data.ByteString.ShortSystem.FilePath.Windows,System.OsPath.Data.ByteString.Short.Internal*System.OsPath.Data.ByteString.Short.Word16System.OsPath.Encoding.InternalSystem.OsPath.Posix.InternalSystem.OsPath.Windows.InternalSystem.OsString.Internal.TypesSystem.OsPath.Types System.OsPathSystem.OsString.PosixSystem.OsString.InternalSystem.OsPath.PosixSystem.OsPath.InternalSystem.OsString.WindowsSystem.OsPath.WindowsSystem.FilePathP takeWhile dropWhile dropWhileEndbreakspanSystem.OsPath.EncodingSystem.OsStringbaseGHC.IOFilePathbytestring-0.11.3.1Data.ByteString.Short.InternalSBSShortByteStringuseAsCStringLen useAsCStringunsnocunpackunfoldrNunfoldrunconstoShort takeWhileEndtakeEndtaketail stripSuffix stripPrefix splitWithsplitAtsplitspanEndsnoc singletonreverse replicate partitionpackCStringLen packCStringpacknullmaplengthlast isSuffixOf isPrefixOf isInfixOf intercalateinit indexMaybeindexhead fromShortfoldr1'foldr1foldr'foldrfoldl1'foldl1foldl'foldl findIndices findIndexfindfilterempty elemIndices elemIndexelemdropEnddropcountconsconcatbreakSubstringbreakEndappendanyall!? pathSeparatorpathSeparatorsisPathSeparatorsearchPathSeparatorisSearchPathSeparator extSeparatorisExtSeparatorsplitSearchPath getSearchPathsplitExtension takeExtension-<.>replaceExtension<.> dropExtension addExtension hasExtension isExtensionOfstripExtensionsplitExtensionsdropExtensionstakeExtensionsreplaceExtensions splitDrive joinDrive takeDrive dropDrivehasDriveisDrive splitFileNamereplaceFileName dropFileName takeFileName takeBaseNamereplaceBaseNamehasTrailingPathSeparatoraddTrailingPathSeparatordropTrailingPathSeparator takeDirectoryreplaceDirectorycombine splitPathsplitDirectoriesjoinPath equalFilePath makeRelative normaliseisValid makeValid isRelative isAbsoluteMBAMBA#BABA#_nulisSpace word16ToCharcreateasBAnewPinnedByteArray newByteArray copyByteArrayunsafeFreezeByteArraycopyAddrToByteArray packCWStringpackCWStringLen useAsCWStringuseAsCWStringLen newCWString moduleErrorIOmoduleErrorMsg packWord16 packLenWord16 unpackWord16 packWord16RevpackLenWord16RevwriteWord16ArrayindexWord8ArrayindexWord16ArrayencodeWord16LE#decodeWord16LE# setByteArraycopyMutableByteArray createAndTrimcreateAndTrim'createAndTrim''findIndexOrLengthfindFromEndUntil assertEven errorEmptySBS moduleErroruncons2 numWord16EncodingException EncodingErrorucs2lemkUcs2le ucs2le_DF ucs2le_EF ucs2le_decode ucs2le_encode utf16le_b mkUTF16le_b utf16le_b_DF utf16le_b_EFutf16le_b_decodeutf16le_b_encodecWcharsToChars_UCS2cWcharsToCharscharsToCWcharswithFilePathWinpeekFilePathWinwithFilePathPosixpeekFilePathPosix decodeWithTE encodeWithTEdecodeWithBasePosixencodeWithBasePosixdecodeWithBaseWindowsencodeWithBaseWindowsshowEncodingExceptionwNUL$fNFDataEncodingException$fExceptionEncodingException$fShowEncodingException$fEqEncodingExceptionOsChar getOsCharOsString getOsString PlatformChar PosixChar getPosixChar WindowsChargetWindowsCharPlatformString PosixStringgetPosixString WindowsStringgetWindowsStringPWunPWWWunWWPSunPSWSunWS$fLiftBoxedRepWindowsString$fShowWindowsString$fLiftBoxedRepPosixString$fShowPosixString$fShowWindowsChar$fShowPosixChar$fLiftBoxedRepOsString$fSemigroupOsString$fMonoidOsString $fOrdOsString $fEqOsString$fShowOsString $fOrdOsChar $fEqOsChar $fShowOsChar$fGenericOsChar$fNFDataOsChar$fGenericOsString$fNFDataOsString $fEqPosixChar$fOrdPosixChar$fGenericPosixChar$fNFDataPosixChar$fEqWindowsChar$fOrdWindowsChar$fGenericWindowsChar$fNFDataWindowsChar$fEqPosixString$fOrdPosixString$fSemigroupPosixString$fMonoidPosixString$fGenericPosixString$fNFDataPosixString$fEqWindowsString$fOrdWindowsString$fSemigroupWindowsString$fMonoidWindowsString$fGenericWindowsString$fNFDataWindowsStringOsPath PlatformPath PosixPath WindowsPath encodeUtf encodeWithencodeFS decodeUtf decodeWithdecodeFS fromBytespstrunsafeFromChartoCharosstrospisPosix isWindows GHC.MaybeNothingJustisLetter combineAlwaysGHC.WordWord16Word8 unsafeIndexGHC.IO.Encoding.Types TextEncoding wordToChar charToWordGHC.BaseString GHC.IO.UnsafeunsafePerformIO