1 -----------------------------------------------------------------------------
3 -- Module : System.FilePath
4 -- Copyright : (c) The University of Glasgow 2004
5 -- License : BSD-style (see the file libraries/base/LICENSE)
7 -- Maintainer : libraries@haskell.org
9 -- Portability : portable
11 -- System-independent pathname manipulations.
13 -----------------------------------------------------------------------------
15 module System.FilePath
40 -- * Filename extensions
46 import Prelude -- necessary to get dependencies right
48 import Data.List(intersperse)
50 --------------------------------------------------------------
52 --------------------------------------------------------------
54 -- | Split the path into directory and file name
60 -- > splitFileName "/" == ("/", "")
61 -- > splitFileName "/foo/bar.ext" == ("/foo", "bar.ext")
62 -- > splitFileName "bar.ext" == (".", "bar.ext")
63 -- > splitFileName "/foo/." == ("/foo", ".")
64 -- > splitFileName "/foo/.." == ("/foo", "..")
68 -- > splitFileName "\\" == ("\\", "")
69 -- > splitFileName "c:\\foo\\bar.ext" == ("c:\\foo", "bar.ext")
70 -- > splitFileName "bar.ext" == (".", "bar.ext")
71 -- > splitFileName "c:\\foo\\." == ("c:\\foo", ".")
72 -- > splitFileName "c:\\foo\\.." == ("c:\\foo", "..")
74 -- The first case in the above examples returns an empty file name.
75 -- This is a special case because the \"\/\" (\"\\\\\" on Windows)
76 -- path doesn\'t refer to an object (file or directory) which resides
77 -- within a directory.
78 splitFileName :: FilePath -> (String, String)
79 splitFileName p = (reverse (path2++drive), reverse fname)
81 #ifdef mingw32_TARGET_OS
82 (path,drive) = break (== ':') (reverse p)
84 (path,drive) = (reverse p,"")
86 (fname,path1) = break isPathSeparator path
89 [_] -> path1 -- don't remove the trailing slash if
90 -- there is only one character
91 (c:path) | isPathSeparator c -> path
94 -- | Split the path into file name and extension. If the file doesn\'t have extension,
95 -- the function will return empty string. The extension doesn\'t include a leading period.
99 -- > splitFileExt "foo.ext" == ("foo", "ext")
100 -- > splitFileExt "foo" == ("foo", "")
101 -- > splitFileExt "." == (".", "")
102 -- > splitFileExt ".." == ("..", "")
103 splitFileExt :: FilePath -> (String, String)
107 (_:pre) -> (reverse (pre++path), reverse suf)
109 (fname,path) = break isPathSeparator (reverse p)
110 (suf,pre) | fname == "." || fname == ".." = (fname,"")
111 | otherwise = break (== '.') fname
113 -- | Split the path into directory, file name and extension.
114 -- The function is an optimized version of the following equation:
116 -- > splitFilePath path = (dir,name,ext)
118 -- > (dir,basename) = splitFileName path
119 -- > (name,ext) = splitFileExt basename
120 splitFilePath :: FilePath -> (String, String, String)
123 [] -> (reverse real_dir, reverse suf, [])
124 (_:pre) -> (reverse real_dir, reverse pre, reverse suf)
126 #ifdef mingw32_TARGET_OS
127 (path,drive) = break (== ':') (reverse p)
129 (path,drive) = (reverse p,"")
131 (file,dir) = break isPathSeparator path
132 (suf,pre) = case file of
134 _ -> break (== '.') file
136 real_dir = case dir of
138 [_] -> pathSeparator:drive
139 (_:dir) -> dir++drive
141 -- | The 'joinFileName' function is the opposite of 'splitFileName'.
142 -- It joins directory and file names to form complete file path.
144 -- The general rule is:
146 -- > dir `joinFileName` basename == path
148 -- > (dir,basename) = splitFileName path
150 -- There might be an exeptions to the rule but in any case the
151 -- reconstructed path will refer to the same object (file or directory).
152 -- An example exception is that on Windows some slashes might be converted
154 joinFileName :: String -> String -> FilePath
155 joinFileName "" fname = fname
156 joinFileName "." fname = fname
157 joinFileName dir "" = dir
158 joinFileName dir fname
159 | isPathSeparator (last dir) = dir++fname
160 | otherwise = dir++pathSeparator:fname
162 -- | The 'joinFileExt' function is the opposite of 'splitFileExt'.
163 -- It joins file name and extension to form complete file path.
165 -- The general rule is:
167 -- > filename `joinFileExt` ext == path
169 -- > (filename,ext) = splitFileExt path
170 joinFileExt :: String -> String -> FilePath
171 joinFileExt path "" = path
172 joinFileExt path ext = path ++ '.':ext
174 -- | Given a directory path \"dir\" and a file\/directory path \"rel\",
175 -- returns a merged path \"full\" with the property that
176 -- (cd dir; do_something_with rel) is equivalent to
177 -- (do_something_with full). If the \"rel\" path is an absolute path
178 -- then the returned path is equal to \"rel\"
179 joinPaths :: FilePath -> FilePath -> FilePath
180 joinPaths path1 path2
181 | isRootedPath path2 = path2
183 #ifdef mingw32_TARGET_OS
185 d:':':path2' | take 2 path1 == [d,':'] -> path1 `joinFileName` path2'
187 _ -> path1 `joinFileName` path2
189 path1 `joinFileName` path2
192 -- | Changes the extension of a file path.
193 changeFileExt :: FilePath -- ^ The path information to modify.
194 -> String -- ^ The new extension (without a leading period).
195 -- Specify an empty string to remove an existing
196 -- extension from path.
197 -> FilePath -- ^ A string containing the modified path information.
198 changeFileExt path ext = joinFileExt name ext
200 (name,_) = splitFileExt path
202 -- | On Unix and Macintosh the 'isRootedPath' function is a synonym to 'isAbsolutePath'.
203 -- The difference is important only on Windows. The rooted path must start from the root
204 -- directory but may not include the drive letter while the absolute path always includes
205 -- the drive letter and the full file path.
206 isRootedPath :: FilePath -> Bool
207 isRootedPath (c:_) | isPathSeparator c = True
208 #ifdef mingw32_TARGET_OS
209 isRootedPath (_:':':c:_) | isPathSeparator c = True -- path with drive letter
211 isRootedPath _ = False
213 -- | Returns True if this path\'s meaning is independent of any OS
214 -- "working directory", False if it isn\'t.
215 isAbsolutePath :: FilePath -> Bool
216 #ifdef mingw32_TARGET_OS
217 isAbsolutePath (_:':':c:_) | isPathSeparator c = True
219 isAbsolutePath (c:_) | isPathSeparator c = True
221 isAbsolutePath _ = False
223 -- | Gets this path and all its parents.
224 -- The function is useful in case if you want to create
225 -- some file but you aren\'t sure whether all directories
226 -- in the path exists or if you want to search upward for some file.
232 -- > pathParents "/" == ["/"]
233 -- > pathParents "/dir1" == ["/", "/dir1"]
234 -- > pathParents "/dir1/dir2" == ["/", "/dir1", "/dir1/dir2"]
235 -- > pathParents "dir1" == [".", "dir1"]
236 -- > pathParents "dir1/dir2" == [".", "dir1", "dir1/dir2"]
238 -- In the above examples \"\/\" isn\'t included in the list
239 -- because you can\'t create root directory.
243 -- > pathParents "c:" == ["c:."]
244 -- > pathParents "c:\\" == ["c:\\"]
245 -- > pathParents "c:\\dir1" == ["c:\\", "c:\\dir1"]
246 -- > pathParents "c:\\dir1\\dir2" == ["c:\\", "c:\\dir1", "c:\\dir1\\dir2"]
247 -- > pathParents "c:dir1" == ["c:.","c:dir1"]
248 -- > pathParents "dir1\\dir2" == [".", "dir1", "dir1\\dir2"]
250 -- Note that if the file is relative then the the current directory (\".\")
251 -- will be explicitly listed.
252 pathParents :: FilePath -> [FilePath]
254 root'' : map ((++) root') (dropEmptyPath $ inits path')
256 #ifdef mingw32_TARGET_OS
257 (root,path) = case break (== ':') p of
258 (path, "") -> ("",path)
259 (root,_:path) -> (root++":",path)
263 (root',root'',path') = case path of
264 (c:path) | isPathSeparator c -> (root++[pathSeparator],root++[pathSeparator],path)
265 _ -> (root ,root++"." ,path)
267 dropEmptyPath ("":paths) = paths
268 dropEmptyPath paths = paths
270 inits :: String -> [String]
275 ".." -> map (joinFileName pre) (dropEmptyPath $ inits suf)
276 _ -> "" : map (joinFileName pre) (inits suf)
278 (pre,suf) = case break isPathSeparator cs of
279 (pre,"") -> (pre, "")
280 (pre,_:suf) -> (pre,suf)
282 -- | Given a list of file paths, returns the longest common parent.
283 commonParent :: [FilePath] -> Maybe FilePath
284 commonParent [] = Nothing
285 commonParent paths@(p:ps) =
286 case common Nothing "" p ps of
287 #ifdef mingw32_TARGET_OS
288 Nothing | all (not . isAbsolutePath) paths ->
289 case foldr getDrive [] paths of
294 Nothing | all (not . isAbsolutePath) paths -> Just "."
298 #ifdef mingw32_TARGET_OS
299 getDrive (d:':':_) ds
300 | not (d `elem` ds) = d:ds
304 common i acc [] ps = checkSep i acc ps
305 common i acc (c:cs) ps
306 | isPathSeparator c = removeSep i acc cs [] ps
307 | otherwise = removeChar i acc c cs [] ps
309 checkSep i acc [] = Just (reverse acc)
310 checkSep i acc ([]:ps) = Just (reverse acc)
311 checkSep i acc ((c1:p):ps)
312 | isPathSeparator c1 = checkSep i acc ps
313 checkSep i acc ps = i
315 removeSep i acc cs pacc [] =
316 common (Just (reverse (pathSeparator:acc))) (pathSeparator:acc) cs pacc
317 removeSep i acc cs pacc ([] :ps) = Just (reverse acc)
318 removeSep i acc cs pacc ((c1:p):ps)
319 | isPathSeparator c1 = removeSep i acc cs (p:pacc) ps
320 removeSep i acc cs pacc ps = i
322 removeChar i acc c cs pacc [] = common i (c:acc) cs pacc
323 removeChar i acc c cs pacc ([] :ps) = i
324 removeChar i acc c cs pacc ((c1:p):ps)
325 | c == c1 = removeChar i acc c cs (p:pacc) ps
326 removeChar i acc c cs pacc ps = i
328 --------------------------------------------------------------
330 --------------------------------------------------------------
332 -- | The function splits the given string to substrings
333 -- using the 'searchPathSeparator'.
334 parseSearchPath :: String -> [FilePath]
335 parseSearchPath path = split searchPathSeparator path
337 split :: Char -> String -> [String]
341 _:rest' -> chunk : split c rest'
343 (chunk, rest) = break (==c) s
345 -- | The function concatenates the given paths to form a
346 -- single string where the paths are separated with 'searchPathSeparator'.
347 mkSearchPath :: [FilePath] -> String
348 mkSearchPath paths = concat (intersperse [searchPathSeparator] paths)
351 --------------------------------------------------------------
353 --------------------------------------------------------------
355 -- | Checks whether the character is a valid path separator for the host
356 -- platform. The valid character is a 'pathSeparator' but since the Windows
357 -- operating system also accepts a slash (\"\/\") since DOS 2, the function
358 -- checks for it on this platform, too.
359 isPathSeparator :: Char -> Bool
360 #ifdef mingw32_TARGET_OS
361 isPathSeparator ch = ch == '/' || ch == '\\'
363 isPathSeparator ch = ch == '/'
366 -- | Provides a platform-specific character used to separate directory levels in
367 -- a path string that reflects a hierarchical file system organization. The
368 -- separator is a slash (@\"\/\"@) on Unix and Macintosh, and a backslash
369 -- (@\"\\\"@) on the Windows operating system.
370 pathSeparator :: Char
371 #ifdef mingw32_TARGET_OS
377 -- | A platform-specific character used to separate search path strings in
378 -- environment variables. The separator is a colon (@\":\"@) on Unix and
379 -- Macintosh, and a semicolon (@\";\"@) on the Windows operating system.
380 searchPathSeparator :: Char
381 #ifdef mingw32_TARGET_OS
382 searchPathSeparator = ';'
384 searchPathSeparator = ':'
387 -- ToDo: This should be determined via autoconf (AC_EXEEXT)
388 -- | Extension for executable files
389 -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2)
390 exeExtension :: String
391 #ifdef mingw32_TARGET_OS
397 -- ToDo: This should be determined via autoconf (AC_OBJEXT)
398 -- | Extension for object files
399 -- (typically @\"o\"@ on Unix and @\"obj\"@ on Windows)
400 objExtension :: String
401 #ifdef mingw32_TARGET_OS
407 -- | Extension for dynamically linked (or shared) libraries
408 -- (typically @\"so\"@ on Unix and @\"dll\"@ on Windows)
409 dllExtension :: String
410 #ifdef mingw32_TARGET_OS