FilePath-0.11: Library for manipulating FilePath's in a cross platform way.ContentsIndex
System.FilePath.Version_0_10
Portabilityportable
Stabilityin-progress
Maintainerhttp://www.cs.york.ac.uk/~ndm/
Contents
The basic functions
Path methods (environment $PATH)
Extension methods
Operations on a filepath, as a list of directories
File name manipulators
Description

This is the current interface for System.FilePath, please import it as System.FilePath.Version_0_10 - do not use System.FilePath directly.

The next version is currently under development, and I have used used 3 codes to indicate what will happen to each method (although they are still useable now):

  • GONE means the method is entirely removed - with where the best alternative is
  • RENAMED means the method got renamed
  • Nothing means the method remains the same.

A library for FilePath manipulations, designed to be cross platform. This library will select the correct type of FilePath's for the platform the code is running on at runtime. For more details see http://www.cs.york.ac.uk/~ndm/projects/libraries.php

Some short examples:

You are given a C file, you want to figure out the corresponding object (.o) file:

replaceExtension file "o"

Haskell module Main imports Test, you have the file named main:

[replaceFileName path_to_main "Test" <.> ext | ext <- ["hs","lhs"] ]

You want to download a file from the web and save it to disk:

do let file = makeValid url
   System.IO.createDirectoryIfMissing True (takeDirectory file)

You want to compile a Haskell file, but put the hi file under "interface"

takeDirectory file </> "interface" </> (takeFileName file `replaceExtension` "hi")

You want to display a filename to the user, as neatly as possible

shortPath file >>= putStrLn

The examples in code format descibed by each function are used to generate tests, and should give clear semantics for the functions.

Synopsis
pathSeparator :: Char
pathSeparators :: [Char]
isPathSeparator :: Char -> Bool
fileSeparator :: Char
isFileSeparator :: Char -> Bool
extSeparator :: Char
isExtSeparator :: Char -> Bool
splitFiles :: String -> [FilePath]
getSearchPath :: IO [FilePath]
splitExtension :: FilePath -> (String, String)
takeExtension :: FilePath -> String
replaceExtension :: FilePath -> String -> FilePath
dropExtension :: FilePath -> FilePath
addExtension :: FilePath -> String -> FilePath
hasExtension :: FilePath -> Bool
(<.>) :: FilePath -> String -> FilePath
splitExtensions :: FilePath -> (FilePath, String)
dropExtensions :: FilePath -> FilePath
takeExtensions :: FilePath -> String
splitFileName :: FilePath -> (String, String)
takeFileName :: FilePath -> FilePath
replaceFileName :: FilePath -> String -> FilePath
dropFileName :: FilePath -> FilePath
addFileName :: FilePath -> String -> FilePath
takeBaseName :: FilePath -> String
replaceBaseName :: FilePath -> String -> FilePath
takeDirectory :: FilePath -> FilePath
replaceDirectory :: FilePath -> String -> FilePath
isDirectory :: FilePath -> Bool
isFile :: FilePath -> Bool
asDirectory :: FilePath -> FilePath
asFile :: FilePath -> FilePath
combine :: FilePath -> FilePath -> FilePath
(</>) :: FilePath -> FilePath -> FilePath
splitPath :: FilePath -> [FilePath]
joinPath :: [FilePath] -> FilePath
splitDirectories :: FilePath -> [FilePath]
normalise :: FilePath -> FilePath
equalFilePath :: FilePath -> FilePath -> Bool
shortPath :: FilePath -> IO FilePath
shortPathWith :: FilePath -> FilePath -> FilePath
isValid :: FilePath -> Bool
The basic functions
pathSeparator :: Char

The character that separates directories. In the case where more than one character is possible, pathSeparator is the ideal one.

 Windows: pathSeparator == '\\'
 Posix:   pathSeparator ==  '/'
 isPathSeparator pathSeparator
pathSeparators :: [Char]

The list of all possible separators.

 Windows: pathSeparators == ['\\', '/']
 Posix:   pathSeparators == ['/']
 pathSeparator `elem` pathSeparators
isPathSeparator :: Char -> Bool

Rather than using (== pathSeparator), use this. Test if something is a path separator.

 isPathSeparator a == (a `elem` pathSeparators)
fileSeparator :: Char

RENAMED - searchPathSeparator. A list of possible file separators, between the $PATH variable

 Windows: fileSeparator == ';'
 Posix:   fileSeparator == ':'
isFileSeparator :: Char -> Bool

RENAMED - isSearchPathSeparator. Is the character a file separator?

 isFileSeparator a == (a == fileSeparator)
extSeparator :: Char

File extension character

 extSeparator == '.'
isExtSeparator :: Char -> Bool

Is the character an extension character?

 isExtSeparator a == (a == extSeparator)
Path methods (environment $PATH)
splitFiles :: String -> [FilePath]

RENAMED - splitSearchPath. Take a string, split it on the fileSeparators character.

 Windows: splitFiles "File1;File2;File3" == ["File1","File2","File3"]
 Posix:   splitFiles "File1:File2:File3" == ["File1","File2","File3"]
getSearchPath :: IO [FilePath]
Get a list of filepaths in the $PATH.
Extension methods
splitExtension :: FilePath -> (String, String)

Split on the extension. addExtension 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/","")
takeExtension :: FilePath -> String

Get the extension of a file, returns "" for no extension, .ext otherwise.

 takeExtension x == snd (splitExtension x)
 takeExtension (addExtension x "ext") == ".ext"
 takeExtension (replaceExtension x "ext") == ".ext"
replaceExtension :: FilePath -> String -> FilePath

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"
dropExtension :: FilePath -> FilePath

Remove last extension, and any . following it.

 dropExtension x == fst (splitExtension x)
