[commit: packages/filepath] master: Fix links and add more tests and comments (5fbec04)
git at git.haskell.org
git at git.haskell.org
Thu Mar 19 11:33:49 UTC 2015
Repository : ssh://git@git.haskell.org/filepath
On branch : master
Link : http://git.haskell.org/packages/filepath.git/commitdiff/5fbec041d755403b2fe72c2d4bfd415349443c71
>---------------------------------------------------------------
commit 5fbec041d755403b2fe72c2d4bfd415349443c71
Author: Thomas Miedema <thomasmiedema at gmail.com>
Date: Sun Sep 14 16:52:05 2014 +0200
Fix links and add more tests and comments
No tests are removed, only new ones added.
The semantics of `combine` is questionable. See also #8752. Not changing
anything for the moment.
>---------------------------------------------------------------
5fbec041d755403b2fe72c2d4bfd415349443c71
System/FilePath/Internal.hs | 70 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 62 insertions(+), 8 deletions(-)
diff --git a/System/FilePath/Internal.hs b/System/FilePath/Internal.hs
index 16790fc..36e88e4 100644
--- a/System/FilePath/Internal.hs
+++ b/System/FilePath/Internal.hs
@@ -39,6 +39,10 @@
--
-- The examples in code format descibed by each function are used to generate
-- tests, and should give clear semantics for the functions.
+--
+-- References:
+-- [1] "Naming Files, Paths, and Namespaces"
+-- http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx
-----------------------------------------------------------------------------
module System.FilePath.MODULE_NAME
@@ -337,9 +341,8 @@ addSlash :: FilePath -> FilePath -> (FilePath, FilePath)
addSlash a xs = (a++c,d)
where (c,d) = span isPathSeparator xs
--- http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
+-- See [1].
-- "\\?\D:\<path>" or "\\?\UNC\<server>\<share>"
--- a is "\\?\"
readDriveUNC :: FilePath -> Maybe (FilePath, FilePath)
readDriveUNC (s1:s2:'?':s3:xs) | all isPathSeparator [s1,s2,s3] =
case map toUpper xs of
@@ -347,6 +350,7 @@ readDriveUNC (s1:s2:'?':s3:xs) | all isPathSeparator [s1,s2,s3] =
let (a,b) = readDriveShareName (drop 4 xs)
in Just (s1:s2:'?':s3:take 4 xs ++ a, b)
_ -> case readDriveLetter xs of
+ -- Extended-length path.
Just (a,b) -> Just (s1:s2:'?':s3:a,b)
Nothing -> Nothing
readDriveUNC _ = Nothing
@@ -365,7 +369,7 @@ readDriveShare (s1:s2:xs) | isPathSeparator s1 && isPathSeparator s2 =
readDriveShare _ = Nothing
{- assume you have already seen \\ -}
-{- share\bob -> "share","\","bob" -}
+{- share\bob -> "share\", "bob" -}
readDriveShareName :: String -> (FilePath, FilePath)
readDriveShareName name = addSlash a b
where (a,b) = break isPathSeparator name
@@ -403,11 +407,21 @@ dropDrive = snd . splitDrive
-- | Does 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 "" == False
hasDrive :: FilePath -> Bool
hasDrive = not . null . takeDrive
-- | Is an element a drive
+--
+-- > Posix: isDrive "/" == True
+-- > Posix: isDrive "/foo" == False
+-- > Windows: isDrive "C:\\" == True
+-- > Windows: isDrive "C:\\foo" == False
isDrive :: FilePath -> Bool
isDrive = null . dropDrive
@@ -509,9 +523,9 @@ addTrailingPathSeparator x = if hasTrailingPathSeparator x then x else x ++ [pat
-- | Remove any trailing path separators
--
-- > dropTrailingPathSeparator "file/test/" == "file/test"
--- > Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
-- > Posix: dropTrailingPathSeparator "/" == "/"
-- > Windows: dropTrailingPathSeparator "\\" == "\\"
+-- > Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
dropTrailingPathSeparator :: FilePath -> FilePath
dropTrailingPathSeparator x =
if hasTrailingPathSeparator x && not (isDrive x)
@@ -524,6 +538,8 @@ dropTrailingPathSeparator x =
--
-- > 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"
@@ -547,10 +563,35 @@ replaceDirectory x dir = combineAlways dir (takeFileName x)
-- | Combine two paths, if the second path 'isAbsolute', then it returns the second.
--
-- > Valid x => combine (takeDirectory x) (takeFileName x) `equalFilePath` x
+--
+-- Combined:
-- > Posix: combine "/" "test" == "/test"
-- > Posix: combine "home" "bob" == "home/bob"
+-- > Posix: combine "x:" "foo" == "x:/foo"
+-- > Windows: combine "C:\\foo" "bar" == "C:\\foo\\bar"
-- > Windows: combine "home" "bob" == "home\\bob"
+--
+-- Not combined:
+-- > Posix: combine "home" "/bob" == "/bob"
+-- > Windows: combine "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 @combine@ is to never combine these forms.
+--
-- > Windows: combine "home" "/bob" == "/bob"
+-- > Windows: combine "home" "\\bob" == "\\bob"
+-- > Windows: combine "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 @combine@ is to never combine these forms.
+--
+-- > Windows: combine "D:\\foo" "C:bar" == "C:bar"
+-- > Windows: combine "C:\\foo" "C:bar" == "C:bar"
combine :: FilePath -> FilePath -> FilePath
combine a b | hasDrive b || (not (null b) && isPathSeparator (head b)) = b
| otherwise = combineAlways a b
@@ -590,8 +631,9 @@ splitPath x = [drive | drive /= ""] ++ f path
-- | Just as 'splitPath', but don't add the trailing slashes to each element.
--
--- > splitDirectories "test/file" == ["test","file"]
--- > splitDirectories "/test/file" == ["/","test","file"]
+-- > 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 "" == []
splitDirectories :: FilePath -> [FilePath]
@@ -741,8 +783,7 @@ normaliseDrive drive = if isJust $ readDriveLetter x2
repSlash x = if isPathSeparator x then pathSeparator else x
--- information for validity functions on Windows
--- see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
+-- Information for validity functions on Windows. See [1].
badCharacters :: [Char]
badCharacters = ":*?><|\""
badElements :: [FilePath]
@@ -812,14 +853,27 @@ makeValid path = joinDrive drv $ validElements $ validChars pth
-- > 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 "/" == False
+--
+-- According to [1]:
+--
+-- * "A UNC name of any format [is never relative]."
+--
+-- * "You cannot use the "\\?\" prefix with a relative path."
isRelative :: FilePath -> Bool
isRelative = isRelativeDrive . takeDrive
+{- c:foo -}
+-- 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."
isRelativeDrive :: String -> Bool
isRelativeDrive x = null x ||
maybe False (not . isPathSeparator . last . fst) (readDriveLetter x)
More information about the ghc-commits
mailing list