filepath-bytestring-1.4.100.3.2/0000755000000000000000000000000007346545000014333 5ustar0000000000000000filepath-bytestring-1.4.100.3.2/CHANGELOG0000644000000000000000000000771107346545000015553 0ustar0000000000000000filepath-bytestring (1.4.100.3.2) unstable; urgency=medium * Relax bounds to allow base-4.19. -- Joey Hess Mon, 29 Jul 2024 19:40:51 -0400 filepath-bytestring (1.4.100.3.1) unstable; urgency=medium * Updated to filepath-1.4.100.3, which fixes a regression. * Allow up to filepath-1.4.200.1. -- Joey Hess Mon, 29 Jul 2024 19:27:57 -0400 filepath-bytestring (1.4.2.1.13) unstable; urgency=medium * Relax bounds to allow base-4.18 and filepath-1.4.100.3 (ghc 9.6). -- Joey Hess Tue, 20 Jun 2023 12:22:31 -0400 filepath-bytestring (1.4.2.1.12) unstable; urgency=medium * Relax bounds to allow base 4.17 (ghc 9.4). -- Joey Hess Thu, 20 Oct 2022 13:25:40 -0400 filepath-bytestring (1.4.2.1.11) unstable; urgency=medium * Deprecated this library, since filepath has added OsPath that is based on a bytestring. -- Joey Hess Sat, 23 Jul 2022 12:41:53 -0400 filepath-bytestring (1.4.2.1.10) unstable; urgency=medium * Depend on bytestring-0.11.2.0, and use it to implement encodeFilePath and decodeFilePath, whose code was previously copied from it. -- Joey Hess Wed, 08 Dec 2021 18:55:24 -0400 filepath-bytestring (1.4.2.1.9) unstable; urgency=medium * Relax bounds to allow base 4.16 (ghc 9.2). -- Joey Hess Fri, 03 Dec 2021 11:49:58 -0400 filepath-bytestring (1.4.2.1.8) unstable; urgency=medium * Faster implementations of encodeFilePath and decodeFilePath. They are approximately 2x and 3x as fast, respectively. * encodeFilePath and decodeFilePath used to truncate at the first NUL. The new implementations do not do this. Since unix filepaths cannot contain NUL, this behavior change is can't cause any problems, unless the functions are used for values that are not actually valid filepaths. * Support cabal bench to benchmark the library. -- Joey Hess Wed, 11 Aug 2021 12:17:15 -0400 filepath-bytestring (1.4.2.1.7) unstable; urgency=medium * Relax QuickCheck bounds to allow 2.14. * Relax base bounds so it will build with ghc 9.0.1. -- Joey Hess Wed, 17 Mar 2021 08:11:27 -0400 filepath-bytestring (1.4.2.1.6) unstable; urgency=medium * Added makeValid. All functions from filepath are now implemented. -- Joey Hess Thu, 02 Jan 2020 16:05:49 -0400 filepath-bytestring (1.4.2.1.5) unstable; urgency=medium * Allow building with filepath-1.4.2 as well as 1.4.2.1; there are no behavior or API differences between the two versions. -- Joey Hess Wed, 01 Jan 2020 14:24:18 -0400 filepath-bytestring (1.4.2.1.4) unstable; urgency=medium * Added splitSearchPath and getSearchPath. * Fix bug in makeRelative, caught by test suite. * Added quickcheck tests for equalFilePath and makeRelative. -- Joey Hess Wed, 01 Jan 2020 11:58:31 -0400 filepath-bytestring (1.4.2.1.3) unstable; urgency=medium * Added equalFilePath. * Added makeRelative. -- Joey Hess Mon, 30 Dec 2019 13:07:31 -0400 filepath-bytestring (1.4.2.1.2) unstable; urgency=medium * Fix build with ghc 8.0 (pre-Semigroup Monoid transition) -- Joey Hess Mon, 30 Dec 2019 12:18:15 -0400 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 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. -- Joey Hess Tue, 10 Dec 2019 15:21:14 -0400 filepath-bytestring-1.4.100.3.2/LICENSE0000644000000000000000000000276407346545000015351 0ustar0000000000000000Copyright Neil Mitchell 2005-2019. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Neil Mitchell nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. filepath-bytestring-1.4.100.3.2/README.md0000644000000000000000000000573007346545000015617 0ustar0000000000000000# FilePath [![Hackage version](https://img.shields.io/hackage/v/filepath.svg?label=Hackage)](https://hackage.haskell.org/package/filepath) [![Linux build status](https://img.shields.io/travis/haskell/filepath/master.svg?label=Linux%20build)](https://travis-ci.org/haskell/filepath) [![Windows build status](https://img.shields.io/appveyor/ci/ndmitchell/filepath/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/ndmitchell/filepath) The `filepath-bytestring` package provides functionality for manipulating `RawFilePath` values (`ByteString`s). Its interface is equivilant to the `filepath` package. It provides three modules: * [`System.FilePath.Posix.ByteString`](http://hackage.haskell.org/package/filepath-bytestring/docs/System-FilePath-Posix-ByteString.html) manipulates POSIX/Linux style `RawFilePath` values (with `/` as the path separator). * [`System.FilePath.Windows.ByteString`](http://hackage.haskell.org/package/filepath-bytestring/docs/System-FilePath-Windows-ByteString.html) manipulates Windows style `RawFilePath` values (with either `\` or `/` as the path separator, and deals with drives). * [`System.FilePath.ByteString`](http://hackage.haskell.org/package/filepath-bytestring/docs/System-FilePath-ByteString.html) is an alias for the module appropriate to your platform. All three modules provide the same API, and the same documentation (calling out differences in the different variants). ### Developer notes This package's version should be the same as the `filepath` it's derived from, with an added revision number. Most of the code is in `System/FilePath/Internal.hs` which is `#include`'d into both `System/FilePath/Posix.hs` and `System/FilePath/Windows.hs` with the `IS_WINDOWS` CPP define set to either `True` or `False`. This Internal module is a bit weird in that it isn't really a Haskell module, but is more an include file. The library has extensive doc tests. Anything starting with `-- >` is transformed into a doc test as a predicate that must evaluate to `True`. These tests follow a few rules: * Tests prefixed with `Windows:` or `Posix:` are only tested against that specific implementation - otherwise tests are run against both implementations. * Any single letter variable, e.g. `x`, is considered universal quantification, and is checked with `QuickCheck`. * If `Valid x =>` appears at the start of a doc test, that means the property will only be tested with `x` passing the `isValid` predicate. Also, all exported functions are quickchecked against the ones from `filepath` to make sure thay generate equivilant results. The tests can be generated by `Generate.hs` in the root of the repo, and will be placed in `tests/TestGen.hs`. The `TestGen.hs` file is checked into the repo, and the CI scripts check that `TestGen.hs` is in sync with what would be generated a fresh - if you don't regenerate `TestGen.hs` the CI will fail. The `.ghci` file is set up to allow you to type `ghci` to open the library, then `:go` will regenerate the tests and run them. filepath-bytestring-1.4.100.3.2/Setup.hs0000644000000000000000000000005607346545000015770 0ustar0000000000000000import Distribution.Simple main = defaultMain filepath-bytestring-1.4.100.3.2/System/FilePath/0000755000000000000000000000000007346545000017313 5ustar0000000000000000filepath-bytestring-1.4.100.3.2/System/FilePath/ByteString.hs0000644000000000000000000000146507346545000021747 0ustar0000000000000000{-# LANGUAGE CPP #-} {- | Module : System.FilePath.ByteString Copyright : (c) Neil Mitchell 2005-2014, (c) Joey Hess 2019 License : BSD3 Maintainer : id@joeyh.name Stability : stable Portability : portable A library for 'RawFilePath' manipulations, using Posix or Windows filepaths depending on the platform. Both "System.FilePath.Posix.ByteString" and "System.FilePath.Windows.ByteString" provide the same interface. See either for examples and a list of the available functions. -} #if defined(mingw32_HOST_OS) || defined(__MINGW32__) module System.FilePath.ByteString(module System.FilePath.Windows.ByteString) where import System.FilePath.Windows.ByteString #else module System.FilePath.ByteString(module System.FilePath.Posix.ByteString) where import System.FilePath.Posix.ByteString #endif filepath-bytestring-1.4.100.3.2/System/FilePath/Internal.hs0000644000000000000000000012642307346545000021433 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} -- This template expects CPP definitions for: -- MODULE_NAME = Posix | Windows -- IS_WINDOWS = False | True -- | -- Module : System.FilePath.MODULE_NAME.ByteString -- Copyright : (c) Neil Mitchell 2005-2014, (c) Joey Hess 2019 -- License : BSD3 -- -- Maintainer : id@joeyh.name -- Stability : stable -- Portability : portable -- -- A library for 'RawFilePath' manipulations, using MODULE_NAME style paths on -- all platforms. Importing "System.FilePath.ByteString" is usually better. -- -- This module is the same as System.FilePath.MODULE_NAME from the filepath -- library, except it uses 'RawFilePath'. -- -- Given the example 'RawFilePath': @\/directory\/file.ext@ -- -- We can use the following functions to extract pieces. -- -- * 'takeFileName' gives @\"file.ext\"@ -- -- * 'takeDirectory' gives @\"\/directory\"@ -- -- * 'takeExtension' gives @\".ext\"@ -- -- * 'dropExtension' gives @\"\/directory\/file\"@ -- -- * 'takeBaseName' gives @\"file\"@ -- -- And we could have built an equivalent path with the following expressions: -- -- * @\"\/directory\" '' \"file.ext\"@. -- -- * @\"\/directory\/file" '<.>' \"ext\"@. -- -- * @\"\/directory\/file.txt" '-<.>' \"ext\"@. -- -- Each function in this module is documented with several examples, -- which are also used as tests. -- -- Here are a few examples of using the @filepath@ functions together: -- -- /Example 1:/ Find the possible locations of a Haskell module @Test@ imported from module @Main@: -- -- @['replaceFileName' path_to_main \"Test\" '<.>' ext | ext <- [\"hs\",\"lhs\"] ]@ -- -- /Example 2:/ Compile a Haskell file, putting the @.hi@ file under @interface@: -- -- @'takeDirectory' file '' \"interface\" '' ('takeFileName' file '-<.>' \"hi\")@ -- -- References: -- [1] (Microsoft MSDN) module System.FilePath.MODULE_NAME.ByteString ( -- * 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, extSeparator, isExtSeparator, -- * @$PATH@ methods splitSearchPath, getSearchPath, -- * Extension functions splitExtension, takeExtension, replaceExtension, (-<.>), dropExtension, addExtension, hasExtension, (<.>), splitExtensions, dropExtensions, takeExtensions, replaceExtensions, isExtensionOf, stripExtension, -- * Filename\/directory functions splitFileName, takeFileName, replaceFileName, dropFileName, takeBaseName, replaceBaseName, takeDirectory, replaceDirectory, combine, (), splitPath, joinPath, splitDirectories, -- * Drive functions splitDrive, joinDrive, takeDrive, hasDrive, dropDrive, isDrive, -- * Trailing slash functions hasTrailingPathSeparator, addTrailingPathSeparator, dropTrailingPathSeparator, -- * File name manipulations normalise, equalFilePath, makeRelative, isRelative, isAbsolute, isValid, makeValid ) where import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as B8 import Data.Char(ord, chr, toUpper, toLower, isAsciiLower, isAsciiUpper) import Data.Maybe(isJust) import Data.Word(Word8) #ifdef mingw32_HOST_OS import qualified Data.ByteString.UTF8 as UTF8 #else import System.IO.Unsafe (unsafePerformIO) #endif import System.Environment (getEnv) import Data.Semigroup import Prelude #ifndef mingw32_HOST_OS -- Import from unix, rather than redefining, so users who import both -- will not get an ambiguous occurance of RawFilePath. import System.Posix.ByteString(RawFilePath) #else -- | A literal file path type RawFilePath = ByteString #endif infixr 7 <.>, -<.> 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. -- -- the implementation of this function assumes that the -- filesystem encoding will not be changed while the program is running. encodeFilePath :: FilePath -> RawFilePath #ifdef mingw32_HOST_OS encodeFilePath = UTF8.fromString #else encodeFilePath = unsafePerformIO . B.fromFilePath #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. -- The implementation of this function assumes that the -- filesystem encoding will not be changed while the program is running. decodeFilePath :: RawFilePath -> FilePath #ifdef mingw32_HOST_OS decodeFilePath = UTF8.toString #else {-# NOINLINE decodeFilePath #-} decodeFilePath = unsafePerformIO . B.toFilePath #endif --------------------------------------------------------------------- -- Platform Abstraction Methods (private) -- | Is the operating system Unix or Linux like isPosix :: Bool isPosix = not isWindows -- | Is the operating system Windows like isWindows :: Bool isWindows = IS_WINDOWS --------------------------------------------------------------------- -- The basic functions -- | The character that separates directories. In the case where more than -- one character is possible, 'pathSeparator' is the \'ideal\' one. -- -- > Windows: pathSeparator == fromIntegral (ord '\\') -- > Posix: pathSeparator == fromIntegral (ord '/') -- > isPathSeparator pathSeparator pathSeparator :: Word8 pathSeparator = if isWindows then fromIntegral (ord '\\') else fromIntegral (ord '/') -- | The list of all possible separators. -- -- > Windows: pathSeparators == [fromIntegral (ord '\\'), fromIntegral (ord '/')] -- > Posix: pathSeparators == [fromIntegral (ord '/')] -- > pathSeparator `elem` pathSeparators pathSeparators :: [Word8] pathSeparators = if isWindows then [fromIntegral (ord '\\'), fromIntegral (ord '/')] else [fromIntegral (ord '/')] -- | Rather than using @(== 'pathSeparator')@, use this. Test if something -- is a path separator. -- -- > isPathSeparator a == (a `elem` pathSeparators) isPathSeparator :: Word8 -> Bool isPathSeparator 47 = True isPathSeparator 92 = isWindows isPathSeparator _ = False -- | The character that is used to separate the entries in the $PATH environment variable. -- -- > Windows: searchPathSeparator == fromIntegral (ord ';') -- > Posix: searchPathSeparator == fromIntegral (ord ':') searchPathSeparator :: Word8 searchPathSeparator = if isWindows then fromIntegral (ord ';') else fromIntegral (ord ':') -- | Is the character a file separator? -- -- > isSearchPathSeparator a == (a == searchPathSeparator) isSearchPathSeparator :: Word8 -> Bool isSearchPathSeparator = (== searchPathSeparator) -- | File extension character -- -- > extSeparator == fromIntegral (ord '.') extSeparator :: Word8 extSeparator = fromIntegral (ord '.') -- | Is the character an extension character? -- -- > isExtSeparator a == (a == extSeparator) isExtSeparator :: Word8 -> Bool isExtSeparator = (== extSeparator) --------------------------------------------------------------------- -- Path methods (environment $PATH) -- | Take a string, split it on the 'searchPathSeparator' character. -- Blank items are ignored on Windows, and converted to @.@ on Posix. -- On Windows path elements are stripped of quotes. -- -- Follows the recommendations in -- -- -- > Posix: splitSearchPath "File1:File2:File3" == ["File1","File2","File3"] -- > Posix: splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"] -- > Windows: splitSearchPath "File1;File2;File3" == ["File1","File2","File3"] -- > Windows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"] -- > Windows: splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"] splitSearchPath :: ByteString -> [RawFilePath] splitSearchPath = f where f x = case B.break isSearchPathSeparator x of (pre, post) | B.null post -> g pre | otherwise -> g pre ++ f (B.drop 1 post) g "" = ["." | isPosix] g x | isWindows = case B.uncons x of Just (q, x') | q == quote -> case B.unsnoc x' of Just (x'', q') | q' == quote -> [x''] _ -> [x] _ -> [x] | otherwise = [x] quote = fromIntegral (ord '"') -- | Get a list of 'RawFilePath's in the $PATH variable. getSearchPath :: IO [RawFilePath] getSearchPath = fmap (splitSearchPath . encodeFilePath) (getEnv "PATH") --------------------------------------------------------------------- -- Extension methods -- | Split on the extension. 'addExtension' is the inverse. -- -- > splitExtension "/directory/path.ext" == ("/directory/path",".ext") -- > uncurry (<>) (splitExtension x) == x -- > Valid 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/","") splitExtension :: RawFilePath -> (ByteString, ByteString) splitExtension x = if B.null nameDot then (x,mempty) else (dir <> B.init nameDot, extSeparator `B.cons` ext) where (dir,file) = splitFileName_ x (nameDot,ext) = B.breakEnd isExtSeparator file -- | Get the extension of a file, returns @\"\"@ for no extension, @.ext@ otherwise. -- -- > takeExtension "/directory/path.ext" == ".ext" -- > takeExtension x == snd (splitExtension x) -- > Valid x => takeExtension (addExtension x "ext") == ".ext" -- > Valid x => takeExtension (replaceExtension x "ext") == ".ext" takeExtension :: RawFilePath -> ByteString takeExtension = snd . splitExtension -- | Remove the current extension and add another, equivalent to 'replaceExtension'. -- -- > "/directory/path.txt" -<.> "ext" == "/directory/path.ext" -- > "/directory/path.txt" -<.> ".ext" == "/directory/path.ext" -- > "foo.o" -<.> "c" == "foo.c" (-<.>) :: RawFilePath -> ByteString -> RawFilePath (-<.>) = replaceExtension -- | Set the extension of a file, overwriting one if already present, equivalent to '-<.>'. -- -- > replaceExtension "/directory/path.txt" "ext" == "/directory/path.ext" -- > replaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext" -- > 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" -- > replaceExtension x y == addExtension (dropExtension x) y replaceExtension :: RawFilePath -> ByteString -> RawFilePath replaceExtension x y = dropExtension x <.> y -- | Add an extension, even if there is already one there, equivalent to 'addExtension'. -- -- > "/directory/path" <.> "ext" == "/directory/path.ext" -- > "/directory/path" <.> ".ext" == "/directory/path.ext" (<.>) :: RawFilePath -> ByteString -> RawFilePath (<.>) = addExtension -- | Remove last extension, and the \".\" preceding it. -- -- > dropExtension "/directory/path.ext" == "/directory/path" -- > dropExtension x == fst (splitExtension x) dropExtension :: RawFilePath -> RawFilePath dropExtension = fst . splitExtension -- | Add an extension, even if there is already one there, equivalent to '<.>'. -- -- > addExtension "/directory/path" "ext" == "/directory/path.ext" -- > addExtension "file.txt" "bib" == "file.txt.bib" -- > addExtension "file." ".bib" == "file..bib" -- > addExtension "file" ".bib" == "file.bib" -- > addExtension "/" "x" == "/.x" -- > addExtension x "" == x -- > Valid x => takeFileName (addExtension (addTrailingPathSeparator x) "ext") == ".ext" -- > Windows: addExtension "\\\\share" ".txt" == "\\\\share\\.txt" addExtension :: RawFilePath -> ByteString -> RawFilePath addExtension file ext = case B.uncons ext of Nothing -> file Just (x,_xs) -> joinDrive a $ if isExtSeparator x then b <> ext else b <> (extSeparator `B.cons` ext) where (a,b) = splitDrive file -- | Does the given filename have an extension? -- -- > hasExtension "/directory/path.ext" == True -- > hasExtension "/directory/path" == False -- > null (takeExtension x) == not (hasExtension x) hasExtension :: RawFilePath -> Bool hasExtension = B.any isExtSeparator . takeFileName -- | Does the given filename have the specified extension? -- -- > "png" `isExtensionOf` "/directory/file.png" == True -- > ".png" `isExtensionOf` "/directory/file.png" == True -- > ".tar.gz" `isExtensionOf` "bar/foo.tar.gz" == True -- > "ar.gz" `isExtensionOf` "bar/foo.tar.gz" == False -- > "png" `isExtensionOf` "/directory/file.png.jpg" == False -- > "csv/table.csv" `isExtensionOf` "/data/csv/table.csv" == False isExtensionOf :: ByteString -> RawFilePath -> Bool isExtensionOf ext = B.isSuffixOf ext' . takeExtensions where ext' = case B.uncons ext of Just (h, _) | isExtSeparator h -> ext _ -> extSeparator `B.cons` ext -- | Drop the given extension from a FilePath, and the @\".\"@ preceding it. -- Returns 'Nothing' if the FilePath does not have the given extension, or -- 'Just' and the part before the extension if it does. -- -- This function can be more predictable than 'dropExtensions', especially if the filename -- might itself contain @.@ characters. -- -- > stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x" -- > stripExtension "hi.o" "foo.x.hs.o" == Nothing -- > dropExtension x == fromJust (stripExtension (takeExtension x) x) -- > dropExtensions x == fromJust (stripExtension (takeExtensions x) x) -- > stripExtension ".c.d" "a.b.c.d" == Just "a.b" -- > stripExtension ".c.d" "a.b..c.d" == Just "a.b." -- > stripExtension "baz" "foo.bar" == Nothing -- > stripExtension "bar" "foobar" == Nothing -- > stripExtension "" x == Just x stripExtension :: ByteString -> RawFilePath -> Maybe RawFilePath stripExtension ext path = case B.uncons ext of Nothing -> Just path Just (x, _) -> let dotExt = if isExtSeparator x then ext else extSeparator `B.cons` ext in B.stripSuffix dotExt path -- | Split on all extensions. -- -- > splitExtensions "/directory/path.ext" == ("/directory/path",".ext") -- > splitExtensions "file.tar.gz" == ("file",".tar.gz") -- > uncurry (<>) (splitExtensions x) == x -- > Valid x => uncurry addExtension (splitExtensions x) == x -- > splitExtensions "file.tar.gz" == ("file",".tar.gz") splitExtensions :: RawFilePath -> (RawFilePath, ByteString) splitExtensions x = (a <> c, d) where (a,b) = splitFileName_ x (c,d) = B.break isExtSeparator b -- | Drop all extensions. -- -- > dropExtensions "/directory/path.ext" == "/directory/path" -- > dropExtensions "file.tar.gz" == "file" -- > not $ hasExtension $ dropExtensions x -- > not $ any isExtSeparator $ takeFileName $ dropExtensions x dropExtensions :: RawFilePath -> RawFilePath dropExtensions = fst . splitExtensions -- | Get all extensions. -- -- > takeExtensions "/directory/path.ext" == ".ext" -- > takeExtensions "file.tar.gz" == ".tar.gz" takeExtensions :: RawFilePath -> ByteString takeExtensions = snd . splitExtensions -- | Replace all extensions of a file with a new extension. Note -- that 'replaceExtension' and 'addExtension' both work for adding -- multiple extensions, so only required when you need to drop -- all extensions first. -- -- > replaceExtensions "file.fred.bob" "txt" == "file.txt" -- > replaceExtensions "file.fred.bob" "tar.gz" == "file.tar.gz" replaceExtensions :: RawFilePath -> ByteString -> RawFilePath replaceExtensions x y = dropExtensions x <.> y --------------------------------------------------------------------- -- Drive methods -- | Is the given character a valid drive letter? -- only a-z and A-Z are letters, not isAlpha which is more unicodey isLetter :: Word8 -> Bool isLetter x = isAsciiLower c || isAsciiUpper c where c = chr (fromIntegral x) -- | Split a path into a drive and a path. -- On Posix, \/ is a Drive. -- -- > uncurry (<>) (splitDrive x) == x -- > Windows: splitDrive "file" == ("","file") -- > Windows: splitDrive "c:/file" == ("c:/","file") -- > Windows: splitDrive "c:\\file" == ("c:\\","file") -- > Windows: splitDrive "\\\\shared\\test" == ("\\\\shared\\","test") -- > Windows: splitDrive "\\\\shared" == ("\\\\shared","") -- > Windows: splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\","file") -- > Windows: splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\","UNCshared\\file") -- > Windows: splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\","file") -- > Windows: splitDrive "/d" == ("","/d") -- > Posix: splitDrive "/test" == ("/","test") -- > Posix: splitDrive "//test" == ("//","test") -- > Posix: splitDrive "test/file" == ("","test/file") -- > Posix: splitDrive "file" == ("","file") splitDrive :: RawFilePath -> (RawFilePath, RawFilePath) splitDrive x | isPosix = B.span (== fromIntegral (ord '/')) x splitDrive x | Just y <- readDriveLetter x = y splitDrive x | Just y <- readDriveUNC x = y splitDrive x | Just y <- readDriveShare x = y splitDrive x = (B.empty,x) addSlash :: RawFilePath -> RawFilePath -> (RawFilePath, RawFilePath) addSlash a xs = (a <> c,d) where (c,d) = B.span isPathSeparator xs -- See [1]. -- "\\?\D:\" or "\\?\UNC\\" readDriveUNC :: RawFilePath -> Maybe (RawFilePath, RawFilePath) readDriveUNC p = do (s1, p') <- B.uncons p (s2, p'') <- B.uncons p' (q, p''') <- B.uncons p'' (s3, rest) <- B.uncons p''' if q == fromIntegral (ord '?') && all isPathSeparator [s1,s2,s3] then case breakunc rest of Just (unc,r) -> let (a,b) = readDriveShareName r in Just (B.pack [s1,s2,q,s3] <> unc <> a, b) Nothing -> case readDriveLetter rest of -- Extended-length path. Just (a,b) -> Just (B.pack [s1,s2,q,s3] <> a, b) Nothing -> Nothing else Nothing where breakunc b = do (u, b') <- B.uncons b (n, b'') <- B.uncons b' (c, b''') <- B.uncons b'' (s, rest) <- B.uncons b''' let isup v ch = toUpper (chr (fromIntegral v)) == ch if isPathSeparator s && isup u 'U' && isup n 'N' && isup c 'C' then Just (B.take 4 b, rest) else Nothing {- c:\ -} readDriveLetter :: RawFilePath -> Maybe (RawFilePath, RawFilePath) readDriveLetter p = case B.uncons p of Just (x, t) | isLetter x -> case B.uncons t of Just (c, t') | c == colon -> case B.uncons t' of Just (y, _) | isPathSeparator y -> Just $ addSlash (B.pack [x, colon]) t' _ -> Just (B.pack [x, colon], t') _ -> Nothing _ -> Nothing where colon = fromIntegral (ord ':') {- \\sharename\ -} readDriveShare :: ByteString -> Maybe (RawFilePath, RawFilePath) readDriveShare p = do (s1, p') <- B.uncons p (s2, p'') <- B.uncons p' let (a,b) = readDriveShareName p'' if isPathSeparator s1 && isPathSeparator s2 then Just (s1 `B.cons` s2 `B.cons` a,b) else Nothing {- assume you have already seen \\ -} {- share\bob -> "share\", "bob" -} readDriveShareName :: ByteString -> (RawFilePath, RawFilePath) readDriveShareName name = addSlash a b where (a,b) = B.break isPathSeparator name -- | Join a drive and the rest of the path. -- -- > Valid x => uncurry joinDrive (splitDrive x) == x -- > Windows: joinDrive "C:" "foo" == "C:foo" -- > Windows: joinDrive "C:\\" "bar" == "C:\\bar" -- > Windows: joinDrive "\\\\share" "foo" == "\\\\share\\foo" -- > Windows: joinDrive "/:" "foo" == "/:\\foo" joinDrive :: RawFilePath -> RawFilePath -> RawFilePath joinDrive = combineAlways -- | Get the drive from a filepath. -- -- > takeDrive x == fst (splitDrive x) takeDrive :: RawFilePath -> RawFilePath takeDrive = fst . splitDrive -- | Delete the drive, if it exists. -- -- > dropDrive x == snd (splitDrive x) dropDrive :: RawFilePath -> RawFilePath 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 :: RawFilePath -> Bool hasDrive = not . B.null . takeDrive -- | Is an element a drive -- -- > Posix: isDrive "/" == True -- > Posix: isDrive "/foo" == False -- > Windows: isDrive "C:\\" == True -- > Windows: isDrive "C:\\foo" == False -- > isDrive "" == False isDrive :: RawFilePath -> Bool isDrive x = not (B.null x) && B.null (dropDrive x) --------------------------------------------------------------------- -- Operations on a filepath, as a list of directories -- | Split a filename into directory and file. '' is the inverse. -- The first component will often end with a trailing slash. -- -- > splitFileName "/directory/file.ext" == ("/directory/","file.ext") -- > Valid x => uncurry () (splitFileName x) == x || fst (splitFileName x) == "./" -- > Valid x => isValid (fst (splitFileName x)) -- > splitFileName "file/bob.txt" == ("file/", "bob.txt") -- > splitFileName "file/" == ("file/", "") -- > splitFileName "bob" == ("./", "bob") -- > Posix: splitFileName "/" == ("/","") -- > Windows: splitFileName "c:" == ("c:","") splitFileName :: RawFilePath -> (ByteString, ByteString) splitFileName x = (if B.null dir then "./" else dir, name) where (dir, name) = splitFileName_ x -- version of splitFileName where, if the FilePath has no directory -- component, the returned directory is "" rather than "./". This -- is used in cases where we are going to combine the returned -- directory to make a valid FilePath, and having a "./" appear would -- look strange and upset simple equality properties. See -- e.g. replaceFileName. splitFileName_ :: RawFilePath -> (ByteString, ByteString) splitFileName_ x = (drv <> dir, file) where (drv,pth) = splitDrive x (dir,file) = B.breakEnd isPathSeparator pth -- | Set the filename. -- -- > replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext" -- > Valid x => replaceFileName x (takeFileName x) == x replaceFileName :: RawFilePath -> ByteString -> RawFilePath replaceFileName x y = a y where (a,_) = splitFileName_ x -- | Drop the filename. Unlike 'takeDirectory', this function will leave -- a trailing path separator on the directory. -- -- > dropFileName "/directory/file.ext" == "/directory/" -- > dropFileName x == fst (splitFileName x) dropFileName :: RawFilePath -> RawFilePath dropFileName = fst . splitFileName -- | Get the file name. -- -- > takeFileName "/directory/file.ext" == "file.ext" -- > takeFileName "test/" == "" -- > takeFileName x `isSuffixOf` x -- > takeFileName x == snd (splitFileName x) -- > Valid x => takeFileName (replaceFileName x "fred") == "fred" -- > Valid x => takeFileName (x "fred") == "fred" -- > Valid x => isRelative (takeFileName x) takeFileName :: RawFilePath -> RawFilePath takeFileName = snd . splitFileName -- | Get the base name, without an extension or path. -- -- > takeBaseName "/directory/file.ext" == "file" -- > takeBaseName "file/test.txt" == "test" -- > takeBaseName "dave.ext" == "dave" -- > takeBaseName "" == "" -- > takeBaseName "test" == "test" -- > takeBaseName (addTrailingPathSeparator x) == "" -- > takeBaseName "file/file.tar.gz" == "file.tar" takeBaseName :: RawFilePath -> ByteString takeBaseName = dropExtension . takeFileName -- | Set the base name. -- -- > replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext" -- > replaceBaseName "file/test.txt" "bob" == "file/bob.txt" -- > replaceBaseName "fred" "bill" == "bill" -- > replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar" -- > Valid x => replaceBaseName x (takeBaseName x) == x replaceBaseName :: RawFilePath -> ByteString -> RawFilePath replaceBaseName pth nam = combineAlways a (nam <.> ext) where (a,b) = splitFileName_ pth ext = takeExtension b -- | Is an item either a directory or the last character a path separator? -- -- > hasTrailingPathSeparator "test" == False -- > hasTrailingPathSeparator "test/" == True hasTrailingPathSeparator :: RawFilePath -> Bool hasTrailingPathSeparator x = case B.unsnoc x of Nothing -> False Just (_i, l) -> isPathSeparator l hasLeadingPathSeparator :: RawFilePath -> Bool hasLeadingPathSeparator x = case B.uncons x of Nothing -> False Just (h,_t) -> isPathSeparator h -- | Add a trailing file path separator if one is not already present. -- -- > hasTrailingPathSeparator (addTrailingPathSeparator x) -- > hasTrailingPathSeparator x ==> addTrailingPathSeparator x == x -- > Posix: addTrailingPathSeparator "test/rest" == "test/rest/" addTrailingPathSeparator :: RawFilePath -> RawFilePath addTrailingPathSeparator x | hasTrailingPathSeparator x = x | otherwise = B.snoc x pathSeparator -- | Remove any trailing path separators -- -- > dropTrailingPathSeparator "file/test/" == "file/test" -- > dropTrailingPathSeparator "/" == "/" -- > Windows: dropTrailingPathSeparator "\\" == "\\" -- > Posix: not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x dropTrailingPathSeparator :: RawFilePath -> RawFilePath dropTrailingPathSeparator x | hasTrailingPathSeparator x && not (isDrive x) = let x' = dropWhileEnd isPathSeparator x in if B.null x' then B.singleton (B.last x) else x' | otherwise = x -- | Get the directory name, move up one level. -- -- > takeDirectory "/directory/other.ext" == "/directory" -- > 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" -- > Windows: takeDirectory "foo\\bar" == "foo" -- > Windows: takeDirectory "foo\\bar\\\\" == "foo\\bar" -- > Windows: takeDirectory "C:\\" == "C:\\" takeDirectory :: RawFilePath -> RawFilePath takeDirectory = dropTrailingPathSeparator . dropFileName -- | Set the directory, keeping the filename the same. -- -- > replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext" -- > Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x 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 || (not isPosix && hasDrive b) = b | otherwise = combineAlways a b -- | Combine two paths, assuming rhs is NOT absolute. combineAlways :: RawFilePath -> RawFilePath -> RawFilePath combineAlways a b | B.null a = b | B.null b = a | hasTrailingPathSeparator a = a <> b | isWindows && B.length a == 2 && B.last a == fromIntegral (ord ':') && isLetter (B.head a) = a <> b | otherwise = a <> B.singleton pathSeparator <> b -- | Combine two paths with a path separator. -- If the second path starts with a path separator or a drive letter, then it returns the second. -- The intention is that @readFile (dir '' file)@ will access the same file as -- @setCurrentDirectory dir; readFile file@. -- -- > Posix: "/directory" "file.ext" == "/directory/file.ext" -- > Windows: "/directory" "file.ext" == "/directory\\file.ext" -- > "directory" "/file.ext" == "/file.ext" -- > Valid x => (takeDirectory x takeFileName x) `equalFilePath` x -- -- Combined: -- -- > Posix: "/" "test" == "/test" -- > Posix: "home" "bob" == "home/bob" -- > Posix: "x:" "foo" == "x:/foo" -- > Windows: "C:\\foo" "bar" == "C:\\foo\\bar" -- > Windows: "home" "bob" == "home\\bob" -- -- Not combined: -- -- > Posix: "home" "/bob" == "/bob" -- > Windows: "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 '' is to never combine these forms. -- -- > Windows: "home" "/bob" == "/bob" -- > Windows: "home" "\\bob" == "\\bob" -- > Windows: "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 '' is to never combine these forms. -- -- > Windows: "D:\\foo" "C:bar" == "C:bar" -- > Windows: "C:\\foo" "C:bar" == "C:bar" () :: RawFilePath -> RawFilePath -> RawFilePath () = combine -- | Split a path by the directory separator. -- -- > splitPath "/directory/file.ext" == ["/","directory/","file.ext"] -- > mconcat (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"] splitPath :: RawFilePath -> [RawFilePath] splitPath x = [drive | not (B.null drive)] ++ f path where (drive,path) = splitDrive x f y | B.null y = [] | otherwise = (a<>c) : f d where (a,b) = B.break isPathSeparator y (c,d) = B.span isPathSeparator b -- | Just as 'splitPath', but don't add the trailing slashes to each element. -- -- > splitDirectories "/directory/file.ext" == ["/","directory","file.ext"] -- > 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 "" == [] -- > Windows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"] -- > splitDirectories "/test///file" == ["/","test","file"] splitDirectories :: RawFilePath -> [RawFilePath] splitDirectories = map dropTrailingPathSeparator . splitPath -- | Join path elements back together. -- -- > joinPath ["/","directory/","file.ext"] == "/directory/file.ext" -- > Valid x => joinPath (splitPath x) == x -- > joinPath [] == "" -- > Posix: joinPath ["test","file","path"] == "test/file/path" joinPath :: [RawFilePath] -> RawFilePath -- Note that this definition on c:\\c:\\, join then split will give c:\\. joinPath = foldr combine mempty --------------------------------------------------------------------- -- File name manipulators -- | Equality of two 'FilePath's. -- 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. -- -- > x == y ==> equalFilePath x y -- > normalise x == normalise y ==> equalFilePath x y -- > equalFilePath "foo" "foo/" -- > not (equalFilePath "foo" "/foo") -- > Posix: not (equalFilePath "foo" "FOO") -- > Windows: equalFilePath "foo" "FOO" -- > Windows: not (equalFilePath "C:" "C:/") equalFilePath :: RawFilePath -> RawFilePath -> Bool equalFilePath a b = f a == f b where f x | isWindows = dropTrailingPathSeparator $ encodeFilePath $ map toLower $ decodeFilePath $ 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 -- . -- -- The corresponding @makeAbsolute@ function can be found in -- @System.Directory@. -- -- > makeRelative "/directory" "/directory/file.ext" == "file.ext" -- > Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x -- > makeRelative x x == "." -- > Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y makeRelative y x) x -- > Windows: makeRelative "C:\\Home" "c:\\home\\bob" == "bob" -- > Windows: makeRelative "C:\\Home" "c:/home/bob" == "bob" -- > Windows: makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob" -- > Windows: makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob" -- > Windows: makeRelative "/Home" "/home/bob" == "bob" -- > Windows: makeRelative "/" "//" == "//" -- > Posix: makeRelative "/Home" "/home/bob" == "/home/bob" -- > Posix: makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar" -- > Posix: makeRelative "/fred" "bob" == "bob" -- > Posix: makeRelative "/file/test" "/file/test/fred" == "fred" -- > Posix: makeRelative "/file/test" "/file/test/fred/" == "fred/" -- > Posix: makeRelative "some/path" "some/path/a/b/c" == "a/b/c" makeRelative :: RawFilePath -> RawFilePath -> RawFilePath makeRelative root path | equalFilePath root path = "." | takeAbs root /= takeAbs path = path | otherwise = f (dropAbs root) (dropAbs path) where f "" y = B.dropWhile isPathSeparator y f x y = let (x1,x2) = g x (y1,y2) = g y in if equalFilePath x1 y1 then f x2 y2 else path g x = (B.dropWhile isPathSeparator a, B.dropWhile isPathSeparator b) where (a,b) = B.break isPathSeparator $ B.dropWhile isPathSeparator x -- on windows, need to drop '/' which is kind of absolute, but not a drive dropAbs x | hasLeadingPathSeparator x && not (hasDrive x) = B.tail x dropAbs x = dropDrive x takeAbs x | hasLeadingPathSeparator x && not (hasDrive x) = B.singleton pathSeparator -- A Windows drive letter is an ascii character, so it's safe to -- operate on the ByteString containing it using B8. takeAbs x = B8.map (\y -> if isPathSeparator (fromIntegral (ord y)) then chr (fromIntegral pathSeparator) else toLower y) $ takeDrive x -- | 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 "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) joinDrive' "" "" = "." joinDrive' d p = joinDrive d p addPathSeparator = isDirPath pth && not (hasTrailingPathSeparator result) && not (isRelativeDrive drv) isDirPath xs = hasTrailingPathSeparator xs || not (B.null xs) && B.last xs == extSeparator && hasTrailingPathSeparator (B.init xs) f :: RawFilePath -> RawFilePath f = joinPath . dropDots . propSep . splitDirectories propSep :: [RawFilePath] -> [RawFilePath] propSep (x:xs) | B.all isPathSeparator x = B.singleton pathSeparator : xs | otherwise = x : xs propSep [] = [] dropDots :: [RawFilePath] -> [RawFilePath] dropDots = filter ("." /=) normaliseDrive :: RawFilePath -> RawFilePath normaliseDrive "" = "" normaliseDrive _ | isPosix = B.singleton pathSeparator normaliseDrive drive | isJust (readDriveLetter x2) = upcasedriveletter x2 | otherwise = x2 where 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 isBadCharacter x = x >= 0 && x <= 31 || x `elem` l where l = map (fromIntegral . ord) ":*?><|\"" badElements :: [FilePath] badElements = ["CON","PRN","AUX","NUL","CLOCK$" ,"COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9" ,"LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9" ] -- | Is a RawFilePath valid, i.e. could you create a file like it? This function checks for invalid names, -- and invalid characters, but does not check if length limits are exceeded, as these are typically -- filesystem dependent. -- -- > isValid "" == False -- > isValid "\0" == False -- > Posix: isValid "/random_ path:*" == True -- > Posix: isValid x == (x /= mempty) -- > 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 -- > Windows: isValid "\\\\" == False -- > Windows: isValid "\\\\\\foo" == False -- > Windows: isValid "\\\\?\\D:file" == False -- > Windows: isValid "foo\tbar" == False -- > Windows: isValid "nul .txt" == False -- > Windows: isValid " nul.txt" == True isValid :: RawFilePath -> Bool isValid path | B.null path = False | B.elem 0 path = False | isPosix = True | otherwise = not (B.any isBadCharacter x2) && not (any f $ splitDirectories x2) && not (isJust (readDriveShare x1) && B.all isPathSeparator x1) && not (isJust (readDriveUNC x1) && not (hasTrailingPathSeparator x1)) where (x1,x2) = splitDrive path f x = map toUpper (decodeFilePath $ dropWhileEnd (== 32) $ dropExtensions x) `elem` badElements -- | Take a FilePath and make it valid; does not change already valid FilePaths. -- -- > isValid (makeValid x) -- > isValid x ==> makeValid x == x -- > makeValid "" == "_" -- > makeValid "file\0name" == "file_name" -- > Windows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid" -- > Windows: makeValid "c:\\test:of_test" == "c:\\test_of_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" -- > Windows: makeValid "c:\\nul\\file" == "c:\\nul_\\file" -- > Windows: makeValid "\\\\\\foo" == "\\\\drive" -- > Windows: makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file" -- > Windows: makeValid "nul .txt" == "nul _.txt" makeValid :: RawFilePath -> RawFilePath makeValid "" = "_" makeValid path | isPosix = B.map (\x -> if x == 0 then underscore else x) path | isJust (readDriveShare drv) && B.all isPathSeparator drv = B.take 2 drv <> "drive" | isJust (readDriveUNC drv) && not (hasTrailingPathSeparator drv) = makeValid (drv <> B.singleton pathSeparator <> pth) | otherwise = joinDrive drv $ validElements $ validChars pth where (drv,pth) = splitDrive path underscore :: Word8 underscore = fromIntegral (ord '_') validChars = B.map f f x = if isBadCharacter x then underscore else x validElements x = joinPath $ map g $ splitPath x g x = h a <> b where (a,b) = B.break isPathSeparator x h x = if map toUpper (decodeFilePath $ dropWhileEnd (== 32) a) `elem` badElements then a <> "_" <.> b else x where (a,b) = splitExtensions x -- | Is a path relative, or is it fixed to the root? -- -- > Windows: isRelative "path\\test" == True -- > Windows: isRelative "c:\\test" == False -- > Windows: isRelative "c:test" == True -- > Windows: isRelative "c:\\" == False -- > 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 :: RawFilePath -> Bool isRelative x = B.null drive || isRelativeDrive drive where drive = takeDrive x {- 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 :: RawFilePath -> Bool isRelativeDrive x = maybe False (not . hasTrailingPathSeparator . fst) (readDriveLetter x) -- | @not . 'isRelative'@ -- -- > isAbsolute x == not (isRelative x) isAbsolute :: RawFilePath -> Bool isAbsolute = not . isRelative ----------------------------------------------------------------------------- -- dropWhileEnd (/= 32) "foo bar" == "foo ") dropWhileEnd :: (Word8 -> Bool) -> ByteString -> ByteString dropWhileEnd p = fst . B.spanEnd p {- -- takeWhileEnd (>2) [1,2,3,4,1,2,3,4] == [3,4]) takeWhileEnd :: (a -> Bool) -> [a] -> [a] takeWhileEnd p = reverse . takeWhile p . reverse -- spanEnd (>2) [1,2,3,4,1,2,3,4] = ([1,2,3,4,1,2], [3,4]) spanEnd :: (a -> Bool) -> [a] -> ([a], [a]) spanEnd p xs = (dropWhileEnd p xs, takeWhileEnd p xs) -- breakEnd (< 2) [1,2,3,4,1,2,3,4] == ([1,2,3,4,1],[2,3,4]) breakEnd :: (a -> Bool) -> [a] -> ([a], [a]) breakEnd p = spanEnd (not . p) -- | The stripSuffix function drops the given suffix from a list. It returns -- Nothing if the list did not end with the suffix given, or Just the list -- before the suffix, if it does. stripSuffix :: Eq a => [a] -> [a] -> Maybe [a] stripSuffix xs ys = fmap reverse $ stripPrefix (reverse xs) (reverse ys) -} filepath-bytestring-1.4.100.3.2/System/FilePath/Posix/0000755000000000000000000000000007346545000020415 5ustar0000000000000000filepath-bytestring-1.4.100.3.2/System/FilePath/Posix/ByteString.hs0000644000000000000000000000015307346545000023042 0ustar0000000000000000{-# LANGUAGE CPP #-} #define MODULE_NAME Posix #define IS_WINDOWS False #include "../Internal.hs" filepath-bytestring-1.4.100.3.2/System/FilePath/Windows/0000755000000000000000000000000007346545000020745 5ustar0000000000000000filepath-bytestring-1.4.100.3.2/System/FilePath/Windows/ByteString.hs0000644000000000000000000000015407346545000023373 0ustar0000000000000000{-# LANGUAGE CPP #-} #define MODULE_NAME Windows #define IS_WINDOWS True #include "../Internal.hs" filepath-bytestring-1.4.100.3.2/TODO0000644000000000000000000000000007346545000015011 0ustar0000000000000000filepath-bytestring-1.4.100.3.2/benchmark/0000755000000000000000000000000007346545000016265 5ustar0000000000000000filepath-bytestring-1.4.100.3.2/benchmark/benchmark.hs0000644000000000000000000000126307346545000020555 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} import qualified System.FilePath as FilePath import qualified System.FilePath.ByteString as RawFilePath import Criterion.Main main :: IO () main = defaultMain [ bgroup "combine" [ bench "FilePath" $ nf (FilePath.combine "foo") "bar" , bench "RawFilePath" $ nf (RawFilePath.combine "foo") "bar" ] , bgroup "dropTrailingPathSeparator" [ bench "FilePath" $ nf FilePath.dropTrailingPathSeparator "foo/" , bench "RawFilePath" $ nf RawFilePath.dropTrailingPathSeparator "foo/" ] , bgroup "FilePath conversion" [ bench "encode" $ nf RawFilePath.encodeFilePath "foobar.baz" , bench "decode" $ nf RawFilePath.decodeFilePath "foobar.baz" ] ] filepath-bytestring-1.4.100.3.2/filepath-bytestring.cabal0000644000000000000000000000602607346545000021307 0ustar0000000000000000cabal-version: 1.18 name: filepath-bytestring version: 1.4.100.3.2 -- NOTE: Don't forget to update CHANGELOG and the filepath dependency below license: BSD3 license-file: LICENSE author: Neil Mitchell maintainer: Joey Hess copyright: Neil Mitchell 2005-2019 Joey Hess 2019-2021 category: System build-type: Simple synopsis: Library for manipulating RawFilePaths in a cross platform way. description: This package provides functionality for manipulating @RawFilePath@ values. It can be used as a drop in replacement for the filepath library to get the benefits of using ByteStrings. It provides three modules: . * "System.FilePath.Posix.ByteString" manipulates POSIX\/Linux style @RawFilePath@ values (with @\/@ as the path separator). . * "System.FilePath.Windows.ByteString" manipulates Windows style @RawFilePath@ values (with either @\\@ or @\/@ as the path separator, and deals with drives). . * "System.FilePath.ByteString" is an alias for the module appropriate to your platform. . All three modules provide the same API, and the same documentation (calling out differences in the different variants). . This package is now deprecated, since filepath 1.4.100.0 provides an OsPath type that is based on a bytestring. See https://hasufell.github.io/posts/2022-06-29-fixing-haskell-filepaths.html extra-source-files: System/FilePath/Internal.hs extra-doc-files: README.md CHANGELOG TODO source-repository head type: git location: git://git.joeyh.name/haskell-filepath-bytestring.git library default-language: Haskell2010 other-extensions: CPP PatternGuards if impl(GHC >= 7.2) other-extensions: Safe exposed-modules: System.FilePath.ByteString System.FilePath.Posix.ByteString System.FilePath.Windows.ByteString build-depends: base >= 4 && < 4.20, bytestring >= 0.11.2.0 if os(Windows) build-depends: utf8-string else build-depends: unix ghc-options: -O2 -Wall -fno-warn-tabs test-suite filepath-tests type: exitcode-stdio-1.0 default-language: Haskell2010 main-is: Test.hs ghc-options: -main-is Test -Wall -fno-warn-tabs hs-source-dirs: tests other-modules: TestEquiv TestGen TestUtil build-depends: filepath-bytestring, base, bytestring >= 0.11.2.0, QuickCheck >= 2.7 && < 2.15, -- Versions of filepath that are equvilant to this -- library, for quickcheck equivilance tests. filepath >= 1.4.100.3 && <= 1.4.200.1 benchmark filepath-bench type: exitcode-stdio-1.0 default-language: Haskell2010 main-is: benchmark.hs ghc-options: -O2 -Wall -fno-warn-tabs hs-source-dirs: benchmark build-depends: base, criterion, filepath-bytestring, -- Version of filepath to benchmark against filepath >= 1.4.100.3 && <= 1.4.200.1 filepath-bytestring-1.4.100.3.2/tests/0000755000000000000000000000000007346545000015475 5ustar0000000000000000filepath-bytestring-1.4.100.3.2/tests/Test.hs0000644000000000000000000000233407346545000016752 0ustar0000000000000000 module Test(main) where import System.Environment import TestGen import TestEquiv import Control.Monad import Data.Maybe import Test.QuickCheck main :: IO () main = do args <- getArgs let count = case args of i:_ -> read i; _ -> 10000 putStrLn $ "Testing with " ++ show count ++ " repetitions" let alltests = equivtests ++ tests let ntotal = length alltests let showOutput x = show x{output=""} ++ "\n" ++ output x bad <- fmap catMaybes $ forM (zip [(1 :: Integer)..] alltests) $ \(i,(msg,prop)) -> do putStrLn $ "Test " ++ show i ++ " of " ++ show ntotal ++ ": " ++ msg res <- quickCheckWithResult stdArgs{chatty=False, maxSuccess=count} prop case res of Success{} -> return Nothing bad -> do putStrLn $ showOutput bad; putStrLn "TEST FAILURE!"; return $ Just (msg,bad) if null bad then putStrLn $ "Success, " ++ show ntotal ++ " tests passed" else do putStrLn $ show (length bad) ++ " FAILURES\n" forM_ (zip [(1 :: Integer)..] bad) $ \(i,(a,b)) -> putStrLn $ "FAILURE " ++ show i ++ ": " ++ a ++ "\n" ++ showOutput b ++ "\n" fail $ "FAILURE, failed " ++ show (length bad) ++ " of " ++ show ntotal ++ " tests" filepath-bytestring-1.4.100.3.2/tests/TestEquiv.hs0000644000000000000000000002010507346545000017760 0ustar0000000000000000-- GENERATED CODE: See ../Generate.hs module TestEquiv(equivtests) where import TestUtil import qualified System.FilePath.Windows.ByteString as OurWindows import qualified System.FilePath.Windows as TheirWindows import qualified System.FilePath.Posix.ByteString as OurPosix import qualified System.FilePath.Posix as TheirPosix equivtests :: [(String, Property)] equivtests = [("equiv Posix.isPathSeparator", equiv_0 OurPosix.isPathSeparator TheirPosix.isPathSeparator) ,("equiv Posix.isSearchPathSeparator", equiv_0 OurPosix.isSearchPathSeparator TheirPosix.isSearchPathSeparator) ,("equiv Posix.isExtSeparator", equiv_0 OurPosix.isExtSeparator TheirPosix.isExtSeparator) ,("equiv Posix.splitExtension", equiv_1 OurPosix.splitExtension TheirPosix.splitExtension) ,("equiv Posix.takeExtension", equiv_1 OurPosix.takeExtension TheirPosix.takeExtension) ,("equiv Posix.replaceExtension", equiv_2 OurPosix.replaceExtension TheirPosix.replaceExtension) ,("equiv Posix.dropExtension", equiv_1 OurPosix.dropExtension TheirPosix.dropExtension) ,("equiv Posix.addExtension", equiv_2 OurPosix.addExtension TheirPosix.addExtension) ,("equiv Posix.hasExtension", equiv_1 OurPosix.hasExtension TheirPosix.hasExtension) ,("equiv Posix.splitExtensions", equiv_1 OurPosix.splitExtensions TheirPosix.splitExtensions) ,("equiv Posix.dropExtensions", equiv_1 OurPosix.dropExtensions TheirPosix.dropExtensions) ,("equiv Posix.takeExtensions", equiv_1 OurPosix.takeExtensions TheirPosix.takeExtensions) ,("equiv Posix.replaceExtensions", equiv_2 OurPosix.replaceExtensions TheirPosix.replaceExtensions) ,("equiv Posix.isExtensionOf", equiv_2 OurPosix.isExtensionOf TheirPosix.isExtensionOf) ,("equiv Posix.stripExtension", equiv_2 OurPosix.stripExtension TheirPosix.stripExtension) ,("equiv Posix.splitFileName", equiv_1 OurPosix.splitFileName TheirPosix.splitFileName) ,("equiv Posix.takeFileName", equiv_1 OurPosix.takeFileName TheirPosix.takeFileName) ,("equiv Posix.replaceFileName", equiv_2 OurPosix.replaceFileName TheirPosix.replaceFileName) ,("equiv Posix.dropFileName", equiv_1 OurPosix.dropFileName TheirPosix.dropFileName) ,("equiv Posix.takeBaseName", equiv_1 OurPosix.takeBaseName TheirPosix.takeBaseName) ,("equiv Posix.replaceBaseName", equiv_2 OurPosix.replaceBaseName TheirPosix.replaceBaseName) ,("equiv Posix.takeDirectory", equiv_1 OurPosix.takeDirectory TheirPosix.takeDirectory) ,("equiv Posix.replaceDirectory", equiv_2 OurPosix.replaceDirectory TheirPosix.replaceDirectory) ,("equiv Posix.combine", equiv_2 OurPosix.combine TheirPosix.combine) ,("equiv Posix.splitPath", equiv_1 OurPosix.splitPath TheirPosix.splitPath) ,("equiv Posix.joinPath", equiv_3 OurPosix.joinPath TheirPosix.joinPath) ,("equiv Posix.splitDirectories", equiv_1 OurPosix.splitDirectories TheirPosix.splitDirectories) ,("equiv Posix.splitDrive", equiv_1 OurPosix.splitDrive TheirPosix.splitDrive) ,("equiv Posix.joinDrive", equiv_2 OurPosix.joinDrive TheirPosix.joinDrive) ,("equiv Posix.takeDrive", equiv_1 OurPosix.takeDrive TheirPosix.takeDrive) ,("equiv Posix.hasDrive", equiv_1 OurPosix.hasDrive TheirPosix.hasDrive) ,("equiv Posix.dropDrive", equiv_1 OurPosix.dropDrive TheirPosix.dropDrive) ,("equiv Posix.isDrive", equiv_1 OurPosix.isDrive TheirPosix.isDrive) ,("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 Posix.equalFilePath", equiv_2 OurPosix.equalFilePath TheirPosix.equalFilePath) ,("equiv Posix.makeRelative", equiv_2 OurPosix.makeRelative TheirPosix.makeRelative) ,("equiv Posix.splitSearchPath", equiv_1 OurPosix.splitSearchPath TheirPosix.splitSearchPath) ,("equiv Posix.makeValid", equiv_1 OurPosix.makeValid TheirPosix.makeValid) ,("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) ,("equiv Windows.splitExtension", equiv_1 OurWindows.splitExtension TheirWindows.splitExtension) ,("equiv Windows.takeExtension", equiv_1 OurWindows.takeExtension TheirWindows.takeExtension) ,("equiv Windows.replaceExtension", equiv_2 OurWindows.replaceExtension TheirWindows.replaceExtension) ,("equiv Windows.dropExtension", equiv_1 OurWindows.dropExtension TheirWindows.dropExtension) ,("equiv Windows.addExtension", equiv_2 OurWindows.addExtension TheirWindows.addExtension) ,("equiv Windows.hasExtension", equiv_1 OurWindows.hasExtension TheirWindows.hasExtension) ,("equiv Windows.splitExtensions", equiv_1 OurWindows.splitExtensions TheirWindows.splitExtensions) ,("equiv Windows.dropExtensions", equiv_1 OurWindows.dropExtensions TheirWindows.dropExtensions) ,("equiv Windows.takeExtensions", equiv_1 OurWindows.takeExtensions TheirWindows.takeExtensions) ,("equiv Windows.replaceExtensions", equiv_2 OurWindows.replaceExtensions TheirWindows.replaceExtensions) ,("equiv Windows.isExtensionOf", equiv_2 OurWindows.isExtensionOf TheirWindows.isExtensionOf) ,("equiv Windows.stripExtension", equiv_2 OurWindows.stripExtension TheirWindows.stripExtension) ,("equiv Windows.splitFileName", equiv_1 OurWindows.splitFileName TheirWindows.splitFileName) ,("equiv Windows.takeFileName", equiv_1 OurWindows.takeFileName TheirWindows.takeFileName) ,("equiv Windows.replaceFileName", equiv_2 OurWindows.replaceFileName TheirWindows.replaceFileName) ,("equiv Windows.dropFileName", equiv_1 OurWindows.dropFileName TheirWindows.dropFileName) ,("equiv Windows.takeBaseName", equiv_1 OurWindows.takeBaseName TheirWindows.takeBaseName) ,("equiv Windows.replaceBaseName", equiv_2 OurWindows.replaceBaseName TheirWindows.replaceBaseName) ,("equiv Windows.takeDirectory", equiv_1 OurWindows.takeDirectory TheirWindows.takeDirectory) ,("equiv Windows.replaceDirectory", equiv_2 OurWindows.replaceDirectory TheirWindows.replaceDirectory) ,("equiv Windows.combine", equiv_2 OurWindows.combine TheirWindows.combine) ,("equiv Windows.splitPath", equiv_1 OurWindows.splitPath TheirWindows.splitPath) ,("equiv Windows.joinPath", equiv_3 OurWindows.joinPath TheirWindows.joinPath) ,("equiv Windows.splitDirectories", equiv_1 OurWindows.splitDirectories TheirWindows.splitDirectories) ,("equiv Windows.splitDrive", equiv_1 OurWindows.splitDrive TheirWindows.splitDrive) ,("equiv Windows.joinDrive", equiv_2 OurWindows.joinDrive TheirWindows.joinDrive) ,("equiv Windows.takeDrive", equiv_1 OurWindows.takeDrive TheirWindows.takeDrive) ,("equiv Windows.hasDrive", equiv_1 OurWindows.hasDrive TheirWindows.hasDrive) ,("equiv Windows.dropDrive", equiv_1 OurWindows.dropDrive TheirWindows.dropDrive) ,("equiv Windows.isDrive", equiv_1 OurWindows.isDrive TheirWindows.isDrive) ,("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) ,("equiv Windows.equalFilePath", equiv_2 OurWindows.equalFilePath TheirWindows.equalFilePath) ,("equiv Windows.makeRelative", equiv_2 OurWindows.makeRelative TheirWindows.makeRelative) ,("equiv Windows.splitSearchPath", equiv_1 OurWindows.splitSearchPath TheirWindows.splitSearchPath) ,("equiv Windows.makeValid", equiv_1 OurWindows.makeValid TheirWindows.makeValid) ] filepath-bytestring-1.4.100.3.2/tests/TestGen.hs0000644000000000000000000016375607346545000017424 0ustar0000000000000000-- GENERATED CODE: See ../Generate.hs {-# LANGUAGE OverloadedStrings #-} module TestGen(tests) where import TestUtil import Data.Char import Data.Semigroup import qualified System.FilePath.Windows.ByteString as W import qualified System.FilePath.Posix.ByteString as P import Data.ByteString (isPrefixOf, isSuffixOf, null, any) import Prelude hiding (null, any) tests :: [(String, Property)] tests = [("W.pathSeparator == fromIntegral (ord '\\\\')", property $ W.pathSeparator == fromIntegral (ord '\\')) ,("P.pathSeparator == fromIntegral (ord '/')", property $ P.pathSeparator == fromIntegral (ord '/')) ,("P.isPathSeparator P.pathSeparator", property $ P.isPathSeparator P.pathSeparator) ,("W.isPathSeparator W.pathSeparator", property $ W.isPathSeparator W.pathSeparator) ,("W.pathSeparators == [fromIntegral (ord '\\\\'), fromIntegral (ord '/')]", property $ W.pathSeparators == [fromIntegral (ord '\\'), fromIntegral (ord '/')]) ,("P.pathSeparators == [fromIntegral (ord '/')]", property $ P.pathSeparators == [fromIntegral (ord '/')]) ,("P.pathSeparator `elem` P.pathSeparators", property $ P.pathSeparator `elem` P.pathSeparators) ,("W.pathSeparator `elem` W.pathSeparators", property $ W.pathSeparator `elem` W.pathSeparators) ,("P.isPathSeparator a == (a `elem` P.pathSeparators)", property $ \a -> P.isPathSeparator a == (a `elem` P.pathSeparators)) ,("W.isPathSeparator a == (a `elem` W.pathSeparators)", property $ \a -> W.isPathSeparator a == (a `elem` W.pathSeparators)) ,("W.searchPathSeparator == fromIntegral (ord ';')", property $ W.searchPathSeparator == fromIntegral (ord ';')) ,("P.searchPathSeparator == fromIntegral (ord ':')", property $ P.searchPathSeparator == fromIntegral (ord ':')) ,("P.isSearchPathSeparator a == (a == P.searchPathSeparator)", property $ \a -> P.isSearchPathSeparator a == (a == P.searchPathSeparator)) ,("W.isSearchPathSeparator a == (a == W.searchPathSeparator)", property $ \a -> W.isSearchPathSeparator a == (a == W.searchPathSeparator)) ,("P.extSeparator == fromIntegral (ord '.')", property $ P.extSeparator == fromIntegral (ord '.')) ,("W.extSeparator == fromIntegral (ord '.')", property $ W.extSeparator == fromIntegral (ord '.')) ,("P.isExtSeparator a == (a == P.extSeparator)", property $ \a -> P.isExtSeparator a == (a == P.extSeparator)) ,("W.isExtSeparator a == (a == W.extSeparator)", property $ \a -> W.isExtSeparator a == (a == W.extSeparator)) ,("P.splitSearchPath \"File1:File2:File3\" == [\"File1\", \"File2\", \"File3\"]", property $ P.splitSearchPath "File1:File2:File3" == ["File1", "File2", "File3"]) ,("P.splitSearchPath \"File1::File2:File3\" == [\"File1\", \".\", \"File2\", \"File3\"]", property $ P.splitSearchPath "File1::File2:File3" == ["File1", ".", "File2", "File3"]) ,("W.splitSearchPath \"File1;File2;File3\" == [\"File1\", \"File2\", \"File3\"]", property $ W.splitSearchPath "File1;File2;File3" == ["File1", "File2", "File3"]) ,("W.splitSearchPath \"File1;;File2;File3\" == [\"File1\", \"File2\", \"File3\"]", property $ W.splitSearchPath "File1;;File2;File3" == ["File1", "File2", "File3"]) ,("W.splitSearchPath \"File1;\\\"File2\\\";File3\" == [\"File1\", \"File2\", \"File3\"]", property $ W.splitSearchPath "File1;\"File2\";File3" == ["File1", "File2", "File3"]) ,("P.splitExtension \"/directory/path.ext\" == (\"/directory/path\", \".ext\")", property $ P.splitExtension "/directory/path.ext" == ("/directory/path", ".ext")) ,("W.splitExtension \"/directory/path.ext\" == (\"/directory/path\", \".ext\")", property $ W.splitExtension "/directory/path.ext" == ("/directory/path", ".ext")) ,("uncurry (<>) (P.splitExtension x) == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in uncurry (<>) (P.splitExtension x) == x) ,("uncurry (<>) (W.splitExtension x) == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in uncurry (<>) (W.splitExtension x) == x) ,("uncurry P.addExtension (P.splitExtension x) == x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in uncurry P.addExtension (P.splitExtension x) == x) ,("uncurry W.addExtension (W.splitExtension x) == x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in uncurry W.addExtension (W.splitExtension x) == x) ,("P.splitExtension \"file.txt\" == (\"file\", \".txt\")", property $ P.splitExtension "file.txt" == ("file", ".txt")) ,("W.splitExtension \"file.txt\" == (\"file\", \".txt\")", property $ W.splitExtension "file.txt" == ("file", ".txt")) ,("P.splitExtension \"file\" == (\"file\", \"\")", property $ P.splitExtension "file" == ("file", "")) ,("W.splitExtension \"file\" == (\"file\", \"\")", property $ W.splitExtension "file" == ("file", "")) ,("P.splitExtension \"file/file.txt\" == (\"file/file\", \".txt\")", property $ P.splitExtension "file/file.txt" == ("file/file", ".txt")) ,("W.splitExtension \"file/file.txt\" == (\"file/file\", \".txt\")", property $ W.splitExtension "file/file.txt" == ("file/file", ".txt")) ,("P.splitExtension \"file.txt/boris\" == (\"file.txt/boris\", \"\")", property $ P.splitExtension "file.txt/boris" == ("file.txt/boris", "")) ,("W.splitExtension \"file.txt/boris\" == (\"file.txt/boris\", \"\")", property $ W.splitExtension "file.txt/boris" == ("file.txt/boris", "")) ,("P.splitExtension \"file.txt/boris.ext\" == (\"file.txt/boris\", \".ext\")", property $ P.splitExtension "file.txt/boris.ext" == ("file.txt/boris", ".ext")) ,("W.splitExtension \"file.txt/boris.ext\" == (\"file.txt/boris\", \".ext\")", property $ W.splitExtension "file.txt/boris.ext" == ("file.txt/boris", ".ext")) ,("P.splitExtension \"file/path.txt.bob.fred\" == (\"file/path.txt.bob\", \".fred\")", property $ P.splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob", ".fred")) ,("W.splitExtension \"file/path.txt.bob.fred\" == (\"file/path.txt.bob\", \".fred\")", property $ W.splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob", ".fred")) ,("P.splitExtension \"file/path.txt/\" == (\"file/path.txt/\", \"\")", property $ P.splitExtension "file/path.txt/" == ("file/path.txt/", "")) ,("W.splitExtension \"file/path.txt/\" == (\"file/path.txt/\", \"\")", property $ W.splitExtension "file/path.txt/" == ("file/path.txt/", "")) ,("P.takeExtension \"/directory/path.ext\" == \".ext\"", property $ P.takeExtension "/directory/path.ext" == ".ext") ,("W.takeExtension \"/directory/path.ext\" == \".ext\"", property $ W.takeExtension "/directory/path.ext" == ".ext") ,("P.takeExtension x == snd (P.splitExtension x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.takeExtension x == snd (P.splitExtension x)) ,("W.takeExtension x == snd (W.splitExtension x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.takeExtension x == snd (W.splitExtension x)) ,("P.takeExtension (P.addExtension x \"ext\") == \".ext\"", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.takeExtension (P.addExtension x "ext") == ".ext") ,("W.takeExtension (W.addExtension x \"ext\") == \".ext\"", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.takeExtension (W.addExtension x "ext") == ".ext") ,("P.takeExtension (P.replaceExtension x \"ext\") == \".ext\"", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.takeExtension (P.replaceExtension x "ext") == ".ext") ,("W.takeExtension (W.replaceExtension x \"ext\") == \".ext\"", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.takeExtension (W.replaceExtension x "ext") == ".ext") ,("\"/directory/path.txt\" P.-<.> \"ext\" == \"/directory/path.ext\"", property $ "/directory/path.txt" P.-<.> "ext" == "/directory/path.ext") ,("\"/directory/path.txt\" W.-<.> \"ext\" == \"/directory/path.ext\"", property $ "/directory/path.txt" W.-<.> "ext" == "/directory/path.ext") ,("\"/directory/path.txt\" P.-<.> \".ext\" == \"/directory/path.ext\"", property $ "/directory/path.txt" P.-<.> ".ext" == "/directory/path.ext") ,("\"/directory/path.txt\" W.-<.> \".ext\" == \"/directory/path.ext\"", property $ "/directory/path.txt" W.-<.> ".ext" == "/directory/path.ext") ,("\"foo.o\" P.-<.> \"c\" == \"foo.c\"", property $ "foo.o" P.-<.> "c" == "foo.c") ,("\"foo.o\" W.-<.> \"c\" == \"foo.c\"", property $ "foo.o" W.-<.> "c" == "foo.c") ,("P.replaceExtension \"/directory/path.txt\" \"ext\" == \"/directory/path.ext\"", property $ P.replaceExtension "/directory/path.txt" "ext" == "/directory/path.ext") ,("W.replaceExtension \"/directory/path.txt\" \"ext\" == \"/directory/path.ext\"", property $ W.replaceExtension "/directory/path.txt" "ext" == "/directory/path.ext") ,("P.replaceExtension \"/directory/path.txt\" \".ext\" == \"/directory/path.ext\"", property $ P.replaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext") ,("W.replaceExtension \"/directory/path.txt\" \".ext\" == \"/directory/path.ext\"", property $ W.replaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext") ,("P.replaceExtension \"file.txt\" \".bob\" == \"file.bob\"", property $ P.replaceExtension "file.txt" ".bob" == "file.bob") ,("W.replaceExtension \"file.txt\" \".bob\" == \"file.bob\"", property $ W.replaceExtension "file.txt" ".bob" == "file.bob") ,("P.replaceExtension \"file.txt\" \"bob\" == \"file.bob\"", property $ P.replaceExtension "file.txt" "bob" == "file.bob") ,("W.replaceExtension \"file.txt\" \"bob\" == \"file.bob\"", property $ W.replaceExtension "file.txt" "bob" == "file.bob") ,("P.replaceExtension \"file\" \".bob\" == \"file.bob\"", property $ P.replaceExtension "file" ".bob" == "file.bob") ,("W.replaceExtension \"file\" \".bob\" == \"file.bob\"", property $ W.replaceExtension "file" ".bob" == "file.bob") ,("P.replaceExtension \"file.txt\" \"\" == \"file\"", property $ P.replaceExtension "file.txt" "" == "file") ,("W.replaceExtension \"file.txt\" \"\" == \"file\"", property $ W.replaceExtension "file.txt" "" == "file") ,("P.replaceExtension \"file.fred.bob\" \"txt\" == \"file.fred.txt\"", property $ P.replaceExtension "file.fred.bob" "txt" == "file.fred.txt") ,("W.replaceExtension \"file.fred.bob\" \"txt\" == \"file.fred.txt\"", property $ W.replaceExtension "file.fred.bob" "txt" == "file.fred.txt") ,("P.replaceExtension x y == P.addExtension (P.dropExtension x) y", property $ \(QFilePath vx) (QFilePath vy) -> let y = toRawFilePath vy in let x = toRawFilePath vx in P.replaceExtension x y == P.addExtension (P.dropExtension x) y) ,("W.replaceExtension x y == W.addExtension (W.dropExtension x) y", property $ \(QFilePath vx) (QFilePath vy) -> let y = toRawFilePath vy in let x = toRawFilePath vx in W.replaceExtension x y == W.addExtension (W.dropExtension x) y) ,("\"/directory/path\" P.<.> \"ext\" == \"/directory/path.ext\"", property $ "/directory/path" P.<.> "ext" == "/directory/path.ext") ,("\"/directory/path\" W.<.> \"ext\" == \"/directory/path.ext\"", property $ "/directory/path" W.<.> "ext" == "/directory/path.ext") ,("\"/directory/path\" P.<.> \".ext\" == \"/directory/path.ext\"", property $ "/directory/path" P.<.> ".ext" == "/directory/path.ext") ,("\"/directory/path\" W.<.> \".ext\" == \"/directory/path.ext\"", property $ "/directory/path" W.<.> ".ext" == "/directory/path.ext") ,("P.dropExtension \"/directory/path.ext\" == \"/directory/path\"", property $ P.dropExtension "/directory/path.ext" == "/directory/path") ,("W.dropExtension \"/directory/path.ext\" == \"/directory/path\"", property $ W.dropExtension "/directory/path.ext" == "/directory/path") ,("P.dropExtension x == fst (P.splitExtension x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.dropExtension x == fst (P.splitExtension x)) ,("W.dropExtension x == fst (W.splitExtension x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.dropExtension x == fst (W.splitExtension x)) ,("P.addExtension \"/directory/path\" \"ext\" == \"/directory/path.ext\"", property $ P.addExtension "/directory/path" "ext" == "/directory/path.ext") ,("W.addExtension \"/directory/path\" \"ext\" == \"/directory/path.ext\"", property $ W.addExtension "/directory/path" "ext" == "/directory/path.ext") ,("P.addExtension \"file.txt\" \"bib\" == \"file.txt.bib\"", property $ P.addExtension "file.txt" "bib" == "file.txt.bib") ,("W.addExtension \"file.txt\" \"bib\" == \"file.txt.bib\"", property $ W.addExtension "file.txt" "bib" == "file.txt.bib") ,("P.addExtension \"file.\" \".bib\" == \"file..bib\"", property $ P.addExtension "file." ".bib" == "file..bib") ,("W.addExtension \"file.\" \".bib\" == \"file..bib\"", property $ W.addExtension "file." ".bib" == "file..bib") ,("P.addExtension \"file\" \".bib\" == \"file.bib\"", property $ P.addExtension "file" ".bib" == "file.bib") ,("W.addExtension \"file\" \".bib\" == \"file.bib\"", property $ W.addExtension "file" ".bib" == "file.bib") ,("P.addExtension \"/\" \"x\" == \"/.x\"", property $ P.addExtension "/" "x" == "/.x") ,("W.addExtension \"/\" \"x\" == \"/.x\"", property $ W.addExtension "/" "x" == "/.x") ,("P.addExtension x \"\" == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.addExtension x "" == x) ,("W.addExtension x \"\" == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.addExtension x "" == x) ,("P.takeFileName (P.addExtension (P.addTrailingPathSeparator x) \"ext\") == \".ext\"", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.takeFileName (P.addExtension (P.addTrailingPathSeparator x) "ext") == ".ext") ,("W.takeFileName (W.addExtension (W.addTrailingPathSeparator x) \"ext\") == \".ext\"", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.takeFileName (W.addExtension (W.addTrailingPathSeparator x) "ext") == ".ext") ,("W.addExtension \"\\\\\\\\share\" \".txt\" == \"\\\\\\\\share\\\\.txt\"", property $ W.addExtension "\\\\share" ".txt" == "\\\\share\\.txt") ,("P.hasExtension \"/directory/path.ext\" == True", property $ P.hasExtension "/directory/path.ext" == True) ,("W.hasExtension \"/directory/path.ext\" == True", property $ W.hasExtension "/directory/path.ext" == True) ,("P.hasExtension \"/directory/path\" == False", property $ P.hasExtension "/directory/path" == False) ,("W.hasExtension \"/directory/path\" == False", property $ W.hasExtension "/directory/path" == False) ,("null (P.takeExtension x) == not (P.hasExtension x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in null (P.takeExtension x) == not (P.hasExtension x)) ,("null (W.takeExtension x) == not (W.hasExtension x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in null (W.takeExtension x) == not (W.hasExtension x)) ,("\"png\" `P.isExtensionOf` \"/directory/file.png\" == True", property $ "png" `P.isExtensionOf` "/directory/file.png" == True) ,("\"png\" `W.isExtensionOf` \"/directory/file.png\" == True", property $ "png" `W.isExtensionOf` "/directory/file.png" == True) ,("\".png\" `P.isExtensionOf` \"/directory/file.png\" == True", property $ ".png" `P.isExtensionOf` "/directory/file.png" == True) ,("\".png\" `W.isExtensionOf` \"/directory/file.png\" == True", property $ ".png" `W.isExtensionOf` "/directory/file.png" == True) ,("\".tar.gz\" `P.isExtensionOf` \"bar/foo.tar.gz\" == True", property $ ".tar.gz" `P.isExtensionOf` "bar/foo.tar.gz" == True) ,("\".tar.gz\" `W.isExtensionOf` \"bar/foo.tar.gz\" == True", property $ ".tar.gz" `W.isExtensionOf` "bar/foo.tar.gz" == True) ,("\"ar.gz\" `P.isExtensionOf` \"bar/foo.tar.gz\" == False", property $ "ar.gz" `P.isExtensionOf` "bar/foo.tar.gz" == False) ,("\"ar.gz\" `W.isExtensionOf` \"bar/foo.tar.gz\" == False", property $ "ar.gz" `W.isExtensionOf` "bar/foo.tar.gz" == False) ,("\"png\" `P.isExtensionOf` \"/directory/file.png.jpg\" == False", property $ "png" `P.isExtensionOf` "/directory/file.png.jpg" == False) ,("\"png\" `W.isExtensionOf` \"/directory/file.png.jpg\" == False", property $ "png" `W.isExtensionOf` "/directory/file.png.jpg" == False) ,("\"csv/table.csv\" `P.isExtensionOf` \"/data/csv/table.csv\" == False", property $ "csv/table.csv" `P.isExtensionOf` "/data/csv/table.csv" == False) ,("\"csv/table.csv\" `W.isExtensionOf` \"/data/csv/table.csv\" == False", property $ "csv/table.csv" `W.isExtensionOf` "/data/csv/table.csv" == False) ,("P.stripExtension \"hs.o\" \"foo.x.hs.o\" == Just \"foo.x\"", property $ P.stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x") ,("W.stripExtension \"hs.o\" \"foo.x.hs.o\" == Just \"foo.x\"", property $ W.stripExtension "hs.o" "foo.x.hs.o" == Just "foo.x") ,("P.stripExtension \"hi.o\" \"foo.x.hs.o\" == Nothing", property $ P.stripExtension "hi.o" "foo.x.hs.o" == Nothing) ,("W.stripExtension \"hi.o\" \"foo.x.hs.o\" == Nothing", property $ W.stripExtension "hi.o" "foo.x.hs.o" == Nothing) ,("P.dropExtension x == fromJust (P.stripExtension (P.takeExtension x) x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.dropExtension x == fromJust (P.stripExtension (P.takeExtension x) x)) ,("W.dropExtension x == fromJust (W.stripExtension (W.takeExtension x) x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.dropExtension x == fromJust (W.stripExtension (W.takeExtension x) x)) ,("P.dropExtensions x == fromJust (P.stripExtension (P.takeExtensions x) x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.dropExtensions x == fromJust (P.stripExtension (P.takeExtensions x) x)) ,("W.dropExtensions x == fromJust (W.stripExtension (W.takeExtensions x) x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.dropExtensions x == fromJust (W.stripExtension (W.takeExtensions x) x)) ,("P.stripExtension \".c.d\" \"a.b.c.d\" == Just \"a.b\"", property $ P.stripExtension ".c.d" "a.b.c.d" == Just "a.b") ,("W.stripExtension \".c.d\" \"a.b.c.d\" == Just \"a.b\"", property $ W.stripExtension ".c.d" "a.b.c.d" == Just "a.b") ,("P.stripExtension \".c.d\" \"a.b..c.d\" == Just \"a.b.\"", property $ P.stripExtension ".c.d" "a.b..c.d" == Just "a.b.") ,("W.stripExtension \".c.d\" \"a.b..c.d\" == Just \"a.b.\"", property $ W.stripExtension ".c.d" "a.b..c.d" == Just "a.b.") ,("P.stripExtension \"baz\" \"foo.bar\" == Nothing", property $ P.stripExtension "baz" "foo.bar" == Nothing) ,("W.stripExtension \"baz\" \"foo.bar\" == Nothing", property $ W.stripExtension "baz" "foo.bar" == Nothing) ,("P.stripExtension \"bar\" \"foobar\" == Nothing", property $ P.stripExtension "bar" "foobar" == Nothing) ,("W.stripExtension \"bar\" \"foobar\" == Nothing", property $ W.stripExtension "bar" "foobar" == Nothing) ,("P.stripExtension \"\" x == Just x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.stripExtension "" x == Just x) ,("W.stripExtension \"\" x == Just x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.stripExtension "" x == Just x) ,("P.splitExtensions \"/directory/path.ext\" == (\"/directory/path\", \".ext\")", property $ P.splitExtensions "/directory/path.ext" == ("/directory/path", ".ext")) ,("W.splitExtensions \"/directory/path.ext\" == (\"/directory/path\", \".ext\")", property $ W.splitExtensions "/directory/path.ext" == ("/directory/path", ".ext")) ,("P.splitExtensions \"file.tar.gz\" == (\"file\", \".tar.gz\")", property $ P.splitExtensions "file.tar.gz" == ("file", ".tar.gz")) ,("W.splitExtensions \"file.tar.gz\" == (\"file\", \".tar.gz\")", property $ W.splitExtensions "file.tar.gz" == ("file", ".tar.gz")) ,("uncurry (<>) (P.splitExtensions x) == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in uncurry (<>) (P.splitExtensions x) == x) ,("uncurry (<>) (W.splitExtensions x) == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in uncurry (<>) (W.splitExtensions x) == x) ,("uncurry P.addExtension (P.splitExtensions x) == x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in uncurry P.addExtension (P.splitExtensions x) == x) ,("uncurry W.addExtension (W.splitExtensions x) == x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in uncurry W.addExtension (W.splitExtensions x) == x) ,("P.splitExtensions \"file.tar.gz\" == (\"file\", \".tar.gz\")", property $ P.splitExtensions "file.tar.gz" == ("file", ".tar.gz")) ,("W.splitExtensions \"file.tar.gz\" == (\"file\", \".tar.gz\")", property $ W.splitExtensions "file.tar.gz" == ("file", ".tar.gz")) ,("P.dropExtensions \"/directory/path.ext\" == \"/directory/path\"", property $ P.dropExtensions "/directory/path.ext" == "/directory/path") ,("W.dropExtensions \"/directory/path.ext\" == \"/directory/path\"", property $ W.dropExtensions "/directory/path.ext" == "/directory/path") ,("P.dropExtensions \"file.tar.gz\" == \"file\"", property $ P.dropExtensions "file.tar.gz" == "file") ,("W.dropExtensions \"file.tar.gz\" == \"file\"", property $ W.dropExtensions "file.tar.gz" == "file") ,("not $ P.hasExtension $ P.dropExtensions x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in not $ P.hasExtension $ P.dropExtensions x) ,("not $ W.hasExtension $ W.dropExtensions x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in not $ W.hasExtension $ W.dropExtensions x) ,("not $ any P.isExtSeparator $ P.takeFileName $ P.dropExtensions x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in not $ any P.isExtSeparator $ P.takeFileName $ P.dropExtensions x) ,("not $ any W.isExtSeparator $ W.takeFileName $ W.dropExtensions x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in not $ any W.isExtSeparator $ W.takeFileName $ W.dropExtensions x) ,("P.takeExtensions \"/directory/path.ext\" == \".ext\"", property $ P.takeExtensions "/directory/path.ext" == ".ext") ,("W.takeExtensions \"/directory/path.ext\" == \".ext\"", property $ W.takeExtensions "/directory/path.ext" == ".ext") ,("P.takeExtensions \"file.tar.gz\" == \".tar.gz\"", property $ P.takeExtensions "file.tar.gz" == ".tar.gz") ,("W.takeExtensions \"file.tar.gz\" == \".tar.gz\"", property $ W.takeExtensions "file.tar.gz" == ".tar.gz") ,("P.replaceExtensions \"file.fred.bob\" \"txt\" == \"file.txt\"", property $ P.replaceExtensions "file.fred.bob" "txt" == "file.txt") ,("W.replaceExtensions \"file.fred.bob\" \"txt\" == \"file.txt\"", property $ W.replaceExtensions "file.fred.bob" "txt" == "file.txt") ,("P.replaceExtensions \"file.fred.bob\" \"tar.gz\" == \"file.tar.gz\"", property $ P.replaceExtensions "file.fred.bob" "tar.gz" == "file.tar.gz") ,("W.replaceExtensions \"file.fred.bob\" \"tar.gz\" == \"file.tar.gz\"", property $ W.replaceExtensions "file.fred.bob" "tar.gz" == "file.tar.gz") ,("uncurry (<>) (P.splitDrive x) == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in uncurry (<>) (P.splitDrive x) == x) ,("uncurry (<>) (W.splitDrive x) == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in uncurry (<>) (W.splitDrive x) == x) ,("W.splitDrive \"file\" == (\"\", \"file\")", property $ W.splitDrive "file" == ("", "file")) ,("W.splitDrive \"c:/file\" == (\"c:/\", \"file\")", property $ W.splitDrive "c:/file" == ("c:/", "file")) ,("W.splitDrive \"c:\\\\file\" == (\"c:\\\\\", \"file\")", property $ W.splitDrive "c:\\file" == ("c:\\", "file")) ,("W.splitDrive \"\\\\\\\\shared\\\\test\" == (\"\\\\\\\\shared\\\\\", \"test\")", property $ W.splitDrive "\\\\shared\\test" == ("\\\\shared\\", "test")) ,("W.splitDrive \"\\\\\\\\shared\" == (\"\\\\\\\\shared\", \"\")", property $ W.splitDrive "\\\\shared" == ("\\\\shared", "")) ,("W.splitDrive \"\\\\\\\\?\\\\UNC\\\\shared\\\\file\" == (\"\\\\\\\\?\\\\UNC\\\\shared\\\\\", \"file\")", property $ W.splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\", "file")) ,("W.splitDrive \"\\\\\\\\?\\\\UNCshared\\\\file\" == (\"\\\\\\\\?\\\\\", \"UNCshared\\\\file\")", property $ W.splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\", "UNCshared\\file")) ,("W.splitDrive \"\\\\\\\\?\\\\d:\\\\file\" == (\"\\\\\\\\?\\\\d:\\\\\", \"file\")", property $ W.splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\", "file")) ,("W.splitDrive \"/d\" == (\"\", \"/d\")", property $ W.splitDrive "/d" == ("", "/d")) ,("P.splitDrive \"/test\" == (\"/\", \"test\")", property $ P.splitDrive "/test" == ("/", "test")) ,("P.splitDrive \"//test\" == (\"//\", \"test\")", property $ P.splitDrive "//test" == ("//", "test")) ,("P.splitDrive \"test/file\" == (\"\", \"test/file\")", property $ P.splitDrive "test/file" == ("", "test/file")) ,("P.splitDrive \"file\" == (\"\", \"file\")", property $ P.splitDrive "file" == ("", "file")) ,("uncurry P.joinDrive (P.splitDrive x) == x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in uncurry P.joinDrive (P.splitDrive x) == x) ,("uncurry W.joinDrive (W.splitDrive x) == x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in uncurry W.joinDrive (W.splitDrive x) == x) ,("W.joinDrive \"C:\" \"foo\" == \"C:foo\"", property $ W.joinDrive "C:" "foo" == "C:foo") ,("W.joinDrive \"C:\\\\\" \"bar\" == \"C:\\\\bar\"", property $ W.joinDrive "C:\\" "bar" == "C:\\bar") ,("W.joinDrive \"\\\\\\\\share\" \"foo\" == \"\\\\\\\\share\\\\foo\"", property $ W.joinDrive "\\\\share" "foo" == "\\\\share\\foo") ,("W.joinDrive \"/:\" \"foo\" == \"/:\\\\foo\"", property $ W.joinDrive "/:" "foo" == "/:\\foo") ,("P.takeDrive x == fst (P.splitDrive x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.takeDrive x == fst (P.splitDrive x)) ,("W.takeDrive x == fst (W.splitDrive x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.takeDrive x == fst (W.splitDrive x)) ,("P.dropDrive x == snd (P.splitDrive x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.dropDrive x == snd (P.splitDrive x)) ,("W.dropDrive x == snd (W.splitDrive x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.dropDrive x == snd (W.splitDrive x)) ,("not (P.hasDrive x) == null (P.takeDrive x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in not (P.hasDrive x) == null (P.takeDrive x)) ,("not (W.hasDrive x) == null (W.takeDrive x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in not (W.hasDrive x) == null (W.takeDrive x)) ,("P.hasDrive \"/foo\" == True", property $ P.hasDrive "/foo" == True) ,("W.hasDrive \"C:\\\\foo\" == True", property $ W.hasDrive "C:\\foo" == True) ,("W.hasDrive \"C:foo\" == True", property $ W.hasDrive "C:foo" == True) ,("P.hasDrive \"foo\" == False", property $ P.hasDrive "foo" == False) ,("W.hasDrive \"foo\" == False", property $ W.hasDrive "foo" == False) ,("P.hasDrive \"\" == False", property $ P.hasDrive "" == False) ,("W.hasDrive \"\" == False", property $ W.hasDrive "" == False) ,("P.isDrive \"/\" == True", property $ P.isDrive "/" == True) ,("P.isDrive \"/foo\" == False", property $ P.isDrive "/foo" == False) ,("W.isDrive \"C:\\\\\" == True", property $ W.isDrive "C:\\" == True) ,("W.isDrive \"C:\\\\foo\" == False", property $ W.isDrive "C:\\foo" == False) ,("P.isDrive \"\" == False", property $ P.isDrive "" == False) ,("W.isDrive \"\" == False", property $ W.isDrive "" == False) ,("P.splitFileName \"/directory/file.ext\" == (\"/directory/\", \"file.ext\")", property $ P.splitFileName "/directory/file.ext" == ("/directory/", "file.ext")) ,("W.splitFileName \"/directory/file.ext\" == (\"/directory/\", \"file.ext\")", property $ W.splitFileName "/directory/file.ext" == ("/directory/", "file.ext")) ,("uncurry (P.) (P.splitFileName x) == x || fst (P.splitFileName x) == \"./\"", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in uncurry (P.) (P.splitFileName x) == x || fst (P.splitFileName x) == "./") ,("uncurry (W.) (W.splitFileName x) == x || fst (W.splitFileName x) == \"./\"", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in uncurry (W.) (W.splitFileName x) == x || fst (W.splitFileName x) == "./") ,("P.isValid (fst (P.splitFileName x))", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.isValid (fst (P.splitFileName x))) ,("W.isValid (fst (W.splitFileName x))", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.isValid (fst (W.splitFileName x))) ,("P.splitFileName \"file/bob.txt\" == (\"file/\", \"bob.txt\")", property $ P.splitFileName "file/bob.txt" == ("file/", "bob.txt")) ,("W.splitFileName \"file/bob.txt\" == (\"file/\", \"bob.txt\")", property $ W.splitFileName "file/bob.txt" == ("file/", "bob.txt")) ,("P.splitFileName \"file/\" == (\"file/\", \"\")", property $ P.splitFileName "file/" == ("file/", "")) ,("W.splitFileName \"file/\" == (\"file/\", \"\")", property $ W.splitFileName "file/" == ("file/", "")) ,("P.splitFileName \"bob\" == (\"./\", \"bob\")", property $ P.splitFileName "bob" == ("./", "bob")) ,("W.splitFileName \"bob\" == (\"./\", \"bob\")", property $ W.splitFileName "bob" == ("./", "bob")) ,("P.splitFileName \"/\" == (\"/\", \"\")", property $ P.splitFileName "/" == ("/", "")) ,("W.splitFileName \"c:\" == (\"c:\", \"\")", property $ W.splitFileName "c:" == ("c:", "")) ,("P.replaceFileName \"/directory/other.txt\" \"file.ext\" == \"/directory/file.ext\"", property $ P.replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext") ,("W.replaceFileName \"/directory/other.txt\" \"file.ext\" == \"/directory/file.ext\"", property $ W.replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext") ,("P.replaceFileName x (P.takeFileName x) == x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.replaceFileName x (P.takeFileName x) == x) ,("W.replaceFileName x (W.takeFileName x) == x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.replaceFileName x (W.takeFileName x) == x) ,("P.dropFileName \"/directory/file.ext\" == \"/directory/\"", property $ P.dropFileName "/directory/file.ext" == "/directory/") ,("W.dropFileName \"/directory/file.ext\" == \"/directory/\"", property $ W.dropFileName "/directory/file.ext" == "/directory/") ,("P.dropFileName x == fst (P.splitFileName x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.dropFileName x == fst (P.splitFileName x)) ,("W.dropFileName x == fst (W.splitFileName x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.dropFileName x == fst (W.splitFileName x)) ,("P.takeFileName \"/directory/file.ext\" == \"file.ext\"", property $ P.takeFileName "/directory/file.ext" == "file.ext") ,("W.takeFileName \"/directory/file.ext\" == \"file.ext\"", property $ W.takeFileName "/directory/file.ext" == "file.ext") ,("P.takeFileName \"test/\" == \"\"", property $ P.takeFileName "test/" == "") ,("W.takeFileName \"test/\" == \"\"", property $ W.takeFileName "test/" == "") ,("P.takeFileName x `isSuffixOf` x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.takeFileName x `isSuffixOf` x) ,("W.takeFileName x `isSuffixOf` x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.takeFileName x `isSuffixOf` x) ,("P.takeFileName x == snd (P.splitFileName x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.takeFileName x == snd (P.splitFileName x)) ,("W.takeFileName x == snd (W.splitFileName x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.takeFileName x == snd (W.splitFileName x)) ,("P.takeFileName (P.replaceFileName x \"fred\") == \"fred\"", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.takeFileName (P.replaceFileName x "fred") == "fred") ,("W.takeFileName (W.replaceFileName x \"fred\") == \"fred\"", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.takeFileName (W.replaceFileName x "fred") == "fred") ,("P.takeFileName (x P. \"fred\") == \"fred\"", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.takeFileName (x P. "fred") == "fred") ,("W.takeFileName (x W. \"fred\") == \"fred\"", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.takeFileName (x W. "fred") == "fred") ,("P.isRelative (P.takeFileName x)", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.isRelative (P.takeFileName x)) ,("W.isRelative (W.takeFileName x)", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.isRelative (W.takeFileName x)) ,("P.takeBaseName \"/directory/file.ext\" == \"file\"", property $ P.takeBaseName "/directory/file.ext" == "file") ,("W.takeBaseName \"/directory/file.ext\" == \"file\"", property $ W.takeBaseName "/directory/file.ext" == "file") ,("P.takeBaseName \"file/test.txt\" == \"test\"", property $ P.takeBaseName "file/test.txt" == "test") ,("W.takeBaseName \"file/test.txt\" == \"test\"", property $ W.takeBaseName "file/test.txt" == "test") ,("P.takeBaseName \"dave.ext\" == \"dave\"", property $ P.takeBaseName "dave.ext" == "dave") ,("W.takeBaseName \"dave.ext\" == \"dave\"", property $ W.takeBaseName "dave.ext" == "dave") ,("P.takeBaseName \"\" == \"\"", property $ P.takeBaseName "" == "") ,("W.takeBaseName \"\" == \"\"", property $ W.takeBaseName "" == "") ,("P.takeBaseName \"test\" == \"test\"", property $ P.takeBaseName "test" == "test") ,("W.takeBaseName \"test\" == \"test\"", property $ W.takeBaseName "test" == "test") ,("P.takeBaseName (P.addTrailingPathSeparator x) == \"\"", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.takeBaseName (P.addTrailingPathSeparator x) == "") ,("W.takeBaseName (W.addTrailingPathSeparator x) == \"\"", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.takeBaseName (W.addTrailingPathSeparator x) == "") ,("P.takeBaseName \"file/file.tar.gz\" == \"file.tar\"", property $ P.takeBaseName "file/file.tar.gz" == "file.tar") ,("W.takeBaseName \"file/file.tar.gz\" == \"file.tar\"", property $ W.takeBaseName "file/file.tar.gz" == "file.tar") ,("P.replaceBaseName \"/directory/other.ext\" \"file\" == \"/directory/file.ext\"", property $ P.replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext") ,("W.replaceBaseName \"/directory/other.ext\" \"file\" == \"/directory/file.ext\"", property $ W.replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext") ,("P.replaceBaseName \"file/test.txt\" \"bob\" == \"file/bob.txt\"", property $ P.replaceBaseName "file/test.txt" "bob" == "file/bob.txt") ,("W.replaceBaseName \"file/test.txt\" \"bob\" == \"file/bob.txt\"", property $ W.replaceBaseName "file/test.txt" "bob" == "file/bob.txt") ,("P.replaceBaseName \"fred\" \"bill\" == \"bill\"", property $ P.replaceBaseName "fred" "bill" == "bill") ,("W.replaceBaseName \"fred\" \"bill\" == \"bill\"", property $ W.replaceBaseName "fred" "bill" == "bill") ,("P.replaceBaseName \"/dave/fred/bob.gz.tar\" \"new\" == \"/dave/fred/new.tar\"", property $ P.replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar") ,("W.replaceBaseName \"/dave/fred/bob.gz.tar\" \"new\" == \"/dave/fred/new.tar\"", property $ W.replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar") ,("P.replaceBaseName x (P.takeBaseName x) == x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.replaceBaseName x (P.takeBaseName x) == x) ,("W.replaceBaseName x (W.takeBaseName x) == x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.replaceBaseName x (W.takeBaseName x) == x) ,("P.hasTrailingPathSeparator \"test\" == False", property $ P.hasTrailingPathSeparator "test" == False) ,("W.hasTrailingPathSeparator \"test\" == False", property $ W.hasTrailingPathSeparator "test" == False) ,("P.hasTrailingPathSeparator \"test/\" == True", property $ P.hasTrailingPathSeparator "test/" == True) ,("W.hasTrailingPathSeparator \"test/\" == True", property $ W.hasTrailingPathSeparator "test/" == True) ,("P.hasTrailingPathSeparator (P.addTrailingPathSeparator x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.hasTrailingPathSeparator (P.addTrailingPathSeparator x)) ,("W.hasTrailingPathSeparator (W.addTrailingPathSeparator x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.hasTrailingPathSeparator (W.addTrailingPathSeparator x)) ,("P.hasTrailingPathSeparator x ==> P.addTrailingPathSeparator x == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.hasTrailingPathSeparator x ==> P.addTrailingPathSeparator x == x) ,("W.hasTrailingPathSeparator x ==> W.addTrailingPathSeparator x == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.hasTrailingPathSeparator x ==> W.addTrailingPathSeparator x == x) ,("P.addTrailingPathSeparator \"test/rest\" == \"test/rest/\"", property $ P.addTrailingPathSeparator "test/rest" == "test/rest/") ,("P.dropTrailingPathSeparator \"file/test/\" == \"file/test\"", property $ P.dropTrailingPathSeparator "file/test/" == "file/test") ,("W.dropTrailingPathSeparator \"file/test/\" == \"file/test\"", property $ W.dropTrailingPathSeparator "file/test/" == "file/test") ,("P.dropTrailingPathSeparator \"/\" == \"/\"", property $ P.dropTrailingPathSeparator "/" == "/") ,("W.dropTrailingPathSeparator \"/\" == \"/\"", property $ W.dropTrailingPathSeparator "/" == "/") ,("W.dropTrailingPathSeparator \"\\\\\" == \"\\\\\"", property $ W.dropTrailingPathSeparator "\\" == "\\") ,("not (P.hasTrailingPathSeparator (P.dropTrailingPathSeparator x)) || P.isDrive x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in not (P.hasTrailingPathSeparator (P.dropTrailingPathSeparator x)) || P.isDrive x) ,("P.takeDirectory \"/directory/other.ext\" == \"/directory\"", property $ P.takeDirectory "/directory/other.ext" == "/directory") ,("W.takeDirectory \"/directory/other.ext\" == \"/directory\"", property $ W.takeDirectory "/directory/other.ext" == "/directory") ,("P.takeDirectory x `isPrefixOf` x || P.takeDirectory x == \".\"", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.takeDirectory x `isPrefixOf` x || P.takeDirectory x == ".") ,("W.takeDirectory x `isPrefixOf` x || W.takeDirectory x == \".\"", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.takeDirectory x `isPrefixOf` x || W.takeDirectory x == ".") ,("P.takeDirectory \"foo\" == \".\"", property $ P.takeDirectory "foo" == ".") ,("W.takeDirectory \"foo\" == \".\"", property $ W.takeDirectory "foo" == ".") ,("P.takeDirectory \"/\" == \"/\"", property $ P.takeDirectory "/" == "/") ,("W.takeDirectory \"/\" == \"/\"", property $ W.takeDirectory "/" == "/") ,("P.takeDirectory \"/foo\" == \"/\"", property $ P.takeDirectory "/foo" == "/") ,("W.takeDirectory \"/foo\" == \"/\"", property $ W.takeDirectory "/foo" == "/") ,("P.takeDirectory \"/foo/bar/baz\" == \"/foo/bar\"", property $ P.takeDirectory "/foo/bar/baz" == "/foo/bar") ,("W.takeDirectory \"/foo/bar/baz\" == \"/foo/bar\"", property $ W.takeDirectory "/foo/bar/baz" == "/foo/bar") ,("P.takeDirectory \"/foo/bar/baz/\" == \"/foo/bar/baz\"", property $ P.takeDirectory "/foo/bar/baz/" == "/foo/bar/baz") ,("W.takeDirectory \"/foo/bar/baz/\" == \"/foo/bar/baz\"", property $ W.takeDirectory "/foo/bar/baz/" == "/foo/bar/baz") ,("P.takeDirectory \"foo/bar/baz\" == \"foo/bar\"", property $ P.takeDirectory "foo/bar/baz" == "foo/bar") ,("W.takeDirectory \"foo/bar/baz\" == \"foo/bar\"", property $ W.takeDirectory "foo/bar/baz" == "foo/bar") ,("W.takeDirectory \"foo\\\\bar\" == \"foo\"", property $ W.takeDirectory "foo\\bar" == "foo") ,("W.takeDirectory \"foo\\\\bar\\\\\\\\\" == \"foo\\\\bar\"", property $ W.takeDirectory "foo\\bar\\\\" == "foo\\bar") ,("W.takeDirectory \"C:\\\\\" == \"C:\\\\\"", property $ W.takeDirectory "C:\\" == "C:\\") ,("P.replaceDirectory \"root/file.ext\" \"/directory/\" == \"/directory/file.ext\"", property $ P.replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext") ,("W.replaceDirectory \"root/file.ext\" \"/directory/\" == \"/directory/file.ext\"", property $ W.replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext") ,("P.replaceDirectory x (P.takeDirectory x) `P.equalFilePath` x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.replaceDirectory x (P.takeDirectory x) `P.equalFilePath` x) ,("W.replaceDirectory x (W.takeDirectory x) `W.equalFilePath` x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.replaceDirectory x (W.takeDirectory x) `W.equalFilePath` x) ,("\"/directory\" P. \"file.ext\" == \"/directory/file.ext\"", property $ "/directory" P. "file.ext" == "/directory/file.ext") ,("\"/directory\" W. \"file.ext\" == \"/directory\\\\file.ext\"", property $ "/directory" W. "file.ext" == "/directory\\file.ext") ,("\"directory\" P. \"/file.ext\" == \"/file.ext\"", property $ "directory" P. "/file.ext" == "/file.ext") ,("\"directory\" W. \"/file.ext\" == \"/file.ext\"", property $ "directory" W. "/file.ext" == "/file.ext") ,("(P.takeDirectory x P. P.takeFileName x) `P.equalFilePath` x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in (P.takeDirectory x P. P.takeFileName x) `P.equalFilePath` x) ,("(W.takeDirectory x W. W.takeFileName x) `W.equalFilePath` x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in (W.takeDirectory x W. W.takeFileName x) `W.equalFilePath` x) ,("\"/\" P. \"test\" == \"/test\"", property $ "/" P. "test" == "/test") ,("\"home\" P. \"bob\" == \"home/bob\"", property $ "home" P. "bob" == "home/bob") ,("\"x:\" P. \"foo\" == \"x:/foo\"", property $ "x:" P. "foo" == "x:/foo") ,("\"C:\\\\foo\" W. \"bar\" == \"C:\\\\foo\\\\bar\"", property $ "C:\\foo" W. "bar" == "C:\\foo\\bar") ,("\"home\" W. \"bob\" == \"home\\\\bob\"", property $ "home" W. "bob" == "home\\bob") ,("\"home\" P. \"/bob\" == \"/bob\"", property $ "home" P. "/bob" == "/bob") ,("\"home\" W. \"C:\\\\bob\" == \"C:\\\\bob\"", property $ "home" W. "C:\\bob" == "C:\\bob") ,("\"home\" W. \"/bob\" == \"/bob\"", property $ "home" W. "/bob" == "/bob") ,("\"home\" W. \"\\\\bob\" == \"\\\\bob\"", property $ "home" W. "\\bob" == "\\bob") ,("\"C:\\\\home\" W. \"\\\\bob\" == \"\\\\bob\"", property $ "C:\\home" W. "\\bob" == "\\bob") ,("\"D:\\\\foo\" W. \"C:bar\" == \"C:bar\"", property $ "D:\\foo" W. "C:bar" == "C:bar") ,("\"C:\\\\foo\" W. \"C:bar\" == \"C:bar\"", property $ "C:\\foo" W. "C:bar" == "C:bar") ,("P.splitPath \"/directory/file.ext\" == [\"/\", \"directory/\", \"file.ext\"]", property $ P.splitPath "/directory/file.ext" == ["/", "directory/", "file.ext"]) ,("W.splitPath \"/directory/file.ext\" == [\"/\", \"directory/\", \"file.ext\"]", property $ W.splitPath "/directory/file.ext" == ["/", "directory/", "file.ext"]) ,("mconcat (P.splitPath x) == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in mconcat (P.splitPath x) == x) ,("mconcat (W.splitPath x) == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in mconcat (W.splitPath x) == x) ,("P.splitPath \"test//item/\" == [\"test//\", \"item/\"]", property $ P.splitPath "test//item/" == ["test//", "item/"]) ,("W.splitPath \"test//item/\" == [\"test//\", \"item/\"]", property $ W.splitPath "test//item/" == ["test//", "item/"]) ,("P.splitPath \"test/item/file\" == [\"test/\", \"item/\", \"file\"]", property $ P.splitPath "test/item/file" == ["test/", "item/", "file"]) ,("W.splitPath \"test/item/file\" == [\"test/\", \"item/\", \"file\"]", property $ W.splitPath "test/item/file" == ["test/", "item/", "file"]) ,("P.splitPath \"\" == []", property $ P.splitPath "" == []) ,("W.splitPath \"\" == []", property $ W.splitPath "" == []) ,("W.splitPath \"c:\\\\test\\\\path\" == [\"c:\\\\\", \"test\\\\\", \"path\"]", property $ W.splitPath "c:\\test\\path" == ["c:\\", "test\\", "path"]) ,("P.splitPath \"/file/test\" == [\"/\", \"file/\", \"test\"]", property $ P.splitPath "/file/test" == ["/", "file/", "test"]) ,("P.splitDirectories \"/directory/file.ext\" == [\"/\", \"directory\", \"file.ext\"]", property $ P.splitDirectories "/directory/file.ext" == ["/", "directory", "file.ext"]) ,("W.splitDirectories \"/directory/file.ext\" == [\"/\", \"directory\", \"file.ext\"]", property $ W.splitDirectories "/directory/file.ext" == ["/", "directory", "file.ext"]) ,("P.splitDirectories \"test/file\" == [\"test\", \"file\"]", property $ P.splitDirectories "test/file" == ["test", "file"]) ,("W.splitDirectories \"test/file\" == [\"test\", \"file\"]", property $ W.splitDirectories "test/file" == ["test", "file"]) ,("P.splitDirectories \"/test/file\" == [\"/\", \"test\", \"file\"]", property $ P.splitDirectories "/test/file" == ["/", "test", "file"]) ,("W.splitDirectories \"/test/file\" == [\"/\", \"test\", \"file\"]", property $ W.splitDirectories "/test/file" == ["/", "test", "file"]) ,("W.splitDirectories \"C:\\\\test\\\\file\" == [\"C:\\\\\", \"test\", \"file\"]", property $ W.splitDirectories "C:\\test\\file" == ["C:\\", "test", "file"]) ,("P.joinPath (P.splitDirectories x) `P.equalFilePath` x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.joinPath (P.splitDirectories x) `P.equalFilePath` x) ,("W.joinPath (W.splitDirectories x) `W.equalFilePath` x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.joinPath (W.splitDirectories x) `W.equalFilePath` x) ,("P.splitDirectories \"\" == []", property $ P.splitDirectories "" == []) ,("W.splitDirectories \"\" == []", property $ W.splitDirectories "" == []) ,("W.splitDirectories \"C:\\\\test\\\\\\\\\\\\file\" == [\"C:\\\\\", \"test\", \"file\"]", property $ W.splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]) ,("P.splitDirectories \"/test///file\" == [\"/\", \"test\", \"file\"]", property $ P.splitDirectories "/test///file" == ["/", "test", "file"]) ,("W.splitDirectories \"/test///file\" == [\"/\", \"test\", \"file\"]", property $ W.splitDirectories "/test///file" == ["/", "test", "file"]) ,("P.joinPath [\"/\", \"directory/\", \"file.ext\"] == \"/directory/file.ext\"", property $ P.joinPath ["/", "directory/", "file.ext"] == "/directory/file.ext") ,("W.joinPath [\"/\", \"directory/\", \"file.ext\"] == \"/directory/file.ext\"", property $ W.joinPath ["/", "directory/", "file.ext"] == "/directory/file.ext") ,("P.joinPath (P.splitPath x) == x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.joinPath (P.splitPath x) == x) ,("W.joinPath (W.splitPath x) == x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.joinPath (W.splitPath x) == x) ,("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") ,("x == y ==> P.equalFilePath x y", property $ \(QFilePath vx) (QFilePath vy) -> let y = toRawFilePath vy in let x = toRawFilePath vx in x == y ==> P.equalFilePath x y) ,("x == y ==> W.equalFilePath x y", property $ \(QFilePath vx) (QFilePath vy) -> let y = toRawFilePath vy in let x = toRawFilePath vx in x == y ==> W.equalFilePath x y) ,("P.normalise x == P.normalise y ==> P.equalFilePath x y", property $ \(QFilePath vx) (QFilePath vy) -> let y = toRawFilePath vy in let x = toRawFilePath vx in P.normalise x == P.normalise y ==> P.equalFilePath x y) ,("W.normalise x == W.normalise y ==> W.equalFilePath x y", property $ \(QFilePath vx) (QFilePath vy) -> let y = toRawFilePath vy in let x = toRawFilePath vx in W.normalise x == W.normalise y ==> W.equalFilePath x y) ,("P.equalFilePath \"foo\" \"foo/\"", property $ P.equalFilePath "foo" "foo/") ,("W.equalFilePath \"foo\" \"foo/\"", property $ W.equalFilePath "foo" "foo/") ,("not (P.equalFilePath \"foo\" \"/foo\")", property $ not (P.equalFilePath "foo" "/foo")) ,("not (W.equalFilePath \"foo\" \"/foo\")", property $ not (W.equalFilePath "foo" "/foo")) ,("not (P.equalFilePath \"foo\" \"FOO\")", property $ not (P.equalFilePath "foo" "FOO")) ,("W.equalFilePath \"foo\" \"FOO\"", property $ W.equalFilePath "foo" "FOO") ,("not (W.equalFilePath \"C:\" \"C:/\")", property $ not (W.equalFilePath "C:" "C:/")) ,("P.makeRelative \"/directory\" \"/directory/file.ext\" == \"file.ext\"", property $ P.makeRelative "/directory" "/directory/file.ext" == "file.ext") ,("W.makeRelative \"/directory\" \"/directory/file.ext\" == \"file.ext\"", property $ W.makeRelative "/directory" "/directory/file.ext" == "file.ext") ,("P.makeRelative (P.takeDirectory x) x `P.equalFilePath` P.takeFileName x", property $ \(QFilePathValidP vx) -> let x = toRawFilePath vx in P.makeRelative (P.takeDirectory x) x `P.equalFilePath` P.takeFileName x) ,("W.makeRelative (W.takeDirectory x) x `W.equalFilePath` W.takeFileName x", property $ \(QFilePathValidW vx) -> let x = toRawFilePath vx in W.makeRelative (W.takeDirectory x) x `W.equalFilePath` W.takeFileName x) ,("P.makeRelative x x == \".\"", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.makeRelative x x == ".") ,("W.makeRelative x x == \".\"", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.makeRelative x x == ".") ,("P.equalFilePath x y || (P.isRelative x && P.makeRelative y x == x) || P.equalFilePath (y P. P.makeRelative y x) x", property $ \(QFilePathValidP vx) (QFilePathValidP vy) -> let y = toRawFilePath vy in let x = toRawFilePath vx in P.equalFilePath x y || (P.isRelative x && P.makeRelative y x == x) || P.equalFilePath (y P. P.makeRelative y x) x) ,("W.equalFilePath x y || (W.isRelative x && W.makeRelative y x == x) || W.equalFilePath (y W. W.makeRelative y x) x", property $ \(QFilePathValidW vx) (QFilePathValidW vy) -> let y = toRawFilePath vy in let x = toRawFilePath vx in W.equalFilePath x y || (W.isRelative x && W.makeRelative y x == x) || W.equalFilePath (y W. W.makeRelative y x) x) ,("W.makeRelative \"C:\\\\Home\" \"c:\\\\home\\\\bob\" == \"bob\"", property $ W.makeRelative "C:\\Home" "c:\\home\\bob" == "bob") ,("W.makeRelative \"C:\\\\Home\" \"c:/home/bob\" == \"bob\"", property $ W.makeRelative "C:\\Home" "c:/home/bob" == "bob") ,("W.makeRelative \"C:\\\\Home\" \"D:\\\\Home\\\\Bob\" == \"D:\\\\Home\\\\Bob\"", property $ W.makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob") ,("W.makeRelative \"C:\\\\Home\" \"C:Home\\\\Bob\" == \"C:Home\\\\Bob\"", property $ W.makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob") ,("W.makeRelative \"/Home\" \"/home/bob\" == \"bob\"", property $ W.makeRelative "/Home" "/home/bob" == "bob") ,("W.makeRelative \"/\" \"//\" == \"//\"", property $ W.makeRelative "/" "//" == "//") ,("P.makeRelative \"/Home\" \"/home/bob\" == \"/home/bob\"", property $ P.makeRelative "/Home" "/home/bob" == "/home/bob") ,("P.makeRelative \"/home/\" \"/home/bob/foo/bar\" == \"bob/foo/bar\"", property $ P.makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar") ,("P.makeRelative \"/fred\" \"bob\" == \"bob\"", property $ P.makeRelative "/fred" "bob" == "bob") ,("P.makeRelative \"/file/test\" \"/file/test/fred\" == \"fred\"", property $ P.makeRelative "/file/test" "/file/test/fred" == "fred") ,("P.makeRelative \"/file/test\" \"/file/test/fred/\" == \"fred/\"", property $ P.makeRelative "/file/test" "/file/test/fred/" == "fred/") ,("P.makeRelative \"some/path\" \"some/path/a/b/c\" == \"a/b/c\"", property $ P.makeRelative "some/path" "some/path/a/b/c" == "a/b/c") ,("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) ,("W.isValid \"\\0\" == False", property $ W.isValid "\0" == False) ,("P.isValid \"/random_ path:*\" == True", property $ P.isValid "/random_ path:*" == True) ,("P.isValid x == (x /= mempty)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.isValid x == (x /= mempty)) ,("W.isValid \"c:\\\\test\" == True", property $ W.isValid "c:\\test" == True) ,("W.isValid \"c:\\\\test:of_test\" == False", property $ W.isValid "c:\\test:of_test" == False) ,("W.isValid \"test*\" == False", property $ W.isValid "test*" == False) ,("W.isValid \"c:\\\\test\\\\nul\" == False", property $ W.isValid "c:\\test\\nul" == False) ,("W.isValid \"c:\\\\test\\\\prn.txt\" == False", property $ W.isValid "c:\\test\\prn.txt" == False) ,("W.isValid \"c:\\\\nul\\\\file\" == False", property $ W.isValid "c:\\nul\\file" == False) ,("W.isValid \"\\\\\\\\\" == False", property $ W.isValid "\\\\" == False) ,("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 \"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 vx) -> let x = toRawFilePath vx in P.isValid (P.makeValid x)) ,("W.isValid (W.makeValid x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.isValid (W.makeValid x)) ,("P.isValid x ==> P.makeValid x == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.isValid x ==> P.makeValid x == x) ,("W.isValid x ==> W.makeValid x == x", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.isValid x ==> W.makeValid x == x) ,("P.makeValid \"\" == \"_\"", property $ P.makeValid "" == "_") ,("W.makeValid \"\" == \"_\"", property $ W.makeValid "" == "_") ,("P.makeValid \"file\\0name\" == \"file_name\"", property $ P.makeValid "file\0name" == "file_name") ,("W.makeValid \"file\\0name\" == \"file_name\"", property $ W.makeValid "file\0name" == "file_name") ,("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 \"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") ,("W.makeValid \"c:\\\\nul\\\\file\" == \"c:\\\\nul_\\\\file\"", property $ W.makeValid "c:\\nul\\file" == "c:\\nul_\\file") ,("W.makeValid \"\\\\\\\\\\\\foo\" == \"\\\\\\\\drive\"", property $ W.makeValid "\\\\\\foo" == "\\\\drive") ,("W.makeValid \"\\\\\\\\?\\\\D:file\" == \"\\\\\\\\?\\\\D:\\\\file\"", property $ W.makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file") ,("W.makeValid \"nul .txt\" == \"nul _.txt\"", property $ W.makeValid "nul .txt" == "nul _.txt") ,("W.isRelative \"path\\\\test\" == True", property $ W.isRelative "path\\test" == True) ,("W.isRelative \"c:\\\\test\" == False", property $ W.isRelative "c:\\test" == False) ,("W.isRelative \"c:test\" == True", property $ W.isRelative "c:test" == True) ,("W.isRelative \"c:\\\\\" == False", property $ W.isRelative "c:\\" == False) ,("W.isRelative \"c:/\" == False", property $ W.isRelative "c:/" == False) ,("W.isRelative \"c:\" == True", property $ W.isRelative "c:" == True) ,("W.isRelative \"\\\\\\\\foo\" == False", property $ W.isRelative "\\\\foo" == False) ,("W.isRelative \"\\\\\\\\?\\\\foo\" == False", property $ W.isRelative "\\\\?\\foo" == False) ,("W.isRelative \"\\\\\\\\?\\\\UNC\\\\foo\" == False", property $ W.isRelative "\\\\?\\UNC\\foo" == False) ,("W.isRelative \"/foo\" == True", property $ W.isRelative "/foo" == True) ,("W.isRelative \"\\\\foo\" == True", property $ W.isRelative "\\foo" == True) ,("P.isRelative \"test/path\" == True", property $ P.isRelative "test/path" == True) ,("P.isRelative \"/test\" == False", property $ P.isRelative "/test" == False) ,("P.isRelative \"/\" == False", property $ P.isRelative "/" == False) ,("P.isAbsolute x == not (P.isRelative x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in P.isAbsolute x == not (P.isRelative x)) ,("W.isAbsolute x == not (W.isRelative x)", property $ \(QFilePath vx) -> let x = toRawFilePath vx in W.isAbsolute x == not (W.isRelative x)) ] filepath-bytestring-1.4.100.3.2/tests/TestUtil.hs0000644000000000000000000001017107346545000017606 0ustar0000000000000000{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses #-} module TestUtil( (==>), QFilePath(..), QFilePathValidW(..), QFilePathValidP(..), toRawFilePath, equiv_0, equiv_1, equiv_2, equiv_3, module Test.QuickCheck, module Data.Maybe ) where import Test.QuickCheck hiding ((==>)) import Data.Maybe import Data.Word import Data.Char import Control.Monad import qualified System.FilePath.Windows as W import qualified System.FilePath.Posix as P import System.FilePath.ByteString (RawFilePath, encodeFilePath) infixr 0 ==> (==>) :: Bool -> Bool -> Bool a ==> b = not a || b class ToRawFilePath t where toRawFilePath :: t -> RawFilePath instance ToRawFilePath [Char] where toRawFilePath = encodeFilePath newtype QFilePathValidW = QFilePathValidW FilePath deriving Show instance ToRawFilePath QFilePathValidW where toRawFilePath (QFilePathValidW p) = toRawFilePath p instance Arbitrary QFilePathValidW where arbitrary = fmap (QFilePathValidW . W.makeValid) arbitraryFilePath shrink (QFilePathValidW x) = shrinkValid QFilePathValidW W.makeValid x newtype QFilePathValidP = QFilePathValidP FilePath deriving Show instance ToRawFilePath QFilePathValidP where toRawFilePath (QFilePathValidP p) = toRawFilePath p instance Arbitrary QFilePathValidP where arbitrary = fmap (QFilePathValidP . P.makeValid) arbitraryFilePath shrink (QFilePathValidP x) = shrinkValid QFilePathValidP P.makeValid x newtype QFilePath = QFilePath FilePath deriving Show instance ToRawFilePath QFilePath where toRawFilePath (QFilePath p) = toRawFilePath p instance Arbitrary QFilePath where arbitrary = fmap QFilePath arbitraryFilePath shrink (QFilePath x) = shrinkValid QFilePath id x -- | Generate an arbitrary FilePath use a few special (interesting) characters. arbitraryFilePath :: Gen FilePath arbitraryFilePath = sized $ \n -> do k <- choose (0,n) replicateM k $ elements "?./:\\a ;_" -- | Shrink, but also apply a validity function. Try and make shorter, or use more -- @a@ (since @a@ is pretty dull), but make sure you terminate even after valid. shrinkValid :: (FilePath -> a) -> (FilePath -> FilePath) -> FilePath -> [a] shrinkValid wrap valid o = [ wrap y | y <- map valid $ shrinkList (\x -> ['a' | x /= 'a']) o , length y < length o || (length y == length o && countA y > countA o)] where countA = length . filter (== 'a') class EquivResult t1 t2 where equivresult :: t1 -> t2 -> Bool instance Eq t => EquivResult t t where equivresult a b = a == b instance (EquivResult a c, EquivResult b d) => EquivResult (a, b) (c, d) where equivresult (a, b) (c, d) = equivresult a c && equivresult b d instance (EquivResult a b) => EquivResult (Maybe a) (Maybe b) where equivresult Nothing Nothing = True equivresult (Just a) (Just b) = equivresult a b equivresult _ _ = False instance (EquivResult a b) => EquivResult [a] [b] where equivresult a b = and (map (uncurry equivresult) (zip a b)) instance EquivResult FilePath RawFilePath where equivresult a b = toRawFilePath a == b instance EquivResult RawFilePath FilePath where equivresult a b = toRawFilePath b == a equiv_0 :: EquivResult a b => (Word8 -> a) -> (Char -> b) -> Property equiv_0 our their = property $ \w -> our w `equivresult` their (chr (fromIntegral w)) equiv_1 :: EquivResult a b => (RawFilePath -> a) -> (FilePath -> b) -> Property equiv_1 our their = property $ \(QFilePath f) -> our (toRawFilePath f) `equivresult` their f equiv_2 :: EquivResult a b => (RawFilePath -> RawFilePath -> a) -> (FilePath -> FilePath -> b) -> Property equiv_2 our their = property $ \(QFilePath a) (QFilePath b) -> our (toRawFilePath a) (toRawFilePath b) `equivresult` their a b equiv_3 :: EquivResult a b => ([RawFilePath] -> a) -> ([FilePath] -> b) -> Property equiv_3 our their = property $ \l -> our (map (\(QFilePath f) -> toRawFilePath f) l) `equivresult` their (map (\(QFilePath f) -> f) l)