a      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`(c) Neil Mitchell 2005-2007BSD3libraries@haskell.orgstableportableSafe4a*Is the operating system Unix or Linux likeb$Is the operating system Windows likedThe 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. kWindows: pathSeparators == ['\\', '/'] Posix: pathSeparators == ['/'] pathSeparator `elem` pathSeparatorsRather than using (== )5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)UThe character that is used to separate the entries in the $PATH environment variable. GWindows: searchPathSeparator == ';' Posix: searchPathSeparator == ':'"Is the character a file separator? 5isSearchPathSeparator 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 /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"] %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","") 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/","") %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" @Set the extension of a file, overwriting one if already present. 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"  Alias to ), for people who like that sort of thing.0Remove last extension, and the "." preceding it. )dropExtension x == fst (splitExtension x)>Add an extension, even if there is already one there. E.g. -addExtension "foo.txt" "bat" -> "foo.txt.bat". 3addExtension "file.txt" "bib" == "file.txt.bib" addExtension "file." ".bib" == "file..bib" addExtension "file" ".bib" == "file.bib" addExtension "/" "x" == "/.x" Valid x => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext" Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"*Does the given filename have an extension? .null (takeExtension x) == not (hasExtension x)Split on all extensions uncurry (++) (splitExtensions x) == x uncurry addExtension (splitExtensions x) == x splitExtensions "file.tar.gz" == ("file",".tar.gz")Drop all extensions %not $ hasExtension (dropExtensions x)Get all extensions )takeExtensions "file.tar.gz" == ".tar.gz"cnIs the given character a valid drive letter? only a-z and A-Z are letters, not isAlpha which is more unicodey?Split a path into a drive and a path. On Unix, / 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")&Join a drive and the rest of the path.  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. LValid 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:","")Set the filename. 2Valid x => replaceFileName x (takeFileName x) == xDrop 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" Valid x => takeFileName (x </> "fred") == "fred" Valid x => isRelative (takeFileName x)0Get the base name, without an extension or path. takeBaseName "file/test.txt" == "test" takeBaseName "dave.ext" == "dave" takeBaseName "" == "" takeBaseName "test" == "test" takeBaseName (addTrailingPathSeparator x) == "" takeBaseName "file/file.tar.gz" == "file.tar"Set the base name. replaceBaseName "file/test.txt" "bob" == "file/bob.txt" replaceBaseName "fred" "bill" == "bill" replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar" Valid x => replaceBaseName x (takeBaseName x) == x EIs an item either a directory or the last character a path separator? QhasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == True!AAdd 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/""#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x Posix: dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\"#*Get the directory name, move up one level.  takeDirectory x `isPrefixOf` x || takeDirectory x == "." 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:\\"$1Set the directory, keeping the filename the same. AValid x => replaceDirectory x (takeDirectory x) `equalFilePath` x%&Combine two paths, if the second path 0, then it returns the second. 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"d0Combine two paths, assuming rhs is NOT absolute.&A nice alias for %.'(Split a path by the directory separator.  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"](Just as '5, but don't add the trailing slashes to each element. splitDirectories "test/file" == ["test","file"] splitDirectories "/test/file" == ["/","test","file"] Valid x => joinPath (splitDirectories x) `equalFilePath` x splitDirectories "" == [])!Join path elements back together. sValid x => joinPath (splitPath x) == x joinPath [] == "" Posix: joinPath ["test","file","path"] == "test/file/path"*Equality of two s. If you call !System.Directory.canonicalizePathk 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 == "." null y || equalFilePath (makeRelative x (x </> y)) y || null (takeFileName 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" 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",Normalise a file)// outside of the drive can be made blank/ -> ./ -> "" Posix: normalise "/file/\\test////" == "/file/\\test/" Posix: normalise "/file/./test" == "/file/test" Posix: normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/" Posix: normalise "../bob/fred/" == "../bob/fred/" Posix: normalise "./bob/fred/" == "bob/fred/" Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\" Windows: normalise "c:\\" == "C:\\" Windows: normalise "\\\\server\\test" == "\\\\server\\test" Windows: normalise "c:/file" == "C:\\file" normalise "." == "." Posix: normalise "./" == "./" Posix: normalise "./." == "./" Posix: normalise "/" == "/" 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.KTake 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_" 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"//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:" == True Windows: isRelative "\\\\foo" == False Windows: isRelative "/foo" == True Posix: isRelative "test/path" == True Posix: isRelative "/test" == False0 not . / "isAbsolute x == not (isRelative x)>ab cefghij !"#$%d&'()*+,klm-./n01  !"#$%&'()*+,-./01  #$%&')( !",*+/0-.>ab cefghij !"#$%d&'()*+,klm-./n0 &(c) Neil Mitchell 2005-2007BSD3libraries@haskell.orgstableportableSafe4o*Is the operating system Unix or Linux likep$Is the operating system Windows like1dThe character that separates directories. In the case where more than one character is possible, 1 is the 'ideal' one. [Windows: pathSeparator == '\\' Posix: pathSeparator == '/' isPathSeparator pathSeparator2$The list of all possible separators. kWindows: pathSeparators == ['\\', '/'] Posix: pathSeparators == ['/'] pathSeparator `elem` pathSeparators3Rather than using (== 1)5, use this. Test if something is a path separator. .isPathSeparator a == (a `elem` pathSeparators)4UThe character that is used to separate the entries in the $PATH environment variable. GWindows: searchPathSeparator == ';' Posix: searchPathSeparator == ':'5"Is the character a file separator? 5isSearchPathSeparator 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 /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"]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","") 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/","");%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"<@Set the extension of a file, overwriting one if already present. 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"= Alias to ?), for people who like that sort of thing.>0Remove last extension, and the "." preceding it. )dropExtension x == fst (splitExtension x)?>Add an extension, even if there is already one there. E.g. -addExtension "foo.txt" "bat" -> "foo.txt.bat". 3addExtension "file.txt" "bib" == "file.txt.bib" addExtension "file." ".bib" == "file..bib" addExtension "file" ".bib" == "file.bib" addExtension "/" "x" == "/.x" Valid x => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext" Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"@*Does the given filename have an extension? .null (takeExtension x) == not (hasExtension x)ASplit on all extensions uncurry (++) (splitExtensions x) == x uncurry addExtension (splitExtensions x) == x splitExtensions "file.tar.gz" == ("file",".tar.gz")BDrop all extensions %not $ hasExtension (dropExtensions x)CGet all extensions )takeExtensions "file.tar.gz" == ".tar.gz"qnIs the given character a valid drive letter? only a-z and A-Z are letters, not isAlpha which is more unicodeyD?Split a path into a drive and a path. On Unix, / 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")E&Join a drive and the rest of the path.  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)GDelete 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 driveJ*Split a filename into directory and file. U is the inverse. LValid 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:","")KSet the filename. 2Valid x => replaceFileName x (takeFileName x) == xLDrop 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" Valid x => takeFileName (x </> "fred") == "fred" Valid x => isRelative (takeFileName x)N0Get the base name, without an extension or path. takeBaseName "file/test.txt" == "test" takeBaseName "dave.ext" == "dave" takeBaseName "" == "" takeBaseName "test" == "test" takeBaseName (addTrailingPathSeparator x) == "" takeBaseName "file/file.tar.gz" == "file.tar"OSet the base name. 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) == xPEIs an item either a directory or the last character a path separator? QhasTrailingPathSeparator "test" == False hasTrailingPathSeparator "test/" == TrueQAAdd 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/"R#Remove any trailing path separators dropTrailingPathSeparator "file/test/" == "file/test" Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x Posix: dropTrailingPathSeparator "/" == "/" Windows: dropTrailingPathSeparator "\\" == "\\"S*Get the directory name, move up one level.  takeDirectory x `isPrefixOf` x || takeDirectory x == "." 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:\\"T1Set the directory, keeping the filename the same. AValid x => replaceDirectory x (takeDirectory x) `equalFilePath` xU&Combine two paths, if the second path `, then it returns the second. 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"r0Combine two paths, assuming rhs is NOT absolute.VA nice alias for U.W(Split a path by the directory separator.  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"]XJust as W5, but don't add the trailing slashes to each element. splitDirectories "test/file" == ["test","file"] splitDirectories "/test/file" == ["/","test","file"] Valid x => joinPath (splitDirectories x) `equalFilePath` x splitDirectories "" == []Y!Join path elements back together. sValid x => joinPath (splitPath x) == x joinPath [] == "" Posix: joinPath ["test","file","path"] == "test/file/path"ZEquality of two s. If you call !System.Directory.canonicalizePathk 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 == "." null y || equalFilePath (makeRelative x (x </> y)) y || null (takeFileName 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" 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"\Normalise a file)// outside of the drive can be made blank/ -> 1./ -> "" Posix: normalise "/file/\\test////" == "/file/\\test/" Posix: normalise "/file/./test" == "/file/test" Posix: normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/" Posix: normalise "../bob/fred/" == "../bob/fred/" Posix: normalise "./bob/fred/" == "bob/fred/" Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\" Windows: normalise "c:\\" == "C:\\" Windows: normalise "\\\\server\\test" == "\\\\server\\test" Windows: normalise "c:/file" == "C:\\file" normalise "." == "." Posix: normalise "./" == "./" Posix: normalise "./." == "./" Posix: normalise "/" == "/" 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^KTake 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_" 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"_/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:" == True Windows: isRelative "\\\\foo" == False Windows: isRelative "/foo" == True Posix: isRelative "test/path" == True Posix: isRelative "/test" == False` not . _ "isAbsolute x == not (isRelative x)>op123456789:;<=>?@ABCqDstuvwEFGHIJxKLMNOPQRSTUrVWXYZ[\yz{]^_|`1123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`1123456789:;<>?@=ABCDEFHGIJMKLNOSTUVWYXPQR\Z[_`]^>op123456789:;<=>?@ABCqDstuvwEFGHIJxKLMNOPQRSTUrVWXYZ[\yz{]^_|`=V(c) Neil Mitchell 2005-2007BSD3libraries@haskell.orgstableportableSafe1123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`}      !"#$%&'()*+,-./0123456      !"#$%&'()*+,-./0123456789:;<=>?@ABCD789:;<=>?@ABCDEfilepath-1.3.0.2System.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 isAbsoluteisPosix isWindowsisLetter combineAlwaysaddSlash readDriveUNCreadDriveLetterreadDriveSharereadDriveShareNamesplitFileName_normaliseDrive badCharacters badElementsisRelativeDrive