úΑ6ŒAa      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`portablestablelibraries@haskell.orgSafe0FThe character that separates directories. In the case where more than  one character is possible,  is the 'ideal' one.  Windows: pathSeparator == '\\'  Posix: pathSeparator == '/'  isPathSeparator pathSeparator %The list of all possible separators. ( Windows: pathSeparators == ['\\', '/'] " Posix: pathSeparators == ['/'] % pathSeparator `elem` pathSeparators Rather than using (== ), use this. Test if something  is a path separator. 0 isPathSeparator a == (a `elem` pathSeparators) VThe character that is used to separate the entries in the $PATH environment variable. % Windows: searchPathSeparator == ';' % Posix: searchPathSeparator == ':' #Is the character a file separator? 7 isSearchPathSeparator a == (a == searchPathSeparator) File extension character  extSeparator == '.' )Is the character an extension character? ) isExtSeparator a == (a == extSeparator) Take a string, split it on the  character. Follows the recommendations in   Fhttp://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html L Posix: splitSearchPath "File1:File2:File3" == ["File1","File2","File3"] P Posix: splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"] L Windows: splitSearchPath "File1;File2;File3" == ["File1","File2","File3"] L Windows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"] &Get a list of filepaths in the $PATH. Split on the extension.  is the inverse. & uncurry (++) (splitExtension x) == x . uncurry addExtension (splitExtension x) == x . splitExtension "file.txt" == ("file",".txt") & splitExtension "file" == ("file","") 8 splitExtension "file/file.txt" == ("file/file",".txt") : splitExtension "file.txt/boris" == ("file.txt/boris","") B splitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext") J splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred") : splitExtension "file/path.txt/" == ("file/path.txt/","") %Get the extension of a file, returns "" for no extension, .ext otherwise. + takeExtension x == snd (splitExtension x) ; Valid x => takeExtension (addExtension x "ext") == ".ext" ? Valid x => takeExtension (replaceExtension x "ext") == ".ext" ASet the extension of a file, overwriting one if already present. 2 replaceExtension "file.txt" ".bob" == "file.bob" 1 replaceExtension "file.txt" "bob" == "file.bob" . replaceExtension "file" ".bob" == "file.bob" * replaceExtension "file.txt" "" == "file" ; replaceExtension "file.fred.bob" "txt" == "file.fred.txt"  Alias to *, for people who like that sort of thing. Remove last extension, and the "." preceding it. + dropExtension x == fst (splitExtension x) 6Add an extension, even if there is already one there.  E.g.  addExtension "foo.txt" "bat" -> " foo.txt.bat". 1 addExtension "file.txt" "bib" == "file.txt.bib" , addExtension "file." ".bib" == "file..bib" * addExtension "file" ".bib" == "file.bib"  addExtension "/" "x" == "/.x" U Valid x => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext" ? Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt" +Does the given filename have an extension? 0 null (takeExtension x) == not (hasExtension x) Split on all extensions ' uncurry (++) (splitExtensions x) == x / uncurry addExtension (splitExtensions x) == x 5 splitExtensions "file.tar.gz" == ("file",".tar.gz") Drop all extensions ' not $ hasExtension (dropExtensions x) Get all extensions + takeExtensions "file.tar.gz" == ".tar.gz" &Split a path into a drive and a path.  On Unix, / is a Drive. " uncurry (++) (splitDrive x) == x + Windows: splitDrive "file" == ("","file") 1 Windows: splitDrive "c:/file" == ("c:/","file") 3 Windows: splitDrive "c:\\file" == ("c:\\","file") C Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test") 7 Windows: splitDrive "\\\\shared" == ("\\\\shared","") S Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file") O Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file") A Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file") ' Windows: splitDrive "/d" == ("","/d") - Posix: splitDrive "/test" == ("/","test") / Posix: splitDrive "//test" == ("//","test") 5 Posix: splitDrive "test/file" == ("","test/file") + Posix: splitDrive "file" == ("","file") 'Join a drive and the rest of the path. 0 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" Get the drive from a filepath. # takeDrive x == fst (splitDrive x)  Delete the drive, if it exists. # dropDrive x == snd (splitDrive x) Does a path have a drive. ( not (hasDrive x) == null (takeDrive x) Is an element a drive *Split a filename into directory and file. % is the inverse. R Valid x => uncurry (</>) (splitFileName x) == x || fst (splitFileName x) == "./" , Valid x => isValid (fst (splitFileName x)) 6 splitFileName "file/bob.txt" == ("file/", "bob.txt") ( splitFileName "file/" == ("file/", "") & splitFileName "bob" == ("./", "bob") ( Posix: splitFileName "/" == ("/","") * Windows: splitFileName "c:" == ("c:","") Set the filename. 4 Valid x => replaceFileName x (takeFileName x) == x Drop the filename. ) dropFileName x == fst (splitFileName x) Get the file name.  takeFileName "test/" == ""  takeFileName x `isSuffixOf` x ) takeFileName x == snd (splitFileName x) > Valid x => takeFileName (replaceFileName x "fred") == "fred" 2 Valid x => takeFileName (x </> "fred") == "fred" ( Valid x => isRelative (takeFileName x) 1Get the base name, without an extension or path. ( takeBaseName "file/test.txt" == "test" # takeBaseName "dave.ext" == "dave"  takeBaseName "" == ""  takeBaseName "test" == "test" 1 takeBaseName (addTrailingPathSeparator x) == "" / takeBaseName "file/file.tar.gz" == "file.tar" Set the base name. 9 replaceBaseName "file/test.txt" "bob" == "file/bob.txt" ) replaceBaseName "fred" "bill" == "bill" G replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar" 4 Valid x => replaceBaseName x (takeBaseName x) == x FIs an item either a directory or the last character a path separator? * hasTrailingPathSeparator "test" == False * hasTrailingPathSeparator "test/" == True !BAdd a trailing file path separator if one is not already present. 7 hasTrailingPathSeparator (addTrailingPathSeparator x) @ hasTrailingPathSeparator x ==> addTrailingPathSeparator x == x @ Posix: addTrailingPathSeparator "test/rest" == "test/rest/" "$Remove any trailing path separators 7 dropTrailingPathSeparator "file/test/" == "file/test" U Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x 0 Posix: dropTrailingPathSeparator "/" == "/" 2 Windows: dropTrailingPathSeparator "\\" == "\\" #+Get the directory name, move up one level. D takeDirectory x `isPrefixOf` x || takeDirectory x == "." & takeDirectory "foo" == "." 6 takeDirectory "/foo/bar/baz" == "/foo/bar" ; takeDirectory "/foo/bar/baz/" == "/foo/bar/baz" 4 takeDirectory "foo/bar/baz" == "foo/bar" - Windows: takeDirectory "foo\\bar" == "foo" 6 Windows: takeDirectory "foo\\bar\\\\" == "foo\\bar" * Windows: takeDirectory "C:\\" == "C:\\" $2Set the directory, keeping the filename the same. C Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x %&Combine two paths, if the second path 0, then it returns the second. I Valid x => combine (takeDirectory x) (takeFileName x) `equalFilePath` x ( Posix: combine "/" "test" == "/test" - Posix: combine "home" "bob" == "home/bob" . Windows: combine "home" "bob" == "home\\bob" * Windows: combine "home" "/bob" == "/bob" &A nice alias for %. ')Split a path by the directory separator.  concat (splitPath x) == x / splitPath "test//item/" == ["test//","item/"] 8 splitPath "test/item/file" == ["test/","item/","file"]  splitPath "" == [] A Windows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"] 9 Posix: splitPath "/file/test" == ["/","file/","test"] (Just as ' , but don',t add the trailing slashes to each element. 1 splitDirectories "test/file" == ["test","file"] 6 splitDirectories "/test/file" == ["/","test","file"] < Valid x => joinPath (splitDirectories x) `equalFilePath` x  splitDirectories "" == [] )"Join path elements back together. ( Valid x => joinPath (splitPath x) == x  joinPath [] == "" < Posix: joinPath ["test","file","path"] == "test/file/path" *Equality of two s.  If you call !System.Directory.canonicalizePath 3 first this has a much better chance of working.  Note that this doesn' t follow symlinks or DOSNAM~1s. ' x == y ==> equalFilePath x y ; normalise x == normalise y ==> equalFilePath x y % Posix: equalFilePath "foo" "foo/" + Posix: not (equalFilePath "foo" "/foo") * Posix: not (equalFilePath "foo" "FOO") $ Windows: equalFilePath "foo" "FOO" +/Contract a filename, based on a relative path. There is no corresponding  makeAbsolute function, instead use  !System.Directory.canonicalizePath which has the same effect. ~ Valid y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> makeRelative y x) x " makeRelative x x == "." X null y || equalFilePath (makeRelative x (x </> y)) y || null (takeFileName x) ; Windows: makeRelative "C:\\Home" "c:\\home\\bob" == "bob" 9 Windows: makeRelative "C:\\Home" "c:/home/bob" == "bob" E Windows: makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob" A Windows: makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob" 4 Windows: makeRelative "/Home" "/home/bob" == "bob" : Posix: makeRelative "/Home" "/home/bob" == "/home/bob" E Posix: makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar" . Posix: makeRelative "/fred" "bob" == "bob" @ Posix: makeRelative "/file/test" "/file/test/fred" == "fred" B Posix: makeRelative "/file/test" "/file/test/fred/" == "fred/" @ Posix: makeRelative "some/path" "some/path/a/b/c" == "a/b/c" ,Normalise a file  //( outside of the drive can be made blank  / ->   ./ -> "" : Posix: normalise "/file/\\test////" == "/file/\\test/" 3 Posix: normalise "/file/./test" == "/file/test" K Posix: normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/" 5 Posix: normalise "../bob/fred/" == "../bob/fred/" 1 Posix: normalise "./bob/fred/" == "bob/fred/" : Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\" % Windows: normalise "c:\\" == "C:\\" = Windows: normalise "\\\\server\\test" == "\\\\server\\test" , Windows: normalise "c:/file" == "C:\\file"  normalise "." == "." ! Posix: normalise "./" == "./" " Posix: normalise "./." == "./"  Posix: normalise "/" == "/" 0 Posix: normalise "bob/fred/." == "bob/fred/" -;Is a FilePath valid, i.e. could you create a file like it?  isValid "" == 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 .LTake a FilePath and make it valid; does not change already valid FilePaths.  isValid (makeValid x)  isValid x ==> makeValid x == x  makeValid "" == "_" = Windows: makeValid "c:\\test:of_test" == "c:\\test_of_test" ' Windows: makeValid "test*" == "test_" 8 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" 8 Windows: makeValid "c:\\nul\\file" == "c:\\nul_\\file" /0Is 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:" == True ( Windows: isRelative "\\\\foo" == False $ Windows: isRelative "/foo" == True ) Posix: isRelative "test/path" == True & Posix: isRelative "/test" == False 0 not . / $ isAbsolute x == not (isRelative x) 0  !"#$%&'()*+,-./01  !"#$%&'()*+,-./01  #$%&')( !",*+/0-.0  !"#$%&'()*+,-./0portablestablelibraries@haskell.orgSafe01FThe character that separates directories. In the case where more than  one character is possible, 1 is the 'ideal' one.  Windows: pathSeparator == '\\'  Posix: pathSeparator == '/'  isPathSeparator pathSeparator 2%The list of all possible separators. ( Windows: pathSeparators == ['\\', '/'] " Posix: pathSeparators == ['/'] % pathSeparator `elem` pathSeparators 3Rather than using (== 1), use this. Test if something  is a path separator. 0 isPathSeparator a == (a `elem` pathSeparators) 4VThe character that is used to separate the entries in the $PATH environment variable. % Windows: searchPathSeparator == ';' % Posix: searchPathSeparator == ':' 5#Is the character a file separator? 7 isSearchPathSeparator a == (a == searchPathSeparator) 6File extension character  extSeparator == '.' 7)Is the character an extension character? ) isExtSeparator a == (a == extSeparator) 8Take a string, split it on the 4 character. Follows the recommendations in   Fhttp://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html L Posix: splitSearchPath "File1:File2:File3" == ["File1","File2","File3"] P Posix: splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"] L Windows: splitSearchPath "File1;File2;File3" == ["File1","File2","File3"] L Windows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"] 9&Get a list of filepaths in the $PATH. :Split on the extension. ? is the inverse. & uncurry (++) (splitExtension x) == x . uncurry addExtension (splitExtension x) == x . splitExtension "file.txt" == ("file",".txt") & splitExtension "file" == ("file","") 8 splitExtension "file/file.txt" == ("file/file",".txt") : splitExtension "file.txt/boris" == ("file.txt/boris","") B splitExtension "file.txt/boris.ext" == ("file.txt/boris",".ext") J splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred") : splitExtension "file/path.txt/" == ("file/path.txt/","") ;%Get the extension of a file, returns "" for no extension, .ext otherwise. + takeExtension x == snd (splitExtension x) ; Valid x => takeExtension (addExtension x "ext") == ".ext" ? Valid x => takeExtension (replaceExtension x "ext") == ".ext" <ASet the extension of a file, overwriting one if already present. 2 replaceExtension "file.txt" ".bob" == "file.bob" 1 replaceExtension "file.txt" "bob" == "file.bob" . replaceExtension "file" ".bob" == "file.bob" * replaceExtension "file.txt" "" == "file" ; replaceExtension "file.fred.bob" "txt" == "file.fred.txt" = Alias to ?*, for people who like that sort of thing. >Remove last extension, and the "." preceding it. + dropExtension x == fst (splitExtension x) ?6Add an extension, even if there is already one there.  E.g.  addExtension "foo.txt" "bat" -> " foo.txt.bat". 1 addExtension "file.txt" "bib" == "file.txt.bib" , addExtension "file." ".bib" == "file..bib" * addExtension "file" ".bib" == "file.bib"  addExtension "/" "x" == "/.x" U Valid x => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext" ? Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt" @+Does the given filename have an extension? 0 null (takeExtension x) == not (hasExtension x) ASplit on all extensions ' uncurry (++) (splitExtensions x) == x / uncurry addExtension (splitExtensions x) == x 5 splitExtensions "file.tar.gz" == ("file",".tar.gz") BDrop all extensions ' not $ hasExtension (dropExtensions x) CGet all extensions + takeExtensions "file.tar.gz" == ".tar.gz" D&Split a path into a drive and a path.  On Unix, / is a Drive. " uncurry (++) (splitDrive x) == x + Windows: splitDrive "file" == ("","file") 1 Windows: splitDrive "c:/file" == ("c:/","file") 3 Windows: splitDrive "c:\\file" == ("c:\\","file") C Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test") 7 Windows: splitDrive "\\\\shared" == ("\\\\shared","") S Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file") O Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file") A Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file") ' Windows: splitDrive "/d" == ("","/d") - Posix: splitDrive "/test" == ("/","test") / Posix: splitDrive "//test" == ("//","test") 5 Posix: splitDrive "test/file" == ("","test/file") + Posix: splitDrive "file" == ("","file") E'Join a drive and the rest of the path. 0 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" FGet the drive from a filepath. # takeDrive x == fst (splitDrive x) G Delete the drive, if it exists. # dropDrive x == snd (splitDrive x) HDoes a path have a drive. ( not (hasDrive x) == null (takeDrive x) IIs an element a drive J*Split a filename into directory and file. U is the inverse. R Valid x => uncurry (</>) (splitFileName x) == x || fst (splitFileName x) == "./" , Valid x => isValid (fst (splitFileName x)) 6 splitFileName "file/bob.txt" == ("file/", "bob.txt") ( splitFileName "file/" == ("file/", "") & splitFileName "bob" == ("./", "bob") ( Posix: splitFileName "/" == ("/","") * Windows: splitFileName "c:" == ("c:","") KSet the filename. 4 Valid x => replaceFileName x (takeFileName x) == x LDrop the filename. ) dropFileName x == fst (splitFileName x) MGet the file name.  takeFileName "test/" == ""  takeFileName x `isSuffixOf` x ) takeFileName x == snd (splitFileName x) > Valid x => takeFileName (replaceFileName x "fred") == "fred" 2 Valid x => takeFileName (x </> "fred") == "fred" ( Valid x => isRelative (takeFileName x) N1Get the base name, without an extension or path. ( takeBaseName "file/test.txt" == "test" # takeBaseName "dave.ext" == "dave"  takeBaseName "" == ""  takeBaseName "test" == "test" 1 takeBaseName (addTrailingPathSeparator x) == "" / takeBaseName "file/file.tar.gz" == "file.tar" OSet the base name. 9 replaceBaseName "file/test.txt" "bob" == "file/bob.txt" ) replaceBaseName "fred" "bill" == "bill" G replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar" 4 Valid x => replaceBaseName x (takeBaseName x) == x PFIs an item either a directory or the last character a path separator? * hasTrailingPathSeparator "test" == False * hasTrailingPathSeparator "test/" == True QBAdd a trailing file path separator if one is not already present. 7 hasTrailingPathSeparator (addTrailingPathSeparator x) @ hasTrailingPathSeparator x ==> addTrailingPathSeparator x == x @ Posix: addTrailingPathSeparator "test/rest" == "test/rest/" R$Remove any trailing path separators 7 dropTrailingPathSeparator "file/test/" == "file/test" U Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x 0 Posix: dropTrailingPathSeparator "/" == "/" 2 Windows: dropTrailingPathSeparator "\\" == "\\" S+Get the directory name, move up one level. D takeDirectory x `isPrefixOf` x || takeDirectory x == "." & takeDirectory "foo" == "." 6 takeDirectory "/foo/bar/baz" == "/foo/bar" ; takeDirectory "/foo/bar/baz/" == "/foo/bar/baz" 4 takeDirectory "foo/bar/baz" == "foo/bar" - Windows: takeDirectory "foo\\bar" == "foo" 6 Windows: takeDirectory "foo\\bar\\\\" == "foo\\bar" * Windows: takeDirectory "C:\\" == "C:\\" T2Set the directory, keeping the filename the same. C Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x U&Combine two paths, if the second path `, then it returns the second. I Valid x => combine (takeDirectory x) (takeFileName x) `equalFilePath` x ( Posix: combine "/" "test" == "/test" - Posix: combine "home" "bob" == "home/bob" . Windows: combine "home" "bob" == "home\\bob" * Windows: combine "home" "/bob" == "/bob" VA nice alias for U. W)Split a path by the directory separator.  concat (splitPath x) == x / splitPath "test//item/" == ["test//","item/"] 8 splitPath "test/item/file" == ["test/","item/","file"]  splitPath "" == [] A Windows: splitPath "c:\\test\\path" == ["c:\\","test\\","path"] 9 Posix: splitPath "/file/test" == ["/","file/","test"] XJust as W , but don',t add the trailing slashes to each element. 1 splitDirectories "test/file" == ["test","file"] 6 splitDirectories "/test/file" == ["/","test","file"] < Valid x => joinPath (splitDirectories x) `equalFilePath` x  splitDirectories "" == [] Y"Join path elements back together. ( Valid x => joinPath (splitPath x) == x  joinPath [] == "" < Posix: joinPath ["test","file","path"] == "test/file/path" ZEquality of two s.  If you call !System.Directory.canonicalizePath 3 first this has a much better chance of working.  Note that this doesn' t follow symlinks or DOSNAM~1s. ' x == y ==> equalFilePath x y ; normalise x == normalise y ==> equalFilePath x y % Posix: equalFilePath "foo" "foo/" + Posix: not (equalFilePath "foo" "/foo") * Posix: not (equalFilePath "foo" "FOO") $ Windows: equalFilePath "foo" "FOO" [/Contract a filename, based on a relative path. There is no corresponding  makeAbsolute function, instead use  !System.Directory.canonicalizePath which has the same effect. ~ Valid y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> makeRelative y x) x " makeRelative x x == "." X null y || equalFilePath (makeRelative x (x </> y)) y || null (takeFileName x) ; Windows: makeRelative "C:\\Home" "c:\\home\\bob" == "bob" 9 Windows: makeRelative "C:\\Home" "c:/home/bob" == "bob" E Windows: makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob" A Windows: makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob" 4 Windows: makeRelative "/Home" "/home/bob" == "bob" : Posix: makeRelative "/Home" "/home/bob" == "/home/bob" E Posix: makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar" . Posix: makeRelative "/fred" "bob" == "bob" @ Posix: makeRelative "/file/test" "/file/test/fred" == "fred" B Posix: makeRelative "/file/test" "/file/test/fred/" == "fred/" @ Posix: makeRelative "some/path" "some/path/a/b/c" == "a/b/c" \Normalise a file  //( outside of the drive can be made blank  / -> 1  ./ -> "" : Posix: normalise "/file/\\test////" == "/file/\\test/" 3 Posix: normalise "/file/./test" == "/file/test" K Posix: normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/" 5 Posix: normalise "../bob/fred/" == "../bob/fred/" 1 Posix: normalise "./bob/fred/" == "bob/fred/" : Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\" % Windows: normalise "c:\\" == "C:\\" = Windows: normalise "\\\\server\\test" == "\\\\server\\test" , Windows: normalise "c:/file" == "C:\\file"  normalise "." == "." ! Posix: normalise "./" == "./" " Posix: normalise "./." == "./"  Posix: normalise "/" == "/" 0 Posix: normalise "bob/fred/." == "bob/fred/" ];Is a FilePath valid, i.e. could you create a file like it?  isValid "" == 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 ^LTake a FilePath and make it valid; does not change already valid FilePaths.  isValid (makeValid x)  isValid x ==> makeValid x == x  makeValid "" == "_" = Windows: makeValid "c:\\test:of_test" == "c:\\test_of_test" ' Windows: makeValid "test*" == "test_" 8 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" 8 Windows: makeValid "c:\\nul\\file" == "c:\\nul_\\file" _0Is 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:" == True ( Windows: isRelative "\\\\foo" == False $ Windows: isRelative "/foo" == True ) Posix: isRelative "test/path" == True & Posix: isRelative "/test" == False ` not . _ $ isAbsolute x == not (isRelative x) 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`1123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`1123456789:;<>?@=ABCDEFHGIJMKLNOSTUVWYXPQR\Z[_`]^0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`portablestablelibraries@haskell.orgSafe1123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a      !"#$%&'()*+,-./0123456      !"#$%&'()*+,-./01234567filepath-1.3.0.0System.FilePath.WindowsSystem.FilePath.PosixSystem.FilePathbaseGHC.IOFilePath pathSeparatorpathSeparatorsisPathSeparatorsearchPathSeparatorisSearchPathSeparator extSeparatorisExtSeparatorsplitSearchPath getSearchPathsplitExtension takeExtensionreplaceExtension<.> dropExtension addExtension hasExtensionsplitExtensionsdropExtensionstakeExtensions splitDrive joinDrive takeDrive dropDrivehasDriveisDrive splitFileNamereplaceFileName dropFileName takeFileName takeBaseNamereplaceBaseNamehasTrailingPathSeparatoraddTrailingPathSeparatordropTrailingPathSeparator takeDirectoryreplaceDirectorycombine splitPathsplitDirectoriesjoinPath equalFilePath makeRelative normaliseisValid makeValid isRelative isAbsolute