packages feed

system-filepath 0.1 → 0.1.1

raw patch · 5 files changed

+130/−29 lines, 5 files

Files

System/FilePath.hs view
@@ -7,6 +7,10 @@ -- Maintainer:  jmillikin@gmail.com -- Portability:  portable --+-- High-level, byte-based file and directory path manipulations. You probably+-- want to import "System.FilePath.CurrentOS" instead, since it handles+-- detecting which rules to use in the current compilation.+-- -----------------------------------------------------------------------------  module System.FilePath@@ -49,7 +53,7 @@  import Prelude hiding (FilePath, concat, null) import qualified Prelude as P-import Data.Maybe (isNothing, listToMaybe)+import Data.Maybe (isNothing) import qualified Data.Monoid as M import System.FilePath.Internal import qualified Data.ByteString as B@@ -64,47 +68,71 @@ -- Basic properties ------------------------------------------------------------------------------- +-- | @null p == (p == 'empty')@ null :: FilePath -> Bool null = (== empty) +-- | Retrieves the 'FilePath'&#x27;s root. root :: FilePath -> FilePath root p = empty { pathRoot = pathRoot p } +-- | Retrieves the 'FilePath'&#x27;s directory. If the path is already a+-- directory, it is returned unchanged. directory :: FilePath -> FilePath directory p = empty 	{ pathRoot = pathRoot p-	, pathComponents = if P.null (pathComponents p) && isNothing (pathRoot p)-		then [B8.pack "."]-		else pathComponents p+	, pathComponents = let+		starts = map (Just . B8.pack) [".", ".."]+		dot = if safeHead (pathComponents p) `elem` starts+			then []+			else if isNothing (pathRoot p)+				then [B8.pack "."]+				else []+		in dot ++ pathComponents p 	} +-- | Retrieves the 'FilePath'&#x27;s parent directory. parent :: FilePath -> FilePath parent p = empty 	{ pathRoot = pathRoot p-	, pathComponents = if P.null (pathComponents p) && isNothing (pathRoot p)-		then [B8.pack "."]-		else if null (filename p)+	, pathComponents = let+		starts = map (Just . B8.pack) [".", ".."]+		components = if null (filename p) 			then safeInit (pathComponents p) 			else pathComponents p+		+		dot = if safeHead components `elem` starts+			then []+			else if isNothing (pathRoot p)+				then [B8.pack "."]+				else []+		+		in dot ++ components 	} +-- | Retrieve the filename component of a 'FilePath'.+-- @filename \"foo/bar.txt\" == \"bar.txt\"@. filename :: FilePath -> FilePath filename p = empty 	{ pathBasename = pathBasename p 	, pathExtensions = pathExtensions p 	} +-- | Retrieve the basename component of a 'FilePath'.+-- @filename \"foo/bar.txt\" == \"bar\"@. basename :: FilePath -> FilePath basename p = empty 	{ pathBasename = pathBasename p 	} +-- | Return whether the path is absolute. absolute :: FilePath -> Bool absolute p = case pathRoot p of 	Just RootPosix -> True 	Just (RootWindowsVolume _) -> True 	_ -> False +-- | Return whether the path is relative. relative :: FilePath -> Bool relative p = case pathRoot p of 	Just _ -> False@@ -114,6 +142,8 @@ -- Basic operations ------------------------------------------------------------------------------- +-- | Appends two 'FilePath's. If the second path is absolute, it is returned+-- unchanged. append :: FilePath -> FilePath -> FilePath append x y = if absolute y then y else xy where 	xy = y@@ -123,15 +153,18 @@ 	components = xComponents ++ pathComponents y 	xComponents = (pathComponents x ++) $ if null (filename x) 		then []-		else [B.concat $ [maybe (B8.pack "") id (pathBasename x)] ++ pathExtensions x]+		else [filenameBytes x] +-- | An alias for 'append'. (</>) :: FilePath -> FilePath -> FilePath (</>) = append +-- | A fold over 'append'. concat :: [FilePath] -> FilePath concat [] = empty concat ps = foldr1 append ps +-- | Find the greatest common prefix between two 'FilePath's. commonPrefix :: [FilePath] -> FilePath commonPrefix [] = empty commonPrefix ps = foldr1 step ps where@@ -155,48 +188,58 @@ 		else []  ---------------------------------------------------------------------------------- Basenames----------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Extensions ------------------------------------------------------------------------------- +-- | Get a 'FilePath'&#x27;s last extension, or 'Nothing' if it has no+-- extensions. extension :: FilePath -> Maybe B.ByteString extension p = case extensions p of 	[] -> Nothing 	es -> Just (last es) +-- | Get a 'FilePath'&#x27;s full extension list. extensions :: FilePath -> [B.ByteString] extensions = pathExtensions +-- | Get whether a 'FilePath'&#x27;s last extension is the predicate. hasExtension :: FilePath -> B.ByteString -> Bool hasExtension p e = extension p == Just e +-- | Append an extension to the end of a 'FilePath'. addExtension :: FilePath -> B.ByteString -> FilePath addExtension p ext = addExtensions p [ext] +-- | Append many extensions to the end of a 'FilePath'. addExtensions :: FilePath -> [B.ByteString] -> FilePath addExtensions p exts = p { pathExtensions = pathExtensions p ++ exts } +-- | An alias for 'addExtension'. (<.>) :: FilePath -> B.ByteString -> FilePath (<.>) = addExtension +-- | Remove a 'FilePath'&#x27;s last extension. dropExtension :: FilePath -> FilePath dropExtension p = p { pathExtensions = safeInit (pathExtensions p) } +-- | Remove all extensions from a 'FilePath'. dropExtensions :: FilePath -> FilePath dropExtensions p = p { pathExtensions = [] } +-- | Replace a 'FilePath'&#x27;s last extension. replaceExtension :: FilePath -> B.ByteString -> FilePath replaceExtension = addExtension . dropExtension +-- | Remove all extensions from a 'FilePath', and replace them with a new+-- list. replaceExtensions :: FilePath -> [B.ByteString] -> FilePath replaceExtensions = addExtensions . dropExtensions +-- | @splitExtension p = ('dropExtension' p, 'extension' p)@ splitExtension :: FilePath -> (FilePath, Maybe B.ByteString) splitExtension p = (dropExtension p, extension p) +-- | @splitExtensions p = ('dropExtensions' p, 'extensions' p)@ splitExtensions :: FilePath -> (FilePath, [B.ByteString]) splitExtensions p = (dropExtensions p, extensions p) @@ -208,3 +251,7 @@ safeInit xs = case xs of 	[] -> [] 	_ -> init xs++safeHead :: [a] -> Maybe a+safeHead [] = Nothing+safeHead (x:_) = Just x
System/FilePath/CurrentOS.hs view
@@ -7,9 +7,11 @@ -- Maintainer:  jmillikin@gmail.com -- Portability:  portable ----- Re-exports contents of "System.FilePath", defaulting to the current OS's--- rules when needed.+-- Re-exports contents of "System.FilePath.Rules", defaulting to the+-- current OS's rules when needed. --+-- Also enables 'Show' and 'S.IsString' instances for 'F.FilePath'.+-- -----------------------------------------------------------------------------  {-# LANGUAGE CPP #-}@@ -54,32 +56,42 @@ 	showsPrec d path = showParen (d > 10) $ 		showString "FilePath " . shows (toBytes path) +-- | See 'R.valid' valid :: F.FilePath -> Bool valid = R.valid currentOS +-- | See 'R.normalise' normalise :: F.FilePath -> F.FilePath normalise = R.normalise currentOS +-- | See 'R.equivalent' equivalent :: F.FilePath -> F.FilePath -> Bool equivalent = R.equivalent currentOS +-- | See 'R.toBytes' toBytes :: F.FilePath -> B.ByteString toBytes = R.toBytes currentOS +-- | See 'R.toLazyBytes' toLazyBytes :: F.FilePath -> BL.ByteString toLazyBytes = R.toLazyBytes currentOS +-- | See 'R.toString' toString :: F.FilePath -> String toString = R.toString currentOS +-- | See 'R.fromBytes' fromBytes :: B.ByteString -> F.FilePath fromBytes = R.fromBytes currentOS +-- | See 'R.fromLazyBytes' fromLazyBytes :: BL.ByteString -> F.FilePath fromLazyBytes = R.fromLazyBytes currentOS +-- | See 'R.fromString' fromString :: String -> F.FilePath fromString = R.fromString currentOS +-- | See 'R.splitSearchPath' splitSearchPath :: B.ByteString -> [F.FilePath] splitSearchPath = R.splitSearchPath currentOS
System/FilePath/Internal.hs view
@@ -16,6 +16,7 @@ import Data.Data (Data) import Data.Typeable (Typeable) import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8  ------------------------------------------------------------------------------- -- File Paths@@ -39,9 +40,19 @@ 	} 	deriving (Eq, Data, Typeable) +-- | A file path with no root, components, or filename empty :: FilePath empty = FilePath Nothing [] Nothing [] +filenameBytes :: FilePath -> B.ByteString+filenameBytes p = B.append name ext where+	name = case pathBasename p of+		Nothing -> B.empty+		Just name' -> name'+	ext = case pathExtensions p of+		[] -> B.empty+		exts -> B.intercalate (B8.pack ".") (B.empty:exts)+ ------------------------------------------------------------------------------- -- Rules -------------------------------------------------------------------------------@@ -49,10 +60,23 @@ data Rules = Rules 	{ rulesName :: String 	, toByteChunks :: FilePath -> [B.ByteString]+	+	-- | Parse a strict 'B.ByteString', such as  those received from+	-- OS libraries, into a 'FilePath'. 	, fromBytes :: B.ByteString -> FilePath 	, caseSensitive :: Bool+	+	-- | Check if a 'FilePath' is valid; that is, it must not contain+	-- any illegal characters, and must have a root appropriate to the+	-- current 'Rules'. 	, valid :: FilePath -> Bool+	+	-- | Split a search path, such as @$PATH@ or @$PYTHONPATH@, into+	-- a list of 'FilePath's. 	, splitSearchPath :: B.ByteString -> [FilePath]+	+	-- | Remove redundant characters. On case-insensitive platforms,+	-- also lowercases any ASCII uppercase characters. 	, normalise :: FilePath -> FilePath 	} 
System/FilePath/Rules.hs view
@@ -40,12 +40,16 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BL8 -import System.FilePath+import System.FilePath hiding (root, filename) import System.FilePath.Internal  ------------------------------------------------------------------------------- -- Rule-specific path properties -------------------------------------------------------------------------------++-- | Check if two different 'FilePath's refer to the same file. This does+-- not perform any link resolution, so some equivalent files might be+-- missed. equivalent :: Rules -> FilePath -> FilePath -> Bool equivalent r x y = n x == n y where 	n p = if caseSensitive r@@ -64,18 +68,32 @@ ------------------------------------------------------------------------------- -- Public helpers -------------------------------------------------------------------------------++-- | Convert a 'FilePath' into a strict 'B.ByteString', suitable for passing+-- to OS libraries. toBytes :: Rules -> FilePath -> B.ByteString toBytes r = B.concat . toByteChunks r +-- | Convert a 'FilePath' into a lazy 'BL.ByteString'. toLazyBytes :: Rules -> FilePath -> BL.ByteString toLazyBytes r = BL.fromChunks . toByteChunks r +-- | Parse a lazy 'BL.ByteString' into a 'FilePath'. fromLazyBytes :: Rules -> BL.ByteString -> FilePath fromLazyBytes r = fromBytes r . B.concat . BL.toChunks +-- | Convert a 'FilePath' into a lazy 'String'. This is useful for+-- interoperating with legacy libraries. No decoding is performed; the+-- string's character ordinals are equal to the path's original bytes. If you+-- need to display a 'FilePath' to the user, use 'toLazyBytes' and an+-- appropriate decoding function. toString :: Rules -> FilePath -> String toString r = BL8.unpack . toLazyBytes r +-- | Parse a lazy 'String' into a 'FilePath'. This is useful for+-- interoperating with legacy libraries. No encoding is performed;+-- characters are truncated to 8 bits. If you need to accept a 'FilePath'+-- from the user, use 'fromLazyBytes' and an appropriate encoding function. fromString :: Rules -> String -> FilePath fromString r = fromBytes r . B8.pack @@ -90,13 +108,7 @@ 	RootWindowsCurrentVolume -> "\\"  byteComponents :: FilePath -> [B.ByteString]-byteComponents path = pathComponents path ++ [name] where-	name = (`B.append` ext) $ case pathBasename path of-		Nothing -> B.empty-		Just name' -> name'-	ext = case pathExtensions path of-		[] -> B.empty-		exts -> B.intercalate (B8.pack ".") (B.empty:exts)+byteComponents path = pathComponents path ++ [filenameBytes path]  upperBytes :: B.ByteString -> B.ByteString upperBytes bytes = (`B.map` bytes) $ \b -> if b >= 0x41 && b <= 0x5A@@ -138,9 +150,12 @@ 			then pastRoot 			else init pastRoot 	-	(name, exts) = case B.split 0x2E (last split') of-		[] -> (Nothing, [])-		(name':exts') -> (Just name', exts')+	filename = last split'+	(name, exts) = if elem filename [B8.pack ".", B8.pack ".."]+		then (Just filename, [])+		else case B.split 0x2E (last split') of+			[] -> (Nothing, [])+			(name':exts') -> (Just name', exts')  posixValid :: FilePath -> Bool posixValid p = validRoot && validComponents where@@ -203,9 +218,12 @@ 			then pastRoot 			else init pastRoot 	-	(name, exts) = case B.split 0x2E (last split') of-		[] -> (Nothing, [])-		(name':exts') -> (Just name', exts')+	filename = last split'+	(name, exts) = if elem filename [B8.pack ".", B8.pack ".."]+		then (Just filename, [])+		else case B.split 0x2E (last split') of+			[] -> (Nothing, [])+			(name':exts') -> (Just name', exts')  winValid :: FilePath -> Bool winValid p = validRoot && noReserved && validCharacters where
system-filepath.cabal view
@@ -1,5 +1,5 @@ name: system-filepath-version: 0.1+version: 0.1.1 synopsis: High-level, byte-based file and directory path manipulations license: MIT license-file: license.txt