diff --git a/System/FilePath/Internal.hs b/System/FilePath/Internal.hs index c3dcb863..bce55063 100644 --- a/System/FilePath/Internal.hs +++ b/System/FilePath/Internal.hs @@ -927,6 +927,10 @@ badElements = -- > Windows: isValid "\\\\\\foo" == False -- > Windows: isValid "\\\\?\\D:file" == False -- > Windows: isValid "foo\tbar" == False +-- > Windows: isValid "foo." == False +-- > Windows: isValid "foo " == False +-- > Windows: isValid "foo.\\bar" == False +-- > Windows: isValid "foo \\bar" == False -- > Windows: isValid "nul .txt" == False -- > Windows: isValid " nul.txt" == True isValid :: FilePath -> Bool @@ -936,11 +940,15 @@ isValid _ | isPosix = True isValid path = not (any isBadCharacter x2) && not (any f $ splitDirectories x2) && + not (any g $ splitDirectories x2) && not (isJust (readDriveShare x1) && all isPathSeparator x1) && not (isJust (readDriveUNC x1) && not (hasTrailingPathSeparator x1)) where (x1,x2) = splitDrive path f x = map toUpper (dropWhileEnd (== ' ') $ dropExtensions x) `elem` badElements + g "." = False + g ".." = False + g x = head (reverse $ '@' : x) `elem` ". " -- | Take a FilePath and make it valid; does not change already valid FilePaths. @@ -952,6 +960,8 @@ isValid path = -- > Windows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid" -- > Windows: makeValid "c:\\test:of_test" == "c:\\test_of_test" -- > Windows: makeValid "test*" == "test_" +-- > Windows: makeValid "test." == "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" @@ -974,10 +984,13 @@ makeValid path f x = if isBadCharacter x then '_' else x validElements x = joinPath $ map g $ splitPath x - g x = h a ++ b + g x = i (h a) ++ b where (a,b) = break isPathSeparator x h x = if map toUpper (dropWhileEnd (== ' ') a) `elem` badElements then a ++ "_" <.> b else x where (a,b) = splitExtensions x + i "." = "." + i ".." = ".." + i x = if head (reverse $ '@' : x) `elem` ". " then x ++ "_" else x -- | Is a path relative, or is it fixed to the root? diff --git a/System/FilePath/Posix.hs b/System/FilePath/Posix.hs index 219f7d19..becb0d11 100644 --- a/System/FilePath/Posix.hs +++ b/System/FilePath/Posix.hs @@ -927,6 +927,10 @@ badElements = -- > Windows: isValid "\\\\\\foo" == False -- > Windows: isValid "\\\\?\\D:file" == False -- > Windows: isValid "foo\tbar" == False +-- > Windows: isValid "foo." == False +-- > Windows: isValid "foo " == False +-- > Windows: isValid "foo.\\bar" == False +-- > Windows: isValid "foo \\bar" == False -- > Windows: isValid "nul .txt" == False -- > Windows: isValid " nul.txt" == True isValid :: FilePath -> Bool @@ -936,11 +940,15 @@ isValid _ | isPosix = True isValid path = not (any isBadCharacter x2) && not (any f $ splitDirectories x2) && + not (any g $ splitDirectories x2) && not (isJust (readDriveShare x1) && all isPathSeparator x1) && not (isJust (readDriveUNC x1) && not (hasTrailingPathSeparator x1)) where (x1,x2) = splitDrive path f x = map toUpper (dropWhileEnd (== ' ') $ dropExtensions x) `elem` badElements + g "." = False + g ".." = False + g x = head (reverse $ '@' : x) `elem` ". " -- | Take a FilePath and make it valid; does not change already valid FilePaths. @@ -952,6 +960,8 @@ isValid path = -- > Windows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid" -- > Windows: makeValid "c:\\test:of_test" == "c:\\test_of_test" -- > Windows: makeValid "test*" == "test_" +-- > Windows: makeValid "test." == "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" @@ -974,10 +984,13 @@ makeValid path f x = if isBadCharacter x then '_' else x validElements x = joinPath $ map g $ splitPath x - g x = h a ++ b + g x = i (h a) ++ b where (a,b) = break isPathSeparator x h x = if map toUpper (dropWhileEnd (== ' ') a) `elem` badElements then a ++ "_" <.> b else x where (a,b) = splitExtensions x + i "." = "." + i ".." = ".." + i x = if head (reverse $ '@' : x) `elem` ". " then x ++ "_" else x -- | Is a path relative, or is it fixed to the root? diff --git a/System/FilePath/Windows.hs b/System/FilePath/Windows.hs index 56aa7197..c085c8e5 100644 --- a/System/FilePath/Windows.hs +++ b/System/FilePath/Windows.hs @@ -927,6 +927,10 @@ badElements = -- > Windows: isValid "\\\\\\foo" == False -- > Windows: isValid "\\\\?\\D:file" == False -- > Windows: isValid "foo\tbar" == False +-- > Windows: isValid "foo." == False +-- > Windows: isValid "foo " == False +-- > Windows: isValid "foo.\\bar" == False +-- > Windows: isValid "foo \\bar" == False -- > Windows: isValid "nul .txt" == False -- > Windows: isValid " nul.txt" == True isValid :: FilePath -> Bool @@ -936,11 +940,15 @@ isValid _ | isPosix = True isValid path = not (any isBadCharacter x2) && not (any f $ splitDirectories x2) && + not (any g $ splitDirectories x2) && not (isJust (readDriveShare x1) && all isPathSeparator x1) && not (isJust (readDriveUNC x1) && not (hasTrailingPathSeparator x1)) where (x1,x2) = splitDrive path f x = map toUpper (dropWhileEnd (== ' ') $ dropExtensions x) `elem` badElements + g "." = False + g ".." = False + g x = head (reverse $ '@' : x) `elem` ". " -- | Take a FilePath and make it valid; does not change already valid FilePaths. @@ -952,6 +960,8 @@ isValid path = -- > Windows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid" -- > Windows: makeValid "c:\\test:of_test" == "c:\\test_of_test" -- > Windows: makeValid "test*" == "test_" +-- > Windows: makeValid "test." == "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" @@ -974,10 +984,13 @@ makeValid path f x = if isBadCharacter x then '_' else x validElements x = joinPath $ map g $ splitPath x - g x = h a ++ b + g x = i (h a) ++ b where (a,b) = break isPathSeparator x h x = if map toUpper (dropWhileEnd (== ' ') a) `elem` badElements then a ++ "_" <.> b else x where (a,b) = splitExtensions x + i "." = "." + i ".." = ".." + i x = if head (reverse $ '@' : x) `elem` ". " then x ++ "_" else x -- | Is a path relative, or is it fixed to the root? diff --git a/changelog.md b/changelog.md index 6026f3b1..b89c59d6 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,11 @@ _Note: below all `FilePath` values are unquoted, so `\\` really means two backslashes._ +## 1.5.0.0 *WIP* + +* breaking changes: + - Make `Windows.isValid` reject paths ending in a dot or space wrt [#94](https://github.com/haskell/filepath/pull/94) + ## 1.4.2.2 *WIP* * Document relation between `joinPath` and `()` wrt [#82](https://github.com/haskell/filepath/issues/82), [#82](https://github.com/haskell/filepath/issues/86) diff --git a/filepath.cabal b/filepath.cabal index e75a4cf8..d4e574cf 100644 --- a/filepath.cabal +++ b/filepath.cabal @@ -1,6 +1,6 @@ cabal-version: 1.18 name: filepath -version: 1.4.2.2 +version: 1.5.0.0 -- NOTE: Don't forget to update ./changelog.md license: BSD3 license-file: LICENSE diff --git a/tests/TestGen.hs b/tests/TestGen.hs index 62eb18f4..e00dd71e 100755 --- a/tests/TestGen.hs +++ b/tests/TestGen.hs @@ -427,6 +427,10 @@ tests = ,("W.isValid \"\\\\\\\\\\\\foo\" == False", property $ W.isValid "\\\\\\foo" == False) ,("W.isValid \"\\\\\\\\?\\\\D:file\" == False", property $ W.isValid "\\\\?\\D:file" == False) ,("W.isValid \"foo\\tbar\" == False", property $ W.isValid "foo\tbar" == False) + ,("W.isValid \"foo.\" == False", property $ W.isValid "foo." == False) + ,("W.isValid \"foo \" == False", property $ W.isValid "foo " == False) + ,("W.isValid \"foo.\\\\bar\" == False", property $ W.isValid "foo.\\bar" == False) + ,("W.isValid \"foo \\\\bar\" == False", property $ W.isValid "foo \\bar" == False) ,("W.isValid \"nul .txt\" == False", property $ W.isValid "nul .txt" == False) ,("W.isValid \" nul.txt\" == True", property $ W.isValid " nul.txt" == True) ,("P.isValid (P.makeValid x)", property $ \(QFilePath x) -> P.isValid (P.makeValid x)) @@ -440,6 +444,8 @@ tests = ,("W.makeValid \"c:\\\\already\\\\/valid\" == \"c:\\\\already\\\\/valid\"", property $ W.makeValid "c:\\already\\/valid" == "c:\\already\\/valid") ,("W.makeValid \"c:\\\\test:of_test\" == \"c:\\\\test_of_test\"", property $ W.makeValid "c:\\test:of_test" == "c:\\test_of_test") ,("W.makeValid \"test*\" == \"test_\"", property $ W.makeValid "test*" == "test_") + ,("W.makeValid \"test.\" == \"test._\"", property $ W.makeValid "test." == "test._") + ,("W.makeValid \"test \" == \"test _\"", property $ W.makeValid "test " == "test _") ,("W.makeValid \"c:\\\\test\\\\nul\" == \"c:\\\\test\\\\nul_\"", property $ W.makeValid "c:\\test\\nul" == "c:\\test\\nul_") ,("W.makeValid \"c:\\\\test\\\\prn.txt\" == \"c:\\\\test\\\\prn_.txt\"", property $ W.makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt") ,("W.makeValid \"c:\\\\test/prn.txt\" == \"c:\\\\test/prn_.txt\"", property $ W.makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt")