{-# LANGUAGE FlexibleInstances #-} module System.Linux.Btrfs.FilePathLike ( FilePathLike(..) , RawFilePath ) where import Data.String import Data.Monoid import Data.List import Foreign.C.String hiding ( peekCString, peekCStringLen , newCString, newCStringLen , withCString, withCStringLen) import qualified Foreign.C.String as S import qualified Data.ByteString as B import qualified Data.ByteString.Unsafe as B import qualified Data.ByteString.Char8 as B8 import System.Posix.Types import System.Posix.IO hiding (openFd) import System.Posix.ByteString.FilePath (RawFilePath) import qualified System.Posix.IO as S (openFd) import qualified System.Posix.IO.ByteString as B (openFd) import System.IO.Unsafe (unsafePerformIO) class (Monoid s, IsString s) => FilePathLike s where asString :: s -> String peekCString :: CString -> IO s peekCStringLen :: CStringLen -> IO s withCString :: s -> (CString -> IO a) -> IO a withCStringLen :: s -> (CStringLen -> IO a) -> IO a unsafeWithCStringLen :: s -> (CStringLen -> IO a) -> IO a openFd :: s -> OpenMode -> Maybe FileMode -> OpenFileFlags -> IO Fd dropTrailingSlash :: s -> s () :: s -> s -> s splitFileName :: s -> (s, s) instance FilePathLike [Char] where asString = id peekCString = S.peekCString peekCStringLen = S.peekCStringLen withCString = S.withCString withCStringLen = S.withCStringLen unsafeWithCStringLen = withCStringLen openFd = S.openFd dropTrailingSlash s = if null s' then "/" else s' where s' = dropWhileEnd (== '/') s _ s2@('/' : _) = s2 s1 "" = s1 "" s2 = s2 s1 s2 = if s1' == "/" then '/' : s2 else s1' ++ "/" ++ s2 where s1' = dropTrailingSlash s1 splitFileName s = (if null d then "./" else reverse d, reverse n) where (n, d) = span (/= '/') $ reverse s instance FilePathLike B.ByteString where asString = convert peekCString = B.packCString peekCStringLen = B.packCStringLen withCString = B.useAsCString withCStringLen = B.useAsCStringLen unsafeWithCStringLen = B.unsafeUseAsCStringLen openFd = B.openFd dropTrailingSlash s = if B.null s' then slashBS else s' where (s', _) = B8.spanEnd (== '/') s s1 s2 | B.null s1 = s2 | B.null s2 = s1 | B8.head s2 == '/' = s2 | otherwise = let s1' = dropTrailingSlash s1 in if B8.last s1' == '/' then slashBS <> s2 else B.concat [s1', slashBS, s2] splitFileName s = (if B.null d then curDir else d, n) where (d, n) = B8.spanEnd (/= '/') s curDir = B8.pack "./" convert :: (FilePathLike s1, FilePathLike s2) => s1 -> s2 convert s = unsafePerformIO $ unsafeWithCStringLen s peekCStringLen slashBS :: B.ByteString slashBS = B8.singleton '/'