addExtension :: FilePath -> String -> FilePath

Add an extension, even if there is already one there. E.g. addExtension "foo.txt" "bat" -> "foo.txt.bat".

 addExtension "file.txt" "bib" == "file.txt.bib"
 addExtension "file." ".bib" == "file..bib"
 addExtension "file" ".bib" == "file.bib"
 Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt"
hasExtension :: FilePath -> Bool

Does the given filename have an extension?

 null (takeExtension x) == not (hasExtension x)
(<.>) :: FilePath -> String -> FilePath
Alias to addExtension, for people who like that sort of thing.
splitExtensions :: FilePath -> (FilePath, String)

Split on all extensions

 splitExtensions "file.tar.gz" == ("file",".tar.gz")
dropExtensions :: FilePath -> FilePath

Drop all extensions

 not $ hasExtension (dropExtensions x)
takeExtensions :: FilePath -> String
Get all extensions
Operations on a filepath, as a list of directories
splitFileName :: FilePath -> (String, String)

Split a filename into directory and file. addFileName is the inverse.

 uncurry (++) (splitFileName x) == x
 uncurry addFileName (splitFileName x) == x
 splitFileName "file/bob.txt" == ("file/", "bob.txt")
 splitFileName "file/" == ("file/", "")
 splitFileName "bob" == ("", "bob")
 Posix:   splitFileName "/" == ("/","")
 Windows: splitFileName "c:" == ("c:","")
takeFileName :: FilePath -> FilePath

Get the file name.

 takeFileName "test/" == ""
 takeFileName x == snd (splitFileName x)
 takeFileName (replaceFileName x "fred") == "fred"
 takeFileName (addFileName x "fred") == "fred"
replaceFileName :: FilePath -> String -> FilePath

Set the filename.

 replaceFileName x (takeFileName x) == x
dropFileName :: FilePath -> FilePath

Drop the filename.

 dropFileName x == fst (splitFileName x)
addFileName :: FilePath -> String -> FilePath

GONE - use combine. Add a filename onto the end of a path.

 addFileName (takeDirectory x) (takeFileName x) `equalFilePath` x
takeBaseName :: FilePath -> String

Get the base name, without an extension or path.

 takeBaseName "file/test.txt" == "test"
 takeBaseName "dave.ext" == "dave"
replaceBaseName :: FilePath -> String -> FilePath

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"
 replaceBaseName x (takeBaseName x) == x
takeDirectory :: FilePath -> FilePath

Get the directory name, move up one level.

 Posix:    takeDirectory "/foo/bar/baz" == "/foo/bar"
 Posix:    takeDirectory "/foo/bar/baz/" == "/foo/bar/baz"
replaceDirectory :: FilePath -> String -> FilePath

Set the directory, keeping the filename the same.

 replaceDirectory x (takeDirectory x) `equalFilePath` x
isDirectory :: FilePath -> Bool

Is an item either a directory or the last character a path separator? This does not query the file system.

 isDirectory "test" == False
 isDirectory "test/" == True
isFile :: FilePath -> Bool

Is an item a file, does not query the file system.

 isDirectory x == not (isFile x)
asDirectory :: FilePath -> FilePath

Make something look like a directory

 isDirectory (asDirectory x)
 if isDirectory x then asDirectory x == x else True
 Posix:    asDirectory "test/rest" == "test/rest/"
asFile :: FilePath -> FilePath

Make something look like a file

 asFile "file/test/" == "file/test"
 not (isDirectory (asFile x)) || isDrive x
 Posix:    asFile "/" == "/"
combine :: FilePath -> FilePath -> FilePath

Combine two paths, if the second path isAbsolute, then it returns the second.

 Posix:   combine "/" "test" == "/test"
 Posix:   combine "home" "bob" == "home/bob"
 Windows: combine "home" "bob" == "home\\bob"
(</>) :: FilePath -> FilePath -> FilePath
A nice alias for combine.
splitPath :: FilePath -> [FilePath]

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"]
joinPath :: [FilePath] -> FilePath

Join path elements back together.

 joinPath (splitPath (makeValid x)) == makeValid x
splitDirectories :: FilePath -> [FilePath]

Just as splitPath, but don't add the trailing slashes to each element.

 splitDirectories "test/file" == ["test","file"]
 splitDirectories "/test/file" == ["/","test","file"]
 joinPath (splitDirectories (makeValid x)) `equalFilePath` makeValid x
 splitDirectories "" == []
File name manipulators
normalise :: FilePath -> FilePath

Normalise a file

  • // outside of the drive can be made blank
  • / -> pathSeparator
  • ./ -> ""
 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 "\\\\server\\test" == "\\\\server\\test"
 Windows: normalise "c:/file" == "C:\\file"
equalFilePath :: FilePath -> FilePath -> Bool
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.
shortPath :: FilePath -> IO FilePath
RENAMED - makeRelativeToCurrentDirectory. shortPathWith the current directory.
shortPathWith :: FilePath -> FilePath -> FilePath

RENAMED - makeRelative. Contract a filename, based on a relative path.

 Posix:   shortPathWith "/home/" "/home/bob/foo/bar" == "bob/foo/bar"
 Posix:   shortPathWith "/fred" "bob" == "bob"
 Posix:   shortPathWith "/file/test" "/file/test/fred" == "fred"
 Posix:   shortPathWith "/file/test" "/file/test/fred/" == "fred/"
 Posix:   shortPathWith "/fred/dave" "/fred/bill" == "../bill"
isValid :: FilePath -> Bool

Is a FilePath valid, i.e. could you create a file like it?

 Posix:   isValid "/random_ path:*" == True
 Posix:   isValid x == True
 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
Produced by Haddock version 0.8