packages feed

filepath-bytestring 1.4.2.1.0 → 1.4.2.1.1

raw patch · 8 files changed

+173/−84 lines, 8 filesdep +utf8-stringPVP: minor bump suggested

API additions: PVP suggests at least a minor version bump

Dependencies added: utf8-string

API changes (from Hackage documentation)

+ System.FilePath.Posix.ByteString: decodeFilePath :: RawFilePath -> FilePath
+ System.FilePath.Posix.ByteString: encodeFilePath :: FilePath -> RawFilePath
+ System.FilePath.Posix.ByteString: normalise :: RawFilePath -> RawFilePath
+ System.FilePath.Windows.ByteString: decodeFilePath :: RawFilePath -> FilePath
+ System.FilePath.Windows.ByteString: encodeFilePath :: FilePath -> RawFilePath
+ System.FilePath.Windows.ByteString: normalise :: RawFilePath -> RawFilePath

Files

CHANGELOG view
@@ -1,3 +1,16 @@+filepath-bytestring (1.4.2.1.1) unstable; urgency=medium++  * When running on Windows, RawFilePath is assumed to be encoded with+    UTF-8, rather than the windows default of UTF-16. This lets the user+    use OverloadedStrings for RawFilePaths embedded in their code.+  * Added two conversion functions, encodeFilePath and decodeFilePath.+  * Added normalise.+  * Optimise with -O2, a benchmark shows that improves the speed+    of </> by around 7%.+  * Inline </>, which speeds it up by around 3%.++ -- Joey Hess <id@joeyh.name>  Wed, 18 Dec 2019 13:42:16 -0400+ filepath-bytestring (1.4.2.1.0) unstable; urgency=medium    * Initial release, based on filepath 1.4.2.1.
System/FilePath/ByteString.hs view
@@ -1,7 +1,4 @@ {-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 704-{-# LANGUAGE Safe #-}-#endif {- | Module      :  System.FilePath.ByteString Copyright   :  (c) Neil Mitchell 2005-2014, (c) Joey Hess 2019
System/FilePath/Internal.hs view
@@ -1,6 +1,3 @@-#if __GLASGOW_HASKELL__ >= 704-{-# LANGUAGE Safe #-}-#endif {-# LANGUAGE OverloadedStrings #-}  -- This template expects CPP definitions for:@@ -63,6 +60,40 @@     (     -- * Types     RawFilePath,+    -- * Filename encoding+    -- +    -- | When using `FilePath`, you do not usually need to care about how+    -- it is encoded, because it is a @[Char]@ and encoding and decoding is+    -- handled by IO actions as needed. Unfortunately the situation is more+    -- complicated when using `RawFilePath`.+    --+    -- It's natural to enable `OverloadedStrings` and use it to construct+    -- a `RawFilePath`, eg @"foo" '</>' "bar"@. A gotcha though is that+    -- any non-ascii characters will be truncated to 8 bits. That is not a+    -- limitation of this library, but of the `IsString` implementation +    -- of `ByteString`.+    --+    -- Posix filenames do not have any defined encoding. This library+    -- assumes that whatever encoding may be used for a `RawFilePath`,+    -- it is compatable with ASCII. In particular, 0x2F (/) is always+    -- a path separator, and 0x2E (.) is assumed to be an extension+    -- separator. All encodings in common use are compatible with ASCII,+    -- and unix tools have always made similar assumptions,+    -- so this is unlikely to be a problem, unless you are dealing with+    -- EBCDIC or similar historical oddities.+    --+    -- Windows's API expects filenames to be encoded with UTF-16.+    -- This is especially problimatic when using OverloadedStrings+    -- since a ByteString "bar" is not a valid encoding for a+    -- Windows filename (but "b\\0a\\0r\\0" is). To avoid this problem,+    -- and to simplify the implementation,+    -- `RawFilePath` is assumed to be encoded with UTF-8 (not UTF-16)+    -- when this library is used on Windows.+    -- There are not currently any libraries for Windows that use+    -- `RawFilePath`, so you will probably need to convert them back to +    -- `FilePath` in order to do IO in any case.+    encodeFilePath,+    decodeFilePath,     -- * Separator predicates     pathSeparator, pathSeparators, isPathSeparator,     searchPathSeparator, isSearchPathSeparator,@@ -95,10 +126,10 @@     dropTrailingPathSeparator,      -- * File name manipulations-    --normalise, equalFilePath,+    normalise, --equalFilePath,     --makeRelative,     isRelative, isAbsolute,-    isValid, -- makeValid+    isValid, --makeValid     )     where @@ -108,6 +139,13 @@ import Data.Char(ord, chr, toUpper, isAsciiLower, isAsciiUpper) import Data.Maybe(isJust) import Data.Word(Word8)+#ifdef mingw32_HOST_OS+import qualified Data.ByteString.UTF8 as UTF8+#else+import qualified GHC.Foreign as GHC+import qualified GHC.IO.Encoding as Encoding+import System.IO.Unsafe (unsafePerformIO)+#endif  #ifndef mingw32_HOST_OS -- Import from unix, rather than redefining, so users who import both@@ -123,6 +161,45 @@ infixr 5  </>  +-- | Convert from FilePath to RawFilePath.+--+-- When run on Unix, this applies the filesystem encoding+-- (see `Encoding.getFileSystemEncoding`).+--+-- When run on Windows, this encodes as UTF-8.+encodeFilePath :: FilePath -> RawFilePath+#ifdef mingw32_HOST_OS+encodeFilePath = UTF8.fromString+#else+encodeFilePath = B.pack . map (fromIntegral . fromEnum) . encodeFilePath'++{-# NOINLINE encodeFilePath' #-}+encodeFilePath' :: FilePath -> String+encodeFilePath' f = unsafePerformIO $ do+        enc <- Encoding.getFileSystemEncoding+        GHC.withCString enc f (GHC.peekCString Encoding.char8)+#endif++-- | Convert from RawFilePath to FilePath+--+-- When run on Unix, this applies the filesystem encoding+-- (see `Encoding.getFileSystemEncoding`).+--+-- When run on Windows, this decodes UTF-8.+decodeFilePath :: RawFilePath -> FilePath+#ifdef mingw32_HOST_OS+decodeFilePath = UTF8.toString+#else+decodeFilePath = decodeFilePath' . map (toEnum . fromIntegral) . B.unpack++{-# NOINLINE decodeFilePath' #-}+decodeFilePath' :: String -> FilePath+decodeFilePath' s = unsafePerformIO $ do+        enc <- Encoding.getFileSystemEncoding+        GHC.withCString Encoding.char8 s (GHC.peekCString enc)+#endif++ --------------------------------------------------------------------- -- Platform Abstraction Methods (private) @@ -688,9 +765,10 @@ replaceDirectory :: RawFilePath -> ByteString -> RawFilePath replaceDirectory x dir = combineAlways dir (takeFileName x) +{-# INLINE combine #-} -- | An alias for '</>'. combine :: RawFilePath -> RawFilePath -> RawFilePath-combine a b | hasLeadingPathSeparator b || hasDrive b = b+combine a b | hasLeadingPathSeparator b || (not isPosix && hasDrive b) = b             | otherwise = combineAlways a b  -- | Combine two paths, assuming rhs is NOT absolute.@@ -794,7 +872,6 @@  {- - --------------------------------------------------------------------- -- File name manipulators @@ -810,13 +887,17 @@ -- > untested Posix:   not (equalFilePath "foo" "FOO") -- > untested Windows: equalFilePath "foo" "FOO" -- > untested Windows: not (equalFilePath "C:" "C:/")-equalFilePath :: FilePath -> FilePath -> Bool+equalFilePath :: RawFilePath -> RawFilePath -> Bool equalFilePath a b = f a == f b     where-        f x | isWindows = dropTrailingPathSeparator $ map toLower $ normalise x+-- FIXME: B8.map will not lower-case non-ascii characters.+        f x | isWindows = dropTrailingPathSeparator $ B8.map toLower $ normalise x             | otherwise = dropTrailingPathSeparator $ normalise x +-} +{-+ -- | Contract a filename, based on a relative path. Note that the resulting path --   will never introduce @..@ paths, as the presence of symlinks means @..\/b@ --   may not reach @a\/b@ if it starts from @a\/c@. For a worked example see@@ -862,6 +943,8 @@         takeAbs x | hasLeadingPathSeparator x && not (hasDrive x) = [pathSeparator]         takeAbs x = map (\y -> if isPathSeparator y then pathSeparator else toLower y) $ takeDrive x +-}+ -- | Normalise a file -- -- * \/\/ outside of the drive can be made blank@@ -870,29 +953,31 @@ -- -- * .\/ -> \"\" ----- > untested Posix:   normalise "/file/\\test////" == "/file/\\test/"--- > untested Posix:   normalise "/file/./test" == "/file/test"--- > untested Posix:   normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/"--- > untested Posix:   normalise "../bob/fred/" == "../bob/fred/"--- > untested Posix:   normalise "./bob/fred/" == "bob/fred/"--- > untested Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"--- > untested Windows: normalise "c:\\" == "C:\\"--- > untested Windows: normalise "C:.\\" == "C:"--- > untested Windows: normalise "\\\\server\\test" == "\\\\server\\test"--- > untested Windows: normalise "//server/test" == "\\\\server\\test"--- > untested Windows: normalise "c:/file" == "C:\\file"--- > untested Windows: normalise "/file" == "\\file"--- > untested Windows: normalise "\\" == "\\"--- > untested Windows: normalise "/./" == "\\"--- > untested          normalise "." == "."--- > untested Posix:   normalise "./" == "./"--- > untested Posix:   normalise "./." == "./"--- > untested Posix:   normalise "/./" == "/"--- > untested Posix:   normalise "/" == "/"--- > untested Posix:   normalise "bob/fred/." == "bob/fred/"--- > untested Posix:   normalise "//home" == "/home"-normalise :: FilePath -> FilePath-normalise path = result ++ [pathSeparator | addPathSeparator]+-- > 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 "C:.\\" == "C:"+-- > Windows: normalise "\\\\server\\test" == "\\\\server\\test"+-- > Windows: normalise "//server/test" == "\\\\server\\test"+-- > Windows: normalise "c:/file" == "C:\\file"+-- > Windows: normalise "/file" == "\\file"+-- > Windows: normalise "\\" == "\\"+-- > Windows: normalise "/./" == "\\"+-- >          normalise "." == "."+-- > Posix:   normalise "./" == "./"+-- > Posix:   normalise "./." == "./"+-- > Posix:   normalise "/./" == "/"+-- > Posix:   normalise "/" == "/"+-- > Posix:   normalise "bob/fred/." == "bob/fred/"+-- > Posix:   normalise "//home" == "/home"+normalise :: RawFilePath -> RawFilePath+normalise path+        | addPathSeparator = result <> B.singleton pathSeparator+        | otherwise = result     where         (drv,pth) = splitDrive path         result = joinDrive' (normaliseDrive drv) (f pth)@@ -905,28 +990,33 @@             && not (isRelativeDrive drv)          isDirPath xs = hasTrailingPathSeparator xs-            || not (null xs) && last xs == '.' && hasTrailingPathSeparator (init xs)+            || not (B.null xs) && B.last xs == extSeparator && hasTrailingPathSeparator (B.init xs) +        f :: RawFilePath -> RawFilePath         f = joinPath . dropDots . propSep . splitDirectories -        propSep (x:xs) | all isPathSeparator x = [pathSeparator] : xs+        propSep :: [RawFilePath] -> [RawFilePath]+        propSep (x:xs) | B.all isPathSeparator x = B.singleton pathSeparator : xs                        | otherwise = x : xs         propSep [] = [] +        dropDots :: [RawFilePath] -> [RawFilePath]         dropDots = filter ("." /=) -normaliseDrive :: FilePath -> FilePath+normaliseDrive :: RawFilePath -> RawFilePath normaliseDrive "" = ""-normaliseDrive _ | isPosix = [pathSeparator]+normaliseDrive _ | isPosix = B.singleton pathSeparator normaliseDrive drive = if isJust $ readDriveLetter x2-                       then map toUpper x2+                       then upcasedriveletter x2                        else x2     where-        x2 = map repSlash drive+        x2 = B.map repSlash drive          repSlash x = if isPathSeparator x then pathSeparator else x --}+        -- A Windows drive letter is an ascii character, so it's safe to+        -- operate on the ByteString containing it using B8.+        upcasedriveletter = B8.map toUpper  -- Information for validity functions on Windows. See [1]. isBadCharacter :: Word8 -> Bool
TODO view
@@ -1,11 +1,3 @@ * Some functions have not been ported from FilePath and are commented out:   splitSearchPath, getSearchPath, normalise, equalFilePath, makeRelative,   makeValid. Implementations would be accepted if you need any of these.-* combine checks hasDrive on posix, this is unnecessary-  since hasLeadingPathSeparator checks the same thing-  there. isWindows && hasDrive would cover windows while-  avoiding that overhead. Benchmark to see if it improves.-* Test suite may not build on Windows due to its use of-  getFileSystemEncoding.-* How filename encoding even works in this ByteString library should perhaps be-  documented.
filepath-bytestring.cabal view
@@ -1,6 +1,6 @@ cabal-version:  >= 1.18 name:           filepath-bytestring-version:        1.4.2.1.0+version:        1.4.2.1.1 -- NOTE: Don't forget to update CHANGELOG and the filepath version below license:        BSD3 license-file:   LICENSE@@ -51,10 +51,12 @@     build-depends:         base >= 4 && < 4.15,         bytestring-    if !os(Windows)+    if os(Windows)+        build-depends: utf8-string+    else         build-depends: unix -    ghc-options: -Wall+    ghc-options: -O2 -Wall  test-suite filepath-tests     type: exitcode-stdio-1.0
tests/TestEquiv.hs view
@@ -44,6 +44,7 @@     ,("equiv Posix.hasTrailingPathSeparator", equiv_1 OurPosix.hasTrailingPathSeparator TheirPosix.hasTrailingPathSeparator)     ,("equiv Posix.addTrailingPathSeparator", equiv_1 OurPosix.addTrailingPathSeparator TheirPosix.addTrailingPathSeparator)     ,("equiv Posix.dropTrailingPathSeparator", equiv_1 OurPosix.dropTrailingPathSeparator TheirPosix.dropTrailingPathSeparator)+    ,("equiv Posix.normalise", equiv_1 OurPosix.normalise TheirPosix.normalise)     ,("equiv Windows.isPathSeparator", equiv_0 OurWindows.isPathSeparator TheirWindows.isPathSeparator)     ,("equiv Windows.isSearchPathSeparator", equiv_0 OurWindows.isSearchPathSeparator TheirWindows.isSearchPathSeparator)     ,("equiv Windows.isExtSeparator", equiv_0 OurWindows.isExtSeparator TheirWindows.isExtSeparator)@@ -80,4 +81,5 @@     ,("equiv Windows.hasTrailingPathSeparator", equiv_1 OurWindows.hasTrailingPathSeparator TheirWindows.hasTrailingPathSeparator)     ,("equiv Windows.addTrailingPathSeparator", equiv_1 OurWindows.addTrailingPathSeparator TheirWindows.addTrailingPathSeparator)     ,("equiv Windows.dropTrailingPathSeparator", equiv_1 OurWindows.dropTrailingPathSeparator TheirWindows.dropTrailingPathSeparator)+    ,("equiv Windows.normalise", equiv_1 OurWindows.normalise TheirWindows.normalise)     ]
tests/TestGen.hs view
@@ -345,6 +345,28 @@     ,("P.joinPath [] == \"\"", property $ P.joinPath [] == "")     ,("W.joinPath [] == \"\"", property $ W.joinPath [] == "")     ,("P.joinPath [\"test\", \"file\", \"path\"] == \"test/file/path\"", property $ P.joinPath ["test", "file", "path"] == "test/file/path")+    ,("P.normalise \"/file/\\\\test////\" == \"/file/\\\\test/\"", property $ P.normalise "/file/\\test////" == "/file/\\test/")+    ,("P.normalise \"/file/./test\" == \"/file/test\"", property $ P.normalise "/file/./test" == "/file/test")+    ,("P.normalise \"/test/file/../bob/fred/\" == \"/test/file/../bob/fred/\"", property $ P.normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/")+    ,("P.normalise \"../bob/fred/\" == \"../bob/fred/\"", property $ P.normalise "../bob/fred/" == "../bob/fred/")+    ,("P.normalise \"./bob/fred/\" == \"bob/fred/\"", property $ P.normalise "./bob/fred/" == "bob/fred/")+    ,("W.normalise \"c:\\\\file/bob\\\\\" == \"C:\\\\file\\\\bob\\\\\"", property $ W.normalise "c:\\file/bob\\" == "C:\\file\\bob\\")+    ,("W.normalise \"c:\\\\\" == \"C:\\\\\"", property $ W.normalise "c:\\" == "C:\\")+    ,("W.normalise \"C:.\\\\\" == \"C:\"", property $ W.normalise "C:.\\" == "C:")+    ,("W.normalise \"\\\\\\\\server\\\\test\" == \"\\\\\\\\server\\\\test\"", property $ W.normalise "\\\\server\\test" == "\\\\server\\test")+    ,("W.normalise \"//server/test\" == \"\\\\\\\\server\\\\test\"", property $ W.normalise "//server/test" == "\\\\server\\test")+    ,("W.normalise \"c:/file\" == \"C:\\\\file\"", property $ W.normalise "c:/file" == "C:\\file")+    ,("W.normalise \"/file\" == \"\\\\file\"", property $ W.normalise "/file" == "\\file")+    ,("W.normalise \"\\\\\" == \"\\\\\"", property $ W.normalise "\\" == "\\")+    ,("W.normalise \"/./\" == \"\\\\\"", property $ W.normalise "/./" == "\\")+    ,("P.normalise \".\" == \".\"", property $ P.normalise "." == ".")+    ,("W.normalise \".\" == \".\"", property $ W.normalise "." == ".")+    ,("P.normalise \"./\" == \"./\"", property $ P.normalise "./" == "./")+    ,("P.normalise \"./.\" == \"./\"", property $ P.normalise "./." == "./")+    ,("P.normalise \"/./\" == \"/\"", property $ P.normalise "/./" == "/")+    ,("P.normalise \"/\" == \"/\"", property $ P.normalise "/" == "/")+    ,("P.normalise \"bob/fred/.\" == \"bob/fred/\"", property $ P.normalise "bob/fred/." == "bob/fred/")+    ,("P.normalise \"//home\" == \"/home\"", property $ P.normalise "//home" == "/home")     ,("P.isValid \"\" == False", property $ P.isValid "" == False)     ,("W.isValid \"\" == False", property $ W.isValid "" == False)     ,("P.isValid \"\\0\" == False", property $ P.isValid "\0" == False)
tests/TestUtil.hs view
@@ -16,11 +16,7 @@ import Control.Monad import qualified System.FilePath.Windows as W import qualified System.FilePath.Posix as P-import System.FilePath.ByteString (RawFilePath)-import qualified GHC.Foreign as GHC-import qualified GHC.IO.Encoding as Encoding-import System.IO.Unsafe-import qualified Data.ByteString as B+import System.FilePath.ByteString (RawFilePath, encodeFilePath)  infixr 0 ==> a ==> b = not a || b@@ -29,32 +25,7 @@         toRawFilePath :: t -> RawFilePath  instance ToRawFilePath [Char] where-        toRawFilePath = B.pack . decodeW8NUL--decodeW8NUL :: String -> [Word8]-decodeW8NUL = intercalate [c2w8 nul] . map decodeW8 . splitc nul-  where-        nul = '\NUL'--c2w8 :: Char -> Word8-c2w8 = fromIntegral . fromEnum--s2w8 :: String -> [Word8]-s2w8 = map c2w8--splitc :: Eq c => c -> [c] -> [[c]]-splitc c s = case break (== c) s of-        (i, _c:rest) -> i : splitc c rest-        (i, []) -> i : []--decodeW8 :: FilePath -> [Word8]-decodeW8 = s2w8 . encodeFilePath--{-# NOINLINE encodeFilePath #-}-encodeFilePath :: String -> String-encodeFilePath fp = unsafePerformIO $ do-        enc <- Encoding.getFileSystemEncoding-        GHC.withCString enc fp (GHC.peekCString Encoding.char8)+        toRawFilePath = encodeFilePath  newtype QFilePathValidW = QFilePathValidW FilePath deriving Show